windows-nt/Source/XPSP1/NT/ds/nw/vwipxspx/dll/util.c
2020-09-26 16:20:57 +08:00

2963 lines
64 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
util.c
Abstract:
ntVdm netWare (Vw) IPX/SPX Functions
Vw: The peoples' network
Contains various utility routines
Contents:
GetInternetAddress
GetMaxPacketSize
RetrieveEcb
RetrieveXEcb
(AllocateXecb)
(DeallocateXecb)
ScheduleEvent
ScanTimerList
CancelTimerEvent
CancelTimedEvents
CancelAsyncEvent
CancelSocketEvent
CancelConnectionEvent
QueueEcb
DequeueEcb
CancelSocketQueue
CancelConnectionQueue
AbortQueue
AbortConnectionEvent
StartIpxSend
GetIoBuffer
(ReleaseIoBuffer)
GatherData
ScatterData
IpxReceiveFirst
IpxReceiveNext
(IpxSendFirst)
IpxSendNext
(QueueReceiveRequest)
(DequeueReceiveRequest)
(QueueSendRequest)
(DequeueSendRequest)
CompleteOrQueueIo
CompleteIo
CompleteOrQueueEcb
CompleteEcb
(QueueAsyncCompletion)
EsrCallback
VWinEsrCallback
FifoAddHead
FifoAdd
FifoRemove
FifoNext
Author:
Richard L Firth (rfirth) 30-Sep-1993
Environment:
User-mode Win32
Revision History:
30-Sep-1993 rfirth
Created
--*/
#include "vw.h"
#pragma hdrstop
//
// private routine prototypes
//
PRIVATE
LPXECB
AllocateXecb(
VOID
);
PRIVATE
VOID
DeallocateXecb(
IN LPXECB pXecb
);
PRIVATE
VOID
ReleaseIoBuffer(
IN LPXECB pXecb
);
PRIVATE
VOID
IpxSendFirst(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
);
PRIVATE
VOID
QueueReceiveRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
);
PRIVATE
LPXECB
DequeueReceiveRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
);
PRIVATE
VOID
QueueSendRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
);
PRIVATE
LPXECB
DequeueSendRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
);
PRIVATE
VOID
QueueAsyncCompletion(
IN LPXECB pXecb,
IN BYTE CompletionCode
);
//
// private data
//
//
// TimerList - singly-linked list of timed events, in order of duration
//
PRIVATE LPXECB TimerList = NULL;
//
// AsyncCompletionQueue - keeps list of completed ECBs awaiting removal via
// ESR callback
//
PRIVATE FIFO AsyncCompletionQueue = {NULL, NULL};
//
// sort-of-private data (matches not-really-global data in other modules)
//
//
// SerializationCritSec - grab this when manipulating SOCKET_INFO list
//
CRITICAL_SECTION SerializationCritSec;
//
// AsyncCritSec - grab this when manipulating AsyncCompletionQueue
//
CRITICAL_SECTION AsyncCritSec;
//
// functions
//
int
GetInternetAddress(
IN OUT LPSOCKADDR_IPX InternetAddress
)
/*++
Routine Description:
Gets the node and net numbers for this station
Arguments:
InternetAddress - pointer to SOCKADDR_IPX structure to fill with internetwork
address for this station
Return Value:
int
Success - 0
Failure - SOCKET_ERROR
--*/
{
SOCKET s;
int rc;
int structureLength = sizeof(*InternetAddress);
s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
if (s != INVALID_SOCKET) {
//
// make dynamic binding (socket number = 0)
//
ZeroMemory(InternetAddress, structureLength);
InternetAddress->sa_family = AF_IPX;
rc = bind(s, (LPSOCKADDR)InternetAddress, structureLength);
if (rc != SOCKET_ERROR) {
rc = getsockname(s, (LPSOCKADDR)InternetAddress, &structureLength);
if (rc) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"GetInternetAddress: getsockname() returns %d\n",
WSAGetLastError()
));
}
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"GetInternetAddress: bind() returns %d\n",
WSAGetLastError()
));
}
closesocket(s);
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"GetInternetAddress: socket() returns %d\n",
WSAGetLastError()
));
rc = SOCKET_ERROR;
}
return rc;
}
int
GetMaxPacketSize(
OUT LPWORD MaxPacketSize
)
/*++
Routine Description:
Returns the maximum packet allowed by the underlying transport
Arguments:
MaxPacketSize - pointer to returned maximum packet size
Return Value:
int
Success - 0
Failure - SOCKET_ERROR
--*/
{
SOCKET s;
int maxLen, maxLenSize = sizeof(maxLen);
int rc;
SOCKADDR_IPX ipxAddr;
s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
if (s != SOCKET_ERROR) {
//
// set socket to 0 - causes any applicable address to be bound
//
ZeroMemory(&ipxAddr, sizeof(ipxAddr));
ipxAddr.sa_family = AF_IPX;
rc = bind(s, (LPSOCKADDR)&ipxAddr, sizeof(ipxAddr));
if (rc != SOCKET_ERROR) {
rc = getsockopt(s,
NSPROTO_IPX,
IPX_MAXSIZE,
(char FAR*)&maxLen,
&maxLenSize
);
if (rc != SOCKET_ERROR) {
//
// IPX_MAXSIZE always returns the amount of data that can be
// transmitted in a single frame. 16-bit IPX/SPX requires that
// the IPX header length be included in the data size
//
maxLen += IPX_HEADER_LENGTH;
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"GetMaxPacketSize: getsockopt() returns %d\n",
WSAGetLastError()
));
}
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"GetMaxPacketSize: bind() returns %d\n",
WSAGetLastError()
));
}
closesocket(s);
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"GetMaxPacketSize: socket() returns %d\n",
WSAGetLastError()
));
rc = SOCKET_ERROR;
}
*MaxPacketSize = (rc != SOCKET_ERROR) ? maxLen : MAXIMUM_IPX_PACKET_LENGTH;
return rc;
}
LPXECB
RetrieveEcb(
IN BYTE EcbType
)
/*++
Routine Description:
Returns pointer to 32-bit extended ECB structure which contains flat pointer
to IPX or AES ECB in VDM memory
We allocate the extended ECB for 3 reasons:
1. Avoids 16-bit app scribbling over our control fields
2. Don't have to make unaligned references to all fields (still need some)
3. Don't have enough space in AES ECB to remember all the stuff we need
However, we do update the 16-bit ECB's LinkAddress field. We use this as a
pointer to the 32-bit XECB we allocate in this routine. This just saves us
having to traverse all the lists looking for the address of the 16-bit ECB
(which we could still do as a fall-back)
Arguments:
EcbType - type of ECB - AES, IPX or SPX
Return Value:
LPXECB - 32-bit pointer to extended ECB structure
--*/
{
WORD segment;
WORD offset;
LPECB pEcb;
segment = IPX_GET_ECB_SEGMENT();
offset = IPX_GET_ECB_OFFSET();
pEcb = (LPIPX_ECB)POINTER_FROM_WORDS(segment, offset, sizeof(IPX_ECB));
return RetrieveXEcb(EcbType, pEcb, (ECB_ADDRESS)MAKELONG(offset,segment));
}
LPXECB
RetrieveXEcb(
IN BYTE EcbType,
LPECB pEcb,
ECB_ADDRESS EcbAddress
)
/*++
Routine Description:
worker for RetrieveEcb, callable from windows functions (ex DOS parms)
Arguments:
EcbType - type of ECB - AES, IPX or SPX
pEcb - pointer to the 16-bit ECB
EcbAddress - address (seg:off in DWORD) of 16-bit ECB
Return Value:
LPXECB
--*/
{
LPXECB pXecb;
if (pEcb) {
//
// tommye - MS 30525
// Make sure the pEcb is valid - we'll go ahead
// and do this before we alloc the XEcb.
//
try {
BYTE x;
// Just deref the ptr to make sure it is okay
x = pEcb->InUse;
} except(1) {
//
// bad pointer: bogus ECB
//
return NULL;
}
//
// allocate and fill-in 32-bit extended ECB structure. If can't allocate
// then return NULL
//
pXecb = AllocateXecb();
if (pXecb) {
pXecb->Ecb = pEcb;
pXecb->EcbAddress = EcbAddress;
pXecb->EsrAddress = pEcb->EsrAddress;
//
// set flags - IPX/AES, SPX, protect-mode
//
pXecb->Flags |= (((EcbType == ECB_TYPE_IPX) || (EcbType == ECB_TYPE_SPX))
? XECB_FLAG_IPX
: XECB_FLAG_AES)
| ((EcbType == ECB_TYPE_SPX) ? XECB_FLAG_SPX : 0)
| ((getMSW() & MSW_PE) ? XECB_FLAG_PROTMODE : 0);
//
// this XECB is not yet on a queue
//
pXecb->QueueId = NO_QUEUE;
//
// mark the 16-bit ECB as being used. We use an undefined value to
// make sure it gets set/reset in the right places
//
pEcb->InUse = ECB_IU_TEMPORARY;
//
// use the LinkAddress field in the 16-bit ECB to point to the XECB.
// We use this when cancelling the ECB
//
pEcb->LinkAddress = pXecb;
//
// AES and IPX ECBs have different sizes and different layouts
//
if ((EcbType == ECB_TYPE_IPX) || (EcbType == ECB_TYPE_SPX)) {
pXecb->SocketNumber = pEcb->SocketNumber;
}
}
} else {
pXecb = NULL;
}
return pXecb;
}
PRIVATE
LPXECB
AllocateXecb(
VOID
)
/*++
Routine Description:
Allocate an XECB; zero it; set the reference count to 1
Arguments:
None.
Return Value:
LPXECB
--*/
{
LPXECB pXecb;
pXecb = (LPXECB)LocalAlloc(LPTR, sizeof(*pXecb));
if (pXecb) {
pXecb->RefCount = 1;
}
return pXecb;
}
PRIVATE
VOID
DeallocateXecb(
IN LPXECB pXecb
)
/*++
Routine Description:
decrement the XECB reference count (while holding SerializationCritSec). If
goes to 0 then free the structure (else other thread is also holding pointer
to XECB)
Arguments:
pXecb - XECB to deallocate
Return Value:
None.
--*/
{
RequestMutex();
--pXecb->RefCount;
if (!pXecb->RefCount) {
#if DBG
FillMemory(pXecb, sizeof(*pXecb), 0xFF);
#endif
FREE_OBJECT(pXecb);
}
ReleaseMutex();
}
VOID
ScheduleEvent(
IN LPXECB pXecb,
IN WORD Ticks
)
/*++
Routine Description:
Adds an ECB to the TimerList, ordered by Ticks. The value of Ticks cannot
be zero
Assumes 1. Ticks != 0
2. pXecb->Next is already NULL (as result of LocalAlloc(LPTR,...)
Arguments:
pXecb - pointer to XECB describing IPX or AES ECB to queue
Ticks - number of ticks to elapse before ECB is cooked
Return Value:
None.
--*/
{
ASSERT(Ticks);
ASSERT(pXecb->Next == NULL);
RequestMutex();
if (!TimerList) {
TimerList = pXecb;
} else {
if (TimerList->Ticks > Ticks) {
TimerList->Ticks -= Ticks;
pXecb->Next = TimerList;
TimerList = pXecb;
} else {
LPXECB previous = (LPXECB)TimerList;
LPXECB this = previous->Next;
Ticks -= TimerList->Ticks;
while (this && Ticks > this->Ticks) {
Ticks -= this->Ticks;
previous = this;
this = this->Next;
}
previous->Next = pXecb;
pXecb->Next = this;
}
}
pXecb->Ticks = Ticks;
pXecb->QueueId = TIMER_QUEUE;
ReleaseMutex();
}
VOID
ScanTimerList(
VOID
)
/*++
Routine Description:
Called once per tick. Decrements the tick count of the ECB at the head of
the list. If it goes to zero, completes the ECB and any subsequent ECBs
which whose tick count would go to zero
Arguments:
None.
Return Value:
None.
--*/
{
LPXECB pXecb;
RequestMutex();
pXecb = TimerList;
if (pXecb) {
//
// Decrement if not already zero. Can be zero because the ECB at the
// front of the list could have been Cancelled. This makes sure we
// do not wrap around to 0xFFFF !!!
//
if (pXecb->Ticks != 0)
--pXecb->Ticks;
if (!pXecb->Ticks) {
//
// complete all ECBs that would go to 0 on this tick
//
while (pXecb->Ticks <= 1) {
TimerList = pXecb->Next;
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"ScanTimerList: ECB %04x:%04x is done\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress)
));
CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
pXecb = TimerList;
if (!pXecb) {
break;
}
}
}
}
ReleaseMutex();
}
BYTE
CancelTimerEvent(
IN LPXECB pXecb
)
/*++
Routine Description:
Cancels a pending event on the timer list
Arguments:
pXecb - pointer to XECB to cancel
Return Value:
BYTE
Success - IPX_SUCCESS
Failure - IPX_ECB_NOT_IN_USE
--*/
{
LPXECB listptr;
LPXECB previous = (LPXECB)&TimerList;
BYTE status;
RequestMutex();
listptr = TimerList;
while (listptr && listptr != pXecb) {
previous = listptr;
listptr = listptr->Next;
}
if (listptr) {
//
// take the XECB out of the list and complete the ECB (in VDM memory).
// Does not generate a call-back to the ESR. When CompleteEcb returns,
// the XECB has been deallocated
//
previous->Next = listptr->Next;
ASSERT(pXecb->RefCount == 2);
--pXecb->RefCount;
CompleteEcb(pXecb, ECB_CC_CANCELLED);
status = IPX_SUCCESS;
} else {
status = IPX_ECB_NOT_IN_USE;
}
ReleaseMutex();
return status;
}
VOID
CancelTimedEvents(
IN WORD SocketNumber,
IN WORD Owner,
IN DWORD TaskId
)
/*++
Routine Description:
traverses the TimerList cancelling any IPX or AES events owned by any of
SocketNumber, Owner or TaskId
Assumes valid SocketNumber, Owner or TaskId cannot be 0
Arguments:
SocketNumber - owning socket of IPX events to cancel
Owner - owning DOS PDB
TaskID - owning Windows Task ID
Return Value:
None.
--*/
{
LPXECB pXecb;
LPXECB prev = (LPXECB)&TimerList;
LPXECB next;
RequestMutex();
pXecb = TimerList;
while (pXecb) {
next = pXecb->Next;
if ((SocketNumber && (pXecb->SocketNumber == SocketNumber))
|| (Owner && !(pXecb->Flags & XECB_FLAG_IPX) && (pXecb->Owner == Owner))
|| (TaskId && (pXecb->TaskId == TaskId))) {
prev->Next = next;
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"CancelTimedEvents: cancelling ECB %08x (%04x:%04x)\n",
pXecb,
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress)
));
CompleteEcb(pXecb, ECB_CC_CANCELLED);
}
else
{
prev = pXecb ;
}
pXecb = next;
}
ReleaseMutex();
}
BYTE
CancelAsyncEvent(
IN LPXECB pXecb
)
/*++
Routine Description:
Called to cancel an event currently on the async completion list. We don't
cancel these events - just return 0xF9 (ECB cannot be cancelled). It is a
race to see who gets there first - us with the cancel, or the ESR callback.
In this case it is fairly immaterial
Arguments:
pXecb - pointer to XECB to cancel (ignored)
Return Value:
BYTE - IPX_CANNOT_CANCEL
--*/
{
//
// we call DeallocateXecb to reduce the reference count. If the other thread
// really tried to deallocate it in the short time we've been looking at it
// on the cancel path, the call will finish up what the other thread started
//
DeallocateXecb(pXecb);
return IPX_CANNOT_CANCEL;
}
BYTE
CancelSocketEvent(
IN LPXECB pXecb
)
/*++
Routine Description:
Called to cancel a pending send or listen from a socket queue. Request can
be IPX or SPX. If IPX event, then the ECB is on either the SendQueue or
ListenQueue. If SPX, it may be on a CONNECTION_INFO ConnectQueue,
AcceptQueue, SendQueue or ListenQueue, or if it is an
SPXListenForSequencedPacket request that is still in the pool then it may
be on the owning SOCKET_INFO ListenQueue
Arguments:
pXecb - pointer to XECB describing ECB to cancel
Return Value:
BYTE - IPX_SUCCESS
--*/
{
LPXECB ptr;
LPVOID pObject;
RequestMutex();
pObject = pXecb->OwningObject;
switch (pXecb->QueueId) {
case SOCKET_LISTEN_QUEUE:
if (pXecb->Flags & XECB_FLAG_SPX) {
ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->ListenQueue);
} else {
ptr = DequeueReceiveRequest(pXecb, (LPSOCKET_INFO)pObject);
}
break;
case SOCKET_SEND_QUEUE:
if (pXecb->Flags & XECB_FLAG_SPX) {
ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->SendQueue);
} else {
ptr = DequeueSendRequest(pXecb, (LPSOCKET_INFO)pObject);
}
break;
case SOCKET_HEADER_QUEUE: // SPX only
if (pXecb->Flags & XECB_FLAG_SPX) {
ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->HeaderQueue);
} else {
ASSERT(FALSE);
}
break;
}
ReleaseMutex();
if (ptr) {
CompleteIo(ptr, ECB_CC_CANCELLED);
}
return IPX_SUCCESS;
}
BYTE
CancelConnectionEvent(
IN LPXECB pXecb
)
/*++
Routine Description:
Cancels a pending SPXListenForConnection or SPXListenForSequencedPacket, the
only cancellable SPX requests
Arguments:
pXecb - pointer to SPX XECB to cancel
Return Value:
BYTE - IPX_SUCCESS
--*/
{
LPXECB ptr;
LPVOID pObject;
LPXECB_QUEUE pQueue;
RequestMutex();
pObject = pXecb->OwningObject;
switch (pXecb->QueueId) {
case CONNECTION_ACCEPT_QUEUE:
pQueue = &((LPCONNECTION_INFO)pObject)->AcceptQueue;
break;
case CONNECTION_LISTEN_QUEUE:
pQueue = &((LPCONNECTION_INFO)pObject)->ListenQueue;
break;
}
ptr = DequeueEcb(pXecb, pQueue);
ReleaseMutex();
if (ptr) {
CompleteIo(ptr, ECB_CC_CANCELLED);
}
return IPX_SUCCESS;
}
VOID
QueueEcb(
IN LPXECB pXecb,
IN LPXECB_QUEUE Queue,
IN QUEUE_ID QueueId
)
/*++
Routine Description:
Adds an XECB to a queue and sets the queue identifier in the XECB.
Arguments:
pXecb - pointer to XECB to queue
Queue - pointer to queue to add XECB to (at tail)
QueueId - identifies Queue
Return Value:
None.
--*/
{
LPVOID owningObject = NULL;
#define CONTAINER_STRUCTURE(p, t, f) (LPVOID)(((LPBYTE)(p)) - (UINT_PTR)(&((t)0)->f))
pXecb->QueueId = QueueId;
switch (QueueId) {
case SOCKET_LISTEN_QUEUE:
if (Queue->Tail && (Queue->Tail->Length < pXecb->Length)) {
FifoAddHead((LPFIFO)Queue, (LPFIFO)pXecb);
} else {
FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
}
owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, ListenQueue);
break;
case SOCKET_SEND_QUEUE:
FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, SendQueue);
break;
case SOCKET_HEADER_QUEUE:
FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, HeaderQueue);
break;
case CONNECTION_CONNECT_QUEUE:
FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, ConnectQueue);
break;
case CONNECTION_ACCEPT_QUEUE:
FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, AcceptQueue);
break;
case CONNECTION_SEND_QUEUE:
FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, SendQueue);
break;
case CONNECTION_LISTEN_QUEUE:
FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, ListenQueue);
break;
}
pXecb->OwningObject = owningObject;
}
LPXECB
DequeueEcb(
IN LPXECB pXecb,
IN LPXECB_QUEUE Queue
)
/*++
Routine Description:
Removes pXecb from Queue and resets the XECB queue identifier (to NO_QUEUE)
Arguments:
pXecb - pointer to XECB to remove
Queue - queue from which to remove pXecb
Return Value:
LPXECB
pointer to removed XECB
--*/
{
LPXECB p;
p = (LPXECB)FifoRemove((LPFIFO)Queue, (LPFIFO)pXecb);
pXecb->QueueId = NO_QUEUE;
pXecb->OwningObject = NULL;
return pXecb;
}
VOID
CancelSocketQueue(
IN LPXECB_QUEUE pXecbQueue
)
/*++
Routine Description:
Cancels all pending ECBs on a SOCKET_INFO queue
Arguments:
pXecbQueue - pointer to (socket/connection) queue
Return Value:
None.
--*/
{
LPXECB ptr;
while (ptr = pXecbQueue->Head) {
CancelSocketEvent(ptr);
}
}
VOID
CancelConnectionQueue(
IN LPXECB_QUEUE pXecbQueue
)
/*++
Routine Description:
Cancels all pending ECBs on a CONNECTION_INFO queue
Arguments:
pXecbQueue - pointer to XECB queue on CONNECTION_INFO
Return Value:
None.
--*/
{
LPXECB ptr;
while (ptr = pXecbQueue->Head) {
CancelConnectionEvent(ptr);
}
}
VOID
AbortQueue(
IN LPXECB_QUEUE pXecbQueue,
IN BYTE CompletionCode
)
/*++
Routine Description:
Aborts or terminates an ECB queue from a CONNECTION_INFO structure
Arguments:
pXecbQueue - pointer to queue
CompletionCode - to put in aborted/terminated ECBs
Return Value:
None.
--*/
{
LPXECB ptr;
while (ptr = pXecbQueue->Head) {
AbortConnectionEvent(ptr, CompletionCode);
}
}
VOID
AbortConnectionEvent(
IN LPXECB pXecb,
IN BYTE CompletionCode
)
/*++
Routine Description:
Aborts a connection ECB
Arguments:
pXecb - pointer to SPX XECB to cancel
CompletionCode - value to put in ECB
Return Value:
None.
--*/
{
LPXECB ptr;
LPCONNECTION_INFO pConnectionInfo;
LPXECB_QUEUE pQueue;
pConnectionInfo = (LPCONNECTION_INFO)pXecb->OwningObject;
switch (pXecb->QueueId) {
case CONNECTION_CONNECT_QUEUE:
pQueue = &pConnectionInfo->ConnectQueue;
break;
case CONNECTION_ACCEPT_QUEUE:
pQueue = &pConnectionInfo->AcceptQueue;
break;
case CONNECTION_SEND_QUEUE:
pQueue = &pConnectionInfo->SendQueue;
break;
case CONNECTION_LISTEN_QUEUE:
pQueue = &pConnectionInfo->ListenQueue;
break;
}
ptr = DequeueEcb(pXecb, pQueue);
if (ptr) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"AbortConnectionEvent: Aborting ECB %04x:%04x\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress)
));
SPX_ECB_CONNECTION_ID(ptr->Ecb) = pConnectionInfo->ConnectionId;
CompleteOrQueueIo(ptr, CompletionCode);
}
}
VOID
StartIpxSend(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Starts a send operation for IPXSendPacket(). Allocates a send buffer if
the ECB has >1 fragment else uses a pointer to the single fragment buffer
in 16-bit address space
Fills in various fields in the ECB and IPX header
Assumes: 1. By the time this function is called, we already know we have
a valid non-zero fragment count and the first fragment is
big enough to hold an IPX packet header
When this function terminates, the ECB is either completed or queued
Arguments:
pXecb - pointer to XECB describing ECB to use for sending
pSocketInfo - pointer to SOCKET_INFO structure
Return Value:
None.
--*/
{
BOOL success;
int packetLength = 0;
LPFRAGMENT pFragment;
int fragmentCount;
LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"StartIpxSend: %d frag(s), 1: address=%x (%04x:%04x), len=%04x\n",
READ_WORD(&pEcb->FragmentCount),
GET_FAR_POINTER(&(ECB_FRAGMENT(pEcb, 0)->Address), IS_PROT_MODE(pXecb)),
GET_SELECTOR(&(ECB_FRAGMENT(pEcb, 0)->Address)),
GET_OFFSET(&(ECB_FRAGMENT(pEcb, 0)->Address)),
READ_WORD(&(ECB_FRAGMENT(pEcb, 0)->Length))
));
//
// mark the ECB as being used by IPX (for send)
//
pEcb->InUse = ECB_IU_SENDING;
//
// the total send buffer size cannot exceed the maximum packet size
//
fragmentCount = (int)pEcb->FragmentCount;
ASSERT(fragmentCount);
pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
while (fragmentCount--) {
packetLength += pFragment->Length;
++pFragment;
}
if (packetLength <= MyMaxPacketSize) {
success = GetIoBuffer(pXecb, TRUE, IPX_HEADER_LENGTH);
if (success) {
LPIPX_PACKET pPacket = (LPIPX_PACKET)GET_FAR_POINTER(
&(ECB_FRAGMENT(pEcb, 0)->Address),
IS_PROT_MODE(pXecb)
);
//
// fill in the following fields in the IPX header:
//
// Checksum
// Length
// TransportControl
// Source (network, node, socket)
//
// Does real IPX modify these fields in app memory?
// If so, does the app expect modified fields?
// If not, we need to always copy then modify memory,
// even if only 1 fragment
//
pPacket->Checksum = 0xFFFF;
pPacket->Length = L2BW((WORD)packetLength);
pPacket->TransportControl = 0;
CopyMemory((LPBYTE)&pPacket->Source,
&MyInternetAddress.sa_netnum,
sizeof(MyInternetAddress.sa_netnum)
+ sizeof(MyInternetAddress.sa_nodenum)
);
pPacket->Source.Socket = pSocketInfo->SocketNumber;
//
// if we allocated a buffer then there is >1 fragment. Collect them
//
if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
GatherData(pXecb, IPX_HEADER_LENGTH);
}
//
// initiate the send. IPX_ECB_BUFFER32(pEcb) points to the data to send,
// IPX_ECB_LENGTH32(pEcb) is the size of data to send
//
IpxSendFirst(pXecb, pSocketInfo);
} else {
//
// couldn't allocate a buffer? Comes under the heading of
// hardware error?
//
CompleteEcb(pXecb, ECB_CC_HARDWARE_ERROR);
if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
KillSocket(pSocketInfo);
}
}
} else {
//
// packet larger than MyMaxPacketSize
//
CompleteOrQueueEcb(pXecb, ECB_CC_BAD_REQUEST);
if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
KillSocket(pSocketInfo);
}
}
}
BOOL
GetIoBuffer(
IN OUT LPXECB pXecb,
IN BOOL Send,
IN WORD HeaderLength
)
/*++
Routine Description:
Allocate a buffer based on the ECB fragment list. If there is only 1 fragment
we use the address of the buffer in the VDM. If >1 fragment, we allocate a
32-bit buffer large enough to hold all the 16-bit fragments
We trim the buffer requirement for a send buffer: we do not send the IPX/SPX
header with the data: it will be provided by the transport
Assumes: 1. If called for a send buffer, the first fragment has already
been verified as >= HeaderLength
Arguments:
pXecb - pointer to XECB which points to IPX_ECB containing fragment
list to allocate buffer for
Send - TRUE if this request is to get a send buffer
HeaderLength - length of the (untransmitted) header portion
Return Value:
BOOL
TRUE - Buffer allocated, XECB updated with address, length and flags
FALSE - either ECB contains bad fragment descriptor list or we
couldn't allocate a buffer
--*/
{
WORD fragmentCount;
WORD bufferLength = 0;
LPBYTE bufferPointer = NULL;
WORD flags = 0;
int i;
int fragIndex = 0; // index of fragment address to use if no allocation required
LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
fragmentCount = READ_WORD(&pEcb->FragmentCount);
for (i = 0; i < (int)fragmentCount; ++i) {
bufferLength += ECB_FRAGMENT(pEcb, i)->Length;
}
if (bufferLength) {
//
// exclude the IPX header from send buffer. If the first send fragment
// contains only the IPX header, reduce the fragment count by 1
//
if (Send) {
bufferLength -= HeaderLength;
if (ECB_FRAGMENT(pEcb, 0)->Length == HeaderLength) {
--fragmentCount;
fragIndex = 1;
}
}
if (bufferLength) {
if (fragmentCount > 1) {
bufferPointer = AllocateBuffer(bufferLength);
if (bufferPointer) {
flags = XECB_FLAG_BUFFER_ALLOCATED;
} else {
//
// need a buffer; failed to allocate it
//
return FALSE;
}
} else {
//
// fragmentCount must be 1 (else bufferLength would be 0)
//
bufferPointer = GET_FAR_POINTER(
&ECB_FRAGMENT(pEcb, fragIndex)->Address,
IS_PROT_MODE(pXecb)
);
if (Send && !fragIndex) {
//
// if we are allocating a send buffer AND there is only 1
// fragment AND it is the first fragment then the one and
// only fragment must contain the IPX header and the data.
// Advance the data pointer past the IPX header
//
bufferPointer += HeaderLength;
}
}
} else {
//
// sending 0 bytes!!!
//
}
} else {
//
// fragments but no buffer length? Sounds like a malformed packet
//
return FALSE;
}
//
// bufferPointer is either the address of a buffer in 32-bit memory which
// must be gather/scattered when the I/O operation completes, or it is the
// address of a single fragment buffer in 16-bit memory. In the former case
// flags is ECB_ALLOCATE_32 and the latter 0
//
pXecb->Buffer = pXecb->Data = bufferPointer;
pXecb->Length = bufferLength;
pXecb->Flags |= flags;
return TRUE;
}
PRIVATE
VOID
ReleaseIoBuffer(
IN LPXECB pXecb
)
/*++
Routine Description:
Deallocates I/O buffer attached to XECB and zaps associated XECB fields
Arguments:
pXecb - pointer to XECB owning buffer to be released
Return Value:
None.
--*/
{
if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
DeallocateBuffer(pXecb->Buffer);
pXecb->Buffer = pXecb->Data = NULL;
pXecb->Flags &= ~XECB_FLAG_BUFFER_ALLOCATED;
}
}
VOID
GatherData(
IN LPXECB pXecb,
IN WORD HeaderLength
)
/*++
Routine Description:
Copies data from fragmented 16-bit memory into single 32-bit memory buffer.
Used to send data. We exclude the IPX header: this information is supplied
by the transport
Assumes: 1. The fragment descriptor list has been verified: we know that
the first fragment contains at least the IPX header
Arguments:
pXecb - pointer to XECB structure. The following IPX_ECB and XECB
fields must contain coherent values:
IPX_ECB.FragmentCount
XECB.Buffer
HeaderLength - length of the (untransmitted) header portion
Return Value:
None.
--*/
{
int fragmentCount;
WORD length;
ULPBYTE pData16;
ULPBYTE pData32;
LPFRAGMENT pFragment;
LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
fragmentCount = (int)pEcb->FragmentCount;
pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
pData32 = pXecb->Buffer;
//
// if the 1st fragment contains more than the IPX/SPX header, copy the data
// after the header
//
if (pFragment->Length > HeaderLength) {
LPBYTE fragAddr = GET_FAR_POINTER(&pFragment->Address,
IS_PROT_MODE(pXecb)
);
length = pFragment->Length - HeaderLength;
CopyMemory((LPVOID)pData32,
fragAddr + HeaderLength,
length
);
pData32 += length;
}
//
// copy subsequent fragments
//
++pFragment;
while (--fragmentCount) {
pData16 = GET_FAR_POINTER(&pFragment->Address, IS_PROT_MODE(pXecb));
if (pData16 == NULL) {
break;
}
length = pFragment->Length;
CopyMemory((PVOID)pData32, (CONST VOID*)pData16, (ULONG)length);
pData32 += length;
++pFragment;
}
}
VOID
ScatterData(
IN LPXECB pXecb
)
/*++
Routine Description:
Copies data from 32-bit memory to 16-bit. The data must be fragmented if
this function has been called (i.e. we determined there were >1 fragments
and allocated a single 32-bit buffer to cover them)
Arguments:
pXecb - pointer to XECB containing 32-bit buffer info
Return Value:
None.
--*/
{
int fragmentCount;
int length;
WORD length16;
WORD length32;
ULPBYTE pData16;
ULPBYTE pData32;
LPFRAGMENT pFragment;
LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
fragmentCount = (int)pEcb->FragmentCount;
pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
pData32 = pXecb->Buffer;
length32 = pXecb->Length;
while (length32) {
pData16 = GET_FAR_POINTER(&pFragment->Address, IS_PROT_MODE(pXecb));
if (pData16 == NULL) {
break;
}
length16 = pFragment->Length;
length = min(length16, length32);
CopyMemory((PVOID)pData16, (CONST VOID*)pData32, (ULONG)length);
pData32 += length;
length32 -= (WORD) length;
++pFragment;
--fragmentCount;
ASSERT(fragmentCount >= 0);
}
}
VOID
IpxReceiveFirst(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Performs a receive against a non-blocking socket. This is the first
receive call for this ECB. If the receive completes immediately with data
or an error that isn't WSAEWOULDBLOCK then the ECB is completed. If the
receives completes with a WSAEWOULDBLOCK error then the request is queued
for deferred processing by the AES thread
Unlike send, receives are not serialized. If there are already receives
pending against the socket there could be a clash between this function
and IpxReceiveNext(), called from the AES thread. In this case, we expect
Winsock to do the right thing and serialize the callers
Arguments:
pXecb - pointer to XECB describing receive ECB
pSocketInfo - pointer to socket structure
Return Value:
None.
--*/
{
SOCKADDR_IPX from;
int fromLen = sizeof(from);
int rc;
BYTE status;
BOOL error;
rc = recvfrom(pSocketInfo->Socket,
(char FAR*)pXecb->Buffer,
(int)pXecb->Length,
0, // flags
(LPSOCKADDR)&from,
&fromLen
);
if (rc != SOCKET_ERROR) {
error = FALSE;
status = ECB_CC_SUCCESS;
} else {
error = TRUE;
rc = WSAGetLastError();
if (rc == WSAEWOULDBLOCK) {
RequestMutex();
QueueReceiveRequest(pXecb, pSocketInfo);
ReleaseMutex();
} else if (rc == WSAEMSGSIZE) {
error = FALSE;
status = ECB_CC_BAD_REQUEST;
rc = pXecb->Length;
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"IpxReceiveFirst: recvfrom() returns %d (buflen=%d)\n",
rc,
pXecb->Length
));
CompleteOrQueueIo(pXecb, ECB_CC_BAD_REQUEST);
}
}
if (!error) {
//
// rc = bytes received, or 0 = connection terminated (even for DGRAM?)
//
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"IpxReceiveFirst: bytes received = %d (%x)\n",
rc,
rc
));
/*
VwDumpEcb(pXecb->Ecb,
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
FALSE,
TRUE,
TRUE,
IS_PROT_MODE(pXecb)
);
*/
IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
//
// if the receive buffers are fragmented, copy the data to 16-bit memory
// (else single buffer: its already there (dude))
//
if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
//
// update the ECB_LENGTH32 field to reflect the amount of data received
//
pXecb->Length = (WORD)rc;
ScatterData(pXecb);
//
// we have finished with the 32-bit buffer: deallocate it
//
ReleaseIoBuffer(pXecb);
}
//
// update the ImmediateAddress field in the ECB with the node address
// of the sender
//
CopyMemory(pXecb->Ecb->ImmediateAddress, from.sa_nodenum, sizeof(from.sa_nodenum));
//
// if this ECB has a non-NULL ESR then queue for asynchronous completion
// else complete immediately (app must poll InUse field)
//
CompleteOrQueueEcb(pXecb, status);
}
}
VOID
IpxReceiveNext(
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Attempts to complete an IPXListenForPacket request that has been deferred due
to the fact the socket was blocked.
The ECB containing all the receive information is at the head of the
ListenQueue on pSocketInfo
We can use any queued listen ECB, but it just so happens we use the one at
the head of the FIFO
Note: SerializationCritSec is held when this function is called.
Arguments:
pSocketInfo - pointer to SOCKET_INFO structure with pending IPX send request
Return Value:
None.
--*/
{
LPXECB pXecb;
SOCKADDR_IPX from;
int fromLen = sizeof(from);
int rc;
BYTE status;
BOOL error;
ASSERT(pSocketInfo);
pXecb = (LPXECB)pSocketInfo->ListenQueue.Head;
ASSERT(pXecb);
rc = recvfrom(pSocketInfo->Socket,
(char FAR*)pXecb->Buffer,
(int)pXecb->Length,
0, // flags
(LPSOCKADDR)&from,
&fromLen
);
if (rc != SOCKET_ERROR) {
error = FALSE;
status = ECB_CC_SUCCESS;
} else {
error = TRUE;
rc = WSAGetLastError();
if (rc == WSAEMSGSIZE) {
error = FALSE;
status = ECB_CC_BAD_REQUEST;
rc = pXecb->Length;
} else if (rc != WSAEWOULDBLOCK) {
DequeueReceiveRequest(pXecb, pSocketInfo);
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"IpxReceiveNext: recvfrom() returns %d\n",
rc
));
CompleteOrQueueIo(pXecb, ECB_CC_CANCELLED);
}
}
if (!error) {
/*
VwDumpEcb(pXecb->Ecb,
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
FALSE,
TRUE,
TRUE,
IS_PROT_MODE(pXecb)
);
*/
//
// data received. Remove ECB from queue
//
DequeueReceiveRequest(pXecb, pSocketInfo);
//
// rc = bytes received, or 0 = connection terminated (even for DGRAM?)
//
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"IpxReceiveNext: ECB %04x:%04x bytes received = %d (%x)\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
rc,
rc
));
IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
//
// if the receive buffers are fragmented, copy the data to 16-bit memory
// (else single buffer: its already there (dude))
//
if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
//
// update the IPX_ECB_LENGTH32 field to reflect the amount of data received
//
pXecb->Length = (WORD)rc;
ScatterData(pXecb);
ReleaseIoBuffer(pXecb);
}
//
// update the ImmediateAddress field in the ECB with the node address
// of the sender
//
CopyMemory(pXecb->Ecb->ImmediateAddress,
from.sa_nodenum,
sizeof(from.sa_nodenum)
);
//
// if this ECB has a non-NULL ESR then queue for asynchronous completion
// else complete immediately (app must poll InUse field)
//
CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
}
}
PRIVATE
VOID
IpxSendFirst(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Tries to send an IPX packet. This is the first attempt to send the packet
described in the ECB. If the send succeeds or fails with an error other
than WSAEWOULDBLOCK we complete the ECB. If the send attempt fails because
the transport can't accept the request at this time, we queue it for later
when the AES thread will attempt to send it.
If there is already a send being attempted then we just queue this request
and let AES handle it in IpxSendNext()
Arguments:
pXecb - pointer to XECB
pSocketInfo - pointer to SOCKET_INFO structure
Return Value:
None.
--*/
{
RequestMutex();
if (pSocketInfo->Flags & SOCKET_FLAG_SENDING) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"IpxSendFirst: queueing ECB %04x:%04x\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress)
));
QueueSendRequest(pXecb, pSocketInfo);
} else {
SOCKADDR_IPX to;
LPIPX_PACKET pPacket;
int length;
int rc;
LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
int type;
/*
VwDumpEcb(pXecb->Ecb,
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
FALSE,
TRUE,
TRUE,
IS_PROT_MODE(pXecb)
);
*/
length = (int)pXecb->Length;
//
// the first fragment holds the destination address info
//
pPacket = (LPIPX_PACKET)GET_FAR_POINTER(&ECB_FRAGMENT(pEcb, 0)->Address,
IS_PROT_MODE(pXecb)
);
to.sa_family = AF_IPX;
//
// copy the destination net number as a DWORD (4 bytes) from the
// destination network address structure in the IPX packet header
//
*(ULPDWORD)&to.sa_netnum[0] = *(ULPDWORD)&pPacket->Destination.Net[0];
//
// copy the immediate (destination) node number as a DWORD (4 bytes) and
// a WORD (2 bytes) from the Destination network address structure in
// the IPX packet header. pPacket is an unaligned pointer, so we are
// safe
//
*(ULPDWORD)&to.sa_nodenum[0] = *(ULPDWORD)&pPacket->Destination.Node[0];
*(LPWORD)&to.sa_nodenum[4] = *(ULPWORD)&pPacket->Destination.Node[4];
//
// copy the destination socket number from the IPX packet header as a
// WORD (2 bytes). Again, the aligned pointer will save us
//
to.sa_socket = pPacket->Destination.Socket;
type = (int)pPacket->PacketType;
rc = setsockopt(pSocketInfo->Socket,
NSPROTO_IPX,
IPX_PTYPE,
(char FAR*)&type,
sizeof(type)
);
if (rc) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"IpxSendFirst: setsockopt(IPX_PTYPE) returns %d\n",
WSAGetLastError()
));
}
rc = sendto(pSocketInfo->Socket,
(char FAR*)pXecb->Buffer,
length,
0, // flags
(LPSOCKADDR)&to,
sizeof(to)
);
if (rc == length) {
//
// all data sent
//
IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
CompleteOrQueueIo(pXecb, ECB_CC_SUCCESS);
if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
KillSocket(pSocketInfo);
}
} else if (rc == SOCKET_ERROR) {
rc = WSAGetLastError();
if (rc == WSAEWOULDBLOCK) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"IpxSendFirst: queueing ECB %04x:%04x (after sendto)\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress)
));
QueueSendRequest(pXecb, pSocketInfo);
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"IpxSendFirst: sendto() returns %d\n",
rc
));
CompleteIo(pXecb, ECB_CC_UNDELIVERABLE);
if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
KillSocket(pSocketInfo);
}
}
} else {
//
// send should send all the data or return an error
//
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_FATAL,
"IpxSendFirst: sendto() returns unexpected %d (length = %d)\n",
rc,
length
));
}
}
ReleaseMutex();
}
VOID
IpxSendNext(
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Attempts to complete an IPXSendPacket request that has been deferred due
to the fact the socket was blocked.
The ECB containing all the send information is at the head of the SendQueue
on pSocketInfo
The SendQueue is serialized in FIFO order
Note: SerializationCritSec is held when this function is called.
Arguments:
pSocketInfo - pointer to SOCKET_INFO structure with pending IPX send request
Return Value:
None.
--*/
{
SOCKADDR_IPX to;
LPIPX_PACKET pPacket;
int length;
int rc;
LPXECB pXecb;
LPIPX_ECB pEcb;
int type;
pXecb = (LPXECB)pSocketInfo->SendQueue.Head;
pEcb = (LPIPX_ECB)pXecb->Ecb;
ASSERT(pXecb);
ASSERT(pEcb);
/*
VwDumpEcb(pXecb->Ecb,
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
FALSE,
TRUE,
TRUE,
IS_PROT_MODE(pXecb)
);
*/
length = (int)pXecb->Length;
//
// even though we have a 32-bit pointer to the IPX packet buffer which
// may be in 16- or 32-bit memory, we still need unaligned access
//
pPacket = (LPIPX_PACKET)pXecb->Buffer;
to.sa_family = AF_IPX;
//
// copy the destination net number as a DWORD (4 bytes) from the
// destination network address structure in the IPX packet header
//
*(ULPDWORD)&to.sa_netnum[0] = *(ULPDWORD)&pPacket->Destination.Net[0];
//
// copy the immediate (destination) node number as a DWORD (4 bytes) and
// a WORD (2 bytes) from the Destination network address structure in
// the IPX packet header. pPacket is an unaligned pointer, so we are
// safe
//
*(ULPDWORD)&to.sa_nodenum[0] = *(ULPDWORD)&pPacket->Destination.Node[0];
*(LPWORD)&to.sa_nodenum[4] = *(ULPWORD)&pPacket->Destination.Node[4];
//
// copy the destination socket number from the IPX packet header as a
// WORD (2 bytes). Again, the aligned pointer will save us
//
to.sa_socket = pPacket->Destination.Socket;
type = (int)pPacket->PacketType;
rc = setsockopt(pSocketInfo->Socket,
NSPROTO_IPX,
IPX_PTYPE,
(char FAR*)&type,
sizeof(type)
);
if (rc) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"IpxSendNext: setsockopt(IPX_PTYPE) returns %d\n",
WSAGetLastError()
));
}
rc = sendto(pSocketInfo->Socket,
(char FAR*)pPacket,
length,
0, // flags
(LPSOCKADDR)&to,
sizeof(to)
);
if (rc == length) {
//
// all data sent - dequeue it
//
IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
DequeueEcb(pXecb, &pSocketInfo->SendQueue);
if (pXecb->EsrAddress) {
if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
ReleaseIoBuffer(pXecb);
}
QueueAsyncCompletion(pXecb, ECB_CC_SUCCESS);
} else {
CompleteIo(pXecb, ECB_CC_SUCCESS);
}
if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
KillSocket(pSocketInfo);
}
} else if (rc == SOCKET_ERROR) {
//
// if the socket is still blocked, there's nothing to do - just leave
// the request hanging around till next time
//
rc = WSAGetLastError();
if (rc != WSAEWOULDBLOCK) {
DequeueSendRequest(pXecb, pSocketInfo);
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_ERROR,
"IpxSendNext: sendto() returns %d\n",
rc
));
CompleteIo(pXecb, ECB_CC_UNDELIVERABLE);
if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
KillSocket(pSocketInfo);
}
}
} else {
//
// send should send all the data or return an error
//
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_FATAL,
"IpxSendNext: sendto() returns unexpected %d (length = %d)\n",
rc,
length
));
}
}
PRIVATE
VOID
QueueReceiveRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Add a listen XECB to queue of listen XECBs on a SOCKET_INFO structure
Arguments:
pXecb - pointer to listen XECB to queue
pSocketInfo - pointer to SOCKET_INFO structure
Return Value:
None.
--*/
{
QueueEcb(pXecb, &pSocketInfo->ListenQueue, SOCKET_LISTEN_QUEUE);
++pSocketInfo->PendingListens;
pSocketInfo->Flags |= SOCKET_FLAG_LISTENING;
}
PRIVATE
LPXECB
DequeueReceiveRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Remove a listen XECB from queue of listen XECBs on a SOCKET_INFO structure
Arguments:
pXecb - pointer to listen XECB to dequeue
pSocketInfo - pointer to SOCKET_INFO structure
Return Value:
LPXECB
--*/
{
LPXECB ptr;
ptr = (LPXECB)DequeueEcb(pXecb, &pSocketInfo->ListenQueue);
if (ptr) {
ASSERT(ptr == pXecb);
--pSocketInfo->PendingListens;
if (!pSocketInfo->PendingListens) {
pSocketInfo->Flags &= ~SOCKET_FLAG_LISTENING;
}
pXecb->Ecb->InUse = ECB_IU_AWAITING_PROCESSING;
}
return ptr;
}
PRIVATE
VOID
QueueSendRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Add a send XECB to queue of send XECBs on a SOCKET_INFO structure
Arguments:
pXecb - pointer to send XECB to queue
pSocketInfo - pointer to SOCKET_INFO structure
Return Value:
None.
--*/
{
QueueEcb(pXecb, &pSocketInfo->SendQueue, SOCKET_SEND_QUEUE);
++pSocketInfo->PendingSends;
pSocketInfo->Flags |= SOCKET_FLAG_SENDING;
pXecb->Ecb->InUse = ECB_IU_SEND_QUEUED;
}
PRIVATE
LPXECB
DequeueSendRequest(
IN LPXECB pXecb,
IN LPSOCKET_INFO pSocketInfo
)
/*++
Routine Description:
Remove a send XECB from queue of send XECBs on a SOCKET_INFO structure
Arguments:
pXecb - pointer to send XECB to dequeue
pSocketInfo - pointer to SOCKET_INFO structure
Return Value:
LPXECB
--*/
{
LPXECB ptr;
ptr = (LPXECB)DequeueEcb(pXecb, &pSocketInfo->SendQueue);
if (ptr) {
ASSERT(ptr == pXecb);
--pSocketInfo->PendingSends;
if (!pSocketInfo->PendingSends) {
pSocketInfo->Flags &= ~SOCKET_FLAG_SENDING;
}
pXecb->Ecb->InUse = ECB_IU_AWAITING_PROCESSING;
}
return ptr;
}
VOID
CompleteOrQueueIo(
IN LPXECB pXecb,
IN BYTE CompletionCode
)
/*++
Routine Description:
Returns any allocated buffer resource then completes or queues the ECB
Arguments:
pXecb - pointer to XECB structure
CompletionCode - value to put in CompletionCode field
Return Value:
None.
--*/
{
//
// if we allocated a buffer, free it
//
if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
ReleaseIoBuffer(pXecb);
}
CompleteOrQueueEcb(pXecb, CompletionCode);
}
VOID
CompleteIo(
IN LPXECB pXecb,
IN BYTE CompletionCode
)
/*++
Routine Description:
Completes a send/receive request by returning any allocated buffer resource
and setting the ECB InUse and CompletionCode fields
Arguments:
pXecb - pointer to XECB structure
CompletionCode - value to put in CompletionCode field
Return Value:
None.
--*/
{
//
// if we allocated a buffer, free it
//
if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
ReleaseIoBuffer(pXecb);
}
CompleteEcb(pXecb, CompletionCode);
}
VOID
CompleteOrQueueEcb(
IN LPXECB pXecb,
IN BYTE CompletionCode
)
/*++
Routine Description:
Queues an XECB for completion by ESR or completes it now
Arguments:
pXecb - pointer to XECB describing ECB to complete
CompletionCode - value to put in ECB CompletionCode field
Return Value:
None.
--*/
{
if (pXecb->EsrAddress) {
QueueAsyncCompletion(pXecb, CompletionCode);
} else {
CompleteIo(pXecb, CompletionCode);
}
}
VOID
CompleteEcb(
IN LPXECB pXecb,
IN BYTE CompletionCode
)
/*++
Routine Description:
Sets the CompletionCode field in the ECB and sets the InUse field to 0.
Deallocates the XECB structure
Arguments:
pXecb - pointer to XECB describing ECB in 16-bit memory to update
CompletionCode - value to put in CompletionCode field
Return Value:
None.
--*/
{
LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"CompleteEcb: completing ECB @%04x:%04x w/ %02x\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
CompletionCode
));
//
// if this is really an AES ECB then CompletionCode is actually the first
// byte of the AES workspace. It shouldn't matter that we write into this
// field - we are supposed to own it
//
pEcb->CompletionCode = CompletionCode;
pEcb->InUse = ECB_IU_NOT_IN_USE;
//
// reset the LinkAddress field. This means we have completed the ECB
//
pEcb->LinkAddress = NULL;
//
// finally, deallocate the XECB. This mustn't have any allocated resources
// (like a buffer)
//
DeallocateXecb(pXecb);
}
PRIVATE
VOID
QueueAsyncCompletion(
IN LPXECB pXecb,
IN BYTE CompletionCode
)
/*++
Routine Description:
Add an XECB to the (serialized) async completion queue and raise a simulated
hardware interrupt in the VDM.
The interrupt will cause the VDM to start executing at the ISR in the TSR
which will call-back to find the address for the ESR, then execute it
Arguments:
pXecb - pointer to XECB describing IPX or AES ECB to add to async
completion list
CompletionCode - the ECB in VDM memory will be updated with this completion
code
Return Value:
None.
--*/
{
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"QueueAsyncCompletion: completing ECB @%04x:%04x w/ %02x\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
CompletionCode
));
pXecb->Ecb->CompletionCode = CompletionCode;
pXecb->QueueId = ASYNC_COMPLETION_QUEUE;
EnterCriticalSection(&AsyncCritSec);
FifoAdd(&AsyncCompletionQueue, (LPFIFO)pXecb);
LeaveCriticalSection(&AsyncCritSec);
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"QueueAsyncCompletion: ECB @ %04x:%04x ESR @ %04x:%04x\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
HIWORD(pXecb->EsrAddress),
LOWORD(pXecb->EsrAddress)
));
VDDSimulateInterrupt(Ica, IcaLine, 1);
}
VOID
EsrCallback(
VOID
)
/*++
Routine Description:
Callback function from within 16-bit TSR ESR function. Returns the address
of the next completed ECB in ES:SI
Any allocated resources (e.g. 32-bit buffer) must have been freed by the
time the ESR callback happens
Arguments:
None.
Return Value:
None.
--*/
{
WORD segment = 0;
WORD offset = 0;
BYTE flags = 0;
VWinEsrCallback( &segment, &offset, &flags );
setES(segment);
setSI(offset);
setAL(flags);
}
VOID
VWinEsrCallback(
WORD *pSegment,
WORD *pOffset,
BYTE *pFlags
)
/*++
Routine Description:
Callback function from within 16-bit function. Returns the address
of the next completed ECB
Any allocated resources (e.g. 32-bit buffer) must have been freed by the
time the ESR callback happens
Arguments:
None.
Return Value:
None.
--*/
{
LPXECB pXecb;
EnterCriticalSection(&AsyncCritSec);
pXecb = AsyncCompletionQueue.Head;
if (pXecb) {
WORD msw = getMSW();
if ((msw & MSW_PE) ^ IS_PROT_MODE(pXecb)) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"EsrCallback: ECB @ %04x:%04x NOT for this proc mode (%d)\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
msw & MSW_PE
));
pXecb = NULL;
} else {
pXecb = (LPXECB)FifoNext(&AsyncCompletionQueue);
}
} else {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_FATAL,
"EsrCallback: no ECBs on AsyncCompletionQueue!\n"
));
}
LeaveCriticalSection(&AsyncCritSec);
if (pXecb) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_ANY,
IPXDBG_LEVEL_INFO,
"EsrCallback: ECB @ %04x:%04x ESR @ %04x:%04x\n",
HIWORD(pXecb->EcbAddress),
LOWORD(pXecb->EcbAddress),
HIWORD(pXecb->EsrAddress),
LOWORD(pXecb->EsrAddress)
));
*pSegment = HIWORD(pXecb->EcbAddress);
*pOffset = LOWORD(pXecb->EcbAddress);
pXecb->Ecb->LinkAddress = NULL;
pXecb->Ecb->InUse = ECB_IU_NOT_IN_USE;
*pFlags = (BYTE)((pXecb->Flags & XECB_FLAG_IPX) ? ECB_TYPE_IPX : ECB_TYPE_AES);
DeallocateXecb(pXecb);
setCF(0);
} else {
setCF(1);
}
}
VOID
FifoAddHead(
IN LPFIFO pFifo,
IN LPFIFO pElement
)
/*++
Routine Description:
Adds an element to the head of a (single-linked) FIFO list
Arguments:
pFifo - pointer to FIFO structure
pElement - pointer to (FIFO) element to add to list
Return Value:
None.
--*/
{
if (!pFifo->Head) {
pFifo->Head = pFifo->Tail = pElement;
pElement->Head = NULL;
} else {
pElement->Head = pFifo->Head;
pFifo->Head = pElement;
}
}
VOID
FifoAdd(
IN LPFIFO pFifo,
IN LPFIFO pElement
)
/*++
Routine Description:
Adds an element to the tail of a (single-linked) FIFO list
Arguments:
pFifo - pointer to FIFO structure
pElement - pointer to (FIFO) element to add to list
Return Value:
None.
--*/
{
if (!pFifo->Head) {
pFifo->Head = pFifo->Tail = pElement;
} else {
((LPFIFO)pFifo->Tail)->Head = pElement;
}
pFifo->Tail = pElement;
pElement->Head = NULL;
}
LPFIFO
FifoRemove(
IN LPFIFO pFifo,
IN LPFIFO pElement
)
/*++
Routine Description:
Removes an element from a (single-linked) FIFO list
Arguments:
pFifo - pointer to FIFO structure
pElement - pointer to (FIFO) element to remove (single-linked)
Return Value:
PFIFO
NULL - pElement not on list
!NULL - pElement removed from list
--*/
{
LPFIFO p;
LPFIFO prev = (LPFIFO)pFifo;
p = (LPFIFO)pFifo->Head;
while (p && (p != pElement)) {
prev = p;
p = p->Head;
}
if (p) {
prev->Head = p->Head;
if (pFifo->Head == NULL) {
pFifo->Tail = NULL;
} else if (pFifo->Tail == p) {
pFifo->Tail = prev;
}
}
return p;
}
LPFIFO
FifoNext(
IN LPFIFO pFifo
)
/*++
Routine Description:
Remove element at head of FIFO queue
Arguments:
pFifo - pointer to FIFO
Return Value:
LPFIFO
NULL - nothing on queue
!NULL - removed element
--*/
{
LPFIFO p;
LPFIFO prev = (LPFIFO)pFifo;
p = (LPFIFO)pFifo->Head;
if (p) {
pFifo->Head = p->Head;
if (!pFifo->Head) {
pFifo->Tail = NULL;
}
}
return p;
}