4.1 NBT Names : Once More with FeelingLet's review what we've learned so far:
4.1.1 Valid NetBIOS Name CharactersAny octet value can be encoded using the first-level mechanism. In theory, then, any eight-bit value can be part of a NetBIOS name. Keep this in mind and be prepared. There are some very strange names in use in the wild. In practice, implementations do place some restrictions on the characters that may be used in NetBIOS names. These restrictions are implemented at the application layer, and should be considered artificial. Under Windows 9x, for example, the "Network Identity" control panel allows only the following characters in a machine name:
Yet the same Windows 9x system may also register the special-purpose name " \x01\x02__MSBROWSE__\x02\x01 ", which contains control characters as shown. Note that the set of alphanumeric characters may include extended characters, such as ' ' or ' '. Unfortunately, these are often represented by different octet values under different operating systems, or even under different configurations of the same operating system. Some examples:
As you can see, the mapping between character sets can be a bit of a challenge particularly since there is no standard character set for use in NBT and no mechanism for negotiating a common character set. [1]
One more thing to consider when dealing with NetBIOS name characters: Windows NT will generate a warning and W2K an error if the Machine Name is not also a valid DNS name. You may need to do some testing to determine which characters Windows considers valid DNS label characters. 4.1.2 NetBIOS Names within ScopeUnder NBT, NetBIOS names exist within a scope . The scope is the set of all machines which can "see" the name. For B nodes, the scope is limited to the IP broadcast domain. For P nodes, the scope is limited to the set of nodes that share the same NBNS. For M and H nodes, the scope is the union of the broadcast domain and the shared NBNS. Scope can be further refined using a Scope ID . The Scope ID effectively sub-divides a virtual NetBIOS LAN into separate, named vLANs. Unfortunately, few (if any) implementations actually support multiple Scope IDs so this feature is of limited practical use. The syntax of the Scope ID matches the best-practices recommendations for DNS domain names. (Some Windows flavors allow almost any character value in a Scope ID string. Sigh.) Scope IDs should be converted to upper case before use on the wire. Annoyance Alert
4.1.3 Encoding and Decoding NBT NamesFirst Level Encoding converts a 16-byte NetBIOS name into a 32-byte encoded name, and then combines it with the Scope ID. For example: "EOGFGLGPCACACACACACACACACACACAAA.CAT.ORG" We have chosen to call this format the NBT Name . Second Level Encoding is applied to the NBT name to create the on-the-wire format, which we will refer to as the Encoded NBT Name : "\x20EOGFGLGPCACACACACACACACACACACAAA\x03CAT\x03ORG"\x20EOGFGLGPCACACACACACACACACACACAAA\x03CAT\x03ORG""\x20EOGFGLGPCACACACACACACACACACACAAA\x03CAT\x03ORG\0"" As previously described, the maximum length of a label in an NBT name is 63 bytes. This is because the label length field is divided into two sub-fields, the first of which is a two-bit flag field with four possible values:
With both bits clear (zero), the next 6 bits are the label LENGTH . The LENGTH field is an unsigned integer with a value in the range 0..63.
If both flag bits are set, however, then the next fourteen bits are a "Label String Pointer"; the offset at which the real label can be found.
Label String Pointers are used to reduce the size of Name Service messages that might otherwise contain two copies of the same NBT name. For example, a NAME REGISTRATION REQUEST message includes both a QUESTION_RECORD and an ADDITIONAL_RECORD , each of which would otherwise contain the same NBT name. Instead of duplicating the name, however, the ADDITIONAL_RECORD.RR_NAME field contains a label string pointer to the QUESTION_RECORD.QUESTION_NAME field. Label String Pointers are a prime example of the NBT theory/practice dichotomy , and another throwback to the DNS system. As it turns out, the only Label String Pointer value ever used in NBT is 0xC00C . The reason for this is quite simple. The NBT header is a fixed size (12 bytes), and is always followed by a block that starts with an encoded NBT Name. Thus, the offset of the first name in the packet is always 12 ( 0x0C ). Any further name field in the packet will point back to the first. So, the rule of thumb is that the encoded NBT name will always be found at byte offset 0x000C . As a shortcut, some implementations work directly with the encoded name and only bother to decode the name when interacting with a user . Decoding, however, is fairly straightforward: Listing 4.1 Level 2 and Level 1 decodingint L2_Decode(uchar *dst, /* Decoded name target buffer. */ uchar *src, /* Encoded name source buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int len; int pos; int next; /* Be safe. */ dst[0] = 'int L2_Decode(uchar *dst, /* Decoded name target buffer. */ uchar *src, /* Encoded name source buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int len; int pos; int next; /* Be safe. */ dst[0] = '\0'; /* Get encoded string length (doesn't include root label). */ len = strlen((char *)&(src[srcpos])); /* If length is zero, return the empty string. */ if(0 == len) return(0); /* Make sure name does not exceed source buffer length. */ if(len >= (srcmax - srcpos)) return(-1); /* Copy source to destination skipping the first label length byte * (but including the terminating nul label length). */ (void) memcpy (dst, &(src[srcpos+1]), len); /* Now find remaining label length bytes * and convert them to dots. */ for(pos = src[srcpos]; /* Read the first label length. */ '\0' != (next = dst[pos]); /* While label length is > 0... */ pos += next + 1) /* Move one byte beyond label. */ { dst[pos] = '.'; } return(--len); /* Return string length. */ } /* L2_Decode */ int L1_Decode(uchar *name, /* Target. Minimum 16 bytes. */ uchar *src, /* Message buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int i; int suffix; uchar *p = &src[srcpos]; /* Make sure we have 32 bytes worth of message to read. */ if((srcmax - srcpos) < 32) { name[0] = '\0'; return(-1); } /* Convert each source pair to their original octet value. */ for(i = 0; i < 32; i++) name[i/2] = ((((int)(p[i]) - 'A') << 4) + ((int)(p[++i]) - 'A')); /* Copy out suffix byte and replace with nul terminator. */ suffix = name[15]; name[15] = '\0'; /* Trim off trailing spaces, if any. */ for(i = 14; (i >= 0) && (' ' == name[i]); i--) name[i] = '\0'; return(suffix); /* Return the suffix value as an int. */ } /* L1_Decode */'; /* Get encoded string length (doesn't include root label). */ len = strlen((char *)&(src[srcpos])); /* If length is zero, return the empty string. */ if(0 == len) return(0); /* Make sure name does not exceed source buffer length. */ if(len >= (srcmax - srcpos)) return(-1); /* Copy source to destination skipping the first label length byte * (but including the terminating nul label length). */ (void)memcpy(dst, &(src[srcpos+1]), len); /* Now find remaining label length bytes * and convert them to dots. */ for(pos = src[srcpos]; /* Read the first label length. */ 'int L2_Decode(uchar *dst, /* Decoded name target buffer. */ uchar *src, /* Encoded name source buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int len; int pos; int next; /* Be safe. */ dst[0] = '\0'; /* Get encoded string length (doesn't include root label). */ len = strlen((char *)&(src[srcpos])); /* If length is zero, return the empty string. */ if(0 == len) return(0); /* Make sure name does not exceed source buffer length. */ if(len >= (srcmax - srcpos)) return(-1); /* Copy source to destination skipping the first label length byte * (but including the terminating nul label length). */ (void) memcpy (dst, &(src[srcpos+1]), len); /* Now find remaining label length bytes * and convert them to dots. */ for(pos = src[srcpos]; /* Read the first label length. */ '\0' != (next = dst[pos]); /* While label length is > 0... */ pos += next + 1) /* Move one byte beyond label. */ { dst[pos] = '.'; } return(--len); /* Return string length. */ } /* L2_Decode */ int L1_Decode(uchar *name, /* Target. Minimum 16 bytes. */ uchar *src, /* Message buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int i; int suffix; uchar *p = &src[srcpos]; /* Make sure we have 32 bytes worth of message to read. */ if((srcmax - srcpos) < 32) { name[0] = '\0'; return(-1); } /* Convert each source pair to their original octet value. */ for(i = 0; i < 32; i++) name[i/2] = ((((int)(p[i]) - 'A') << 4) + ((int)(p[++i]) - 'A')); /* Copy out suffix byte and replace with nul terminator. */ suffix = name[15]; name[15] = '\0'; /* Trim off trailing spaces, if any. */ for(i = 14; (i >= 0) && (' ' == name[i]); i--) name[i] = '\0'; return(suffix); /* Return the suffix value as an int. */ } /* L1_Decode */' != (next = dst[pos]); /* While label length is > 0... */ pos += next + 1) /* Move one byte beyond label. */ { dst[pos] = '.'; } return(--len); /* Return string length. */ } /* L2_Decode */ int L1_Decode(uchar *name, /* Target. Minimum 16 bytes. */ uchar *src, /* Message buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int i; int suffix; uchar *p = &src[srcpos]; /* Make sure we have 32 bytes worth of message to read. */ if((srcmax - srcpos) < 32) { name[0] = 'int L2_Decode(uchar *dst, /* Decoded name target buffer. */ uchar *src, /* Encoded name source buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int len; int pos; int next; /* Be safe. */ dst[0] = '\0'; /* Get encoded string length (doesn't include root label). */ len = strlen((char *)&(src[srcpos])); /* If length is zero, return the empty string. */ if(0 == len) return(0); /* Make sure name does not exceed source buffer length. */ if(len >= (srcmax - srcpos)) return(-1); /* Copy source to destination skipping the first label length byte * (but including the terminating nul label length). */ (void) memcpy (dst, &(src[srcpos+1]), len); /* Now find remaining label length bytes * and convert them to dots. */ for(pos = src[srcpos]; /* Read the first label length. */ '\0' != (next = dst[pos]); /* While label length is > 0... */ pos += next + 1) /* Move one byte beyond label. */ { dst[pos] = '.'; } return(--len); /* Return string length. */ } /* L2_Decode */ int L1_Decode(uchar *name, /* Target. Minimum 16 bytes. */ uchar *src, /* Message buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int i; int suffix; uchar *p = &src[srcpos]; /* Make sure we have 32 bytes worth of message to read. */ if((srcmax - srcpos) < 32) { name[0] = '\0'; return(-1); } /* Convert each source pair to their original octet value. */ for(i = 0; i < 32; i++) name[i/2] = ((((int)(p[i]) - 'A') << 4) + ((int)(p[++i]) - 'A')); /* Copy out suffix byte and replace with nul terminator. */ suffix = name[15]; name[15] = '\0'; /* Trim off trailing spaces, if any. */ for(i = 14; (i >= 0) && (' ' == name[i]); i--) name[i] = '\0'; return(suffix); /* Return the suffix value as an int. */ } /* L1_Decode */'; return(-1); } /* Convert each source pair to their original octet value. */ for(i = 0; i < 32; i++) name[i/2] = ((((int)(p[i]) - 'A') << 4) + ((int)(p[++i]) - 'A')); /* Copy out suffix byte and replace with nul terminator. */ suffix = name[15]; name[15] = 'int L2_Decode(uchar *dst, /* Decoded name target buffer. */ uchar *src, /* Encoded name source buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int len; int pos; int next; /* Be safe. */ dst[0] = '\0'; /* Get encoded string length (doesn't include root label). */ len = strlen((char *)&(src[srcpos])); /* If length is zero, return the empty string. */ if(0 == len) return(0); /* Make sure name does not exceed source buffer length. */ if(len >= (srcmax - srcpos)) return(-1); /* Copy source to destination skipping the first label length byte * (but including the terminating nul label length). */ (void) memcpy (dst, &(src[srcpos+1]), len); /* Now find remaining label length bytes * and convert them to dots. */ for(pos = src[srcpos]; /* Read the first label length. */ '\0' != (next = dst[pos]); /* While label length is > 0... */ pos += next + 1) /* Move one byte beyond label. */ { dst[pos] = '.'; } return(--len); /* Return string length. */ } /* L2_Decode */ int L1_Decode(uchar *name, /* Target. Minimum 16 bytes. */ uchar *src, /* Message buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int i; int suffix; uchar *p = &src[srcpos]; /* Make sure we have 32 bytes worth of message to read. */ if((srcmax - srcpos) < 32) { name[0] = '\0'; return(-1); } /* Convert each source pair to their original octet value. */ for(i = 0; i < 32; i++) name[i/2] = ((((int)(p[i]) - 'A') << 4) + ((int)(p[++i]) - 'A')); /* Copy out suffix byte and replace with nul terminator. */ suffix = name[15]; name[15] = '\0'; /* Trim off trailing spaces, if any. */ for(i = 14; (i >= 0) && (' ' == name[i]); i--) name[i] = '\0'; return(suffix); /* Return the suffix value as an int. */ } /* L1_Decode */'; /* Trim off trailing spaces, if any. */ for(i = 14; (i >= 0) && (' ' == name[i]); i--) name[i] = 'int L2_Decode(uchar *dst, /* Decoded name target buffer. */ uchar *src, /* Encoded name source buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int len; int pos; int next; /* Be safe. */ dst[0] = '\0'; /* Get encoded string length (doesn't include root label). */ len = strlen((char *)&(src[srcpos])); /* If length is zero, return the empty string. */ if(0 == len) return(0); /* Make sure name does not exceed source buffer length. */ if(len >= (srcmax - srcpos)) return(-1); /* Copy source to destination skipping the first label length byte * (but including the terminating nul label length). */ (void) memcpy (dst, &(src[srcpos+1]), len); /* Now find remaining label length bytes * and convert them to dots. */ for(pos = src[srcpos]; /* Read the first label length. */ '\0' != (next = dst[pos]); /* While label length is > 0... */ pos += next + 1) /* Move one byte beyond label. */ { dst[pos] = '.'; } return(--len); /* Return string length. */ } /* L2_Decode */ int L1_Decode(uchar *name, /* Target. Minimum 16 bytes. */ uchar *src, /* Message buffer. */ int srcpos, /* Start position of name. */ int srcmax) /* Size of source buffer. */ { int i; int suffix; uchar *p = &src[srcpos]; /* Make sure we have 32 bytes worth of message to read. */ if((srcmax - srcpos) < 32) { name[0] = '\0'; return(-1); } /* Convert each source pair to their original octet value. */ for(i = 0; i < 32; i++) name[i/2] = ((((int)(p[i]) - 'A') << 4) + ((int)(p[++i]) - 'A')); /* Copy out suffix byte and replace with nul terminator. */ suffix = name[15]; name[15] = '\0'; /* Trim off trailing spaces, if any. */ for(i = 14; (i >= 0) && (' ' == name[i]); i--) name[i] = '\0'; return(suffix); /* Return the suffix value as an int. */ } /* L1_Decode */'; return(suffix); /* Return the suffix value as an int. */ } /* L1_Decode */ The L2_Decode() function copies the encoded NBT name to the destination buffer, skipping the first label length byte and replacing internal label length bytes with the dot character. That is, given the input string: "\x20EOGFGLGPCACACACACACACACACACACAAA\x03CAT\x03ORG"\x20EOGFGLGPCACACACACACACACACACACAAA\x03CAT\x03ORG""\x20EOGFGLGPCACACACACACACACACACACAAA\x03CAT\x03ORG\0"" it will produce the string: "EOGFGLGPCACACACACACACACACACACAAA.CAT.ORG" The L1_Decode() function decodes the First Level Encoded NetBIOS name, and hands back the suffix byte as its return value. |