Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygennbt.c
Go to the documentation of this file.
00001 /* Copyright (c) 2003 Juan Lang 00002 * 00003 * This library is free software; you can redistribute it and/or 00004 * modify it under the terms of the GNU Lesser General Public 00005 * License as published by the Free Software Foundation; either 00006 * version 2.1 of the License, or (at your option) any later version. 00007 * 00008 * This library is distributed in the hope that it will be useful, 00009 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 * Lesser General Public License for more details. 00012 * 00013 * You should have received a copy of the GNU Lesser General Public 00014 * License along with this library; if not, write to the Free Software 00015 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00016 * 00017 * I am heavily indebted to Chris Hertel's excellent Implementing CIFS, 00018 * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT. 00019 * I also stole from Mike McCormack's smb.c and netapi32.c, although little of 00020 * that code remains. 00021 * Lack of understanding and bugs are my fault. 00022 * 00023 * FIXME: 00024 * - Of the NetBIOS session functions, only client functions are supported, and 00025 * it's likely they'll be the only functions supported. NBT requires session 00026 * servers to listen on TCP/139. This requires root privilege, and Samba is 00027 * likely to be listening here already. This further restricts NetBIOS 00028 * applications, both explicit users and implicit ones: CreateNamedPipe 00029 * won't actually create a listening pipe, for example, so applications can't 00030 * act as RPC servers using a named pipe protocol binding, DCOM won't be able 00031 * to support callbacks or servers over the named pipe protocol, etc. 00032 * 00033 * - Datagram support is omitted for the same reason. To send a NetBIOS 00034 * datagram, you must include the NetBIOS name by which your application is 00035 * known. This requires you to have registered the name previously, and be 00036 * able to act as a NetBIOS datagram server (listening on UDP/138). 00037 * 00038 * - Name registration functions are omitted for the same reason--registering a 00039 * name requires you to be able to defend it, and this means listening on 00040 * UDP/137. 00041 * Win98 requires you either use your computer's NetBIOS name (with the NULL 00042 * suffix byte) as the calling name when creating a session, or to register 00043 * a new name before creating one: it disallows '*' as the calling name. 00044 * Win2K initially starts with an empty name table, and doesn't allow you to 00045 * use the machine's NetBIOS name (with the NULL suffix byte) as the calling 00046 * name. Although it allows sessions to be created with '*' as the calling 00047 * name, doing so results in timeouts for all receives, because the 00048 * application never gets them. 00049 * So, a well-behaved NetBIOS application will typically want to register a 00050 * name. I should probably support a do-nothing name list that allows 00051 * NCBADDNAME to add to it, but doesn't actually register the name, or does 00052 * attempt to register it without being able to defend it. 00053 * 00054 * - Name lookups may not behave quite as you'd expect/like if you have 00055 * multiple LANAs. If a name is resolvable through DNS, or if you're using 00056 * WINS, it'll resolve on _any_ LANA. So, a Call will succeed on any LANA as 00057 * well. 00058 * I'm not sure how Windows behaves in this case. I could try to force 00059 * lookups to the correct adapter by using one of the GetPreferred* 00060 * functions, but with the possibility of multiple adapters in the same 00061 * same subnet, there's no guarantee that what IpHlpApi thinks is the 00062 * preferred adapter will actually be a LANA. (It's highly probable because 00063 * this is an unusual configuration, but not guaranteed.) 00064 * 00065 * See also other FIXMEs in the code. 00066 */ 00067 00068 #include "config.h" 00069 #include <stdarg.h> 00070 00071 #include "winsock2.h" 00072 #include "windef.h" 00073 #include "winbase.h" 00074 #include "wine/debug.h" 00075 #include "winreg.h" 00076 #include "iphlpapi.h" 00077 00078 #include "netbios.h" 00079 #include "nbnamecache.h" 00080 00081 WINE_DEFAULT_DEBUG_CHANNEL(netbios); 00082 00083 #define PORT_NBNS 137 00084 #define PORT_NBDG 138 00085 #define PORT_NBSS 139 00086 00087 #ifndef INADDR_NONE 00088 #define INADDR_NONE ~0UL 00089 #endif 00090 00091 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word) 00092 #define NBR_GETWORD(p) ntohs(*(WORD *)(p)) 00093 00094 #define MIN_QUERIES 1 00095 #define MAX_QUERIES 0xffff 00096 #define MIN_QUERY_TIMEOUT 100 00097 #define MAX_QUERY_TIMEOUT 0xffffffff 00098 #define BCAST_QUERIES 3 00099 #define BCAST_QUERY_TIMEOUT 750 00100 #define WINS_QUERIES 3 00101 #define WINS_QUERY_TIMEOUT 750 00102 #define MAX_WINS_SERVERS 2 00103 #define MIN_CACHE_TIMEOUT 60000 00104 #define CACHE_TIMEOUT 360000 00105 00106 #define MAX_NBT_NAME_SZ 255 00107 #define SIMPLE_NAME_QUERY_PKT_SIZE 16 + MAX_NBT_NAME_SZ 00108 00109 #define NBNS_TYPE_NB 0x0020 00110 #define NBNS_TYPE_NBSTAT 0x0021 00111 #define NBNS_CLASS_INTERNET 0x00001 00112 #define NBNS_HEADER_SIZE (sizeof(WORD) * 6) 00113 #define NBNS_RESPONSE_AND_OPCODE 0xf800 00114 #define NBNS_RESPONSE_AND_QUERY 0x8000 00115 #define NBNS_REPLYCODE 0x0f 00116 00117 #define NBSS_HDRSIZE 4 00118 00119 #define NBSS_MSG 0x00 00120 #define NBSS_REQ 0x81 00121 #define NBSS_ACK 0x82 00122 #define NBSS_NACK 0x83 00123 #define NBSS_RETARGET 0x84 00124 #define NBSS_KEEPALIVE 0x85 00125 00126 #define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80 00127 #define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81 00128 #define NBSS_ERR_BAD_NAME 0x82 00129 #define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83 00130 00131 #define NBSS_EXTENSION 0x01 00132 00133 typedef struct _NetBTSession 00134 { 00135 CRITICAL_SECTION cs; 00136 SOCKET fd; 00137 DWORD bytesPending; 00138 } NetBTSession; 00139 00140 typedef struct _NetBTAdapter 00141 { 00142 MIB_IPADDRROW ipr; 00143 WORD nameQueryXID; 00144 struct NBNameCache *nameCache; 00145 DWORD xmit_success; 00146 DWORD recv_success; 00147 } NetBTAdapter; 00148 00149 static ULONG gTransportID; 00150 static BOOL gEnableDNS; 00151 static DWORD gBCastQueries; 00152 static DWORD gBCastQueryTimeout; 00153 static DWORD gWINSQueries; 00154 static DWORD gWINSQueryTimeout; 00155 static DWORD gWINSServers[MAX_WINS_SERVERS]; 00156 static int gNumWINSServers; 00157 static char gScopeID[MAX_SCOPE_ID_LEN]; 00158 static DWORD gCacheTimeout; 00159 static struct NBNameCache *gNameCache; 00160 00161 /* Converts from a NetBIOS name into a Second Level Encoding-formatted name. 00162 * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ 00163 * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes 00164 * if p is NULL-terminated. Returns the number of bytes stored in buffer. 00165 */ 00166 static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer) 00167 { 00168 int i,len=0; 00169 00170 if (!p) return 0; 00171 if (!buffer) return 0; 00172 00173 buffer[len++] = NCBNAMSZ * 2; 00174 for (i = 0; p[i] && i < NCBNAMSZ; i++) 00175 { 00176 buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A'; 00177 buffer[len++] = (p[i] & 0x0f) + 'A'; 00178 } 00179 while (len < NCBNAMSZ * 2) 00180 { 00181 buffer[len++] = 'C'; 00182 buffer[len++] = 'A'; 00183 } 00184 if (*gScopeID) 00185 { 00186 int scopeIDLen = strlen(gScopeID); 00187 00188 memcpy(buffer + len, gScopeID, scopeIDLen); 00189 len += scopeIDLen; 00190 } 00191 buffer[len++] = 0; /* add second terminator */ 00192 return len; 00193 } 00194 00195 /* Creates a NBT name request packet for name in buffer. If broadcast is true, 00196 * creates a broadcast request, otherwise creates a unicast request. 00197 * Returns the number of bytes stored in buffer. 00198 */ 00199 static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype, 00200 BOOL broadcast, UCHAR *buffer, int len) 00201 { 00202 int i = 0; 00203 00204 if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0; 00205 00206 NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */ 00207 if (broadcast) 00208 { 00209 NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */ 00210 i+=2; 00211 } 00212 else 00213 { 00214 NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */ 00215 i+=2; 00216 } 00217 NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */ 00218 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */ 00219 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */ 00220 NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */ 00221 00222 i += NetBTNameEncode(name, &buffer[i]); 00223 00224 NBR_ADDWORD(&buffer[i],qtype); i+=2; 00225 NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2; 00226 00227 return i; 00228 } 00229 00230 /* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on 00231 * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not 00232 * NULL. 00233 * Returns 0 on success, -1 on failure. 00234 */ 00235 static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid, 00236 WORD qtype, DWORD destAddr, BOOL broadcast) 00237 { 00238 int ret = 0, on = 1; 00239 struct in_addr addr; 00240 00241 addr.s_addr = destAddr; 00242 TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr)); 00243 00244 if (broadcast) 00245 ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on)); 00246 if(ret == 0) 00247 { 00248 WSABUF wsaBuf; 00249 UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE]; 00250 struct sockaddr_in sin; 00251 00252 memset(&sin, 0, sizeof(sin)); 00253 sin.sin_addr.s_addr = destAddr; 00254 sin.sin_family = AF_INET; 00255 sin.sin_port = htons(PORT_NBNS); 00256 00257 wsaBuf.buf = (CHAR*)buf; 00258 wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf, 00259 sizeof(buf)); 00260 if (wsaBuf.len > 0) 00261 { 00262 DWORD bytesSent; 00263 00264 ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0, 00265 (struct sockaddr*)&sin, sizeof(sin), NULL, NULL); 00266 if (ret < 0 || bytesSent < wsaBuf.len) 00267 ret = -1; 00268 else 00269 ret = 0; 00270 } 00271 else 00272 ret = -1; 00273 } 00274 return ret; 00275 } 00276 00277 typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount, 00278 WORD answerIndex, PUCHAR rData, WORD rdLength); 00279 00280 /* Waits on fd until GetTickCount() returns a value greater than or equal to 00281 * waitUntil for a name service response. If a name response matching xid 00282 * is received, calls answerCallback once for each answer resource record in 00283 * the response. (The callback's answerCount will be the total number of 00284 * answers to expect, and answerIndex will be the 0-based index that's being 00285 * sent this time.) Quits parsing if answerCallback returns FALSE. 00286 * Returns NRC_GOODRET on timeout or a valid response received, something else 00287 * on error. 00288 */ 00289 static UCHAR NetBTWaitForNameResponse(const NetBTAdapter *adapter, SOCKET fd, 00290 DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data) 00291 { 00292 BOOL found = FALSE; 00293 DWORD now; 00294 UCHAR ret = NRC_GOODRET; 00295 00296 if (!adapter) return NRC_BADDR; 00297 if (fd == INVALID_SOCKET) return NRC_BADDR; 00298 if (!answerCallback) return NRC_BADDR; 00299 00300 while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil) 00301 { 00302 DWORD msToWait = waitUntil - now; 00303 struct fd_set fds; 00304 struct timeval timeout = { msToWait / 1000, msToWait % 1000 }; 00305 int r; 00306 00307 FD_ZERO(&fds); 00308 FD_SET(fd, &fds); 00309 r = select(fd + 1, &fds, NULL, NULL, &timeout); 00310 if (r < 0) 00311 ret = NRC_SYSTEM; 00312 else if (r == 1) 00313 { 00314 /* FIXME: magic #, is this always enough? */ 00315 UCHAR buffer[256]; 00316 int fromsize; 00317 struct sockaddr_in fromaddr; 00318 WORD respXID, flags, queryCount, answerCount; 00319 WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer }; 00320 DWORD bytesReceived, recvFlags = 0; 00321 00322 fromsize = sizeof(fromaddr); 00323 r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, 00324 (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL); 00325 if(r < 0) 00326 { 00327 ret = NRC_SYSTEM; 00328 break; 00329 } 00330 00331 if (bytesReceived < NBNS_HEADER_SIZE) 00332 continue; 00333 00334 respXID = NBR_GETWORD(buffer); 00335 if (adapter->nameQueryXID != respXID) 00336 continue; 00337 00338 flags = NBR_GETWORD(buffer + 2); 00339 queryCount = NBR_GETWORD(buffer + 4); 00340 answerCount = NBR_GETWORD(buffer + 6); 00341 00342 /* a reply shouldn't contain a query, ignore bad packet */ 00343 if (queryCount > 0) 00344 continue; 00345 00346 if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY) 00347 { 00348 if ((flags & NBNS_REPLYCODE) != 0) 00349 ret = NRC_NAMERR; 00350 else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0) 00351 { 00352 PUCHAR ptr = buffer + NBNS_HEADER_SIZE; 00353 BOOL shouldContinue = TRUE; 00354 WORD answerIndex = 0; 00355 00356 found = TRUE; 00357 /* decode one answer at a time */ 00358 while (ret == NRC_GOODRET && answerIndex < answerCount && 00359 ptr - buffer < bytesReceived && shouldContinue) 00360 { 00361 WORD rLen; 00362 00363 /* scan past name */ 00364 for (; ptr[0] && ptr - buffer < bytesReceived; ) 00365 ptr += ptr[0] + 1; 00366 ptr++; 00367 ptr += 2; /* scan past type */ 00368 if (ptr - buffer < bytesReceived && ret == NRC_GOODRET 00369 && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET) 00370 ptr += sizeof(WORD); 00371 else 00372 ret = NRC_SYSTEM; /* parse error */ 00373 ptr += sizeof(DWORD); /* TTL */ 00374 rLen = NBR_GETWORD(ptr); 00375 rLen = min(rLen, bytesReceived - (ptr - buffer)); 00376 ptr += sizeof(WORD); 00377 shouldContinue = answerCallback(data, answerCount, 00378 answerIndex, ptr, rLen); 00379 ptr += rLen; 00380 answerIndex++; 00381 } 00382 } 00383 } 00384 } 00385 } 00386 TRACE("returning 0x%02x\n", ret); 00387 return ret; 00388 } 00389 00390 typedef struct _NetBTNameQueryData { 00391 NBNameCacheEntry *cacheEntry; 00392 UCHAR ret; 00393 } NetBTNameQueryData; 00394 00395 /* Name query callback function for NetBTWaitForNameResponse, creates a cache 00396 * entry on the first answer, adds each address as it's called again (as long 00397 * as there's space). If there's an error that should be propagated as the 00398 * NetBIOS error, modifies queryData's ret member to the proper return code. 00399 */ 00400 static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount, 00401 WORD answerIndex, PUCHAR rData, WORD rLen) 00402 { 00403 NetBTNameQueryData *queryData = pVoid; 00404 BOOL ret; 00405 00406 if (queryData) 00407 { 00408 if (queryData->cacheEntry == NULL) 00409 { 00410 queryData->cacheEntry = HeapAlloc( 00411 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) + 00412 (answerCount - 1) * sizeof(DWORD)); 00413 if (queryData->cacheEntry) 00414 queryData->cacheEntry->numAddresses = 0; 00415 else 00416 queryData->ret = NRC_OSRESNOTAV; 00417 } 00418 if (rLen == 6 && queryData->cacheEntry && 00419 queryData->cacheEntry->numAddresses < answerCount) 00420 { 00421 queryData->cacheEntry->addresses[queryData->cacheEntry-> 00422 numAddresses++] = *(const DWORD *)(rData + 2); 00423 ret = queryData->cacheEntry->numAddresses < answerCount; 00424 } 00425 else 00426 ret = FALSE; 00427 } 00428 else 00429 ret = FALSE; 00430 return ret; 00431 } 00432 00433 /* Workhorse NetBT name lookup function. Sends a name lookup query for 00434 * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using 00435 * adapter->nameQueryXID as the transaction ID. Waits up to timeout 00436 * milliseconds, and retries up to maxQueries times, waiting for a reply. 00437 * If a valid response is received, stores the looked up addresses as a 00438 * NBNameCacheEntry in *cacheEntry. 00439 * Returns NRC_GOODRET on success, though this may not mean the name was 00440 * resolved--check whether *cacheEntry is NULL. 00441 */ 00442 static UCHAR NetBTNameWaitLoop(const NetBTAdapter *adapter, SOCKET fd, const NCB *ncb, 00443 DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries, 00444 NBNameCacheEntry **cacheEntry) 00445 { 00446 unsigned int queries; 00447 NetBTNameQueryData queryData; 00448 00449 if (!adapter) return NRC_BADDR; 00450 if (fd == INVALID_SOCKET) return NRC_BADDR; 00451 if (!ncb) return NRC_BADDR; 00452 if (!cacheEntry) return NRC_BADDR; 00453 00454 queryData.cacheEntry = NULL; 00455 queryData.ret = NRC_GOODRET; 00456 for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries; 00457 queries++) 00458 { 00459 if (!NCB_CANCELLED(ncb)) 00460 { 00461 int r = NetBTSendNameQuery(fd, ncb->ncb_callname, 00462 adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast); 00463 00464 if (r == 0) 00465 queryData.ret = NetBTWaitForNameResponse(adapter, fd, 00466 GetTickCount() + timeout, NetBTFindNameAnswerCallback, 00467 &queryData); 00468 else 00469 queryData.ret = NRC_SYSTEM; 00470 } 00471 else 00472 queryData.ret = NRC_CMDCAN; 00473 } 00474 if (queryData.cacheEntry) 00475 { 00476 memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ); 00477 memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ); 00478 } 00479 *cacheEntry = queryData.cacheEntry; 00480 return queryData.ret; 00481 } 00482 00483 /* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache 00484 * has not yet been created, creates it, using gCacheTimeout as the cache 00485 * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails, 00486 * frees cacheEntry. 00487 * Returns NRC_GOODRET on success, and something else on failure. 00488 */ 00489 static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache, 00490 NBNameCacheEntry *cacheEntry) 00491 { 00492 UCHAR ret; 00493 00494 if (!nameCache) return NRC_BADDR; 00495 if (!cacheEntry) return NRC_BADDR; 00496 00497 if (!*nameCache) 00498 *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout); 00499 if (*nameCache) 00500 ret = NBNameCacheAddEntry(*nameCache, cacheEntry) 00501 ? NRC_GOODRET : NRC_OSRESNOTAV; 00502 else 00503 { 00504 HeapFree(GetProcessHeap(), 0, cacheEntry); 00505 ret = NRC_OSRESNOTAV; 00506 } 00507 return ret; 00508 } 00509 00510 /* Attempts to resolve name using inet_addr(), then gethostbyname() if 00511 * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name 00512 * can be looked up, returns 0 and stores the looked up addresses as a 00513 * NBNameCacheEntry in *cacheEntry. 00514 * Returns NRC_GOODRET on success, though this may not mean the name was 00515 * resolved--check whether *cacheEntry is NULL. Returns something else on 00516 * error. 00517 */ 00518 static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ], 00519 NBNameCacheEntry **cacheEntry) 00520 { 00521 UCHAR ret = NRC_GOODRET; 00522 00523 TRACE("name %s, cacheEntry %p\n", name, cacheEntry); 00524 00525 if (!name) return NRC_BADDR; 00526 if (!cacheEntry) return NRC_BADDR; 00527 00528 if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 || 00529 name[NCBNAMSZ - 1] == 0x20)) 00530 { 00531 CHAR toLookup[NCBNAMSZ]; 00532 unsigned int i; 00533 00534 for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++) 00535 toLookup[i] = name[i]; 00536 toLookup[i] = '\0'; 00537 00538 if (isdigit(toLookup[0])) 00539 { 00540 unsigned long addr = inet_addr(toLookup); 00541 00542 if (addr != INADDR_NONE) 00543 { 00544 *cacheEntry = HeapAlloc(GetProcessHeap(), 00545 0, sizeof(NBNameCacheEntry)); 00546 if (*cacheEntry) 00547 { 00548 memcpy((*cacheEntry)->name, name, NCBNAMSZ); 00549 memset((*cacheEntry)->nbname, 0, NCBNAMSZ); 00550 (*cacheEntry)->nbname[0] = '*'; 00551 (*cacheEntry)->numAddresses = 1; 00552 (*cacheEntry)->addresses[0] = addr; 00553 } 00554 else 00555 ret = NRC_OSRESNOTAV; 00556 } 00557 } 00558 if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry) 00559 { 00560 struct hostent *host; 00561 00562 if ((host = gethostbyname(toLookup)) != NULL) 00563 { 00564 for (i = 0; ret == NRC_GOODRET && host->h_addr_list && 00565 host->h_addr_list[i]; i++) 00566 ; 00567 if (host->h_addr_list && host->h_addr_list[0]) 00568 { 00569 *cacheEntry = HeapAlloc( 00570 GetProcessHeap(), 0, sizeof(NBNameCacheEntry) + 00571 (i - 1) * sizeof(DWORD)); 00572 if (*cacheEntry) 00573 { 00574 memcpy((*cacheEntry)->name, name, NCBNAMSZ); 00575 memset((*cacheEntry)->nbname, 0, NCBNAMSZ); 00576 (*cacheEntry)->nbname[0] = '*'; 00577 (*cacheEntry)->numAddresses = i; 00578 for (i = 0; i < (*cacheEntry)->numAddresses; i++) 00579 (*cacheEntry)->addresses[i] = 00580 (DWORD)host->h_addr_list[i]; 00581 } 00582 else 00583 ret = NRC_OSRESNOTAV; 00584 } 00585 } 00586 } 00587 } 00588 00589 TRACE("returning 0x%02x\n", ret); 00590 return ret; 00591 } 00592 00593 /* Looks up the name in ncb->ncb_callname, first in the name caches (global 00594 * and this adapter's), then using gethostbyname(), next by WINS if configured, 00595 * and finally using broadcast NetBT name resolution. In NBT parlance, this 00596 * makes this an "H-node". Stores an entry in the appropriate name cache for a 00597 * found node, and returns it as *cacheEntry. 00598 * Assumes data, ncb, and cacheEntry are not NULL. 00599 * Returns NRC_GOODRET on success--which doesn't mean the name was resolved, 00600 * just that all name lookup operations completed successfully--and something 00601 * else on failure. *cacheEntry will be NULL if the name was not found. 00602 */ 00603 static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb, 00604 const NBNameCacheEntry **cacheEntry) 00605 { 00606 UCHAR ret = NRC_GOODRET; 00607 00608 TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry); 00609 00610 if (!cacheEntry) return NRC_BADDR; 00611 *cacheEntry = NULL; 00612 00613 if (!adapter) return NRC_BADDR; 00614 if (!ncb) return NRC_BADDR; 00615 00616 if (ncb->ncb_callname[0] == '*') 00617 ret = NRC_NOWILD; 00618 else 00619 { 00620 *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname); 00621 if (!*cacheEntry) 00622 *cacheEntry = NBNameCacheFindEntry(adapter->nameCache, 00623 ncb->ncb_callname); 00624 if (!*cacheEntry) 00625 { 00626 NBNameCacheEntry *newEntry = NULL; 00627 00628 ret = NetBTinetResolve(ncb->ncb_callname, &newEntry); 00629 if (ret == NRC_GOODRET && newEntry) 00630 { 00631 ret = NetBTStoreCacheEntry(&gNameCache, newEntry); 00632 if (ret != NRC_GOODRET) 00633 newEntry = NULL; 00634 } 00635 else 00636 { 00637 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 00638 0, WSA_FLAG_OVERLAPPED); 00639 00640 if(fd == INVALID_SOCKET) 00641 ret = NRC_OSRESNOTAV; 00642 else 00643 { 00644 int winsNdx; 00645 00646 adapter->nameQueryXID++; 00647 for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL 00648 && winsNdx < gNumWINSServers; winsNdx++) 00649 ret = NetBTNameWaitLoop(adapter, fd, ncb, 00650 gWINSServers[winsNdx], FALSE, gWINSQueryTimeout, 00651 gWINSQueries, &newEntry); 00652 if (ret == NRC_GOODRET && newEntry) 00653 { 00654 ret = NetBTStoreCacheEntry(&gNameCache, newEntry); 00655 if (ret != NRC_GOODRET) 00656 newEntry = NULL; 00657 } 00658 if (ret == NRC_GOODRET && *cacheEntry == NULL) 00659 { 00660 DWORD bcastAddr = 00661 adapter->ipr.dwAddr & adapter->ipr.dwMask; 00662 00663 if (adapter->ipr.dwBCastAddr) 00664 bcastAddr |= ~adapter->ipr.dwMask; 00665 ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr, 00666 TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry); 00667 if (ret == NRC_GOODRET && newEntry) 00668 { 00669 ret = NetBTStoreCacheEntry(&adapter->nameCache, 00670 newEntry); 00671 if (ret != NRC_GOODRET) 00672 newEntry = NULL; 00673 } 00674 } 00675 closesocket(fd); 00676 } 00677 } 00678 *cacheEntry = newEntry; 00679 } 00680 } 00681 TRACE("returning 0x%02x\n", ret); 00682 return ret; 00683 } 00684 00685 typedef struct _NetBTNodeQueryData 00686 { 00687 BOOL gotResponse; 00688 PADAPTER_STATUS astat; 00689 WORD astatLen; 00690 } NetBTNodeQueryData; 00691 00692 /* Callback function for NetBTAstatRemote, parses the rData for the node 00693 * status and name list of the remote node. Always returns FALSE, since 00694 * there's never more than one answer we care about in a node status response. 00695 */ 00696 static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount, 00697 WORD answerIndex, PUCHAR rData, WORD rLen) 00698 { 00699 NetBTNodeQueryData *data = pVoid; 00700 00701 if (data && !data->gotResponse && rData && rLen >= 1) 00702 { 00703 /* num names is first byte; each name is NCBNAMSZ + 2 bytes */ 00704 if (rLen >= rData[0] * (NCBNAMSZ + 2)) 00705 { 00706 WORD i; 00707 PUCHAR src; 00708 PNAME_BUFFER dst; 00709 00710 data->gotResponse = TRUE; 00711 data->astat->name_count = rData[0]; 00712 for (i = 0, src = rData + 1, 00713 dst = (PNAME_BUFFER)((PUCHAR)data->astat + 00714 sizeof(ADAPTER_STATUS)); 00715 i < data->astat->name_count && src - rData < rLen && 00716 (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen; 00717 i++, dst++, src += NCBNAMSZ + 2) 00718 { 00719 UCHAR flags = *(src + NCBNAMSZ); 00720 00721 memcpy(dst->name, src, NCBNAMSZ); 00722 /* we won't actually see a registering name in the returned 00723 * response. It's useful to see if no other flags are set; if 00724 * none are, then the name is registered. */ 00725 dst->name_flags = REGISTERING; 00726 if (flags & 0x80) 00727 dst->name_flags |= GROUP_NAME; 00728 if (flags & 0x10) 00729 dst->name_flags |= DEREGISTERED; 00730 if (flags & 0x08) 00731 dst->name_flags |= DUPLICATE; 00732 if (dst->name_flags == REGISTERING) 00733 dst->name_flags = REGISTERED; 00734 } 00735 /* arbitrarily set HW type to Ethernet */ 00736 data->astat->adapter_type = 0xfe; 00737 if (src - rData < rLen) 00738 memcpy(data->astat->adapter_address, src, 00739 min(rLen - (src - rData), 6)); 00740 } 00741 } 00742 return FALSE; 00743 } 00744 00745 /* This uses the WINS timeout and query values, as they're the 00746 * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs. 00747 */ 00748 static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb) 00749 { 00750 UCHAR ret = NRC_GOODRET; 00751 const NBNameCacheEntry *cacheEntry = NULL; 00752 00753 TRACE("adapter %p, NCB %p\n", adapter, ncb); 00754 00755 if (!adapter) return NRC_BADDR; 00756 if (!ncb) return NRC_INVADDRESS; 00757 00758 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); 00759 if (ret == NRC_GOODRET && cacheEntry) 00760 { 00761 if (cacheEntry->numAddresses > 0) 00762 { 00763 SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 00764 WSA_FLAG_OVERLAPPED); 00765 00766 if(fd == INVALID_SOCKET) 00767 ret = NRC_OSRESNOTAV; 00768 else 00769 { 00770 NetBTNodeQueryData queryData; 00771 DWORD queries; 00772 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; 00773 00774 adapter->nameQueryXID++; 00775 astat->name_count = 0; 00776 queryData.gotResponse = FALSE; 00777 queryData.astat = astat; 00778 queryData.astatLen = ncb->ncb_length; 00779 for (queries = 0; !queryData.gotResponse && 00780 queries < gWINSQueries; queries++) 00781 { 00782 if (!NCB_CANCELLED(ncb)) 00783 { 00784 int r = NetBTSendNameQuery(fd, ncb->ncb_callname, 00785 adapter->nameQueryXID, NBNS_TYPE_NBSTAT, 00786 cacheEntry->addresses[0], FALSE); 00787 00788 if (r == 0) 00789 ret = NetBTWaitForNameResponse(adapter, fd, 00790 GetTickCount() + gWINSQueryTimeout, 00791 NetBTNodeStatusAnswerCallback, &queryData); 00792 else 00793 ret = NRC_SYSTEM; 00794 } 00795 else 00796 ret = NRC_CMDCAN; 00797 } 00798 closesocket(fd); 00799 } 00800 } 00801 else 00802 ret = NRC_CMDTMO; 00803 } 00804 else if (ret == NRC_CMDCAN) 00805 ; /* do nothing, we were cancelled */ 00806 else 00807 ret = NRC_CMDTMO; 00808 TRACE("returning 0x%02x\n", ret); 00809 return ret; 00810 } 00811 00812 static UCHAR NetBTAstat(void *adapt, PNCB ncb) 00813 { 00814 NetBTAdapter *adapter = adapt; 00815 UCHAR ret; 00816 00817 TRACE("adapt %p, NCB %p\n", adapt, ncb); 00818 00819 if (!adapter) return NRC_ENVNOTDEF; 00820 if (!ncb) return NRC_INVADDRESS; 00821 if (!ncb->ncb_buffer) return NRC_BADDR; 00822 if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN; 00823 00824 if (ncb->ncb_callname[0] == '*') 00825 { 00826 DWORD physAddrLen; 00827 MIB_IFROW ifRow; 00828 PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; 00829 00830 memset(astat, 0, sizeof(ADAPTER_STATUS)); 00831 astat->rev_major = 3; 00832 ifRow.dwIndex = adapter->ipr.dwIndex; 00833 if (GetIfEntry(&ifRow) != NO_ERROR) 00834 ret = NRC_BRIDGE; 00835 else 00836 { 00837 physAddrLen = min(ifRow.dwPhysAddrLen, 6); 00838 if (physAddrLen > 0) 00839 memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen); 00840 /* doubt anyone cares, but why not.. */ 00841 if (ifRow.dwType == MIB_IF_TYPE_TOKENRING) 00842 astat->adapter_type = 0xff; 00843 else 00844 astat->adapter_type = 0xfe; /* for Ethernet */ 00845 astat->max_sess_pkt_size = 0xffff; 00846 astat->xmit_success = adapter->xmit_success; 00847 astat->recv_success = adapter->recv_success; 00848 ret = NRC_GOODRET; 00849 } 00850 } 00851 else 00852 ret = NetBTAstatRemote(adapter, ncb); 00853 TRACE("returning 0x%02x\n", ret); 00854 return ret; 00855 } 00856 00857 static UCHAR NetBTFindName(void *adapt, PNCB ncb) 00858 { 00859 NetBTAdapter *adapter = adapt; 00860 UCHAR ret; 00861 const NBNameCacheEntry *cacheEntry = NULL; 00862 PFIND_NAME_HEADER foundName; 00863 00864 TRACE("adapt %p, NCB %p\n", adapt, ncb); 00865 00866 if (!adapter) return NRC_ENVNOTDEF; 00867 if (!ncb) return NRC_INVADDRESS; 00868 if (!ncb->ncb_buffer) return NRC_BADDR; 00869 if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN; 00870 00871 foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer; 00872 memset(foundName, 0, sizeof(FIND_NAME_HEADER)); 00873 00874 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); 00875 if (ret == NRC_GOODRET) 00876 { 00877 if (cacheEntry) 00878 { 00879 DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) / 00880 sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses); 00881 DWORD ndx; 00882 00883 for (ndx = 0; ndx < spaceFor; ndx++) 00884 { 00885 PFIND_NAME_BUFFER findNameBuffer; 00886 00887 findNameBuffer = 00888 (PFIND_NAME_BUFFER)((PUCHAR)foundName + 00889 sizeof(FIND_NAME_HEADER) + foundName->node_count * 00890 sizeof(FIND_NAME_BUFFER)); 00891 memset(findNameBuffer->destination_addr, 0, 2); 00892 memcpy(findNameBuffer->destination_addr + 2, 00893 &adapter->ipr.dwAddr, sizeof(DWORD)); 00894 memset(findNameBuffer->source_addr, 0, 2); 00895 memcpy(findNameBuffer->source_addr + 2, 00896 &cacheEntry->addresses[ndx], sizeof(DWORD)); 00897 foundName->node_count++; 00898 } 00899 if (spaceFor < cacheEntry->numAddresses) 00900 ret = NRC_BUFLEN; 00901 } 00902 else 00903 ret = NRC_CMDTMO; 00904 } 00905 TRACE("returning 0x%02x\n", ret); 00906 return ret; 00907 } 00908 00909 static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName, 00910 const UCHAR *callingName) 00911 { 00912 UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret; 00913 int r; 00914 unsigned int len = 0; 00915 DWORD bytesSent, bytesReceived, recvFlags = 0; 00916 WSABUF wsaBuf; 00917 00918 buffer[0] = NBSS_REQ; 00919 buffer[1] = 0; 00920 00921 len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]); 00922 len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]); 00923 00924 NBR_ADDWORD(&buffer[2], len); 00925 00926 wsaBuf.len = len + NBSS_HDRSIZE; 00927 wsaBuf.buf = (char*)buffer; 00928 00929 r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL); 00930 if(r < 0 || bytesSent < len + NBSS_HDRSIZE) 00931 { 00932 ERR("send failed\n"); 00933 return NRC_SABORT; 00934 } 00935 00936 /* I've already set the recv timeout on this socket (if it supports it), so 00937 * just block. Hopefully we'll always receive the session acknowledgement 00938 * within one timeout. 00939 */ 00940 wsaBuf.len = NBSS_HDRSIZE + 1; 00941 r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL); 00942 if (r < 0 || bytesReceived < NBSS_HDRSIZE) 00943 ret = NRC_SABORT; 00944 else if (buffer[0] == NBSS_NACK) 00945 { 00946 if (r == NBSS_HDRSIZE + 1) 00947 { 00948 switch (buffer[NBSS_HDRSIZE]) 00949 { 00950 case NBSS_ERR_INSUFFICIENT_RESOURCES: 00951 ret = NRC_REMTFUL; 00952 break; 00953 default: 00954 ret = NRC_NOCALL; 00955 } 00956 } 00957 else 00958 ret = NRC_NOCALL; 00959 } 00960 else if (buffer[0] == NBSS_RETARGET) 00961 { 00962 FIXME("Got a session retarget, can't deal\n"); 00963 ret = NRC_NOCALL; 00964 } 00965 else if (buffer[0] == NBSS_ACK) 00966 ret = NRC_GOODRET; 00967 else 00968 ret = NRC_SYSTEM; 00969 00970 TRACE("returning 0x%02x\n", ret); 00971 return ret; 00972 } 00973 00974 static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess) 00975 { 00976 NetBTAdapter *adapter = adapt; 00977 UCHAR ret; 00978 const NBNameCacheEntry *cacheEntry = NULL; 00979 00980 TRACE("adapt %p, ncb %p\n", adapt, ncb); 00981 00982 if (!adapter) return NRC_ENVNOTDEF; 00983 if (!ncb) return NRC_INVADDRESS; 00984 if (!sess) return NRC_BADDR; 00985 00986 ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); 00987 if (ret == NRC_GOODRET) 00988 { 00989 if (cacheEntry && cacheEntry->numAddresses > 0) 00990 { 00991 SOCKET fd; 00992 00993 fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 00994 WSA_FLAG_OVERLAPPED); 00995 if (fd != INVALID_SOCKET) 00996 { 00997 DWORD timeout; 00998 struct sockaddr_in sin; 00999 01000 if (ncb->ncb_rto > 0) 01001 { 01002 timeout = ncb->ncb_rto * 500; 01003 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, 01004 sizeof(timeout)); 01005 } 01006 if (ncb->ncb_rto > 0) 01007 { 01008 timeout = ncb->ncb_sto * 500; 01009 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, 01010 sizeof(timeout)); 01011 } 01012 01013 memset(&sin, 0, sizeof(sin)); 01014 memcpy(&sin.sin_addr, &cacheEntry->addresses[0], 01015 sizeof(sin.sin_addr)); 01016 sin.sin_family = AF_INET; 01017 sin.sin_port = htons(PORT_NBSS); 01018 /* FIXME: use nonblocking mode for the socket, check the 01019 * cancel flag periodically 01020 */ 01021 if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) 01022 == SOCKET_ERROR) 01023 ret = NRC_CMDTMO; 01024 else 01025 { 01026 static const UCHAR fakedCalledName[] = "*SMBSERVER"; 01027 const UCHAR *calledParty = cacheEntry->nbname[0] == '*' 01028 ? fakedCalledName : cacheEntry->nbname; 01029 01030 ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name); 01031 if (ret != NRC_GOODRET && calledParty[0] == '*') 01032 { 01033 FIXME("NBT session to \"*SMBSERVER\" refused,\n"); 01034 FIXME("should try finding name using ASTAT\n"); 01035 } 01036 } 01037 if (ret != NRC_GOODRET) 01038 closesocket(fd); 01039 else 01040 { 01041 NetBTSession *session = HeapAlloc( 01042 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession)); 01043 01044 if (session) 01045 { 01046 session->fd = fd; 01047 InitializeCriticalSection(&session->cs); 01048 session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NetBTSession.cs"); 01049 *sess = session; 01050 } 01051 else 01052 { 01053 ret = NRC_OSRESNOTAV; 01054 closesocket(fd); 01055 } 01056 } 01057 } 01058 else 01059 ret = NRC_OSRESNOTAV; 01060 } 01061 else 01062 ret = NRC_NAMERR; 01063 } 01064 TRACE("returning 0x%02x\n", ret); 01065 return ret; 01066 } 01067 01068 /* Notice that I don't protect against multiple thread access to NetBTSend. 01069 * This is because I don't update any data in the adapter, and I only make a 01070 * single call to WSASend, which I assume to act atomically (not interleaving 01071 * data from other threads). 01072 * I don't lock, because I only depend on the fd being valid, and this won't be 01073 * true until a session setup is completed. 01074 */ 01075 static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb) 01076 { 01077 NetBTAdapter *adapter = adapt; 01078 NetBTSession *session = sess; 01079 UCHAR buffer[NBSS_HDRSIZE], ret; 01080 int r; 01081 WSABUF wsaBufs[2]; 01082 DWORD bytesSent; 01083 01084 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb); 01085 01086 if (!adapter) return NRC_ENVNOTDEF; 01087 if (!ncb) return NRC_INVADDRESS; 01088 if (!ncb->ncb_buffer) return NRC_BADDR; 01089 if (!session) return NRC_SNUMOUT; 01090 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT; 01091 01092 buffer[0] = NBSS_MSG; 01093 buffer[1] = 0; 01094 NBR_ADDWORD(&buffer[2], ncb->ncb_length); 01095 01096 wsaBufs[0].len = NBSS_HDRSIZE; 01097 wsaBufs[0].buf = (char*)buffer; 01098 wsaBufs[1].len = ncb->ncb_length; 01099 wsaBufs[1].buf = (char*)ncb->ncb_buffer; 01100 01101 r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]), 01102 &bytesSent, 0, NULL, NULL); 01103 if (r == SOCKET_ERROR) 01104 { 01105 NetBIOSHangupSession(ncb); 01106 ret = NRC_SABORT; 01107 } 01108 else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length) 01109 { 01110 FIXME("Only sent %d bytes (of %d), hanging up session\n", bytesSent, 01111 NBSS_HDRSIZE + ncb->ncb_length); 01112 NetBIOSHangupSession(ncb); 01113 ret = NRC_SABORT; 01114 } 01115 else 01116 { 01117 ret = NRC_GOODRET; 01118 adapter->xmit_success++; 01119 } 01120 TRACE("returning 0x%02x\n", ret); 01121 return ret; 01122 } 01123 01124 static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb) 01125 { 01126 NetBTAdapter *adapter = adapt; 01127 NetBTSession *session = sess; 01128 UCHAR buffer[NBSS_HDRSIZE], ret; 01129 int r; 01130 WSABUF wsaBufs[2]; 01131 DWORD bufferCount, bytesReceived, flags; 01132 01133 TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb); 01134 01135 if (!adapter) return NRC_ENVNOTDEF; 01136 if (!ncb) return NRC_BADDR; 01137 if (!ncb->ncb_buffer) return NRC_BADDR; 01138 if (!session) return NRC_SNUMOUT; 01139 if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT; 01140 01141 EnterCriticalSection(&session->cs); 01142 bufferCount = 0; 01143 if (session->bytesPending == 0) 01144 { 01145 bufferCount++; 01146 wsaBufs[0].len = NBSS_HDRSIZE; 01147 wsaBufs[0].buf = (char*)buffer; 01148 } 01149 wsaBufs[bufferCount].len = ncb->ncb_length; 01150 wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer; 01151 bufferCount++; 01152 01153 flags = 0; 01154 /* FIXME: should poll a bit so I can check the cancel flag */ 01155 r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags, 01156 NULL, NULL); 01157 if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) 01158 { 01159 LeaveCriticalSection(&session->cs); 01160 ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError()); 01161 NetBIOSHangupSession(ncb); 01162 ret = NRC_SABORT; 01163 } 01164 else if (NCB_CANCELLED(ncb)) 01165 { 01166 LeaveCriticalSection(&session->cs); 01167 ret = NRC_CMDCAN; 01168 } 01169 else 01170 { 01171 if (bufferCount == 2) 01172 { 01173 if (buffer[0] == NBSS_KEEPALIVE) 01174 { 01175 LeaveCriticalSection(&session->cs); 01176 FIXME("Oops, received a session keepalive and lost my place\n"); 01177 /* need to read another session header until we get a session 01178 * message header. */ 01179 NetBIOSHangupSession(ncb); 01180 ret = NRC_SABORT; 01181 goto error; 01182 } 01183 else if (buffer[0] != NBSS_MSG) 01184 { 01185 LeaveCriticalSection(&session->cs); 01186 FIXME("Received unexpected session msg type %d\n", buffer[0]); 01187 NetBIOSHangupSession(ncb); 01188 ret = NRC_SABORT; 01189 goto error; 01190 } 01191 else 01192 { 01193 if (buffer[1] & NBSS_EXTENSION) 01194 { 01195 LeaveCriticalSection(&session->cs); 01196 FIXME("Received a message that's too long for my taste\n"); 01197 NetBIOSHangupSession(ncb); 01198 ret = NRC_SABORT; 01199 goto error; 01200 } 01201 else 01202 { 01203 session->bytesPending = NBSS_HDRSIZE 01204 + NBR_GETWORD(&buffer[2]) - bytesReceived; 01205 ncb->ncb_length = bytesReceived - NBSS_HDRSIZE; 01206 LeaveCriticalSection(&session->cs); 01207 } 01208 } 01209 } 01210 else 01211 { 01212 if (bytesReceived < session->bytesPending) 01213 session->bytesPending -= bytesReceived; 01214 else 01215 session->bytesPending = 0; 01216 LeaveCriticalSection(&session->cs); 01217 ncb->ncb_length = bytesReceived; 01218 } 01219 if (session->bytesPending > 0) 01220 ret = NRC_INCOMP; 01221 else 01222 { 01223 ret = NRC_GOODRET; 01224 adapter->recv_success++; 01225 } 01226 } 01227 error: 01228 TRACE("returning 0x%02x\n", ret); 01229 return ret; 01230 } 01231 01232 static UCHAR NetBTHangup(void *adapt, void *sess) 01233 { 01234 NetBTSession *session = sess; 01235 01236 TRACE("adapt %p, session %p\n", adapt, session); 01237 01238 if (!session) return NRC_SNUMOUT; 01239 01240 /* I don't lock the session, because NetBTRecv knows not to decrement 01241 * past 0, so if a receive completes after this it should still deal. 01242 */ 01243 closesocket(session->fd); 01244 session->fd = INVALID_SOCKET; 01245 session->bytesPending = 0; 01246 session->cs.DebugInfo->Spare[0] = 0; 01247 DeleteCriticalSection(&session->cs); 01248 HeapFree(GetProcessHeap(), 0, session); 01249 01250 return NRC_GOODRET; 01251 } 01252 01253 static void NetBTCleanupAdapter(void *adapt) 01254 { 01255 TRACE("adapt %p\n", adapt); 01256 if (adapt) 01257 { 01258 NetBTAdapter *adapter = adapt; 01259 01260 if (adapter->nameCache) 01261 NBNameCacheDestroy(adapter->nameCache); 01262 HeapFree(GetProcessHeap(), 0, adapt); 01263 } 01264 } 01265 01266 static void NetBTCleanup(void) 01267 { 01268 TRACE("\n"); 01269 if (gNameCache) 01270 { 01271 NBNameCacheDestroy(gNameCache); 01272 gNameCache = NULL; 01273 } 01274 } 01275 01276 static UCHAR NetBTRegisterAdapter(const MIB_IPADDRROW *ipRow) 01277 { 01278 UCHAR ret; 01279 NetBTAdapter *adapter; 01280 01281 if (!ipRow) return NRC_BADDR; 01282 01283 adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter)); 01284 if (adapter) 01285 { 01286 adapter->ipr = *ipRow; 01287 if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter)) 01288 { 01289 NetBTCleanupAdapter(adapter); 01290 ret = NRC_SYSTEM; 01291 } 01292 else 01293 ret = NRC_GOODRET; 01294 } 01295 else 01296 ret = NRC_OSRESNOTAV; 01297 return ret; 01298 } 01299 01300 /* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to 01301 * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the 01302 * NetBIOS adapter table. For each callback, checks if the passed-in adapt 01303 * has an entry in the table; if so, this adapter was enumerated previously, 01304 * and it's enabled. As a flag, the table's dwAddr entry is changed to 01305 * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter. 01306 * The NetBTEnum function will add any remaining adapters from the 01307 * MIB_IPADDRTABLE to the NetBIOS adapter table. 01308 */ 01309 static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex, 01310 ULONG transport, const NetBIOSAdapterImpl *data, void *closure) 01311 { 01312 BOOL ret; 01313 PMIB_IPADDRTABLE table = closure; 01314 01315 if (table && data) 01316 { 01317 DWORD ndx; 01318 01319 ret = FALSE; 01320 for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++) 01321 { 01322 const NetBTAdapter *adapter = data->data; 01323 01324 if (table->table[ndx].dwIndex == adapter->ipr.dwIndex) 01325 { 01326 NetBIOSEnableAdapter(data->lana); 01327 table->table[ndx].dwAddr = INADDR_LOOPBACK; 01328 ret = TRUE; 01329 } 01330 } 01331 } 01332 else 01333 ret = FALSE; 01334 return ret; 01335 } 01336 01337 /* Enumerates adapters by: 01338 * - retrieving the IP address table for the local machine 01339 * - eliminating loopback addresses from the table 01340 * - eliminating redundant addresses, that is, multiple addresses on the same 01341 * subnet 01342 * Calls NetBIOSEnumAdapters, passing the resulting table as the callback 01343 * data. The callback reenables each adapter that's already in the NetBIOS 01344 * table. After NetBIOSEnumAdapters returns, this function adds any remaining 01345 * adapters to the NetBIOS table. 01346 */ 01347 static UCHAR NetBTEnum(void) 01348 { 01349 UCHAR ret; 01350 DWORD size = 0; 01351 01352 TRACE("\n"); 01353 01354 if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER) 01355 { 01356 PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL; 01357 DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) / 01358 sizeof(MIB_IPADDRROW) + 1; 01359 01360 ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); 01361 if (ipAddrs) 01362 coalesceTable = HeapAlloc(GetProcessHeap(), 01363 HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) + 01364 (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW)); 01365 if (ipAddrs && coalesceTable) 01366 { 01367 if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS) 01368 { 01369 DWORD ndx; 01370 01371 for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++) 01372 { 01373 if ((ipAddrs->table[ndx].dwAddr & 01374 ipAddrs->table[ndx].dwMask) != 01375 htonl((INADDR_LOOPBACK & IN_CLASSA_NET))) 01376 { 01377 BOOL newNetwork = TRUE; 01378 DWORD innerIndex; 01379 01380 /* make sure we don't have more than one entry 01381 * for a subnet */ 01382 for (innerIndex = 0; newNetwork && 01383 innerIndex < coalesceTable->dwNumEntries; innerIndex++) 01384 if ((ipAddrs->table[ndx].dwAddr & 01385 ipAddrs->table[ndx].dwMask) == 01386 (coalesceTable->table[innerIndex].dwAddr 01387 & coalesceTable->table[innerIndex].dwMask)) 01388 newNetwork = FALSE; 01389 01390 if (newNetwork) 01391 memcpy(&coalesceTable->table[ 01392 coalesceTable->dwNumEntries++], 01393 &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW)); 01394 } 01395 } 01396 01397 NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback, 01398 coalesceTable); 01399 ret = NRC_GOODRET; 01400 for (ndx = 0; ret == NRC_GOODRET && 01401 ndx < coalesceTable->dwNumEntries; ndx++) 01402 if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK) 01403 ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]); 01404 } 01405 else 01406 ret = NRC_SYSTEM; 01407 HeapFree(GetProcessHeap(), 0, ipAddrs); 01408 HeapFree(GetProcessHeap(), 0, coalesceTable); 01409 } 01410 else 01411 ret = NRC_OSRESNOTAV; 01412 } 01413 else 01414 ret = NRC_SYSTEM; 01415 TRACE("returning 0x%02x\n", ret); 01416 return ret; 01417 } 01418 01419 static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r', 01420 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v', 01421 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' }; 01422 static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u', 01423 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r', 01424 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t', 01425 'e','r','s','\0' }; 01426 static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' }; 01427 static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m', 01428 'e','Q','u','e','r','y','C','o','u','n','t','\0' }; 01429 static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m', 01430 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' }; 01431 static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v', 01432 'Q','u','e','r','y','C','o','u','n','t','\0' }; 01433 static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v', 01434 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' }; 01435 static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' }; 01436 static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o', 01437 'u','t','\0' }; 01438 static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\', 01439 'W','i','n','e','\\','N','e','t','w','o','r','k','\0' }; 01440 01441 /* Initializes global variables and registers the NetBT transport */ 01442 void NetBTInit(void) 01443 { 01444 HKEY hKey; 01445 NetBIOSTransport transport; 01446 LONG ret; 01447 01448 TRACE("\n"); 01449 01450 gEnableDNS = TRUE; 01451 gBCastQueries = BCAST_QUERIES; 01452 gBCastQueryTimeout = BCAST_QUERY_TIMEOUT; 01453 gWINSQueries = WINS_QUERIES; 01454 gWINSQueryTimeout = WINS_QUERY_TIMEOUT; 01455 gNumWINSServers = 0; 01456 memset(gWINSServers, 0, sizeof(gWINSServers)); 01457 gScopeID[0] = '\0'; 01458 gCacheTimeout = CACHE_TIMEOUT; 01459 01460 /* Try to open the Win9x NetBT configuration key */ 01461 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey); 01462 /* If that fails, try the WinNT NetBT configuration key */ 01463 if (ret != ERROR_SUCCESS) 01464 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ, 01465 &hKey); 01466 if (ret == ERROR_SUCCESS) 01467 { 01468 DWORD dword, size; 01469 01470 size = sizeof(dword); 01471 if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL, 01472 (LPBYTE)&dword, &size) == ERROR_SUCCESS) 01473 gEnableDNS = dword; 01474 size = sizeof(dword); 01475 if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL, 01476 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES 01477 && dword <= MAX_QUERIES) 01478 gBCastQueries = dword; 01479 size = sizeof(dword); 01480 if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL, 01481 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT) 01482 gBCastQueryTimeout = dword; 01483 size = sizeof(dword); 01484 if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL, 01485 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES 01486 && dword <= MAX_QUERIES) 01487 gWINSQueries = dword; 01488 size = sizeof(dword); 01489 if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL, 01490 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT) 01491 gWINSQueryTimeout = dword; 01492 size = sizeof(gScopeID) - 1; 01493 if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size) 01494 == ERROR_SUCCESS) 01495 { 01496 /* convert into L2-encoded version, suitable for use by 01497 NetBTNameEncode */ 01498 char *ptr, *lenPtr; 01499 01500 for (ptr = gScopeID + 1; ptr - gScopeID < sizeof(gScopeID) && *ptr; ) 01501 { 01502 for (lenPtr = ptr - 1, *lenPtr = 0; 01503 ptr - gScopeID < sizeof(gScopeID) && *ptr && *ptr != '.'; 01504 ptr++) 01505 *lenPtr += 1; 01506 ptr++; 01507 } 01508 } 01509 if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL, 01510 (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT) 01511 gCacheTimeout = dword; 01512 RegCloseKey(hKey); 01513 } 01514 /* WINE-specific NetBT registry settings. Because our adapter naming is 01515 * different than MS', we can't do per-adapter WINS configuration in the 01516 * same place. Just do a global WINS configuration instead. 01517 */ 01518 /* @@ Wine registry key: HKCU\Software\Wine\Network */ 01519 if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS) 01520 { 01521 static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" }; 01522 char nsString[16]; 01523 DWORD size, ndx; 01524 01525 for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]); 01526 ndx++) 01527 { 01528 size = sizeof(nsString) / sizeof(char); 01529 if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL, 01530 (LPBYTE)nsString, &size) == ERROR_SUCCESS) 01531 { 01532 unsigned long addr = inet_addr(nsString); 01533 01534 if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS) 01535 gWINSServers[gNumWINSServers++] = addr; 01536 } 01537 } 01538 RegCloseKey(hKey); 01539 } 01540 01541 transport.enumerate = NetBTEnum; 01542 transport.astat = NetBTAstat; 01543 transport.findName = NetBTFindName; 01544 transport.call = NetBTCall; 01545 transport.send = NetBTSend; 01546 transport.recv = NetBTRecv; 01547 transport.hangup = NetBTHangup; 01548 transport.cleanupAdapter = NetBTCleanupAdapter; 01549 transport.cleanup = NetBTCleanup; 01550 memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG)); 01551 NetBIOSRegisterTransport(gTransportID, &transport); 01552 } Generated on Sat May 26 2012 04:23:59 for ReactOS by
1.7.6.1
|