📄 netbios.c
字号:
/* 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;
LONG 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 = HeapReAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, gNBTable.table,
newSize * sizeof(NetBIOSAdapter));
else
gNBTable.table = 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)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -