bafd325775fc6e283f150243fb9706231b214b91
[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.7 1999/11/11 16:20:24 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 /*--------------------------------------*/
200 /* end of Hash array with program names */
201 /*--------------------------------------*/
202
203
204 /* Placeholder for future dissectors.
205 It should vanish, if they are finally present. Up to this point, this
206 minimal variant serves as a detector for RPC services and can even find
207 request/reply pairs. */
208
209 #define MNT_PROGRAM     100005
210 #define NLM_PROGRAM     100021
211 #define STAT_PROGRAM    100024
212
213 static int proto_mnt = -1;
214 static int proto_nlm = -1;
215 static int proto_stat = -1;
216
217 void init_incomplete_dissect(void)
218 {
219         proto_mnt = proto_register_protocol("Mount", "MNT");
220         rpc_init_prog(proto_mnt, MNT_PROGRAM, ETT_MNT);
221
222         proto_nlm = proto_register_protocol("Network Lock Manager", "NLM");
223         rpc_init_prog(proto_nlm, NLM_PROGRAM, ETT_NLM);
224
225         proto_stat = proto_register_protocol("Status", "STAT");
226         rpc_init_prog(proto_stat, STAT_PROGRAM, ETT_STAT);
227 }
228
229
230 /*
231  * Init the hash tables. It will be called from ethereal_proto_init().
232  * ethereal_proto_init() calls later proto_init(), which calls 
233  * register_all_protocols().
234  * The proto_register_<some rpc program> functions use these hash tables
235  * here, so we need this order!
236  */
237 void
238 init_dissect_rpc()
239 {
240         rpc_progs = g_hash_table_new(rpc_prog_hash, rpc_prog_equal);
241         rpc_procs = g_hash_table_new(rpc_proc_hash, rpc_proc_equal);
242 }
243
244  
245 /* static array, first quick implementation, I'll switch over to GList soon */ 
246 rpc_call_info rpc_call_table[RPC_CALL_TABLE_LENGTH];
247 guint32 rpc_call_index = 0;
248 guint32 rpc_call_firstfree = 0;
249
250 void
251 rpc_call_insert(rpc_call_info *call)
252 {
253         /* some space left? */
254         if (rpc_call_firstfree<RPC_CALL_TABLE_LENGTH) {
255                 /* some space left */
256                 /* take the first free entry */
257                 rpc_call_index = rpc_call_firstfree;
258                 /* increase this limit */
259                 rpc_call_firstfree++;
260                 /* rpc_call_firstfree may now be RPC_CALL_TABLE_LENGTH */
261         }
262         else {
263                 /* no space left */
264                 /* the next entry, with wrap around */
265                 rpc_call_index = (rpc_call_index+1) % rpc_call_firstfree;
266         }
267                 
268         /* put the entry in */
269         memcpy(&rpc_call_table[rpc_call_index],call,sizeof(*call));
270         return;
271 }
272
273
274 rpc_call_info*
275 rpc_call_lookup(rpc_call_info *call)
276 {
277         int i;
278
279         i = rpc_call_index;
280         do {
281                 if (
282                         rpc_call_table[i].xid == call->xid &&
283                         rpc_call_table[i].conversation == call->conversation
284                 ) {
285                         return &rpc_call_table[i];
286                 }
287                 if (rpc_call_firstfree) {
288                         /* decrement by one, go to rpc_call_firstfree-1 
289                            at the start of the list */
290                         i = (i-1+rpc_call_firstfree) % rpc_call_firstfree;
291                 }
292         } while (i!=rpc_call_index);
293         return NULL;
294 }
295
296
297 unsigned int
298 roundup(unsigned int a)
299 {
300         unsigned int mod = a % 4;
301         return a + ((mod)? 4-mod : 0);
302 }
303
304
305 int
306 dissect_rpc_uint32(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
307 char* name, char* type)
308 {
309         guint32 value;
310
311         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
312         value = EXTRACT_UINT(pd, offset+0);
313
314         if (tree) {
315                 proto_tree_add_text(tree, offset, 4,
316                 "%s (%s): %u", name, type, value);
317         }
318
319         offset += 4;
320         return offset;
321 }
322
323
324 int
325 dissect_rpc_uint64(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
326 char* name, char* type)
327 {
328         guint32 value_low;
329         guint32 value_high;
330
331         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
332         value_high = EXTRACT_UINT(pd, offset+0);
333         value_low = EXTRACT_UINT(pd, offset+4);
334
335         if (tree) {
336                 if (value_high)
337                         proto_tree_add_text(tree, offset, 8,
338                                 "%s (%s): %x%08x", name, type, value_high, value_low);
339                 else
340                         proto_tree_add_text(tree, offset, 8,
341                                 "%s (%s): %u", name, type, value_low);
342         }
343
344         offset += 8;
345         return offset;
346 }
347
348
349
350 /* arbitrary limit */
351 #define RPC_STRING_MAXBUF 1024
352
353 int
354 dissect_rpc_string(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, char* name)
355 {
356         proto_item *string_item;
357         proto_tree *string_tree = NULL;
358
359         guint32 string_length;
360         guint32 string_fill;
361         guint32 string_length_full;
362         char string_buffer[RPC_STRING_MAXBUF];
363
364         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
365         string_length = EXTRACT_UINT(pd,offset+0);
366         string_length_full = roundup(string_length);
367         string_fill = string_length_full - string_length;
368         if (!BYTES_ARE_IN_FRAME(offset+4,string_length_full)) return offset;
369         if (string_length>=sizeof(string_buffer)) return offset;
370         memcpy(string_buffer,pd+offset+4,string_length);
371         string_buffer[string_length] = '\0';
372         if (tree) {
373                 string_item = proto_tree_add_text(tree,offset+0,
374                         4+string_length_full,
375                         "%s: %s", name, string_buffer);
376                 if (string_item) {
377                         string_tree = proto_item_add_subtree(string_item, ETT_RPC_STRING);
378                 }
379         }
380         if (string_tree) {
381                 proto_tree_add_text(string_tree,offset+0,4,
382                         "length: %u", string_length);
383                 proto_tree_add_text(string_tree,offset+4,string_length,
384                         "text: %s", string_buffer);
385                 if (string_fill)
386                         proto_tree_add_text(string_tree,offset+4+string_length,string_fill,
387                                 "fill bytes: opaque data");
388         }
389
390         offset += 4 + string_length_full;
391         return offset;
392 }
393
394 int
395 dissect_rpc_string_item(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int hfindex)
396 {
397         proto_item *string_item;
398         proto_tree *string_tree = NULL;
399
400         guint32 string_length;
401         guint32 string_fill;
402         guint32 string_length_full;
403         char string_buffer[RPC_STRING_MAXBUF];
404
405         if (!BYTES_ARE_IN_FRAME(offset,4)) return offset;
406         string_length = EXTRACT_UINT(pd,offset+0);
407         string_length_full = roundup(string_length);
408         string_fill = string_length_full - string_length;
409         if (!BYTES_ARE_IN_FRAME(offset+4,string_length_full)) return offset;
410         if (string_length>=sizeof(string_buffer)) return offset;
411         memcpy(string_buffer,pd+offset+4,string_length);
412         string_buffer[string_length] = '\0';
413         if (tree) {
414                 string_item = proto_tree_add_text(tree,offset+0,
415                         4+string_length_full,
416                         "%s: %s", proto_registrar_get_name(hfindex), string_buffer);
417                 proto_tree_add_item_hidden(tree, hfindex, offset+4,
418                         string_length, string_buffer);
419                 if (string_item) {
420                         string_tree = proto_item_add_subtree(string_item, ETT_RPC_STRING);
421                 }
422         }
423         if (string_tree) {
424                 proto_tree_add_text(string_tree,offset+0,4,
425                         "length: %u", string_length);
426                 proto_tree_add_text(string_tree,offset+4,string_length,
427                         "text: %s", string_buffer);
428                 if (string_fill)
429                         proto_tree_add_text(string_tree,offset+4+string_length,string_fill,
430                                 "fill bytes: opaque data");
431         }
432
433         offset += 4 + string_length_full;
434         return offset;
435 }
436
437
438 void
439 dissect_rpc_auth( const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
440 {
441         guint flavor;
442         guint length;
443         guint length_full;
444
445         /* both checks are made outside */
446         /* if (!BYTES_ARE_IN_FRAME(offset,8)) return; */
447         flavor = EXTRACT_UINT(pd,offset+0);
448         length = EXTRACT_UINT(pd,offset+4);
449         length_full = roundup(length);
450         /* if (!BYTES_ARE_IN_FRAME(offset+8,full_length)) return; */
451
452         if (tree) {
453                 proto_tree_add_text(tree,offset+0,4,
454                 "Flavor: %s (%u)", val_to_str(flavor,rpc_auth_flavor,"Unknown"),flavor);
455                 proto_tree_add_text(tree,offset+4,4,
456                         "Length: %u", length);
457         }
458
459         offset += 8;
460
461         switch (flavor) {
462                 case AUTH_UNIX: {
463                         guint stamp;
464                         guint uid;
465                         guint gid;
466                         guint gids_count;
467                         guint gids_i;
468                         guint gids_entry;
469                         proto_item *gitem;
470                         proto_tree *gtree = NULL;
471
472                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
473                         stamp = EXTRACT_UINT(pd,offset+0);
474                         if (tree)
475                                 proto_tree_add_text(tree,offset+0,4,
476                                         "stamp: 0x%08x", stamp);
477                         offset += 4;
478
479                         offset = dissect_rpc_string(pd,offset,fd,tree,"machinename");
480
481                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
482                         uid = EXTRACT_UINT(pd,offset+0);
483                         if (tree)
484                                 proto_tree_add_text(tree,offset+0,4,
485                                         "uid: %u", uid);
486                         offset += 4;
487
488                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
489                         gid = EXTRACT_UINT(pd,offset+0);
490                         if (tree)
491                                 proto_tree_add_text(tree,offset+0,4,
492                                         "gid: %u", gid);
493                         offset += 4;
494
495                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
496                         gids_count = EXTRACT_UINT(pd,offset+0);
497                         if (tree) {
498                                 gitem = proto_tree_add_text(tree, offset, 4+gids_count*4,
499                                 "gids");
500                                 gtree = proto_item_add_subtree(gitem, ETT_RPC_GIDS);
501                         }
502                         offset += 4;
503                         if (!BYTES_ARE_IN_FRAME(offset,4*gids_count)) return;
504                         for (gids_i = 0 ; gids_i < gids_count ; gids_i++) {
505                                 gids_entry = EXTRACT_UINT(pd,offset+0);
506                                 if (gtree)
507                                 proto_tree_add_text(gtree, offset, 4, 
508                                         "%u", gids_entry);
509                                 offset+=4;
510                         }
511                         /* how can I NOW change the gitem to print a list with
512                                 the first 16 gids? */
513                 }
514                 break;
515                 /*
516                 case AUTH_SHORT:
517
518                 break;
519                 */
520                 /* I have no tcpdump file with such a packet to verify the
521                         info from the RFC 1050 */
522                 /*
523                 case AUTH_DES:
524
525                 break;
526                 */
527                 default:
528                         if (length_full) {
529                                 if (tree)
530                                 proto_tree_add_text(tree,offset,
531                                 length_full, "opaque data");
532                         }
533         }
534 }
535
536 int
537 dissect_rpc_cred( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
538 {
539         guint length;
540         guint length_full;
541         proto_item *citem;
542         proto_tree *ctree;
543
544         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
545         length = EXTRACT_UINT(pd,offset+4);
546         length_full = roundup(length);
547         if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
548
549         if (tree) {
550                 citem = proto_tree_add_text(tree, offset, 8+length_full,
551                         "Credentials");
552                 ctree = proto_item_add_subtree(citem, ETT_RPC_CRED);
553                 dissect_rpc_auth(pd, offset, fd, ctree);
554         }
555         offset += 8 + length_full;
556
557         return offset;
558 }
559
560
561 int
562 dissect_rpc_verf( const u_char *pd, int offset, frame_data *fd, proto_tree *tree )
563 {
564         unsigned int length;
565         unsigned int length_full;
566         proto_item *vitem;
567         proto_tree *vtree;
568
569         if (!BYTES_ARE_IN_FRAME(offset,8)) return offset;
570         length = EXTRACT_UINT(pd,offset+4);
571         length_full = roundup(length);
572         if (!BYTES_ARE_IN_FRAME(offset+8,length_full)) return offset;
573
574         if (tree) {
575                 vitem = proto_tree_add_text(tree, offset, 8+length_full,
576                         "Verifier");
577                 vtree = proto_item_add_subtree(vitem, ETT_RPC_VERF);
578                 dissect_rpc_auth(pd, offset, fd, vtree);
579         }
580         offset += 8 + length_full;
581
582         return offset;
583 }
584
585
586 void
587 dissect_rpc( const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
588                 guint32 msg_type, void* info)
589 {
590         unsigned int xid;
591         unsigned int rpcvers;
592         unsigned int prog;
593         unsigned int vers = 0;
594         unsigned int proc = 0;
595         int     proto = 0;
596         int     ett = 0;
597
598         unsigned int reply_state;
599         unsigned int accept_state;
600         unsigned int reject_state;
601
602         char *msg_type_name = NULL;
603         char *progname;
604         char *procname = NULL;
605         static char procname_static[20];
606
607         unsigned int vers_low;
608         unsigned int vers_high;
609
610         unsigned int auth_state;
611
612         proto_item *rpc_item=NULL;
613         proto_tree *rpc_tree = NULL;
614
615         proto_item *pitem=NULL;
616         proto_tree *ptree = NULL;
617         int offset_old = offset;
618
619         rpc_call_info   rpc_call_msg;
620         rpc_proc_info_key       key;
621         rpc_proc_info_value     *value = NULL;
622         conversation_t* conversation;
623
624         /* the last parameter can be either of these two types */
625         rpc_call_info   *rpc_call;
626         rpc_prog_info_key       rpc_prog_key;
627         rpc_prog_info_value     *rpc_prog;
628
629         dissect_function_t *dissect_function = NULL;
630
631         if (check_col(fd, COL_PROTOCOL))
632                 col_add_str(fd, COL_PROTOCOL, "RPC");
633
634         if (tree) {
635                 rpc_item = proto_tree_add_item(tree, proto_rpc, offset, END_OF_FRAME, NULL);
636                 if (rpc_item) {
637                         rpc_tree = proto_item_add_subtree(rpc_item, ETT_RPC);
638                 }
639         }
640
641         xid      = EXTRACT_UINT(pd,offset+0);
642         if (rpc_tree) {
643                 proto_tree_add_text(rpc_tree,offset+0,4,
644                         "XID: 0x%x (%u)", xid, xid);
645         }
646
647         /* we should better compare this with the argument?! */
648         msg_type = EXTRACT_UINT(pd,offset+4);
649         msg_type_name = val_to_str(msg_type,rpc_msg_type,"%u");
650         if (rpc_tree) {
651                 proto_tree_add_text(rpc_tree,offset+4,4,
652                         "msg_type: %s (%u)",
653                         msg_type_name, msg_type);
654         }
655
656         offset += 8;
657
658         if (msg_type==RPC_CALL) {
659                 /* we know already the proto-entry and the ETT-const */
660                 rpc_prog = (rpc_prog_info_value*)info;
661                 proto = rpc_prog->proto;
662                 ett = rpc_prog->ett;
663                 progname = rpc_prog->progname;
664
665                 rpcvers = EXTRACT_UINT(pd,offset+0);
666                 if (rpc_tree) {
667                         proto_tree_add_text(rpc_tree,offset+0,4,
668                                 "RPC Version: %u", rpcvers);
669                 }
670
671                 prog = EXTRACT_UINT(pd,offset+4);
672                 
673                 if (rpc_tree) {
674                         proto_tree_add_text(rpc_tree,offset+4,4,
675                                 "Program: %s (%u)", progname, prog);
676                 }
677                 
678                 if (check_col(fd, COL_PROTOCOL)) {
679                         /* Set the protocol name to the underlying
680                            program name. */
681                         col_add_fstr(fd, COL_PROTOCOL, "%s", progname);
682                 }
683
684                 if (!BYTES_ARE_IN_FRAME(offset+8,4)) return;
685                 vers = EXTRACT_UINT(pd,offset+8);
686                 if (rpc_tree) {
687                         proto_tree_add_text(rpc_tree,offset+8,4,
688                                 "Program Version: %u",vers);
689                 }
690
691                 if (!BYTES_ARE_IN_FRAME(offset+12,4)) return;
692                 proc = EXTRACT_UINT(pd,offset+12);
693
694                 key.prog = prog;
695                 key.vers = vers;
696                 key.proc = proc;
697
698                 value = g_hash_table_lookup(rpc_procs,&key);
699                 if (value != NULL) {
700                         dissect_function = value->dissect_call;
701                         procname = value->name;
702                 }
703                 else {
704                         /* happens only with strange program versions or
705                            non-existing dissectors */
706                         dissect_function = NULL;
707                         sprintf(procname_static, "proc-%u", proc);
708                         procname = procname_static;
709                 }
710                 if (rpc_tree) {
711                         proto_tree_add_text(rpc_tree,offset+12,4,
712                                 "Procedure: %s (%u)", procname, proc);
713                 }
714
715                 if (check_col(fd, COL_INFO)) {
716                         col_add_fstr(fd, COL_INFO,"V%u %s %s XID 0x%x",
717                                 vers,
718                                 procname,
719                                 msg_type_name,
720                                 xid);
721                 }
722
723                 conversation = find_conversation(&pi.src, &pi.dst, pi.ptype,
724                         pi.srcport, pi.destport);
725                 if (conversation == NULL) {
726                         /* It's not part of any conversation - create a new one. */
727                         conversation = conversation_new(&pi.src, &pi.dst, pi.ptype,
728                                 pi.srcport, pi.destport, NULL);
729                 }
730
731                 /* prepare the key data */
732                 rpc_call_msg.xid = xid;
733                 rpc_call_msg.conversation = conversation;
734
735                 /* look up the request */
736                 if (rpc_call_lookup(&rpc_call_msg)) {
737                         /* duplicate request */
738                         if (check_col(fd, COL_INFO)) {
739                                 col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
740                         }
741                 }
742                 else {
743                         /* prepare the value data */
744                         rpc_call_msg.replies = 0;
745                         rpc_call_msg.prog = prog;
746                         rpc_call_msg.vers = vers;
747                         rpc_call_msg.proc = proc;
748                         rpc_call_msg.proc_info = value;
749                         /* store it */
750                         rpc_call_insert(&rpc_call_msg);
751                 }
752
753                 offset += 16;
754
755                 offset = dissect_rpc_cred(pd, offset, fd, rpc_tree);
756                 offset = dissect_rpc_verf(pd, offset, fd, rpc_tree);
757
758                 /* go to the next dissector */
759                 /* goto dissect_rpc_prog; */
760
761         } /* end of RPC call */
762         else if (msg_type == RPC_REPLY)
763         {
764                 /* we know already the type from the calling routine */
765                 rpc_call = (rpc_call_info*)info;
766                 prog = rpc_call->prog;
767                 vers = rpc_call->vers;
768                 proc = rpc_call->proc;
769                 if (rpc_call->proc_info != NULL) {
770                         dissect_function = rpc_call->proc_info->dissect_reply;
771                         if (rpc_call->proc_info->name != NULL) {
772                                 procname = rpc_call->proc_info->name;
773                         }
774                         else {
775                                 sprintf(procname_static, "proc-%u", proc);
776                                 procname = procname_static;
777                         }
778                 }
779                 else {
780                         dissect_function = NULL;
781                         sprintf(procname_static, "proc-%u", proc);
782                         procname = procname_static;
783                 }
784                 rpc_call->replies++;
785
786                 rpc_prog_key.prog = prog;
787                 if ((rpc_prog = g_hash_table_lookup(rpc_progs,&rpc_prog_key)) == NULL) {
788                         proto = 0;
789                         ett = 0;
790                         progname = "Unknown";
791                 }
792                 else {
793                         proto = rpc_prog->proto;
794                         ett = rpc_prog->ett;
795                         progname = rpc_prog->progname;
796
797                         if (check_col(fd, COL_PROTOCOL)) {
798                                 /* Set the protocol name to the underlying
799                                    program name. */
800                                 col_add_fstr(fd, COL_PROTOCOL, "%s",
801                                     progname);
802                         }
803                 }
804
805                 if (check_col(fd, COL_INFO)) {
806                         col_add_fstr(fd, COL_INFO,"V%u %s %s XID 0x%x",
807                                 vers,
808                                 procname,
809                                 msg_type_name,
810                                 xid);
811                 }
812
813                 if (rpc_tree) {
814                         proto_tree_add_text(rpc_tree,0,0,
815                                 "Program: %s (%u)", 
816                                 progname, prog);
817                         proto_tree_add_text(rpc_tree,0,0,
818                                 "Program Version: %u", vers);
819                         proto_tree_add_text(rpc_tree,0,0,
820                                 "Procedure: %s (%u)", procname, proc);
821                 }
822
823                 if (rpc_call->replies>1) {
824                         if (check_col(fd, COL_INFO)) {
825                                 col_append_fstr(fd, COL_INFO, " dup XID 0x%x", xid);
826                         }
827                 }
828
829                 if (!BYTES_ARE_IN_FRAME(offset,4)) return;
830                 reply_state = EXTRACT_UINT(pd,offset+0);
831                 if (rpc_tree) {
832                         proto_tree_add_text(rpc_tree,offset+0, 4,
833                                 "Reply State: %s (%u)",
834                                 val_to_str(reply_state,rpc_reply_state,"Unknown"),
835                                 reply_state);
836                 }
837                 offset += 4;
838
839                 if (reply_state == MSG_ACCEPTED) {
840                         offset = dissect_rpc_verf(pd, offset, fd, rpc_tree);
841                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
842                         accept_state = EXTRACT_UINT(pd,offset+0);
843                         if (rpc_tree) {
844                                 proto_tree_add_text(rpc_tree,offset+0, 4,
845                                         "Accept State: %s (%u)", 
846                                         val_to_str(accept_state,rpc_accept_state,"Unknown"),
847                                         accept_state);
848                         }
849                         offset += 4;
850                         switch (accept_state) {
851                                 case SUCCESS:
852                                         /* now goto the lower protocol */
853                                         goto dissect_rpc_prog;
854                                 break;
855                                 case PROG_MISMATCH:
856                                         if (!BYTES_ARE_IN_FRAME(offset,8)) return;
857                                         vers_low = EXTRACT_UINT(pd,offset+0);
858                                         vers_high = EXTRACT_UINT(pd,offset+4);
859                                         if (rpc_tree) {
860                                                 proto_tree_add_text(rpc_tree,
861                                                         offset+0, 4,
862                                                         "min. Program Version: %u",
863                                                         vers_low);
864                                                 proto_tree_add_text(rpc_tree,
865                                                         offset+4, 4,
866                                                         "max. Program Version: %u",
867                                                         vers_high);
868                                         }
869                                         offset += 8;
870                                 break;
871                                 default:
872                                         /* void */
873                                 break;
874                         }
875                 } else if (reply_state == MSG_DENIED) {
876                         if (!BYTES_ARE_IN_FRAME(offset,4)) return;
877                         reject_state = EXTRACT_UINT(pd,offset+0);
878                         if (rpc_tree) {
879                                 proto_tree_add_text(rpc_tree, offset+0, 4,
880                                         "Reject State: %s (%u)",
881                                         val_to_str(reject_state,rpc_reject_state,"Unknown"),
882                                         reject_state);
883                         }
884                         offset += 4;
885
886                         if (reject_state==RPC_MISMATCH) {
887                                 if (!BYTES_ARE_IN_FRAME(offset,8)) return;
888                                 vers_low = EXTRACT_UINT(pd,offset+0);
889                                 vers_high = EXTRACT_UINT(pd,offset+4);
890                                 if (rpc_tree) {
891                                         proto_tree_add_text(rpc_tree,
892                                                 offset+0, 4,
893                                                 "min. RPC Version: %u",
894                                                 vers_low);
895                                         proto_tree_add_text(rpc_tree,
896                                                 offset+4, 4,
897                                                 "max. RPC Version: %u",
898                                                 vers_high);
899                                 }
900                                 offset += 8;
901                         } else if (reject_state==AUTH_ERROR) {
902                                 if (!BYTES_ARE_IN_FRAME(offset,4)) return;
903                                 auth_state = EXTRACT_UINT(pd,offset+0);
904                                 if (rpc_tree) {
905                                         proto_tree_add_text(rpc_tree,
906                                                 offset+0, 4,
907                                                 "Authentication error: %s (%u)",
908                                                 val_to_str(auth_state,rpc_auth_state,"Unknown"),
909                                                 auth_state);
910                                 }
911                                 offset += 4;
912                         }
913                 } 
914         } /* end of RPC reply */
915
916 dissect_rpc_prog:
917         /* I know, goto is evil but it works as it is. */
918
919         /* now we know, that RPC was shorter */
920         if (rpc_item) {
921                 proto_item_set_len(rpc_item, offset - offset_old);
922         }
923
924         /* create here the program specific sub-tree */
925         if (tree) {
926                 pitem = proto_tree_add_item(tree, proto, offset, END_OF_FRAME);
927                 if (pitem)
928                         ptree = proto_item_add_subtree(pitem, ett);
929                 }
930
931         /* call a specific dissection */
932         if (dissect_function != NULL) {
933                 offset = dissect_function(pd, offset, fd, ptree);
934         }
935
936         /* dissect any remaining bytes (incomplete dissection) as pure data in
937            the ptree */
938         dissect_data(pd, offset, fd, ptree);
939 }
940
941 /* will be called from file.c on every new file open */
942 void
943 rpc_init_protocol(void)
944 {
945         rpc_call_index = 0;
946         rpc_call_firstfree = 0;
947 }
948
949
950 /* will be called once from register.c at startup time */
951 void
952 proto_register_rpc(void)
953 {
954         proto_rpc = proto_register_protocol("Remote Procedure Call", "rpc");
955
956         /* please remove this, if all specific dissectors are ready */
957         init_incomplete_dissect();
958 }
959