22.1 Making Sense of SMBtrans

As has already been pointed out, we are dealing with layered protocols and layered protocols can cause some terminology confusion. For example, earlier in the book SMB messages were described as having a header, a parameter section, and a data section (and there was a cute picture of an insect to highlight the anatomy). SMB transactions half a protocol layer up also have parameters and data. The terms get recycled, as demonstrated by the structure presented above in which the Parameters[] and Data[] fields are both carried within the SMB_DATA block (the Bytes ) of the SMB message.

SMB transaction messages generally represent some sort of network function call. In an SMB transaction:

  • The Parameters represent the values passed directly to the function that was called.

  • The Data represents any indirect values, such as objects indicated by pointers passed as parameters (i.e. objects passed by reference).

That's a very general description, and it may be slightly inaccurate in practice. It works well enough in theory, though, and it provides a conceptual foothold. If you want, you can go one step further and think of the SetupCount and Setup[] fields as the transaction header.

Okay, now that we have that out of the way, here's what the SMBtrans fields are all about:

SMB Parameters

TotalParamCount

You may recall from earlier discussions that the SMBtrans transaction has the ability to carry a total payload of 64 Kbytes potentially much more than the SMB buffer size will allow. It does this by sending zero or more secondary transaction messages which contain the additional parameters and/or data.

The TotalParamCount field indicates the total number of parameter bytes that the server should expect to receive over the course of the transaction. It may take multiple messages to get them all there.

TotalDataCount

Similar to the previous field, this indicates the total number of data bytes the server should expect to receive.

If you think about it, you will see that the theoretical SMBtrans data transfer limit is actually 2 x (216 1). Both the Parameter and Data lengths are 16-bit values so, in theory, SMBtrans can send 128 Kbytes (minus two bytes), which is double the 64K we've been claiming.

MaxParameterCount , MaxDataCount , and MaxSetupCount

These fields let the client inform the server of the maximum number of Parameter[] , Data[] , and Setup[] bytes, respectively, that the client is willing to receive in the server's reply. These are total bytes for the transaction, not per-message bytes.

A note regarding the MaxSetupCount field: The X/ Open documentation lists this as a 16-bit field, but in the Leach/Naik CIFS draft it is given as a single byte followed by a one-byte nul pad. Because the value is given in SMB byte order (and because it will not exceed 255), either way works.

Flags

There are two flags defined in this field, but they don't appear to be used much in Browser Protocol messages.

SMBtrans flags

Bit

Bitmask

Description

152

0xFFFC

<Reserved> (must be zero)

1

0x0002

If set, this is a one-way transaction and the server should not send a response. In theory, this bit should be set in all Class 2 Mailslot messages, but in packet captures this bit always seems to be clear.

0x0001

If set, it indicates that the client wishes to disconnect from the share indicated by the TID field in the SMB header when the transaction completes.

Mailslot messages will have a zero TID value, so this bit should not be set. RAP calls always use the IPC$ share, which will have been opened using an earlier TREE CONNECT message. So, in theory, this bit could be set in RAP calls... but it was clear in all of the packets captured during testing.

Timeout

The documentation is scattered . The X/Open docs provide only a few words regarding this particular field. The SNIA doc has a small section (Section 3.2.9) that covers timeouts in general, and some additional information can be found in various places throughout each of those references.

The field indicates the number of milliseconds (1/1000ths of a second) that the server should wait for a transaction to complete. A value of zero indicates that the server should return immediately, sending an error code if it could not obtain required resources right away. A value of 1 indicates that the client is willing to have the server wait forever. The documentation doesn't make it clear, but the correct DOS error code in the case of a timeout is probably ERRSRV/ERRtimeout ( 0x02/0x0058 ).

ParameterCount

The number of bytes in the Parameter[] block of this message. Keep in mind that this may be lower than the TotalParamCount value. If it is, then the rest of the parameters will follow in secondary transaction messages.

ParameterOffset

The offset from the beginning of the SMB message at which the parameter block starts.

DataCount

The number of bytes in the Data[] block of this message. Once again, this may be lower than the TotalDataCount value. If so, the rest of the data will follow in additional messages.

DataOffset

The offset from the beginning of the SMB message at which the data block starts.

SetupCount

The number of setup words . As with the MaxSetupCount field, SetupCount is presented as an unsigned short in the X/Open document but is given as an unsigned byte followed by a one-byte pad in the Leach/Naik draft.

