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