r8520: fixed a pile of warnings from the build farm gcc -Wall output on
[kamenim/samba.git] / source4 / libcli / nbt / nbtname.c
index 0d2840e0b5b36d802ae66cabfc04624fe1db13c5..d7f0b1b077296bc1d0838dc45eaea92c1f91c08c 100644 (file)
 */
 
 #include "includes.h"
+#include "system/iconv.h"
 #include "librpc/gen_ndr/ndr_nbt.h"
 
 /* don't allow an unlimited number of name components */
 #define MAX_COMPONENTS 10
 
 /*
-  pull one component of a compressed name
+  print a nbt string
+*/
+void ndr_print_nbt_string(struct ndr_print *ndr, const char *name, const char *s)
+{
+       ndr_print_string(ndr, name, s);
+}
+
+/*
+  pull one component of a nbt_string
 */
 static NTSTATUS ndr_pull_component(struct ndr_pull *ndr, uint8_t **component,
                                   uint32_t *offset, uint32_t *max_offset)
@@ -54,8 +63,9 @@ static NTSTATUS ndr_pull_component(struct ndr_pull *ndr, uint8_t **component,
                        if (1 + *offset >= ndr->data_size) {
                                return NT_STATUS_BAD_NETWORK_NAME;
                        }
+                       *max_offset = MAX(*max_offset, *offset + 2);
                        *offset = ((len&0x3F)<<8) | ndr->data[1 + *offset];
-                       *max_offset = MAX(*max_offset, *offset + 1);
+                       *max_offset = MAX(*max_offset, *offset);
                        loops++;
                        continue;
                }
@@ -77,6 +87,119 @@ static NTSTATUS ndr_pull_component(struct ndr_pull *ndr, uint8_t **component,
        return NT_STATUS_BAD_NETWORK_NAME;
 }
 
+/*
+  pull a nbt_string from the wire
+*/
+NTSTATUS ndr_pull_nbt_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
+{
+       NTSTATUS status;
+       uint32_t offset = ndr->offset;
+       uint32_t max_offset = offset;
+       unsigned num_components;
+       char *name;
+
+       if (!(ndr_flags & NDR_SCALARS)) {
+               return NT_STATUS_OK;
+       }
+
+       name = NULL;
+
+       /* break up name into a list of components */
+       for (num_components=0;num_components<MAX_COMPONENTS;num_components++) {
+               uint8_t *component;
+               status = ndr_pull_component(ndr, &component, &offset, &max_offset);
+               NT_STATUS_NOT_OK_RETURN(status);
+               if (component == NULL) break;
+               if (name) {
+                       name = talloc_asprintf_append(name, ".%s", component);
+                       NT_STATUS_HAVE_NO_MEMORY(name);
+               } else {
+                       name = component;
+               }
+       }
+       if (num_components == MAX_COMPONENTS) {
+               return NT_STATUS_BAD_NETWORK_NAME;
+       }
+       if (num_components == 0) {
+               name = talloc_strdup(ndr, "");
+               NT_STATUS_HAVE_NO_MEMORY(name);
+       }
+
+       (*s) = name;
+       ndr->offset = max_offset;
+
+       return NT_STATUS_OK;
+}
+
+/*
+  push a nbt string to the wire
+*/
+NTSTATUS ndr_push_nbt_string(struct ndr_push *ndr, int ndr_flags, const char *s)
+{
+       if (!(ndr_flags & NDR_SCALARS)) {
+               return NT_STATUS_OK;
+       }
+
+       while (s && *s) {
+               NTSTATUS status;
+               char *compname;
+               size_t complen;
+               uint32_t offset;
+
+               /* see if we have pushed the remaing string allready,
+                * if so we use a label pointer to this string
+                */
+               status = ndr_token_retrieve_cmp_fn(&ndr->nbt_string_list, s, &offset, (comparison_fn_t)strcmp, False);
+               if (NT_STATUS_IS_OK(status)) {
+                       uint8_t b[2];
+                       
+                       if (offset > 0x3FFF) {
+                               return ndr_push_error(ndr, NDR_ERR_STRING,
+                                                     "offset for nbt string label pointer %u[%08X] > 0x00003FFF",
+                                                     offset, offset);
+                       }
+
+                       b[0] = 0xC0 | (offset>>8);
+                       b[1] = (offset & 0xFF);
+
+                       return ndr_push_bytes(ndr, b, 2);
+               }
+
+               complen = strcspn(s, ".");
+
+               /* we need to make sure the length fits into 6 bytes */
+               if (complen >= 0x3F) {
+                       return ndr_push_error(ndr, NDR_ERR_STRING,
+                                             "component length %u[%08X] > 0x00003F",
+                                             (unsigned)complen, (unsigned)complen);
+               }
+
+               compname = talloc_asprintf(ndr, "%c%*.*s",
+                                               (unsigned char)complen,
+                                               (unsigned char)complen,
+                                               (unsigned char)complen, s);
+               NT_STATUS_HAVE_NO_MEMORY(compname);
+
+               /* remember the current componemt + the rest of the string
+                * so it can be reused later
+                */
+               NDR_CHECK(ndr_token_store(ndr, &ndr->nbt_string_list, s, ndr->offset));
+
+               /* push just this component into the blob */
+               NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)compname, complen+1));
+               talloc_free(compname);
+
+               s += complen;
+               if (*s == '.') s++;
+       }
+
+       /* if we reach the end of the string and have pushed the last component
+        * without using a label pointer, we need to terminate the string
+        */
+       return ndr_push_bytes(ndr, (const uint8_t *)"", 1);
+}
+
+
 /*
   decompress a 'compressed' name component
  */
@@ -131,7 +254,7 @@ static uint8_t *compress_name(TALLOC_CTX *mem_ctx,
                cname[2*i]   = 'A' + (name[i]>>4);
                cname[1+2*i] = 'A' + (name[i]&0xF);
        }
-       if (name[0] == '*') {
+       if (strcmp(name, "*") == 0) {
                pad_char = 0;
        } else {
                pad_char = ' ';
@@ -149,57 +272,49 @@ static uint8_t *compress_name(TALLOC_CTX *mem_ctx,
        return cname;
 }
 
+
 /*
   pull a nbt name from the wire
 */
 NTSTATUS ndr_pull_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name *r)
 {
        NTSTATUS status;
-       uint_t num_components;
-       uint32_t offset = ndr->offset;
-       uint32_t max_offset = offset;
-       uint8_t *components[MAX_COMPONENTS];
-       int i;
        uint8_t *scope;
+       char *cname;
+       const char *s;
 
        if (!(ndr_flags & NDR_SCALARS)) {
                return NT_STATUS_OK;
        }
 
-       /* break up name into a list of components */
-       for (num_components=0;num_components<MAX_COMPONENTS;num_components++) {
-               status = ndr_pull_component(ndr, &components[num_components], 
-                                           &offset, &max_offset);
-               NT_STATUS_NOT_OK_RETURN(status);
-               if (components[num_components] == NULL) break;
-       }
-       if (num_components == MAX_COMPONENTS ||
-           num_components == 0) {
-               return NT_STATUS_BAD_NETWORK_NAME;
+       status = ndr_pull_nbt_string(ndr, ndr_flags, &s);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       scope = strchr(s, '.');
+       if (scope) {
+               *scope = 0;
+               r->scope = talloc_strdup(ndr, scope+1);
+               NT_STATUS_HAVE_NO_MEMORY(r->scope);
+       } else {
+               r->scope = NULL;
        }
 
-       ndr->offset = max_offset;
+       cname = discard_const_p(char, s);
 
        /* the first component is limited to 16 bytes in the DOS charset,
           which is 32 in the 'compressed' form */
-       if (strlen(components[0]) > 32) {
+       if (strlen(cname) > 32) {
                return NT_STATUS_BAD_NETWORK_NAME;
        }
 
        /* decompress the first component */
-       status = decompress_name(components[0], &r->type);
+       status = decompress_name(cname, &r->type);
        NT_STATUS_NOT_OK_RETURN(status);
 
-       r->name = components[0];
+       r->name = talloc_strdup(ndr, cname);
+       NT_STATUS_HAVE_NO_MEMORY(r->name);
 
-       /* combine the remaining components into the scope */
-       scope = components[1];
-       for (i=2;i<num_components;i++) {
-               scope = talloc_asprintf_append(scope, ".%s", components[i]);
-               NT_STATUS_HAVE_NO_MEMORY(scope);
-       }
-
-       r->scope = scope;
+       talloc_free(cname);
 
        return NT_STATUS_OK;
 }
@@ -207,53 +322,34 @@ NTSTATUS ndr_pull_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name
 /*
   push a nbt name to the wire
 */
-NTSTATUS ndr_push_nbt_name(struct ndr_push *ndr, int ndr_flags, struct nbt_name *r)
+NTSTATUS ndr_push_nbt_name(struct ndr_push *ndr, int ndr_flags, const struct nbt_name *r)
 {
-       uint_t num_components;
-       uint8_t *components[MAX_COMPONENTS];
-       char *dscope=NULL, *p;
-       uint8_t *cname;
-       int i;
+       uint8_t *cname, *fullname;
+       NTSTATUS status;
 
        if (!(ndr_flags & NDR_SCALARS)) {
                return NT_STATUS_OK;
        }
 
-       if (r->scope) {
-               dscope = talloc_strdup(ndr, r->scope);
-               NT_STATUS_HAVE_NO_MEMORY(dscope);
-       }
-
        cname = compress_name(ndr, r->name, r->type);
        NT_STATUS_HAVE_NO_MEMORY(cname);
 
-       /* form the base components */
-       components[0] = cname;
-       num_components = 1;
-
-       while (dscope && (p=strchr(dscope, '.')) && 
-              num_components < MAX_COMPONENTS) {
-               *p = 0;
-               components[num_components] = dscope;
-               dscope = p+1;
-               num_components++;
-       }
-       if (dscope && num_components < MAX_COMPONENTS) {
-               components[num_components++] = dscope;
-       }
-       if (num_components == MAX_COMPONENTS) {
-               return NT_STATUS_BAD_NETWORK_NAME;
-       }
-               
-       /* push the components */
-       for (i=0;i<num_components;i++) {
-               uint8_t len = strlen(components[i]);
-               NDR_CHECK(ndr_push_uint8(ndr, NDR_SCALARS, len));
-               NDR_CHECK(ndr_push_bytes(ndr, components[i], len));
+       if (r->scope) {
+               fullname = talloc_asprintf(ndr, "%s.%s", cname, r->scope);
+               NT_STATUS_HAVE_NO_MEMORY(fullname);
+               talloc_free(cname);
+       } else {
+               fullname = cname;
        }
-       NDR_CHECK(ndr_push_uint8(ndr, NDR_SCALARS, 0));
-
-       return NT_STATUS_OK;
+       
+       status = ndr_push_nbt_string(ndr, ndr_flags, fullname);
+#if 0
+       /* this free conflicts with the use of pointers into strings
+          in the ndr_token_store() calls above. Metze, can you look
+          at this? */
+       talloc_free(fullname);
+#endif
+       return status;
 }
 
 
@@ -310,13 +406,72 @@ void nbt_choose_called_name(TALLOC_CTX *mem_ctx,
        }
        if (strlen(name) > 15) {
                const char *p = strchr(name, '.');
+               char *s;
                if (p - name > 15) {
                        n->name = "*SMBSERVER";
                        return;
                }
-               n->name = talloc_strndup(mem_ctx, name, PTR_DIFF(p, name));
+               s = talloc_strndup(mem_ctx, name, PTR_DIFF(p, name));
+               n->name = strupper_talloc(mem_ctx, s);
                return;
        }
 
-       n->name = talloc_strdup(mem_ctx, name);
+       n->name = strupper_talloc(mem_ctx, name);
 }
+
+
+/*
+  escape a string into a form containing only a small set of characters,
+  the rest is hex encoded. This is similar to URL encoding
+*/
+static const char *nbt_hex_encode(TALLOC_CTX *mem_ctx, const char *s)
+{
+       int i, len;
+       char *ret;
+       const char *valid_chars = "_-.$@ ";
+#define NBT_CHAR_ALLOW(c) (isalnum((unsigned char)c) || strchr(valid_chars, c))
+
+       for (len=i=0;s[i];i++,len++) {
+               if (!NBT_CHAR_ALLOW(s[i])) {
+                       len += 2;
+               }
+       }
+
+       ret = talloc_array(mem_ctx, char, len+1);
+       if (ret == NULL) return NULL;
+
+       for (len=i=0;s[i];i++) {
+               if (NBT_CHAR_ALLOW(s[i])) {
+                       ret[len++] = s[i];
+               } else {
+                       snprintf(&ret[len], 4, "%%%02x", (unsigned char)s[i]);
+                       len += 3;
+               }
+       }
+       ret[len] = 0;
+
+       return ret;
+}
+
+
+/*
+  form a string for a NBT name
+*/
+char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name)
+{
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       char *ret;
+       if (name->scope) {              
+               ret = talloc_asprintf(mem_ctx, "%s<%02x>-%s",
+                                     nbt_hex_encode(tmp_ctx, name->name),
+                                     name->type, 
+                                     nbt_hex_encode(tmp_ctx, name->scope));
+       } else {
+               ret = talloc_asprintf(mem_ctx, "%s<%02x>", 
+                                     nbt_hex_encode(tmp_ctx, name->name), 
+                                     name->type);
+       }
+       talloc_free(tmp_ctx);
+       return ret;
+}
+