Setup[]

An array of 16-byte values used to "set up" the transaction (the transaction, not the function call). This might be considered the header portion of the transaction.

SMB Data

Name []

The name of the Named Pipe or Mailslot to which the transaction is being sent (for example, " \PIPE\LANMAN ").

Parameters[]

The marshalled parameters.

Data[]

The marshalled data. A little later on, we will carefully avoid explaining how the parameters and data get packaged.

Pad and Pad1

Some (but not all) clients and servers will add padding bytes (typically, but not necessarily , nul) to force word or longword alignment of the Parameters[] and Data[] sections. That really messes things up. You must:

  • Be sure to use ByteCount to figure out how large the SMB_DATA section really is.

  • Use ParameterOffset and ParameterCount to figure out where the transaction parameters begin and how many bytes there are.

  • Use DataOffset and DataCount to figure out where the transaction data begins and how many bytes there are.

    Gotta love this stuff...

There is a lot more information in both the X/Open documentation and the Leach/Naik CIFS drafts. For some reason, specific details regarding SMBtrans were left out of the SNIA doc, although there is a discussion of Mailslots and Named Pipes (and the other transaction types are covered). All of the listed docs do explain how secondary transaction messages may be used to transfer Setup[] , Parameter[] , and/or Data[] blocks that are larger than the allowed SMB buffer size.

There are also some warnings given in the SNIA doc regarding variations in implementation. It seems you need to be careful with CIFS (no surprise there). See the last paragraph of Section 3.15.3 in the SNIA doc if'n your curious .

...but now it's time for some code.

Listing 22.1 is a bit dense, but it does a decent job of putting together an SMBtrans message from parts . It doesn't fill in the NBT or SMB headers, but there are code examples and descriptions elsewhere in the book that cover those issues. What it does do is provide a starting point for managing SMBtrans transactions, particularly those that might exceed the server's SMB buffer limit and need to be fragmented .

