diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/nw/vwipxspx/dll/socket.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/nw/vwipxspx/dll/socket.c')
-rw-r--r-- | private/nw/vwipxspx/dll/socket.c | 2309 |
1 files changed, 2309 insertions, 0 deletions
diff --git a/private/nw/vwipxspx/dll/socket.c b/private/nw/vwipxspx/dll/socket.c new file mode 100644 index 000000000..874d4691f --- /dev/null +++ b/private/nw/vwipxspx/dll/socket.c @@ -0,0 +1,2309 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + socket.c + +Abstract: + + Contains functions to create, delete and manipulate IPX sockets and SPX + connections + + Contents: + CreateSocket + AllocateTemporarySocket + QueueSocket + DequeueSocket + FindSocket + FindActiveSocket + ReopenSocket + KillSocket + KillShortLivedSockets + AllocateConnection + DeallocateConnection + FindConnection + QueueConnection + DequeueConnection + KillConnection + AbortOrTerminateConnection + CheckPendingSpxRequests + (CheckSocketState) + (CheckSelectRead) + (CheckSelectWrite) + (AsyncReadAction) + (AsyncWriteAction) + (CompleteAccept) + (CompleteReceive) + (CompleteConnect) + (CompleteSend) + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +#include "vw.h" +#pragma hdrstop + +// +// miscellaneous manifests +// + +#define ARBITRARY_CONNECTION_INCREMENT 2 + +// +// macros +// + +#define ALLOCATE_CONNECTION_NUMBER() (ConnectionNumber += ARBITRARY_CONNECTION_INCREMENT) + +// +// private data +// + +PRIVATE LPSOCKET_INFO SocketList = NULL; +PRIVATE LPCONNECTION_INFO ConnectionList = NULL; +PRIVATE WORD ConnectionNumber = ARBITRARY_CONNECTION_NUMBER; + +// +// private functions +// + +PRIVATE +BOOL +CheckSocketState( + IN SOCKET Socket, + OUT LPBOOL Readable, + OUT LPBOOL Writeable, + OUT LPBOOL Error + ); + + +PRIVATE +VOID +CheckSelectRead( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *CheckRead + ); + +PRIVATE +VOID +CheckSelectWrite( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *CheckWrite + ); + +PRIVATE +VOID +AsyncReadAction( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *ReadPerformed + ); + +PRIVATE +VOID +AsyncWriteAction( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *WritePerformed + ); + +PRIVATE +VOID +CompleteAccept( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ); + +PRIVATE +VOID +CompleteReceive( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ); + +PRIVATE +VOID +CompleteConnect( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ); + +PRIVATE +VOID +CompleteSend( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ); + +#if SPX_HACK +PRIVATE VOID ModifyFirstReceive(LPBYTE, LPDWORD, WORD, SOCKET); +#endif + +// +// public functions +// + + +int +CreateSocket( + IN SOCKET_TYPE SocketType, + IN OUT ULPWORD pSocketNumber, + OUT SOCKET* pSocket + ) + +/*++ + +Routine Description: + + Creates a socket for IPX or SPX (a connection). Once the socket is created + we have to bind it to the IPX/SPX 'socket' - i.e. port. We also need to + change a few things about the standard socket: + + * if this is an SPX request then we must set the REUSEADDR socket option + since there may typically be several connect requests over the same + WinSock socket: we need to be able to bind multiple connections to the + same socket number + + * all sockets opened by this function are put into non-blocking mode + * all sockets opened by this function will return the packet header in + any received data (IPX_RECVHDR) + + The requested socket number can be 0 in which case we bind to a dynamic + socket number. We always return the number of the socket bound to: if not 0 + on input, this should always be the same value as that requested in + pSocketNumber + + If any WinSock call fails (and the socket was created) then we close the + socket before returning + +Arguments: + + SocketType - SOCKET_TYPE_IPX or SOCKET_TYPE_SPX + pSocketNumber - input: socket number to bind (can be 0) + output: socket number bound + pSocket - pointer to address of socket identifier to return + +Return Value: + + int + Success - IPX_SUCCESS/SPX_SUCCESS (0) + + Failure - IPX_SOCKET_TABLE_FULL + WinSock cannot create the socket + + IPX_SOCKET_ALREADY_OPEN + Assume the request was for an IPX socket: we do not allow + multiple IPX sockets to be bound to the same socket number, + only SPX + + +--*/ + +{ + SOCKET s; + SOCKADDR_IPX socketAddress; + BOOL true = TRUE; + int rc; + int status = IPX_SOCKET_TABLE_FULL; // default error + + s = socket(AF_IPX, + (SocketType == SOCKET_TYPE_SPX) ? SOCK_SEQPACKET : SOCK_DGRAM, + (SocketType == SOCKET_TYPE_SPX) ? NSPROTO_SPX : NSPROTO_IPX + ); + + if (s != INVALID_SOCKET) { + + // + // for stream (SPX) sockets, we need multiple sockets bound to the + // same socket number if we are to have multiple connections on the + // same SPX socket + // + + if (SocketType == SOCKET_TYPE_SPX) { + rc = setsockopt(s, + SOL_SOCKET, + SO_REUSEADDR, + (char FAR*)&true, + sizeof(true) + ); + if (rc == SOCKET_ERROR) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CreateSocket: setsockopt(SO_REUSEADDR) returns %d\n", + WSAGetLastError() + )); + + } else { + rc = setsockopt(s, + SOL_SOCKET, + SO_OOBINLINE, + (char FAR*)&true, + sizeof(true) + ); + + if (rc == SOCKET_ERROR) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CreateSocket: setsockopt(SO_OOBINLINE) returns %d\n", + WSAGetLastError() + )); + + } + } + } else { + + // + // allow broadcasts to be transmitted on IPX sockets + // + + rc = setsockopt(s, + SOL_SOCKET, + SO_BROADCAST, + (char FAR*)&true, + sizeof(true) + ); + } + if (!rc) { + + // + // bind the socket to the local socket number (port) + // + + ZeroMemory(&socketAddress, sizeof(socketAddress)); + socketAddress.sa_family = AF_IPX; + socketAddress.sa_socket = *pSocketNumber; + rc = bind(s, (LPSOCKADDR)&socketAddress, sizeof(socketAddress)); + if (rc != SOCKET_ERROR) { + + int length = sizeof(socketAddress); + + ZeroMemory(&socketAddress, sizeof(socketAddress)); + socketAddress.sa_family = AF_IPX; + + // + // use getsockname() to find the (big-endian) socket value that + // was actually assigned: should only be different from + // *pSocketNumber if the latter was 0 on input + // + + rc = getsockname(s, (LPSOCKADDR)&socketAddress, &length); + if (rc != SOCKET_ERROR) { + + u_long arg = !0; + + // + // put the socket into non-blocking mode. Neither IPX nor + // SPX sockets are blocking: the app starts an I/O request + // and if it doesn't complete immediately, will be completed + // by AES which periodically polls the outstanding I/O + // requests + // + + rc = ioctlsocket(s, FIONBIO, &arg); + if (rc != SOCKET_ERROR) { + + // + // return protocol header on receive frames + // + + rc = setsockopt(s, + NSPROTO_IPX, + IPX_RECVHDR, + (char FAR*)&true, + sizeof(true) + ); + if (rc != SOCKET_ERROR) { + *pSocketNumber = socketAddress.sa_socket; + *pSocket = s; + status = IPX_SUCCESS; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CreateSocket: setsockopt(RECVHDR) returns %d\n", + WSAGetLastError() + )); + + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CreateSocket: ioctlsocket(FIONBIO) returns %d\n", + WSAGetLastError() + )); + + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CreateSocket: getsockname() returns %d\n", + WSAGetLastError() + )); + + } + } else { + + // + // bind() failed - either an expected error (the requested socket + // is already in use), or (horror) an unexpected error, in which + // case report table full (?) + // + + switch (WSAGetLastError()) { + case WSAEADDRINUSE: + + ASSERT(*pSocketNumber != 0); + ASSERT(SocketType == SOCKET_TYPE_IPX); + + status = IPX_SOCKET_ALREADY_OPEN; + break; + + default: + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CreateSocket: bind() on socket %#x returns %d\n", + s, + WSAGetLastError() + )); + + } + } + } + } else { + + // + // the socket() call failed - treat as table full + // + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CreateSocket: socket() returns %d\n", + WSAGetLastError() + )); + + } + if (status != IPX_SUCCESS) { + if (s != INVALID_SOCKET) { + closesocket(s); + } + } + return status; +} + + +LPSOCKET_INFO +AllocateTemporarySocket( + VOID + ) + +/*++ + +Routine Description: + + Allocates a temporary socket. Creates an IPX socket having a dynamically + allocated socket number + +Arguments: + + None. + +Return Value: + + LPSOCKET_INFO + Success - pointer to SOCKET_INFO structure + Failure - NULL + +--*/ + +{ + LPSOCKET_INFO pSocketInfo; + int rc; + + pSocketInfo = AllocateSocket(); + if (pSocketInfo) { + + // + // assumption: the SOCKET_INFO structure was zeroed by LocalAlloc(LPTR,.. + // hence the SocketNumber fields is 0. This causes CreateSocket to + // generate a dynamic socket number + // + + rc = CreateSocket(SOCKET_TYPE_IPX, + &pSocketInfo->SocketNumber, + &pSocketInfo->Socket + ); + if (rc == IPX_SUCCESS) { + pSocketInfo->Flags |= SOCKET_FLAG_TEMPORARY; + } else { + DeallocateSocket(pSocketInfo); + pSocketInfo = NULL; + } + } + return pSocketInfo; +} + + +VOID +QueueSocket( + IN LPSOCKET_INFO pSocketInfo + ) + +/*++ + +Routine Description: + + Add a SOCKET_INFO structure to the list (LIFO) of (opened) sockets + +Arguments: + + pSocketInfo - pointer to filled-in SOCKET_INFO structure + +Return Value: + + None. + +--*/ + +{ + RequestMutex(); + pSocketInfo->Next = SocketList; + SocketList = pSocketInfo; + ReleaseMutex(); +} + + +LPSOCKET_INFO +DequeueSocket( + IN LPSOCKET_INFO pSocketInfo + ) + +/*++ + +Routine Description: + + Remove a SOCKET_INFO structure from the list + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO structure to remove + +Return Value: + + LPSOCKET_INFO + pSocketInfo - should be this value + NULL - couldn't find pSocketInfo (should not get this!) + +--*/ + +{ + LPSOCKET_INFO prev, p; + + ASSERT(SocketList); + + RequestMutex(); + prev = (LPSOCKET_INFO)&SocketList; + p = SocketList; + while (p) { + if (p == pSocketInfo) { + prev->Next = p->Next; + p->Next = NULL; + break; + } else { + prev = p; + p = p->Next; + } + } + + if (!p) { + + // + // should never reach here + // + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_FATAL, + "DequeueSocket: can't find socket structure %08x on queue\n", + pSocketInfo + )); + + } + + ReleaseMutex(); + return p; +} + + +LPSOCKET_INFO +FindSocket( + IN WORD SocketNumber + ) + +/*++ + +Routine Description: + + Locate a SOCKET_INFO structure in the list, by (big-endian) socket number + + Assumes: 1. There is 1 and only 1 SOCKET_INFO structure that contains + SocketNumber + +Arguments: + + SocketNumber - big-endian socket number to find + +Return Value: + + LPSOCKET_INFO + NULL - couldn't find requested socket + !NULL - pointer to discovered SOCKET_INFO structure + +--*/ + +{ + LPSOCKET_INFO p; + + RequestMutex(); + p = SocketList; + while (p) { + if (p->SocketNumber == SocketNumber) { + break; + } else { + p = p->Next; + } + } + ReleaseMutex(); + return p; +} + + +LPSOCKET_INFO +FindActiveSocket( + IN LPSOCKET_INFO pSocketInfo + ) + +/*++ + +Routine Description: + + Find a SOCKET_INFO structure with pending send or receive. Called as FindFirst, + FindNext - first call made with pSocketInfo == NULL: enters critical section + if an active socket is found, returns pointer + + Subsequent calls are made with pSocketInfo pointing to last returned + SOCKET_INFO. This continues the search. When search exhausted, critical + section is released + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO structure: first time must be NULL + +Return Value: + + LPSOCKET_INFO - next active SOCKET_INFO structure or NULL + +--*/ + +{ + if (!pSocketInfo) { + RequestMutex(); + pSocketInfo = SocketList; + } else { + pSocketInfo = pSocketInfo->Next; + } + for (; pSocketInfo; pSocketInfo = pSocketInfo->Next) { + if (pSocketInfo->Flags & (SOCKET_FLAG_SENDING | SOCKET_FLAG_LISTENING)) { + return pSocketInfo; + } + } + ReleaseMutex(); + return NULL; +} + + +int +ReopenSocket( + LPSOCKET_INFO pSocketInfo + ) + +/*++ + +Routine Description: + + Called expressly to close an IPX socket and reassign the descriptor to SPX. + Note that after this function completes, IPXSendPacket and IPXListenForPacket + cannot be made agains the IPX socket + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO which currently describes an IPX socket + +Return Value: + + int - return code from CreateSocket + +--*/ + +{ + int rc; + + rc = closesocket(pSocketInfo->Socket); + if (rc == SOCKET_ERROR) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "ReopenSocket: closesocket() returns %d\n", + WSAGetLastError() + )); + + } + + // + // mark this socket as connection-based (SPX) socket + // + + pSocketInfo->SpxSocket = TRUE; + + // + // BUGBUG: need the DOS/Windows task ID: owner may have changed! + // + + // + // re-open the socket for SPX use + // + + return CreateSocket(SOCKET_TYPE_SPX, + &pSocketInfo->SocketNumber, + &pSocketInfo->Socket + ); +} + + +VOID +KillSocket( + IN LPSOCKET_INFO pSocketInfo + ) + +/*++ + +Routine Description: + + closes a socket, removes the SOCKET_INFO structure from the list and cancels + any pending send, listen or timed events associated with the socket + +Arguments: + + pSocketInfo - identifying socket to kill + +Return Value: + + None. + +--*/ + +{ + + int rc; + + // + // remove the SOCKET_INFO structure from the list of sockets. Cancel + // any pending ECB requests and any IPX timed events that have the + // same socket number + // + + DequeueSocket(pSocketInfo); + rc = closesocket(pSocketInfo->Socket); + if (rc == SOCKET_ERROR) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "KillSocket: closesocket() returns %d\n", + WSAGetLastError() + )); + + } + + // + // the socket has been removed from SocketList: no need to grab mutex to + // perform the following + // + + CancelTimedEvents(pSocketInfo->SocketNumber, 0, 0); + CancelSocketQueue(&pSocketInfo->ListenQueue); + CancelSocketQueue(&pSocketInfo->HeaderQueue); + CancelSocketQueue(&pSocketInfo->SendQueue); + if (pSocketInfo->SpxSocket) { + + LPCONNECTION_INFO pConnectionInfo; + + while (pConnectionInfo = pSocketInfo->Connections) { + DequeueConnection(pSocketInfo, pConnectionInfo); + KillConnection(pConnectionInfo); + } + } + DeallocateSocket(pSocketInfo); +} + + +VOID +KillShortLivedSockets( + IN WORD Owner + ) + +/*++ + +Routine Description: + + For all those sockets created by a DOS process as SHORT_LIVED, terminate + the sockets, cancelling any outstanding ECBs + +Arguments: + + Owner - DOS PDB which opened sockets + +Return Value: + + None. + +--*/ + +{ + LPSOCKET_INFO pSocketInfo; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "KillShortLivedSockets(%04x)\n", + Owner + )); + + RequestMutex(); + + // + // kill any non-socket (AES) timed events owned by this DOS process + // + + CancelTimedEvents(0, Owner, 0); + + // + // kill all sockets owned by this PDB + // + + pSocketInfo = SocketList; + while (pSocketInfo) { + + LPSOCKET_INFO next; + + next = pSocketInfo->Next; + if (!pSocketInfo->LongLived && (pSocketInfo->Owner == Owner)) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "KillShortLivedSockets: Socket %04x owned by %04x\n", + B2LW(pSocketInfo->SocketNumber), + pSocketInfo->Owner + )); + + KillSocket(pSocketInfo); + } + pSocketInfo = next; + } + ReleaseMutex(); +} + + +LPCONNECTION_INFO +AllocateConnection( + LPSOCKET_INFO pSocketInfo + ) + +/*++ + +Routine Description: + + Allocates a CONNECTION_INFO structure. If successful, links it at the head + of ConnectionList + +Arguments: + + pSocketInfo - pointer to owner SOCKET_INFO + +Return Value: + + LPCONNECTION_INFO + Success - !NULL + Failure - NULL + +--*/ + +{ + LPCONNECTION_INFO pConnectionInfo; + + pConnectionInfo = (LPCONNECTION_INFO)LocalAlloc(LPTR, sizeof(*pConnectionInfo)); + if (pConnectionInfo) { + RequestMutex(); + pConnectionInfo->ConnectionId = ALLOCATE_CONNECTION_NUMBER(); + pConnectionInfo->List = ConnectionList; + ConnectionList = pConnectionInfo; + ReleaseMutex(); + } + +#if SPX_HACK + pConnectionInfo->Flags = CF_1ST_RECEIVE; +#endif + + return pConnectionInfo; +} + + +VOID +DeallocateConnection( + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Undoes the work of AllocateConnection - removes pConnectionInfo from + ConnectionList and deallocates the structure + +Arguments: + + pConnectionInfo - pointer to CONNECTION_INFO to deallocate + +Return Value: + + None. + +--*/ + +{ + LPCONNECTION_INFO p; + LPCONNECTION_INFO prev = (LPCONNECTION_INFO)&ConnectionList; + + RequestMutex(); + for (p = ConnectionList; p != pConnectionInfo; ) { + prev = p; + p = p->List; + } + + // + // if p is NULL or differs from pConnectionInfo then there's a problem + // + + ASSERT(p); + + // + // special case if pConnectionInfo is first on list: can't say + // &ConnectionList->List - accesses one pointer beyond ConnectionList + // which is WRONG + // + + if (prev == (LPCONNECTION_INFO)&ConnectionList) { + ConnectionList = p->List; + } else { + prev->List = p->List; + } + FREE_OBJECT(pConnectionInfo); + ReleaseMutex(); +} + + +LPCONNECTION_INFO +FindConnection( + IN WORD ConnectionId + ) + +/*++ + +Routine Description: + + Returns a pointer to CONNECTION_INFO given a unique connection ID + +Arguments: + + ConnectionId - value to find + +Return Value: + + LPCONNECTION_INFO + Success - !NULL + Failure - NULL + +--*/ + +{ + LPCONNECTION_INFO pConnectionInfo; + + RequestMutex(); + for (pConnectionInfo = ConnectionList; pConnectionInfo; ) { + if (pConnectionInfo->ConnectionId == ConnectionId) { + break; + } else { + pConnectionInfo = pConnectionInfo->List; + } + } + ReleaseMutex(); + return pConnectionInfo; +} + + +VOID +QueueConnection( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Adds a CONNECTION_INFO to the list of connections owned by a SOCKET_INFO. + Points the CONNECTION_INFO back to the SOCKET_INFO + +Arguments: + + pSocketInfo - owning SOCKET_INFO + pConnectionInfo - CONNECTION_INFO to add + +Return Value: + + None. + +--*/ + +{ + pConnectionInfo->Next = pSocketInfo->Connections; + pSocketInfo->Connections = pConnectionInfo; + pConnectionInfo->OwningSocket = pSocketInfo; +} + + +LPCONNECTION_INFO +DequeueConnection( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Removes a CONNECTION_INFO from the list of connections owned by a SOCKET_INFO + +Arguments: + + pSocketInfo - owning SOCKET_INFO + pConnectionInfo - CONNECTION_INFO to remove + +Return Value: + + LPCONNECTION_INFO + Success - pointer to removed CONNECTION_INFO (should be same as + pConnectionInfo) + Failure - NULL (not expected) + +--*/ + +{ + LPCONNECTION_INFO prev = (LPCONNECTION_INFO)&pSocketInfo->Connections; + LPCONNECTION_INFO p = prev->Next; + + while (p && p != pConnectionInfo) { + prev = p; + p = p->Next; + } + + ASSERT(p == pConnectionInfo); + + prev->Next = p->Next; + p->OwningSocket = NULL; + return p; +} + + +VOID +KillConnection( + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Closes a socket belonging to a connection and cancels all outstanding + requests. The CONNECTION_INFO is deallocated + +Arguments: + + pConnectionInfo - pointer to CONNECTION_INFO to kill + +Return Value: + + None. + +--*/ + +{ + if (pConnectionInfo->Socket) { + closesocket(pConnectionInfo->Socket); + } + CancelConnectionQueue(&pConnectionInfo->ConnectQueue); + CancelConnectionQueue(&pConnectionInfo->AcceptQueue); + CancelConnectionQueue(&pConnectionInfo->ListenQueue); + CancelConnectionQueue(&pConnectionInfo->SendQueue); + DeallocateConnection(pConnectionInfo); +} + + +VOID +AbortOrTerminateConnection( + IN LPCONNECTION_INFO pConnectionInfo, + IN BYTE CompletionCode + ) + +/*++ + +Routine Description: + + Aborts or terminates a connection: closes the socket, dequeues and completes + all outstanding ECBs with relevant code and deallocates the CONNECTION_INFO + structure + + The CONNECTION_INFO must NOT be queued on a SOCKET_INFO when this routine + is called + +Arguments: + + pConnectionInfo - pointer to CONNECTION_INFO to kill + CompletionCode - completion code to put in pending ECBs + +Return Value: + + None. + +--*/ + +{ + if (pConnectionInfo->Socket) { + closesocket(pConnectionInfo->Socket); + } + AbortQueue(&pConnectionInfo->ConnectQueue, CompletionCode); + AbortQueue(&pConnectionInfo->AcceptQueue, CompletionCode); + AbortQueue(&pConnectionInfo->ListenQueue, CompletionCode); + AbortQueue(&pConnectionInfo->SendQueue, CompletionCode); + DeallocateConnection(pConnectionInfo); +} + + +VOID +CheckPendingSpxRequests( + BOOL *pfOperationPerformed + ) + +/*++ + +Routine Description: + + Checks the open non-blocking SPX sockets for: + + errors + outgoing established connections (connect) + incoming established connections (listen/accept) + data to receive (recv) + send completions (send) + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + LPSOCKET_INFO pSocketInfo; + + *pfOperationPerformed = FALSE ; + + RequestMutex(); + pSocketInfo = SocketList; + while (pSocketInfo) { + if (pSocketInfo->SpxSocket) { + + LPCONNECTION_INFO pConnectionInfo; + + pConnectionInfo = pSocketInfo->Connections; + while (pConnectionInfo) { + + LPCONNECTION_INFO next; + + // + // pluck out the Next field now, in case this CONNECTION_INFO + // is destroyed as the result of an error + // + + next = pConnectionInfo->Next; + + // + // if this connection has an active socket or we have issued + // SPXListenForConnection against the socket then check the + // state + // + + if (pConnectionInfo->Socket + || (pConnectionInfo->State == CI_WAITING)) { + + SOCKET sock; + BOOL readable; + BOOL writeable; + BOOL sockError; + + CheckSelectRead(pSocketInfo, + pConnectionInfo, + &readable); + + CheckSelectWrite(pSocketInfo, + pConnectionInfo, + &writeable); + + sock = pConnectionInfo->Socket + ? pConnectionInfo->Socket + : pSocketInfo->Socket + ; + + if (CheckSocketState(sock, &readable, &writeable, &sockError)) { + if (!sockError) { + + if (readable) { + AsyncReadAction(pSocketInfo, + pConnectionInfo, + pfOperationPerformed); + } + if (writeable) { + AsyncWriteAction(pSocketInfo, + pConnectionInfo, + pfOperationPerformed); + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CheckPendingSpxRequests: socket %x has error. Connection %08x state %d\n", + sock, + pConnectionInfo, + pConnectionInfo->State + )); + + // + // irrespective of the error, we just abort any + // connection that gets an error + // + + DequeueConnection(pConnectionInfo->OwningSocket, + pConnectionInfo + ); + AbortOrTerminateConnection(pConnectionInfo, + ECB_CC_CONNECTION_ABORTED + ); + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CheckPendingSpxRequests: CheckSocketState returns %d\n", + WSAGetLastError() + )); + + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CheckPendingSpxRequests: connection %04x (%08x) in weird state?\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + } + pConnectionInfo = next; + } + } + pSocketInfo = pSocketInfo->Next; + } + ReleaseMutex(); +} + + +PRIVATE +BOOL +CheckSocketState( + IN SOCKET Socket, + OUT LPBOOL Readable, + OUT LPBOOL Writeable, + OUT LPBOOL Error + ) + +/*++ + +Routine Description: + + Given a socket descriptor, checks to see if it is in one of the following + states: + + readable - if waiting for a connection, connection has been made + else if established, data is ready to be received + + writeable - if waiting to make a connection, connection has been + made, else if established, we can send data on this + socket + + error - some error has occurred on the socket + +Arguments: + + Socket - socket descriptor to check + Readable - returned TRUE if readable + Writeable - returned TRUE if writeable + Error - returned TRUE if error on socket + +Return Value: + + BOOL + TRUE - contents of Readable, Writeable and Error are valid + FALSE - an error occurred performing the select + +--*/ + +{ + fd_set errors; + fd_set reads; + fd_set writes; + int n; + static struct timeval timeout = {0, 0}; + + FD_ZERO(&errors); + FD_ZERO(&reads); + FD_ZERO(&writes); + + if (*Readable) + FD_SET(Socket, &reads); + if (*Writeable) + FD_SET(Socket, &writes); + FD_SET(Socket, &errors); + + n = select(0, &reads, &writes, &errors, &timeout); + + if (n != SOCKET_ERROR) { + *Readable = (BOOL)(reads.fd_count == 1); + *Writeable = (BOOL)(writes.fd_count == 1); + *Error = (BOOL)(errors.fd_count == 1); + return TRUE; + } else if (n) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CheckSocketState: select returns %d\n", + WSAGetLastError() + )); + + } + return FALSE; +} + + +PRIVATE +VOID +AsyncReadAction( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *ReadPerformed + ) + +/*++ + +Routine Description: + + A connection has some read action to complete - complete a pending + SPXListenForConnection or SPXListenForSequencedPacket + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + *ReadPerformed = FALSE ; + + switch (pConnectionInfo->State) { + case CI_STARTING: + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "AsyncReadAction: STARTING connection %04x (%08x) readable\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + break; + + case CI_WAITING: + if (pConnectionInfo->AcceptQueue.Head) { + CompleteAccept(pSocketInfo, pConnectionInfo); + *ReadPerformed = TRUE ; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "AsyncReadAction: connection %04x (%08x): no AcceptQueue\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + } + break; + + case CI_ESTABLISHED: + if (pSocketInfo->ListenQueue.Head) { + CompleteReceive(pSocketInfo, pConnectionInfo); + *ReadPerformed = TRUE ; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_WARNING, + "AsyncReadAction: connection %04x (%08x): no ListenQueue\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + } + break; + + case CI_TERMINATING: + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "AsyncReadAction: TERMINATING connection %04x (%08x) readable\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + break; + } +} + + +PRIVATE +VOID +AsyncWriteAction( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *WritePerformed + ) + +/*++ + +Routine Description: + + A connection has some write action to complete - complete a pending + SPXEstablishConnection or SPXSendSequencedPacket + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + *WritePerformed = FALSE ; + + switch (pConnectionInfo->State) { + case CI_STARTING: + if (pConnectionInfo->ConnectQueue.Head) { + CompleteConnect(pSocketInfo, pConnectionInfo); + *WritePerformed = TRUE ; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "AsyncWriteAction: connection %04x (%08x): no ConnectQueue\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + } + break; + + case CI_WAITING: + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "AsyncWriteAction: WAITING connection %04x (%08x) is writeable\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + break; + + case CI_ESTABLISHED: + if (pConnectionInfo->SendQueue.Head) { + CompleteSend(pSocketInfo, pConnectionInfo); + *WritePerformed = TRUE ; + } else { +/* + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_WARNING, + "AsyncWriteAction: connection %04x (%08x): no SendQueue\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); +*/ + } + break; + + case CI_TERMINATING: + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "AsyncWriteAction: TERMINATING connection %04x (%08x) writeable\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + break; + } +} + +PRIVATE +VOID +CheckSelectRead( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *CheckRead + ) + +/*++ + +Routine Description: + + See if want to check for Read readiness in select statement. + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + *CheckRead = FALSE ; + + switch (pConnectionInfo->State) + { + case CI_WAITING: + + if (pConnectionInfo->AcceptQueue.Head) + *CheckRead = TRUE ; + break; + + case CI_ESTABLISHED: + + if (pSocketInfo->ListenQueue.Head) + *CheckRead = TRUE ; + break; + + default: + + break; + } +} + + +PRIVATE +VOID +CheckSelectWrite( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo, + OUT BOOL *CheckWrite + ) + +/*++ + +Routine Description: + + See if want to check for Write readiness in select statement. + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + *CheckWrite = FALSE ; + + switch (pConnectionInfo->State) + { + + case CI_STARTING: + + if (pConnectionInfo->ConnectQueue.Head) + *CheckWrite = TRUE ; + break; + + case CI_ESTABLISHED: + + if (pConnectionInfo->SendQueue.Head) + *CheckWrite = TRUE ; + break; + + default: + + break; + } +} + + + +PRIVATE +VOID +CompleteAccept( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Complete a SPXListenForConnection + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + SOCKET conn; + SOCKADDR_IPX remoteAddress; + int addressLength = sizeof(remoteAddress); + LPXECB pXecb = pConnectionInfo->AcceptQueue.Head; + BOOL true = TRUE; + int rc; + + conn = accept(pSocketInfo->Socket, (LPSOCKADDR)&remoteAddress, &addressLength); + if (conn != SOCKET_ERROR) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CompleteAccept: connection %04x (%08x) socket=%x\n", + pConnectionInfo->ConnectionId, + pConnectionInfo, + conn + )); + + // + // we want to receive the frame headers from this socket + // + + rc = setsockopt(conn, + NSPROTO_IPX, + IPX_RECVHDR, + (char FAR*)&true, + sizeof(true) + ); + rc = !SOCKET_ERROR; + if (rc != SOCKET_ERROR) { + + // + // update the CONNECTION_INFO structure with the actual socket + // identifier and set the connection state to established + // + + pConnectionInfo->Socket = conn; + pConnectionInfo->State = CI_ESTABLISHED; + + // + // update the app's ECB with the connection ID + // + + SPX_ECB_CONNECTION_ID(pXecb->Ecb) = pConnectionInfo->ConnectionId; + + // + // and with the partner address info + // + + CopyMemory(&pXecb->Ecb->DriverWorkspace, + &remoteAddress.sa_netnum, + sizeof(pXecb->Ecb->DriverWorkspace) + ); + + // + // fill in the immediate address field + // + + CopyMemory(&pXecb->Ecb->ImmediateAddress, + &remoteAddress.sa_nodenum, + sizeof(pXecb->Ecb->ImmediateAddress) + ); + + // + // remove the XECB from AcceptQueue and complete the SPXListenForConnection ECB + // + + DequeueEcb(pXecb, &pConnectionInfo->AcceptQueue); + + IPXDUMPECB((pXecb->Ecb, + HIWORD(pXecb->EcbAddress), + LOWORD(pXecb->EcbAddress), + ECB_TYPE_SPX, + FALSE, + FALSE, + IS_PROT_MODE(pXecb) + )); + + CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS); + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CompleteAccept: setsockopt(IPX_RECVHDR) returns %d\n", + WSAGetLastError() + )); + + // + // BUGBUG: value? + // + + closesocket(conn); + DequeueEcb(pXecb, &pConnectionInfo->AcceptQueue); + DequeueConnection(pSocketInfo, pConnectionInfo); + DeallocateConnection(pConnectionInfo); + CompleteOrQueueEcb(pXecb, ECB_CC_CONNECTION_ABORTED); + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CompleteAccept: accept() returns %d\n", + WSAGetLastError() + )); + + } +} + + +PRIVATE +VOID +CompleteReceive( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Complete a SPXListenForSequencedPacket + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb; + int rc; + BOOL conn_q; + LPXECB_QUEUE pQueue; + int len; + BOOL completeRequest; + BYTE status; + + // + // receive packets while there are listen ECBs and data waiting + // + + while (1) { + if (pConnectionInfo->ListenQueue.Head) { + pQueue = &pConnectionInfo->ListenQueue; + pXecb = pConnectionInfo->ListenQueue.Head; + conn_q = TRUE; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CompleteReceive: XECB %08x from CONNECTION_INFO %08x\n", + pXecb, + pConnectionInfo + )); + + + } else if (pSocketInfo->ListenQueue.Head) { + pQueue = &pSocketInfo->ListenQueue; + pXecb = pSocketInfo->ListenQueue.Head; + conn_q = FALSE; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CompleteReceive: XECB %08x from SOCKET_INFO %08x\n", + pXecb, + pSocketInfo + )); + + } else { + break; + } + + rc = recv(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, 0); + + if (rc != SOCKET_ERROR) { + + len = rc; + status = ECB_CC_SUCCESS; + completeRequest = TRUE; + + } else { + rc = WSAGetLastError(); + if (rc == WSAEMSGSIZE) { + len = pXecb->Length; + status = ECB_CC_PACKET_OVERFLOW; + completeRequest = TRUE; + } else { + completeRequest = FALSE; + + // + // if no data to receive, quit the loop (don't go down error path) + // + + if (rc == WSAEWOULDBLOCK) { + break; + } + } + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CompleteReceive: error %d on socket %08x (CID %04x)\n", + rc, + pConnectionInfo->Socket, + pConnectionInfo->ConnectionId + )); + + DUMPXECB(pXecb); + + } + + if( rc == WSAEDISCON ) { + + // + // handle the disconnect case - we still need to complete the + // ECB. + // + + LPSPX_PACKET pPacket = (LPSPX_PACKET)pXecb->Buffer; + + status = ECB_CC_SUCCESS; + + + pPacket->DestinationConnectId = pConnectionInfo->ConnectionId; + pPacket->SourceConnectId = pConnectionInfo->RemoteConnectionId; + pPacket->DataStreamType = SPX_DS_TERMINATE ; + pPacket->Checksum = 0xffff; + pPacket->Length = L2BW(SPX_HEADER_LENGTH); + pPacket->TransportControl = 0; + pPacket->PacketType = 5; + + pXecb->Length = SPX_HEADER_LENGTH ; + ScatterData(pXecb); + + DequeueEcb(pXecb, pQueue); + + // + // Put the remote node address in the ECB's immediate address + // field + // + + CopyMemory(pXecb->Ecb->ImmediateAddress, + pConnectionInfo->RemoteNode, + sizeof(pXecb->Ecb->ImmediateAddress) + ); + + CompleteOrQueueIo(pXecb, status); + + DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo); + AbortOrTerminateConnection(pConnectionInfo, ECB_CC_CONNECTION_ABORTED); + break ; + + } + else if (completeRequest) { + +#if SPX_HACK + if (pConnectionInfo->Flags & CF_1ST_RECEIVE) { + pConnectionInfo->Flags &= ~CF_1ST_RECEIVE; + ModifyFirstReceive(pXecb->Data, &len, pSocketInfo->SocketNumber, pConnectionInfo->Socket); + } +#endif + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CompleteReceive: recv() on socket %#x returns %d bytes (Addr=%08x)\n", + pConnectionInfo->Socket, + len, + pXecb->Data + )); + + IPXDUMPDATA((pXecb->Data, 0, 0, FALSE, (WORD)len)); + + pXecb->Length -= len; + pXecb->ActualLength += len; + pXecb->Data += len; + if (pXecb->ActualLength >= SPX_HEADER_LENGTH) { + if (pXecb->Flags & XECB_FLAG_FIRST_RECEIVE) { + + LPSPX_PACKET pPacket = (LPSPX_PACKET)pXecb->Buffer; + + // + // record in the SPX header the local connection id we invented + // + + pPacket->DestinationConnectId = pConnectionInfo->ConnectionId; + + // + // record the actual frame length from the header + // + + pXecb->FrameLength = B2LW(((LPSPX_PACKET)pXecb->Buffer)->Length); + pXecb->Flags &= ~XECB_FLAG_FIRST_RECEIVE; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CompleteReceive: FrameLength=%x (%d)\n", + pXecb->FrameLength, + pXecb->FrameLength + )); + + } + + // + // if we received all the data in the packet (according to length + // field in the SPX header) OR we ran out of buffer space, remove + // the ECB from its queue and complete it + // + + if (!pXecb->Length || (pXecb->ActualLength == pXecb->FrameLength)) { + if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) { + + // + // update the XECB.Length field to reflect the amount of + // data received and copy it to the fragmented buffers + // in VDM. do not overflow buffer if FrameLength turns + // out to be larger than we expect. + // + + pXecb->Length = min(pXecb->FrameLength, + pXecb->ActualLength); + ScatterData(pXecb); + } + DequeueEcb(pXecb, pQueue); + + // DUMPXECB(pXecb); + + + IPXDUMPECB((pXecb->Ecb, + HIWORD(pXecb->EcbAddress), + LOWORD(pXecb->EcbAddress), + ECB_TYPE_SPX, + TRUE, + TRUE, + IS_PROT_MODE(pXecb) + )); + + // + // Put the remote node address in the ECB's immediate address + // field + // + + CopyMemory(pXecb->Ecb->ImmediateAddress, + pConnectionInfo->RemoteNode, + sizeof(pXecb->Ecb->ImmediateAddress) + ); + CompleteOrQueueIo(pXecb, status); + } else { + + // + // partial receive. If the listen ECB came off the socket + // queue then put it on the connection queue: this is the + // ECB that will be used for this connection until all data + // received or we get an error + // + + if (!conn_q) { + DequeueEcb(pXecb, &pSocketInfo->ListenQueue); + QueueEcb(pXecb, + &pConnectionInfo->ListenQueue, + CONNECTION_LISTEN_QUEUE + ); + } + + // + // not enough data to satisfy read: don't continue yet + // + + break; + } + } + } else { + + // + // error occurred - abort the connection + // + + if (!conn_q) { + DequeueEcb(pXecb, &pSocketInfo->ListenQueue); + QueueEcb(pXecb, + &pConnectionInfo->ListenQueue, + CONNECTION_LISTEN_QUEUE + ); + } + DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo); + AbortOrTerminateConnection(pConnectionInfo, ECB_CC_CONNECTION_ABORTED); + + // + // don't continue in this case + // + + break; + } + } +} + + +PRIVATE +VOID +CompleteConnect( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Complete a SPXEstablishConnection + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = pConnectionInfo->ConnectQueue.Head; +/* + LPSPX_PACKET pPacket; + + // + // the connection ID also appears in the first segment of the establish + // ECB + // + + pPacket = (LPSPX_PACKET)GET_FAR_POINTER(&ECB_FRAGMENT(pXecb->Ecb, 0)->Address, + IS_PROT_MODE(pXecb) + ); + pPacket->Checksum = 0xffff; + pPacket->Length = L2BW(SPX_HEADER_LENGTH); + pPacket->TransportControl = 0; + pPacket->PacketType = 5; + pPacket->Source.Socket = pSocketInfo->SocketNumber; + pPacket->ConnectionControl = 0xc0; + pPacket->DataStreamType = 0; + pPacket->SourceConnectId = pConnectionInfo->ConnectionId; + pPacket->DestinationConnectId = 0xffff; + pPacket->SequenceNumber = 0; + pPacket->AckNumber = 0; + pPacket->AllocationNumber = 0; +*/ + + pConnectionInfo->State = CI_ESTABLISHED; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CompleteConnect: connection %04x (%08x) completed\n", + pConnectionInfo->ConnectionId, + pConnectionInfo + )); + + DUMPCONN(pConnectionInfo); + + DequeueEcb(pXecb, &pConnectionInfo->ConnectQueue); + + IPXDUMPECB((pXecb->Ecb, + HIWORD(pXecb->EcbAddress), + LOWORD(pXecb->EcbAddress), + ECB_TYPE_SPX, + TRUE, + TRUE, + IS_PROT_MODE(pXecb) + )); + + CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS); +} + + +PRIVATE +VOID +CompleteSend( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ) + +/*++ + +Routine Description: + + Complete a SPXSendSequencedPacket + +Arguments: + + pSocketInfo - pointer to SOCKET_INFO + pConnectionInfo - pointer to CONNECTION_INFO + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = pConnectionInfo->SendQueue.Head; + int rc; + BYTE status; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "CompleteSend: sending %d (0x%x) bytes from %08x\n", + pXecb->Length, + pXecb->Length, + pXecb->Data + )); + + IPXDUMPECB((pXecb->Ecb, + HIWORD(pXecb->EcbAddress), + LOWORD(pXecb->EcbAddress), + ECB_TYPE_SPX, + TRUE, + TRUE, + IS_PROT_MODE(pXecb) + )); + + rc = send(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, 0); + if (rc == pXecb->Length) { + + // + // all data sent + // + + status = ECB_CC_SUCCESS; + } else if (rc == SOCKET_ERROR) { + rc = WSAGetLastError(); + if (rc == WSAEWOULDBLOCK) { + + // + // huh??? + // + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CompleteSend: send() returns WSAEWOODBLOCK??\n" + )); + + // + // leave ECB on queue + // + + return; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "CompleteSend: send() returns %d\n", + rc + )); + + // + // BUGBUG: abort connection? + // + + status = ECB_CC_CONNECTION_ABORTED; + } + } else { + + // + // partial data sent. Update the buffer pointer and length fields + // and leave this ECB at the head of the send queue + // + + pXecb->Data += rc; + pXecb->Length -= (WORD)rc; + return; + } + DequeueEcb(pXecb, &pConnectionInfo->SendQueue); + CompleteOrQueueIo(pXecb, status); +} + +#if SPX_HACK + +PRIVATE +VOID +ModifyFirstReceive( + LPBYTE Buffer, + LPDWORD pLength, + WORD SocketNumber, + SOCKET Socket + ) +{ + WORD len = *(LPWORD)pLength; + + if ((*(ULPWORD)Buffer != 0xffff) && (*(ULPWORD)(Buffer+2) != L2BW(len))) { + + LPSPX_PACKET packet; + SOCKADDR_IPX remote; + int rc; + int remlen; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "ModifyFirstReceive: Modifying: Buffer=%08x Length=%04x SocketNumber=%04x Socket=%08x\n", + Buffer, + len, + B2LW(SocketNumber), + Socket + )); + + MoveMemory(Buffer+42, Buffer, len); + packet = (LPSPX_PACKET)Buffer; + packet->Checksum = 0xffff; + packet->Length = L2BW(42+len); + packet->TransportControl = 0; + packet->PacketType = 5; + CopyMemory((LPVOID)&packet->Destination, + (LPVOID)&MyInternetAddress.sa_netnum, + sizeof(INTERNET_ADDRESS) + ); + packet->Destination.Socket = SocketNumber; + rc = getpeername(Socket, (LPSOCKADDR)&remote, &remlen); + if (rc != SOCKET_ERROR) { + CopyMemory((LPVOID)&packet->Source, + (LPVOID)&remote.sa_netnum, + sizeof(NETWARE_ADDRESS) + ); + } else { + ZeroMemory((LPVOID)&packet->Source, sizeof(NETWARE_ADDRESS)); + } + packet->ConnectionControl = 0x40; + packet->DataStreamType = 0; + packet->SourceConnectId = 0; + packet->DestinationConnectId = 0; + packet->SequenceNumber = 0; + packet->AckNumber = 0; + packet->AllocationNumber = 0; + *pLength += 42; + } +} + +#endif |