/*++ 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); }