Commit e7fd6fd2 authored by Juan Lang's avatar Juan Lang Committed by Alexandre Julliard

Implemented a lot of Netbios().

parent e94dad18
......@@ -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
......
/* 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);
}
}
/* 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 <stdarg.h>
#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__ */
/* 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);
}
}
/* 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 */
/* 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 <stdarg.h>
#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);
}
/*
* 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,183 +18,34 @@
#include "config.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#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:
{
DisableThreadLibraryCalls(hinstDLL);
NETAPI32_hModule = hinstDLL;
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;
return TRUE;
}
NET_API_STATUS WINAPI NetServerEnum(
......
/* 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;
}
/* 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 <stdarg.h>
#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__ */
/*
* 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,16 +66,24 @@ 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;
for (i = 0; i<max(adapter->AddressLength, 6); i++)
if (!buffer)
return;
if (len < 1)
return;
if (!ifRow)
{
*buffer = '\0';
return;
}
for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++)
{
val = adapter->Address[i];
val = ifRow->bPhysAddr[i];
if ((val >>4) >9)
buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10);
else
......@@ -84,33 +93,144 @@ static void wprint_mac(WCHAR* buffer, PIP_ADAPTER_INFO adapter)
else
buffer[2*i+1] = (WCHAR)((val & 0xf) + '0');
}
buffer[12]=(WCHAR)0;
}
else
buffer[0] = 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
#define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_"
#define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_"
static void wprint_name(WCHAR *buffer, int len, PIP_ADAPTER_INFO adapter)
static void wprint_name(WCHAR *buffer, int len, ULONG transport,
PMIB_IFROW ifRow)
{
WCHAR *ptr;
WCHAR *ptr1, *ptr2;
const char *name;
if (!buffer)
return;
if (!adapter)
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
......@@ -118,12 +238,14 @@ NetWkstaTransportEnum(LPCWSTR ServerName, DWORD level, LPBYTE* pbuf,
DWORD prefmaxlen, LPDWORD read_entries,
LPDWORD total_entries, LPDWORD hresume)
{
FIXME(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName),
NET_API_STATUS ret;
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");
return ERROR_INVALID_LEVEL;
ret = ERROR_INVALID_LEVEL;
}
else
{
......@@ -137,77 +259,33 @@ NetWkstaTransportEnum(LPCWSTR ServerName, DWORD level, LPBYTE* pbuf,
{
case 0: /* transport info */
{
PWKSTA_TRANSPORT_INFO_0 ti;
int i,size_needed,n_adapt;
DWORD apiReturn, adaptInfoSize = 0;
PIP_ADAPTER_INFO info, ptr;
ULONG allTransports;
struct WkstaTransportEnumData enumData;
apiReturn = GetAdaptersInfo(NULL, &adaptInfoSize);
if (apiReturn == ERROR_NO_DATA)
if (NetBIOSNumAdapters() == 0)
return ERROR_NETWORK_UNREACHABLE;
if (!read_entries)
return STATUS_ACCESS_VIOLATION;
if (!total_entries || !pbuf)
return RPC_X_NULL_REF_POINTER;
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;
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);
return ERROR_INVALID_LEVEL;
ret = ERROR_INVALID_LEVEL;
}
return NERR_Success;
}
return ret;
}
......
......@@ -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;
......
......@@ -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;
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_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
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment