Initial revision
[obnox/wireshark/wip.git] / packet-ipx.c
1 /* packet-ipx.c
2  * Routines for NetWare's IPX
3  * Gilbert Ramirez <gram@verdict.uthscsa.edu>
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@unicom.net>
7  * Copyright 1998 Gerald Combs
8  *
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <gtk/gtk.h>
30 #include <pcap.h>
31
32 #include <stdio.h>
33
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
40 #endif
41
42 #include "ethereal.h"
43 #include "packet.h"
44
45 /* The information in this module (IPX, SPX, NCP) comes from:
46         NetWare LAN Analysis, Second Edition
47         Laura A. Chappell and Dan E. Hakes
48         (c) 1994 Novell, Inc.
49         Novell Press, San Jose.
50         ISBN: 0-7821-1362-1
51 */
52
53 static void
54 dissect_spx(const u_char *pd, int offset, frame_data *fd, GtkTree *tree);
55 static void
56 dissect_ncp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree);
57
58
59 struct port_info {
60         u_short port;
61         char    *text;
62 };
63
64 struct conn_info {
65         u_char  ctrl;
66         char    *text;
67 };
68
69 struct req_info {
70         u_short req;
71         char    *text;
72 };
73
74 /* ================================================================= */
75 /* IPX                                                               */
76 /* ================================================================= */
77 static char*
78 port_text(u_short port) {
79         int i=0;
80
81         static struct port_info ports[] = {
82                 { 0x0451, "NCP" },
83                 { 0x0452, "SAP" },
84                 { 0x0453, "RIP" },
85                 { 0x0455, "NetBIOS" },
86                 { 0x0456, "Diagnostic" },
87                 { 0x0457, "Serialization" },
88                 { 0x0000, NULL }
89         };
90
91         while (ports[i].text != NULL) {
92                 if (ports[i].port == port) {
93                         return ports[i].text;
94                 }
95                 i++;
96         }
97         return "Unknown";
98 }
99
100 char *
101 ipx_packet_type(u_char val)
102 {
103         if (val == 0) {
104                 return "IPX";
105         }
106         else if (val == 5) {
107                 return "SPX";
108         }
109         else if (val == 17) {
110                 return "NCP";
111         }
112         else if (val == 20) {
113                 return "NetBIOS";
114         }
115         else if (val >= 16 && val <= 31) {
116                 return "Experimental Protocol";
117         }
118         else {
119                 return "Unknown";
120         }
121 }
122
123 gchar*
124 network_to_string(const guint8 *ad)
125 {
126         static gchar    str[3][12];
127         static gchar    *cur;
128
129         if (cur == &str[0][0]) {
130                 cur = &str[1][0];
131         } else if (cur == &str[1][0]) {
132                 cur = &str[2][0];
133         } else {
134                 cur = &str[0][0];
135         }
136
137         sprintf(cur, "%02X %02X %02X %02X", ad[0], ad[1], ad[2], ad[3]);
138         return cur;
139 }
140
141 void
142 dissect_ipx(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
143
144         GtkWidget       *ipx_tree, *ti;
145         u_char          ipx_type;
146
147         char            *dnet, *snet;
148         guint16         dsocket, ssocket;
149
150         /* Calculate here for use in win_info[] and in tree */
151         dnet = network_to_string((guint8*)&pd[offset+6]);
152         snet = network_to_string((guint8*)&pd[offset+18]);
153         dsocket = pntohs(&pd[offset+16]);
154
155         if (fd->win_info[0]) {
156                 strcpy(fd->win_info[3], "IPX");
157                 /*sprintf(fd->win_info[4], "Network %s --> %s", snet, dnet);*/
158                 sprintf(fd->win_info[4], "%s (0x%04X)", port_text(dsocket), dsocket);
159         }
160
161         ipx_type = pd[offset+5];
162
163         if (tree) {
164                 ti = add_item_to_tree(GTK_WIDGET(tree), offset, 30,
165                         "Internetwork Packet Exchange");
166                 ipx_tree = gtk_tree_new();
167                 add_subtree(ti, ipx_tree, ETT_IPX);
168                 add_item_to_tree(ipx_tree, offset,      2, "Checksum: 0x%04X",
169                         (pd[offset] << 8) | pd[offset+1]);
170                 add_item_to_tree(ipx_tree, offset+2,    2, "Length: %d bytes",
171                         (pd[offset+2] << 8) | pd[offset+3]);
172                 add_item_to_tree(ipx_tree, offset+4,    1, "Transport Control: %d hops",
173                         pd[offset+4]);
174                 add_item_to_tree(ipx_tree, offset+5,    1, "Packet Type: %s",
175                         ipx_packet_type(ipx_type));
176                 add_item_to_tree(ipx_tree, offset+6,    4, "Destination Network: %s",
177                         dnet);
178                 add_item_to_tree(ipx_tree, offset+10,   6, "Destination Node: %s",
179                         ether_to_str((guint8*)&pd[offset+10]));
180                 /*dsocket = ntohs(*((guint16*)&pd[offset+16]));*/
181                 add_item_to_tree(ipx_tree, offset+16,   2,
182                         "Destination Socket: %s (0x%04X)", port_text(dsocket), dsocket);
183                 add_item_to_tree(ipx_tree, offset+18,   4, "Source Network: %s",
184                         snet);
185                 add_item_to_tree(ipx_tree, offset+22,   6, "Source Node: %s",
186                         ether_to_str((guint8*)&pd[offset+22]));
187                 ssocket = pntohs(&pd[offset+28]);
188                 add_item_to_tree(ipx_tree, offset+28,   2,
189                         "Source Socket: %s (0x%04X)", port_text(ssocket), ssocket);
190         }
191         offset += 30;
192
193         switch (ipx_type) {
194                 case 0: /* IPX */
195                         dissect_data(pd, offset, fd, tree); /* the IPX payload */
196                         break;
197                 case 5: /* SPX */
198                         dissect_spx(pd, offset, fd, tree);
199                         break;
200                 case 17: /* NCP */
201                         dissect_ncp(pd, offset, fd, tree);
202                         break;
203                 case 20: /* NetBIOS */
204                         dissect_data(pd, offset, fd, tree); /* until implemented */
205                         break;
206                 default:
207                         dissect_data(pd, offset, fd, tree);
208                         break;
209         }
210 }
211
212
213 /* ================================================================= */
214 /* SPX                                                               */
215 /* ================================================================= */
216 static char*
217 spx_conn_ctrl(u_char ctrl)
218 {
219         int i=0;
220
221         static struct conn_info conns[] = {
222                 { 0x10, "End-of-Message" },
223                 { 0x20, "Attention" },
224                 { 0x40, "Acknowledgment Required"},
225                 { 0x80, "System Packet"}
226         };
227
228         while (conns[i].text != NULL) {
229                 if (conns[i].ctrl == ctrl) {
230                         return conns[i].text;
231                 }
232                 i++;
233         }
234         return "Unknown";
235 }
236
237 static char*
238 datastream(u_char type)
239 {
240         switch (type) {
241                 case 0xfe:
242                         return "End-of-Connection";
243                 case 0xff:
244                         return "End-of-Connection Acknowledgment";
245                 default:
246                         return "Client-Defined";
247         }
248 }
249
250 static void
251 dissect_spx(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
252
253         GtkWidget       *spx_tree, *ti;
254
255         if (fd->win_info[0]) {
256                 strcpy(fd->win_info[3], "SPX");
257                 strcpy(fd->win_info[4], "SPX");
258         }
259
260         if (tree) {
261                 ti = add_item_to_tree(GTK_WIDGET(tree), offset, 12,
262                         "Sequenced Packet Exchange");
263                 spx_tree = gtk_tree_new();
264                 add_subtree(ti, spx_tree, ETT_SPX);
265
266                 add_item_to_tree(spx_tree, offset,      1,
267                         "Connection Control: %s (0x%02X)",
268                         spx_conn_ctrl(pd[offset]), pd[offset]);
269
270                 add_item_to_tree(spx_tree, offset+1,     1,
271                         "Datastream Type: %s (0x%02X)",
272                         datastream(pd[offset+1]), pd[offset+1]);
273
274                 add_item_to_tree(spx_tree, offset+2,     2,
275                         "Source Connection ID: %d", pntohs( &pd[offset+2] ) );
276
277                 add_item_to_tree(spx_tree, offset+4,     2,
278                         "Destination Connection ID: %d", pntohs( &pd[offset+4] ) );
279
280                 add_item_to_tree(spx_tree, offset+6,     2,
281                         "Sequence Number: %d", pntohs( &pd[offset+6] ) );
282
283                 add_item_to_tree(spx_tree, offset+8,     2,
284                         "Acknowledgment Number: %d", pntohs( &pd[offset+8] ) );
285
286                 add_item_to_tree(spx_tree, offset+10,     2,
287                         "Allocation Number: %d", pntohs( &pd[offset+10] ) );
288
289                 offset += 12;
290                 dissect_data(pd, offset, fd, tree);
291         }
292 }
293
294 /* ================================================================= */
295 /* NCP                                                               */
296 /* ================================================================= */
297 static char*
298 req_text(u_short req) {
299         int i=0;
300
301         static struct req_info  reqs[] = {
302                 { 0x1111,       "Create a service connection" },
303                 { 0x2222, "Service request" },
304                 { 0x3333, "Service reply" },
305                 { 0x5555, "Destroy service connection" },
306                 { 0x7777, "Burst mode transfer" },
307                 { 0x9999, "Request being processed" },
308                 { 0x0000, NULL }
309         };
310
311         while (reqs[i].text != NULL) {
312                 if (reqs[i].req == req) {
313                         return reqs[i].text;
314                 }
315                 i++;
316         }
317         return "Unknown";
318 }
319
320 static char*
321 ncp2222_func(u_short func) {
322         int i=0;
323
324         static struct req_info  ncp[] = {
325                 { 17,   "Print and Queue Services" },
326                 { 21,   "Message Services" },
327                 { 22,   "File and Directory Services" },
328                 { 23,   "Binding and Rights Services" },
329                 { 34,   "Transaction Tacking Services" },
330                 { 35,   "Apple File Services" },
331                 { 86,   "Extended Attributes Services" },
332                 { 87,   "File and Directory Services" },
333                 { 88,   "Auditing Services" },
334                 { 104,  "Netware Directory Services" },
335                 { 123,  "Netware 4.x Statistical Information Services" },
336                 { 0,    NULL }
337         };
338
339         while (ncp[i].text != NULL) {
340                 if (ncp[i].req == func) {
341                         return ncp[i].text;
342                 }
343                 i++;
344         }
345         return "Unknown";
346 }
347
348 static char*
349 ncp2222_subfunc(u_short func, u_short subfunc) {
350         int i=0;
351         struct req_info *info_ptr = NULL;
352
353         /* Accounting Services */
354         static struct req_info  ncp_23[] = {
355                 { 150,  "Get Current Account Status" },
356                 { 151,  "Submit Account Charge" },
357                 { 152,  "Submit Account Hold" },
358                 { 153,  "Submit Account Note" },
359                 { 0,    NULL }
360         };
361
362         /* Apple File Services */
363         static struct req_info  ncp_35[] = {
364                 { 1,    "AFP Create Directory" },
365                 { 2,    "AFP Create File" },
366                 { 3,    "AFP Delete" },
367                 { 4,    "AFP Get Entry ID from Name" },
368                 { 5,    "AFP Get File Information" },
369                 { 6,    "AFP Get Entry ID From NetWare Handle" },
370                 { 7,    "AFP Rename" },
371                 { 8,    "AFP Open File Fork" },
372                 { 9,    "AFP Set File Information" },
373                 { 10,   "AFP Scan File Information" },
374                 { 11,   "AFP 2.0 Alloc Temporary Directory Handle" },
375                 { 12,   "AFP Get Entry ID from Name Path" },
376                 { 13,   "AFP 2.0 Create Directory" },
377                 { 14,   "AFP 2.0 Create File" },
378 /* ???  { 15,   "AFP 2.0 Delete File" }, just guessing */
379                 { 16,   "AFP 2.0 Set File Information" },
380                 { 17,   "AFP 2.0 Scan File Information" },
381                 { 18,   "AFP Get DOS Name from Entry ID" },
382                 { 19,   "AFP Get Macintosh Info on Deleted File" },
383                 { 0,    NULL }
384         };
385
386         /* Auditing Services */
387         static struct req_info  ncp_88[] = {
388                 { 1,    "Query Volume Audit Status" },
389                 { 2,    "Add Audit Property" },
390                 { 3,    "Add Auditor Access" },
391
392                 { 0,    NULL }
393         };
394
395         switch (func) {
396                 case 23:
397                         info_ptr = ncp_23;
398                         break;
399                 case 35:
400                         info_ptr = ncp_35;
401                         break;
402                 case 88:
403                         info_ptr = ncp_88;
404                         break;
405                 default:
406                         return "Unkown function";
407         }
408
409
410         while (info_ptr[i].text != NULL) {
411                 if (info_ptr[i].req == subfunc) {
412                         printf("subfunc=%s\n", info_ptr[i].text);
413                         return info_ptr[i].text;
414                 }
415                 i++;
416         }
417         return "Unknown";
418 }
419
420
421 static void
422 dissect_ncp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
423
424         GtkWidget       *ncp_tree, *ti;
425         guint16         ncp_type;
426         int                     ncp_hdr;
427
428         if (fd->win_info[0]) {
429                 strcpy(fd->win_info[3], "NCP");
430                 strcpy(fd->win_info[4], "NCP");
431         }
432
433         ncp_type = pntohs(&pd[offset]);
434
435         if (ncp_type == 0x1111 || ncp_type == 0x2222 || ncp_type == 0x5555 ||
436                 ncp_type == 0x7777) {
437                 ncp_hdr = 6;
438         }
439         else if (ncp_type == 0x3333 || ncp_type == 0x9999) {
440                 ncp_hdr = 8;
441         }
442         else {
443                 ncp_hdr = 1; /* ? */
444         }
445
446         if (tree) {
447                 ti = add_item_to_tree(GTK_WIDGET(tree), offset, ncp_hdr,
448                         "NetWare Core Protocol");
449                 ncp_tree = gtk_tree_new();
450                 add_subtree(ti, ncp_tree, ETT_NCP);
451
452                 add_item_to_tree(ncp_tree, offset,      2,
453                         "Type: %s", req_text( pntohs( &pd[offset] ) ) );
454
455                 add_item_to_tree(ncp_tree, offset+2,    1,
456                         "Sequence Number: %d", pd[offset+2]);
457
458                 add_item_to_tree(ncp_tree, offset+3,    1,
459                         "Connection Number Low: %d", pd[offset+3]);
460
461                 add_item_to_tree(ncp_tree, offset+4,    1,
462                         "Task Number: %d", pd[offset+4]);
463
464                 add_item_to_tree(ncp_tree, offset+5,    1,
465                         "Connection Number High: %d", pd[offset+5]);
466
467                 if (ncp_hdr == 8) {
468                         add_item_to_tree(ncp_tree, offset+6,    1,
469                                 "Completion Code: %d", pd[offset+6]);
470
471                         add_item_to_tree(ncp_tree, offset+7,    1,
472                                 "Connection Status: %d", pd[offset+7]);
473                 }
474
475                 offset += ncp_hdr;
476
477                 if (ncp_type == 0x2222) {
478                         /* my offset is different now */
479                         add_item_to_tree(ncp_tree, offset,              1,
480                                 "Function Code: %s (%d)",
481                                 ncp2222_func(pd[offset]), pd[offset]);
482
483                         add_item_to_tree(ncp_tree, offset+2,    1,
484                                 "Subfunction Code: %s (%d)",
485                                 ncp2222_subfunc(pd[offset], pd[offset+2]), pd[offset+2]);
486
487                         offset += 3;
488                 }
489
490                 dissect_data(pd, offset, fd, tree);
491         }
492 }