r13840: Mark some functions as public.
[bbaumbach/samba-autobuild/.git] / source4 / libcli / nbt / nbtname.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    manipulate nbt name structures
5
6    Copyright (C) Andrew Tridgell 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24   see rfc1002 for the detailed format of compressed names
25 */
26
27 #include "includes.h"
28 #include "system/iconv.h"
29 #include "librpc/gen_ndr/ndr_nbt.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31
32 /* don't allow an unlimited number of name components */
33 #define MAX_COMPONENTS 10
34
35 /**
36   print a nbt string
37 */
38 _PUBLIC_ void ndr_print_nbt_string(struct ndr_print *ndr, const char *name, const char *s)
39 {
40         ndr_print_string(ndr, name, s);
41 }
42
43 /*
44   pull one component of a nbt_string
45 */
46 static NTSTATUS ndr_pull_component(struct ndr_pull *ndr, uint8_t **component,
47                                    uint32_t *offset, uint32_t *max_offset)
48 {
49         uint8_t len;
50         uint_t loops = 0;
51         while (loops < 5) {
52                 if (*offset >= ndr->data_size) {
53                         return NT_STATUS_BAD_NETWORK_NAME;
54                 }
55                 len = ndr->data[*offset];
56                 if (len == 0) {
57                         *offset += 1;
58                         *max_offset = MAX(*max_offset, *offset);
59                         *component = NULL;
60                         return NT_STATUS_OK;
61                 }
62                 if ((len & 0xC0) == 0xC0) {
63                         /* its a label pointer */
64                         if (1 + *offset >= ndr->data_size) {
65                                 return NT_STATUS_BAD_NETWORK_NAME;
66                         }
67                         *max_offset = MAX(*max_offset, *offset + 2);
68                         *offset = ((len&0x3F)<<8) | ndr->data[1 + *offset];
69                         *max_offset = MAX(*max_offset, *offset);
70                         loops++;
71                         continue;
72                 }
73                 if ((len & 0xC0) != 0) {
74                         /* its a reserved length field */
75                         return NT_STATUS_BAD_NETWORK_NAME;
76                 }
77                 if (*offset + len + 2 > ndr->data_size) {
78                         return NT_STATUS_BAD_NETWORK_NAME;
79                 }
80                 *component = (uint8_t*)talloc_strndup(ndr, (const char *)&ndr->data[1 + *offset], len);
81                 NT_STATUS_HAVE_NO_MEMORY(*component);
82                 *offset += len + 1;
83                 *max_offset = MAX(*max_offset, *offset);
84                 return NT_STATUS_OK;
85         }
86
87         /* too many pointers */
88         return NT_STATUS_BAD_NETWORK_NAME;
89 }
90
91 /**
92   pull a nbt_string from the wire
93 */
94 _PUBLIC_ NTSTATUS ndr_pull_nbt_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
95 {
96         NTSTATUS status;
97         uint32_t offset = ndr->offset;
98         uint32_t max_offset = offset;
99         unsigned num_components;
100         char *name;
101
102         if (!(ndr_flags & NDR_SCALARS)) {
103                 return NT_STATUS_OK;
104         }
105
106         name = NULL;
107
108         /* break up name into a list of components */
109         for (num_components=0;num_components<MAX_COMPONENTS;num_components++) {
110                 uint8_t *component;
111                 status = ndr_pull_component(ndr, &component, &offset, &max_offset);
112                 NT_STATUS_NOT_OK_RETURN(status);
113                 if (component == NULL) break;
114                 if (name) {
115                         name = talloc_asprintf_append(name, ".%s", component);
116                         NT_STATUS_HAVE_NO_MEMORY(name);
117                 } else {
118                         name = (char *)component;
119                 }
120         }
121         if (num_components == MAX_COMPONENTS) {
122                 return NT_STATUS_BAD_NETWORK_NAME;
123         }
124         if (num_components == 0) {
125                 name = talloc_strdup(ndr, "");
126                 NT_STATUS_HAVE_NO_MEMORY(name);
127         }
128
129         (*s) = name;
130         ndr->offset = max_offset;
131
132         return NT_STATUS_OK;
133 }
134
135 /**
136   push a nbt string to the wire
137 */
138 _PUBLIC_ NTSTATUS ndr_push_nbt_string(struct ndr_push *ndr, int ndr_flags, const char *s)
139 {
140         if (!(ndr_flags & NDR_SCALARS)) {
141                 return NT_STATUS_OK;
142         }
143
144         while (s && *s) {
145                 NTSTATUS status;
146                 char *compname;
147                 size_t complen;
148                 uint32_t offset;
149
150                 /* see if we have pushed the remaing string allready,
151                  * if so we use a label pointer to this string
152                  */
153                 status = ndr_token_retrieve_cmp_fn(&ndr->nbt_string_list, s, &offset, (comparison_fn_t)strcmp, False);
154                 if (NT_STATUS_IS_OK(status)) {
155                         uint8_t b[2];
156                         
157                         if (offset > 0x3FFF) {
158                                 return ndr_push_error(ndr, NDR_ERR_STRING,
159                                                       "offset for nbt string label pointer %u[%08X] > 0x00003FFF",
160                                                       offset, offset);
161                         }
162
163                         b[0] = 0xC0 | (offset>>8);
164                         b[1] = (offset & 0xFF);
165
166                         return ndr_push_bytes(ndr, b, 2);
167                 }
168
169                 complen = strcspn(s, ".");
170
171                 /* we need to make sure the length fits into 6 bytes */
172                 if (complen >= 0x3F) {
173                         return ndr_push_error(ndr, NDR_ERR_STRING,
174                                               "component length %u[%08X] > 0x00003F",
175                                               (unsigned)complen, (unsigned)complen);
176                 }
177
178                 compname = talloc_asprintf(ndr, "%c%*.*s",
179                                                 (unsigned char)complen,
180                                                 (unsigned char)complen,
181                                                 (unsigned char)complen, s);
182                 NT_STATUS_HAVE_NO_MEMORY(compname);
183
184                 /* remember the current componemt + the rest of the string
185                  * so it can be reused later
186                  */
187                 NDR_CHECK(ndr_token_store(ndr, &ndr->nbt_string_list, s, ndr->offset));
188
189                 /* push just this component into the blob */
190                 NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)compname, complen+1));
191                 talloc_free(compname);
192
193                 s += complen;
194                 if (*s == '.') s++;
195         }
196
197         /* if we reach the end of the string and have pushed the last component
198          * without using a label pointer, we need to terminate the string
199          */
200         return ndr_push_bytes(ndr, (const uint8_t *)"", 1);
201 }
202
203
204 /*
205   decompress a 'compressed' name component
206  */
207 static NTSTATUS decompress_name(char *name, enum nbt_name_type *type)
208 {
209         int i;
210         for (i=0;name[2*i];i++) {
211                 uint8_t c1 = name[2*i];
212                 uint8_t c2 = name[1+(2*i)];
213                 if (c1 < 'A' || c1 > 'P' ||
214                     c2 < 'A' || c2 > 'P') {
215                         return NT_STATUS_BAD_NETWORK_NAME;
216                 }
217                 name[i] = ((c1-'A')<<4) | (c2-'A');                 
218         }
219         name[i] = 0;
220         if (i == 16) {
221                 *type = (enum nbt_name_type)(name[15]);
222                 name[15] = 0;
223                 i--;
224         } else {
225                 *type = NBT_NAME_CLIENT;
226         }
227
228         /* trim trailing spaces */
229         for (;i>0 && name[i-1]==' ';i--) {
230                 name[i-1] = 0;
231         }
232         
233         return NT_STATUS_OK;
234 }
235
236
237 /*
238   compress a name component
239  */
240 static uint8_t *compress_name(TALLOC_CTX *mem_ctx, 
241                               const uint8_t *name, enum nbt_name_type type)
242 {
243         uint8_t *cname;
244         int i;
245         uint8_t pad_char;
246
247         if (strlen((const char *)name) > 15) {
248                 return NULL;
249         }
250
251         cname = talloc_array(mem_ctx, uint8_t, 33);
252         if (cname == NULL) return NULL;
253
254         for (i=0;name[i];i++) {
255                 cname[2*i]   = 'A' + (name[i]>>4);
256                 cname[1+2*i] = 'A' + (name[i]&0xF);
257         }
258         if (strcmp((const char *)name, "*") == 0) {
259                 pad_char = 0;
260         } else {
261                 pad_char = ' ';
262         }
263         for (;i<15;i++) {
264                 cname[2*i]   = 'A' + (pad_char>>4);
265                 cname[1+2*i] = 'A' + (pad_char&0xF);
266         }
267
268         pad_char = type;
269         cname[2*i]   = 'A' + (pad_char>>4);
270         cname[1+2*i] = 'A' + (pad_char&0xF);
271
272         cname[32] = 0;
273         return cname;
274 }
275
276
277 /**
278   pull a nbt name from the wire
279 */
280 _PUBLIC_ NTSTATUS ndr_pull_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name *r)
281 {
282         NTSTATUS status;
283         uint8_t *scope;
284         char *cname;
285         const char *s;
286
287         if (!(ndr_flags & NDR_SCALARS)) {
288                 return NT_STATUS_OK;
289         }
290
291         status = ndr_pull_nbt_string(ndr, ndr_flags, &s);
292         NT_STATUS_NOT_OK_RETURN(status);
293
294         scope = (uint8_t *)strchr(s, '.');
295         if (scope) {
296                 *scope = 0;
297                 r->scope = talloc_strdup(ndr->current_mem_ctx, (const char *)&scope[1]);
298                 NT_STATUS_HAVE_NO_MEMORY(r->scope);
299         } else {
300                 r->scope = NULL;
301         }
302
303         cname = discard_const_p(char, s);
304
305         /* the first component is limited to 16 bytes in the DOS charset,
306            which is 32 in the 'compressed' form */
307         if (strlen(cname) > 32) {
308                 return NT_STATUS_BAD_NETWORK_NAME;
309         }
310
311         /* decompress the first component */
312         status = decompress_name(cname, &r->type);
313         NT_STATUS_NOT_OK_RETURN(status);
314
315         r->name = talloc_strdup(ndr->current_mem_ctx, cname);
316         NT_STATUS_HAVE_NO_MEMORY(r->name);
317
318         talloc_free(cname);
319
320         return NT_STATUS_OK;
321 }
322
323 /**
324   push a nbt name to the wire
325 */
326 _PUBLIC_ NTSTATUS ndr_push_nbt_name(struct ndr_push *ndr, int ndr_flags, const struct nbt_name *r)
327 {
328         uint8_t *cname, *fullname;
329         NTSTATUS status;
330
331         if (!(ndr_flags & NDR_SCALARS)) {
332                 return NT_STATUS_OK;
333         }
334
335         cname = compress_name(ndr, (const uint8_t *)r->name, r->type);
336         NT_STATUS_HAVE_NO_MEMORY(cname);
337
338         if (r->scope) {
339                 fullname = (uint8_t *)talloc_asprintf(ndr, "%s.%s", cname, r->scope);
340                 NT_STATUS_HAVE_NO_MEMORY(fullname);
341                 talloc_free(cname);
342         } else {
343                 fullname = cname;
344         }
345         
346         status = ndr_push_nbt_string(ndr, ndr_flags, (const char *)fullname);
347
348         return status;
349 }
350
351
352 /**
353   copy a nbt name structure
354 */
355 _PUBLIC_ NTSTATUS nbt_name_dup(TALLOC_CTX *mem_ctx, struct nbt_name *name, struct nbt_name *newname)
356 {
357         *newname = *name;
358         newname->name = talloc_strdup(mem_ctx, newname->name);
359         NT_STATUS_HAVE_NO_MEMORY(newname->name);
360         newname->scope = talloc_strdup(mem_ctx, newname->scope);
361         if (name->scope) {
362                 NT_STATUS_HAVE_NO_MEMORY(newname->scope);
363         }
364         return NT_STATUS_OK;
365 }
366
367 /**
368   push a nbt name into a blob
369 */
370 _PUBLIC_ NTSTATUS nbt_name_to_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct nbt_name *name)
371 {
372         return ndr_push_struct_blob(blob, mem_ctx, name, 
373                                     (ndr_push_flags_fn_t)ndr_push_nbt_name);
374 }
375
376
377 /**
378   pull a nbt name from a blob
379 */
380 _PUBLIC_ NTSTATUS nbt_name_from_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, struct nbt_name *name)
381 {
382         return ndr_pull_struct_blob(blob, mem_ctx, name, 
383                                     (ndr_pull_flags_fn_t)ndr_pull_nbt_name);
384 }
385
386
387 /**
388   choose a name to use when calling a server in a NBT session request.
389   we use heuristics to see if the name we have been given is a IP
390   address, or a too-long name. If it is then use *SMBSERVER, or a
391   truncated name
392 */
393 _PUBLIC_ void nbt_choose_called_name(TALLOC_CTX *mem_ctx,
394                             struct nbt_name *n, const char *name, int type)
395 {
396         n->scope = NULL;
397         n->type = type;
398
399         if (is_ipaddress(name)) {
400                 n->name = "*SMBSERVER";
401                 return;
402         }
403         if (strlen(name) > 15) {
404                 const char *p = strchr(name, '.');
405                 char *s;
406                 if (p - name > 15) {
407                         n->name = "*SMBSERVER";
408                         return;
409                 }
410                 s = talloc_strndup(mem_ctx, name, PTR_DIFF(p, name));
411                 n->name = strupper_talloc(mem_ctx, s);
412                 return;
413         }
414
415         n->name = strupper_talloc(mem_ctx, name);
416 }
417
418
419 /*
420   escape a string into a form containing only a small set of characters,
421   the rest is hex encoded. This is similar to URL encoding
422 */
423 static const char *nbt_hex_encode(TALLOC_CTX *mem_ctx, const char *s)
424 {
425         int i, len;
426         char *ret;
427         const char *valid_chars = "_-.$@ ";
428 #define NBT_CHAR_ALLOW(c) (isalnum((unsigned char)c) || strchr(valid_chars, c))
429
430         for (len=i=0;s[i];i++,len++) {
431                 if (!NBT_CHAR_ALLOW(s[i])) {
432                         len += 2;
433                 }
434         }
435
436         ret = talloc_array(mem_ctx, char, len+1);
437         if (ret == NULL) return NULL;
438
439         for (len=i=0;s[i];i++) {
440                 if (NBT_CHAR_ALLOW(s[i])) {
441                         ret[len++] = s[i];
442                 } else {
443                         snprintf(&ret[len], 4, "%%%02x", (unsigned char)s[i]);
444                         len += 3;
445                 }
446         }
447         ret[len] = 0;
448
449         return ret;
450 }
451
452
453 /**
454   form a string for a NBT name
455 */
456 _PUBLIC_ char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name)
457 {
458         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
459         char *ret;
460         if (name->scope) {              
461                 ret = talloc_asprintf(mem_ctx, "%s<%02x>-%s",
462                                       nbt_hex_encode(tmp_ctx, name->name),
463                                       name->type, 
464                                       nbt_hex_encode(tmp_ctx, name->scope));
465         } else {
466                 ret = talloc_asprintf(mem_ctx, "%s<%02x>", 
467                                       nbt_hex_encode(tmp_ctx, name->name), 
468                                       name->type);
469         }
470         talloc_free(tmp_ctx);
471         return ret;
472 }
473
474 /**
475   pull a nbt name, WINS Replication uses another on wire format for nbt name
476 */
477 _PUBLIC_ NTSTATUS ndr_pull_wrepl_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name **_r)
478 {
479         struct nbt_name *r;
480         uint8_t *namebuf;
481         uint32_t namebuf_len;
482
483         if (!(ndr_flags & NDR_SCALARS)) {
484                 return NT_STATUS_OK;
485         }
486
487         NDR_CHECK(ndr_pull_align(ndr, 4));
488         NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &namebuf_len));
489         if (namebuf_len < 1 || namebuf_len > 255) {
490                 return ndr_pull_error(ndr, NDR_ERR_ALLOC, "value out of range");
491         }
492         NDR_PULL_ALLOC_N(ndr, namebuf, namebuf_len);
493         NDR_CHECK(ndr_pull_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len));
494
495         NDR_PULL_ALLOC(ndr, r); 
496
497         /* oh wow, what a nasty bug in windows ... */
498         if (namebuf[0] == 0x1b && namebuf_len >= 16) {
499                 namebuf[0] = namebuf[15];
500                 namebuf[15] = 0x1b;
501         }
502
503         if (namebuf_len < 17) {
504                 r->type = 0x00;
505
506                 r->name = talloc_strndup(r, (char *)namebuf, namebuf_len);
507                 if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory");
508
509                 r->scope= NULL;
510
511                 talloc_free(namebuf);
512                 *_r = r;
513                 return NT_STATUS_OK;
514         }
515
516         r->type = namebuf[15];
517
518         namebuf[15] = '\0';
519         trim_string((char *)namebuf, NULL, " ");
520         r->name = talloc_strdup(r, (char *)namebuf);
521         if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory");
522
523         if (namebuf_len > 18) {
524                 r->scope = talloc_strndup(r, (char *)(namebuf+17), namebuf_len-17);
525                 if (!r->scope) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory");
526         } else {
527                 r->scope = NULL;
528         }
529
530         talloc_free(namebuf);
531         *_r = r;
532         return NT_STATUS_OK;
533 }
534
535 /**
536   push a nbt name, WINS Replication uses another on wire format for nbt name
537 */
538 _PUBLIC_ NTSTATUS ndr_push_wrepl_nbt_name(struct ndr_push *ndr, int ndr_flags, const struct nbt_name *r)
539 {
540         uint8_t *namebuf;
541         uint32_t namebuf_len;
542         uint32_t name_len;
543         uint32_t scope_len = 0;
544
545         if (r == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;
546
547         if (!(ndr_flags & NDR_SCALARS)) {
548                 return NT_STATUS_OK;
549         }
550
551         name_len = strlen(r->name);
552         if (name_len > 15) {
553                 return NT_STATUS_INVALID_PARAMETER_MIX;
554         }
555
556         if (r->scope) {
557                 scope_len = strlen(r->scope);
558         }
559         if (scope_len > 238) {
560                 return NT_STATUS_INVALID_PARAMETER_MIX;
561         }
562
563         namebuf = (uint8_t *)talloc_asprintf(ndr, "%-15s%c%s",
564                                              r->name, 'X',
565                                              (r->scope?r->scope:""));
566         if (!namebuf) return ndr_push_error(ndr, NDR_ERR_ALLOC, "out of memory");
567
568         namebuf_len = strlen((char *)namebuf) + 1;
569
570         /*
571          * we need to set the type here, and use a place-holder in the talloc_asprintf()
572          * as the type can be 0x00, and then the namebuf_len = strlen(namebuf); would give wrong results
573          */
574         namebuf[15] = r->type;
575
576         /* oh wow, what a nasty bug in windows ... */
577         if (r->type == 0x1b) {
578                 namebuf[15] = namebuf[0];
579                 namebuf[0] = 0x1b;
580         }
581
582         NDR_CHECK(ndr_push_align(ndr, 4));
583         NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, namebuf_len));
584         NDR_CHECK(ndr_push_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len));
585
586         talloc_free(namebuf);
587         return NT_STATUS_OK;
588 }
589
590 _PUBLIC_ void ndr_print_wrepl_nbt_name(struct ndr_print *ndr, const char *name, const struct nbt_name *r)
591 {
592         char *s = nbt_name_string(ndr, r);
593         ndr_print_string(ndr, name, s);
594         talloc_free(s);
595 }