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