From e7fd6fd2a1b1aeee1284e5b7c0ae4badd110118a Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Sat, 29 Nov 2003 00:19:19 +0000 Subject: [PATCH] Implemented a lot of Netbios(). --- dlls/netapi32/Makefile.in | 6 +- dlls/netapi32/nbcmdqueue.c | 199 +++++ dlls/netapi32/nbcmdqueue.h | 66 ++ dlls/netapi32/nbnamecache.c | 215 +++++ dlls/netapi32/nbnamecache.h | 79 ++ dlls/netapi32/nbt.c | 1501 +++++++++++++++++++++++++++++++++++ dlls/netapi32/netapi32.c | 177 +---- dlls/netapi32/netbios.c | 846 ++++++++++++++++++++ dlls/netapi32/netbios.h | 183 +++++ dlls/netapi32/wksta.c | 338 +++++--- include/lmwksta.h | 3 - include/nb30.h | 141 +++- 12 files changed, 3439 insertions(+), 315 deletions(-) create mode 100644 dlls/netapi32/nbcmdqueue.c create mode 100644 dlls/netapi32/nbcmdqueue.h create mode 100644 dlls/netapi32/nbnamecache.c create mode 100644 dlls/netapi32/nbnamecache.h create mode 100644 dlls/netapi32/nbt.c create mode 100644 dlls/netapi32/netbios.c create mode 100644 dlls/netapi32/netbios.h diff --git a/dlls/netapi32/Makefile.in b/dlls/netapi32/Makefile.in index e4cc33282ce..da5963ae906 100644 --- a/dlls/netapi32/Makefile.in +++ b/dlls/netapi32/Makefile.in @@ -4,13 +4,17 @@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = netapi32.dll -IMPORTS = iphlpapi advapi32 kernel32 +IMPORTS = iphlpapi ws2_32 advapi32 kernel32 C_SRCS = \ access.c \ apibuf.c \ browsr.c \ + nbcmdqueue.c \ + nbnamecache.c \ + nbt.c \ netapi32.c \ + netbios.c \ wksta.c SUBDIRS = tests diff --git a/dlls/netapi32/nbcmdqueue.c b/dlls/netapi32/nbcmdqueue.c new file mode 100644 index 00000000000..15af9dd2e4e --- /dev/null +++ b/dlls/netapi32/nbcmdqueue.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2003 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" +#include "wine/debug.h" +#include "nbcmdqueue.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +struct NBCmdQueue +{ + HANDLE heap; + CRITICAL_SECTION cs; + PNCB head; +}; + +#define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserved) +#define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserved + sizeof(HANDLE)) + +/* The reserved area of an ncb will be used for the following data: + * - a cancelled flag (BOOL, 4 bytes??) + * - a handle to an event that's set by a cancelled command on completion + * (HANDLE, 4 bytes) + * These members are used in the following way + * - on cancel, set the event member of the reserved field (with create event) + * - NBCmdComplete will delete the ncb from the queue of there's no event; + * otherwise it will set the event and not delete the ncb + * - cancel must lock the queue before finding the ncb in it, and can unlock it + * once it's set the event (and the cancelled flag) + * - NBCmdComplete must lock the queue before attempting to remove the ncb or + * check the event + * - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue. + * It'll then unlock the queue, and wait on the event in the head of the queue + * until there's no more ncb's in the queue. + * Space optimization: use the handle as a boolean. NULL == 0 => not cancelled. + * Non-NULL == valid handle => cancelled. This allows storing a next pointer + * in the ncb's reserved field as well, avoiding a memory alloc for a new + * command (cool). + */ + +struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap) +{ + struct NBCmdQueue *queue; + + if (heap == NULL) + heap = GetProcessHeap(); + queue = (struct NBCmdQueue *)HeapAlloc(heap, 0, sizeof(struct NBCmdQueue)); + if (queue) + { + queue->heap = heap; + InitializeCriticalSection(&queue->cs); + queue->head = NULL; + } + return queue; +} + +UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb) +{ + UCHAR ret; + + TRACE(": queue %p, ncb %p\n", queue, ncb); + + if (!queue) + return NRC_BADDR; + if (!ncb) + return NRC_INVADDRESS; + + *CANCEL_EVENT_PTR(ncb) = NULL; + EnterCriticalSection(&queue->cs); + *NEXT_PTR(ncb) = queue->head; + queue->head = ncb; + ret = NRC_GOODRET; + LeaveCriticalSection(&queue->cs); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb) +{ + PNCB *ret; + + if (!queue || !ncb) + ret = NULL; + else + { + ret = &queue->head; + while (ret && *ret != ncb) + ret = NEXT_PTR(*ret); + } + return ret; +} + +UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb) +{ + UCHAR ret; + PNCB *spot; + + TRACE(": queue %p, ncb %p\n", queue, ncb); + + if (!queue) + return NRC_BADDR; + if (!ncb) + return NRC_INVADDRESS; + + EnterCriticalSection(&queue->cs); + spot = NBCmdQueueFindNBC(queue, ncb); + if (spot) + { + *CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL); + WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE); + CloseHandle(*CANCEL_EVENT_PTR(*spot)); + *spot = *NEXT_PTR(*spot); + if (ncb->ncb_retcode == NRC_CMDCAN) + ret = NRC_CMDCAN; + else + ret = NRC_CANOCCR; + } + else + ret = NRC_INVADDRESS; + LeaveCriticalSection(&queue->cs); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode) +{ + UCHAR ret; + PNCB *spot; + + TRACE(": queue %p, ncb %p\n", queue, ncb); + + if (!queue) + return NRC_BADDR; + if (!ncb) + return NRC_INVADDRESS; + + EnterCriticalSection(&queue->cs); + spot = NBCmdQueueFindNBC(queue, ncb); + if (spot) + { + if (*CANCEL_EVENT_PTR(*spot)) + SetEvent(*CANCEL_EVENT_PTR(*spot)); + else + *spot = *NEXT_PTR(*spot); + ret = NRC_GOODRET; + } + else + ret = NRC_INVADDRESS; + LeaveCriticalSection(&queue->cs); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue) +{ + UCHAR ret; + + TRACE(": queue %p\n", queue); + + if (!queue) + return NRC_BADDR; + + EnterCriticalSection(&queue->cs); + while (queue->head) + { + TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head, + queue->head->ncb_command); + NBCmdQueueCancel(queue, queue->head); + } + LeaveCriticalSection(&queue->cs); + ret = NRC_GOODRET; + TRACE("returning 0x%02x\n", ret); + return ret; +} + +void NBCmdQueueDestroy(struct NBCmdQueue *queue) +{ + TRACE(": queue %p\n", queue); + + if (queue) + { + NBCmdQueueCancelAll(queue); + DeleteCriticalSection(&queue->cs); + HeapFree(queue->heap, 0, queue); + } +} diff --git a/dlls/netapi32/nbcmdqueue.h b/dlls/netapi32/nbcmdqueue.h new file mode 100644 index 00000000000..f06f95b539e --- /dev/null +++ b/dlls/netapi32/nbcmdqueue.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2003 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __NBCMDQUEUE_H__ +#define __NBCMDQUEUE_H__ + +#include +#include "windef.h" +#include "winbase.h" +#include "nb30.h" + +/* This file defines a queue of pending NetBIOS commands. The queue operations + * are thread safe, with the exception of NBCmdQueueDestroy: ensure no other + * threads are manipulating the queue when calling NBCmdQueueDestroy. + */ + +struct NBCmdQueue; + +/* Allocates a new command queue from heap. */ +struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap); + +/* Adds ncb to queue. Assumes queue is not NULL, and ncb is not already in the + * queue. If ncb is already in the queue, returns NRC_TOOMANY. + */ +UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb); + +/* Cancels the given ncb. Blocks until the command completes. Implicitly + * removes ncb from the queue. Assumes queue and ncb are not NULL, and that + * ncb has been added to queue previously. + * Returns NRC_CMDCAN on a successful cancellation, NRC_CMDOCCR if the command + * completed before it could be cancelled, and various other return values for + * different failures. + */ +UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb); + +/* Sets the return code of the given ncb, and implicitly removes the command + * from the queue. Assumes queue and ncb are not NULL, and that ncb has been + * added to queue previously. + * Returns NRC_GOODRET on success. + */ +UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode); + +/* Cancels all pending commands in the queue (useful for a RESET or a shutdown). + * Returns when all commands have been completed. + */ +UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue); + +/* Frees all memory associated with the queue. Blocks until all commands + * pending in the queue have been completed. + */ +void NBCmdQueueDestroy(struct NBCmdQueue *queue); + +#endif /* __NBCMDQUEUE_H__ */ diff --git a/dlls/netapi32/nbnamecache.c b/dlls/netapi32/nbnamecache.c new file mode 100644 index 00000000000..60ed1f8be92 --- /dev/null +++ b/dlls/netapi32/nbnamecache.c @@ -0,0 +1,215 @@ +/* Copyright (c) 2003 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This implementation uses a linked list, because I don't have a decent + * hash table implementation handy. This is somewhat inefficient, but it's + * rather more efficient than not having a name cache at all. + */ + +#include "wine/debug.h" +#include "nbnamecache.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +typedef struct _NBNameCacheNode +{ + DWORD expireTime; + NBNameCacheEntry *entry; + struct _NBNameCacheNode *next; +} NBNameCacheNode; + +struct NBNameCache +{ + HANDLE heap; + CRITICAL_SECTION cs; + DWORD entryExpireTimeMS; + NBNameCacheNode *head; +}; + +/* Unlinks the node pointed to by *prev, and frees any associated memory. + * If that node's next pointed to another node, *prev now points to it. + * Assumes the caller owns cache's lock. + */ +static void NBNameCacheUnlinkNode(struct NBNameCache *cache, + NBNameCacheNode **prev) +{ + if (cache && prev && *prev) + { + NBNameCacheNode *next = (*prev)->next; + + if ((*prev)->entry) + HeapFree(cache->heap, 0, (*prev)->entry); + HeapFree(cache->heap, 0, *prev); + *prev = next; + } +} + +/* Walks the list beginning with cache->head looking for the node with name + * name. If the node is found, returns a pointer to the next pointer of the + * node _prior_ to the found node (or head if head points to it). Thus, if the + * node's all you want, dereference the return value twice. If you want to + * modify the list, modify the referent of the return value. + * While it's at it, deletes nodes whose time has expired (except the node + * you're looking for, of course). + * Returns NULL if the node isn't found. + * Assumes the caller owns cache's lock. + */ +static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache, + const char name[NCBNAMSZ]) +{ + NBNameCacheNode **ret = NULL; + + if (cache && cache->head) + { + NBNameCacheNode **ptr; + + ptr = &cache->head; + while (ptr && *ptr && (*ptr)->entry) + { + if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1)) + ret = ptr; + else + { + if (GetTickCount() > (*ptr)->expireTime) + NBNameCacheUnlinkNode(cache, ptr); + } + if (*ptr) + ptr = &(*ptr)->next; + } + } + return ret; +} + +struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS) +{ + struct NBNameCache *cache; + + + if (!heap) + heap = GetProcessHeap(); + cache = (struct NBNameCache *)HeapAlloc(heap, 0, + sizeof(struct NBNameCache)); + if (cache) + { + cache->heap = heap; + InitializeCriticalSection(&cache->cs); + cache->entryExpireTimeMS = entryExpireTimeMS; + cache->head = NULL; + } + return cache; +} + +BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry) +{ + BOOL ret; + + if (cache && entry) + { + NBNameCacheNode **node; + + EnterCriticalSection(&cache->cs); + node = NBNameCacheWalk(cache, entry->name); + if (node) + { + (*node)->expireTime = GetTickCount() + + cache->entryExpireTimeMS; + HeapFree(cache->heap, 0, (*node)->entry); + (*node)->entry = entry; + ret = TRUE; + } + else + { + NBNameCacheNode *newNode = (NBNameCacheNode *)HeapAlloc( + cache->heap, 0, sizeof(NBNameCacheNode)); + if (newNode) + { + newNode->expireTime = GetTickCount() + + cache->entryExpireTimeMS; + newNode->entry = entry; + newNode->next = cache->head; + cache->head = newNode; + ret = TRUE; + } + else + ret = FALSE; + } + LeaveCriticalSection(&cache->cs); + } + else + ret = FALSE; + return ret; +} + +const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ]) +{ + const NBNameCacheEntry *ret; + UCHAR printName[NCBNAMSZ]; + + memcpy(printName, name, NCBNAMSZ - 1); + printName[NCBNAMSZ - 1] = '\0'; + if (cache) + { + NBNameCacheNode **node; + + EnterCriticalSection(&cache->cs); + node = NBNameCacheWalk(cache, name); + if (node) + ret = (*node)->entry; + else + ret = NULL; + LeaveCriticalSection(&cache->cs); + } + else + ret = NULL; + return ret; +} + +BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]) +{ + BOOL ret; + + if (cache) + { + NBNameCacheNode **node; + + EnterCriticalSection(&cache->cs); + node = NBNameCacheWalk(cache, name); + if (node && *node && (*node)->entry) + { + memcpy((*node)->entry->nbname, nbname, NCBNAMSZ); + ret = TRUE; + } + else + ret = FALSE; + LeaveCriticalSection(&cache->cs); + } + else + ret = FALSE; + return ret; +} + +void NBNameCacheDestroy(struct NBNameCache *cache) +{ + if (cache) + { + DeleteCriticalSection(&cache->cs); + while (cache->head) + NBNameCacheUnlinkNode(cache, &cache->head); + HeapFree(cache->heap, 0, cache); + } +} diff --git a/dlls/netapi32/nbnamecache.h b/dlls/netapi32/nbnamecache.h new file mode 100644 index 00000000000..22861581af6 --- /dev/null +++ b/dlls/netapi32/nbnamecache.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2003 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __WINE_NBNAMECACHE_H +#define __WINE_NBNAMECACHE_H + +#include "winbase.h" +#include "nb30.h" + +struct NBNameCache; + +/* Represents an entry in the name cache. If the NetBIOS name is known, it's + * in nbname. Otherwise, nbname begins with '*'. numAddresses defines the + * number of addresses in addresses. + * Notice that it allows multiple addresses per name, but doesn't explicitly + * allow group names. That's because all names so far are unique; if a use for + * group names comes up, adding a flag here is simple enough. + * Also, only the first NCBNAMSZ - 1 bytes are considered significant. This is + * because a name may have been resolved using DNS, and the suffix byte is + * always truncated for DNS lookups. + */ +typedef struct _NBNameCacheEntry +{ + UCHAR name[NCBNAMSZ]; + UCHAR nbname[NCBNAMSZ]; + DWORD numAddresses; + DWORD addresses[1]; +} NBNameCacheEntry; + +/* Functions that create, manipulate, and destroy a name cache. Thread-safe, + * with the exception of NBNameCacheDestroy--ensure that no other threads are + * manipulating the cache before destoying it. + */ + +/* Allocates a new name cache from heap, and sets the expire time on new + * entries to entryExpireTimeMS after a cache entry is added. + */ +struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS); + +/* Adds an entry to the cache. The entry is assumed to have been allocated + * from the same heap as the name cache; the name cache will own the entry + * from now on. The entry's expire time is initialized at this time to + * entryExpireTimeMS + the current time in MS. If an existing entry with the + * same name was in the cache, the entry is replaced. Returns TRUE on success + * or FALSE on failure. + */ +BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry); + +/* Finds the entry with name name in the cache and returns a pointer to it, or + * NULL if it isn't found. + */ +const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ]); + +/* If the entry with name name is in the cache, updates its nbname member to + * nbname. The entry's expire time is implicitly updated to entryExpireTimeMS + * + the current time in MS, since getting the NetBIOS name meant validating + * the name and address anyway. + * Returns TRUE on success or FALSE on failure. + */ +BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]); + +void NBNameCacheDestroy(struct NBNameCache *cache); + +#endif /* ndef __WINE_NBNAMECACHE_H */ diff --git a/dlls/netapi32/nbt.c b/dlls/netapi32/nbt.c new file mode 100644 index 00000000000..5b0ff7533ed --- /dev/null +++ b/dlls/netapi32/nbt.c @@ -0,0 +1,1501 @@ +/* Copyright (c) 2003 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * I am heavily indebted to Chris Hertel's excellent Implementing CIFS, + * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT. + * I also stole from Mike McCormack's smb.c and netapi32.c, although little of + * that code remains. + * Lack of understanding and bugs are my fault. + * + * FIXME: + * - Of the NetBIOS session functions, only client functions are supported, and + * it's likely they'll be the only functions supported. NBT requires session + * servers to listen on TCP/139. This requires root privilege, and Samba is + * likely to be listening here already. This further restricts NetBIOS + * applications, both explicit users and implicit ones: CreateNamedPipe + * won't actually create a listening pipe, for example, so applications can't + * act as RPC servers using a named pipe protocol binding, DCOM won't be able + * to support callbacks or servers over the named pipe protocol, etc. + * + * - Datagram support is omitted for the same reason. To send a NetBIOS + * datagram, you must include the NetBIOS name by which your application is + * known. This requires you to have registered the name previously, and be + * able to act as a NetBIOS datagram server (listening on UDP/138). + * + * - Name registration functions are omitted for the same reason--registering a + * name requires you to be able to defend it, and this means listening on + * UDP/137. + * Win98 requires you either use your computer's NetBIOS name (with the NULL + * suffix byte) as the calling name when creating a session, or to register + * a new name before creating one: it disallows '*' as the calling name. + * Win2K initially starts with an empty name table, and doesn't allow you to + * use the machine's NetBIOS name (with the NULL suffix byte) as the calling + * name. Although it allows sessions to be created with '*' as the calling + * name, doing so results in timeouts for all receives, because the + * application never gets them. + * So, a well-behaved Netbios application will typically want to register a + * name. I should probably support a do-nothing name list that allows + * NCBADDNAME to add to it, but doesn't actually register the name, or does + * attempt to register it without being able to defend it. + * + * - Name lookups may not behave quite as you'd expect/like if you have + * multiple LANAs. If a name is resolvable through DNS, or if you're using + * WINS, it'll resolve on _any_ LANA. So, a Call will succeed on any LANA as + * well. + * I'm not sure how Windows behaves in this case. I could try to force + * lookups to the correct adapter by using one of the GetPreferred* + * functions, but with the possibility of multiple adapters in the same + * same subnet, there's no guarantee that what IpHlpApi thinks is the + * preferred adapter will actually be a LANA. (It's highly probable because + * this is an unusual configuration, but not guaranteed.) + * + * See also other FIXMEs in the code. + */ + +#include "config.h" + +#include +#include "windef.h" +#include "winbase.h" +#include "wine/debug.h" +#include "winreg.h" +#include "iphlpapi.h" +#include "winsock2.h" +#include "netbios.h" +#include "nbnamecache.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +#define PORT_NBNS 137 +#define PORT_NBDG 138 +#define PORT_NBSS 139 + +#define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word) +#define NBR_GETWORD(p) ntohs(*(WORD *)(p)) + +#define MIN_QUERIES 1 +#define MAX_QUERIES 0xffff +#define MIN_QUERY_TIMEOUT 100 +#define MAX_QUERY_TIMEOUT 0xffffffff +#define BCAST_QUERIES 3 +#define BCAST_QUERY_TIMEOUT 750 +#define WINS_QUERIES 3 +#define WINS_QUERY_TIMEOUT 750 +#define MAX_WINS_SERVERS 2 +#define MIN_CACHE_TIMEOUT 60000 +#define CACHE_TIMEOUT 360000 + +#define MAX_NBT_NAME_SZ (NCBNAMSZ * 2 + MAX_DOMAIN_NAME_LEN + 2) +#define SIMPLE_NAME_QUERY_PKT_SIZE 26 + MAX_NBT_NAME_SZ + +#define DEFAULT_NBT_SESSIONS 16 + +#define NBNS_TYPE_NB 0x0020 +#define NBNS_TYPE_NBSTAT 0x0021 +#define NBNS_CLASS_INTERNET 0x00001 +#define NBNS_HEADER_SIZE (sizeof(WORD) * 6) +#define NBNS_RESPONSE_AND_OPCODE 0xf800 +#define NBNS_RESPONSE_AND_QUERY 0x8000 +#define NBNS_REPLYCODE 0x0f + +#define NBSS_HDRSIZE 4 + +#define NBSS_MSG 0x00 +#define NBSS_REQ 0x81 +#define NBSS_ACK 0x82 +#define NBSS_NACK 0x83 +#define NBSS_RETARGET 0x84 +#define NBSS_KEEPALIVE 0x85 + +#define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80 +#define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81 +#define NBSS_ERR_BAD_NAME 0x82 +#define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83 + +#define NBSS_EXTENSION 0x01 + +typedef struct _NetBTSession +{ + CRITICAL_SECTION cs; + SOCKET fd; + DWORD bytesPending; +} NetBTSession; + +typedef struct _NetBTAdapter +{ + MIB_IPADDRROW ipr; + WORD nameQueryXID; + struct NBNameCache *nameCache; + DWORD xmit_success; + DWORD recv_success; +} NetBTAdapter; + +static ULONG gTransportID; +static DWORD gBCastQueries; +static DWORD gBCastQueryTimeout; +static DWORD gWINSQueries; +static DWORD gWINSQueryTimeout; +static DWORD gWINSServers[MAX_WINS_SERVERS]; +static int gNumWINSServers; +static char gScopeID[MAX_DOMAIN_NAME_LEN]; +static DWORD gCacheTimeout; +static struct NBNameCache *gNameCache; + +/* Converts from a NetBIOS name into a Second Level Encoding-formatted name. + * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ + * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes + * if p is NULL-terminated. Returns the number of bytes stored in buffer. + */ +static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer) +{ + int i,len=0; + + if (!p) return 0; + if (!buffer) return 0; + + buffer[len++] = NCBNAMSZ * 2; + for (i = 0; p[i] && i < NCBNAMSZ; i++) + { + buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A'; + buffer[len++] = (p[i] & 0x0f) + 'A'; + } + while (len < NCBNAMSZ * 2) + { + buffer[len++] = 'C'; + buffer[len++] = 'A'; + } + if (*gScopeID) + { + int scopeIDLen = strlen(gScopeID); + + memcpy(buffer + len, gScopeID, scopeIDLen); + len += scopeIDLen; + } + buffer[len++] = 0; /* add second terminator */ + return len; +} + +/* Creates a NBT name request packet for name in buffer. If broadcast is true, + * creates a broadcast request, otherwise creates a unicast request. + * Returns the number of bytes stored in buffer. + */ +static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype, + BOOL broadcast, UCHAR *buffer, int len) +{ + int i = 0; + + if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0; + + NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */ + if (broadcast) + { + NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */ + i+=2; + } + else + { + NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */ + i+=2; + } + NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */ + NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */ + NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */ + NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */ + + i += NetBTNameEncode(name, &buffer[i]); + + NBR_ADDWORD(&buffer[i],qtype); i+=2; + NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2; + + return i; +} + +/* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on + * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not + * NULL. + * Returns 0 on success, -1 on failure. + */ +static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid, + WORD qtype, DWORD destAddr, BOOL broadcast) +{ + int ret = 0, on = 1; + struct in_addr addr; + + addr.s_addr = destAddr; + TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr)); + + if (broadcast) + ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (PUCHAR)&on, sizeof(on)); + if(ret == 0) + { + WSABUF wsaBuf; + UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE]; + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_addr.s_addr = destAddr; + sin.sin_family = AF_INET; + sin.sin_port = htons(PORT_NBNS); + + wsaBuf.buf = buf; + wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf, + sizeof(buf)); + if (wsaBuf.len > 0) + { + DWORD bytesSent; + + ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0, + (struct sockaddr*)&sin, sizeof(sin), NULL, NULL); + if (ret < 0 || bytesSent < wsaBuf.len) + ret = -1; + else + ret = 0; + } + else + ret = -1; + } + return ret; +} + +typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount, + WORD answerIndex, PUCHAR rData, WORD rdLength); + +/* Waits on fd until GetTickCount() returns a value greater than or equal to + * waitUntil for a name service response. If a name response matching xid + * is received, calls answerCallback once for each answer resource record in + * the response. (The callback's answerCount will be the total number of + * answers to expect, and answerIndex will be the 0-based index that's being + * sent this time.) Quits parsing if answerCallback returns FALSE. + * Returns NRC_GOODRET on timeout or a valid response received, something else + * on error. + */ +static UCHAR NetBTWaitForNameResponse(NetBTAdapter *adapter, SOCKET fd, + DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data) +{ + BOOL found = FALSE; + DWORD now; + UCHAR ret = NRC_GOODRET; + + if (!adapter) return NRC_BADDR; + if (fd == INVALID_SOCKET) return NRC_BADDR; + if (!answerCallback) return NRC_BADDR; + + while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil) + { + DWORD msToWait = waitUntil - now; + FD_SET fds; + struct timeval timeout = { msToWait / 1000, msToWait % 1000 }; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + r = select(fd + 1, &fds, NULL, NULL, &timeout); + if (r < 0) + ret = NRC_SYSTEM; + else if (r == 1) + { + /* FIXME: magic #, is this always enough? */ + UCHAR buffer[256]; + int fromsize; + struct sockaddr_in fromaddr; + WORD respXID, flags, queryCount, answerCount; + WSABUF wsaBuf = { sizeof(buffer), buffer }; + DWORD bytesReceived, recvFlags = 0; + + fromsize = sizeof(fromaddr); + r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, + (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL); + if(r < 0) + { + ret = NRC_SYSTEM; + break; + } + + if (bytesReceived < NBNS_HEADER_SIZE) + continue; + + respXID = NBR_GETWORD(buffer); + if (adapter->nameQueryXID != respXID) + continue; + + flags = NBR_GETWORD(buffer + 2); + queryCount = NBR_GETWORD(buffer + 4); + answerCount = NBR_GETWORD(buffer + 6); + + /* a reply shouldn't contain a query, ignore bad packet */ + if (queryCount > 0) + continue; + + if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY) + { + if ((flags & NBNS_REPLYCODE) != 0) + ret = NRC_NAMERR; + else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0) + { + PUCHAR ptr = buffer + NBNS_HEADER_SIZE; + BOOL shouldContinue = TRUE; + WORD answerIndex = 0; + + found = TRUE; + /* decode one answer at a time */ + while (ret == NRC_GOODRET && answerIndex < answerCount && + ptr - buffer < bytesReceived && shouldContinue) + { + WORD rLen; + + /* scan past name */ + for (; ptr[0] && ptr - buffer < bytesReceived; ) + ptr += ptr[0] + 1; + ptr++; + ptr += 2; /* scan past type */ + if (ptr - buffer < bytesReceived && ret == NRC_GOODRET + && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET) + ptr += sizeof(WORD); + else + ret = NRC_SYSTEM; /* parse error */ + ptr += sizeof(DWORD); /* TTL */ + rLen = NBR_GETWORD(ptr); + rLen = min(rLen, bytesReceived - (ptr - buffer)); + ptr += sizeof(WORD); + shouldContinue = answerCallback(data, answerCount, + answerIndex, ptr, rLen); + ptr += rLen; + answerIndex++; + } + } + } + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +typedef struct _NetBTNameQueryData { + NBNameCacheEntry *cacheEntry; + UCHAR ret; +} NetBTNameQueryData; + +/* Name query callback function for NetBTWaitForNameResponse, creates a cache + * entry on the first answer, adds each address as it's called again (as long + * as there's space). If there's an error that should be propagated as the + * NetBIOS error, modifies queryData's ret member to the proper return code. + */ +static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount, + WORD answerIndex, PUCHAR rData, WORD rLen) +{ + NetBTNameQueryData *queryData = (NetBTNameQueryData *)pVoid; + BOOL ret; + + if (queryData) + { + if (queryData->cacheEntry == NULL) + { + queryData->cacheEntry = (NBNameCacheEntry *)HeapAlloc( + GetProcessHeap(), 0, sizeof(NBNameCacheEntry) + + (answerCount - 1) * sizeof(DWORD)); + if (queryData->cacheEntry) + queryData->cacheEntry->numAddresses = 0; + else + { + ret = FALSE; + queryData->ret = NRC_OSRESNOTAV; + } + } + if (rLen == 6 && queryData->cacheEntry && + queryData->cacheEntry->numAddresses < answerCount) + { + queryData->cacheEntry->addresses[queryData->cacheEntry-> + numAddresses++] = *(PDWORD)(rData + 2); + ret = queryData->cacheEntry->numAddresses < answerCount; + } + else + ret = FALSE; + } + else + ret = FALSE; + return ret; +} + +/* Workhorse NetBT name lookup function. Sends a name lookup query for + * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using + * adapter->nameQueryXID as the transaction ID. Waits up to timeout + * milliseconds, and retries up to maxQueries times, waiting for a reply. + * If a valid response is received, stores the looked up addresses as a + * NBNameCacheEntry in *cacheEntry. + * Returns NRC_GOODRET on success, though this may not mean the name was + * resolved--check whether *cacheEntry is NULL. + */ +static UCHAR NetBTNameWaitLoop(NetBTAdapter *adapter, SOCKET fd, PNCB ncb, + DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries, + NBNameCacheEntry **cacheEntry) +{ + int queries; + NetBTNameQueryData queryData; + + if (!adapter) return NRC_BADDR; + if (fd == INVALID_SOCKET) return NRC_BADDR; + if (!ncb) return NRC_BADDR; + if (!cacheEntry) return NRC_BADDR; + + queryData.cacheEntry = NULL; + queryData.ret = NRC_GOODRET; + for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries; + queries++) + { + if (!NCB_CANCELLED(ncb)) + { + int r = NetBTSendNameQuery(fd, ncb->ncb_callname, + adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast); + + if (r == 0) + queryData.ret = NetBTWaitForNameResponse(adapter, fd, + GetTickCount() + timeout, NetBTFindNameAnswerCallback, + &queryData); + else + queryData.ret = NRC_SYSTEM; + } + else + queryData.ret = NRC_CMDCAN; + } + if (queryData.cacheEntry) + { + memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ); + memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ); + } + *cacheEntry = queryData.cacheEntry; + return queryData.ret; +} + +/* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache + * has not yet been created, creates it, using gCacheTimeout as the cache + * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails, + * frees cacheEntry. + * Returns NRC_GOODRET on success, and something else on failure. + */ +static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache, + NBNameCacheEntry *cacheEntry) +{ + UCHAR ret; + + if (!nameCache) return NRC_BADDR; + if (!cacheEntry) return NRC_BADDR; + + if (!*nameCache) + *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout); + if (*nameCache) + ret = NBNameCacheAddEntry(*nameCache, cacheEntry) + ? NRC_GOODRET : NRC_OSRESNOTAV; + else + { + HeapFree(GetProcessHeap(), 0, cacheEntry); + ret = NRC_OSRESNOTAV; + } + return ret; +} + +/* Attempts to look up name using gethostbyname(), if the suffix byte is either + * <00> or <20>. If the name can be looked up, returns 0 and stores the looked + * up addresses as a NBNameCacheEntry in *cacheEntry. + * Returns NRC_GOODRET on success, though this may not mean the name was + * resolved--check whether *cacheEntry is NULL. Returns something else on + * error. + */ +static UCHAR NetBTgethostbyname(const UCHAR name[NCBNAMSZ], + NBNameCacheEntry **cacheEntry) +{ + UCHAR ret = NRC_GOODRET; + + TRACE("name %s, cacheEntry %p\n", name, cacheEntry); + + if (!name) return NRC_BADDR; + if (!cacheEntry) return NRC_BADDR; + + if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 || + name[NCBNAMSZ - 1] == 0x20)) + { + UCHAR toLookup[NCBNAMSZ]; + int i; + + for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++) + toLookup[i] = name[i]; + toLookup[i] = '\0'; + + if (isdigit(toLookup[0])) + { + unsigned long addr = inet_addr(toLookup); + + if (addr != INADDR_NONE) + { + *cacheEntry = (NBNameCacheEntry *)HeapAlloc(GetProcessHeap(), + 0, sizeof(NBNameCacheEntry)); + if (*cacheEntry) + { + memcpy((*cacheEntry)->name, name, NCBNAMSZ); + memset((*cacheEntry)->nbname, 0, NCBNAMSZ); + (*cacheEntry)->nbname[0] = '*'; + (*cacheEntry)->numAddresses = 1; + (*cacheEntry)->addresses[0] = addr; + } + else + ret = NRC_OSRESNOTAV; + } + } + if (ret == NRC_GOODRET && !*cacheEntry) + { + struct hostent *host; + + if ((host = gethostbyname(toLookup)) != NULL) + { + for (i = 0; ret == NRC_GOODRET && host->h_addr_list && + host->h_addr_list[i]; i++) + ; + if (host->h_addr_list && host->h_addr_list[0]) + { + *cacheEntry = (NBNameCacheEntry *)HeapAlloc( + GetProcessHeap(), 0, sizeof(NBNameCacheEntry) + + (i - 1) * sizeof(DWORD)); + if (*cacheEntry) + { + memcpy((*cacheEntry)->name, name, NCBNAMSZ); + memset((*cacheEntry)->nbname, 0, NCBNAMSZ); + (*cacheEntry)->nbname[0] = '*'; + (*cacheEntry)->numAddresses = i; + for (i = 0; i < (*cacheEntry)->numAddresses; i++) + (*cacheEntry)->addresses[i] = + (DWORD)host->h_addr_list[i]; + } + else + ret = NRC_OSRESNOTAV; + } + } + } + } + + TRACE("returning 0x%02x\n", ret); + return ret; +} + +/* Looks up the name in ncb->ncb_callname, first in the name caches (global + * and this adapter's), then using gethostbyname(), next by WINS if configured, + * and finally using broadcast NetBT name resolution. In NBT parlance, this + * makes this an "H-node". Stores an entry in the appropriate name cache for a + * found node, and returns it as *cacheEntry. + * Assumes data, ncb, and cacheEntry are not NULL. + * Returns NRC_GOODRET on success--which doesn't mean the name was resolved, + * just that all name lookup operations completed successfully--and something + * else on failure. *cacheEntry will be NULL if the name was not found. + */ +static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb, + const NBNameCacheEntry **cacheEntry) +{ + UCHAR ret = NRC_GOODRET; + + TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry); + + if (!cacheEntry) return NRC_BADDR; + *cacheEntry = NULL; + + if (!adapter) return NRC_BADDR; + if (!ncb) return NRC_BADDR; + + if (ncb->ncb_callname[0] == '*') + ret = NRC_NOWILD; + else + { + *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname); + if (!*cacheEntry) + *cacheEntry = NBNameCacheFindEntry(adapter->nameCache, + ncb->ncb_callname); + if (!*cacheEntry) + { + NBNameCacheEntry *newEntry = NULL; + + ret = NetBTgethostbyname(ncb->ncb_callname, &newEntry); + if (ret == NRC_GOODRET && newEntry) + { + ret = NetBTStoreCacheEntry(&gNameCache, newEntry); + if (ret != NRC_GOODRET) + newEntry = NULL; + } + else + { + SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, + 0, WSA_FLAG_OVERLAPPED); + + if(fd == INVALID_SOCKET) + ret = NRC_OSRESNOTAV; + else + { + int winsNdx; + + adapter->nameQueryXID++; + for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL + && winsNdx < gNumWINSServers; winsNdx++) + ret = NetBTNameWaitLoop(adapter, fd, ncb, + gWINSServers[winsNdx], FALSE, gWINSQueryTimeout, + gWINSQueries, &newEntry); + if (ret == NRC_GOODRET && newEntry) + { + ret = NetBTStoreCacheEntry(&gNameCache, newEntry); + if (ret != NRC_GOODRET) + newEntry = NULL; + } + if (ret == NRC_GOODRET && *cacheEntry == NULL) + { + ret = NetBTNameWaitLoop(adapter, fd, ncb, + adapter->ipr.dwBCastAddr, TRUE, gBCastQueryTimeout, + gBCastQueries, &newEntry); + if (ret == NRC_GOODRET && newEntry) + { + ret = NetBTStoreCacheEntry(&adapter->nameCache, + newEntry); + if (ret != NRC_GOODRET) + newEntry = NULL; + } + } + closesocket(fd); + } + } + *cacheEntry = newEntry; + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +typedef struct _NetBTNodeQueryData +{ + BOOL gotResponse; + PADAPTER_STATUS astat; + WORD astatLen; +} NetBTNodeQueryData; + +/* Callback function for NetBTAstatRemote, parses the rData for the node + * status and name list of the remote node. Always returns FALSE, since + * there's never more than one answer we care about in a node status response. + */ +static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount, + WORD answerIndex, PUCHAR rData, WORD rLen) +{ + NetBTNodeQueryData *data = (NetBTNodeQueryData *)pVoid; + + if (data && !data->gotResponse && rData && rLen >= 1) + { + /* num names is first byte; each name is NCBNAMSZ + 2 bytes */ + if (rLen >= rData[0] * (NCBNAMSZ + 2)) + { + WORD i; + PUCHAR src; + PNAME_BUFFER dst; + + data->gotResponse = TRUE; + data->astat->name_count = rData[0]; + for (i = 0, src = rData + 1, + dst = (PNAME_BUFFER)((PUCHAR)data->astat + + sizeof(ADAPTER_STATUS)); + i < data->astat->name_count && src - rData < rLen && + (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen; + i++, dst++, src += NCBNAMSZ + 2) + { + UCHAR flags = *(src + NCBNAMSZ); + + memcpy(dst->name, src, NCBNAMSZ); + /* we won't actually see a registering name in the returned + * response. It's useful to see if no other flags are set; if + * none are, then the name is registered. */ + dst->name_flags = REGISTERING; + if (flags & 0x80) + dst->name_flags |= GROUP_NAME; + if (flags & 0x10) + dst->name_flags |= DEREGISTERED; + if (flags & 0x08) + dst->name_flags |= DUPLICATE; + if (dst->name_flags == REGISTERING) + dst->name_flags = REGISTERED; + } + /* arbitrarily set HW type to Ethernet */ + data->astat->adapter_type = 0xfe; + if (src - rData < rLen) + memcpy(data->astat->adapter_address, src, + min(rLen - (src - rData), 6)); + } + } + return FALSE; +} + +/* This uses the WINS timeout and query values, as they're the + * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs. + */ +static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb) +{ + UCHAR ret = NRC_GOODRET; + const NBNameCacheEntry *cacheEntry = NULL; + + TRACE("adapter %p, NCB %p\n", adapter, ncb); + + if (!adapter) return NRC_BADDR; + if (!ncb) return NRC_INVADDRESS; + + ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); + if (ret == NRC_GOODRET && cacheEntry) + { + if (cacheEntry->numAddresses > 0) + { + SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, + WSA_FLAG_OVERLAPPED); + + if(fd == INVALID_SOCKET) + ret = NRC_OSRESNOTAV; + else + { + NetBTNodeQueryData queryData; + DWORD queries; + PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; + + adapter->nameQueryXID++; + astat->name_count = 0; + queryData.gotResponse = FALSE; + queryData.astat = astat; + queryData.astatLen = ncb->ncb_length; + for (queries = 0; !queryData.gotResponse && + queries < gWINSQueries; queries++) + { + if (!NCB_CANCELLED(ncb)) + { + int r = NetBTSendNameQuery(fd, ncb->ncb_callname, + adapter->nameQueryXID, NBNS_TYPE_NBSTAT, + cacheEntry->addresses[0], FALSE); + + if (r == 0) + ret = NetBTWaitForNameResponse(adapter, fd, + GetTickCount() + gWINSQueryTimeout, + NetBTNodeStatusAnswerCallback, &queryData); + else + ret = NRC_SYSTEM; + } + else + ret = NRC_CMDCAN; + } + closesocket(fd); + } + } + else + ret = NRC_CMDTMO; + } + else if (ret == NRC_CMDCAN) + ; /* do nothing, we were cancelled */ + else + ret = NRC_CMDTMO; + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTAstat(void *adapt, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + UCHAR ret; + + TRACE("adapt %p, NCB %p\n", adapt, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN; + + if (ncb->ncb_callname[0] == '*') + { + DWORD physAddrLen; + MIB_IFROW ifRow; + PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; + + memset(astat, 0, sizeof(ADAPTER_STATUS)); + astat->rev_major = 3; + ifRow.dwIndex = adapter->ipr.dwIndex; + if (GetIfEntry(&ifRow) != NO_ERROR) + ret = NRC_BRIDGE; + else + { + physAddrLen = min(ifRow.dwPhysAddrLen, 6); + if (physAddrLen > 0) + memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen); + /* doubt anyone cares, but why not.. */ + if (ifRow.dwType == MIB_IF_TYPE_TOKENRING) + astat->adapter_type = 0xff; + else + astat->adapter_type = 0xfe; /* for Ethernet */ + astat->max_sess_pktsize = 0xffff; + astat->xmit_success = adapter->xmit_success; + astat->recv_success = adapter->recv_success; + } + ret = NRC_GOODRET; + } + else + ret = NetBTAstatRemote(adapter, ncb); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTFindName(void *adapt, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + UCHAR ret; + const NBNameCacheEntry *cacheEntry = NULL; + PFIND_NAME_HEADER foundName; + + TRACE("adapt %p, NCB %p\n", adapt, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN; + + foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer; + memset(foundName, 0, sizeof(FIND_NAME_HEADER)); + + ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); + if (ret == NRC_GOODRET) + { + if (cacheEntry) + { + DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) / + sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses); + DWORD ndx; + + for (ndx = 0; ndx < spaceFor; ndx++) + { + PFIND_NAME_BUFFER findNameBuffer; + + findNameBuffer = + (PFIND_NAME_BUFFER)((PUCHAR)foundName + + sizeof(FIND_NAME_HEADER) + foundName->node_count * + sizeof(FIND_NAME_BUFFER)); + memset(findNameBuffer->destination_addr, 0, 2); + memcpy(findNameBuffer->destination_addr + 2, + &adapter->ipr.dwAddr, sizeof(DWORD)); + memset(findNameBuffer->source_addr, 0, 2); + memcpy(findNameBuffer->source_addr + 2, + &cacheEntry->addresses[ndx], sizeof(DWORD)); + foundName->node_count++; + } + if (spaceFor < cacheEntry->numAddresses) + ret = NRC_BUFLEN; + } + else + ret = NRC_CMDTMO; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName, + const UCHAR *callingName) +{ + UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret; + int len = 0, r; + DWORD bytesSent, bytesReceived, recvFlags = 0; + WSABUF wsaBuf; + + buffer[0] = NBSS_REQ; + buffer[1] = 0; + + len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]); + len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]); + + NBR_ADDWORD(&buffer[2], len); + + wsaBuf.len = len + NBSS_HDRSIZE; + wsaBuf.buf = buffer; + + r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL); + if(r < 0 || bytesSent < len + NBSS_HDRSIZE) + { + ERR("send failed\n"); + return NRC_SABORT; + } + + /* I've already set the recv timeout on this socket (if it supports it), so + * just block. Hopefully we'll always receive the session acknowledgement + * within one timeout. + */ + wsaBuf.len = NBSS_HDRSIZE + 1; + r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL); + if (r < 0 || bytesReceived < NBSS_HDRSIZE) + ret = NRC_SABORT; + else if (buffer[0] == NBSS_NACK) + { + if (r == NBSS_HDRSIZE + 1) + { + switch (buffer[NBSS_HDRSIZE]) + { + case NBSS_ERR_INSUFFICIENT_RESOURCES: + ret = NRC_REMTFUL; + break; + default: + ret = NRC_NOCALL; + } + } + else + ret = NRC_NOCALL; + } + else if (buffer[0] == NBSS_RETARGET) + { + FIXME("Got a session retarget, can't deal\n"); + ret = NRC_NOCALL; + } + else if (buffer[0] == NBSS_ACK) + ret = NRC_GOODRET; + else + ret = NRC_SYSTEM; + + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + UCHAR ret; + const NBNameCacheEntry *cacheEntry = NULL; + + TRACE("adapt %p, ncb %p", adapt, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!sess) return NRC_BADDR; + + ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); + if (ret == NRC_GOODRET) + { + if (cacheEntry && cacheEntry->numAddresses > 0) + { + SOCKET fd; + + fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (fd != INVALID_SOCKET) + { + DWORD timeout; + struct sockaddr_in sin; + + if (ncb->ncb_rto > 0) + { + timeout = ncb->ncb_rto * 500; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (PUCHAR)&timeout, + sizeof(timeout)); + } + if (ncb->ncb_rto > 0) + { + timeout = ncb->ncb_sto * 500; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (PUCHAR)&timeout, + sizeof(timeout)); + } + + memset(&sin, 0, sizeof(sin)); + memcpy(&sin.sin_addr, &cacheEntry->addresses[0], + sizeof(sin.sin_addr)); + sin.sin_family = AF_INET; + sin.sin_port = htons(PORT_NBSS); + /* FIXME: use nonblocking mode for the socket, check the + * cancel flag periodically + */ + if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) + == SOCKET_ERROR) + ret = NRC_CMDTMO; + else + { + static UCHAR fakedCalledName[] = "*SMBSERVER"; + const UCHAR *calledParty = cacheEntry->nbname[0] == '*' + ? fakedCalledName : cacheEntry->nbname; + + ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name); + if (ret != NRC_GOODRET && calledParty[0] == '*') + { + FIXME("NBT session to \"*SMBSERVER\" refused,\n"); + FIXME("should try finding name using ASTAT\n"); + } + } + if (ret != NRC_GOODRET) + closesocket(fd); + else + { + NetBTSession *session = (NetBTSession *)HeapAlloc( + GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession)); + + if (session) + { + session->fd = fd; + InitializeCriticalSection(&session->cs); + *sess = session; + } + else + { + ret = NRC_OSRESNOTAV; + closesocket(fd); + } + } + } + else + ret = NRC_OSRESNOTAV; + } + else + ret = NRC_NAMERR; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +/* Notice that I don't protect against multiple thread access to NetBTSend. + * This is because I don't update any data in the adapter, and I only make a + * single call to WSASend, which I assume to act atomically (not interleaving + * data from other threads). + * I don't lock, because I only depend on the fd being valid, and this won't be + * true until a session setup is completed. + */ +static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + NetBTSession *session = (NetBTSession *)sess; + UCHAR buffer[NBSS_HDRSIZE], ret; + int r; + WSABUF wsaBufs[2]; + DWORD bytesSent; + + TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (!session) return NRC_SNUMOUT; + if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT; + + buffer[0] = NBSS_MSG; + buffer[1] = 0; + NBR_ADDWORD(&buffer[2], ncb->ncb_length); + + wsaBufs[0].len = NBSS_HDRSIZE; + wsaBufs[0].buf = buffer; + wsaBufs[1].len = ncb->ncb_length; + wsaBufs[1].buf = ncb->ncb_buffer; + + r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]), + &bytesSent, 0, NULL, NULL); + if (r == SOCKET_ERROR) + { + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length) + { + FIXME("Only sent %ld bytes (of %d), hanging up session\n", bytesSent, + NBSS_HDRSIZE + ncb->ncb_length); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else + { + ret = NRC_GOODRET; + adapter->xmit_success++; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + NetBTSession *session = (NetBTSession *)sess; + UCHAR buffer[NBSS_HDRSIZE], ret; + int r; + WSABUF wsaBufs[2]; + DWORD bufferCount, bytesReceived, flags; + + TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_BADDR; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (!session) return NRC_SNUMOUT; + if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT; + + EnterCriticalSection(&session->cs); + bufferCount = 0; + if (session->bytesPending == 0) + { + bufferCount++; + wsaBufs[0].len = NBSS_HDRSIZE; + wsaBufs[0].buf = buffer; + } + wsaBufs[bufferCount].len = ncb->ncb_length; + wsaBufs[bufferCount].buf = ncb->ncb_buffer; + bufferCount++; + + flags = 0; + /* FIXME: should poll a bit so I can check the cancel flag */ + r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags, + NULL, NULL); + if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) + { + LeaveCriticalSection(&session->cs); + ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError()); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else if (NCB_CANCELLED(ncb)) + { + LeaveCriticalSection(&session->cs); + ret = NRC_CMDCAN; + } + else + { + if (bufferCount == 2) + { + if (buffer[0] == NBSS_KEEPALIVE) + { + LeaveCriticalSection(&session->cs); + FIXME("Oops, received a session keepalive and lost my place\n"); + /* need to read another session header until we get a session + * message header. */ + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else if (buffer[0] != NBSS_MSG) + { + LeaveCriticalSection(&session->cs); + FIXME("Received unexpected session msg type %d\n", buffer[0]); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else + { + if (buffer[1] & NBSS_EXTENSION) + { + LeaveCriticalSection(&session->cs); + FIXME("Received a message that's too long for my taste\n"); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else + { + session->bytesPending = NBSS_HDRSIZE + + NBR_GETWORD(&buffer[2]) - bytesReceived; + ncb->ncb_length = bytesReceived - NBSS_HDRSIZE; + LeaveCriticalSection(&session->cs); + } + } + } + else + { + if (bytesReceived < session->bytesPending) + session->bytesPending -= bytesReceived; + else + session->bytesPending = 0; + LeaveCriticalSection(&session->cs); + ncb->ncb_length = bytesReceived; + } + if (session->bytesPending > 0) + ret = NRC_INCOMP; + else + { + ret = NRC_GOODRET; + adapter->recv_success++; + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTHangup(void *adapt, void *sess) +{ + NetBTSession *session = (NetBTSession *)sess; + + TRACE("adapt %p, session %p\n", adapt, session); + + if (!session) return NRC_SNUMOUT; + + /* I don't lock the session, because NetBTRecv knows not to decrement + * past 0, so if a receive completes after this it should still deal. + */ + closesocket(session->fd); + session->fd = INVALID_SOCKET; + session->bytesPending = 0; + DeleteCriticalSection(&session->cs); + HeapFree(GetProcessHeap(), 0, session); + + return NRC_GOODRET; +} + +static void NetBTCleanupAdapter(void *adapt) +{ + TRACE("adapt %p\n", adapt); + if (adapt) + { + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + + if (adapter->nameCache) + NBNameCacheDestroy(adapter->nameCache); + HeapFree(GetProcessHeap(), 0, adapt); + } +} + +static void NetBTCleanup(void) +{ + TRACE("\n"); + if (gNameCache) + { + NBNameCacheDestroy(gNameCache); + gNameCache = NULL; + } +} + +static UCHAR NetBTRegisterAdapter(PMIB_IPADDRROW ipRow) +{ + UCHAR ret; + NetBTAdapter *adapter; + + if (!ipRow) return NRC_BADDR; + + adapter = (NetBTAdapter *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(NetBTAdapter)); + if (adapter) + { + memcpy(&adapter->ipr, ipRow, sizeof(MIB_IPADDRROW)); + if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter)) + { + NetBTCleanupAdapter(adapter); + ret = NRC_SYSTEM; + } + else + ret = NRC_GOODRET; + } + else + ret = NRC_OSRESNOTAV; + return ret; +} + +/* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to + * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the + * NetBIOS adapter table. For each callback, checks if the passed-in adapt + * has an entry in the table; if so, this adapter was enumerated previously, + * and it's enabled. As a flag, the table's dwAddr entry is changed to + * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter. + * The NetBTEnum function will add any remaining adapters from the + * MIB_IPADDRTABLE to the NetBIOS adapter table. + */ +static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex, + ULONG transport, const NetBIOSAdapterImpl *data, void *closure) +{ + BOOL ret; + PMIB_IPADDRTABLE table = (PMIB_IPADDRTABLE)closure; + + if (table && data) + { + DWORD ndx; + + ret = FALSE; + for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++) + { + const NetBTAdapter *adapter = (const NetBTAdapter *)data->data; + + if (table->table[ndx].dwIndex == adapter->ipr.dwIndex) + { + NetBIOSEnableAdapter(data->lana); + table->table[ndx].dwAddr = INADDR_LOOPBACK; + ret = TRUE; + } + } + } + else + ret = FALSE; + return ret; +} + +/* Enumerates adapters by: + * - retrieving the IP address table for the local machine + * - eliminating loopback addresses from the table + * - eliminating redundant addresses, that is, multiple addresses on the same + * subnet + * Calls NetBIOSEnumAdapters, passing the resulting table as the callback + * data. The callback reenables each adapter that's already in the NetBIOS + * table. After NetBIOSEnumAdapters returns, this function adds any remaining + * adapters to the NetBIOS table. + */ +static UCHAR NetBTEnum(void) +{ + UCHAR ret; + DWORD size = 0; + + TRACE("\n"); + + if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER) + { + PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL; + DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) / + sizeof(MIB_IPADDRROW) + 1; + + ipAddrs = (PMIB_IPADDRTABLE)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, size); + if (ipAddrs) + coalesceTable = (PMIB_IPADDRTABLE)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) + + (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW)); + if (ipAddrs && coalesceTable) + { + if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS) + { + DWORD ndx; + + for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++) + { + if ((ipAddrs->table[ndx].dwAddr & + ipAddrs->table[ndx].dwMask) != + htonl((INADDR_LOOPBACK & IN_CLASSA_NET))) + { + BOOL newNetwork = TRUE; + DWORD innerIndex; + + /* make sure we don't have more than one entry + * for a subnet */ + for (innerIndex = 0; newNetwork && + innerIndex < coalesceTable->dwNumEntries; innerIndex++) + if ((ipAddrs->table[ndx].dwAddr & + ipAddrs->table[ndx].dwMask) == + (coalesceTable->table[innerIndex].dwAddr + & coalesceTable->table[innerIndex].dwMask)) + newNetwork = FALSE; + + if (newNetwork) + memcpy(&coalesceTable->table[ + coalesceTable->dwNumEntries++], + &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW)); + } + } + + NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback, + coalesceTable); + ret = NRC_GOODRET; + for (ndx = 0; ret == NRC_GOODRET && + ndx < coalesceTable->dwNumEntries; ndx++) + if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK) + ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]); + } + else + ret = NRC_SYSTEM; + HeapFree(GetProcessHeap(), 0, ipAddrs); + HeapFree(GetProcessHeap(), 0, coalesceTable); + } + else + ret = NRC_OSRESNOTAV; + } + else + ret = NRC_SYSTEM; + TRACE("returning 0x%02x\n", ret); + return ret; +} + +/* Initializes global variables and registers the NetBT transport */ +void NetBTInit(void) +{ + HKEY hKey; + NetBIOSTransport transport; + + TRACE("\n"); + + gBCastQueries = BCAST_QUERIES; + gBCastQueryTimeout = BCAST_QUERY_TIMEOUT; + gWINSQueries = WINS_QUERIES; + gWINSQueryTimeout = WINS_QUERY_TIMEOUT; + gNumWINSServers = 0; + memset(gWINSServers, 0, sizeof(gWINSServers)); + gScopeID[0] = '\0'; + gCacheTimeout = CACHE_TIMEOUT; + + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "\\SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0, KEY_READ, &hKey) + == ERROR_SUCCESS) + { + DWORD dword, size = sizeof(dword), ndx; + static const char *nsValueNames[] = + { "NameServer", "BackupNameServer" }; + char nsString[16]; + + size = sizeof(dword); + if (RegQueryValueExA(hKey, "BcastNameQueryCount", NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES + && dword <= MAX_QUERIES) + gBCastQueries = dword; + size = sizeof(dword); + if (RegQueryValueExA(hKey, "BcastNameQueryTimeout", NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT + && dword <= MAX_QUERY_TIMEOUT) + gBCastQueryTimeout = dword; + size = sizeof(dword); + if (RegQueryValueExA(hKey, "NameSrvQueryCount", NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES + && dword <= MAX_QUERIES) + gWINSQueries = dword; + size = sizeof(dword); + if (RegQueryValueExA(hKey, "NameSrvQueryTimeout", NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT + && dword <= MAX_QUERY_TIMEOUT) + gWINSQueryTimeout = dword; + for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]); + ndx++) + { + size = sizeof(nsString) / sizeof(char); + if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL, + (LPBYTE)nsString, &size) == ERROR_SUCCESS) + { + unsigned long addr = inet_addr(nsString); + + if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS) + gWINSServers[gNumWINSServers++] = addr; + } + } + size = MAX_DOMAIN_NAME_LEN - 1; + if (RegQueryValueExA(hKey, "ScopeID", NULL, NULL, gScopeID + 1, &size) + == ERROR_SUCCESS) + { + /* convert into L2-encoded version, suitable for use by + NetBTNameEncode */ + char *ptr, *lenPtr; + + for (ptr = gScopeID + 1; *ptr && + ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ) + { + for (lenPtr = ptr - 1, *lenPtr = 0; *ptr && *ptr != '.' && + ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ptr++) + *lenPtr += 1; + ptr++; + } + } + if (RegQueryValueExA(hKey, "CacheTimeout", NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT) + gCacheTimeout = dword; + RegCloseKey(hKey); + } + + transport.enumerate = NetBTEnum; + transport.astat = NetBTAstat; + transport.findName = NetBTFindName; + transport.call = NetBTCall; + transport.send = NetBTSend; + transport.recv = NetBTRecv; + transport.hangup = NetBTHangup; + transport.cleanupAdapter = NetBTCleanupAdapter; + transport.cleanup = NetBTCleanup; + memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG)); + NetBIOSRegisterTransport(gTransportID, &transport); +} diff --git a/dlls/netapi32/netapi32.c b/dlls/netapi32/netapi32.c index 05540d013c2..f62ea1d8778 100644 --- a/dlls/netapi32/netapi32.c +++ b/dlls/netapi32/netapi32.c @@ -1,5 +1,5 @@ -/* - * Copyright 2001 Mike McCormack +/* Copyright 2001 Mike McCormack + * Copyright 2003 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,185 +18,36 @@ #include "config.h" -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -# include -#endif - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "wingdi.h" -#include "winuser.h" #include "wine/debug.h" -#include "winerror.h" -#include "nb30.h" -#include "lm.h" -#include "iphlpapi.h" +#include "netbios.h" WINE_DEFAULT_DEBUG_CHANNEL(netbios); HMODULE NETAPI32_hModule = 0; -static UCHAR NETBIOS_Enum(PNCB ncb) -{ - int i; - LANA_ENUM *lanas = (PLANA_ENUM) ncb->ncb_buffer; - DWORD apiReturn, size = 0; - PMIB_IFTABLE table; - UCHAR ret; - - TRACE("NCBENUM\n"); - - apiReturn = GetIfTable(NULL, &size, FALSE); - if (apiReturn != NO_ERROR) - { - table = (PMIB_IFTABLE)malloc(size); - if (table) - { - apiReturn = GetIfTable(table, &size, FALSE); - if (apiReturn == NO_ERROR) - { - lanas->length = 0; - for (i = 0; i < table->dwNumEntries && lanas->length < MAX_LANA; - i++) - { - if (table->table[i].dwType != MIB_IF_TYPE_LOOPBACK) - { - lanas->lana[lanas->length] = table->table[i].dwIndex; - lanas->length++; - } - } - ret = NRC_GOODRET; - } - else - ret = NRC_SYSTEM; - free(table); - } - else - ret = NRC_NORESOURCES; - } - else - ret = NRC_SYSTEM; - return ret; -} - - -static UCHAR NETBIOS_Astat(PNCB ncb) -{ - PADAPTER_STATUS astat = (PADAPTER_STATUS) ncb->ncb_buffer; - MIB_IFROW row; - - TRACE("NCBASTAT (Adapter %d)\n", ncb->ncb_lana_num); - - memset(astat, 0, sizeof astat); - - row.dwIndex = ncb->ncb_lana_num; - if (GetIfEntry(&row) != NO_ERROR) - return NRC_INVADDRESS; - /* doubt anyone cares, but why not.. */ - if (row.dwType == MIB_IF_TYPE_TOKENRING) - astat->adapter_type = 0xff; - else - astat->adapter_type = 0xfe; /* for Ethernet */ - return NRC_GOODRET; -} - BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved); switch (fdwReason) { - case DLL_PROCESS_ATTACH: + case DLL_PROCESS_ATTACH: + { DisableThreadLibraryCalls(hinstDLL); NETAPI32_hModule = hinstDLL; - break; - case DLL_PROCESS_DETACH: - break; + NetBIOSInit(); + NetBTInit(); + break; + } + case DLL_PROCESS_DETACH: + { + NetBIOSShutdown(); + break; + } } return TRUE; } -UCHAR WINAPI Netbios(PNCB pncb) -{ - UCHAR ret = NRC_ILLCMD; - - TRACE("ncb = %p\n",pncb); - - if(!pncb) - return NRC_INVADDRESS; - - switch(pncb->ncb_command&0x7f) - { - case NCBRESET: - FIXME("NCBRESET adapter %d\n",pncb->ncb_lana_num); - if(pncb->ncb_lana_num < MAX_LANA ) - { - MIB_IFROW row; - - row.dwIndex = pncb->ncb_lana_num; - if (GetIfEntry(&row) != NO_ERROR) - ret = NRC_GOODRET; - else - ret = NRC_ILLCMD; /* NetBIOS emulator not found */ - } - else - ret = NRC_ILLCMD; /* NetBIOS emulator not found */ - break; - - case NCBADDNAME: - FIXME("NCBADDNAME\n"); - break; - - case NCBADDGRNAME: - FIXME("NCBADDGRNAME\n"); - break; - - case NCBDELNAME: - FIXME("NCBDELNAME\n"); - break; - - case NCBSEND: - FIXME("NCBSEND\n"); - break; - - case NCBRECV: - FIXME("NCBRECV\n"); - break; - - case NCBHANGUP: - FIXME("NCBHANGUP\n"); - break; - - case NCBCANCEL: - FIXME("NCBCANCEL\n"); - break; - - case NCBLISTEN: - FIXME("NCBLISTEN\n"); - break; - - case NCBASTAT: - ret = NETBIOS_Astat(pncb); - break; - - case NCBENUM: - ret = NETBIOS_Enum(pncb); - break; - - default: - FIXME("(%p): command code %02x\n", pncb, pncb->ncb_command); - - ret = NRC_ILLCMD; /* NetBIOS emulator not found */ - } - pncb->ncb_retcode = ret; - return ret; -} - NET_API_STATUS WINAPI NetServerEnum( LPCWSTR servername, DWORD level, diff --git a/dlls/netapi32/netbios.c b/dlls/netapi32/netbios.c new file mode 100644 index 00000000000..b0793bf27e0 --- /dev/null +++ b/dlls/netapi32/netbios.c @@ -0,0 +1,846 @@ +/* Copyright (c) 2003 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" +#include "wine/debug.h" +#include "nbcmdqueue.h" +#include "netbios.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +/* This file provides a NetBIOS emulator that implements the NetBIOS interface, + * including thread safety and asynchronous call support. The protocol + * implementation is separate, with blocking (synchronous) functions. + */ + +#define ADAPTERS_INCR 8 +#define DEFAULT_NUM_SESSIONS 16 + +typedef struct _NetBIOSTransportTableEntry +{ + ULONG id; + NetBIOSTransport transport; +} NetBIOSTransportTableEntry; + +typedef struct _NetBIOSSession +{ + BOOL inUse; + UCHAR state; + UCHAR local_name[NCBNAMSZ]; + UCHAR remote_name[NCBNAMSZ]; + void *data; +} NetBIOSSession; + +/* This struct needs a little explanation, unfortunately. enabled is only + * used by nbInternalEnum (see). If transport_id is not 0 and transport + * is not NULL, the adapter is considered valid. (transport is a pointer to + * an entry in a NetBIOSTransportTableEntry.) data has data for the callers of + * NetBIOSEnumAdapters to be able to see. The lana is repeated there, even + * though I don't use it internally--it's for transports to use reenabling + * adapters using NetBIOSEnableAdapter. + */ +typedef struct _NetBIOSAdapter +{ + BOOL enabled; + BOOL shuttingDown; + ULONG resetting; + ULONG transport_id; + NetBIOSTransport *transport; + NetBIOSAdapterImpl impl; + struct NBCmdQueue *cmdQueue; + CRITICAL_SECTION cs; + DWORD sessionsLen; + NetBIOSSession *sessions; +} NetBIOSAdapter; + +typedef struct _NetBIOSAdapterTable { + CRITICAL_SECTION cs; + BOOL enumerated; + BOOL enumerating; + UCHAR tableSize; + NetBIOSAdapter *table; +} NetBIOSAdapterTable; + +/* Just enough space for NBT right now */ +static NetBIOSTransportTableEntry gTransports[1]; +static UCHAR gNumTransports = 0; +static NetBIOSAdapterTable gNBTable; + +static UCHAR nbResizeAdapterTable(UCHAR newSize) +{ + UCHAR ret; + + if (gNBTable.table) + gNBTable.table = (NetBIOSAdapter *)HeapReAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, gNBTable.table, + newSize * sizeof(NetBIOSAdapter)); + else + gNBTable.table = (NetBIOSAdapter *)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter)); + if (gNBTable.table) + { + gNBTable.tableSize = newSize; + ret = NRC_GOODRET; + } + else + ret = NRC_OSRESNOTAV; + return ret; +} + +void NetBIOSInit(void) +{ + memset(&gNBTable, 0, sizeof(gNBTable)); + InitializeCriticalSection(&gNBTable.cs); +} + +void NetBIOSShutdown(void) +{ + UCHAR i; + + EnterCriticalSection(&gNBTable.cs); + for (i = 0; i < gNBTable.tableSize; i++) + { + if (gNBTable.table[i].transport && + gNBTable.table[i].transport->cleanupAdapter) + gNBTable.table[i].transport->cleanupAdapter( + gNBTable.table[i].impl.data); + } + for (i = 0; i < gNumTransports; i++) + if (gTransports[i].transport.cleanup) + gTransports[i].transport.cleanup(); + LeaveCriticalSection(&gNBTable.cs); + DeleteCriticalSection(&gNBTable.cs); + HeapFree(GetProcessHeap(), 0, gNBTable.table); +} + +BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport) +{ + BOOL ret; + + TRACE(": transport 0x%08lx, p %p\n", id, transport); + if (!transport) + ret = FALSE; + else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0])) + { + FIXME("You tried to add %d transports, but I only have space for %d\n", + gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0])); + ret = FALSE; + } + else + { + UCHAR i; + + ret = FALSE; + for (i = 0; !ret && i < gNumTransports; i++) + { + if (gTransports[i].id == id) + { + WARN("Replacing NetBIOS transport ID %ld\n", id); + memcpy(&gTransports[i].transport, transport, + sizeof(NetBIOSTransport)); + ret = TRUE; + } + } + if (!ret) + { + gTransports[gNumTransports].id = id; + memcpy(&gTransports[gNumTransports].transport, transport, + sizeof(NetBIOSTransport)); + gNumTransports++; + ret = TRUE; + } + } + TRACE("returning %d\n", ret); + return ret; +} + +/* In this, I acquire the table lock to make sure no one else is modifying it. + * This is _probably_ overkill since it should only be called during the + * context of a NetBIOSEnum call, but just to be safe.. + */ +BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data) +{ + BOOL ret; + UCHAR i; + + TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex, + data); + for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++) + ; + if (gTransports[i].id == transport) + { + NetBIOSTransport *transportPtr = &gTransports[i].transport; + + TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport); + + EnterCriticalSection(&gNBTable.cs); + ret = FALSE; + for (i = 0; i < gNBTable.tableSize && + gNBTable.table[i].transport != 0; i++) + ; + if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1) + { + UCHAR newSize; + + if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR) + newSize = gNBTable.tableSize + ADAPTERS_INCR; + else + newSize = MAX_LANA + 1; + nbResizeAdapterTable(newSize); + } + if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0) + { + TRACE(": registering as LANA %d\n", i); + gNBTable.table[i].transport_id = transport; + gNBTable.table[i].transport = transportPtr; + gNBTable.table[i].impl.lana = i; + gNBTable.table[i].impl.ifIndex = ifIndex; + gNBTable.table[i].impl.data = data; + gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap()); + InitializeCriticalSection(&gNBTable.table[i].cs); + gNBTable.table[i].enabled = TRUE; + ret = TRUE; + } + LeaveCriticalSection(&gNBTable.cs); + } + else + ret = FALSE; + TRACE("returning %d\n", ret); + return ret; +} + +/* In this, I acquire the table lock to make sure no one else is modifying it. + * This is _probably_ overkill since it should only be called during the + * context of a NetBIOSEnum call, but just to be safe.. + */ +void NetBIOSEnableAdapter(UCHAR lana) +{ + TRACE(": %d\n", lana); + if (lana < gNBTable.tableSize) + { + EnterCriticalSection(&gNBTable.cs); + if (gNBTable.table[lana].transport != 0) + gNBTable.table[lana].enabled = TRUE; + LeaveCriticalSection(&gNBTable.cs); + } +} + +static void nbShutdownAdapter(NetBIOSAdapter *adapter) +{ + if (adapter) + { + adapter->shuttingDown = TRUE; + NBCmdQueueCancelAll(adapter->cmdQueue); + if (adapter->transport->cleanupAdapter) + adapter->transport->cleanupAdapter(adapter->impl.data); + NBCmdQueueDestroy(adapter->cmdQueue); + DeleteCriticalSection(&adapter->cs); + memset(adapter, 0, sizeof(NetBIOSAdapter)); + } +} + +static void nbInternalEnum(void) +{ + UCHAR i; + + EnterCriticalSection(&gNBTable.cs); + TRACE("before mark\n"); + /* mark: */ + for (i = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0) + gNBTable.table[i].enabled = FALSE; + + TRACE("marked, before store, %d transports\n", gNumTransports); + /* store adapters: */ + for (i = 0; i < gNumTransports; i++) + if (gTransports[i].transport.enumerate) + gTransports[i].transport.enumerate(); + + TRACE("before sweep\n"); + /* sweep: */ + for (i = 0; i < gNBTable.tableSize; i++) + if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0) + nbShutdownAdapter(&gNBTable.table[i]); + gNBTable.enumerated = TRUE; + LeaveCriticalSection(&gNBTable.cs); +} + +UCHAR NetBIOSNumAdapters(void) +{ + UCHAR ret, i; + + if (!gNBTable.enumerated) + nbInternalEnum(); + for (i = 0, ret = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].transport != 0) + ret++; + return ret; +} + +void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb, + void *closure) +{ + TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb, + closure); + if (cb) + { + BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0; + UCHAR i, numLANAs = 0; + + EnterCriticalSection(&gNBTable.cs); + if (!gNBTable.enumerating) + { + gNBTable.enumerating = TRUE; + nbInternalEnum(); + gNBTable.enumerating = FALSE; + } + for (i = 0; i < gNBTable.tableSize; i++) + if (enumAll || gNBTable.table[i].transport_id == transport) + numLANAs++; + if (numLANAs > 0) + { + UCHAR lanaIndex = 0; + + for (i = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].transport_id != 0 && + (enumAll || gNBTable.table[i].transport_id == transport)) + cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id, + &gNBTable.table[i].impl, closure); + } + LeaveCriticalSection(&gNBTable.cs); + } +} + +static NetBIOSAdapter *nbGetAdapter(UCHAR lana) +{ + NetBIOSAdapter *ret = NULL; + + TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize); + if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0 + && gNBTable.table[lana].transport) + ret = &gNBTable.table[lana]; + TRACE("returning %p\n", ret); + return ret; +} + +static UCHAR nbEnum(PNCB ncb) +{ + PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer; + UCHAR i, ret; + + TRACE(": ncb %p\n", ncb); + + if (!lanas) + ret = NRC_BUFLEN; + else if (ncb->ncb_length < sizeof(LANA_ENUM)) + ret = NRC_BUFLEN; + else + { + nbInternalEnum(); + lanas->length = 0; + for (i = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].transport) + { + lanas->length++; + lanas->lana[i] = i; + } + ret = NRC_GOODRET; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session); + +static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + + TRACE(": adapter %p, ncb %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + + switch (ncb->ncb_command & 0x7f) + { + case NCBCANCEL: + case NCBADDNAME: + case NCBADDGRNAME: + case NCBDELNAME: + case NCBRESET: + case NCBSSTAT: + ret = NRC_CANCEL; + break; + + /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated + * session if cancelled */ + case NCBCALL: + case NCBSEND: + case NCBCHAINSEND: + case NCBSENDNA: + case NCBCHAINSENDNA: + case NCBHANGUP: + { + if (ncb->ncb_lsn >= adapter->sessionsLen) + ret = NRC_SNUMOUT; + else if (!adapter->sessions[ncb->ncb_lsn].inUse) + ret = NRC_SNUMOUT; + else + { + ret = NBCmdQueueCancel(adapter->cmdQueue, ncb); + if (ret == NRC_CMDCAN || ret == NRC_CANOCCR) + nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]); + } + break; + } + + default: + ret = NBCmdQueueCancel(adapter->cmdQueue, ncb); + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +/* Resizes adapter to contain space for at least sessionsLen sessions. + * If allocating more space for sessions, sets the adapter's sessionsLen to + * sessionsLen. If the adapter's sessionsLen was already at least sessionsLen, + * does nothing. Does not modify existing sessions. Assumes the adapter is + * locked. + * Returns NRC_GOODRET on success, and something else on failure. + */ +static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen) +{ + UCHAR ret = NRC_GOODRET; + + if (adapter && adapter->sessionsLen < sessionsLen) + { + NetBIOSSession *newSessions; + + if (adapter->sessions) + newSessions = (NetBIOSSession *)HeapReAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen * + sizeof(NetBIOSSession)); + else + newSessions = (NetBIOSSession *)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession)); + if (newSessions) + { + adapter->sessions = newSessions; + adapter->sessionsLen = sessionsLen; + } + else + ret = NRC_OSRESNOTAV; + } + return ret; +} + +static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + + TRACE(": adapter %p, ncb %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + + if (InterlockedIncrement(&adapter->resetting) == 1) + { + UCHAR i, resizeTo; + + NBCmdQueueCancelAll(adapter->cmdQueue); + + EnterCriticalSection(&adapter->cs); + for (i = 0; i < adapter->sessionsLen; i++) + if (adapter->sessions[i].inUse) + nbInternalHangup(adapter, &adapter->sessions[i]); + if (!ncb->ncb_lsn) + resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS : + ncb->ncb_callname[0]; + else if (adapter->sessionsLen == 0) + resizeTo = DEFAULT_NUM_SESSIONS; + else + resizeTo = 0; + if (resizeTo > 0) + ret = nbResizeAdapter(adapter, resizeTo); + else + ret = NRC_GOODRET; + LeaveCriticalSection(&adapter->cs); + } + else + ret = NRC_TOOMANY; + InterlockedDecrement(&adapter->resetting); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret, i, spaceFor; + PSESSION_HEADER sstat; + + TRACE(": adapter %p, NCB %p\n", adapter, ncb); + + if (!adapter) return NRC_BADDR; + if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN; + + sstat = (PSESSION_HEADER)ncb->ncb_buffer; + ret = NRC_GOODRET; + memset(sstat, 0, sizeof(SESSION_HEADER)); + spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) / + sizeof(SESSION_BUFFER); + EnterCriticalSection(&adapter->cs); + for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++) + { + if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' || + !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ))) + { + if (sstat->num_sess < spaceFor) + { + PSESSION_BUFFER buf; + + buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER) + + sstat->num_sess * sizeof(SESSION_BUFFER)); + buf->lsn = i; + buf->state = adapter->sessions[i].state; + memcpy(buf->local_name, adapter->sessions[i].local_name, + NCBNAMSZ); + memcpy(buf->remote_name, adapter->sessions[i].remote_name, + NCBNAMSZ); + buf->rcvs_outstanding = buf->sends_outstanding = 0; + sstat->num_sess++; + } + else + ret = NRC_BUFLEN; + } + } + LeaveCriticalSection(&adapter->cs); + + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret, i; + + TRACE(": adapter %p, NCB %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF; + if (!adapter->transport->call) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + + EnterCriticalSection(&adapter->cs); + for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++) + ; + if (i < adapter->sessionsLen) + { + adapter->sessions[i].inUse = TRUE; + adapter->sessions[i].state = CALL_PENDING; + memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ); + memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ); + ret = NRC_GOODRET; + } + else + ret = NRC_LOCTFUL; + LeaveCriticalSection(&adapter->cs); + + if (ret == NRC_GOODRET) + { + ret = adapter->transport->call(adapter->impl.data, ncb, + &adapter->sessions[i].data); + if (ret == NRC_GOODRET) + { + ncb->ncb_lsn = i; + adapter->sessions[i].state = SESSION_ESTABLISHED; + } + else + { + adapter->sessions[i].inUse = FALSE; + adapter->sessions[i].state = 0; + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + NetBIOSSession *session; + + if (!adapter) return NRC_BRIDGE; + if (!adapter->transport->send) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; + if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; + if (!ncb->ncb_buffer) return NRC_BADDR; + + session = &adapter->sessions[ncb->ncb_lsn]; + if (session->state != SESSION_ESTABLISHED) + ret = NRC_SNUMOUT; + else + ret = adapter->transport->send(adapter->impl.data, session->data, ncb); + return ret; +} + +static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + NetBIOSSession *session; + + if (!adapter) return NRC_BRIDGE; + if (!adapter->transport->recv) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; + if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; + if (!ncb->ncb_buffer) return NRC_BADDR; + + session = &adapter->sessions[ncb->ncb_lsn]; + if (session->state != SESSION_ESTABLISHED) + ret = NRC_SNUMOUT; + else + ret = adapter->transport->recv(adapter->impl.data, session->data, ncb); + return ret; +} + +static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session) +{ + UCHAR ret; + + if (!adapter) return NRC_BRIDGE; + if (!session) return NRC_SNUMOUT; + + if (adapter->transport->hangup) + ret = adapter->transport->hangup(adapter->impl.data, session->data); + else + ret = NRC_ILLCMD; + EnterCriticalSection(&adapter->cs); + memset(session, 0, sizeof(NetBIOSSession)); + LeaveCriticalSection(&adapter->cs); + return NRC_GOODRET; +} + +static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + NetBIOSSession *session; + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; + if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; + + session = &adapter->sessions[ncb->ncb_lsn]; + if (session->state != SESSION_ESTABLISHED) + ret = NRC_SNUMOUT; + else + { + session->state = HANGUP_PENDING; + ret = nbInternalHangup(adapter, session); + } + return ret; +} + +void NetBIOSHangupSession(PNCB ncb) +{ + NetBIOSAdapter *adapter; + + if (!ncb) return; + + adapter = nbGetAdapter(ncb->ncb_lana_num); + if (adapter) + { + if (ncb->ncb_lsn < adapter->sessionsLen && + adapter->sessions[ncb->ncb_lsn].inUse) + nbHangup(adapter, ncb); + } +} + +static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + + if (!adapter) return NRC_BRIDGE; + if (!adapter->transport->astat) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN; + + ret = adapter->transport->astat(adapter->impl.data, ncb); + if (ncb->ncb_callname[0] == '*') + { + PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; + + astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen; + } + return ret; +} + +static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret, cmd; + + TRACE(": adapter %p, ncb %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + + cmd = ncb->ncb_command & 0x7f; + if (cmd == NCBRESET) + ret = nbReset(adapter, ncb); + else + { + ret = NBCmdQueueAdd(adapter->cmdQueue, ncb); + if (ret == NRC_GOODRET) + { + switch (cmd) + { + case NCBCALL: + ret = nbCall(adapter, ncb); + break; + + /* WinNT doesn't chain sends, it always sends immediately. + * Doubt there's any real significance to the NA variants. + */ + case NCBSEND: + case NCBSENDNA: + case NCBCHAINSEND: + case NCBCHAINSENDNA: + ret = nbSend(adapter, ncb); + break; + + case NCBRECV: + ret = nbRecv(adapter, ncb); + break; + + case NCBHANGUP: + ret = nbHangup(adapter, ncb); + break; + + case NCBASTAT: + ret = nbAStat(adapter, ncb); + break; + + case NCBFINDNAME: + if (adapter->transport->findName) + ret = adapter->transport->findName(adapter->impl.data, + ncb); + else + ret = NRC_ILLCMD; + break; + + default: + FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command); + ret = NRC_ILLCMD; + } + NBCmdQueueComplete(adapter->cmdQueue, ncb, ret); + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static DWORD WINAPI nbCmdThread(LPVOID lpVoid) +{ + PNCB ncb = (PNCB)lpVoid; + + if (ncb) + { + UCHAR ret; + NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num); + + if (adapter) + ret = nbDispatch(adapter, ncb); + else + ret = NRC_BRIDGE; + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret; + if (ncb->ncb_post) + ncb->ncb_post(ncb); + else if (ncb->ncb_event) + SetEvent(ncb->ncb_event); + } + return 0; +} + +UCHAR WINAPI Netbios(PNCB ncb) +{ + UCHAR ret, cmd; + + TRACE("ncb = %p\n", ncb); + + if (!ncb) return NRC_INVADDRESS; + + TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n", + ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length); + cmd = ncb->ncb_command & 0x7f; + + if (cmd == NCBENUM) + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb); + else + { + NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num); + + if (!adapter) + ret = NRC_BRIDGE; + else + { + if (adapter->shuttingDown) + ret = NRC_IFBUSY; + else if (adapter->resetting) + ret = NRC_TOOMANY; + else + { + /* non-asynch commands first */ + if (cmd == NCBCANCEL) + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + nbCancel(adapter, ncb); + else if (cmd == NCBSSTAT) + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + nbSStat(adapter, ncb); + else + { + if (ncb->ncb_command & ASYNCH) + { + HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb, + CREATE_SUSPENDED, NULL); + + if (thread != NULL) + { + ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING; + if (ncb->ncb_event) + ResetEvent(ncb->ncb_event); + ResumeThread(thread); + CloseHandle(thread); + ret = NRC_GOODRET; + } + else + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + NRC_OSRESNOTAV; + } + else + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + nbDispatch(adapter, ncb); + } + } + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} diff --git a/dlls/netapi32/netbios.h b/dlls/netapi32/netbios.h new file mode 100644 index 00000000000..1fe50c7846f --- /dev/null +++ b/dlls/netapi32/netbios.h @@ -0,0 +1,183 @@ +/* Copyright (c) 2003 Juan Lang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __WINE_NETBIOS_H__ +#define __WINE_NETBIOS_H__ + +#include +#include "windef.h" +#include "winbase.h" +#include "lm.h" +#include "nb30.h" + +/* This file describes the interface WINE's NetBIOS implementation uses to + * interact with a transport implementation (where a transport might be + * NetBIOS-over-TCP/IP (aka NetBT, NBT), NetBIOS-over-IPX, etc.) + */ + +/** + * Public functions + */ + +void NetBIOSInit(void); +void NetBIOSShutdown(void); + +struct _NetBIOSTransport; + +/* A transport should register itself during its init function (see below) with + * a unique id (the transport_id of ACTION_HEADER, for example) and an + * implementation. Returns TRUE on success, and FALSE on failure. + */ +BOOL NetBIOSRegisterTransport(ULONG id, struct _NetBIOSTransport *transport); + +/* Registers an adapter with the given transport and ifIndex with NetBIOS. + * ifIndex is an interface index usable by the IpHlpApi. ifIndex is not + * required to be unique, but is required so that NetWkstaTransportEnum can use + * GetIfEntry to get the name and hardware address of the adapter. + * Returns TRUE on success, FALSE on failure. + * FIXME: need functions for retrieving the name and hardware index, rather + * than assuming a correlation with IpHlpApi. + */ +BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *adapter); + +/* During enumeration, all adapters from your transport are disabled + * internally. If an adapter is still valid, reenable it with this function. + * Adapters you don't enable will have their transport's NetBIOSCleanupAdapter + * function (see below) called on them, and will be removed from the table. + * (This is to deal with lack of plug-and-play--sorry.) + */ +void NetBIOSEnableAdapter(UCHAR lana); + +/* Gets a quick count of the number of NetBIOS adapters. Not guaranteed not + * to change from one call to the next, depending on what's been enumerated + * lately. See also NetBIOSEnumAdapters. + */ +UCHAR NetBIOSNumAdapters(void); + +typedef struct _NetBIOSAdapterImpl { + UCHAR lana; + DWORD ifIndex; + void *data; +} NetBIOSAdapterImpl; + +typedef BOOL (*NetBIOSEnumAdaptersCallback)(UCHAR totalLANAs, UCHAR lanaIndex, + ULONG transport, const NetBIOSAdapterImpl *data, void *closure); + +/* Enumerates all NetBIOS adapters for the transport transport, or for all + * transports if transport is ALL_TRANSPORTS. Your callback will be called + * once for every enumerated adapter, with a count of how many adapters have + * been enumerated, a 0-based index relative to that count, the adapter's + * transport, and its ifIndex. + * Your callback should return FALSE if it no longer wishes to be called. + */ +void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb, + void *closure); + +/* Hangs up the session identified in the NCB; the NCB need not be a NCBHANGUP. + * Will result in the transport's hangup function being called, so release any + * locks you own before calling to avoid deadlock. + * This function is intended for use by a transport, if the session is closed + * by some error in the transport layer. + */ +void NetBIOSHangupSession(PNCB ncb); + +/** + * Functions a transport implementation must implement + */ + +/* This function is called to ask a transport implementation to enumerate any + * LANAs into the NetBIOS adapter table by: + * - calling NetBIOSRegisterAdapter for any new adapters + * - calling NetBIOSEnableAdapter for any existing adapters + * NetBIOSEnumAdapters (see) may be of use to determine which adapters already + * exist. + * A transport can assume no other thread is modifying the NetBIOS adapter + * table during the lifetime of its NetBIOSEnum function (and, therefore, that + * this function won't be called reentrantly). + */ +typedef UCHAR (*NetBIOSEnum)(void); + +/* A cleanup function for a transport. This is the last function called on a + * transport. + */ +typedef void (*NetBIOSCleanup)(void); + +/* Adapter functions */ + +/* Functions with direct mappings to the Netbios interface. These functions + * are expected to be synchronous, although the first four bytes of the + * reserved member of the ncb are a cancel flag. A long-running function + * should check whether this is not FALSE from time to time (see the + * NCB_CANCELLED macro), and return NRC_CMDCAN if it's been cancelled. (The + * remainder of the NCB's reserved field is, well, reserved.) + */ + +/* Used to see whether the pointer to an NCB has been cancelled. The NetBIOS + * interface designates certain functions as non-cancellable functions, but I + * use this flag for all NCBs. Support it if you can. + * FIXME: this isn't enough, need to support an EVENT or some such, because + * some calls (recv) will block indefinitely, so a reset, shutdown, etc. will + * never occur. + */ +#define NCB_CANCELLED(pncb) *(PBOOL)((pncb)->ncb_reserved) + +typedef UCHAR (*NetBIOSAstat)(void *adapter, PNCB ncb); +typedef UCHAR (*NetBIOSFindName)(void *adapter, PNCB ncb); + +/* Functions to support the session service */ + +/* Implement to support the NCBCALL command. If you need data stored for the + * session, return it in *session. You can clean it up in your NetBIOSHangup + * function (see). + */ +typedef UCHAR (*NetBIOSCall)(void *adapter, PNCB ncb, void **session); +typedef UCHAR (*NetBIOSSend)(void *adapter, void *session, PNCB ncb); +typedef UCHAR (*NetBIOSRecv)(void *adapter, void *session, PNCB ncb); +typedef UCHAR (*NetBIOSHangup)(void *adapter, void *session); + +/* The last function called on an adapter; it is not called reentrantly, and + * no new calls will be made on the adapter once this has been entered. Clean + * up any resources allocated for the adapter here. + */ +typedef void (*NetBIOSCleanupAdapter)(void *adapter); + +typedef struct _NetBIOSTransport +{ + NetBIOSEnum enumerate; + NetBIOSAstat astat; + NetBIOSFindName findName; + NetBIOSCall call; + NetBIOSSend send; + NetBIOSRecv recv; + NetBIOSHangup hangup; + NetBIOSCleanupAdapter cleanupAdapter; + NetBIOSCleanup cleanup; +} NetBIOSTransport; + +/* Transport-specific functions. When adding a transport, add a call to its + * init function in netapi32's DllMain. The transport can do any global + * initialization it needs here. It should call NetBIOSRegisterTransport to + * register itself with NetBIOS. + */ + +/* NetBIOS-over-TCP/IP (NetBT) functions */ + +/* Not defined by MS, so make my own private define: */ +#define TRANSPORT_NBT "MNBT" + +void NetBTInit(void); + +#endif /* ndef __WINE_NETBIOS_H__ */ diff --git a/dlls/netapi32/wksta.c b/dlls/netapi32/wksta.c index 06149fe9f64..8fae79d9718 100644 --- a/dlls/netapi32/wksta.c +++ b/dlls/netapi32/wksta.c @@ -1,5 +1,5 @@ -/* - * Copyright 2002 Andriy Palamarchuk +/* Copyright 2002 Andriy Palamarchuk + * Copyright (c) 2003 Juan Lang * * netapi32 user functions * @@ -33,6 +33,7 @@ #include "winreg.h" #include "winternl.h" #include "ntsecapi.h" +#include "netbios.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(netapi32); @@ -65,151 +66,228 @@ BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName) } } -static void wprint_mac(WCHAR* buffer, PIP_ADAPTER_INFO adapter) +static void wprint_mac(WCHAR* buffer, int len, PMIB_IFROW ifRow) { - if (adapter != NULL) - { - int i; - unsigned char val; + int i; + unsigned char val; - for (i = 0; iAddressLength, 6); i++) - { - val = adapter->Address[i]; - if ((val >>4) >9) - buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10); - else - buffer[2*i] = (WCHAR)((val >>4) + '0'); - if ((val & 0xf ) >9) - buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10); - else - buffer[2*i+1] = (WCHAR)((val & 0xf) + '0'); - } - buffer[12]=(WCHAR)0; + if (!buffer) + return; + if (len < 1) + return; + if (!ifRow) + { + *buffer = '\0'; + return; } - else - buffer[0] = 0; + + for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++) + { + val = ifRow->bPhysAddr[i]; + if ((val >>4) >9) + buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10); + else + buffer[2*i] = (WCHAR)((val >>4) + '0'); + if ((val & 0xf ) >9) + buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10); + else + buffer[2*i+1] = (WCHAR)((val & 0xf) + '0'); + } + buffer[2*i]=(WCHAR)0; } -#define TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_" -#define TRANSPORT_NAME_LEN \ - (sizeof(TRANSPORT_NAME_HEADER) + MAX_ADAPTER_NAME_LENGTH) +/* Theoretically this could be too short, except that MS defines + * MAX_ADAPTER_NAME as 128, and MAX_INTERFACE_NAME_LEN as 256, and both + * represent a count of WCHARs, so even with an extroardinarily long header + * this will be plenty + */ +#define MAX_TRANSPORT_NAME MAX_INTERFACE_NAME_LEN +#define MAX_TRANSPORT_ADDR 13 -static void wprint_name(WCHAR *buffer, int len, PIP_ADAPTER_INFO adapter) +#define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_" +#define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_" + +static void wprint_name(WCHAR *buffer, int len, ULONG transport, + PMIB_IFROW ifRow) { - WCHAR *ptr; - const char *name; + WCHAR *ptr1, *ptr2; + const char *name; - if (!buffer) - return; - if (!adapter) - return; + if (!buffer) + return; + if (!ifRow) + { + *buffer = '\0'; + return; + } - for (ptr = buffer, name = TRANSPORT_NAME_HEADER; *name && ptr < buffer + len; - ptr++, name++) - *ptr = *name; - for (name = adapter->AdapterName; name && *name && ptr < buffer + len; - ptr++, name++) - *ptr = *name; - *ptr = '\0'; + if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG))) + name = NBT_TRANSPORT_NAME_HEADER; + else + name = UNKNOWN_TRANSPORT_NAME_HEADER; + + for (ptr1 = buffer; *name && ptr1 < buffer + len; ptr1++, name++) + *ptr1 = *name; + for (ptr2 = ifRow->wszName; *ptr2 && ptr1 < buffer + len; ptr1++, ptr2++) + *ptr1 = *ptr2; + *ptr1 = '\0'; +} + +struct WkstaTransportEnumData +{ + UCHAR n_adapt; + UCHAR n_read; + DWORD prefmaxlen; + LPBYTE *pbuf; + NET_API_STATUS ret; +}; + +static BOOL WkstaEnumAdaptersCallback(UCHAR totalLANAs, UCHAR lanaIndex, + ULONG transport, const NetBIOSAdapterImpl *data, void *closure) +{ + BOOL ret; + struct WkstaTransportEnumData *enumData = (struct WkstaTransportEnumData *) + closure; + + if (enumData && enumData->pbuf) + { + if (lanaIndex == 0) + { + DWORD toAllocate; + + enumData->n_adapt = totalLANAs; + enumData->n_read = 0; + + toAllocate = totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) + + MAX_TRANSPORT_NAME * sizeof(WCHAR) + + MAX_TRANSPORT_ADDR * sizeof(WCHAR)); + if (enumData->prefmaxlen != MAX_PREFERRED_LENGTH) + toAllocate = enumData->prefmaxlen; + NetApiBufferAllocate(toAllocate, (LPVOID *)enumData->pbuf); + } + if (*(enumData->pbuf)) + { + UCHAR spaceFor; + + if (enumData->prefmaxlen == MAX_PREFERRED_LENGTH) + spaceFor = totalLANAs; + else + spaceFor = enumData->prefmaxlen / + (sizeof(WKSTA_TRANSPORT_INFO_0) + (MAX_TRANSPORT_NAME + + MAX_TRANSPORT_ADDR) * sizeof(WCHAR)); + if (enumData->n_read < spaceFor) + { + PWKSTA_TRANSPORT_INFO_0 ti; + LPWSTR transport_name, transport_addr; + MIB_IFROW ifRow; + + ti = (PWKSTA_TRANSPORT_INFO_0)(*(enumData->pbuf) + + enumData->n_read * sizeof(WKSTA_TRANSPORT_INFO_0)); + transport_name = (LPWSTR)(*(enumData->pbuf) + + totalLANAs * sizeof(WKSTA_TRANSPORT_INFO_0) + + enumData->n_read * MAX_TRANSPORT_NAME * sizeof(WCHAR)); + transport_addr = (LPWSTR)(*(enumData->pbuf) + + totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) + + MAX_TRANSPORT_NAME * sizeof(WCHAR)) + + (enumData->n_read + MAX_TRANSPORT_ADDR) * sizeof(WCHAR)); + + ifRow.dwIndex = data->ifIndex; + GetIfEntry(&ifRow); + ti->wkti0_quality_of_service = 0; + ti->wkti0_number_of_vcs = 0; + ti->wkti0_transport_name = transport_name; + wprint_name(ti->wkti0_transport_name, MAX_TRANSPORT_NAME, + transport, &ifRow); + ti->wkti0_transport_address = transport_addr; + wprint_mac(ti->wkti0_transport_address, MAX_TRANSPORT_ADDR, + &ifRow); + if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG))) + ti->wkti0_wan_ish = TRUE; + else + ti->wkti0_wan_ish = FALSE; + TRACE("%d of %d:ti at %p\n", lanaIndex, totalLANAs, ti); + TRACE("transport_name at %p %s\n", + ti->wkti0_transport_name, + debugstr_w(ti->wkti0_transport_name)); + TRACE("transport_address at %p %s\n", + ti->wkti0_transport_address, + debugstr_w(ti->wkti0_transport_address)); + enumData->n_read++; + enumData->ret = NERR_Success; + ret = TRUE; + } + else + { + enumData->ret = ERROR_MORE_DATA; + ret = FALSE; + } + } + else + { + enumData->ret = ERROR_OUTOFMEMORY; + ret = FALSE; + } + } + else + ret = FALSE; + return ret; } NET_API_STATUS WINAPI NetWkstaTransportEnum(LPCWSTR ServerName, DWORD level, LPBYTE* pbuf, - DWORD prefmaxlen, LPDWORD read_entries, - LPDWORD total_entries, LPDWORD hresume) + DWORD prefmaxlen, LPDWORD read_entries, + LPDWORD total_entries, LPDWORD hresume) { - FIXME(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName), - level, pbuf, prefmaxlen, read_entries, total_entries,hresume); - if (!NETAPI_IsLocalComputer(ServerName)) - { - FIXME(":not implemented for non-local computers\n"); - return ERROR_INVALID_LEVEL; - } - else - { - if (hresume && *hresume) - { - FIXME(":resume handle not implemented\n"); - return ERROR_INVALID_LEVEL; - } - - switch (level) - { - case 0: /* transport info */ - { - PWKSTA_TRANSPORT_INFO_0 ti; - int i,size_needed,n_adapt; - DWORD apiReturn, adaptInfoSize = 0; - PIP_ADAPTER_INFO info, ptr; - - apiReturn = GetAdaptersInfo(NULL, &adaptInfoSize); - if (apiReturn == ERROR_NO_DATA) - return ERROR_NETWORK_UNREACHABLE; - if (!read_entries) - return STATUS_ACCESS_VIOLATION; - if (!total_entries || !pbuf) - return RPC_X_NULL_REF_POINTER; + NET_API_STATUS ret; - info = (PIP_ADAPTER_INFO)malloc(adaptInfoSize); - apiReturn = GetAdaptersInfo(info, &adaptInfoSize); - if (apiReturn != NO_ERROR) - { - free(info); - return apiReturn; - } - - for (n_adapt = 0, ptr = info; ptr; ptr = ptr->Next) - n_adapt++; - size_needed = n_adapt * sizeof(WKSTA_TRANSPORT_INFO_0) - + n_adapt * TRANSPORT_NAME_LEN * sizeof (WCHAR) - + n_adapt * 13 * sizeof (WCHAR); - if (prefmaxlen == MAX_PREFERRED_LENGTH) - NetApiBufferAllocate( size_needed, (LPVOID *) pbuf); - else - { - if (size_needed > prefmaxlen) - { - free(info); - return ERROR_MORE_DATA; - } - NetApiBufferAllocate(prefmaxlen, - (LPVOID *) pbuf); - } - for (i = 0, ptr = info; ptr; ptr = ptr->Next, i++) - { - ti = (PWKSTA_TRANSPORT_INFO_0) - ((PBYTE) *pbuf + i * sizeof(WKSTA_TRANSPORT_INFO_0)); - ti->wkti0_quality_of_service=0; - ti->wkti0_number_of_vcs=0; - ti->wkti0_transport_name= (LPWSTR) - ((PBYTE )*pbuf + - n_adapt * sizeof(WKSTA_TRANSPORT_INFO_0) - + i * TRANSPORT_NAME_LEN * sizeof (WCHAR)); - wprint_name(ti->wkti0_transport_name,TRANSPORT_NAME_LEN, ptr); - ti->wkti0_transport_address= (LPWSTR) - ((PBYTE )*pbuf + - n_adapt * sizeof(WKSTA_TRANSPORT_INFO_0) + - n_adapt * TRANSPORT_NAME_LEN * sizeof (WCHAR) - + i * 13 * sizeof (WCHAR)); - ti->wkti0_wan_ish=TRUE; /*TCPIP/NETBIOS Protocoll*/ - wprint_mac(ti->wkti0_transport_address, ptr); - TRACE("%d of %d:ti at %p transport_address at %p %s\n",i,n_adapt, - ti, ti->wkti0_transport_address, debugstr_w(ti->wkti0_transport_address)); - } - *read_entries = n_adapt; - *total_entries = n_adapt; - free(info); - if(hresume) *hresume= 0; - break; - } - default: - ERR("Invalid level %ld is specified\n", level); - return ERROR_INVALID_LEVEL; - } - return NERR_Success; + TRACE(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName), + level, pbuf, prefmaxlen, read_entries, total_entries,hresume); + if (!NETAPI_IsLocalComputer(ServerName)) + { + FIXME(":not implemented for non-local computers\n"); + ret = ERROR_INVALID_LEVEL; } + else + { + if (hresume && *hresume) + { + FIXME(":resume handle not implemented\n"); + return ERROR_INVALID_LEVEL; + } + + switch (level) + { + case 0: /* transport info */ + { + ULONG allTransports; + struct WkstaTransportEnumData enumData; + + if (NetBIOSNumAdapters() == 0) + return ERROR_NETWORK_UNREACHABLE; + if (!read_entries) + return STATUS_ACCESS_VIOLATION; + if (!total_entries || !pbuf) + return RPC_X_NULL_REF_POINTER; + + enumData.prefmaxlen = prefmaxlen; + enumData.pbuf = pbuf; + memcpy(&allTransports, ALL_TRANSPORTS, sizeof(ULONG)); + NetBIOSEnumAdapters(allTransports, WkstaEnumAdaptersCallback, + &enumData); + *read_entries = enumData.n_read; + *total_entries = enumData.n_adapt; + if (hresume) *hresume= 0; + ret = enumData.ret; + break; + } + default: + ERR("Invalid level %ld is specified\n", level); + ret = ERROR_INVALID_LEVEL; + } + } + return ret; } - + /************************************************************ * NetWkstaUserGetInfo (NETAPI32.@) diff --git a/include/lmwksta.h b/include/lmwksta.h index 8eba78af300..12960d77571 100644 --- a/include/lmwksta.h +++ b/include/lmwksta.h @@ -25,9 +25,6 @@ extern "C" { #endif -/* NetBIOS */ -UCHAR WINAPI Netbios(PNCB pncb); - typedef struct _WKSTA_TRANSPORT_INFO_0 { DWORD wkti0_quality_of_service; DWORD wkti0_number_of_vcs; diff --git a/include/nb30.h b/include/nb30.h index a9d7bebd3d3..b3c7591aa03 100644 --- a/include/nb30.h +++ b/include/nb30.h @@ -26,18 +26,34 @@ extern "C" { #define NCBNAMSZ 16 #define MAX_LANA 0xfe -#define NCBRESET 0x32 -#define NCBADDNAME 0x30 -#define NCBADDGRNAME 0x36 -#define NCBDELNAME 0x31 +#define NCBCALL 0x10 +#define NCBLISTEN 0x11 +#define NCBHANGUP 0x12 #define NCBSEND 0x14 #define NCBRECV 0x15 -#define NCBHANGUP 0x12 -#define NCBCANCEL 0x35 -#define NCBLISTEN 0x11 -#define NCBCALL 0x10 +#define NCBRECVANY 0x16 +#define NCBCHAINSEND 0x17 +#define NCBDGSEND 0x20 +#define NCBDGRECV 0x21 +#define NCBDGSENDBC 0x22 +#define NCBDGRECVBC 0x23 +#define NCBADDNAME 0x30 +#define NCBDELNAME 0x31 +#define NCBRESET 0x32 #define NCBASTAT 0x33 +#define NCBSSTAT 0x34 +#define NCBCANCEL 0x35 +#define NCBADDGRNAME 0x36 #define NCBENUM 0x37 +#define NCBUNLINK 0x70 +#define NCBSENDNA 0x71 +#define NCBCHAINSENDNA 0x72 +#define NCBLANSTALERT 0x73 +#define NCBACTION 0x77 +#define NCBFINDNAME 0x78 +#define NCBTRACE 0x79 + +#define ASYNCH 0x80 typedef struct _NCB { @@ -51,7 +67,7 @@ typedef struct _NCB UCHAR ncb_name[NCBNAMSZ]; UCHAR ncb_rto; UCHAR ncb_sto; - VOID (*ncb_post)(struct _NCB *); + VOID (CALLBACK *ncb_post)(struct _NCB *); UCHAR ncb_lana_num; UCHAR ncb_cmd_cplt; UCHAR ncb_reserved[10]; @@ -89,22 +105,111 @@ typedef struct _ADAPTER_STATUS WORD name_count; } ADAPTER_STATUS, *PADAPTER_STATUS; +typedef struct _NAME_BUFFER +{ + UCHAR name[NCBNAMSZ]; + UCHAR name_num; + UCHAR name_flags; +} NAME_BUFFER, *PNAME_BUFFER; + +#define NAME_FLAGS_MASK 0x87 +#define GROUP_NAME 0x80 +#define UNIQUE_NAME 0x00 +#define REGISTERING 0x00 +#define REGISTERED 0x04 +#define DEREGISTERED 0x05 +#define DUPLICATE 0x06 +#define DUPLICATE_DEREG 0x07 + typedef struct _LANA_ENUM { UCHAR length; UCHAR lana[MAX_LANA+1]; } LANA_ENUM, *PLANA_ENUM; -#define NRC_GOODRET 0x00 -#define NRC_BUFLEN 0x01 -#define NRC_ILLCMD 0x03 -#define NRC_CMDTMO 0x05 -#define NRC_INCOMP 0x06 +typedef struct _FIND_NAME_HEADER +{ + WORD node_count; + UCHAR reserved; + UCHAR unique_group; +} FIND_NAME_HEADER, *PFIND_NAME_HEADER; + +typedef struct _FIND_NAME_BUFFER +{ + UCHAR length; + UCHAR access_control; + UCHAR frame_control; + UCHAR destination_addr[6]; + UCHAR source_addr[6]; + UCHAR routing_info[6]; +} FIND_NAME_BUFFER, *PFIND_NAME_BUFFER; + +typedef struct _SESSION_HEADER { + UCHAR sess_name; + UCHAR num_sess; + UCHAR rcv_dg_outstanding; + UCHAR rcv_any_outstanding; +} SESSION_HEADER, *PSESSION_HEADER; + +typedef struct _SESSION_BUFFER { + UCHAR lsn; + UCHAR state; + UCHAR local_name[NCBNAMSZ]; + UCHAR remote_name[NCBNAMSZ]; + UCHAR rcvs_outstanding; + UCHAR sends_outstanding; +} SESSION_BUFFER, *PSESSION_BUFFER; + +#define LISTEN_OUTSTANDING 0x01 +#define CALL_PENDING 0x02 +#define SESSION_ESTABLISHED 0x03 +#define HANGUP_PENDING 0x04 +#define HANGUP_COMPLETE 0x05 +#define SESSION_ABORTED 0x06 + +#define ALL_TRANSPORTS "M\0\0\0" + +#define NRC_GOODRET 0x00 +#define NRC_BUFLEN 0x01 +#define NRC_ILLCMD 0x03 +#define NRC_CMDTMO 0x05 +#define NRC_INCOMP 0x06 +#define NRC_BADDR 0x07 +#define NRC_SNUMOUT 0x08 +#define NRC_NORES 0x09 +#define NRC_SCLOSED 0x0a +#define NRC_CMDCAN 0x0b +#define NRC_DUPNAME 0x0d +#define NRC_NAMTFUL 0x0e +#define NRC_ACTSES 0x0f +#define NRC_LOCTFUL 0x11 +#define NRC_REMTFUL 0x12 +#define NRC_ILLNN 0x13 +#define NRC_NOCALL 0x14 +#define NRC_NOWILD 0x15 +#define NRC_INUSE 0x16 +#define NRC_NAMERR 0x17 +#define NRC_SABORT 0x18 +#define NRC_NAMCONF 0x19 +#define NRC_IFBUSY 0x21 +#define NRC_TOOMANY 0x22 +#define NRC_BRIDGE 0x23 +#define NRC_CANOCCR 0x24 +#define NRC_CANCEL 0x26 +#define NRC_DUPENV 0x30 +#define NRC_ENVNOTDEF 0x34 +#define NRC_OSRESNOTAV 0x35 +#define NRC_MAXAPPS 0x36 +#define NRC_NOSAPS 0x37 #define NRC_NORESOURCES 0x38 -#define NRC_INVADDRESS 0x39 -#define NRC_PENDING 0xff -#define NRC_OPENERROR 0x3f -#define NRC_SYSTEM 0x40 +#define NRC_INVADDRESS 0x39 +#define NRC_INVDDID 0x3b +#define NRC_LOCKFAIL 0x3c +#define NRC_OPENERROR 0x3f +#define NRC_SYSTEM 0x40 +#define NRC_PENDING 0xff + +UCHAR WINAPI Netbios(PNCB pncb); #ifdef __cplusplus }