Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1610 lines
45 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
nbltsvr.c
Abstract:
Loadable transport for NT & OS/2 NetBios - server side.
This file is packaged as a dynamic link library, that is loaded on
demand by the RPC runtime. It provides basic connection oriented,
message based operations. This module will create a static or dynamic
(that is, the name is generated by this module) NetBios name. Once
the name is created, it will post listens on the name to accept new
sessions from clients. The ReceiveAny function will wait for a new
message on any of the sessions open with this name. Replies are
returned via the Send function to a given client.
Author:
Steven Zeck (stevez) 2/12/92
Danny Glasser (dannygl) 2/18/93
--*/
#include <NetBCom.h>
#include <limits.h>
#include <stdlib.h>
#define MAX_ADAPTER_PER_CARD 8
#define MAX_ENDPOINT_NUMBER UCHAR_MAX
#define INITIAL_ENDPOINT_NUMBER 0x21 // 0-0x20 are used by LANMAN components
// This table maps system error codes into RPC generic ones.
ERROR_TABLE NetBiosErrors[] =
{
NRC_BFULL, RPC_S_OUT_OF_RESOURCES,
NRC_CMDTMO, RPC_P_TIMEOUT,
NRC_NORES, RPC_S_OUT_OF_RESOURCES,
NRC_DUPNAME, RPC_S_DUPLICATE_ENDPOINT,
NRC_NAMTFUL, RPC_S_OUT_OF_RESOURCES,
NRC_ACTSES, RPC_S_OUT_OF_RESOURCES,
NRC_LOCTFUL, RPC_S_OUT_OF_RESOURCES,
NRC_REMTFUL, RPC_S_SERVER_TOO_BUSY,
NRC_TOOMANY, RPC_S_OUT_OF_RESOURCES,
NRC_MAXAPPS, RPC_S_OUT_OF_RESOURCES,
NRC_NORESOURCES, RPC_S_OUT_OF_RESOURCES,
NRC_SCLOSED, RPC_P_SEND_FAILED,
NRC_SABORT, RPC_P_SEND_FAILED,
NRC_BRIDGE, RPC_S_PROTSEQ_NOT_SUPPORTED,
NRC_SNUMOUT, RPC_P_SEND_FAILED,
NRC_SYSTEM, RPC_S_OUT_OF_RESOURCES,
0
};
// Copy a string from an ANSI buffer to a native format buffer
#ifdef NTWIN32RPC
#define COPY_TO_NATIVE_STRING mbstowcs
#endif
#ifdef DOSWIN32RPC
#define COPY_TO_NATIVE_STRING memcpy
#endif
// The following macro is used to build a unique connection key to pass to
// I_RpcTransServer{New,Find}Connection().
#define MAKE_CONNECTION_KEY(lana, lsn) ((lana) << 8 | (lsn))
// The following macros are used to determine whether to add or delete
// Receive-Any NCBs dynamically, given the current number of connections
// and Receive-Any NCBs.
//
// The algorithm is that the number of Receive-Any NCBs increases
// logarithmically w.r.t. the number of clients, e.g. 1 RA for 0-1 clients,
// 2 RAs for 2-3 clients, 3 RAs for 4-7 clients, 4 RAs for 8-15 clients,
// etc.
#define TOO_FEW_RCVS(conns, rcvs) ( (1 << (rcvs)) <= (conns) )
#define TOO_MANY_RCVS(conns, rcvs) ( ((rcvs) > 1) \
&& (1 << ((rcvs) - 1)) > (conns) )
// The following is a generic linked list forward pointer.
typedef void ** PLIST_ITEM;
// The following is per receive-any NCB information. All receive-any's
// are linked in a list owned by the adapter. Note: the first
// member of a linked list structure is the forward pointer.
typedef struct _RECEIVE
{
struct _RECEIVE *NextReceive; // Forward link in receive list
UINT AddrIndex; // Index in address's receive
// pointer table
NCB theNCB; // The NCB structure
CLIENT_BUFFER buffer; // The NCB buffer
} RECEIVE, *PRECEIVE;
// Following is the per client session information.
typedef struct _CONNECTION{
struct _ADAPTER *Adapter; // Adapter over which I'm connected
UCHAR lsn; // netbios Local Session Number
DWORD ClientSeqNum; // Sequence # of next client buffer
} CONNECTION, *PCONNECTION;
// Following is the per adapter information. This allows one address
// and protocol pair to be used on all the cards that support the protocol.
typedef struct _ADAPTER{
UCHAR lana_num; // the adapter #
UCHAR iName; // NetBios name number
INT ConnectionCount; // # of connections on this lana
NCB ListenNCB; // NCB for async listens
INT ReceiveCount; // # of receive-anys on this lana
struct _RECEIVE *FirstReceive; // First receive in the list
} ADAPTER, *PADAPTER;
// Following is the per NetBios address information.
typedef struct _TADDRESS{
UINT AdapterCount; // Number of adapters and a
PADAPTER Adapters; // control array
UINT ReceivePtrCount; // Number of receive pointers
PRECEIVE *ReceivePtrs; // and an array
HANDLE Events[MAXIMUM_WAIT_OBJECTS]; // Array of events
} TADDRESS, *PTADDRESS;
// EVENT_COUNT - This macro, which computes the number of events in an
// address's event array, is primarily for readability.
#define EVENT_COUNT(pAdd) ((pAdd)->AdapterCount + (pAdd)->ReceivePtrCount)
extern RPC_SERVER_TRANSPORT_INFO TransInfo;
INTERNAL_FUNCTION PRECEIVE
DeleteReceive(
IN PTADDRESS pAdd,
IN int Number,
IN PRECEIVE pReceive
);
INTERNAL_FUNCTION PLIST_ITEM
RemoveLinkList(
PLIST_ITEM pListFirst,
void *Delete
)
/*++
Routine Description:
Search through a singly linked list and remove the requested item.
Linked lists use a single forward pointer as the first member of
the structure.
Arguments:
pListFirst - pointer to the head of the linked list
Delete - the item to delete
Returns:
A pointer to the linked list element that precedes the one being
removed.
--*/
{
CRITICAL_ENTER();
// Find the node pointing to the item to be deleted.
while (*pListFirst && *pListFirst != Delete)
pListFirst = (PLIST_ITEM) *pListFirst;
ASSERT(pListFirst);
// Set the forward link to the deleted one's.
*pListFirst = *(PLIST_ITEM) Delete;
CRITICAL_LEAVE();
return pListFirst;
}
INTERNAL_FUNCTION RPC_TRANS_STATUS
SubmitReceive(
IN PTADDRESS pAdd,
IN int Number,
IN PRECEIVE pReceive OPTIONAL
)
/*++
Routine Description:
Submit an async receive-any NCB on the specific adapter and address.
Arguments:
pAdd - the address on which to submit the async receive any.
Number - which adapter card to operate on.
pReceive - the receive buffer to be submitted. If 0, a new receive is
to be allocated.
--*/
{
RPC_TRANS_STATUS status = 0;
HANDLE event;
PADAPTER Card = &pAdd->Adapters[Number];
if (! pReceive)
{
CRITICAL_ENTER();
// Verify that we have room for another event, allocate memory for
// the receive, and create an event for the receive.
if (EVENT_COUNT(pAdd) >= MAXIMUM_WAIT_OBJECTS)
{
status = RPC_S_OUT_OF_RESOURCES;
}
else if (! (pReceive = I_RpcAllocate(sizeof(*pReceive))))
{
status = RPC_S_OUT_OF_MEMORY;
}
else if (! (event = CreateEvent(NULL, TRUE, TRUE, NULL)))
{
I_RpcFree(pReceive);
status = RPC_S_OUT_OF_RESOURCES;
}
else
{
// Everything succeeded!
// Add this receive to the adapter's linked list and increment
// the count.
pReceive->NextReceive = Card->FirstReceive;
Card->FirstReceive = pReceive;
Card->ReceiveCount++;
// Add the receive pointer and event to the address's arrays.
pAdd->ReceivePtrs[pAdd->ReceivePtrCount] = pReceive;
pAdd->Events[EVENT_COUNT(pAdd)] = event;
// Store the pointer's index in the receive itself, and
// increment the pointer count.
pReceive->AddrIndex = pAdd->ReceivePtrCount++;
// Zero the NCB and store the event handle in it.
memset(&pReceive->theNCB, 0, sizeof(pReceive->theNCB));
pReceive->theNCB.ncb_event = event;
}
CRITICAL_LEAVE();
if (status)
return status;
}
// Initialize the NCB
pReceive->theNCB.ncb_num = Card->iName;
pReceive->theNCB.ncb_buffer = (PUCHAR) &pReceive->buffer;
pReceive->theNCB.ncb_length = sizeof(pReceive->buffer);
pReceive->theNCB.ncb_lana_num = Card->lana_num;
// Submit the NCB
status = execNCB(NCBRECVANY | ASYNCH, &pReceive->theNCB);
// Ignore session-closed immediate errors
if (status == NRC_SCLOSED)
status = 0;
if (status == NRC_BRIDGE) {
// lana disappeared
DeleteReceive(pAdd, Number, pReceive);
status = 0;
}
ASSERT(! status);
return(MapStatusCode(NetBiosErrors, status, RPC_S_INTERNAL_ERROR));
}
INTERNAL_FUNCTION PRECEIVE
DeleteReceive(
IN PTADDRESS pAdd,
IN int Number,
IN PRECEIVE pReceive
)
/*++
Routine Description:
Deletes a receive-any NCB and the associated data buffer.
This function assumes that, once ReceiveAny() has been called for
this receive's address, it won't be expected to cancel any pending
NCBs. It should only need to cancel active NCBs when it is called
by RemoveAdapter().
Arguments:
pAdd - the address on which to remove the receive.
Number - which adapter card to operate on.
pReceive - the receive buffer to be deleted.
Returns:
A pointer to the Receive that precedes this one in the adapter's
linked list.
--*/
{
NCB theNCB;
PADAPTER Card = &pAdd->Adapters[Number];
PRECEIVE PrevReceive;
ASSERT(pReceive);
// Cancel the receive NCB if it's pending
if (pReceive->theNCB.ncb_cmd_cplt == NRC_PENDING)
{
memset(&theNCB, 0, sizeof(NCB));
theNCB.ncb_lana_num = Card->lana_num;
theNCB.ncb_buffer = (PUCHAR) &pReceive->theNCB;
execNCB(NCBCANCEL, &theNCB);
if (theNCB.ncb_retcode != NRC_CANOCCR)
{
MapStatusCode(NetBiosErrors,
theNCB.ncb_retcode,
RPC_P_SERVER_TRANSPORT_ERROR);
}
}
CRITICAL_ENTER();
// Remove the NCB from the adapter's linked list of NCBs.
PrevReceive = (PRECEIVE) RemoveLinkList((PLIST_ITEM) &Card->FirstReceive,
pReceive);
ASSERT(PrevReceive);
// Decrement the adapter receive buffer count.
Card->ReceiveCount--;
ASSERT(Card->ReceiveCount >= 0);
// Remove the event and the receive pointer from the address's list.
// We do this by taking this last elements of the receive pointer
// and event arrays and moving them into the positions of the element
// we're deleting.
ASSERT(pAdd->ReceivePtrs[pReceive->AddrIndex] == pReceive);
ASSERT(pAdd->Events[pAdd->AdapterCount + pReceive->AddrIndex]
== pReceive->theNCB.ncb_event);
if (pReceive->AddrIndex != pAdd->ReceivePtrCount - 1)
{
pAdd->ReceivePtrs[pReceive->AddrIndex] =
pAdd->ReceivePtrs[pAdd->ReceivePtrCount - 1];
pAdd->Events[pAdd->AdapterCount + pReceive->AddrIndex] =
pAdd->Events[EVENT_COUNT(pAdd) - 1];
// We need to modify the moved element's address index
pAdd->ReceivePtrs[pReceive->AddrIndex]->AddrIndex =
pReceive->AddrIndex;
}
pAdd->ReceivePtrCount--;
CRITICAL_LEAVE();
// Free the receive's event
CloseHandle(pReceive->theNCB.ncb_event);
// Free the buffer
I_RpcFree(pReceive);
return PrevReceive;
}
INTERNAL_FUNCTION RPC_STATUS
AddAdapter(
IN PADAPTER Card,
IN UCHAR *Name,
IN UCHAR LanaNum
)
/*++
Routine Description:
Add the NetBIOS name and allocate buffers for a given adapter.
Arguments:
Card - Adapter to add. We assume it is passed to this function with
all fields zeroed.
Name - NetBIOS name to be added.
LanaNum - Lana number for this adapter.
--*/
{
RPC_STATUS status = 0;
NCB theNCB;
// Initialize the ADDNAME NCB
memset(&theNCB, 0, sizeof(NCB));
theNCB.ncb_lana_num = LanaNum;
memcpy(theNCB.ncb_name, Name, sizeof(theNCB.ncb_name));
// Submit the ADDNAME NCB
if ((status = (RPC_STATUS) execNCB(NCBADDNAME, &theNCB))
|| (status = theNCB.ncb_retcode))
{
return(MapStatusCode(NetBiosErrors,
status, RPC_S_CANT_CREATE_ENDPOINT));
}
// Fill in the appropriate fields of the adapter structure
Card->lana_num = LanaNum;
Card->iName = theNCB.ncb_num;
return status;
}
INTERNAL_FUNCTION void
RemoveAdapter(
IN PTADDRESS pAdd,
IN int Number
)
/*++
Routine Description:
Remove the NetBIOS name and free buffers for a given adapter.
This function assumes that ReceiveAny() has not been called for this
adapter's address. Otherwise, ReceiveAny() would have to deal with
the effects of the cancelled NCBs, cancelled sessions, and deleted
names.
Arguments:
pAdd - the address on which to remove the adapter.
Number - which adapter card to operate on.
--*/
{
PADAPTER Card = &pAdd->Adapters[Number];
NCB theNCB;
// Cancel the listen if it has been submitted
if (Card->ListenNCB.ncb_cmd_cplt == NRC_PENDING)
{
memset(&theNCB, 0, sizeof(NCB));
theNCB.ncb_lana_num = Card->lana_num;
theNCB.ncb_buffer = (PUCHAR) &Card->ListenNCB;
execNCB(NCBCANCEL, &theNCB);
if (theNCB.ncb_retcode != NRC_CANOCCR)
{
MapStatusCode(NetBiosErrors,
theNCB.ncb_retcode,
RPC_P_SERVER_TRANSPORT_ERROR);
}
}
// Hang up the session if the listen completed
if (Card->ListenNCB.ncb_retcode == NRC_GOODRET
&& Card->ListenNCB.ncb_lsn)
{
memset(&theNCB, 0, sizeof(NCB));
theNCB.ncb_lana_num = Card->lana_num;
theNCB.ncb_lsn = Card->ListenNCB.ncb_lsn;
execNCB(NCBHANGUP, &theNCB);
if (theNCB.ncb_retcode != NRC_SCLOSED)
{
MapStatusCode(NetBiosErrors,
theNCB.ncb_retcode,
RPC_P_SERVER_TRANSPORT_ERROR);
}
}
// Delete the per-receive data
while (Card->FirstReceive)
DeleteReceive(pAdd, Number, Card->FirstReceive);
// Delete the NetBIOS name on this lana, if it has been added
if (Card->iName)
{
memset(&theNCB, 0, sizeof(NCB));
theNCB.ncb_lana_num = Card->lana_num;
memcpy(theNCB.ncb_name,
Card->ListenNCB.ncb_name,
sizeof(theNCB.ncb_name));
execNCB(NCBDELNAME, &theNCB);
}
else
{
theNCB.ncb_retcode = 0;
}
MapStatusCode(NetBiosErrors,
theNCB.ncb_retcode,
RPC_P_SERVER_TRANSPORT_ERROR);
}
RPC_SERVER_TRANSPORT_INFO * RPC_ENTRY
TransportLoad (
IN RPC_CHAR * RpcProtocolSequence
)
/*++
Routine Description:
Loadable transport initialization function.
Arguments:
RpcProtocolSequence - the protocol string that mapped to this library.
Returns:
A pointer to a RPC_SERVER_TRANSPORT_INFO describing this transport.
--*/
{
InitNBMutex();
return(SetupNetBios(RpcProtocolSequence)? &TransInfo: 0);
}
INTERNAL_FUNCTION RPC_STATUS
SubmitListen (
IN PTADDRESS Address,
IN int Number
)
/*++
Routine Description:
This function sets up NetBios to listen for new connections
on the address name that we have established.
Arguments:
pAdd - the address on which to submit the async listen.
Number - which adapter card to operate on.
Returns:
RPC_S_OK, RPC_S_CANT_CREATE_ENDPOINT, Status code mapping.
--*/
{
RPC_STATUS status;
PADAPTER Card = &Address->Adapters[Number];
// Note: The ncb_name and ncb_event fields of the ListenNCB are filled
// in by CreateEndpoint() when the adapter structure is initialized.
Card->ListenNCB.ncb_callname[0] = '*';
Card->ListenNCB.ncb_callname[1] = 0;
Card->ListenNCB.ncb_rto = 0;
Card->ListenNCB.ncb_sto = 0;
Card->ListenNCB.ncb_lana_num = Card->lana_num;
status = execNCB(NCBLISTEN | ASYNCH, &Card->ListenNCB);
return(MapStatusCode(NetBiosErrors, status, RPC_S_CANT_CREATE_ENDPOINT));
}
void RPC_ENTRY
AbortSetupAddress (
IN PTADDRESS pAdd
)
/*++
Routine Description:
This routine is called if an error occurs in setting up the
address between the time that SetupWithEndpoint or SetupUnknownEndpoint
successfully completed and before the next call into this loadable
transport module.
It is also called by CreateEndpoint() if this function encounters an
error while initializing the address.
Arguments:
pAdd - Supplies the address which is being aborted.
--*/
{
unsigned int Index;
// Perform adapter cleanup
if (pAdd->Adapters)
{
// Perform per-adapter cleanup
for (Index = 0; Index < pAdd->AdapterCount; Index++)
RemoveAdapter(pAdd, Index);
// Free memory for the adapter array
I_RpcFree(pAdd->Adapters);
}
// Perform receive pointer cleanup
if (pAdd->ReceivePtrs)
{
// We don't need to free the receive buffers themselves here.
// It is done on a per-adapter basis (RemoveAdapter calls
// DeleteReceive).
I_RpcFree(pAdd->ReceivePtrs);
}
// Perform event cleanup - delete the per-adapter listen events,
// stopping when we hit a NULL (the receive events, if any, are
// deleted by DeleteReceive).
ASSERT(pAdd->ReceivePtrCount == 0);
for (Index = 0;
Index < pAdd->AdapterCount && pAdd->Events[Index];
Index++)
{
CloseHandle(pAdd->Events[Index]);
}
}
INTERNAL_FUNCTION RPC_TRANS_STATUS RPC_ENTRY
CreateEndpoint (
IN PTADDRESS pAdd,
IN unsigned int EndpointNumber,
OUT RPC_CHAR __RPC_FAR * lNetworkAddress,
OUT unsigned int PAPI * NumNetworkAddress,
IN unsigned int NetworkAddressLength,
IN void __RPC_FAR * SecurityDescriptor, OPTIONAL
IN unsigned int PendingQueueSize,
IN RPC_CHAR __RPC_FAR * RpcProtocolSequence
)
/*++
Routine Description:
This routine is used to setup a NetBios address with the specified
endpoint. We also return the network address of this server. It
is designed to be called by both SetupWithEndpoint() and
SetupUnknownEndpoint().
Arguments:
pAdd - Supplies this loadable transport interface address.
EndpointNumber - Supplies the endpoint number to create. It is used
as the last byte of the NetBIOS name.
NetworkAddress - Returns the network address for this machine. This
buffer will have been allocated by the caller.
NetworkAddressLength - Supplies the length of the network address
argument.
SecurityDescriptor - Unused and not allowed.
PendingQueueSize - Unused.
RpcProtocolSequence - this string is used to map to a NetBios apdater
number. Format of the string is "ncacn_nb_<protocol>. This
<protocol> (<> are not in the string) is used to do the mapping.
Return Value:
RPC_S_OK, RPC_P_NETWORK_ADDRESS_TOO_SMALL, RPC_S_INVALID_ARG,
RPC_S_OUT_OF_RESOURCES, RPC_S_CANT_CREATE_ENDPOINT,
RPC_S_DUPLICATE_ENDPOINT, RPC_S_INVALID_ENDPOINT_FORMAT,
SubmitListen(), Status error code mapping.
--*/
{
RPC_TRANS_STATUS Status;
PPROTOCOL_MAP ProtocolEntry;
PADAPTER Card;
unsigned int Index;
char LanaNumbers[MAX_ADAPTER_PER_CARD];
UINT MaxReceives;
UCHAR ncb_name[NCBNAMSZ];
UCHAR ncb_status;
char * PAPI * tmpPtr;
// Perform parameter validation.
UNUSED(PendingQueueSize); PUNUSED(RpcProtocolSequence);
if (SecurityDescriptor)
return(RPC_S_INVALID_ARG);
if (EndpointNumber > MAX_ENDPOINT_NUMBER)
return(RPC_S_INVALID_ENDPOINT_FORMAT);
if (sizeof(MachineName) + sizeof(RPC_CHAR *) > NetworkAddressLength)
return(RPC_P_NETWORK_ADDRESS_TOO_SMALL);
// Initialize all of the fields of the structure to 0. This lets us
// know later what fields have been initialized, in the event that we
// need to abort initialization.
memset(pAdd, 0, sizeof(TADDRESS));
tmpPtr = (char * PAPI *) lNetworkAddress;
tmpPtr[0] = (char *) lNetworkAddress + sizeof(RPC_CHAR * ) ;
*NumNetworkAddress = 1;
// Copy the network address from the machine name
COPY_TO_NATIVE_STRING(tmpPtr[0],
MachineName,
MachineNameLengthUnpadded);
lNetworkAddress[sizeof(RPC_CHAR *) + MachineNameLengthUnpadded] = 0;
Status = 0;
// Extract the lana_num from the protocol transport field of the protocol.
CRITICAL_ENTER();
// For all the cards that support this protocol, initialize.
for (;pAdd->AdapterCount < MAX_ADAPTER_PER_CARD; pAdd->AdapterCount++)
{
if (Status = MapProtocol(RpcProtocolSequence, pAdd->AdapterCount,
&ProtocolEntry))
break;
LanaNumbers[pAdd->AdapterCount] = ProtocolEntry->Lana;
// Call the client DLL to allocate resources for the adapter #.
if (ncb_status = AdapterReset(ProtocolEntry))
{
CRITICAL_LEAVE();
return(MapStatusCode(NetBiosErrors,
ncb_status, RPC_S_PROTSEQ_NOT_SUPPORTED));
}
}
CRITICAL_LEAVE();
if (pAdd->AdapterCount == 0)
return(Status);
Status = 0;
// Verify that we don't have too many adapters. We need to be able to
// wait on at least two events per adapter.
if (pAdd->AdapterCount * 2 > MAXIMUM_WAIT_OBJECTS)
{
return RPC_S_OUT_OF_RESOURCES;
}
// Now that we know how many adapters, allocate memory for them.
//
// Note: Since we zero all of the adapter structures, we don't need
// to worry about initializing fields that default to 0.
if (!(pAdd->Adapters = I_RpcAllocate(pAdd->AdapterCount * sizeof(ADAPTER))))
{
Status = RPC_S_OUT_OF_MEMORY;
goto AddrInitFailed;
}
memset(pAdd->Adapters, 0, pAdd->AdapterCount * sizeof(ADAPTER));
// Allocate and initialize memory for the receive pointers. The
// maximum number of receives we can have per address is the maximum
// number of objects supported by WaitForMultipleObjects() less the
// number of adapters. This is because we have one event per receive
// and one per listen, and we use one listen per adapter.
MaxReceives = MAXIMUM_WAIT_OBJECTS - pAdd->AdapterCount;
if (!(pAdd->ReceivePtrs = I_RpcAllocate(MaxReceives * sizeof(PRECEIVE))))
{
Status = RPC_S_OUT_OF_MEMORY;
goto AddrInitFailed;
}
memset(pAdd->ReceivePtrs, 0, MaxReceives * sizeof(PRECEIVE));
// Create the events for the listen NCBs
for (Index = 0; Index < pAdd->AdapterCount; Index++)
{
if (! (pAdd->Events[Index] = CreateEvent(NULL, TRUE, TRUE, NULL)))
{
Status = RPC_S_OUT_OF_RESOURCES;
goto AddrInitFailed;
}
}
// Add the NCB names to the adapters and submit the listen and
// receive-any NCBs.
memcpy(ncb_name, MachineName, sizeof(MachineName));
ncb_name[NAME_LAST_BYTE] = (unsigned char) EndpointNumber;
for (Index = 0, Card = &pAdd->Adapters[0];
Index < pAdd->AdapterCount;
Index++, Card++)
{
// Initialize the name and event fields for the Listen NCB
memcpy(Card->ListenNCB.ncb_name,
ncb_name,
sizeof(Card->ListenNCB.ncb_name));
Card->ListenNCB.ncb_event = pAdd->Events[Index];
if (Status = AddAdapter(Card, ncb_name, LanaNumbers[Index]))
{
goto AddrInitFailed;
}
// Finally, setup to receive new clients and calls.
Status = SubmitListen(pAdd, Index);
if (Status)
goto AddrInitFailed;
Status = SubmitReceive(pAdd, Index, 0);
if (Status)
goto AddrInitFailed;
Status = 0;
}
AddrInitFailed:
if (Status)
AbortSetupAddress(pAdd);
return(Status);
}
RPC_TRANS_STATUS RPC_ENTRY
SetupWithEndpoint (
IN PTADDRESS pAdd,
IN RPC_CHAR __RPC_FAR * Endpoint,
OUT RPC_CHAR __RPC_FAR * lNetworkAddress,
OUT unsigned int PAPI * NumNetworkAddress,
IN unsigned int NetworkAddressLength,
IN void __RPC_FAR * SecurityDescriptor, OPTIONAL
IN unsigned int PendingQueueSize,
IN RPC_CHAR __RPC_FAR * RpcProtocolSequence
)
/*++
Routine Description:
This routine is used to setup a NetBios address with the specified
endpoint. It is a front end to CreateEndpoint(), which does most of
the real work.
Arguments:
pAdd - Supplies this loadable transport interface address.
Endpoint - Supplies the endpoint netbios name to create.
NetworkAddress | Passed through to CreateEndpoint
NetworkAddressLength |
SecurityDescriptor |
PendingQueueSize |
RpcProtocolSequence |
Return Value:
RPC_S_INVALID_ENDPOINT_FORMAT, CreateEndpoint().
--*/
{
unsigned int EndpointNumber;
// Convert the endpoint string to a single byte.
for (EndpointNumber = 0;
*Endpoint >= RPC_CONST_CHAR('0') && *Endpoint <= RPC_CONST_CHAR('9') &&
EndpointNumber <= MAX_ENDPOINT_NUMBER;
Endpoint++)
EndpointNumber = EndpointNumber*10 + *Endpoint - RPC_CONST_CHAR('0');
if (*Endpoint != 0)
return(RPC_S_INVALID_ENDPOINT_FORMAT);
// BUGBUG - Should we validate that the endpoint number is in the
// INITIAL_ENDPOINT_NUMBER - UCHAR_MAX range?
return CreateEndpoint(pAdd, EndpointNumber, lNetworkAddress, NumNetworkAddress,
NetworkAddressLength, SecurityDescriptor,
PendingQueueSize, RpcProtocolSequence);
}
RPC_TRANS_STATUS RPC_ENTRY
SetupUnknownEndpoint (
IN PTADDRESS pAdd,
OUT RPC_CHAR __RPC_FAR * Endpoint,
IN unsigned int EndpointLength,
OUT RPC_CHAR __RPC_FAR * lNetworkAddress,
OUT unsigned int PAPI * NumNetworkAddress,
IN unsigned int NetworkAddressLength,
IN void __RPC_FAR * SecurityDescriptor, OPTIONAL
IN unsigned int PendingQueueSize,
IN RPC_CHAR __RPC_FAR * RpcProtocolSequence
)
/*++
Routine Description:
This routine is used to generate an endpoint and setup a NetBios
address with that endpoint. It is a front end to CreateEndpoint(),
which does most of the real work.
Arguments:
pAdd - Supplies this loadable transport interface address.
Endpoint - Returns the endpoint generated for this address. This
buffer will have been allocated by the caller.
EndpointLength - Supplies the length of the endpoint argument.
NetworkAddress | Passed through to CreateEndpoint
NetworkAddressLength |
SecurityDescriptor |
PendingQueueSize |
RpcProtocolSequence |
Return Value:
RPC_P_ENDPOINT_TOO_SMALL, RPC_S_OUT_OF_RESOURCES, CreateEndpoint()
--*/
{
// This counter keeps track of the last endpoint we tried to create.
static unsigned char EndpointCounter = INITIAL_ENDPOINT_NUMBER - 1;
RPC_STATUS status;
char Number[4]; // 4 = 1 + log10(MAX_ENDPOINT_NUMBER)
unsigned char LastEndpoint = EndpointCounter;
if (EndpointLength < sizeof(Number) / sizeof(*Number))
return(RPC_P_ENDPOINT_TOO_SMALL);
// To generate a new endpoint, we call the SetupWithEndpoint function
// repeatedly until successful or we have tried all 255 values.
do
{
EndpointCounter++;
status = CreateEndpoint(pAdd, EndpointCounter,
lNetworkAddress, NumNetworkAddress, NetworkAddressLength,
SecurityDescriptor, PendingQueueSize, RpcProtocolSequence);
}
while (status == RPC_S_DUPLICATE_ENDPOINT
&& EndpointCounter != LastEndpoint);
// Return an error if we've tried all of the values
if (status == RPC_S_DUPLICATE_ENDPOINT)
return(RPC_S_OUT_OF_RESOURCES);
// BUGBUG - Should we decrement the counter if we got an error other
// than trying to add the name (e.g. RPC_P_NETWORK_ADDRESS_TOO_SMALL)?
// On success, convert the endpoint number to an RPC_CHAR string.
if (status == RPC_S_OK)
{
RpcItoa(EndpointCounter, Number, 10);
COPY_TO_NATIVE_STRING(Endpoint, Number, EndpointLength);
}
return(status);
}
RPC_TRANS_STATUS RPC_ENTRY
Close (
PCONNECTION pConn
)
/*++
Routine Description:
Close a client connection, by hanging up the NetBios session
with the client.
Arguments:
pConn - Client connection to close.
Returns:
RPC_S_OK
--*/
{
NCB theNCB;
// We use the connection's lsn field to determine if the connection
// has already been closed. This protects us against race conditions
// where the client is closing the connection at the same time.
CRITICAL_ENTER();
if (pConn->lsn)
{
memset(&theNCB, 0, sizeof(NCB));
theNCB.ncb_lsn = pConn->lsn;
theNCB.ncb_lana_num = pConn->Adapter->lana_num;
execNCB(NCBHANGUP, &theNCB);
// Decrement the adapter's connection count.
pConn->Adapter->ConnectionCount--;
ASSERT(pConn->Adapter->ConnectionCount >= 0);
// Reset the connection's lsn field.
pConn->lsn = 0;
}
else
{
// The session is already closed, so fake a successful return
theNCB.ncb_retcode = 0;
}
CRITICAL_LEAVE();
if (theNCB.ncb_retcode == NRC_SCLOSED ||
theNCB.ncb_retcode == NRC_SNUMOUT)
return(RPC_S_OK);
return MapStatusCode(NetBiosErrors, theNCB.ncb_retcode,
RPC_P_SERVER_TRANSPORT_ERROR);
}
RPC_TRANS_STATUS RPC_ENTRY
Send (
PCONNECTION pConn,
void __RPC_FAR * Buffer,
unsigned int BufferLength
)
/*++
Routine Description:
Write a message to a client session.
Arguments:
pConn - Client connection to write the message on.
Buffer - address of buffer to write.
BufferLength - size of buffer to write.
Returns:
MapStatusCode()
--*/
{
NCB theNCB;
RPC_TRANS_STATUS RpcTransStatus;
ASSERT(BufferLength <= NETB_MAXIMUM_DATA);
// Now that we're performing the send, we can reset the sequence number
// on client receives.
pConn->ClientSeqNum = 0;
// Submit the NCB.
theNCB.ncb_lana_num = pConn->Adapter->lana_num;
theNCB.ncb_lsn = pConn->lsn;
theNCB.ncb_buffer = Buffer;
theNCB.ncb_length = (unsigned short) BufferLength;
execNCB(NCBSEND, &theNCB);
RpcTransStatus = MapStatusCode(NetBiosErrors, theNCB.ncb_retcode,
RPC_P_SEND_FAILED);
if ( RpcTransStatus == RPC_P_SEND_FAILED )
{
RpcTransStatus = Close(pConn);
ASSERT( RpcTransStatus == RPC_S_OK );
return(RPC_P_SEND_FAILED);
}
return(RpcTransStatus);
}
RPC_TRANS_STATUS RPC_ENTRY
ReceiveAny (
IN PTADDRESS pAdd,
OUT PCONNECTION *pConnCur,
IN OUT void __RPC_FAR * __RPC_FAR * Buffer,
IN OUT unsigned int __RPC_FAR * BufferLength,
IN long Timeout
)
/*++
Routine Description:
This routine manages connections on the specified address, performing
three main tasks:
1) Accepting new connections from clients.
2) Shutting down connections that the client has closed.
3) Returning data sent by clients over existing connections.
It returns to the caller when either 2 or 3 has occurred.
Arguments:
pAdd - The address to act on.
pConnCur - Pointer to the connection which is set to the
connection of the call.
Buffer - pointer to a buffer pointer to return the message in.
BufferLength - pointer to where the size of the buffer is returned.
Timeout - how long to wait for a message.
Returns:
RPC_S_OK, RPC_P_SERVER_TRANSPORT_ERROR, RPC_P_CONNECTION_CLOSED,
RPC_S_OUT_OF_MEMORY, RPC_P_TIMEOUT
pConnCur is set if there is new data (RPC_S_OK) or a client death
(RPC_P_CONNECTION_CLOSED).
--*/
{
RPC_TRANS_STATUS status = 0;
BOOL ready_to_return = FALSE;
DWORD wait_time = Timeout == -1 ? INFINITE : Timeout;
DWORD event_index;
PADAPTER Card;
PCONNECTION pConn;
PNCB theNCB;
unsigned int ReceiveDirectFlag;
do
{
// Wait for any of the events associated with async listen or
// receive-any NCBs to be signalled.
event_index = WaitForMultipleObjects(EVENT_COUNT(pAdd),
pAdd->Events,
FALSE,
wait_time);
ASSERT(event_index != 0xFFFFFFFF);
// Return immediately if we timed out or if we hit an error
if (event_index == WAIT_TIMEOUT)
return RPC_P_TIMEOUT;
if (event_index == 0xFFFFFFFF)
return RPC_P_SERVER_TRANSPORT_ERROR;
// Otherwise, verify that the index is valid.
event_index -= WAIT_OBJECT_0;
ASSERT(event_index >= 0 && event_index < EVENT_COUNT(pAdd));
if (event_index >= EVENT_COUNT(pAdd))
return RPC_P_SERVER_TRANSPORT_ERROR;
// The event index is valid, so we now process the NCB associated
// with the event. Events 0 to AdapterCount-1 are for the Listen
// NCBs on the respective adapters, events AdapterCount and up are
// for the Receive-Any NCBs that correspond with the pointers in
// the address's receive pointer array.
if (event_index < pAdd->AdapterCount)
{
// A Listen NCB completed.
Card = &pAdd->Adapters[event_index];
theNCB = &Card->ListenNCB;
if (theNCB->ncb_cmd_cplt == NRC_NAMERR) {
// BUGBUG: Ultimately, we will want to delete all RPC resources associated
// with this lana, and listen for new lanas to come online, but Chicago
// support for that isn't available yet.
ResetEvent(pAdd->Events[event_index]);
ready_to_return = FALSE;
continue;
}
ASSERT(theNCB->ncb_cmd_cplt != NRC_PENDING);
if (theNCB->ncb_retcode != NRC_GOODRET)
{
// BUGBUG - We should think about not resubmitting if
// this occurs.
#if 0
ASSERT(!"Bad ErrorCode on Listen?");
status = MapStatusCode(NetBiosErrors,
theNCB->ncb_retcode,
RPC_P_SERVER_TRANSPORT_ERROR);
#endif
goto ResubmitListenNCB;
}
// Create a new connection object for this session.
pConn = I_RpcTransServerNewConnection(
pAdd,
MAKE_CONNECTION_KEY(Card->lana_num,
theNCB->ncb_lsn),
&ReceiveDirectFlag);
ASSERT( ReceiveDirectFlag == 0 );
if (!pConn)
{
ASSERT(!"RT assigned no connections");
status = RPC_S_OUT_OF_MEMORY;
ready_to_return = TRUE;
goto ResubmitListenNCB;
}
pConn->Adapter = Card;
pConn->lsn = theNCB->ncb_lsn;
pConn->ClientSeqNum = 0;
// Increment the connection count for this adapter
CRITICAL_ENTER();
Card->ConnectionCount++;
// We may want to submit an additional Receive, depending
// upon the number of connections.
if (TOO_FEW_RCVS(Card->ConnectionCount, Card->ReceiveCount))
{
SubmitReceive(pAdd, event_index, 0);
}
CRITICAL_LEAVE();
// Post another Async Listen NCB for the next new client.
ResubmitListenNCB:
// Don't bother saving the error code if we already
// have one.
if (status)
{
SubmitListen(pAdd, event_index);
}
else
{
status = SubmitListen(pAdd, event_index);
if (status)
{
ready_to_return = TRUE;
}
}
} // End of processing completed Listen NCB
else
{
// A Receive-Any NCB completed.
PRECEIVE pReceive =
pAdd->ReceivePtrs[event_index - pAdd->AdapterCount];
Card = NULL;
theNCB = &pReceive->theNCB;
ASSERT(theNCB->ncb_cmd_cplt != NRC_PENDING);
// First, we try to find the matching connection
pConn = *pConnCur = (PCONNECTION)
I_RpcTransServerFindConnection(
pAdd,
MAKE_CONNECTION_KEY(theNCB->ncb_lana_num,
theNCB->ncb_lsn));
// If we don't find the connection, we assume that the
// RPC run-time has already closed it explicitly, and
// so ignore the contents of the NCB. If we do find it,
// then we process the NCB according to its return code.
if (! pConn)
{
UINT index;
/* ASSERT(theNCB->ncb_retcode == NRC_SCLOSED); */
#ifdef DEBUGRPC
PrintToDebugger("No conn. for this lsn -> %x \n" ,
theNCB->ncb_retcode);
PrintToDebugger("command of this NCB-> %x \n" ,
theNCB->ncb_command);
#endif
// In order to resubmit this NCB, we need to associate
// it with an adapter. Normally we do this by locating
// the connection (which contains the associated adapter),
// but if we get here we didn't find the connection.
// So walk the address's adapter list until we find one
// with a lana number that matches the one in the NCB.
for (index = 0, Card = &pAdd->Adapters[0];
index < pAdd->AdapterCount;
index++, Card++)
{
if (Card->lana_num == theNCB->ncb_lana_num)
break;
}
// This should never fail!
ASSERT(index < pAdd->AdapterCount);
ASSERT(Card->iName == theNCB->ncb_num);
if (theNCB->ncb_cmd_cplt == NRC_NAMERR ||
theNCB->ncb_cmd_cplt == NRC_BRIDGE) {
DeleteReceive(pAdd, Card - pAdd->Adapters, pReceive);
ready_to_return = FALSE;
continue;
}
goto ResubmitReceiveNCB;
}
// We've found the connection, so we have the adapter.
Card = pConn->Adapter;
ASSERT(Card->lana_num == theNCB->ncb_lana_num);
// Process the NCB, based on its return code.
if (theNCB->ncb_retcode == NRC_SCLOSED ||
theNCB->ncb_retcode == NRC_NAMERR ||
theNCB->ncb_retcode == NRC_BRIDGE)
{
// Process a session with a client closing.
// Decrement the connection count, if it hasn't been
// done already by Close(). We set the lsn field of
// the connection to 0 to indicate that the connection
// is gone (from the transport's perspective}.
CRITICAL_ENTER();
if (pConn->lsn)
{
Card->ConnectionCount--;
ASSERT(Card->ConnectionCount >= 0);
pConn->lsn = 0;
}
CRITICAL_LEAVE();
status = RPC_P_CONNECTION_CLOSED;
ready_to_return = TRUE;
}
else if (theNCB->ncb_retcode == NRC_GOODRET)
{
// Process the returned data
// Verify that the sequence numbers match.
//
// If they do, then we return the data to the caller
// and increment the sequence number. If they don't,
// then we need to locate the completed Receive with
// the matching sequence number.
if (pConn->ClientSeqNum != pReceive->buffer.seq_num)
{
PRECEIVE pOrigReceive = pReceive;
for (pReceive = Card->FirstReceive;
pReceive != NULL;
pReceive = pReceive->NextReceive)
{
// We need to find a Receive-Any NCB that has
// completed successfully, has a matching lsn and
// sequence number. (We also need a matching
// lana number, but we don't have to check that
// because we're only looking at Receive-Any NCBs
// that are submitted on this lana.) If we don't
// find one, it's a fatal error, because an NCB
// should not have completed for sequence number
// N+1 unless one also completed for sequence
// number N.
theNCB = &pReceive->theNCB;
if (theNCB->ncb_cmd_cplt != NRC_PENDING
&& theNCB->ncb_retcode == NRC_GOODRET
&& theNCB->ncb_lsn == pConn->lsn
&& pConn->ClientSeqNum
== pReceive->buffer.seq_num)
{
ASSERT(Card->lana_num == theNCB->ncb_lana_num);
break;
}
}
ASSERT(pReceive);
// If we can't find the matching packet, we just
// resubmit the original NCB.
if (pReceive == NULL)
{
pReceive = pOrigReceive;
status = RPC_P_SERVER_TRANSPORT_ERROR;
ready_to_return = TRUE;
goto ResubmitReceiveNCB;
}
}
// If we get here and pReceive is non-NULL, then we
// have the correct data.
if (pReceive)
{
// Allocate the buffer for the data
*BufferLength = theNCB->ncb_length - NETB_OVERHEAD;
status = I_RpcTransServerReallocBuffer(pConn,
Buffer, 0, *BufferLength);
if (! status)
{
// Copy the data into the buffer
memcpy(*Buffer,
pReceive->buffer.data,
*BufferLength);
}
else
{
status = RPC_S_OUT_OF_MEMORY;
}
ready_to_return = TRUE;
pConn->ClientSeqNum++;
}
}
else
{
// The Receive completed with a failure (other than
// session closed).
// BUGBUG - We should think about not resubmitting if
// this occurs.
status = MapStatusCode(NetBiosErrors,
theNCB->ncb_retcode,
RPC_P_SERVER_TRANSPORT_ERROR);
ready_to_return = TRUE;
}
ResubmitReceiveNCB:
ASSERT(Card);
// Resubmit or delete the receive NCB we just handled,
// depending upon the connection count.
if (pReceive)
{
CRITICAL_ENTER();
if (TOO_MANY_RCVS(Card->ConnectionCount, Card->ReceiveCount))
{
DeleteReceive(pAdd, Card - pAdd->Adapters, pReceive);
}
else
{
SubmitReceive(pAdd, Card - pAdd->Adapters, pReceive);
}
CRITICAL_LEAVE();
}
} // End of processing completed Receive-Any NCB
// When we get here, we've finished doing processing based on the
// event that completed. If an event completed that causes us to
// return information to the caller, then we're done. Otherwise,
// We go back to the top of the main loop and wait for the next
// event to complete
} // end of do-while loop body
while (! ready_to_return);
return status;
}
// This structure is returned to the RPC runtime when the DLL is loaded.
RPC_SERVER_TRANSPORT_INFO TransInfo = {
RPC_TRANSPORT_INTERFACE_VERSION, // version # of loadable trans interface
NETB_MAXIMUM_DATA, // maximum # bytes for send or receive
sizeof(TADDRESS), // # of bytes to allocate for address (server)
sizeof(CONNECTION), // # of bytes to allocate for connections
(TRANS_SERVER_SETUPWITHENDPOINT) SetupWithEndpoint,
(TRANS_SERVER_SETUPUNKNOWNENDPOINT) SetupUnknownEndpoint,
(TRANS_SERVER_ABORTSETUPADDRESS) AbortSetupAddress,
(TRANS_SERVER_CLOSE) Close,
(TRANS_SERVER_SEND) Send,
(TRANS_SERVER_RECEIVEANY) ReceiveAny,
0, 0, 0, 0, 0
};