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