smbutil.c revision 127668
197403Sobrien/*
297403Sobrien * Copyright (C) Andrew Tridgell 1995-1999
397403Sobrien *
497403Sobrien * This software may be distributed either under the terms of the
597403Sobrien * BSD-style license that accompanies tcpdump or the GNU GPL version 2
697403Sobrien * or later
797403Sobrien */
897403Sobrien
997403Sobrien#ifdef HAVE_CONFIG_H
1097403Sobrien#include "config.h"
1197403Sobrien#endif
1297403Sobrien
1397403Sobrien#ifndef lint
1497403Sobrienstatic const char rcsid[] _U_ =
1597403Sobrien     "@(#) $Header: /tcpdump/master/tcpdump/smbutil.c,v 1.26.2.2 2003/11/16 08:51:56 guy Exp $";
1697403Sobrien#endif
1797403Sobrien
18169691Skan#include <tcpdump-stdinc.h>
1997403Sobrien
2097403Sobrien#include <stdio.h>
2197403Sobrien#include <stdlib.h>
2297403Sobrien#include <string.h>
2397403Sobrien
2497403Sobrien#include "interface.h"
2597403Sobrien#include "extract.h"
2697403Sobrien#include "smb.h"
2797403Sobrien
2897403Sobrienextern const u_char *startbuf;
2997403Sobrien
3097403Sobrien/*
3197403Sobrien * interpret a 32 bit dos packed date/time to some parameters
3297403Sobrien */
3397403Sobrienstatic void
3497403Sobrieninterpret_dos_date(u_int32_t date, struct tm *tp)
3597403Sobrien{
3697403Sobrien    u_int32_t p0, p1, p2, p3;
3797403Sobrien
3897403Sobrien    p0 = date & 0xFF;
3997403Sobrien    p1 = ((date & 0xFF00) >> 8) & 0xFF;
4097403Sobrien    p2 = ((date & 0xFF0000) >> 16) & 0xFF;
4197403Sobrien    p3 = ((date & 0xFF000000) >> 24) & 0xFF;
4297403Sobrien
4397403Sobrien    tp->tm_sec = 2 * (p0 & 0x1F);
4497403Sobrien    tp->tm_min = ((p0 >> 5) & 0xFF) + ((p1 & 0x7) << 3);
4597403Sobrien    tp->tm_hour = (p1 >> 3) & 0xFF;
4697403Sobrien    tp->tm_mday = (p2 & 0x1F);
4797403Sobrien    tp->tm_mon = ((p2 >> 5) & 0xFF) + ((p3 & 0x1) << 3) - 1;
4897403Sobrien    tp->tm_year = ((p3 >> 1) & 0xFF) + 80;
4997403Sobrien}
5097403Sobrien
5197403Sobrien/*
5297403Sobrien * common portion:
5397403Sobrien * create a unix date from a dos date
5497403Sobrien */
5597403Sobrienstatic time_t
56132720Skanint_unix_date(u_int32_t dos_date)
57132720Skan{
5897403Sobrien    struct tm t;
5997403Sobrien
6097403Sobrien    if (dos_date == 0)
6197403Sobrien	return(0);
6297403Sobrien
6397403Sobrien    interpret_dos_date(dos_date, &t);
6497403Sobrien    t.tm_wday = 1;
65132720Skan    t.tm_yday = 1;
66    t.tm_isdst = 0;
67
68    return (mktime(&t));
69}
70
71/*
72 * create a unix date from a dos date
73 * in network byte order
74 */
75static time_t
76make_unix_date(const u_char *date_ptr)
77{
78    u_int32_t dos_date = 0;
79
80    dos_date = EXTRACT_LE_32BITS(date_ptr);
81
82    return int_unix_date(dos_date);
83}
84
85/*
86 * create a unix date from a dos date
87 * in halfword-swapped network byte order!
88 */
89static time_t
90make_unix_date2(const u_char *date_ptr)
91{
92    u_int32_t x, x2;
93
94    x = EXTRACT_LE_32BITS(date_ptr);
95    x2 = ((x & 0xFFFF) << 16) | ((x & 0xFFFF0000) >> 16);
96    return int_unix_date(x2);
97}
98
99/*
100 * interpret an 8 byte "filetime" structure to a time_t
101 * It's originally in "100ns units since jan 1st 1601"
102 */
103static time_t
104interpret_long_date(const u_char *p)
105{
106    double d;
107    time_t ret;
108
109    TCHECK2(p[4], 4);
110
111    /* this gives us seconds since jan 1st 1601 (approx) */
112    d = (EXTRACT_LE_32BITS(p + 4) * 256.0 + p[3]) * (1.0e-7 * (1 << 24));
113
114    /* now adjust by 369 years to make the secs since 1970 */
115    d -= 369.0 * 365.25 * 24 * 60 * 60;
116
117    /* and a fudge factor as we got it wrong by a few days */
118    d += (3 * 24 * 60 * 60 + 6 * 60 * 60 + 2);
119
120    if (d < 0)
121	return(0);
122
123    ret = (time_t)d;
124
125    return(ret);
126trunc:
127    return(0);
128}
129
130/*
131 * interpret the weird netbios "name". Return the name type, or -1 if
132 * we run past the end of the buffer
133 */
134static int
135name_interpret(const u_char *in, const u_char *maxbuf, char *out)
136{
137    int ret;
138    int len;
139
140    if (in >= maxbuf)
141	return(-1);	/* name goes past the end of the buffer */
142    TCHECK2(*in, 1);
143    len = (*in++) / 2;
144
145    *out=0;
146
147    if (len > 30 || len < 1)
148	return(0);
149
150    while (len--) {
151	TCHECK2(*in, 2);
152	if (in + 1 >= maxbuf)
153	    return(-1);	/* name goes past the end of the buffer */
154	if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
155	    *out = 0;
156	    return(0);
157	}
158	*out = ((in[0] - 'A') << 4) + (in[1] - 'A');
159	in += 2;
160	out++;
161    }
162    *out = 0;
163    ret = out[-1];
164
165    return(ret);
166
167trunc:
168    return(-1);
169}
170
171/*
172 * find a pointer to a netbios name
173 */
174static const u_char *
175name_ptr(const u_char *buf, int ofs, const u_char *maxbuf)
176{
177    const u_char *p;
178    u_char c;
179
180    p = buf + ofs;
181    if (p >= maxbuf)
182	return(NULL);	/* name goes past the end of the buffer */
183    TCHECK2(*p, 1);
184
185    c = *p;
186
187    /* XXX - this should use the same code that the DNS dissector does */
188    if ((c & 0xC0) == 0xC0) {
189	u_int16_t l = EXTRACT_16BITS(buf + ofs) & 0x3FFF;
190	if (l == 0) {
191	    /* We have a pointer that points to itself. */
192	    return(NULL);
193	}
194	p = buf + l;
195	if (p >= maxbuf)
196	    return(NULL);	/* name goes past the end of the buffer */
197	TCHECK2(*p, 1);
198	return(buf + l);
199    } else
200	return(buf + ofs);
201
202trunc:
203    return(NULL);	/* name goes past the end of the buffer */
204}
205
206/*
207 * extract a netbios name from a buf
208 */
209static int
210name_extract(const u_char *buf, int ofs, const u_char *maxbuf, char *name)
211{
212    const u_char *p = name_ptr(buf, ofs, maxbuf);
213    if (p == NULL)
214	return(-1);	/* error (probably name going past end of buffer) */
215    name[0] = '\0';
216    return(name_interpret(p, maxbuf, name));
217}
218
219
220/*
221 * return the total storage length of a mangled name
222 */
223static int
224name_len(const unsigned char *s, const unsigned char *maxbuf)
225{
226    const unsigned char *s0 = s;
227    unsigned char c;
228
229    if (s >= maxbuf)
230	return(-1);	/* name goes past the end of the buffer */
231    TCHECK2(*s, 1);
232    c = *s;
233    if ((c & 0xC0) == 0xC0)
234	return(2);
235    while (*s) {
236	if (s >= maxbuf)
237	    return(-1);	/* name goes past the end of the buffer */
238	TCHECK2(*s, 1);
239	s += (*s) + 1;
240    }
241    return(PTR_DIFF(s, s0) + 1);
242
243trunc:
244    return(-1);	/* name goes past the end of the buffer */
245}
246
247static void
248print_asc(const unsigned char *buf, int len)
249{
250    int i;
251    for (i = 0; i < len; i++)
252	safeputchar(buf[i]);
253}
254
255static const char *
256name_type_str(int name_type)
257{
258    const char *f = NULL;
259
260    switch (name_type) {
261    case 0:    f = "Workstation"; break;
262    case 0x03: f = "Client?"; break;
263    case 0x20: f = "Server"; break;
264    case 0x1d: f = "Master Browser"; break;
265    case 0x1b: f = "Domain Controller"; break;
266    case 0x1e: f = "Browser Server"; break;
267    default:   f = "Unknown"; break;
268    }
269    return(f);
270}
271
272void
273print_data(const unsigned char *buf, int len)
274{
275    int i = 0;
276
277    if (len <= 0)
278	return;
279    printf("[%03X] ", i);
280    for (i = 0; i < len; /*nothing*/) {
281	printf("%02X ", buf[i] & 0xff);
282	i++;
283	if (i%8 == 0)
284	    printf(" ");
285	if (i % 16 == 0) {
286	    print_asc(&buf[i - 16], 8);
287	    printf(" ");
288	    print_asc(&buf[i - 8], 8);
289	    printf("\n");
290	    if (i < len)
291		printf("[%03X] ", i);
292	}
293    }
294    if (i % 16) {
295	int n;
296
297	n = 16 - (i % 16);
298	printf(" ");
299	if (n>8)
300	    printf(" ");
301	while (n--)
302	    printf("   ");
303
304	n = SMBMIN(8, i % 16);
305	print_asc(&buf[i - (i % 16)], n);
306	printf(" ");
307	n = (i % 16) - n;
308	if (n > 0)
309	    print_asc(&buf[i - n], n);
310	printf("\n");
311    }
312}
313
314
315static void
316write_bits(unsigned int val, const char *fmt)
317{
318    const char *p = fmt;
319    int i = 0;
320
321    while ((p = strchr(fmt, '|'))) {
322	size_t l = PTR_DIFF(p, fmt);
323	if (l && (val & (1 << i)))
324	    printf("%.*s ", (int)l, fmt);
325	fmt = p + 1;
326	i++;
327    }
328}
329
330/* convert a UCS2 string into iso-8859-1 string */
331static const char *
332unistr(const u_char *s, int *len)
333{
334    static char buf[1000];
335    int l=0;
336    static int use_unicode = -1;
337
338    if (use_unicode == -1) {
339	char *p = getenv("USE_UNICODE");
340	if (p && (atoi(p) == 1))
341	    use_unicode = 1;
342	else
343	    use_unicode = 0;
344    }
345
346    /* maybe it isn't unicode - a cheap trick */
347    if (!use_unicode || (s[0] && s[1])) {
348	*len = strlen((const char *)s) + 1;
349	return (const char *)s;
350    }
351
352    *len = 0;
353
354    if (s[0] == 0 && s[1] != 0) {
355	s++;
356	*len = 1;
357    }
358
359    while (l < (int)(sizeof(buf) - 1) && s[0] && s[1] == 0) {
360	buf[l] = s[0];
361	s += 2;
362	l++;
363	*len += 2;
364    }
365    buf[l] = 0;
366    *len += 2;
367    return buf;
368}
369
370static const u_char *
371smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf)
372{
373    int reverse = 0;
374    const char *attrib_fmt = "READONLY|HIDDEN|SYSTEM|VOLUME|DIR|ARCHIVE|";
375    int len;
376
377    while (*fmt && buf<maxbuf) {
378	switch (*fmt) {
379	case 'a':
380	    write_bits(buf[0], attrib_fmt);
381	    buf++;
382	    fmt++;
383	    break;
384
385	case 'A':
386	    write_bits(EXTRACT_LE_16BITS(buf), attrib_fmt);
387	    buf += 2;
388	    fmt++;
389	    break;
390
391	case '{':
392	  {
393	    char bitfmt[128];
394	    char *p;
395	    int l;
396
397	    p = strchr(++fmt, '}');
398	    l = PTR_DIFF(p, fmt);
399
400	    if ((unsigned int)l > sizeof(bitfmt) - 1)
401		    l = sizeof(bitfmt)-1;
402
403	    strncpy(bitfmt, fmt, l);
404	    bitfmt[l] = '\0';
405	    fmt = p + 1;
406	    write_bits(buf[0], bitfmt);
407	    buf++;
408	    break;
409	  }
410
411	case 'P':
412	  {
413	    int l = atoi(fmt + 1);
414	    buf += l;
415	    fmt++;
416	    while (isdigit((unsigned char)*fmt))
417		fmt++;
418	    break;
419	  }
420	case 'r':
421	    reverse = !reverse;
422	    fmt++;
423	    break;
424	case 'D':
425	  {
426	    unsigned int x;
427
428	    TCHECK2(buf[0], 4);
429	    x = reverse ? EXTRACT_32BITS(buf) : EXTRACT_LE_32BITS(buf);
430	    printf("%d (0x%x)", x, x);
431	    buf += 4;
432	    fmt++;
433	    break;
434	  }
435	case 'L':
436	  {
437	    unsigned int x1, x2;
438
439	    TCHECK2(buf[4], 4);
440	    x1 = reverse ? EXTRACT_32BITS(buf) :
441			   EXTRACT_LE_32BITS(buf);
442	    x2 = reverse ? EXTRACT_32BITS(buf + 4) :
443			   EXTRACT_LE_32BITS(buf + 4);
444	    if (x2)
445		printf("0x%08x:%08x", x2, x1);
446	    else
447		printf("%d (0x%08x%08x)", x1, x2, x1);
448	    buf += 8;
449	    fmt++;
450	    break;
451	  }
452	case 'd':
453	  {
454	    unsigned int x;
455	    TCHECK2(buf[0], 2);
456	    x = reverse ? EXTRACT_16BITS(buf) :
457			  EXTRACT_LE_16BITS(buf);
458	    printf("%d (0x%x)", x, x);
459	    buf += 2;
460	    fmt++;
461	    break;
462	  }
463	case 'W':
464	  {
465	    unsigned int x;
466	    TCHECK2(buf[0], 4);
467	    x = reverse ? EXTRACT_32BITS(buf) :
468			  EXTRACT_LE_32BITS(buf);
469	    printf("0x%X", x);
470	    buf += 4;
471	    fmt++;
472	    break;
473	  }
474	case 'w':
475	  {
476	    unsigned int x;
477	    TCHECK2(buf[0], 2);
478	    x = reverse ? EXTRACT_16BITS(buf) :
479			  EXTRACT_LE_16BITS(buf);
480	    printf("0x%X", x);
481	    buf += 2;
482	    fmt++;
483	    break;
484	  }
485	case 'B':
486	  {
487	    unsigned int x;
488	    TCHECK(buf[0]);
489	    x = buf[0];
490	    printf("0x%X", x);
491	    buf += 1;
492	    fmt++;
493	    break;
494	  }
495	case 'b':
496	  {
497	    unsigned int x;
498	    TCHECK(buf[0]);
499	    x = buf[0];
500	    printf("%u (0x%x)", x, x);
501	    buf += 1;
502	    fmt++;
503	    break;
504	  }
505	case 'S':
506	  {
507	    /*XXX unistr() */
508	    printf("%.*s", (int)PTR_DIFF(maxbuf, buf), unistr(buf, &len));
509	    buf += len;
510	    fmt++;
511	    break;
512	  }
513	case 'Z':
514	  {
515	    if (*buf != 4 && *buf != 2)
516		printf("Error! ASCIIZ buffer of type %u (safety=%lu)\n", *buf,
517		    (unsigned long)PTR_DIFF(maxbuf, buf));
518	    printf("%.*s", (int)PTR_DIFF(maxbuf, buf + 1),
519		unistr(buf + 1, &len));
520	    buf += len + 1;
521	    fmt++;
522	    break;
523	  }
524	case 's':
525	  {
526	    int l = atoi(fmt + 1);
527	    printf("%-*.*s", l, l, buf);
528	    buf += l;
529	    fmt++;
530	    while (isdigit((unsigned char)*fmt))
531		fmt++;
532	    break;
533	  }
534	case 'h':
535	  {
536	    int l = atoi(fmt + 1);
537	    while (l--)
538		printf("%02x", *buf++);
539	    fmt++;
540	    while (isdigit((unsigned char)*fmt))
541		fmt++;
542	    break;
543	  }
544	case 'n':
545	  {
546	    int t = atoi(fmt+1);
547	    char nbuf[255];
548	    int name_type;
549	    int len;
550
551	    switch (t) {
552	    case 1:
553		name_type = name_extract(startbuf, PTR_DIFF(buf, startbuf),
554		    maxbuf, nbuf);
555		if (name_type < 0)
556		    goto trunc;
557		len = name_len(buf, maxbuf);
558		if (len < 0)
559		    goto trunc;
560		buf += len;
561		printf("%-15.15s NameType=0x%02X (%s)", nbuf, name_type,
562		    name_type_str(name_type));
563		break;
564	    case 2:
565		name_type = buf[15];
566		printf("%-15.15s NameType=0x%02X (%s)", buf, name_type,
567		    name_type_str(name_type));
568		buf += 16;
569		break;
570	    }
571	    fmt++;
572	    while (isdigit((unsigned char)*fmt))
573		fmt++;
574	    break;
575	  }
576	case 'T':
577	  {
578	    time_t t;
579	    struct tm *lt;
580	    const char *tstring;
581	    u_int32_t x;
582	    x = EXTRACT_LE_32BITS(buf);
583
584	    switch (atoi(fmt + 1)) {
585	    case 1:
586		if (x == 0 || x == 0xFFFFFFFF)
587		    t = 0;
588		else
589		    t = make_unix_date(buf);
590		buf += 4;
591		break;
592	    case 2:
593		if (x == 0 || x == 0xFFFFFFFF)
594		    t = 0;
595		else
596		    t = make_unix_date2(buf);
597		buf += 4;
598		break;
599	    case 3:
600		t = interpret_long_date(buf);
601		buf += 8;
602		break;
603	    }
604	    if (t != 0) {
605		lt = localtime(&t);
606		if (lt != NULL)
607		    tstring = asctime(lt);
608		else
609		    tstring = "(Can't convert time)\n";
610	    } else
611		tstring = "NULL\n";
612	    printf("%s", tstring);
613	    fmt++;
614	    while (isdigit((unsigned char)*fmt))
615		fmt++;
616	    break;
617	  }
618	default:
619	    putchar(*fmt);
620	    fmt++;
621	    break;
622	}
623    }
624
625    if (buf >= maxbuf && *fmt)
626	printf("END OF BUFFER\n");
627
628    return(buf);
629
630trunc:
631    printf("\n");
632    printf("WARNING: Short packet. Try increasing the snap length\n");
633    return(NULL);
634}
635
636const u_char *
637smb_fdata(const u_char *buf, const char *fmt, const u_char *maxbuf)
638{
639    static int depth = 0;
640    char s[128];
641    char *p;
642
643    while (*fmt) {
644	switch (*fmt) {
645	case '*':
646	    fmt++;
647	    while (buf < maxbuf) {
648		const u_char *buf2;
649		depth++;
650		buf2 = smb_fdata(buf, fmt, maxbuf);
651		depth--;
652		if (buf2 == NULL)
653		    return(NULL);
654		if (buf2 == buf)
655		    return(buf);
656		buf = buf2;
657	    }
658	    return(buf);
659
660	case '|':
661	    fmt++;
662	    if (buf >= maxbuf)
663		return(buf);
664	    break;
665
666	case '%':
667	    fmt++;
668	    buf = maxbuf;
669	    break;
670
671	case '#':
672	    fmt++;
673	    return(buf);
674	    break;
675
676	case '[':
677	    fmt++;
678	    if (buf >= maxbuf)
679		return(buf);
680	    memset(s, 0, sizeof(s));
681	    p = strchr(fmt, ']');
682	    if ((size_t)(p - fmt + 1) > sizeof(s)) {
683		/* overrun */
684		return(buf);
685	    }
686	    strncpy(s, fmt, p - fmt);
687	    s[p - fmt] = '\0';
688	    fmt = p + 1;
689	    buf = smb_fdata1(buf, s, maxbuf);
690	    if (buf == NULL)
691		return(NULL);
692	    break;
693
694	default:
695	    putchar(*fmt);
696	    fmt++;
697	    fflush(stdout);
698	    break;
699	}
700    }
701    if (!depth && buf < maxbuf) {
702	size_t len = PTR_DIFF(maxbuf, buf);
703	printf("Data: (%lu bytes)\n", (unsigned long)len);
704	print_data(buf, len);
705	return(buf + len);
706    }
707    return(buf);
708}
709
710typedef struct {
711    const char *name;
712    int code;
713    const char *message;
714} err_code_struct;
715
716/* Dos Error Messages */
717static err_code_struct dos_msgs[] = {
718    { "ERRbadfunc", 1, "Invalid function." },
719    { "ERRbadfile", 2, "File not found." },
720    { "ERRbadpath", 3, "Directory invalid." },
721    { "ERRnofids", 4, "No file descriptors available" },
722    { "ERRnoaccess", 5, "Access denied." },
723    { "ERRbadfid", 6, "Invalid file handle." },
724    { "ERRbadmcb", 7, "Memory control blocks destroyed." },
725    { "ERRnomem", 8, "Insufficient server memory to perform the requested function." },
726    { "ERRbadmem", 9, "Invalid memory block address." },
727    { "ERRbadenv", 10, "Invalid environment." },
728    { "ERRbadformat", 11, "Invalid format." },
729    { "ERRbadaccess", 12, "Invalid open mode." },
730    { "ERRbaddata", 13, "Invalid data." },
731    { "ERR", 14, "reserved." },
732    { "ERRbaddrive", 15, "Invalid drive specified." },
733    { "ERRremcd", 16, "A Delete Directory request attempted  to  remove  the  server's  current directory." },
734    { "ERRdiffdevice", 17, "Not same device." },
735    { "ERRnofiles", 18, "A File Search command can find no more files matching the specified criteria." },
736    { "ERRbadshare", 32, "The sharing mode specified for an Open conflicts with existing  FIDs  on the file." },
737    { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an  invalid mode,   or an Unlock requested attempted to remove a lock held by another process." },
738    { "ERRfilexists", 80, "The file named in a Create Directory,  Make  New  File  or  Link  request already exists." },
739    { "ERRbadpipe", 230, "Pipe invalid." },
740    { "ERRpipebusy", 231, "All instances of the requested pipe are busy." },
741    { "ERRpipeclosing", 232, "Pipe close in progress." },
742    { "ERRnotconnected", 233, "No process on other end of pipe." },
743    { "ERRmoredata", 234, "There is more data to be returned." },
744    { NULL, -1, NULL }
745 };
746
747/* Server Error Messages */
748err_code_struct server_msgs[] = {
749    { "ERRerror", 1, "Non-specific error code." },
750    { "ERRbadpw", 2, "Bad password - name/password pair in a Tree Connect or Session Setup are invalid." },
751    { "ERRbadtype", 3, "reserved." },
752    { "ERRaccess", 4, "The requester does not have  the  necessary  access  rights  within  the specified  context for the requested function. The context is defined by the TID or the UID." },
753    { "ERRinvnid", 5, "The tree ID (TID) specified in a command was invalid." },
754    { "ERRinvnetname", 6, "Invalid network name in tree connect." },
755    { "ERRinvdevice", 7, "Invalid device - printer request made to non-printer connection or  non-printer request made to printer connection." },
756    { "ERRqfull", 49, "Print queue full (files) -- returned by open print file." },
757    { "ERRqtoobig", 50, "Print queue full -- no space." },
758    { "ERRqeof", 51, "EOF on print queue dump." },
759    { "ERRinvpfid", 52, "Invalid print file FID." },
760    { "ERRsmbcmd", 64, "The server did not recognize the command received." },
761    { "ERRsrverror", 65, "The server encountered an internal error,  e.g.,  system file unavailable." },
762    { "ERRfilespecs", 67, "The file handle (FID) and pathname parameters contained an invalid  combination of values." },
763    { "ERRreserved", 68, "reserved." },
764    { "ERRbadpermits", 69, "The access permissions specified for a file or directory are not a valid combination.  The server cannot set the requested attribute." },
765    { "ERRreserved", 70, "reserved." },
766    { "ERRsetattrmode", 71, "The attribute mode in the Set File Attribute request is invalid." },
767    { "ERRpaused", 81, "Server is paused." },
768    { "ERRmsgoff", 82, "Not receiving messages." },
769    { "ERRnoroom", 83, "No room to buffer message." },
770    { "ERRrmuns", 87, "Too many remote user names." },
771    { "ERRtimeout", 88, "Operation timed out." },
772    { "ERRnoresource", 89, "No resources currently available for request." },
773    { "ERRtoomanyuids", 90, "Too many UIDs active on this session." },
774    { "ERRbaduid", 91, "The UID is not known as a valid ID on this session." },
775    { "ERRusempx", 250, "Temp unable to support Raw,  use MPX mode." },
776    { "ERRusestd", 251, "Temp unable to support Raw,  use standard read/write." },
777    { "ERRcontmpx", 252, "Continue in MPX mode." },
778    { "ERRreserved", 253, "reserved." },
779    { "ERRreserved", 254, "reserved." },
780    { "ERRnosupport", 0xFFFF, "Function not supported." },
781    { NULL, -1, NULL }
782};
783
784/* Hard Error Messages */
785err_code_struct hard_msgs[] = {
786    { "ERRnowrite", 19, "Attempt to write on write-protected diskette." },
787    { "ERRbadunit", 20, "Unknown unit." },
788    { "ERRnotready", 21, "Drive not ready." },
789    { "ERRbadcmd", 22, "Unknown command." },
790    { "ERRdata", 23, "Data error (CRC)." },
791    { "ERRbadreq", 24, "Bad request structure length." },
792    { "ERRseek", 25 , "Seek error." },
793    { "ERRbadmedia", 26, "Unknown media type." },
794    { "ERRbadsector", 27, "Sector not found." },
795    { "ERRnopaper", 28, "Printer out of paper." },
796    { "ERRwrite", 29, "Write fault." },
797    { "ERRread", 30, "Read fault." },
798    { "ERRgeneral", 31, "General failure." },
799    { "ERRbadshare", 32, "A open conflicts with an existing open." },
800    { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode,  or an Unlock requested attempted to remove a lock held by another process." },
801    { "ERRwrongdisk", 34, "The wrong disk was found in a drive." },
802    { "ERRFCBUnavail", 35, "No FCBs are available to process request." },
803    { "ERRsharebufexc", 36, "A sharing buffer has been exceeded." },
804    { NULL, -1, NULL }
805};
806
807static struct {
808    int code;
809    const char *class;
810    err_code_struct *err_msgs;
811} err_classes[] = {
812    { 0, "SUCCESS", NULL },
813    { 0x01, "ERRDOS", dos_msgs },
814    { 0x02, "ERRSRV", server_msgs },
815    { 0x03, "ERRHRD", hard_msgs },
816    { 0x04, "ERRXOS", NULL },
817    { 0xE1, "ERRRMX1", NULL },
818    { 0xE2, "ERRRMX2", NULL },
819    { 0xE3, "ERRRMX3", NULL },
820    { 0xFF, "ERRCMD", NULL },
821    { -1, NULL, NULL }
822};
823
824/*
825 * return a SMB error string from a SMB buffer
826 */
827char *
828smb_errstr(int class, int num)
829{
830    static char ret[128];
831    int i, j;
832
833    ret[0] = 0;
834
835    for (i = 0; err_classes[i].class; i++)
836	if (err_classes[i].code == class) {
837	    if (err_classes[i].err_msgs) {
838		err_code_struct *err = err_classes[i].err_msgs;
839		for (j = 0; err[j].name; j++)
840		    if (num == err[j].code) {
841			snprintf(ret, sizeof(ret), "%s - %s (%s)",
842			    err_classes[i].class, err[j].name, err[j].message);
843			return ret;
844		    }
845	    }
846
847	    snprintf(ret, sizeof(ret), "%s - %d", err_classes[i].class, num);
848	    return ret;
849	}
850
851    snprintf(ret, sizeof(ret), "ERROR: Unknown error (%d,%d)", class, num);
852    return(ret);
853}
854