4.2 NBT Name Service Packets

4.2 NBT Name Service Packets

RFC 1002 lists 17 different Name Service packet types, constructed from three basic building blocks:

  • header,

  • query records, and

  • resource records.

These pieces are described in more detail below.

4.2.1 Name Service Headers

The header is an array of six 16-bit values, as follows :

graphics/49fig01.gif

Managing Name Service headers is fairly straightforward. With the exception of the FLAGS field, all of the fields are simple unsigned integers. The entire thing can be represented in memory as an array of unsigned short int , or whatever is appropriate in your programming language of choice.

The FLAGS field is further broken down thus:

graphics/49fig02.gif

Handling the bits in the FLAGS field is fairly trivial for any seasoned programmer. One simple solution is to shift the values given in RFC 1002, Section 4.2.1.1 into their absolute positions . For example, an OPCODE value of 0x7 ( WACK ) would be left-shifted 11 bits to align it properly in the OPCODE subfield:

 (0x0007 << 11) = 0x3800 = 0  0111  00000000000(bin) 

...which puts it where it's supposed to be:

graphics/49fig03.gif

Listing 4.2 presents NS_Header.h , a header file that will be referenced as we move forward. It provides a set of re-aligned FLAGS subfield values plus a few extra constants. These values will be covered below, when we explain how to use each of the Name Service message types.

Listing 4.2 Name Service packet header FLAGS subfield values: NS_Header.h
 #define NBTNS_R_BIT       0x8000  /* The 'R'esponse bit   */ /* OPCODE values */ #define OPCODE_QUERY      0x0000  /* Query        (0<<11) */ #define OPCODE_REGISTER   0x2800  /* Registration (5<<11) */ #define OPCODE_RELEASE    0x3000  /* Release      (6<<11) */ #define OPCODE_WACK       0x3800  /* WACK         (7<<11) */ #define OPCODE_REFRESH    0x4000  /* Refresh      (8<<11) */ #define OPCODE_ALTREFRESH 0x4800  /* Alt Refresh  (9<<11) */ #define OPCODE_MULTIHOMED 0x7800  /* Multi-homed  (f<<11) */ #define OPCODE_MASK       0x7800  /* Mask                 */ /* NM_FLAGS subfield bits */ #define NM_AA_BIT         0x0400  /* Authoritative Answer */ #define NM_TR_BIT         0x0200  /* TRuncation flag      */ #define NM_RD_BIT         0x0100  /* Recursion Desired    */ #define NM_RA_BIT         0x0080  /* Recursion Available  */ #define NM_B_BIT          0x0010  /* Broadcast flag       */ /* Return Codes */ #define RCODE_POS_RSP     0x0000  /* Positive Response    */ #define RCODE_FMT_ERR     0x0001  /* Format Error         */ #define RCODE_SRV_ERR     0x0002  /* Server failure       */ #define RCODE_NAM_ERR     0x0003  /* Name Not Found       */ #define RCODE_IMP_ERR     0x0004  /* Unsupported request  */ #define RCODE_RFS_ERR     0x0005  /* Refused              */ #define RCODE_ACT_ERR     0x0006  /* Active error         */ #define RCODE_CFT_ERR     0x0007  /* Name in conflict     */ #define RCODE_MASK        0x0007  /* Mask                 */ /* Used to set the record count fields. */ #define QUERYREC          0x1000  /* Query Record         */ #define ANSREC            0x0100  /* Answer Record        */ #define NSREC             0x0010  /* NS Rec (never used)  */ #define ADDREC            0x0001  /* Additional Record    */ 

The NAME_TRN_ID field is the transaction ID, which should probably be handled by the bit of code that sends and receives the NBT messages. Many implementations use a simple counter to generate new transaction IDs (Samba uses a random number generator), but these should always be checked to ensure that they are not, by chance, the same as the transaction ID of a conversation initiated by some other node. Better yet, the originating node's IP address should be used as an additional key for segregating transactions.

The four COUNT fields indicate the number of Question and Resource Records which follow. In theory, each of these fields can contain a value in the range 0..65535. In practice, however, the count fields will contain either 0 or 1 as shown in the record layouts in RFC 1002, Section 4.2. It appears as though some implementations either ignore these fields or read them as simple booleans.

