1 /* Copyright (c) 2003 Juan Lang
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.
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.
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
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
21 * Lack of understanding and bugs are my fault.
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.
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).
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
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.
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
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.)
65 * See also other FIXMEs in the code.
74 #include "wine/debug.h"
79 #include "nbnamecache.h"
81 WINE_DEFAULT_DEBUG_CHANNEL(netbios
);
88 #define INADDR_NONE ~0UL
91 #define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
92 #define NBR_GETWORD(p) ntohs(*(WORD *)(p))
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
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
109 #define DEFAULT_NBT_SESSIONS 16
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
119 #define NBSS_HDRSIZE 4
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
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
133 #define NBSS_EXTENSION 0x01
135 typedef struct _NetBTSession
142 typedef struct _NetBTAdapter
146 struct NBNameCache
*nameCache
;
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
;
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.
168 static int NetBTNameEncode(const UCHAR
*p
, UCHAR
*buffer
)
173 if (!buffer
) return 0;
175 buffer
[len
++] = NCBNAMSZ
* 2;
176 for (i
= 0; p
[i
] && i
< NCBNAMSZ
; i
++)
178 buffer
[len
++] = ((p
[i
] & 0xf0) >> 4) + 'A';
179 buffer
[len
++] = (p
[i
] & 0x0f) + 'A';
181 while (len
< NCBNAMSZ
* 2)
188 int scopeIDLen
= strlen(gScopeID
);
190 memcpy(buffer
+ len
, gScopeID
, scopeIDLen
);
193 buffer
[len
++] = 0; /* add second terminator */
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.
201 static DWORD
NetBTNameReq(const UCHAR name
[NCBNAMSZ
], WORD xid
, WORD qtype
,
202 BOOL broadcast
, UCHAR
*buffer
, int len
)
206 if (len
< SIMPLE_NAME_QUERY_PKT_SIZE
) return 0;
208 NBR_ADDWORD(&buffer
[i
],xid
); i
+=2; /* transaction */
211 NBR_ADDWORD(&buffer
[i
],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
216 NBR_ADDWORD(&buffer
[i
],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
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 */
224 i
+= NetBTNameEncode(name
, &buffer
[i
]);
226 NBR_ADDWORD(&buffer
[i
],qtype
); i
+=2;
227 NBR_ADDWORD(&buffer
[i
],NBNS_CLASS_INTERNET
); i
+=2;
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
235 * Returns 0 on success, -1 on failure.
237 static int NetBTSendNameQuery(SOCKET fd
, const UCHAR name
[NCBNAMSZ
], WORD xid
,
238 WORD qtype
, DWORD destAddr
, BOOL broadcast
)
243 addr
.s_addr
= destAddr
;
244 TRACE("name %s, dest addr %s\n", name
, inet_ntoa(addr
));
247 ret
= setsockopt(fd
, SOL_SOCKET
, SO_BROADCAST
, (const char*)&on
, sizeof(on
));
251 UCHAR buf
[SIMPLE_NAME_QUERY_PKT_SIZE
];
252 struct sockaddr_in sin
;
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
);
259 wsaBuf
.buf
= (CHAR
*)buf
;
260 wsaBuf
.len
= NetBTNameReq(name
, xid
, qtype
, broadcast
, buf
,
266 ret
= WSASendTo(fd
, &wsaBuf
, 1, &bytesSent
, 0,
267 (struct sockaddr
*)&sin
, sizeof(sin
), NULL
, NULL
);
268 if (ret
< 0 || bytesSent
< wsaBuf
.len
)
279 typedef BOOL (*NetBTAnswerCallback
)(void *data
, WORD answerCount
,
280 WORD answerIndex
, PUCHAR rData
, WORD rdLength
);
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
291 static UCHAR
NetBTWaitForNameResponse(const NetBTAdapter
*adapter
, SOCKET fd
,
292 DWORD waitUntil
, NetBTAnswerCallback answerCallback
, void *data
)
296 UCHAR ret
= NRC_GOODRET
;
298 if (!adapter
) return NRC_BADDR
;
299 if (fd
== INVALID_SOCKET
) return NRC_BADDR
;
300 if (!answerCallback
) return NRC_BADDR
;
302 while (!found
&& ret
== NRC_GOODRET
&& (now
= GetTickCount()) < waitUntil
)
304 DWORD msToWait
= waitUntil
- now
;
306 struct timeval timeout
= { msToWait
/ 1000, msToWait
% 1000 };
311 r
= select(fd
+ 1, &fds
, NULL
, NULL
, &timeout
);
316 /* FIXME: magic #, is this always enough? */
319 struct sockaddr_in fromaddr
;
320 WORD respXID
, flags
, queryCount
, answerCount
;
321 WSABUF wsaBuf
= { sizeof(buffer
), (CHAR
*)buffer
};
322 DWORD bytesReceived
, recvFlags
= 0;
324 fromsize
= sizeof(fromaddr
);
325 r
= WSARecvFrom(fd
, &wsaBuf
, 1, &bytesReceived
, &recvFlags
,
326 (struct sockaddr
*)&fromaddr
, &fromsize
, NULL
, NULL
);
333 if (bytesReceived
< NBNS_HEADER_SIZE
)
336 respXID
= NBR_GETWORD(buffer
);
337 if (adapter
->nameQueryXID
!= respXID
)
340 flags
= NBR_GETWORD(buffer
+ 2);
341 queryCount
= NBR_GETWORD(buffer
+ 4);
342 answerCount
= NBR_GETWORD(buffer
+ 6);
344 /* a reply shouldn't contain a query, ignore bad packet */
348 if ((flags
& NBNS_RESPONSE_AND_OPCODE
) == NBNS_RESPONSE_AND_QUERY
)
350 if ((flags
& NBNS_REPLYCODE
) != 0)
352 else if ((flags
& NBNS_REPLYCODE
) == 0 && answerCount
> 0)
354 PUCHAR ptr
= buffer
+ NBNS_HEADER_SIZE
;
355 BOOL shouldContinue
= TRUE
;
356 WORD answerIndex
= 0;
359 /* decode one answer at a time */
360 while (ret
== NRC_GOODRET
&& answerIndex
< answerCount
&&
361 ptr
- buffer
< bytesReceived
&& shouldContinue
)
366 for (; ptr
[0] && ptr
- buffer
< bytesReceived
; )
369 ptr
+= 2; /* scan past type */
370 if (ptr
- buffer
< bytesReceived
&& ret
== NRC_GOODRET
371 && NBR_GETWORD(ptr
) == NBNS_CLASS_INTERNET
)
374 ret
= NRC_SYSTEM
; /* parse error */
375 ptr
+= sizeof(DWORD
); /* TTL */
376 rLen
= NBR_GETWORD(ptr
);
377 rLen
= min(rLen
, bytesReceived
- (ptr
- buffer
));
379 shouldContinue
= answerCallback(data
, answerCount
,
380 answerIndex
, ptr
, rLen
);
388 TRACE("returning 0x%02x\n", ret
);
392 typedef struct _NetBTNameQueryData
{
393 NBNameCacheEntry
*cacheEntry
;
395 } NetBTNameQueryData
;
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.
402 static BOOL
NetBTFindNameAnswerCallback(void *pVoid
, WORD answerCount
,
403 WORD answerIndex
, PUCHAR rData
, WORD rLen
)
405 NetBTNameQueryData
*queryData
= (NetBTNameQueryData
*)pVoid
;
410 if (queryData
->cacheEntry
== NULL
)
412 queryData
->cacheEntry
= HeapAlloc(
413 GetProcessHeap(), 0, sizeof(NBNameCacheEntry
) +
414 (answerCount
- 1) * sizeof(DWORD
));
415 if (queryData
->cacheEntry
)
416 queryData
->cacheEntry
->numAddresses
= 0;
420 queryData
->ret
= NRC_OSRESNOTAV
;
423 if (rLen
== 6 && queryData
->cacheEntry
&&
424 queryData
->cacheEntry
->numAddresses
< answerCount
)
426 queryData
->cacheEntry
->addresses
[queryData
->cacheEntry
->
427 numAddresses
++] = *(const DWORD
*)(rData
+ 2);
428 ret
= queryData
->cacheEntry
->numAddresses
< answerCount
;
438 /* Workhorse NetBT name lookup function. Sends a name lookup query for
439 * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
440 * adapter->nameQueryXID as the transaction ID. Waits up to timeout
441 * milliseconds, and retries up to maxQueries times, waiting for a reply.
442 * If a valid response is received, stores the looked up addresses as a
443 * NBNameCacheEntry in *cacheEntry.
444 * Returns NRC_GOODRET on success, though this may not mean the name was
445 * resolved--check whether *cacheEntry is NULL.
447 static UCHAR
NetBTNameWaitLoop(const NetBTAdapter
*adapter
, SOCKET fd
, const NCB
*ncb
,
448 DWORD sendTo
, BOOL broadcast
, DWORD timeout
, DWORD maxQueries
,
449 NBNameCacheEntry
**cacheEntry
)
451 unsigned int queries
;
452 NetBTNameQueryData queryData
;
454 if (!adapter
) return NRC_BADDR
;
455 if (fd
== INVALID_SOCKET
) return NRC_BADDR
;
456 if (!ncb
) return NRC_BADDR
;
457 if (!cacheEntry
) return NRC_BADDR
;
459 queryData
.cacheEntry
= NULL
;
460 queryData
.ret
= NRC_GOODRET
;
461 for (queries
= 0; queryData
.cacheEntry
== NULL
&& queries
< maxQueries
;
464 if (!NCB_CANCELLED(ncb
))
466 int r
= NetBTSendNameQuery(fd
, ncb
->ncb_callname
,
467 adapter
->nameQueryXID
, NBNS_TYPE_NB
, sendTo
, broadcast
);
470 queryData
.ret
= NetBTWaitForNameResponse(adapter
, fd
,
471 GetTickCount() + timeout
, NetBTFindNameAnswerCallback
,
474 queryData
.ret
= NRC_SYSTEM
;
477 queryData
.ret
= NRC_CMDCAN
;
479 if (queryData
.cacheEntry
)
481 memcpy(queryData
.cacheEntry
->name
, ncb
->ncb_callname
, NCBNAMSZ
);
482 memcpy(queryData
.cacheEntry
->nbname
, ncb
->ncb_callname
, NCBNAMSZ
);
484 *cacheEntry
= queryData
.cacheEntry
;
485 return queryData
.ret
;
488 /* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
489 * has not yet been created, creates it, using gCacheTimeout as the cache
490 * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails,
492 * Returns NRC_GOODRET on success, and something else on failure.
494 static UCHAR
NetBTStoreCacheEntry(struct NBNameCache
**nameCache
,
495 NBNameCacheEntry
*cacheEntry
)
499 if (!nameCache
) return NRC_BADDR
;
500 if (!cacheEntry
) return NRC_BADDR
;
503 *nameCache
= NBNameCacheCreate(GetProcessHeap(), gCacheTimeout
);
505 ret
= NBNameCacheAddEntry(*nameCache
, cacheEntry
)
506 ? NRC_GOODRET
: NRC_OSRESNOTAV
;
509 HeapFree(GetProcessHeap(), 0, cacheEntry
);
510 ret
= NRC_OSRESNOTAV
;
515 /* Attempts to resolve name using inet_addr(), then gethostbyname() if
516 * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name
517 * can be looked up, returns 0 and stores the looked up addresses as a
518 * NBNameCacheEntry in *cacheEntry.
519 * Returns NRC_GOODRET on success, though this may not mean the name was
520 * resolved--check whether *cacheEntry is NULL. Returns something else on
523 static UCHAR
NetBTinetResolve(const UCHAR name
[NCBNAMSZ
],
524 NBNameCacheEntry
**cacheEntry
)
526 UCHAR ret
= NRC_GOODRET
;
528 TRACE("name %s, cacheEntry %p\n", name
, cacheEntry
);
530 if (!name
) return NRC_BADDR
;
531 if (!cacheEntry
) return NRC_BADDR
;
533 if (isalnum(name
[0]) && (name
[NCBNAMSZ
- 1] == 0 ||
534 name
[NCBNAMSZ
- 1] == 0x20))
536 CHAR toLookup
[NCBNAMSZ
];
539 for (i
= 0; i
< NCBNAMSZ
- 1 && name
[i
] && name
[i
] != ' '; i
++)
540 toLookup
[i
] = name
[i
];
543 if (isdigit(toLookup
[0]))
545 unsigned long addr
= inet_addr(toLookup
);
547 if (addr
!= INADDR_NONE
)
549 *cacheEntry
= HeapAlloc(GetProcessHeap(),
550 0, sizeof(NBNameCacheEntry
));
553 memcpy((*cacheEntry
)->name
, name
, NCBNAMSZ
);
554 memset((*cacheEntry
)->nbname
, 0, NCBNAMSZ
);
555 (*cacheEntry
)->nbname
[0] = '*';
556 (*cacheEntry
)->numAddresses
= 1;
557 (*cacheEntry
)->addresses
[0] = addr
;
560 ret
= NRC_OSRESNOTAV
;
563 if (gEnableDNS
&& ret
== NRC_GOODRET
&& !*cacheEntry
)
565 struct hostent
*host
;
567 if ((host
= gethostbyname(toLookup
)) != NULL
)
569 for (i
= 0; ret
== NRC_GOODRET
&& host
->h_addr_list
&&
570 host
->h_addr_list
[i
]; i
++)
572 if (host
->h_addr_list
&& host
->h_addr_list
[0])
574 *cacheEntry
= HeapAlloc(
575 GetProcessHeap(), 0, sizeof(NBNameCacheEntry
) +
576 (i
- 1) * sizeof(DWORD
));
579 memcpy((*cacheEntry
)->name
, name
, NCBNAMSZ
);
580 memset((*cacheEntry
)->nbname
, 0, NCBNAMSZ
);
581 (*cacheEntry
)->nbname
[0] = '*';
582 (*cacheEntry
)->numAddresses
= i
;
583 for (i
= 0; i
< (*cacheEntry
)->numAddresses
; i
++)
584 (*cacheEntry
)->addresses
[i
] =
585 (DWORD
)host
->h_addr_list
[i
];
588 ret
= NRC_OSRESNOTAV
;
594 TRACE("returning 0x%02x\n", ret
);
598 /* Looks up the name in ncb->ncb_callname, first in the name caches (global
599 * and this adapter's), then using gethostbyname(), next by WINS if configured,
600 * and finally using broadcast NetBT name resolution. In NBT parlance, this
601 * makes this an "H-node". Stores an entry in the appropriate name cache for a
602 * found node, and returns it as *cacheEntry.
603 * Assumes data, ncb, and cacheEntry are not NULL.
604 * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
605 * just that all name lookup operations completed successfully--and something
606 * else on failure. *cacheEntry will be NULL if the name was not found.
608 static UCHAR
NetBTInternalFindName(NetBTAdapter
*adapter
, PNCB ncb
,
609 const NBNameCacheEntry
**cacheEntry
)
611 UCHAR ret
= NRC_GOODRET
;
613 TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter
, ncb
, cacheEntry
);
615 if (!cacheEntry
) return NRC_BADDR
;
618 if (!adapter
) return NRC_BADDR
;
619 if (!ncb
) return NRC_BADDR
;
621 if (ncb
->ncb_callname
[0] == '*')
625 *cacheEntry
= NBNameCacheFindEntry(gNameCache
, ncb
->ncb_callname
);
627 *cacheEntry
= NBNameCacheFindEntry(adapter
->nameCache
,
631 NBNameCacheEntry
*newEntry
= NULL
;
633 ret
= NetBTinetResolve(ncb
->ncb_callname
, &newEntry
);
634 if (ret
== NRC_GOODRET
&& newEntry
)
636 ret
= NetBTStoreCacheEntry(&gNameCache
, newEntry
);
637 if (ret
!= NRC_GOODRET
)
642 SOCKET fd
= WSASocketA(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, NULL
,
643 0, WSA_FLAG_OVERLAPPED
);
645 if(fd
== INVALID_SOCKET
)
646 ret
= NRC_OSRESNOTAV
;
651 adapter
->nameQueryXID
++;
652 for (winsNdx
= 0; ret
== NRC_GOODRET
&& *cacheEntry
== NULL
653 && winsNdx
< gNumWINSServers
; winsNdx
++)
654 ret
= NetBTNameWaitLoop(adapter
, fd
, ncb
,
655 gWINSServers
[winsNdx
], FALSE
, gWINSQueryTimeout
,
656 gWINSQueries
, &newEntry
);
657 if (ret
== NRC_GOODRET
&& newEntry
)
659 ret
= NetBTStoreCacheEntry(&gNameCache
, newEntry
);
660 if (ret
!= NRC_GOODRET
)
663 if (ret
== NRC_GOODRET
&& *cacheEntry
== NULL
)
666 adapter
->ipr
.dwAddr
& adapter
->ipr
.dwMask
;
668 if (adapter
->ipr
.dwBCastAddr
)
669 bcastAddr
|= ~adapter
->ipr
.dwMask
;
670 ret
= NetBTNameWaitLoop(adapter
, fd
, ncb
, bcastAddr
,
671 TRUE
, gBCastQueryTimeout
, gBCastQueries
, &newEntry
);
672 if (ret
== NRC_GOODRET
&& newEntry
)
674 ret
= NetBTStoreCacheEntry(&adapter
->nameCache
,
676 if (ret
!= NRC_GOODRET
)
683 *cacheEntry
= newEntry
;
686 TRACE("returning 0x%02x\n", ret
);
690 typedef struct _NetBTNodeQueryData
693 PADAPTER_STATUS astat
;
695 } NetBTNodeQueryData
;
697 /* Callback function for NetBTAstatRemote, parses the rData for the node
698 * status and name list of the remote node. Always returns FALSE, since
699 * there's never more than one answer we care about in a node status response.
701 static BOOL
NetBTNodeStatusAnswerCallback(void *pVoid
, WORD answerCount
,
702 WORD answerIndex
, PUCHAR rData
, WORD rLen
)
704 NetBTNodeQueryData
*data
= (NetBTNodeQueryData
*)pVoid
;
706 if (data
&& !data
->gotResponse
&& rData
&& rLen
>= 1)
708 /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
709 if (rLen
>= rData
[0] * (NCBNAMSZ
+ 2))
715 data
->gotResponse
= TRUE
;
716 data
->astat
->name_count
= rData
[0];
717 for (i
= 0, src
= rData
+ 1,
718 dst
= (PNAME_BUFFER
)((PUCHAR
)data
->astat
+
719 sizeof(ADAPTER_STATUS
));
720 i
< data
->astat
->name_count
&& src
- rData
< rLen
&&
721 (PUCHAR
)dst
- (PUCHAR
)data
->astat
< data
->astatLen
;
722 i
++, dst
++, src
+= NCBNAMSZ
+ 2)
724 UCHAR flags
= *(src
+ NCBNAMSZ
);
726 memcpy(dst
->name
, src
, NCBNAMSZ
);
727 /* we won't actually see a registering name in the returned
728 * response. It's useful to see if no other flags are set; if
729 * none are, then the name is registered. */
730 dst
->name_flags
= REGISTERING
;
732 dst
->name_flags
|= GROUP_NAME
;
734 dst
->name_flags
|= DEREGISTERED
;
736 dst
->name_flags
|= DUPLICATE
;
737 if (dst
->name_flags
== REGISTERING
)
738 dst
->name_flags
= REGISTERED
;
740 /* arbitrarily set HW type to Ethernet */
741 data
->astat
->adapter_type
= 0xfe;
742 if (src
- rData
< rLen
)
743 memcpy(data
->astat
->adapter_address
, src
,
744 min(rLen
- (src
- rData
), 6));
750 /* This uses the WINS timeout and query values, as they're the
751 * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
753 static UCHAR
NetBTAstatRemote(NetBTAdapter
*adapter
, PNCB ncb
)
755 UCHAR ret
= NRC_GOODRET
;
756 const NBNameCacheEntry
*cacheEntry
= NULL
;
758 TRACE("adapter %p, NCB %p\n", adapter
, ncb
);
760 if (!adapter
) return NRC_BADDR
;
761 if (!ncb
) return NRC_INVADDRESS
;
763 ret
= NetBTInternalFindName(adapter
, ncb
, &cacheEntry
);
764 if (ret
== NRC_GOODRET
&& cacheEntry
)
766 if (cacheEntry
->numAddresses
> 0)
768 SOCKET fd
= WSASocketA(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, NULL
, 0,
769 WSA_FLAG_OVERLAPPED
);
771 if(fd
== INVALID_SOCKET
)
772 ret
= NRC_OSRESNOTAV
;
775 NetBTNodeQueryData queryData
;
777 PADAPTER_STATUS astat
= (PADAPTER_STATUS
)ncb
->ncb_buffer
;
779 adapter
->nameQueryXID
++;
780 astat
->name_count
= 0;
781 queryData
.gotResponse
= FALSE
;
782 queryData
.astat
= astat
;
783 queryData
.astatLen
= ncb
->ncb_length
;
784 for (queries
= 0; !queryData
.gotResponse
&&
785 queries
< gWINSQueries
; queries
++)
787 if (!NCB_CANCELLED(ncb
))
789 int r
= NetBTSendNameQuery(fd
, ncb
->ncb_callname
,
790 adapter
->nameQueryXID
, NBNS_TYPE_NBSTAT
,
791 cacheEntry
->addresses
[0], FALSE
);
794 ret
= NetBTWaitForNameResponse(adapter
, fd
,
795 GetTickCount() + gWINSQueryTimeout
,
796 NetBTNodeStatusAnswerCallback
, &queryData
);
809 else if (ret
== NRC_CMDCAN
)
810 ; /* do nothing, we were cancelled */
813 TRACE("returning 0x%02x\n", ret
);
817 static UCHAR
NetBTAstat(void *adapt
, PNCB ncb
)
819 NetBTAdapter
*adapter
= (NetBTAdapter
*)adapt
;
822 TRACE("adapt %p, NCB %p\n", adapt
, ncb
);
824 if (!adapter
) return NRC_ENVNOTDEF
;
825 if (!ncb
) return NRC_INVADDRESS
;
826 if (!ncb
->ncb_buffer
) return NRC_BADDR
;
827 if (ncb
->ncb_length
< sizeof(ADAPTER_STATUS
)) return NRC_BUFLEN
;
829 if (ncb
->ncb_callname
[0] == '*')
833 PADAPTER_STATUS astat
= (PADAPTER_STATUS
)ncb
->ncb_buffer
;
835 memset(astat
, 0, sizeof(ADAPTER_STATUS
));
836 astat
->rev_major
= 3;
837 ifRow
.dwIndex
= adapter
->ipr
.dwIndex
;
838 if (GetIfEntry(&ifRow
) != NO_ERROR
)
842 physAddrLen
= min(ifRow
.dwPhysAddrLen
, 6);
844 memcpy(astat
->adapter_address
, ifRow
.bPhysAddr
, physAddrLen
);
845 /* doubt anyone cares, but why not.. */
846 if (ifRow
.dwType
== MIB_IF_TYPE_TOKENRING
)
847 astat
->adapter_type
= 0xff;
849 astat
->adapter_type
= 0xfe; /* for Ethernet */
850 astat
->max_sess_pkt_size
= 0xffff;
851 astat
->xmit_success
= adapter
->xmit_success
;
852 astat
->recv_success
= adapter
->recv_success
;
857 ret
= NetBTAstatRemote(adapter
, ncb
);
858 TRACE("returning 0x%02x\n", ret
);
862 static UCHAR
NetBTFindName(void *adapt
, PNCB ncb
)
864 NetBTAdapter
*adapter
= (NetBTAdapter
*)adapt
;
866 const NBNameCacheEntry
*cacheEntry
= NULL
;
867 PFIND_NAME_HEADER foundName
;
869 TRACE("adapt %p, NCB %p\n", adapt
, ncb
);
871 if (!adapter
) return NRC_ENVNOTDEF
;
872 if (!ncb
) return NRC_INVADDRESS
;
873 if (!ncb
->ncb_buffer
) return NRC_BADDR
;
874 if (ncb
->ncb_length
< sizeof(FIND_NAME_HEADER
)) return NRC_BUFLEN
;
876 foundName
= (PFIND_NAME_HEADER
)ncb
->ncb_buffer
;
877 memset(foundName
, 0, sizeof(FIND_NAME_HEADER
));
879 ret
= NetBTInternalFindName(adapter
, ncb
, &cacheEntry
);
880 if (ret
== NRC_GOODRET
)
884 DWORD spaceFor
= min((ncb
->ncb_length
- sizeof(FIND_NAME_HEADER
)) /
885 sizeof(FIND_NAME_BUFFER
), cacheEntry
->numAddresses
);
888 for (ndx
= 0; ndx
< spaceFor
; ndx
++)
890 PFIND_NAME_BUFFER findNameBuffer
;
893 (PFIND_NAME_BUFFER
)((PUCHAR
)foundName
+
894 sizeof(FIND_NAME_HEADER
) + foundName
->node_count
*
895 sizeof(FIND_NAME_BUFFER
));
896 memset(findNameBuffer
->destination_addr
, 0, 2);
897 memcpy(findNameBuffer
->destination_addr
+ 2,
898 &adapter
->ipr
.dwAddr
, sizeof(DWORD
));
899 memset(findNameBuffer
->source_addr
, 0, 2);
900 memcpy(findNameBuffer
->source_addr
+ 2,
901 &cacheEntry
->addresses
[ndx
], sizeof(DWORD
));
902 foundName
->node_count
++;
904 if (spaceFor
< cacheEntry
->numAddresses
)
910 TRACE("returning 0x%02x\n", ret
);
914 static UCHAR
NetBTSessionReq(SOCKET fd
, const UCHAR
*calledName
,
915 const UCHAR
*callingName
)
917 UCHAR buffer
[NBSS_HDRSIZE
+ MAX_DOMAIN_NAME_LEN
* 2], ret
;
919 unsigned int len
= 0;
920 DWORD bytesSent
, bytesReceived
, recvFlags
= 0;
923 buffer
[0] = NBSS_REQ
;
926 len
+= NetBTNameEncode(calledName
, &buffer
[NBSS_HDRSIZE
]);
927 len
+= NetBTNameEncode(callingName
, &buffer
[NBSS_HDRSIZE
+ len
]);
929 NBR_ADDWORD(&buffer
[2], len
);
931 wsaBuf
.len
= len
+ NBSS_HDRSIZE
;
932 wsaBuf
.buf
= (char*)buffer
;
934 r
= WSASend(fd
, &wsaBuf
, 1, &bytesSent
, 0, NULL
, NULL
);
935 if(r
< 0 || bytesSent
< len
+ NBSS_HDRSIZE
)
937 ERR("send failed\n");
941 /* I've already set the recv timeout on this socket (if it supports it), so
942 * just block. Hopefully we'll always receive the session acknowledgement
943 * within one timeout.
945 wsaBuf
.len
= NBSS_HDRSIZE
+ 1;
946 r
= WSARecv(fd
, &wsaBuf
, 1, &bytesReceived
, &recvFlags
, NULL
, NULL
);
947 if (r
< 0 || bytesReceived
< NBSS_HDRSIZE
)
949 else if (buffer
[0] == NBSS_NACK
)
951 if (r
== NBSS_HDRSIZE
+ 1)
953 switch (buffer
[NBSS_HDRSIZE
])
955 case NBSS_ERR_INSUFFICIENT_RESOURCES
:
965 else if (buffer
[0] == NBSS_RETARGET
)
967 FIXME("Got a session retarget, can't deal\n");
970 else if (buffer
[0] == NBSS_ACK
)
975 TRACE("returning 0x%02x\n", ret
);
979 static UCHAR
NetBTCall(void *adapt
, PNCB ncb
, void **sess
)
981 NetBTAdapter
*adapter
= (NetBTAdapter
*)adapt
;
983 const NBNameCacheEntry
*cacheEntry
= NULL
;
985 TRACE("adapt %p, ncb %p\n", adapt
, ncb
);
987 if (!adapter
) return NRC_ENVNOTDEF
;
988 if (!ncb
) return NRC_INVADDRESS
;
989 if (!sess
) return NRC_BADDR
;
991 ret
= NetBTInternalFindName(adapter
, ncb
, &cacheEntry
);
992 if (ret
== NRC_GOODRET
)
994 if (cacheEntry
&& cacheEntry
->numAddresses
> 0)
998 fd
= WSASocketA(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
, NULL
, 0,
999 WSA_FLAG_OVERLAPPED
);
1000 if (fd
!= INVALID_SOCKET
)
1003 struct sockaddr_in sin
;
1005 if (ncb
->ncb_rto
> 0)
1007 timeout
= ncb
->ncb_rto
* 500;
1008 setsockopt(fd
, SOL_SOCKET
, SO_RCVTIMEO
, (char*)&timeout
,
1011 if (ncb
->ncb_rto
> 0)
1013 timeout
= ncb
->ncb_sto
* 500;
1014 setsockopt(fd
, SOL_SOCKET
, SO_SNDTIMEO
, (char*)&timeout
,
1018 memset(&sin
, 0, sizeof(sin
));
1019 memcpy(&sin
.sin_addr
, &cacheEntry
->addresses
[0],
1020 sizeof(sin
.sin_addr
));
1021 sin
.sin_family
= AF_INET
;
1022 sin
.sin_port
= htons(PORT_NBSS
);
1023 /* FIXME: use nonblocking mode for the socket, check the
1024 * cancel flag periodically
1026 if (connect(fd
, (struct sockaddr
*)&sin
, sizeof(sin
))
1031 static const UCHAR fakedCalledName
[] = "*SMBSERVER";
1032 const UCHAR
*calledParty
= cacheEntry
->nbname
[0] == '*'
1033 ? fakedCalledName
: cacheEntry
->nbname
;
1035 ret
= NetBTSessionReq(fd
, calledParty
, ncb
->ncb_name
);
1036 if (ret
!= NRC_GOODRET
&& calledParty
[0] == '*')
1038 FIXME("NBT session to \"*SMBSERVER\" refused,\n");
1039 FIXME("should try finding name using ASTAT\n");
1042 if (ret
!= NRC_GOODRET
)
1046 NetBTSession
*session
= HeapAlloc(
1047 GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(NetBTSession
));
1052 InitializeCriticalSection(&session
->cs
);
1053 session
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": NetBTSession.cs");
1058 ret
= NRC_OSRESNOTAV
;
1064 ret
= NRC_OSRESNOTAV
;
1069 TRACE("returning 0x%02x\n", ret
);
1073 /* Notice that I don't protect against multiple thread access to NetBTSend.
1074 * This is because I don't update any data in the adapter, and I only make a
1075 * single call to WSASend, which I assume to act atomically (not interleaving
1076 * data from other threads).
1077 * I don't lock, because I only depend on the fd being valid, and this won't be
1078 * true until a session setup is completed.
1080 static UCHAR
NetBTSend(void *adapt
, void *sess
, PNCB ncb
)
1082 NetBTAdapter
*adapter
= (NetBTAdapter
*)adapt
;
1083 NetBTSession
*session
= (NetBTSession
*)sess
;
1084 UCHAR buffer
[NBSS_HDRSIZE
], ret
;
1089 TRACE("adapt %p, session %p, NCB %p\n", adapt
, session
, ncb
);
1091 if (!adapter
) return NRC_ENVNOTDEF
;
1092 if (!ncb
) return NRC_INVADDRESS
;
1093 if (!ncb
->ncb_buffer
) return NRC_BADDR
;
1094 if (!session
) return NRC_SNUMOUT
;
1095 if (session
->fd
== INVALID_SOCKET
) return NRC_SNUMOUT
;
1097 buffer
[0] = NBSS_MSG
;
1099 NBR_ADDWORD(&buffer
[2], ncb
->ncb_length
);
1101 wsaBufs
[0].len
= NBSS_HDRSIZE
;
1102 wsaBufs
[0].buf
= (char*)buffer
;
1103 wsaBufs
[1].len
= ncb
->ncb_length
;
1104 wsaBufs
[1].buf
= (char*)ncb
->ncb_buffer
;
1106 r
= WSASend(session
->fd
, wsaBufs
, sizeof(wsaBufs
) / sizeof(wsaBufs
[0]),
1107 &bytesSent
, 0, NULL
, NULL
);
1108 if (r
== SOCKET_ERROR
)
1110 NetBIOSHangupSession(ncb
);
1113 else if (bytesSent
< NBSS_HDRSIZE
+ ncb
->ncb_length
)
1115 FIXME("Only sent %d bytes (of %d), hanging up session\n", bytesSent
,
1116 NBSS_HDRSIZE
+ ncb
->ncb_length
);
1117 NetBIOSHangupSession(ncb
);
1123 adapter
->xmit_success
++;
1125 TRACE("returning 0x%02x\n", ret
);
1129 static UCHAR
NetBTRecv(void *adapt
, void *sess
, PNCB ncb
)
1131 NetBTAdapter
*adapter
= (NetBTAdapter
*)adapt
;
1132 NetBTSession
*session
= (NetBTSession
*)sess
;
1133 UCHAR buffer
[NBSS_HDRSIZE
], ret
;
1136 DWORD bufferCount
, bytesReceived
, flags
;
1138 TRACE("adapt %p, session %p, NCB %p\n", adapt
, session
, ncb
);
1140 if (!adapter
) return NRC_ENVNOTDEF
;
1141 if (!ncb
) return NRC_BADDR
;
1142 if (!ncb
->ncb_buffer
) return NRC_BADDR
;
1143 if (!session
) return NRC_SNUMOUT
;
1144 if (session
->fd
== INVALID_SOCKET
) return NRC_SNUMOUT
;
1146 EnterCriticalSection(&session
->cs
);
1148 if (session
->bytesPending
== 0)
1151 wsaBufs
[0].len
= NBSS_HDRSIZE
;
1152 wsaBufs
[0].buf
= (char*)buffer
;
1154 wsaBufs
[bufferCount
].len
= ncb
->ncb_length
;
1155 wsaBufs
[bufferCount
].buf
= (char*)ncb
->ncb_buffer
;
1159 /* FIXME: should poll a bit so I can check the cancel flag */
1160 r
= WSARecv(session
->fd
, wsaBufs
, bufferCount
, &bytesReceived
, &flags
,
1162 if (r
== SOCKET_ERROR
&& WSAGetLastError() != WSAEWOULDBLOCK
)
1164 LeaveCriticalSection(&session
->cs
);
1165 ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
1166 NetBIOSHangupSession(ncb
);
1169 else if (NCB_CANCELLED(ncb
))
1171 LeaveCriticalSection(&session
->cs
);
1176 if (bufferCount
== 2)
1178 if (buffer
[0] == NBSS_KEEPALIVE
)
1180 LeaveCriticalSection(&session
->cs
);
1181 FIXME("Oops, received a session keepalive and lost my place\n");
1182 /* need to read another session header until we get a session
1183 * message header. */
1184 NetBIOSHangupSession(ncb
);
1187 else if (buffer
[0] != NBSS_MSG
)
1189 LeaveCriticalSection(&session
->cs
);
1190 FIXME("Received unexpected session msg type %d\n", buffer
[0]);
1191 NetBIOSHangupSession(ncb
);
1196 if (buffer
[1] & NBSS_EXTENSION
)
1198 LeaveCriticalSection(&session
->cs
);
1199 FIXME("Received a message that's too long for my taste\n");
1200 NetBIOSHangupSession(ncb
);
1205 session
->bytesPending
= NBSS_HDRSIZE
1206 + NBR_GETWORD(&buffer
[2]) - bytesReceived
;
1207 ncb
->ncb_length
= bytesReceived
- NBSS_HDRSIZE
;
1208 LeaveCriticalSection(&session
->cs
);
1214 if (bytesReceived
< session
->bytesPending
)
1215 session
->bytesPending
-= bytesReceived
;
1217 session
->bytesPending
= 0;
1218 LeaveCriticalSection(&session
->cs
);
1219 ncb
->ncb_length
= bytesReceived
;
1221 if (session
->bytesPending
> 0)
1226 adapter
->recv_success
++;
1229 TRACE("returning 0x%02x\n", ret
);
1233 static UCHAR
NetBTHangup(void *adapt
, void *sess
)
1235 NetBTSession
*session
= (NetBTSession
*)sess
;
1237 TRACE("adapt %p, session %p\n", adapt
, session
);
1239 if (!session
) return NRC_SNUMOUT
;
1241 /* I don't lock the session, because NetBTRecv knows not to decrement
1242 * past 0, so if a receive completes after this it should still deal.
1244 closesocket(session
->fd
);
1245 session
->fd
= INVALID_SOCKET
;
1246 session
->bytesPending
= 0;
1247 session
->cs
.DebugInfo
->Spare
[0] = 0;
1248 DeleteCriticalSection(&session
->cs
);
1249 HeapFree(GetProcessHeap(), 0, session
);
1254 static void NetBTCleanupAdapter(void *adapt
)
1256 TRACE("adapt %p\n", adapt
);
1259 NetBTAdapter
*adapter
= (NetBTAdapter
*)adapt
;
1261 if (adapter
->nameCache
)
1262 NBNameCacheDestroy(adapter
->nameCache
);
1263 HeapFree(GetProcessHeap(), 0, adapt
);
1267 static void NetBTCleanup(void)
1272 NBNameCacheDestroy(gNameCache
);
1277 static UCHAR
NetBTRegisterAdapter(const MIB_IPADDRROW
*ipRow
)
1280 NetBTAdapter
*adapter
;
1282 if (!ipRow
) return NRC_BADDR
;
1284 adapter
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(NetBTAdapter
));
1287 memcpy(&adapter
->ipr
, ipRow
, sizeof(MIB_IPADDRROW
));
1288 if (!NetBIOSRegisterAdapter(gTransportID
, ipRow
->dwIndex
, adapter
))
1290 NetBTCleanupAdapter(adapter
);
1297 ret
= NRC_OSRESNOTAV
;
1301 /* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to
1302 * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
1303 * NetBIOS adapter table. For each callback, checks if the passed-in adapt
1304 * has an entry in the table; if so, this adapter was enumerated previously,
1305 * and it's enabled. As a flag, the table's dwAddr entry is changed to
1306 * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
1307 * The NetBTEnum function will add any remaining adapters from the
1308 * MIB_IPADDRTABLE to the NetBIOS adapter table.
1310 static BOOL
NetBTEnumCallback(UCHAR totalLANAs
, UCHAR lanaIndex
,
1311 ULONG transport
, const NetBIOSAdapterImpl
*data
, void *closure
)
1314 PMIB_IPADDRTABLE table
= (PMIB_IPADDRTABLE
)closure
;
1321 for (ndx
= 0; !ret
&& ndx
< table
->dwNumEntries
; ndx
++)
1323 const NetBTAdapter
*adapter
= (const NetBTAdapter
*)data
->data
;
1325 if (table
->table
[ndx
].dwIndex
== adapter
->ipr
.dwIndex
)
1327 NetBIOSEnableAdapter(data
->lana
);
1328 table
->table
[ndx
].dwAddr
= INADDR_LOOPBACK
;
1338 /* Enumerates adapters by:
1339 * - retrieving the IP address table for the local machine
1340 * - eliminating loopback addresses from the table
1341 * - eliminating redundant addresses, that is, multiple addresses on the same
1343 * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
1344 * data. The callback reenables each adapter that's already in the NetBIOS
1345 * table. After NetBIOSEnumAdapters returns, this function adds any remaining
1346 * adapters to the NetBIOS table.
1348 static UCHAR
NetBTEnum(void)
1355 if (GetIpAddrTable(NULL
, &size
, FALSE
) == ERROR_INSUFFICIENT_BUFFER
)
1357 PMIB_IPADDRTABLE ipAddrs
, coalesceTable
= NULL
;
1358 DWORD numIPAddrs
= (size
- sizeof(MIB_IPADDRTABLE
)) /
1359 sizeof(MIB_IPADDRROW
) + 1;
1361 ipAddrs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, size
);
1363 coalesceTable
= HeapAlloc(GetProcessHeap(),
1364 HEAP_ZERO_MEMORY
, sizeof(MIB_IPADDRTABLE
) +
1365 (min(numIPAddrs
, MAX_LANA
+ 1) - 1) * sizeof(MIB_IPADDRROW
));
1366 if (ipAddrs
&& coalesceTable
)
1368 if (GetIpAddrTable(ipAddrs
, &size
, FALSE
) == ERROR_SUCCESS
)
1372 for (ndx
= 0; ndx
< ipAddrs
->dwNumEntries
; ndx
++)
1374 if ((ipAddrs
->table
[ndx
].dwAddr
&
1375 ipAddrs
->table
[ndx
].dwMask
) !=
1376 htonl((INADDR_LOOPBACK
& IN_CLASSA_NET
)))
1378 BOOL newNetwork
= TRUE
;
1381 /* make sure we don't have more than one entry
1383 for (innerIndex
= 0; newNetwork
&&
1384 innerIndex
< coalesceTable
->dwNumEntries
; innerIndex
++)
1385 if ((ipAddrs
->table
[ndx
].dwAddr
&
1386 ipAddrs
->table
[ndx
].dwMask
) ==
1387 (coalesceTable
->table
[innerIndex
].dwAddr
1388 & coalesceTable
->table
[innerIndex
].dwMask
))
1392 memcpy(&coalesceTable
->table
[
1393 coalesceTable
->dwNumEntries
++],
1394 &ipAddrs
->table
[ndx
], sizeof(MIB_IPADDRROW
));
1398 NetBIOSEnumAdapters(gTransportID
, NetBTEnumCallback
,
1401 for (ndx
= 0; ret
== NRC_GOODRET
&&
1402 ndx
< coalesceTable
->dwNumEntries
; ndx
++)
1403 if (coalesceTable
->table
[ndx
].dwAddr
!= INADDR_LOOPBACK
)
1404 ret
= NetBTRegisterAdapter(&coalesceTable
->table
[ndx
]);
1408 HeapFree(GetProcessHeap(), 0, ipAddrs
);
1409 HeapFree(GetProcessHeap(), 0, coalesceTable
);
1412 ret
= NRC_OSRESNOTAV
;
1416 TRACE("returning 0x%02x\n", ret
);
1420 static const WCHAR VxD_MSTCPW
[] = { 'S','Y','S','T','E','M','\\','C','u','r',
1421 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
1422 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
1423 static const WCHAR NetBT_ParametersW
[] = { 'S','Y','S','T','E','M','\\','C','u',
1424 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
1425 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
1427 static const WCHAR EnableDNSW
[] = { 'E','n','a','b','l','e','D','N','S','\0' };
1428 static const WCHAR BcastNameQueryCountW
[] = { 'B','c','a','s','t','N','a','m',
1429 'e','Q','u','e','r','y','C','o','u','n','t','\0' };
1430 static const WCHAR BcastNameQueryTimeoutW
[] = { 'B','c','a','s','t','N','a','m',
1431 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1432 static const WCHAR NameSrvQueryCountW
[] = { 'N','a','m','e','S','r','v',
1433 'Q','u','e','r','y','C','o','u','n','t','\0' };
1434 static const WCHAR NameSrvQueryTimeoutW
[] = { 'N','a','m','e','S','r','v',
1435 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
1436 static const WCHAR ScopeIDW
[] = { 'S','c','o','p','e','I','D','\0' };
1437 static const WCHAR CacheTimeoutW
[] = { 'C','a','c','h','e','T','i','m','e','o',
1439 static const WCHAR Config_NetworkW
[] = { 'S','o','f','t','w','a','r','e','\\',
1440 'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
1442 /* Initializes global variables and registers the NetBT transport */
1443 void NetBTInit(void)
1446 NetBIOSTransport transport
;
1452 gBCastQueries
= BCAST_QUERIES
;
1453 gBCastQueryTimeout
= BCAST_QUERY_TIMEOUT
;
1454 gWINSQueries
= WINS_QUERIES
;
1455 gWINSQueryTimeout
= WINS_QUERY_TIMEOUT
;
1456 gNumWINSServers
= 0;
1457 memset(gWINSServers
, 0, sizeof(gWINSServers
));
1459 gCacheTimeout
= CACHE_TIMEOUT
;
1461 /* Try to open the Win9x NetBT configuration key */
1462 ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, VxD_MSTCPW
, 0, KEY_READ
, &hKey
);
1463 /* If that fails, try the WinNT NetBT configuration key */
1464 if (ret
!= ERROR_SUCCESS
)
1465 ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, NetBT_ParametersW
, 0, KEY_READ
,
1467 if (ret
== ERROR_SUCCESS
)
1471 size
= sizeof(dword
);
1472 if (RegQueryValueExW(hKey
, EnableDNSW
, NULL
, NULL
,
1473 (LPBYTE
)&dword
, &size
) == ERROR_SUCCESS
)
1475 size
= sizeof(dword
);
1476 if (RegQueryValueExW(hKey
, BcastNameQueryCountW
, NULL
, NULL
,
1477 (LPBYTE
)&dword
, &size
) == ERROR_SUCCESS
&& dword
>= MIN_QUERIES
1478 && dword
<= MAX_QUERIES
)
1479 gBCastQueries
= dword
;
1480 size
= sizeof(dword
);
1481 if (RegQueryValueExW(hKey
, BcastNameQueryTimeoutW
, NULL
, NULL
,
1482 (LPBYTE
)&dword
, &size
) == ERROR_SUCCESS
&& dword
>= MIN_QUERY_TIMEOUT
1483 && dword
<= MAX_QUERY_TIMEOUT
)
1484 gBCastQueryTimeout
= dword
;
1485 size
= sizeof(dword
);
1486 if (RegQueryValueExW(hKey
, NameSrvQueryCountW
, NULL
, NULL
,
1487 (LPBYTE
)&dword
, &size
) == ERROR_SUCCESS
&& dword
>= MIN_QUERIES
1488 && dword
<= MAX_QUERIES
)
1489 gWINSQueries
= dword
;
1490 size
= sizeof(dword
);
1491 if (RegQueryValueExW(hKey
, NameSrvQueryTimeoutW
, NULL
, NULL
,
1492 (LPBYTE
)&dword
, &size
) == ERROR_SUCCESS
&& dword
>= MIN_QUERY_TIMEOUT
1493 && dword
<= MAX_QUERY_TIMEOUT
)
1494 gWINSQueryTimeout
= dword
;
1495 size
= MAX_DOMAIN_NAME_LEN
- 1;
1496 if (RegQueryValueExW(hKey
, ScopeIDW
, NULL
, NULL
, (LPBYTE
)gScopeID
+ 1, &size
)
1499 /* convert into L2-encoded version, suitable for use by
1503 for (ptr
= gScopeID
+ 1; *ptr
&&
1504 ptr
- gScopeID
< MAX_DOMAIN_NAME_LEN
; )
1506 for (lenPtr
= ptr
- 1, *lenPtr
= 0; *ptr
&& *ptr
!= '.' &&
1507 ptr
- gScopeID
< MAX_DOMAIN_NAME_LEN
; ptr
++)
1512 if (RegQueryValueExW(hKey
, CacheTimeoutW
, NULL
, NULL
,
1513 (LPBYTE
)&dword
, &size
) == ERROR_SUCCESS
&& dword
>= MIN_CACHE_TIMEOUT
)
1514 gCacheTimeout
= dword
;
1517 /* WINE-specific NetBT registry settings. Because our adapter naming is
1518 * different than MS', we can't do per-adapter WINS configuration in the
1519 * same place. Just do a global WINS configuration instead.
1521 /* @@ Wine registry key: HKCU\Software\Wine\Network */
1522 if (RegOpenKeyW(HKEY_CURRENT_USER
, Config_NetworkW
, &hKey
) == ERROR_SUCCESS
)
1524 static const char *nsValueNames
[] = { "WinsServer", "BackupWinsServer" };
1528 for (ndx
= 0; ndx
< sizeof(nsValueNames
) / sizeof(nsValueNames
[0]);
1531 size
= sizeof(nsString
) / sizeof(char);
1532 if (RegQueryValueExA(hKey
, nsValueNames
[ndx
], NULL
, NULL
,
1533 (LPBYTE
)nsString
, &size
) == ERROR_SUCCESS
)
1535 unsigned long addr
= inet_addr(nsString
);
1537 if (addr
!= INADDR_NONE
&& gNumWINSServers
< MAX_WINS_SERVERS
)
1538 gWINSServers
[gNumWINSServers
++] = addr
;
1544 transport
.enumerate
= NetBTEnum
;
1545 transport
.astat
= NetBTAstat
;
1546 transport
.findName
= NetBTFindName
;
1547 transport
.call
= NetBTCall
;
1548 transport
.send
= NetBTSend
;
1549 transport
.recv
= NetBTRecv
;
1550 transport
.hangup
= NetBTHangup
;
1551 transport
.cleanupAdapter
= NetBTCleanupAdapter
;
1552 transport
.cleanup
= NetBTCleanup
;
1553 memcpy(&gTransportID
, TRANSPORT_NBT
, sizeof(ULONG
));
1554 NetBIOSRegisterTransport(gTransportID
, &transport
);