From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/nw/vwipxspx/dll/makefile | 6 + private/nw/vwipxspx/dll/socket.c | 2309 ++++++++++++++++++++++++++ private/nw/vwipxspx/dll/socket.h | 318 ++++ private/nw/vwipxspx/dll/sources | 64 + private/nw/vwipxspx/dll/util.c | 2966 ++++++++++++++++++++++++++++++++++ private/nw/vwipxspx/dll/util.h | 233 +++ private/nw/vwipxspx/dll/vw.h | 57 + private/nw/vwipxspx/dll/vwasync.c | 167 ++ private/nw/vwipxspx/dll/vwasync.h | 27 + private/nw/vwipxspx/dll/vwdebug.c | 825 ++++++++++ private/nw/vwipxspx/dll/vwdebug.h | 184 +++ private/nw/vwipxspx/dll/vwdll.c | 502 ++++++ private/nw/vwipxspx/dll/vwdll.h | 27 + private/nw/vwipxspx/dll/vwdos.c | 1502 +++++++++++++++++ private/nw/vwipxspx/dll/vwinapi.c | 1185 ++++++++++++++ private/nw/vwipxspx/dll/vwinapi.h | 186 +++ private/nw/vwipxspx/dll/vwint.h | 146 ++ private/nw/vwipxspx/dll/vwipx.c | 839 ++++++++++ private/nw/vwipxspx/dll/vwipx.h | 122 ++ private/nw/vwipxspx/dll/vwipxspx.def | 64 + private/nw/vwipxspx/dll/vwipxspx.h | 586 +++++++ private/nw/vwipxspx/dll/vwipxspx.rc | 11 + private/nw/vwipxspx/dll/vwmisc.c | 74 + private/nw/vwipxspx/dll/vwmisc.h | 27 + private/nw/vwipxspx/dll/vwspx.c | 1339 +++++++++++++++ private/nw/vwipxspx/dll/vwspx.h | 62 + private/nw/vwipxspx/dll/vwvdm.h | 151 ++ 27 files changed, 13979 insertions(+) create mode 100644 private/nw/vwipxspx/dll/makefile create mode 100644 private/nw/vwipxspx/dll/socket.c create mode 100644 private/nw/vwipxspx/dll/socket.h create mode 100644 private/nw/vwipxspx/dll/sources create mode 100644 private/nw/vwipxspx/dll/util.c create mode 100644 private/nw/vwipxspx/dll/util.h create mode 100644 private/nw/vwipxspx/dll/vw.h create mode 100644 private/nw/vwipxspx/dll/vwasync.c create mode 100644 private/nw/vwipxspx/dll/vwasync.h create mode 100644 private/nw/vwipxspx/dll/vwdebug.c create mode 100644 private/nw/vwipxspx/dll/vwdebug.h create mode 100644 private/nw/vwipxspx/dll/vwdll.c create mode 100644 private/nw/vwipxspx/dll/vwdll.h create mode 100644 private/nw/vwipxspx/dll/vwdos.c create mode 100644 private/nw/vwipxspx/dll/vwinapi.c create mode 100644 private/nw/vwipxspx/dll/vwinapi.h create mode 100644 private/nw/vwipxspx/dll/vwint.h create mode 100644 private/nw/vwipxspx/dll/vwipx.c create mode 100644 private/nw/vwipxspx/dll/vwipx.h create mode 100644 private/nw/vwipxspx/dll/vwipxspx.def create mode 100644 private/nw/vwipxspx/dll/vwipxspx.h create mode 100644 private/nw/vwipxspx/dll/vwipxspx.rc create mode 100644 private/nw/vwipxspx/dll/vwmisc.c create mode 100644 private/nw/vwipxspx/dll/vwmisc.h create mode 100644 private/nw/vwipxspx/dll/vwspx.c create mode 100644 private/nw/vwipxspx/dll/vwspx.h create mode 100644 private/nw/vwipxspx/dll/vwvdm.h (limited to 'private/nw/vwipxspx/dll') diff --git a/private/nw/vwipxspx/dll/makefile b/private/nw/vwipxspx/dll/makefile new file mode 100644 index 000000000..f0db8e4a7 --- /dev/null +++ b/private/nw/vwipxspx/dll/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def 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 diff --git a/private/nw/vwipxspx/dll/socket.h b/private/nw/vwipxspx/dll/socket.h new file mode 100644 index 000000000..facf7c6f7 --- /dev/null +++ b/private/nw/vwipxspx/dll/socket.h @@ -0,0 +1,318 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + socket.h + +Abstract: + + Contains macros, prototypes and structures for socket.c + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +#define ARBITRARY_CONNECTION_NUMBER 0x6c8e + +// +// forward declarations +// + +typedef struct _FIFO *LPFIFO; +typedef struct _XECB *LPXECB; +typedef struct _XECB_QUEUE *LPXECB_QUEUE; +typedef struct _SOCKET_INFO* LPSOCKET_INFO; +typedef struct _CONNECTION_INFO *LPCONNECTION_INFO; + +// +// FIFO - standard single-linked FIFO queue structure +// + +typedef struct _FIFO { + LPVOID Head; + LPVOID Tail; +} FIFO; + +// +// function type for cancelling XECB/ECB +// + +typedef BYTE (*ECB_CANCEL_ROUTINE)(LPXECB); + +// +// QUEUE_ID - indicator of which queue an ECB is on +// + +typedef enum { + NO_QUEUE = 0x10cadd1e, + ASYNC_COMPLETION_QUEUE = 0xCC5055C0, // arbitrary numbers make life interesting + TIMER_QUEUE, + SOCKET_LISTEN_QUEUE, + SOCKET_SEND_QUEUE, + SOCKET_HEADER_QUEUE, // special queue for small ECBs that cannot hold data + CONNECTION_CONNECT_QUEUE, + CONNECTION_ACCEPT_QUEUE, + CONNECTION_SEND_QUEUE, + CONNECTION_LISTEN_QUEUE +} QUEUE_ID; + +// +// XECB - our copy of the ECB (IPX or AES) +// + +typedef struct _XECB { + LPXECB Next; + LPECB Ecb; // points to ECB in DOS memory + ECB_ADDRESS EcbAddress; // segmented address of ECB in DOS memory + ESR_ADDRESS EsrAddress; // Event Service Routine in DOS memory + LPBYTE Buffer; // address of 32-bit buffer + LPBYTE Data; // moveable data pointer + WORD FrameLength; // actual size of frame (from IPX/SPX header) + WORD ActualLength; // same as FrameLength. Not decremented + WORD Length; // length of 32-bit buffer + WORD Ticks; // for AES + WORD SocketNumber; // number of owning socket + WORD Owner; // owning DOS Task ID + DWORD TaskId; // owning Windows Task ID + DWORD Flags; // see below + QUEUE_ID QueueId; // identifies the queue for quick location + LPVOID OwningObject; // which SOCKET_INFO or CONNECTION_INFO the queue is on + DWORD RefCount; // the dreaded reference count +} XECB; + +// +// XECB flags +// + +#define XECB_FLAG_AES 0x00000000 +#define XECB_FLAG_IPX 0x00000001 +#define XECB_FLAG_TEMPORARY_SOCKET 0x00000002 +#define XECB_FLAG_BUFFER_ALLOCATED 0x00000004 +#define XECB_FLAG_LISTEN 0x00000008 +#define XECB_FLAG_SEND 0x00000010 +#define XECB_FLAG_TIMER 0x00000020 +#define XECB_FLAG_ASYNC 0x00000040 +#define XECB_FLAG_FIRST_RECEIVE 0x00000080 +#define XECB_FLAG_SPX 0x00000100 +#define XECB_FLAG_PROTMODE 0x00000200 + +#define IS_PROT_MODE(p) (((p)->Flags & XECB_FLAG_PROTMODE) ? TRUE : FALSE) + +// +// XECB_QUEUE - queue of XECBs +// + +typedef struct _XECB_QUEUE { + LPXECB Head; + LPXECB Tail; +} XECB_QUEUE; + +// +// SOCKET_INFO - maintains info about IPX sockets +// + +typedef struct _SOCKET_INFO { + LPSOCKET_INFO Next; + WORD SocketNumber; // big-endian socket (bound port) + WORD Owner; // DOS PDB + DWORD TaskId; // Windows owner + SOCKET Socket; // the WinSock socket handle + DWORD Flags; + + // + // BUGBUG: these next 2 fields can be flags + // + + BOOL LongLived; // TRUE if keep-alive when app dies + BOOL SpxSocket; // TRUE if socket opened for SPX + + // + // BUGBUG: these next 2 fields can we WORD or even BYTE (do we even need + // them - only used to tell AES if pending requests - it can look at the + // queue heads? + // + + DWORD PendingSends; // used by cancel + DWORD PendingListens; // used by cancel + + // + // ListenQueue is used for IPXListenForPacket and SPXListenForSequencedPacket + // + + XECB_QUEUE ListenQueue; // pool of listening ECBs against this socket + + // + // SendQueue is used by IPX for IPXSendPacket + // + + XECB_QUEUE SendQueue; // queue of pending send ECBs against this socket + + // + // HeaderQueue is used to hold small ECBs that can only take header info. + // We have this separate queue to make sure that we do not put ECBs that + // really cant accept any data into the Listen Queue. + // + + XECB_QUEUE HeaderQueue; // pool of header ECBs against this socket + + LPCONNECTION_INFO Connections; +} SOCKET_INFO; + +#define SOCKET_FLAG_LISTENING 0x00000001 +#define SOCKET_FLAG_SENDING 0x00000002 +#define SOCKET_FLAG_TEMPORARY 0x80000000 + +// +// CONNECTION_INFO - maintains info about SPX sockets +// + +typedef struct _CONNECTION_INFO { + LPCONNECTION_INFO Next; // next CONNECTION_INFO by OwningSocket + LPCONNECTION_INFO List; // all CONNECTION_INFO are linked together + LPSOCKET_INFO OwningSocket; // back-pointer to SOCKET_INFO + SOCKET Socket; // handle to socket + DWORD TaskId; // identifies windows task/owner + WORD ConnectionId; // analogous to SocketNumber + BYTE Flags; + BYTE State; + XECB_QUEUE ConnectQueue; // outgoing connections being made + XECB_QUEUE AcceptQueue; // waiting for incoming connections + XECB_QUEUE SendQueue; // packet sends on this connection + XECB_QUEUE ListenQueue; // partially complete receive + BYTE RemoteNode[6]; + WORD RemoteConnectionId; +} CONNECTION_INFO; + +// +// CONNECTION_INFO Flag field values +// + +#define CF_1ST_RECEIVE 0x80 // hack-o-rama till NWLink timing problem fixed + +// +// CONNECTION_INFO State field values +// + +#define CI_WAITING 0x01 +#define CI_STARTING 0x02 +#define CI_ESTABLISHED 0x03 +#define CI_TERMINATING 0x04 + +// +// one-line function macros +// + +#define AllocateSocket() (LPSOCKET_INFO)LocalAlloc(LPTR, sizeof(SOCKET_INFO)) +#define DeallocateSocket(p) FREE_OBJECT(p) + +// +// SocketType parameter for CreateSocket +// + +typedef enum { + SOCKET_TYPE_IPX, + SOCKET_TYPE_SPX +} SOCKET_TYPE; + +// +// function prototypes +// + +int +CreateSocket( + IN SOCKET_TYPE SocketType, + IN OUT ULPWORD pSocketNumber, + OUT SOCKET* pSocket + ); + +LPSOCKET_INFO +AllocateTemporarySocket( + VOID + ); + +VOID +QueueSocket( + IN LPSOCKET_INFO pSocketInfo + ); + +LPSOCKET_INFO +DequeueSocket( + IN LPSOCKET_INFO pSocketInfo + ); + +LPSOCKET_INFO +FindSocket( + IN WORD SocketNumber + ); + +LPSOCKET_INFO +FindActiveSocket( + IN LPSOCKET_INFO pSocketInfo + ); + +int +ReopenSocket( + LPSOCKET_INFO pSocketInfo + ); + +VOID +KillSocket( + IN LPSOCKET_INFO pSocketInfo + ); + +VOID +KillShortLivedSockets( + IN WORD Owner + ); + +LPCONNECTION_INFO +AllocateConnection( + LPSOCKET_INFO pSocketInfo + ); + +VOID +DeallocateConnection( + IN LPCONNECTION_INFO pConnectionInfo + ); + +LPCONNECTION_INFO +FindConnection( + IN WORD ConnectionId + ); + +VOID +QueueConnection( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ); + +LPCONNECTION_INFO +DequeueConnection( + IN LPSOCKET_INFO pSocketInfo, + IN LPCONNECTION_INFO pConnectionInfo + ); + +VOID +KillConnection( + IN LPCONNECTION_INFO pConnectionInfo + ); + +VOID +AbortOrTerminateConnection( + IN LPCONNECTION_INFO pConnectionInfo, + IN BYTE CompletionCode + ); + +VOID +CheckPendingSpxRequests( + BOOL *pfOperationPerformed + ); diff --git a/private/nw/vwipxspx/dll/sources b/private/nw/vwipxspx/dll/sources new file mode 100644 index 000000000..2cb76d030 --- /dev/null +++ b/private/nw/vwipxspx/dll/sources @@ -0,0 +1,64 @@ +!IF 0 + +Copyright (c) 1989-1991 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +History: + + Created 12-Sep-1991 by Richard L Firth (rfirth) + from template created 12-Apr-1990 by Steve Wood (stevewo) + + +NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=nw +MINORCOMP=vwipxspx + +TARGETNAME=vwipxspx +TARGETPATH=$(BASEDIR)\public\sdk\lib +TARGETTYPE=DYNLINK +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\ntvdm.lib \ + $(BASEDIR)\public\sdk\lib\*\wsock32.lib + +DLLENTRY=VwDllEntryPoint +DLLBASE=0x03200000 + +USE_CRTDLL=1 + +MSC_WARNING_LEVEL=/W3 /WX + +INCLUDES=.;..\..\inc;$(BASEDIR)\private\inc;$(BASEDIR)\private\mvdm\vdd\h + +SOURCES=vwdll.c \ + vwipx.c \ + vwspx.c \ + vwasync.c \ + vwmisc.c \ + vwdebug.c \ + socket.c \ + vwinapi.c \ + vwdos.c \ + vwipxspx.rc \ + util.c + +C_DEFINES=-DMAX_OPEN_SOCKETS=150 -DSPX_HACK=1 -DREUSEADDR=1 + +!ifdef MARS_PCH +PRECOMPILED_INCLUDE=vw.h +PRECOMPILED_PCH=vw.pch +PRECOMPILED_OBJ=vw.obj +!endif 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; +} diff --git a/private/nw/vwipxspx/dll/util.h b/private/nw/vwipxspx/dll/util.h new file mode 100644 index 000000000..d75781952 --- /dev/null +++ b/private/nw/vwipxspx/dll/util.h @@ -0,0 +1,233 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + util.h + +Abstract: + + Contains macros, prototypes and structures for util.c + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +// +// external data +// + +extern CRITICAL_SECTION SerializationCritSec; + +// +// one-line function macros +// + +#define RequestMutex() EnterCriticalSection(&SerializationCritSec) +#define ReleaseMutex() LeaveCriticalSection(&SerializationCritSec) + +// +// function prototypes +// + +int +GetInternetAddress( + IN OUT LPSOCKADDR_IPX InternetAddress + ); + +int +GetMaxPacketSize( + OUT LPWORD MaxPacketSize + ); + +LPXECB +RetrieveEcb( + IN BYTE Type + ); + +LPXECB +RetrieveXEcb( + IN BYTE Type, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +ScheduleEvent( + IN LPXECB pXecb, + IN WORD Ticks + ); + +VOID +ScanTimerList( + VOID + ); + +BYTE +CancelTimerEvent( + IN LPXECB pXecb + ); + +VOID +CancelTimedEvents( + IN WORD SocketNumber, + IN WORD Owner, + IN DWORD TaskId + ); + +BYTE +CancelAsyncEvent( + IN LPXECB pXecb + ); + +BYTE +CancelSocketEvent( + IN LPXECB pXecb + ); + +BYTE +CancelConnectionEvent( + IN LPXECB pXecb + ); + +VOID +QueueEcb( + IN LPXECB pXecb, + IN LPXECB_QUEUE Queue, + IN QUEUE_ID QueueId + ); + +LPXECB +DequeueEcb( + IN LPXECB pXecb, + IN LPXECB_QUEUE Queue + ); + +VOID +CancelSocketQueue( + IN LPXECB_QUEUE pXecbQueue + ); + +VOID +CancelConnectionQueue( + IN LPXECB_QUEUE pXecbQueue + ); + +VOID +AbortQueue( + IN LPXECB_QUEUE pXecbQueue, + IN BYTE CompletionCode + ); + +VOID +AbortConnectionEvent( + IN LPXECB pXecb, + IN BYTE CompletionCode + ); + +VOID +StartIpxSend( + IN LPXECB pEcb, + IN LPSOCKET_INFO pSocketInfo + ); + +BOOL +GetIoBuffer( + IN OUT LPXECB pXecb, + IN BOOL Send, + IN WORD HeaderLength + ); + +VOID +GatherData( + IN LPXECB pXecb, + IN WORD HeaderLength + ); + +VOID +ScatterData( + IN LPXECB pXecb + ); + +VOID +IpxReceiveFirst( + IN LPXECB pXecb, + IN LPSOCKET_INFO pSocketInfo + ); + +VOID +IpxReceiveNext( + IN LPSOCKET_INFO pSocketInfo + ); + +VOID +IpxSendNext( + IN LPSOCKET_INFO pSocketInfo + ); + +VOID +CompleteOrQueueIo( + IN LPXECB pXecb, + IN BYTE CompletionCode + ); + +VOID +CompleteIo( + IN LPXECB pXecb, + IN BYTE CompletionCode + ); + +VOID +CompleteOrQueueEcb( + IN LPXECB pXecb, + IN BYTE CompletionCode + ); + +VOID +CompleteEcb( + IN LPXECB pEcb, + IN BYTE CompletionCode + ); + +VOID +EsrCallback( + VOID + ); + +VOID +VWinEsrCallback( + WORD *pSegment, + WORD *pOffset, + BYTE *pFlags + ); + +VOID +FifoAddHead( + IN LPFIFO pFifo, + IN LPFIFO pElement + ); + +VOID +FifoAdd( + IN LPFIFO pFifo, + IN LPFIFO pElement + ); + +LPFIFO +FifoRemove( + IN LPFIFO pFifo, + IN LPFIFO pElement + ); + +LPFIFO +FifoNext( + IN LPFIFO pFifo + ); diff --git a/private/nw/vwipxspx/dll/vw.h b/private/nw/vwipxspx/dll/vw.h new file mode 100644 index 000000000..e14ec83e0 --- /dev/null +++ b/private/nw/vwipxspx/dll/vw.h @@ -0,0 +1,57 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vw.h + +Abstract: + + Top-level include file for VWIPXSPX DLL. Pulls in all other required header + files + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +// +// all include files required by VWIPXSPX.DLL +// + +#include +#include +#include + +#include +#include +#include +#include +#define FD_SETSIZE MAX_OPEN_SOCKETS +#include +#include +#include +#include // GetVDMAddress, GetVDMPointer +#undef getMSW // BUGBUG: there's no c_getMSW in the lib!!! + +extern WORD getMSW(VOID); + +#include "vwvdm.h" +#include "vwdll.h" +#include "vwipxspx.h" +#include "vwasync.h" +#include "vwmisc.h" +#include "vwipx.h" +#include "vwspx.h" +#include "socket.h" +#include "util.h" +#include "vwdebug.h" +#include "vwinapi.h" +#include "vwint.h" diff --git a/private/nw/vwipxspx/dll/vwasync.c b/private/nw/vwipxspx/dll/vwasync.c new file mode 100644 index 000000000..686d05816 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwasync.c @@ -0,0 +1,167 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwasync.c + +Abstract: + + ntVdm netWare (Vw) IPX/SPX Functions + + Vw: The peoples' network + + Contains Asyncrhonous Event Scheduler (thread) + + Contents: + VwAesThread + (CheckPendingIpxRequests) + +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 +VOID +CheckPendingIpxRequests( + VOID + ); + +// +// global data +// + +WORD AesTickCount; + +// +// functions +// + + +DWORD +VwAesThread( + IN LPVOID Parameters + ) + +/*++ + +Routine Description: + + Provides the functionality of the Asynchronous Event Scheduler (AES) in the + Netware world: + + - updates the tick count + - completes any matured timer events + - checks any pending requests and schedules the next action + + This thread wakes up every PC tick (1/18 second) + +Arguments: + + Parameters - unused + +Return Value: + + DWORD + 0 + +--*/ + +{ + BOOL fOperationPerformed = FALSE ; + static int n = 1 ; + + UNREFERENCED_PARAMETER(Parameters); + + while (TRUE) + { + // + // we will always yield in this loop to be friendly to others, + // but occasionally we will forcefully do a non zero sleep for + // lower priority threads to run. + // + if ((n % 100) == 0) + { + Sleep(ONE_TICK) ; + n = 1 ; + } + if (!fOperationPerformed && ((n % 4) == 0)) + { + Sleep(10) ; + n++ ; + } + else + { + Sleep(0) ; + n++ ; + } + + ++AesTickCount; + ScanTimerList(); + CheckPendingIpxRequests(); + CheckPendingSpxRequests(&fOperationPerformed); + } + + return 0; // compiler-pacifier +} + + +PRIVATE +VOID +CheckPendingIpxRequests( + VOID + ) + +/*++ + +Routine Description: + + Polls the opened, active non-blocking IPX sockets to see if there is anything + to do (data to receive, availability to send, timeouts) + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + LPSOCKET_INFO pActiveSocket = NULL; + + // + // search SOCKET_INFO structures for something to do. Could do select() + // but we have most of the info anyway. We use the BFI filter mechanism + // + + while (pActiveSocket = FindActiveSocket(pActiveSocket)) { + if (pActiveSocket->Flags & SOCKET_FLAG_SENDING) { + IpxSendNext(pActiveSocket); + } + if (pActiveSocket->Flags & SOCKET_FLAG_LISTENING) { + IpxReceiveNext(pActiveSocket); + } + } +} diff --git a/private/nw/vwipxspx/dll/vwasync.h b/private/nw/vwipxspx/dll/vwasync.h new file mode 100644 index 000000000..4c4bd4779 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwasync.h @@ -0,0 +1,27 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vwipx.h + +Abstract: + + Contains function prototypes for VWASYNC.C + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +DWORD +VwAesThread( + IN LPVOID Parameters + ); diff --git a/private/nw/vwipxspx/dll/vwdebug.c b/private/nw/vwipxspx/dll/vwdebug.c new file mode 100644 index 000000000..5aa94c933 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwdebug.c @@ -0,0 +1,825 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwdebug.c + +Abstract: + + Contains debug routines for VWIPXSPX.DLL + + Contents: + VwDebugStart + VwDebugEnd + VwDebugPrint + VwDumpData + VwDumpEcb + VwDumpFragment + VwDumpPacketHeader + VwDumpXecb + VwDumpSocketInfo + VwDumpConnectionInfo + VwDumpConnectionStats + VwLog + CheckInterrupts + +Author: + + Richard L Firth (rfirth) 5-Oct-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 5-Oct-1993 rfirth + Created + +--*/ + +#include "vw.h" +#pragma hdrstop + +#if DBG + +// +// private prototypes +// + +int PokeLevelString(LPSTR, DWORD, DWORD, LPSTR); +LPSTR StripNameFromPath(LPSTR); + +// +// private data +// + +DWORD VwDebugFlags = 0; +DWORD VwDebugFunctions = 0; +DWORD VwShow = SHOW_ECBS | SHOW_HEADERS; +DWORD VwDebugLevel = IPXDBG_MIN_LEVEL; +DWORD VwDebugDump = 0; +BOOL VwDebugInitialized = FALSE; +FILE* hVwDebugLog = NULL; +DWORD DebugFlagsEx = 0 ; + +// +// functions +// + +VOID VwDebugStart() { + + // + // a little run-time diagnostication, madam? + // + + LPSTR ptr; + + if (VwDebugInitialized) { + return; + } + + // + // override VwDebugFlags from VWFLAGS environment variable + // + + if (ptr = getenv("VWFLAGS")) { + VwDebugFlags = (DWORD)strtoul(ptr, NULL, 0); + } + if (ptr = getenv("VWFUNCS")) { + VwDebugFunctions = (DWORD)strtoul(ptr, NULL, 0); + } + if (ptr = getenv("VWSHOW")) { + VwShow = (DWORD)strtoul(ptr, NULL, 0); + } + if (ptr = getenv("VWLEVEL")) { + VwDebugLevel = strtoul(ptr, NULL, 0); + if (VwDebugLevel > IPXDBG_MAX_LEVEL) { + VwDebugLevel = IPXDBG_MAX_LEVEL; + } + } + if (ptr = getenv("VWDUMP")) { + VwDebugDump = strtoul(ptr, NULL, 0); + } + IF_DEBUG(TO_FILE) { + if ((hVwDebugLog = fopen(VWDEBUG_FILE, "w+")) == NULL) { + VwDebugFlags &= ~DEBUG_TO_FILE; + } else { + +#if 0 + + char currentDirectory[256]; + int n; + + currentDirectory[0] = 0; + if (n = GetCurrentDirectory(sizeof(currentDirectory), currentDirectory)) { + if (currentDirectory[n-1] == '\\') { + currentDirectory[n-1] = 0; + } + } + + DbgPrint("VWIPXSPX: Writing debug output to %s\\" VWDEBUG_FILE "\n", currentDirectory); +#endif + + } + } + VwDebugInitialized = TRUE; +} + +VOID VwDebugEnd() { + IF_DEBUG(TO_FILE) { + fflush(hVwDebugLog); + fclose(hVwDebugLog); + } +} + +VOID VwDebugPrint(LPSTR Module, DWORD Line, DWORD Function, DWORD Level, LPSTR Format, ...) { + + char buf[1024]; + va_list p; + + IF_DEBUG(NOTHING) { + return; + } + + // + // only log something if we are tracking this Function and Level is above + // (or equal to) the filter cut-off or Level >= minimum alert level (error) + // + + if (((Function & VwDebugFunctions) && (Level >= VwDebugLevel)) || (Level >= IPXDBG_LEVEL_ERROR)) { + va_start(p, Format); + vsprintf(buf+PokeLevelString(Module, Line, Level, buf), Format, p); + VwLog(buf); + va_end(p); + } +} + +int PokeLevelString(LPSTR Module, DWORD Line, DWORD Level, LPSTR Buffer) { + + int length; + static char levelString[4] = " : "; + char level; + + switch (Level) { + case IPXDBG_LEVEL_INFO: + level = 'I'; + break; + + case IPXDBG_LEVEL_WARNING: + level = 'W'; + break; + + case IPXDBG_LEVEL_ERROR: + level = 'E'; + break; + + case IPXDBG_LEVEL_FATAL: + level = 'F'; + break; + } + + levelString[0] = level; + strcpy(Buffer, levelString); + length = strlen(levelString); + + if (Level >= IPXDBG_LEVEL_ERROR) { + length += sprintf(Buffer + length, "%s [% 5d]: ", StripNameFromPath(Module), Line); + } + + return length; +} + +LPSTR StripNameFromPath(LPSTR Path) { + + LPSTR p; + + p = strrchr(Path, '\\'); + return p ? p+1 : Path; +} + +VOID VwDumpData(ULPBYTE Address, WORD Seg, WORD Off, BOOL InVdm, WORD Size) { + + char buf[128]; + int i, len; + + IF_NOT_DEBUG(DATA) { + return; + } + + while (Size) { + len = min(Size, 16); + if (InVdm) { + sprintf(buf, "%04x:%04x ", Seg, Off); + } else { + sprintf(buf, "%08x ", Address); + } + for (i = 0; i < len; ++i) { + sprintf(&buf[10+i*3], "%02.2x ", Address[i] & 0xff); + } + for (i = len; i < 17; ++i) { + strcat(buf, " "); + } + for (i = 0; i < len; ++i) { + + char ch; + + ch = Address[i]; + buf[61+i] = ((ch < 32) || (ch > 127)) ? '.' : ch; + } + buf[61+i++] = '\n'; + buf[61+i] = 0; + VwLog(buf); + Address += len; + Size -= len; + Off += len; + } + VwLog("\n"); +} + +VOID VwDumpEcb(LPECB pEcb, WORD Seg, WORD Off, BYTE Type, BOOL Frags, BOOL Data, BOOL Mode) { + + char buf[512]; + int n; + char* bufptr; + + IF_NOT_DEBUG(ECB) { + return; + } + + IF_NOT_SHOW(ECBS) { + VwDumpData((ULPBYTE)pEcb, + Seg, + Off, + TRUE, + (WORD)((Type == ECB_TYPE_AES) + ? sizeof(AES_ECB) + : (sizeof(IPX_ECB) + sizeof(FRAGMENT) * (pEcb->FragmentCount - 1))) + ); + return; + } + + n = sprintf(buf, + "\n" + "%s ECB @ %04x:%04x:\n" + "LinkAddress %04x:%04x\n" + "EsrAddress %04x:%04x\n" + "InUse %02x\n", + (Type == ECB_TYPE_AES) + ? "AES" + : (Type == ECB_TYPE_IPX) + ? "IPX" + : "SPX", + Seg, + Off, + GET_SEGMENT(&pEcb->LinkAddress), + GET_OFFSET(&pEcb->LinkAddress), + GET_SEGMENT(&pEcb->EsrAddress), + GET_OFFSET(&pEcb->EsrAddress), + pEcb->InUse + ); + bufptr = buf + n; + if (Type == ECB_TYPE_AES) { + sprintf(bufptr, + "AesWorkspace %02x-%02x-%02x-%02x-%02x\n", + ((LPAES_ECB)pEcb)->AesWorkspace[0] & 0xff, + ((LPAES_ECB)pEcb)->AesWorkspace[1] & 0xff, + ((LPAES_ECB)pEcb)->AesWorkspace[2] & 0xff, + ((LPAES_ECB)pEcb)->AesWorkspace[3] & 0xff, + ((LPAES_ECB)pEcb)->AesWorkspace[4] & 0xff + ); + } else { + sprintf(bufptr, + "CompletionCode %02x\n" + "SocketNumber %04x\n" + "IpxWorkspace %08x\n" + "DriverWorkspace %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n" + "ImmediateAddress %02x-%02x-%02x-%02x-%02x-%02x\n" + "FragmentCount %04x\n", + pEcb->CompletionCode, + B2LW(pEcb->SocketNumber), + pEcb->IpxWorkspace, + pEcb->DriverWorkspace[0] & 0xff, + pEcb->DriverWorkspace[1] & 0xff, + pEcb->DriverWorkspace[2] & 0xff, + pEcb->DriverWorkspace[3] & 0xff, + pEcb->DriverWorkspace[4] & 0xff, + pEcb->DriverWorkspace[5] & 0xff, + pEcb->DriverWorkspace[6] & 0xff, + pEcb->DriverWorkspace[7] & 0xff, + pEcb->DriverWorkspace[8] & 0xff, + pEcb->DriverWorkspace[9] & 0xff, + pEcb->DriverWorkspace[10] & 0xff, + pEcb->DriverWorkspace[11] & 0xff, + pEcb->ImmediateAddress[0] & 0xff, + pEcb->ImmediateAddress[1] & 0xff, + pEcb->ImmediateAddress[2] & 0xff, + pEcb->ImmediateAddress[3] & 0xff, + pEcb->ImmediateAddress[4] & 0xff, + pEcb->ImmediateAddress[5] & 0xff, + pEcb->FragmentCount + ); + } + + VwLog(buf); + + if ((Type != ECB_TYPE_AES) && Frags) { + + ASSERT(pEcb->FragmentCount < 10); + + VwDumpFragment(pEcb->FragmentCount, + (LPFRAGMENT)(pEcb + 1), + Type, + Data, + Mode + ); + } +} + +VOID VwDumpFragment(WORD Count, LPFRAGMENT pFrag, BYTE Type, BOOL Data, BOOL Mode) { + + char buf[256]; + int i; + + IF_NOT_DEBUG(FRAGMENTS) { + return; + } + + for (i = 0; i < Count; ++i) { + sprintf(buf, + "Fragment %d:\n" + " Address %04x:%04x\n" + " Length %04x\n", + i + 1, + GET_SEGMENT(&pFrag->Address), + GET_OFFSET(&pFrag->Address), + pFrag->Length + ); + VwLog(buf); + if (Data) { + + ULPBYTE ptr; + WORD size; + WORD offset; + + ptr = GET_FAR_POINTER(&pFrag->Address, Mode); + size = pFrag->Length; + offset = GET_OFFSET(&pFrag->Address); + + // + // this allows us to show headers vs. raw data + // + + IF_SHOW(HEADERS) { + if (i == 0) { + VwDumpPacketHeader(ptr, Type); + + if (Type == ECB_TYPE_IPX) { + ptr += IPX_HEADER_LENGTH; + size -= IPX_HEADER_LENGTH; + offset += IPX_HEADER_LENGTH; + } else { + ptr += SPX_HEADER_LENGTH; + size -= SPX_HEADER_LENGTH; + offset += SPX_HEADER_LENGTH; + } + } + } + + VwDumpData(ptr, + GET_SEGMENT(&pFrag->Address), + offset, + TRUE, + size + ); + } + ++pFrag; + } +} + +VOID VwDumpPacketHeader(ULPBYTE pPacket, BYTE Type) { + + char buf[512]; + + IF_NOT_DEBUG(HEADERS) { + return; + } + + sprintf(buf, + "Checksum %04x\n" + "Length %04x\n" + "TransportControl %02x\n" + "PacketType %02x\n" + "Destination %02x-%02x-%02x-%02x : %02x-%02x-%02x-%02x-%02x-%02x : %04x\n" + "Source %02x-%02x-%02x-%02x : %02x-%02x-%02x-%02x-%02x-%02x : %04x\n", + ((LPIPX_PACKET)pPacket)->Checksum, + B2LW(((LPIPX_PACKET)pPacket)->Length), + ((LPIPX_PACKET)pPacket)->TransportControl, + ((LPIPX_PACKET)pPacket)->PacketType, + ((LPIPX_PACKET)pPacket)->Destination.Net[0] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Net[1] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Net[2] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Net[3] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Node[0] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Node[1] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Node[2] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Node[3] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Node[4] & 0xff, + ((LPIPX_PACKET)pPacket)->Destination.Node[5] & 0xff, + B2LW(((LPIPX_PACKET)pPacket)->Destination.Socket), + ((LPIPX_PACKET)pPacket)->Source.Net[0] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Net[1] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Net[2] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Net[3] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Node[0] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Node[1] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Node[2] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Node[3] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Node[4] & 0xff, + ((LPIPX_PACKET)pPacket)->Source.Node[5] & 0xff, + B2LW(((LPIPX_PACKET)pPacket)->Source.Socket) + ); + VwLog(buf); + if (Type == ECB_TYPE_SPX) { + sprintf(buf, + "ConnectControl %02x\n" + "DataStreamType %02x\n" + "SourceConnectId %04x\n" + "DestConnectId %04x\n" + "SequenceNumber %04x\n" + "AckNumber %04x\n" + "AllocationNumber %04x\n", + ((LPSPX_PACKET)pPacket)->ConnectionControl, + ((LPSPX_PACKET)pPacket)->DataStreamType, + B2LW(((LPSPX_PACKET)pPacket)->SourceConnectId), + B2LW(((LPSPX_PACKET)pPacket)->DestinationConnectId), + B2LW(((LPSPX_PACKET)pPacket)->SequenceNumber), + B2LW(((LPSPX_PACKET)pPacket)->AckNumber), + B2LW(((LPSPX_PACKET)pPacket)->AllocationNumber) + ); + VwLog(buf); + } + VwLog("\n"); +} + +VOID VwDumpXecb(LPXECB pXecb) { + + char buf[512]; + + IF_NOT_DEBUG(XECB) { + return; + } + + sprintf(buf, + "XECB @ %08x:\n" + "Next %08x\n" + "Ecb %08x\n" + "EcbAddress %08x\n" + "EsrAddress %08x\n" + "Buffer %08x\n" + "Data %08x\n" + "FrameLength %04x\n" + "ActualLength %04x\n" + "Length %04x\n" + "Ticks %04x\n" + "SocketNumber %04x\n" + "Owner %04x\n" + "TaskId %08x\n" + "Flags %08x\n" + "QueueId %08x\n" + "OwningObject %08x\n" + "RefCount %08x\n", + pXecb, + pXecb->Next, + pXecb->Ecb, + pXecb->EcbAddress, + pXecb->EsrAddress, + pXecb->Buffer, + pXecb->Data, + pXecb->FrameLength, + pXecb->ActualLength, + pXecb->Length, + pXecb->Ticks, + B2LW(pXecb->SocketNumber), + pXecb->Owner, + pXecb->TaskId, + pXecb->Flags, + pXecb->QueueId, + pXecb->OwningObject, + pXecb->RefCount + ); + VwLog(buf); +} + +VOID VwDumpSocketInfo(LPSOCKET_INFO pSocketInfo) { + + char buf[512]; + + IF_NOT_DEBUG(SOCKINFO) { + return; + } + + sprintf(buf, + "SOCKET_INFO @ %08x:\n" + "Next %08x\n" + "SocketNumber %04x\n" + "Owner %04x\n" + "TaskId %08x\n" + "Socket %08x\n" + "Flags %08x\n" + "LongLived %d\n" + "SpxSocket %d\n" + "PendingSends %08x\n" + "PendingListens %08x\n" + "ListenQueue %08x, %08x\n" + "SendQueue %08x, %08x\n" + "HeaderQueue %08x, %08x\n" + "Connections %08x\n", + pSocketInfo, + pSocketInfo->Next, + B2LW(pSocketInfo->SocketNumber), + pSocketInfo->Owner, + pSocketInfo->TaskId, + pSocketInfo->Socket, + pSocketInfo->Flags, + pSocketInfo->LongLived, + pSocketInfo->SpxSocket, + pSocketInfo->PendingSends, + pSocketInfo->PendingListens, + pSocketInfo->ListenQueue.Head, + pSocketInfo->ListenQueue.Tail, + pSocketInfo->SendQueue.Head, + pSocketInfo->SendQueue.Tail, + pSocketInfo->HeaderQueue.Head, + pSocketInfo->HeaderQueue.Tail, + pSocketInfo->Connections + ); + VwLog(buf); +} + +VOID VwDumpConnectionInfo(LPCONNECTION_INFO pConnectionInfo) { + + char buf[512]; + + IF_NOT_DEBUG(CONNINFO) { + return; + } + + sprintf(buf, + "CONNECTION_INFO @ %08x:\n" + "Next %08x\n" + "List %08x\n" + "OwningSocket %08x\n" + "Socket %08x\n" + "TaskId %08x\n" + "ConnectionId %04x\n" + "Flags %02x\n" + "State %02x\n" + "ConnectQueue %08x, %08x\n" + "AcceptQueue %08x, %08x\n" + "SendQueue %08x, %08x\n" + "ListenQueue %08x, %08x\n", + pConnectionInfo, + pConnectionInfo->Next, + pConnectionInfo->List, + pConnectionInfo->OwningSocket, + pConnectionInfo->Socket, + pConnectionInfo->TaskId, + pConnectionInfo->ConnectionId, + pConnectionInfo->Flags, + pConnectionInfo->State, + pConnectionInfo->ConnectQueue.Head, + pConnectionInfo->ConnectQueue.Tail, + pConnectionInfo->AcceptQueue.Head, + pConnectionInfo->AcceptQueue.Tail, + pConnectionInfo->SendQueue.Head, + pConnectionInfo->SendQueue.Tail, + pConnectionInfo->ListenQueue.Head, + pConnectionInfo->ListenQueue.Tail + ); + VwLog(buf); +} + +VOID VwDumpConnectionStats(LPSPX_CONNECTION_STATS pStats) { + + char buf[1024]; + + IF_NOT_DEBUG(STATS) { + return; + } + + sprintf(buf, + "State %02x\n" + "WatchDog %02x\n" + "LocalConnectionId %04x\n" + "RemoteConnectionId %04x\n" + "LocalSequenceNumber %04x\n" + "LocalAckNumber %04x\n" + "LocalAllocNumber %04x\n" + "RemoteAckNumber %04x\n" + "RemoteAllocNumber %04x\n" + "LocalSocket %04x\n" + "ImmediateAddress %02x-%02x-%02x-%02x-%02x-%02x\n" + "RemoteNetwork %02x-%02x-%02x-%02x\n" + "RemoteNode %02x-%02x-%02x-%02x-%02x-%02x\n" + "RemoteSocket %04x\n" + "RetransmissionCount %04x\n" + "EstimatedRoundTripDelay %04x\n" + "RetransmittedPackets %04x\n", + pStats->State, + pStats->WatchDog, + B2LW(pStats->LocalConnectionId), + B2LW(pStats->RemoteConnectionId), + B2LW(pStats->LocalSequenceNumber), + B2LW(pStats->LocalAckNumber), + B2LW(pStats->LocalAllocNumber), + B2LW(pStats->RemoteAckNumber), + B2LW(pStats->RemoteAllocNumber), + B2LW(pStats->LocalSocket), + pStats->ImmediateAddress[0] & 0xff, + pStats->ImmediateAddress[1] & 0xff, + pStats->ImmediateAddress[2] & 0xff, + pStats->ImmediateAddress[3] & 0xff, + pStats->ImmediateAddress[4] & 0xff, + pStats->ImmediateAddress[5] & 0xff, + pStats->RemoteNetwork[0] & 0xff, + pStats->RemoteNetwork[1] & 0xff, + pStats->RemoteNetwork[2] & 0xff, + pStats->RemoteNetwork[3] & 0xff, + pStats->RemoteNode[0] & 0xff, + pStats->RemoteNode[1] & 0xff, + pStats->RemoteNode[2] & 0xff, + pStats->RemoteNode[3] & 0xff, + pStats->RemoteNode[4] & 0xff, + pStats->RemoteNode[5] & 0xff, + B2LW(pStats->RemoteSocket), + B2LW(pStats->RetransmissionCount), + B2LW(pStats->EstimatedRoundTripDelay), + B2LW(pStats->RetransmittedPackets) + ); + + VwLog(buf); +} + +VOID VwLog(LPSTR buf) { + + IF_DEBUG(NOTHING) { + return; + } + + IF_DEBUG(TO_FILE) { + fputs(buf, hVwDebugLog); + IF_DEBUG(FLUSH) { + fflush(hVwDebugLog); + } + } else IF_DEBUG(TO_DBG) { + OutputDebugString(buf); + } +} + +VOID CheckInterrupts(LPSTR name) { + + IF_DEBUG(CHECK_INT) { + + LPWORD pDosIntFlag = (LPWORD)GET_POINTER(0x40, 0x314, 2, FALSE); + + if ((getIF() == 0) || !(*pDosIntFlag & 0x200)) { + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "*** CheckInterrupts: ints off in %s (IF=%d, 40:314=%04x)\n", + name, + getIF(), + *pDosIntFlag + )); + } + } +} + +extern LPCONNECTION_INFO ConnectionList ; +extern LPSOCKET_INFO SocketList ; + +VOID VwDumpAll(VOID) +{ + char buf[512]; + LPCONNECTION_INFO pConnectionInfo; + LPSOCKET_INFO pSocketInfo; + + if (DebugFlagsEx == 0) + return ; + + DebugFlagsEx = 0 ; + + RequestMutex(); + + + pSocketInfo = SocketList; + while (pSocketInfo) { + + LPXECB pXecb ; + + if (!(pSocketInfo->SpxSocket)) { + pSocketInfo = pSocketInfo->Next; + continue ; + } + + sprintf(buf, + "%sSOCKET_INFO @ %08x:\n" + " SocketNumber %04x\n" + " Owner %04x\n" + " TaskId %08x\n" + " Socket %08x\n" + " Flags %08x\n" + " LongLived %d\n" + " PendingSends %08x\n" + " PendingListens %08x\n" + " ListenQueue %08x, %08x\n" + " SendQueue %08x, %08x\n" + " HeaderQueue %08x, %08x\n" + " Connections %08x\n\n", + (pSocketInfo->SpxSocket)?"SPX ":"", + pSocketInfo, + B2LW(pSocketInfo->SocketNumber), + pSocketInfo->Owner, + pSocketInfo->TaskId, + pSocketInfo->Socket, + pSocketInfo->Flags, + pSocketInfo->LongLived, + pSocketInfo->PendingSends, + pSocketInfo->PendingListens, + pSocketInfo->ListenQueue.Head, + pSocketInfo->ListenQueue.Tail, + pSocketInfo->SendQueue.Head, + pSocketInfo->SendQueue.Tail, + pSocketInfo->HeaderQueue.Head, + pSocketInfo->HeaderQueue.Tail, + pSocketInfo->Connections + ); + OutputDebugString(buf); + + pConnectionInfo = pSocketInfo->Connections ; + + while(pConnectionInfo) { + sprintf(buf, + "CONNECTION_INFO @ %08x:\n" + " List %08x\n" + " OwningSocket %08x\n" + " Socket %08x\n" + " TaskId %08x\n" + " ConnectionId %04x\n" + " Flags %02x\n" + " State %02x\n" + " ConnectQueue %08x, %08x\n" + " AcceptQueue %08x, %08x\n" + " SendQueue %08x, %08x\n" + " ListenQueue %08x, %08x\n\n", + pConnectionInfo, + pConnectionInfo->List, + pConnectionInfo->OwningSocket, + pConnectionInfo->Socket, + pConnectionInfo->TaskId, + pConnectionInfo->ConnectionId, + pConnectionInfo->Flags, + pConnectionInfo->State, + pConnectionInfo->ConnectQueue.Head, + pConnectionInfo->ConnectQueue.Tail, + pConnectionInfo->AcceptQueue.Head, + pConnectionInfo->AcceptQueue.Tail, + pConnectionInfo->SendQueue.Head, + pConnectionInfo->SendQueue.Tail, + pConnectionInfo->ListenQueue.Head, + pConnectionInfo->ListenQueue.Tail + ); + OutputDebugString(buf); + pConnectionInfo = pConnectionInfo->Next ; + } + + pXecb = pSocketInfo->ListenQueue.Head ; + while(pXecb) { + sprintf(buf, + " XECB @ %08x: (Ecb %08x)\n" + " EcbAddress/EsrAddress %08x %08x\n" + " Flags/RefCount %08x %08x\n" + " Buffer/QueueId %08x %08x\n" + " OwningObject %08x\n", + pXecb, + pXecb->Ecb, + pXecb->EcbAddress, + pXecb->EsrAddress, + pXecb->Flags, + pXecb->RefCount, + pXecb->Buffer, + pXecb->QueueId, + pXecb->OwningObject + ); + OutputDebugString(buf); + pXecb = pXecb->Next ; + } + pSocketInfo = pSocketInfo->Next; + } + ReleaseMutex(); +} + +#endif // DBG diff --git a/private/nw/vwipxspx/dll/vwdebug.h b/private/nw/vwipxspx/dll/vwdebug.h new file mode 100644 index 000000000..142c7e22a --- /dev/null +++ b/private/nw/vwipxspx/dll/vwdebug.h @@ -0,0 +1,184 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwdebug.h + +Abstract: + + Prototypes, structures, manifests, macros for VWIPXSPX debug routines + +Author: + + Richard L Firth (rfirth) 5-Oct-1993 + +Revision History: + + 5-Oct-1993 rfirth + Created + +--*/ + +#ifndef _VWDEBUG_H_ +#define _VWDEBUG_H_ + +// +// debug flags +// + +#define DEBUG_ANY 0xFFFFFFFF // any debug flags set +#define DEBUG_NOTHING 0x00000001 // no debug output +#define DEBUG_CHECK_INT 0x00080000 // check interrupts (DOS) +#define DEBUG_STATS 0x00100000 // dump connection stats +#define DEBUG_DATA 0x00200000 // dump data (send) +#define DEBUG_FRAGMENTS 0x00400000 // dump fragments +#define DEBUG_HEADERS 0x00800000 // dump IPX/SPX headers +#define DEBUG_ECB 0x01000000 // dump 16-bit ECBs +#define DEBUG_XECB 0x02000000 // dump 32-bit XECBs +#define DEBUG_SOCKINFO 0x04000000 // dump SOCKET_INFO structs +#define DEBUG_CONNINFO 0x08000000 // dump CONNECTION_INFO structs +#define DEBUG_DLL 0x10000000 // include DLL attach/detach info +#define DEBUG_FLUSH 0x20000000 // flush every write +#define DEBUG_TO_FILE 0x40000000 // write debug stuff to file +#define DEBUG_TO_DBG 0x80000000 // debug stuff to debugger + +#define VWDEBUG_FILE "VWDEBUG.LOG" + +// +// function designators +// + +#define FUNCTION_ANY 0xFFFFFFFF +#define FUNCTION_IPXOpenSocket 0x00000001 // 0x00 +#define FUNCTION_IPXCloseSocket 0x00000002 // 0x01 +#define FUNCTION_IPXGetLocalTarget 0x00000004 // 0x02 +#define FUNCTION_IPXSendPacket 0x00000008 // 0x03 +#define FUNCTION_IPXListenForPacket 0x00000010 // 0x04 +#define FUNCTION_IPXScheduleIPXEvent 0x00000020 // 0x05 +#define FUNCTION_IPXCancelEvent 0x00000040 // 0x06 +#define FUNCTION_IPXScheduleAESEvent 0x00000080 // 0x07 +#define FUNCTION_IPXGetIntervalMarker 0x00000100 // 0x08 +#define FUNCTION_IPXGetInternetworkAddress 0x00000200 // 0x09 +#define FUNCTION_IPXRelinquishControl 0x00000400 // 0x0A +#define FUNCTION_IPXDisconnectFromTarget 0x00000800 // 0x0B +#define FUNCTION_InvalidFunction_0C 0x00001000 // 0x0C +#define FUNCTION_InvalidFunction_0D 0x00002000 // 0x0D +#define FUNCTION_InvalidFunction_0E 0x00004000 // 0x0E +#define FUNCTION_InvalidFunction_0F 0x00008000 // 0x0F +#define FUNCTION_SPXInitialize 0x00010000 // 0x10 +#define FUNCTION_SPXEstablishConnection 0x00020000 // 0x11 +#define FUNCTION_SPXListenForConnection 0x00040000 // 0x12 +#define FUNCTION_SPXTerminateConnection 0x00080000 // 0x13 +#define FUNCTION_SPXAbortConnection 0x00100000 // 0x14 +#define FUNCTION_SPXGetConnectionStatus 0x00200000 // 0x15 +#define FUNCTION_SPXSendSequencedPacket 0x00400000 // 0x16 +#define FUNCTION_SPXListenForSequencedPacket 0x00800000 // 0x17 +#define FUNCTION_InvalidFunction_18 0x01000000 // 0x18 +#define FUNCTION_InvalidFunction_19 0x02000000 // 0x19 +#define FUNCTION_IPXGetMaxPacketSize 0x04000000 // 0x1A +#define FUNCTION_InvalidFunction_1B 0x08000000 // 0x1B +#define FUNCTION_InvalidFunction_1C 0x10000000 // 0x1C +#define FUNCTION_InvalidFunction_1D 0x20000000 // 0x1D +#define FUNCTION_InvalidFunction_1E 0x40000000 // 0x1E +#define FUNCTION_IPXGetInformation 0x80000000 // 0x1F +#define FUNCTION_IPXSendWithChecksum 0xFFFFFFFF // 0x20 +#define FUNCTION_IPXGenerateChecksum 0xFFFFFFFF // 0x21 +#define FUNCTION_IPXVerifyChecksum 0xFFFFFFFF // 0x22 + +// +// debug levels +// + +#define IPXDBG_LEVEL_ALL 0 +#define IPXDBG_LEVEL_INFO 1 +#define IPXDBG_LEVEL_WARNING 2 +#define IPXDBG_LEVEL_ERROR 3 +#define IPXDBG_LEVEL_FATAL 4 + +#define IPXDBG_MIN_LEVEL IPXDBG_LEVEL_ALL +#define IPXDBG_MAX_LEVEL IPXDBG_LEVEL_FATAL + +// +// info dump flags (VWDUMP) +// + +#define DUMP_ECB_IN 0x00000001 +#define DUMP_ECB_OUT 0x00000002 +#define DUMP_SEND_DATA 0x00000004 +#define DUMP_RECEIVE_DATA 0x00000008 + +// +// show flags +// + +#define SHOW_ECBS 0x00000001 // show ECBs vs. raw data +#define SHOW_HEADERS 0x00000002 // show IPX/SPX headers vs. raw data + +#if DBG + +extern DWORD VwDebugFlags; +extern DWORD VwDebugFunctions; +extern DWORD VwShow; +extern DWORD DebugFlagsEx; + + +#define IF_DEBUG(f) if (VwDebugFlags & DEBUG_ ## f) +#define IF_NOT_DEBUG(f) if (!(VwDebugFlags & DEBUG_ ## f)) +#define IF_SHOW(f) if (VwShow & SHOW_ ## f) +#define IF_NOT_SHOW(f) if (!(VwShow & SHOW_ ## f)) +#define PRIVATE +#define IPXDBGPRINT(x) VwDebugPrint x +#define IPXDBGSTART() VwDebugStart() +#define IPXDBGEND() VwDebugEnd +#define VWASSERT(a, b) ASSERT((a) == (b)) +#define IPXDUMPDATA(x) VwDumpData x +#define IPXDUMPECB(x) VwDumpEcb x +#define DUMPXECB(x) VwDumpXecb(x) +#define DUMPCONN(x) VwDumpConnectionInfo(x) +#define DUMPSTATS(x) VwDumpConnectionStats(x) +#define CHECK_INTERRUPTS(s) CheckInterrupts(s) +#define DUMPALL() VwDumpAll() + +#else + +#define IF_DEBUG(f) if (0) +#define IF_NOT_DEBUG(f) if (0) +#define IF_SHOW(f) if (0) +#define IF_NOT_SHOW(f) if (0) +#define PRIVATE static +#define IPXDBGPRINT(x) +#define IPXDBGSTART() +#define IPXDBGEND() +#define VWASSERT(a, b) a +#define IPXDUMPDATA(x) +#define IPXDUMPECB(x) +#define DUMPXECB(x) +#define DUMPCONN(x) +#define DUMPSTATS(x) +#define CHECK_INTERRUPTS(s) +#define DUMPALL() + +#endif + +// +// debug function prototypes +// + +extern VOID VwDebugStart(VOID); +extern VOID VwDebugEnd(VOID); +extern VOID VwDebugPrint(LPSTR, DWORD, DWORD, DWORD, LPSTR, ...); +extern VOID VwDumpData(ULPBYTE, WORD, WORD, BOOL, WORD); +extern VOID VwDumpEcb(LPECB, WORD, WORD, BYTE, BOOL, BOOL, BOOL); +extern VOID VwDumpFragment(WORD, LPFRAGMENT, BYTE, BOOL, BOOL); +extern VOID VwDumpPacketHeader(ULPBYTE, BYTE); +extern VOID VwDumpXecb(LPXECB); +extern VOID VwDumpSocketInfo(LPSOCKET_INFO); +extern VOID VwDumpConnectionInfo(LPCONNECTION_INFO); +extern VOID VwDumpConnectionStats(LPSPX_CONNECTION_STATS); +extern VOID VwLog(LPSTR); +extern VOID CheckInterrupts(LPSTR); +extern VOID VwDumpAll(VOID); + +#endif // _VWDEBUG_H_ diff --git a/private/nw/vwipxspx/dll/vwdll.c b/private/nw/vwipxspx/dll/vwdll.c new file mode 100644 index 000000000..0b7b9fcd7 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwdll.c @@ -0,0 +1,502 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwdll.c + +Abstract: + + ntVdm netWare (Vw) IPX/SPX Functions + + Vw: The peoples' network + + VDD functions for DOS/WOW IPX/SPX support + + Contents: + VwDllEntryPoint + VwInitialize + VWinInitialize + VwDispatcher + VwInvalidFunction + +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 prototypes +// + +PRIVATE +VOID +VwInvalidFunction( + VOID + ); + +// +// private data +// + +PRIVATE +VOID +(*VwDispatchTable[])(VOID) = { + VwIPXOpenSocket, // 0x00 + VwIPXCloseSocket, // 0x01 + VwIPXGetLocalTarget, // 0x02 + VwIPXSendPacket, // 0x03 + VwIPXListenForPacket, // 0x04 + VwIPXScheduleIPXEvent, // 0x05 + VwIPXCancelEvent, // 0x06 + VwIPXScheduleAESEvent, // 0x07 + VwIPXGetIntervalMarker, // 0x08 + VwIPXGetInternetworkAddress, // 0x09 + VwIPXRelinquishControl, // 0x0A + VwIPXDisconnectFromTarget, // 0x0B + VwInvalidFunction, // 0x0C + VwInvalidFunction, // 0x0D old-style GetMaxPacketSize + VwInvalidFunction, // 0x0E + VwInvalidFunction, // 0x0F internal send packet function + VwSPXInitialize, // 0x10 + VwSPXEstablishConnection, // 0x11 + VwSPXListenForConnection, // 0x12 + VwSPXTerminateConnection, // 0x13 + VwSPXAbortConnection, // 0x14 + VwSPXGetConnectionStatus, // 0x15 + VwSPXSendSequencedPacket, // 0x16 + VwSPXListenForSequencedPacket, // 0x17 + VwInvalidFunction, // 0x18 + VwInvalidFunction, // 0x19 + VwIPXGetMaxPacketSize, // 0x1A + VwInvalidFunction, // 0x1B + VwInvalidFunction, // 0x1C + VwInvalidFunction, // 0x1D + VwInvalidFunction, // 0x1E + VwIPXGetInformation, // 0x1F + VwIPXSendWithChecksum, // 0x20 + VwIPXGenerateChecksum, // 0x21 + VwIPXVerifyChecksum // 0x22 +}; + +#define MAX_IPXSPX_FUNCTION LAST_ELEMENT(VwDispatchTable) + +WSADATA WsaData = {0}; +HANDLE hAesThread = NULL; + +// +// global data +// + +SOCKADDR_IPX MyInternetAddress; +WORD MyMaxPacketSize; +int Ica; +BYTE IcaLine; + +// +// not-really-global data +// + +extern CRITICAL_SECTION SerializationCritSec; +extern CRITICAL_SECTION AsyncCritSec; + +// +// functions +// + + +BOOL +WINAPI +VwDllEntryPoint( + IN PVOID DllHandle, + IN ULONG Reason, + IN PCONTEXT Context OPTIONAL + ) + +/*++ + +Routine Description: + + Called when the process attaches (LoadLibrary/init) and detaches (FreeLibrary/ + process termination) from this DLL + + Attach: + initialize Winsock DLL + get internet address for this station + get maximum packet size supported by transport (IPX) + create AES thread + + Detach: + terminate Winsock DLL + +Arguments: + + DllHandle - unused + Reason - checked for process attach/detach + Context - unused + +Return Value: + + BOOLEAN + +--*/ + +{ + DWORD aesThreadId; // unused outside of this function + + static BOOL CriticalSectionsAreInitialized = FALSE; + + UNREFERENCED_PARAMETER(DllHandle); + UNREFERENCED_PARAMETER(Context); + + IPXDBGSTART(); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwDllEntryPoint: %s\n", + Reason == DLL_PROCESS_ATTACH ? "DLL_PROCESS_ATTACH" + : Reason == DLL_PROCESS_DETACH ? "DLL_PROCESS_DETACH" + : Reason == DLL_THREAD_ATTACH ? "DLL_THREAD_ATTACH" + : Reason == DLL_THREAD_DETACH ? "DLL_THREAD_DETACH" + : "?" + )); + + if (Reason == DLL_PROCESS_ATTACH) { + + int err; + + // + // BUGBUG: get ICA values from new VDD service. Right now we grab + // line 4 on the slave (base = 0x70, modifier = 0x03) + // + + Ica = ICA_SLAVE; + IcaLine = 3; + + err = WSAStartup(MAKEWORD(1, 1), &WsaData); + if (err) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_FATAL, + "VwDllEntryPoint: WSAStartup() returns %d\n", + err + )); + + return FALSE; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwDllEntryPoint: WsaData:\n" + "\twVersion : 0x%04x\n" + "\twHighVersion : 0x%04x\n" + "\tszDescription : \"%s\"\n" + "\tszSystemStatus : \"%s\"\n" + "\tiMaxSockets : %d\n" + "\tiMaxUdpDg : %d\n" + "\tlpVendorInfo : 0x%08x\n", + WsaData.wVersion, + WsaData.wHighVersion, + WsaData.szDescription, + WsaData.szSystemStatus, + WsaData.iMaxSockets, + WsaData.iMaxUdpDg, + WsaData.lpVendorInfo + )); + + } + + // + // retrieve the internet address for this station. Used in + // IPXGetInternetworkAddress() and IPXSendPacket() + // + + err = GetInternetAddress(&MyInternetAddress); + if (err) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_FATAL, + "VwDllEntryPoint: GetInternetAddress() returns %d\n", + WSAGetLastError() + )); + + goto attach_error_exit; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwDllEntryPoint: MyInternetAddress:\n" + "\tNet : %02.2x-%02.2x-%02.2x-%02.2x\n" + "\tNode : %02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x\n", + MyInternetAddress.sa_netnum[0] & 0xff, + MyInternetAddress.sa_netnum[1] & 0xff, + MyInternetAddress.sa_netnum[2] & 0xff, + MyInternetAddress.sa_netnum[3] & 0xff, + MyInternetAddress.sa_nodenum[0] & 0xff, + MyInternetAddress.sa_nodenum[1] & 0xff, + MyInternetAddress.sa_nodenum[2] & 0xff, + MyInternetAddress.sa_nodenum[3] & 0xff, + MyInternetAddress.sa_nodenum[4] & 0xff, + MyInternetAddress.sa_nodenum[5] & 0xff + )); + + } + + // + // get the maximum packet size supported by IPX. Used in + // IPXGetMaxPacketSize() + // + + err = GetMaxPacketSize(&MyMaxPacketSize); + if (err) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_FATAL, + "VwDllEntryPoint: GetMaxPacketSize() returns %d\n", + WSAGetLastError() + )); + + goto attach_error_exit; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwDllEntryPoint: GetMaxPacketSize: %04x (%d)\n", + MyMaxPacketSize, + MyMaxPacketSize + )); + + } + + hAesThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)VwAesThread, + NULL, + 0, + &aesThreadId + ); + if (hAesThread == NULL) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_FATAL, + "VwDllEntryPoint: CreateThread() returns %d\n", + GetLastError() + )); + + goto attach_error_exit; + } + + // + // finally initialize any critical sections + // + + InitializeCriticalSection(&SerializationCritSec); + InitializeCriticalSection(&AsyncCritSec); + CriticalSectionsAreInitialized = TRUE; + } else if (Reason == DLL_PROCESS_DETACH) { + if (hAesThread != NULL) { + WaitForSingleObject(hAesThread, ONE_TICK * 2); + CloseHandle(hAesThread); + } + + WSACleanup(); + + if (CriticalSectionsAreInitialized) { + DeleteCriticalSection(&SerializationCritSec); + DeleteCriticalSection(&AsyncCritSec); + } + + IPXDBGEND(); + } + return TRUE; + +attach_error_exit: + + // + // here if any fatal errors on process attach after successfully performing + // WSAStartup + // + + WSACleanup(); + return FALSE; +} + +BYTE +VWinInitialize( + VOID + ) +/*++ + +Routine Description: + + Called by interface when nwipxspx.dll is loaded. We + return the IRQ value. + +Arguments: + + None. + +Return Value: + + The IRQ value. + +--*/ + +{ + return 0x73; +} + + + +VOID +VwInitialize( + VOID + ) + +/*++ + +Routine Description: + + Called by VDD interface when DLL loaded via call to RegisterModule. We + get the IRQ value and return it as an interrupt vector in BX + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwInitialize\n" + )); + + // + // only lines on slave PIC are available. Currently, lines 3, 4 and 7 are + // not used. We'll grab line 3 here, but in the future we expect a function + // to return the available IRQ line + // + + setBX( VWinInitialize() ); +} + + +VOID +VwDispatcher( + VOID + ) + +/*++ + +Routine Description: + + Branches to relevant IPX/SPX handler for DOS calls, based on contents of + VDM BX register. + + Control transfered here from 16-bit entry point, either as result of call + to far address returned from INT 2Fh/AH=7A or INT 7Ah + + Special: we use BX = 0xFFFF to indicate that the app is terminating. The + TSR hooks INT 0x2F/AX=0x1122 (IFSResetEnvironment) + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + DWORD dispatchIndex; + + dispatchIndex = (DWORD)getBX() & 0x7fff; + + if (dispatchIndex <= MAX_IPXSPX_FUNCTION) { + VwDispatchTable[dispatchIndex](); + } else if (dispatchIndex == 0x7FFE) { + EsrCallback(); + } else if (dispatchIndex == 0x7FFF) { + VwTerminateProgram(); + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_ERROR, + "ERROR: VwDispatcher: dispatchIndex = %x\n", + dispatchIndex + )); + + // + // BUGBUG - what's the correct error code to return in this situation? + // (will it ever arise?) + // + + setAX(ERROR_INVALID_FUNCTION); + setCF(1); + } +} + + +PRIVATE +VOID +VwInvalidFunction( + VOID + ) + +/*++ + +Routine Description: + + Just alerts us to the fact that an invalid function request was made. + Useful if any app makes a bad call, or we miss out a required function + during design/implementation + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwInvalidFunction: BX=%04x\n", + getBX() + )); +} diff --git a/private/nw/vwipxspx/dll/vwdll.h b/private/nw/vwipxspx/dll/vwdll.h new file mode 100644 index 000000000..aa87daddb --- /dev/null +++ b/private/nw/vwipxspx/dll/vwdll.h @@ -0,0 +1,27 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vwdll.h + +Abstract: + + Contains external data declarations for VWDLL.C + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +extern SOCKADDR_IPX MyInternetAddress; +extern WORD MyMaxPacketSize; +extern int Ica; +extern BYTE IcaLine; diff --git a/private/nw/vwipxspx/dll/vwdos.c b/private/nw/vwipxspx/dll/vwdos.c new file mode 100644 index 000000000..31a8ee5ba --- /dev/null +++ b/private/nw/vwipxspx/dll/vwdos.c @@ -0,0 +1,1502 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwdos.c + +Abstract: + + ntVdm netWare (Vw) IPX/SPX Functions + + Vw: The peoples' network + + Contains handlers for DOS IPX/SPX calls (netware functions). The IPX APIs + use WinSock to perform the actual operations + + Contents: + VwIPXCancelEvent + VwIPXCloseSocket + VwIPXDisconnectFromTarget + VwIPXGenerateChecksum + VwIPXGetInformation + VwIPXGetInternetworkAddress + VwIPXGetIntervalMarker + VwIPXGetLocalTarget + VwIPXGetLocalTargetAsync + VwIPXGetMaxPacketSize + VwIPXInitialize + VwIPXListenForPacket + VwIPXOpenSocket + VwIPXRelinquishControl + VwIPXScheduleAESEvent + VwIPXScheduleIPXEvent + VwIPXSendPacket + VwIPXSendWithChecksum + VwIPXSPXDeinit + VwIPXVerifyChecksum + + VwSPXAbortConnection + VwSPXEstablishConnection + VwSPXGetConnectionStatus + VwSPXInitialize + VwSPXListenForConnection + VwSPXListenForSequencedPacket + VwSPXSendSequencedPacket + VwSPXTerminateConnection + +Author: + + Richard L Firth (rfirth) 30-Sep-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 30-Sep-1993 rfirth + Created + +--*/ + +#include "vw.h" +#pragma hdrstop + +// +// functions +// + + +VOID +VwIPXCancelEvent( + VOID + ) + +/*++ + +Routine Description: + + Cancels event described by an ECB + + This call is Synchronous + +Arguments: + + Inputs + BX 06h + ES:SI ECB + + Outputs + AL Completion code: + 00h Success + F9h Can't cancel ECB + FFh ECB not in use + +Return Value: + + None. + +--*/ + +{ + LPECB pEcb; + WORD status; + + CHECK_INTERRUPTS("VwIPXCancelEvent"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXCancelEvent, + IPXDBG_LEVEL_INFO, + "VwIPXCancelEvent(%04x:%04x)\n", + getES(), + getSI() + )); + + IPX_GET_IPX_ECB(pEcb); + + status = _VwIPXCancelEvent( pEcb ); + + IPX_SET_STATUS(status); +} + + +VOID +VwIPXCloseSocket( + VOID + ) + +/*++ + +Routine Description: + + Closes a socket and cancels any outstanding events on the socket. + Closing an unopened socket does not return an error + ESRs in cancelled ECBs are not called + + This call is Synchronous + +Arguments: + + Inputs + BX 01h + DX Socket Number + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + WORD socketNumber; + + CHECK_INTERRUPTS("VwIPXCloseSocket"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXCloseSocket, + IPXDBG_LEVEL_INFO, + "VwIPXCloseSocket(%#x)\n", + B2LW(IPX_SOCKET_PARM()) + )); + + IPX_GET_SOCKET(socketNumber); + + _VwIPXCloseSocket( socketNumber ); + +} + + +VOID +VwIPXDisconnectFromTarget( + VOID + ) + +/*++ + +Routine Description: + + Performs no action for NTVDM IPX + + This call is Synchronous + +Arguments: + + Inputs + BX 0Bh + ES:SI Request buffer: + Destination Network DB 4 DUP (?) + Destination Node DB 6 DUP (?) + Destination Socket DB 2 DUP (?) + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXDisconnectFromTarget"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXDisconnectFromTarget, + IPXDBG_LEVEL_INFO, + "VwIPXDisconnectFromTarget\n" + )); +} + + +VOID +VwIPXGenerateChecksum( + VOID + ) + +/*++ + +Routine Description: + + Generates checksum for a transmit ECB + + This call is Synchronous + +Arguments: + + Inputs + BX 21h + ES:SI ECB address + + Outputs + No registers + ECB checksum field is updated + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXGenerateChecksum"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGenerateChecksum, + IPXDBG_LEVEL_INFO, + "VwIPXGenerateChecksum\n" + )); +} + + +VOID +VwIPXGetInformation( + VOID + ) + +/*++ + +Routine Description: + + Returns a bit-map of supported functions + + This call is Synchronous + +Arguments: + + Inputs + BX 1Fh + DX 0000h + + Outputs + DX Bit map: + 0001h Set if IPX is IPXODI.COM, not dedicated IPX + 0002h Set if checksum functions (20h, 21h, 22h) supported + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXGetInformation"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetInformation, + IPXDBG_LEVEL_INFO, + "VwIPXGetInformation\n" + )); + + IPX_SET_INFORMATION(IPX_ODI); +} + + +VOID +VwIPXGetInternetworkAddress( + VOID + ) + +/*++ + +Routine Description: + + Returns a buffer containing the net number and node number for this + station. + + This function cannot return an error (!) + + Assumes: 1. GetInternetAddress has been successfully called in the + DLL initialization phase + + This call is Synchronous + +Arguments: + + Inputs + BX 09h + + Outputs + ES:SI Buffer + Network Address DB 4 DUP (?) + Node Address DB 6 DUP (?) + +Return Value: + + None. + +--*/ + +{ + LPINTERNET_ADDRESS pAddr; + + CHECK_INTERRUPTS("VwIPXGetInternetworkAddress"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetInternetworkAddress, + IPXDBG_LEVEL_INFO, + "VwIPXGetInternetworkAddress(%04x:%04x)\n", + getES(), + getSI() + )); + + pAddr = (LPINTERNET_ADDRESS)IPX_BUFFER_PARM(sizeof(*pAddr)); + + _VwIPXGetInternetworkAddress( pAddr ); +} + + +VOID +VwIPXGetIntervalMarker( + VOID + ) + +/*++ + +Routine Description: + + Just returns the tick count maintained by Asynchronous Event Scheduler + + This call is Synchronous + +Arguments: + + Inputs + BX 08h + + Outputs + AX Interval marker + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXGetIntervalMarker"); + + setAX( _VwIPXGetIntervalMarker() ); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetIntervalMarker, + IPXDBG_LEVEL_INFO, + "VwIPXGetIntervalMarker: Returning %04x\n", + getAX() + )); +} + + +VOID +VwIPXGetLocalTarget( + VOID + ) + +/*++ + +Routine Description: + + Given a target address of the form (network address {4}, node address {6}), + returns the node address of the target if on the same network, or the node + address of the router which knows how to get to the next hop in reaching the + eventual target + + This call is Synchronous + +Arguments: + + Inputs + BX 02h + ES:SI Request buffer + Destination Network DB 4 DUP (?) + Destination Node DB 6 DUP (?) + Destination Socket DB 2 DUP (?) + ES:DI Response buffer + Local Target DB 6 DUP (?) + + Outputs + AL Completion code + 00h Success + FAh No path to destination node found + AH Number of hops to destination + CX Transport time + +Return Value: + + None. + +--*/ + +{ + LPBYTE pImmediateAddress; + LPBYTE pNetworkAddress; + WORD transportTime; + WORD status; + + CHECK_INTERRUPTS("VwIPXGetLocalTarget"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetLocalTarget, + IPXDBG_LEVEL_INFO, + "VwIPXGetLocalTarget(target buf @ %04x:%04x, local buf @ %04x:%04x)\n", + getES(), + getSI(), + getES(), + getDI() + )); + + + pImmediateAddress = POINTER_FROM_WORDS(getES(), getDI(), 6); + pNetworkAddress = POINTER_FROM_WORDS(getES(), getSI(), 12); + + status = _VwIPXGetLocalTarget( pNetworkAddress, + pImmediateAddress, + &transportTime ); + + + setCX( transportTime ); + setAH(1); + + IPX_SET_STATUS(status); +} + + +VOID +VwIPXGetLocalTargetAsync( + VOID + ) + +/*++ + +Routine Description: + + description-of-function. + + This call is Asynchronous + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXGetLocalTargetAsync"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwIPXGetLocalTargetAsync\n" + )); +} + + +VOID +VwIPXGetMaxPacketSize( + VOID + ) + +/*++ + +Routine Description: + + Returns the maximum packet size the underlying network can handle + + Assumes: 1. A successfull call to GetMaxPacketSize has been made during + DLL initialization + 2. Maximum packet size is constant + + This call is Synchronous + +Arguments: + + Inputs + BX 1Ah + + Outputs + AX Maximum packet size + CX IPX retry count + +Return Value: + + None. + +--*/ + +{ + WORD maxPacketSize; + WORD retryCount; + + CHECK_INTERRUPTS("VwIPXGetMaxPacketSize"); + + maxPacketSize = _VwIPXGetMaxPacketSize( &retryCount ); + + setAX(maxPacketSize); + + // + // BUGBUG: The DOS Assembly and C manuals differ slightly here: DOS says + // we return the IPX retry count in CX. There is no corresponding parameter + // in the C interface? + // + + setCX(retryCount); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetMaxPacketSize, + IPXDBG_LEVEL_INFO, + "VwIPXGetMaxPacketSize: PacketSize=%d, RetryCount=%d\n", + getAX(), + getCX() + )); +} + + +VOID +VwIPXInitialize( + VOID + ) + +/*++ + +Routine Description: + + description-of-function. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXInitialize"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwIPXInitialize\n" + )); +} + + +VOID +VwIPXListenForPacket( + VOID + ) + +/*++ + +Routine Description: + + Queue a listen request against a socket. All listen requests will be + completed asynchronously, unless cancelled by app + + This call is Asynchronous + +Arguments: + + Inputs + BX 04h + ES:SI ECB address + + Outputs + AL Completion code + FFh Socket doesn't exist + +Return Value: + + None. + +--*/ + +{ + LPECB pEcb; + WORD status; + + CHECK_INTERRUPTS("VwIPXListenForPacket"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXListenForPacket, + IPXDBG_LEVEL_INFO, + "VwIPXListenForPacket(%04x:%04x)\n", + getES(), + getSI() + )); + + IPX_GET_IPX_ECB(pEcb); + + status = _VwIPXListenForPacket( pEcb, ECB_PARM_ADDRESS() ); + + IPX_SET_STATUS(status); +} + + +VOID +VwIPXOpenSocket( + VOID + ) + +/*++ + +Routine Description: + + Opens a socket for use by IPX or SPX. Puts the socket into non-blocking mode. + The socket will be bound to IPX + + This call is Synchronous + +Arguments: + + Inputs + AL Socket Longevity flag + This parameter is actually in BP - AX has been sequestered + by the VDD dispatcher + BX 00h + DX Requested Socket Number + + CX DOS PDB. This parameter is not part of the IPX API. + Added because we need to remember which DOS executable created + the socket: we need to clean-up short-lived sockets when the + executable terminates + + Outputs + AL Completion code: + 00h Success + FFh Socket already open + FEh Socket table full + DX Assigned socket number + +Return Value: + + None. + +--*/ + +{ + BYTE socketLife; + WORD socketNumber; + WORD status; + + CHECK_INTERRUPTS("VwIPXOpenSocket"); + + IPX_GET_SOCKET_LIFE(socketLife); + IPX_GET_SOCKET(socketNumber); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXOpenSocket, + IPXDBG_LEVEL_INFO, + "VwIPXOpenSocket(Life=%02x, Socket=%04x, Owner=%04x)\n", + socketLife, + B2LW(socketNumber), + IPX_SOCKET_OWNER_PARM() + )); + + + status = _VwIPXOpenSocket( &socketNumber, + socketLife, + IPX_SOCKET_OWNER_PARM() ); + + if ( status == IPX_SUCCESS ) + IPX_SET_SOCKET(socketNumber); + + IPX_SET_STATUS(status); +} + + +VOID +VwIPXRelinquishControl( + VOID + ) + +/*++ + +Routine Description: + + Just sleep for a nominal amount. Netware seems to be dependent on the + default setting of the PC clock, so one timer tick (1/18 second) would + seem to be a good value + + This call is Synchronous + +Arguments: + + Inputs + BX 0Ah + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXRelinquishControl"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXRelinquishControl, + IPXDBG_LEVEL_INFO, + "VwIPXRelinquishControl\n" + )); + + _VwIPXRelinquishControl(); + +} + + +VOID +VwIPXScheduleAESEvent( + VOID + ) + +/*++ + +Routine Description: + + Schedules a an event to occur in some number of ticks. When the tick count + reaches 0, the ECB InUse field is cleared and any ESR called + + This call is Asynchronous + +Arguments: + + Inputs + BX 07h + AX Delay time - number of 1/18 second ticks + ES:SI ECB address + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = AES_ECB_PARM(); + WORD ticks = IPX_TICKS_PARM(); + + CHECK_INTERRUPTS("VwIPXScheduleAESEvent"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXScheduleAESEvent, + IPXDBG_LEVEL_INFO, + "VwIPXScheduleAESEvent(%04x:%04x, %04x)\n", + getES(), + getSI(), + ticks + )); + + ScheduleEvent(pXecb, ticks); +} + + +VOID +VwIPXScheduleIPXEvent( + VOID + ) + +/*++ + +Routine Description: + + Schedules a an event to occur in some number of ticks. When the tick count + reaches 0, the ECB InUse field is cleared and any ESR called + + This call is Asynchronous + +Arguments: + + Inputs + BX 05h + AX Delay time - number of 1/18 second ticks + ES:SI ECB address + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPECB pEcb; + WORD ticks = IPX_TICKS_PARM(); + + CHECK_INTERRUPTS("VwIPXScheduleIPXEvent"); + + IPX_GET_IPX_ECB(pEcb); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXScheduleIPXEvent, + IPXDBG_LEVEL_INFO, + "VwIPXScheduleIPXEvent(%04x:%04x, %04x)\n", + getES(), + getSI(), + ticks + )); + + _VwIPXScheduleIPXEvent( ticks, pEcb, ECB_PARM_ADDRESS() ); + +} + + +VOID +VwIPXSendPacket( + VOID + ) + +/*++ + +Routine Description: + + Sends a packet to the target machine/router. This call can be made on a + socket that is not open + + The app must have filled in the following IPX_ECB fields: + + EsrAddress + Socket + ImmediateAddress + FragmentCount + fragment descriptor fields + + and the following IPX_PACKET fields: + + PacketType + Destination.Net + Destination.Node + Destination.Socket + + This call is Asynchronous + +Arguments: + + Inputs + BX 03h + CX DOS PDB. This parameter is not part of the IPX API. + Added because we need to remember which DOS executable owns the + socket IF WE MUST CREATE A TEMPORTARY SOCKET: we need to clean-up + short-lived sockets when the executable terminates + ES:SI ECB Address + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPECB pEcb; + WORD owner; + + CHECK_INTERRUPTS("VwIPXSendPacket"); + + IPX_GET_IPX_ECB(pEcb); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXSendPacket, + IPXDBG_LEVEL_INFO, + "VwIPXSendPacket(%04x:%04x), owner = %04x\n", + getES(), + getSI(), + IPX_SOCKET_OWNER_PARM() + )); + + _VwIPXSendPacket(pEcb, + ECB_PARM_ADDRESS(), + IPX_SOCKET_OWNER_PARM() + ); +} + + +VOID +VwIPXSendWithChecksum( + VOID + ) + +/*++ + +Routine Description: + + description-of-function. + + This call is Asynchronous + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXSendWithChecksum"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXSendWithChecksum, + IPXDBG_LEVEL_INFO, + "VwIPXSendWithChecksum\n" + )); +} + + +VOID +VwIPXSPXDeinit( + VOID + ) + +/*++ + +Routine Description: + + description-of-function. + + This call is Synchronous + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXSPXDeinit"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwIPXSPXDeinit\n" + )); +} + + +VOID +VwIPXVerifyChecksum( + VOID + ) + +/*++ + +Routine Description: + + description-of-function. + + This call is Synchronous + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + CHECK_INTERRUPTS("VwIPXVerifyChecksum"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXVerifyChecksum, + IPXDBG_LEVEL_INFO, + "VwIPXVerifyChecksum\n" + )); +} + + +VOID +VwSPXAbortConnection( + VOID + ) + +/*++ + +Routine Description: + + Aborts this end of a connection + + This call is Asynchronous + +Arguments: + + Inputs + BX 14h + DX Connection ID + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + WORD connectionId = SPX_CONNECTION_PARM(); + + CHECK_INTERRUPTS("VwSPXAbortConnection"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXAbortConnection, + IPXDBG_LEVEL_INFO, + "VwSPXAbortConnection(%04x)\n", + connectionId + )); + + _VwSPXAbortConnection(connectionId); +} + + +VOID +VwSPXEstablishConnection( + VOID + ) + +/*++ + +Routine Description: + + Creates a connection with a remote SPX socket. The remote end can be on + this machine (i.e. same app in DOS world) + + This call is Asynchronous + +Arguments: + + Inputs + BX 11h + AL Retry count + AH WatchDog flag + ES:SI ECB Address + + Outputs + AL Completion code: + 00h Attempting to talk to remote + EFh Local connection table full + FDh Fragment count not 1; buffer size not 42 + FFh Send socket not open + DX Connection ID + +Return Value: + + None. + +--*/ + +{ + WORD status; + BYTE retryCount = SPX_RETRY_COUNT_PARM(); + BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM(); + WORD connectionId; + LPECB pEcb; + + CHECK_INTERRUPTS("VwSPXEstablishConnection"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_INFO, + "VwSPXEstablishConnection(%02x, %02x, %04x:%04x)\n", + retryCount, + watchDogFlag, + ECB_PARM_SEGMENT(), + ECB_PARM_OFFSET() + )); + + IPX_GET_IPX_ECB( pEcb ); + + IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE)); + + status = _VwSPXEstablishConnection( retryCount, + watchDogFlag, + &connectionId, + pEcb, + ECB_PARM_ADDRESS() ); + + + SPX_SET_CONNECTION_ID( connectionId ); + SPX_SET_STATUS( status ); +} + + +VOID +VwSPXGetConnectionStatus( + VOID + ) + +/*++ + +Routine Description: + + Returns buffer crammed full of useful statistics or something (hu hu huh) + + This call is Synchronous + +Arguments: + + Inputs + BX 15h + DX Connection ID + ES:SI Buffer address + + Outputs + AL Completion code: + 00h Connection is active + EEh No such connection + + on output, buffer in ES:SI contains: + + BYTE ConnectionStatus + BYTE WatchDogActive + WORD LocalConnectionID + WORD RemoteConnectionID + WORD SequenceNumber + WORD LocalAckNumber + WORD LocalAllocationNumber + WORD RemoteAckNumber + WORD RemoteAllocationNumber + WORD LocalSocket + BYTE ImmediateAddress[6] + BYTE RemoteNetwork[4] + WORD RetransmissionCount + WORD RetransmittedPackets + WORD SuppressedPackets + +Return Value: + + None. + +--*/ + +{ + WORD status; + WORD connectionId = SPX_CONNECTION_PARM(); + LPSPX_CONNECTION_STATS pStats = (LPSPX_CONNECTION_STATS)SPX_BUFFER_PARM(sizeof(*pStats)); + + CHECK_INTERRUPTS("VwSPXGetConnectionStatus"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXGetConnectionStatus, + IPXDBG_LEVEL_INFO, + "VwSPXGetConnectionStatus: connectionId=%04x\n", + connectionId + )); + + status = _VwSPXGetConnectionStatus( connectionId, + pStats ); + + + SPX_SET_STATUS(status); +} + + +VOID +VwSPXInitialize( + VOID + ) + +/*++ + +Routine Description: + + Informs the app that SPX is present on this station + + This call is Synchronous + +Arguments: + + Inputs + BX 10h + AL 00h + + Outputs + AL Installation flag: + 00h Not installed + FFh Installed + BH SPX Major revision number + BL SPX Minor revision number + CX Maximum SPX connections supported + normally from SHELL.CFG + DX Available SPX connections + +Return Value: + + None. + +--*/ + +{ + WORD status; + BYTE majorRevisionNumber; + BYTE minorRevisionNumber; + WORD maxConnections; + WORD availableConnections; + + CHECK_INTERRUPTS("VwSPXInitialize"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXInitialize, + IPXDBG_LEVEL_INFO, + "VwSPXInitialize\n" + )); + + + status = _VwSPXInitialize( &majorRevisionNumber, + &minorRevisionNumber, + &maxConnections, + &availableConnections ); + + + setBH( majorRevisionNumber ); + setBL( minorRevisionNumber ); + setCX( maxConnections ); + setDX( availableConnections ); + SPX_SET_STATUS(status); +} + + +VOID +VwSPXListenForConnection( + VOID + ) + +/*++ + +Routine Description: + + Listens for an incoming connection request + + This call is Asynchronous + +Arguments: + + Inputs + BX 12h + AL Retry count + AH SPX WatchDog flag + ES:SI ECB Address + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + BYTE retryCount = SPX_RETRY_COUNT_PARM(); + BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM(); + LPECB pEcb; + + CHECK_INTERRUPTS("VwSPXListenForConnection"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForConnection, + IPXDBG_LEVEL_INFO, + "VwSPXListenForConnection(%02x, %02x, %04x:%04x)\n", + retryCount, + watchDogFlag, + ECB_PARM_SEGMENT(), + ECB_PARM_OFFSET() + )); + + IPX_GET_IPX_ECB( pEcb ); + + IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE)); + + _VwSPXListenForConnection( retryCount, + watchDogFlag, + pEcb, + ECB_PARM_ADDRESS() ); +} + + +VOID +VwSPXListenForSequencedPacket( + VOID + ) + +/*++ + +Routine Description: + + Attempts to receive an SPX packet. This call is made against the top-level + socket (the socket in SPX-speak, not the connection). We can receive a + packet from any connection assigned to this socket. In this function, we + just queue the ECB (since there is no return status, we expect that the + app has supplied an ESR) and let AES handle it + + This call is Asynchronous + +Arguments: + + Inputs + BX 17h + ES:SI ECB Address + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPECB pEcb; + + CHECK_INTERRUPTS("VwSPXListenForSequencedPacket"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForSequencedPacket, + IPXDBG_LEVEL_INFO, + "VwSPXListenForSequencedPacket(%04x:%04x)\n", + ECB_PARM_SEGMENT(), + ECB_PARM_OFFSET() + )); + + IPX_GET_IPX_ECB( pEcb ); + + IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE)); + + _VwSPXListenForSequencedPacket( pEcb, + ECB_PARM_ADDRESS()); + +} + + +VOID +VwSPXSendSequencedPacket( + VOID + ) + +/*++ + +Routine Description: + + Sends a packet on an SPX connection + + This call is Asynchronous + +Arguments: + + Inputs + BX 16h + DX Connection ID + ES:SI ECB address + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + WORD connectionId = SPX_CONNECTION_PARM(); + LPECB pEcb; + + CHECK_INTERRUPTS("VwSPXSendSequencedPacket""VwSPXSendSequencedPacket"); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXSendSequencedPacket, + IPXDBG_LEVEL_INFO, + "VwSPXSendSequencedPacket(%04x, %04x:%04x)\n", + connectionId, + getES(), + getSI() + )); + + IPX_GET_IPX_ECB( pEcb ); + + IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE)); + + _VwSPXSendSequencedPacket( connectionId, + pEcb, + ECB_PARM_ADDRESS() ); + +} + + +VOID +VwSPXTerminateConnection( + VOID + ) + +/*++ + +Routine Description: + + Terminates a connection + + This call is Asynchronous + +Arguments: + + Inputs + BX 13h + DX Connection ID + ES:SI ECB Address + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + WORD connectionId = SPX_CONNECTION_PARM(); + LPECB pEcb; + + CHECK_INTERRUPTS("VwSPXTerminateConnection"); + + IPX_GET_IPX_ECB( pEcb ); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXTerminateConnection, + IPXDBG_LEVEL_INFO, + "VwSPXTerminateConnection(%04x, %04x:%04x)\n", + connectionId, + ECB_PARM_SEGMENT(), + ECB_PARM_OFFSET() + )); + + _VwSPXTerminateConnection(connectionId, pEcb, ECB_PARM_ADDRESS()); +} diff --git a/private/nw/vwipxspx/dll/vwinapi.c b/private/nw/vwipxspx/dll/vwinapi.c new file mode 100644 index 000000000..303be996c --- /dev/null +++ b/private/nw/vwipxspx/dll/vwinapi.c @@ -0,0 +1,1185 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwinapi.c + +Abstract: + + ntVdm netWare (Vw) IPX/SPX Functions + + Contains handlers for WOW IPX/SPX calls (netware functions). The IPX/SPX + APIs use WinSock to perform the actual operations + + Contents: + VWinIPXCancelEvent + VWinIPXCloseSocket + VWinIPXDisconnectFromTarget + VWinIPXGetInternetworkAddress + VWinIPXGetIntervalMarker + VWinIPXGetLocalTarget + VWinIPXGetLocalTargetAsync + VWinIPXGetMaxPacketSize + VWinIPXInitialize + VWinIPXListenForPacket + VWinIPXOpenSocket + VWinIPXRelinquishControl + VWinIPXScheduleIPXEvent + VWinIPXSendPacket + VWinIPXSPXDeinit + + VWinSPXAbortConnection + VWinSPXEstablishConnection + VWinSPXGetConnectionStatus + VWinSPXInitialize + VWinSPXListenForConnection + VWinSPXListenForSequencedPacket + VWinSPXSendSequencedPacket + VWinSPXTerminateConnection + +Author: + + Yi-Hsin Sung ( yihsins ) 28-Oct-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 28-Oct-1993 yihsins + Created + +--*/ + +#include "vw.h" +#pragma hdrstop + +// +// functions +// + + +WORD +VWinIPXCancelEvent( + IN DWORD IPXTaskID, + IN LPECB pEcb + ) + +/*++ + +Routine Description: + + Cancels event described by an ECB + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + pECB + +Return Value: + + 00h Success + F9h Can't cancel ECB + FFh ECB not in use + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXCancelEvent, + IPXDBG_LEVEL_INFO, + "VWinIPXCancelEvent\n" + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + return _VwIPXCancelEvent( pEcb ); +} + + +VOID +VWinIPXCloseSocket( + IN DWORD IPXTaskID, + IN WORD socketNumber + ) + +/*++ + +Routine Description: + + Closes a socket and cancels any outstanding events on the socket. + Closing an unopened socket does not return an error + ESRs in cancelled ECBs are not called + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + socketNumber + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXCloseSocket, + IPXDBG_LEVEL_INFO, + "VWinIPXCloseSocket(%#x)\n", + B2LW(socketNumber) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwIPXCloseSocket( socketNumber ); +} + + +VOID +VWinIPXDisconnectFromTarget( + IN DWORD IPXTaskID, + IN LPBYTE pNetworkAddress + ) + +/*++ + +Routine Description: + + Performs no action for NTVDM IPX + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + pNetworkAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXDisconnectFromTarget, + IPXDBG_LEVEL_INFO, + "VWinIPXDisconnectFromTarget\n" + )); +} + + +VOID +VWinIPXGetInternetworkAddress( + IN DWORD IPXTaskID, + OUT LPINTERNET_ADDRESS pNetworkAddress + ) + +/*++ + +Routine Description: + + Returns a buffer containing the net number and node number for this + station. + + This function cannot return an error (!) + + Assumes: 1. GetInternetAddress has been successfully called in the + DLL initialization phase + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + + Outputs + pNetworkAddress + +Return Value: + + None. + +--*/ + +{ + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetInternetworkAddress, + IPXDBG_LEVEL_INFO, + "VWinIPXGetInternetworkAddress\n" + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwIPXGetInternetworkAddress( pNetworkAddress ); + +} + + +WORD +VWinIPXGetIntervalMarker( + IN DWORD IPXTaskID + ) + +/*++ + +Routine Description: + + Just returns the tick count maintained by Asynchronous Event Scheduler + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + + Outputs + +Return Value: + + The tick count. + +--*/ + +{ + WORD intervalMarker = _VwIPXGetIntervalMarker(); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetIntervalMarker, + IPXDBG_LEVEL_INFO, + "VWinIPXGetIntervalMarker: Returning %04x\n", + intervalMarker + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + return intervalMarker; +} + + +WORD +VWinIPXGetLocalTarget( + IN DWORD IPXTaskID, + IN LPBYTE pNetworkAddress, + OUT LPBYTE pImmediateAddress, + OUT ULPWORD pTransportTime + ) + +/*++ + +Routine Description: + + Given a target address of the form (network address {4}, node address {6}), + returns the node address of the target if on the same network, or the node + address of the router which knows how to get to the next hop in reaching the + eventual target + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + pNetworkAddress + + Outputs + pImmediateAddress + pTransportTime + + +Return Value: + + 00h Success + F1h Ipx/Spx Not Initialized + FAh No path to destination node found + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetLocalTarget, + IPXDBG_LEVEL_INFO, + "VWinIPXGetLocalTarget\n" + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + return _VwIPXGetLocalTarget( pNetworkAddress, + pImmediateAddress, + pTransportTime ); +} + + +WORD +VWinIPXGetLocalTargetAsync( + IN LPBYTE pSendAGLT, + OUT LPBYTE pListenAGLT, + IN WORD windowsHandle + ) + +/*++ + +Routine Description: + + description-of-function. + + This call is Asynchronous + +Arguments: + + pSendAGLT + pListenAGLT + windowsHandle + +Return Value: + + 00h Success + F1h Ipx/Spx Not Initialized + FAh No Local Target Identified + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VWinIPXGetLocalTargetAsync\n" + )); + + return IPX_SUCCESS; // return success for now +} + + +WORD +VWinIPXGetMaxPacketSize( + VOID + ) + +/*++ + +Routine Description: + + Returns the maximum packet size the underlying network can handle + + Assumes: 1. A successfull call to GetMaxPacketSize has been made during + DLL initialization + 2. Maximum packet size is constant + + This call is Synchronous + +Arguments: + + Inputs + None. + +Return Value: + + The max packet size. + +--*/ + +{ + // + // this is a WORD function in DOS and Windows: always return MaxPacketSize + // in AX + // + + WORD maxPacketSize = _VwIPXGetMaxPacketSize( NULL ); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetMaxPacketSize, + IPXDBG_LEVEL_INFO, + "VWinIPXGetMaxPacketSize: PacketSize=%d\n", + maxPacketSize + )); + + return maxPacketSize; +} + + +WORD +VWinIPXInitialize( + IN OUT ULPDWORD pIPXTaskID, + IN WORD maxECBs, + IN WORD maxPacketSize + ) + +/*++ + +Routine Description: + + Get the entry address for the IPX Interface. + +Arguments: + + Inputs + maxECBs + maxPacketSize + + Output + pIPXTaskID + +Return Value: + + 00h Success + F0h Ipx NotInstalled + F1h Ipx/Spx Not Initialized + F2h No Dos Memory + F3h No Free Ecb + F4h Lock Failed + F5h Over the maximum limit + F6h Ipx/Spx Previously Initialized + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VWinIPXInitialize (MaxECBs=%04x, MaxPacketSize=%04x)\n", + maxECBs, + maxPacketSize + )); + + UNREFERENCED_PARAMETER( maxECBs ); // ignore for now + UNREFERENCED_PARAMETER( maxPacketSize ); // ignore for now + +// *pIPXTaskID = 0; // BUGBUG Can we ignore this? + + return IPX_SUCCESS; +} + + +VOID +VWinIPXListenForPacket( + IN DWORD IPXTaskID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Queue a listen request against a socket. All listen requests will be + completed asynchronously, unless cancelled by app + + This call is Asynchronous + +Arguments: + + Inputs + IPXTaskID + pEcb + EcbAddress + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXListenForPacket, + IPXDBG_LEVEL_INFO, + "VWinIPXListenForPacket(%04x:%04x)\n", + HIWORD(EcbAddress), + LOWORD(EcbAddress) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + (VOID) _VwIPXListenForPacket( pEcb, EcbAddress ); +} + + +WORD +VWinIPXOpenSocket( + IN DWORD IPXTaskID, + IN OUT ULPWORD pSocketNumber, + IN BYTE socketType + ) + +/*++ + +Routine Description: + + Opens a socket for use by IPX or SPX.Puts the socket into non-blocking mode. + The socket will be bound to IPX. + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + *pSocketNumber + socketType - Socket Longevity flag + + Outputs + pSocketNumber - Assigned socket number + +Return Value: + + 00h Success + F0h Ipx Not Installed + F1h Ipx/Spx Not Initialized + FEh Socket table full + FFh Socket already open + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXOpenSocket, + IPXDBG_LEVEL_INFO, + "VwIPXOpenSocket(Life=%02x, Socket=%04x)\n", + socketType, + B2LW(*pSocketNumber) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + return _VwIPXOpenSocket( pSocketNumber, + socketType, + 0 ); + +} + + +VOID +VWinIPXRelinquishControl( + VOID + ) + +/*++ + +Routine Description: + + Just sleep for a nominal amount. Netware seems to be dependent on the + default setting of the PC clock, so one timer tick (1/18 second) would + seem to be a good value + + This call is Synchronous + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXRelinquishControl, + IPXDBG_LEVEL_INFO, + "VWinIPXRelinquishControl\n" + )); + + _VwIPXRelinquishControl(); +} + + +VOID +VWinIPXScheduleIPXEvent( + IN DWORD IPXTaskID, + IN WORD time, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Schedules a an event to occur in some number of ticks. When the tick count + reaches 0, the ECB InUse field is cleared and any ESR called + + This call is Asynchronous + +Arguments: + + Inputs + IPXTaskID + time + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXScheduleIPXEvent, + IPXDBG_LEVEL_INFO, + "VWinIPXScheduleIPXEvent(%04x:%04x, Time:%04x)\n", + HIWORD( EcbAddress ), + LOWORD( EcbAddress ), + time + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwIPXScheduleIPXEvent( time, pEcb, EcbAddress ); +} + + +VOID +VWinIPXSendPacket( + IN DWORD IPXTaskID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Sends a packet to the target machine/router. This call can be made on a + socket that is not open + + The app must have filled in the following IPX_ECB fields: + + EsrAddress + Socket + ImmediateAddress + FragmentCount + fragment descriptor fields + + and the following IPX_PACKET fields: + + PacketType + Destination.Net + Destination.Node + Destination.Socket + + This call is Asynchronous + +Arguments: + + Inputs + IPXTaskID + pEcb + EcbAddress + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXSendPacket, + IPXDBG_LEVEL_INFO, + "VWinIPXSendPacket(%04x:%04x)\n", + HIWORD( EcbAddress ), + LOWORD( EcbAddress ) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwIPXSendPacket( pEcb, EcbAddress, 0); +} + + +WORD +VWinIPXSPXDeinit( + IN DWORD IPXTaskID + ) + +/*++ + +Routine Description: + + Release any resources allocated to an application by NWIPXSPX.DLL + for use by other applications. + + This call is Synchronous + +Arguments: + + None. + +Return Value: + + 00h Successful + F1h IPX/SPX Not Initialized + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwIPXSPXDeinit\n" + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + return IPX_SUCCESS; +} + + +VOID +VWinSPXAbortConnection( + IN WORD SPXConnectionID + ) + +/*++ + +Routine Description: + + Abort an SPX connection. + + This call is Synchronous + +Arguments: + + SPXConnectionID + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXAbortConnection, + IPXDBG_LEVEL_INFO, + "VWinSPXAbortConnection(%04x)\n", + SPXConnectionID + )); + + _VwSPXAbortConnection(SPXConnectionID); +} + + +WORD +VWinSPXEstablishConnection( + IN DWORD IPXTaskID, + IN BYTE retryCount, + IN BYTE watchDog, + OUT ULPWORD pSPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Establish a connection with a listening socket. + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + retryCount + watchDog + pEcb + EcbAddress + + Outputs + pSPXConnectionID + pEcb + +Return Value: + + 00h Success + EFh Connection Table Full + F1h IPX/SPX Not Initialized + FDh Malformed Packet + FFh Socket Not Opened + +--*/ + +{ + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_INFO, + "VWinSPXEstablishConnection(%02x, %02x, %04x:%04x)\n", + retryCount, + watchDog, + HIWORD(EcbAddress), + LOWORD(EcbAddress) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + return _VwSPXEstablishConnection( retryCount, + watchDog, + pSPXConnectionID, + pEcb, + EcbAddress ); +} + + +WORD +VWinSPXGetConnectionStatus( + IN DWORD IPXTaskID, + IN WORD SPXConnectionID, + IN LPSPX_CONNECTION_STATS pConnectionStats + ) + +/*++ + +Routine Description: + + Return the status of an SPX connection. + + This call is Synchronous + +Arguments: + + Inputs + IPXTaskID + SPXConnectionID + + Outputs + pConnectionStats + +Return Value: + + 00h Success + EEh Invalid Connection + F1h IPX/SPX Not Initialized + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXGetConnectionStatus, + IPXDBG_LEVEL_INFO, + "VWinSPXGetConnectionStatus\n" + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + return _VwSPXGetConnectionStatus( SPXConnectionID, + pConnectionStats ); +} + + +WORD +VWinSPXInitialize( + IN OUT ULPDWORD pIPXTaskID, + IN WORD maxECBs, + IN WORD maxPacketSize, + OUT LPBYTE pMajorRevisionNumber, + OUT LPBYTE pMinorRevisionNumber, + OUT ULPWORD pMaxConnections, + OUT ULPWORD pAvailableConnections + ) + +/*++ + +Routine Description: + + Informs the app that SPX is present on this station + + This call is Synchronous + +Arguments: + + pIPXTaskID - on input, specifies how resources will be + allocated: + + 0x00000000 - directly to calling application + 0xFFFFFFFE - directly to calling application, + but multiple initializations are + allowed + 0xFFFFFFFF - resources allocated in a pool for + multiple applications + maxECBs - maximum number of outstanding ECBs + maxPacketSize - maximum packet size to be sent by the app + pMajorRevisionNumber - returned SPX major version # + pMinorRevisionNumber - returned SPX minor version # + pMaxConnections - maximum connections supported by this SPX version + pAvailableConnections - number of connections available to this app + +Return Value: + + WORD + 0x0000 SPX not installed + 0x00F1 IPX/SPX not installed + 0x00F2 no DOS memory + 0x00F3 no free ECBs + 0x00F4 lock failed + 0x00F5 exceeded maximum limit + 0x00F6 IPX/SPX already initialized + 0x00FF SPX installed + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXInitialize, + IPXDBG_LEVEL_INFO, + "VWinSPXInitialize\n" + )); + + UNREFERENCED_PARAMETER( maxECBs ); // ignore for now + UNREFERENCED_PARAMETER( maxPacketSize ); // ignore for now + + // + // do the same thing as 16-bit windows and return the task ID unchanged + // + +// *pIPXTaskID = 0; + + return _VwSPXInitialize( pMajorRevisionNumber, + pMinorRevisionNumber, + pMaxConnections, + pAvailableConnections ); +} + + +VOID +VWinSPXListenForConnection( + IN DWORD IPXTaskID, + IN BYTE retryCount, + IN BYTE watchDog, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Listens for an incoming connection request + + This call is Asynchronous + +Arguments: + + Inputs + IPXTaskID + retryCount + watchDogFlag + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForConnection, + IPXDBG_LEVEL_INFO, + "VWinSPXListenForConnection(%02x, %02x, %04x:%04x)\n", + retryCount, + watchDog, + HIWORD(EcbAddress), + LOWORD(EcbAddress) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwSPXListenForConnection( retryCount, + watchDog, + pEcb, + EcbAddress ); +} + + +VOID +VWinSPXListenForSequencedPacket( + IN DWORD IPXTaskID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Attempts to receive an SPX packet. + + This call is Asynchronous + +Arguments: + + Inputs + IPXTaskID + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForSequencedPacket, + IPXDBG_LEVEL_INFO, + "VWinSPXListenForSequencedPacket(%04x:%04x)\n", + HIWORD(EcbAddress), + LOWORD(EcbAddress) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwSPXListenForSequencedPacket( pEcb, + EcbAddress ); +} + + +VOID +VWinSPXSendSequencedPacket( + IN DWORD IPXTaskID, + IN WORD SPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Sends a packet on an SPX connection + + This call is Asynchronous + +Arguments: + + Inputs + IPXTaskID + SPXConnectionID + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXSendSequencedPacket, + IPXDBG_LEVEL_INFO, + "VWinSPXSendSequencedPacket(%04x, %04x:%04x)\n", + SPXConnectionID, + HIWORD(EcbAddress), + LOWORD(EcbAddress) + )); + + // ignore IPXTaskID for now + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwSPXSendSequencedPacket( SPXConnectionID, + pEcb, + EcbAddress ); +} + + +VOID +VWinSPXTerminateConnection( + IN DWORD IPXTaskID, + IN WORD SPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Terminate an SPX connection by passing a connection ID and an + ECB address to SPX. Then return control to the calling application. + + This call is Asynchronous + +Arguments: + + Inputs + IPXTaskID + SPXConnectionID + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXTerminateConnection, + IPXDBG_LEVEL_INFO, + "VWinSPXTerminateConnection(%04x, %04x:%04x)\n", + SPXConnectionID, + HIWORD(EcbAddress), + LOWORD(EcbAddress) + )); + + // ignore IPXTaskID for now + + UNREFERENCED_PARAMETER( IPXTaskID ); + + _VwSPXTerminateConnection(SPXConnectionID, pEcb, EcbAddress); +} diff --git a/private/nw/vwipxspx/dll/vwinapi.h b/private/nw/vwipxspx/dll/vwinapi.h new file mode 100644 index 000000000..d706d7c87 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwinapi.h @@ -0,0 +1,186 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwinapi.h + +Abstract: + + Contains function prototypes for WIN IPX/SPX functions + +Author: + + +Environment: + + User-mode Win32 + +Revision History: + + 28-Oct-1993 yihsins + Created + +--*/ + +WORD +VWinIPXCancelEvent( + IN DWORD IPXTaskID, + IN LPECB pEcb + ); + +VOID +VWinIPXCloseSocket( + IN DWORD IPXTaskID, + IN WORD socketNumber + ); + +VOID +VWinIPXDisconnectFromTarget( + IN DWORD IPXTaskID, + OUT LPBYTE pNetworkAddress + ); + +VOID +VWinIPXGetInternetworkAddress( + IN DWORD IPXTaskID, + OUT LPINTERNET_ADDRESS pNetworkAddress + ); + +WORD +VWinIPXGetIntervalMarker( + IN DWORD IPXTaskID + ); + +WORD +VWinIPXGetLocalTarget( + IN DWORD IPXTaskID, + IN LPBYTE pNetworkAddress, + OUT LPBYTE pImmediateAddress, + OUT ULPWORD pTransportTime + ); + +WORD +VWinIPXGetLocalTargetAsync( + IN LPBYTE pSendAGLT, + OUT LPBYTE pListenAGLT, + IN WORD windowsHandle + ); + +WORD +VWinIPXGetMaxPacketSize( + VOID + ); + +WORD +VWinIPXInitialize( + IN OUT ULPDWORD pIPXTaskID, + IN WORD maxECBs, + IN WORD maxPacketSize + ); + +VOID +VWinIPXListenForPacket( + DWORD IPXTaskID, + LPECB pEcb, + ECB_ADDRESS EcbAddress + ); + +WORD +VWinIPXOpenSocket( + IN DWORD IPXTaskID, + IN OUT ULPWORD pSocketNumber, + IN BYTE socketType + ); + +VOID +VWinIPXRelinquishControl( + VOID + ); + +VOID +VWinIPXScheduleIPXEvent( + IN DWORD IPXTaskID, + IN WORD time, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +VWinIPXSendPacket( + IN DWORD IPXTaskID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +WORD +VWinIPXSPXDeinit( + IN DWORD IPXTaskID + ); + +VOID +VWinSPXAbortConnection( + IN WORD SPXConnectionID + ); + +WORD +VWinSPXEstablishConnection( + IN DWORD IPXTaskID, + IN BYTE retryCount, + IN BYTE watchDog, + OUT ULPWORD pSPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +WORD +VWinSPXGetConnectionStatus( + IN DWORD IPXTaskID, + IN WORD SPXConnectionID, + IN LPSPX_CONNECTION_STATS pConnectionStats + ); + +WORD +VWinSPXInitialize( + IN OUT DWORD UNALIGNED* pIPXTaskID, + IN WORD maxECBs, + IN WORD maxPacketSize, + OUT LPBYTE pMajorRevisionNumber, + OUT LPBYTE pMinorRevisionNumber, + OUT WORD UNALIGNED* pMaxConnections, + OUT WORD UNALIGNED* pAvailableConnections + ); + +VOID +VWinSPXListenForConnection( + IN DWORD IPXTaskID, + IN BYTE retryCount, + IN BYTE watchDog, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + + +VOID +VWinSPXListenForSequencedPacket( + IN DWORD IPXTaskID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +VWinSPXSendSequencedPacket( + IN DWORD IPXTaskID, + IN WORD SPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +VWinSPXTerminateConnection( + IN DWORD IPXTaskID, + IN WORD SPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); diff --git a/private/nw/vwipxspx/dll/vwint.h b/private/nw/vwipxspx/dll/vwint.h new file mode 100644 index 000000000..25ba92631 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwint.h @@ -0,0 +1,146 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwint.h + +Abstract: + + Contains internal function prototypes used by DOS/WIN IPX/SPX functions + +Author: + + Yi-Hsin Sung (yihsins) 28-Oct-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 28-Oct-1993 yihsins + Created + +--*/ + +WORD +_VwIPXCancelEvent( + IN LPECB pEcb + ); + +VOID +_VwIPXCloseSocket( + IN WORD SocketNumber + ); + +VOID +_VwIPXGetInternetworkAddress( + OUT LPINTERNET_ADDRESS pNetworkAddress + ); + +WORD +_VwIPXGetIntervalMarker( + VOID + ); + +WORD +_VwIPXGetLocalTarget( + IN LPBYTE pNetworkAddress, + OUT LPBYTE pImmediateAddress, + OUT ULPWORD pTransportTime + ); + +WORD +_VwIPXGetMaxPacketSize( + OUT ULPWORD pRetryCount + ); + +WORD +_VwIPXListenForPacket( + IN OUT LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +WORD +_VwIPXOpenSocket( + IN OUT ULPWORD pSocketNumber, + IN BYTE SocketType, + IN WORD DosPDB + ); + +VOID +_VwIPXRelinquishControl( + VOID + ); + +VOID +_VwIPXScheduleIPXEvent( + IN WORD Time, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +_VwIPXSendPacket( + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress, + IN WORD DosPDB + ); + +VOID +_VwSPXAbortConnection( + IN WORD SPXConnectionID + ); + +WORD +_VwSPXEstablishConnection( + IN BYTE RetryCount, + IN BYTE WatchDog, + OUT ULPWORD pSPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +WORD +_VwSPXGetConnectionStatus( + IN WORD SPXConnectionID, + OUT LPSPX_CONNECTION_STATS pStats + ); + +WORD +_VwSPXInitialize( + OUT ULPBYTE pMajorRevisionNumber, + OUT ULPBYTE pMinorRevisionNumber, + OUT ULPWORD pMaxConnections, + OUT ULPWORD pAvailableConnections + ); + +VOID +_VwSPXListenForConnection( + IN BYTE RetryCount, + IN BYTE WatchDog, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +_VwSPXListenForSequencedPacket( + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +_VwSPXSendSequencedPacket( + IN WORD SPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); + +VOID +_VwSPXTerminateConnection( + IN WORD SPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ); diff --git a/private/nw/vwipxspx/dll/vwipx.c b/private/nw/vwipxspx/dll/vwipx.c new file mode 100644 index 000000000..64ddcaf34 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwipx.c @@ -0,0 +1,839 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwipx.c + +Abstract: + + ntVdm netWare (Vw) IPX/SPX Functions + + Vw: The peoples' network + + Internal worker routines for DOS/WOW IPX calls (netware functions). + The IPX APIs use WinSock to perform the actual operations + + Contents: + _VwIPXCancelEvent + _VwIPXCloseSocket + _VwIPXGetInternetworkAddress + _VwIPXGetIntervalMarker + _VwIPXGetLocalTarget + _VwIPXGetMaxPacketSize + _VwIPXListenForPacket + _VwIPXOpenSocket + _VwIPXRelinquishControl + _VwIPXScheduleIPXEvent + _VwIPXSendPacket + +Author: + + Richard L Firth (rfirth) 30-Sep-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 30-Sep-1993 rfirth + Created + +--*/ + +#include "vw.h" +#pragma hdrstop + +extern WORD AesTickCount; + +// +// functions +// + + +WORD +_VwIPXCancelEvent( + IN LPECB pEcb + ) + +/*++ + +Routine Description: + + Internal routine shared by DOS and WIN that cancels event + described by an ECB + + This call is Synchronous + +Arguments: + + Inputs + pECB + +Return Value: + + 00h Success + F9h Can't cancel ECB + FFh ECB not in use + +--*/ + +{ + LPXECB pXecb; + WORD status; + + // + // BUGBUG: find out what real IPX does if given a NULL pointer + // + + if (!pEcb) { + return IPX_ECB_NOT_IN_USE; + } + + // + // BUGBUG: if this ECB references an SPX action then return 0xF9 + // + + // + // if the ECB is still in the state we left it then LinkAddress will be the + // address of the XECB which subsequently points back to the ECB. If both + // these pan out then we have an ECB which we have at least seen before. + // Maybe we can cancel it? + // + // Note: we grab the serialization semaphore here just in case the AES thread + // is about to complete the ECB + // + + status = IPX_CANNOT_CANCEL; + RequestMutex(); + pXecb = (LPXECB)pEcb->LinkAddress; + if (pXecb) { + try { + if (pXecb->Ecb == pEcb) { + status = IPX_SUCCESS; + + // + // pXecb ok: increase reference count in case other thread tries + // to deallocate it while we're trying to cancel it + // + + ++pXecb->RefCount; + } + } except(1) { + + // + // bad pointer: bogus ECB + // + + } + } else { + + // + // NULL pointer: event probably completed already + // + + status = IPX_ECB_NOT_IN_USE; + } + ReleaseMutex(); + if (status == IPX_SUCCESS) { + + ECB_CANCEL_ROUTINE cancelRoutine; + + // + // we have an ECB to cancel. If we still have it, it will be on one of + // the socket queues, the timer list or the async completion list. If + // the latter we are in a race. Treat such events as already happened. + // We will cancel events on the timer list and queued send and receive + // events only + // + + switch (pXecb->QueueId) { + case NO_QUEUE: + status = ECB_CC_CANCELLED; + goto cancel_exit; + + case ASYNC_COMPLETION_QUEUE: + cancelRoutine = CancelAsyncEvent; + break; + + case TIMER_QUEUE: + cancelRoutine = CancelTimerEvent; + break; + + case SOCKET_LISTEN_QUEUE: + case SOCKET_SEND_QUEUE: + cancelRoutine = CancelSocketEvent; + break; + + case CONNECTION_CONNECT_QUEUE: + case CONNECTION_SEND_QUEUE: + + // + // SPXEstablishConnection and SPXSendSequencedPacket cannot be + // cancelled using IPXCancelEvent + // + + status = ECB_CC_CANNOT_CANCEL; + goto cancel_exit; + + case CONNECTION_ACCEPT_QUEUE: + case CONNECTION_LISTEN_QUEUE: + cancelRoutine = CancelConnectionEvent; + break; + } + return cancelRoutine(pXecb); + } + + // + // app tried to sneak us an unknown ECB, -OR- the ECB was stomped on, + // destroying the LinkAddress and hence the address of the XECB. We + // could search the various lists looking for an XECB whose Ecb field + // matches pEcb, but if the app has scribbled over the ECB when we + // (make that Novell) told it not to, chances are it would fail real + // well on DOS. Probable worst case is that the app is terminating and + // the ECB may sometime later call an ESR which won't be there. Crashola + // + +cancel_exit: + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXCancelEvent, + IPXDBG_LEVEL_ERROR, + "VwIPXCancelEvent: cannot find/cancel ECB %04x:%04x\n", + HIWORD(pEcb), + LOWORD(pEcb) + )); + + pEcb->CompletionCode = (BYTE)status; + pEcb->InUse = ECB_IU_NOT_IN_USE; + return status; +} + + +VOID +_VwIPXCloseSocket( + IN WORD socketNumber + ) + +/*++ + +Routine Description: + + Closes a socket and cancels any outstanding events on the socket. + Closing an unopened socket does not return an error + ESRs in cancelled ECBs are not called + + This call is Synchronous + +Arguments: + + Inputs + socketNumber + +Return Value: + + None. + +--*/ + +{ + LPSOCKET_INFO pSocketInfo; + + pSocketInfo = FindSocket(socketNumber); + if (pSocketInfo != NULL) { + KillSocket(pSocketInfo); + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXCloseSocket, + IPXDBG_LEVEL_WARNING, + "_VwIPXCloseSocket: can't locate socket 0x%04x\n", + B2LW(socketNumber) + )); + + } +} + + +VOID +_VwIPXGetInternetworkAddress( + IN LPINTERNET_ADDRESS pNetworkAddress + ) + +/*++ + +Routine Description: + + Returns a buffer containing the net number and node number for this + station. + + This function cannot return an error (!) + + Assumes: 1. GetInternetAddress has been successfully called in the + DLL initialization phase + + This call is Synchronous + +Arguments: + + Inputs + Nothing. + + Outputs + pNetworkAddress + +Return Value: + + None. + +--*/ + +{ + CopyMemory((LPBYTE)pNetworkAddress, + (LPBYTE)&MyInternetAddress.sa_netnum, + sizeof(*pNetworkAddress) + ); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetInternetworkAddress, + IPXDBG_LEVEL_INFO, + "VwIPXGetInternetworkAddress: %02x-%02x-%02x-%02x : %02x-%02x-%02x-%02x-%02x-%02x\n", + pNetworkAddress->Net[0] & 0xff, + pNetworkAddress->Net[1] & 0xff, + pNetworkAddress->Net[2] & 0xff, + pNetworkAddress->Net[3] & 0xff, + pNetworkAddress->Node[0] & 0xff, + pNetworkAddress->Node[1] & 0xff, + pNetworkAddress->Node[2] & 0xff, + pNetworkAddress->Node[3] & 0xff, + pNetworkAddress->Node[4] & 0xff, + pNetworkAddress->Node[5] & 0xff + )); + +} + + +WORD +_VwIPXGetIntervalMarker( + VOID + ) + +/*++ + +Routine Description: + + Just returns the tick count maintained by Asynchronous Event Scheduler + + This call is Synchronous + +Arguments: + + None. + +Return Value: + + The tick count. + +--*/ + +{ + Sleep(0); + return AesTickCount; +} + + +WORD +_VwIPXGetLocalTarget( + IN LPBYTE pNetworkAddress, + OUT LPBYTE pImmediateAddress, + OUT ULPWORD pTransportTime + ) + +/*++ + +Routine Description: + + Given a target address of the form (network address {4}, node address {6}), + returns the node address of the target if on the same network, or the node + address of the router which knows how to get to the next hop in reaching the + eventual target + + This call is Synchronous + +Arguments: + + Inputs + pNetworkAddress + + Outputs + pImmediateAddress + pTransportTime + +Return Value: + + 00h Success + F1h Ipx/Spx Not Initialized + FAh No path to destination node found + +--*/ + +{ + // + // the transport handles real routing, so we always return the immediate + // address as the target address. The transport will only look at the + // target when routing + // + + CopyMemory( pImmediateAddress, + pNetworkAddress + 4, + 6 + ); + + *pTransportTime = 1; // ticks + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXGetLocalTarget, + IPXDBG_LEVEL_INFO, + "VwIPXGetLocalTarget: IN: %02x-%02x-%02x-%02x:%02x-%02x-%02x-%02x-%02x-%02x OUT: %02x-%02x-%02x-%02x-%02x-%02x\n", + ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[0] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[1] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[2] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[3] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[0] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[1] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[2] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[3] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[4] & 0xff, + ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[5] & 0xff, + pImmediateAddress[0] & 0xff, + pImmediateAddress[1] & 0xff, + pImmediateAddress[2] & 0xff, + pImmediateAddress[3] & 0xff, + pImmediateAddress[4] & 0xff, + pImmediateAddress[5] & 0xff + )); + + return IPX_SUCCESS; +} + + + +WORD +_VwIPXGetMaxPacketSize( + OUT ULPWORD pRetryCount + ) + +/*++ + +Routine Description: + + Returns the maximum packet size the underlying network can handle + + Assumes: 1. A successfull call to GetMaxPacketSize has been made during + DLL initialization + 2. Maximum packet size is constant + + This call is Synchronous + +Arguments: + + Outputs + pRetryCount + + +Return Value: + + The maximum packet size. + +--*/ + +{ + if ( pRetryCount ) { + *pRetryCount = 5; // arbitrary? + } + return MyMaxPacketSize; +} + + +WORD +_VwIPXListenForPacket( + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Queue a listen request against a socket. All listen requests will be + completed asynchronously, unless cancelled by app + + This call is Asynchronous + +Arguments: + + Inputs + pEcb + EcbAddress + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress); + LPSOCKET_INFO pSocketInfo; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXListenForPacket, + IPXDBG_LEVEL_INFO, + "_VwIPXListenForPacket(%04x:%04x) socket=%04x ESR=%04x:%04x\n", + HIWORD(EcbAddress), + LOWORD(EcbAddress), + B2LW(pXecb->SocketNumber), + HIWORD(pXecb->EsrAddress), + LOWORD(pXecb->EsrAddress) + )); + + // + // don't know what real IPX/SPX does if it gets a NULL pointer + // + + if (!pXecb) { + return IPX_BAD_REQUEST; + } + + // + // the socket must be open already before we can perform a listen + // + + pSocketInfo = FindSocket(pXecb->SocketNumber); + + // + // we also return NON_EXISTENT_SOCKET (0xFF) if the socket is in use for SPX + // + + // + // BUGBUG: There is nothing in the farty netware documentation that explains + // what gets returned if this is the case, only a warning about IPX listens + // and sends can't be made on a socket open for SPX. Really definitive + // + + if (!pSocketInfo || pSocketInfo->SpxSocket) { + CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET); + return IPX_NON_EXISTENT_SOCKET; + } + + // + // initiate the receive. It may complete if there is data waiting or an + // error occurs, otherwise the ECB will be placed in a receive pending queue + // for this socket + // + + if (GetIoBuffer(pXecb, FALSE, IPX_HEADER_LENGTH)) { + pXecb->Ecb->InUse = ECB_IU_LISTENING; + IpxReceiveFirst(pXecb, pSocketInfo); + } else { + CompleteEcb(pXecb, ECB_CC_CANCELLED); + } + + // + // return success. Any errors will be communicated asynchronously - either + // indirectly by relevant values in CompletionCode and InUse fields of the + // ECB or directly by an ESR callback + // + + return IPX_SUCCESS; +} + + +WORD +_VwIPXOpenSocket( + IN OUT ULPWORD pSocketNumber, + IN BYTE socketType, + IN WORD dosPDB + ) + +/*++ + +Routine Description: + + Opens a socket for use by IPX or SPX. Puts the socket into non-blocking mode. + The socket will be bound to IPX + + This call is Synchronous + +Arguments: + Inputs + *pSocketNumber - The requested socket number + socketType - Socket Longevity flag + dosPDB - DOS PDB. This parameter is not part of the IPX API. + Added because we need to remember which DOS executable created + the socket: we need to clean-up short-lived sockets when the + executable terminates + + Outputs + pSocketNumber - Assigned socket number + +Return Value: + + 00h Success + F0h Ipx Not Installed + F1h Ipx/Spx Not Initialized + FEh Socket table full + FFh Socket already open + +--*/ + +{ + LPSOCKET_INFO pSocketInfo; + WORD status; + + if ((pSocketInfo = AllocateSocket()) == NULL) { + return IPX_SOCKET_TABLE_FULL; + } + status = CreateSocket(SOCKET_TYPE_IPX, pSocketNumber, &pSocketInfo->Socket); + if (status == IPX_SUCCESS) { + + // + // set up the SOCKET_INFO fields and add it to our list of open sockets + // + + pSocketInfo->Owner = dosPDB; + pSocketInfo->SocketNumber = *pSocketNumber; + + // + // treat socketType == 0 as short-lived, anything else as long-lived. + // There doesn't appear to be an error return if the flag is not 0 or 0xFF + // + + pSocketInfo->LongLived = (BOOL)(socketType != 0); + QueueSocket(pSocketInfo); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXOpenSocket, + IPXDBG_LEVEL_INFO, + "_VwIPXOpenSocket: created socket %04x\n", + B2LW(*pSocketNumber) + )); + + } else { + DeallocateSocket(pSocketInfo); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_IPXOpenSocket, + IPXDBG_LEVEL_ERROR, + "_VwIPXOpenSocket: Failure: returning %x\n", + status + )); + + } + return status; +} + + +VOID +_VwIPXRelinquishControl( + VOID + ) + +/*++ + +Routine Description: + + Just sleep for a nominal amount. + + This call is Synchronous + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Sleep(0); +} + + + +VOID +_VwIPXScheduleIPXEvent( + IN WORD time, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Schedules a an event to occur in some number of ticks. When the tick count + reaches 0, the ECB InUse field is cleared and any ESR called + + This call is Asynchronous + +Arguments: + + Inputs + time - the delay time ( number of 1/18 second ticks ) + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress); + + ScheduleEvent(pXecb, time); +} + + +VOID +_VwIPXSendPacket( + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress, + IN WORD DosPDB + ) + +/*++ + +Routine Description: + + Sends a packet to the target machine/router. This call can be made on a + socket that is not open + + The app must have filled in the following IPX_ECB fields: + + EsrAddress + Socket + ImmediateAddress + FragmentCount + fragment descriptor fields + + and the following IPX_PACKET fields: + + PacketType + Destination.Net + Destination.Node + Destination.Socket + + This call is Asynchronous + +Arguments: + + Inputs + pEcb + EcbAddress + DosPDB + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress); + LPSOCKET_INFO pSocketInfo; + + // + // this function returns no immediate status so we must assume that the + // ECB pointer is valid + // + + // + // check the ECB for correctness + // + + if ((pXecb->Ecb->FragmentCount == 0) + || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < IPX_HEADER_LENGTH)) { + CompleteEcb(pXecb, ECB_CC_BAD_REQUEST); + return; + } + + // + // IPXSendPacket() can be called on an unopened socket: we must try to + // temporarily allocate the socket + // + // Q: Is the following scenario possible with real IPX: + // IPXSendPacket() on unopened socket X + // Send fails & gets queued + // app makes IPXOpenSocket() call on X; X gets opened + // + // Currently, we would create the temporary socket and fail IPXOpenSocket() + // because it is already open! + // + + pSocketInfo = FindSocket(pXecb->SocketNumber); + if (!pSocketInfo) { + + // + // BUGBUG: when is temporary socket deleted? After send completed? + // when app dies? when? Novell documentation is not specific (used + // to say something else :-)) + // + + pSocketInfo = AllocateTemporarySocket(); + if (pSocketInfo) { + + // + // set up the SOCKET_INFO fields and add it to our list of open sockets + // + + pSocketInfo->Owner = DosPDB; + + // + // temporary sockets are always short-lived + // + + pSocketInfo->LongLived = FALSE; + QueueSocket(pSocketInfo); + + } else { + + // + // BUGBUG: completion code??? + // + + CompleteEcb(pXecb, ECB_CC_SOCKET_TABLE_FULL); + return; + } + } else if (pSocketInfo->SpxSocket) { + + // + // see complaint in IPXListenForPacket + // + // can't make IPX requests on socket opened for SPX + // + // BUGBUG: completion code?? + // + + CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET); + return; + } + + // + // start the send: tries to send the data in one go. Either succeeds, fails + // with an error, or queues the ECB for subsequent attempts via AES/IPX + // deferred processing. + // + // In the first 2 cases, the ECB has been completed already + // + + StartIpxSend(pXecb, pSocketInfo); +} diff --git a/private/nw/vwipxspx/dll/vwipx.h b/private/nw/vwipxspx/dll/vwipx.h new file mode 100644 index 000000000..351351eab --- /dev/null +++ b/private/nw/vwipxspx/dll/vwipx.h @@ -0,0 +1,122 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vwipx.h + +Abstract: + + Contains function prototypes for VWIPX.C + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +VOID +VwIPXCancelEvent( + VOID + ); + +VOID +VwIPXCloseSocket( + VOID + ); + +VOID +VwIPXDisconnectFromTarget( + VOID + ); + +VOID +VwIPXGenerateChecksum( + VOID + ); + +VOID +VwIPXGetInformation( + VOID + ); + +VOID +VwIPXGetInternetworkAddress( + VOID + ); + +VOID +VwIPXGetIntervalMarker( + VOID + ); + +VOID +VwIPXGetLocalTarget( + VOID + ); + +VOID +VwIPXGetLocalTargetAsync( + VOID + ); + +VOID +VwIPXGetMaxPacketSize( + VOID + ); + +VOID +VwIPXInitialize( + VOID + ); + +VOID +VwIPXListenForPacket( + VOID + ); + +VOID +VwIPXOpenSocket( + VOID + ); + +VOID +VwIPXRelinquishControl( + VOID + ); + +VOID +VwIPXScheduleAESEvent( + VOID + ); + +VOID +VwIPXScheduleIPXEvent( + VOID + ); + +VOID +VwIPXSendPacket( + VOID + ); + +VOID +VwIPXSendWithChecksum( + VOID + ); + +VOID +VwIPXSPXDeinit( + VOID + ); + +VOID +VwIPXVerifyChecksum( + VOID + ); diff --git a/private/nw/vwipxspx/dll/vwipxspx.def b/private/nw/vwipxspx/dll/vwipxspx.def new file mode 100644 index 000000000..c62a71f84 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwipxspx.def @@ -0,0 +1,64 @@ +LIBRARY VWIPXSPX + +DESCRIPTION 'WOW Network Support - IPX/SPX APIs' + +EXPORTS + VwInitialize + VwDispatcher + + VwIPXCancelEvent + VwIPXCloseSocket + VwIPXDisconnectFromTarget + VwIPXGenerateChecksum + VwIPXGetInformation + VwIPXGetInternetworkAddress + VwIPXGetIntervalMarker + VwIPXGetLocalTarget + VwIPXGetLocalTargetAsync + VwIPXGetMaxPacketSize + VwIPXInitialize + VwIPXListenForPacket + VwIPXOpenSocket + VwIPXRelinquishControl + VwIPXScheduleAESEvent + VwIPXScheduleIPXEvent + VwIPXSendPacket + VwIPXSendWithChecksum + VwIPXSPXDeinit + VwIPXVerifyChecksum + VwSPXAbortConnection + VwSPXEstablishConnection + VwSPXGetConnectionStatus + VwSPXInitialize + VwSPXListenForConnection + VwSPXListenForSequencedPacket + VwSPXSendSequencedPacket + VwSPXTerminateConnection + + VWinEsrCallback + VWinInitialize + + VWinIPXCancelEvent + VWinIPXCloseSocket + VWinIPXDisconnectFromTarget + VWinIPXGetInternetworkAddress + VWinIPXGetIntervalMarker + VWinIPXGetLocalTarget + VWinIPXGetLocalTargetAsync + VWinIPXGetMaxPacketSize + VWinIPXInitialize + VWinIPXListenForPacket + VWinIPXOpenSocket + VWinIPXRelinquishControl + VWinIPXScheduleIPXEvent + VWinIPXSendPacket + VWinIPXSPXDeinit + + VWinSPXAbortConnection + VWinSPXEstablishConnection + VWinSPXGetConnectionStatus + VWinSPXInitialize + VWinSPXListenForConnection + VWinSPXListenForSequencedPacket + VWinSPXSendSequencedPacket + VWinSPXTerminateConnection diff --git a/private/nw/vwipxspx/dll/vwipxspx.h b/private/nw/vwipxspx/dll/vwipxspx.h new file mode 100644 index 000000000..01ad6ec5a --- /dev/null +++ b/private/nw/vwipxspx/dll/vwipxspx.h @@ -0,0 +1,586 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwipxspx.h + +Abstract: + + Contains manifests, typedefs, structures, macros for NTVDM IPX/SPX support + +Author: + + Richard L Firth (rfirth) 30-Sep-1993 + +Environment: + + Structures are expected to live in segmented VDM address space, but be + accessible from flat 32-bit protect mode. The VDM can be in real or protect + mode + +Revision History: + + 30-Sep-1993 rfirth + Created + +--*/ + +#ifndef _VWIPXSPX_H_ +#define _VWIPXSPX_H_ + +// +// FREE_OBJECT - in free version, just calls LocalFree. For debug version, fills +// memory with some arbitrary value, then frees the pointer and checks that what +// LocalFree thought that the pointer pointed at a valid, freeable object +// + +#if DBG + +#define FREE_OBJECT(p) {\ + FillMemory(p, sizeof(*p), 0xFF);\ + VWASSERT(LocalFree((HLOCAL)(p)), NULL);\ + } +#else + +#define FREE_OBJECT(p) VWASSERT(LocalFree((HLOCAL)(p)), NULL) + +#endif + +// +// simple function macros +// + +//#define AllocateXecb() (LPXECB)LocalAlloc(LPTR, sizeof(XECB)) +//#define DeallocateXecb(p) FREE_OBJECT(p) +#define AllocateBuffer(s) (LPVOID)LocalAlloc(LMEM_FIXED, (s)) +#define DeallocateBuffer(p) FREE_OBJECT(p) + +// +// pseudo-types for 16-bit addresses +// + +#define ESR_ADDRESS DWORD +#define ECB_ADDRESS DWORD + +// +// from Novell documentation, the default maximum open sockets. Max max is 150 +// + +#ifndef DEFAULT_MAX_OPEN_SOCKETS +#define DEFAULT_MAX_OPEN_SOCKETS 20 +#endif + +#ifndef MAX_OPEN_SOCKETS +#define MAX_OPEN_SOCKETS 150 +#endif + +#define SPX_INSTALLED 0xFF + +#define MAX_LISTEN_QUEUE_SIZE 5 // ? + +// +// misc. macros +// + +// +// B2LW, L2Bx - big-endian to little-endian macros +// + +#define B2LW(w) (WORD)(((WORD)(w) << 8) | ((WORD)(w) >> 8)) +#define B2LD(d) (DWORD)(B2LW((DWORD)(d) << 16) | B2LW((DWORD)(d) >> 16)) +#define L2BW(w) B2LW(w) +#define L2BD(d) B2LD(d) + +// +// miscellaneous manifests +// + +#define ONE_TICK (1000/18) // 1/18 sec in milliseconds (55.55 mSec) +#define SLEEP_TIME ONE_TICK // amount of time we Sleep() during IPXRelinquishControl + +// +// options for IPXGetInformation +// + +#define IPX_ODI 0x0001 +#define IPX_CHECKSUM_FUNCTIONS 0x0002 + +// +// IPX/SPX structures. The following structures are in VDM format, and should +// be packed on a byte-boundary +// +// Netware maintains certain structure fields in network (big-endian) format +// + +#include + +// +// INTERNET_ADDRESS - structure returned by IPXGetInternetworkAddress +// + +typedef struct { + BYTE Net[4]; + BYTE Node[6]; +} INTERNET_ADDRESS ; + +typedef INTERNET_ADDRESS UNALIGNED *LPINTERNET_ADDRESS; + +// +// NETWARE_ADDRESS - address of an application on the network, as defined by +// its network segment, node address and socket number +// + +typedef struct { + BYTE Net[4]; // hi-lo + BYTE Node[6]; // hi-lo + WORD Socket; // hi-lo +} NETWARE_ADDRESS ; + +typedef NETWARE_ADDRESS UNALIGNED *LPNETWARE_ADDRESS; + +// +// FRAGMENT - ECB/IPX/SPX buffers are split into 'fragments' +// + +typedef struct { + LPVOID Address; // offset-segment + WORD Length; // hi-lo +} FRAGMENT ; + +typedef FRAGMENT UNALIGNED *LPFRAGMENT; + +// +// IPX_PACKET - format of packet submitted to IPX for sending. The maximum +// size of an IPX packet is 576 bytes, 30 bytes header, 546 bytes data +// + +typedef struct { + WORD Checksum; // always set to 0xFFFF + WORD Length; // set by IPX - header + data + BYTE TransportControl; // set by IPX to 0. Used by routers + + // + // for IPX, PacketType is 0 (Unknown Packet Type) or 4 (Packet Exchange + // Packet) + // + + BYTE PacketType; + NETWARE_ADDRESS Destination; + NETWARE_ADDRESS Source; + BYTE Data[]; // 546 bytes max. +} IPX_PACKET ; + +typedef IPX_PACKET UNALIGNED *LPIPX_PACKET; + +#define IPX_HEADER_LENGTH sizeof(IPX_PACKET) +#define MAXIMUM_IPX_PACKET_LENGTH 576 +#define MAXIMUM_IPX_DATA_LENGTH (MAXIMUM_IPX_PACKET_LENGTH - IPX_HEADER_LENGTH) + +#define IPX_PACKET_TYPE 4 + +// +// SPX_PACKET - format of packet submitted to SPX for sending. The maximum +// size of an SPX packet is 576 bytes, 42 bytes header, 534 bytes data +// + +typedef struct { + WORD Checksum; // always set to 0xFFFF + WORD Length; // set by IPX - header + data + BYTE TransportControl; // set by IPX to 0. Used by routers + + // + // for SPX, PacketType is set to 5 (Sequenced Packet Protocol Packet) + // + + BYTE PacketType; + NETWARE_ADDRESS Destination; + NETWARE_ADDRESS Source; + + // + // ConnectionControl is a bitmap which control bi-directional flow over a + // link. The bits are defined (by Xerox SPP) as: + // + // 0-3 undefined + // 4 end-of-message + // This is the only bit which can be directly manipulated by an + // app. The bit is passed through unchanged by SPX + // 5 attention + // Ignored by SPX, but passed through + // 6 acknowledge + // Set by SPX if an ack is required + // 7 system packet + // Set by SPX if the packet is internal control. An app should + // never see this bit (i.e. should never see a system packet) + // + + BYTE ConnectionControl; + + // + // DataStreamType defines the type of data in the packet: + // + // 0x00 - 0xFD client-defined. + // Ignored by SPX + // 0xFE end-of-connection. + // When active connection is terminated, SPX + // generates and sends a packet with this bit set. + // This will be the last packet sent on the connection + // 0xFF end-of-connection acknowledgement + // SPX generates a system packet to acknowledge an + // end-of-connection packet + // + + BYTE DataStreamType; + WORD SourceConnectId; // assigned by SPX + WORD DestinationConnectId; + WORD SequenceNumber; // managed by SPX + WORD AckNumber; // managed by SPX + WORD AllocationNumber; // managed by SPX + BYTE Data[]; // 534 bytes max. + +} SPX_PACKET ; + +typedef SPX_PACKET UNALIGNED *LPSPX_PACKET; + +#define SPX_HEADER_LENGTH sizeof(SPX_PACKET) +#define MAXIMUM_SPX_PACKET_LENGTH MAXIMUM_IPX_PACKET_LENGTH +#define MAXIMUM_SPX_DATA_LENGTH (MAXIMUM_SPX_PACKET_LENGTH - SPX_HEADER_LENGTH) + +#define SPX_PACKET_TYPE 5 + +// +// ConnectionControl flags +// + +#define SPX_CONNECTION_RESERVED 0x0F +#define SPX_END_OF_MESSAGE 0x10 +#define SPX_ATTENTION 0x20 +#define SPX_ACK_REQUIRED 0x40 +#define SPX_SYSTEM_PACKET 0x80 + +// +// DataStreamType values +// + +#define SPX_DS_ESTABLISH 0x00 +#define SPX_DS_TERMINATE 0xfe + +// +// IPX_ECB - Event Control Block. This structure is used by most IPX/SPX APIs, +// especially when deferred IPX/AES processing is required. The following +// structure is a socket-based ECB +// + +typedef struct { + + // + // LinkAddress is reserved for use by IPX. We use it to link the ECB onto + // a queue. We appropriate the space used for an x86 segmented address + // (real or protect mode) as a flat 32-bit pointer + // + + ULPVOID LinkAddress; // offset-segment + + // + // EsrAddress is non-NULL if an Event Service Routine will be called when + // the event described by the ECB completes. This will always be an x86 + // segmented address (real or protect mode) + // + + ESR_ADDRESS EsrAddress; // offset-segment + + // + // IPX uses the InUse field to mark the ECB as owned by IPX (!0) or by the + // app (0): + // + // 0xF8 App tried to send a packet while IPX was busy; IPX queued + // the ECB + // 0xFA IPX is processing the ECB + // 0xFB IPX has used the ECB for some event and put it on a queue + // for processing + // 0xFC the ECB is waiting for an AES event to occur + // 0xFD the ECB is waiting for an IPX event to occur + // 0xFE IPX is listening on a socket for incoming packets + // 0xFF IPX is using the ECB to send a packet + // + + BYTE InUse; + + // + // CompletionCode is used to return a status from a deferred request. This + // field is not valid until InUse has been set to 0 + // + // NOTE: We have to differentiate between AES and IPX ECBs on callbacks: due + // to their different sizes, we store the 16-bit segment and offset in + // different places. In order to differentiate the ECBs, we use CompletionCode + // field (AesWorkspace[0]) as the owner. The real CompletionCode for IPX ECBs + // goes in IPX_ECB_COMPLETE (DriverWorkspace[7]). But only for completed ECBs + // that have an ESR + // + + BYTE CompletionCode; + WORD SocketNumber; // hi-lo + + // + // the first word of IpxWorkspace is used to return the connection ID of + // an SPX connection + // + + DWORD IpxWorkspace; + BYTE DriverWorkspace[12]; + + // + // ImmediateAddress is the local network node at the remote end of this + // connection. It is either the node address of the remote machine if it + // is on this LAN, or it is the node address of the router if the remote + // machine is on a different LAN + // + // This field must be initialized when talking over IPX, but not SPX + // + + BYTE ImmediateAddress[6]; + + // + // FragmentCount - number of FRAGMENT structures that comprise the request. + // Must be at least 1 + // + + WORD FragmentCount; + + // + // FragmentCount fragments start here + // + +} IPX_ECB ; + +typedef IPX_ECB UNALIGNED *LPIPX_ECB; + +// +// ECB InUse values +// + +#define ECB_IU_NOT_IN_USE 0x00 +#define ECB_IU_TEMPORARY 0xCC +#define ECB_IU_LISTENING_SPX 0xF7 // same as win16 (by observation) +#define ECB_IU_SEND_QUEUED 0xF8 +#define ECB_IU_AWAITING_CONNECTION 0xF9 // same as win16 (by observation) +#define ECB_IU_BEING_PROCESSED 0xFA +#define ECB_IU_AWAITING_PROCESSING 0xFB +#define ECB_IU_AWAITING_AES_EVENT 0xFC +#define ECB_IU_AWAITING_IPX_EVENT 0xFD +#define ECB_IU_LISTENING 0xFE +#define ECB_IU_SENDING 0xFF + +// +// ECB CompletionCode values +// + +#define ECB_CC_SUCCESS 0x00 +#define ECB_CC_CONNECTION_TERMINATED 0xEC +#define ECB_CC_CONNECTION_ABORTED 0xED +#define ECB_CC_INVALID_CONNECTION 0xEE +#define ECB_CC_CONNECTION_TABLE_FULL 0xEF +#define ECB_CC_CANNOT_CANCEL 0xF9 +#define ECB_CC_CANCELLED 0xFC +#define ECB_CC_BAD_REQUEST 0xFD +#define ECB_CC_BAD_SEND_REQUEST 0xFD +#define ECB_CC_PACKET_OVERFLOW 0xFD +#define ECB_CC_UNDELIVERABLE 0xFE +#define ECB_CC_SOCKET_TABLE_FULL 0xFE +#define ECB_CC_BAD_LISTEN_REQUEST 0xFF +#define ECB_CC_HARDWARE_ERROR 0xFF +#define ECB_CC_NON_EXISTENT_SOCKET 0xFF + +// +// we commandeer certain (reserved) fields for our own internal use: +// +// LPECB EcbLink LinkAddress +// PVOID Buffer32 DriverWorkspace[0] +// WORD Length32 DriverWorkspace[4] +// WORD Flags32 DriverWorkspace[6] +// WORD OriginalEs DriverWorkspace[8] +// WORD OriginalSi DriverWorkspace[10] +// + +#define ECB_TYPE(p) (((LPIPX_ECB)(p))->CompletionCode) +#define IPX_ECB_SEGMENT(p) (WORD)*((ULPWORD)&(((LPIPX_ECB)(p))->IpxWorkspace)+0) +#define IPX_ECB_OFFSET(p) (WORD)*((ULPWORD)&(((LPIPX_ECB)(p))->IpxWorkspace)+2) +#define IPX_ECB_BUFFER32(p) (ULPVOID)*(ULPVOID*)&(((LPIPX_ECB)(p))->DriverWorkspace[0]) +#define IPX_ECB_LENGTH32(p) (WORD)*(ULPWORD)&(((LPIPX_ECB)(p))->DriverWorkspace[4]) +#define IPX_ECB_FLAGS32(p) (((LPIPX_ECB)(p))->DriverWorkspace[6]) +#define IPX_ECB_COMPLETE(p) (((LPIPX_ECB)(p))->DriverWorkspace[7]) + +#define SPX_ECB_CONNECTION_ID(p) (WORD)*(ULPWORD)&(((LPIPX_ECB)(p))->IpxWorkspace) + +// +// ECB Flags32 flags +// + +#define ECB_FLAG_BUFFER_ALLOCATED 0x01 + +// +// ECB types +// + +#define ECB_TYPE_AES 0 +#define ECB_TYPE_IPX 1 +#define ECB_TYPE_SPX 2 + +// +// ECB owners +// + +#define ECB_OWNER_IPX 0xFF +#define ECB_OWNER_AES 0x00 + +// +// ECB_FRAGMENT - macro which gives the address of the first fragment structure +// within a socket-based ECB +// + +#define ECB_FRAGMENT(p, n) ((LPFRAGMENT)(((LPIPX_ECB)(p) + 1)) + (n)) + +// +// AES_ECB - used by AES, these socket-less ECBs are used to schedule events +// + +typedef struct { + ULPVOID LinkAddress; // offset-segment + ESR_ADDRESS EsrAddress; // offset-segment + BYTE InUse; + + // + // first 3 bytes overlay CompletionCode (1) and SocketNumber (2) fields of + // IPX_ECB. Last 2 bytes overlay first 2 bytes of IpxWorkspace (4) field of + // IPX_ECB. We use the 1st byte of the common unused fields as the ECB type + // (send/receive/timed-event) + // + + BYTE AesWorkspace[5]; +} AES_ECB ; + +typedef AES_ECB UNALIGNED *LPAES_ECB; + +// +// as with IPX_ECB, we 'borrow' some of the reserved fields for our own use +// + +#define AES_ECB_SEGMENT(p) (WORD)*(ULPWORD)&(((LPAES_ECB)(p))->AesWorkspace[1]) +#define AES_ECB_OFFSET(p) (WORD)*(ULPWORD)&(((LPAES_ECB)(p))->AesWorkspace[3]) + +// +// LPECB - points to either IPX_ECB or AES_ECB. Both in VDM workspace +// + +#define LPECB LPIPX_ECB + +// +// SPX_CONNECTION_STATS - returned by SPXGetConnectionStatus. All WORD fields +// are to be returned HiLo (ie to Hawaii). All fields come back from NT SPX +// transport in HiLo format also (this was changed recently, used to be in +// Intel order). +// + +typedef struct { + BYTE State; + BYTE WatchDog; + WORD LocalConnectionId; + WORD RemoteConnectionId; + WORD LocalSequenceNumber; + WORD LocalAckNumber; + WORD LocalAllocNumber; + WORD RemoteAckNumber; + WORD RemoteAllocNumber; + WORD LocalSocket; + BYTE ImmediateAddress[6]; + BYTE RemoteNetwork[4]; + BYTE RemoteNode[6]; + WORD RemoteSocket; + WORD RetransmissionCount; + WORD EstimatedRoundTripDelay; + WORD RetransmittedPackets; + WORD SuppressedPackets; +} SPX_CONNECTION_STATS ; + +typedef SPX_CONNECTION_STATS UNALIGNED* LPSPX_CONNECTION_STATS; + +#include + +// +// 16-bit parameter get/set macros. These may change depending on requirements +// of real/protect mode parameters (e.g. stack based vs. register based) +// + +#define IPX_GET_AES_ECB(p) (p) = (LPAES_ECB)POINTER_FROM_WORDS(getES(), getSI(), sizeof(AES_ECB)) +#define IPX_GET_IPX_ECB(p) (p) = (LPIPX_ECB)POINTER_FROM_WORDS(getES(), getSI(), sizeof(IPX_ECB)) +#define IPX_GET_SOCKET(s) (s) = (WORD)getDX() +#define IPX_GET_SOCKET_LIFE(l) (l) = (BYTE)getBP() +#define IPX_GET_SOCKET_OWNER(o) (o) = (WORD)getCX() +#define IPX_GET_BUFFER(p, s) (p) = (ULPBYTE)POINTER_FROM_WORDS(getES(), getSI(), (s)) +#define IPX_GET_ECB_SEGMENT() getES() +#define IPX_GET_ECB_OFFSET() getSI() + +#define IPX_SET_STATUS(s) setAL((BYTE)(s)) +#define IPX_SET_SOCKET(s) setDX((WORD)(s)) +#define IPX_SET_INFORMATION(v) setDX((WORD)(v)) + +#define SPX_SET_STATUS(s) setAL((BYTE)(s)) +#define SPX_SET_CONNECTION_ID(i) setDX((WORD)(i)) + +// +// macros returning 16-bit API parameters - may fetch register contents or values +// from stack/memory +// + +#define ECB_PARM_SEGMENT() getES() +#define ECB_PARM_OFFSET() getSI() +#define ECB_PARM_ADDRESS() (ECB_ADDRESS)MAKELONG(getSI(), getES()) + +#define AES_ECB_PARM() RetrieveEcb(ECB_TYPE_AES) + +#define IPX_ECB_PARM() RetrieveEcb(ECB_TYPE_IPX) +#define IPX_SOCKET_PARM() getDX() +#define IPX_SOCKET_LIFE_PARM() (BYTE)getBP() +#define IPX_SOCKET_OWNER_PARM() getCX() +#define IPX_BUFFER_PARM(s) (ULPBYTE)POINTER_FROM_WORDS(getES(), getSI(), (s)) +#define IPX_TICKS_PARM() getBP() + +#define SPX_RETRY_COUNT_PARM() (BYTE)getBP() +#define SPX_WATCHDOG_FLAG_PARM() ((BYTE)(getBP() >> 8)) +#define SPX_ECB_PARM() RetrieveEcb(ECB_TYPE_IPX) +#define SPX_CONNECTION_PARM() getDX() +#define SPX_BUFFER_PARM(s) (ULPBYTE)POINTER_FROM_WORDS(getES(), getSI(), (s)) + +// +// IPX error codes - same codes used in different circumstances +// + +#define IPX_SUCCESS 0x00 +#define IPX_CANNOT_CANCEL 0xF9 +#define IPX_NO_PATH_TO_DESTINATION 0xFA +#define IPX_CANCELLED 0xFC +#define IPX_BAD_REQUEST 0xFD +#define IPX_SOCKET_TABLE_FULL 0xFE +#define IPX_UNDELIVERABLE 0xFE +#define IPX_SOCKET_ALREADY_OPEN 0xFF +#define IPX_HARDWARE_ERROR 0xFF +#define IPX_NON_EXISTENT_SOCKET 0xFF +#define IPX_ECB_NOT_IN_USE 0xFF + +// +// SPX error codes - same codes used in different circumstances +// + +#define SPX_SUCCESS 0x00 +#define SPX_CONNECTION_TERMINATED 0xEC +#define SPX_CONNECTION_ABORTED 0xED +#define SPX_INVALID_CONNECTION 0xEE +#define SPX_CONNECTION_TABLE_FULL 0xEF +#define SPX_SOCKET_CLOSED 0xFC +#define SPX_PACKET_OVERFLOW 0xFD +#define SPX_BAD_SEND_REQUEST 0xFD // malformed packet +#define SPX_BAD_LISTEN_REQUEST 0xFF +#define SPX_NON_EXISTENT_SOCKET 0xFF + +#endif // _VWIPXSPX_H_ diff --git a/private/nw/vwipxspx/dll/vwipxspx.rc b/private/nw/vwipxspx/dll/vwipxspx.rc new file mode 100644 index 000000000..c355cffea --- /dev/null +++ b/private/nw/vwipxspx/dll/vwipxspx.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "Virtual Dos Machine IPX/SPX Interface Library" +#define VER_INTERNALNAME_STR "VWIPXSPX.DLL" +#define VER_ORIGINALFILENAME_STR "VWIPXSPX.DLL" + +#include "common.ver" diff --git a/private/nw/vwipxspx/dll/vwmisc.c b/private/nw/vwipxspx/dll/vwmisc.c new file mode 100644 index 000000000..2d7821dee --- /dev/null +++ b/private/nw/vwipxspx/dll/vwmisc.c @@ -0,0 +1,74 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwmisc.c + +Abstract: + + ntVdm netWare (Vw) IPX/SPX Functions + + Vw: The peoples' network + + Contains miscellaneous (non-IPX/SPX) functions + + Contents: + VwTerminateProgram + +Author: + + Richard L Firth (rfirth) 30-Sep-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 30-Sep-1993 rfirth + Created + +--*/ + +#include "vw.h" +#pragma hdrstop + +// +// functions +// + + +VOID +VwTerminateProgram( + VOID + ) + +/*++ + +Routine Description: + + When a DOS program terminates, we must close any open sockets that were + specified as SHORT_LIVED + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_ANY, + IPXDBG_LEVEL_INFO, + "VwTerminateProgram: PDB=%04x\n", + getCX() + )); + + KillShortLivedSockets(getCX()); +} diff --git a/private/nw/vwipxspx/dll/vwmisc.h b/private/nw/vwipxspx/dll/vwmisc.h new file mode 100644 index 000000000..0367bc7d7 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwmisc.h @@ -0,0 +1,27 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vwipx.h + +Abstract: + + Contains function prototypes for VWMISC.C + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +VOID +VwTerminateProgram( + VOID + ); diff --git a/private/nw/vwipxspx/dll/vwspx.c b/private/nw/vwipxspx/dll/vwspx.c new file mode 100644 index 000000000..02ef58481 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwspx.c @@ -0,0 +1,1339 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + vwspx.c + +Abstract: + + ntVdm netWare (Vw) IPX/SPX Functions + + Vw: The peoples' network + + Contains internal routines for DOS/WOW SPX calls (netware functions). + The SPX APIs use WinSock to perform the actual operations + + Contents: + _VwSPXAbortConnection + _VwSPXEstablishConnection + _VwSPXGetConnectionStatus + _VwSPXInitialize + _VwSPXListenForConnection + _VwSPXListenForSequencedPacket + _VwSPXSendSequencedPacket + _VwSPXTerminateConnection + + The SPX functions build on the IPX functions (VWIPX.C). SPX maintains + connections, IPX maintains sockets. A socket may have a list of connections + +Author: + + Richard L Firth (rfirth) 30-Sep-1993 + +Environment: + + User-mode Win32 + +Revision History: + + 30-Sep-1993 rfirth + Created + +--*/ + +#include "vw.h" +#pragma hdrstop + +// +// functions +// + + +VOID +_VwSPXAbortConnection( + IN WORD SPXConnectionID + ) + +/*++ + +Routine Description: + + Aborts a connection. Because NWLink doesn't differentiate between abrupt + and graceful closes, this function has the same effect as + VwSPXTerminateConnection + +Arguments: + + SPXConnectionID - connection to abort + +Return Value: + + None. + +--*/ + +{ + LPCONNECTION_INFO pConnectionInfo; + + RequestMutex(); + pConnectionInfo = FindConnection(SPXConnectionID); + if (pConnectionInfo) { + DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo); + AbortOrTerminateConnection(pConnectionInfo, ECB_CC_CONNECTION_ABORTED); + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXAbortConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXAbortConnection: cannot find connection %04x\n", + SPXConnectionID + )); + + } + ReleaseMutex(); +} + + +WORD +_VwSPXEstablishConnection( + IN BYTE retryCount, + IN BYTE watchDogFlag, + OUT ULPWORD pSPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Creates a connection with a remote SPX socket. The remote end can be on + this machine (i.e. same app in DOS world) + + This call is Asynchronous + +Arguments: + + Inputs + retryCount + watchDogFlag + pEcb + EcbAddress + + Outputs + pSPXConnectionID + +Return Value: + + 00h Attempting to talk to remote + EFh Local connection table full + FDh Fragment count not 1; buffer size not 42 + FFh Send socket not open + +--*/ + +{ + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress); + LPSOCKET_INFO pSocketInfo; + LPCONNECTION_INFO pConnectionInfo; + WORD connectionId; + LPSPX_PACKET pPacket; + SOCKADDR_IPX destination; + int rc; + SOCKET s; + + pSocketInfo = FindSocket(pXecb->SocketNumber); + if (!pSocketInfo) { + CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET); + return SPX_NON_EXISTENT_SOCKET; + } + + // + // if no outstanding IPX operations, change socket to SPX + // + + if (!pSocketInfo->SpxSocket) { + if (!(pSocketInfo->PendingSends && pSocketInfo->PendingListens)) { + rc = ReopenSocket(pSocketInfo); + } else { + rc = ECB_CC_BAD_SEND_REQUEST; + } + if (rc != SPX_SUCCESS) { + CompleteOrQueueEcb(pXecb, (BYTE)rc); + return SPX_BAD_SEND_REQUEST; + } + } + + // + // real SPX will use the ECB to send an ESTABLISH CONNECTION packet. This + // is handled for us within the SPX transport. Nevertheless we must check + // the fragment and dismiss the request if its not sufficient + // + + if ((pXecb->Ecb->FragmentCount != 1) + || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < SPX_HEADER_LENGTH)) { + CompleteEcb(pXecb, ECB_CC_BAD_SEND_REQUEST); + return SPX_BAD_SEND_REQUEST; + } + + // + // the socket is open for SPX. Allocate a connection/connection ID + // + + pConnectionInfo = AllocateConnection(pSocketInfo); + if (pConnectionInfo) { + + // + // create new socket, bound to the same local address as the parent + // socket. This is the 'connection' + // + +#if REUSEADDR + + connectionId = pSocketInfo->SocketNumber; + rc = CreateSocket(SOCKET_TYPE_SPX, &connectionId, &pConnectionInfo->Socket); + s = pConnectionInfo->Socket; +// if (rc == SPX_SUCCESS) { + +#else + + s = socket(AF_IPX, SOCK_SEQPACKET, NSPROTO_SPX); + if (s != INVALID_SOCKET) { + + u_long arg = !0; + + // + // put the socket in non-blocking I/O mode + // + + rc = ioctlsocket(s, FIONBIO, &arg); + if (rc != SOCKET_ERROR) { + + int true = 1; + + rc = setsockopt(s, + NSPROTO_IPX, + IPX_RECVHDR, + (char FAR*)&true, + sizeof(true) + ); + if (rc != SOCKET_ERROR) { + pConnectionInfo->Socket = s; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXEstablishConnection: setsockopt(IPX_RECVHDR) returns %d\n", + WSAGetLastError() + )); + + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXEstablishConnection: ioctlsocket(FIONBIO) returns %d\n", + WSAGetLastError() + )); + + } + } else { + rc = WSAGetLastError(); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXEstablishConnection: socket() returns %d\n", + rc + )); + + } + +#endif + + } else { + rc = !SPX_SUCCESS; + } + + if (rc == SPX_SUCCESS) { + pConnectionInfo->State = CI_STARTING; + connectionId = pConnectionInfo->ConnectionId; + + // + // set the ECB InUse field to 0xF7. Same as snowball by observation (and + // probably correct anyway since it looks as though 0xF7 means 'waiting + // to receive SPX packet', which is true in this case - normally SPX + // creates a connection by sending an establish frame then waits for the + // ack frame + // + + pXecb->Ecb->InUse = ECB_IU_LISTENING_SPX; + } else { + + // + // if we failed to get CONNECTION_INFO or create the new socket, return + // immediately with an error (socket table full?) + // + + if (s != INVALID_SOCKET) { + closesocket(s); + } + if (pConnectionInfo) { + DeallocateConnection(pConnectionInfo); + } + CompleteEcb(pXecb, ECB_CC_CONNECTION_TABLE_FULL); + return SPX_CONNECTION_TABLE_FULL; + } + + // + // get the destination info from the SPX header in VDM memory and set up the + // connection. If the connect request would wait then we leave AES to + // periodically check the progress of the request + // + + pPacket = (LPSPX_PACKET)GET_FAR_POINTER(&ECB_FRAGMENT(pXecb->Ecb, 0)->Address, + IS_PROT_MODE(pXecb) + ); + // + // fill in the packet details (app shouldn't look at these until the command + // completes). In 16-bit SPX, these values are filled in by the transport. + // Our transport does not return these values, so we have to 'invent' them, + // but since they are fairly static it should be ok (maybe the transport + // should return them) + // + + pPacket->Checksum = 0xffff; + pPacket->Length = L2BW(SPX_HEADER_LENGTH); + pPacket->TransportControl = 0; + pPacket->PacketType = SPX_PACKET_TYPE; + pPacket->Source.Socket = pSocketInfo->SocketNumber; + pPacket->ConnectionControl = SPX_SYSTEM_PACKET | SPX_ACK_REQUIRED; + pPacket->DataStreamType = SPX_DS_ESTABLISH; + pPacket->SourceConnectId = pConnectionInfo->ConnectionId; + pPacket->DestinationConnectId = 0xffff; + pPacket->SequenceNumber = 0; + pPacket->AckNumber = 0; + pPacket->AllocationNumber = 0; + + // + // get the destination address info + // + + CopyMemory(&destination.sa_netnum, + (LPBYTE)&pPacket->Destination, + sizeof(pPacket->Destination) + ); + destination.sa_family = AF_IPX; + + // + // initiate the connection + // + + rc = connect(s, (LPSOCKADDR)&destination, sizeof(destination)); + if (rc != SOCKET_ERROR) { + + // + // add the CONNECTION_INFO structure to the list of connections owned + // by this socket and set the connection state to ESTABLISHED + // + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_INFO, + "VwSPXEstablishConnection: socket connected\n" + )); + + RequestMutex(); + QueueConnection(pSocketInfo, pConnectionInfo); + pConnectionInfo->State = CI_ESTABLISHED; + ReleaseMutex(); + + // + // the connection ID also appears in the first segment of the establish + // ECB + // + + pPacket->SourceConnectId = connectionId; + + // + // the SPXEstablishConnection ECB is done! + // + + CompleteEcb(pXecb, ECB_CC_SUCCESS); + } else { + rc = WSAGetLastError(); + if (rc == WSAEWOULDBLOCK) { + + // + // the connect request is in progress. Add it to the queue of + // pending SPXEstablishConnection requests (SHOULD ONLY BE 1 PER + // CONNECTION!!!) and add the CONNECTION_INFO structure to the + // owning SOCKET_INFO structure + // + + RequestMutex(); + QueueEcb(pXecb, + &pConnectionInfo->ConnectQueue, + CONNECTION_CONNECT_QUEUE + ); + QueueConnection(pSocketInfo, pConnectionInfo); + ReleaseMutex(); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_INFO, + "VwSPXEstablishConnection: connect() queued\n" + )); + + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXEstablishConnection: connect(%x) returns %d\n", + s, + rc + )); + + // + // the connect request failed. Deallocate all resources (socket, + // CONNECTION_INFO, XECB) and return failure + // + + closesocket(pConnectionInfo->Socket); + DeallocateConnection(pConnectionInfo); + CompleteEcb(pXecb, ECB_CC_CONNECTION_ABORTED); + return SPX_CONNECTION_ABORTED; + } + } + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXEstablishConnection, + IPXDBG_LEVEL_INFO, + "VwSPXEstablishConnection: returning %04x\n", + connectionId + )); + + *pSPXConnectionID = connectionId; + return SPX_SUCCESS; +} + + +WORD +_VwSPXGetConnectionStatus( + IN WORD connectionId, + OUT LPSPX_CONNECTION_STATS pStats + ) + +/*++ + +Routine Description: + + Returns buffer crammed full of useful statistics or something (hu hu huh) + + This call is Synchronous + +Arguments: + + Inputs + connectionId + pStats + + Outputs + on output, buffer in pStats contains: + + BYTE ConnectionStatus + BYTE WatchDogActive + WORD LocalConnectionID + WORD RemoteConnectionID + WORD SequenceNumber + WORD LocalAckNumber + WORD LocalAllocationNumber + WORD RemoteAckNumber + WORD RemoteAllocationNumber + WORD LocalSocket + BYTE ImmediateAddress[6] + BYTE RemoteNetwork[4] + WORD RetransmissionCount + WORD RetransmittedPackets + WORD SuppressedPackets + +Return Value: + 00h Connection is active + EEh No such connection + +--*/ + +{ + int rc; + IPX_SPXCONNSTATUS_DATA buf; + int buflen = sizeof(buf); + LPCONNECTION_INFO pConnectionInfo; + + pConnectionInfo = FindConnection(connectionId); + if (!pConnectionInfo) { + return SPX_INVALID_CONNECTION; + } + + // + // get the stats + // + + rc = getsockopt(pConnectionInfo->Socket, + NSPROTO_IPX, + IPX_SPXGETCONNECTIONSTATUS, + (char FAR*)&buf, + &buflen + ); + if (rc == SOCKET_ERROR) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXGetConnectionStatus, + IPXDBG_LEVEL_ERROR, + "VwSPXGetConnectionStatus: getsockopt() returns %d\n", + WSAGetLastError() + )); + + // + // the request to get the stats failed - probably because the socket is + // not yet connected. Fill in those bits we know about + // + + ZeroMemory((LPBYTE)pStats, sizeof(*pStats)); + } else { + + // + // copy the returned fields + // + + pStats->RemoteConnectionId = buf.RemoteConnectionId; + pStats->LocalSequenceNumber = buf.LocalSequenceNumber; + pStats->LocalAckNumber = buf.LocalAckNumber; + pStats->LocalAllocNumber = buf.LocalAllocNumber; + pStats->RemoteAckNumber = buf.RemoteAckNumber; + pStats->RemoteAllocNumber = buf.RemoteAllocNumber; + pStats->LocalSocket = buf.LocalSocket; + CopyMemory(&pStats->ImmediateAddress, + &buf.ImmediateAddress, + sizeof(buf.ImmediateAddress) + ); + + // + // copy remote network as a DWORD. Endian format is same for both + // + + *(ULPDWORD)&pStats->RemoteNetwork = *(LPDWORD)&buf.RemoteNetwork; + CopyMemory(&pStats->RemoteNode, + &buf.RemoteNode, + sizeof(buf.RemoteNode) + ); + pStats->RemoteSocket = buf.RemoteSocket; + pStats->RetransmissionCount = buf.RetransmissionCount; + pStats->EstimatedRoundTripDelay = buf.EstimatedRoundTripDelay; + pStats->RetransmittedPackets = buf.RetransmittedPackets; + pStats->SuppressedPackets = buf.SuppressedPacket; + } + + // + // fill in common, known fields + // + + pStats->State = pConnectionInfo->State; // not returned by NWIPX + pStats->WatchDog = 0x02; // see novell dog-umentation + pStats->LocalConnectionId = L2BW(pConnectionInfo->ConnectionId); + pStats->LocalSocket = pConnectionInfo->OwningSocket->SocketNumber; + + DUMPSTATS(pStats); + + // + // we are returning some kind o stats - therefore success + // + + return SPX_SUCCESS; +} + + +WORD +_VwSPXInitialize( + OUT ULPBYTE pMajorRevisionNumber, + OUT ULPBYTE pMinorRevisionNumber, + OUT ULPWORD pMaxConnections, + OUT ULPWORD pAvailableConnections + ) + +/*++ + +Routine Description: + + Informs the app that SPX is present on this station + + This call is Synchronous + +Arguments: + + Inputs + + Outputs + pMajorRevisionNumber - SPX Major revision number + pminorRevisionNumber - SPX Minor revision number + pMaxConnections - Maximum SPX connections supported + normally from SHELL.CFG + pAvailableConnections - Available SPX connections + +Return Value: + + 00h Not installed + FFh Installed + +--*/ + +{ + + // + // The following values are returned same as per Windows For Workgroups + // v3.10 + // + + *pMajorRevisionNumber = 3; + *pMinorRevisionNumber = 10; + *pMaxConnections = 128; + *pAvailableConnections = *pMaxConnections - 1 ; + + return SPX_INSTALLED; +} + + +VOID +_VwSPXListenForConnection( + IN BYTE retryCount, + IN BYTE watchDogFlag, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Listens for an incoming connection request + + This call is Asynchronous + +Arguments: + + Inputs + retryCount + watchDogFlag + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress); + LPSOCKET_INFO pSocketInfo; + LPCONNECTION_INFO pConnectionInfo; + SOCKET sock; + SOCKET conn; + SOCKADDR_IPX remoteAddress; + int rc; + BYTE completionCode; + + // + // it turns out that SPXListenForConnection doesn't need a fragment to + // receive connection info - that is handled by SPXListenForSequencedPacket + // that the app has dutifully initiated + // + + pSocketInfo = FindSocket(pXecb->SocketNumber); + if (!pSocketInfo) { + completionCode = ECB_CC_NON_EXISTENT_SOCKET; + goto lc_completion_exit; + } + + // + // if no outstanding IPX operations, change socket to SPX + // + + if (!pSocketInfo->SpxSocket) { + if (!(pSocketInfo->PendingSends && pSocketInfo->PendingListens)) { + rc = ReopenSocket(pSocketInfo); + } else { + rc = ECB_CC_BAD_LISTEN_REQUEST; + } + if (rc != SPX_SUCCESS) { + completionCode = (BYTE)rc; + goto lc_completion_exit; + } + } + + // + // the socket is open for SPX. Allocate a connection/connection ID + // + + pConnectionInfo = AllocateConnection(pSocketInfo); + if (!pConnectionInfo) { + completionCode = ECB_CC_CONNECTION_TABLE_FULL; + goto lc_completion_exit; + } + + // + // put the socket into listening state and try to accept a connection + // + + sock = pSocketInfo->Socket; + + // + // BUGBUG: if the socket is already listening, will probably return an + // error: just queue + // + + rc = listen(sock, MAX_LISTEN_QUEUE_SIZE); + if (rc != SOCKET_ERROR) { + + int addressLength = sizeof(remoteAddress); + + conn = accept(sock, (LPSOCKADDR)&remoteAddress, &addressLength); + if (conn != SOCKET_ERROR) { + + // + // we want to receive the frame headers from this socket + // + + BOOL bval = TRUE; + + rc = setsockopt(conn, + NSPROTO_IPX, + IPX_RECVHDR, + (char FAR*)&bval, + sizeof(bval) + ); + 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; + + // + // add the CONNECTION_INFO structure to the list of connections owned + // by this socket + // + + RequestMutex(); + QueueConnection(pSocketInfo, pConnectionInfo); + ReleaseMutex(); + + // + // 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) + ); + completionCode = ECB_CC_SUCCESS; + goto lc_completion_exit; + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXListenForConnection: setsockopt(RECVHDR) returns %d\n", + WSAGetLastError() + )); + + // + // BUGBUG: value? + // + + closesocket(conn); + completionCode = ECB_CC_CONNECTION_ABORTED; + goto lc_deallocate_exit; + } + } else { + rc = WSAGetLastError(); + if (rc == WSAEWOULDBLOCK) { + + // + // the accept request is in progress. Add it to the queue of + // pending SPXListenForConnection requests (SHOULD ONLY BE 1 PER + // CONNECTION!!!) and add the CONNECTION_INFO structure to the + // owning SOCKET_INFO structure + // + + pConnectionInfo->State = CI_WAITING; // waiting for incoming connect + RequestMutex(); + QueueEcb(pXecb, + &pConnectionInfo->AcceptQueue, + CONNECTION_ACCEPT_QUEUE + ); + QueueConnection(pSocketInfo, pConnectionInfo); + pXecb->Ecb->InUse = ECB_IU_AWAITING_CONNECTION; + ReleaseMutex(); + } else { + + // + // the accept request failed. Deallocate all resources + // (CONNECTION_INFO, XECB) and complete the ECB with a failure + // indication + // + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXListenForConnection: accept() returns %d\n", + rc + )); + + // + // BUGBUG: completion code? + // + + completionCode = ECB_CC_CONNECTION_ABORTED; + goto lc_deallocate_exit; + } + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForConnection, + IPXDBG_LEVEL_ERROR, + "VwSPXListenForConnection: listen() returns %d\n", + WSAGetLastError() + )); + + // + // listen failed? Bogus. Complete the ECB and we're outta here + // + + // + // BUGBUG: completion code? + // + + completionCode = ECB_CC_CONNECTION_ABORTED; + goto lc_deallocate_exit; + } + + // + // here if we queued the listen request + // + + return; + +lc_deallocate_exit: + DeallocateConnection(pConnectionInfo); + +lc_completion_exit: + CompleteEcb(pXecb, completionCode); +} + + +VOID +_VwSPXListenForSequencedPacket( + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Attempts to receive an SPX packet. This call is made against the top-level + socket (the socket in SPX-speak, not the connection). We can receive a + packet from any connection assigned to this socket. In this function, we + just queue the ECB (since there is no return status, we expect that the + app has supplied an ESR) and let AES handle it + + This call is Asynchronous + +Arguments: + + Inputs + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress); + LPSOCKET_INFO pSocketInfo; + int rc; + BOOL dummy ; + BYTE status; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXListenForSequencedPacket, + IPXDBG_LEVEL_INFO, + "VwSPXListenForSequencedPacket(%04x:%04x) socket=%04x ESR=%04x:%04x\n", + HIWORD(pXecb->EcbAddress), + LOWORD(pXecb->EcbAddress), + B2LW(pXecb->SocketNumber), + HIWORD(pXecb->EsrAddress), + LOWORD(pXecb->EsrAddress) + )); + + pSocketInfo = FindSocket(pXecb->SocketNumber); + if (!pSocketInfo) { + status = ECB_CC_NON_EXISTENT_SOCKET; + goto lp_exit; + } + + // + // if no outstanding IPX operations, change socket to SPX + // + + if (!pSocketInfo->SpxSocket) { + if (!(pSocketInfo->PendingSends && pSocketInfo->PendingListens)) { + rc = ReopenSocket(pSocketInfo); + } else { + rc = ECB_CC_BAD_LISTEN_REQUEST; + } + if (rc != SPX_SUCCESS) { + status = (BYTE)rc; + goto lp_exit; + } + } + + // + // the first fragment must be large enough to hold an SPX packet header + // + + if ((pXecb->Ecb->FragmentCount == 0) + || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < SPX_HEADER_LENGTH)) { + status = ECB_CC_BAD_LISTEN_REQUEST; + goto lp_exit; + } + + // + // we have a socket and the receive buffer looks good. Get a buffer for recv() + // + + if (!GetIoBuffer(pXecb, FALSE, SPX_HEADER_LENGTH)) { + status = ECB_CC_BAD_LISTEN_REQUEST; + goto lp_exit; + } else { + + // + // when recv() is attempted against this request, it will be the first + // time we tried to receive anything to this buffer. That means (if we + // get anything) that the buffer will contain the length of the entire + // frame + // + + pXecb->Flags |= XECB_FLAG_FIRST_RECEIVE; + } + + // + // mark the VDM ECB as in use + // + + pXecb->Ecb->InUse = ECB_IU_LISTENING_SPX; + + // + // add this ECB to the queue of listens for the top-level socket and quit + // + + RequestMutex(); + + if ((pXecb->Ecb->FragmentCount == 1) && + (ECB_FRAGMENT(pXecb->Ecb, 0)->Length == SPX_HEADER_LENGTH)) + { + QueueEcb(pXecb, &pSocketInfo->HeaderQueue, SOCKET_HEADER_QUEUE); + } + else + { + QueueEcb(pXecb, &pSocketInfo->ListenQueue, SOCKET_LISTEN_QUEUE); + } + + ReleaseMutex(); + + // + // see if we are ready to rock + // + + CheckPendingSpxRequests(&dummy); + return; + +lp_exit: + CompleteOrQueueEcb(pXecb, status); +} + + +VOID +_VwSPXSendSequencedPacket( + IN WORD connectionId, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Sends a packet on an SPX connection + + This call is Asynchronous + +Arguments: + + Inputs + connectionId + pEcb + EcbAddress + + Outputs + Nothing + +Return Value: + + None. + +--*/ + +{ + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress); + LPCONNECTION_INFO pConnectionInfo; + int rc; + BOOL addToQueue; + LPSPX_PACKET pPacket; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXSendSequencedPacket, + IPXDBG_LEVEL_INFO, + "VwSPXSendSequencedPacket(%04x:%04x) Connection=%04x ESR=%04x:%04x\n", + HIWORD(pXecb->EcbAddress), + LOWORD(pXecb->EcbAddress), + connectionId, + HIWORD(pXecb->EsrAddress), + LOWORD(pXecb->EsrAddress) + )); + + IPXDUMPECB((pXecb->Ecb, + HIWORD(pXecb->EcbAddress), + LOWORD(pXecb->EcbAddress), + ECB_TYPE_SPX, + TRUE, + TRUE, + IS_PROT_MODE(pXecb) + )); + + // + // find the connection. No need to check if this is an SPX socket: if we + // can't find the connection, its a bad connection, else the socket must + // be open for SPX + // + + pConnectionInfo = FindConnection(connectionId); + if (!pConnectionInfo || (pConnectionInfo->State != CI_ESTABLISHED)) { + CompleteOrQueueEcb(pXecb, ECB_CC_INVALID_CONNECTION); + return; + } + + // + // the first fragment must be large enough to hold an SPX packet header + // + + if ((pXecb->Ecb->FragmentCount == 0) + || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < SPX_HEADER_LENGTH)) { + CompleteOrQueueEcb(pXecb, ECB_CC_BAD_SEND_REQUEST); + return; + } + if (!GetIoBuffer(pXecb, TRUE, SPX_HEADER_LENGTH)) { + CompleteOrQueueEcb(pXecb, ECB_CC_BAD_SEND_REQUEST); + return; + } + + pPacket = (LPSPX_PACKET)GET_FAR_POINTER( + &(ECB_FRAGMENT(pXecb->Ecb, 0)->Address), + IS_PROT_MODE(pXecb) + ); + + // + // fill in the following fields in the SPX 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; + + // + // since the transport adds the SPX header, we subtracted the length of + // the header from our transmit length; add it back when updating the + // header in the app's space + // + + pPacket->Length = L2BW(pXecb->Length + SPX_HEADER_LENGTH); + pPacket->TransportControl = 0; + CopyMemory((LPBYTE)&pPacket->Source, + &MyInternetAddress.sa_netnum, + sizeof(MyInternetAddress.sa_netnum) + + sizeof(MyInternetAddress.sa_nodenum) + ); + pPacket->Source.Socket = pConnectionInfo->OwningSocket->SocketNumber; + + // + // if we allocated a buffer then there is >1 fragment. Collect them + // + + if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) { + GatherData(pXecb, SPX_HEADER_LENGTH); + } + + // + // BUGBUG: length check: >576 == error?? + // + + // + // mark the VDM ECB as in use + // + + pXecb->Ecb->InUse = ECB_IU_SENDING; + + // + // if there is a send queued on this connection already, add this request + // to the back of the queue and let AES do the rest + // + + RequestMutex(); + if (pConnectionInfo->SendQueue.Head) { + addToQueue = TRUE; + } else { + + int dataStreamType; + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXSendSequencedPacket, + IPXDBG_LEVEL_INFO, + "VwSPXSendSequencedPacket: sending %d (0x%x) bytes from %08x\n", + pXecb->Length, + pXecb->Length, + pXecb->Data + )); + + // + // no outstanding sends queued for this connection. Start sending this + // packet + // + + dataStreamType = (int)pPacket->DataStreamType; + rc = setsockopt(pConnectionInfo->Socket, + NSPROTO_IPX, + IPX_DSTYPE, + (char FAR*)&dataStreamType, + sizeof(dataStreamType) + ); + if (rc != SOCKET_ERROR) { + + // + // if the app set the END_OF_MESSAGE bit in the ConnectionControl + // field then set the flags to 0: NWLink will automatically set the + // end-of-message bit in the packet; otherwise set flags to MSG_PARTIAL + // to indicate to NWLink that it *shouldn't* set the bit in the packet + // + + int flags = (pPacket->ConnectionControl & SPX_END_OF_MESSAGE) + ? 0 + : MSG_PARTIAL + ; + + rc = send(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, flags); + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXSendSequencedPacket, + IPXDBG_LEVEL_INFO, + "VwSPXSendSequencedPacket: send() returns %d\n", + rc + )); + + if (rc == pXecb->Length) { + + // + // all data sent + // + + CompleteOrQueueIo(pXecb, ECB_CC_SUCCESS); + addToQueue = FALSE; + } else if (rc == SOCKET_ERROR) { + rc = WSAGetLastError(); + if (rc == WSAEWOULDBLOCK) { + + // + // can't send right now. Queue it for AES + // + + addToQueue = TRUE; + } + } else { + + // + // partial data sent. Update the buffer pointer and length fields + // and queue this request + // + + pXecb->Data += rc; + pXecb->Length -= (WORD)rc; + addToQueue = TRUE; + } + } else { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXSendSequencedPacket, + IPXDBG_LEVEL_ERROR, + "VwSPXSendSequencedPacket: setsockopt(IPX_DSTYPE) returns %d\n", + WSAGetLastError() + )); + + CompleteOrQueueIo(pXecb, ECB_CC_BAD_REQUEST); + } + } + + // + // if addToQueue set then we can't do anything right now - add this + // request to the send queue + // + + if (addToQueue) { + + IPXDBGPRINT((__FILE__, __LINE__, + FUNCTION_SPXSendSequencedPacket, + IPXDBG_LEVEL_WARNING, + "VwSPXSendSequencedPacket: adding XECB %08x to send queue\n", + pXecb + )); + + QueueEcb(pXecb, &pConnectionInfo->SendQueue, CONNECTION_SEND_QUEUE); + } + ReleaseMutex(); +} + + +VOID +_VwSPXTerminateConnection( + IN WORD SPXConnectionID, + IN LPECB pEcb, + IN ECB_ADDRESS EcbAddress + ) + +/*++ + +Routine Description: + + Terminates a connection + +Arguments: + + SPXConnectionID - connection to terminate + pEcb - pointer to 16-bit ECB to use + EcbAddress - address of 16-bit ECB in 16:16 format + +Return Value: + + None. + +--*/ + +{ + LPCONNECTION_INFO pConnectionInfo; + LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress); + BYTE status; + BYTE completionCode; + + RequestMutex(); + pConnectionInfo = FindConnection(SPXConnectionID); + if (pConnectionInfo) { + + // + // once dequeued, pConnectionInfo no longer points to OwningSocket + // + + WORD socketNumber = pConnectionInfo->OwningSocket->SocketNumber; + + DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo); + if ((pXecb->Ecb->FragmentCount >= 1) + && (ECB_FRAGMENT(pXecb->Ecb, 0)->Length >= SPX_HEADER_LENGTH)) { + + LPSPX_PACKET pPacket; + SOCKADDR_IPX remote; + int remoteLen = sizeof(remote); + + completionCode = ECB_CC_CONNECTION_TERMINATED; + status = ECB_CC_SUCCESS; + + // + // fill in the packet header: this would normally contain the + // acknowledgement packet from the remote partner + // + + 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 = SPX_PACKET_TYPE; + getpeername(pConnectionInfo->Socket, (LPSOCKADDR)&remote, &remoteLen); + CopyMemory((LPBYTE)&pPacket->Destination, + (LPBYTE)&remote.sa_netnum, + sizeof(NETWARE_ADDRESS) + ); + CopyMemory((LPBYTE)&pPacket->Source, + (LPBYTE)&MyInternetAddress.sa_netnum, + sizeof(INTERNET_ADDRESS) + ); + pPacket->Source.Socket = socketNumber; + pPacket->ConnectionControl = SPX_ACK_REQUIRED; + pPacket->DataStreamType = SPX_DS_TERMINATE; + pPacket->SourceConnectId = pConnectionInfo->ConnectionId; + pPacket->DestinationConnectId = 0; + pPacket->SequenceNumber = 0; + pPacket->AckNumber = 0; + pPacket->AllocationNumber = 0; + + } else { + completionCode = ECB_CC_CONNECTION_ABORTED; + status = ECB_CC_BAD_REQUEST; + } + AbortOrTerminateConnection(pConnectionInfo, completionCode); + } else { + status = ECB_CC_INVALID_CONNECTION; + } + ReleaseMutex(); + CompleteOrQueueEcb(pXecb, status); +} diff --git a/private/nw/vwipxspx/dll/vwspx.h b/private/nw/vwipxspx/dll/vwspx.h new file mode 100644 index 000000000..68e1b63fa --- /dev/null +++ b/private/nw/vwipxspx/dll/vwspx.h @@ -0,0 +1,62 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vwspx.h + +Abstract: + + Contains function prototypes for VWSPX.C + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +VOID +VwSPXAbortConnection( + VOID + ); + +VOID +VwSPXEstablishConnection( + VOID + ); + +VOID +VwSPXGetConnectionStatus( + VOID + ); + +VOID +VwSPXInitialize( + VOID + ); + +VOID +VwSPXListenForConnection( + VOID + ); + +VOID +VwSPXListenForSequencedPacket( + VOID + ); + +VOID +VwSPXSendSequencedPacket( + VOID + ); + +VOID +VwSPXTerminateConnection( + VOID + ); diff --git a/private/nw/vwipxspx/dll/vwvdm.h b/private/nw/vwipxspx/dll/vwvdm.h new file mode 100644 index 000000000..2fd23ba22 --- /dev/null +++ b/private/nw/vwipxspx/dll/vwvdm.h @@ -0,0 +1,151 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + vwvdm.h + +Abstract: + + Contains macros, manifests, includes for dealing with VDM + +Author: + + Richard L Firth (rfirth) 25-Oct-1993 + +Revision History: + + 25-Oct-1993 rfirth + Created + +--*/ + +#ifndef _VWVDM_H_ +#define _VWVDM_H_ + +// +// unaligned pointers - non-Intel platforms must use UNALIGNED to access data +// in VDM which can (and most likely will) be aligned on odd-byte and word +// boundaries +// + +#ifndef ULPBYTE +#define ULPBYTE BYTE UNALIGNED FAR* +#endif + +#ifndef ULPWORD +#define ULPWORD WORD UNALIGNED FAR* +#endif + +#ifndef ULPDWORD +#define ULPDWORD DWORD UNALIGNED FAR* +#endif + +#ifndef ULPVOID +#define ULPVOID VOID UNALIGNED FAR* +#endif + +// +// VDM macros +// + +#define MSW_PE 0x0001 // Protect-mode Enable bit in x86 Machine Status Word + +// +// POINTER_FROM_WORDS - returns 32-bit pointer to address in VDM memory described +// by seg:off. If seg:off = 0:0, returns NULL +// + +#define POINTER_FROM_WORDS(seg, off, size) \ + _inlinePointerFromWords((WORD)(seg), (WORD)(off), (WORD)(size)) + +// +// _inlinePointerFromWords - the POINTER_FROM_WORDS macro is inefficient if the +// arguments are calls to eg. getES(), getBX() - the calls are made twice if +// the pointer turns out to be non-zero. Use an inline function to achieve the +// same results, but only call function arguments once +// + +__inline LPVOID _inlinePointerFromWords(WORD seg, WORD off, WORD size) { + return (seg | off) + ? (LPVOID)GetVDMPointer((ULONG)(MAKELONG(off, seg)), size, (CHAR)((getMSW() & MSW_PE) ? TRUE : FALSE)) + : NULL; +} + +// +// GET_POINTER - does the same thing as POINTER_FROM_WORDS, but we know beforehand +// which processor mode we are in +// + +#define GET_POINTER(seg, off, size, mode) \ + _inlineGetPointer((WORD)(seg), (WORD)(off), (WORD)(size), (BOOL)(mode)) + +__inline LPVOID _inlineGetPointer(WORD seg, WORD off, WORD size, BOOL mode) { + return (seg | off) + ? (LPVOID)GetVDMPointer(MAKELONG(off, seg), size, (UCHAR)mode) + : NULL; +} + +// +// GET_FAR_POINTER - same as READ_FAR_POINTER with the same proviso as for +// GET_POINTER +// + +#define GET_FAR_POINTER(addr, mode) ((LPBYTE)(GET_POINTER(GET_SELECTOR(addr), GET_OFFSET(addr), sizeof(LPBYTE), mode))) + +// +// GET_SELECTOR - retrieves the selector word from the intel 32-bit far pointer +// (DWORD) pointed at by (remember: stored as offset, segment) +// + +#define GET_SELECTOR(pointer) READ_WORD((LPWORD)(pointer)+1) + +// +// GET_SEGMENT - same as GET_SELECTOR +// + +#define GET_SEGMENT(pointer) GET_SELECTOR(pointer) + +// +// GET_OFFSET - retrieves the offset word from an intel 32-bit far pointer +// (DWORD) pointed at by (remember: stored as offset, segment) +// + +#define GET_OFFSET(pointer) READ_WORD((LPWORD)(pointer)) + +// +// READ_FAR_POINTER - read the pair of words in VDM memory, currently pointed at +// by a 32-bit flat pointer and convert them to a 32-bit flat pointer +// + +#define READ_FAR_POINTER(addr) ((LPBYTE)(POINTER_FROM_WORDS(GET_SELECTOR(addr), GET_OFFSET(addr), sizeof(LPBYTE)))) + +// +// READ_WORD - read a single 16-bit little-endian word from VDM memory. On non +// Intel platforms, use unaligned pointer to access data +// + +#define READ_WORD(addr) (*((ULPWORD)(addr))) + +// +// READ_DWORD - read a 4-byte little-endian double word from VDM memory. On non +// Intel platforms, use unaligned pointer to access data +// + +#define READ_DWORD(addr) (*((ULPDWORD)(addr))) + +// +// ARRAY_ELEMENTS - gives the number of elements of a particular type in an +// array +// + +#define ARRAY_ELEMENTS(a) (sizeof(a)/sizeof((a)[0])) + +// +// LAST_ELEMENT - returns the index of the last element in array +// + +#define LAST_ELEMENT(a) (ARRAY_ELEMENTS(a)-1) + +#endif // _VWVDM_H_ -- cgit v1.2.3