📄 nbt.c
字号:
/* Copyright (c) 2003 Juan Lang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* I am heavily indebted to Chris Hertel's excellent Implementing CIFS,
* http://ubiqx.org/cifs/ , for whatever understanding I have of NBT.
* I also stole from Mike McCormack's smb.c and netapi32.c, although little of
* that code remains.
* Lack of understanding and bugs are my fault.
*
* FIXME:
* - Of the NetBIOS session functions, only client functions are supported, and
* it's likely they'll be the only functions supported. NBT requires session
* servers to listen on TCP/139. This requires root privilege, and Samba is
* likely to be listening here already. This further restricts NetBIOS
* applications, both explicit users and implicit ones: CreateNamedPipe
* won't actually create a listening pipe, for example, so applications can't
* act as RPC servers using a named pipe protocol binding, DCOM won't be able
* to support callbacks or servers over the named pipe protocol, etc.
*
* - Datagram support is omitted for the same reason. To send a NetBIOS
* datagram, you must include the NetBIOS name by which your application is
* known. This requires you to have registered the name previously, and be
* able to act as a NetBIOS datagram server (listening on UDP/138).
*
* - Name registration functions are omitted for the same reason--registering a
* name requires you to be able to defend it, and this means listening on
* UDP/137.
* Win98 requires you either use your computer's NetBIOS name (with the NULL
* suffix byte) as the calling name when creating a session, or to register
* a new name before creating one: it disallows '*' as the calling name.
* Win2K initially starts with an empty name table, and doesn't allow you to
* use the machine's NetBIOS name (with the NULL suffix byte) as the calling
* name. Although it allows sessions to be created with '*' as the calling
* name, doing so results in timeouts for all receives, because the
* application never gets them.
* So, a well-behaved Netbios application will typically want to register a
* name. I should probably support a do-nothing name list that allows
* NCBADDNAME to add to it, but doesn't actually register the name, or does
* attempt to register it without being able to defend it.
*
* - Name lookups may not behave quite as you'd expect/like if you have
* multiple LANAs. If a name is resolvable through DNS, or if you're using
* WINS, it'll resolve on _any_ LANA. So, a Call will succeed on any LANA as
* well.
* I'm not sure how Windows behaves in this case. I could try to force
* lookups to the correct adapter by using one of the GetPreferred*
* functions, but with the possibility of multiple adapters in the same
* same subnet, there's no guarantee that what IpHlpApi thinks is the
* preferred adapter will actually be a LANA. (It's highly probable because
* this is an unusual configuration, but not guaranteed.)
*
* See also other FIXMEs in the code.
*/
#include "config.h"
#include <stdarg.h>
#include "winsock2.h"
#include "windef.h"
#include "winbase.h"
#include "wine/debug.h"
#include "winreg.h"
#include "iphlpapi.h"
#include "netbios.h"
#include "nbnamecache.h"
WINE_DEFAULT_DEBUG_CHANNEL(netbios);
#define PORT_NBNS 137
#define PORT_NBDG 138
#define PORT_NBSS 139
#ifndef INADDR_NONE
#define INADDR_NONE ~0UL
#endif
#define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
#define NBR_GETWORD(p) ntohs(*(WORD *)(p))
#define MIN_QUERIES 1
#define MAX_QUERIES 0xffff
#define MIN_QUERY_TIMEOUT 100
#define MAX_QUERY_TIMEOUT 0xffffffff
#define BCAST_QUERIES 3
#define BCAST_QUERY_TIMEOUT 750
#define WINS_QUERIES 3
#define WINS_QUERY_TIMEOUT 750
#define MAX_WINS_SERVERS 2
#define MIN_CACHE_TIMEOUT 60000
#define CACHE_TIMEOUT 360000
#define MAX_NBT_NAME_SZ (NCBNAMSZ * 2 + MAX_DOMAIN_NAME_LEN + 2)
#define SIMPLE_NAME_QUERY_PKT_SIZE 26 + MAX_NBT_NAME_SZ
#define DEFAULT_NBT_SESSIONS 16
#define NBNS_TYPE_NB 0x0020
#define NBNS_TYPE_NBSTAT 0x0021
#define NBNS_CLASS_INTERNET 0x00001
#define NBNS_HEADER_SIZE (sizeof(WORD) * 6)
#define NBNS_RESPONSE_AND_OPCODE 0xf800
#define NBNS_RESPONSE_AND_QUERY 0x8000
#define NBNS_REPLYCODE 0x0f
#define NBSS_HDRSIZE 4
#define NBSS_MSG 0x00
#define NBSS_REQ 0x81
#define NBSS_ACK 0x82
#define NBSS_NACK 0x83
#define NBSS_RETARGET 0x84
#define NBSS_KEEPALIVE 0x85
#define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80
#define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
#define NBSS_ERR_BAD_NAME 0x82
#define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83
#define NBSS_EXTENSION 0x01
typedef struct _NetBTSession
{
CRITICAL_SECTION cs;
SOCKET fd;
DWORD bytesPending;
} NetBTSession;
typedef struct _NetBTAdapter
{
MIB_IPADDRROW ipr;
WORD nameQueryXID;
struct NBNameCache *nameCache;
DWORD xmit_success;
DWORD recv_success;
} NetBTAdapter;
static ULONG gTransportID;
static BOOL gEnableDNS;
static DWORD gBCastQueries;
static DWORD gBCastQueryTimeout;
static DWORD gWINSQueries;
static DWORD gWINSQueryTimeout;
static DWORD gWINSServers[MAX_WINS_SERVERS];
static int gNumWINSServers;
static char gScopeID[MAX_DOMAIN_NAME_LEN];
static DWORD gCacheTimeout;
static struct NBNameCache *gNameCache;
/* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
* Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
* bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes
* if p is NULL-terminated. Returns the number of bytes stored in buffer.
*/
static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
{
int i,len=0;
if (!p) return 0;
if (!buffer) return 0;
buffer[len++] = NCBNAMSZ * 2;
for (i = 0; p[i] && i < NCBNAMSZ; i++)
{
buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
buffer[len++] = (p[i] & 0x0f) + 'A';
}
while (len < NCBNAMSZ * 2)
{
buffer[len++] = 'C';
buffer[len++] = 'A';
}
if (*gScopeID)
{
int scopeIDLen = strlen(gScopeID);
memcpy(buffer + len, gScopeID, scopeIDLen);
len += scopeIDLen;
}
buffer[len++] = 0; /* add second terminator */
return len;
}
/* Creates a NBT name request packet for name in buffer. If broadcast is true,
* creates a broadcast request, otherwise creates a unicast request.
* Returns the number of bytes stored in buffer.
*/
static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
BOOL broadcast, UCHAR *buffer, int len)
{
int i = 0;
if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */
if (broadcast)
{
NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
i+=2;
}
else
{
NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
i+=2;
}
NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
i += NetBTNameEncode(name, &buffer[i]);
NBR_ADDWORD(&buffer[i],qtype); i+=2;
NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
return i;
}
/* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on
* fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not
* NULL.
* Returns 0 on success, -1 on failure.
*/
static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
WORD qtype, DWORD destAddr, BOOL broadcast)
{
int ret = 0, on = 1;
struct in_addr addr;
addr.s_addr = destAddr;
TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
if (broadcast)
ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
if(ret == 0)
{
WSABUF wsaBuf;
UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_addr.s_addr = destAddr;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT_NBNS);
wsaBuf.buf = (CHAR*)buf;
wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
sizeof(buf));
if (wsaBuf.len > 0)
{
DWORD bytesSent;
ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
(struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
if (ret < 0 || bytesSent < wsaBuf.len)
ret = -1;
else
ret = 0;
}
else
ret = -1;
}
return ret;
}
typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
WORD answerIndex, PUCHAR rData, WORD rdLength);
/* Waits on fd until GetTickCount() returns a value greater than or equal to
* waitUntil for a name service response. If a name response matching xid
* is received, calls answerCallback once for each answer resource record in
* the response. (The callback's answerCount will be the total number of
* answers to expect, and answerIndex will be the 0-based index that's being
* sent this time.) Quits parsing if answerCallback returns FALSE.
* Returns NRC_GOODRET on timeout or a valid response received, something else
* on error.
*/
static UCHAR NetBTWaitForNameResponse(const NetBTAdapter *adapter, SOCKET fd,
DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
{
BOOL found = FALSE;
DWORD now;
UCHAR ret = NRC_GOODRET;
if (!adapter) return NRC_BADDR;
if (fd == INVALID_SOCKET) return NRC_BADDR;
if (!answerCallback) return NRC_BADDR;
while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
{
DWORD msToWait = waitUntil - now;
struct fd_set fds;
struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
int r;
FD_ZERO(&fds);
FD_SET(fd, &fds);
r = select(fd + 1, &fds, NULL, NULL, &timeout);
if (r < 0)
ret = NRC_SYSTEM;
else if (r == 1)
{
/* FIXME: magic #, is this always enough? */
UCHAR buffer[256];
int fromsize;
struct sockaddr_in fromaddr;
WORD respXID, flags, queryCount, answerCount;
WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
DWORD bytesReceived, recvFlags = 0;
fromsize = sizeof(fromaddr);
r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
(struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
if(r < 0)
{
ret = NRC_SYSTEM;
break;
}
if (bytesReceived < NBNS_HEADER_SIZE)
continue;
respXID = NBR_GETWORD(buffer);
if (adapter->nameQueryXID != respXID)
continue;
flags = NBR_GETWORD(buffer + 2);
queryCount = NBR_GETWORD(buffer + 4);
answerCount = NBR_GETWORD(buffer + 6);
/* a reply shouldn't contain a query, ignore bad packet */
if (queryCount > 0)
continue;
if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
{
if ((flags & NBNS_REPLYCODE) != 0)
ret = NRC_NAMERR;
else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
{
PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
BOOL shouldContinue = TRUE;
WORD answerIndex = 0;
found = TRUE;
/* decode one answer at a time */
while (ret == NRC_GOODRET && answerIndex < answerCount &&
ptr - buffer < bytesReceived && shouldContinue)
{
WORD rLen;
/* scan past name */
for (; ptr[0] && ptr - buffer < bytesReceived; )
ptr += ptr[0] + 1;
ptr++;
ptr += 2; /* scan past type */
if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
&& NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
ptr += sizeof(WORD);
else
ret = NRC_SYSTEM; /* parse error */
ptr += sizeof(DWORD); /* TTL */
rLen = NBR_GETWORD(ptr);
rLen = min(rLen, bytesReceived - (ptr - buffer));
ptr += sizeof(WORD);
shouldContinue = answerCallback(data, answerCount,
answerIndex, ptr, rLen);
ptr += rLen;
answerIndex++;
}
}
}
}
}
TRACE("returning 0x%02x\n", ret);
return ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -