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