diff options
Diffstat (limited to 'private/nw/vwipxspx/dll/util.c')
-rw-r--r-- | private/nw/vwipxspx/dll/util.c | 2966 |
1 files changed, 2966 insertions, 0 deletions
diff --git a/private/nw/vwipxspx/dll/util.c b/private/nw/vwipxspx/dll/util.c new file mode 100644 index 000000000..5e4aac78f --- /dev/null +++ b/private/nw/vwipxspx/dll/util.c @@ -0,0 +1,2966 @@ +/*++ + +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; + } + + // + // BUGBUG: since IPX and SPX can only deal in packets of max size 576 bytes, + // should we just return 576 if the indicated size is larger??? + // + + *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) { + + // + // 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)) - (DWORD)(&((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) + // + // BUGBUG: 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)); + 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)); + length16 = pFragment->Length; + length = min(length16, length32); + CopyMemory((PVOID)pData16, (CONST VOID*)pData32, (ULONG)length); + pData32 += length; + length32 -= 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 + )); + + // + // BUGBUG: map error + // + + 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 + )); + + // + // BUGBUG: completion code? + // + + 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 + )); + + // + // BUGBUG: couple of possible completion codes here + // + + CompleteIo(pXecb, ECB_CC_UNDELIVERABLE); + if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) { + KillSocket(pSocketInfo); + } + } + } else { + + // + // BUGBUG: Is 0 a valid value for rc? + // + + // + // 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 + )); + + // + // BUGBUG: couple of possible completion codes here + // + + CompleteIo(pXecb, ECB_CC_UNDELIVERABLE); + if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) { + KillSocket(pSocketInfo); + } + } + } else { + + // + // BUGBUG: Is 0 a valid value for rc? + // + + // + // 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; + } + + // + // BUGBUG: Is this correct value? + // + + 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; + + 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; +} |