xref: /reactos/dll/win32/netapi32/nbt.c (revision c2c66aff)
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  */
NetBTNameEncode(const UCHAR * p,UCHAR * buffer)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  */
NetBTNameReq(const UCHAR name[NCBNAMSZ],WORD xid,WORD qtype,BOOL broadcast,UCHAR * buffer,int len)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  */
NetBTSendNameQuery(SOCKET fd,const UCHAR name[NCBNAMSZ],WORD xid,WORD qtype,DWORD destAddr,BOOL broadcast)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  */
NetBTWaitForNameResponse(const NetBTAdapter * adapter,SOCKET fd,DWORD waitUntil,NetBTAnswerCallback answerCallback,void * data)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  */
NetBTFindNameAnswerCallback(void * pVoid,WORD answerCount,WORD answerIndex,PUCHAR rData,WORD rLen)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  */
NetBTNameWaitLoop(const NetBTAdapter * adapter,SOCKET fd,const NCB * ncb,DWORD sendTo,BOOL broadcast,DWORD timeout,DWORD maxQueries,NBNameCacheEntry ** cacheEntry)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  */
NetBTStoreCacheEntry(struct NBNameCache ** nameCache,NBNameCacheEntry * cacheEntry)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  */
NetBTinetResolve(const UCHAR name[NCBNAMSZ],NBNameCacheEntry ** cacheEntry)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  */
NetBTInternalFindName(NetBTAdapter * adapter,PNCB ncb,const NBNameCacheEntry ** cacheEntry)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  */
NetBTNodeStatusAnswerCallback(void * pVoid,WORD answerCount,WORD answerIndex,PUCHAR rData,WORD rLen)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  */
NetBTAstatRemote(NetBTAdapter * adapter,PNCB ncb)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 
NetBTAstat(void * adapt,PNCB ncb)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 
NetBTFindName(void * adapt,PNCB ncb)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 
NetBTSessionReq(SOCKET fd,const UCHAR * calledName,const UCHAR * callingName)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 
NetBTCall(void * adapt,PNCB ncb,void ** sess)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  */
NetBTSend(void * adapt,void * sess,PNCB ncb)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 
NetBTRecv(void * adapt,void * sess,PNCB ncb)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 
NetBTHangup(void * adapt,void * sess)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 
NetBTCleanupAdapter(void * adapt)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 
NetBTCleanup(void)1256 static void NetBTCleanup(void)
1257 {
1258     TRACE("\n");
1259     if (gNameCache)
1260     {
1261         NBNameCacheDestroy(gNameCache);
1262         gNameCache = NULL;
1263     }
1264 }
1265 
NetBTRegisterAdapter(const MIB_IPADDRROW * ipRow)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  */
NetBTEnumCallback(UCHAR totalLANAs,UCHAR lanaIndex,ULONG transport,const NetBIOSAdapterImpl * data,void * closure)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  */
NetBTEnum(void)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 */
NetBTInit(void)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