]> The Tcpdump Group git mirrors - tcpdump/blob - smbutil.c
14e600a56cd526d9e66d2317707b43b8d192c914
[tcpdump] / smbutil.c
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[] _U_ =
15 "@(#) $Header: /tcpdump/master/tcpdump/smbutil.c,v 1.32 2004-12-28 22:29:45 guy Exp $";
16 #endif
17
18 #include <tcpdump-stdinc.h>
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "interface.h"
25 #include "extract.h"
26 #include "smb.h"
27
28 extern const u_char *startbuf;
29
30 /*
31 * interpret a 32 bit dos packed date/time to some parameters
32 */
33 static void
34 interpret_dos_date(u_int32_t date, struct tm *tp)
35 {
36 u_int32_t p0, p1, p2, p3;
37
38 p0 = date & 0xFF;
39 p1 = ((date & 0xFF00) >> 8) & 0xFF;
40 p2 = ((date & 0xFF0000) >> 16) & 0xFF;
41 p3 = ((date & 0xFF000000) >> 24) & 0xFF;
42
43 tp->tm_sec = 2 * (p0 & 0x1F);
44 tp->tm_min = ((p0 >> 5) & 0xFF) + ((p1 & 0x7) << 3);
45 tp->tm_hour = (p1 >> 3) & 0xFF;
46 tp->tm_mday = (p2 & 0x1F);
47 tp->tm_mon = ((p2 >> 5) & 0xFF) + ((p3 & 0x1) << 3) - 1;
48 tp->tm_year = ((p3 >> 1) & 0xFF) + 80;
49 }
50
51 /*
52 * common portion:
53 * create a unix date from a dos date
54 */
55 static time_t
56 int_unix_date(u_int32_t dos_date)
57 {
58 struct tm t;
59
60 if (dos_date == 0)
61 return(0);
62
63 interpret_dos_date(dos_date, &t);
64 t.tm_wday = 1;
65 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 */
75 static time_t
76 make_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 */
89 static time_t
90 make_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 */
103 static time_t
104 interpret_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);
126 trunc:
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 */
134 static int
135 name_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
167 trunc:
168 return(-1);
169 }
170
171 /*
172 * find a pointer to a netbios name
173 */
174 static const u_char *
175 name_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
202 trunc:
203 return(NULL); /* name goes past the end of the buffer */
204 }
205
206 /*
207 * extract a netbios name from a buf
208 */
209 static int
210 name_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 */
223 static int
224 name_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
243 trunc:
244 return(-1); /* name goes past the end of the buffer */
245 }
246
247 static void
248 print_asc(const unsigned char *buf, int len)
249 {
250 int i;
251 for (i = 0; i < len; i++)
252 safeputchar(buf[i]);
253 }
254
255 static const char *
256 name_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
272 void
273 print_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
315 static void
316 write_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 */
331 static const char *
332 unistr(const u_char *s, int *len, int use_unicode)
333 {
334 static char buf[1000];
335 int l=0;
336
337 if (!use_unicode) {
338 *len = strlen((const char *)s) + 1;
339 return (const char *)s;
340 }
341
342 /*
343 * Skip padding that puts the string on an even boundary.
344 */
345 if (((s - startbuf) % 2) != 0)
346 s++;
347
348 *len = 0;
349
350 if (s[0] == 0 && s[1] != 0) {
351 s++;
352 *len = 1;
353 }
354
355 while (l < (int)(sizeof(buf) - 1) && s[0] && s[1] == 0) {
356 buf[l] = s[0];
357 s += 2;
358 l++;
359 *len += 2;
360 }
361 buf[l] = 0;
362 *len += 2;
363 return buf;
364 }
365
366 static const u_char *
367 smb_fdata1(const u_char *buf, const char *fmt, const u_char *maxbuf,
368 int unicodestr)
369 {
370 int reverse = 0;
371 const char *attrib_fmt = "READONLY|HIDDEN|SYSTEM|VOLUME|DIR|ARCHIVE|";
372 int len;
373
374 while (*fmt && buf<maxbuf) {
375 switch (*fmt) {
376 case 'a':
377 write_bits(buf[0], attrib_fmt);
378 buf++;
379 fmt++;
380 break;
381
382 case 'A':
383 write_bits(EXTRACT_LE_16BITS(buf), attrib_fmt);
384 buf += 2;
385 fmt++;
386 break;
387
388 case '{':
389 {
390 char bitfmt[128];
391 char *p;
392 int l;
393
394 p = strchr(++fmt, '}');
395 l = PTR_DIFF(p, fmt);
396
397 if ((unsigned int)l > sizeof(bitfmt) - 1)
398 l = sizeof(bitfmt)-1;
399
400 strncpy(bitfmt, fmt, l);
401 bitfmt[l] = '\0';
402 fmt = p + 1;
403 write_bits(buf[0], bitfmt);
404 buf++;
405 break;
406 }
407
408 case 'P':
409 {
410 int l = atoi(fmt + 1);
411 buf += l;
412 fmt++;
413 while (isdigit((unsigned char)*fmt))
414 fmt++;
415 break;
416 }
417 case 'r':
418 reverse = !reverse;
419 fmt++;
420 break;
421 case 'b':
422 {
423 unsigned int x;
424 TCHECK(buf[0]);
425 x = buf[0];
426 printf("%u (0x%x)", x, x);
427 buf += 1;
428 fmt++;
429 break;
430 }
431 case 'd':
432 {
433 unsigned int x;
434 TCHECK2(buf[0], 2);
435 x = reverse ? EXTRACT_16BITS(buf) :
436 EXTRACT_LE_16BITS(buf);
437 printf("%d (0x%x)", x, x);
438 buf += 2;
439 fmt++;
440 break;
441 }
442 case 'D':
443 {
444 unsigned int x;
445 TCHECK2(buf[0], 4);
446 x = reverse ? EXTRACT_32BITS(buf) :
447 EXTRACT_LE_32BITS(buf);
448 printf("%d (0x%x)", x, x);
449 buf += 4;
450 fmt++;
451 break;
452 }
453 case 'L':
454 {
455 u_int64_t x;
456 TCHECK2(buf[0], 8);
457 x = reverse ? EXTRACT_64BITS(buf) :
458 EXTRACT_LE_64BITS(buf);
459 printf("%" PRIu64 " (0x%" PRIx64 ")", x, x);
460 buf += 8;
461 fmt++;
462 break;
463 }
464 case 'M':
465 {
466 /* Weird mixed-endian length values in 64-bit locks */
467 u_int32_t x1, x2;
468 u_int64_t x;
469 TCHECK2(buf[0], 8);
470 x1 = reverse ? EXTRACT_32BITS(buf) :
471 EXTRACT_LE_32BITS(buf);
472 x2 = reverse ? EXTRACT_32BITS(buf + 4) :
473 EXTRACT_LE_32BITS(buf + 4);
474 x = (((u_int64_t)x1) << 32) | x2;
475 printf("%" PRIu64 " (0x%" PRIx64 ")", x, x);
476 buf += 8;
477 fmt++;
478 break;
479 }
480 case 'B':
481 {
482 unsigned int x;
483 TCHECK(buf[0]);
484 x = buf[0];
485 printf("0x%X", x);
486 buf += 1;
487 fmt++;
488 break;
489 }
490 case 'w':
491 {
492 unsigned int x;
493 TCHECK2(buf[0], 2);
494 x = reverse ? EXTRACT_16BITS(buf) :
495 EXTRACT_LE_16BITS(buf);
496 printf("0x%X", x);
497 buf += 2;
498 fmt++;
499 break;
500 }
501 case 'W':
502 {
503 unsigned int x;
504 TCHECK2(buf[0], 4);
505 x = reverse ? EXTRACT_32BITS(buf) :
506 EXTRACT_LE_32BITS(buf);
507 printf("0x%X", x);
508 buf += 4;
509 fmt++;
510 break;
511 }
512 case 'S':
513 case 'R': /* like 'S', but always ASCII */
514 {
515 /*XXX unistr() */
516 printf("%.*s", (int)PTR_DIFF(maxbuf, buf),
517 unistr(buf, &len, (*fmt == 'R') ? 0 : unicodestr));
518 buf += len;
519 fmt++;
520 break;
521 }
522 case 'Z':
523 case 'Y': /* like 'Z', but always ASCII */
524 {
525 if (*buf != 4 && *buf != 2)
526 printf("Error! ASCIIZ buffer of type %u (safety=%lu)\n", *buf,
527 (unsigned long)PTR_DIFF(maxbuf, buf));
528 printf("%.*s", (int)PTR_DIFF(maxbuf, buf + 1),
529 unistr(buf + 1, &len, (*fmt == 'Y') ? 0 : unicodestr));
530 buf += len + 1;
531 fmt++;
532 break;
533 }
534 case 's':
535 {
536 int l = atoi(fmt + 1);
537 printf("%-*.*s", l, l, buf);
538 buf += l;
539 fmt++;
540 while (isdigit((unsigned char)*fmt))
541 fmt++;
542 break;
543 }
544 case 'h':
545 {
546 int l = atoi(fmt + 1);
547 while (l--)
548 printf("%02x", *buf++);
549 fmt++;
550 while (isdigit((unsigned char)*fmt))
551 fmt++;
552 break;
553 }
554 case 'n':
555 {
556 int t = atoi(fmt+1);
557 char nbuf[255];
558 int name_type;
559 int len;
560
561 switch (t) {
562 case 1:
563 name_type = name_extract(startbuf, PTR_DIFF(buf, startbuf),
564 maxbuf, nbuf);
565 if (name_type < 0)
566 goto trunc;
567 len = name_len(buf, maxbuf);
568 if (len < 0)
569 goto trunc;
570 buf += len;
571 printf("%-15.15s NameType=0x%02X (%s)", nbuf, name_type,
572 name_type_str(name_type));
573 break;
574 case 2:
575 name_type = buf[15];
576 printf("%-15.15s NameType=0x%02X (%s)", buf, name_type,
577 name_type_str(name_type));
578 buf += 16;
579 break;
580 }
581 fmt++;
582 while (isdigit((unsigned char)*fmt))
583 fmt++;
584 break;
585 }
586 case 'T':
587 {
588 time_t t;
589 struct tm *lt;
590 const char *tstring;
591 u_int32_t x;
592 x = EXTRACT_LE_32BITS(buf);
593
594 switch (atoi(fmt + 1)) {
595 case 1:
596 if (x == 0 || x == 0xFFFFFFFF)
597 t = 0;
598 else
599 t = make_unix_date(buf);
600 buf += 4;
601 break;
602 case 2:
603 if (x == 0 || x == 0xFFFFFFFF)
604 t = 0;
605 else
606 t = make_unix_date2(buf);
607 buf += 4;
608 break;
609 case 3:
610 t = interpret_long_date(buf);
611 buf += 8;
612 break;
613 }
614 if (t != 0) {
615 lt = localtime(&t);
616 if (lt != NULL)
617 tstring = asctime(lt);
618 else
619 tstring = "(Can't convert time)\n";
620 } else
621 tstring = "NULL\n";
622 printf("%s", tstring);
623 fmt++;
624 while (isdigit((unsigned char)*fmt))
625 fmt++;
626 break;
627 }
628 default:
629 putchar(*fmt);
630 fmt++;
631 break;
632 }
633 }
634
635 if (buf >= maxbuf && *fmt)
636 printf("END OF BUFFER\n");
637
638 return(buf);
639
640 trunc:
641 printf("\n");
642 printf("WARNING: Short packet. Try increasing the snap length\n");
643 return(NULL);
644 }
645
646 const u_char *
647 smb_fdata(const u_char *buf, const char *fmt, const u_char *maxbuf,
648 int unicodestr)
649 {
650 static int depth = 0;
651 char s[128];
652 char *p;
653
654 while (*fmt) {
655 switch (*fmt) {
656 case '*':
657 fmt++;
658 while (buf < maxbuf) {
659 const u_char *buf2;
660 depth++;
661 buf2 = smb_fdata(buf, fmt, maxbuf, unicodestr);
662 depth--;
663 if (buf2 == NULL)
664 return(NULL);
665 if (buf2 == buf)
666 return(buf);
667 buf = buf2;
668 }
669 return(buf);
670
671 case '|':
672 fmt++;
673 if (buf >= maxbuf)
674 return(buf);
675 break;
676
677 case '%':
678 fmt++;
679 buf = maxbuf;
680 break;
681
682 case '#':
683 fmt++;
684 return(buf);
685 break;
686
687 case '[':
688 fmt++;
689 if (buf >= maxbuf)
690 return(buf);
691 memset(s, 0, sizeof(s));
692 p = strchr(fmt, ']');
693 if ((size_t)(p - fmt + 1) > sizeof(s)) {
694 /* overrun */
695 return(buf);
696 }
697 strncpy(s, fmt, p - fmt);
698 s[p - fmt] = '\0';
699 fmt = p + 1;
700 buf = smb_fdata1(buf, s, maxbuf, unicodestr);
701 if (buf == NULL)
702 return(NULL);
703 break;
704
705 default:
706 putchar(*fmt);
707 fmt++;
708 fflush(stdout);
709 break;
710 }
711 }
712 if (!depth && buf < maxbuf) {
713 size_t len = PTR_DIFF(maxbuf, buf);
714 printf("Data: (%lu bytes)\n", (unsigned long)len);
715 print_data(buf, len);
716 return(buf + len);
717 }
718 return(buf);
719 }
720
721 typedef struct {
722 const char *name;
723 int code;
724 const char *message;
725 } err_code_struct;
726
727 /* Dos Error Messages */
728 static err_code_struct dos_msgs[] = {
729 { "ERRbadfunc", 1, "Invalid function." },
730 { "ERRbadfile", 2, "File not found." },
731 { "ERRbadpath", 3, "Directory invalid." },
732 { "ERRnofids", 4, "No file descriptors available" },
733 { "ERRnoaccess", 5, "Access denied." },
734 { "ERRbadfid", 6, "Invalid file handle." },
735 { "ERRbadmcb", 7, "Memory control blocks destroyed." },
736 { "ERRnomem", 8, "Insufficient server memory to perform the requested function." },
737 { "ERRbadmem", 9, "Invalid memory block address." },
738 { "ERRbadenv", 10, "Invalid environment." },
739 { "ERRbadformat", 11, "Invalid format." },
740 { "ERRbadaccess", 12, "Invalid open mode." },
741 { "ERRbaddata", 13, "Invalid data." },
742 { "ERR", 14, "reserved." },
743 { "ERRbaddrive", 15, "Invalid drive specified." },
744 { "ERRremcd", 16, "A Delete Directory request attempted to remove the server's current directory." },
745 { "ERRdiffdevice", 17, "Not same device." },
746 { "ERRnofiles", 18, "A File Search command can find no more files matching the specified criteria." },
747 { "ERRbadshare", 32, "The sharing mode specified for an Open conflicts with existing FIDs on the file." },
748 { "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." },
749 { "ERRfilexists", 80, "The file named in a Create Directory, Make New File or Link request already exists." },
750 { "ERRbadpipe", 230, "Pipe invalid." },
751 { "ERRpipebusy", 231, "All instances of the requested pipe are busy." },
752 { "ERRpipeclosing", 232, "Pipe close in progress." },
753 { "ERRnotconnected", 233, "No process on other end of pipe." },
754 { "ERRmoredata", 234, "There is more data to be returned." },
755 { NULL, -1, NULL }
756 };
757
758 /* Server Error Messages */
759 err_code_struct server_msgs[] = {
760 { "ERRerror", 1, "Non-specific error code." },
761 { "ERRbadpw", 2, "Bad password - name/password pair in a Tree Connect or Session Setup are invalid." },
762 { "ERRbadtype", 3, "reserved." },
763 { "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." },
764 { "ERRinvnid", 5, "The tree ID (TID) specified in a command was invalid." },
765 { "ERRinvnetname", 6, "Invalid network name in tree connect." },
766 { "ERRinvdevice", 7, "Invalid device - printer request made to non-printer connection or non-printer request made to printer connection." },
767 { "ERRqfull", 49, "Print queue full (files) -- returned by open print file." },
768 { "ERRqtoobig", 50, "Print queue full -- no space." },
769 { "ERRqeof", 51, "EOF on print queue dump." },
770 { "ERRinvpfid", 52, "Invalid print file FID." },
771 { "ERRsmbcmd", 64, "The server did not recognize the command received." },
772 { "ERRsrverror", 65, "The server encountered an internal error, e.g., system file unavailable." },
773 { "ERRfilespecs", 67, "The file handle (FID) and pathname parameters contained an invalid combination of values." },
774 { "ERRreserved", 68, "reserved." },
775 { "ERRbadpermits", 69, "The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute." },
776 { "ERRreserved", 70, "reserved." },
777 { "ERRsetattrmode", 71, "The attribute mode in the Set File Attribute request is invalid." },
778 { "ERRpaused", 81, "Server is paused." },
779 { "ERRmsgoff", 82, "Not receiving messages." },
780 { "ERRnoroom", 83, "No room to buffer message." },
781 { "ERRrmuns", 87, "Too many remote user names." },
782 { "ERRtimeout", 88, "Operation timed out." },
783 { "ERRnoresource", 89, "No resources currently available for request." },
784 { "ERRtoomanyuids", 90, "Too many UIDs active on this session." },
785 { "ERRbaduid", 91, "The UID is not known as a valid ID on this session." },
786 { "ERRusempx", 250, "Temp unable to support Raw, use MPX mode." },
787 { "ERRusestd", 251, "Temp unable to support Raw, use standard read/write." },
788 { "ERRcontmpx", 252, "Continue in MPX mode." },
789 { "ERRreserved", 253, "reserved." },
790 { "ERRreserved", 254, "reserved." },
791 { "ERRnosupport", 0xFFFF, "Function not supported." },
792 { NULL, -1, NULL }
793 };
794
795 /* Hard Error Messages */
796 err_code_struct hard_msgs[] = {
797 { "ERRnowrite", 19, "Attempt to write on write-protected diskette." },
798 { "ERRbadunit", 20, "Unknown unit." },
799 { "ERRnotready", 21, "Drive not ready." },
800 { "ERRbadcmd", 22, "Unknown command." },
801 { "ERRdata", 23, "Data error (CRC)." },
802 { "ERRbadreq", 24, "Bad request structure length." },
803 { "ERRseek", 25 , "Seek error." },
804 { "ERRbadmedia", 26, "Unknown media type." },
805 { "ERRbadsector", 27, "Sector not found." },
806 { "ERRnopaper", 28, "Printer out of paper." },
807 { "ERRwrite", 29, "Write fault." },
808 { "ERRread", 30, "Read fault." },
809 { "ERRgeneral", 31, "General failure." },
810 { "ERRbadshare", 32, "A open conflicts with an existing open." },
811 { "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." },
812 { "ERRwrongdisk", 34, "The wrong disk was found in a drive." },
813 { "ERRFCBUnavail", 35, "No FCBs are available to process request." },
814 { "ERRsharebufexc", 36, "A sharing buffer has been exceeded." },
815 { NULL, -1, NULL }
816 };
817
818 static struct {
819 int code;
820 const char *class;
821 err_code_struct *err_msgs;
822 } err_classes[] = {
823 { 0, "SUCCESS", NULL },
824 { 0x01, "ERRDOS", dos_msgs },
825 { 0x02, "ERRSRV", server_msgs },
826 { 0x03, "ERRHRD", hard_msgs },
827 { 0x04, "ERRXOS", NULL },
828 { 0xE1, "ERRRMX1", NULL },
829 { 0xE2, "ERRRMX2", NULL },
830 { 0xE3, "ERRRMX3", NULL },
831 { 0xFF, "ERRCMD", NULL },
832 { -1, NULL, NULL }
833 };
834
835 /*
836 * return a SMB error string from a SMB buffer
837 */
838 char *
839 smb_errstr(int class, int num)
840 {
841 static char ret[128];
842 int i, j;
843
844 ret[0] = 0;
845
846 for (i = 0; err_classes[i].class; i++)
847 if (err_classes[i].code == class) {
848 if (err_classes[i].err_msgs) {
849 err_code_struct *err = err_classes[i].err_msgs;
850 for (j = 0; err[j].name; j++)
851 if (num == err[j].code) {
852 snprintf(ret, sizeof(ret), "%s - %s (%s)",
853 err_classes[i].class, err[j].name, err[j].message);
854 return ret;
855 }
856 }
857
858 snprintf(ret, sizeof(ret), "%s - %d", err_classes[i].class, num);
859 return ret;
860 }
861
862 snprintf(ret, sizeof(ret), "ERROR: Unknown error (%d,%d)", class, num);
863 return(ret);
864 }