1/*
2 * Copyright (C) Andrew Tridgell 1995-1999
3 *
4 * This software may be distributed either under the terms of the
5 * BSD-style license that accompanies tcpdump or the GNU GPL version 2
6 * or later
7 */
8
9#include <sys/cdefs.h>
10#ifndef lint
11__RCSID("$NetBSD: print-smb.c,v 1.9 2023/08/17 20:19:40 christos Exp $");
12#endif
13
14/* \summary: SMB/CIFS printer */
15
16#ifdef HAVE_CONFIG_H
17#include <config.h>
18#endif
19
20#include "netdissect-stdinc.h"
21
22#include <string.h>
23
24#include "netdissect.h"
25#include "extract.h"
26#include "smb.h"
27
28
29static int request = 0;
30static int unicodestr = 0;
31
32extern const u_char *startbuf;
33
34const u_char *startbuf = NULL;
35
36struct smbdescript {
37    const char *req_f1;
38    const char *req_f2;
39    const char *rep_f1;
40    const char *rep_f2;
41    void (*fn)(netdissect_options *, const u_char *, const u_char *, const u_char *, const u_char *);
42};
43
44struct smbdescriptint {
45    const char *req_f1;
46    const char *req_f2;
47    const char *rep_f1;
48    const char *rep_f2;
49    void (*fn)(netdissect_options *, const u_char *, const u_char *, u_int, u_int);
50};
51
52struct smbfns
53{
54    int id;
55    const char *name;
56    int flags;
57    struct smbdescript descript;
58};
59
60struct smbfnsint
61{
62    int id;
63    const char *name;
64    int flags;
65    struct smbdescriptint descript;
66};
67
68#define DEFDESCRIPT	{ NULL, NULL, NULL, NULL, NULL }
69
70#define FLG_CHAIN	(1 << 0)
71
72static const struct smbfns *
73smbfind(int id, const struct smbfns *list)
74{
75    int sindex;
76
77    for (sindex = 0; list[sindex].name; sindex++)
78	if (list[sindex].id == id)
79	    return(&list[sindex]);
80
81    return(&list[0]);
82}
83
84static const struct smbfnsint *
85smbfindint(int id, const struct smbfnsint *list)
86{
87    int sindex;
88
89    for (sindex = 0; list[sindex].name; sindex++)
90	if (list[sindex].id == id)
91	    return(&list[sindex]);
92
93    return(&list[0]);
94}
95
96static void
97trans2_findfirst(netdissect_options *ndo,
98                 const u_char *param, const u_char *data, u_int pcnt, u_int dcnt)
99{
100    const char *fmt;
101
102    if (request)
103	fmt = "Attribute=[A]\nSearchCount=[u]\nFlags=[w]\nLevel=[uP4]\nFile=[S]\n";
104    else
105	fmt = "Handle=[w]\nCount=[u]\nEOS=[w]\nEoffset=[u]\nLastNameOfs=[w]\n";
106
107    smb_fdata(ndo, param, fmt, param + pcnt, unicodestr);
108    if (dcnt) {
109	ND_PRINT("data:\n");
110	smb_data_print(ndo, data, dcnt);
111    }
112}
113
114static void
115trans2_qfsinfo(netdissect_options *ndo,
116               const u_char *param, const u_char *data, u_int pcnt, u_int dcnt)
117{
118    static u_int level = 0;
119    const char *fmt="";
120
121    if (request) {
122	level = GET_LE_U_2(param);
123	fmt = "InfoLevel=[u]\n";
124	smb_fdata(ndo, param, fmt, param + pcnt, unicodestr);
125    } else {
126	switch (level) {
127	case 1:
128	    fmt = "idFileSystem=[W]\nSectorUnit=[U]\nUnit=[U]\nAvail=[U]\nSectorSize=[u]\n";
129	    break;
130	case 2:
131	    fmt = "CreationTime=[T2]VolNameLength=[lb]\nVolumeLabel=[c]\n";
132	    break;
133	case 0x105:
134	    fmt = "Capabilities=[W]\nMaxFileLen=[U]\nVolNameLen=[lU]\nVolume=[C]\n";
135	    break;
136	default:
137	    fmt = "UnknownLevel\n";
138	    break;
139	}
140	smb_fdata(ndo, data, fmt, data + dcnt, unicodestr);
141    }
142    if (dcnt) {
143	ND_PRINT("data:\n");
144	smb_data_print(ndo, data, dcnt);
145    }
146}
147
148static const struct smbfnsint trans2_fns[] = {
149    { 0, "TRANSACT2_OPEN", 0,
150	{ "Flags2=[w]\nMode=[w]\nSearchAttrib=[A]\nAttrib=[A]\nTime=[T2]\nOFun=[w]\nSize=[U]\nRes=([w, w, w, w, w])\nPath=[S]",
151	  NULL,
152	  "Handle=[u]\nAttrib=[A]\nTime=[T2]\nSize=[U]\nAccess=[w]\nType=[w]\nState=[w]\nAction=[w]\nInode=[W]\nOffErr=[u]\n|EALength=[u]\n",
153	  NULL, NULL }},
154    { 1, "TRANSACT2_FINDFIRST", 0,
155	{ NULL, NULL, NULL, NULL, trans2_findfirst }},
156    { 2, "TRANSACT2_FINDNEXT", 0, DEFDESCRIPT },
157    { 3, "TRANSACT2_QFSINFO", 0,
158	{ NULL, NULL, NULL, NULL, trans2_qfsinfo }},
159    { 4, "TRANSACT2_SETFSINFO", 0, DEFDESCRIPT },
160    { 5, "TRANSACT2_QPATHINFO", 0, DEFDESCRIPT },
161    { 6, "TRANSACT2_SETPATHINFO", 0, DEFDESCRIPT },
162    { 7, "TRANSACT2_QFILEINFO", 0, DEFDESCRIPT },
163    { 8, "TRANSACT2_SETFILEINFO", 0, DEFDESCRIPT },
164    { 9, "TRANSACT2_FSCTL", 0, DEFDESCRIPT },
165    { 10, "TRANSACT2_IOCTL", 0, DEFDESCRIPT },
166    { 11, "TRANSACT2_FINDNOTIFYFIRST", 0, DEFDESCRIPT },
167    { 12, "TRANSACT2_FINDNOTIFYNEXT", 0, DEFDESCRIPT },
168    { 13, "TRANSACT2_MKDIR", 0, DEFDESCRIPT },
169    { -1, NULL, 0, DEFDESCRIPT }
170};
171
172
173static void
174print_trans2(netdissect_options *ndo,
175             const u_char *words, const u_char *dat, const u_char *buf, const u_char *maxbuf)
176{
177    u_int bcc;
178    static const struct smbfnsint *fn = &trans2_fns[0];
179    const u_char *data, *param;
180    const u_char *w = words + 1;
181    const char *f1 = NULL, *f2 = NULL;
182    u_int pcnt, dcnt;
183
184    ND_TCHECK_1(words);
185    if (request) {
186	ND_TCHECK_2(w + (14 * 2));
187	pcnt = GET_LE_U_2(w + 9 * 2);
188	param = buf + GET_LE_U_2(w + 10 * 2);
189	dcnt = GET_LE_U_2(w + 11 * 2);
190	data = buf + GET_LE_U_2(w + 12 * 2);
191	fn = smbfindint(GET_LE_U_2(w + 14 * 2), trans2_fns);
192    } else {
193	if (GET_U_1(words) == 0) {
194	    ND_PRINT("%s\n", fn->name);
195	    ND_PRINT("Trans2Interim\n");
196	    return;
197	}
198	ND_TCHECK_2(w + (7 * 2));
199	pcnt = GET_LE_U_2(w + 3 * 2);
200	param = buf + GET_LE_U_2(w + 4 * 2);
201	dcnt = GET_LE_U_2(w + 6 * 2);
202	data = buf + GET_LE_U_2(w + 7 * 2);
203    }
204
205    ND_PRINT("%s param_length=%u data_length=%u\n", fn->name, pcnt, dcnt);
206
207    if (request) {
208	if (GET_U_1(words) == 8) {
209	    smb_fdata(ndo, words + 1,
210		"Trans2Secondary\nTotParam=[u]\nTotData=[u]\nParamCnt=[u]\nParamOff=[u]\nParamDisp=[u]\nDataCnt=[u]\nDataOff=[u]\nDataDisp=[u]\nHandle=[u]\n",
211		maxbuf, unicodestr);
212	    return;
213	} else {
214	    smb_fdata(ndo, words + 1,
215		"TotParam=[u]\nTotData=[u]\nMaxParam=[u]\nMaxData=[u]\nMaxSetup=[b][P1]\nFlags=[w]\nTimeOut=[D]\nRes1=[w]\nParamCnt=[u]\nParamOff=[u]\nDataCnt=[u]\nDataOff=[u]\nSetupCnt=[b][P1]\n",
216		words + 1 + 14 * 2, unicodestr);
217	}
218	f1 = fn->descript.req_f1;
219	f2 = fn->descript.req_f2;
220    } else {
221	smb_fdata(ndo, words + 1,
222	    "TotParam=[u]\nTotData=[u]\nRes1=[w]\nParamCnt=[u]\nParamOff=[u]\nParamDisp[u]\nDataCnt=[u]\nDataOff=[u]\nDataDisp=[u]\nSetupCnt=[b][P1]\n",
223	    words + 1 + 10 * 2, unicodestr);
224	f1 = fn->descript.rep_f1;
225	f2 = fn->descript.rep_f2;
226    }
227
228    bcc = GET_LE_U_2(dat);
229    ND_PRINT("smb_bcc=%u\n", bcc);
230    if (fn->descript.fn)
231	(*fn->descript.fn)(ndo, param, data, pcnt, dcnt);
232    else {
233	smb_fdata(ndo, param, f1 ? f1 : "Parameters=\n", param + pcnt, unicodestr);
234	smb_fdata(ndo, data, f2 ? f2 : "Data=\n", data + dcnt, unicodestr);
235    }
236    return;
237trunc:
238    nd_print_trunc(ndo);
239}
240
241static void
242print_browse(netdissect_options *ndo,
243             const u_char *param, u_int paramlen, const u_char *data, u_int datalen)
244{
245    const u_char *maxbuf = data + datalen;
246    u_int command;
247
248    command = GET_U_1(data);
249
250    smb_fdata(ndo, param, "BROWSE PACKET\n|Param ", param+paramlen, unicodestr);
251
252    switch (command) {
253    case 0xF:
254	data = smb_fdata(ndo, data,
255	    "BROWSE PACKET:\nType=[B] (LocalMasterAnnouncement)\nUpdateCount=[w]\nRes1=[B]\nAnnounceInterval=[u]\nName=[n2]\nMajorVersion=[B]\nMinorVersion=[B]\nServerType=[W]\nElectionVersion=[w]\nBrowserConstant=[w]\n",
256	    maxbuf, unicodestr);
257	break;
258
259    case 0x1:
260	data = smb_fdata(ndo, data,
261	    "BROWSE PACKET:\nType=[B] (HostAnnouncement)\nUpdateCount=[w]\nRes1=[B]\nAnnounceInterval=[u]\nName=[n2]\nMajorVersion=[B]\nMinorVersion=[B]\nServerType=[W]\nElectionVersion=[w]\nBrowserConstant=[w]\n",
262	    maxbuf, unicodestr);
263	break;
264
265    case 0x2:
266	data = smb_fdata(ndo, data,
267	    "BROWSE PACKET:\nType=[B] (AnnouncementRequest)\nFlags=[B]\nReplySystemName=[S]\n",
268	    maxbuf, unicodestr);
269	break;
270
271    case 0xc:
272	data = smb_fdata(ndo, data,
273	    "BROWSE PACKET:\nType=[B] (WorkgroupAnnouncement)\nUpdateCount=[w]\nRes1=[B]\nAnnounceInterval=[u]\nName=[n2]\nMajorVersion=[B]\nMinorVersion=[B]\nServerType=[W]\nCommentPointer=[W]\nServerName=[S]\n",
274	    maxbuf, unicodestr);
275	break;
276
277    case 0x8:
278	data = smb_fdata(ndo, data,
279	    "BROWSE PACKET:\nType=[B] (ElectionFrame)\nElectionVersion=[B]\nOSSummary=[W]\nUptime=[(W, W)]\nServerName=[S]\n",
280	    maxbuf, unicodestr);
281	break;
282
283    case 0xb:
284	data = smb_fdata(ndo, data,
285	    "BROWSE PACKET:\nType=[B] (BecomeBackupBrowser)\nName=[S]\n",
286	    maxbuf, unicodestr);
287	break;
288
289    case 0x9:
290	data = smb_fdata(ndo, data,
291	    "BROWSE PACKET:\nType=[B] (GetBackupList)\nListCount?=[B]\nToken=[W]\n",
292	    maxbuf, unicodestr);
293	break;
294
295    case 0xa:
296	data = smb_fdata(ndo, data,
297	    "BROWSE PACKET:\nType=[B] (BackupListResponse)\nServerCount?=[B]\nToken=[W]\n*Name=[S]\n",
298	    maxbuf, unicodestr);
299	break;
300
301    case 0xd:
302	data = smb_fdata(ndo, data,
303	    "BROWSE PACKET:\nType=[B] (MasterAnnouncement)\nMasterName=[S]\n",
304	    maxbuf, unicodestr);
305	break;
306
307    case 0xe:
308	data = smb_fdata(ndo, data,
309	    "BROWSE PACKET:\nType=[B] (ResetBrowser)\nOptions=[B]\n", maxbuf, unicodestr);
310	break;
311
312    default:
313	data = smb_fdata(ndo, data, "Unknown Browser Frame ", maxbuf, unicodestr);
314	break;
315    }
316}
317
318
319static void
320print_ipc(netdissect_options *ndo,
321          const u_char *param, u_int paramlen, const u_char *data, u_int datalen)
322{
323    if (paramlen)
324	smb_fdata(ndo, param, "Command=[w]\nStr1=[S]\nStr2=[S]\n", param + paramlen,
325	    unicodestr);
326    if (datalen)
327	smb_fdata(ndo, data, "IPC ", data + datalen, unicodestr);
328}
329
330
331static void
332print_trans(netdissect_options *ndo,
333            const u_char *words, const u_char *data1, const u_char *buf, const u_char *maxbuf)
334{
335    u_int bcc;
336    const char *f1, *f2, *f3, *f4;
337    const u_char *data, *param;
338    const u_char *w = words + 1;
339    u_int datalen, paramlen;
340
341    if (request) {
342	ND_TCHECK_2(w + (12 * 2));
343	paramlen = GET_LE_U_2(w + 9 * 2);
344	param = buf + GET_LE_U_2(w + 10 * 2);
345	datalen = GET_LE_U_2(w + 11 * 2);
346	data = buf + GET_LE_U_2(w + 12 * 2);
347	f1 = "TotParamCnt=[u]\nTotDataCnt=[u]\nMaxParmCnt=[u]\nMaxDataCnt=[u]\nMaxSCnt=[u]\nTransFlags=[w]\nRes1=[w]\nRes2=[w]\nRes3=[w]\nParamCnt=[u]\nParamOff=[u]\nDataCnt=[u]\nDataOff=[u]\nSUCnt=[u]\n";
348	f2 = "|Name=[S]\n";
349	f3 = "|Param ";
350	f4 = "|Data ";
351    } else {
352	ND_TCHECK_2(w + (7 * 2));
353	paramlen = GET_LE_U_2(w + 3 * 2);
354	param = buf + GET_LE_U_2(w + 4 * 2);
355	datalen = GET_LE_U_2(w + 6 * 2);
356	data = buf + GET_LE_U_2(w + 7 * 2);
357	f1 = "TotParamCnt=[u]\nTotDataCnt=[u]\nRes1=[u]\nParamCnt=[u]\nParamOff=[u]\nRes2=[u]\nDataCnt=[u]\nDataOff=[u]\nRes3=[u]\nLsetup=[u]\n";
358	f2 = "|Unknown ";
359	f3 = "|Param ";
360	f4 = "|Data ";
361    }
362
363    smb_fdata(ndo, words + 1, f1,
364              ND_MIN(words + 1 + 2 * GET_U_1(words), maxbuf),
365              unicodestr);
366
367    bcc = GET_LE_U_2(data1);
368    ND_PRINT("smb_bcc=%u\n", bcc);
369    if (bcc > 0) {
370	smb_fdata(ndo, data1 + 2, f2, maxbuf - (paramlen + datalen), unicodestr);
371
372#define MAILSLOT_BROWSE_STR "\\MAILSLOT\\BROWSE"
373	ND_TCHECK_LEN(data1 + 2, strlen(MAILSLOT_BROWSE_STR) + 1);
374	if (strcmp((const char *)(data1 + 2), MAILSLOT_BROWSE_STR) == 0) {
375	    print_browse(ndo, param, paramlen, data, datalen);
376	    return;
377	}
378#undef MAILSLOT_BROWSE_STR
379
380#define PIPE_LANMAN_STR "\\PIPE\\LANMAN"
381	ND_TCHECK_LEN(data1 + 2, strlen(PIPE_LANMAN_STR) + 1);
382	if (strcmp((const char *)(data1 + 2), PIPE_LANMAN_STR) == 0) {
383	    print_ipc(ndo, param, paramlen, data, datalen);
384	    return;
385	}
386#undef PIPE_LANMAN_STR
387
388	if (paramlen)
389	    smb_fdata(ndo, param, f3, ND_MIN(param + paramlen, maxbuf), unicodestr);
390	if (datalen)
391	    smb_fdata(ndo, data, f4, ND_MIN(data + datalen, maxbuf), unicodestr);
392    }
393    return;
394trunc:
395    nd_print_trunc(ndo);
396}
397
398
399static void
400print_negprot(netdissect_options *ndo,
401              const u_char *words, const u_char *data, const u_char *buf _U_, const u_char *maxbuf)
402{
403    u_int wct, bcc;
404    const char *f1 = NULL, *f2 = NULL;
405
406    wct = GET_U_1(words);
407    if (request)
408	f2 = "*|Dialect=[Y]\n";
409    else {
410	if (wct == 1)
411	    f1 = "Core Protocol\nDialectIndex=[u]";
412	else if (wct == 17)
413	    f1 = "NT1 Protocol\nDialectIndex=[u]\nSecMode=[B]\nMaxMux=[u]\nNumVcs=[u]\nMaxBuffer=[U]\nRawSize=[U]\nSessionKey=[W]\nCapabilities=[W]\nServerTime=[T3]TimeZone=[u]\nCryptKey=";
414	else if (wct == 13)
415	    f1 = "Coreplus/Lanman1/Lanman2 Protocol\nDialectIndex=[u]\nSecMode=[w]\nMaxXMit=[u]\nMaxMux=[u]\nMaxVcs=[u]\nBlkMode=[w]\nSessionKey=[W]\nServerTime=[T1]TimeZone=[u]\nRes=[W]\nCryptKey=";
416    }
417
418    if (f1)
419	smb_fdata(ndo, words + 1, f1, ND_MIN(words + 1 + wct * 2, maxbuf),
420	    unicodestr);
421    else
422	smb_data_print(ndo, words + 1, ND_MIN(wct * 2, ND_BYTES_BETWEEN(maxbuf, words + 1)));
423
424    bcc = GET_LE_U_2(data);
425    ND_PRINT("smb_bcc=%u\n", bcc);
426    if (bcc > 0) {
427	if (f2)
428	    smb_fdata(ndo, data + 2, f2, ND_MIN(data + 2 + GET_LE_U_2(data),
429                                             maxbuf), unicodestr);
430	else
431	    smb_data_print(ndo, data + 2,
432                           ND_MIN(GET_LE_U_2(data), ND_BYTES_BETWEEN(maxbuf, data + 2)));
433    }
434}
435
436static void
437print_sesssetup(netdissect_options *ndo,
438                const u_char *words, const u_char *data, const u_char *buf _U_, const u_char *maxbuf)
439{
440    u_int wct, bcc;
441    const char *f1 = NULL, *f2 = NULL;
442
443    wct = GET_U_1(words);
444    if (request) {
445	if (wct == 10)
446	    f1 = "Com2=[w]\nOff2=[u]\nBufSize=[u]\nMpxMax=[u]\nVcNum=[u]\nSessionKey=[W]\nPassLen=[u]\nCryptLen=[u]\nCryptOff=[u]\nPass&Name=\n";
447	else
448	    f1 = "Com2=[B]\nRes1=[B]\nOff2=[u]\nMaxBuffer=[u]\nMaxMpx=[u]\nVcNumber=[u]\nSessionKey=[W]\nCaseInsensitivePasswordLength=[u]\nCaseSensitivePasswordLength=[u]\nRes=[W]\nCapabilities=[W]\nPass1&Pass2&Account&Domain&OS&LanMan=\n";
449    } else {
450	if (wct == 3) {
451	    f1 = "Com2=[w]\nOff2=[u]\nAction=[w]\n";
452	} else if (wct == 13) {
453	    f1 = "Com2=[B]\nRes=[B]\nOff2=[u]\nAction=[w]\n";
454	    f2 = "NativeOS=[S]\nNativeLanMan=[S]\nPrimaryDomain=[S]\n";
455	}
456    }
457
458    if (f1)
459	smb_fdata(ndo, words + 1, f1, ND_MIN(words + 1 + wct * 2, maxbuf),
460	    unicodestr);
461    else
462	smb_data_print(ndo, words + 1, ND_MIN(wct * 2, ND_BYTES_BETWEEN(maxbuf, words + 1)));
463
464    bcc = GET_LE_U_2(data);
465    ND_PRINT("smb_bcc=%u\n", bcc);
466    if (bcc > 0) {
467	if (f2)
468	    smb_fdata(ndo, data + 2, f2, ND_MIN(data + 2 + GET_LE_U_2(data),
469                                             maxbuf), unicodestr);
470	else
471	    smb_data_print(ndo, data + 2,
472                           ND_MIN(GET_LE_U_2(data), ND_BYTES_BETWEEN(maxbuf, data + 2)));
473    }
474}
475
476static void
477print_lockingandx(netdissect_options *ndo,
478                  const u_char *words, const u_char *data, const u_char *buf _U_, const u_char *maxbuf)
479{
480    u_int wct, bcc;
481    const u_char *maxwords;
482    const char *f1 = NULL, *f2 = NULL;
483
484    wct = GET_U_1(words);
485    if (request) {
486	f1 = "Com2=[w]\nOff2=[u]\nHandle=[u]\nLockType=[w]\nTimeOut=[D]\nUnlockCount=[u]\nLockCount=[u]\n";
487	if (GET_U_1(words + 7) & 0x10)
488	    f2 = "*Process=[u]\n[P2]Offset=[M]\nLength=[M]\n";
489	else
490	    f2 = "*Process=[u]\nOffset=[D]\nLength=[U]\n";
491    } else {
492	f1 = "Com2=[w]\nOff2=[u]\n";
493    }
494
495    maxwords = ND_MIN(words + 1 + wct * 2, maxbuf);
496    if (wct)
497	smb_fdata(ndo, words + 1, f1, maxwords, unicodestr);
498
499    bcc = GET_LE_U_2(data);
500    ND_PRINT("smb_bcc=%u\n", bcc);
501    if (bcc > 0) {
502	if (f2)
503	    smb_fdata(ndo, data + 2, f2, ND_MIN(data + 2 + GET_LE_U_2(data),
504                                             maxbuf), unicodestr);
505	else
506	    smb_data_print(ndo, data + 2,
507                           ND_MIN(GET_LE_U_2(data), ND_BYTES_BETWEEN(maxbuf, data + 2)));
508    }
509}
510
511
512static const struct smbfns smb_fns[] = {
513    { -1, "SMBunknown", 0, DEFDESCRIPT },
514
515    { SMBtcon, "SMBtcon", 0,
516	{ NULL, "Path=[Z]\nPassword=[Z]\nDevice=[Z]\n",
517	  "MaxXmit=[u]\nTreeId=[u]\n", NULL,
518	  NULL } },
519
520    { SMBtdis, "SMBtdis", 0, DEFDESCRIPT },
521    { SMBexit,  "SMBexit", 0, DEFDESCRIPT },
522    { SMBioctl, "SMBioctl", 0, DEFDESCRIPT },
523
524    { SMBecho, "SMBecho", 0,
525	{ "ReverbCount=[u]\n", NULL,
526	  "SequenceNum=[u]\n", NULL,
527	  NULL } },
528
529    { SMBulogoffX, "SMBulogoffX", FLG_CHAIN, DEFDESCRIPT },
530
531    { SMBgetatr, "SMBgetatr", 0,
532	{ NULL, "Path=[Z]\n",
533	  "Attribute=[A]\nTime=[T2]Size=[U]\nRes=([w,w,w,w,w])\n", NULL,
534	  NULL } },
535
536    { SMBsetatr, "SMBsetatr", 0,
537	{ "Attribute=[A]\nTime=[T2]Res=([w,w,w,w,w])\n", "Path=[Z]\n",
538	  NULL, NULL, NULL } },
539
540    { SMBchkpth, "SMBchkpth", 0,
541       { NULL, "Path=[Z]\n", NULL, NULL, NULL } },
542
543    { SMBsearch, "SMBsearch", 0,
544	{ "Count=[u]\nAttrib=[A]\n",
545	  "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\n",
546	  "Count=[u]\n",
547	  "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
548	  NULL } },
549
550    { SMBopen, "SMBopen", 0,
551	{ "Mode=[w]\nAttribute=[A]\n", "Path=[Z]\n",
552	  "Handle=[u]\nOAttrib=[A]\nTime=[T2]Size=[U]\nAccess=[w]\n",
553	  NULL, NULL } },
554
555    { SMBcreate, "SMBcreate", 0,
556	{ "Attrib=[A]\nTime=[T2]", "Path=[Z]\n", "Handle=[u]\n", NULL, NULL } },
557
558    { SMBmknew, "SMBmknew", 0,
559	{ "Attrib=[A]\nTime=[T2]", "Path=[Z]\n", "Handle=[u]\n", NULL, NULL } },
560
561    { SMBunlink, "SMBunlink", 0,
562	{ "Attrib=[A]\n", "Path=[Z]\n", NULL, NULL, NULL } },
563
564    { SMBread, "SMBread", 0,
565	{ "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
566	  "Count=[u]\nRes=([w,w,w,w])\n", NULL, NULL } },
567
568    { SMBwrite, "SMBwrite", 0,
569	{ "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
570	  "Count=[u]\n", NULL, NULL } },
571
572    { SMBclose, "SMBclose", 0,
573	{ "Handle=[u]\nTime=[T2]", NULL, NULL, NULL, NULL } },
574
575    { SMBmkdir, "SMBmkdir", 0,
576	{ NULL, "Path=[Z]\n", NULL, NULL, NULL } },
577
578    { SMBrmdir, "SMBrmdir", 0,
579	{ NULL, "Path=[Z]\n", NULL, NULL, NULL } },
580
581    { SMBdskattr, "SMBdskattr", 0,
582	{ NULL, NULL,
583	  "TotalUnits=[u]\nBlocksPerUnit=[u]\nBlockSize=[u]\nFreeUnits=[u]\nMedia=[w]\n",
584	  NULL, NULL } },
585
586    { SMBmv, "SMBmv", 0,
587	{ "Attrib=[A]\n", "OldPath=[Z]\nNewPath=[Z]\n", NULL, NULL, NULL } },
588
589    /*
590     * this is a Pathworks specific call, allowing the
591     * changing of the root path
592     */
593    { pSETDIR, "SMBsetdir", 0, { NULL, "Path=[Z]\n", NULL, NULL, NULL } },
594
595    { SMBlseek, "SMBlseek", 0,
596	{ "Handle=[u]\nMode=[w]\nOffset=[D]\n", "Offset=[D]\n", NULL, NULL, NULL } },
597
598    { SMBflush, "SMBflush", 0, { "Handle=[u]\n", NULL, NULL, NULL, NULL } },
599
600    { SMBsplopen, "SMBsplopen", 0,
601	{ "SetupLen=[u]\nMode=[w]\n", "Ident=[Z]\n", "Handle=[u]\n",
602	  NULL, NULL } },
603
604    { SMBsplclose, "SMBsplclose", 0,
605	{ "Handle=[u]\n", NULL, NULL, NULL, NULL } },
606
607    { SMBsplretq, "SMBsplretq", 0,
608	{ "MaxCount=[u]\nStartIndex=[u]\n", NULL,
609	  "Count=[u]\nIndex=[u]\n",
610	  "*Time=[T2]Status=[B]\nJobID=[u]\nSize=[U]\nRes=[B]Name=[s16]\n",
611	  NULL } },
612
613    { SMBsplwr, "SMBsplwr", 0,
614	{ "Handle=[u]\n", NULL, NULL, NULL, NULL } },
615
616    { SMBlock, "SMBlock", 0,
617	{ "Handle=[u]\nCount=[U]\nOffset=[D]\n", NULL, NULL, NULL, NULL } },
618
619    { SMBunlock, "SMBunlock", 0,
620	{ "Handle=[u]\nCount=[U]\nOffset=[D]\n", NULL, NULL, NULL, NULL } },
621
622    /* CORE+ PROTOCOL FOLLOWS */
623
624    { SMBreadbraw, "SMBreadbraw", 0,
625	{ "Handle=[u]\nOffset=[D]\nMaxCount=[u]\nMinCount=[u]\nTimeOut=[D]\nRes=[u]\n",
626	  NULL, NULL, NULL, NULL } },
627
628    { SMBwritebraw, "SMBwritebraw", 0,
629	{ "Handle=[u]\nTotalCount=[u]\nRes=[w]\nOffset=[D]\nTimeOut=[D]\nWMode=[w]\nRes2=[W]\n|DataSize=[u]\nDataOff=[u]\n",
630	  NULL, "WriteRawAck", NULL, NULL } },
631
632    { SMBwritec, "SMBwritec", 0,
633	{ NULL, NULL, "Count=[u]\n", NULL, NULL } },
634
635    { SMBwriteclose, "SMBwriteclose", 0,
636	{ "Handle=[u]\nCount=[u]\nOffset=[D]\nTime=[T2]Res=([w,w,w,w,w,w])",
637	  NULL, "Count=[u]\n", NULL, NULL } },
638
639    { SMBlockread, "SMBlockread", 0,
640	{ "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
641	  "Count=[u]\nRes=([w,w,w,w])\n", NULL, NULL } },
642
643    { SMBwriteunlock, "SMBwriteunlock", 0,
644	{ "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
645	  "Count=[u]\n", NULL, NULL } },
646
647    { SMBreadBmpx, "SMBreadBmpx", 0,
648	{ "Handle=[u]\nOffset=[D]\nMaxCount=[u]\nMinCount=[u]\nTimeOut=[D]\nRes=[w]\n",
649	  NULL,
650	  "Offset=[D]\nTotCount=[u]\nRemaining=[u]\nRes=([w,w])\nDataSize=[u]\nDataOff=[u]\n",
651	  NULL, NULL } },
652
653    { SMBwriteBmpx, "SMBwriteBmpx", 0,
654	{ "Handle=[u]\nTotCount=[u]\nRes=[w]\nOffset=[D]\nTimeOut=[D]\nWMode=[w]\nRes2=[W]\nDataSize=[u]\nDataOff=[u]\n", NULL,
655	  "Remaining=[u]\n", NULL, NULL } },
656
657    { SMBwriteBs, "SMBwriteBs", 0,
658	{ "Handle=[u]\nTotCount=[u]\nOffset=[D]\nRes=[W]\nDataSize=[u]\nDataOff=[u]\n",
659	  NULL, "Count=[u]\n", NULL, NULL } },
660
661    { SMBsetattrE, "SMBsetattrE", 0,
662	{ "Handle=[u]\nCreationTime=[T2]AccessTime=[T2]ModifyTime=[T2]", NULL,
663	  NULL, NULL, NULL } },
664
665    { SMBgetattrE, "SMBgetattrE", 0,
666	{ "Handle=[u]\n", NULL,
667	  "CreationTime=[T2]AccessTime=[T2]ModifyTime=[T2]Size=[U]\nAllocSize=[U]\nAttribute=[A]\n",
668	  NULL, NULL } },
669
670    { SMBtranss, "SMBtranss", 0, DEFDESCRIPT },
671    { SMBioctls, "SMBioctls", 0, DEFDESCRIPT },
672
673    { SMBcopy, "SMBcopy", 0,
674	{ "TreeID2=[u]\nOFun=[w]\nFlags=[w]\n", "Path=[S]\nNewPath=[S]\n",
675	  "CopyCount=[u]\n",  "|ErrStr=[S]\n",  NULL } },
676
677    { SMBmove, "SMBmove", 0,
678	{ "TreeID2=[u]\nOFun=[w]\nFlags=[w]\n", "Path=[S]\nNewPath=[S]\n",
679	  "MoveCount=[u]\n",  "|ErrStr=[S]\n",  NULL } },
680
681    { SMBopenX, "SMBopenX", FLG_CHAIN,
682	{ "Com2=[w]\nOff2=[u]\nFlags=[w]\nMode=[w]\nSearchAttrib=[A]\nAttrib=[A]\nTime=[T2]OFun=[w]\nSize=[U]\nTimeOut=[D]\nRes=[W]\n",
683	  "Path=[S]\n",
684	  "Com2=[w]\nOff2=[u]\nHandle=[u]\nAttrib=[A]\nTime=[T2]Size=[U]\nAccess=[w]\nType=[w]\nState=[w]\nAction=[w]\nFileID=[W]\nRes=[w]\n",
685	  NULL, NULL } },
686
687    { SMBreadX, "SMBreadX", FLG_CHAIN,
688	{ "Com2=[w]\nOff2=[u]\nHandle=[u]\nOffset=[D]\nMaxCount=[u]\nMinCount=[u]\nTimeOut=[D]\nCountLeft=[u]\n",
689	  NULL,
690	  "Com2=[w]\nOff2=[u]\nRemaining=[u]\nRes=[W]\nDataSize=[u]\nDataOff=[u]\nRes=([w,w,w,w])\n",
691	  NULL, NULL } },
692
693    { SMBwriteX, "SMBwriteX", FLG_CHAIN,
694	{ "Com2=[w]\nOff2=[u]\nHandle=[u]\nOffset=[D]\nTimeOut=[D]\nWMode=[w]\nCountLeft=[u]\nRes=[w]\nDataSize=[u]\nDataOff=[u]\n",
695	  NULL,
696	  "Com2=[w]\nOff2=[u]\nCount=[u]\nRemaining=[u]\nRes=[W]\n",
697	  NULL, NULL } },
698
699    { SMBffirst, "SMBffirst", 0,
700	{ "Count=[u]\nAttrib=[A]\n",
701	  "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\n",
702	  "Count=[u]\n",
703	  "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
704	  NULL } },
705
706    { SMBfunique, "SMBfunique", 0,
707	{ "Count=[u]\nAttrib=[A]\n",
708	  "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\n",
709	  "Count=[u]\n",
710	  "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
711	  NULL } },
712
713    { SMBfclose, "SMBfclose", 0,
714	{ "Count=[u]\nAttrib=[A]\n",
715	  "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\n",
716	  "Count=[u]\n",
717	  "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
718	  NULL } },
719
720    { SMBfindnclose, "SMBfindnclose", 0,
721	{ "Handle=[u]\n", NULL, NULL, NULL, NULL } },
722
723    { SMBfindclose, "SMBfindclose", 0,
724	{ "Handle=[u]\n", NULL, NULL, NULL, NULL } },
725
726    { SMBsends, "SMBsends", 0,
727	{ NULL, "Source=[Z]\nDest=[Z]\n", NULL, NULL, NULL } },
728
729    { SMBsendstrt, "SMBsendstrt", 0,
730	{ NULL, "Source=[Z]\nDest=[Z]\n", "GroupID=[u]\n", NULL, NULL } },
731
732    { SMBsendend, "SMBsendend", 0,
733	{ "GroupID=[u]\n", NULL, NULL, NULL, NULL } },
734
735    { SMBsendtxt, "SMBsendtxt", 0,
736	{ "GroupID=[u]\n", NULL, NULL, NULL, NULL } },
737
738    { SMBsendb, "SMBsendb", 0,
739	{ NULL, "Source=[Z]\nDest=[Z]\n", NULL, NULL, NULL } },
740
741    { SMBfwdname, "SMBfwdname", 0, DEFDESCRIPT },
742    { SMBcancelf, "SMBcancelf", 0, DEFDESCRIPT },
743    { SMBgetmac, "SMBgetmac", 0, DEFDESCRIPT },
744
745    { SMBnegprot, "SMBnegprot", 0,
746	{ NULL, NULL, NULL, NULL, print_negprot } },
747
748    { SMBsesssetupX, "SMBsesssetupX", FLG_CHAIN,
749	{ NULL, NULL, NULL, NULL, print_sesssetup } },
750
751    { SMBtconX, "SMBtconX", FLG_CHAIN,
752	{ "Com2=[w]\nOff2=[u]\nFlags=[w]\nPassLen=[u]\nPasswd&Path&Device=\n",
753	  NULL, "Com2=[w]\nOff2=[u]\n", "ServiceType=[R]\n", NULL } },
754
755    { SMBlockingX, "SMBlockingX", FLG_CHAIN,
756	{ NULL, NULL, NULL, NULL, print_lockingandx } },
757
758    { SMBtrans2, "SMBtrans2", 0, { NULL, NULL, NULL, NULL, print_trans2 } },
759
760    { SMBtranss2, "SMBtranss2", 0, DEFDESCRIPT },
761    { SMBctemp, "SMBctemp", 0, DEFDESCRIPT },
762    { SMBreadBs, "SMBreadBs", 0, DEFDESCRIPT },
763    { SMBtrans, "SMBtrans", 0, { NULL, NULL, NULL, NULL, print_trans } },
764
765    { SMBnttrans, "SMBnttrans", 0, DEFDESCRIPT },
766    { SMBnttranss, "SMBnttranss", 0, DEFDESCRIPT },
767
768    { SMBntcreateX, "SMBntcreateX", FLG_CHAIN,
769	{ "Com2=[w]\nOff2=[u]\nRes=[b]\nNameLen=[lu]\nFlags=[W]\nRootDirectoryFid=[U]\nAccessMask=[W]\nAllocationSize=[L]\nExtFileAttributes=[W]\nShareAccess=[W]\nCreateDisposition=[W]\nCreateOptions=[W]\nImpersonationLevel=[W]\nSecurityFlags=[b]\n",
770	  "Path=[C]\n",
771	  "Com2=[w]\nOff2=[u]\nOplockLevel=[b]\nFid=[u]\nCreateAction=[W]\nCreateTime=[T3]LastAccessTime=[T3]LastWriteTime=[T3]ChangeTime=[T3]ExtFileAttributes=[W]\nAllocationSize=[L]\nEndOfFile=[L]\nFileType=[w]\nDeviceState=[w]\nDirectory=[b]\n",
772	  NULL, NULL } },
773
774    { SMBntcancel, "SMBntcancel", 0, DEFDESCRIPT },
775
776    { -1, NULL, 0, DEFDESCRIPT }
777};
778
779
780/*
781 * print a SMB message
782 */
783static void
784print_smb(netdissect_options *ndo,
785          const u_char *buf, const u_char *maxbuf)
786{
787    uint16_t flags2;
788    u_int nterrcodes;
789    u_int command;
790    uint32_t nterror;
791    const u_char *words, *maxwords, *data;
792    const struct smbfns *fn;
793    const char *fmt_smbheader =
794        "[P4]SMB Command   =  [B]\nError class   =  [BP1]\nError code    =  [u]\nFlags1        =  [B]\nFlags2        =  [B][P13]\nTree ID       =  [u]\nProc ID       =  [u]\nUID           =  [u]\nMID           =  [u]\nWord Count    =  [b]\n";
795    u_int smboffset;
796
797    ndo->ndo_protocol = "smb";
798
799    request = (GET_U_1(buf + 9) & 0x80) ? 0 : 1;
800    startbuf = buf;
801
802    command = GET_U_1(buf + 4);
803
804    fn = smbfind(command, smb_fns);
805
806    if (ndo->ndo_vflag > 1)
807	ND_PRINT("\n");
808
809    ND_PRINT("SMB PACKET: %s (%s)", fn->name, request ? "REQUEST" : "REPLY");
810
811    if (ndo->ndo_vflag < 2)
812	return;
813
814    ND_PRINT("\n");
815    flags2 = GET_LE_U_2(buf + 10);
816    unicodestr = flags2 & 0x8000;
817    nterrcodes = flags2 & 0x4000;
818
819    /* print out the header */
820    smb_fdata(ndo, buf, fmt_smbheader, buf + 33, unicodestr);
821
822    if (nterrcodes) {
823	nterror = GET_LE_U_4(buf + 5);
824	if (nterror)
825	    ND_PRINT("NTError = %s\n", nt_errstr(nterror));
826    } else {
827	if (GET_U_1(buf + 5))
828	    ND_PRINT("SMBError = %s\n", smb_errstr(GET_U_1(buf + 5),
829                                                   GET_LE_U_2(buf + 7)));
830    }
831
832    smboffset = 32;
833
834    for (;;) {
835	const char *f1, *f2;
836	int wct;
837	u_int bcc;
838	u_int newsmboffset;
839
840	words = buf + smboffset;
841	wct = GET_U_1(words);
842	data = words + 1 + wct * 2;
843	maxwords = ND_MIN(data, maxbuf);
844
845	if (request) {
846	    f1 = fn->descript.req_f1;
847	    f2 = fn->descript.req_f2;
848	} else {
849	    f1 = fn->descript.rep_f1;
850	    f2 = fn->descript.rep_f2;
851	}
852
853	smb_reset();
854	if (fn->descript.fn)
855	    (*fn->descript.fn)(ndo, words, data, buf, maxbuf);
856	else {
857	    if (wct) {
858		if (f1)
859		    smb_fdata(ndo, words + 1, f1, words + 1 + wct * 2, unicodestr);
860		else {
861		    u_int i;
862		    u_int v;
863
864		    for (i = 0; words + 1 + 2 * i < maxwords; i++) {
865			v = GET_LE_U_2(words + 1 + 2 * i);
866			ND_PRINT("smb_vwv[%u]=%u (0x%X)\n", i, v, v);
867		    }
868		}
869	    }
870
871	    bcc = GET_LE_U_2(data);
872	    ND_PRINT("smb_bcc=%u\n", bcc);
873	    if (f2) {
874		if (bcc > 0)
875		    smb_fdata(ndo, data + 2, f2, data + 2 + bcc, unicodestr);
876	    } else {
877		if (bcc > 0) {
878		    ND_PRINT("smb_buf[]=\n");
879		    smb_data_print(ndo, data + 2, ND_MIN(bcc, ND_BYTES_BETWEEN(maxbuf, data + 2)));
880		}
881	    }
882	}
883
884	if ((fn->flags & FLG_CHAIN) == 0)
885	    break;
886	if (wct == 0)
887	    break;
888	command = GET_U_1(words + 1);
889	if (command == 0xFF)
890	    break;
891	newsmboffset = GET_LE_U_2(words + 3);
892
893	fn = smbfind(command, smb_fns);
894
895	ND_PRINT("\nSMB PACKET: %s (%s) (CHAINED)\n",
896	    fn->name, request ? "REQUEST" : "REPLY");
897	if (newsmboffset <= smboffset) {
898	    ND_PRINT("Bad andX offset: %u <= %u\n", newsmboffset, smboffset);
899	    break;
900	}
901	smboffset = newsmboffset;
902    }
903}
904
905
906/*
907 * print a NBT packet received across tcp on port 139
908 */
909void
910nbt_tcp_print(netdissect_options *ndo,
911              const u_char *data, u_int length)
912{
913    u_int caplen;
914    u_int type;
915    u_int nbt_len;
916    const u_char *maxbuf;
917
918    ndo->ndo_protocol = "nbt_tcp";
919    if (length < 4)
920	goto trunc;
921    if (ndo->ndo_snapend < data)
922	goto trunc;
923    caplen = ND_BYTES_AVAILABLE_AFTER(data);
924    if (caplen < 4)
925	goto trunc;
926    maxbuf = data + caplen;
927    type = GET_U_1(data);
928    nbt_len = GET_BE_U_2(data + 2);
929    length -= 4;
930    caplen -= 4;
931
932    startbuf = data;
933
934    if (ndo->ndo_vflag < 2) {
935	ND_PRINT(" NBT Session Packet: ");
936	switch (type) {
937	case 0x00:
938	    ND_PRINT("Session Message");
939	    break;
940
941	case 0x81:
942	    ND_PRINT("Session Request");
943	    break;
944
945	case 0x82:
946	    ND_PRINT("Session Granted");
947	    break;
948
949	case 0x83:
950	  {
951	    u_int ecode;
952
953	    if (nbt_len < 4)
954		goto trunc;
955	    if (length < 4)
956		goto trunc;
957	    if (caplen < 4)
958		goto trunc;
959	    ecode = GET_U_1(data + 4);
960
961	    ND_PRINT("Session Reject, ");
962	    switch (ecode) {
963	    case 0x80:
964		ND_PRINT("Not listening on called name");
965		break;
966	    case 0x81:
967		ND_PRINT("Not listening for calling name");
968		break;
969	    case 0x82:
970		ND_PRINT("Called name not present");
971		break;
972	    case 0x83:
973		ND_PRINT("Called name present, but insufficient resources");
974		break;
975	    default:
976		ND_PRINT("Unspecified error 0x%X", ecode);
977		break;
978	    }
979	  }
980	    break;
981
982	case 0x85:
983	    ND_PRINT("Session Keepalive");
984	    break;
985
986	default:
987	    data = smb_fdata(ndo, data, "Unknown packet type [rB]", maxbuf, 0);
988	    break;
989	}
990    } else {
991	ND_PRINT("\n>>> NBT Session Packet\n");
992	switch (type) {
993	case 0x00:
994	    data = smb_fdata(ndo, data, "[P1]NBT Session Message\nFlags=[B]\nLength=[ru]\n",
995		data + 4, 0);
996	    if (data == NULL)
997		break;
998	    if (nbt_len >= 4 && caplen >= 4 && memcmp(data,"\377SMB",4) == 0) {
999		if (nbt_len > caplen) {
1000		    if (nbt_len > length)
1001			ND_PRINT("WARNING: Packet is continued in later TCP segments\n");
1002		    else
1003			ND_PRINT("WARNING: Short packet. Try increasing the snap length by %u\n",
1004			    nbt_len - caplen);
1005		}
1006		print_smb(ndo, data, maxbuf > data + nbt_len ? data + nbt_len : maxbuf);
1007	    } else
1008		ND_PRINT("Session packet:(raw data or continuation?)\n");
1009	    break;
1010
1011	case 0x81:
1012	    data = smb_fdata(ndo, data,
1013		"[P1]NBT Session Request\nFlags=[B]\nLength=[ru]\nDestination=[n1]\nSource=[n1]\n",
1014		maxbuf, 0);
1015	    break;
1016
1017	case 0x82:
1018	    data = smb_fdata(ndo, data, "[P1]NBT Session Granted\nFlags=[B]\nLength=[ru]\n", maxbuf, 0);
1019	    break;
1020
1021	case 0x83:
1022	  {
1023	    const u_char *origdata;
1024	    u_int ecode;
1025
1026	    origdata = data;
1027	    data = smb_fdata(ndo, data, "[P1]NBT SessionReject\nFlags=[B]\nLength=[ru]\nReason=[B]\n",
1028		maxbuf, 0);
1029	    if (data == NULL)
1030		break;
1031	    if (nbt_len >= 1 && caplen >= 1) {
1032		ecode = GET_U_1(origdata + 4);
1033		switch (ecode) {
1034		case 0x80:
1035		    ND_PRINT("Not listening on called name\n");
1036		    break;
1037		case 0x81:
1038		    ND_PRINT("Not listening for calling name\n");
1039		    break;
1040		case 0x82:
1041		    ND_PRINT("Called name not present\n");
1042		    break;
1043		case 0x83:
1044		    ND_PRINT("Called name present, but insufficient resources\n");
1045		    break;
1046		default:
1047		    ND_PRINT("Unspecified error 0x%X\n", ecode);
1048		    break;
1049		}
1050	    }
1051	  }
1052	    break;
1053
1054	case 0x85:
1055	    data = smb_fdata(ndo, data, "[P1]NBT Session Keepalive\nFlags=[B]\nLength=[ru]\n", maxbuf, 0);
1056	    break;
1057
1058	default:
1059	    data = smb_fdata(ndo, data, "NBT - Unknown packet type\nType=[B]\n", maxbuf, 0);
1060	    break;
1061	}
1062    }
1063    return;
1064trunc:
1065    nd_print_trunc(ndo);
1066}
1067
1068static const struct tok opcode_str[] = {
1069	{ 0,  "QUERY"                   },
1070	{ 5,  "REGISTRATION"            },
1071	{ 6,  "RELEASE"                 },
1072	{ 7,  "WACK"                    },
1073	{ 8,  "REFRESH(8)"              },
1074	{ 9,  "REFRESH"                 },
1075	{ 15, "MULTIHOMED REGISTRATION" },
1076	{ 0, NULL }
1077};
1078
1079/*
1080 * print a NBT packet received across udp on port 137
1081 */
1082void
1083nbt_udp137_print(netdissect_options *ndo,
1084                 const u_char *data, u_int length)
1085{
1086    const u_char *maxbuf = data + length;
1087    u_int name_trn_id, response, opcode, nm_flags, rcode;
1088    u_int qdcount, ancount, nscount, arcount;
1089    const u_char *p;
1090    u_int total, i;
1091
1092    ndo->ndo_protocol = "nbt_udp137";
1093    name_trn_id = GET_BE_U_2(data);
1094    response = (GET_U_1(data + 2) >> 7);
1095    opcode = (GET_U_1(data + 2) >> 3) & 0xF;
1096    nm_flags = ((GET_U_1(data + 2) & 0x7) << 4) + (GET_U_1(data + 3) >> 4);
1097    rcode = GET_U_1(data + 3) & 0xF;
1098    qdcount = GET_BE_U_2(data + 4);
1099    ancount = GET_BE_U_2(data + 6);
1100    nscount = GET_BE_U_2(data + 8);
1101    arcount = GET_BE_U_2(data + 10);
1102    startbuf = data;
1103
1104    if (maxbuf <= data)
1105	return;
1106
1107    if (ndo->ndo_vflag > 1)
1108	ND_PRINT("\n>>> ");
1109
1110    ND_PRINT("NBT UDP PACKET(137): %s", tok2str(opcode_str, "OPUNKNOWN", opcode));
1111    if (response) {
1112        ND_PRINT("; %s", rcode ? "NEGATIVE" : "POSITIVE");
1113    }
1114    ND_PRINT("; %s; %s", response ? "RESPONSE" : "REQUEST",
1115              (nm_flags & 1) ? "BROADCAST" : "UNICAST");
1116
1117    if (ndo->ndo_vflag < 2)
1118	return;
1119
1120    ND_PRINT("\nTrnID=0x%X\nOpCode=%u\nNmFlags=0x%X\nRcode=%u\nQueryCount=%u\nAnswerCount=%u\nAuthorityCount=%u\nAddressRecCount=%u\n",
1121	name_trn_id, opcode, nm_flags, rcode, qdcount, ancount, nscount,
1122	arcount);
1123
1124    p = data + 12;
1125
1126    total = ancount + nscount + arcount;
1127
1128    if (qdcount > 100 || total > 100) {
1129	ND_PRINT("Corrupt packet??\n");
1130	return;
1131    }
1132
1133    if (qdcount) {
1134	ND_PRINT("QuestionRecords:\n");
1135	for (i = 0; i < qdcount; i++) {
1136	    p = smb_fdata(ndo, p,
1137		"|Name=[n1]\nQuestionType=[rw]\nQuestionClass=[rw]\n#",
1138		maxbuf, 0);
1139	    if (p == NULL)
1140		goto out;
1141	}
1142    }
1143
1144    if (total) {
1145	ND_PRINT("\nResourceRecords:\n");
1146	for (i = 0; i < total; i++) {
1147	    u_int rdlen;
1148	    u_int restype;
1149
1150	    p = smb_fdata(ndo, p, "Name=[n1]\n#", maxbuf, 0);
1151	    if (p == NULL)
1152		goto out;
1153	    restype = GET_BE_U_2(p);
1154	    p = smb_fdata(ndo, p, "ResType=[rw]\nResClass=[rw]\nTTL=[rU]\n", p + 8, 0);
1155	    if (p == NULL)
1156		goto out;
1157	    rdlen = GET_BE_U_2(p);
1158	    ND_PRINT("ResourceLength=%u\nResourceData=\n", rdlen);
1159	    p += 2;
1160	    if (rdlen == 6) {
1161		p = smb_fdata(ndo, p, "AddrType=[rw]\nAddress=[b.b.b.b]\n", p + rdlen, 0);
1162		if (p == NULL)
1163		    goto out;
1164	    } else {
1165		if (restype == 0x21) {
1166		    u_int numnames;
1167
1168		    numnames = GET_U_1(p);
1169		    p = smb_fdata(ndo, p, "NumNames=[B]\n", p + 1, 0);
1170		    if (p == NULL)
1171			goto out;
1172		    while (numnames) {
1173			p = smb_fdata(ndo, p, "Name=[n2]\t#", maxbuf, 0);
1174			if (p == NULL)
1175			    goto out;
1176			ND_TCHECK_1(p);
1177			if (p >= maxbuf)
1178			    goto out;
1179			if (GET_U_1(p) & 0x80)
1180			    ND_PRINT("<GROUP> ");
1181			switch (GET_U_1(p) & 0x60) {
1182			case 0x00: ND_PRINT("B "); break;
1183			case 0x20: ND_PRINT("P "); break;
1184			case 0x40: ND_PRINT("M "); break;
1185			case 0x60: ND_PRINT("_ "); break;
1186			}
1187			if (GET_U_1(p) & 0x10)
1188			    ND_PRINT("<DEREGISTERING> ");
1189			if (GET_U_1(p) & 0x08)
1190			    ND_PRINT("<CONFLICT> ");
1191			if (GET_U_1(p) & 0x04)
1192			    ND_PRINT("<ACTIVE> ");
1193			if (GET_U_1(p) & 0x02)
1194			    ND_PRINT("<PERMANENT> ");
1195			ND_PRINT("\n");
1196			p += 2;
1197			numnames--;
1198		    }
1199		} else {
1200		    if (p >= maxbuf)
1201		        goto out;
1202		    smb_data_print(ndo, p, ND_MIN(rdlen, length - ND_BYTES_BETWEEN(p, data)));
1203		    p += rdlen;
1204		}
1205	    }
1206	}
1207    }
1208
1209    if (p < maxbuf)
1210	smb_fdata(ndo, p, "AdditionalData:\n", maxbuf, 0);
1211
1212out:
1213    return;
1214trunc:
1215    nd_print_trunc(ndo);
1216}
1217
1218/*
1219 * Print an SMB-over-TCP packet received across tcp on port 445
1220 */
1221void
1222smb_tcp_print(netdissect_options *ndo,
1223              const u_char * data, u_int length)
1224{
1225    u_int caplen;
1226    u_int smb_len;
1227    const u_char *maxbuf;
1228
1229    ndo->ndo_protocol = "smb_tcp";
1230    if (length < 4)
1231	goto trunc;
1232    if (ndo->ndo_snapend < data)
1233	goto trunc;
1234    caplen = ND_BYTES_AVAILABLE_AFTER(data);
1235    if (caplen < 4)
1236	goto trunc;
1237    maxbuf = data + caplen;
1238    smb_len = GET_BE_U_3(data + 1);
1239    length -= 4;
1240    caplen -= 4;
1241
1242    startbuf = data;
1243    data += 4;
1244
1245    if (smb_len >= 4 && caplen >= 4 && memcmp(data,"\377SMB",4) == 0) {
1246	if (smb_len > caplen) {
1247	    if (smb_len > length)
1248		ND_PRINT(" WARNING: Packet is continued in later TCP segments\n");
1249	    else
1250		ND_PRINT(" WARNING: Short packet. Try increasing the snap length by %u\n",
1251		    smb_len - caplen);
1252	} else
1253	    ND_PRINT(" ");
1254	print_smb(ndo, data, maxbuf > data + smb_len ? data + smb_len : maxbuf);
1255    } else
1256	ND_PRINT(" SMB-over-TCP packet:(raw data or continuation?)\n");
1257    return;
1258trunc:
1259    nd_print_trunc(ndo);
1260}
1261
1262/*
1263 * print a NBT packet received across udp on port 138
1264 */
1265void
1266nbt_udp138_print(netdissect_options *ndo,
1267                 const u_char *data, u_int length)
1268{
1269    const u_char *maxbuf = data + length;
1270
1271    ndo->ndo_protocol = "nbt_udp138";
1272    if (maxbuf > ndo->ndo_snapend)
1273	maxbuf = ndo->ndo_snapend;
1274    if (maxbuf <= data)
1275	return;
1276    startbuf = data;
1277
1278    if (ndo->ndo_vflag < 2) {
1279	ND_PRINT("NBT UDP PACKET(138)");
1280	return;
1281    }
1282
1283    data = smb_fdata(ndo, data,
1284	"\n>>> NBT UDP PACKET(138) Res=[rw] ID=[rw] IP=[b.b.b.b] Port=[ru] Length=[ru] Res2=[rw]\nSourceName=[n1]\nDestName=[n1]\n#",
1285	maxbuf, 0);
1286
1287    if (data != NULL) {
1288	/* If there isn't enough data for "\377SMB", don't check for it. */
1289	if ((data + 3) >= maxbuf)
1290	    goto out;
1291
1292	if (memcmp(data, "\377SMB",4) == 0)
1293	    print_smb(ndo, data, maxbuf);
1294    }
1295out:
1296    return;
1297}
1298
1299
1300/*
1301   print netbeui frames
1302*/
1303static struct nbf_strings {
1304	const char	*name;
1305	const char	*nonverbose;
1306	const char	*verbose;
1307} nbf_strings[0x20] = {
1308	{ "Add Group Name Query", ", [P23]Name to add=[n2]#",
1309	  "[P5]ResponseCorrelator=[w]\n[P16]Name to add=[n2]\n" },
1310	{ "Add Name Query", ", [P23]Name to add=[n2]#",
1311	  "[P5]ResponseCorrelator=[w]\n[P16]Name to add=[n2]\n" },
1312	{ "Name In Conflict", NULL, NULL },
1313	{ "Status Query", NULL, NULL },
1314	{ NULL, NULL, NULL },	/* not used */
1315	{ NULL, NULL, NULL },	/* not used */
1316	{ NULL, NULL, NULL },	/* not used */
1317	{ "Terminate Trace", NULL, NULL },
1318	{ "Datagram", NULL,
1319	  "[P7]Destination=[n2]\nSource=[n2]\n" },
1320	{ "Broadcast Datagram", NULL,
1321	  "[P7]Destination=[n2]\nSource=[n2]\n" },
1322	{ "Name Query", ", [P7]Name=[n2]#",
1323	  "[P1]SessionNumber=[B]\nNameType=[B][P2]\nResponseCorrelator=[w]\nName=[n2]\nName of sender=[n2]\n" },
1324	{ NULL, NULL, NULL },	/* not used */
1325	{ NULL, NULL, NULL },	/* not used */
1326	{ "Add Name Response", ", [P1]GroupName=[w] [P4]Destination=[n2] Source=[n2]#",
1327	  "AddNameInProcess=[B]\nGroupName=[w]\nTransmitCorrelator=[w][P2]\nDestination=[n2]\nSource=[n2]\n" },
1328	{ "Name Recognized", NULL,
1329	  "[P1]Data2=[w]\nTransmitCorrelator=[w]\nResponseCorelator=[w]\nDestination=[n2]\nSource=[n2]\n" },
1330	{ "Status Response", NULL, NULL },
1331	{ NULL, NULL, NULL },	/* not used */
1332	{ NULL, NULL, NULL },	/* not used */
1333	{ NULL, NULL, NULL },	/* not used */
1334	{ "Terminate Trace", NULL, NULL },
1335	{ "Data Ack", NULL,
1336	  "[P3]TransmitCorrelator=[w][P2]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1337	{ "Data First/Middle", NULL,
1338	  "Flags=[{RECEIVE_CONTINUE|NO_ACK||PIGGYBACK_ACK_INCLUDED|}]\nResyncIndicator=[w][P2]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1339	{ "Data Only/Last", NULL,
1340	  "Flags=[{|NO_ACK|PIGGYBACK_ACK_ALLOWED|PIGGYBACK_ACK_INCLUDED|}]\nResyncIndicator=[w][P2]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1341	{ "Session Confirm", NULL,
1342	  "Data1=[B]\nData2=[w]\nTransmitCorrelator=[w]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1343	{ "Session End", NULL,
1344	  "[P1]Data2=[w][P4]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1345	{ "Session Initialize", NULL,
1346	  "Data1=[B]\nData2=[w]\nTransmitCorrelator=[w]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1347	{ "No Receive", NULL,
1348	  "Flags=[{|SEND_NO_ACK}]\nDataBytesAccepted=[b][P4]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1349	{ "Receive Outstanding", NULL,
1350	  "[P1]DataBytesAccepted=[b][P4]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1351	{ "Receive Continue", NULL,
1352	  "[P2]TransmitCorrelator=[w]\n[P2]RemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
1353	{ NULL, NULL, NULL },	/* not used */
1354	{ NULL, NULL, NULL },	/* not used */
1355	{ "Session Alive", NULL, NULL }
1356};
1357
1358void
1359netbeui_print(netdissect_options *ndo,
1360              u_short control, const u_char *data, u_int length)
1361{
1362    const u_char *maxbuf = data + length;
1363    u_int len;
1364    u_int command;
1365    const u_char *data2;
1366    int is_truncated = 0;
1367
1368    ndo->ndo_protocol = "netbeui";
1369    if (maxbuf > ndo->ndo_snapend)
1370	maxbuf = ndo->ndo_snapend;
1371    len = GET_LE_U_2(data);
1372    command = GET_U_1(data + 4);
1373    data2 = data + len;
1374    if (data2 >= maxbuf) {
1375	data2 = maxbuf;
1376	is_truncated = 1;
1377    }
1378
1379    startbuf = data;
1380
1381    if (ndo->ndo_vflag < 2) {
1382	ND_PRINT("NBF Packet: ");
1383	data = smb_fdata(ndo, data, "[P5]#", maxbuf, 0);
1384    } else {
1385	ND_PRINT("\n>>> NBF Packet\nType=0x%X ", control);
1386	data = smb_fdata(ndo, data, "Length=[u] Signature=[w] Command=[B]\n#", maxbuf, 0);
1387    }
1388    if (data == NULL)
1389	goto out;
1390
1391    if (command > 0x1f || nbf_strings[command].name == NULL) {
1392	if (ndo->ndo_vflag < 2)
1393	    data = smb_fdata(ndo, data, "Unknown NBF Command#", data2, 0);
1394	else
1395	    data = smb_fdata(ndo, data, "Unknown NBF Command\n", data2, 0);
1396    } else {
1397	if (ndo->ndo_vflag < 2) {
1398	    ND_PRINT("%s", nbf_strings[command].name);
1399	    if (nbf_strings[command].nonverbose != NULL)
1400		data = smb_fdata(ndo, data, nbf_strings[command].nonverbose, data2, 0);
1401	} else {
1402	    ND_PRINT("%s:\n", nbf_strings[command].name);
1403	    if (nbf_strings[command].verbose != NULL)
1404		data = smb_fdata(ndo, data, nbf_strings[command].verbose, data2, 0);
1405	    else
1406		ND_PRINT("\n");
1407	}
1408    }
1409
1410    if (ndo->ndo_vflag < 2)
1411	return;
1412
1413    if (data == NULL)
1414	goto out;
1415
1416    if (is_truncated) {
1417	/* data2 was past the end of the buffer */
1418	goto out;
1419    }
1420
1421    /* If this isn't a command that would contain an SMB message, quit. */
1422    if (command != 0x08 && command != 0x09 && command != 0x15 &&
1423        command != 0x16)
1424	goto out;
1425
1426    /* If there isn't enough data for "\377SMB", don't look for it. */
1427    if ((data2 + 3) >= maxbuf)
1428	goto out;
1429
1430    if (memcmp(data2, "\377SMB",4) == 0)
1431	print_smb(ndo, data2, maxbuf);
1432    else {
1433	u_int i;
1434	for (i = 0; i < 128; i++) {
1435	    if ((data2 + i + 3) >= maxbuf)
1436		break;
1437	    if (memcmp(data2 + i, "\377SMB", 4) == 0) {
1438		ND_PRINT("found SMB packet at %u\n", i);
1439		print_smb(ndo, data2 + i, maxbuf);
1440		break;
1441	    }
1442	}
1443    }
1444
1445out:
1446    return;
1447}
1448
1449
1450/*
1451 * print IPX-Netbios frames
1452 */
1453void
1454ipx_netbios_print(netdissect_options *ndo,
1455                  const u_char *data, u_int length)
1456{
1457    /*
1458     * this is a hack till I work out how to parse the rest of the
1459     * NetBIOS-over-IPX stuff
1460     */
1461    u_int i;
1462    const u_char *maxbuf;
1463
1464    ndo->ndo_protocol = "ipx_netbios";
1465    maxbuf = data + length;
1466    /* Don't go past the end of the captured data in the packet. */
1467    if (maxbuf > ndo->ndo_snapend)
1468	maxbuf = ndo->ndo_snapend;
1469    startbuf = data;
1470    for (i = 0; i < 128; i++) {
1471	if ((data + i + 4) > maxbuf)
1472	    break;
1473	if (memcmp(data + i, "\377SMB", 4) == 0) {
1474	    smb_fdata(ndo, data, "\n>>> IPX transport ", data + i, 0);
1475	    print_smb(ndo, data + i, maxbuf);
1476	    break;
1477	}
1478    }
1479    if (i == 128)
1480	smb_fdata(ndo, data, "\n>>> Unknown IPX ", maxbuf, 0);
1481}
1482