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