One final consideration is the byte order of NBT messages. True to its DNS roots, NBT uses network byte order (big-endian). Some microprocessors including Alpha, MIPS, and Intel i386 family use or can use little-endian byte order. [2] If your target system is little-endian, or if you want your code to be portable, you will need to ensure that your integers are properly converted to and from network byte order. Many systems provide the htonl () , htons() , ntohl () , and ntohs() functions for exactly this purpose.

[2] Big-endian byte order is also known as "normal," "intuitive," or "obvious" byte order. Little-endian is sometimes referred to as "annoying," " dysfunctional ," or "stupid." These designations do not, of course, reflect any bias or preference.

Bizarre Twist Alert

graphics/alert.gif

The SMB protocol was originally built to run on DOS. DOS was originally built to run on Intel chips, so SMB is little-endian... the opposite of the NBT transport!


This next bit of code is nbt_nsHeader.c . It shows how to create and parse NBT Name Service headers. As with all of the code presented in this book, it is designed to be illustrative , not efficient. (We know you can do better.)

Listing 4.3 Read and write Name Service headers: NS_Header.c
 #include <netinet/in.h>     /* htons(), ntohs(), etc. */ #include "NS_Header.h"      /* From Listing 4.2.      */ void Put_NS_TID( ushort hdr[], ushort TrnID )   /* ---------------------------------------------------- **    * Store the transaction ID in the Name Service header.    * ---------------------------------------------------- **    */   {   hdr[0] = htons( TrnID );   } /* Put_NS_TID */ void Put_NS_Hdr_Flags( ushort hdr[], ushort flags )   /* ---------------------------------------------------- **    * Store the flags in the NBT Name Service header.    * ---------------------------------------------------- **    */   {   hdr[1] = htons( flags );   } /* Put_NS_Hdr_Flags */ void Put_NS_Hdr_Rec_Counts( ushort hdr[], int reccount )   /* ---------------------------------------------------- **    * Place (ushort)1 into each record count field for    * which the matching flag bit is set in reccount.    * ---------------------------------------------------- **    */   {   ushort one;   one = htons( 1 );   hdr[2] = ( QUERYREC & reccount ) ? one : 0;   hdr[3] = ( ANSREC   & reccount ) ? one : 0;   hdr[4] = ( NSREC    & reccount ) ? one : 0;   hdr[5] = ( ADDREC   & reccount ) ? one : 0;   } /* Put_NS_Hdr_Rec_Counts */ ushort Get_NS_Hdr_TID( ushort hdr[] )   /* ---------------------------------------------------- **    * Read and return the transaction ID.    * ---------------------------------------------------- **    */   {   return( ntohs( hdr[0] ) );   } /* Get_NS_Hdr_TID */ ushort Get_NS_Hdr_Flags( ushort hdr[] )   /* ---------------------------------------------------- **    * Read and return the flags field.    * ---------------------------------------------------- **    */   {   return( ntohs( hdr[1] ) );   } /* Get_NS_Hdr_Flags */ int Get_NS_Hdr_Rec_Counts( ushort hdr[] )   /* ---------------------------------------------------- **    * Convert the four record count fields into a single    * flagset.    * ---------------------------------------------------- **    */   {   int tmp = 0;   if( hdr[2] )     tmp = QUERYREC;   if( hdr[3] )     tmp = ANSREC;   if( hdr[4] )     tmp = NSREC;   if( hdr[5] )     tmp = ADDREC;   return( tmp );   } /* Get_NS_Hdr_Rec_Counts */ 

4.2.2 Name Service Question Records

The question record is also simple. It consists of an encoded NBT name (in the QUESTION_NAME field) followed by two unsigned 16-bit integer fields: the QUESTION_TYPE and QUESTION_CLASS .

The length of an encoded NBT name is at least 34 bytes, but it will be longer if a Scope ID is used, so the QUESTION_NAME field has no fixed length. There is also no padding done to align the integer fields. The QUESTION_TYPE and QUESTION_CLASS follow immediately after the QUESTION_NAME .

graphics/53fig01.gif

There are only two valid values for the QUESTION_TYPE field. These are:

NB == 0x0020 indicates a standard Name Query,

NBSTAT == 0x0021 indicates a Node Status Query.

The QUESTION_CLASS field always has a value of:

IN == 0x0001 indicates the "Internet Class."

Go back and take a look at the broadcast name query example presented earlier. In that example, we hard-coded both the NBT Name Service header and the tail-end of the question record. Now that you have a clearer understanding of the fields involved, you should be able to design much more flexible code. Here's a start:

