Add a comment explaining why we're defining S_ISDIR and company.
[obnox/wireshark/wip.git] / packet-ipx.c
1 /* packet-ipx.c
2  * Routines for NetWare's IPX
3  * Gilbert Ramirez <gram@xiexie.org>
4  *
5  * $Id: packet-ipx.c,v 1.62 2000/06/15 03:48:40 gram Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #include <stdio.h>
36 #include <glib.h>
37 #include "etypes.h"
38 #include "ppptypes.h"
39 #include "llcsaps.h"
40 #include "packet.h"
41 #include "packet-ipx.h"
42 #include "packet-nbipx.h"
43 #include "resolv.h"
44
45 #include "packet-snmp.h"
46
47 /* The information in this module (IPX, SPX, NCP) comes from:
48         NetWare LAN Analysis, Second Edition
49         Laura A. Chappell and Dan E. Hakes
50         (c) 1994 Novell, Inc.
51         Novell Press, San Jose.
52         ISBN: 0-7821-1362-1
53
54   And from the ncpfs source code by Volker Lendecke
55
56 */
57         
58 static int proto_ipx = -1;
59 static int hf_ipx_checksum = -1;
60 static int hf_ipx_len = -1;
61 static int hf_ipx_hops = -1;
62 static int hf_ipx_packet_type = -1;
63 static int hf_ipx_dnet = -1;
64 static int hf_ipx_dnode = -1;
65 static int hf_ipx_dsocket = -1;
66 static int hf_ipx_snet = -1;
67 static int hf_ipx_snode = -1;
68 static int hf_ipx_ssocket = -1;
69
70 static gint ett_ipx = -1;
71
72 static dissector_table_t ipx_type_dissector_table;
73 static dissector_table_t ipx_socket_dissector_table;
74
75 static int proto_spx = -1;
76 static int hf_spx_connection_control = -1;
77 static int hf_spx_datastream_type = -1;
78 static int hf_spx_src_id = -1;
79 static int hf_spx_dst_id = -1;
80 static int hf_spx_seq_nr = -1;
81 static int hf_spx_ack_nr = -1;
82 static int hf_spx_all_nr = -1;
83
84 static gint ett_spx = -1;
85
86 static int proto_ipxrip = -1;
87 static int hf_ipxrip_request = -1;
88 static int hf_ipxrip_response = -1;
89
90 static gint ett_ipxrip = -1;
91
92 static int proto_sap = -1;
93 static int hf_sap_request = -1;
94 static int hf_sap_response = -1;
95
96 static gint ett_ipxsap = -1;
97 static gint ett_ipxsap_server = -1;
98
99 static gint ett_ipxmsg = -1;
100 static int proto_ipxmsg = -1;
101 static int hf_msg_conn = -1;
102 static int hf_msg_sigchar = -1;
103
104 static void
105 dissect_spx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
106
107 static void
108 dissect_ipxrip(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
109
110 static void
111 dissect_ipxsap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
112
113 static void
114 dissect_ipxmsg(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
115
116 #define UDP_PORT_IPX    213             /* RFC 1234 */
117
118 #define IPX_HEADER_LEN  30              /* It's *always* 30 bytes */
119
120 /* ================================================================= */
121 /* IPX                                                               */
122 /* ================================================================= */
123 static const value_string ipx_socket_vals[] = {
124         { IPX_SOCKET_PING_CISCO,                "CISCO PING" },
125         { IPX_SOCKET_NCP,                       "NCP" },
126         { IPX_SOCKET_SAP,                       "SAP" },
127         { IPX_SOCKET_IPXRIP,                    "RIP" },
128         { IPX_SOCKET_NETBIOS,                   "NetBIOS" },
129         { IPX_SOCKET_DIAGNOSTIC,                "Diagnostic" },
130         { IPX_SOCKET_SERIALIZATION,             "Serialization" },
131         { IPX_SOCKET_NWLINK_SMB_NAMEQUERY,      "NWLink SMB Name Query" },
132         { IPX_SOCKET_NWLINK_SMB_DGRAM,          "NWLink SMB Datagram" },
133         { IPX_SOCKET_NWLINK_SMB_BROWSE,         "NWLink SMB Browse" },
134         { IPX_SOCKET_ATTACHMATE_GW,             "Attachmate Gateway" },
135         { IPX_SOCKET_IPX_MESSAGE,               "IPX Message" },
136         { IPX_SOCKET_SNMP_AGENT,                "SNMP Agent" },
137         { IPX_SOCKET_SNMP_SINK,                 "SNMP Sink" },
138         { IPX_SOCKET_PING_NOVELL,               "Novell PING" },
139         { IPX_SOCKET_UDP_TUNNEL,                "UDP Tunnel" },
140         { IPX_SOCKET_TCP_TUNNEL,                "TCP Tunnel" },
141         { IPX_SOCKET_TCP_TUNNEL,                "TCP Tunnel" },
142         { IPX_SOCKET_ADSM,                      "ADSM" },
143         { IPX_SOCKET_EIGRP,                     "Cisco EIGRP for IPX" },
144         { IPX_SOCKET_WIDE_AREA_ROUTER,          "Wide Area Router" },
145         { 0x0000,                               NULL }
146 };
147
148 static const char*
149 socket_text(guint16 socket)
150 {
151         const char      *p;
152
153         p = match_strval(socket, ipx_socket_vals);
154         if (p) {
155                 return p;
156         }
157         else {
158                 return "Unknown";
159         }
160 }
161
162 static const value_string ipx_packet_type_vals[] = {
163         { IPX_PACKET_TYPE_IPX,          "IPX" },
164         { IPX_PACKET_TYPE_RIP,          "RIP" },
165         { IPX_PACKET_TYPE_ECHO,         "Echo" },
166         { IPX_PACKET_TYPE_ERROR,        "Error" },
167         { IPX_PACKET_TYPE_PEP,          "PEP" }, /* Packet Exchange Packet */
168         { IPX_PACKET_TYPE_SPX,          "SPX" },
169         { 16,                           "Experimental Protocol" },
170         { IPX_PACKET_TYPE_NCP,          "NCP" },
171         { 18,                           "Experimental Protocol" },
172         { 19,                           "Experimental Protocol" },
173         { IPX_PACKET_TYPE_WANBCAST,     "NetBIOS Broadcast" },
174         { 21,                           "Experimental Protocol" },
175         { 22,                           "Experimental Protocol" },
176         { 23,                           "Experimental Protocol" },
177         { 24,                           "Experimental Protocol" },
178         { 25,                           "Experimental Protocol" },
179         { 26,                           "Experimental Protocol" },
180         { 27,                           "Experimental Protocol" },
181         { 28,                           "Experimental Protocol" },
182         { 29,                           "Experimental Protocol" },
183         { 30,                           "Experimental Protocol" },
184         { 31,                           "Experimental Protocol" },
185         { 0,                            NULL }
186 };
187
188 static const value_string ipxmsg_sigchar_vals[] = {
189         { '?', "Poll inactive station" },
190         { 0, NULL }
191 };
192
193 gchar*
194 ipxnet_to_string(const guint8 *ad)
195 {
196         guint32 addr = pntohl(ad);
197         return ipxnet_to_str_punct(addr, ' ');
198 }
199
200 /* We use a different representation of hardware addresses
201  * than ether_to_str(); we don't put punctuation between the hex
202  * digits.
203  */
204
205 gchar*
206 ipx_addr_to_str(guint32 net, const guint8 *ad)
207 {
208         static gchar    str[3][8+1+MAXNAMELEN+1]; /* 8 digits, 1 period, NAME, 1 null */
209         static gchar    *cur;
210         char            *name;
211
212         if (cur == &str[0][0]) {
213                 cur = &str[1][0];
214         } else if (cur == &str[1][0]) {
215                 cur = &str[2][0];
216         } else {
217                 cur = &str[0][0];
218         }
219
220         name = get_ether_name_if_known(ad);
221
222         if (name) {
223                 sprintf(cur, "%s.%s", get_ipxnet_name(net), name);
224         }
225         else {
226                 sprintf(cur, "%s.%s", get_ipxnet_name(net), ether_to_str_punct(ad, '\0'));
227         }
228         return cur;
229 }
230
231 gchar *
232 ipxnet_to_str_punct(const guint32 ad, char punct)
233 {
234   static gchar  str[3][12];
235   static gchar *cur;
236   gchar        *p;
237   int          i;
238   guint32      octet;
239   static const gchar hex_digits[16] = "0123456789ABCDEF";
240   static const guint32  octet_mask[4] =
241           { 0xff000000 , 0x00ff0000, 0x0000ff00, 0x000000ff };
242
243   if (cur == &str[0][0]) {
244     cur = &str[1][0];
245   } else if (cur == &str[1][0]) {  
246     cur = &str[2][0];
247   } else {  
248     cur = &str[0][0];
249   }
250   p = &cur[12];
251   *--p = '\0';
252   i = 3;
253   for (;;) {
254     octet = (ad & octet_mask[i]) >> ((3 - i) * 8);
255     *--p = hex_digits[octet&0xF];
256     octet >>= 4;
257     *--p = hex_digits[octet&0xF];
258     if (i == 0)
259       break;
260     if (punct)
261       *--p = punct;
262     i--;
263   }
264   return p;
265 }
266
267 void
268 capture_ipx(const u_char *pd, int offset, packet_counts *ld)
269 {
270         ld->ipx++;
271 }
272
273 #if 0
274 void
275 dissect_ipx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
276 {
277 #else
278 void
279 dissect_ipx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
280 {
281         packet_info     *pinfo = &pi;
282         tvbuff_t        *tvb = tvb_create_from_top(offset);
283 #endif
284         tvbuff_t        *next_tvb;
285         const guint8    *this_pd;
286         int             this_offset, len;
287         const guint8    *next_pd;
288         int             next_offset;
289
290         proto_tree      *ipx_tree;
291         proto_item      *ti;
292
293         guint8          *src_net_node, *dst_net_node;
294
295         guint8          ipx_type, ipx_hops;
296         guint16         ipx_length;
297         int             reported_length, available_length;
298
299         guint16         ipx_dsocket, ipx_ssocket;
300
301
302         pinfo->current_proto = "IPX";
303
304         /* Calculate here for use in pinfo and in tree */
305         ipx_dsocket     = tvb_get_ntohs(tvb, 16);
306         ipx_ssocket     = tvb_get_ntohs(tvb, 28);
307         ipx_type        = tvb_get_guint8(tvb, 5);
308         ipx_length      = tvb_get_ntohs(tvb, 2);
309
310         /* Set the payload and captured-payload lengths to the minima of
311            (the IPX length plus the length of the headers above it) and
312            the frame lengths. XXX - remove once all dissectors use tvbuffs */
313         tvb_compat(tvb, &this_pd, &this_offset);
314         len = ipx_length + this_offset;
315         if (pi.len > len)
316                 pi.len = len;
317         if (pi.captured_len > len)
318                 pi.captured_len = len;
319
320         src_net_node = tvb_get_ptr(tvb, 18, 10);
321         dst_net_node = tvb_get_ptr(tvb, 6,  10);
322
323         SET_ADDRESS(&pi.net_src,        AT_IPX, 10, src_net_node);
324         SET_ADDRESS(&pi.src,            AT_IPX, 10, src_net_node);
325         SET_ADDRESS(&pi.net_dst,        AT_IPX, 10, dst_net_node);
326         SET_ADDRESS(&pi.dst,            AT_IPX, 10, dst_net_node);
327
328         if (check_col(pinfo->fd, COL_PROTOCOL))
329                 col_add_str(pinfo->fd, COL_PROTOCOL, "IPX");
330         if (check_col(pinfo->fd, COL_INFO))
331                 col_add_fstr(pinfo->fd, COL_INFO, "%s (0x%04X)",
332                                 socket_text(ipx_dsocket), ipx_dsocket);
333
334         if (tree) {
335
336                 ti = proto_tree_add_item(tree, proto_ipx, tvb, 0, IPX_HEADER_LEN, FALSE);
337                 ipx_tree = proto_item_add_subtree(ti, ett_ipx);
338
339                 proto_tree_add_item(ipx_tree, hf_ipx_checksum, tvb, 0, 2, FALSE);
340                 proto_tree_add_uint_format(ipx_tree, hf_ipx_len, tvb, 2, 2, ipx_length,
341                         "Length: %d bytes", ipx_length);
342                 ipx_hops = tvb_get_guint8(tvb, 4);
343                 proto_tree_add_uint_format(ipx_tree, hf_ipx_hops, tvb, 4, 1, ipx_hops,
344                         "Transport Control: %d hops", ipx_hops);
345                 proto_tree_add_uint(ipx_tree, hf_ipx_packet_type, tvb, 5, 1, ipx_type);
346
347                 /* Destination */
348                 proto_tree_add_item(ipx_tree, hf_ipx_dnet, tvb, 6, 4, FALSE);
349                 proto_tree_add_item(ipx_tree, hf_ipx_dnode, tvb, 10, 6, FALSE);
350                 proto_tree_add_uint_format(ipx_tree, hf_ipx_dsocket, tvb, 16, 2,
351                         ipx_dsocket, "Destination Socket: %s (0x%04X)",
352                         socket_text(ipx_dsocket), ipx_dsocket);
353
354                 /* Source */
355                 proto_tree_add_item(ipx_tree, hf_ipx_snet, tvb, 18, 4, FALSE);
356                 proto_tree_add_item(ipx_tree, hf_ipx_snode, tvb, 22, 6, FALSE);
357                 proto_tree_add_uint_format(ipx_tree, hf_ipx_ssocket, tvb, 28, 2,
358                         ipx_ssocket, "Source Socket: %s (0x%04X)", socket_text(ipx_ssocket),
359                         ipx_ssocket);
360         }
361
362         /* Make the next tvbuff */
363         reported_length = ipx_length - IPX_HEADER_LEN;
364         available_length = tvb_length(tvb) - IPX_HEADER_LEN;
365         next_tvb = tvb_new_subset(tvb, IPX_HEADER_LEN,
366                         MIN(available_length, reported_length),
367                         reported_length);
368
369         tvb_compat(next_tvb, &next_pd, &next_offset);
370
371         if (dissector_try_port(ipx_type_dissector_table, ipx_type, next_pd,
372             next_offset, pinfo->fd, tree))
373                 return;
374
375         switch (ipx_type) {
376                 case IPX_PACKET_TYPE_WANBCAST:
377                 case IPX_PACKET_TYPE_PEP:
378                         if (ipx_dsocket == IPX_SOCKET_NETBIOS) {
379                                 dissect_nbipx(next_pd, next_offset, pinfo->fd, tree);
380                                 return;
381                         }
382                         /* else fall through */
383
384                 case 0: /* IPX, fall through to default */
385                         /* XXX - should type 0's be dissected as NBIPX
386                            if they're aimed at the NetBIOS socket? */
387                         break;
388         }
389
390         if (dissector_try_port(ipx_socket_dissector_table, ipx_dsocket, next_pd,
391             next_offset, pinfo->fd, tree))
392                 return;
393         if (dissector_try_port(ipx_socket_dissector_table, ipx_ssocket, next_pd,
394             next_offset, pinfo->fd, tree))
395                 return;
396         dissect_data_tvb(next_tvb, pinfo, tree);
397 }
398
399
400 /* ================================================================= */
401 /* SPX                                                               */
402 /* ================================================================= */
403 static const char*
404 spx_conn_ctrl(guint8 ctrl)
405 {
406         const char *p;
407
408         static const value_string conn_vals[] = {
409                 { 0x10, "End-of-Message" },
410                 { 0x20, "Attention" },
411                 { 0x40, "Acknowledgment Required"},
412                 { 0x80, "System Packet"},
413                 { 0x00, NULL }
414         };
415
416         p = match_strval(ctrl, conn_vals);
417
418         if (p) {
419                 return p;
420         }
421         else {
422                 return "Unknown";
423         }
424 }
425
426 static const char*
427 spx_datastream(guint8 type)
428 {
429         switch (type) {
430                 case 0xfe:
431                         return "End-of-Connection";
432                 case 0xff:
433                         return "End-of-Connection Acknowledgment";
434                 default:
435                         return "Client-Defined";
436         }
437 }
438
439 #define SPX_HEADER_LEN  12
440
441 #if 0
442 static void
443 dissect_spx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
444 {
445 #else
446 static void
447 dissect_spx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
448 {
449         packet_info     *pinfo = &pi;
450         tvbuff_t        *tvb = tvb_create_from_top(offset);
451 #endif
452         proto_tree      *spx_tree;
453         proto_item      *ti;
454         tvbuff_t        *next_tvb;
455
456         guint8          conn_ctrl;
457         guint8          datastream_type;
458
459         pinfo->current_proto = "SPX";
460         if (check_col(pinfo->fd, COL_PROTOCOL))
461                 col_add_str(pinfo->fd, COL_PROTOCOL, "SPX");
462         if (check_col(pinfo->fd, COL_INFO))
463                 col_add_str(pinfo->fd, COL_INFO, "SPX");
464
465         if (tree) {
466                 ti = proto_tree_add_item(tree, proto_spx, tvb, 0, SPX_HEADER_LEN, FALSE);
467                 spx_tree = proto_item_add_subtree(ti, ett_spx);
468
469                 conn_ctrl = tvb_get_guint8(tvb, 0);
470                 proto_tree_add_uint_format(spx_tree, hf_spx_connection_control, tvb,
471                                            0, 1, conn_ctrl,
472                                            "Connection Control: %s (0x%02X)",
473                                            spx_conn_ctrl(conn_ctrl), conn_ctrl);
474
475                 datastream_type = tvb_get_guint8(tvb, 1);
476                 proto_tree_add_uint_format(spx_tree, hf_spx_datastream_type, tvb,
477                                            1, 1, datastream_type,
478                                            "Datastream Type: %s (0x%02X)",
479                                            spx_datastream(datastream_type), datastream_type);
480
481                 proto_tree_add_item(spx_tree, hf_spx_src_id, tvb,  2, 2, FALSE);
482                 proto_tree_add_item(spx_tree, hf_spx_dst_id, tvb,  4, 2, FALSE);
483                 proto_tree_add_item(spx_tree, hf_spx_seq_nr, tvb,  6, 2, FALSE);
484                 proto_tree_add_item(spx_tree, hf_spx_ack_nr, tvb,  8, 2, FALSE);
485                 proto_tree_add_item(spx_tree, hf_spx_all_nr, tvb, 10, 2, FALSE);
486
487                 next_tvb = tvb_new_subset(tvb, SPX_HEADER_LEN, -1, -1);
488                 dissect_data_tvb(next_tvb, pinfo, tree);
489         }
490 }
491
492 /* ================================================================= */
493 /* IPX Message                                                       */
494 /* ================================================================= */
495 #if 0
496 static void
497 dissect_ipxmsg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
498 {
499 #else
500 static void
501 dissect_ipxmsg(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
502 {
503         packet_info     *pinfo = &pi;
504         tvbuff_t        *tvb = tvb_create_from_top(offset);
505 #endif
506         proto_tree      *msg_tree;
507         proto_item      *ti;
508         guint8          conn_number, sig_char;
509
510         pinfo->current_proto = "IPX MSG";
511
512         if (check_col(pinfo->fd, COL_PROTOCOL))
513          col_add_str(pinfo->fd, COL_PROTOCOL, "IPX MSG");
514
515         conn_number = tvb_get_guint8(tvb, 0);
516         sig_char = tvb_get_guint8(tvb, 1);
517
518         if (check_col(pinfo->fd, COL_PROTOCOL)) {
519                 col_add_fstr(pinfo->fd, COL_INFO, 
520                         "%s, Connection %d", 
521                         val_to_str(sig_char, ipxmsg_sigchar_vals, "Unknown Signature Char"), conn_number);
522         }
523
524         if (tree) {
525                 ti = proto_tree_add_item(tree, proto_ipxmsg, tvb, 0, tvb_length(tvb), FALSE);
526                 msg_tree = proto_item_add_subtree(ti, ett_ipxmsg);
527
528                 proto_tree_add_uint(msg_tree, hf_msg_conn, tvb, 0, 1, conn_number);
529                 proto_tree_add_uint(msg_tree, hf_msg_sigchar, tvb, 1, 1, sig_char);
530         }
531 }
532
533
534 /* ================================================================= */
535 /* IPX RIP                                                           */
536 /* ================================================================= */
537 #if 0
538 static void
539 dissect_ipxrip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
540 {
541 #else
542 static void
543 dissect_ipxrip(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
544 {
545         packet_info     *pinfo = &pi;
546         tvbuff_t        *tvb = tvb_create_from_top(offset);
547 #endif
548         proto_tree      *rip_tree;
549         proto_item      *ti;
550         guint16         operation;
551         struct ipx_rt_def route;
552         int             cursor;
553         int             available_length;
554
555         char            *rip_type[3] = { "Request", "Response", "Unknown" };
556
557         pinfo->current_proto = "IPX RIP";
558         if (check_col(pinfo->fd, COL_PROTOCOL))
559          col_add_str(pinfo->fd, COL_PROTOCOL, "IPX RIP");
560
561         operation = tvb_get_ntohs(tvb, 0) - 1;
562
563         if (check_col(pinfo->fd, COL_PROTOCOL)) {
564                 /* rip_types 0 and 1 are valid, anything else becomes 2 or "Unknown" */
565                 col_add_str(pinfo->fd, COL_INFO, rip_type[MIN(operation, 2)]);
566         }
567
568         if (tree) {
569                 ti = proto_tree_add_item(tree, proto_ipxrip, tvb, 0, tvb_length(tvb), FALSE);
570                 rip_tree = proto_item_add_subtree(ti, ett_ipxrip);
571
572                 if (operation < 2) {
573                         proto_tree_add_text(rip_tree, tvb, 0, 2,
574                         "RIP packet type: %s", rip_type[operation]);
575
576                         if (operation == 0) {
577                           proto_tree_add_boolean_hidden(rip_tree, 
578                                                      hf_ipxrip_request, 
579                                                      tvb, 0, 2, 1);
580                         } else {
581                           proto_tree_add_boolean_hidden(rip_tree, 
582                                                      hf_ipxrip_response, 
583                                                      tvb, 0, 2, 1);
584                         }
585
586                 }
587                 else {
588                         proto_tree_add_text(rip_tree, tvb, 0, 2, "Unknown RIP packet type");
589                 }
590
591                 available_length = tvb_length(tvb);
592                 for (cursor =  2; cursor < available_length; cursor += 8) {
593                         memcpy(&route.network, tvb_get_ptr(tvb, cursor, 4), 4);
594                         route.hops = tvb_get_ntohs(tvb, cursor+4);
595                         route.ticks = tvb_get_ntohs(tvb, cursor+6);
596
597                         if (operation == IPX_RIP_REQUEST - 1) {
598                                 proto_tree_add_text(rip_tree, tvb, cursor,      8,
599                                         "Route Vector: %s, %d hop%s, %d tick%s",
600                                         ipxnet_to_string((guint8*)&route.network),
601                                         route.hops,  route.hops  == 1 ? "" : "s",
602                                         route.ticks, route.ticks == 1 ? "" : "s");
603                         }
604                         else {
605                                 proto_tree_add_text(rip_tree, tvb, cursor,      8,
606                                         "Route Vector: %s, %d hop%s, %d tick%s (%d ms)",
607                                         ipxnet_to_string((guint8*)&route.network),
608                                         route.hops,  route.hops  == 1 ? "" : "s",
609                                         route.ticks, route.ticks == 1 ? "" : "s",
610                                         route.ticks * 1000 / 18);
611                         }
612                 }
613         }
614 }
615
616
617
618 /* ================================================================= */
619 /* SAP                                                                   */
620 /* ================================================================= */
621 static const char*
622 server_type(guint16 type)
623 {
624         const char *p;
625
626         /* some of these are from ncpfs, others are from the book */
627         static const value_string server_vals[] = {
628                 { 0x0001,       "User" },
629                 { 0x0002,       "User Group" },
630                 { 0x0003,       "Print Queue" },
631                 { 0x0004,       "File server" },
632                 { 0x0005,       "Job server" },
633                 { 0x0007,       "Print server" },
634                 { 0x0008,       "Archive server" },
635                 { 0x0009,       "Archive server" },
636                 { 0x000a,       "Job queue" },
637                 { 0x000b,       "Administration" },
638                 { 0x0021,       "NAS SNA gateway" },
639                 { 0x0024,       "Remote bridge" },
640                 { 0x0026,       "Bridge server" },
641                 { 0x0027,       "TCP/IP gateway" },
642                 { 0x002d,       "Time Synchronization VAP" },
643                 { 0x002e,       "Archive Server Dynamic SAP" },
644                 { 0x0047,       "Advertising print server" },
645                 { 0x004b,       "Btrieve VAP 5.0" },
646                 { 0x004c,       "SQL VAP" },
647                 { 0x0050,       "Btrieve VAP" },
648                 { 0x0053,       "Print Queue VAP" },
649                 { 0x007a,       "TES NetWare for VMS" },
650                 { 0x0098,       "NetWare access server" },
651                 { 0x009a,       "Named Pipes server" },
652                 { 0x009e,       "Portable NetWare Unix" },
653                 { 0x0107,       "NetWare 386" },
654                 { 0x0111,       "Test server" },
655                 { 0x0133,       "NetWare Name Service" },
656                 { 0x0166,       "NetWare management" },
657                 { 0x023f,       "SMS Testing and Development" },
658                 { 0x026a,       "NetWare management" },
659                 { 0x026b,       "Time synchronization" },
660                 { 0x027b,       "NetWare Management Agent" },
661                 { 0x0278,       "NetWare Directory server" },
662                 { 0x030c,       "HP LaserJet / Quick Silver" },
663                 { 0x0355,       "Arcada Software" },
664                 { 0x0361,       "NETINELO" },
665                 { 0x037e,       "Powerchute UPS Monitoring" },
666                 { 0x03e1,       "UnixWare Application Server" },
667                 { 0x044c,       "Archive" },
668                 { 0x055d,       "Attachmate SNA gateway" },
669                 { 0x0610,       "Adaptec SCSI Management" },
670                 { 0x0640,       "NT Server-RPC/GW for NW/Win95 User Level Sec" },
671                 { 0x064e,       "NT Server-IIS" },
672                 { 0x0810,       "ELAN License Server Demo" },
673                 { 0x8002,       "Intel NetPort Print Server" },
674
675 /* For any unidentified ones, I found a really big list of them at: */
676 /*    http://www.inpnet.org/cnpweb/saplist.txt */
677 /*    http://www.isi.edu/in-notes/iana/assignments/novell-sap-numbers */
678
679                 { 0x0000,       NULL }
680         };
681
682         p = match_strval(type, server_vals);
683         if (p) {
684                 return p;
685         }
686         else {
687                 return "Unknown";
688         }
689 }
690
691 #if 0
692 static void
693 dissect_ipxsap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
694 {
695 #else
696 static void
697 dissect_ipxsap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
698 {
699         packet_info     *pinfo = &pi;
700         tvbuff_t        *tvb = tvb_create_from_top(offset);
701 #endif
702
703         proto_tree      *sap_tree, *s_tree;
704         proto_item      *ti;
705         int             cursor;
706         struct sap_query query;
707         struct sap_server_ident server;
708
709         char            *sap_type[4] = { "General Query", "General Response",
710                 "Nearest Query", "Nearest Response" };
711
712         pinfo->current_proto = "IPX SAP";
713         if (check_col(pinfo->fd, COL_PROTOCOL))
714                 col_add_str(pinfo->fd, COL_PROTOCOL, "IPX SAP");
715
716         query.query_type = tvb_get_ntohs(tvb, 0);
717         query.server_type = tvb_get_ntohs(tvb, 2);
718
719         if (check_col(pinfo->fd, COL_INFO)) {
720                 if (query.query_type >= 1 && query.query_type <= 4) {
721                         col_add_str(pinfo->fd, COL_INFO, sap_type[query.query_type - 1]);
722                 }
723                 else {
724                         col_add_str(pinfo->fd, COL_INFO, "Unknown Packet Type");
725                 }
726         }
727
728         if (tree) {
729                 ti = proto_tree_add_item(tree, proto_sap, tvb, 0, tvb_length(tvb), FALSE);
730                 sap_tree = proto_item_add_subtree(ti, ett_ipxsap);
731
732                 if (query.query_type >= 1 && query.query_type <= 4) {
733                         proto_tree_add_text(sap_tree, tvb, 0, 2, sap_type[query.query_type - 1]);
734                         if ((query.query_type - 1) % 2) {
735                           proto_tree_add_boolean_hidden(sap_tree, 
736                                                      hf_sap_response, 
737                                                      tvb, 0, 2, 1);
738                         } else {
739                           proto_tree_add_boolean_hidden(sap_tree, 
740                                                      hf_sap_request, 
741                                                      tvb, 0, 2, 1);
742                         }
743                 }
744                 else {
745                         proto_tree_add_text(sap_tree, tvb, 0, 2,
746                                         "Unknown SAP Packet Type %d", query.query_type);
747                 }
748
749                 if (query.query_type == IPX_SAP_GENERAL_RESPONSE ||
750                                 query.query_type == IPX_SAP_NEAREST_RESPONSE) { /* responses */
751
752                         int available_length = tvb_length(tvb);
753                         for (cursor =  2; (cursor + 64) <= available_length; cursor += 64) {
754                                 server.server_type = tvb_get_ntohs(tvb, cursor);
755                                 memcpy(server.server_name, tvb_get_ptr(tvb, cursor+2, 48), 48);
756                                 memcpy(&server.server_network, tvb_get_ptr(tvb, cursor+50, 4), 4);
757                                 memcpy(&server.server_node, tvb_get_ptr(tvb, cursor+54, 6), 6);
758                                 server.server_port = tvb_get_ntohs(tvb, cursor+60);
759                                 server.intermediate_network = tvb_get_ntohs(tvb, cursor+62);
760
761                                 ti = proto_tree_add_text(sap_tree, tvb, cursor+2, 48,
762                                         "Server Name: %s", server.server_name);
763                                 s_tree = proto_item_add_subtree(ti, ett_ipxsap_server);
764
765                                 proto_tree_add_text(s_tree, tvb, cursor, 2, "Server Type: %s (0x%04X)",
766                                                 server_type(server.server_type), server.server_type);
767                                 proto_tree_add_text(s_tree, tvb, cursor+50, 4, "Network: %s",
768                                                 ipxnet_to_string((guint8*)tvb_get_ptr(tvb, cursor+50, 4)));
769                                 proto_tree_add_text(s_tree, tvb, cursor+54, 6, "Node: %s",
770                                                 ether_to_str((guint8*)tvb_get_ptr(tvb, cursor+54, 6)));
771                                 proto_tree_add_text(s_tree, tvb, cursor+60, 2, "Socket: %s (0x%04X)",
772                                                 socket_text(server.server_port), server.server_port);
773                                 proto_tree_add_text(s_tree, tvb, cursor+62, 2,
774                                                 "Intermediate Networks: %d",
775                                                 server.intermediate_network);
776                         }
777                 }
778                 else {  /* queries */
779                         proto_tree_add_text(sap_tree, tvb, 2, 2, "Server Type: %s (0x%04X)",
780                                         server_type(query.server_type), query.server_type);
781                 }
782         }
783 }
784
785 void
786 proto_register_ipx(void)
787 {
788         static hf_register_info hf_ipx[] = {
789                 { &hf_ipx_checksum,
790                 { "Checksum",           "ipx.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
791                         "" }},
792
793                 { &hf_ipx_len,
794                 { "Length",             "ipx.len", FT_UINT16, BASE_DEC, NULL, 0x0,
795                         "" }},
796
797                 { &hf_ipx_hops,
798                 { "Transport Control (Hops)", "ipx.hops", FT_UINT8, BASE_DEC, NULL, 0x0,
799                         "" }},
800
801                 { &hf_ipx_packet_type,
802                 { "Packet Type",        "ipx.packet_type", FT_UINT8, BASE_HEX, VALS(ipx_packet_type_vals),
803                         0x0,
804                         "" }},
805
806                 { &hf_ipx_dnet,
807                 { "Destination Network","ipx.dst.net", FT_IPXNET, BASE_NONE, NULL, 0x0,
808                         "" }},
809
810                 { &hf_ipx_dnode,
811                 { "Destination Node",   "ipx.dst.node", FT_ETHER, BASE_NONE, NULL, 0x0,
812                         "" }},
813
814                 { &hf_ipx_dsocket,
815                 { "Destination Socket", "ipx.dst.socket", FT_UINT16, BASE_HEX, NULL, 0x0,
816                         "" }},
817
818                 { &hf_ipx_snet,
819                 { "Source Network","ipx.src.net", FT_IPXNET, BASE_NONE, NULL, 0x0,
820                         "" }},
821
822                 { &hf_ipx_snode,
823                 { "Source Node",        "ipx.src.node", FT_ETHER, BASE_NONE, NULL, 0x0,
824                         "" }},
825
826                 { &hf_ipx_ssocket,
827                 { "Source Socket",      "ipx.src.socket", FT_UINT16, BASE_HEX, NULL, 0x0,
828                         "" }},
829         };
830
831         static hf_register_info hf_spx[] = {
832                 { &hf_spx_connection_control,
833                 { "Connection Control",         "spx.ctl", 
834                   FT_UINT8,     BASE_HEX,       NULL,   0x0,
835                   "" }},
836
837                 { &hf_spx_datastream_type,
838                 { "Datastream type",            "spx.type", 
839                   FT_UINT8,     BASE_HEX,       NULL,   0x0,
840                   "" }},
841
842                 { &hf_spx_src_id,
843                 { "Source Connection ID",       "spx.src", 
844                   FT_UINT16,    BASE_DEC,       NULL,   0x0,
845                   "" }},
846
847                 { &hf_spx_dst_id,
848                 { "Destination Connection ID",  "spx.dst", 
849                   FT_UINT16,    BASE_DEC,       NULL,   0x0,
850                   "" }},
851
852                 { &hf_spx_seq_nr,
853                 { "Sequence Number",            "spx.seq", 
854                   FT_UINT16,    BASE_DEC,       NULL,   0x0,
855                   "" }},
856
857                 { &hf_spx_ack_nr,
858                 { "Acknowledgment Number",      "spx.ack", 
859                   FT_UINT16,    BASE_DEC,       NULL,   0x0,
860                   "" }},
861
862                 { &hf_spx_all_nr,
863                 { "Allocation Number",          "spx.alloc", 
864                   FT_UINT16,    BASE_DEC,       NULL,   0x0,
865                   "" }}
866         };
867
868         static hf_register_info hf_ipxrip[] = {
869                 { &hf_ipxrip_request,
870                 { "Request",                    "ipxrip.request", 
871                   FT_BOOLEAN,   BASE_NONE,      NULL,   0x0,
872                   "TRUE if IPX RIP request" }},
873
874                 { &hf_ipxrip_response,
875                 { "Response",                   "ipxrip.response", 
876                   FT_BOOLEAN,   BASE_NONE,      NULL,   0x0,
877                   "TRUE if IPX RIP response" }}
878         };
879
880         static hf_register_info hf_sap[] = {
881                 { &hf_sap_request,
882                 { "Request",                    "ipxsap.request", 
883                   FT_BOOLEAN,   BASE_NONE,      NULL,   0x0,
884                   "TRUE if SAP request" }},
885
886                 { &hf_sap_response,
887                 { "Response",                   "ipxsap.response", 
888                   FT_BOOLEAN,   BASE_NONE,      NULL,   0x0,
889                   "TRUE if SAP response" }}
890         };
891
892         static hf_register_info hf_ipxmsg[] = {
893                 { &hf_msg_conn,
894                 { "Connection Number",                  "ipxmsg.conn", 
895                   FT_UINT8,     BASE_NONE,      NULL,   0x0,
896                   "Connection Number" }},
897
898                 { &hf_msg_sigchar,
899                 { "Signature Char",                     "ipxmsg.sigchar", 
900                   FT_UINT8,     BASE_NONE,      VALS(ipxmsg_sigchar_vals),      0x0,
901                   "Signature Char" }}
902         };
903
904         static gint *ett[] = {
905                 &ett_ipx,
906                 &ett_spx,
907                 &ett_ipxmsg,
908                 &ett_ipxrip,
909                 &ett_ipxsap,
910                 &ett_ipxsap_server,
911         };
912
913         proto_ipx = proto_register_protocol ("Internetwork Packet eXchange", "ipx");
914         proto_register_field_array(proto_ipx, hf_ipx, array_length(hf_ipx));
915
916         proto_spx = proto_register_protocol ("Sequenced Packet eXchange", "spx");
917         proto_register_field_array(proto_spx, hf_spx, array_length(hf_spx));
918
919         proto_ipxrip = proto_register_protocol ("IPX Routing Information Protocol", "ipxrip");
920         proto_register_field_array(proto_ipxrip, hf_ipxrip, array_length(hf_ipxrip));
921
922         proto_ipxmsg = proto_register_protocol ("IPX Message", "ipxmsg");
923         proto_register_field_array(proto_ipxmsg, hf_ipxmsg, array_length(hf_ipxmsg));
924
925         proto_sap = proto_register_protocol ("Service Advertisement Protocol", "ipxsap");
926         proto_register_field_array(proto_sap, hf_sap, array_length(hf_sap));
927
928         proto_register_subtree_array(ett, array_length(ett));
929
930         ipx_type_dissector_table = register_dissector_table("ipx.packet_type");
931         ipx_socket_dissector_table = register_dissector_table("ipx.socket");
932 }
933
934 void
935 proto_reg_handoff_ipx(void)
936 {
937         dissector_add("udp.port", UDP_PORT_IPX, dissect_ipx);
938         dissector_add("ethertype", ETHERTYPE_IPX, dissect_ipx);
939         dissector_add("ppp.protocol", PPP_IPX, dissect_ipx);
940         dissector_add("llc.dsap", SAP_NETWARE, dissect_ipx);
941         dissector_add("ipx.packet_type", IPX_PACKET_TYPE_SPX, dissect_spx);
942         dissector_add("ipx.socket", IPX_SOCKET_SAP, dissect_ipxsap);
943         dissector_add("ipx.socket", IPX_SOCKET_IPXRIP, dissect_ipxrip);
944         dissector_add("ipx.socket", IPX_SOCKET_IPX_MESSAGE, dissect_ipxmsg);
945 }