windows-nt/Source/XPSP1/NT/ds/nw/vwipxspx/dll/socket.c
2020-09-26 16:20:57 +08:00

2322 lines
57 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
//
// 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()
));
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 -= (USHORT) len;
pXecb->ActualLength += (USHORT)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;
LPSPX_PACKET pPacket; //Multi-User addition
int flags = 0; //Multi-User addition
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)
));
//======Multi-User code merge ==============================
// 2/18/97 cjc Code copied from _VwSPXSendSequencedPacket (vwspx.c) to fix
// problem where EndOfMessage bit was being set prematurely and
// caused BSPXCOM8 error messages with Btrieve.
//
// 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
//
pPacket = (LPSPX_PACKET)GET_FAR_POINTER(
&(ECB_FRAGMENT(pXecb->Ecb, 0)->Address),
IS_PROT_MODE(pXecb)
);
if (pPacket) {
flags = (pPacket->ConnectionControl & SPX_END_OF_MESSAGE)
? 0
: MSG_PARTIAL
;
}
rc = send(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, flags);
//rc = send(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, 0); //Original
//======Multi-User code merge End ==============================
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
));
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