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:
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
SMB Data
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 messagestypedef 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. |