Listing 22.1 SMBtrans messages
 typedef struct   {   ushort  SetupCount;        /* Setup word count          */   ushort *Setup;             /* Setup words               */   ushort  Flags;             /* 0x1=Disconnect;0x2=oneway */   ulong   Timeout;           /* Server timeout in ms      */   ushort  MaxParameterCount; /* Max param bytes to return */   ushort  MaxDataCount;      /* Max data bytes to return  */   ushort  MaxSetupCount;     /* Max setup words to return */   ushort  TotalParamCount;   /* Total param bytes to send */   ushort  TotalDataCount;    /* Total data bytes to send  */   ushort  ParamsSent;        /* Parameters already sent   */   ushort  DataSent;          /* Data already sent         */   uchar  *Name;              /* Transaction service name  */   uchar  *Parameters;        /* Parameter bytes           */   uchar  *Data;              /* Data bytes                */   } smb_Transaction_Request; int SetStr( uchar *dst, int offset, char *src )   /* ---------------------------------------------------- **    * Quick function to copy a string into a buffer and    * return the *total* length, including the terminating    * nul byte.  Does *no* limit checking (bad).    * Input:  dst    - Destination buffer.    *         offset - Starting point within destination.    *         src    - Source string.    * Output: Number of bytes transferred.    * ---------------------------------------------------- **    */   {   int i;   for( i = 0; ' 
 typedef struct { ushort SetupCount; /* Setup word count */ ushort *Setup; /* Setup words */ ushort Flags; /* 0x1=Disconnect;0x2= oneway */ ulong Timeout; /* Server timeout in ms */ ushort MaxParameterCount; /* Max param bytes to return */ ushort MaxDataCount; /* Max data bytes to return */ ushort MaxSetupCount; /* Max setup words to return */ ushort TotalParamCount; /* Total param bytes to send */ ushort TotalDataCount; /* Total data bytes to send */ ushort ParamsSent; /* Parameters already sent */ ushort DataSent; /* Data already sent */ uchar *Name; /* Transaction service name */ uchar *Parameters; /* Parameter bytes */ uchar *Data; /* Data bytes */ } smb_Transaction_Request; int SetStr( uchar *dst, int offset, char *src ) /* ---------------------------------------------------- ** * Quick function to copy a string into a buffer and * return the *total* length, including the terminating * nul byte. Does *no* limit checking (bad). * Input: dst - Destination buffer. * offset - Starting point within destination. * src - Source string. * Output: Number of bytes transferred. * ---------------------------------------------------- ** */ { int i; for( i = 0; '\0' != src[i]; i++ ) dst[offset+i] = src[i]; dst[offset+i] = '\0'; return( i+1 ); } /* SetStr */ int smb_TransRequest( uchar *bufr, int bSize, smb_Transaction_Request *Request ) /* ---------------------------------------------------- ** * Format an SMBtrans request message. * ---------------------------------------------------- ** */ { int offset = 0; int keep_offset; int bcc_offset; int result; int i; /* See that we have enough room for the SMB-level params: * Setup + 14 bytes of SMB params + 2 bytes for Bytecount. */ if( bSize < (Request->SetupCount + 14 + 2) ) Fail( "Transaction buffer too small.\n" ); /* Fill the SMB-level parameter block. */ bufr[offset++] = (uchar)(Request->SetupCount + 14); smb_SetShort( bufr, offset, Request->TotalParamCount ); offset += 2; smb_SetShort( bufr, offset, Request->TotalDataCount ); offset += 2; smb_SetShort( bufr, offset, Request->MaxParameterCount); offset += 2; smb_SetShort( bufr, offset, Request->MaxDataCount ); offset += 2; smb_SetShort( bufr, offset, Request->MaxSetupCount ); offset += 2; smb_SetShort( bufr, offset, (Request->Flags & 0x0003) ); offset += 2; smb_SetLong( bufr, offset, Request->Timeout ); offset += 4; smb_SetShort( bufr, offset, 0 ); /* Reserved word */ offset += 2; keep_offset = offset; /* Remember ParamCount location */ offset += 8; /* Skip ahead to SetupCount. */ smb_SetShort( bufr, offset, Request->SetupCount ); offset += 2; for( i = 0; i < Request->SetupCount; i++ ) { smb_SetShort( bufr, offset, Request->Setup[i] ); offset += 2; } /* Fill the SMB-level data block... * We skip the ByteCount field until the end. */ bcc_offset = offset; /* Keep the Bytecount offset. */ offset += 2; /* We need to have enough room to specify the * pipe or mailslot. */ if( strlen( Request->Name ) >= (bSize - offset) ) Fail( "No room for Transaction Name: %s\n", Request->Name ); /* Start with the pipe or mailslot name. */ offset += SetStr( bufr, offset, Request->Name ); /* Now figure out how many SMBtrans parameter bytes * we can copy, and copy them. */ result = bSize - offset; if( result > Request->TotalParamCount ) result = Request->TotalParamCount; Request->ParamsSent = result; if( result > 0 ) (void)memcpy( &bufr[offset], Request->Parameters, result ); /* Go back and fill in Param Count and Param Offset. */ smb_SetShort( bufr, keep_offset, result ); keep_offset += 2; smb_SetShort( bufr, keep_offset, SMB_HDR_SIZE + offset ); keep_offset += 2; offset += result; /* Now figure out how many SMBtrans data bytes we * can copy, and copy them. */ result = bSize - offset; if( result > Request->TotalDataCount ) result = Request->TotalDataCount; Request->DataSent = result; if( result > 0 ) (void) memcpy ( &bufr[offset], Request->Data, result ); /* Go back and fill in Data Count and Data Offset. */ smb_SetShort( bufr, keep_offset, result ); keep_offset += 2; smb_SetShort( bufr, keep_offset, SMB_HDR_SIZE + offset ); keep_offset += 2; /* not really needed any more */ offset += result; /* Go back and fill in the byte count. */ smb_SetShort( bufr, bcc_offset, offset - (bcc_offset+2) ); /* Done. */ return( offset ); } /* smb_TransRequest */ 
' != src[i]; i++ ) dst[offset+i] = src[i]; dst[offset+i] = '
 typedef struct { ushort SetupCount; /* Setup word count */ ushort *Setup; /* Setup words */ ushort Flags; /* 0x1=Disconnect;0x2= oneway */ ulong Timeout; /* Server timeout in ms */ ushort MaxParameterCount; /* Max param bytes to return */ ushort MaxDataCount; /* Max data bytes to return */ ushort MaxSetupCount; /* Max setup words to return */ ushort TotalParamCount; /* Total param bytes to send */ ushort TotalDataCount; /* Total data bytes to send */ ushort ParamsSent; /* Parameters already sent */ ushort DataSent; /* Data already sent */ uchar *Name; /* Transaction service name */ uchar *Parameters; /* Parameter bytes */ uchar *Data; /* Data bytes */ } smb_Transaction_Request; int SetStr( uchar *dst, int offset, char *src ) /* ---------------------------------------------------- ** * Quick function to copy a string into a buffer and * return the *total* length, including the terminating * nul byte. Does *no* limit checking (bad). * Input: dst - Destination buffer. * offset - Starting point within destination. * src - Source string. * Output: Number of bytes transferred. * ---------------------------------------------------- ** */ { int i; for( i = 0; '\0' != src[i]; i++ ) dst[offset+i] = src[i]; dst[offset+i] = '\0'; return( i+1 ); } /* SetStr */ int smb_TransRequest( uchar *bufr, int bSize, smb_Transaction_Request *Request ) /* ---------------------------------------------------- ** * Format an SMBtrans request message. * ---------------------------------------------------- ** */ { int offset = 0; int keep_offset; int bcc_offset; int result; int i; /* See that we have enough room for the SMB-level params: * Setup + 14 bytes of SMB params + 2 bytes for Bytecount. */ if( bSize < (Request->SetupCount + 14 + 2) ) Fail( "Transaction buffer too small.\n" ); /* Fill the SMB-level parameter block. */ bufr[offset++] = (uchar)(Request->SetupCount + 14); smb_SetShort( bufr, offset, Request->TotalParamCount ); offset += 2; smb_SetShort( bufr, offset, Request->TotalDataCount ); offset += 2; smb_SetShort( bufr, offset, Request->MaxParameterCount); offset += 2; smb_SetShort( bufr, offset, Request->MaxDataCount ); offset += 2; smb_SetShort( bufr, offset, Request->MaxSetupCount ); offset += 2; smb_SetShort( bufr, offset, (Request->Flags & 0x0003) ); offset += 2; smb_SetLong( bufr, offset, Request->Timeout ); offset += 4; smb_SetShort( bufr, offset, 0 ); /* Reserved word */ offset += 2; keep_offset = offset; /* Remember ParamCount location */ offset += 8; /* Skip ahead to SetupCount. */ smb_SetShort( bufr, offset, Request->SetupCount ); offset += 2; for( i = 0; i < Request->SetupCount; i++ ) { smb_SetShort( bufr, offset, Request->Setup[i] ); offset += 2; } /* Fill the SMB-level data block... * We skip the ByteCount field until the end. */ bcc_offset = offset; /* Keep the Bytecount offset. */ offset += 2; /* We need to have enough room to specify the * pipe or mailslot. */ if( strlen( Request->Name ) >= (bSize - offset) ) Fail( "No room for Transaction Name: %s\n", Request->Name ); /* Start with the pipe or mailslot name. */ offset += SetStr( bufr, offset, Request->Name ); /* Now figure out how many SMBtrans parameter bytes * we can copy, and copy them. */ result = bSize - offset; if( result > Request->TotalParamCount ) result = Request->TotalParamCount; Request->ParamsSent = result; if( result > 0 ) (void)memcpy( &bufr[offset], Request->Parameters, result ); /* Go back and fill in Param Count and Param Offset. */ smb_SetShort( bufr, keep_offset, result ); keep_offset += 2; smb_SetShort( bufr, keep_offset, SMB_HDR_SIZE + offset ); keep_offset += 2; offset += result; /* Now figure out how many SMBtrans data bytes we * can copy, and copy them. */ result = bSize - offset; if( result > Request->TotalDataCount ) result = Request->TotalDataCount; Request->DataSent = result; if( result > 0 ) (void) memcpy ( &bufr[offset], Request->Data, result ); /* Go back and fill in Data Count and Data Offset. */ smb_SetShort( bufr, keep_offset, result ); keep_offset += 2; smb_SetShort( bufr, keep_offset, SMB_HDR_SIZE + offset ); keep_offset += 2; /* not really needed any more */ offset += result; /* Go back and fill in the byte count. */ smb_SetShort( bufr, bcc_offset, offset - (bcc_offset+2) ); /* Done. */ return( offset ); } /* smb_TransRequest */ 
'; return( i+1 ); } /* SetStr */ int smb_TransRequest( uchar *bufr, int bSize, smb_Transaction_Request *Request ) /* ---------------------------------------------------- ** * Format an SMBtrans request message. * ---------------------------------------------------- ** */ { int offset = 0; int keep_offset; int bcc_offset; int result; int i; /* See that we have enough room for the SMB-level params: * Setup + 14 bytes of SMB params + 2 bytes for Bytecount. */ if( bSize < (Request->SetupCount + 14 + 2) ) Fail( "Transaction buffer too small.\n" ); /* Fill the SMB-level parameter block. */ bufr[offset++] = (uchar)(Request->SetupCount + 14); smb_SetShort( bufr, offset, Request->TotalParamCount ); offset += 2; smb_SetShort( bufr, offset, Request->TotalDataCount ); offset += 2; smb_SetShort( bufr, offset, Request->MaxParameterCount); offset += 2; smb_SetShort( bufr, offset, Request->MaxDataCount ); offset += 2; smb_SetShort( bufr, offset, Request->MaxSetupCount ); offset += 2; smb_SetShort( bufr, offset, (Request->Flags & 0x0003) ); offset += 2; smb_SetLong( bufr, offset, Request->Timeout ); offset += 4; smb_SetShort( bufr, offset, 0 ); /* Reserved word */ offset += 2; keep_offset = offset; /* Remember ParamCount location */ offset += 8; /* Skip ahead to SetupCount. */ smb_SetShort( bufr, offset, Request->SetupCount ); offset += 2; for( i = 0; i < Request->SetupCount; i++ ) { smb_SetShort( bufr, offset, Request->Setup[i] ); offset += 2; } /* Fill the SMB-level data block... * We skip the ByteCount field until the end. */ bcc_offset = offset; /* Keep the Bytecount offset. */ offset += 2; /* We need to have enough room to specify the * pipe or mailslot. */ if( strlen( Request->Name ) >= (bSize - offset) ) Fail( "No room for Transaction Name: %s\n", Request->Name ); /* Start with the pipe or mailslot name. */ offset += SetStr( bufr, offset, Request->Name ); /* Now figure out how many SMBtrans parameter bytes * we can copy, and copy them. */ result = bSize - offset; if( result > Request->TotalParamCount ) result = Request->TotalParamCount; Request->ParamsSent = result; if( result > 0 ) (void)memcpy( &bufr[offset], Request->Parameters, result ); /* Go back and fill in Param Count and Param Offset. */ smb_SetShort( bufr, keep_offset, result ); keep_offset += 2; smb_SetShort( bufr, keep_offset, SMB_HDR_SIZE + offset ); keep_offset += 2; offset += result; /* Now figure out how many SMBtrans data bytes we * can copy, and copy them. */ result = bSize - offset; if( result > Request->TotalDataCount ) result = Request->TotalDataCount; Request->DataSent = result; if( result > 0 ) (void)memcpy( &bufr[offset], Request->Data, result ); /* Go back and fill in Data Count and Data Offset. */ smb_SetShort( bufr, keep_offset, result ); keep_offset += 2; smb_SetShort( bufr, keep_offset, SMB_HDR_SIZE + offset ); keep_offset += 2; /* not really needed any more */ offset += result; /* Go back and fill in the byte count. */ smb_SetShort( bufr, bcc_offset, offset - (bcc_offset+2) ); /* Done. */ return( offset ); } /* smb_TransRequest */

The smb_Transaction_Request structure in the listing differs from the wire-format version. The former is designed to keep track of a transaction while it is being built and until it has been completely transmitted. With a little more code, it should be able to compose secondary transaction messages too. Fortunately, all of the Browse Service requests are small enough to fit into a typical SMB buffer, so you shouldn't have to worry about sending secondary SMB transaction messages. At least not right away. On the other hand, a Browse Server's reply to a NetServerEnum2 call can easily exceed the SMB buffer size so you may need to know how to rebuild a fragmented response. With that in mind, we will explain how multi-part messages work when we cover NetServerEnum2 .

It is probably worth noting, at this point, just how many layers of abstraction we're dealing with. If you look at a packet capture of an NetServerEnum2 request, you'll find that it is way the heck down at the bottom of a large pile:

 Ethernet II + IP   + TCP     + NBT Session Service       + SMB (SMB_COM_TRANSACTION)         + SMB Pipe Protocol           + Microsoft Windows Remote Administration Protocol             + NetServerEnum2 

It sure is getting deep around here...

All those layers make things seem more complicated than they really are, but if we chip away at it one small workable piece at a time it will all be easier to understand.



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