1 /* Copyright (c) 2003 Juan Lang
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * I am heavily indebted to Chris Hertel's excellent Implementing CIFS,
18 * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT.
19 * I also stole from Mike McCormack's smb.c and netapi32.c, although little of
21 * Lack of understanding and bugs are my fault.
24 * - Of the NetBIOS session functions, only client functions are supported, and
25 * it's likely they'll be the only functions supported. NBT requires session
26 * servers to listen on TCP/139. This requires root privilege, and Samba is
27 * likely to be listening here already. This further restricts NetBIOS
28 * applications, both explicit users and implicit ones: CreateNamedPipe
29 * won't actually create a listening pipe, for example, so applications can't
30 * act as RPC servers using a named pipe protocol binding, DCOM won't be able
31 * to support callbacks or servers over the named pipe protocol, etc.
33 * - Datagram support is omitted for the same reason. To send a NetBIOS
34 * datagram, you must include the NetBIOS name by which your application is
35 * known. This requires you to have registered the name previously, and be
36 * able to act as a NetBIOS datagram server (listening on UDP/138).
38 * - Name registration functions are omitted for the same reason--registering a
39 * name requires you to be able to defend it, and this means listening on
41 * Win98 requires you either use your computer's NetBIOS name (with the NULL
42 * suffix byte) as the calling name when creating a session, or to register
43 * a new name before creating one: it disallows '*' as the calling name.
44 * Win2K initially starts with an empty name table, and doesn't allow you to
45 * use the machine's NetBIOS name (with the NULL suffix byte) as the calling
46 * name. Although it allows sessions to be created with '*' as the calling
47 * name, doing so results in timeouts for all receives, because the
48 * application never gets them.
49 * So, a well-behaved Netbios application will typically want to register a
50 * name. I should probably support a do-nothing name list that allows
51 * NCBADDNAME to add to it, but doesn't actually register the name, or does
52 * attempt to register it without being able to defend it.
54 * - Name lookups may not behave quite as you'd expect/like if you have
55 * multiple LANAs. If a name is resolvable through DNS, or if you're using
56 * WINS, it'll resolve on _any_ LANA. So, a Call will succeed on any LANA as
58 * I'm not sure how Windows behaves in this case. I could try to force
59 * lookups to the correct adapter by using one of the GetPreferred*
60 * functions, but with the possibility of multiple adapters in the same
61 * same subnet, there's no guarantee that what IpHlpApi thinks is the
62 * preferred adapter will actually be a LANA. (It's highly probable because
63 * this is an unusual configuration, but not guaranteed.)
65 * See also other FIXMEs in the code.
73 #include "wine/debug.h"
78 #include "nbnamecache.h"
80 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
86 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
87 #define NBR_GETWORD(p) ntohs(*(WORD *)(p))
90 #define MAX_QUERIES 0xffff
91 #define MIN_QUERY_TIMEOUT 100
92 #define MAX_QUERY_TIMEOUT 0xffffffff
93 #define BCAST_QUERIES 3
94 #define BCAST_QUERY_TIMEOUT 750
95 #define WINS_QUERIES 3
96 #define WINS_QUERY_TIMEOUT 750
97 #define MAX_WINS_SERVERS 2
98 #define MIN_CACHE_TIMEOUT 60000
99 #define CACHE_TIMEOUT 360000
101 #define MAX_NBT_NAME_SZ (NCBNAMSZ * 2 + MAX_DOMAIN_NAME_LEN + 2)
102 #define SIMPLE_NAME_QUERY_PKT_SIZE 26 + MAX_NBT_NAME_SZ
104 #define DEFAULT_NBT_SESSIONS 16
106 #define NBNS_TYPE_NB 0x0020
107 #define NBNS_TYPE_NBSTAT 0x0021
108 #define NBNS_CLASS_INTERNET 0x00001
109 #define NBNS_HEADER_SIZE (sizeof(WORD) * 6)
110 #define NBNS_RESPONSE_AND_OPCODE 0xf800
111 #define NBNS_RESPONSE_AND_QUERY 0x8000
112 #define NBNS_REPLYCODE 0x0f
114 #define NBSS_HDRSIZE 4
116 #define NBSS_MSG 0x00
117 #define NBSS_REQ 0x81
118 #define NBSS_ACK 0x82
119 #define NBSS_NACK 0x83
120 #define NBSS_RETARGET 0x84
121 #define NBSS_KEEPALIVE 0x85
123 #define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80
124 #define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
125 #define NBSS_ERR_BAD_NAME 0x82
126 #define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83
128 #define NBSS_EXTENSION 0x01
130 typedef struct _NetBTSession
137 typedef struct _NetBTAdapter
141 struct NBNameCache *nameCache;
146 static ULONG gTransportID;
147 static DWORD gBCastQueries;
148 static DWORD gBCastQueryTimeout;
149 static DWORD gWINSQueries;
150 static DWORD gWINSQueryTimeout;
151 static DWORD gWINSServers[MAX_WINS_SERVERS];
152 static int gNumWINSServers;
153 static char gScopeID[MAX_DOMAIN_NAME_LEN];
154 static DWORD gCacheTimeout;
155 static struct NBNameCache *gNameCache;
157 /* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
158 * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
159 * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes
160 * if p is NULL-terminated. Returns the number of bytes stored in buffer.
162 static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
167 if (!buffer) return 0;
169 buffer[len++] = NCBNAMSZ * 2;
170 for (i = 0; p[i] && i < NCBNAMSZ; i++)
172 buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
173 buffer[len++] = (p[i] & 0x0f) + 'A';
175 while (len < NCBNAMSZ * 2)
182 int scopeIDLen = strlen(gScopeID);
184 memcpy(buffer + len, gScopeID, scopeIDLen);
187 buffer[len++] = 0; /* add second terminator */
191 /* Creates a NBT name request packet for name in buffer. If broadcast is true,
192 * creates a broadcast request, otherwise creates a unicast request.
193 * Returns the number of bytes stored in buffer.
195 static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
196 BOOL broadcast, UCHAR *buffer, int len)
200 if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
202 NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */
205 NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
210 NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
213 NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
214 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
215 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
216 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
218 i += NetBTNameEncode(name, &buffer[i]);
220 NBR_ADDWORD(&buffer[i],qtype); i+=2;
221 NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
226 /* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on
227 * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not
229 * Returns 0 on success, -1 on failure.
231 static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
232 WORD qtype, DWORD destAddr, BOOL broadcast)
237 addr.s_addr = destAddr;
238 TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
241 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (PUCHAR)&on, sizeof(on));
245 UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
246 struct sockaddr_in sin;
248 memset(&sin, 0, sizeof(sin));
249 sin.sin_addr.s_addr = destAddr;
250 sin.sin_family = AF_INET;
251 sin.sin_port = htons(PORT_NBNS);
254 wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
260 ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
261 (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
262 if (ret < 0 || bytesSent < wsaBuf.len)
273 typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
274 WORD answerIndex, PUCHAR rData, WORD rdLength);
276 /* Waits on fd until GetTickCount() returns a value greater than or equal to
277 * waitUntil for a name service response. If a name response matching xid
278 * is received, calls answerCallback once for each answer resource record in
279 * the response. (The callback's answerCount will be the total number of
280 * answers to expect, and answerIndex will be the 0-based index that's being
281 * sent this time.) Quits parsing if answerCallback returns FALSE.
282 * Returns NRC_GOODRET on timeout or a valid response received, something else
285 static UCHAR NetBTWaitForNameResponse(NetBTAdapter *adapter, SOCKET fd,
286 DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
290 UCHAR ret = NRC_GOODRET;
292 if (!adapter) return NRC_BADDR;
293 if (fd == INVALID_SOCKET) return NRC_BADDR;
294 if (!answerCallback) return NRC_BADDR;
296 while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
298 DWORD msToWait = waitUntil - now;
300 struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
305 r = select(fd + 1, &fds, NULL, NULL, &timeout);
310 /* FIXME: magic #, is this always enough? */
313 struct sockaddr_in fromaddr;
314 WORD respXID, flags, queryCount, answerCount;
315 WSABUF wsaBuf = { sizeof(buffer), buffer };
316 DWORD bytesReceived, recvFlags = 0;
318 fromsize = sizeof(fromaddr);
319 r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
320 (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
327 if (bytesReceived < NBNS_HEADER_SIZE)
330 respXID = NBR_GETWORD(buffer);
331 if (adapter->nameQueryXID != respXID)
334 flags = NBR_GETWORD(buffer + 2);
335 queryCount = NBR_GETWORD(buffer + 4);
336 answerCount = NBR_GETWORD(buffer + 6);
338 /* a reply shouldn't contain a query, ignore bad packet */
342 if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
344 if ((flags & NBNS_REPLYCODE) != 0)
346 else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
348 PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
349 BOOL shouldContinue = TRUE;
350 WORD answerIndex = 0;
353 /* decode one answer at a time */
354 while (ret == NRC_GOODRET && answerIndex < answerCount &&
355 ptr - buffer < bytesReceived && shouldContinue)
360 for (; ptr[0] && ptr - buffer < bytesReceived; )
363 ptr += 2; /* scan past type */
364 if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
365 && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
368 ret = NRC_SYSTEM; /* parse error */
369 ptr += sizeof(DWORD); /* TTL */
370 rLen = NBR_GETWORD(ptr);
371 rLen = min(rLen, bytesReceived - (ptr - buffer));
373 shouldContinue = answerCallback(data, answerCount,
374 answerIndex, ptr, rLen);
382 TRACE("returning 0x%02x\n", ret);
386 typedef struct _NetBTNameQueryData {
387 NBNameCacheEntry *cacheEntry;
389 } NetBTNameQueryData;
391 /* Name query callback function for NetBTWaitForNameResponse, creates a cache
392 * entry on the first answer, adds each address as it's called again (as long
393 * as there's space). If there's an error that should be propagated as the
394 * NetBIOS error, modifies queryData's ret member to the proper return code.
396 static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
397 WORD answerIndex, PUCHAR rData, WORD rLen)
399 NetBTNameQueryData *queryData = (NetBTNameQueryData *)pVoid;
404 if (queryData->cacheEntry == NULL)
406 queryData->cacheEntry = (NBNameCacheEntry *)HeapAlloc(
407 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
408 (answerCount - 1) * sizeof(DWORD));
409 if (queryData->cacheEntry)
410 queryData->cacheEntry->numAddresses = 0;
414 queryData->ret = NRC_OSRESNOTAV;
417 if (rLen == 6 && queryData->cacheEntry &&
418 queryData->cacheEntry->numAddresses < answerCount)
420 queryData->cacheEntry->addresses[queryData->cacheEntry->
421 numAddresses++] = *(PDWORD)(rData + 2);
422 ret = queryData->cacheEntry->numAddresses < answerCount;
432 /* Workhorse NetBT name lookup function. Sends a name lookup query for
433 * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
434 * adapter->nameQueryXID as the transaction ID. Waits up to timeout
435 * milliseconds, and retries up to maxQueries times, waiting for a reply.
436 * If a valid response is received, stores the looked up addresses as a
437 * NBNameCacheEntry in *cacheEntry.
438 * Returns NRC_GOODRET on success, though this may not mean the name was
439 * resolved--check whether *cacheEntry is NULL.
441 static UCHAR NetBTNameWaitLoop(NetBTAdapter *adapter, SOCKET fd, PNCB ncb,
442 DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
443 NBNameCacheEntry **cacheEntry)
446 NetBTNameQueryData queryData;
448 if (!adapter) return NRC_BADDR;
449 if (fd == INVALID_SOCKET) return NRC_BADDR;
450 if (!ncb) return NRC_BADDR;
451 if (!cacheEntry) return NRC_BADDR;
453 queryData.cacheEntry = NULL;
454 queryData.ret = NRC_GOODRET;
455 for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
458 if (!NCB_CANCELLED(ncb))
460 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
461 adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
464 queryData.ret = NetBTWaitForNameResponse(adapter, fd,
465 GetTickCount() + timeout, NetBTFindNameAnswerCallback,
468 queryData.ret = NRC_SYSTEM;
471 queryData.ret = NRC_CMDCAN;
473 if (queryData.cacheEntry)
475 memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
476 memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
478 *cacheEntry = queryData.cacheEntry;
479 return queryData.ret;
482 /* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
483 * has not yet been created, creates it, using gCacheTimeout as the cache
484 * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails,
486 * Returns NRC_GOODRET on success, and something else on failure.
488 static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
489 NBNameCacheEntry *cacheEntry)
493 if (!nameCache) return NRC_BADDR;
494 if (!cacheEntry) return NRC_BADDR;
497 *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
499 ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
500 ? NRC_GOODRET : NRC_OSRESNOTAV;
503 HeapFree(GetProcessHeap(), 0, cacheEntry);
504 ret = NRC_OSRESNOTAV;
509 /* Attempts to look up name using gethostbyname(), if the suffix byte is either
510 * <00> or <20>. If the name can be looked up, returns 0 and stores the looked
511 * up addresses as a NBNameCacheEntry in *cacheEntry.
512 * Returns NRC_GOODRET on success, though this may not mean the name was
513 * resolved--check whether *cacheEntry is NULL. Returns something else on
516 static UCHAR NetBTgethostbyname(const UCHAR name[NCBNAMSZ],
517 NBNameCacheEntry **cacheEntry)
519 UCHAR ret = NRC_GOODRET;
521 TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
523 if (!name) return NRC_BADDR;
524 if (!cacheEntry) return NRC_BADDR;
526 if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
527 name[NCBNAMSZ - 1] == 0x20))
529 UCHAR toLookup[NCBNAMSZ];
532 for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
533 toLookup[i] = name[i];
536 if (isdigit(toLookup[0]))
538 unsigned long addr = inet_addr(toLookup);
540 if (addr != INADDR_NONE)
542 *cacheEntry = (NBNameCacheEntry *)HeapAlloc(GetProcessHeap(),
543 0, sizeof(NBNameCacheEntry));
546 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
547 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
548 (*cacheEntry)->nbname[0] = '*';
549 (*cacheEntry)->numAddresses = 1;
550 (*cacheEntry)->addresses[0] = addr;
553 ret = NRC_OSRESNOTAV;
556 if (ret == NRC_GOODRET && !*cacheEntry)
558 struct hostent *host;
560 if ((host = gethostbyname(toLookup)) != NULL)
562 for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
563 host->h_addr_list[i]; i++)
565 if (host->h_addr_list && host->h_addr_list[0])
567 *cacheEntry = (NBNameCacheEntry *)HeapAlloc(
568 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
569 (i - 1) * sizeof(DWORD));
572 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
573 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
574 (*cacheEntry)->nbname[0] = '*';
575 (*cacheEntry)->numAddresses = i;
576 for (i = 0; i < (*cacheEntry)->numAddresses; i++)
577 (*cacheEntry)->addresses[i] =
578 (DWORD)host->h_addr_list[i];
581 ret = NRC_OSRESNOTAV;
587 TRACE("returning 0x%02x\n", ret);
591 /* Looks up the name in ncb->ncb_callname, first in the name caches (global
592 * and this adapter's), then using gethostbyname(), next by WINS if configured,
593 * and finally using broadcast NetBT name resolution. In NBT parlance, this
594 * makes this an "H-node". Stores an entry in the appropriate name cache for a
595 * found node, and returns it as *cacheEntry.
596 * Assumes data, ncb, and cacheEntry are not NULL.
597 * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
598 * just that all name lookup operations completed successfully--and something
599 * else on failure. *cacheEntry will be NULL if the name was not found.
601 static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
602 const NBNameCacheEntry **cacheEntry)
604 UCHAR ret = NRC_GOODRET;
606 TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
608 if (!cacheEntry) return NRC_BADDR;
611 if (!adapter) return NRC_BADDR;
612 if (!ncb) return NRC_BADDR;
614 if (ncb->ncb_callname[0] == '*')
618 *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
620 *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
624 NBNameCacheEntry *newEntry = NULL;
626 ret = NetBTgethostbyname(ncb->ncb_callname, &newEntry);
627 if (ret == NRC_GOODRET && newEntry)
629 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
630 if (ret != NRC_GOODRET)
635 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
636 0, WSA_FLAG_OVERLAPPED);
638 if(fd == INVALID_SOCKET)
639 ret = NRC_OSRESNOTAV;
644 adapter->nameQueryXID++;
645 for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
646 && winsNdx < gNumWINSServers; winsNdx++)
647 ret = NetBTNameWaitLoop(adapter, fd, ncb,
648 gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
649 gWINSQueries, &newEntry);
650 if (ret == NRC_GOODRET && newEntry)
652 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
653 if (ret != NRC_GOODRET)
656 if (ret == NRC_GOODRET && *cacheEntry == NULL)
658 ret = NetBTNameWaitLoop(adapter, fd, ncb,
659 adapter->ipr.dwBCastAddr, TRUE, gBCastQueryTimeout,
660 gBCastQueries, &newEntry);
661 if (ret == NRC_GOODRET && newEntry)
663 ret = NetBTStoreCacheEntry(&adapter->nameCache,
665 if (ret != NRC_GOODRET)
672 *cacheEntry = newEntry;
675 TRACE("returning 0x%02x\n", ret);
679 typedef struct _NetBTNodeQueryData
682 PADAPTER_STATUS astat;
684 } NetBTNodeQueryData;
686 /* Callback function for NetBTAstatRemote, parses the rData for the node
687 * status and name list of the remote node. Always returns FALSE, since
688 * there's never more than one answer we care about in a node status response.
690 static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
691 WORD answerIndex, PUCHAR rData, WORD rLen)
693 NetBTNodeQueryData *data = (NetBTNodeQueryData *)pVoid;
695 if (data && !data->gotResponse && rData && rLen >= 1)
697 /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
698 if (rLen >= rData[0] * (NCBNAMSZ + 2))
704 data->gotResponse = TRUE;
705 data->astat->name_count = rData[0];
706 for (i = 0, src = rData + 1,
707 dst = (PNAME_BUFFER)((PUCHAR)data->astat +
708 sizeof(ADAPTER_STATUS));
709 i < data->astat->name_count && src - rData < rLen &&
710 (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
711 i++, dst++, src += NCBNAMSZ + 2)
713 UCHAR flags = *(src + NCBNAMSZ);
715 memcpy(dst->name, src, NCBNAMSZ);
716 /* we won't actually see a registering name in the returned
717 * response. It's useful to see if no other flags are set; if
718 * none are, then the name is registered. */
719 dst->name_flags = REGISTERING;
721 dst->name_flags |= GROUP_NAME;
723 dst->name_flags |= DEREGISTERED;
725 dst->name_flags |= DUPLICATE;
726 if (dst->name_flags == REGISTERING)
727 dst->name_flags = REGISTERED;
729 /* arbitrarily set HW type to Ethernet */
730 data->astat->adapter_type = 0xfe;
731 if (src - rData < rLen)
732 memcpy(data->astat->adapter_address, src,
733 min(rLen - (src - rData), 6));
739 /* This uses the WINS timeout and query values, as they're the
740 * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
742 static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
744 UCHAR ret = NRC_GOODRET;
745 const NBNameCacheEntry *cacheEntry = NULL;
747 TRACE("adapter %p, NCB %p\n", adapter, ncb);
749 if (!adapter) return NRC_BADDR;
750 if (!ncb) return NRC_INVADDRESS;
752 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
753 if (ret == NRC_GOODRET && cacheEntry)
755 if (cacheEntry->numAddresses > 0)
757 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
758 WSA_FLAG_OVERLAPPED);
760 if(fd == INVALID_SOCKET)
761 ret = NRC_OSRESNOTAV;
764 NetBTNodeQueryData queryData;
766 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
768 adapter->nameQueryXID++;
769 astat->name_count = 0;
770 queryData.gotResponse = FALSE;
771 queryData.astat = astat;
772 queryData.astatLen = ncb->ncb_length;
773 for (queries = 0; !queryData.gotResponse &&
774 queries < gWINSQueries; queries++)
776 if (!NCB_CANCELLED(ncb))
778 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
779 adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
780 cacheEntry->addresses[0], FALSE);
783 ret = NetBTWaitForNameResponse(adapter, fd,
784 GetTickCount() + gWINSQueryTimeout,
785 NetBTNodeStatusAnswerCallback, &queryData);
798 else if (ret == NRC_CMDCAN)
799 ; /* do nothing, we were cancelled */
802 TRACE("returning 0x%02x\n", ret);
806 static UCHAR NetBTAstat(void *adapt, PNCB ncb)
808 NetBTAdapter *adapter = (NetBTAdapter *)adapt;
811 TRACE("adapt %p, NCB %p\n", adapt, ncb);
813 if (!adapter) return NRC_ENVNOTDEF;
814 if (!ncb) return NRC_INVADDRESS;
815 if (!ncb->ncb_buffer) return NRC_BADDR;
816 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
818 if (ncb->ncb_callname[0] == '*')
822 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
824 memset(astat, 0, sizeof(ADAPTER_STATUS));
825 astat->rev_major = 3;
826 ifRow.dwIndex = adapter->ipr.dwIndex;
827 if (GetIfEntry(&ifRow) != NO_ERROR)
831 physAddrLen = min(ifRow.dwPhysAddrLen, 6);
833 memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
834 /* doubt anyone cares, but why not.. */
835 if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
836 astat->adapter_type = 0xff;
838 astat->adapter_type = 0xfe; /* for Ethernet */
839 astat->max_sess_pktsize = 0xffff;
840 astat->xmit_success = adapter->xmit_success;
841 astat->recv_success = adapter->recv_success;
846 ret = NetBTAstatRemote(adapter, ncb);
847 TRACE("returning 0x%02x\n", ret);
851 static UCHAR NetBTFindName(void *adapt, PNCB ncb)
853 NetBTAdapter *adapter = (NetBTAdapter *)adapt;
855 const NBNameCacheEntry *cacheEntry = NULL;
856 PFIND_NAME_HEADER foundName;
858 TRACE("adapt %p, NCB %p\n", adapt, ncb);
860 if (!adapter) return NRC_ENVNOTDEF;
861 if (!ncb) return NRC_INVADDRESS;
862 if (!ncb->ncb_buffer) return NRC_BADDR;
863 if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
865 foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
866 memset(foundName, 0, sizeof(FIND_NAME_HEADER));
868 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
869 if (ret == NRC_GOODRET)
873 DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
874 sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
877 for (ndx = 0; ndx < spaceFor; ndx++)
879 PFIND_NAME_BUFFER findNameBuffer;
882 (PFIND_NAME_BUFFER)((PUCHAR)foundName +
883 sizeof(FIND_NAME_HEADER) + foundName->node_count *
884 sizeof(FIND_NAME_BUFFER));
885 memset(findNameBuffer->destination_addr, 0, 2);
886 memcpy(findNameBuffer->destination_addr + 2,
887 &adapter->ipr.dwAddr, sizeof(DWORD));
888 memset(findNameBuffer->source_addr, 0, 2);
889 memcpy(findNameBuffer->source_addr + 2,
890 &cacheEntry->addresses[ndx], sizeof(DWORD));
891 foundName->node_count++;
893 if (spaceFor < cacheEntry->numAddresses)
899 TRACE("returning 0x%02x\n", ret);
903 static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
904 const UCHAR *callingName)
906 UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
908 DWORD bytesSent, bytesReceived, recvFlags = 0;
911 buffer[0] = NBSS_REQ;
914 len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
915 len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
917 NBR_ADDWORD(&buffer[2], len);
919 wsaBuf.len = len + NBSS_HDRSIZE;
922 r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
923 if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
925 ERR("send failed\n");
929 /* I've already set the recv timeout on this socket (if it supports it), so
930 * just block. Hopefully we'll always receive the session acknowledgement
931 * within one timeout.
933 wsaBuf.len = NBSS_HDRSIZE + 1;
934 r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
935 if (r < 0 || bytesReceived < NBSS_HDRSIZE)
937 else if (buffer[0] == NBSS_NACK)
939 if (r == NBSS_HDRSIZE + 1)
941 switch (buffer[NBSS_HDRSIZE])
943 case NBSS_ERR_INSUFFICIENT_RESOURCES:
953 else if (buffer[0] == NBSS_RETARGET)
955 FIXME("Got a session retarget, can't deal\n");
958 else if (buffer[0] == NBSS_ACK)
963 TRACE("returning 0x%02x\n", ret);
967 static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
969 NetBTAdapter *adapter = (NetBTAdapter *)adapt;
971 const NBNameCacheEntry *cacheEntry = NULL;
973 TRACE("adapt %p, ncb %p", adapt, ncb);
975 if (!adapter) return NRC_ENVNOTDEF;
976 if (!ncb) return NRC_INVADDRESS;
977 if (!sess) return NRC_BADDR;
979 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
980 if (ret == NRC_GOODRET)
982 if (cacheEntry && cacheEntry->numAddresses > 0)
986 fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
987 WSA_FLAG_OVERLAPPED);
988 if (fd != INVALID_SOCKET)
991 struct sockaddr_in sin;
993 if (ncb->ncb_rto > 0)
995 timeout = ncb->ncb_rto * 500;
996 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (PUCHAR)&timeout,
999 if (ncb->ncb_rto > 0)
1001 timeout = ncb->ncb_sto * 500;
1002 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (PUCHAR)&timeout,
1006 memset(&sin, 0, sizeof(sin));
1007 memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
1008 sizeof(sin.sin_addr));
1009 sin.sin_family = AF_INET;
1010 sin.sin_port = htons(PORT_NBSS);
1011 /* FIXME: use nonblocking mode for the socket, check the
1012 * cancel flag periodically
1014 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
1019 static UCHAR fakedCalledName[] = "*SMBSERVER";
1020 const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
1021 ? fakedCalledName : cacheEntry->nbname;
1023 ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
1024 if (ret != NRC_GOODRET && calledParty[0] == '*')
1026 FIXME("NBT session to \"*SMBSERVER\" refused,\n");
1027 FIXME("should try finding name using ASTAT\n");
1030 if (ret != NRC_GOODRET)
1034 NetBTSession *session = (NetBTSession *)HeapAlloc(
1035 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
1040 InitializeCriticalSection(&session->cs);
1045 ret = NRC_OSRESNOTAV;
1051 ret = NRC_OSRESNOTAV;
1056 TRACE("returning 0x%02x\n", ret);
1060 /* Notice that I don't protect against multiple thread access to NetBTSend.
1061 * This is because I don't update any data in the adapter, and I only make a
1062 * single call to WSASend, which I assume to act atomically (not interleaving
1063 * data from other threads).
1064 * I don't lock, because I only depend on the fd being valid, and this won't be
1065 * true until a session setup is completed.
1067 static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
1069 NetBTAdapter *adapter = (NetBTAdapter *)adapt;
1070 NetBTSession *session = (NetBTSession *)sess;
1071 UCHAR buffer[NBSS_HDRSIZE], ret;
1076 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1078 if (!adapter) return NRC_ENVNOTDEF;
1079 if (!ncb) return NRC_INVADDRESS;
1080 if (!ncb->ncb_buffer) return NRC_BADDR;
1081 if (!session) return NRC_SNUMOUT;
1082 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1084 buffer[0] = NBSS_MSG;
1086 NBR_ADDWORD(&buffer[2], ncb->ncb_length);
1088 wsaBufs[0].len = NBSS_HDRSIZE;
1089 wsaBufs[0].buf = buffer;
1090 wsaBufs[1].len = ncb->ncb_length;
1091 wsaBufs[1].buf = ncb->ncb_buffer;
1093 r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
1094 &bytesSent, 0, NULL, NULL);
1095 if (r == SOCKET_ERROR)
1097 NetBIOSHangupSession(ncb);
1100 else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
1102 FIXME("Only sent %ld bytes (of %d), hanging up session\n", bytesSent,
1103 NBSS_HDRSIZE + ncb->ncb_length);
1104 NetBIOSHangupSession(ncb);
1110 adapter->xmit_success++;
1112 TRACE("returning 0x%02x\n", ret);
1116 static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
1118 NetBTAdapter *adapter = (NetBTAdapter *)adapt;
1119 NetBTSession *session = (NetBTSession *)sess;
1120 UCHAR buffer[NBSS_HDRSIZE], ret;
1123 DWORD bufferCount, bytesReceived, flags;
1125 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1127 if (!adapter) return NRC_ENVNOTDEF;
1128 if (!ncb) return NRC_BADDR;
1129 if (!ncb->ncb_buffer) return NRC_BADDR;
1130 if (!session) return NRC_SNUMOUT;
1131 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1133 EnterCriticalSection(&session->cs);
1135 if (session->bytesPending == 0)
1138 wsaBufs[0].len = NBSS_HDRSIZE;
1139 wsaBufs[0].buf = buffer;
1141 wsaBufs[bufferCount].len = ncb->ncb_length;
1142 wsaBufs[bufferCount].buf = ncb->ncb_buffer;
1146 /* FIXME: should poll a bit so I can check the cancel flag */
1147 r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
1149 if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
1151 LeaveCriticalSection(&session->cs);
1152 ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
1153 NetBIOSHangupSession(ncb);
1156 else if (NCB_CANCELLED(ncb))
1158 LeaveCriticalSection(&session->cs);
1163 if (bufferCount == 2)
1165 if (buffer[0] == NBSS_KEEPALIVE)
1167 LeaveCriticalSection(&session->cs);
1168 FIXME("Oops, received a session keepalive and lost my place\n");
1169 /* need to read another session header until we get a session
1170 * message header. */
1171 NetBIOSHangupSession(ncb);
1174 else if (buffer[0] != NBSS_MSG)
1176 LeaveCriticalSection(&session->cs);
1177 FIXME("Received unexpected session msg type %d\n", buffer[0]);
1178 NetBIOSHangupSession(ncb);
1183 if (buffer[1] & NBSS_EXTENSION)
1185 LeaveCriticalSection(&session->cs);
1186 FIXME("Received a message that's too long for my taste\n");
1187 NetBIOSHangupSession(ncb);
1192 session->bytesPending = NBSS_HDRSIZE
1193 + NBR_GETWORD(&buffer[2]) - bytesReceived;
1194 ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
1195 LeaveCriticalSection(&session->cs);
1201 if (bytesReceived < session->bytesPending)
1202 session->bytesPending -= bytesReceived;
1204 session->bytesPending = 0;
1205 LeaveCriticalSection(&session->cs);
1206 ncb->ncb_length = bytesReceived;
1208 if (session->bytesPending > 0)
1213 adapter->recv_success++;
1216 TRACE("returning 0x%02x\n", ret);
1220 static UCHAR NetBTHangup(void *adapt, void *sess)
1222 NetBTSession *session = (NetBTSession *)sess;
1224 TRACE("adapt %p, session %p\n", adapt, session);
1226 if (!session) return NRC_SNUMOUT;
1228 /* I don't lock the session, because NetBTRecv knows not to decrement
1229 * past 0, so if a receive completes after this it should still deal.
1231 closesocket(session->fd);
1232 session->fd = INVALID_SOCKET;
1233 session->bytesPending = 0;
1234 DeleteCriticalSection(&session->cs);
1235 HeapFree(GetProcessHeap(), 0, session);
1240 static void NetBTCleanupAdapter(void *adapt)
1242 TRACE("adapt %p\n", adapt);
1245 NetBTAdapter *adapter = (NetBTAdapter *)adapt;
1247 if (adapter->nameCache)
1248 NBNameCacheDestroy(adapter->nameCache);
1249 HeapFree(GetProcessHeap(), 0, adapt);
1253 static void NetBTCleanup(void)
1258 NBNameCacheDestroy(gNameCache);
1263 static UCHAR NetBTRegisterAdapter(PMIB_IPADDRROW ipRow)
1266 NetBTAdapter *adapter;
1268 if (!ipRow) return NRC_BADDR;
1270 adapter = (NetBTAdapter *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1271 sizeof(NetBTAdapter));
1274 memcpy(&adapter->ipr, ipRow, sizeof(MIB_IPADDRROW));
1275 if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
1277 NetBTCleanupAdapter(adapter);
1284 ret = NRC_OSRESNOTAV;
1288 /* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to
1289 * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
1290 * NetBIOS adapter table. For each callback, checks if the passed-in adapt
1291 * has an entry in the table; if so, this adapter was enumerated previously,
1292 * and it's enabled. As a flag, the table's dwAddr entry is changed to
1293 * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
1294 * The NetBTEnum function will add any remaining adapters from the
1295 * MIB_IPADDRTABLE to the NetBIOS adapter table.
1297 static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
1298 ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
1301 PMIB_IPADDRTABLE table = (PMIB_IPADDRTABLE)closure;
1308 for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
1310 const NetBTAdapter *adapter = (const NetBTAdapter *)data->data;
1312 if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
1314 NetBIOSEnableAdapter(data->lana);
1315 table->table[ndx].dwAddr = INADDR_LOOPBACK;
1325 /* Enumerates adapters by:
1326 * - retrieving the IP address table for the local machine
1327 * - eliminating loopback addresses from the table
1328 * - eliminating redundant addresses, that is, multiple addresses on the same
1330 * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
1331 * data. The callback reenables each adapter that's already in the NetBIOS
1332 * table. After NetBIOSEnumAdapters returns, this function adds any remaining
1333 * adapters to the NetBIOS table.
1335 static UCHAR NetBTEnum(void)
1342 if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
1344 PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
1345 DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
1346 sizeof(MIB_IPADDRROW) + 1;
1348 ipAddrs = (PMIB_IPADDRTABLE)HeapAlloc(GetProcessHeap(),
1349 HEAP_ZERO_MEMORY, size);
1351 coalesceTable = (PMIB_IPADDRTABLE)HeapAlloc(GetProcessHeap(),
1352 HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
1353 (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
1354 if (ipAddrs && coalesceTable)
1356 if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
1360 for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
1362 if ((ipAddrs->table[ndx].dwAddr &
1363 ipAddrs->table[ndx].dwMask) !=
1364 htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
1366 BOOL newNetwork = TRUE;
1369 /* make sure we don't have more than one entry
1371 for (innerIndex = 0; newNetwork &&
1372 innerIndex < coalesceTable->dwNumEntries; innerIndex++)
1373 if ((ipAddrs->table[ndx].dwAddr &
1374 ipAddrs->table[ndx].dwMask) ==
1375 (coalesceTable->table[innerIndex].dwAddr
1376 & coalesceTable->table[innerIndex].dwMask))
1380 memcpy(&coalesceTable->table[
1381 coalesceTable->dwNumEntries++],
1382 &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
1386 NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
1389 for (ndx = 0; ret == NRC_GOODRET &&
1390 ndx < coalesceTable->dwNumEntries; ndx++)
1391 if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
1392 ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
1396 HeapFree(GetProcessHeap(), 0, ipAddrs);
1397 HeapFree(GetProcessHeap(), 0, coalesceTable);
1400 ret = NRC_OSRESNOTAV;
1404 TRACE("returning 0x%02x\n", ret);
1408 /* Initializes global variables and registers the NetBT transport */
1409 void NetBTInit(void)
1412 NetBIOSTransport transport;
1416 gBCastQueries = BCAST_QUERIES;
1417 gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
1418 gWINSQueries = WINS_QUERIES;
1419 gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
1420 gNumWINSServers = 0;
1421 memset(gWINSServers, 0, sizeof(gWINSServers));
1423 gCacheTimeout = CACHE_TIMEOUT;
1425 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
1426 "\\SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0, KEY_READ, &hKey)
1429 DWORD dword, size = sizeof(dword), ndx;
1430 static const char *nsValueNames[] =
1431 { "NameServer", "BackupNameServer" };
1434 size = sizeof(dword);
1435 if (RegQueryValueExA(hKey, "BcastNameQueryCount", NULL, NULL,
1436 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1437 && dword <= MAX_QUERIES)
1438 gBCastQueries = dword;
1439 size = sizeof(dword);
1440 if (RegQueryValueExA(hKey, "BcastNameQueryTimeout", NULL, NULL,
1441 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
1442 && dword <= MAX_QUERY_TIMEOUT)
1443 gBCastQueryTimeout = dword;
1444 size = sizeof(dword);
1445 if (RegQueryValueExA(hKey, "NameSrvQueryCount", NULL, NULL,
1446 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1447 && dword <= MAX_QUERIES)
1448 gWINSQueries = dword;
1449 size = sizeof(dword);
1450 if (RegQueryValueExA(hKey, "NameSrvQueryTimeout", NULL, NULL,
1451 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
1452 && dword <= MAX_QUERY_TIMEOUT)
1453 gWINSQueryTimeout = dword;
1454 for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
1457 size = sizeof(nsString) / sizeof(char);
1458 if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
1459 (LPBYTE)nsString, &size) == ERROR_SUCCESS)
1461 unsigned long addr = inet_addr(nsString);
1463 if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
1464 gWINSServers[gNumWINSServers++] = addr;
1467 size = MAX_DOMAIN_NAME_LEN - 1;
1468 if (RegQueryValueExA(hKey, "ScopeID", NULL, NULL, gScopeID + 1, &size)
1471 /* convert into L2-encoded version, suitable for use by
1475 for (ptr = gScopeID + 1; *ptr &&
1476 ptr - gScopeID < MAX_DOMAIN_NAME_LEN; )
1478 for (lenPtr = ptr - 1, *lenPtr = 0; *ptr && *ptr != '.' &&
1479 ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ptr++)
1484 if (RegQueryValueExA(hKey, "CacheTimeout", NULL, NULL,
1485 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
1486 gCacheTimeout = dword;
1490 transport.enumerate = NetBTEnum;
1491 transport.astat = NetBTAstat;
1492 transport.findName = NetBTFindName;
1493 transport.call = NetBTCall;
1494 transport.send = NetBTSend;
1495 transport.recv = NetBTRecv;
1496 transport.hangup = NetBTHangup;
1497 transport.cleanupAdapter = NetBTCleanupAdapter;
1498 transport.cleanup = NetBTCleanup;
1499 memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
1500 NetBIOSRegisterTransport(gTransportID, &transport);