Understand this at the outset: Examining a function of the RAP protocol is like studying the runic carvings on the lid of Pandora's box. They might just be large friendly letters ... or they could be the manufacturer's warning label. We are not going to open the box. The NetServerEnum2 function can be implemented without having to fully understand the inner workings of RAP, so there really is no need. If you want to, you can rummage around in the RAP functions by reading through Appendix B of the X/Open book Protocols for X/Open PC Interworking: SMB, Version 2 . After that, there is yet again another additional further Leach/Naik draft already. You can find the Leach/Naik CIFS Remote Administration Protocol Preliminary Draft under the filename cifsrap2.txt on Microsoft's FTP server. It is definitely a draft, but it provides a lot of good information if you read it carefully . One more resource a die-hard RAP-per will want to check is Remoted Net API Format Strings , which is an email message that was sent to Microsoft's CIFS mailing list by Paul Leach. It provides details on the formatting of RAP messages. All of these sources are, of course, listed in the References section. One of the downsides of RAP, from our perspective, is that it defines yet another layer of parameters and data... and there's a heap, too. Gotta love layers . RAP provides a formula for marshalling its parameters, data, and heap, passing them over a network connection, and then passing the results back again. A complete RAP implementation would most likely automate the marshalling and unmarshalling process, and the human eye would never need to see it. That would be overkill in our case, so we're stuck doing things the easy way by hand. RAP functions are sent via a Named Pipe, not a Mailslot, so the whole communications process is different. Like the Mailslot-based functions, RAP functions are packed into an SMBtrans transaction, but that's just about all that's really the same. The steps which must be followed in order to execute a RAP call are:
You can see all of this very clearly in a packet capture. Having a sniff handy as you read through this section is highly recommended, by the way. Don't forget to listen on 139/TCP instead of (or in addition to) 138/UDP. 22.3.1 NetServerEnum2 RequestYou can generate a NetServerEnum2 exchange in a variety of ways. For example, you can refresh the server list in the Windows Network Neighborhood or use the jCIFS List.java utility with the URL " smb:// workgroup / ". The request, as displayed by the packet sniffer, should look something like this: + Transmission Control Protocol + NetBIOS Session Service + SMB (Server Message Block Protocol) SMB Pipe Protocol - Microsoft Windows Lanman Remote API Protocol Function Code: NetServerEnum2 (104) Parameter Descriptor: WrLehDz Return Descriptor: B16BBDz Detail Level: 1 Receive Buffer Length: 65535 Server Type: 0xffffffff Enumeration Domain: WORKGROUP The Descriptor fields are a distinctive feature of RAP requests . These are the cryptic runes of which we spoke earlier. They are format strings, used to define the structure of the parameters and data being sent as well as that expected in the reply. They can be used to automate the packing and unpacking of the packets, or they can be stuffed into the packet as constants with no regard to their meaning. The latter is the simpler course. With that in mind, here is the (simplified, but still correct) C-style format of a NetServerEnum2 request: struct { ushort RAPCode; uchar *ParamDesc; uchar *DataDesc; struct { ushort InfoLevel; ushort BufrSize; ulong ServerType; uchar *Workgroup; } Params; } NetServerEnum2Req; So, given the above structure, the NetServerEnum2 request is filled in as shown below. Note that, at the SMBtrans-level, there are no Setup[] words, the Data[] section is empty, and all of the above structure is bundled into the Parameter[] block. smb_Transaction_Request { TotalParamCount = 27 + strlen( Workgroup ) MaxParameterCount = 8 MaxDataCount = <Size of the reply buffer> Name = "\PIPE\LANMAN" Data { RAPCode = 104 (0x0068) ParamDesc = "WrLehDz" DataDesc = "B16BBDz" RAP_Params { InfoLevel = 1 <See below> BufrSize = <Same as MaxDataCount> ServerType = <See below> Workgroup = <Name of the workgroup to list> } } } A few of those fields need a little discussion. TotalParamCount
MaxDataCount and BufrSize
InfoLevel
ServerType
Workgroup
22.3.2 NetServerEnum2 ReplyThe response message is a bit more involved, so you may want to take notes. A packet capture, once again, is a highly recommended visual aide. Starting at the top... The TotalParamCount field in the SMBtrans reply message will have a value of 8, indicating the size of the SMBtrans-level Parameter[] block. Those bytes fall out as follows : struct { ushort Status; /* Error Code */ ushort Convert; /* See below */ ushort EntryCount; /* Entries returned */ ushort AvailCount; /* Entries available */ } Status
Convert
EntryCount
AvailCount
That's all there is to the Parameter[] block. It's nicely simple, but things get a little wilder as we move on. Do keep track of that Convert value... The SMB-level Data[] block will start with a series of ServerInfo_1 structures, as described below: struct { uchar Name[16]; /* Provider name */ uchar OSMajorVers; /* Provider OS Rev */ uchar OSMinorVers; /* Provider OS Point */ ulong ServerType; /* See below */ uchar *Comment; /* Pointer */ } ServerInfo_1; There will be <EntryCount> such structures packed neatly together. It is fairly easy to parse them out, because the Name field is a fixed-length, nul- padded string and the Comment field really is a pointer. The Leach/Naik Browser draft suggests that the Comment strings themselves may follow each ServerInfo_1 structure, but all examples seen on the wire show four bytes. Hang on to those four bytes... we'll explain in a moment. Anywhich, the above structure has a fixed length 26 bytes, to be precise. That makes it easy to parse ServerInfo_1 structures from the Data[] block. The values in the ServerInfo_1 are the same ones announced by the Provider in its HostAnnouncement or DomainAnnouncement frames . They are stored in an internal database on the browser node. Some of these fields have been discussed before, but a detailed description of the ServerType field has been postponed at every opportunity. Similarly, the pointer value in the Comment field really needs some clarification . Let's start with the Comment pointer... The Comment pointer may just possibly be a relic of the long lost days of DOS. Those who know more about 16-bit DOS internals may judge. In any case, what you need to do is this:
Well that was easy. This stuff is so lovable you just want to give it a hug, don't you? Some further notes:
Right... Having tilted that windmill, let's take a look at the (more sensible , but also much more verbose) ServerType field. We have delayed describing this field for quite a while. Here, finally, it is... well, mostly. The list below is based on Samba sources. It is close to Ethereal's list, and less close to the list given in the Leach/Naik draft. Let the buyer beware.
Just to polish this subject off, here's a little code that can parse a NetServerEnum2 response message and print the results: Listing 22.3 Parsing NetServerEnum2 Replies#define NERR_Success 0 #define SV_TYPE_ALL 0xFFFFFFFF #define SV_TYPE_UNKNOWN 0x1F000000 #define SV_TYPE_DOMAIN_ENUM 0x80000000 #define SV_TYPE_LOCAL_LIST_ONLY 0x40000000 #define SV_TYPE_ALTERNATE_XPORT 0x20000000 #define SV_TYPE_DFS_SERVER 0x00800000 #define SV_TYPE_WIN95_PLUS 0x00400000 #define SV_TYPE_SERVER_VMS 0x00200000 #define SV_TYPE_SERVER_OSF 0x00100000 #define SV_TYPE_DOMAIN_MASTER 0x00080000 #define SV_TYPE_MASTER_BROWSER 0x00040000 #define SV_TYPE_BACKUP_BROWSER 0x00020000 #define SV_TYPE_POTENTIAL_BROWSER 0x00010000 #define SV_TYPE_SERVER_NT 0x00008000 #define SV_TYPE_SERVER_MFPN 0x00004000 #define SV_TYPE_WFW 0x00002000 #define SV_TYPE_NT 0x00001000 #define SV_TYPE_SERVER_UNIX 0x00000800 #define SV_TYPE_DIALIN_SERVER 0x00000400 #define SV_TYPE_PRINTQ_SERVER 0x00000200 #define SV_TYPE_DOMAIN_MEMBER 0x00000100 #define SV_TYPE_NOVELL 0x00000080 #define SV_TYPE_AFP 0x00000040 #define SV_TYPE_TIME_SOURCE 0x00000020 #define SV_TYPE_DOMAIN_BAKCTRL 0x00000010 #define SV_TYPE_DOMAIN_CTRL 0x00000008 #define SV_TYPE_SQLSERVER 0x00000004 #define SV_TYPE_SERVER 0x00000002 #define SV_TYPE_WORKSTATION 0x00000001 typedef struct { ushort Status; ushort Convert; ushort EntryCount; ushort AvailCount; } NSE2_ReplyParams; void PrintBrowserBits( ulong ServerType ) /* ---------------------------------------------------- ** * Itemize Browse Service Provider Type Bits. * This is boring, and could probably be done better * using an array and a for() loop. * ---------------------------------------------------- ** */ { if( SV_TYPE_ALL == ServerType ) { printf( " All/Any Server types.\n" ); return; } if( SV_TYPE_UNKNOWN & ServerType ) printf( " Warning: Undefined bits set.\n" ); if( SV_TYPE_DOMAIN_ENUM & ServerType ) printf( " Enumerate Domains\n" ); if( SV_TYPE_LOCAL_LIST_ONLY & ServerType ) printf( " Local List Only\n" ); if( SV_TYPE_ALTERNATE_XPORT & ServerType ) printf( " Alternate Export (Unknown type)\n" ); if( SV_TYPE_DFS_SERVER & ServerType ) printf( " DFS Support\n" ); if( SV_TYPE_WIN95_PLUS & ServerType ) printf( " Windows 95+\n" ); if( SV_TYPE_SERVER_VMS & ServerType ) printf( " VMS (Pathworks) Server\n" ); if( SV_TYPE_SERVER_OSF & ServerType ) printf( " OSF Unix Server\n" ); if( SV_TYPE_DOMAIN_MASTER & ServerType ) printf( " Domain Master Browser\n" ); if( SV_TYPE_MASTER_BROWSER & ServerType ) printf( " Local Master Browser\n" ); if( SV_TYPE_BACKUP_BROWSER & ServerType ) printf( " Backup Browser\n" ); if( SV_TYPE_POTENTIAL_BROWSER & ServerType ) printf( " Potential Browser\n" ); if( SV_TYPE_SERVER_NT & ServerType ) printf( " Windows NT (or compatible) Server\n" ); if( SV_TYPE_SERVER_MFPN & ServerType ) printf( " MFPN (Unkown type)\n" ); if( SV_TYPE_WFW & ServerType ) printf( " Windows for Workgroups\n" ); if( SV_TYPE_NT & ServerType ) printf( " Windows NT Workstation\n" ); if( SV_TYPE_SERVER_UNIX & ServerType ) printf( " Unix/Xenix/Samba Server\n" ); if( SV_TYPE_DIALIN_SERVER & ServerType ) printf( " Dialin Server\n" ); if( SV_TYPE_PRINTQ_SERVER & ServerType ) printf( " Print Server\n" ); if( SV_TYPE_DOMAIN_MEMBER & ServerType ) printf( " NT Domain Member Server\n" ); if( SV_TYPE_NOVELL & ServerType ) printf( " Novell Server\n" ); if( SV_TYPE_AFP & ServerType ) printf( " Apple Server\n" ); if( SV_TYPE_TIME_SOURCE & ServerType ) printf( " Time Source\n" ); if( SV_TYPE_DOMAIN_BAKCTRL & ServerType ) printf( " Backup Domain Controller\n" ); if( SV_TYPE_DOMAIN_CTRL & ServerType ) printf( " Domain Controller\n" ); if( SV_TYPE_SQLSERVER & ServerType ) printf( " SQL Server\n" ); if( SV_TYPE_SERVER & ServerType ) printf( " SMB Server\n" ); if( SV_TYPE_WORKSTATION & ServerType ) printf( " Workstation\n" ); } /* PrintBrowserBits */ void PrintNetServerEnum2Reply( uchar *ParamBlock, int ParamLen, uchar *DataBlock, int DataLen ) /* ---------------------------------------------------- ** * Parse a NetServerEnum2 Reply and print the contents. * ---------------------------------------------------- ** */ { NSE2_ReplyParams Rep; int i; int offset; uchar *pos; /* Check for an obvious error. */ if( ParamLen != 8 ) Fail( "Error parsing NetServerEnum2 reply.\n" ); /* Grab all of the parameter words. */ Rep.Status = smb_GetShort( ParamBlock, 0 ); Rep.Convert = smb_GetShort( ParamBlock, 2 ); Rep.EntryCount = smb_GetShort( ParamBlock, 4 ); Rep.AvailCount = smb_GetShort( ParamBlock, 6 ); /* Check for problems (errors and warnings). */ if( Rep.Status != NERR_Success ) Fail( "NetServerEnum2 Error: %d.\n", Rep.Status ); if( Rep.EntryCount < Rep.AvailCount ) printf( "Warning: The list is incomplete.\n" ); /* Dump the ServerInfo_1 records. */ pos = DataBlock; for( i = 0; i < Rep.EntryCount; i++ ) { printf( "%-15s V%d.%d\n", pos, pos[16], pos[17] ); PrintBrowserBits( smb_GetLong( pos, 18 ) ); offset = 0x0000FFFF & smb_GetLong( pos, 22 ); offset -= Rep.Convert; if( offset >= DataLen ) Fail( "Packet offset error.\n" ); printf( " Comment: %s\n", (DataBlock + offset) ); pos += 26; } } /* PrintNetServerEnum2Reply */ 22.3.3 On the Outskirts of TownThere is another RAP call that you need to know about. It comes in handy at times. It doesn't really belong to the Browse Service, but you may have heard its name mentioned in that context. It lives on the edge, somewhere between browsing and filesharing, and it goes by the name NetShareEnum . The NetShareEnum RAP call does the job of listing the shares offered by a server. The shares, as you already know, are the virtual roots of the directory trees made available via SMB. The wire format of the request is as follows: struct { ushort RAPCode; uchar *ParamDesc; uchar *DataDesc; struct { ushort InfoLevel; ushort BufrSize; } Params; } NetShareEnumReq; and it is filled in like so: NetShareEnumReq { RAPCode = 0 (NetShareEnum) ParamDesc = "WrLeh" DataDesc = "B13BWz" Params { InfoLevel = 1 (No other values defined) BufrSize = <Same as smb_Transaction_Request.MaxDataCount> } } Yes, the RAP code for NetShareEnum is zero ( ). There's not much to that call, particularly once you've gotten the NetServerEnum2 figured out. The response also contains some familiar concepts. In fact, the Parameter[] section is exactly the same. The RAP-level data section is supposed to contain an array of ShareInfo_1 structures, which look like this: struct { uchar ShareName[13]; uchar pad; ushort ShareType; uchar *Comment; } ShareInfo_1; Again, there are many similarities to what we have seen before. In this case, though, the ShareType field has a smaller set of possible values than the comparable ServerType field.
...and that is "all you need to know" about the NetShareEnum call. Oh, wait... There is one more thing... Can't Get It Out Of My Head Alert
22.3.4 Transaction FragmentationA promise is a promise, and we did promise to cover fragmented transactions. [2]
The idea is fairly simple. If you have twenty-five sacks of grain to bring to town, and a wagon that can hold only twelve sacks, then you will need to make a few trips. Likewise with transactions. Due to the limits of the negotiated buffer size, a transaction may attempt to transfer more data than can be carried in a single SMB. The solution is to split up the data and send it using multiple SMB messages. The mechanism used is the same for SMBtrans, Trans2, and NTtrans. There are slight differences between the transaction request and transaction response, though, so pay attention. Sending a fragmented transaction request works like this:
Now go back and take a look at Listing 22.1. Note that the smb_Transaction_Request structure keeps track of the number of Parameter[] and Data[] bytes already packed for shipping. That makes it easy to build the secondary messages should they be needed. Fragmenting a transaction response is a simpler process. The response SMBs all have the same structure (no special secondary messages) and they all have an SMB header, which may contain an error code if necessary. So, the server can just send as many transaction response SMBs as needed to transfer all of the results. That's it. 22.3.5 RAP AnnoyancesRAP can be quite annoying that's just its nature. There are two particular annoyances of which you should be aware: Authentication
Limitations and Permutations
|