1.1 A Short Bio of NetBIOS
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
In those days spirits were |
It all started back in the frontier days of the PC when
Microsoft was a lot smaller, IBM seemed a whole lot bigger, and
Apple owned personal computer territory as far as the eye could
see. Back then, you didn't need no dang standards. If you wanted
to sell LANs, you just went out and branded yourself a protocol.
Apple had AppleTalk, Digital had DECnet and, for their longhorn
Mainframes, IBM had Systems Network Architecture
(SNA). SNA was a mighty big horse for little PCs, so IBM hired on a
company called Sytek
PC Network was a Local Area Network (LAN) system designed to support about 80 nodes at best, with no provision for routing. NetBIOS (Network Basic Input Output System) was the software interface to the PC Network hardware. It offered a set of commands that could control the hardware, establish and delete sessions, transfer data, etc. 1.1.1 NetBIOS and DOS: The Early YearsStarting with DOS version 3.1, Microsoft used the NetBIOS API to transport SMB file service messages. They created something called a redirector, and its job was to catch disk drive or port references (eg. "C:" or "LPT3:") and look them up in a table. If the device was not in the table, the call was passed along to DOS. If the device was in the table, then the call would be redirected. For example:
1.2 Speaking NetBIOSThe hardware part of IBM's PC Network is no longer in use and
the protocol that actually ran on the wire is all but forgotten, yet
the NetBIOS API remains. Vast untold hoards of programs--including
DOS itself--were written to use NetBIOS. Like COBOL, it may never
die.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Genuine Imitation |
Many vendors, eager for a piece of the Microsoft desktop pie, figured out how to implement the NetBIOS API on top of other protocols. There is NetBIOS over DECnet, NetBIOS over NetWare, NetBIOS over mashed potatoes and gravy with creamed corn, NetBIOS over SNA, NetBIOS over TCP/IP, and more. Of these, the most popular, tasty, and important is NetBIOS over TCP/IP, and that's what this chapter is really all about. NetBIOS over TCP/IP is sometimes called NetBT or NBT. Folks from IBM--for reasons unfathomable--sometimes call it TCPBEUI. NBT is the simplest and most common name, so we'll stick with that. On the 7-layer OSI reference model, NetBIOS is a session-layer (layer 5) API. Under DOS and its offspring, applications talk to NetBIOS by filling in a record structure known as a Network Control Block (NCB) and signaling an interrupt. The NCBs are used to pass commands and messages between applications and the underlying protocol stack. Fortunately, the NetBIOS API is specific to DOS and its kin. Unix and other systems do not need to implement the NetBIOS API, as there is no legacy of programs that use it. Instead, these systems participate in NBT networks by directly handling the TCP and UDP packets described in two Internet Engineering Task Force (IETF) Request for Comments documents: RFC 1001 and RFC 1002 (known collectively as Internet Standard #19). These RFCs describe a set of services which work together to create virtual NetBIOS LANs over IP. 1.2.1 Emulating "NetBIOS LANs"
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
...there is often confusion in |
At this point, we hit an interesting twist in the terminology. NetBIOS is a driver that presents an API; it is neither a protocol nor a topology. The API does, however, make a number of assumptions about the workings of the underlying network, and it presents some quirky restrictions. The terms "NetBIOS Network" and "NetBIOS LAN" are commonly used to identify the network architecture that is, essentially, defined by the NetBIOS API. RFC 1001 and 1002 list three basic services which must be supported in order to implement NetBIOS LAN emulation. These are:
The Name Service is used to map NetBIOS names (addresses) to IP addresses in the underlying IP network. The Datagram Service provides for the delivery of NetBIOS datagrams via UDP, and the Session Service is used to establish and maintain point-to-point, connection-oriented NetBIOS sessions over TCP. 1.2.1.1 The NetBIOS Name Service
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The NetBIOS name service is the collection of procedures through which nodes acquire, defend, and locate the holders of NetBIOS names. -- RFC 1001, Section 15 |
The NetBIOS LAN architecture is very simple. No routers, no switches--just a bunch of nodes connected to a (virtual) wire. There is no need for separate hardware addresses, network addresses, or even port numbers as there is in IP. Instead, the communications endpoints are identified by 16-byte strings known as "NetBIOS Names". NetBIOS addressing is dynamic. Applications may add names as needed, and remove those names when they are finished. Each node on the LAN will also have a default name, known as the Machine Name or the Workstation Service Name, which is typically added when NetBIOS starts. The process of adding a name is called registration. There are two kinds of names that can be registered: unique and group. Group names may be shared by multiple clients, thus providing a mechanism for multicast. In contrast, unique names may only be used by one client per LAN. Keep in mind, though, that these are virtual LANs which may actually be spread out across different subnets in a routed IP internetwork. The Name Service is supposed to keep track of all of the NetBIOS
names in use within the virtual LAN, and ensure that messages sent to
a given NetBIOS name are directed to the correct underlying IP
address. It does this in two ways:
These are the two basic modes of NetBIOS Name Resolution over NBT. There are, of course, others. The RFCs describe 'M mode' (mixed mode), which combines P and B mode characteristics. 'H mode' (hybrid mode) was introduced later. It is similar to M mode except for the order in which B and P mode behavior is applied. The Name Service runs on UDP port 137. According to the RFCs the use of TCP port 137 can be negotiated for some queries, though few (if any) implementations actually support this. 1.2.1.2 The NetBIOS Datagram Service
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Upon receipt, the duct tape is removed and the paper copy of the datagram is optically scanned into a[n] electronically transmittable form. -- RFC 1149 |
In the IP world, TCP provides connection-oriented sessions in which packets are acknowledged, put in order, and retransmitted if lost. This creates the illusion of a continuous, sequential data stream from one end to the other. In contrast, UDP datagrams are simply sent. Thus, UDP requires less overhead, but it is less reliable than TCP. NetBIOS also provides connection-oriented (session) and connectionless (datagram) communications. Naturally, NBT maps NetBIOS sessions to TCP and NetBIOS datagrams to UDP. The Datagram Distribution Service is the NBT service that handles NetBIOS datagram transport. It runs on UDP port 138, and can handle unicast (also known as "specific"), multicast (group), and broadcast NetBIOS datagrams.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
In theory, theory and practice are the same. In practice, they're not. -- Unknown |
The Datagram Service is probably the second-least well understood aspect of NBT, most likely because correct implementation isn't critical to filesharing. Many implementations get it wrong, and there is much debate over the value of getting it right. 1.2.1.3 The NetBIOS Session ServiceThe Session Service is the traditional transport for SMB, and this is our primary reason for caring about NetBIOS at all. The Session Service runs on TCP port 1393. There is no particular mechanism for multicast or broadcast because each session is, by definition, a one-to-one connection. The RFCs do, however, briefly discuss what might happen if a session setup request were sent to a group name (see RFC 1001, Section 16.1.1.2). We will get to the details of session creation, use, and closure when we discuss Session Service implementation.
1.2.2 Scope: The Final FrontierThis is a good point at which to get up, stretch, make a nice hot cup of tea for yourself, take a soothing bath, play with your cat, go for a long walk in the park, take dance lessons, volunteer in your community, sort and organize your old photographs, or join a United Nations Peace Keeping Force. The Datagram Service was previously described as 'the second-least well understood aspect of NBT'. Guess which bit wins first prize. Scope is an oddity of NBT, not because it was a bad idea (though perhaps it was) but because few have ever bothered to really understand it. In practice this feature is rarely used, in part because it is rarely implemented to its full potential. In the RFCs, the term scope is used as a name for:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Proof by Scope: The proof of this theorem is beyond the scope of this book. -- Jonathan Young, PhD. |
...but the last of these is beyond the scope of this discussion, so let's take a closer look at the first two. Scope is explained in RFC 1001, Section 9, which starts off by saying: A "NetBIOS Scope" is the population of computers across which a registered NetBIOS name is known. NetBIOS broadcast and multicast datagram operations must reach the entire extent of the NetBIOS scope. This basically means all nodes connected to the virtual LAN. So, for B nodes the NetBIOS scope consists of all nodes within the local IP broadcast domain that are running NBT. For P nodes, the NetBIOS scope includes all nodes across the routed internetwork that run NBT and share the same NBNS. For an M or H node, the scope is the union of the local broadcast and the NBNS scopes. This is all quite straight-forward when all NBT nodes are of the same node type, but strange things can happen when you mix modes, particularly in a routed environment.
We now have a good handle on our first definition of scope: "the set of NetBIOS nodes that participate in a virtual LAN". What about the second: "an identifier used to distinguish one virtual LAN from another"? (This is a good point at which to get up, stretch, make a nice hot cup of tea for yourself...) Every scope has a name, called the Scope Identifier (Scope ID). The most common Scope ID is the empty string: "". Indeed, this is the default in Windows, Samba, jCIFS, and every other system encountered so far. The only problem with this name is that it becomes too easy to forget that the Scope ID exists. We have already seen that distinct NetBIOS vLANs can be created
by using the behavior of B, P, M, and H nodes to create separate
scopes. For example, multiple scopes are defined when multiple
independent NBNS's provide service for P nodes. B nodes on separate
IP LANs are also in separate scopes, and so on. The Scope ID provides
another, more refined mechanism for separating scopes.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I can listen, but I can't hear. -- Turn a Deaf Ear Rab Noakes |
Think of an IP LAN with a bunch of B nodes. Some of the B nodes have Scope ID DOG, and others have Scope ID CAT. Only members of scope DOG will listen to messages sent with that ID; the cats will ignore messages sent to the dogs. So, even though all of the B nodes are on the same wire, we have two separate scopes. The same applies to P and M nodes. The Scope IDs identify, and separate, virtual NetBIOS LANs. Note, though, that an NBNS will handle requests from any node regardless of scope. A single NBNS server can, therefore, support multiple scopes. According to RFC 1001/1002, a node may belong to more than one scope. In practice, however, it is much easier to choose a single scope and stick with it. This is particularly true for DOS and Windows systems because NetBIOS itself has no concept of scope. The Scope ID is a feature of NBT, and programs that call the NetBIOS API have no way of telling NBT which scope to use. The RFCs suggest that extensions might be added to NetBIOS to manage scope, but using those extensions would require changes to applications. Further, other NetBIOS transports would not support the extensions which would result in compatibility problems.
1.2.3 Thus Endeth the OverviewNow that you have a clear and precise understanding of the workings of NetBIOS over TCP, go read RFC 1001. That ought to muddy the waters a bit. Clear or not, the next step is to write some code and see what works--and what doesn't. Actual implementation will provide a lot of opportunity to discuss details, bugs, and common errors. 1.3 The Basics of NBT Implementation
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
This is where the rubber meets the road. -- Unknown |
Ready? We have identified the three key parts of NBT: the Name Service, the Datagram Service, and the Session Service. This is enough to get us started. We will begin by coding up a simple Name Service Query, just to see what kind of trouble that gets us into. Before we start, though, it's probably a good idea to check our tools.
Ready! In this section, we will implement a broadcast NAME QUERY REQUEST. That is, B mode name resolution. This will allow us to introduce some of the basic concepts and establish a frame of reference. In other words, we have to start somewhere and this seems to be as good a place as any. 1.3.1 You Got the Name, Look Up the Number
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Shirley Shirley Bo Birley Banana Fanna Fo Firley -- The Name Game, Shirley Ellis |
The structure of an NBT name query is similar to that of a Domain Name System query. As RFC 1001, section 11.1.1, explains: The NBNS design attempts to align itself with the Domain Name System in a number of ways. The goal of this attempted alignment was an eventual merger between the NBNS and the DNS system. The NBT authors even predicted dynamic DNS update. With Windows 2000, Microsoft did move CIFS naming services to Dynamic DNS, though the mechanism is not quite what was envisioned by the authors of the NBT RFCs. 1.3.1.1 Encoding NetBIOS NamesRFC 1001 & 1002 reference RFC 883 when discussing domain name syntax rules. RFC 883 was later superseded by RFC 1035, but both give the same preferred4 syntax for domain names:
<domain> ::= <subdomain> | " "
<subdomain> ::= <label> | <subdomain> "." <label>
<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
<let-dig-hyp> ::= <let-dig> | "-"
<let-dig> ::= <letter> | <digit>
<letter> ::= any one of the 52 alphabetic characters
A through Z in upper case and a through
z in lower case
<digit> ::= any one of the ten digits 0 through 9
This is the syntax that the NBT authors tried to match. Unfortunately, except for the 16-byte length restriction, there are few syntax rules for NetBIOS names. With a few notable exceptions just about any octet value may be used, so the NBT authors came up with a scheme to force NetBIOS names into compliance. Here's how it works:
This is called First Level Encoding, and is described in RFC 1001, Section 14.1. Using First Level Encoding, the name "Neko" would be converted as follows:
This results in the string:
EOGFGLGPCACACACACACACACACACACACA | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Coding style is very personal... -- Linus Torvalds |
...and here is our first bit of code: This function reads up to 16 characters from the input string
name and converts each to the encoded format, stuffing
the result into the target string dst. The space
character (0x20) always converts to the two-character value "CA" so,
if the source string is less than 16 bytes, we simply pad the target
string with CACA. Note that the target character array must
be at least 33 bytes long--one extra byte to account for the nul
terminator6.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Bedevere: Oooohoohohooo! Lancelot: No, no. 'Aaaauugggh', at the back of the throat. Aaauugh. -- Monty Python And The Holy Grail, Monty Python's Flying Circus |
1.3.1.2 Fully Qualified NBT NamesNow that we've managed to convert the NetBIOS name into a DNS-aligned form, it is time to combine it with the NBT Scope ID. The result will be a fully-qualified NBT address, which we will call the "NBT Name". To be pedantic, when the RFCs talk about First Level Encoding, this fully qualified form is what they really mean. As expected, the syntax of the Scope ID follows the DNS recommendations given in RFC 883 (and repeated in RFC 1035). That is, a Scope ID looks like a DNS name. So, if the Scope ID is cat.org, and the NetBIOS name is Neko, the resultant NBT name would be: EOGFGLGPCACACACACACACACACACACACA.CAT.ORG Imagine typing that into your web browser. This is why the RFC 1001/1002 scheme for merging the NBNS with the DNS never took hold. 1.3.1.3 Second Level EncodingNow that we have an NBT name in a nice familiar format, it is time to convert it into something else. DNS names (and, therefore, NBT names) are made up of labels separated by dots. Dividing the name above into its component labels gives us:
The Second Level Encoded NBT name is a concatenation of the lengths and the labels, as in: '\x20' + "EOGFGLGPCACACACACACACACACACACACA" + '\x03' + "CAT" + '\x03' + "ORG" + '\0' The empty label at the end is important. It is a label of zero
length, and it represents the root of the DNS (and NBT) namespace.
That means that the final nul byte is part of the encoded NBT
name, and not a mere terminator. In practice, you can manipulate
the encoded NBT name as if it were a nul-terminated string, but always
keep in mind that it is really a series of length-delimited strings7.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Any useful piece of code deserves to be rewritten at least once. -- Unknown |
Our second bit of code will convert a NetBIOS name and Scope ID into a Second Level Encoded string: Not the prettiest piece of code, but it does the job. We will run through the function quickly, just to familiarize you with the workings of this particular programmer's twisted little brain. If the code is fairly obvious to you, feel free to skip ahead to the next section.
if( NULL == L1_Encode( &dst[1], name ) )
return( -1 );
dst[0] = 0x20;
lenpos = 33;
Call L1_Encode() to convert the NetBIOS name into its First Level Encoded form, then prefix the encoded name with a length byte. This gives us the first label of the encoded NBT name. Note that we check for a NULL return value. This is paranoia on the programmer's part since this version of L1_Encode() does not return NULL. (An improved version of L1_Encode() might return NULL if it detected an error.) The variable lenpos is set to the offset at which the next length byte will be written. The L1_Encode() function has already placed a nul byte at this location so, if the scope string is empty, the NBT name is already completely encoded.
if( '\0' != *scope )
{
do
{
:
} while( '.' == *(scope++) );
dst[lenpos] = '\0';
}
The processing of scope labels is contained within the do..while loop. If the scope is empty, then we can skip this loop entirely. Note that the root label is added to the end of the target string, dst, following the scope labels.
for( i = 0, j = (lenpos + 1);
('.' != scope[i]) && ('\0' != scope[i]);
i++, j++)
dst[j] = scope[i];
Run through the current label, copying it to the destination string. The variable i keeps track of the length of the label. A dot or a nul will mark the end of the current label.
dst[lenpos] = (uchar)i;
lenpos += i + 1;
scope += i;
Write the length byte for the current label, and then move on to the next by advancing lenpos. The variable scope is advanced by the length of the current label, which should leave it pointing to the dot or nul that terminated the label. It will be advanced one more byte within the while clause at the end of the loop. Hopefully that was a nice, short waste of time. As we progress, it will become necessary to move more quickly and provide less code and less analysis of the code. There is a lot of ground to cover. 1.3.1.4 Name Service Packet HeadersOnce again, our attention is drawn to the ancient lore of RFC 883, which was written about four years ahead of RFC 1001/1002 and was eventually replaced by RFC 1035. The comings and goings of the RFCs are a study unto themselves. NBT Name Service packets are an intentional rip-off of DNS Messages. New flag field values, operation codes, and return codes were added but the design was in keeping with the goal of eventually merging NBNS services into the DNS. This, conceptually, is what a Name Service packet header looks like:
...and here is a description of the fields:
So, for a broadcast NAME QUERY REQUEST, our header will look like this:
To make it easier to write the code for the above query, we will hand-convert the header into a string of bytes. We could do this in code (in fact, that will be necessary for a real implementation), but dealing with such details at this point would be an unnecessary tangent. So...
unsigned char header[] =
{
0x07, 0xAC, /* 1964 == 0x07AC. */
0x01, 0x10, /* 0 0000 0010001 0000 */
0x00, 0x01, /* One name query. */
0x00, 0x00, /* Zero answers. */
0x00, 0x00, /* Zero authorities. */
0x00, 0x00 /* Zero additional. */
};
1.3.1.5 The Query EntryThe query entries follow the header. A query entry consists of a Level 2 Encoded NBT name followed by two additional fields: the QUESTION_TYPE and QUESTION_CLASS. Once again, this is taken directly from the DNS query packet. Under NBT, the QUESTION_TYPE field is limited to two possible values, representing the two types of query that are defined in the RFCs. These are:
Only one QUESTION_CLASS is defined for NBT, and that is the Internet Class: 0x0001. So, our completed NAME QUERY REQUEST packet will consist of:
1.3.1.6 Some Trouble Ahead
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
He felt that if once he went beyond the crown of the pass and took one step veritably down into the land of Mordor, that step would be irrevocable. He could never come back. -- J.R.R. Tolkein The Lord of the Rings |
It would seem that it should now be easy to send a broadcast name query. Just put the pieces together and send them to UDP port 137 at the broadcast address. Yes that should be easy...except that we are now crossing the line between theory and practice, and that means trouble. Be brave.
1.3.1.7 Finally! A Simple Broadcast Name Query
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
...almost, but not quite, entirely unlike tea... -- The Hitchhikers Guide To The Galaxy, Douglas Adams |
This next bit of code is full of shortcuts. The packet header is hard-coded, as are the QUESTION_TYPE and QUESTION_CLASS. No syntax checking is done on the scope string. Worst of all, the program sends the query but does not bother to listen for a reply. For that, we will use a sniffer. Tools such as the nmblookup utility that comes with Samba, or Microsoft's nbtstat program, could also be used to send a name query. The goal, however, is to implement these tools on our own, and the next bit of code gives us a start10. The updated L1_Encode() function takes two new parameters: pad and sfx. These allow us to specify the padding character and the suffix, respectively. The L2_Encode() function also takes these additional parameters, so that it can pass them along to L1_Encode(), and both functions make use of toupper() to ensure that the NetBIOS name and Scope ID are in upper case. The function Send_Nbtn_Bcast() does the job of transmitting a block of data via UDP. The destination is port UDP/137 at the universal broadcast address. The program mainline simply strings together the various pieces of the NBT query, taking the NetBIOS name and Scope ID from the command line. Compile the code and give the executable the name namequery. The program takes one or two arguments. The first is the NetBIOS name, and the second is the Scope ID (the Scope ID is optional). For example, on a Unix system the command line (including the $ prompt) might be: $ namequery neko cat.org Start your sniffer with the filter set to capture only packets sent to/from UDP port 137. If you are using TCPDump or Ethereal the filter string is: udp port 137. Depending on your OS, you may need to have Root or Administrator privilege in order to run the sniffer. Run namequery with the input shown above, and then stop the capture. You should get something like this:
+ Frame 1 (100 on wire, 100 captured)
+ Ethernet II
+ Internet Protocol
+ User Datagram Protocol
- NetBIOS Name Service
Transaction ID: 0x07ac
+ Flags: 0x0110 (Name query)
Questions: 1
Answer RRs: 0
Authority RRs: 0
Additional RRs: 0
- Queries
+ NEKO <00>.CAT.ORG: type NB, class inet
This example is copied from Ethereal output. Compare the parsed output provided by the sniffer against the hard-coded information in the program. They should match up. Next, try a query using a name on your own network and take a look at the response. If you use the name of a Workgroup or NT Domain, you may get responses from several systems. Another way to get multiple replies is to use a wildcard query. If all NBT nodes on your local LAN use the same Scope ID, and if they are not P nodes, then they will all respond to the wildcard name. To try this, you must first change the call to L2_Encode() within main() so that it passes '\0' as the padding character. That is: total_len += L2_Encode( &bufr[total_len], name, '\0', '\0', scope ); ...then recompile and give the asterisk as the NetBIOS name: $ namequery "*" Try using other tools such as nbtstat in Windows or Samba's nmblookup to generate queries, and spend a bit of time looking at the results of these captures. You can also simply let the sniffer run for a while. If your network is active you will see all sorts of NetBIOS packets fly by (particularly if you are on a shared rather than a switched LAN). 1.3.2 InterludeWe now have method, madness, and a vague sense of the direction. We are ready to head out on the open code. Let us first take a moment to meditate on what we have covered so far. Start by considering this mental image... Imagine a cold, rainy autumn day. Still thinking of summer, you have forgotten to wear a jacket. The chill of the rain runs through your entire body as you hurry along the street. You try to keep your neck dry by pulling up your thin sweater and hunching your shoulders. Down the road you spot a café. It looks warm and bright inside. You quicken your pace, then dash through the door as the drizzly rain becomes more enthusiastic and thunder rumbles in the distance. The shop is cozy, but not too small. There are potted plants scattered about. Light jazz plays over well-hidden speakers. The clientele are trendy urban business types having quiet, serious discussions in pairs at small tables. Paintings by a local artist hang on the walls. You step to the counter. A young woman with a dozen earrings and short-cut hair smiles and asks you what you would like. A nice, hot cup of tea. She reaches down behind the counter and grabs a large white mug. Then she opens a box and pulls out a tea bag that is at least three years old, drops it into the mug, and pours in hot water from the sink. "Three dollars" she says, still smiling. If you are a coffee drinker, you probably don't understand. Replace the words "opens a box and pulls out a tea bag" with "opens a jar and scoops out one spoonful of freeze-dried instant" and you will get the point. The point is that details matter. Certainly, an old tea bag in warm water will make a cup of tea...but not one worth drinking11. Just so, our examples provide some working code but are far from satisfying. If we are going to write something truly enjoyable we need to dig into the details. Let's get to it. 1.4 The Name Service in Detail
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
This is gonna hurt me more than it does you. -- common lie |
Think of the Name Service as a database system. The data may be stored in an NBNS server (P mode), distributed across all of the participating nodes in an IP subnet (B mode), or a combination of the two (M or H mode). Name Service messages are the transactions that maintain and utilize the NBT name-to-IP address mapping database. These transactions fall into three basic categories:
These three represent the lifecycle of an NBT name.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Hello, I love you Won't you tell me your name? -- Hello, I Love You The Doors |
The RFCs also specify support for the NetBIOS API Adapter Status Query function. Implementation of the Adapter Status Query is quite similar to that of the Name Query, so it gets lumped in with the Name Service. This is fairly reasonable, since the query packets are almost identical and the most important result of the status query is a list of names owned by the target node. 1.4.1 NBT Names: Once More With FeelingLet's review what we've learned so far:
1.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 alpha-numeric characters may include extended characters, such as 'Å' and 'Ü'. 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 set12. 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. 1.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.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The best way to eliminate the problem is to remove Scopes completely. -- John Terpstra, Samba Team, in a message to the Samba-Technical mailing list. |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
1.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\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 throw-back 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 short-cut, some implementations work directly with the encoded name and only bother to decode the name when interacting with a user. Decoding, however, is fairly straight forward: 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\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. 1.4.2 NBT Name Service PacketsRFC 1002 lists 17 different Name Service packet types, constructed from three basic building blocks:
These pieces are described in more detail below. 1.4.2.1 Name Service HeadersThe header is an array of six 16-bit values, as follows:
Managing Name Service headers is fairly straight-forward. 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:
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:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
It's just a jump to the left -- Time Warp Richard O'Brien |
(0x0007 << 11) = 0x3800 = 0011100000000000(bin) ...which puts it where it's supposed to be:
Listing 1.5 presents nbt_nsHeader.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. 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 order13.
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.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
My brain hurts! -- Attributed to Mr. T. F. Gumby Gumby Brain Specialist Monty Python's Flying Circus |
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.) 1.4.2.2 Name Service Question RecordsThe 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.
There are only two valid values for the QUESTION_TYPE field. These are:
The QUESTION_CLASS field always has a value of:
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: 1.4.2.3 Name Service Resource RecordsFor convenience, we will break the Resource Record into three sub-parts:
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.
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:
The values marked "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:
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.
The last sub-part of the resource record is the resource data section, which is made up of two fields:
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 message type to message type. The Resource Record structure, as described in section 4.2.1.3 of RFC 1002, looks just like this:
It is always good to have some code to play with. This next set of functions can be used to manipulate Resource Records. 1.4.3 Conversations with the Name ServiceWe will now introduce a simple syntax for describing how to fill network packets. This syntax is neither standard nor rigorous, just something the author whipped up to help explain what goes into a message. If it looks like someone else's syntax (one which perhaps took long hours of study, concentration, and thought to develop) then apologies are probably in order.
A broadcast name query, described using our little syntax, would look like this:
NAME QUERY REQUEST (Broadcast)
{
HEADER
{
NAME_TRN_ID = <Set when packet is transmitted>
FLAGS
{
OPCODE = 0x0
RD = TRUE
B = TRUE
}
QDCOUNT = 1
}
QUESTION_RECORD
{
QUESTION_NAME = <Encoded NBT Name>
QUESTION_TYPE = NB (0x0020)
QUESTION_CLASS = IN (0x0001)
}
}
Basically, the rules are these:
It's not a particularly formal syntax, but it will serve the purpose. 1.4.3.1 Name RegistrationNodes send NAME REGISTRATION REQUEST messages when they wish to claim ownership of a name. The messages may be broadcast on the local LAN (B mode), or sent directly to an NBNS (P mode). (M and H mode are combinations of B and P mode with their own special quirks. We will get to those further on.) A NAME REGISTRATION REQUEST message looks like this:
NAME REGISTRATION REQUEST
{
HEADER
{
NAME_TRN_ID = <Set when packet is transmitted>
FLAGS
{
OPCODE = 0x5 (Registration)
RD = TRUE (1)
B = <TRUE for broadcast registration, else FALSE>
}
QDCOUNT = 1
ARCOUNT = 1
}
QUESTION_RECORD
{
QUESTION_NAME = <Encoded NBT name to be registered>
QUESTION_TYPE = NB (0x0020)
QUESTION_CLASS = IN (0x0001)
}
ADDITIONAL_RECORD
{
RR_NAME = 0xC00C (Label String Pointer to QUESTION_NAME)
RR_TYPE = NB (0x0020)
RR_CLASS = IN (0x0001)
TTL = <Zero for broadcast, about three days for unicast>
RDLENGTH = 6
RDATA
{
NB_FLAGS
{
G = <TRUE for a group name, FALSE for a unique name>
ONT = <Owner type>
}
NB_ADDRESS = <Requesting node's IP address>
}
}
}
The NAME REGISTRATION REQUEST includes both a QUESTION_RECORD and an ADDITIONAL_RECORD. In a sense, it is two messages in one. It says "Does anyone own this name?" and "I want to own this name!", both in the same packet. The NAME REGISTRATION REQUEST gives us our first look at a Label String Pointer in its native habitat. In the packet above the QUESTION_NAME and the RR_NAME are the same name, so the latter field contains a pointer back to the former. The size of the header is constant; if there is a QUESTION_NAME in a packet it will always be found at offset 0x000C (12). The field value is 0xC00C because (as is always the case with label string pointers) the first two bits are set in order to indicate that the remainder is a pointer rather than a 6-bit label length. So, Label String Pointers in NBT messages always have the value 0xC00C. The TTL field in the ADDITIONAL_RECORD provides a Time-To-Live value, in seconds, for the name. In B mode, the TTL value is not significant and is generally set to zero. In P mode, the TTL is used by the NBNS to determine when to purge old entries from the database, and is typically set to something on the order of three days in the NAME REGISTRATION REQUEST. The NBNS may override the client's request and reply with a different TTL value, which the client must accept. The ADDITIONAL_RECORD.RDATA field is 6 bytes long (as shown in ADDITIONAL_RECORD.RDLENGTH) and contains two subfields. The first is the NB_FLAGS field, which provides information about the name and its owner. It looks something like this:
The NB_FLAGS.G bit indicates whether the name is a group name or a unique name, and NB_FLAGS.ONT identifies the owner node type. ONT is a two-bit field with the following possible values:
The ADDITIONAL_RECORD.RDATA.NB_ADDRESS holds the 4-byte IPV4 address that will be mapped to the name. This should, of course, match the address of the node registering the name. Take a good look at the structure of the RDATA subrecord in the NAME REGISTRATION REQUEST. This is the most common RDATA format, which gives us an excuse for writing a little more code... 1.4.3.1.1 Broadcast Name RegistrationYou've seen the basic form of NAME REGISTRATION REQUEST packet. When sending a broadcast registration, the following rules apply.
A node sending a broadcast NAME REGISTRATION REQUEST (the requester) may receive a unicast NEGATIVE NAME REGISTRATION RESPONSE from another node that already claims ownership of the name (the owner). That is the only valid message in response to a broadcast registration.
NAME REGISTRATION RESPONSE (Negative)
{
HEADER
{
NAME_TRN_ID = <Must match REQUEST transaction ID>
FLAGS
{
R = TRUE (1; This is a response packet)
OPCODE = 0x5 (Registration)
AA = TRUE (1)
RD = TRUE (1)
RA = TRUE (1)
RCODE = ACT_ERR (0x6)
B = FALSE (0; Message is unicast back to requester)
}
ANCOUNT = 1
}
ANSWER_RECORD
{
RR_NAME = <The Encoded NBT Name>
RR_TYPE = NB (0x0020)
RR_CLASS = IN (0x0001)
TTL = 0 (TTL has no meaning in this context)
RDLENGTH = 6
RDATA
{
NB_FLAGS
{
G = <TRUE for a group name, FALSE for a unique name>
ONT = <Owner type>
}
NB_ADDRESS = <Owner's IP address>
}
}
}
When a requester receives a NEGATIVE NAME REGISTRATION RESPONSE, it is obliged to give up. Registration has failed because another node has prior--and conflicting--claim to the name. That is, the name already has an owner. The RCODE field of the response will be ACT_ERR (0x6), indicating that the name is in use. The RDATA field should contain the real owner's name information:
Recall that the NAME REGISTRATION REQUEST contains a name query, so the ANSWER_RECORD in the reply should be constructed as it would be in a POSITIVE NAME QUERY RESPONSE. It is wrong to simply parrot back the information in the request14. NEGATIVE NAME REGISTRATION RESPONSE messages are only sent if a unique name is involved. Owners of a group name will not complain if a requester tries to join the group. If, however, a requester tries to register a unique name that matches an already registered group name, the members of the group will send negative responses. In a broadcast environment, a single unique name registration request can generate a large number of negative replies. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I want it, I want it, I want it... You can't have it! -- Magic Bus The Who |
If there are no conflicts the requesting node will hear no complaints, in which case it must retry the request two more times...just to be sure. The RFCs specify a minimum timeout of 250 milliseconds between broadcast retries (Windows uses 750ms). After the third query has timed out, the requesting node should broadcast a NAME OVERWRITE DEMAND declaring itself the victor and owner of the name. The NAME OVERWRITE DEMAND message is identical to the NAME REGISTRATION REQUEST, except that the RD bit is clear (Recursion Desired is 0). This next program will allow you to play around with broadcast name registration. It uses functions and constants from previous listings to format a NAME REGISTRATION REQUEST and broadcast it on the local IP subnet, then it listens for and reports any replies it receives. The transaction ID in the NAME_TRN_ID field should be the
same for all three registration attempts, for the final NAME
OVERWRITE DEMAND, and for any negative response packets a remote
node may care to send. All of these are part of the same transaction.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
...the correct expression is "up and died." -- from the errata for Applied Cryptography, 2nd. Ed., By Bruce Schneier |
1.4.3.1.2 Unicast (NBNS) Name RegistrationUnicast name registrations are subtly different from the broadcast variety.
The NBNS should respond with a NAME REGISTRATION RESPONSE, which will include one of the following RCODE values:
Note that the difference between a positive and negative NAME REGISTRATION RESPONSE is simply the RCODE value. If you get no response then it is correct to assume that the NBNS is "down". If the name cannot be registered then your node does not own it, and your application should recover as gracefully as possible. In P mode, handle a non-responsive NBNS as you would a NEGATIVE NAME REGISTRATION RESPONSE. (If the client is running in H or M mode, then it may--with caution--revert to B mode operation until the NBNS is available again.) There are two other packet types that you may receive when registering a name with an NBNS. These are WACK and END-NODE CHALLENGE NAME REGISTRATION RESPONSE. The WACK message tells the client to wait while the NBNS figures things out. This is typically done so that the NBNS has time to send queries to another node that has claimed ownership of the requested name. A WACK looks like this:
WAIT FOR ACKNOWLEDGEMENT (WACK) RESPONSE
{
HEADER
{
NAME_TRN_ID = <Must match REQUEST transaction ID>
FLAGS
{
R = TRUE (1; This is a response packet)
OPCODE = 0x7 (WACK)
AA = TRUE (1)
}
ANCOUNT = 1
}
ANSWER_RECORD
{
RR_NAME = <The Encoded NBT Name from the request>
RR_TYPE = NB (0x0020; note the typo in RFC 1002, 4.2.16)
RR_CLASS = IN (0x0001)
TTL = <Number of seconds to wait; 0 == Infinite>
RDLENGTH = 2
RDATA = <Copy of the two-byte HEADER.FLAGS field
of the original request>
}
}
The key field in the WACK is the TTL field, which tells the client how long to wait for a response. This is used to extend the timeout period on the client, and give the NBNS a chance to do a reality check. Samba uses a TTL value of 60 seconds, which provides ample time to generate a proper reply. Unless it is shut down after sending the WACK message, Samba's NBNS service will always send a NAME REGISTRATION RESPONSE (positive or negative) well before the 60 seconds has elapsed. Microsoft's WINS takes a different approach, using a value of only 2 seconds. If the 2 seconds expire, however, the requesting client will simply send another NAME REGISTRATION REQUEST, and then another for a total of three tries. WINS should be able to respond within that total timeframe. WACK messages are sent by honest, hard-working servers that take good care of their clients. In contrast, a lazy and careless NBNS server will send an END-NODE CHALLENGE NAME REGISTRATION RESPONSE. This latter response tells the client that the requested name has a registered owner, but the NBNS is not going to bother to do the work to check that the owner is still up and running and using the name. Once again, the format of this message is so familiar that there is no need to list all of the fields. The END-NODE CHALLENGE NAME REGISTRATION RESPONSE packet is just a NAME REGISTRATION RESPONSE with:
The annoying thing a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||