Added mount dissector.
[obnox/wireshark/wip.git] / packet-rpc.c
1 /* packet-rpc.c
2  * Routines for rpc dissection
3  * Copyright 1999, Uwe Girlich <Uwe.Girlich@philosys.de>
4  * 
5  * $Id: packet-rpc.c,v 1.8 1999/11/11 21:22:00 nneul Exp $
6  * 
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@unicom.net>
9  * Copyright 1998 Gerald Combs
10  * 
11  * Copied from packet-smb.c
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35
36 #include <glib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include "packet.h"
40 #include "conversation.h"
41 #include "packet-rpc.h"
42
43
44 const value_string rpc_msg_type[3] = {
45         { RPC_CALL, "Call" },
46         { RPC_REPLY, "Reply" },
47         { 0, NULL }
48 };
49
50 const value_string rpc_reply_state[3] = {
51         { MSG_ACCEPTED, "accepted" },
52         { MSG_DENIED, "denied" },
53         { 0, NULL }
54 };
55
56 const value_string rpc_auth_flavor[5] = {
57         { AUTH_NULL, "AUTH_NULL" },
58         { AUTH_UNIX, "AUTH_UNIX" },
59         { AUTH_SHORT, "AUTH_SHORT" },
60         { AUTH_DES, "AUTH_DES" },
61         { 0, NULL }
62 };
63
64 const value_string rpc_accept_state[6] = {
65         { SUCCESS, "RPC executed successfully" },
66         { PROG_UNAVAIL, "remote hasn't exported program" },
67         { PROG_MISMATCH, "remote can't support version #" },
68         { PROC_UNAVAIL, "program can't support procedure" },
69         { GARBAGE_ARGS, "procedure can't decode params" },
70         { 0, NULL }
71 };
72
73 const value_string rpc_reject_state[3] = {
74         { RPC_MISMATCH, "RPC_MISMATCH" },
75         { AUTH_ERROR, "AUTH_ERROR" },
76         { 0, NULL }
77 };
78
79 const value_string rpc_auth_state[6] = {
80         { AUTH_BADCRED, "bad credential (seal broken)" },
81         { AUTH_REJECTEDCRED, "client must begin new session" },
82         { AUTH_BADVERF, "bad verifier (seal broken)" },
83         { AUTH_REJECTEDVERF, "verifier expired or replayed" },
84         { AUTH_TOOWEAK, "rejected for security reasons" },
85         { 0, NULL }
86 };
87
88
89 /* the protocol number */
90 static int proto_rpc = -1;
91
92
93 /* Hash table with info on RPC program numbers */
94 GHashTable *rpc_progs;
95
96 /* Hash table with info on RPC procedure numbers */
97 GHashTable *rpc_procs;
98
99
100 /***********************************/
101 /* Hash array with procedure names */
102 /***********************************/
103
104 /* compare 2 keys */
105 gint
106 rpc_proc_equal(gconstpointer k1, gconstpointer k2)
107 {
108         rpc_proc_info_key* key1 = (rpc_proc_info_key*) k1;
109         rpc_proc_info_key* key2 = (rpc_proc_info_key*) k2;
110
111         return ((key1->prog == key2->prog && 
112                 key1->vers == key2->vers &&
113                 key1->proc == key2->proc) ?
114         TRUE : FALSE);
115 }
116
117 /* calculate a hash key */
118 guint
119 rpc_proc_hash(gconstpointer k)
120 {
121         rpc_proc_info_key* key = (rpc_proc_info_key*) k;
122
123         return (key->prog ^ (key->vers<<16) ^ (key->proc<<24));
124 }
125
126
127 /* insert some entries */
128 void
129 rpc_init_proc_table(guint prog, guint vers, const vsff *proc_table)
130 {
131         const vsff *proc;
132
133         for (proc = proc_table ; proc->strptr!=NULL; proc++) {
134                 rpc_proc_info_key *key;
135                 rpc_proc_info_value *value;
136
137                 key = (rpc_proc_info_key *) g_malloc(sizeof(rpc_proc_info_key));
138                 key->prog = prog;
139                 key->vers = vers;
140                 key->proc = proc->value;
141
142                 value = (rpc_proc_info_value *) g_malloc(sizeof(rpc_proc_info_value));
143                 value->name = proc->strptr;
144                 value->dissect_call = proc->dissect_call;
145                 value->dissect_reply = proc->dissect_reply;
146
147                 g_hash_table_insert(rpc_procs,key,value);
148         }
149 }
150
151 /*----------------------------------------*/
152 /* end of Hash array with procedure names */
153 /*----------------------------------------*/
154
155
156 /*********************************/
157 /* Hash array with program names */
158 /*********************************/
159
160 /* compare 2 keys */
161 gint
162 rpc_prog_equal(gconstpointer k1, gconstpointer k2)
163 {
164         rpc_prog_info_key* key1 = (rpc_prog_info_key*) k1;
165         rpc_prog_info_key* key2 = (rpc_prog_info_key*) k2;
166
167         return ((key1->prog == key2->prog) ?
168         TRUE : FALSE);
169 }
170
171
172 /* calculate a hash key */
173 guint
174 rpc_prog_hash(gconstpointer k)
175 {
176         rpc_prog_info_key* key = (rpc_prog_info_key*) k;
177
178         return (key->prog);
179 }
180
181
182 void
183 rpc_init_prog(int proto, guint32 prog, int ett)
184 {
185         rpc_prog_info_key *key;
186         rpc_prog_info_value *value;
187
188         key = (rpc_prog_info_key *) g_malloc(sizeof(rpc_prog_info_key));
189         key->prog = prog;
190
191         value = (rpc_prog_info_value *) g_malloc(sizeof(rpc_prog_info_value));
192         value->proto = proto;
193         value->ett = ett;
194         value->progname = proto_registrar_get_abbrev(proto);
195
196         g_hash_table_insert(rpc_progs,key,value);
197 }
198
199 /*      return the name associated with a previously registered program. This
200         should probably eventually be expanded to use the rpc YP/NIS map
201         so that it can give names for programs not handled by ethereal */
202 char *rpc_prog_name(guint32 prog)
203 {
204         char *progname = NULL;
205         rpc_prog_info_key       rpc_prog_key;
206         rpc_prog_info_value     *rpc_prog;
207
208         rpc_prog_key.prog = prog;
209         if ((rpc_prog = g_hash_table_lookup(rpc_progs,&rpc_prog_key)) == NULL) {
210                 progname = "Unknown";
211         }
212         else {
213                 progname = rpc_prog->progname;
214         }
215         return progname;
216 }
217
218
219 /*--------------------------------------*/
220 /* end of Hash array with program names */
221 /*--------------------------------------*/
222
223
224 /* Placeholder for future dissectors.
225 It should vanish, if they are finally present. Up to this point, this
226 minimal variant serves as a detector for RPC services and can even find
227 request/reply pairs. */
228
229 #define NLM_PROGRAM     100021
230
231 static int proto_nlm = -1;
232
233 void init_incomplete_dissect(void)
234 {
235         proto_nlm = proto_register_protocol("Network Lock Manager", "NLM");
236         rpc_init_prog(proto_nlm, NLM_PROGRAM, ETT_NLM);
237 }
238
239
240 /*
241  * Init the hash tables. It will be called from ethereal_proto_init().
242  * ethereal_proto_init() calls later proto_init(), which calls 
243  * register_all_protocols().
244  * The proto_register_<some rpc program> functions use these hash tables
245  * here, so we need this order!
246  */
247 void
248 init_dissect_rpc()
249 {
250         rpc_progs = g_hash_table_new(rpc_prog_hash, rpc_prog_equal);
251         rpc_procs = g_hash_table_new(rpc_proc_hash, rpc_proc_equal);
252 }
253
254  
255 /* static array, first quick implementation, I'll switch over to GList soon */ 
256 rpc_call_info rpc_call_table[RPC_CALL_TABLE_LENGTH];
257 guint32 rpc_call_index = 0;
258 guint32 rpc_call_firstfree = 0;
259
260 void
261 rpc_call_insert(rpc_call_info *call)
262 {
263         /* some space left? */
264         if (rpc_call_firstfree<RPC_CALL_TABLE_LENGTH) {
265                 /* some space left */
266                 /* take the first free entry */
267                 rpc_call_index = rpc_call_firstfree;
268                 /* increase this limit */
269                 rpc_call_firstfree++;
270                 /* rpc_call_firstfree may now be RPC_CALL_TABLE_LENGTH */
271         }
272         else {
273                 /* no space left */
274                 /* the next entry, with wrap around */
275                 rpc_call_index = (rpc_call_index+1) % rpc_call_firstfree;
276         }
277                 
278         /* put the entry in */
279         memcpy(&rpc_call_table[rpc_call_index],call,sizeof(*call));
280         return;
281 }
282
283
284 rpc_call_info*
285 rpc_call_lookup(rpc_call_info *call)
286 {
287         int i;
288
289         i = rpc_call_index;
290         do {
291                 if (
292                         rpc_call_table[i].xid == call->xid &&
293                         rpc_call_table[i].conversation == call->conversation
294                 ) {
295                         return &rpc_call_table[i];
296                 }
297                 if (rpc_call_firstfree) {
298                         /* decrement by one, go to rpc_call_firstfree-1 
299                            at the start of the list */
300                         i = (i-1+rpc_call_firstfree) % rpc_call_firstfree;
301                 }
302         } while (i!=rpc_call_index);
303         return NULL;
304 }
305
306
307 unsigned int
308 roundup(unsigned int a)
309 {
310         unsigned int mod = a % 4;
311         return a + ((mod)? 4-mod : 0);
312 }
313
314
315 int
316 dissect_rpc_uint32(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
317 char* name, char* type)
318 {
319         guint32 value;
320
321         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
322         value = EXTRACT_UINT(pd, offset+0);
323
324         if (tree) {
325                 proto_tree_add_text(tree, offset, 4,
326                 "%s (%s): %u", name, type, value);
327         }
328
329         offset += 4;
330         return offset;
331 }
332
333
334 int
335 dissect_rpc_uint64(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
336 char* name, char* type)
337 {
338         guint32 value_low;
339         guint32 value_high;
340
341         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
342         value_high = EXTRACT_UINT(pd, offset+0);
343         value_low = EXTRACT_UINT(pd, offset+4);
344
345         if (tree) {
346                 if (value_high)
347                         proto_tree_add_text(tree, offset, 8,
348                                 "%s (%s): %x%08x", name, type, value_high, value_low);
349                 else
350                         proto_tree_add_text(tree, offset, 8,
351                                 "%s (%s): %u", name, type, value_low);
352         }
353
354         offset += 8;
355         return offset;
356 }
357
358
359
360 /* arbitrary limit */
361 #define RPC_STRING_MAXBUF 1024
362
363 int
364 dissect_rpc_string(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
365 {
366         proto_item *string_item;
367         proto_tree *string_tree = NULL;
368
369         guint32 string_length;
370         guint32 string_fill;
371         guint32 string_length_full;
372         char string_buffer[RPC_STRING_MAXBUF];
373
374         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
375         string_length = EXTRACT_UINT(pd,offset+0);
376         string_length_full = roundup(string_length);
377         string_fill = string_length_full - string_length;
378         if (!BYTES_ARE_IN_FRAME(offset+4,string_length_full)) return offset;
379         if (string_length>=sizeof(string_buffer)) return offset;
380         memcpy(string_buffer,pd+offset+4,string_length);
381         string_buffer[string_length] = '\0';
382         if (tree) {
383                 string_item = proto_tree_add_text(tree,offset+0,
384                         4+string_length_full,
385                         "%s: %s", name, string_buffer);
386                 if (string_item) {
387                         string_tree = proto_item_add_subtree(string_item, ETT_RPC_STRING);
388                 }
389         }
390         if (string_tree) {
391                 proto_tree_add_text(string_tree,offset+0,4,
392                         "length: %u", string_length);
393                 proto_tree_add_text(string_tree,offset+4,string_length,
394                         "text: %s", string_buffer);
395                 if (string_fill)
396                         proto_tree_add_text(string_tree,offset+4+string_length,string_fill,
397                                 "fill bytes: opaque data");
398         }
399
400         offset += 4 + string_length_full;
401         return offset;
402 }
403
404 int
405 dissect_rpc_string_item(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hfindex)
406 {
407         proto_item *string_item;
408         proto_tree *string_tree = NULL;
409
410         guint32 string_length;
411         guint32 string_fill;
412         guint32 string_length_full;
413         char string_buffer[RPC_STRING_MAXBUF];
414
415         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
416         string_length = EXTRACT_UINT(pd,offset+0);
417         string_length_full = roundup(string_length);
418         string_fill = string_length_full - string_length;
419         if (!BYTES_ARE_IN_FRAME(offset+4,string_length_full)) return offset;
420         if (string_length>=sizeof(string_buffer)) return offset;
421         memcpy(string_buffer,pd+offset+4,string_length);
422         string_buffer[string_length] = '\0';
423         if (tree) {
424                 string_item = proto_tree_add_text(tree,offset+0,
425                         4+string_length_full,
426                         "%s: %s", proto_registrar_get_name(hfindex), string_buffer);
427                 proto_tree_add_item_hidden(tree, hfindex, offset+4,
428                         string_length, string_buffer);
429                 if (string_item) {
430                         string_tree = proto_item_add_subtree(string_item, ETT_RPC_STRING);
431                 }
432         }
433         if (string_tree) {
434                 proto_tree_add_text(string_tree,offset+0,4,
435                         "length: %u", string_length);
436                 proto_tree_add_text(string_tree,offset+4,string_length,
437                         "text: %s", string_buffer);
438                 if (string_fill)
439                         proto_tree_add_text(string_tree,offset+4+string_length,string_fill,
440                                 "fill bytes: opaque data");
441         }
442
443         offset += 4 + string_length_full;
444         return offset;
445 }
446
447
448 void
449 dissect_rpc_auth( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
450 {
451         guint flavor;
452         guint length;
453         guint length_full;
454
455         /* both checks are made outside */
456         /* if (!BYTES_ARE_IN_FRAME(offset,8)) return; */
457         flavor = EXTRACT_UINT(pd,offset+0);
458         length = EXTRACT_UINT(pd,offset+4);
459         length_full = roundup(length);
460         /* if (!BYTES_ARE_IN_FRAME(offset+8,full_length)) return; */
461
462         if (tree) {
463                 proto_tree_add_text(tree,offset+0,4,
464                 "Flavor: %s (%u)", val_to_str(flavor,rpc_auth_flavor,"Unknown"),flavor);
465                 proto_tree_add_text(tree,offset+4,4,
466                         "Length: %u", length);
467         }
468
469         offset += 8;
470
471         switch (flavor) {
472                 case AUTH_UNIX: {
473                         guint stamp;
474                         guint uid;
475                         guint gid;
476                         guint gids_count;
477                         guint gids_i;
478                         guint gids_entry;
479                         proto_item *gitem;
480                         proto_tree *gtree = NULL;
481
482                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
483                         stamp = EXTRACT_UINT(pd,offset+0);
484                         if (tree)
485                                 proto_tree_add_text(tree,offset+0,4,
486                                         "stamp: 0x%08x", stamp);
487                         offset += 4;
488
489                         offset = dissect_rpc_string(pd,offset,fd,tree,"machinename");
490
491                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
492                         uid = EXTRACT_UINT(pd,offset+0);
493                         if (tree)
494                                 proto_tree_add_text(tree,offset+0,4,
495                                         "uid: %u", uid);
496                         offset += 4;
497
498                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
499                         gid = EXTRACT_UINT(pd,offset+0);
500                         if (tree)
501                                 proto_tree_add_text(tree,offset+0,4,
502                                         "gid: %u", gid);
503                         offset += 4;
504
505                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
506                         gids_count = EXTRACT_UINT(pd,offset+0);
507                         if (tree) {
508                                 gitem = proto_tree_add_text(tree, offset, 4+gids_count*4,
509                                 "gids");
510                                 gtree = proto_item_add_subtree(gitem, ETT_RPC_GIDS);
511                         }
512                         offset += 4;
513                         if (!BYTES_ARE_IN_FRAME(offset,4*gids_count)) return;
514                         for (gids_i = 0 ; gids_i < gids_count ; gids_i++) {
515                                 gids_entry = EXTRACT_UINT(pd,offset+0);
516                                 if (gtree)
517                                 proto_tree_add_text(gtree, offset, 4, 
518                                         "%u", gids_entry);
519                                 offset+=4;
520                         }
521                         /* how can I NOW change the gitem to print a list with
522                                 the first 16 gids? */
523                 }
524                 break;
525                 /*
526                 case AUTH_SHORT:
527
528                 break;
529                 */
530                 /* I have no tcpdump file with such a packet to verify the
531                         info from the RFC 1050 */
532                 /*
533                 case AUTH_DES:
534
535                 break;
536                 */
537                 default:
538                         if (length_full) {
539                                 if (tree)
540                                 proto_tree_add_text(tree,offset,
541                                 length_full, "opaque data");
542                         }
543         }
544 }
545
546 int
547 dissect_rpc_cred( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
548 {
549         guint length;
550         guint length_full;
551         proto_item *citem;
552         proto_tree *ctree;
553
554         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
555         length = EXTRACT_UINT(pd,offset+4);
556         length_full = roundup(length);
557         if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
558
559         if (tree) {
560                 citem = proto_tree_add_text(tree, offset, 8+length_full,
561                         "Credentials");
562                 ctree = proto_item_add_subtree(citem, ETT_RPC_CRED);
563                 dissect_rpc_auth(pd, offset, fd, ctree);
564         }
565         offset += 8 + length_full;
566
567         return offset;
568 }
569
570
571 int
572 dissect_rpc_verf( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
573 {
574         unsigned int length;
575         unsigned int length_full;
576         proto_item *vitem;
577         proto_tree *vtree;
578
579         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
580         length = EXTRACT_UINT(pd,offset+4);
581         length_full = roundup(length);
582         if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
583
584         if (tree) {
585                 vitem = proto_tree_add_text(tree, offset, 8+length_full,
586                         "Verifier");
587                 vtree = proto_item_add_subtree(vitem, ETT_RPC_VERF);
588                 dissect_rpc_auth(pd, offset, fd, vtree);
589         }
590         offset += 8 + length_full;
591
592         return offset;
593 }
594
595
596 void
597 dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
598                 guint32 msg_type, void* info)
599 {
600         unsigned int xid;
601         unsigned int rpcvers;
602         unsigned int prog;
603         unsigned int vers = 0;
604         unsigned int proc = 0;
605         int     proto = 0;
606         int     ett = 0;
607
608         unsigned int reply_state;
609         unsigned int accept_state;
610         unsigned int reject_state;
611
612         char *msg_type_name = NULL;
613         char *progname;
614         char *procname = NULL;
615         static char procname_static[20];
616
617         unsigned int vers_low;
618         unsigned int vers_high;
619
620         unsigned int auth_state;
621
622         proto_item *rpc_item=NULL;
623         proto_tree *rpc_tree = NULL;
624
625         proto_item *pitem=NULL;
626         proto_tree *ptree = NULL;
627         int offset_old = offset;
628
629         rpc_call_info   rpc_call_msg;
630         rpc_proc_info_key       key;
631         rpc_proc_info_value     *value = NULL;
632         conversation_t* conversation;
633
634         /* the last parameter can be either of these two types */
635         rpc_call_info   *rpc_call;
636         rpc_prog_info_key       rpc_prog_key;
637         rpc_prog_info_value     *rpc_prog;
638
639         dissect_function_t *dissect_function = NULL;
640
641         if (check_col(fd, COL_PROTOCOL))
642                 col_add_str(fd, COL_PROTOCOL, "RPC");
643
644         if (tree) {
645                 rpc_item = proto_tree_add_item(tree, proto_rpc, offset, END_OF_FRAME, NULL);
646                 if (rpc_item) {
647                         rpc_tree = proto_item_add_subtree(rpc_item, ETT_RPC);
648                 }
649         }
650
651         xid      = EXTRACT_UINT(pd,offset+0);
652         if (rpc_tree) {
653                 proto_tree_add_text(rpc_tree,offset+0,4,
654                         "XID: 0x%x (%u)", xid, xid);
655         }
656
657         /* we should better compare this with the argument?! */
658         msg_type = EXTRACT_UINT(pd,offset+4);
659         msg_type_name = val_to_str(msg_type,rpc_msg_type,"%u");
660         if (rpc_tree) {
661                 proto_tree_add_text(rpc_tree,offset+4,4,
662                         "msg_type: %s (%u)",
663                         msg_type_name, msg_type);
664         }
665
666         offset += 8;
667
668         if (msg_type==RPC_CALL) {
669                 /* we know already the proto-entry and the ETT-const */
670                 rpc_prog = (rpc_prog_info_value*)info;
671                 proto = rpc_prog->proto;
672                 ett = rpc_prog->ett;
673                 progname = rpc_prog->progname;
674
675                 rpcvers = EXTRACT_UINT(pd,offset+0);
676                 if (rpc_tree) {
677                         proto_tree_add_text(rpc_tree,offset+0,4,
678                                 "RPC Version: %u", rpcvers);
679                 }
680
681                 prog = EXTRACT_UINT(pd,offset+4);
682                 
683                 if (rpc_tree) {
684                         proto_tree_add_text(rpc_tree,offset+4,4,
685                                 "Program: %s (%u)", progname, prog);
686                 }
687                 
688                 if (check_col(fd, COL_PROTOCOL)) {
689                         /* Set the protocol name to the underlying
690                            program name. */
691                         col_add_fstr(fd, COL_PROTOCOL, "%s", progname);
692                 }
693
694                 if (!BYTES_ARE_IN_FRAME(offset+8,4)) return;
695                 vers = EXTRACT_UINT(pd,offset+8);
696                 if (rpc_tree) {
697                         proto_tree_add_text(rpc_tree,offset+8,4,
698                                 "Program Version: %u",vers);
699                 }
700
701                 if (!BYTES_ARE_IN_FRAME(offset+12,4)) return;
702                 proc = EXTRACT_UINT(pd,offset+12);
703
704                 key.prog = prog;
705                 key.vers = vers;
706                 key.proc = proc;
707
708                 value = g_hash_table_lookup(rpc_procs,&key);
709                 if (value != NULL) {
710                         dissect_function = value->dissect_call;
711                         procname = value->name;
712                 }
713                 else {
714                         /* happens only with strange program versions or
715                            non-existing dissectors */
716                         dissect_function = NULL;
717                         sprintf(procname_static, "proc-%u", proc);
718                         procname = procname_static;
719                 }
720                 if (rpc_tree) {
721                         proto_tree_add_text(rpc_tree,offset+12,4,
722                                 "Procedure: %s (%u)", procname, proc);
723                 }
724
725                 if (check_col(fd, COL_INFO)) {
726                         col_add_fstr(fd, COL_INFO,"V%u %s %s XID 0x%x",
727                                 vers,
728                                 procname,
729                                 msg_type_name,
730                                 xid);
731                 }
732
733                 conversation = find_conversation(&pi.src, &pi.dst, pi.ptype,
734                         pi.srcport, pi.destport);
735                 if (conversation == NULL) {
736                         /* It's not part of any conversation - create a new one. */
737                         conversation = conversation_new(&pi.src, &pi.dst, pi.ptype,
738                                 pi.srcport, pi.destport, NULL);
739                 }
740
741                 /* prepare the key data */
742                 rpc_call_msg.xid = xid;
743                 rpc_call_msg.conversation = conversation;
744
745                 /* look up the request */
746                 if (rpc_call_lookup(&rpc_call_msg)) {
747                         /* duplicate request */
748                         if (check_col(fd, COL_INFO)) {
749                                 col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
750                         }
751                 }
752                 else {
753                         /* prepare the value data */
754                         rpc_call_msg.replies = 0;
755                         rpc_call_msg.prog = prog;
756                         rpc_call_msg.vers = vers;
757                         rpc_call_msg.proc = proc;
758                         rpc_call_msg.proc_info = value;
759                         /* store it */
760                         rpc_call_insert(&rpc_call_msg);
761                 }
762
763                 offset += 16;
764
765                 offset = dissect_rpc_cred(pd, offset, fd, rpc_tree);
766                 offset = dissect_rpc_verf(pd, offset, fd, rpc_tree);
767
768                 /* go to the next dissector */
769                 /* goto dissect_rpc_prog; */
770
771         } /* end of RPC call */
772         else if (msg_type == RPC_REPLY)
773         {
774                 /* we know already the type from the calling routine */
775                 rpc_call = (rpc_call_info*)info;
776                 prog = rpc_call->prog;
777                 vers = rpc_call->vers;
778                 proc = rpc_call->proc;
779                 if (rpc_call->proc_info != NULL) {
780                         dissect_function = rpc_call->proc_info->dissect_reply;
781                         if (rpc_call->proc_info->name != NULL) {
782                                 procname = rpc_call->proc_info->name;
783                         }
784                         else {
785                                 sprintf(procname_static, "proc-%u", proc);
786                                 procname = procname_static;
787                         }
788                 }
789                 else {
790                         dissect_function = NULL;
791                         sprintf(procname_static, "proc-%u", proc);
792                         procname = procname_static;
793                 }
794                 rpc_call->replies++;
795
796                 rpc_prog_key.prog = prog;
797                 if ((rpc_prog = g_hash_table_lookup(rpc_progs,&rpc_prog_key)) == NULL) {
798                         proto = 0;
799                         ett = 0;
800                         progname = "Unknown";
801                 }
802                 else {
803                         proto = rpc_prog->proto;
804                         ett = rpc_prog->ett;
805                         progname = rpc_prog->progname;
806
807                         if (check_col(fd, COL_PROTOCOL)) {
808                                 /* Set the protocol name to the underlying
809                                    program name. */
810                                 col_add_fstr(fd, COL_PROTOCOL, "%s",
811                                     progname);
812                         }
813                 }
814
815                 if (check_col(fd, COL_INFO)) {
816                         col_add_fstr(fd, COL_INFO,"V%u %s %s XID 0x%x",
817                                 vers,
818                                 procname,
819                                 msg_type_name,
820                                 xid);
821                 }
822
823                 if (rpc_tree) {
824                         proto_tree_add_text(rpc_tree,0,0,
825                                 "Program: %s (%u)", 
826                                 progname, prog);
827                         proto_tree_add_text(rpc_tree,0,0,
828                                 "Program Version: %u", vers);
829                         proto_tree_add_text(rpc_tree,0,0,
830                                 "Procedure: %s (%u)", procname, proc);
831                 }
832
833                 if (rpc_call->replies>1) {
834                         if (check_col(fd, COL_INFO)) {
835                                 col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
836                         }
837                 }
838
839                 if (!BYTES_ARE_IN_FRAME(offset,4)) return;
840                 reply_state = EXTRACT_UINT(pd,offset+0);
841                 if (rpc_tree) {
842                         proto_tree_add_text(rpc_tree,offset+0, 4,
843                                 "Reply State: %s (%u)",
844                                 val_to_str(reply_state,rpc_reply_state,"Unknown"),
845                                 reply_state);
846                 }
847                 offset += 4;
848
849                 if (reply_state == MSG_ACCEPTED) {
850                         offset = dissect_rpc_verf(pd, offset, fd, rpc_tree);
851                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
852                         accept_state = EXTRACT_UINT(pd,offset+0);
853                         if (rpc_tree) {
854                                 proto_tree_add_text(rpc_tree,offset+0, 4,
855                                         "Accept State: %s (%u)", 
856                                         val_to_str(accept_state,rpc_accept_state,"Unknown"),
857                                         accept_state);
858                         }
859                         offset += 4;
860                         switch (accept_state) {
861                                 case SUCCESS:
862                                         /* now goto the lower protocol */
863                                         goto dissect_rpc_prog;
864                                 break;
865                                 case PROG_MISMATCH:
866                                         if (!BYTES_ARE_IN_FRAME(offset,8)) return;
867                                         vers_low = EXTRACT_UINT(pd,offset+0);
868                                         vers_high = EXTRACT_UINT(pd,offset+4);
869                                         if (rpc_tree) {
870                                                 proto_tree_add_text(rpc_tree,
871                                                         offset+0, 4,
872                                                         "min. Program Version: %u",
873                                                         vers_low);
874                                                 proto_tree_add_text(rpc_tree,
875                                                         offset+4, 4,
876                                                         "max. Program Version: %u",
877                                                         vers_high);
878                                         }
879                                         offset += 8;
880                                 break;
881                                 default:
882                                         /* void */
883                                 break;
884                         }
885                 } else if (reply_state == MSG_DENIED) {
886                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
887                         reject_state = EXTRACT_UINT(pd,offset+0);
888                         if (rpc_tree) {
889                                 proto_tree_add_text(rpc_tree, offset+0, 4,
890                                         "Reject State: %s (%u)",
891                                         val_to_str(reject_state,rpc_reject_state,"Unknown"),
892                                         reject_state);
893                         }
894                         offset += 4;
895
896                         if (reject_state==RPC_MISMATCH) {
897                                 if (!BYTES_ARE_IN_FRAME(offset,8)) return;
898                                 vers_low = EXTRACT_UINT(pd,offset+0);
899                                 vers_high = EXTRACT_UINT(pd,offset+4);
900                                 if (rpc_tree) {
901                                         proto_tree_add_text(rpc_tree,
902                                                 offset+0, 4,
903                                                 "min. RPC Version: %u",
904                                                 vers_low);
905                                         proto_tree_add_text(rpc_tree,
906                                                 offset+4, 4,
907                                                 "max. RPC Version: %u",
908                                                 vers_high);
909                                 }
910                                 offset += 8;
911                         } else if (reject_state==AUTH_ERROR) {
912                                 if (!BYTES_ARE_IN_FRAME(offset,4)) return;
913                                 auth_state = EXTRACT_UINT(pd,offset+0);
914                                 if (rpc_tree) {
915                                         proto_tree_add_text(rpc_tree,
916                                                 offset+0, 4,
917                                                 "Authentication error: %s (%u)",
918                                                 val_to_str(auth_state,rpc_auth_state,"Unknown"),
919                                                 auth_state);
920                                 }
921                                 offset += 4;
922                         }
923                 } 
924         } /* end of RPC reply */
925
926 dissect_rpc_prog:
927         /* I know, goto is evil but it works as it is. */
928
929         /* now we know, that RPC was shorter */
930         if (rpc_item) {
931                 proto_item_set_len(rpc_item, offset - offset_old);
932         }
933
934         /* create here the program specific sub-tree */
935         if (tree) {
936                 pitem = proto_tree_add_item(tree, proto, offset, END_OF_FRAME);
937                 if (pitem)
938                         ptree = proto_item_add_subtree(pitem, ett);
939                 }
940
941         /* call a specific dissection */
942         if (dissect_function != NULL) {
943                 offset = dissect_function(pd, offset, fd, ptree);
944         }
945
946         /* dissect any remaining bytes (incomplete dissection) as pure data in
947            the ptree */
948         dissect_data(pd, offset, fd, ptree);
949 }
950
951 /* will be called from file.c on every new file open */
952 void
953 rpc_init_protocol(void)
954 {
955         rpc_call_index = 0;
956         rpc_call_firstfree = 0;
957 }
958
959
960 /* will be called once from register.c at startup time */
961 void
962 proto_register_rpc(void)
963 {
964         proto_rpc = proto_register_protocol("Remote Procedure Call", "rpc");
965
966         /* please remove this, if all specific dissectors are ready */
967         init_incomplete_dissect();
968 }
969