Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1396 lines
37 KiB

/*++
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;
// tommye - MS 30525
//
// Make sure the xecb alloc didn't fail
// We don't know what the real SPX does if a malloc fails.
//
if (pXecb == NULL) {
return SPX_BAD_SEND_REQUEST;
}
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)
);
if (pPacket == NULL) {
CompleteEcb(pXecb, ECB_CC_BAD_SEND_REQUEST);
return SPX_BAD_SEND_REQUEST;
}
//
// 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;
// tommye - MS 30525
//
// Make sure the xecb alloc didn't fail
//
if (pXecb == NULL) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_SPXListenForConnection,
IPXDBG_LEVEL_ERROR,
"RetrieveXEcb returned NULL pointer"
));
return;
}
//
// 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;
//
// 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()
));
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
));
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
//
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;
// tommye - MS 30525
//
// Make sure the xecb alloc didn't fail
//
if (pXecb == NULL) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_SPXListenForSequencedPacket,
IPXDBG_LEVEL_ERROR,
"RetrieveXEcb returned NULL pointer"
));
return;
}
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;
// tommye - MS 30525
//
// Make sure the xecb alloc didn't fail
//
if (pXecb == NULL) {
IPXDBGPRINT((__FILE__, __LINE__,
FUNCTION_SPXSendSequencedPacket,
IPXDBG_LEVEL_ERROR,
"RetrieveXEcb returned NULL pointer"
));
return;
}
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)
//
// 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);
}
//
// 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;
// tommye - MS 30525
//
// Make sure the xecb alloc didn't fail
//
if (pXecb == NULL) {
return;
}
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)
);
if (pPacket == NULL) {
completionCode = ECB_CC_CONNECTION_ABORTED;
status = ECB_CC_BAD_REQUEST;
}
else {
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);
}