wbemprox: Add a partial implementation of Win32_NetworkAdapterConfiguration.
[wine.git] / dlls / netapi32 / nbt.c
blob2a871de7ba21dbc86589535421f46a13f0336368
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
20 * that code remains.
21 * Lack of understanding and bugs are my fault.
23 * FIXME:
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
40 * UDP/137.
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
57 * well.
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.
68 #include "config.h"
69 #include <stdarg.h>
71 #include "winsock2.h"
72 #include "windef.h"
73 #include "winbase.h"
74 #include "wine/debug.h"
75 #include "winreg.h"
76 #include "iphlpapi.h"
78 #include "netbios.h"
79 #include "nbnamecache.h"
81 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
83 #define PORT_NBNS 137
84 #define PORT_NBDG 138
85 #define PORT_NBSS 139
87 #ifndef INADDR_NONE
88 #define INADDR_NONE ~0UL
89 #endif
91 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
92 #define NBR_GETWORD(p) ntohs(*(WORD *)(p))
94 #define MIN_QUERIES 1
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
135 CRITICAL_SECTION cs;
136 SOCKET fd;
137 DWORD bytesPending;
138 } NetBTSession;
140 typedef struct _NetBTAdapter
142 MIB_IPADDRROW ipr;
143 WORD nameQueryXID;
144 struct NBNameCache *nameCache;
145 DWORD xmit_success;
146 DWORD recv_success;
147 } NetBTAdapter;
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)
168 int i,len=0;
170 if (!p) return 0;
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)
181 buffer[len++] = 'C';
182 buffer[len++] = 'A';
184 if (*gScopeID)
186 int scopeIDLen = strlen(gScopeID);
188 memcpy(buffer + len, gScopeID, scopeIDLen);
189 len += scopeIDLen;
191 buffer[len++] = 0; /* add second terminator */
192 return len;
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)
202 int i = 0;
204 if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
206 NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */
207 if (broadcast)
209 NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
210 i+=2;
212 else
214 NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
215 i+=2;
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;
227 return i;
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
232 * NULL.
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)
238 int ret = 0, on = 1;
239 struct in_addr addr;
241 addr.s_addr = destAddr;
242 TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
244 if (broadcast)
245 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
246 if(ret == 0)
248 WSABUF wsaBuf;
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,
259 sizeof(buf));
260 if (wsaBuf.len > 0)
262 DWORD bytesSent;
264 ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
265 (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
266 if (ret < 0 || bytesSent < wsaBuf.len)
267 ret = -1;
268 else
269 ret = 0;
271 else
272 ret = -1;
274 return ret;
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
287 * on error.
289 static UCHAR NetBTWaitForNameResponse(const NetBTAdapter *adapter, SOCKET fd,
290 DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
292 BOOL found = FALSE;
293 DWORD now;
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 && (int)((now = GetTickCount()) - waitUntil) < 0)
302 DWORD msToWait = waitUntil - now;
303 struct fd_set fds;
304 struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
305 int r;
307 FD_ZERO(&fds);
308 FD_SET(fd, &fds);
309 r = select(fd + 1, &fds, NULL, NULL, &timeout);
310 if (r < 0)
311 ret = NRC_SYSTEM;
312 else if (r == 1)
314 /* FIXME: magic #, is this always enough? */
315 UCHAR buffer[256];
316 int fromsize;
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);
325 if(r < 0)
327 ret = NRC_SYSTEM;
328 break;
331 if (bytesReceived < NBNS_HEADER_SIZE)
332 continue;
334 respXID = NBR_GETWORD(buffer);
335 if (adapter->nameQueryXID != respXID)
336 continue;
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 */
343 if (queryCount > 0)
344 continue;
346 if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
348 if ((flags & NBNS_REPLYCODE) != 0)
349 ret = NRC_NAMERR;
350 else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
352 PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
353 BOOL shouldContinue = TRUE;
354 WORD answerIndex = 0;
356 found = TRUE;
357 /* decode one answer at a time */
358 while (ret == NRC_GOODRET && answerIndex < answerCount &&
359 ptr - buffer < bytesReceived && shouldContinue)
361 WORD rLen;
363 /* scan past name */
364 for (; ptr[0] && ptr - buffer < bytesReceived; )
365 ptr += ptr[0] + 1;
366 ptr++;
367 ptr += 2; /* scan past type */
368 if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
369 && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
370 ptr += sizeof(WORD);
371 else
372 ret = NRC_SYSTEM; /* parse error */
373 ptr += sizeof(DWORD); /* TTL */
374 rLen = NBR_GETWORD(ptr);
375 rLen = min(rLen, bytesReceived - (ptr - buffer));
376 ptr += sizeof(WORD);
377 shouldContinue = answerCallback(data, answerCount,
378 answerIndex, ptr, rLen);
379 ptr += rLen;
380 answerIndex++;
386 TRACE("returning 0x%02x\n", ret);
387 return ret;
390 typedef struct _NetBTNameQueryData {
391 NBNameCacheEntry *cacheEntry;
392 UCHAR ret;
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;
404 BOOL ret;
406 if (queryData)
408 if (queryData->cacheEntry == NULL)
410 queryData->cacheEntry = HeapAlloc(GetProcessHeap(), 0,
411 FIELD_OFFSET(NBNameCacheEntry, addresses[answerCount]));
412 if (queryData->cacheEntry)
413 queryData->cacheEntry->numAddresses = 0;
414 else
415 queryData->ret = NRC_OSRESNOTAV;
417 if (rLen == 6 && queryData->cacheEntry &&
418 queryData->cacheEntry->numAddresses < answerCount)
420 queryData->cacheEntry->addresses[queryData->cacheEntry->
421 numAddresses++] = *(const DWORD *)(rData + 2);
422 ret = queryData->cacheEntry->numAddresses < answerCount;
424 else
425 ret = FALSE;
427 else
428 ret = FALSE;
429 return ret;
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(const NetBTAdapter *adapter, SOCKET fd, const NCB *ncb,
442 DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
443 NBNameCacheEntry **cacheEntry)
445 unsigned int queries;
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;
456 queries++)
458 if (!NCB_CANCELLED(ncb))
460 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
461 adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
463 if (r == 0)
464 queryData.ret = NetBTWaitForNameResponse(adapter, fd,
465 GetTickCount() + timeout, NetBTFindNameAnswerCallback,
466 &queryData);
467 else
468 queryData.ret = NRC_SYSTEM;
470 else
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,
485 * frees cacheEntry.
486 * Returns NRC_GOODRET on success, and something else on failure.
488 static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
489 NBNameCacheEntry *cacheEntry)
491 UCHAR ret;
493 if (!nameCache) return NRC_BADDR;
494 if (!cacheEntry) return NRC_BADDR;
496 if (!*nameCache)
497 *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
498 if (*nameCache)
499 ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
500 ? NRC_GOODRET : NRC_OSRESNOTAV;
501 else
503 HeapFree(GetProcessHeap(), 0, cacheEntry);
504 ret = NRC_OSRESNOTAV;
506 return ret;
509 /* Attempts to resolve name using inet_addr(), then gethostbyname() if
510 * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name
511 * can be looked up, returns 0 and stores the looked up addresses as a
512 * NBNameCacheEntry in *cacheEntry.
513 * Returns NRC_GOODRET on success, though this may not mean the name was
514 * resolved--check whether *cacheEntry is NULL. Returns something else on
515 * error.
517 static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
518 NBNameCacheEntry **cacheEntry)
520 UCHAR ret = NRC_GOODRET;
522 TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
524 if (!name) return NRC_BADDR;
525 if (!cacheEntry) return NRC_BADDR;
527 if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
528 name[NCBNAMSZ - 1] == 0x20))
530 CHAR toLookup[NCBNAMSZ];
531 unsigned int i;
533 for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
534 toLookup[i] = name[i];
535 toLookup[i] = '\0';
537 if (isdigit(toLookup[0]))
539 unsigned long addr = inet_addr(toLookup);
541 if (addr != INADDR_NONE)
543 *cacheEntry = HeapAlloc(GetProcessHeap(), 0,
544 FIELD_OFFSET(NBNameCacheEntry, addresses[1]));
545 if (*cacheEntry)
547 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
548 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
549 (*cacheEntry)->nbname[0] = '*';
550 (*cacheEntry)->numAddresses = 1;
551 (*cacheEntry)->addresses[0] = addr;
553 else
554 ret = NRC_OSRESNOTAV;
557 if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
559 struct hostent *host;
561 if ((host = gethostbyname(toLookup)) != NULL)
563 for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
564 host->h_addr_list[i]; i++)
566 if (host->h_addr_list && host->h_addr_list[0])
568 *cacheEntry = HeapAlloc(GetProcessHeap(), 0,
569 FIELD_OFFSET(NBNameCacheEntry, addresses[i]));
570 if (*cacheEntry)
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];
580 else
581 ret = NRC_OSRESNOTAV;
587 TRACE("returning 0x%02x\n", ret);
588 return 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;
609 *cacheEntry = NULL;
611 if (!adapter) return NRC_BADDR;
612 if (!ncb) return NRC_BADDR;
614 if (ncb->ncb_callname[0] == '*')
615 ret = NRC_NOWILD;
616 else
618 *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
619 if (!*cacheEntry)
620 *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
621 ncb->ncb_callname);
622 if (!*cacheEntry)
624 NBNameCacheEntry *newEntry = NULL;
626 ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
627 if (ret == NRC_GOODRET && newEntry)
629 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
630 if (ret != NRC_GOODRET)
631 newEntry = NULL;
633 else
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;
640 else
642 int winsNdx;
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)
654 newEntry = NULL;
656 if (ret == NRC_GOODRET && *cacheEntry == NULL)
658 DWORD bcastAddr =
659 adapter->ipr.dwAddr & adapter->ipr.dwMask;
661 if (adapter->ipr.dwBCastAddr)
662 bcastAddr |= ~adapter->ipr.dwMask;
663 ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
664 TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
665 if (ret == NRC_GOODRET && newEntry)
667 ret = NetBTStoreCacheEntry(&adapter->nameCache,
668 newEntry);
669 if (ret != NRC_GOODRET)
670 newEntry = NULL;
673 closesocket(fd);
676 *cacheEntry = newEntry;
679 TRACE("returning 0x%02x\n", ret);
680 return ret;
683 typedef struct _NetBTNodeQueryData
685 BOOL gotResponse;
686 PADAPTER_STATUS astat;
687 WORD astatLen;
688 } NetBTNodeQueryData;
690 /* Callback function for NetBTAstatRemote, parses the rData for the node
691 * status and name list of the remote node. Always returns FALSE, since
692 * there's never more than one answer we care about in a node status response.
694 static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
695 WORD answerIndex, PUCHAR rData, WORD rLen)
697 NetBTNodeQueryData *data = pVoid;
699 if (data && !data->gotResponse && rData && rLen >= 1)
701 /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
702 if (rLen >= rData[0] * (NCBNAMSZ + 2))
704 WORD i;
705 PUCHAR src;
706 PNAME_BUFFER dst;
708 data->gotResponse = TRUE;
709 data->astat->name_count = rData[0];
710 for (i = 0, src = rData + 1,
711 dst = (PNAME_BUFFER)((PUCHAR)data->astat +
712 sizeof(ADAPTER_STATUS));
713 i < data->astat->name_count && src - rData < rLen &&
714 (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
715 i++, dst++, src += NCBNAMSZ + 2)
717 UCHAR flags = *(src + NCBNAMSZ);
719 memcpy(dst->name, src, NCBNAMSZ);
720 /* we won't actually see a registering name in the returned
721 * response. It's useful to see if no other flags are set; if
722 * none are, then the name is registered. */
723 dst->name_flags = REGISTERING;
724 if (flags & 0x80)
725 dst->name_flags |= GROUP_NAME;
726 if (flags & 0x10)
727 dst->name_flags |= DEREGISTERED;
728 if (flags & 0x08)
729 dst->name_flags |= DUPLICATE;
730 if (dst->name_flags == REGISTERING)
731 dst->name_flags = REGISTERED;
733 /* arbitrarily set HW type to Ethernet */
734 data->astat->adapter_type = 0xfe;
735 if (src - rData < rLen)
736 memcpy(data->astat->adapter_address, src,
737 min(rLen - (src - rData), 6));
740 return FALSE;
743 /* This uses the WINS timeout and query values, as they're the
744 * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
746 static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
748 UCHAR ret = NRC_GOODRET;
749 const NBNameCacheEntry *cacheEntry = NULL;
751 TRACE("adapter %p, NCB %p\n", adapter, ncb);
753 if (!adapter) return NRC_BADDR;
754 if (!ncb) return NRC_INVADDRESS;
756 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
757 if (ret == NRC_GOODRET && cacheEntry)
759 if (cacheEntry->numAddresses > 0)
761 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
762 WSA_FLAG_OVERLAPPED);
764 if(fd == INVALID_SOCKET)
765 ret = NRC_OSRESNOTAV;
766 else
768 NetBTNodeQueryData queryData;
769 DWORD queries;
770 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
772 adapter->nameQueryXID++;
773 astat->name_count = 0;
774 queryData.gotResponse = FALSE;
775 queryData.astat = astat;
776 queryData.astatLen = ncb->ncb_length;
777 for (queries = 0; !queryData.gotResponse &&
778 queries < gWINSQueries; queries++)
780 if (!NCB_CANCELLED(ncb))
782 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
783 adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
784 cacheEntry->addresses[0], FALSE);
786 if (r == 0)
787 ret = NetBTWaitForNameResponse(adapter, fd,
788 GetTickCount() + gWINSQueryTimeout,
789 NetBTNodeStatusAnswerCallback, &queryData);
790 else
791 ret = NRC_SYSTEM;
793 else
794 ret = NRC_CMDCAN;
796 closesocket(fd);
799 else
800 ret = NRC_CMDTMO;
802 else if (ret == NRC_CMDCAN)
803 ; /* do nothing, we were cancelled */
804 else
805 ret = NRC_CMDTMO;
806 TRACE("returning 0x%02x\n", ret);
807 return ret;
810 static UCHAR NetBTAstat(void *adapt, PNCB ncb)
812 NetBTAdapter *adapter = adapt;
813 UCHAR ret;
815 TRACE("adapt %p, NCB %p\n", adapt, ncb);
817 if (!adapter) return NRC_ENVNOTDEF;
818 if (!ncb) return NRC_INVADDRESS;
819 if (!ncb->ncb_buffer) return NRC_BADDR;
820 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
822 if (ncb->ncb_callname[0] == '*')
824 DWORD physAddrLen;
825 MIB_IFROW ifRow;
826 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
828 memset(astat, 0, sizeof(ADAPTER_STATUS));
829 astat->rev_major = 3;
830 ifRow.dwIndex = adapter->ipr.dwIndex;
831 if (GetIfEntry(&ifRow) != NO_ERROR)
832 ret = NRC_BRIDGE;
833 else
835 physAddrLen = min(ifRow.dwPhysAddrLen, 6);
836 if (physAddrLen > 0)
837 memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
838 /* doubt anyone cares, but why not.. */
839 if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
840 astat->adapter_type = 0xff;
841 else
842 astat->adapter_type = 0xfe; /* for Ethernet */
843 astat->max_sess_pkt_size = 0xffff;
844 astat->xmit_success = adapter->xmit_success;
845 astat->recv_success = adapter->recv_success;
846 ret = NRC_GOODRET;
849 else
850 ret = NetBTAstatRemote(adapter, ncb);
851 TRACE("returning 0x%02x\n", ret);
852 return ret;
855 static UCHAR NetBTFindName(void *adapt, PNCB ncb)
857 NetBTAdapter *adapter = adapt;
858 UCHAR ret;
859 const NBNameCacheEntry *cacheEntry = NULL;
860 PFIND_NAME_HEADER foundName;
862 TRACE("adapt %p, NCB %p\n", adapt, ncb);
864 if (!adapter) return NRC_ENVNOTDEF;
865 if (!ncb) return NRC_INVADDRESS;
866 if (!ncb->ncb_buffer) return NRC_BADDR;
867 if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
869 foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
870 memset(foundName, 0, sizeof(FIND_NAME_HEADER));
872 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
873 if (ret == NRC_GOODRET)
875 if (cacheEntry)
877 DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
878 sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
879 DWORD ndx;
881 for (ndx = 0; ndx < spaceFor; ndx++)
883 PFIND_NAME_BUFFER findNameBuffer;
885 findNameBuffer =
886 (PFIND_NAME_BUFFER)((PUCHAR)foundName +
887 sizeof(FIND_NAME_HEADER) + foundName->node_count *
888 sizeof(FIND_NAME_BUFFER));
889 memset(findNameBuffer->destination_addr, 0, 2);
890 memcpy(findNameBuffer->destination_addr + 2,
891 &adapter->ipr.dwAddr, sizeof(DWORD));
892 memset(findNameBuffer->source_addr, 0, 2);
893 memcpy(findNameBuffer->source_addr + 2,
894 &cacheEntry->addresses[ndx], sizeof(DWORD));
895 foundName->node_count++;
897 if (spaceFor < cacheEntry->numAddresses)
898 ret = NRC_BUFLEN;
900 else
901 ret = NRC_CMDTMO;
903 TRACE("returning 0x%02x\n", ret);
904 return ret;
907 static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
908 const UCHAR *callingName)
910 UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
911 int r;
912 unsigned int len = 0;
913 DWORD bytesSent, bytesReceived, recvFlags = 0;
914 WSABUF wsaBuf;
916 buffer[0] = NBSS_REQ;
917 buffer[1] = 0;
919 len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
920 len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
922 NBR_ADDWORD(&buffer[2], len);
924 wsaBuf.len = len + NBSS_HDRSIZE;
925 wsaBuf.buf = (char*)buffer;
927 r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
928 if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
930 ERR("send failed\n");
931 return NRC_SABORT;
934 /* I've already set the recv timeout on this socket (if it supports it), so
935 * just block. Hopefully we'll always receive the session acknowledgement
936 * within one timeout.
938 wsaBuf.len = NBSS_HDRSIZE + 1;
939 r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
940 if (r < 0 || bytesReceived < NBSS_HDRSIZE)
941 ret = NRC_SABORT;
942 else if (buffer[0] == NBSS_NACK)
944 if (r == NBSS_HDRSIZE + 1)
946 switch (buffer[NBSS_HDRSIZE])
948 case NBSS_ERR_INSUFFICIENT_RESOURCES:
949 ret = NRC_REMTFUL;
950 break;
951 default:
952 ret = NRC_NOCALL;
955 else
956 ret = NRC_NOCALL;
958 else if (buffer[0] == NBSS_RETARGET)
960 FIXME("Got a session retarget, can't deal\n");
961 ret = NRC_NOCALL;
963 else if (buffer[0] == NBSS_ACK)
964 ret = NRC_GOODRET;
965 else
966 ret = NRC_SYSTEM;
968 TRACE("returning 0x%02x\n", ret);
969 return ret;
972 static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
974 NetBTAdapter *adapter = adapt;
975 UCHAR ret;
976 const NBNameCacheEntry *cacheEntry = NULL;
978 TRACE("adapt %p, ncb %p\n", adapt, ncb);
980 if (!adapter) return NRC_ENVNOTDEF;
981 if (!ncb) return NRC_INVADDRESS;
982 if (!sess) return NRC_BADDR;
984 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
985 if (ret == NRC_GOODRET)
987 if (cacheEntry && cacheEntry->numAddresses > 0)
989 SOCKET fd;
991 fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
992 WSA_FLAG_OVERLAPPED);
993 if (fd != INVALID_SOCKET)
995 DWORD timeout;
996 struct sockaddr_in sin;
998 if (ncb->ncb_rto > 0)
1000 timeout = ncb->ncb_rto * 500;
1001 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
1002 sizeof(timeout));
1004 if (ncb->ncb_sto > 0)
1006 timeout = ncb->ncb_sto * 500;
1007 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
1008 sizeof(timeout));
1011 memset(&sin, 0, sizeof(sin));
1012 memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
1013 sizeof(sin.sin_addr));
1014 sin.sin_family = AF_INET;
1015 sin.sin_port = htons(PORT_NBSS);
1016 /* FIXME: use nonblocking mode for the socket, check the
1017 * cancel flag periodically
1019 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
1020 == SOCKET_ERROR)
1021 ret = NRC_CMDTMO;
1022 else
1024 static const UCHAR fakedCalledName[] = "*SMBSERVER";
1025 const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
1026 ? fakedCalledName : cacheEntry->nbname;
1028 ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
1029 if (ret != NRC_GOODRET && calledParty[0] == '*')
1031 FIXME("NBT session to \"*SMBSERVER\" refused,\n");
1032 FIXME("should try finding name using ASTAT\n");
1035 if (ret != NRC_GOODRET)
1036 closesocket(fd);
1037 else
1039 NetBTSession *session = HeapAlloc(
1040 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
1042 if (session)
1044 session->fd = fd;
1045 InitializeCriticalSection(&session->cs);
1046 session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBTSession.cs");
1047 *sess = session;
1049 else
1051 ret = NRC_OSRESNOTAV;
1052 closesocket(fd);
1056 else
1057 ret = NRC_OSRESNOTAV;
1059 else
1060 ret = NRC_NAMERR;
1062 TRACE("returning 0x%02x\n", ret);
1063 return ret;
1066 /* Notice that I don't protect against multiple thread access to NetBTSend.
1067 * This is because I don't update any data in the adapter, and I only make a
1068 * single call to WSASend, which I assume to act atomically (not interleaving
1069 * data from other threads).
1070 * I don't lock, because I only depend on the fd being valid, and this won't be
1071 * true until a session setup is completed.
1073 static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
1075 NetBTAdapter *adapter = adapt;
1076 NetBTSession *session = sess;
1077 UCHAR buffer[NBSS_HDRSIZE], ret;
1078 int r;
1079 WSABUF wsaBufs[2];
1080 DWORD bytesSent;
1082 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1084 if (!adapter) return NRC_ENVNOTDEF;
1085 if (!ncb) return NRC_INVADDRESS;
1086 if (!ncb->ncb_buffer) return NRC_BADDR;
1087 if (!session) return NRC_SNUMOUT;
1088 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1090 buffer[0] = NBSS_MSG;
1091 buffer[1] = 0;
1092 NBR_ADDWORD(&buffer[2], ncb->ncb_length);
1094 wsaBufs[0].len = NBSS_HDRSIZE;
1095 wsaBufs[0].buf = (char*)buffer;
1096 wsaBufs[1].len = ncb->ncb_length;
1097 wsaBufs[1].buf = (char*)ncb->ncb_buffer;
1099 r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
1100 &bytesSent, 0, NULL, NULL);
1101 if (r == SOCKET_ERROR)
1103 NetBIOSHangupSession(ncb);
1104 ret = NRC_SABORT;
1106 else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
1108 FIXME("Only sent %d bytes (of %d), hanging up session\n", bytesSent,
1109 NBSS_HDRSIZE + ncb->ncb_length);
1110 NetBIOSHangupSession(ncb);
1111 ret = NRC_SABORT;
1113 else
1115 ret = NRC_GOODRET;
1116 adapter->xmit_success++;
1118 TRACE("returning 0x%02x\n", ret);
1119 return ret;
1122 static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
1124 NetBTAdapter *adapter = adapt;
1125 NetBTSession *session = sess;
1126 UCHAR buffer[NBSS_HDRSIZE], ret;
1127 int r;
1128 WSABUF wsaBufs[2];
1129 DWORD bufferCount, bytesReceived, flags;
1131 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1133 if (!adapter) return NRC_ENVNOTDEF;
1134 if (!ncb) return NRC_BADDR;
1135 if (!ncb->ncb_buffer) return NRC_BADDR;
1136 if (!session) return NRC_SNUMOUT;
1137 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1139 EnterCriticalSection(&session->cs);
1140 bufferCount = 0;
1141 if (session->bytesPending == 0)
1143 bufferCount++;
1144 wsaBufs[0].len = NBSS_HDRSIZE;
1145 wsaBufs[0].buf = (char*)buffer;
1147 wsaBufs[bufferCount].len = ncb->ncb_length;
1148 wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
1149 bufferCount++;
1151 flags = 0;
1152 /* FIXME: should poll a bit so I can check the cancel flag */
1153 r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
1154 NULL, NULL);
1155 if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
1157 LeaveCriticalSection(&session->cs);
1158 ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
1159 NetBIOSHangupSession(ncb);
1160 ret = NRC_SABORT;
1162 else if (NCB_CANCELLED(ncb))
1164 LeaveCriticalSection(&session->cs);
1165 ret = NRC_CMDCAN;
1167 else
1169 if (bufferCount == 2)
1171 if (buffer[0] == NBSS_KEEPALIVE)
1173 LeaveCriticalSection(&session->cs);
1174 FIXME("Oops, received a session keepalive and lost my place\n");
1175 /* need to read another session header until we get a session
1176 * message header. */
1177 NetBIOSHangupSession(ncb);
1178 ret = NRC_SABORT;
1179 goto error;
1181 else if (buffer[0] != NBSS_MSG)
1183 LeaveCriticalSection(&session->cs);
1184 FIXME("Received unexpected session msg type %d\n", buffer[0]);
1185 NetBIOSHangupSession(ncb);
1186 ret = NRC_SABORT;
1187 goto error;
1189 else
1191 if (buffer[1] & NBSS_EXTENSION)
1193 LeaveCriticalSection(&session->cs);
1194 FIXME("Received a message that's too long for my taste\n");
1195 NetBIOSHangupSession(ncb);
1196 ret = NRC_SABORT;
1197 goto error;
1199 else
1201 session->bytesPending = NBSS_HDRSIZE
1202 + NBR_GETWORD(&buffer[2]) - bytesReceived;
1203 ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
1204 LeaveCriticalSection(&session->cs);
1208 else
1210 if (bytesReceived < session->bytesPending)
1211 session->bytesPending -= bytesReceived;
1212 else
1213 session->bytesPending = 0;
1214 LeaveCriticalSection(&session->cs);
1215 ncb->ncb_length = bytesReceived;
1217 if (session->bytesPending > 0)
1218 ret = NRC_INCOMP;
1219 else
1221 ret = NRC_GOODRET;
1222 adapter->recv_success++;
1225 error:
1226 TRACE("returning 0x%02x\n", ret);
1227 return ret;
1230 static UCHAR NetBTHangup(void *adapt, void *sess)
1232 NetBTSession *session = sess;
1234 TRACE("adapt %p, session %p\n", adapt, session);
1236 if (!session) return NRC_SNUMOUT;
1238 /* I don't lock the session, because NetBTRecv knows not to decrement
1239 * past 0, so if a receive completes after this it should still deal.
1241 closesocket(session->fd);
1242 session->fd = INVALID_SOCKET;
1243 session->bytesPending = 0;
1244 session->cs.DebugInfo->Spare[0] = 0;
1245 DeleteCriticalSection(&session->cs);
1246 HeapFree(GetProcessHeap(), 0, session);
1248 return NRC_GOODRET;
1251 static void NetBTCleanupAdapter(void *adapt)
1253 TRACE("adapt %p\n", adapt);
1254 if (adapt)
1256 NetBTAdapter *adapter = adapt;
1258 if (adapter->nameCache)
1259 NBNameCacheDestroy(adapter->nameCache);
1260 HeapFree(GetProcessHeap(), 0, adapt);
1264 static void NetBTCleanup(void)
1266 TRACE("\n");
1267 if (gNameCache)
1269 NBNameCacheDestroy(gNameCache);
1270 gNameCache = NULL;
1274 static UCHAR NetBTRegisterAdapter(const MIB_IPADDRROW *ipRow)
1276 UCHAR ret;
1277 NetBTAdapter *adapter;
1279 if (!ipRow) return NRC_BADDR;
1281 adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
1282 if (adapter)
1284 adapter->ipr = *ipRow;
1285 if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
1287 NetBTCleanupAdapter(adapter);
1288 ret = NRC_SYSTEM;
1290 else
1291 ret = NRC_GOODRET;
1293 else
1294 ret = NRC_OSRESNOTAV;
1295 return ret;
1298 /* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to
1299 * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
1300 * NetBIOS adapter table. For each callback, checks if the passed-in adapt
1301 * has an entry in the table; if so, this adapter was enumerated previously,
1302 * and it's enabled. As a flag, the table's dwAddr entry is changed to
1303 * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
1304 * The NetBTEnum function will add any remaining adapters from the
1305 * MIB_IPADDRTABLE to the NetBIOS adapter table.
1307 static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
1308 ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
1310 BOOL ret;
1311 PMIB_IPADDRTABLE table = closure;
1313 if (table && data)
1315 DWORD ndx;
1317 ret = FALSE;
1318 for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
1320 const NetBTAdapter *adapter = data->data;
1322 if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
1324 NetBIOSEnableAdapter(data->lana);
1325 table->table[ndx].dwAddr = INADDR_LOOPBACK;
1326 ret = TRUE;
1330 else
1331 ret = FALSE;
1332 return ret;
1335 /* Enumerates adapters by:
1336 * - retrieving the IP address table for the local machine
1337 * - eliminating loopback addresses from the table
1338 * - eliminating redundant addresses, that is, multiple addresses on the same
1339 * subnet
1340 * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
1341 * data. The callback reenables each adapter that's already in the NetBIOS
1342 * table. After NetBIOSEnumAdapters returns, this function adds any remaining
1343 * adapters to the NetBIOS table.
1345 static UCHAR NetBTEnum(void)
1347 UCHAR ret;
1348 DWORD size = 0;
1350 TRACE("\n");
1352 if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
1354 PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
1355 DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
1356 sizeof(MIB_IPADDRROW) + 1;
1358 ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
1359 if (ipAddrs)
1360 coalesceTable = HeapAlloc(GetProcessHeap(),
1361 HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
1362 (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
1363 if (ipAddrs && coalesceTable)
1365 if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
1367 DWORD ndx;
1369 for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
1371 if ((ipAddrs->table[ndx].dwAddr &
1372 ipAddrs->table[ndx].dwMask) !=
1373 htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
1375 BOOL newNetwork = TRUE;
1376 DWORD innerIndex;
1378 /* make sure we don't have more than one entry
1379 * for a subnet */
1380 for (innerIndex = 0; newNetwork &&
1381 innerIndex < coalesceTable->dwNumEntries; innerIndex++)
1382 if ((ipAddrs->table[ndx].dwAddr &
1383 ipAddrs->table[ndx].dwMask) ==
1384 (coalesceTable->table[innerIndex].dwAddr
1385 & coalesceTable->table[innerIndex].dwMask))
1386 newNetwork = FALSE;
1388 if (newNetwork)
1389 memcpy(&coalesceTable->table[
1390 coalesceTable->dwNumEntries++],
1391 &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
1395 NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
1396 coalesceTable);
1397 ret = NRC_GOODRET;
1398 for (ndx = 0; ret == NRC_GOODRET &&
1399 ndx < coalesceTable->dwNumEntries; ndx++)
1400 if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
1401 ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
1403 else
1404 ret = NRC_SYSTEM;
1405 HeapFree(GetProcessHeap(), 0, ipAddrs);
1406 HeapFree(GetProcessHeap(), 0, coalesceTable);
1408 else
1409 ret = NRC_OSRESNOTAV;
1411 else
1412 ret = NRC_SYSTEM;
1413 TRACE("returning 0x%02x\n", ret);
1414 return ret;
1417 static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r',
1418 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
1419 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
1420 static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u',
1421 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
1422 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
1423 'e','r','s','\0' };
1424 static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' };
1425 static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m',
1426 'e','Q','u','e','r','y','C','o','u','n','t','\0' };
1427 static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m',
1428 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1429 static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v',
1430 'Q','u','e','r','y','C','o','u','n','t','\0' };
1431 static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v',
1432 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1433 static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' };
1434 static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o',
1435 'u','t','\0' };
1436 static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\',
1437 'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
1439 /* Initializes global variables and registers the NetBT transport */
1440 void NetBTInit(void)
1442 HKEY hKey;
1443 NetBIOSTransport transport;
1444 LONG ret;
1446 TRACE("\n");
1448 gEnableDNS = TRUE;
1449 gBCastQueries = BCAST_QUERIES;
1450 gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
1451 gWINSQueries = WINS_QUERIES;
1452 gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
1453 gNumWINSServers = 0;
1454 memset(gWINSServers, 0, sizeof(gWINSServers));
1455 gScopeID[0] = '\0';
1456 gCacheTimeout = CACHE_TIMEOUT;
1458 /* Try to open the Win9x NetBT configuration key */
1459 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey);
1460 /* If that fails, try the WinNT NetBT configuration key */
1461 if (ret != ERROR_SUCCESS)
1462 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ,
1463 &hKey);
1464 if (ret == ERROR_SUCCESS)
1466 DWORD dword, size;
1468 size = sizeof(dword);
1469 if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL,
1470 (LPBYTE)&dword, &size) == ERROR_SUCCESS)
1471 gEnableDNS = dword;
1472 size = sizeof(dword);
1473 if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL,
1474 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1475 && dword <= MAX_QUERIES)
1476 gBCastQueries = dword;
1477 size = sizeof(dword);
1478 if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL,
1479 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
1480 gBCastQueryTimeout = dword;
1481 size = sizeof(dword);
1482 if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL,
1483 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1484 && dword <= MAX_QUERIES)
1485 gWINSQueries = dword;
1486 size = sizeof(dword);
1487 if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL,
1488 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
1489 gWINSQueryTimeout = dword;
1490 size = sizeof(gScopeID) - 1;
1491 if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size)
1492 == ERROR_SUCCESS)
1494 /* convert into L2-encoded version, suitable for use by
1495 NetBTNameEncode */
1496 char *ptr, *lenPtr;
1498 for (ptr = gScopeID + 1, lenPtr = gScopeID; ptr - gScopeID < sizeof(gScopeID) && *ptr; ++ptr)
1500 if (*ptr == '.')
1502 lenPtr = ptr;
1503 *lenPtr = 0;
1505 else
1507 ++*lenPtr;
1511 if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL,
1512 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
1513 gCacheTimeout = dword;
1514 RegCloseKey(hKey);
1516 /* WINE-specific NetBT registry settings. Because our adapter naming is
1517 * different than MS', we can't do per-adapter WINS configuration in the
1518 * same place. Just do a global WINS configuration instead.
1520 /* @@ Wine registry key: HKCU\Software\Wine\Network */
1521 if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS)
1523 static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
1524 char nsString[16];
1525 DWORD size, ndx;
1527 for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
1528 ndx++)
1530 size = sizeof(nsString) / sizeof(char);
1531 if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
1532 (LPBYTE)nsString, &size) == ERROR_SUCCESS)
1534 unsigned long addr = inet_addr(nsString);
1536 if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
1537 gWINSServers[gNumWINSServers++] = addr;
1540 RegCloseKey(hKey);
1543 transport.enumerate = NetBTEnum;
1544 transport.astat = NetBTAstat;
1545 transport.findName = NetBTFindName;
1546 transport.call = NetBTCall;
1547 transport.send = NetBTSend;
1548 transport.recv = NetBTRecv;
1549 transport.hangup = NetBTHangup;
1550 transport.cleanupAdapter = NetBTCleanupAdapter;
1551 transport.cleanup = NetBTCleanup;
1552 memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
1553 NetBIOSRegisterTransport(gTransportID, &transport);