2322 lines
57 KiB
C
2322 lines
57 KiB
C
/*++
|
||
|
||
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
|