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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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.
74 #include "wine/debug.h"
79 #include "nbnamecache.h"
81 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
88 #define INADDR_NONE ~0UL
91 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
92 #define NBR_GETWORD(p) ntohs(*(WORD *)(p))
95 #define MAX_QUERIES 0xffff
96 #define MIN_QUERY_TIMEOUT 100
97 #define MAX_QUERY_TIMEOUT 0xffffffff
98 #define BCAST_QUERIES 3
99 #define BCAST_QUERY_TIMEOUT 750
100 #define WINS_QUERIES 3
101 #define WINS_QUERY_TIMEOUT 750
102 #define MAX_WINS_SERVERS 2
103 #define MIN_CACHE_TIMEOUT 60000
104 #define CACHE_TIMEOUT 360000
106 #define MAX_NBT_NAME_SZ 255
107 #define SIMPLE_NAME_QUERY_PKT_SIZE 16 + MAX_NBT_NAME_SZ
109 #define NBNS_TYPE_NB 0x0020
110 #define NBNS_TYPE_NBSTAT 0x0021
111 #define NBNS_CLASS_INTERNET 0x00001
112 #define NBNS_HEADER_SIZE (sizeof(WORD) * 6)
113 #define NBNS_RESPONSE_AND_OPCODE 0xf800
114 #define NBNS_RESPONSE_AND_QUERY 0x8000
115 #define NBNS_REPLYCODE 0x0f
117 #define NBSS_HDRSIZE 4
119 #define NBSS_MSG 0x00
120 #define NBSS_REQ 0x81
121 #define NBSS_ACK 0x82
122 #define NBSS_NACK 0x83
123 #define NBSS_RETARGET 0x84
124 #define NBSS_KEEPALIVE 0x85
126 #define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80
127 #define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
128 #define NBSS_ERR_BAD_NAME 0x82
129 #define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83
131 #define NBSS_EXTENSION 0x01
133 typedef struct _NetBTSession
140 typedef struct _NetBTAdapter
144 struct NBNameCache *nameCache;
149 static ULONG gTransportID;
150 static BOOL gEnableDNS;
151 static DWORD gBCastQueries;
152 static DWORD gBCastQueryTimeout;
153 static DWORD gWINSQueries;
154 static DWORD gWINSQueryTimeout;
155 static DWORD gWINSServers[MAX_WINS_SERVERS];
156 static int gNumWINSServers;
157 static char gScopeID[MAX_SCOPE_ID_LEN];
158 static DWORD gCacheTimeout;
159 static struct NBNameCache *gNameCache;
161 /* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
162 * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
163 * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes
164 * if p is NULL-terminated. Returns the number of bytes stored in buffer.
166 static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
171 if (!buffer) return 0;
173 buffer[len++] = NCBNAMSZ * 2;
174 for (i = 0; p[i] && i < NCBNAMSZ; i++)
176 buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
177 buffer[len++] = (p[i] & 0x0f) + 'A';
179 while (len < NCBNAMSZ * 2)
186 int scopeIDLen = strlen(gScopeID);
188 memcpy(buffer + len, gScopeID, scopeIDLen);
191 buffer[len++] = 0; /* add second terminator */
195 /* Creates a NBT name request packet for name in buffer. If broadcast is true,
196 * creates a broadcast request, otherwise creates a unicast request.
197 * Returns the number of bytes stored in buffer.
199 static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
200 BOOL broadcast, UCHAR *buffer, int len)
204 if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
206 NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */
209 NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
214 NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
217 NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
218 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
219 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
220 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
222 i += NetBTNameEncode(name, &buffer[i]);
224 NBR_ADDWORD(&buffer[i],qtype); i+=2;
225 NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
230 /* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on
231 * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not
233 * Returns 0 on success, -1 on failure.
235 static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
236 WORD qtype, DWORD destAddr, BOOL broadcast)
241 addr.s_addr = destAddr;
242 TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
245 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
249 UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
250 struct sockaddr_in sin;
252 memset(&sin, 0, sizeof(sin));
253 sin.sin_addr.s_addr = destAddr;
254 sin.sin_family = AF_INET;
255 sin.sin_port = htons(PORT_NBNS);
257 wsaBuf.buf = (CHAR*)buf;
258 wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
264 ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
265 (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
266 if (ret < 0 || bytesSent < wsaBuf.len)
277 typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
278 WORD answerIndex, PUCHAR rData, WORD rdLength);
280 /* Waits on fd until GetTickCount() returns a value greater than or equal to
281 * waitUntil for a name service response. If a name response matching xid
282 * is received, calls answerCallback once for each answer resource record in
283 * the response. (The callback's answerCount will be the total number of
284 * answers to expect, and answerIndex will be the 0-based index that's being
285 * sent this time.) Quits parsing if answerCallback returns FALSE.
286 * Returns NRC_GOODRET on timeout or a valid response received, something else
289 static UCHAR NetBTWaitForNameResponse(const NetBTAdapter *adapter, SOCKET fd,
290 DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
294 UCHAR ret = NRC_GOODRET;
296 if (!adapter) return NRC_BADDR;
297 if (fd == INVALID_SOCKET) return NRC_BADDR;
298 if (!answerCallback) return NRC_BADDR;
300 while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
302 DWORD msToWait = waitUntil - now;
304 struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
309 r = select(fd + 1, &fds, NULL, NULL, &timeout);
314 /* FIXME: magic #, is this always enough? */
317 struct sockaddr_in fromaddr;
318 WORD respXID, flags, queryCount, answerCount;
319 WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
320 DWORD bytesReceived, recvFlags = 0;
322 fromsize = sizeof(fromaddr);
323 r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
324 (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
331 if (bytesReceived < NBNS_HEADER_SIZE)
334 respXID = NBR_GETWORD(buffer);
335 if (adapter->nameQueryXID != respXID)
338 flags = NBR_GETWORD(buffer + 2);
339 queryCount = NBR_GETWORD(buffer + 4);
340 answerCount = NBR_GETWORD(buffer + 6);
342 /* a reply shouldn't contain a query, ignore bad packet */
346 if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
348 if ((flags & NBNS_REPLYCODE) != 0)
350 else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
352 PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
353 BOOL shouldContinue = TRUE;
354 WORD answerIndex = 0;
357 /* decode one answer at a time */
358 while (ret == NRC_GOODRET && answerIndex < answerCount &&
359 ptr - buffer < bytesReceived && shouldContinue)
364 for (; ptr[0] && ptr - buffer < bytesReceived; )
367 ptr += 2; /* scan past type */
368 if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
369 && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
372 ret = NRC_SYSTEM; /* parse error */
373 ptr += sizeof(DWORD); /* TTL */
374 rLen = NBR_GETWORD(ptr);
375 rLen = min(rLen, bytesReceived - (ptr - buffer));
377 shouldContinue = answerCallback(data, answerCount,
378 answerIndex, ptr, rLen);
386 TRACE("returning 0x%02x\n", ret);
390 typedef struct _NetBTNameQueryData {
391 NBNameCacheEntry *cacheEntry;
393 } NetBTNameQueryData;
395 /* Name query callback function for NetBTWaitForNameResponse, creates a cache
396 * entry on the first answer, adds each address as it's called again (as long
397 * as there's space). If there's an error that should be propagated as the
398 * NetBIOS error, modifies queryData's ret member to the proper return code.
400 static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
401 WORD answerIndex, PUCHAR rData, WORD rLen)
403 NetBTNameQueryData *queryData = pVoid;
408 if (queryData->cacheEntry == NULL)
410 queryData->cacheEntry = HeapAlloc(
411 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
412 (answerCount - 1) * sizeof(DWORD));
413 if (queryData->cacheEntry)
414 queryData->cacheEntry->numAddresses = 0;
416 queryData->ret = NRC_OSRESNOTAV;
418 if (rLen == 6 && queryData->cacheEntry &&
419 queryData->cacheEntry->numAddresses < answerCount)
421 queryData->cacheEntry->addresses[queryData->cacheEntry->
422 numAddresses++] = *(const DWORD *)(rData + 2);
423 ret = queryData->cacheEntry->numAddresses < answerCount;
433 /* Workhorse NetBT name lookup function. Sends a name lookup query for
434 * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
435 * adapter->nameQueryXID as the transaction ID. Waits up to timeout
436 * milliseconds, and retries up to maxQueries times, waiting for a reply.
437 * If a valid response is received, stores the looked up addresses as a
438 * NBNameCacheEntry in *cacheEntry.
439 * Returns NRC_GOODRET on success, though this may not mean the name was
440 * resolved--check whether *cacheEntry is NULL.
442 static UCHAR NetBTNameWaitLoop(const NetBTAdapter *adapter, SOCKET fd, const NCB *ncb,
443 DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
444 NBNameCacheEntry **cacheEntry)
446 unsigned int queries;
447 NetBTNameQueryData queryData;
449 if (!adapter) return NRC_BADDR;
450 if (fd == INVALID_SOCKET) return NRC_BADDR;
451 if (!ncb) return NRC_BADDR;
452 if (!cacheEntry) return NRC_BADDR;
454 queryData.cacheEntry = NULL;
455 queryData.ret = NRC_GOODRET;
456 for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
459 if (!NCB_CANCELLED(ncb))
461 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
462 adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
465 queryData.ret = NetBTWaitForNameResponse(adapter, fd,
466 GetTickCount() + timeout, NetBTFindNameAnswerCallback,
469 queryData.ret = NRC_SYSTEM;
472 queryData.ret = NRC_CMDCAN;
474 if (queryData.cacheEntry)
476 memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
477 memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
479 *cacheEntry = queryData.cacheEntry;
480 return queryData.ret;
483 /* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
484 * has not yet been created, creates it, using gCacheTimeout as the cache
485 * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails,
487 * Returns NRC_GOODRET on success, and something else on failure.
489 static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
490 NBNameCacheEntry *cacheEntry)
494 if (!nameCache) return NRC_BADDR;
495 if (!cacheEntry) return NRC_BADDR;
498 *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
500 ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
501 ? NRC_GOODRET : NRC_OSRESNOTAV;
504 HeapFree(GetProcessHeap(), 0, cacheEntry);
505 ret = NRC_OSRESNOTAV;
510 /* Attempts to resolve name using inet_addr(), then gethostbyname() if
511 * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name
512 * can be looked up, returns 0 and stores the looked up addresses as a
513 * NBNameCacheEntry in *cacheEntry.
514 * Returns NRC_GOODRET on success, though this may not mean the name was
515 * resolved--check whether *cacheEntry is NULL. Returns something else on
518 static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
519 NBNameCacheEntry **cacheEntry)
521 UCHAR ret = NRC_GOODRET;
523 TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
525 if (!name) return NRC_BADDR;
526 if (!cacheEntry) return NRC_BADDR;
528 if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
529 name[NCBNAMSZ - 1] == 0x20))
531 CHAR toLookup[NCBNAMSZ];
534 for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
535 toLookup[i] = name[i];
538 if (isdigit(toLookup[0]))
540 unsigned long addr = inet_addr(toLookup);
542 if (addr != INADDR_NONE)
544 *cacheEntry = HeapAlloc(GetProcessHeap(),
545 0, sizeof(NBNameCacheEntry));
548 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
549 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
550 (*cacheEntry)->nbname[0] = '*';
551 (*cacheEntry)->numAddresses = 1;
552 (*cacheEntry)->addresses[0] = addr;
555 ret = NRC_OSRESNOTAV;
558 if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
560 struct hostent *host;
562 if ((host = gethostbyname(toLookup)) != NULL)
564 for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
565 host->h_addr_list[i]; i++)
567 if (host->h_addr_list && host->h_addr_list[0])
569 *cacheEntry = HeapAlloc(
570 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
571 (i - 1) * sizeof(DWORD));
574 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
575 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
576 (*cacheEntry)->nbname[0] = '*';
577 (*cacheEntry)->numAddresses = i;
578 for (i = 0; i < (*cacheEntry)->numAddresses; i++)
579 (*cacheEntry)->addresses[i] =
580 (DWORD)host->h_addr_list[i];
583 ret = NRC_OSRESNOTAV;
589 TRACE("returning 0x%02x\n", ret);
593 /* Looks up the name in ncb->ncb_callname, first in the name caches (global
594 * and this adapter's), then using gethostbyname(), next by WINS if configured,
595 * and finally using broadcast NetBT name resolution. In NBT parlance, this
596 * makes this an "H-node". Stores an entry in the appropriate name cache for a
597 * found node, and returns it as *cacheEntry.
598 * Assumes data, ncb, and cacheEntry are not NULL.
599 * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
600 * just that all name lookup operations completed successfully--and something
601 * else on failure. *cacheEntry will be NULL if the name was not found.
603 static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
604 const NBNameCacheEntry **cacheEntry)
606 UCHAR ret = NRC_GOODRET;
608 TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
610 if (!cacheEntry) return NRC_BADDR;
613 if (!adapter) return NRC_BADDR;
614 if (!ncb) return NRC_BADDR;
616 if (ncb->ncb_callname[0] == '*')
620 *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
622 *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
626 NBNameCacheEntry *newEntry = NULL;
628 ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
629 if (ret == NRC_GOODRET && newEntry)
631 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
632 if (ret != NRC_GOODRET)
637 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
638 0, WSA_FLAG_OVERLAPPED);
640 if(fd == INVALID_SOCKET)
641 ret = NRC_OSRESNOTAV;
646 adapter->nameQueryXID++;
647 for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
648 && winsNdx < gNumWINSServers; winsNdx++)
649 ret = NetBTNameWaitLoop(adapter, fd, ncb,
650 gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
651 gWINSQueries, &newEntry);
652 if (ret == NRC_GOODRET && newEntry)
654 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
655 if (ret != NRC_GOODRET)
658 if (ret == NRC_GOODRET && *cacheEntry == NULL)
661 adapter->ipr.dwAddr & adapter->ipr.dwMask;
663 if (adapter->ipr.dwBCastAddr)
664 bcastAddr |= ~adapter->ipr.dwMask;
665 ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
666 TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
667 if (ret == NRC_GOODRET && newEntry)
669 ret = NetBTStoreCacheEntry(&adapter->nameCache,
671 if (ret != NRC_GOODRET)
678 *cacheEntry = newEntry;
681 TRACE("returning 0x%02x\n", ret);
685 typedef struct _NetBTNodeQueryData
688 PADAPTER_STATUS astat;
690 } NetBTNodeQueryData;
692 /* Callback function for NetBTAstatRemote, parses the rData for the node
693 * status and name list of the remote node. Always returns FALSE, since
694 * there's never more than one answer we care about in a node status response.
696 static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
697 WORD answerIndex, PUCHAR rData, WORD rLen)
699 NetBTNodeQueryData *data = pVoid;
701 if (data && !data->gotResponse && rData && rLen >= 1)
703 /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
704 if (rLen >= rData[0] * (NCBNAMSZ + 2))
710 data->gotResponse = TRUE;
711 data->astat->name_count = rData[0];
712 for (i = 0, src = rData + 1,
713 dst = (PNAME_BUFFER)((PUCHAR)data->astat +
714 sizeof(ADAPTER_STATUS));
715 i < data->astat->name_count && src - rData < rLen &&
716 (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
717 i++, dst++, src += NCBNAMSZ + 2)
719 UCHAR flags = *(src + NCBNAMSZ);
721 memcpy(dst->name, src, NCBNAMSZ);
722 /* we won't actually see a registering name in the returned
723 * response. It's useful to see if no other flags are set; if
724 * none are, then the name is registered. */
725 dst->name_flags = REGISTERING;
727 dst->name_flags |= GROUP_NAME;
729 dst->name_flags |= DEREGISTERED;
731 dst->name_flags |= DUPLICATE;
732 if (dst->name_flags == REGISTERING)
733 dst->name_flags = REGISTERED;
735 /* arbitrarily set HW type to Ethernet */
736 data->astat->adapter_type = 0xfe;
737 if (src - rData < rLen)
738 memcpy(data->astat->adapter_address, src,
739 min(rLen - (src - rData), 6));
745 /* This uses the WINS timeout and query values, as they're the
746 * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
748 static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
750 UCHAR ret = NRC_GOODRET;
751 const NBNameCacheEntry *cacheEntry = NULL;
753 TRACE("adapter %p, NCB %p\n", adapter, ncb);
755 if (!adapter) return NRC_BADDR;
756 if (!ncb) return NRC_INVADDRESS;
758 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
759 if (ret == NRC_GOODRET && cacheEntry)
761 if (cacheEntry->numAddresses > 0)
763 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
764 WSA_FLAG_OVERLAPPED);
766 if(fd == INVALID_SOCKET)
767 ret = NRC_OSRESNOTAV;
770 NetBTNodeQueryData queryData;
772 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
774 adapter->nameQueryXID++;
775 astat->name_count = 0;
776 queryData.gotResponse = FALSE;
777 queryData.astat = astat;
778 queryData.astatLen = ncb->ncb_length;
779 for (queries = 0; !queryData.gotResponse &&
780 queries < gWINSQueries; queries++)
782 if (!NCB_CANCELLED(ncb))
784 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
785 adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
786 cacheEntry->addresses[0], FALSE);
789 ret = NetBTWaitForNameResponse(adapter, fd,
790 GetTickCount() + gWINSQueryTimeout,
791 NetBTNodeStatusAnswerCallback, &queryData);
804 else if (ret == NRC_CMDCAN)
805 ; /* do nothing, we were cancelled */
808 TRACE("returning 0x%02x\n", ret);
812 static UCHAR NetBTAstat(void *adapt, PNCB ncb)
814 NetBTAdapter *adapter = adapt;
817 TRACE("adapt %p, NCB %p\n", adapt, ncb);
819 if (!adapter) return NRC_ENVNOTDEF;
820 if (!ncb) return NRC_INVADDRESS;
821 if (!ncb->ncb_buffer) return NRC_BADDR;
822 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
824 if (ncb->ncb_callname[0] == '*')
828 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
830 memset(astat, 0, sizeof(ADAPTER_STATUS));
831 astat->rev_major = 3;
832 ifRow.dwIndex = adapter->ipr.dwIndex;
833 if (GetIfEntry(&ifRow) != NO_ERROR)
837 physAddrLen = min(ifRow.dwPhysAddrLen, 6);
839 memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
840 /* doubt anyone cares, but why not.. */
841 if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
842 astat->adapter_type = 0xff;
844 astat->adapter_type = 0xfe; /* for Ethernet */
845 astat->max_sess_pkt_size = 0xffff;
846 astat->xmit_success = adapter->xmit_success;
847 astat->recv_success = adapter->recv_success;
852 ret = NetBTAstatRemote(adapter, ncb);
853 TRACE("returning 0x%02x\n", ret);
857 static UCHAR NetBTFindName(void *adapt, PNCB ncb)
859 NetBTAdapter *adapter = adapt;
861 const NBNameCacheEntry *cacheEntry = NULL;
862 PFIND_NAME_HEADER foundName;
864 TRACE("adapt %p, NCB %p\n", adapt, ncb);
866 if (!adapter) return NRC_ENVNOTDEF;
867 if (!ncb) return NRC_INVADDRESS;
868 if (!ncb->ncb_buffer) return NRC_BADDR;
869 if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
871 foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
872 memset(foundName, 0, sizeof(FIND_NAME_HEADER));
874 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
875 if (ret == NRC_GOODRET)
879 DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
880 sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
883 for (ndx = 0; ndx < spaceFor; ndx++)
885 PFIND_NAME_BUFFER findNameBuffer;
888 (PFIND_NAME_BUFFER)((PUCHAR)foundName +
889 sizeof(FIND_NAME_HEADER) + foundName->node_count *
890 sizeof(FIND_NAME_BUFFER));
891 memset(findNameBuffer->destination_addr, 0, 2);
892 memcpy(findNameBuffer->destination_addr + 2,
893 &adapter->ipr.dwAddr, sizeof(DWORD));
894 memset(findNameBuffer->source_addr, 0, 2);
895 memcpy(findNameBuffer->source_addr + 2,
896 &cacheEntry->addresses[ndx], sizeof(DWORD));
897 foundName->node_count++;
899 if (spaceFor < cacheEntry->numAddresses)
905 TRACE("returning 0x%02x\n", ret);
909 static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
910 const UCHAR *callingName)
912 UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
914 unsigned int len = 0;
915 DWORD bytesSent, bytesReceived, recvFlags = 0;
918 buffer[0] = NBSS_REQ;
921 len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
922 len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
924 NBR_ADDWORD(&buffer[2], len);
926 wsaBuf.len = len + NBSS_HDRSIZE;
927 wsaBuf.buf = (char*)buffer;
929 r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
930 if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
932 ERR("send failed\n");
936 /* I've already set the recv timeout on this socket (if it supports it), so
937 * just block. Hopefully we'll always receive the session acknowledgement
938 * within one timeout.
940 wsaBuf.len = NBSS_HDRSIZE + 1;
941 r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
942 if (r < 0 || bytesReceived < NBSS_HDRSIZE)
944 else if (buffer[0] == NBSS_NACK)
946 if (r == NBSS_HDRSIZE + 1)
948 switch (buffer[NBSS_HDRSIZE])
950 case NBSS_ERR_INSUFFICIENT_RESOURCES:
960 else if (buffer[0] == NBSS_RETARGET)
962 FIXME("Got a session retarget, can't deal\n");
965 else if (buffer[0] == NBSS_ACK)
970 TRACE("returning 0x%02x\n", ret);
974 static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
976 NetBTAdapter *adapter = adapt;
978 const NBNameCacheEntry *cacheEntry = NULL;
980 TRACE("adapt %p, ncb %p\n", adapt, ncb);
982 if (!adapter) return NRC_ENVNOTDEF;
983 if (!ncb) return NRC_INVADDRESS;
984 if (!sess) return NRC_BADDR;
986 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
987 if (ret == NRC_GOODRET)
989 if (cacheEntry && cacheEntry->numAddresses > 0)
993 fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
994 WSA_FLAG_OVERLAPPED);
995 if (fd != INVALID_SOCKET)
998 struct sockaddr_in sin;
1000 if (ncb->ncb_rto > 0)
1002 timeout = ncb->ncb_rto * 500;
1003 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
1006 if (ncb->ncb_rto > 0)
1008 timeout = ncb->ncb_sto * 500;
1009 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
1013 memset(&sin, 0, sizeof(sin));
1014 memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
1015 sizeof(sin.sin_addr));
1016 sin.sin_family = AF_INET;
1017 sin.sin_port = htons(PORT_NBSS);
1018 /* FIXME: use nonblocking mode for the socket, check the
1019 * cancel flag periodically
1021 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
1026 static const UCHAR fakedCalledName[] = "*SMBSERVER";
1027 const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
1028 ? fakedCalledName : cacheEntry->nbname;
1030 ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
1031 if (ret != NRC_GOODRET && calledParty[0] == '*')
1033 FIXME("NBT session to \"*SMBSERVER\" refused,\n");
1034 FIXME("should try finding name using ASTAT\n");
1037 if (ret != NRC_GOODRET)
1041 NetBTSession *session = HeapAlloc(
1042 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
1047 InitializeCriticalSection(&session->cs);
1048 session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBTSession.cs");
1053 ret = NRC_OSRESNOTAV;
1059 ret = NRC_OSRESNOTAV;
1064 TRACE("returning 0x%02x\n", ret);
1068 /* Notice that I don't protect against multiple thread access to NetBTSend.
1069 * This is because I don't update any data in the adapter, and I only make a
1070 * single call to WSASend, which I assume to act atomically (not interleaving
1071 * data from other threads).
1072 * I don't lock, because I only depend on the fd being valid, and this won't be
1073 * true until a session setup is completed.
1075 static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
1077 NetBTAdapter *adapter = adapt;
1078 NetBTSession *session = sess;
1079 UCHAR buffer[NBSS_HDRSIZE], ret;
1084 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1086 if (!adapter) return NRC_ENVNOTDEF;
1087 if (!ncb) return NRC_INVADDRESS;
1088 if (!ncb->ncb_buffer) return NRC_BADDR;
1089 if (!session) return NRC_SNUMOUT;
1090 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1092 buffer[0] = NBSS_MSG;
1094 NBR_ADDWORD(&buffer[2], ncb->ncb_length);
1096 wsaBufs[0].len = NBSS_HDRSIZE;
1097 wsaBufs[0].buf = (char*)buffer;
1098 wsaBufs[1].len = ncb->ncb_length;
1099 wsaBufs[1].buf = (char*)ncb->ncb_buffer;
1101 r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
1102 &bytesSent, 0, NULL, NULL);
1103 if (r == SOCKET_ERROR)
1105 NetBIOSHangupSession(ncb);
1108 else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
1110 FIXME("Only sent %d bytes (of %d), hanging up session\n", bytesSent,
1111 NBSS_HDRSIZE + ncb->ncb_length);
1112 NetBIOSHangupSession(ncb);
1118 adapter->xmit_success++;
1120 TRACE("returning 0x%02x\n", ret);
1124 static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
1126 NetBTAdapter *adapter = adapt;
1127 NetBTSession *session = sess;
1128 UCHAR buffer[NBSS_HDRSIZE], ret;
1131 DWORD bufferCount, bytesReceived, flags;
1133 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1135 if (!adapter) return NRC_ENVNOTDEF;
1136 if (!ncb) return NRC_BADDR;
1137 if (!ncb->ncb_buffer) return NRC_BADDR;
1138 if (!session) return NRC_SNUMOUT;
1139 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1141 EnterCriticalSection(&session->cs);
1143 if (session->bytesPending == 0)
1146 wsaBufs[0].len = NBSS_HDRSIZE;
1147 wsaBufs[0].buf = (char*)buffer;
1149 wsaBufs[bufferCount].len = ncb->ncb_length;
1150 wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
1154 /* FIXME: should poll a bit so I can check the cancel flag */
1155 r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
1157 if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
1159 LeaveCriticalSection(&session->cs);
1160 ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
1161 NetBIOSHangupSession(ncb);
1164 else if (NCB_CANCELLED(ncb))
1166 LeaveCriticalSection(&session->cs);
1171 if (bufferCount == 2)
1173 if (buffer[0] == NBSS_KEEPALIVE)
1175 LeaveCriticalSection(&session->cs);
1176 FIXME("Oops, received a session keepalive and lost my place\n");
1177 /* need to read another session header until we get a session
1178 * message header. */
1179 NetBIOSHangupSession(ncb);
1183 else if (buffer[0] != NBSS_MSG)
1185 LeaveCriticalSection(&session->cs);
1186 FIXME("Received unexpected session msg type %d\n", buffer[0]);
1187 NetBIOSHangupSession(ncb);
1193 if (buffer[1] & NBSS_EXTENSION)
1195 LeaveCriticalSection(&session->cs);
1196 FIXME("Received a message that's too long for my taste\n");
1197 NetBIOSHangupSession(ncb);
1203 session->bytesPending = NBSS_HDRSIZE
1204 + NBR_GETWORD(&buffer[2]) - bytesReceived;
1205 ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
1206 LeaveCriticalSection(&session->cs);
1212 if (bytesReceived < session->bytesPending)
1213 session->bytesPending -= bytesReceived;
1215 session->bytesPending = 0;
1216 LeaveCriticalSection(&session->cs);
1217 ncb->ncb_length = bytesReceived;
1219 if (session->bytesPending > 0)
1224 adapter->recv_success++;
1228 TRACE("returning 0x%02x\n", ret);
1232 static UCHAR NetBTHangup(void *adapt, void *sess)
1234 NetBTSession *session = sess;
1236 TRACE("adapt %p, session %p\n", adapt, session);
1238 if (!session) return NRC_SNUMOUT;
1240 /* I don't lock the session, because NetBTRecv knows not to decrement
1241 * past 0, so if a receive completes after this it should still deal.
1243 closesocket(session->fd);
1244 session->fd = INVALID_SOCKET;
1245 session->bytesPending = 0;
1246 session->cs.DebugInfo->Spare[0] = 0;
1247 DeleteCriticalSection(&session->cs);
1248 HeapFree(GetProcessHeap(), 0, session);
1253 static void NetBTCleanupAdapter(void *adapt)
1255 TRACE("adapt %p\n", adapt);
1258 NetBTAdapter *adapter = adapt;
1260 if (adapter->nameCache)
1261 NBNameCacheDestroy(adapter->nameCache);
1262 HeapFree(GetProcessHeap(), 0, adapt);
1266 static void NetBTCleanup(void)
1271 NBNameCacheDestroy(gNameCache);
1276 static UCHAR NetBTRegisterAdapter(const MIB_IPADDRROW *ipRow)
1279 NetBTAdapter *adapter;
1281 if (!ipRow) return NRC_BADDR;
1283 adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
1286 adapter->ipr = *ipRow;
1287 if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
1289 NetBTCleanupAdapter(adapter);
1296 ret = NRC_OSRESNOTAV;
1300 /* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to
1301 * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
1302 * NetBIOS adapter table. For each callback, checks if the passed-in adapt
1303 * has an entry in the table; if so, this adapter was enumerated previously,
1304 * and it's enabled. As a flag, the table's dwAddr entry is changed to
1305 * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
1306 * The NetBTEnum function will add any remaining adapters from the
1307 * MIB_IPADDRTABLE to the NetBIOS adapter table.
1309 static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
1310 ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
1313 PMIB_IPADDRTABLE table = closure;
1320 for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
1322 const NetBTAdapter *adapter = data->data;
1324 if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
1326 NetBIOSEnableAdapter(data->lana);
1327 table->table[ndx].dwAddr = INADDR_LOOPBACK;
1337 /* Enumerates adapters by:
1338 * - retrieving the IP address table for the local machine
1339 * - eliminating loopback addresses from the table
1340 * - eliminating redundant addresses, that is, multiple addresses on the same
1342 * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
1343 * data. The callback reenables each adapter that's already in the NetBIOS
1344 * table. After NetBIOSEnumAdapters returns, this function adds any remaining
1345 * adapters to the NetBIOS table.
1347 static UCHAR NetBTEnum(void)
1354 if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
1356 PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
1357 DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
1358 sizeof(MIB_IPADDRROW) + 1;
1360 ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
1362 coalesceTable = HeapAlloc(GetProcessHeap(),
1363 HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
1364 (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
1365 if (ipAddrs && coalesceTable)
1367 if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
1371 for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
1373 if ((ipAddrs->table[ndx].dwAddr &
1374 ipAddrs->table[ndx].dwMask) !=
1375 htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
1377 BOOL newNetwork = TRUE;
1380 /* make sure we don't have more than one entry
1382 for (innerIndex = 0; newNetwork &&
1383 innerIndex < coalesceTable->dwNumEntries; innerIndex++)
1384 if ((ipAddrs->table[ndx].dwAddr &
1385 ipAddrs->table[ndx].dwMask) ==
1386 (coalesceTable->table[innerIndex].dwAddr
1387 & coalesceTable->table[innerIndex].dwMask))
1391 memcpy(&coalesceTable->table[
1392 coalesceTable->dwNumEntries++],
1393 &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
1397 NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
1400 for (ndx = 0; ret == NRC_GOODRET &&
1401 ndx < coalesceTable->dwNumEntries; ndx++)
1402 if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
1403 ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
1407 HeapFree(GetProcessHeap(), 0, ipAddrs);
1408 HeapFree(GetProcessHeap(), 0, coalesceTable);
1411 ret = NRC_OSRESNOTAV;
1415 TRACE("returning 0x%02x\n", ret);
1419 static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r',
1420 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
1421 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
1422 static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u',
1423 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
1424 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
1426 static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' };
1427 static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m',
1428 'e','Q','u','e','r','y','C','o','u','n','t','\0' };
1429 static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m',
1430 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1431 static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v',
1432 'Q','u','e','r','y','C','o','u','n','t','\0' };
1433 static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v',
1434 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1435 static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' };
1436 static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o',
1438 static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\',
1439 'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
1441 /* Initializes global variables and registers the NetBT transport */
1442 void NetBTInit(void)
1445 NetBIOSTransport transport;
1451 gBCastQueries = BCAST_QUERIES;
1452 gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
1453 gWINSQueries = WINS_QUERIES;
1454 gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
1455 gNumWINSServers = 0;
1456 memset(gWINSServers, 0, sizeof(gWINSServers));
1458 gCacheTimeout = CACHE_TIMEOUT;
1460 /* Try to open the Win9x NetBT configuration key */
1461 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey);
1462 /* If that fails, try the WinNT NetBT configuration key */
1463 if (ret != ERROR_SUCCESS)
1464 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ,
1466 if (ret == ERROR_SUCCESS)
1470 size = sizeof(dword);
1471 if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL,
1472 (LPBYTE)&dword, &size) == ERROR_SUCCESS)
1474 size = sizeof(dword);
1475 if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL,
1476 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1477 && dword <= MAX_QUERIES)
1478 gBCastQueries = dword;
1479 size = sizeof(dword);
1480 if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL,
1481 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
1482 gBCastQueryTimeout = dword;
1483 size = sizeof(dword);
1484 if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL,
1485 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1486 && dword <= MAX_QUERIES)
1487 gWINSQueries = dword;
1488 size = sizeof(dword);
1489 if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL,
1490 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
1491 gWINSQueryTimeout = dword;
1492 size = sizeof(gScopeID) - 1;
1493 if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size)
1496 /* convert into L2-encoded version, suitable for use by
1500 for (ptr = gScopeID + 1; ptr - gScopeID < sizeof(gScopeID) && *ptr; )
1502 for (lenPtr = ptr - 1, *lenPtr = 0;
1503 ptr - gScopeID < sizeof(gScopeID) && *ptr && *ptr != '.';
1509 if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL,
1510 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
1511 gCacheTimeout = dword;
1514 /* WINE-specific NetBT registry settings. Because our adapter naming is
1515 * different than MS', we can't do per-adapter WINS configuration in the
1516 * same place. Just do a global WINS configuration instead.
1518 /* @@ Wine registry key: HKCU\Software\Wine\Network */
1519 if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS)
1521 static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
1525 for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
1528 size = sizeof(nsString) / sizeof(char);
1529 if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
1530 (LPBYTE)nsString, &size) == ERROR_SUCCESS)
1532 unsigned long addr = inet_addr(nsString);
1534 if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
1535 gWINSServers[gNumWINSServers++] = addr;
1541 transport.enumerate = NetBTEnum;
1542 transport.astat = NetBTAstat;
1543 transport.findName = NetBTFindName;
1544 transport.call = NetBTCall;
1545 transport.send = NetBTSend;
1546 transport.recv = NetBTRecv;
1547 transport.hangup = NetBTHangup;
1548 transport.cleanupAdapter = NetBTCleanupAdapter;
1549 transport.cleanup = NetBTCleanup;
1550 memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
1551 NetBIOSRegisterTransport(gTransportID, &transport);