private string queryName(IPAddress winsaddr, string netbiosname) { fSocket = new UDPCli(); byte[] encodedname = buildSecondLevelEncodedName(netbiosname); int trid = nextTRID(); clearHeader(); int pos = HDR_NAME_TRN_ID_2; setShortAt(pos, fData, trid); int opcode; int rcode; int nmflags = RECURSIVE_DESIRED; int flags = (nmflags << NM_FLAGS_SHIFT); pos = HDR_FLAGS_2; setShortAt(pos, fData, (flags & 0xffff)); pos = HDR_QDCOUNT_2; setShortAt(pos, fData, 1); pos = QUESTION_NAME_OFF; for(int i=0; i<encodedname.Length; i++) fData[pos++] = encodedname[i]; setShortAt(pos, fData, QUESTION_TYPE_NB); pos += 2; setShortAt(pos, fData, QUESTION_CLASS_IN); pos += 2; //byte[] packet = new byte[fData.Length]; if(Debug.DebugOn && Debug.DebugLevel >= Debug.Buffer) { Debug.WriteLine(Debug.Buffer, "NBNS name query request:"); Debug.WriteLine(Debug.Buffer, fData, 0, pos); } IPEndPoint ip = new IPEndPoint(winsaddr, NAME_SERVICE_UDP_PORT); fSocket.Send(fData, pos, ip); StringBuilder rrname = new StringBuilder(); while(true) { bool bSuccess = fSocket.DoReceive(ref ip, ref fData); if(!bSuccess) // no response from server break; if(trid != getShortAt(0, fData)) continue; if(Debug.DebugOn && Debug.DebugLevel >= Debug.Buffer) { Debug.WriteLine(Debug.Buffer, "NBNS response:"); Debug.WriteLine(Debug.Buffer, fData, 0, fData.Length); } flags = getShortAt(2, fData); rcode = flags & RCODE_MASK; if ((flags & RESPONSE_PACKET) == 0) break; // not a response opcode = ((flags & OPCODE_MASK) >> OPCODE_SHIFT) & 0xf; nmflags = ((flags & NM_FLAGS_MASK) >> NM_FLAGS_SHIFT) & 0x7f; if (opcode == OPCODE_WACK) { /* WAIT FOR ACKNOWLEDGEMENT (WACK) RESPONSE 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NAME_TRN_ID |1| 0x7 |1|0|0|0|0 0|0| 0x0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0001 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0000 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | / RR_NAME / / / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NULL (0x0020) | IN (0x0001) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TTL | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0002 | OPCODE | NM_FLAGS | 0x0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The NAME_TRN_ID of the WACK RESPONSE packet is the same NAME_TRN_ID of the request that the NBNS is telling the requestor to wait longer to complete. The RR_NAME is the name from the request, if any. If no name is available from the request then it is a null name, single byte of zero. The TTL field of the ResourceRecord is the new time to wait, in seconds, for the request to complete. The RDATA field contains the OPCODE and NM_FLAGS of the request. A TTL value of 0 means that the NBNS can not estimate the time it may take to complete a response. */ // read RR_NAME pos = HDR_SIZE; pos = parseSecondLevelEncodedName(fData, pos, rrname); // skip pos += 4; int ttl = getIntAt(pos, fData); if(ttl == 0) fTimeout = UCAST_REQ_RETRY_TIMEOUT; else fTimeout = ttl; continue; } if (rcode == 0) { //check if redirect if((nmflags & AUTHORITATIVE_ANSWER) != 0) { /* POSITIVE NAME QUERY RESPONSE 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NAME_TRN_ID |1| 0x0 |1|T|1|?|0 0|0| 0x0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0001 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0000 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | / RR_NAME / / / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NB (0x0020) | IN (0x0001) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TTL | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RDLENGTH | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | / ADDR_ENTRY ARRAY / / / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The ADDR_ENTRY ARRAY a sequence of zero or more ADDR_ENTRY records. Each ADDR_ENTRY record represents an owner of a name. For group names there may be multiple entries. However, the list may be incomplete due to packet size limitations. Bit 22, "T", will be set to indicate truncated data. Each ADDR_ENTRY has the following format: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NB_FLAGS | NB_ADDRESS | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NB_ADDRESS (continued) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ // read RR_NAME pos = HDR_SIZE; pos = parseSecondLevelEncodedName(fData, pos, rrname); // skip pos += 8; int rdlength = getShortAt(pos, fData); pos += 2; if(rdlength >= 6) { pos += 2; // skip NB_FLAGS fSocket = null; return Util.Util.GetIpAddress(fData, pos); } } else { /* REDIRECT NAME QUERY RESPONSE 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NAME_TRN_ID |1| 0x0 |0|0|1|0|0 0|0| 0x0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0000 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0001 | 0x0001 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | / RR_NAME / / / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NS (0x0002) | IN (0x0001) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TTL | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RDLENGTH | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | / NSD_NAME / / / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | / RR_NAME / / / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | A (0x0001) | IN (0x0001) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TTL | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0004 | NSD_IP_ADDR | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NSD_IP_ADDR, continued | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ An end node responding to a NAME QUERY REQUEST always responds with the AA and RA bits set for both the NEGATIVE and POSITIVE NAME QUERY RESPONSE packets. An end node never sends a REDIRECT NAME QUERY RESPONSE packet. When the requestor receives the REDIRECT NAME QUERY RESPONSE it must reiterate the NAME QUERY REQUEST to the NBNS specified by the NSD_IP_ADDR field of the A type RESOURCE RECORD in the ADDITIONAL section of the response packet. This is an optional packet for the NBNS. The NSD_NAME and the RR_NAME in the ADDITIONAL section of the response packet are the same name. Space can be optimized if label string pointers are used in the RR_NAME which point to the labels in the NSD_NAME. The RR_NAME in the AUTHORITY section is the name of the domain the NBNS called by NSD_NAME has authority over. */ Debug.WriteLine(Debug.Error, "NB redirect not implemented"); fSocket = null; return null; } } else { // NEGATIVE NAME QUERY RESPONSE if(Debug.DebugOn && Debug.DebugLevel >= Debug.Error) Debug.WriteLine(Debug.Error, "WINS error: " + rcode); fSocket = null; return null; } break; } fSocket = null; return null; }
/// <summary> /// This does a NB Status Request to a given host. /// </summary> /// <param name="hostIP">IPAddress of the host to query</param> /// <returns>The NetBIOS name, or null if unreachable/non SMB</returns> public NbInfo queryStatus(IPAddress hostIP) { fSocket = new UDPCli(); byte[] encodedname = buildSecondLevelEncodedName(NBT_STAT_QNAME, false); int trid = nextTRID(); clearHeader(); int pos = HDR_NAME_TRN_ID_2; setShortAt(pos, fData, trid); int opcode; int rcode; int nmflags = RECURSIVE_DESIRED; int flags = (nmflags << NM_FLAGS_SHIFT); pos = HDR_FLAGS_2; setShortAt(pos, fData, (flags & 0xffff)); pos = HDR_QDCOUNT_2; setShortAt(pos, fData, 1); pos = QUESTION_NAME_OFF; for(int i=0; i<encodedname.Length; i++) fData[pos++] = encodedname[i]; setShortAt(pos, fData, QUESTION_TYPE_NBSTAT); pos += 2; setShortAt(pos, fData, QUESTION_CLASS_IN); pos += 2; //byte[] packet = new byte[fData.Length]; if(Debug.DebugOn && Debug.DebugLevel >= Debug.Buffer) { Debug.WriteLine(Debug.Buffer, "NBNS name query request:"); Debug.WriteLine(Debug.Buffer, fData, 0, pos); } IPEndPoint ip = new IPEndPoint(hostIP, NAME_SERVICE_UDP_PORT); int sent = fSocket.Send(fData, pos, ip); while(true) { bool bSuccess = fSocket.DoReceive(ref ip, ref fData); if(!bSuccess) break; if(Debug.DebugOn && Debug.DebugLevel >= Debug.Buffer) { Debug.WriteLine(Debug.Buffer, "NBNS response:"); Debug.WriteLine(Debug.Buffer, fData, 0, fData.Length); } if(trid != getShortAt(0, fData)) continue; flags = getShortAt(2, fData); rcode = flags & RCODE_MASK; if ((flags & RESPONSE_PACKET) == 0) break; // not a response opcode = ((flags & OPCODE_MASK) >> OPCODE_SHIFT) & 0xf; nmflags = ((flags & NM_FLAGS_MASK) >> NM_FLAGS_SHIFT) & 0x7f; if (rcode == 0) { /* NODE STATUS RESPONSE 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NAME_TRN_ID |1| 0x0 |1|0|0|0|0 0|0| 0x0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0001 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x0000 | 0x0000 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | / RR_NAME / | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NBSTAT (0x0021) | IN (0x0001) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0x00000000 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RDLENGTH | NUM_NAMES | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + / NODE_NAME ARRAY / + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + / STATISTICS / + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries of NODE_NAME records. Each NODE_NAME entry represents an active name in the same NetBIOS scope as the requesting name in the local name table of the responder. RR_NAME is the requesting name. NODE_NAME Entry: 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +--- ---+ | | +--- NETBIOS FORMAT NAME ---+ | | +--- ---+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NAME_FLAGS | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ // Read machine name; string NBTname = Encoding.UTF8.GetString(fData, NBT_STAT_COMP_OFF, 15); // Get the NetBIOS name NBTname = NBTname.Trim(); // remove the whitespace // Read workgroup/domain string NBTgroup = Encoding.UTF8.GetString(fData, NBT_STAT_WORKGRP_OFF1, 15); // Get the NetBIOS group NBTgroup = NBTgroup.Trim(); // remove the whitespaces if(NBTname == NBTgroup) // Try the alternate location { NBTgroup = Encoding.UTF8.GetString(fData, NBT_STAT_WORKGRP_OFF2, 15); NBTgroup = NBTgroup.Trim(); } NbInfo info = new NbInfo(); info.Name = (string)NBTname.Clone(); info.Workgroup = (string)NBTgroup.Clone(); fSocket = null; return info; } else { // NEGATIVE NAME QUERY RESPONSE if(Debug.DebugOn && Debug.DebugLevel >= Debug.Error) Debug.WriteLine(Debug.Error, "WINS error: " + rcode); fSocket = null; return new NbInfo(); } } fSocket = null; return new NbInfo(); }