Listing 4.4a Reading/writing question records: NS_Qrec.h
 /* Query Type */ #define QTYPE_NB      0x0020  /* Name Query     */ #define QTYPE_NBSTAT  0x0021  /* Adapter Status */ /* Query Class */ #define QCLASS_IN     0x0001  /* Internet Class */ 
Listing 4.4b Reading/writing question records: NS_Qrec.c
 #include <string.h>     /* For memcpy() */ #include <netinet/in.h> /* htons(), ntohs(), etc. */ #include "NS_Qrec.h" int Put_Qrec( uchar       *dst,               const uchar *name,               const uchar  pad,               const uchar  sfx,               const uchar *scope,               const ushort qtype )   /* ---------------------------------------------------- **    * Store the fully encoded NBT name in the destination    * buffer.  Also write the QUERY_TYPE and QUERY_CLASS    * values.    * ---------------------------------------------------- **    */   {   int    len;   ushort tmp;   ushort qclass_in;   qclass_in = htons( QCLASS_IN );   /* Validate the qtype. */   if( (QTYPE_NB != qtype)    && (QTYPE_NBSTAT != qtype ) )     return( -1 );   len = L2_Encode( dst, name, pad, sfx, scope );   if( len < 0 )     return( len );   tmp = htons( qtype );   (void)memcpy( &(dst[len]), &tmp, 2 );   len += 2;   (void)memcpy( &(dst[len]), &qclass_in, 2 );   return( len + 2 );   } /* Put_Qrec */ ushort Get_Qtype( const uchar *qrec, int offset )   /* ---------------------------------------------------- **    * Read the QUERY_TYPE field from a query record.    * Note that the offset parameter can be derived using    * L2_Decode() function in Listing 4.1.  Either that,    * or use 1+strlen( qrec ).    * ---------------------------------------------------- **    */   {   ushort tmp;   /* Read the two bytes from the packet.    */   tmp  = (ushort)(qrec[offset]) * 256;   tmp = qrec[1+offset];   /* Convert to host byte order and return. */   return( ntohs( tmp ) );   } /* Get_Qtype */ 

4.2.3 Name Service Resource Records

For convenience, we will break the Resource Record into three sub- parts :

  • the Name section,

  • the TTL field, and

  • the Resource Data section.

The Name section has the same structure as a Query Entry record, except that the RR_NAME field may contain a 16-bit Label String Pointer instead of a complete NBT name.

graphics/56fig01.gif

The RR_TYPE field is used to indicate the type of the resource record, which has an effect on the structure of the resource data section. The available values for this field are:

A == 0x0001

(not used in practice)

NS == 0x0002

(not used in practice)

NULL == 0x000A

(not used in practice)

NB == 0x0020

NBSTAT == 0x0021

The values marked as "not used in practice" are described in the RFCs, and indicated as valid values, but are never really used in modern implementations. The value of RR_TYPE will be NB except in a NODE STATUS REPLY , in which case NBSTAT is used.

As with the question record, the RR_CLASS field always has a value of:

 IN == 0x0001 

The TTL field follows the name section. It indicates the "Time To Live" value associated with a resource record. Each NBT name-to-IP-address mapping in the NBNS database has a TTL value. This allows records to "fade out" if they are not renewed or properly released. The TTL field is an unsigned long integer, measured in seconds. A value of zero indicates infinite TTL .

graphics/56fig02.gif

The last sub-part of the resource record is the resource data section, which is made up of two fields:

graphics/57fig01.gif

The RDLENGTH field is an unsigned 16-bit integer value indicating the length, in bytes, of the RDATA field. The structure of the contents of the RDATA field will vary from one message type to another.

The Resource Record structure, as described in Section 4.2.1.3 of RFC 1002, looks just like this:

graphics/57fig02.gif

It is always good to have some code to play with. This next set of functions can be used to manipulate Resource Records.

Listing 4.5a Name Service Resource Records: NS_Rrec.h
 /* Label String Pointer. */ #define LSP           0xC00C  /* Pointer to offset 12 */ /* Resource Record Type. */ #define RRTYPE_A      0x0001  /* IP Addr RR (unused)  */ #define RRTYPE_NS     0x0002  /* Name Server (unused) */ #define RRTYPE_NULL   0x000A  /* NULL RR (unused)     */ #define RRTYPE_NB     0x0020  /* NetBIOS              */ #define RRTYPE_NBSTAT 0x0021  /* NB Status Response   */ /* Resource Record Class. */ #define RRCLASS_IN    0x0001  /* Internet Class       */ 
