|
|
/*++
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
|