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