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