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