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