winevulkan: Merge body and body_conversion.
[wine.git] / dlls / netapi32 / nbt.c
blobc364a116fca2c1956e15172acc318ed1a1c5b50e
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 <stdarg.h>
70 #include "winsock2.h"
71 #include "windef.h"
72 #include "winbase.h"
73 #include "wine/debug.h"
74 #include "winreg.h"
75 #include "iphlpapi.h"
77 #include "netbios.h"
78 #include "nbnamecache.h"
80 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
82 #define PORT_NBNS 137
83 #define PORT_NBDG 138
84 #define PORT_NBSS 139
86 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
87 #define NBR_GETWORD(p) ntohs(*(WORD *)(p))
89 #define MIN_QUERIES 1
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 255
102 #define SIMPLE_NAME_QUERY_PKT_SIZE 16 + MAX_NBT_NAME_SZ
104 #define NBNS_TYPE_NB 0x0020
105 #define NBNS_TYPE_NBSTAT 0x0021
106 #define NBNS_CLASS_INTERNET 0x00001
107 #define NBNS_HEADER_SIZE (sizeof(WORD) * 6)
108 #define NBNS_RESPONSE_AND_OPCODE 0xf800
109 #define NBNS_RESPONSE_AND_QUERY 0x8000
110 #define NBNS_REPLYCODE 0x0f
112 #define NBSS_HDRSIZE 4
114 #define NBSS_MSG 0x00
115 #define NBSS_REQ 0x81
116 #define NBSS_ACK 0x82
117 #define NBSS_NACK 0x83
118 #define NBSS_RETARGET 0x84
119 #define NBSS_KEEPALIVE 0x85
121 #define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80
122 #define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
123 #define NBSS_ERR_BAD_NAME 0x82
124 #define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83
126 #define NBSS_EXTENSION 0x01
128 typedef struct _NetBTSession
130 CRITICAL_SECTION cs;
131 SOCKET fd;
132 DWORD bytesPending;
133 } NetBTSession;
135 typedef struct _NetBTAdapter
137 MIB_IPADDRROW ipr;
138 WORD nameQueryXID;
139 struct NBNameCache *nameCache;
140 DWORD xmit_success;
141 DWORD recv_success;
142 } NetBTAdapter;
144 static ULONG gTransportID;
145 static BOOL gEnableDNS;
146 static DWORD gBCastQueries;
147 static DWORD gBCastQueryTimeout;
148 static DWORD gWINSQueries;
149 static DWORD gWINSQueryTimeout;
150 static DWORD gWINSServers[MAX_WINS_SERVERS];
151 static int gNumWINSServers;
152 static char gScopeID[MAX_SCOPE_ID_LEN];
153 static DWORD gCacheTimeout;
154 static struct NBNameCache *gNameCache;
156 /* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
157 * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
158 * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes
159 * if p is NULL-terminated. Returns the number of bytes stored in buffer.
161 static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
163 int i,len=0;
165 if (!p) return 0;
166 if (!buffer) return 0;
168 buffer[len++] = NCBNAMSZ * 2;
169 for (i = 0; i < NCBNAMSZ && p[i]; i++)
171 buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
172 buffer[len++] = (p[i] & 0x0f) + 'A';
174 while (len < NCBNAMSZ * 2)
176 buffer[len++] = 'C';
177 buffer[len++] = 'A';
179 if (*gScopeID)
181 int scopeIDLen = strlen(gScopeID);
183 memcpy(buffer + len, gScopeID, scopeIDLen);
184 len += scopeIDLen;
186 buffer[len++] = 0; /* add second terminator */
187 return len;
190 /* Creates a NBT name request packet for name in buffer. If broadcast is true,
191 * creates a broadcast request, otherwise creates a unicast request.
192 * Returns the number of bytes stored in buffer.
194 static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
195 BOOL broadcast, UCHAR *buffer, int len)
197 int i = 0;
199 if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
201 NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */
202 if (broadcast)
204 NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
205 i+=2;
207 else
209 NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
210 i+=2;
212 NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
213 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
214 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
215 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
217 i += NetBTNameEncode(name, &buffer[i]);
219 NBR_ADDWORD(&buffer[i],qtype); i+=2;
220 NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
222 return i;
225 /* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on
226 * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not
227 * NULL.
228 * Returns 0 on success, -1 on failure.
230 static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
231 WORD qtype, DWORD destAddr, BOOL broadcast)
233 int ret = 0, on = 1;
234 struct in_addr addr;
236 addr.s_addr = destAddr;
237 TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
239 if (broadcast)
240 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
241 if(ret == 0)
243 WSABUF wsaBuf;
244 UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
245 struct sockaddr_in sin;
247 memset(&sin, 0, sizeof(sin));
248 sin.sin_addr.s_addr = destAddr;
249 sin.sin_family = AF_INET;
250 sin.sin_port = htons(PORT_NBNS);
252 wsaBuf.buf = (CHAR*)buf;
253 wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
254 sizeof(buf));
255 if (wsaBuf.len > 0)
257 DWORD bytesSent;
259 ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
260 (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
261 if (ret < 0 || bytesSent < wsaBuf.len)
262 ret = -1;
263 else
264 ret = 0;
266 else
267 ret = -1;
269 return ret;
272 typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
273 WORD answerIndex, PUCHAR rData, WORD rdLength);
275 /* Waits on fd until GetTickCount() returns a value greater than or equal to
276 * waitUntil for a name service response. If a name response matching xid
277 * is received, calls answerCallback once for each answer resource record in
278 * the response. (The callback's answerCount will be the total number of
279 * answers to expect, and answerIndex will be the 0-based index that's being
280 * sent this time.) Quits parsing if answerCallback returns FALSE.
281 * Returns NRC_GOODRET on timeout or a valid response received, something else
282 * on error.
284 static UCHAR NetBTWaitForNameResponse(const NetBTAdapter *adapter, SOCKET fd,
285 DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
287 BOOL found = FALSE;
288 DWORD now;
289 UCHAR ret = NRC_GOODRET;
291 if (!adapter) return NRC_BADDR;
292 if (fd == INVALID_SOCKET) return NRC_BADDR;
293 if (!answerCallback) return NRC_BADDR;
295 while (!found && ret == NRC_GOODRET && (int)((now = GetTickCount()) - waitUntil) < 0)
297 DWORD msToWait = waitUntil - now;
298 struct fd_set fds;
299 struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
300 int r;
302 FD_ZERO(&fds);
303 FD_SET(fd, &fds);
304 r = select(fd + 1, &fds, NULL, NULL, &timeout);
305 if (r < 0)
306 ret = NRC_SYSTEM;
307 else if (r == 1)
309 /* FIXME: magic #, is this always enough? */
310 UCHAR buffer[256];
311 int fromsize;
312 struct sockaddr_in fromaddr;
313 WORD respXID, flags, queryCount, answerCount;
314 WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
315 DWORD bytesReceived, recvFlags = 0;
317 fromsize = sizeof(fromaddr);
318 r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
319 (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
320 if(r < 0)
322 ret = NRC_SYSTEM;
323 break;
326 if (bytesReceived < NBNS_HEADER_SIZE)
327 continue;
329 respXID = NBR_GETWORD(buffer);
330 if (adapter->nameQueryXID != respXID)
331 continue;
333 flags = NBR_GETWORD(buffer + 2);
334 queryCount = NBR_GETWORD(buffer + 4);
335 answerCount = NBR_GETWORD(buffer + 6);
337 /* a reply shouldn't contain a query, ignore bad packet */
338 if (queryCount > 0)
339 continue;
341 if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
343 if ((flags & NBNS_REPLYCODE) != 0)
344 ret = NRC_NAMERR;
345 else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
347 PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
348 BOOL shouldContinue = TRUE;
349 WORD answerIndex = 0;
351 found = TRUE;
352 /* decode one answer at a time */
353 while (ret == NRC_GOODRET && answerIndex < answerCount &&
354 ptr - buffer < bytesReceived && shouldContinue)
356 WORD rLen;
358 /* scan past name */
359 for (; ptr[0] && ptr - buffer < bytesReceived; )
360 ptr += ptr[0] + 1;
361 ptr++;
362 ptr += 2; /* scan past type */
363 if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
364 && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
365 ptr += sizeof(WORD);
366 else
367 ret = NRC_SYSTEM; /* parse error */
368 ptr += sizeof(DWORD); /* TTL */
369 rLen = NBR_GETWORD(ptr);
370 rLen = min(rLen, bytesReceived - (ptr - buffer));
371 ptr += sizeof(WORD);
372 shouldContinue = answerCallback(data, answerCount,
373 answerIndex, ptr, rLen);
374 ptr += rLen;
375 answerIndex++;
381 TRACE("returning 0x%02x\n", ret);
382 return ret;
385 typedef struct _NetBTNameQueryData {
386 NBNameCacheEntry *cacheEntry;
387 UCHAR ret;
388 } NetBTNameQueryData;
390 /* Name query callback function for NetBTWaitForNameResponse, creates a cache
391 * entry on the first answer, adds each address as it's called again (as long
392 * as there's space). If there's an error that should be propagated as the
393 * NetBIOS error, modifies queryData's ret member to the proper return code.
395 static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
396 WORD answerIndex, PUCHAR rData, WORD rLen)
398 NetBTNameQueryData *queryData = pVoid;
399 BOOL ret;
401 if (queryData)
403 if (queryData->cacheEntry == NULL)
405 queryData->cacheEntry = HeapAlloc(GetProcessHeap(), 0,
406 FIELD_OFFSET(NBNameCacheEntry, addresses[answerCount]));
407 if (queryData->cacheEntry)
408 queryData->cacheEntry->numAddresses = 0;
409 else
410 queryData->ret = NRC_OSRESNOTAV;
412 if (rLen == 6 && queryData->cacheEntry &&
413 queryData->cacheEntry->numAddresses < answerCount)
415 queryData->cacheEntry->addresses[queryData->cacheEntry->
416 numAddresses++] = *(const DWORD *)(rData + 2);
417 ret = queryData->cacheEntry->numAddresses < answerCount;
419 else
420 ret = FALSE;
422 else
423 ret = FALSE;
424 return ret;
427 /* Workhorse NetBT name lookup function. Sends a name lookup query for
428 * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
429 * adapter->nameQueryXID as the transaction ID. Waits up to timeout
430 * milliseconds, and retries up to maxQueries times, waiting for a reply.
431 * If a valid response is received, stores the looked up addresses as a
432 * NBNameCacheEntry in *cacheEntry.
433 * Returns NRC_GOODRET on success, though this may not mean the name was
434 * resolved--check whether *cacheEntry is NULL.
436 static UCHAR NetBTNameWaitLoop(const NetBTAdapter *adapter, SOCKET fd, const NCB *ncb,
437 DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
438 NBNameCacheEntry **cacheEntry)
440 unsigned int queries;
441 NetBTNameQueryData queryData;
443 if (!adapter) return NRC_BADDR;
444 if (fd == INVALID_SOCKET) return NRC_BADDR;
445 if (!ncb) return NRC_BADDR;
446 if (!cacheEntry) return NRC_BADDR;
448 queryData.cacheEntry = NULL;
449 queryData.ret = NRC_GOODRET;
450 for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
451 queries++)
453 if (!NCB_CANCELLED(ncb))
455 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
456 adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
458 if (r == 0)
459 queryData.ret = NetBTWaitForNameResponse(adapter, fd,
460 GetTickCount() + timeout, NetBTFindNameAnswerCallback,
461 &queryData);
462 else
463 queryData.ret = NRC_SYSTEM;
465 else
466 queryData.ret = NRC_CMDCAN;
468 if (queryData.cacheEntry)
470 memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
471 memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
473 *cacheEntry = queryData.cacheEntry;
474 return queryData.ret;
477 /* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
478 * has not yet been created, creates it, using gCacheTimeout as the cache
479 * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails,
480 * frees cacheEntry.
481 * Returns NRC_GOODRET on success, and something else on failure.
483 static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
484 NBNameCacheEntry *cacheEntry)
486 UCHAR ret;
488 if (!nameCache) return NRC_BADDR;
489 if (!cacheEntry) return NRC_BADDR;
491 if (!*nameCache)
492 *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
493 if (*nameCache)
494 ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
495 ? NRC_GOODRET : NRC_OSRESNOTAV;
496 else
498 HeapFree(GetProcessHeap(), 0, cacheEntry);
499 ret = NRC_OSRESNOTAV;
501 return ret;
504 /* Attempts to resolve name using inet_addr(), then gethostbyname() if
505 * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name
506 * can be looked up, returns 0 and stores the looked up addresses as a
507 * NBNameCacheEntry in *cacheEntry.
508 * Returns NRC_GOODRET on success, though this may not mean the name was
509 * resolved--check whether *cacheEntry is NULL. Returns something else on
510 * error.
512 static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
513 NBNameCacheEntry **cacheEntry)
515 UCHAR ret = NRC_GOODRET;
517 TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
519 if (!name) return NRC_BADDR;
520 if (!cacheEntry) return NRC_BADDR;
522 if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
523 name[NCBNAMSZ - 1] == 0x20))
525 CHAR toLookup[NCBNAMSZ];
526 unsigned int i;
528 for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
529 toLookup[i] = name[i];
530 toLookup[i] = '\0';
532 if (isdigit(toLookup[0]))
534 unsigned long addr = inet_addr(toLookup);
536 if (addr != INADDR_NONE)
538 *cacheEntry = HeapAlloc(GetProcessHeap(), 0,
539 FIELD_OFFSET(NBNameCacheEntry, addresses[1]));
540 if (*cacheEntry)
542 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
543 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
544 (*cacheEntry)->nbname[0] = '*';
545 (*cacheEntry)->numAddresses = 1;
546 (*cacheEntry)->addresses[0] = addr;
548 else
549 ret = NRC_OSRESNOTAV;
552 if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
554 struct hostent *host;
556 if ((host = gethostbyname(toLookup)) != NULL)
558 for (i = 0; host->h_addr_list && host->h_addr_list[i]; i++)
560 if (host->h_addr_list && host->h_addr_list[0])
562 *cacheEntry = HeapAlloc(GetProcessHeap(), 0,
563 FIELD_OFFSET(NBNameCacheEntry, addresses[i]));
564 if (*cacheEntry)
566 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
567 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
568 (*cacheEntry)->nbname[0] = '*';
569 (*cacheEntry)->numAddresses = i;
570 for (i = 0; i < (*cacheEntry)->numAddresses; i++)
571 (*cacheEntry)->addresses[i] =
572 *(DWORD*)host->h_addr_list[i];
574 else
575 ret = NRC_OSRESNOTAV;
581 TRACE("returning 0x%02x\n", ret);
582 return ret;
585 /* Looks up the name in ncb->ncb_callname, first in the name caches (global
586 * and this adapter's), then using gethostbyname(), next by WINS if configured,
587 * and finally using broadcast NetBT name resolution. In NBT parlance, this
588 * makes this an "H-node". Stores an entry in the appropriate name cache for a
589 * found node, and returns it as *cacheEntry.
590 * Assumes data, ncb, and cacheEntry are not NULL.
591 * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
592 * just that all name lookup operations completed successfully--and something
593 * else on failure. *cacheEntry will be NULL if the name was not found.
595 static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
596 const NBNameCacheEntry **cacheEntry)
598 UCHAR ret = NRC_GOODRET;
600 TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
602 if (!cacheEntry) return NRC_BADDR;
603 *cacheEntry = NULL;
605 if (!adapter) return NRC_BADDR;
606 if (!ncb) return NRC_BADDR;
608 if (ncb->ncb_callname[0] == '*')
609 ret = NRC_NOWILD;
610 else
612 *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
613 if (!*cacheEntry)
614 *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
615 ncb->ncb_callname);
616 if (!*cacheEntry)
618 NBNameCacheEntry *newEntry = NULL;
620 ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
621 if (ret == NRC_GOODRET && newEntry)
623 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
624 if (ret != NRC_GOODRET)
625 newEntry = NULL;
627 else
629 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
630 0, WSA_FLAG_OVERLAPPED);
632 if(fd == INVALID_SOCKET)
633 ret = NRC_OSRESNOTAV;
634 else
636 int winsNdx;
638 adapter->nameQueryXID++;
639 for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
640 && winsNdx < gNumWINSServers; winsNdx++)
641 ret = NetBTNameWaitLoop(adapter, fd, ncb,
642 gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
643 gWINSQueries, &newEntry);
644 if (ret == NRC_GOODRET && newEntry)
646 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
647 if (ret != NRC_GOODRET)
648 newEntry = NULL;
650 if (ret == NRC_GOODRET && *cacheEntry == NULL)
652 DWORD bcastAddr =
653 adapter->ipr.dwAddr & adapter->ipr.dwMask;
655 if (adapter->ipr.dwBCastAddr)
656 bcastAddr |= ~adapter->ipr.dwMask;
657 ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
658 TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
659 if (ret == NRC_GOODRET && newEntry)
661 ret = NetBTStoreCacheEntry(&adapter->nameCache,
662 newEntry);
663 if (ret != NRC_GOODRET)
664 newEntry = NULL;
667 closesocket(fd);
670 *cacheEntry = newEntry;
673 TRACE("returning 0x%02x\n", ret);
674 return ret;
677 typedef struct _NetBTNodeQueryData
679 BOOL gotResponse;
680 PADAPTER_STATUS astat;
681 WORD astatLen;
682 } NetBTNodeQueryData;
684 /* Callback function for NetBTAstatRemote, parses the rData for the node
685 * status and name list of the remote node. Always returns FALSE, since
686 * there's never more than one answer we care about in a node status response.
688 static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
689 WORD answerIndex, PUCHAR rData, WORD rLen)
691 NetBTNodeQueryData *data = pVoid;
693 if (data && !data->gotResponse && rData && rLen >= 1)
695 /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
696 if (rLen >= rData[0] * (NCBNAMSZ + 2))
698 WORD i;
699 PUCHAR src;
700 PNAME_BUFFER dst;
702 data->gotResponse = TRUE;
703 data->astat->name_count = rData[0];
704 for (i = 0, src = rData + 1,
705 dst = (PNAME_BUFFER)((PUCHAR)data->astat +
706 sizeof(ADAPTER_STATUS));
707 i < data->astat->name_count && src - rData < rLen &&
708 (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
709 i++, dst++, src += NCBNAMSZ + 2)
711 UCHAR flags = *(src + NCBNAMSZ);
713 memcpy(dst->name, src, NCBNAMSZ);
714 /* we won't actually see a registering name in the returned
715 * response. It's useful to see if no other flags are set; if
716 * none are, then the name is registered. */
717 dst->name_flags = REGISTERING;
718 if (flags & 0x80)
719 dst->name_flags |= GROUP_NAME;
720 if (flags & 0x10)
721 dst->name_flags |= DEREGISTERED;
722 if (flags & 0x08)
723 dst->name_flags |= DUPLICATE;
724 if (dst->name_flags == REGISTERING)
725 dst->name_flags = REGISTERED;
727 /* arbitrarily set HW type to Ethernet */
728 data->astat->adapter_type = 0xfe;
729 if (src - rData < rLen)
730 memcpy(data->astat->adapter_address, src,
731 min(rLen - (src - rData), 6));
734 return FALSE;
737 /* This uses the WINS timeout and query values, as they're the
738 * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
740 static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
742 UCHAR ret = NRC_GOODRET;
743 const NBNameCacheEntry *cacheEntry = NULL;
745 TRACE("adapter %p, NCB %p\n", adapter, ncb);
747 if (!adapter) return NRC_BADDR;
748 if (!ncb) return NRC_INVADDRESS;
750 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
751 if (ret == NRC_GOODRET && cacheEntry)
753 if (cacheEntry->numAddresses > 0)
755 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
756 WSA_FLAG_OVERLAPPED);
758 if(fd == INVALID_SOCKET)
759 ret = NRC_OSRESNOTAV;
760 else
762 NetBTNodeQueryData queryData;
763 DWORD queries;
764 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
766 adapter->nameQueryXID++;
767 astat->name_count = 0;
768 queryData.gotResponse = FALSE;
769 queryData.astat = astat;
770 queryData.astatLen = ncb->ncb_length;
771 for (queries = 0; !queryData.gotResponse &&
772 queries < gWINSQueries; queries++)
774 if (!NCB_CANCELLED(ncb))
776 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
777 adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
778 cacheEntry->addresses[0], FALSE);
780 if (r == 0)
781 ret = NetBTWaitForNameResponse(adapter, fd,
782 GetTickCount() + gWINSQueryTimeout,
783 NetBTNodeStatusAnswerCallback, &queryData);
784 else
785 ret = NRC_SYSTEM;
787 else
788 ret = NRC_CMDCAN;
790 closesocket(fd);
793 else
794 ret = NRC_CMDTMO;
796 else if (ret == NRC_CMDCAN)
797 ; /* do nothing, we were cancelled */
798 else
799 ret = NRC_CMDTMO;
800 TRACE("returning 0x%02x\n", ret);
801 return ret;
804 static UCHAR NetBTAstat(void *adapt, PNCB ncb)
806 NetBTAdapter *adapter = adapt;
807 UCHAR ret;
809 TRACE("adapt %p, NCB %p\n", adapt, ncb);
811 if (!adapter) return NRC_ENVNOTDEF;
812 if (!ncb) return NRC_INVADDRESS;
813 if (!ncb->ncb_buffer) return NRC_BADDR;
814 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
816 if (ncb->ncb_callname[0] == '*')
818 DWORD physAddrLen;
819 MIB_IFROW ifRow;
820 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
822 memset(astat, 0, sizeof(ADAPTER_STATUS));
823 astat->rev_major = 3;
824 ifRow.dwIndex = adapter->ipr.dwIndex;
825 if (GetIfEntry(&ifRow) != NO_ERROR)
826 ret = NRC_BRIDGE;
827 else
829 physAddrLen = min(ifRow.dwPhysAddrLen, 6);
830 if (physAddrLen > 0)
831 memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
832 /* doubt anyone cares, but why not.. */
833 if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
834 astat->adapter_type = 0xff;
835 else
836 astat->adapter_type = 0xfe; /* for Ethernet */
837 astat->max_sess_pkt_size = 0xffff;
838 astat->xmit_success = adapter->xmit_success;
839 astat->recv_success = adapter->recv_success;
840 ret = NRC_GOODRET;
843 else
844 ret = NetBTAstatRemote(adapter, ncb);
845 TRACE("returning 0x%02x\n", ret);
846 return ret;
849 static UCHAR NetBTFindName(void *adapt, PNCB ncb)
851 NetBTAdapter *adapter = adapt;
852 UCHAR ret;
853 const NBNameCacheEntry *cacheEntry = NULL;
854 PFIND_NAME_HEADER foundName;
856 TRACE("adapt %p, NCB %p\n", adapt, ncb);
858 if (!adapter) return NRC_ENVNOTDEF;
859 if (!ncb) return NRC_INVADDRESS;
860 if (!ncb->ncb_buffer) return NRC_BADDR;
861 if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
863 foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
864 memset(foundName, 0, sizeof(FIND_NAME_HEADER));
866 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
867 if (ret == NRC_GOODRET)
869 if (cacheEntry)
871 DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
872 sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
873 DWORD ndx;
875 for (ndx = 0; ndx < spaceFor; ndx++)
877 PFIND_NAME_BUFFER findNameBuffer;
879 findNameBuffer =
880 (PFIND_NAME_BUFFER)((PUCHAR)foundName +
881 sizeof(FIND_NAME_HEADER) + foundName->node_count *
882 sizeof(FIND_NAME_BUFFER));
883 memset(findNameBuffer->destination_addr, 0, 2);
884 memcpy(findNameBuffer->destination_addr + 2,
885 &adapter->ipr.dwAddr, sizeof(DWORD));
886 memset(findNameBuffer->source_addr, 0, 2);
887 memcpy(findNameBuffer->source_addr + 2,
888 &cacheEntry->addresses[ndx], sizeof(DWORD));
889 foundName->node_count++;
891 if (spaceFor < cacheEntry->numAddresses)
892 ret = NRC_BUFLEN;
894 else
895 ret = NRC_CMDTMO;
897 TRACE("returning 0x%02x\n", ret);
898 return ret;
901 static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
902 const UCHAR *callingName)
904 UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
905 int r;
906 unsigned int len = 0;
907 DWORD bytesSent, bytesReceived, recvFlags = 0;
908 WSABUF wsaBuf;
910 buffer[0] = NBSS_REQ;
911 buffer[1] = 0;
913 len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
914 len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
916 NBR_ADDWORD(&buffer[2], len);
918 wsaBuf.len = len + NBSS_HDRSIZE;
919 wsaBuf.buf = (char*)buffer;
921 r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
922 if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
924 ERR("send failed\n");
925 return NRC_SABORT;
928 /* I've already set the recv timeout on this socket (if it supports it), so
929 * just block. Hopefully we'll always receive the session acknowledgement
930 * within one timeout.
932 wsaBuf.len = NBSS_HDRSIZE + 1;
933 r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
934 if (r < 0 || bytesReceived < NBSS_HDRSIZE)
935 ret = NRC_SABORT;
936 else if (buffer[0] == NBSS_NACK)
938 if (r == NBSS_HDRSIZE + 1)
940 switch (buffer[NBSS_HDRSIZE])
942 case NBSS_ERR_INSUFFICIENT_RESOURCES:
943 ret = NRC_REMTFUL;
944 break;
945 default:
946 ret = NRC_NOCALL;
949 else
950 ret = NRC_NOCALL;
952 else if (buffer[0] == NBSS_RETARGET)
954 FIXME("Got a session retarget, can't deal\n");
955 ret = NRC_NOCALL;
957 else if (buffer[0] == NBSS_ACK)
958 ret = NRC_GOODRET;
959 else
960 ret = NRC_SYSTEM;
962 TRACE("returning 0x%02x\n", ret);
963 return ret;
966 static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
968 NetBTAdapter *adapter = adapt;
969 UCHAR ret;
970 const NBNameCacheEntry *cacheEntry = NULL;
972 TRACE("adapt %p, ncb %p\n", adapt, ncb);
974 if (!adapter) return NRC_ENVNOTDEF;
975 if (!ncb) return NRC_INVADDRESS;
976 if (!sess) return NRC_BADDR;
978 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
979 if (ret == NRC_GOODRET)
981 if (cacheEntry && cacheEntry->numAddresses > 0)
983 SOCKET fd;
985 fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
986 WSA_FLAG_OVERLAPPED);
987 if (fd != INVALID_SOCKET)
989 DWORD timeout;
990 struct sockaddr_in sin;
992 if (ncb->ncb_rto > 0)
994 timeout = ncb->ncb_rto * 500;
995 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
996 sizeof(timeout));
998 if (ncb->ncb_sto > 0)
1000 timeout = ncb->ncb_sto * 500;
1001 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
1002 sizeof(timeout));
1005 memset(&sin, 0, sizeof(sin));
1006 memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
1007 sizeof(sin.sin_addr));
1008 sin.sin_family = AF_INET;
1009 sin.sin_port = htons(PORT_NBSS);
1010 /* FIXME: use nonblocking mode for the socket, check the
1011 * cancel flag periodically
1013 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
1014 == SOCKET_ERROR)
1015 ret = NRC_CMDTMO;
1016 else
1018 static const UCHAR fakedCalledName[] = "*SMBSERVER";
1019 const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
1020 ? fakedCalledName : cacheEntry->nbname;
1022 ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
1023 if (ret != NRC_GOODRET && calledParty[0] == '*')
1025 FIXME("NBT session to \"*SMBSERVER\" refused,\n");
1026 FIXME("should try finding name using ASTAT\n");
1029 if (ret != NRC_GOODRET)
1030 closesocket(fd);
1031 else
1033 NetBTSession *session = HeapAlloc(
1034 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
1036 if (session)
1038 session->fd = fd;
1039 InitializeCriticalSection(&session->cs);
1040 session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBTSession.cs");
1041 *sess = session;
1043 else
1045 ret = NRC_OSRESNOTAV;
1046 closesocket(fd);
1050 else
1051 ret = NRC_OSRESNOTAV;
1053 else
1054 ret = NRC_NAMERR;
1056 TRACE("returning 0x%02x\n", ret);
1057 return 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 = adapt;
1070 NetBTSession *session = sess;
1071 UCHAR buffer[NBSS_HDRSIZE], ret;
1072 int r;
1073 WSABUF wsaBufs[2];
1074 DWORD bytesSent;
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;
1085 buffer[1] = 0;
1086 NBR_ADDWORD(&buffer[2], ncb->ncb_length);
1088 wsaBufs[0].len = NBSS_HDRSIZE;
1089 wsaBufs[0].buf = (char*)buffer;
1090 wsaBufs[1].len = ncb->ncb_length;
1091 wsaBufs[1].buf = (char*)ncb->ncb_buffer;
1093 r = WSASend(session->fd, wsaBufs, ARRAY_SIZE(wsaBufs), &bytesSent, 0, NULL, NULL);
1094 if (r == SOCKET_ERROR)
1096 NetBIOSHangupSession(ncb);
1097 ret = NRC_SABORT;
1099 else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
1101 FIXME("Only sent %ld bytes (of %d), hanging up session\n", bytesSent,
1102 NBSS_HDRSIZE + ncb->ncb_length);
1103 NetBIOSHangupSession(ncb);
1104 ret = NRC_SABORT;
1106 else
1108 ret = NRC_GOODRET;
1109 adapter->xmit_success++;
1111 TRACE("returning 0x%02x\n", ret);
1112 return ret;
1115 static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
1117 NetBTAdapter *adapter = adapt;
1118 NetBTSession *session = sess;
1119 UCHAR buffer[NBSS_HDRSIZE], ret;
1120 int r;
1121 WSABUF wsaBufs[2];
1122 DWORD bufferCount, bytesReceived, flags;
1124 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1126 if (!adapter) return NRC_ENVNOTDEF;
1127 if (!ncb) return NRC_BADDR;
1128 if (!ncb->ncb_buffer) return NRC_BADDR;
1129 if (!session) return NRC_SNUMOUT;
1130 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1132 EnterCriticalSection(&session->cs);
1133 bufferCount = 0;
1134 if (session->bytesPending == 0)
1136 bufferCount++;
1137 wsaBufs[0].len = NBSS_HDRSIZE;
1138 wsaBufs[0].buf = (char*)buffer;
1140 wsaBufs[bufferCount].len = ncb->ncb_length;
1141 wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
1142 bufferCount++;
1144 flags = 0;
1145 /* FIXME: should poll a bit so I can check the cancel flag */
1146 r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
1147 NULL, NULL);
1148 if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
1150 LeaveCriticalSection(&session->cs);
1151 ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
1152 NetBIOSHangupSession(ncb);
1153 ret = NRC_SABORT;
1155 else if (NCB_CANCELLED(ncb))
1157 LeaveCriticalSection(&session->cs);
1158 ret = NRC_CMDCAN;
1160 else
1162 if (bufferCount == 2)
1164 if (buffer[0] == NBSS_KEEPALIVE)
1166 LeaveCriticalSection(&session->cs);
1167 FIXME("Oops, received a session keepalive and lost my place\n");
1168 /* need to read another session header until we get a session
1169 * message header. */
1170 NetBIOSHangupSession(ncb);
1171 ret = NRC_SABORT;
1172 goto error;
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);
1179 ret = NRC_SABORT;
1180 goto error;
1182 else
1184 if (buffer[1] & NBSS_EXTENSION)
1186 LeaveCriticalSection(&session->cs);
1187 FIXME("Received a message that's too long for my taste\n");
1188 NetBIOSHangupSession(ncb);
1189 ret = NRC_SABORT;
1190 goto error;
1192 else
1194 session->bytesPending = NBSS_HDRSIZE
1195 + NBR_GETWORD(&buffer[2]) - bytesReceived;
1196 ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
1197 LeaveCriticalSection(&session->cs);
1201 else
1203 if (bytesReceived < session->bytesPending)
1204 session->bytesPending -= bytesReceived;
1205 else
1206 session->bytesPending = 0;
1207 LeaveCriticalSection(&session->cs);
1208 ncb->ncb_length = bytesReceived;
1210 if (session->bytesPending > 0)
1211 ret = NRC_INCOMP;
1212 else
1214 ret = NRC_GOODRET;
1215 adapter->recv_success++;
1218 error:
1219 TRACE("returning 0x%02x\n", ret);
1220 return ret;
1223 static UCHAR NetBTHangup(void *adapt, void *sess)
1225 NetBTSession *session = sess;
1227 TRACE("adapt %p, session %p\n", adapt, session);
1229 if (!session) return NRC_SNUMOUT;
1231 /* I don't lock the session, because NetBTRecv knows not to decrement
1232 * past 0, so if a receive completes after this it should still deal.
1234 closesocket(session->fd);
1235 session->fd = INVALID_SOCKET;
1236 session->bytesPending = 0;
1237 session->cs.DebugInfo->Spare[0] = 0;
1238 DeleteCriticalSection(&session->cs);
1239 HeapFree(GetProcessHeap(), 0, session);
1241 return NRC_GOODRET;
1244 static void NetBTCleanupAdapter(void *adapt)
1246 TRACE("adapt %p\n", adapt);
1247 if (adapt)
1249 NetBTAdapter *adapter = adapt;
1251 if (adapter->nameCache)
1252 NBNameCacheDestroy(adapter->nameCache);
1253 HeapFree(GetProcessHeap(), 0, adapt);
1257 static void NetBTCleanup(void)
1259 TRACE("\n");
1260 if (gNameCache)
1262 NBNameCacheDestroy(gNameCache);
1263 gNameCache = NULL;
1267 static UCHAR NetBTRegisterAdapter(const MIB_IPADDRROW *ipRow)
1269 UCHAR ret;
1270 NetBTAdapter *adapter;
1272 if (!ipRow) return NRC_BADDR;
1274 adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
1275 if (adapter)
1277 adapter->ipr = *ipRow;
1278 if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
1280 NetBTCleanupAdapter(adapter);
1281 ret = NRC_SYSTEM;
1283 else
1284 ret = NRC_GOODRET;
1286 else
1287 ret = NRC_OSRESNOTAV;
1288 return ret;
1291 /* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to
1292 * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
1293 * NetBIOS adapter table. For each callback, checks if the passed-in adapt
1294 * has an entry in the table; if so, this adapter was enumerated previously,
1295 * and it's enabled. As a flag, the table's dwAddr entry is changed to
1296 * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
1297 * The NetBTEnum function will add any remaining adapters from the
1298 * MIB_IPADDRTABLE to the NetBIOS adapter table.
1300 static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
1301 ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
1303 BOOL ret;
1304 PMIB_IPADDRTABLE table = closure;
1306 if (table && data)
1308 DWORD ndx;
1310 ret = FALSE;
1311 for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
1313 const NetBTAdapter *adapter = data->data;
1315 if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
1317 NetBIOSEnableAdapter(data->lana);
1318 table->table[ndx].dwAddr = INADDR_LOOPBACK;
1319 ret = TRUE;
1323 else
1324 ret = FALSE;
1325 return ret;
1328 /* Enumerates adapters by:
1329 * - retrieving the IP address table for the local machine
1330 * - eliminating loopback addresses from the table
1331 * - eliminating redundant addresses, that is, multiple addresses on the same
1332 * subnet
1333 * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
1334 * data. The callback reenables each adapter that's already in the NetBIOS
1335 * table. After NetBIOSEnumAdapters returns, this function adds any remaining
1336 * adapters to the NetBIOS table.
1338 static UCHAR NetBTEnum(void)
1340 UCHAR ret;
1341 DWORD size = 0;
1343 TRACE("\n");
1345 if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
1347 PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
1348 DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
1349 sizeof(MIB_IPADDRROW) + 1;
1351 ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
1352 if (ipAddrs)
1353 coalesceTable = HeapAlloc(GetProcessHeap(),
1354 HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
1355 (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
1356 if (ipAddrs && coalesceTable)
1358 if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
1360 DWORD ndx;
1362 for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
1364 if ((ipAddrs->table[ndx].dwAddr &
1365 ipAddrs->table[ndx].dwMask) !=
1366 htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
1368 BOOL newNetwork = TRUE;
1369 DWORD innerIndex;
1371 /* make sure we don't have more than one entry
1372 * for a subnet */
1373 for (innerIndex = 0; newNetwork &&
1374 innerIndex < coalesceTable->dwNumEntries; innerIndex++)
1375 if ((ipAddrs->table[ndx].dwAddr &
1376 ipAddrs->table[ndx].dwMask) ==
1377 (coalesceTable->table[innerIndex].dwAddr
1378 & coalesceTable->table[innerIndex].dwMask))
1379 newNetwork = FALSE;
1381 if (newNetwork)
1382 memcpy(&coalesceTable->table[
1383 coalesceTable->dwNumEntries++],
1384 &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
1388 NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
1389 coalesceTable);
1390 ret = NRC_GOODRET;
1391 for (ndx = 0; ret == NRC_GOODRET &&
1392 ndx < coalesceTable->dwNumEntries; ndx++)
1393 if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
1394 ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
1396 else
1397 ret = NRC_SYSTEM;
1398 HeapFree(GetProcessHeap(), 0, ipAddrs);
1399 HeapFree(GetProcessHeap(), 0, coalesceTable);
1401 else
1402 ret = NRC_OSRESNOTAV;
1404 else
1405 ret = NRC_SYSTEM;
1406 TRACE("returning 0x%02x\n", ret);
1407 return ret;
1410 /* Initializes global variables and registers the NetBT transport */
1411 void NetBTInit(void)
1413 WSADATA wsa_data;
1414 HKEY hKey;
1415 NetBIOSTransport transport;
1416 LONG ret;
1418 TRACE("\n");
1420 WSAStartup(MAKEWORD(2, 2), &wsa_data);
1422 gEnableDNS = TRUE;
1423 gBCastQueries = BCAST_QUERIES;
1424 gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
1425 gWINSQueries = WINS_QUERIES;
1426 gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
1427 gNumWINSServers = 0;
1428 memset(gWINSServers, 0, sizeof(gWINSServers));
1429 gScopeID[0] = '\0';
1430 gCacheTimeout = CACHE_TIMEOUT;
1432 /* Try to open the Win9x NetBT configuration key */
1433 ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP",
1434 0, KEY_READ, &hKey );
1435 /* If that fails, try the WinNT NetBT configuration key */
1436 if (ret != ERROR_SUCCESS)
1437 ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\NetBT\\Parameters",
1438 0, KEY_READ, &hKey );
1439 if (ret == ERROR_SUCCESS)
1441 DWORD dword, size;
1443 size = sizeof(dword);
1444 if (!RegQueryValueExW( hKey, L"EnableDNS", NULL, NULL, (BYTE *)&dword, &size ))
1445 gEnableDNS = dword;
1446 size = sizeof(dword);
1447 if (!RegQueryValueExW( hKey, L"BcastNameQueryCount", NULL, NULL, (BYTE *)&dword, &size )
1448 && dword >= MIN_QUERIES && dword <= MAX_QUERIES)
1449 gBCastQueries = dword;
1450 size = sizeof(dword);
1451 if (!RegQueryValueExW( hKey, L"BcastNameQueryTimeout", NULL, NULL, (BYTE *)&dword, &size )
1452 && dword >= MIN_QUERY_TIMEOUT)
1453 gBCastQueryTimeout = dword;
1454 size = sizeof(dword);
1455 if (!RegQueryValueExW( hKey, L"NameSrvQueryCount", NULL, NULL, (BYTE *)&dword, &size )
1456 && dword >= MIN_QUERIES && dword <= MAX_QUERIES)
1457 gWINSQueries = dword;
1458 size = sizeof(dword);
1459 if (!RegQueryValueExW( hKey, L"NameSrvQueryTimeout", NULL, NULL, (BYTE *)&dword, &size )
1460 && dword >= MIN_QUERY_TIMEOUT)
1461 gWINSQueryTimeout = dword;
1462 size = sizeof(gScopeID) - 1;
1463 if (!RegQueryValueExW( hKey, L"ScopeID", NULL, NULL, (BYTE *)gScopeID + 1, &size ))
1465 /* convert into L2-encoded version, suitable for use by
1466 NetBTNameEncode */
1467 char *ptr, *lenPtr;
1469 for (ptr = gScopeID + 1, lenPtr = gScopeID; ptr - gScopeID < sizeof(gScopeID) && *ptr; ++ptr)
1471 if (*ptr == '.')
1473 lenPtr = ptr;
1474 *lenPtr = 0;
1476 else
1478 ++*lenPtr;
1482 if (!RegQueryValueExW( hKey, L"CacheTimeout", NULL, NULL, (BYTE *)&dword, &size )
1483 && dword >= MIN_CACHE_TIMEOUT)
1484 gCacheTimeout = dword;
1485 RegCloseKey(hKey);
1487 /* WINE-specific NetBT registry settings. Because our adapter naming is
1488 * different than MS', we can't do per-adapter WINS configuration in the
1489 * same place. Just do a global WINS configuration instead.
1491 /* @@ Wine registry key: HKCU\Software\Wine\Network */
1492 if (!RegOpenKeyW( HKEY_CURRENT_USER, L"Software\\Wine\\Network", &hKey ))
1494 static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
1495 char nsString[16];
1496 DWORD size, ndx;
1498 for (ndx = 0; ndx < ARRAY_SIZE(nsValueNames); ndx++)
1500 size = ARRAY_SIZE(nsString);
1501 if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
1502 (LPBYTE)nsString, &size) == ERROR_SUCCESS)
1504 unsigned long addr = inet_addr(nsString);
1506 if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
1507 gWINSServers[gNumWINSServers++] = addr;
1510 RegCloseKey(hKey);
1513 transport.enumerate = NetBTEnum;
1514 transport.astat = NetBTAstat;
1515 transport.findName = NetBTFindName;
1516 transport.call = NetBTCall;
1517 transport.send = NetBTSend;
1518 transport.recv = NetBTRecv;
1519 transport.hangup = NetBTHangup;
1520 transport.cleanupAdapter = NetBTCleanupAdapter;
1521 transport.cleanup = NetBTCleanup;
1522 memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
1523 NetBIOSRegisterTransport(gTransportID, &transport);