That last section was a bit of a rant. Sorry. Time to pick up the pieces and move on. Let's talk about how the parts that work actually work. As with the Name Service, each Datagram Service packet has a header. The datagram header is 10 bytes long, arranged as follows :
Here is a quick rundown of the fields: MSG_TYPE (1 byte)
FLAGS (1 byte)
DGM_ID (2 bytes)
SOURCE_IP (4 bytes)
SOURCE_PORT (2 bytes)
The above fields are common to all Datagram Service messages. RFC 1002 includes two more fields in its header layout: the DGM_LENGTH and PACKET_OFFSET fields. It is kind of silly to have those fields in the header, as they are specific to the messages which actually carry a data payload: the DIRECT_UNIQUE , DIRECT_GROUP , and BROADCAST DATAGRAM message types. Since the DGM_LENGTH and PACKET_OFFSET fields are associated with the datagram transport messages, we will break with tradition and put those fields together with the message structure. Here is a record layout:
DGM_LENGTH (2 bytes)
PACKET_OFFSET (2 bytes)
SOURCE_NAME (34..255 bytes)
DESTINATION_NAME (34..255 bytes)
USER_DATA (0..512 bytes)
That's basically all there is to it, with the exception of fragmentation. The example packet below describes an unfragmented message. DATAGRAM_HEADER (unfragmented) { MSG_TYPE = <10 = unicast, 11 = multicast, 12 = broadcast> FLAGS { SNT = <Node type, as shown above> F = TRUE (This is the first in the series) M = FALSE (No additional fragments follow) } DGM_ID = <Datagram identifier> SOURCE_IP = <IP address of the originating node> SOURCE_PORT = <Originating UDP port> } DATAGRAM_DATA { DGM_LENGTH = <Name lengths plus USER_DATA length> PACKET_OFFSET = 0 SOURCE_NAME = <Fully encoded NBT name of the sender> DESTINATION_NAME = <Fully encoded NBT name of the receiver> USER_DATA = <Datagram payload> } Some quick notes:
5.3.1 Fragmenting DatagramsA little way back, we mentioned the NetBIOS API Send Specific Datagram and Send Broadcast Datagram functions. These functions each accept up to 512 bytes of data on input. Given that number, the maximum on-the-wire size of an NBT datagram is: 10 bytes (Header) + 2 bytes (DGM_LENGTH) + 2 bytes (PACKET_OFFSET) + 255 bytes (maximum size of SOURCE_NAME) + 255 bytes (maximum size of DESTINATION_NAME) + 512 bytes (maximum size of USER_DATA) ----- 1036 bytes and that, of course, does not include the UDP and IP headers. The whole thing is fairly chunky easily more than double the size of the data actually being sent. The RFC authors were concerned that the UDP transport might not carry datagrams that big, so they provided a mechanism for breaking the USER_DATA into smaller fragments, like so: first fragment FLAGS.F = TRUE (This is the first fragment) FLAGS.M = TRUE (Additional fragments follow) PACKET_OFFSET = 0 middle fragments final fragment The value of the PACKET_OFFSET field is the sum of the lengths of all previous fragments. This value is included in the message so that the receiver can keep the fragments in sync as it rebuilds the original USER_DATA . This is necessary, because datagrams do not always arrive in the order in which they were sent. Now that you have learned all of that, you can forget most of it. As is typical for the Datagram Service, the fragmentation feature is rarely if ever used. The IP layer has its own mechanism for handling large packets so NBT datagram fragmentation is redundant. It is possible that someone, somewhere, has implemented fragmentation, so an NBT implementation should be prepared to deal with it. One simple option is to discard fragments. This can be considered valid because the Datagram Service is considered "unreliable." Something else to keep in mind: The 512-byte maximum size for the USER_DATA field is required at the NetBIOS layer, but not the NBT layer. Since the NetBIOS API is not required for implementing NBT, you mustn't expect that the datagrams you receive will fit within the limit. Code defensively. 5.3.2 Receiving DatagramsNBT receives datagram messages on UDP port 138, so clients must listen on that port at the UDP level. When a message datagram is received ( MSG_TYPE is one of 0x10 , 0x11 , or 0x12 ) the DESTINATION_NAME is checked against the local name table. If the name is not found, the client should reply with a DATAGRAM ERROR MESSAGE . The available error codes are: 0x82 == DESTINATION NAME NOT PRESENT 0x83 == INVALID SOURCE NAME FORMAT 0x84 == INVALID DESTINATION NAME FORMAT The first value is used whenever the DESTINATION_NAME is not in the local name table at the receiving end. The other two codes are sent whenever the source or destination NBT names, respectively, cannot be parsed. If the name is found in the local table, then the datagram may be passed to any application or service that is listening for the given DESTINATION_NAME . The NetBIOS API provides the Receive Specific Datagram and Receive Broadcast Datagram calls for this purpose. If there are no Receive Datagram requests waiting, the datagram is quietly discarded. NBDD processing (for those bold enough to want to implement an NBDD) is similar. When the NBDD receives a datagram it will search the NBNS database instead of the local name table. Error messages are returned as above for missing or malformed names. One more note: As a safety precaution, the receiving node should probably verify that the SOURCE_IP field in the datagram header matches either the source address in the IP header, or the NBDD address (if there is one). 5.3.3 Querying the NBDDThe NBDD query message is simply an NBT Datagram Service header with the DESTINATION_NAME appended: DATAGRAM_HEADER { MSG_TYPE = 0x14 (DATAGRAM QUERY REQUEST) FLAGS { SNT = <Node type> F = TRUE M = FALSE } DGM_ID = <Datagram identifier> SOURCE_IP = <IP address of the originating node> SOURCE_PORT = <Originating UDP port> } DATAGRAM_DATA { DESTINATION_NAME = <Encoded NBT name of the intended receiver> } If there is an NBDD, and if it can relay the request, it will change the MSG_TYPE field to 0x15 ( POSITIVE QUERY RESPONSE ) and echo the packet back to the sender. If the NBDD is unwilling or unable to relay the message it will set MSG_TYPE to 0x16 ( NEGATIVE QUERY RESPONSE ) before sending the reply. 5.3.4 The Second Least Well Understood Aspect of NBTIt really should have been much simpler, but given the design flaws and implementation errors it is no wonder people have trouble with the Datagram Service. Our hope is that this section has cleared things up a bit, and explained the problems well enough to make them easier to solve. Just to finish up, here are a few tips:
|