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 } 1547