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