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