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()); }