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