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