[NETAPI32]
[reactos.git] / reactos / dll / win32 / netapi32 / nbt.c
1 /* Copyright (c) 2003 Juan Lang
2 *
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.
7 *
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.
12 *
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
16 *
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.
22 *
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.
32 *
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).
37 *
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.
53 *
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.)
64 *
65 * See also other FIXMEs in the code.
66 */
67
68 #define WIN32_NO_STATUS
69 #define _INC_WINDOWS
70 #define COM_NO_WINDOWS_H
71
72 #include <config.h>
73 #include <stdarg.h>
74
75 #include <windef.h>
76 #include <winbase.h>
77 #include <winsock2.h>
78 #include <wine/debug.h>
79 #include <winreg.h>
80 #include <iphlpapi.h>
81
82 #include "netbios.h"
83 #include "nbnamecache.h"
84
85 WINE_DEFAULT_DEBUG_CHANNEL(netbios);
86
87 #define PORT_NBNS 137
88 #define PORT_NBDG 138
89 #define PORT_NBSS 139
90
91 #ifndef INADDR_NONE
92 #define INADDR_NONE ~0UL
93 #endif
94
95 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
96 #define NBR_GETWORD(p) ntohs(*(WORD *)(p))
97
98 #define MIN_QUERIES 1
99 #define MAX_QUERIES 0xffff
100 #define MIN_QUERY_TIMEOUT 100
101 #define MAX_QUERY_TIMEOUT 0xffffffff
102 #define BCAST_QUERIES 3
103 #define BCAST_QUERY_TIMEOUT 750
104 #define WINS_QUERIES 3
105 #define WINS_QUERY_TIMEOUT 750
106 #define MAX_WINS_SERVERS 2
107 #define MIN_CACHE_TIMEOUT 60000
108 #define CACHE_TIMEOUT 360000
109
110 #define MAX_NBT_NAME_SZ 255
111 #define SIMPLE_NAME_QUERY_PKT_SIZE 16 + MAX_NBT_NAME_SZ
112
113 #define NBNS_TYPE_NB 0x0020
114 #define NBNS_TYPE_NBSTAT 0x0021
115 #define NBNS_CLASS_INTERNET 0x00001
116 #define NBNS_HEADER_SIZE (sizeof(WORD) * 6)
117 #define NBNS_RESPONSE_AND_OPCODE 0xf800
118 #define NBNS_RESPONSE_AND_QUERY 0x8000
119 #define NBNS_REPLYCODE 0x0f
120
121 #define NBSS_HDRSIZE 4
122
123 #define NBSS_MSG 0x00
124 #define NBSS_REQ 0x81
125 #define NBSS_ACK 0x82
126 #define NBSS_NACK 0x83
127 #define NBSS_RETARGET 0x84
128 #define NBSS_KEEPALIVE 0x85
129
130 #define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80
131 #define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
132 #define NBSS_ERR_BAD_NAME 0x82
133 #define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83
134
135 #define NBSS_EXTENSION 0x01
136
137 typedef struct _NetBTSession
138 {
139 CRITICAL_SECTION cs;
140 SOCKET fd;
141 DWORD bytesPending;
142 } NetBTSession;
143
144 typedef struct _NetBTAdapter
145 {
146 MIB_IPADDRROW ipr;
147 WORD nameQueryXID;
148 struct NBNameCache *nameCache;
149 DWORD xmit_success;
150 DWORD recv_success;
151 } NetBTAdapter;
152
153 static ULONG gTransportID;
154 static BOOL gEnableDNS;
155 static DWORD gBCastQueries;
156 static DWORD gBCastQueryTimeout;
157 static DWORD gWINSQueries;
158 static DWORD gWINSQueryTimeout;
159 static DWORD gWINSServers[MAX_WINS_SERVERS];
160 static int gNumWINSServers;
161 static char gScopeID[MAX_SCOPE_ID_LEN];
162 static DWORD gCacheTimeout;
163 static struct NBNameCache *gNameCache;
164
165 /* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
166 * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
167 * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes
168 * if p is NULL-terminated. Returns the number of bytes stored in buffer.
169 */
170 static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
171 {
172 int i,len=0;
173
174 if (!p) return 0;
175 if (!buffer) return 0;
176
177 buffer[len++] = NCBNAMSZ * 2;
178 for (i = 0; p[i] && i < NCBNAMSZ; i++)
179 {
180 buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
181 buffer[len++] = (p[i] & 0x0f) + 'A';
182 }
183 while (len < NCBNAMSZ * 2)
184 {
185 buffer[len++] = 'C';
186 buffer[len++] = 'A';
187 }
188 if (*gScopeID)
189 {
190 int scopeIDLen = strlen(gScopeID);
191
192 memcpy(buffer + len, gScopeID, scopeIDLen);
193 len += scopeIDLen;
194 }
195 buffer[len++] = 0; /* add second terminator */
196 return len;
197 }
198
199 /* Creates a NBT name request packet for name in buffer. If broadcast is true,
200 * creates a broadcast request, otherwise creates a unicast request.
201 * Returns the number of bytes stored in buffer.
202 */
203 static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
204 BOOL broadcast, UCHAR *buffer, int len)
205 {
206 int i = 0;
207
208 if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
209
210 NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */
211 if (broadcast)
212 {
213 NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
214 i+=2;
215 }
216 else
217 {
218 NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
219 i+=2;
220 }
221 NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
222 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
223 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
224 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
225
226 i += NetBTNameEncode(name, &buffer[i]);
227
228 NBR_ADDWORD(&buffer[i],qtype); i+=2;
229 NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
230
231 return i;
232 }
233
234 /* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on
235 * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not
236 * NULL.
237 * Returns 0 on success, -1 on failure.
238 */
239 static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
240 WORD qtype, DWORD destAddr, BOOL broadcast)
241 {
242 int ret = 0, on = 1;
243 struct in_addr addr;
244
245 addr.s_addr = destAddr;
246 TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
247
248 if (broadcast)
249 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
250 if(ret == 0)
251 {
252 WSABUF wsaBuf;
253 UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
254 struct sockaddr_in sin;
255
256 memset(&sin, 0, sizeof(sin));
257 sin.sin_addr.s_addr = destAddr;
258 sin.sin_family = AF_INET;
259 sin.sin_port = htons(PORT_NBNS);
260
261 wsaBuf.buf = (CHAR*)buf;
262 wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
263 sizeof(buf));
264 if (wsaBuf.len > 0)
265 {
266 DWORD bytesSent;
267
268 ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
269 (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
270 if (ret < 0 || bytesSent < wsaBuf.len)
271 ret = -1;
272 else
273 ret = 0;
274 }
275 else
276 ret = -1;
277 }
278 return ret;
279 }
280
281 typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
282 WORD answerIndex, PUCHAR rData, WORD rdLength);
283
284 /* Waits on fd until GetTickCount() returns a value greater than or equal to
285 * waitUntil for a name service response. If a name response matching xid
286 * is received, calls answerCallback once for each answer resource record in
287 * the response. (The callback's answerCount will be the total number of
288 * answers to expect, and answerIndex will be the 0-based index that's being
289 * sent this time.) Quits parsing if answerCallback returns FALSE.
290 * Returns NRC_GOODRET on timeout or a valid response received, something else
291 * on error.
292 */
293 static UCHAR NetBTWaitForNameResponse(const NetBTAdapter *adapter, SOCKET fd,
294 DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
295 {
296 BOOL found = FALSE;
297 DWORD now;
298 UCHAR ret = NRC_GOODRET;
299
300 if (!adapter) return NRC_BADDR;
301 if (fd == INVALID_SOCKET) return NRC_BADDR;
302 if (!answerCallback) return NRC_BADDR;
303
304 while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
305 {
306 DWORD msToWait = waitUntil - now;
307 struct fd_set fds;
308 struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
309 int r;
310
311 FD_ZERO(&fds);
312 FD_SET(fd, &fds);
313 r = select(fd + 1, &fds, NULL, NULL, &timeout);
314 if (r < 0)
315 ret = NRC_SYSTEM;
316 else if (r == 1)
317 {
318 /* FIXME: magic #, is this always enough? */
319 UCHAR buffer[256];
320 int fromsize;
321 struct sockaddr_in fromaddr;
322 WORD respXID, flags, queryCount, answerCount;
323 WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
324 DWORD bytesReceived, recvFlags = 0;
325
326 fromsize = sizeof(fromaddr);
327 r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
328 (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
329 if(r < 0)
330 {
331 ret = NRC_SYSTEM;
332 break;
333 }
334
335 if (bytesReceived < NBNS_HEADER_SIZE)
336 continue;
337
338 respXID = NBR_GETWORD(buffer);
339 if (adapter->nameQueryXID != respXID)
340 continue;
341
342 flags = NBR_GETWORD(buffer + 2);
343 queryCount = NBR_GETWORD(buffer + 4);
344 answerCount = NBR_GETWORD(buffer + 6);
345
346 /* a reply shouldn't contain a query, ignore bad packet */
347 if (queryCount > 0)
348 continue;
349
350 if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
351 {
352 if ((flags & NBNS_REPLYCODE) != 0)
353 ret = NRC_NAMERR;
354 else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
355 {
356 PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
357 BOOL shouldContinue = TRUE;
358 WORD answerIndex = 0;
359
360 found = TRUE;
361 /* decode one answer at a time */
362 while (ret == NRC_GOODRET && answerIndex < answerCount &&
363 ptr - buffer < bytesReceived && shouldContinue)
364 {
365 WORD rLen;
366
367 /* scan past name */
368 for (; ptr[0] && ptr - buffer < bytesReceived; )
369 ptr += ptr[0] + 1;
370 ptr++;
371 ptr += 2; /* scan past type */
372 if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
373 && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
374 ptr += sizeof(WORD);
375 else
376 ret = NRC_SYSTEM; /* parse error */
377 ptr += sizeof(DWORD); /* TTL */
378 rLen = NBR_GETWORD(ptr);
379 rLen = min(rLen, bytesReceived - (ptr - buffer));
380 ptr += sizeof(WORD);
381 shouldContinue = answerCallback(data, answerCount,
382 answerIndex, ptr, rLen);
383 ptr += rLen;
384 answerIndex++;
385 }
386 }
387 }
388 }
389 }
390 TRACE("returning 0x%02x\n", ret);
391 return ret;
392 }
393
394 typedef struct _NetBTNameQueryData {
395 NBNameCacheEntry *cacheEntry;
396 UCHAR ret;
397 } NetBTNameQueryData;
398
399 /* Name query callback function for NetBTWaitForNameResponse, creates a cache
400 * entry on the first answer, adds each address as it's called again (as long
401 * as there's space). If there's an error that should be propagated as the
402 * NetBIOS error, modifies queryData's ret member to the proper return code.
403 */
404 static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
405 WORD answerIndex, PUCHAR rData, WORD rLen)
406 {
407 NetBTNameQueryData *queryData = pVoid;
408 BOOL ret;
409
410 if (queryData)
411 {
412 if (queryData->cacheEntry == NULL)
413 {
414 queryData->cacheEntry = HeapAlloc(
415 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
416 (answerCount - 1) * sizeof(DWORD));
417 if (queryData->cacheEntry)
418 queryData->cacheEntry->numAddresses = 0;
419 else
420 queryData->ret = NRC_OSRESNOTAV;
421 }
422 if (rLen == 6 && queryData->cacheEntry &&
423 queryData->cacheEntry->numAddresses < answerCount)
424 {
425 queryData->cacheEntry->addresses[queryData->cacheEntry->
426 numAddresses++] = *(const DWORD *)(rData + 2);
427 ret = queryData->cacheEntry->numAddresses < answerCount;
428 }
429 else
430 ret = FALSE;
431 }
432 else
433 ret = FALSE;
434 return ret;
435 }
436
437 /* Workhorse NetBT name lookup function. Sends a name lookup query for
438 * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
439 * adapter->nameQueryXID as the transaction ID. Waits up to timeout
440 * milliseconds, and retries up to maxQueries times, waiting for a reply.
441 * If a valid response is received, stores the looked up addresses as a
442 * NBNameCacheEntry in *cacheEntry.
443 * Returns NRC_GOODRET on success, though this may not mean the name was
444 * resolved--check whether *cacheEntry is NULL.
445 */
446 static UCHAR NetBTNameWaitLoop(const NetBTAdapter *adapter, SOCKET fd, const NCB *ncb,
447 DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
448 NBNameCacheEntry **cacheEntry)
449 {
450 unsigned int queries;
451 NetBTNameQueryData queryData;
452
453 if (!adapter) return NRC_BADDR;
454 if (fd == INVALID_SOCKET) return NRC_BADDR;
455 if (!ncb) return NRC_BADDR;
456 if (!cacheEntry) return NRC_BADDR;
457
458 queryData.cacheEntry = NULL;
459 queryData.ret = NRC_GOODRET;
460 for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
461 queries++)
462 {
463 if (!NCB_CANCELLED(ncb))
464 {
465 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
466 adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
467
468 if (r == 0)
469 queryData.ret = NetBTWaitForNameResponse(adapter, fd,
470 GetTickCount() + timeout, NetBTFindNameAnswerCallback,
471 &queryData);
472 else
473 queryData.ret = NRC_SYSTEM;
474 }
475 else
476 queryData.ret = NRC_CMDCAN;
477 }
478 if (queryData.cacheEntry)
479 {
480 memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
481 memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
482 }
483 *cacheEntry = queryData.cacheEntry;
484 return queryData.ret;
485 }
486
487 /* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
488 * has not yet been created, creates it, using gCacheTimeout as the cache
489 * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails,
490 * frees cacheEntry.
491 * Returns NRC_GOODRET on success, and something else on failure.
492 */
493 static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
494 NBNameCacheEntry *cacheEntry)
495 {
496 UCHAR ret;
497
498 if (!nameCache) return NRC_BADDR;
499 if (!cacheEntry) return NRC_BADDR;
500
501 if (!*nameCache)
502 *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
503 if (*nameCache)
504 ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
505 ? NRC_GOODRET : NRC_OSRESNOTAV;
506 else
507 {
508 HeapFree(GetProcessHeap(), 0, cacheEntry);
509 ret = NRC_OSRESNOTAV;
510 }
511 return ret;
512 }
513
514 /* Attempts to resolve name using inet_addr(), then gethostbyname() if
515 * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name
516 * can be looked up, returns 0 and stores the looked up addresses as a
517 * NBNameCacheEntry in *cacheEntry.
518 * Returns NRC_GOODRET on success, though this may not mean the name was
519 * resolved--check whether *cacheEntry is NULL. Returns something else on
520 * error.
521 */
522 static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
523 NBNameCacheEntry **cacheEntry)
524 {
525 UCHAR ret = NRC_GOODRET;
526
527 TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
528
529 if (!name) return NRC_BADDR;
530 if (!cacheEntry) return NRC_BADDR;
531
532 if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
533 name[NCBNAMSZ - 1] == 0x20))
534 {
535 CHAR toLookup[NCBNAMSZ];
536 unsigned int i;
537
538 for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
539 toLookup[i] = name[i];
540 toLookup[i] = '\0';
541
542 if (isdigit(toLookup[0]))
543 {
544 unsigned long addr = inet_addr(toLookup);
545
546 if (addr != INADDR_NONE)
547 {
548 *cacheEntry = HeapAlloc(GetProcessHeap(),
549 0, sizeof(NBNameCacheEntry));
550 if (*cacheEntry)
551 {
552 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
553 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
554 (*cacheEntry)->nbname[0] = '*';
555 (*cacheEntry)->numAddresses = 1;
556 (*cacheEntry)->addresses[0] = addr;
557 }
558 else
559 ret = NRC_OSRESNOTAV;
560 }
561 }
562 if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
563 {
564 struct hostent *host;
565
566 if ((host = gethostbyname(toLookup)) != NULL)
567 {
568 for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
569 host->h_addr_list[i]; i++)
570 ;
571 if (host->h_addr_list && host->h_addr_list[0])
572 {
573 *cacheEntry = HeapAlloc(
574 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
575 (i - 1) * sizeof(DWORD));
576 if (*cacheEntry)
577 {
578 memcpy((*cacheEntry)->name, name, NCBNAMSZ);
579 memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
580 (*cacheEntry)->nbname[0] = '*';
581 (*cacheEntry)->numAddresses = i;
582 for (i = 0; i < (*cacheEntry)->numAddresses; i++)
583 (*cacheEntry)->addresses[i] =
584 (DWORD)host->h_addr_list[i];
585 }
586 else
587 ret = NRC_OSRESNOTAV;
588 }
589 }
590 }
591 }
592
593 TRACE("returning 0x%02x\n", ret);
594 return ret;
595 }
596
597 /* Looks up the name in ncb->ncb_callname, first in the name caches (global
598 * and this adapter's), then using gethostbyname(), next by WINS if configured,
599 * and finally using broadcast NetBT name resolution. In NBT parlance, this
600 * makes this an "H-node". Stores an entry in the appropriate name cache for a
601 * found node, and returns it as *cacheEntry.
602 * Assumes data, ncb, and cacheEntry are not NULL.
603 * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
604 * just that all name lookup operations completed successfully--and something
605 * else on failure. *cacheEntry will be NULL if the name was not found.
606 */
607 static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
608 const NBNameCacheEntry **cacheEntry)
609 {
610 UCHAR ret = NRC_GOODRET;
611
612 TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
613
614 if (!cacheEntry) return NRC_BADDR;
615 *cacheEntry = NULL;
616
617 if (!adapter) return NRC_BADDR;
618 if (!ncb) return NRC_BADDR;
619
620 if (ncb->ncb_callname[0] == '*')
621 ret = NRC_NOWILD;
622 else
623 {
624 *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
625 if (!*cacheEntry)
626 *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
627 ncb->ncb_callname);
628 if (!*cacheEntry)
629 {
630 NBNameCacheEntry *newEntry = NULL;
631
632 ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
633 if (ret == NRC_GOODRET && newEntry)
634 {
635 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
636 if (ret != NRC_GOODRET)
637 newEntry = NULL;
638 }
639 else
640 {
641 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
642 0, WSA_FLAG_OVERLAPPED);
643
644 if(fd == INVALID_SOCKET)
645 ret = NRC_OSRESNOTAV;
646 else
647 {
648 int winsNdx;
649
650 adapter->nameQueryXID++;
651 for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
652 && winsNdx < gNumWINSServers; winsNdx++)
653 ret = NetBTNameWaitLoop(adapter, fd, ncb,
654 gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
655 gWINSQueries, &newEntry);
656 if (ret == NRC_GOODRET && newEntry)
657 {
658 ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
659 if (ret != NRC_GOODRET)
660 newEntry = NULL;
661 }
662 if (ret == NRC_GOODRET && *cacheEntry == NULL)
663 {
664 DWORD bcastAddr =
665 adapter->ipr.dwAddr & adapter->ipr.dwMask;
666
667 if (adapter->ipr.dwBCastAddr)
668 bcastAddr |= ~adapter->ipr.dwMask;
669 ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
670 TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
671 if (ret == NRC_GOODRET && newEntry)
672 {
673 ret = NetBTStoreCacheEntry(&adapter->nameCache,
674 newEntry);
675 if (ret != NRC_GOODRET)
676 newEntry = NULL;
677 }
678 }
679 closesocket(fd);
680 }
681 }
682 *cacheEntry = newEntry;
683 }
684 }
685 TRACE("returning 0x%02x\n", ret);
686 return ret;
687 }
688
689 typedef struct _NetBTNodeQueryData
690 {
691 BOOL gotResponse;
692 PADAPTER_STATUS astat;
693 WORD astatLen;
694 } NetBTNodeQueryData;
695
696 /* Callback function for NetBTAstatRemote, parses the rData for the node
697 * status and name list of the remote node. Always returns FALSE, since
698 * there's never more than one answer we care about in a node status response.
699 */
700 static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
701 WORD answerIndex, PUCHAR rData, WORD rLen)
702 {
703 NetBTNodeQueryData *data = pVoid;
704
705 if (data && !data->gotResponse && rData && rLen >= 1)
706 {
707 /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
708 if (rLen >= rData[0] * (NCBNAMSZ + 2))
709 {
710 WORD i;
711 PUCHAR src;
712 PNAME_BUFFER dst;
713
714 data->gotResponse = TRUE;
715 data->astat->name_count = rData[0];
716 for (i = 0, src = rData + 1,
717 dst = (PNAME_BUFFER)((PUCHAR)data->astat +
718 sizeof(ADAPTER_STATUS));
719 i < data->astat->name_count && src - rData < rLen &&
720 (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
721 i++, dst++, src += NCBNAMSZ + 2)
722 {
723 UCHAR flags = *(src + NCBNAMSZ);
724
725 memcpy(dst->name, src, NCBNAMSZ);
726 /* we won't actually see a registering name in the returned
727 * response. It's useful to see if no other flags are set; if
728 * none are, then the name is registered. */
729 dst->name_flags = REGISTERING;
730 if (flags & 0x80)
731 dst->name_flags |= GROUP_NAME;
732 if (flags & 0x10)
733 dst->name_flags |= DEREGISTERED;
734 if (flags & 0x08)
735 dst->name_flags |= DUPLICATE;
736 if (dst->name_flags == REGISTERING)
737 dst->name_flags = REGISTERED;
738 }
739 /* arbitrarily set HW type to Ethernet */
740 data->astat->adapter_type = 0xfe;
741 if (src - rData < rLen)
742 memcpy(data->astat->adapter_address, src,
743 min(rLen - (src - rData), 6));
744 }
745 }
746 return FALSE;
747 }
748
749 /* This uses the WINS timeout and query values, as they're the
750 * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
751 */
752 static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
753 {
754 UCHAR ret = NRC_GOODRET;
755 const NBNameCacheEntry *cacheEntry = NULL;
756
757 TRACE("adapter %p, NCB %p\n", adapter, ncb);
758
759 if (!adapter) return NRC_BADDR;
760 if (!ncb) return NRC_INVADDRESS;
761
762 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
763 if (ret == NRC_GOODRET && cacheEntry)
764 {
765 if (cacheEntry->numAddresses > 0)
766 {
767 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
768 WSA_FLAG_OVERLAPPED);
769
770 if(fd == INVALID_SOCKET)
771 ret = NRC_OSRESNOTAV;
772 else
773 {
774 NetBTNodeQueryData queryData;
775 DWORD queries;
776 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
777
778 adapter->nameQueryXID++;
779 astat->name_count = 0;
780 queryData.gotResponse = FALSE;
781 queryData.astat = astat;
782 queryData.astatLen = ncb->ncb_length;
783 for (queries = 0; !queryData.gotResponse &&
784 queries < gWINSQueries; queries++)
785 {
786 if (!NCB_CANCELLED(ncb))
787 {
788 int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
789 adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
790 cacheEntry->addresses[0], FALSE);
791
792 if (r == 0)
793 ret = NetBTWaitForNameResponse(adapter, fd,
794 GetTickCount() + gWINSQueryTimeout,
795 NetBTNodeStatusAnswerCallback, &queryData);
796 else
797 ret = NRC_SYSTEM;
798 }
799 else
800 ret = NRC_CMDCAN;
801 }
802 closesocket(fd);
803 }
804 }
805 else
806 ret = NRC_CMDTMO;
807 }
808 else if (ret == NRC_CMDCAN)
809 ; /* do nothing, we were cancelled */
810 else
811 ret = NRC_CMDTMO;
812 TRACE("returning 0x%02x\n", ret);
813 return ret;
814 }
815
816 static UCHAR NetBTAstat(void *adapt, PNCB ncb)
817 {
818 NetBTAdapter *adapter = adapt;
819 UCHAR ret;
820
821 TRACE("adapt %p, NCB %p\n", adapt, ncb);
822
823 if (!adapter) return NRC_ENVNOTDEF;
824 if (!ncb) return NRC_INVADDRESS;
825 if (!ncb->ncb_buffer) return NRC_BADDR;
826 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
827
828 if (ncb->ncb_callname[0] == '*')
829 {
830 DWORD physAddrLen;
831 MIB_IFROW ifRow;
832 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
833
834 memset(astat, 0, sizeof(ADAPTER_STATUS));
835 astat->rev_major = 3;
836 ifRow.dwIndex = adapter->ipr.dwIndex;
837 if (GetIfEntry(&ifRow) != NO_ERROR)
838 ret = NRC_BRIDGE;
839 else
840 {
841 physAddrLen = min(ifRow.dwPhysAddrLen, 6);
842 if (physAddrLen > 0)
843 memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
844 /* doubt anyone cares, but why not.. */
845 if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
846 astat->adapter_type = 0xff;
847 else
848 astat->adapter_type = 0xfe; /* for Ethernet */
849 astat->max_sess_pkt_size = 0xffff;
850 astat->xmit_success = adapter->xmit_success;
851 astat->recv_success = adapter->recv_success;
852 ret = NRC_GOODRET;
853 }
854 }
855 else
856 ret = NetBTAstatRemote(adapter, ncb);
857 TRACE("returning 0x%02x\n", ret);
858 return ret;
859 }
860
861 static UCHAR NetBTFindName(void *adapt, PNCB ncb)
862 {
863 NetBTAdapter *adapter = adapt;
864 UCHAR ret;
865 const NBNameCacheEntry *cacheEntry = NULL;
866 PFIND_NAME_HEADER foundName;
867
868 TRACE("adapt %p, NCB %p\n", adapt, ncb);
869
870 if (!adapter) return NRC_ENVNOTDEF;
871 if (!ncb) return NRC_INVADDRESS;
872 if (!ncb->ncb_buffer) return NRC_BADDR;
873 if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
874
875 foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
876 memset(foundName, 0, sizeof(FIND_NAME_HEADER));
877
878 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
879 if (ret == NRC_GOODRET)
880 {
881 if (cacheEntry)
882 {
883 DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
884 sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
885 DWORD ndx;
886
887 for (ndx = 0; ndx < spaceFor; ndx++)
888 {
889 PFIND_NAME_BUFFER findNameBuffer;
890
891 findNameBuffer =
892 (PFIND_NAME_BUFFER)((PUCHAR)foundName +
893 sizeof(FIND_NAME_HEADER) + foundName->node_count *
894 sizeof(FIND_NAME_BUFFER));
895 memset(findNameBuffer->destination_addr, 0, 2);
896 memcpy(findNameBuffer->destination_addr + 2,
897 &adapter->ipr.dwAddr, sizeof(DWORD));
898 memset(findNameBuffer->source_addr, 0, 2);
899 memcpy(findNameBuffer->source_addr + 2,
900 &cacheEntry->addresses[ndx], sizeof(DWORD));
901 foundName->node_count++;
902 }
903 if (spaceFor < cacheEntry->numAddresses)
904 ret = NRC_BUFLEN;
905 }
906 else
907 ret = NRC_CMDTMO;
908 }
909 TRACE("returning 0x%02x\n", ret);
910 return ret;
911 }
912
913 static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
914 const UCHAR *callingName)
915 {
916 UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
917 int r;
918 unsigned int len = 0;
919 DWORD bytesSent, bytesReceived, recvFlags = 0;
920 WSABUF wsaBuf;
921
922 buffer[0] = NBSS_REQ;
923 buffer[1] = 0;
924
925 len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
926 len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
927
928 NBR_ADDWORD(&buffer[2], len);
929
930 wsaBuf.len = len + NBSS_HDRSIZE;
931 wsaBuf.buf = (char*)buffer;
932
933 r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
934 if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
935 {
936 ERR("send failed\n");
937 return NRC_SABORT;
938 }
939
940 /* I've already set the recv timeout on this socket (if it supports it), so
941 * just block. Hopefully we'll always receive the session acknowledgement
942 * within one timeout.
943 */
944 wsaBuf.len = NBSS_HDRSIZE + 1;
945 r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
946 if (r < 0 || bytesReceived < NBSS_HDRSIZE)
947 ret = NRC_SABORT;
948 else if (buffer[0] == NBSS_NACK)
949 {
950 if (r == NBSS_HDRSIZE + 1)
951 {
952 switch (buffer[NBSS_HDRSIZE])
953 {
954 case NBSS_ERR_INSUFFICIENT_RESOURCES:
955 ret = NRC_REMTFUL;
956 break;
957 default:
958 ret = NRC_NOCALL;
959 }
960 }
961 else
962 ret = NRC_NOCALL;
963 }
964 else if (buffer[0] == NBSS_RETARGET)
965 {
966 FIXME("Got a session retarget, can't deal\n");
967 ret = NRC_NOCALL;
968 }
969 else if (buffer[0] == NBSS_ACK)
970 ret = NRC_GOODRET;
971 else
972 ret = NRC_SYSTEM;
973
974 TRACE("returning 0x%02x\n", ret);
975 return ret;
976 }
977
978 static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
979 {
980 NetBTAdapter *adapter = adapt;
981 UCHAR ret;
982 const NBNameCacheEntry *cacheEntry = NULL;
983
984 TRACE("adapt %p, ncb %p\n", adapt, ncb);
985
986 if (!adapter) return NRC_ENVNOTDEF;
987 if (!ncb) return NRC_INVADDRESS;
988 if (!sess) return NRC_BADDR;
989
990 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
991 if (ret == NRC_GOODRET)
992 {
993 if (cacheEntry && cacheEntry->numAddresses > 0)
994 {
995 SOCKET fd;
996
997 fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
998 WSA_FLAG_OVERLAPPED);
999 if (fd != INVALID_SOCKET)
1000 {
1001 DWORD timeout;
1002 struct sockaddr_in sin;
1003
1004 if (ncb->ncb_rto > 0)
1005 {
1006 timeout = ncb->ncb_rto * 500;
1007 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
1008 sizeof(timeout));
1009 }
1010 if (ncb->ncb_rto > 0)
1011 {
1012 timeout = ncb->ncb_sto * 500;
1013 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
1014 sizeof(timeout));
1015 }
1016
1017 memset(&sin, 0, sizeof(sin));
1018 memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
1019 sizeof(sin.sin_addr));
1020 sin.sin_family = AF_INET;
1021 sin.sin_port = htons(PORT_NBSS);
1022 /* FIXME: use nonblocking mode for the socket, check the
1023 * cancel flag periodically
1024 */
1025 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
1026 == SOCKET_ERROR)
1027 ret = NRC_CMDTMO;
1028 else
1029 {
1030 static const UCHAR fakedCalledName[] = "*SMBSERVER";
1031 const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
1032 ? fakedCalledName : cacheEntry->nbname;
1033
1034 ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
1035 if (ret != NRC_GOODRET && calledParty[0] == '*')
1036 {
1037 FIXME("NBT session to \"*SMBSERVER\" refused,\n");
1038 FIXME("should try finding name using ASTAT\n");
1039 }
1040 }
1041 if (ret != NRC_GOODRET)
1042 closesocket(fd);
1043 else
1044 {
1045 NetBTSession *session = HeapAlloc(
1046 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
1047
1048 if (session)
1049 {
1050 session->fd = fd;
1051 InitializeCriticalSection(&session->cs);
1052 session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBTSession.cs");
1053 *sess = session;
1054 }
1055 else
1056 {
1057 ret = NRC_OSRESNOTAV;
1058 closesocket(fd);
1059 }
1060 }
1061 }
1062 else
1063 ret = NRC_OSRESNOTAV;
1064 }
1065 else
1066 ret = NRC_NAMERR;
1067 }
1068 TRACE("returning 0x%02x\n", ret);
1069 return ret;
1070 }
1071
1072 /* Notice that I don't protect against multiple thread access to NetBTSend.
1073 * This is because I don't update any data in the adapter, and I only make a
1074 * single call to WSASend, which I assume to act atomically (not interleaving
1075 * data from other threads).
1076 * I don't lock, because I only depend on the fd being valid, and this won't be
1077 * true until a session setup is completed.
1078 */
1079 static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
1080 {
1081 NetBTAdapter *adapter = adapt;
1082 NetBTSession *session = sess;
1083 UCHAR buffer[NBSS_HDRSIZE], ret;
1084 int r;
1085 WSABUF wsaBufs[2];
1086 DWORD bytesSent;
1087
1088 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1089
1090 if (!adapter) return NRC_ENVNOTDEF;
1091 if (!ncb) return NRC_INVADDRESS;
1092 if (!ncb->ncb_buffer) return NRC_BADDR;
1093 if (!session) return NRC_SNUMOUT;
1094 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1095
1096 buffer[0] = NBSS_MSG;
1097 buffer[1] = 0;
1098 NBR_ADDWORD(&buffer[2], ncb->ncb_length);
1099
1100 wsaBufs[0].len = NBSS_HDRSIZE;
1101 wsaBufs[0].buf = (char*)buffer;
1102 wsaBufs[1].len = ncb->ncb_length;
1103 wsaBufs[1].buf = (char*)ncb->ncb_buffer;
1104
1105 r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
1106 &bytesSent, 0, NULL, NULL);
1107 if (r == SOCKET_ERROR)
1108 {
1109 NetBIOSHangupSession(ncb);
1110 ret = NRC_SABORT;
1111 }
1112 else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
1113 {
1114 FIXME("Only sent %d bytes (of %d), hanging up session\n", bytesSent,
1115 NBSS_HDRSIZE + ncb->ncb_length);
1116 NetBIOSHangupSession(ncb);
1117 ret = NRC_SABORT;
1118 }
1119 else
1120 {
1121 ret = NRC_GOODRET;
1122 adapter->xmit_success++;
1123 }
1124 TRACE("returning 0x%02x\n", ret);
1125 return ret;
1126 }
1127
1128 static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
1129 {
1130 NetBTAdapter *adapter = adapt;
1131 NetBTSession *session = sess;
1132 UCHAR buffer[NBSS_HDRSIZE], ret;
1133 int r;
1134 WSABUF wsaBufs[2];
1135 DWORD bufferCount, bytesReceived, flags;
1136
1137 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
1138
1139 if (!adapter) return NRC_ENVNOTDEF;
1140 if (!ncb) return NRC_BADDR;
1141 if (!ncb->ncb_buffer) return NRC_BADDR;
1142 if (!session) return NRC_SNUMOUT;
1143 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
1144
1145 EnterCriticalSection(&session->cs);
1146 bufferCount = 0;
1147 if (session->bytesPending == 0)
1148 {
1149 bufferCount++;
1150 wsaBufs[0].len = NBSS_HDRSIZE;
1151 wsaBufs[0].buf = (char*)buffer;
1152 }
1153 wsaBufs[bufferCount].len = ncb->ncb_length;
1154 wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
1155 bufferCount++;
1156
1157 flags = 0;
1158 /* FIXME: should poll a bit so I can check the cancel flag */
1159 r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
1160 NULL, NULL);
1161 if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
1162 {
1163 LeaveCriticalSection(&session->cs);
1164 ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
1165 NetBIOSHangupSession(ncb);
1166 ret = NRC_SABORT;
1167 }
1168 else if (NCB_CANCELLED(ncb))
1169 {
1170 LeaveCriticalSection(&session->cs);
1171 ret = NRC_CMDCAN;
1172 }
1173 else
1174 {
1175 if (bufferCount == 2)
1176 {
1177 if (buffer[0] == NBSS_KEEPALIVE)
1178 {
1179 LeaveCriticalSection(&session->cs);
1180 FIXME("Oops, received a session keepalive and lost my place\n");
1181 /* need to read another session header until we get a session
1182 * message header. */
1183 NetBIOSHangupSession(ncb);
1184 ret = NRC_SABORT;
1185 goto error;
1186 }
1187 else if (buffer[0] != NBSS_MSG)
1188 {
1189 LeaveCriticalSection(&session->cs);
1190 FIXME("Received unexpected session msg type %d\n", buffer[0]);
1191 NetBIOSHangupSession(ncb);
1192 ret = NRC_SABORT;
1193 goto error;
1194 }
1195 else
1196 {
1197 if (buffer[1] & NBSS_EXTENSION)
1198 {
1199 LeaveCriticalSection(&session->cs);
1200 FIXME("Received a message that's too long for my taste\n");
1201 NetBIOSHangupSession(ncb);
1202 ret = NRC_SABORT;
1203 goto error;
1204 }
1205 else
1206 {
1207 session->bytesPending = NBSS_HDRSIZE
1208 + NBR_GETWORD(&buffer[2]) - bytesReceived;
1209 ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
1210 LeaveCriticalSection(&session->cs);
1211 }
1212 }
1213 }
1214 else
1215 {
1216 if (bytesReceived < session->bytesPending)
1217 session->bytesPending -= bytesReceived;
1218 else
1219 session->bytesPending = 0;
1220 LeaveCriticalSection(&session->cs);
1221 ncb->ncb_length = bytesReceived;
1222 }
1223 if (session->bytesPending > 0)
1224 ret = NRC_INCOMP;
1225 else
1226 {
1227 ret = NRC_GOODRET;
1228 adapter->recv_success++;
1229 }
1230 }
1231 error:
1232 TRACE("returning 0x%02x\n", ret);
1233 return ret;
1234 }
1235
1236 static UCHAR NetBTHangup(void *adapt, void *sess)
1237 {
1238 NetBTSession *session = sess;
1239
1240 TRACE("adapt %p, session %p\n", adapt, session);
1241
1242 if (!session) return NRC_SNUMOUT;
1243
1244 /* I don't lock the session, because NetBTRecv knows not to decrement
1245 * past 0, so if a receive completes after this it should still deal.
1246 */
1247 closesocket(session->fd);
1248 session->fd = INVALID_SOCKET;
1249 session->bytesPending = 0;
1250 session->cs.DebugInfo->Spare[0] = 0;
1251 DeleteCriticalSection(&session->cs);
1252 HeapFree(GetProcessHeap(), 0, session);
1253
1254 return NRC_GOODRET;
1255 }
1256
1257 static void NetBTCleanupAdapter(void *adapt)
1258 {
1259 TRACE("adapt %p\n", adapt);
1260 if (adapt)
1261 {
1262 NetBTAdapter *adapter = adapt;
1263
1264 if (adapter->nameCache)
1265 NBNameCacheDestroy(adapter->nameCache);
1266 HeapFree(GetProcessHeap(), 0, adapt);
1267 }
1268 }
1269
1270 static void NetBTCleanup(void)
1271 {
1272 TRACE("\n");
1273 if (gNameCache)
1274 {
1275 NBNameCacheDestroy(gNameCache);
1276 gNameCache = NULL;
1277 }
1278 }
1279
1280 static UCHAR NetBTRegisterAdapter(const MIB_IPADDRROW *ipRow)
1281 {
1282 UCHAR ret;
1283 NetBTAdapter *adapter;
1284
1285 if (!ipRow) return NRC_BADDR;
1286
1287 adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
1288 if (adapter)
1289 {
1290 adapter->ipr = *ipRow;
1291 if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
1292 {
1293 NetBTCleanupAdapter(adapter);
1294 ret = NRC_SYSTEM;
1295 }
1296 else
1297 ret = NRC_GOODRET;
1298 }
1299 else
1300 ret = NRC_OSRESNOTAV;
1301 return ret;
1302 }
1303
1304 /* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to
1305 * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
1306 * NetBIOS adapter table. For each callback, checks if the passed-in adapt
1307 * has an entry in the table; if so, this adapter was enumerated previously,
1308 * and it's enabled. As a flag, the table's dwAddr entry is changed to
1309 * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
1310 * The NetBTEnum function will add any remaining adapters from the
1311 * MIB_IPADDRTABLE to the NetBIOS adapter table.
1312 */
1313 static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
1314 ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
1315 {
1316 BOOL ret;
1317 PMIB_IPADDRTABLE table = closure;
1318
1319 if (table && data)
1320 {
1321 DWORD ndx;
1322
1323 ret = FALSE;
1324 for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
1325 {
1326 const NetBTAdapter *adapter = data->data;
1327
1328 if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
1329 {
1330 NetBIOSEnableAdapter(data->lana);
1331 table->table[ndx].dwAddr = INADDR_LOOPBACK;
1332 ret = TRUE;
1333 }
1334 }
1335 }
1336 else
1337 ret = FALSE;
1338 return ret;
1339 }
1340
1341 /* Enumerates adapters by:
1342 * - retrieving the IP address table for the local machine
1343 * - eliminating loopback addresses from the table
1344 * - eliminating redundant addresses, that is, multiple addresses on the same
1345 * subnet
1346 * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
1347 * data. The callback reenables each adapter that's already in the NetBIOS
1348 * table. After NetBIOSEnumAdapters returns, this function adds any remaining
1349 * adapters to the NetBIOS table.
1350 */
1351 static UCHAR NetBTEnum(void)
1352 {
1353 UCHAR ret;
1354 DWORD size = 0;
1355
1356 TRACE("\n");
1357
1358 if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
1359 {
1360 PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
1361 DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
1362 sizeof(MIB_IPADDRROW) + 1;
1363
1364 ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
1365 if (ipAddrs)
1366 coalesceTable = HeapAlloc(GetProcessHeap(),
1367 HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
1368 (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
1369 if (ipAddrs && coalesceTable)
1370 {
1371 if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
1372 {
1373 DWORD ndx;
1374
1375 for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
1376 {
1377 if ((ipAddrs->table[ndx].dwAddr &
1378 ipAddrs->table[ndx].dwMask) !=
1379 htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
1380 {
1381 BOOL newNetwork = TRUE;
1382 DWORD innerIndex;
1383
1384 /* make sure we don't have more than one entry
1385 * for a subnet */
1386 for (innerIndex = 0; newNetwork &&
1387 innerIndex < coalesceTable->dwNumEntries; innerIndex++)
1388 if ((ipAddrs->table[ndx].dwAddr &
1389 ipAddrs->table[ndx].dwMask) ==
1390 (coalesceTable->table[innerIndex].dwAddr
1391 & coalesceTable->table[innerIndex].dwMask))
1392 newNetwork = FALSE;
1393
1394 if (newNetwork)
1395 memcpy(&coalesceTable->table[
1396 coalesceTable->dwNumEntries++],
1397 &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
1398 }
1399 }
1400
1401 NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
1402 coalesceTable);
1403 ret = NRC_GOODRET;
1404 for (ndx = 0; ret == NRC_GOODRET &&
1405 ndx < coalesceTable->dwNumEntries; ndx++)
1406 if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
1407 ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
1408 }
1409 else
1410 ret = NRC_SYSTEM;
1411 HeapFree(GetProcessHeap(), 0, ipAddrs);
1412 HeapFree(GetProcessHeap(), 0, coalesceTable);
1413 }
1414 else
1415 ret = NRC_OSRESNOTAV;
1416 }
1417 else
1418 ret = NRC_SYSTEM;
1419 TRACE("returning 0x%02x\n", ret);
1420 return ret;
1421 }
1422
1423 static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r',
1424 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
1425 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
1426 static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u',
1427 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
1428 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
1429 'e','r','s','\0' };
1430 static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' };
1431 static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m',
1432 'e','Q','u','e','r','y','C','o','u','n','t','\0' };
1433 static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m',
1434 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1435 static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v',
1436 'Q','u','e','r','y','C','o','u','n','t','\0' };
1437 static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v',
1438 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1439 static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' };
1440 static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o',
1441 'u','t','\0' };
1442 static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\',
1443 'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
1444
1445 /* Initializes global variables and registers the NetBT transport */
1446 void NetBTInit(void)
1447 {
1448 HKEY hKey;
1449 NetBIOSTransport transport;
1450 LONG ret;
1451
1452 TRACE("\n");
1453
1454 gEnableDNS = TRUE;
1455 gBCastQueries = BCAST_QUERIES;
1456 gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
1457 gWINSQueries = WINS_QUERIES;
1458 gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
1459 gNumWINSServers = 0;
1460 memset(gWINSServers, 0, sizeof(gWINSServers));
1461 gScopeID[0] = '\0';
1462 gCacheTimeout = CACHE_TIMEOUT;
1463
1464 /* Try to open the Win9x NetBT configuration key */
1465 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey);
1466 /* If that fails, try the WinNT NetBT configuration key */
1467 if (ret != ERROR_SUCCESS)
1468 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ,
1469 &hKey);
1470 if (ret == ERROR_SUCCESS)
1471 {
1472 DWORD dword, size;
1473
1474 size = sizeof(dword);
1475 if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL,
1476 (LPBYTE)&dword, &size) == ERROR_SUCCESS)
1477 gEnableDNS = dword;
1478 size = sizeof(dword);
1479 if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL,
1480 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1481 && dword <= MAX_QUERIES)
1482 gBCastQueries = dword;
1483 size = sizeof(dword);
1484 if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL,
1485 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
1486 gBCastQueryTimeout = dword;
1487 size = sizeof(dword);
1488 if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL,
1489 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
1490 && dword <= MAX_QUERIES)
1491 gWINSQueries = dword;
1492 size = sizeof(dword);
1493 if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL,
1494 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT)
1495 gWINSQueryTimeout = dword;
1496 size = sizeof(gScopeID) - 1;
1497 if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size)
1498 == ERROR_SUCCESS)
1499 {
1500 /* convert into L2-encoded version, suitable for use by
1501 NetBTNameEncode */
1502 char *ptr, *lenPtr;
1503
1504 for (ptr = gScopeID + 1; ptr - gScopeID < sizeof(gScopeID) && *ptr; )
1505 {
1506 for (lenPtr = ptr - 1, *lenPtr = 0;
1507 ptr - gScopeID < sizeof(gScopeID) && *ptr && *ptr != '.';
1508 ptr++)
1509 *lenPtr += 1;
1510 ptr++;
1511 }
1512 }
1513 if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL,
1514 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
1515 gCacheTimeout = dword;
1516 RegCloseKey(hKey);
1517 }
1518 /* WINE-specific NetBT registry settings. Because our adapter naming is
1519 * different than MS', we can't do per-adapter WINS configuration in the
1520 * same place. Just do a global WINS configuration instead.
1521 */
1522 /* @@ Wine registry key: HKCU\Software\Wine\Network */
1523 if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS)
1524 {
1525 static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
1526 char nsString[16];
1527 DWORD size, ndx;
1528
1529 for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
1530 ndx++)
1531 {
1532 size = sizeof(nsString) / sizeof(char);
1533 if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
1534 (LPBYTE)nsString, &size) == ERROR_SUCCESS)
1535 {
1536 unsigned long addr = inet_addr(nsString);
1537
1538 if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
1539 gWINSServers[gNumWINSServers++] = addr;
1540 }
1541 }
1542 RegCloseKey(hKey);
1543 }
1544
1545 transport.enumerate = NetBTEnum;
1546 transport.astat = NetBTAstat;
1547 transport.findName = NetBTFindName;
1548 transport.call = NetBTCall;
1549 transport.send = NetBTSend;
1550 transport.recv = NetBTRecv;
1551 transport.hangup = NetBTHangup;
1552 transport.cleanupAdapter = NetBTCleanupAdapter;
1553 transport.cleanup = NetBTCleanup;
1554 memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
1555 NetBIOSRegisterTransport(gTransportID, &transport);
1556 }