forked from Mirrors/wine-wine
Implemented a lot of Netbios().
parent
e94dad187a
commit
e7fd6fd2a1
|
@ -4,13 +4,17 @@ TOPOBJDIR = ../..
|
||||||
SRCDIR = @srcdir@
|
SRCDIR = @srcdir@
|
||||||
VPATH = @srcdir@
|
VPATH = @srcdir@
|
||||||
MODULE = netapi32.dll
|
MODULE = netapi32.dll
|
||||||
IMPORTS = iphlpapi advapi32 kernel32
|
IMPORTS = iphlpapi ws2_32 advapi32 kernel32
|
||||||
|
|
||||||
C_SRCS = \
|
C_SRCS = \
|
||||||
access.c \
|
access.c \
|
||||||
apibuf.c \
|
apibuf.c \
|
||||||
browsr.c \
|
browsr.c \
|
||||||
|
nbcmdqueue.c \
|
||||||
|
nbnamecache.c \
|
||||||
|
nbt.c \
|
||||||
netapi32.c \
|
netapi32.c \
|
||||||
|
netbios.c \
|
||||||
wksta.c
|
wksta.c
|
||||||
|
|
||||||
SUBDIRS = tests
|
SUBDIRS = tests
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
/* Copyright (c) 2003 Juan Lang
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include "config.h"
|
||||||
|
#include "wine/debug.h"
|
||||||
|
#include "nbcmdqueue.h"
|
||||||
|
|
||||||
|
WINE_DEFAULT_DEBUG_CHANNEL(netbios);
|
||||||
|
|
||||||
|
struct NBCmdQueue
|
||||||
|
{
|
||||||
|
HANDLE heap;
|
||||||
|
CRITICAL_SECTION cs;
|
||||||
|
PNCB head;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserved)
|
||||||
|
#define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserved + sizeof(HANDLE))
|
||||||
|
|
||||||
|
/* The reserved area of an ncb will be used for the following data:
|
||||||
|
* - a cancelled flag (BOOL, 4 bytes??)
|
||||||
|
* - a handle to an event that's set by a cancelled command on completion
|
||||||
|
* (HANDLE, 4 bytes)
|
||||||
|
* These members are used in the following way
|
||||||
|
* - on cancel, set the event member of the reserved field (with create event)
|
||||||
|
* - NBCmdComplete will delete the ncb from the queue of there's no event;
|
||||||
|
* otherwise it will set the event and not delete the ncb
|
||||||
|
* - cancel must lock the queue before finding the ncb in it, and can unlock it
|
||||||
|
* once it's set the event (and the cancelled flag)
|
||||||
|
* - NBCmdComplete must lock the queue before attempting to remove the ncb or
|
||||||
|
* check the event
|
||||||
|
* - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue.
|
||||||
|
* It'll then unlock the queue, and wait on the event in the head of the queue
|
||||||
|
* until there's no more ncb's in the queue.
|
||||||
|
* Space optimization: use the handle as a boolean. NULL == 0 => not cancelled.
|
||||||
|
* Non-NULL == valid handle => cancelled. This allows storing a next pointer
|
||||||
|
* in the ncb's reserved field as well, avoiding a memory alloc for a new
|
||||||
|
* command (cool).
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap)
|
||||||
|
{
|
||||||
|
struct NBCmdQueue *queue;
|
||||||
|
|
||||||
|
if (heap == NULL)
|
||||||
|
heap = GetProcessHeap();
|
||||||
|
queue = (struct NBCmdQueue *)HeapAlloc(heap, 0, sizeof(struct NBCmdQueue));
|
||||||
|
if (queue)
|
||||||
|
{
|
||||||
|
queue->heap = heap;
|
||||||
|
InitializeCriticalSection(&queue->cs);
|
||||||
|
queue->head = NULL;
|
||||||
|
}
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
|
||||||
|
TRACE(": queue %p, ncb %p\n", queue, ncb);
|
||||||
|
|
||||||
|
if (!queue)
|
||||||
|
return NRC_BADDR;
|
||||||
|
if (!ncb)
|
||||||
|
return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
*CANCEL_EVENT_PTR(ncb) = NULL;
|
||||||
|
EnterCriticalSection(&queue->cs);
|
||||||
|
*NEXT_PTR(ncb) = queue->head;
|
||||||
|
queue->head = ncb;
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
LeaveCriticalSection(&queue->cs);
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb)
|
||||||
|
{
|
||||||
|
PNCB *ret;
|
||||||
|
|
||||||
|
if (!queue || !ncb)
|
||||||
|
ret = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = &queue->head;
|
||||||
|
while (ret && *ret != ncb)
|
||||||
|
ret = NEXT_PTR(*ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
PNCB *spot;
|
||||||
|
|
||||||
|
TRACE(": queue %p, ncb %p\n", queue, ncb);
|
||||||
|
|
||||||
|
if (!queue)
|
||||||
|
return NRC_BADDR;
|
||||||
|
if (!ncb)
|
||||||
|
return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
EnterCriticalSection(&queue->cs);
|
||||||
|
spot = NBCmdQueueFindNBC(queue, ncb);
|
||||||
|
if (spot)
|
||||||
|
{
|
||||||
|
*CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
|
WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE);
|
||||||
|
CloseHandle(*CANCEL_EVENT_PTR(*spot));
|
||||||
|
*spot = *NEXT_PTR(*spot);
|
||||||
|
if (ncb->ncb_retcode == NRC_CMDCAN)
|
||||||
|
ret = NRC_CMDCAN;
|
||||||
|
else
|
||||||
|
ret = NRC_CANOCCR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NRC_INVADDRESS;
|
||||||
|
LeaveCriticalSection(&queue->cs);
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
PNCB *spot;
|
||||||
|
|
||||||
|
TRACE(": queue %p, ncb %p\n", queue, ncb);
|
||||||
|
|
||||||
|
if (!queue)
|
||||||
|
return NRC_BADDR;
|
||||||
|
if (!ncb)
|
||||||
|
return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
EnterCriticalSection(&queue->cs);
|
||||||
|
spot = NBCmdQueueFindNBC(queue, ncb);
|
||||||
|
if (spot)
|
||||||
|
{
|
||||||
|
if (*CANCEL_EVENT_PTR(*spot))
|
||||||
|
SetEvent(*CANCEL_EVENT_PTR(*spot));
|
||||||
|
else
|
||||||
|
*spot = *NEXT_PTR(*spot);
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NRC_INVADDRESS;
|
||||||
|
LeaveCriticalSection(&queue->cs);
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
|
||||||
|
TRACE(": queue %p\n", queue);
|
||||||
|
|
||||||
|
if (!queue)
|
||||||
|
return NRC_BADDR;
|
||||||
|
|
||||||
|
EnterCriticalSection(&queue->cs);
|
||||||
|
while (queue->head)
|
||||||
|
{
|
||||||
|
TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head,
|
||||||
|
queue->head->ncb_command);
|
||||||
|
NBCmdQueueCancel(queue, queue->head);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&queue->cs);
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NBCmdQueueDestroy(struct NBCmdQueue *queue)
|
||||||
|
{
|
||||||
|
TRACE(": queue %p\n", queue);
|
||||||
|
|
||||||
|
if (queue)
|
||||||
|
{
|
||||||
|
NBCmdQueueCancelAll(queue);
|
||||||
|
DeleteCriticalSection(&queue->cs);
|
||||||
|
HeapFree(queue->heap, 0, queue);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* Copyright (c) 2003 Juan Lang
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#ifndef __NBCMDQUEUE_H__
|
||||||
|
#define __NBCMDQUEUE_H__
|
||||||
|
|
||||||
|
#include <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__ */
|
|
@ -0,0 +1,215 @@
|
||||||
|
/* Copyright (c) 2003 Juan Lang
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
* This implementation uses a linked list, because I don't have a decent
|
||||||
|
* hash table implementation handy. This is somewhat inefficient, but it's
|
||||||
|
* rather more efficient than not having a name cache at all.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wine/debug.h"
|
||||||
|
#include "nbnamecache.h"
|
||||||
|
|
||||||
|
WINE_DEFAULT_DEBUG_CHANNEL(netbios);
|
||||||
|
|
||||||
|
typedef struct _NBNameCacheNode
|
||||||
|
{
|
||||||
|
DWORD expireTime;
|
||||||
|
NBNameCacheEntry *entry;
|
||||||
|
struct _NBNameCacheNode *next;
|
||||||
|
} NBNameCacheNode;
|
||||||
|
|
||||||
|
struct NBNameCache
|
||||||
|
{
|
||||||
|
HANDLE heap;
|
||||||
|
CRITICAL_SECTION cs;
|
||||||
|
DWORD entryExpireTimeMS;
|
||||||
|
NBNameCacheNode *head;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Unlinks the node pointed to by *prev, and frees any associated memory.
|
||||||
|
* If that node's next pointed to another node, *prev now points to it.
|
||||||
|
* Assumes the caller owns cache's lock.
|
||||||
|
*/
|
||||||
|
static void NBNameCacheUnlinkNode(struct NBNameCache *cache,
|
||||||
|
NBNameCacheNode **prev)
|
||||||
|
{
|
||||||
|
if (cache && prev && *prev)
|
||||||
|
{
|
||||||
|
NBNameCacheNode *next = (*prev)->next;
|
||||||
|
|
||||||
|
if ((*prev)->entry)
|
||||||
|
HeapFree(cache->heap, 0, (*prev)->entry);
|
||||||
|
HeapFree(cache->heap, 0, *prev);
|
||||||
|
*prev = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walks the list beginning with cache->head looking for the node with name
|
||||||
|
* name. If the node is found, returns a pointer to the next pointer of the
|
||||||
|
* node _prior_ to the found node (or head if head points to it). Thus, if the
|
||||||
|
* node's all you want, dereference the return value twice. If you want to
|
||||||
|
* modify the list, modify the referent of the return value.
|
||||||
|
* While it's at it, deletes nodes whose time has expired (except the node
|
||||||
|
* you're looking for, of course).
|
||||||
|
* Returns NULL if the node isn't found.
|
||||||
|
* Assumes the caller owns cache's lock.
|
||||||
|
*/
|
||||||
|
static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache,
|
||||||
|
const char name[NCBNAMSZ])
|
||||||
|
{
|
||||||
|
NBNameCacheNode **ret = NULL;
|
||||||
|
|
||||||
|
if (cache && cache->head)
|
||||||
|
{
|
||||||
|
NBNameCacheNode **ptr;
|
||||||
|
|
||||||
|
ptr = &cache->head;
|
||||||
|
while (ptr && *ptr && (*ptr)->entry)
|
||||||
|
{
|
||||||
|
if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1))
|
||||||
|
ret = ptr;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GetTickCount() > (*ptr)->expireTime)
|
||||||
|
NBNameCacheUnlinkNode(cache, ptr);
|
||||||
|
}
|
||||||
|
if (*ptr)
|
||||||
|
ptr = &(*ptr)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS)
|
||||||
|
{
|
||||||
|
struct NBNameCache *cache;
|
||||||
|
|
||||||
|
|
||||||
|
if (!heap)
|
||||||
|
heap = GetProcessHeap();
|
||||||
|
cache = (struct NBNameCache *)HeapAlloc(heap, 0,
|
||||||
|
sizeof(struct NBNameCache));
|
||||||
|
if (cache)
|
||||||
|
{
|
||||||
|
cache->heap = heap;
|
||||||
|
InitializeCriticalSection(&cache->cs);
|
||||||
|
cache->entryExpireTimeMS = entryExpireTimeMS;
|
||||||
|
cache->head = NULL;
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry)
|
||||||
|
{
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
if (cache && entry)
|
||||||
|
{
|
||||||
|
NBNameCacheNode **node;
|
||||||
|
|
||||||
|
EnterCriticalSection(&cache->cs);
|
||||||
|
node = NBNameCacheWalk(cache, entry->name);
|
||||||
|
if (node)
|
||||||
|
{
|
||||||
|
(*node)->expireTime = GetTickCount() +
|
||||||
|
cache->entryExpireTimeMS;
|
||||||
|
HeapFree(cache->heap, 0, (*node)->entry);
|
||||||
|
(*node)->entry = entry;
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NBNameCacheNode *newNode = (NBNameCacheNode *)HeapAlloc(
|
||||||
|
cache->heap, 0, sizeof(NBNameCacheNode));
|
||||||
|
if (newNode)
|
||||||
|
{
|
||||||
|
newNode->expireTime = GetTickCount() +
|
||||||
|
cache->entryExpireTimeMS;
|
||||||
|
newNode->entry = entry;
|
||||||
|
newNode->next = cache->head;
|
||||||
|
cache->head = newNode;
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&cache->cs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = FALSE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
|
||||||
|
const UCHAR name[NCBNAMSZ])
|
||||||
|
{
|
||||||
|
const NBNameCacheEntry *ret;
|
||||||
|
UCHAR printName[NCBNAMSZ];
|
||||||
|
|
||||||
|
memcpy(printName, name, NCBNAMSZ - 1);
|
||||||
|
printName[NCBNAMSZ - 1] = '\0';
|
||||||
|
if (cache)
|
||||||
|
{
|
||||||
|
NBNameCacheNode **node;
|
||||||
|
|
||||||
|
EnterCriticalSection(&cache->cs);
|
||||||
|
node = NBNameCacheWalk(cache, name);
|
||||||
|
if (node)
|
||||||
|
ret = (*node)->entry;
|
||||||
|
else
|
||||||
|
ret = NULL;
|
||||||
|
LeaveCriticalSection(&cache->cs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
|
||||||
|
const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ])
|
||||||
|
{
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
if (cache)
|
||||||
|
{
|
||||||
|
NBNameCacheNode **node;
|
||||||
|
|
||||||
|
EnterCriticalSection(&cache->cs);
|
||||||
|
node = NBNameCacheWalk(cache, name);
|
||||||
|
if (node && *node && (*node)->entry)
|
||||||
|
{
|
||||||
|
memcpy((*node)->entry->nbname, nbname, NCBNAMSZ);
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = FALSE;
|
||||||
|
LeaveCriticalSection(&cache->cs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = FALSE;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NBNameCacheDestroy(struct NBNameCache *cache)
|
||||||
|
{
|
||||||
|
if (cache)
|
||||||
|
{
|
||||||
|
DeleteCriticalSection(&cache->cs);
|
||||||
|
while (cache->head)
|
||||||
|
NBNameCacheUnlinkNode(cache, &cache->head);
|
||||||
|
HeapFree(cache->heap, 0, cache);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/* Copyright (c) 2003 Juan Lang
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#ifndef __WINE_NBNAMECACHE_H
|
||||||
|
#define __WINE_NBNAMECACHE_H
|
||||||
|
|
||||||
|
#include "winbase.h"
|
||||||
|
#include "nb30.h"
|
||||||
|
|
||||||
|
struct NBNameCache;
|
||||||
|
|
||||||
|
/* Represents an entry in the name cache. If the NetBIOS name is known, it's
|
||||||
|
* in nbname. Otherwise, nbname begins with '*'. numAddresses defines the
|
||||||
|
* number of addresses in addresses.
|
||||||
|
* Notice that it allows multiple addresses per name, but doesn't explicitly
|
||||||
|
* allow group names. That's because all names so far are unique; if a use for
|
||||||
|
* group names comes up, adding a flag here is simple enough.
|
||||||
|
* Also, only the first NCBNAMSZ - 1 bytes are considered significant. This is
|
||||||
|
* because a name may have been resolved using DNS, and the suffix byte is
|
||||||
|
* always truncated for DNS lookups.
|
||||||
|
*/
|
||||||
|
typedef struct _NBNameCacheEntry
|
||||||
|
{
|
||||||
|
UCHAR name[NCBNAMSZ];
|
||||||
|
UCHAR nbname[NCBNAMSZ];
|
||||||
|
DWORD numAddresses;
|
||||||
|
DWORD addresses[1];
|
||||||
|
} NBNameCacheEntry;
|
||||||
|
|
||||||
|
/* Functions that create, manipulate, and destroy a name cache. Thread-safe,
|
||||||
|
* with the exception of NBNameCacheDestroy--ensure that no other threads are
|
||||||
|
* manipulating the cache before destoying it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Allocates a new name cache from heap, and sets the expire time on new
|
||||||
|
* entries to entryExpireTimeMS after a cache entry is added.
|
||||||
|
*/
|
||||||
|
struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS);
|
||||||
|
|
||||||
|
/* Adds an entry to the cache. The entry is assumed to have been allocated
|
||||||
|
* from the same heap as the name cache; the name cache will own the entry
|
||||||
|
* from now on. The entry's expire time is initialized at this time to
|
||||||
|
* entryExpireTimeMS + the current time in MS. If an existing entry with the
|
||||||
|
* same name was in the cache, the entry is replaced. Returns TRUE on success
|
||||||
|
* or FALSE on failure.
|
||||||
|
*/
|
||||||
|
BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry);
|
||||||
|
|
||||||
|
/* Finds the entry with name name in the cache and returns a pointer to it, or
|
||||||
|
* NULL if it isn't found.
|
||||||
|
*/
|
||||||
|
const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
|
||||||
|
const UCHAR name[NCBNAMSZ]);
|
||||||
|
|
||||||
|
/* If the entry with name name is in the cache, updates its nbname member to
|
||||||
|
* nbname. The entry's expire time is implicitly updated to entryExpireTimeMS
|
||||||
|
* + the current time in MS, since getting the NetBIOS name meant validating
|
||||||
|
* the name and address anyway.
|
||||||
|
* Returns TRUE on success or FALSE on failure.
|
||||||
|
*/
|
||||||
|
BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
|
||||||
|
const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]);
|
||||||
|
|
||||||
|
void NBNameCacheDestroy(struct NBNameCache *cache);
|
||||||
|
|
||||||
|
#endif /* ndef __WINE_NBNAMECACHE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/* Copyright 2001 Mike McCormack
|
||||||
* Copyright 2001 Mike McCormack
|
* Copyright 2003 Juan Lang
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -18,185 +18,36 @@
|
||||||
|
|
||||||
#include "config.h"
|
#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 "wine/debug.h"
|
||||||
#include "winerror.h"
|
#include "netbios.h"
|
||||||
#include "nb30.h"
|
|
||||||
#include "lm.h"
|
|
||||||
#include "iphlpapi.h"
|
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(netbios);
|
WINE_DEFAULT_DEBUG_CHANNEL(netbios);
|
||||||
|
|
||||||
HMODULE NETAPI32_hModule = 0;
|
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)
|
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||||
{
|
{
|
||||||
TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
|
TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
|
||||||
|
|
||||||
switch (fdwReason) {
|
switch (fdwReason) {
|
||||||
case DLL_PROCESS_ATTACH:
|
case DLL_PROCESS_ATTACH:
|
||||||
|
{
|
||||||
DisableThreadLibraryCalls(hinstDLL);
|
DisableThreadLibraryCalls(hinstDLL);
|
||||||
NETAPI32_hModule = hinstDLL;
|
NETAPI32_hModule = hinstDLL;
|
||||||
break;
|
NetBIOSInit();
|
||||||
case DLL_PROCESS_DETACH:
|
NetBTInit();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
{
|
||||||
|
NetBIOSShutdown();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
UCHAR WINAPI Netbios(PNCB pncb)
|
|
||||||
{
|
|
||||||
UCHAR ret = NRC_ILLCMD;
|
|
||||||
|
|
||||||
TRACE("ncb = %p\n",pncb);
|
|
||||||
|
|
||||||
if(!pncb)
|
|
||||||
return NRC_INVADDRESS;
|
|
||||||
|
|
||||||
switch(pncb->ncb_command&0x7f)
|
|
||||||
{
|
|
||||||
case NCBRESET:
|
|
||||||
FIXME("NCBRESET adapter %d\n",pncb->ncb_lana_num);
|
|
||||||
if(pncb->ncb_lana_num < MAX_LANA )
|
|
||||||
{
|
|
||||||
MIB_IFROW row;
|
|
||||||
|
|
||||||
row.dwIndex = pncb->ncb_lana_num;
|
|
||||||
if (GetIfEntry(&row) != NO_ERROR)
|
|
||||||
ret = NRC_GOODRET;
|
|
||||||
else
|
|
||||||
ret = NRC_ILLCMD; /* NetBIOS emulator not found */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ret = NRC_ILLCMD; /* NetBIOS emulator not found */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBADDNAME:
|
|
||||||
FIXME("NCBADDNAME\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBADDGRNAME:
|
|
||||||
FIXME("NCBADDGRNAME\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBDELNAME:
|
|
||||||
FIXME("NCBDELNAME\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBSEND:
|
|
||||||
FIXME("NCBSEND\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBRECV:
|
|
||||||
FIXME("NCBRECV\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBHANGUP:
|
|
||||||
FIXME("NCBHANGUP\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBCANCEL:
|
|
||||||
FIXME("NCBCANCEL\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBLISTEN:
|
|
||||||
FIXME("NCBLISTEN\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBASTAT:
|
|
||||||
ret = NETBIOS_Astat(pncb);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NCBENUM:
|
|
||||||
ret = NETBIOS_Enum(pncb);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
FIXME("(%p): command code %02x\n", pncb, pncb->ncb_command);
|
|
||||||
|
|
||||||
ret = NRC_ILLCMD; /* NetBIOS emulator not found */
|
|
||||||
}
|
|
||||||
pncb->ncb_retcode = ret;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
NET_API_STATUS WINAPI NetServerEnum(
|
NET_API_STATUS WINAPI NetServerEnum(
|
||||||
LPCWSTR servername,
|
LPCWSTR servername,
|
||||||
DWORD level,
|
DWORD level,
|
||||||
|
|
|
@ -0,0 +1,846 @@
|
||||||
|
/* Copyright (c) 2003 Juan Lang
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include "config.h"
|
||||||
|
#include "wine/debug.h"
|
||||||
|
#include "nbcmdqueue.h"
|
||||||
|
#include "netbios.h"
|
||||||
|
|
||||||
|
WINE_DEFAULT_DEBUG_CHANNEL(netbios);
|
||||||
|
|
||||||
|
/* This file provides a NetBIOS emulator that implements the NetBIOS interface,
|
||||||
|
* including thread safety and asynchronous call support. The protocol
|
||||||
|
* implementation is separate, with blocking (synchronous) functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ADAPTERS_INCR 8
|
||||||
|
#define DEFAULT_NUM_SESSIONS 16
|
||||||
|
|
||||||
|
typedef struct _NetBIOSTransportTableEntry
|
||||||
|
{
|
||||||
|
ULONG id;
|
||||||
|
NetBIOSTransport transport;
|
||||||
|
} NetBIOSTransportTableEntry;
|
||||||
|
|
||||||
|
typedef struct _NetBIOSSession
|
||||||
|
{
|
||||||
|
BOOL inUse;
|
||||||
|
UCHAR state;
|
||||||
|
UCHAR local_name[NCBNAMSZ];
|
||||||
|
UCHAR remote_name[NCBNAMSZ];
|
||||||
|
void *data;
|
||||||
|
} NetBIOSSession;
|
||||||
|
|
||||||
|
/* This struct needs a little explanation, unfortunately. enabled is only
|
||||||
|
* used by nbInternalEnum (see). If transport_id is not 0 and transport
|
||||||
|
* is not NULL, the adapter is considered valid. (transport is a pointer to
|
||||||
|
* an entry in a NetBIOSTransportTableEntry.) data has data for the callers of
|
||||||
|
* NetBIOSEnumAdapters to be able to see. The lana is repeated there, even
|
||||||
|
* though I don't use it internally--it's for transports to use reenabling
|
||||||
|
* adapters using NetBIOSEnableAdapter.
|
||||||
|
*/
|
||||||
|
typedef struct _NetBIOSAdapter
|
||||||
|
{
|
||||||
|
BOOL enabled;
|
||||||
|
BOOL shuttingDown;
|
||||||
|
ULONG resetting;
|
||||||
|
ULONG transport_id;
|
||||||
|
NetBIOSTransport *transport;
|
||||||
|
NetBIOSAdapterImpl impl;
|
||||||
|
struct NBCmdQueue *cmdQueue;
|
||||||
|
CRITICAL_SECTION cs;
|
||||||
|
DWORD sessionsLen;
|
||||||
|
NetBIOSSession *sessions;
|
||||||
|
} NetBIOSAdapter;
|
||||||
|
|
||||||
|
typedef struct _NetBIOSAdapterTable {
|
||||||
|
CRITICAL_SECTION cs;
|
||||||
|
BOOL enumerated;
|
||||||
|
BOOL enumerating;
|
||||||
|
UCHAR tableSize;
|
||||||
|
NetBIOSAdapter *table;
|
||||||
|
} NetBIOSAdapterTable;
|
||||||
|
|
||||||
|
/* Just enough space for NBT right now */
|
||||||
|
static NetBIOSTransportTableEntry gTransports[1];
|
||||||
|
static UCHAR gNumTransports = 0;
|
||||||
|
static NetBIOSAdapterTable gNBTable;
|
||||||
|
|
||||||
|
static UCHAR nbResizeAdapterTable(UCHAR newSize)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
|
||||||
|
if (gNBTable.table)
|
||||||
|
gNBTable.table = (NetBIOSAdapter *)HeapReAlloc(GetProcessHeap(),
|
||||||
|
HEAP_ZERO_MEMORY, gNBTable.table,
|
||||||
|
newSize * sizeof(NetBIOSAdapter));
|
||||||
|
else
|
||||||
|
gNBTable.table = (NetBIOSAdapter *)HeapAlloc(GetProcessHeap(),
|
||||||
|
HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter));
|
||||||
|
if (gNBTable.table)
|
||||||
|
{
|
||||||
|
gNBTable.tableSize = newSize;
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NRC_OSRESNOTAV;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetBIOSInit(void)
|
||||||
|
{
|
||||||
|
memset(&gNBTable, 0, sizeof(gNBTable));
|
||||||
|
InitializeCriticalSection(&gNBTable.cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetBIOSShutdown(void)
|
||||||
|
{
|
||||||
|
UCHAR i;
|
||||||
|
|
||||||
|
EnterCriticalSection(&gNBTable.cs);
|
||||||
|
for (i = 0; i < gNBTable.tableSize; i++)
|
||||||
|
{
|
||||||
|
if (gNBTable.table[i].transport &&
|
||||||
|
gNBTable.table[i].transport->cleanupAdapter)
|
||||||
|
gNBTable.table[i].transport->cleanupAdapter(
|
||||||
|
gNBTable.table[i].impl.data);
|
||||||
|
}
|
||||||
|
for (i = 0; i < gNumTransports; i++)
|
||||||
|
if (gTransports[i].transport.cleanup)
|
||||||
|
gTransports[i].transport.cleanup();
|
||||||
|
LeaveCriticalSection(&gNBTable.cs);
|
||||||
|
DeleteCriticalSection(&gNBTable.cs);
|
||||||
|
HeapFree(GetProcessHeap(), 0, gNBTable.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport)
|
||||||
|
{
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
TRACE(": transport 0x%08lx, p %p\n", id, transport);
|
||||||
|
if (!transport)
|
||||||
|
ret = FALSE;
|
||||||
|
else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
|
||||||
|
{
|
||||||
|
FIXME("You tried to add %d transports, but I only have space for %d\n",
|
||||||
|
gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0]));
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UCHAR i;
|
||||||
|
|
||||||
|
ret = FALSE;
|
||||||
|
for (i = 0; !ret && i < gNumTransports; i++)
|
||||||
|
{
|
||||||
|
if (gTransports[i].id == id)
|
||||||
|
{
|
||||||
|
WARN("Replacing NetBIOS transport ID %ld\n", id);
|
||||||
|
memcpy(&gTransports[i].transport, transport,
|
||||||
|
sizeof(NetBIOSTransport));
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
gTransports[gNumTransports].id = id;
|
||||||
|
memcpy(&gTransports[gNumTransports].transport, transport,
|
||||||
|
sizeof(NetBIOSTransport));
|
||||||
|
gNumTransports++;
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("returning %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In this, I acquire the table lock to make sure no one else is modifying it.
|
||||||
|
* This is _probably_ overkill since it should only be called during the
|
||||||
|
* context of a NetBIOSEnum call, but just to be safe..
|
||||||
|
*/
|
||||||
|
BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
|
||||||
|
{
|
||||||
|
BOOL ret;
|
||||||
|
UCHAR i;
|
||||||
|
|
||||||
|
TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex,
|
||||||
|
data);
|
||||||
|
for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
|
||||||
|
;
|
||||||
|
if (gTransports[i].id == transport)
|
||||||
|
{
|
||||||
|
NetBIOSTransport *transportPtr = &gTransports[i].transport;
|
||||||
|
|
||||||
|
TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport);
|
||||||
|
|
||||||
|
EnterCriticalSection(&gNBTable.cs);
|
||||||
|
ret = FALSE;
|
||||||
|
for (i = 0; i < gNBTable.tableSize &&
|
||||||
|
gNBTable.table[i].transport != 0; i++)
|
||||||
|
;
|
||||||
|
if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
|
||||||
|
{
|
||||||
|
UCHAR newSize;
|
||||||
|
|
||||||
|
if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR)
|
||||||
|
newSize = gNBTable.tableSize + ADAPTERS_INCR;
|
||||||
|
else
|
||||||
|
newSize = MAX_LANA + 1;
|
||||||
|
nbResizeAdapterTable(newSize);
|
||||||
|
}
|
||||||
|
if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
|
||||||
|
{
|
||||||
|
TRACE(": registering as LANA %d\n", i);
|
||||||
|
gNBTable.table[i].transport_id = transport;
|
||||||
|
gNBTable.table[i].transport = transportPtr;
|
||||||
|
gNBTable.table[i].impl.lana = i;
|
||||||
|
gNBTable.table[i].impl.ifIndex = ifIndex;
|
||||||
|
gNBTable.table[i].impl.data = data;
|
||||||
|
gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap());
|
||||||
|
InitializeCriticalSection(&gNBTable.table[i].cs);
|
||||||
|
gNBTable.table[i].enabled = TRUE;
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&gNBTable.cs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = FALSE;
|
||||||
|
TRACE("returning %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In this, I acquire the table lock to make sure no one else is modifying it.
|
||||||
|
* This is _probably_ overkill since it should only be called during the
|
||||||
|
* context of a NetBIOSEnum call, but just to be safe..
|
||||||
|
*/
|
||||||
|
void NetBIOSEnableAdapter(UCHAR lana)
|
||||||
|
{
|
||||||
|
TRACE(": %d\n", lana);
|
||||||
|
if (lana < gNBTable.tableSize)
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&gNBTable.cs);
|
||||||
|
if (gNBTable.table[lana].transport != 0)
|
||||||
|
gNBTable.table[lana].enabled = TRUE;
|
||||||
|
LeaveCriticalSection(&gNBTable.cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbShutdownAdapter(NetBIOSAdapter *adapter)
|
||||||
|
{
|
||||||
|
if (adapter)
|
||||||
|
{
|
||||||
|
adapter->shuttingDown = TRUE;
|
||||||
|
NBCmdQueueCancelAll(adapter->cmdQueue);
|
||||||
|
if (adapter->transport->cleanupAdapter)
|
||||||
|
adapter->transport->cleanupAdapter(adapter->impl.data);
|
||||||
|
NBCmdQueueDestroy(adapter->cmdQueue);
|
||||||
|
DeleteCriticalSection(&adapter->cs);
|
||||||
|
memset(adapter, 0, sizeof(NetBIOSAdapter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbInternalEnum(void)
|
||||||
|
{
|
||||||
|
UCHAR i;
|
||||||
|
|
||||||
|
EnterCriticalSection(&gNBTable.cs);
|
||||||
|
TRACE("before mark\n");
|
||||||
|
/* mark: */
|
||||||
|
for (i = 0; i < gNBTable.tableSize; i++)
|
||||||
|
if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
|
||||||
|
gNBTable.table[i].enabled = FALSE;
|
||||||
|
|
||||||
|
TRACE("marked, before store, %d transports\n", gNumTransports);
|
||||||
|
/* store adapters: */
|
||||||
|
for (i = 0; i < gNumTransports; i++)
|
||||||
|
if (gTransports[i].transport.enumerate)
|
||||||
|
gTransports[i].transport.enumerate();
|
||||||
|
|
||||||
|
TRACE("before sweep\n");
|
||||||
|
/* sweep: */
|
||||||
|
for (i = 0; i < gNBTable.tableSize; i++)
|
||||||
|
if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
|
||||||
|
nbShutdownAdapter(&gNBTable.table[i]);
|
||||||
|
gNBTable.enumerated = TRUE;
|
||||||
|
LeaveCriticalSection(&gNBTable.cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
UCHAR NetBIOSNumAdapters(void)
|
||||||
|
{
|
||||||
|
UCHAR ret, i;
|
||||||
|
|
||||||
|
if (!gNBTable.enumerated)
|
||||||
|
nbInternalEnum();
|
||||||
|
for (i = 0, ret = 0; i < gNBTable.tableSize; i++)
|
||||||
|
if (gNBTable.table[i].transport != 0)
|
||||||
|
ret++;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
|
||||||
|
void *closure)
|
||||||
|
{
|
||||||
|
TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb,
|
||||||
|
closure);
|
||||||
|
if (cb)
|
||||||
|
{
|
||||||
|
BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
|
||||||
|
UCHAR i, numLANAs = 0;
|
||||||
|
|
||||||
|
EnterCriticalSection(&gNBTable.cs);
|
||||||
|
if (!gNBTable.enumerating)
|
||||||
|
{
|
||||||
|
gNBTable.enumerating = TRUE;
|
||||||
|
nbInternalEnum();
|
||||||
|
gNBTable.enumerating = FALSE;
|
||||||
|
}
|
||||||
|
for (i = 0; i < gNBTable.tableSize; i++)
|
||||||
|
if (enumAll || gNBTable.table[i].transport_id == transport)
|
||||||
|
numLANAs++;
|
||||||
|
if (numLANAs > 0)
|
||||||
|
{
|
||||||
|
UCHAR lanaIndex = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < gNBTable.tableSize; i++)
|
||||||
|
if (gNBTable.table[i].transport_id != 0 &&
|
||||||
|
(enumAll || gNBTable.table[i].transport_id == transport))
|
||||||
|
cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id,
|
||||||
|
&gNBTable.table[i].impl, closure);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&gNBTable.cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
|
||||||
|
{
|
||||||
|
NetBIOSAdapter *ret = NULL;
|
||||||
|
|
||||||
|
TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize);
|
||||||
|
if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0
|
||||||
|
&& gNBTable.table[lana].transport)
|
||||||
|
ret = &gNBTable.table[lana];
|
||||||
|
TRACE("returning %p\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbEnum(PNCB ncb)
|
||||||
|
{
|
||||||
|
PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
|
||||||
|
UCHAR i, ret;
|
||||||
|
|
||||||
|
TRACE(": ncb %p\n", ncb);
|
||||||
|
|
||||||
|
if (!lanas)
|
||||||
|
ret = NRC_BUFLEN;
|
||||||
|
else if (ncb->ncb_length < sizeof(LANA_ENUM))
|
||||||
|
ret = NRC_BUFLEN;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nbInternalEnum();
|
||||||
|
lanas->length = 0;
|
||||||
|
for (i = 0; i < gNBTable.tableSize; i++)
|
||||||
|
if (gNBTable.table[i].transport)
|
||||||
|
{
|
||||||
|
lanas->length++;
|
||||||
|
lanas->lana[i] = i;
|
||||||
|
}
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
}
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
|
||||||
|
|
||||||
|
static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
|
||||||
|
TRACE(": adapter %p, ncb %p\n", adapter, ncb);
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
switch (ncb->ncb_command & 0x7f)
|
||||||
|
{
|
||||||
|
case NCBCANCEL:
|
||||||
|
case NCBADDNAME:
|
||||||
|
case NCBADDGRNAME:
|
||||||
|
case NCBDELNAME:
|
||||||
|
case NCBRESET:
|
||||||
|
case NCBSSTAT:
|
||||||
|
ret = NRC_CANCEL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated
|
||||||
|
* session if cancelled */
|
||||||
|
case NCBCALL:
|
||||||
|
case NCBSEND:
|
||||||
|
case NCBCHAINSEND:
|
||||||
|
case NCBSENDNA:
|
||||||
|
case NCBCHAINSENDNA:
|
||||||
|
case NCBHANGUP:
|
||||||
|
{
|
||||||
|
if (ncb->ncb_lsn >= adapter->sessionsLen)
|
||||||
|
ret = NRC_SNUMOUT;
|
||||||
|
else if (!adapter->sessions[ncb->ncb_lsn].inUse)
|
||||||
|
ret = NRC_SNUMOUT;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
|
||||||
|
if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
|
||||||
|
nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
|
||||||
|
}
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resizes adapter to contain space for at least sessionsLen sessions.
|
||||||
|
* If allocating more space for sessions, sets the adapter's sessionsLen to
|
||||||
|
* sessionsLen. If the adapter's sessionsLen was already at least sessionsLen,
|
||||||
|
* does nothing. Does not modify existing sessions. Assumes the adapter is
|
||||||
|
* locked.
|
||||||
|
* Returns NRC_GOODRET on success, and something else on failure.
|
||||||
|
*/
|
||||||
|
static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
|
||||||
|
{
|
||||||
|
UCHAR ret = NRC_GOODRET;
|
||||||
|
|
||||||
|
if (adapter && adapter->sessionsLen < sessionsLen)
|
||||||
|
{
|
||||||
|
NetBIOSSession *newSessions;
|
||||||
|
|
||||||
|
if (adapter->sessions)
|
||||||
|
newSessions = (NetBIOSSession *)HeapReAlloc(GetProcessHeap(),
|
||||||
|
HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen *
|
||||||
|
sizeof(NetBIOSSession));
|
||||||
|
else
|
||||||
|
newSessions = (NetBIOSSession *)HeapAlloc(GetProcessHeap(),
|
||||||
|
HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession));
|
||||||
|
if (newSessions)
|
||||||
|
{
|
||||||
|
adapter->sessions = newSessions;
|
||||||
|
adapter->sessionsLen = sessionsLen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NRC_OSRESNOTAV;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
|
||||||
|
TRACE(": adapter %p, ncb %p\n", adapter, ncb);
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
if (InterlockedIncrement(&adapter->resetting) == 1)
|
||||||
|
{
|
||||||
|
UCHAR i, resizeTo;
|
||||||
|
|
||||||
|
NBCmdQueueCancelAll(adapter->cmdQueue);
|
||||||
|
|
||||||
|
EnterCriticalSection(&adapter->cs);
|
||||||
|
for (i = 0; i < adapter->sessionsLen; i++)
|
||||||
|
if (adapter->sessions[i].inUse)
|
||||||
|
nbInternalHangup(adapter, &adapter->sessions[i]);
|
||||||
|
if (!ncb->ncb_lsn)
|
||||||
|
resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS :
|
||||||
|
ncb->ncb_callname[0];
|
||||||
|
else if (adapter->sessionsLen == 0)
|
||||||
|
resizeTo = DEFAULT_NUM_SESSIONS;
|
||||||
|
else
|
||||||
|
resizeTo = 0;
|
||||||
|
if (resizeTo > 0)
|
||||||
|
ret = nbResizeAdapter(adapter, resizeTo);
|
||||||
|
else
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
LeaveCriticalSection(&adapter->cs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NRC_TOOMANY;
|
||||||
|
InterlockedDecrement(&adapter->resetting);
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret, i, spaceFor;
|
||||||
|
PSESSION_HEADER sstat;
|
||||||
|
|
||||||
|
TRACE(": adapter %p, NCB %p\n", adapter, ncb);
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BADDR;
|
||||||
|
if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
if (!ncb->ncb_buffer) return NRC_BADDR;
|
||||||
|
if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN;
|
||||||
|
|
||||||
|
sstat = (PSESSION_HEADER)ncb->ncb_buffer;
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
memset(sstat, 0, sizeof(SESSION_HEADER));
|
||||||
|
spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) /
|
||||||
|
sizeof(SESSION_BUFFER);
|
||||||
|
EnterCriticalSection(&adapter->cs);
|
||||||
|
for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++)
|
||||||
|
{
|
||||||
|
if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
|
||||||
|
!memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
|
||||||
|
{
|
||||||
|
if (sstat->num_sess < spaceFor)
|
||||||
|
{
|
||||||
|
PSESSION_BUFFER buf;
|
||||||
|
|
||||||
|
buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER)
|
||||||
|
+ sstat->num_sess * sizeof(SESSION_BUFFER));
|
||||||
|
buf->lsn = i;
|
||||||
|
buf->state = adapter->sessions[i].state;
|
||||||
|
memcpy(buf->local_name, adapter->sessions[i].local_name,
|
||||||
|
NCBNAMSZ);
|
||||||
|
memcpy(buf->remote_name, adapter->sessions[i].remote_name,
|
||||||
|
NCBNAMSZ);
|
||||||
|
buf->rcvs_outstanding = buf->sends_outstanding = 0;
|
||||||
|
sstat->num_sess++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NRC_BUFLEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&adapter->cs);
|
||||||
|
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret, i;
|
||||||
|
|
||||||
|
TRACE(": adapter %p, NCB %p\n", adapter, ncb);
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
|
||||||
|
if (!adapter->transport->call) return NRC_ILLCMD;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
EnterCriticalSection(&adapter->cs);
|
||||||
|
for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
|
||||||
|
;
|
||||||
|
if (i < adapter->sessionsLen)
|
||||||
|
{
|
||||||
|
adapter->sessions[i].inUse = TRUE;
|
||||||
|
adapter->sessions[i].state = CALL_PENDING;
|
||||||
|
memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ);
|
||||||
|
memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ);
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = NRC_LOCTFUL;
|
||||||
|
LeaveCriticalSection(&adapter->cs);
|
||||||
|
|
||||||
|
if (ret == NRC_GOODRET)
|
||||||
|
{
|
||||||
|
ret = adapter->transport->call(adapter->impl.data, ncb,
|
||||||
|
&adapter->sessions[i].data);
|
||||||
|
if (ret == NRC_GOODRET)
|
||||||
|
{
|
||||||
|
ncb->ncb_lsn = i;
|
||||||
|
adapter->sessions[i].state = SESSION_ESTABLISHED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adapter->sessions[i].inUse = FALSE;
|
||||||
|
adapter->sessions[i].state = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
NetBIOSSession *session;
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!adapter->transport->send) return NRC_ILLCMD;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
|
||||||
|
if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
|
||||||
|
if (!ncb->ncb_buffer) return NRC_BADDR;
|
||||||
|
|
||||||
|
session = &adapter->sessions[ncb->ncb_lsn];
|
||||||
|
if (session->state != SESSION_ESTABLISHED)
|
||||||
|
ret = NRC_SNUMOUT;
|
||||||
|
else
|
||||||
|
ret = adapter->transport->send(adapter->impl.data, session->data, ncb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
NetBIOSSession *session;
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!adapter->transport->recv) return NRC_ILLCMD;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
|
||||||
|
if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
|
||||||
|
if (!ncb->ncb_buffer) return NRC_BADDR;
|
||||||
|
|
||||||
|
session = &adapter->sessions[ncb->ncb_lsn];
|
||||||
|
if (session->state != SESSION_ESTABLISHED)
|
||||||
|
ret = NRC_SNUMOUT;
|
||||||
|
else
|
||||||
|
ret = adapter->transport->recv(adapter->impl.data, session->data, ncb);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!session) return NRC_SNUMOUT;
|
||||||
|
|
||||||
|
if (adapter->transport->hangup)
|
||||||
|
ret = adapter->transport->hangup(adapter->impl.data, session->data);
|
||||||
|
else
|
||||||
|
ret = NRC_ILLCMD;
|
||||||
|
EnterCriticalSection(&adapter->cs);
|
||||||
|
memset(session, 0, sizeof(NetBIOSSession));
|
||||||
|
LeaveCriticalSection(&adapter->cs);
|
||||||
|
return NRC_GOODRET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
NetBIOSSession *session;
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
|
||||||
|
if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
|
||||||
|
|
||||||
|
session = &adapter->sessions[ncb->ncb_lsn];
|
||||||
|
if (session->state != SESSION_ESTABLISHED)
|
||||||
|
ret = NRC_SNUMOUT;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
session->state = HANGUP_PENDING;
|
||||||
|
ret = nbInternalHangup(adapter, session);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetBIOSHangupSession(PNCB ncb)
|
||||||
|
{
|
||||||
|
NetBIOSAdapter *adapter;
|
||||||
|
|
||||||
|
if (!ncb) return;
|
||||||
|
|
||||||
|
adapter = nbGetAdapter(ncb->ncb_lana_num);
|
||||||
|
if (adapter)
|
||||||
|
{
|
||||||
|
if (ncb->ncb_lsn < adapter->sessionsLen &&
|
||||||
|
adapter->sessions[ncb->ncb_lsn].inUse)
|
||||||
|
nbHangup(adapter, ncb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!adapter->transport->astat) return NRC_ILLCMD;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
if (!ncb->ncb_buffer) return NRC_BADDR;
|
||||||
|
if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
|
||||||
|
|
||||||
|
ret = adapter->transport->astat(adapter->impl.data, ncb);
|
||||||
|
if (ncb->ncb_callname[0] == '*')
|
||||||
|
{
|
||||||
|
PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
|
||||||
|
|
||||||
|
astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret, cmd;
|
||||||
|
|
||||||
|
TRACE(": adapter %p, ncb %p\n", adapter, ncb);
|
||||||
|
|
||||||
|
if (!adapter) return NRC_BRIDGE;
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
cmd = ncb->ncb_command & 0x7f;
|
||||||
|
if (cmd == NCBRESET)
|
||||||
|
ret = nbReset(adapter, ncb);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
|
||||||
|
if (ret == NRC_GOODRET)
|
||||||
|
{
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case NCBCALL:
|
||||||
|
ret = nbCall(adapter, ncb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* WinNT doesn't chain sends, it always sends immediately.
|
||||||
|
* Doubt there's any real significance to the NA variants.
|
||||||
|
*/
|
||||||
|
case NCBSEND:
|
||||||
|
case NCBSENDNA:
|
||||||
|
case NCBCHAINSEND:
|
||||||
|
case NCBCHAINSENDNA:
|
||||||
|
ret = nbSend(adapter, ncb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NCBRECV:
|
||||||
|
ret = nbRecv(adapter, ncb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NCBHANGUP:
|
||||||
|
ret = nbHangup(adapter, ncb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NCBASTAT:
|
||||||
|
ret = nbAStat(adapter, ncb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NCBFINDNAME:
|
||||||
|
if (adapter->transport->findName)
|
||||||
|
ret = adapter->transport->findName(adapter->impl.data,
|
||||||
|
ncb);
|
||||||
|
else
|
||||||
|
ret = NRC_ILLCMD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
|
||||||
|
ret = NRC_ILLCMD;
|
||||||
|
}
|
||||||
|
NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
|
||||||
|
{
|
||||||
|
PNCB ncb = (PNCB)lpVoid;
|
||||||
|
|
||||||
|
if (ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret;
|
||||||
|
NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
|
||||||
|
|
||||||
|
if (adapter)
|
||||||
|
ret = nbDispatch(adapter, ncb);
|
||||||
|
else
|
||||||
|
ret = NRC_BRIDGE;
|
||||||
|
ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret;
|
||||||
|
if (ncb->ncb_post)
|
||||||
|
ncb->ncb_post(ncb);
|
||||||
|
else if (ncb->ncb_event)
|
||||||
|
SetEvent(ncb->ncb_event);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCHAR WINAPI Netbios(PNCB ncb)
|
||||||
|
{
|
||||||
|
UCHAR ret, cmd;
|
||||||
|
|
||||||
|
TRACE("ncb = %p\n", ncb);
|
||||||
|
|
||||||
|
if (!ncb) return NRC_INVADDRESS;
|
||||||
|
|
||||||
|
TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n",
|
||||||
|
ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length);
|
||||||
|
cmd = ncb->ncb_command & 0x7f;
|
||||||
|
|
||||||
|
if (cmd == NCBENUM)
|
||||||
|
ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
|
||||||
|
|
||||||
|
if (!adapter)
|
||||||
|
ret = NRC_BRIDGE;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (adapter->shuttingDown)
|
||||||
|
ret = NRC_IFBUSY;
|
||||||
|
else if (adapter->resetting)
|
||||||
|
ret = NRC_TOOMANY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* non-asynch commands first */
|
||||||
|
if (cmd == NCBCANCEL)
|
||||||
|
ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
|
||||||
|
nbCancel(adapter, ncb);
|
||||||
|
else if (cmd == NCBSSTAT)
|
||||||
|
ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
|
||||||
|
nbSStat(adapter, ncb);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ncb->ncb_command & ASYNCH)
|
||||||
|
{
|
||||||
|
HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
|
||||||
|
CREATE_SUSPENDED, NULL);
|
||||||
|
|
||||||
|
if (thread != NULL)
|
||||||
|
{
|
||||||
|
ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING;
|
||||||
|
if (ncb->ncb_event)
|
||||||
|
ResetEvent(ncb->ncb_event);
|
||||||
|
ResumeThread(thread);
|
||||||
|
CloseHandle(thread);
|
||||||
|
ret = NRC_GOODRET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
|
||||||
|
NRC_OSRESNOTAV;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
|
||||||
|
nbDispatch(adapter, ncb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("returning 0x%02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/* Copyright (c) 2003 Juan Lang
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#ifndef __WINE_NETBIOS_H__
|
||||||
|
#define __WINE_NETBIOS_H__
|
||||||
|
|
||||||
|
#include <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__ */
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/* Copyright 2002 Andriy Palamarchuk
|
||||||
* Copyright 2002 Andriy Palamarchuk
|
* Copyright (c) 2003 Juan Lang
|
||||||
*
|
*
|
||||||
* netapi32 user functions
|
* netapi32 user functions
|
||||||
*
|
*
|
||||||
|
@ -33,6 +33,7 @@
|
||||||
#include "winreg.h"
|
#include "winreg.h"
|
||||||
#include "winternl.h"
|
#include "winternl.h"
|
||||||
#include "ntsecapi.h"
|
#include "ntsecapi.h"
|
||||||
|
#include "netbios.h"
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
|
WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
|
||||||
|
@ -65,151 +66,228 @@ BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wprint_mac(WCHAR* buffer, PIP_ADAPTER_INFO adapter)
|
static void wprint_mac(WCHAR* buffer, int len, PMIB_IFROW ifRow)
|
||||||
{
|
{
|
||||||
if (adapter != NULL)
|
int i;
|
||||||
{
|
unsigned char val;
|
||||||
int i;
|
|
||||||
unsigned char val;
|
|
||||||
|
|
||||||
for (i = 0; i<max(adapter->AddressLength, 6); i++)
|
if (!buffer)
|
||||||
{
|
return;
|
||||||
val = adapter->Address[i];
|
if (len < 1)
|
||||||
if ((val >>4) >9)
|
return;
|
||||||
buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10);
|
if (!ifRow)
|
||||||
else
|
{
|
||||||
buffer[2*i] = (WCHAR)((val >>4) + '0');
|
*buffer = '\0';
|
||||||
if ((val & 0xf ) >9)
|
return;
|
||||||
buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10);
|
|
||||||
else
|
|
||||||
buffer[2*i+1] = (WCHAR)((val & 0xf) + '0');
|
|
||||||
}
|
|
||||||
buffer[12]=(WCHAR)0;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
buffer[0] = 0;
|
for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++)
|
||||||
|
{
|
||||||
|
val = ifRow->bPhysAddr[i];
|
||||||
|
if ((val >>4) >9)
|
||||||
|
buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10);
|
||||||
|
else
|
||||||
|
buffer[2*i] = (WCHAR)((val >>4) + '0');
|
||||||
|
if ((val & 0xf ) >9)
|
||||||
|
buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10);
|
||||||
|
else
|
||||||
|
buffer[2*i+1] = (WCHAR)((val & 0xf) + '0');
|
||||||
|
}
|
||||||
|
buffer[2*i]=(WCHAR)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_"
|
/* Theoretically this could be too short, except that MS defines
|
||||||
#define TRANSPORT_NAME_LEN \
|
* MAX_ADAPTER_NAME as 128, and MAX_INTERFACE_NAME_LEN as 256, and both
|
||||||
(sizeof(TRANSPORT_NAME_HEADER) + MAX_ADAPTER_NAME_LENGTH)
|
* represent a count of WCHARs, so even with an extroardinarily long header
|
||||||
|
* this will be plenty
|
||||||
|
*/
|
||||||
|
#define MAX_TRANSPORT_NAME MAX_INTERFACE_NAME_LEN
|
||||||
|
#define MAX_TRANSPORT_ADDR 13
|
||||||
|
|
||||||
static void wprint_name(WCHAR *buffer, int len, PIP_ADAPTER_INFO adapter)
|
#define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_"
|
||||||
|
#define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_"
|
||||||
|
|
||||||
|
static void wprint_name(WCHAR *buffer, int len, ULONG transport,
|
||||||
|
PMIB_IFROW ifRow)
|
||||||
{
|
{
|
||||||
WCHAR *ptr;
|
WCHAR *ptr1, *ptr2;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return;
|
return;
|
||||||
if (!adapter)
|
if (!ifRow)
|
||||||
return;
|
{
|
||||||
|
*buffer = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (ptr = buffer, name = TRANSPORT_NAME_HEADER; *name && ptr < buffer + len;
|
if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG)))
|
||||||
ptr++, name++)
|
name = NBT_TRANSPORT_NAME_HEADER;
|
||||||
*ptr = *name;
|
else
|
||||||
for (name = adapter->AdapterName; name && *name && ptr < buffer + len;
|
name = UNKNOWN_TRANSPORT_NAME_HEADER;
|
||||||
ptr++, name++)
|
|
||||||
*ptr = *name;
|
for (ptr1 = buffer; *name && ptr1 < buffer + len; ptr1++, name++)
|
||||||
*ptr = '\0';
|
*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
|
NET_API_STATUS WINAPI
|
||||||
NetWkstaTransportEnum(LPCWSTR ServerName, DWORD level, LPBYTE* pbuf,
|
NetWkstaTransportEnum(LPCWSTR ServerName, DWORD level, LPBYTE* pbuf,
|
||||||
DWORD prefmaxlen, LPDWORD read_entries,
|
DWORD prefmaxlen, LPDWORD read_entries,
|
||||||
LPDWORD total_entries, LPDWORD hresume)
|
LPDWORD total_entries, LPDWORD hresume)
|
||||||
{
|
{
|
||||||
FIXME(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName),
|
NET_API_STATUS ret;
|
||||||
level, pbuf, prefmaxlen, read_entries, total_entries,hresume);
|
|
||||||
if (!NETAPI_IsLocalComputer(ServerName))
|
|
||||||
{
|
|
||||||
FIXME(":not implemented for non-local computers\n");
|
|
||||||
return ERROR_INVALID_LEVEL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (hresume && *hresume)
|
|
||||||
{
|
|
||||||
FIXME(":resume handle not implemented\n");
|
|
||||||
return ERROR_INVALID_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
case 0: /* transport info */
|
|
||||||
{
|
|
||||||
PWKSTA_TRANSPORT_INFO_0 ti;
|
|
||||||
int i,size_needed,n_adapt;
|
|
||||||
DWORD apiReturn, adaptInfoSize = 0;
|
|
||||||
PIP_ADAPTER_INFO info, ptr;
|
|
||||||
|
|
||||||
apiReturn = GetAdaptersInfo(NULL, &adaptInfoSize);
|
|
||||||
if (apiReturn == ERROR_NO_DATA)
|
|
||||||
return ERROR_NETWORK_UNREACHABLE;
|
|
||||||
if (!read_entries)
|
|
||||||
return STATUS_ACCESS_VIOLATION;
|
|
||||||
if (!total_entries || !pbuf)
|
|
||||||
return RPC_X_NULL_REF_POINTER;
|
|
||||||
|
|
||||||
info = (PIP_ADAPTER_INFO)malloc(adaptInfoSize);
|
TRACE(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName),
|
||||||
apiReturn = GetAdaptersInfo(info, &adaptInfoSize);
|
level, pbuf, prefmaxlen, read_entries, total_entries,hresume);
|
||||||
if (apiReturn != NO_ERROR)
|
if (!NETAPI_IsLocalComputer(ServerName))
|
||||||
{
|
{
|
||||||
free(info);
|
FIXME(":not implemented for non-local computers\n");
|
||||||
return apiReturn;
|
ret = ERROR_INVALID_LEVEL;
|
||||||
}
|
|
||||||
|
|
||||||
for (n_adapt = 0, ptr = info; ptr; ptr = ptr->Next)
|
|
||||||
n_adapt++;
|
|
||||||
size_needed = n_adapt * sizeof(WKSTA_TRANSPORT_INFO_0)
|
|
||||||
+ n_adapt * TRANSPORT_NAME_LEN * sizeof (WCHAR)
|
|
||||||
+ n_adapt * 13 * sizeof (WCHAR);
|
|
||||||
if (prefmaxlen == MAX_PREFERRED_LENGTH)
|
|
||||||
NetApiBufferAllocate( size_needed, (LPVOID *) pbuf);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (size_needed > prefmaxlen)
|
|
||||||
{
|
|
||||||
free(info);
|
|
||||||
return ERROR_MORE_DATA;
|
|
||||||
}
|
|
||||||
NetApiBufferAllocate(prefmaxlen,
|
|
||||||
(LPVOID *) pbuf);
|
|
||||||
}
|
|
||||||
for (i = 0, ptr = info; ptr; ptr = ptr->Next, i++)
|
|
||||||
{
|
|
||||||
ti = (PWKSTA_TRANSPORT_INFO_0)
|
|
||||||
((PBYTE) *pbuf + i * sizeof(WKSTA_TRANSPORT_INFO_0));
|
|
||||||
ti->wkti0_quality_of_service=0;
|
|
||||||
ti->wkti0_number_of_vcs=0;
|
|
||||||
ti->wkti0_transport_name= (LPWSTR)
|
|
||||||
((PBYTE )*pbuf +
|
|
||||||
n_adapt * sizeof(WKSTA_TRANSPORT_INFO_0)
|
|
||||||
+ i * TRANSPORT_NAME_LEN * sizeof (WCHAR));
|
|
||||||
wprint_name(ti->wkti0_transport_name,TRANSPORT_NAME_LEN, ptr);
|
|
||||||
ti->wkti0_transport_address= (LPWSTR)
|
|
||||||
((PBYTE )*pbuf +
|
|
||||||
n_adapt * sizeof(WKSTA_TRANSPORT_INFO_0) +
|
|
||||||
n_adapt * TRANSPORT_NAME_LEN * sizeof (WCHAR)
|
|
||||||
+ i * 13 * sizeof (WCHAR));
|
|
||||||
ti->wkti0_wan_ish=TRUE; /*TCPIP/NETBIOS Protocoll*/
|
|
||||||
wprint_mac(ti->wkti0_transport_address, ptr);
|
|
||||||
TRACE("%d of %d:ti at %p transport_address at %p %s\n",i,n_adapt,
|
|
||||||
ti, ti->wkti0_transport_address, debugstr_w(ti->wkti0_transport_address));
|
|
||||||
}
|
|
||||||
*read_entries = n_adapt;
|
|
||||||
*total_entries = n_adapt;
|
|
||||||
free(info);
|
|
||||||
if(hresume) *hresume= 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ERR("Invalid level %ld is specified\n", level);
|
|
||||||
return ERROR_INVALID_LEVEL;
|
|
||||||
}
|
|
||||||
return NERR_Success;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hresume && *hresume)
|
||||||
|
{
|
||||||
|
FIXME(":resume handle not implemented\n");
|
||||||
|
return ERROR_INVALID_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 0: /* transport info */
|
||||||
|
{
|
||||||
|
ULONG allTransports;
|
||||||
|
struct WkstaTransportEnumData enumData;
|
||||||
|
|
||||||
|
if (NetBIOSNumAdapters() == 0)
|
||||||
|
return ERROR_NETWORK_UNREACHABLE;
|
||||||
|
if (!read_entries)
|
||||||
|
return STATUS_ACCESS_VIOLATION;
|
||||||
|
if (!total_entries || !pbuf)
|
||||||
|
return RPC_X_NULL_REF_POINTER;
|
||||||
|
|
||||||
|
enumData.prefmaxlen = prefmaxlen;
|
||||||
|
enumData.pbuf = pbuf;
|
||||||
|
memcpy(&allTransports, ALL_TRANSPORTS, sizeof(ULONG));
|
||||||
|
NetBIOSEnumAdapters(allTransports, WkstaEnumAdaptersCallback,
|
||||||
|
&enumData);
|
||||||
|
*read_entries = enumData.n_read;
|
||||||
|
*total_entries = enumData.n_adapt;
|
||||||
|
if (hresume) *hresume= 0;
|
||||||
|
ret = enumData.ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ERR("Invalid level %ld is specified\n", level);
|
||||||
|
ret = ERROR_INVALID_LEVEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* NetWkstaUserGetInfo (NETAPI32.@)
|
* NetWkstaUserGetInfo (NETAPI32.@)
|
||||||
|
|
|
@ -25,9 +25,6 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* NetBIOS */
|
|
||||||
UCHAR WINAPI Netbios(PNCB pncb);
|
|
||||||
|
|
||||||
typedef struct _WKSTA_TRANSPORT_INFO_0 {
|
typedef struct _WKSTA_TRANSPORT_INFO_0 {
|
||||||
DWORD wkti0_quality_of_service;
|
DWORD wkti0_quality_of_service;
|
||||||
DWORD wkti0_number_of_vcs;
|
DWORD wkti0_number_of_vcs;
|
||||||
|
|
141
include/nb30.h
141
include/nb30.h
|
@ -26,18 +26,34 @@ extern "C" {
|
||||||
#define NCBNAMSZ 16
|
#define NCBNAMSZ 16
|
||||||
#define MAX_LANA 0xfe
|
#define MAX_LANA 0xfe
|
||||||
|
|
||||||
#define NCBRESET 0x32
|
#define NCBCALL 0x10
|
||||||
#define NCBADDNAME 0x30
|
#define NCBLISTEN 0x11
|
||||||
#define NCBADDGRNAME 0x36
|
#define NCBHANGUP 0x12
|
||||||
#define NCBDELNAME 0x31
|
|
||||||
#define NCBSEND 0x14
|
#define NCBSEND 0x14
|
||||||
#define NCBRECV 0x15
|
#define NCBRECV 0x15
|
||||||
#define NCBHANGUP 0x12
|
#define NCBRECVANY 0x16
|
||||||
#define NCBCANCEL 0x35
|
#define NCBCHAINSEND 0x17
|
||||||
#define NCBLISTEN 0x11
|
#define NCBDGSEND 0x20
|
||||||
#define NCBCALL 0x10
|
#define NCBDGRECV 0x21
|
||||||
|
#define NCBDGSENDBC 0x22
|
||||||
|
#define NCBDGRECVBC 0x23
|
||||||
|
#define NCBADDNAME 0x30
|
||||||
|
#define NCBDELNAME 0x31
|
||||||
|
#define NCBRESET 0x32
|
||||||
#define NCBASTAT 0x33
|
#define NCBASTAT 0x33
|
||||||
|
#define NCBSSTAT 0x34
|
||||||
|
#define NCBCANCEL 0x35
|
||||||
|
#define NCBADDGRNAME 0x36
|
||||||
#define NCBENUM 0x37
|
#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
|
typedef struct _NCB
|
||||||
{
|
{
|
||||||
|
@ -51,7 +67,7 @@ typedef struct _NCB
|
||||||
UCHAR ncb_name[NCBNAMSZ];
|
UCHAR ncb_name[NCBNAMSZ];
|
||||||
UCHAR ncb_rto;
|
UCHAR ncb_rto;
|
||||||
UCHAR ncb_sto;
|
UCHAR ncb_sto;
|
||||||
VOID (*ncb_post)(struct _NCB *);
|
VOID (CALLBACK *ncb_post)(struct _NCB *);
|
||||||
UCHAR ncb_lana_num;
|
UCHAR ncb_lana_num;
|
||||||
UCHAR ncb_cmd_cplt;
|
UCHAR ncb_cmd_cplt;
|
||||||
UCHAR ncb_reserved[10];
|
UCHAR ncb_reserved[10];
|
||||||
|
@ -89,22 +105,111 @@ typedef struct _ADAPTER_STATUS
|
||||||
WORD name_count;
|
WORD name_count;
|
||||||
} ADAPTER_STATUS, *PADAPTER_STATUS;
|
} 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
|
typedef struct _LANA_ENUM
|
||||||
{
|
{
|
||||||
UCHAR length;
|
UCHAR length;
|
||||||
UCHAR lana[MAX_LANA+1];
|
UCHAR lana[MAX_LANA+1];
|
||||||
} LANA_ENUM, *PLANA_ENUM;
|
} LANA_ENUM, *PLANA_ENUM;
|
||||||
|
|
||||||
#define NRC_GOODRET 0x00
|
typedef struct _FIND_NAME_HEADER
|
||||||
#define NRC_BUFLEN 0x01
|
{
|
||||||
#define NRC_ILLCMD 0x03
|
WORD node_count;
|
||||||
#define NRC_CMDTMO 0x05
|
UCHAR reserved;
|
||||||
#define NRC_INCOMP 0x06
|
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_NORESOURCES 0x38
|
||||||
#define NRC_INVADDRESS 0x39
|
#define NRC_INVADDRESS 0x39
|
||||||
#define NRC_PENDING 0xff
|
#define NRC_INVDDID 0x3b
|
||||||
#define NRC_OPENERROR 0x3f
|
#define NRC_LOCKFAIL 0x3c
|
||||||
#define NRC_SYSTEM 0x40
|
#define NRC_OPENERROR 0x3f
|
||||||
|
#define NRC_SYSTEM 0x40
|
||||||
|
#define NRC_PENDING 0xff
|
||||||
|
|
||||||
|
UCHAR WINAPI Netbios(PNCB pncb);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue