an, awi, cue and kue don't use the miibus interface.
[dragonfly.git] / contrib / tcpdump-3.8.3 / 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.26.2.2 2003/11/16 08:51:56 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)
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
370 static const u_char *
371 smb_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
630 trunc:
631     printf("\n");
632     printf("WARNING: Short packet. Try increasing the snap length\n");
633     return(NULL);
634 }
635
636 const u_char *
637 smb_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
710 typedef struct {
711     const char *name;
712     int code;
713     const char *message;
714 } err_code_struct;
715
716 /* Dos Error Messages */
717 static 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 */
748 err_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 */
785 err_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
807 static 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  */
827 char *
828 smb_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 }