Listing 4.5b Name Service Resource Records: NS_Rrec.c
 #include <string.h>     /* For memcpy() */ #include <netinet/in.h> /* htons(), ntohs(), etc. */ #include "NS_Rrec.h" int Put_RRec_Name( uchar       *rrec,                    const uchar *name,                    const uchar  pad,                    const uchar  sfx,                    const uchar *scope,                    const ushort rrtype )   /* ---------------------------------------------------- **    * Create and store the fully qualified NBT name in the    * destination buffer.  Also store the RR_TYPE and    * RR_CLASS values.    * Return the number of bytes written.    * ---------------------------------------------------- **    */   {   int    len;   ushort tmp;   ushort rrclass_in;   /* Validate the rrtype.    * Note that we exclude the A, NS, and NULL RRTYPEs    * as these are never used.    */   if( (RRTYPE_NB != rrtype)    && (RRTYPE_NBSTAT != rrtype ) )     return( -1 );   len = L2_Encode( rrec, name, pad, sfx, scope );   if( len < 0 )     return( len );   tmp = htons( rrtype );   (void)memcpy( &(rrec[len]), &tmp, 2 );   len += 2;   rrclass_in = htons( RRCLASS_IN );   (void)memcpy( &(rrec[len]), &rrclass_in, 2 );   return( len + 2 );   } /* Put_RRec_Name */ int Put_RRec_LSP( uchar *rrec, const ushort rrtype )   /* ---------------------------------------------------- **    * Write a Label String Pointer (LSP) instead of an NBT    * name.  RR_TYPE and RR_CLASS are also written.    * Return the number of bytes written (always 6).    * ---------------------------------------------------- **    */   {   ushort tmp;   ushort lsp;   ushort rrclass_in;   lsp = htons( 0xC00C );   (void)memcpy( rrec, &lsp, 2 );   tmp = htons( rrtype );   (void)memcpy( &(rrec[2]), &tmp, 2 );   rrclass_in = htons( RRCLASS_IN );   (void)memcpy( &(rrec[4]), &rrclass_in, 2 );   return( 6 );   } /* Put_RRec_LSP */ int Put_RRec_TTL( uchar *rrec, int offset, ulong ttl )   /* ---------------------------------------------------- **    * Write the TTL value at rrec[offset].    * By this point it should be obvious that functions or    * macros for transferring long and short integers to    * and from packet buffers would be a good idea.    * ---------------------------------------------------- **    */   {   ttl = htonl( ttl );   (void)memcpy( &(rrec[offset]), &ttl, 4 );   return( 4 );   } /* Put_RRec_TTL */ int Is_RRec_LSP( const uchar *rrec )   /* ---------------------------------------------------- **    * Check the Resource Record to see if the name field    * is actually a Label String Pointer.    *    * If the name is not an LSP, the function returns 0.    * If the name is a valid LSP, the function returns 12    * (which is the offset into the received packet at    * which the QUERY_NAME can be found).    * If the name contains an invalid label length, or    * an invalid LSP, the function will return -1.    * ---------------------------------------------------- **    */   {   if( 0 == (0xC0 & rrec[0]) )     return( 0 );  /* Not an LSP */   if( (0xC0 == rrec[0]) && (0x0C == rrec[1]) )     return( 12 ); /* Valid LSP */   return( -1 );   /* Bogon */   } /* Is_RRec_LSP */ ushort Get_RR_type( const uchar *rrec, int offset )   /* ---------------------------------------------------- **    * Read the RR_TYPE value.  The offset can be    * determined by decoding the NBT name using the    * L2_Decode() function from Listing 4.1.    * ---------------------------------------------------- **    */   {   ushort tmp;   /* Read the two bytes from the packet.    */   (void)memcpy( &tmp, &(rrec[offset]), 2 );   /* Convert to host byte order and return. */   return( ntohs( tmp ) );   } /* Get_RR_type */ ulong Get_RRec_TTL( const uchar *rrec, int offset )   /* ---------------------------------------------------- **    * Read the TTL value.    * ---------------------------------------------------- **    */   {   ulong tmp;   (void)memcpy( &tmp, &(rrec[offset]), 4 );   return( ntohl( tmp ) );   } /* Get_RRec_TTL */ 


Implementing CIFS. The Common Internet File System
Implementing CIFS: The Common Internet File System
ISBN: 013047116X
EAN: 2147483647
Year: 2002
Pages: 210

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net