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