Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3680 lines
100 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
sockets.c
Abstract:
Implements the network interface for the home net transport.
Author:
Jim Schmidt (jimschm) 01-Jul-2000
Revision History:
<full name> (<alias>) <date> <comments>
--*/
//
// Includes
//
#include "pch.h"
#include <winsock2.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include <wsnetbs.h>
#include <nb30.h>
#include <ws2tcpip.h>
#include <lmcons.h>
#include <lmaccess.h>
#include <lmwksta.h>
#include "homenetp.h"
#define DBG_HOMENET "HomeNet"
//
// Strings
//
#define S_64CHARTAG TEXT("usmt-v2@01234567890123456789012345678901234567890123456789012345")
//
// Constants
//
#define IDLE_TIMEOUT 45
#define TCPIP_BROADCAST_PORT 2048
#define IPX_BROADCAST_PORT 1150
#define NETBIOS_BROADCAST_PORT 0x50
#define TCPIP_CONNECT_PORT 2049
#define IPX_CONNECT_PORT 1151
#define NETBIOS_CONNECT_PORT 0x51
#define NAME_SIZE 64
#define NAME_SIZE_PLUS_NUL 65
#define NAME_SIZE_PLUS_COMMA 65
#define NAME_SIZE_PLUS_COMMA_PLUS_NUL 66
#define MIN_MESSAGE_SIZE (NAME_SIZE_PLUS_COMMA_PLUS_NUL + 2)
#define MAX_DATA_PACKET_SIZE 65536
//
// Macros
//
// none
//
// Types
//
typedef INT (WSAIOCTL)(
SOCKET s,
DWORD IoControlCode,
PVOID InBuffer,
DWORD InBufferSize,
PVOID OutBuffer,
DWORD OutBufferSize,
PDWORD BytesReturned,
WSAOVERLAPPED *Overlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine
);
typedef WSAIOCTL *PWSAIOCTL;
typedef struct {
SOCKET Socket;
BYTE BroadcastAddress[MAX_SOCKADDR];
INT AddressLen;
INT Family;
INT Protocol;
} BROADCASTSOCKET, *PBROADCASTSOCKET;
typedef struct {
SOCKET Socket;
INT Family;
INT Protocol;
BOOL Datagram;
} LISTENSOCKET, *PLISTENSOCKET;
typedef struct {
PBROADCASTSOCKET BroadcastSockets;
INT BroadcastCount;
PLISTENSOCKET ListenSockets;
INT ListenCount;
CONNECTIONSOCKET ConnectionSocket;
PGROWBUFFER AddressArray;
UINT Timeout;
} BROADCASTARGS, *PBROADCASTARGS;
typedef NET_API_STATUS(WINAPI NETWKSTAGETINFO)(PWSTR, DWORD, PBYTE *);
typedef NETWKSTAGETINFO *PNETWKSTAGETINFO;
typedef NET_API_STATUS(WINAPI NETAPIBUFFERFREE)(PVOID);
typedef NETAPIBUFFERFREE *PNETAPIBUFFERFREE;
//
// Globals
//
HANDLE g_StopHandle;
HANDLE g_ConnectionDone;
CHAR g_GlobalKey [GLOBALKEY_SIZE + 1];
//
// Macro expansion list
//
// none
//
// Private function prototypes
//
// none
//
// Macro expansion definition
//
// none
//
// Code
//
PBROADCASTSOCKET
pOpenOneBroadcastSocket (
IN OUT PGROWBUFFER BroadcastSockets,
IN SOCKADDR *SockAddr,
IN INT SockAddrLen,
IN INT Family,
IN INT Protocol,
IN PCTSTR DebugText
)
/*++
Routine Description:
pOpenOneBroadcastSocket opens a socket for the specified
family/protocol/address combination, sets the socket into SO_REUSEADDR and
SO_BROADCAST mode, and puts the socket information in the array stored in
the caller's grow buffer.
The socket opened will be used for broadcast send or receive.
Arguments:
BroadcastSockets - Specifies the grow buffer that holds the array of
BROADCASTSOCKET elements. Receives an additional entry
on success.
SockAddr - Specifies the protocol-specific socket address structure
(cast to SOCKADDR), giving the broadcast address.
SockAddrLen - Specifies the length of SockAddr, in bytes
Family - Specifies the protocol family (AF_IPX, AF_INET)
Protocol - Specifies the protocol (IPPROTO_UDP, NSPROTO_IPX, -lana)
DebugText - Specifies the protocol in text form for debug messages
Return Value:
A pointer to the new BROADCASTSOCKET element allocated from BroadcastSockets,
or NULL if the socket could not be opened.
NOTE: BroadcastSockets->Buf will potentially change on success. Do not rely
on this address.
--*/
{
PBROADCASTSOCKET broadcastSocket;
BOOL b;
broadcastSocket = (PBROADCASTSOCKET) GbGrow (BroadcastSockets, sizeof (BROADCASTSOCKET));
broadcastSocket->Socket = socket (Family, SOCK_DGRAM, Protocol);
if (broadcastSocket->Socket != INVALID_SOCKET) {
b = TRUE;
setsockopt (broadcastSocket->Socket, SOL_SOCKET, SO_BROADCAST, (PBYTE) &b, sizeof (b));
setsockopt (broadcastSocket->Socket, SOL_SOCKET, SO_REUSEADDR, (PBYTE) &b, sizeof (b));
if (bind (broadcastSocket->Socket, SockAddr, SockAddrLen)) {
DEBUGMSG ((DBG_ERROR, "Can't bind to %s socket", DebugText));
closesocket (broadcastSocket->Socket);
broadcastSocket->Socket = INVALID_SOCKET;
}
}
if (broadcastSocket->Socket == INVALID_SOCKET) {
BroadcastSockets->End -= sizeof (BROADCASTSOCKET);
broadcastSocket = NULL;
} else {
DEBUGMSG ((
DBG_HOMENET,
"%s is available for broadcast on socket %u",
DebugText,
(BroadcastSockets->End / sizeof (BROADCASTSOCKET)) - 1
));
broadcastSocket->AddressLen = SockAddrLen;
MYASSERT (SockAddrLen <= MAX_SOCKADDR);
CopyMemory (broadcastSocket->BroadcastAddress, (PBYTE) SockAddr, SockAddrLen);
broadcastSocket->Family = Family;
broadcastSocket->Protocol = Protocol;
}
return broadcastSocket;
}
INT
pOpenBroadcastSockets (
OUT PGROWBUFFER BroadcastSockets
)
/*++
Routine Description:
pOpenBroadcastSockets opens a broadcast socket on each supported protocol.
Arguments:
BroadcastSockets - Receives an array of BROADCASTSOCKET elements (one for
each protocol). IMPORTANT: This parameter must be
zero-initialized by the caller.
Return Value:
The number of elements in BroadcastSockets, or zero on failure.
--*/
{
SOCKADDR_IPX ipxAddr;
SOCKADDR_IN tcpipAddr;
PBROADCASTSOCKET broadcastSocket;
MYASSERT (!BroadcastSockets->Buf && !BroadcastSockets->End);
//
// Open sockets for broadcasts
//
// IPX
ZeroMemory (&ipxAddr, sizeof (ipxAddr));
ipxAddr.sa_family = AF_IPX;
ipxAddr.sa_socket = IPX_BROADCAST_PORT;
broadcastSocket = pOpenOneBroadcastSocket (
BroadcastSockets,
(SOCKADDR *) &ipxAddr,
sizeof (ipxAddr),
AF_IPX,
NSPROTO_IPX,
TEXT("IPX")
);
if (broadcastSocket) {
memset (ipxAddr.sa_nodenum, 0xFF, 6);
CopyMemory (broadcastSocket->BroadcastAddress, &ipxAddr, sizeof (ipxAddr));
}
// TCP/IP
ZeroMemory (&tcpipAddr, sizeof (tcpipAddr));
tcpipAddr.sin_family = AF_INET;
tcpipAddr.sin_addr.s_addr = htonl (INADDR_ANY);
tcpipAddr.sin_port = TCPIP_BROADCAST_PORT;
broadcastSocket = pOpenOneBroadcastSocket (
BroadcastSockets,
(SOCKADDR *) &tcpipAddr,
sizeof (tcpipAddr),
AF_INET,
IPPROTO_UDP,
TEXT("UDP")
);
if (broadcastSocket) {
tcpipAddr.sin_addr.s_addr = htonl (INADDR_BROADCAST);
CopyMemory (broadcastSocket->BroadcastAddress, &tcpipAddr, sizeof (tcpipAddr));
}
return BroadcastSockets->End / sizeof (BROADCASTSOCKET);
}
PLISTENSOCKET
pOpenOneListenSocket (
IN OUT PGROWBUFFER ListenSockets,
IN SOCKADDR *SockAddr,
IN INT SockAddrLen,
IN INT Family,
IN BOOL Multicast,
IN INT Protocol,
IN PCTSTR DebugText
)
/*++
Routine Description:
pOpenOneListenSocket opens a socket for the specified
family/protocol/address combination, sets the socket into SO_REUSEADDR mode,
and puts the socket information in the array stored in the caller's grow
buffer. If Multicast is specified, then SO_BROADCAST is also set.
Otherwise, the socket is set to listen for one connection.
The socket opened will be used to accept connections.
Arguments:
ListenSockets - Specifies the grow buffer that holds the array of
LISTENSOCKET elements. Receives an additional entry on
success.
SockAddr - Specifies the protocol-specific socket address structure
(cast to SOCKADDR), giving the local address for binding.
SockAddrLen - Specifies the length of SockAddr, in bytes
Family - Specifies the protocol family (AF_IPX, AF_INET)
Multicast - Specifies TRUE if the protocol family does not support
streaming sockets, but instead uses datagrams for all data
transfer. (NetBIOS for example is a multicast protocol.)
NOTE: UNSUPPORTED because NetBIOS is not implemented anymore
Protocol - Specifies the protocol (IPPROTO_UDP, NSPROTO_IPX, -lana)
DebugText - Specifies the protocol in text form for debug messages
Return Value:
A pointer to the new LISTENSOCKET element allocated from ListenSockets, or
NULL if the socket could not be opened.
NOTE: ListenSockets->Buf will potentially change on success. Do not rely on
this address.
--*/
{
PLISTENSOCKET listenSocket;
BOOL b;
listenSocket = (PLISTENSOCKET) GbGrow (ListenSockets, sizeof (LISTENSOCKET));
listenSocket->Socket = socket (Family, Multicast ? SOCK_DGRAM : SOCK_STREAM, Protocol);
listenSocket->Datagram = Multicast;
listenSocket->Family = Family;
listenSocket->Protocol = Protocol;
if (listenSocket->Socket != INVALID_SOCKET) {
b = TRUE;
setsockopt (listenSocket->Socket, SOL_SOCKET, SO_REUSEADDR, (PBYTE) &b, sizeof (b));
if (Multicast) {
setsockopt (listenSocket->Socket, SOL_SOCKET, SO_BROADCAST, (PBYTE) &b, sizeof (b));
}
if (bind (listenSocket->Socket, SockAddr, SockAddrLen) ||
(!Multicast && listen (listenSocket->Socket, 1))
) {
DEBUGMSG ((DBG_ERROR, "Can't bind/listen to %s socket", DebugText));
closesocket (listenSocket->Socket);
listenSocket->Socket = INVALID_SOCKET;
}
}
if (listenSocket->Socket == INVALID_SOCKET) {
ListenSockets->End -= sizeof (LISTENSOCKET);
listenSocket = NULL;
} else {
DEBUGMSG ((
DBG_HOMENET,
"%s is availble for connection on socket %u",
DebugText,
(ListenSockets->End / sizeof (LISTENSOCKET)) - 1
));
}
return listenSocket;
}
INT
pOpenListenSockets (
OUT PGROWBUFFER ListenSockets
)
/*++
Routine Description:
pOpenListenSockets opens a connection socket on each supported protocol.
Arguments:
ListenSockets - Receives an array of LISTENSOCKET elements (one for each
protocol). IMPORTANT: This parameter must be
zero-initialized by the caller.
Return Value:
The number of elements in ListenSockets, or zero on failure.
--*/
{
SOCKADDR_IPX ipxAddr;
SOCKADDR_IN tcpipAddr;
MYASSERT (!ListenSockets->Buf && !ListenSockets->End);
//
// Open sockets to accept inbound connections
//
// SPX
ZeroMemory (&ipxAddr, sizeof (ipxAddr));
ipxAddr.sa_family = AF_IPX;
ipxAddr.sa_socket = IPX_CONNECT_PORT;
pOpenOneListenSocket (
ListenSockets,
(SOCKADDR *) &ipxAddr,
sizeof (ipxAddr),
AF_IPX,
FALSE,
NSPROTO_SPX,
TEXT("SPX")
);
// TCP/IP
ZeroMemory (&tcpipAddr, sizeof (tcpipAddr));
tcpipAddr.sin_family = AF_INET;
tcpipAddr.sin_port = TCPIP_CONNECT_PORT;
pOpenOneListenSocket (
ListenSockets,
(SOCKADDR *) &tcpipAddr,
sizeof (tcpipAddr),
AF_INET,
FALSE,
IPPROTO_TCP,
TEXT("TCP")
);
return ListenSockets->End / sizeof (LISTENSOCKET);
}
PCTSTR
pGetNameFromMessage (
IN PCWSTR Message
)
/*++
Routine Description:
pGetNameFromMessage extracts the computer name from a broadcast.
Arguments:
Message - Specifies the encoded message.
Return Value:
The computer name encoded in the message, or NULL if the message is garbage.
--*/
{
PCTSTR message = NULL;
PCTSTR orgMessage = NULL;
TCHAR sigStr [sizeof (TEXT("0xFFFFFFFF"))];
DWORD signature = 0;
PCTSTR p;
PCTSTR name = NULL;
INT len;
CHARTYPE ch;
PCTSTR tag = S_64CHARTAG; // must be 64 chars
TCHAR alternateTag[NAME_SIZE_PLUS_NUL];
TCHAR prefix[NAME_SIZE_PLUS_COMMA_PLUS_NUL];
PTSTR q, r;
#ifdef UNICODE
orgMessage = Message;
#else
orgMessage = ConvertWtoA (Message);
#endif
if (!orgMessage) {
return name;
}
message = orgMessage;
__try {
p = _tcschr (message, TEXT(','));
if (!p) {
DEBUGMSG ((DBG_HOMENET, "Invalid Signature"));
__leave;
}
ZeroMemory (sigStr, sizeof (sigStr));
CopyMemory (sigStr, message, min (sizeof (sigStr) - 1, ((UINT)(p - message)) * sizeof (TCHAR)));
_stscanf (sigStr, TEXT("0x%08X"), &signature);
if (signature != HOMENETTR_SIG) {
DEBUGMSG ((DBG_HOMENET, "Signature does not match"));
__leave;
}
message = _tcsinc (p);
if (!message) {
DEBUGMSG ((DBG_HOMENET, "Invalid Signature"));
__leave;
}
if (IsmCopyEnvironmentString (PLATFORM_SOURCE, NULL, TRANSPORT_ENVVAR_HOMENET_TAG, alternateTag)) {
q = GetEndOfString (alternateTag);
r = alternateTag + NAME_SIZE;
while (q < r) {
*q++ = TEXT('@');
}
*r = 0;
tag = alternateTag;
}
DEBUGMSG ((DBG_HOMENET, "Comparing our tag %s against message %s", tag, message));
StringCopy (prefix, tag);
StringCat (prefix, TEXT(","));
if (StringIPrefix (message, prefix)) {
p = message + NAME_SIZE_PLUS_COMMA;
len = 0;
while (*p) {
ch = (CHARTYPE) _tcsnextc (p);
p = _tcsinc (p);
if (ch == TEXT(',')) {
break;
}
if (ch < TEXT('0') || ch > TEXT('9')) {
break;
}
len = len * 10 + (ch - TEXT('0'));
}
if (ch == TEXT(',') && len < MAX_COMPUTER_NAME) {
name = p;
while (*p && len) {
if (*p < 32) {
break;
}
p++;
len--;
}
if (len || *p) {
name = NULL;
}
}
}
ELSE_DEBUGMSG ((DBG_HOMENET, "TAG does not match"));
}
__finally {
#ifndef UNICODE
if (orgMessage) {
FreeConvertedStr (orgMessage);
orgMessage = NULL;
}
#endif
}
return name;
}
VOID
pTranslateBroadcastAddrToConnectAddr (
IN INT Family,
IN OUT PINT Protocol,
IN OUT PBOOL Datagram,
IN OUT SOCKADDR *SockAddr
)
/*++
Routine Description:
pTranslateBroadcastAddrToConnectAddr transforms a broadcast address into a
connection address. The broadcast address is typically obtained from a
datagram response, and must be transformed before accepting a sequenced
connection.
Arguments:
Family - Specifies the protocol family
Protocol - Specifies the datagram protocol; receives the sequenced packet
protocol if available
Datagram - Specifies a pointer to FALSE, receives TRUE if the protocol does
not support sequenced connections.
SockAddr - Specifies the peer socket address. Receives the updated address
(a different port is used for connections).
Return Value:
None.
--*/
{
SOCKADDR_IPX *ipxAddr;
SOCKADDR_IN *tcpipAddr;
switch (Family) {
case AF_INET:
*Protocol = IPPROTO_TCP;
tcpipAddr = (SOCKADDR_IN *) SockAddr;
tcpipAddr->sin_port = TCPIP_CONNECT_PORT;
break;
case AF_IPX:
*Protocol = NSPROTO_SPX;
ipxAddr = (SOCKADDR_IPX *) SockAddr;
ipxAddr->sa_socket = IPX_CONNECT_PORT;
break;
}
}
VOID
pResetPort (
IN INT Family,
IN OUT SOCKADDR *SockAddr
)
/*++
Routine Description:
pResetPort sets the port to zero for TCP/IP, so that the system will pick
an unused port for the local address. This is used when connecting.
Arguments:
Family - Specifies the protocol family (such as AF_INET)
SockAddr - Specifies the address to reset
Return Value:
None.
--*/
{
SOCKADDR_IN *tcpipAddr;
switch (Family) {
case AF_INET:
tcpipAddr = (SOCKADDR_IN *) SockAddr;
tcpipAddr->sin_port = 0;
break;
}
}
INT
pSourceBroadcast (
IN OUT PBROADCASTARGS Args
)
/*++
Routine Description:
pSourceBroadcast implements the name resolution mechanism for the source
end of the connection. This involves checking for cancel, collecting
inbound datagrams from all transports, and parsing the datagrams to obtain
the server name.
Arguments:
Args - Specifies a structure containing all of the parameters, such as
the socket array and socket addresses.
Return Value:
The number of server addresses collected, or 0 if the collection was
cancelled.
--*/
{
INT i;
INT bytesIn;
DWORD rc;
WCHAR message[256];
FD_SET set;
TIMEVAL zero = {0,0};
INT waitCycle = -1;
BOOL result = FALSE;
PCTSTR name;
PCONNECTADDRESS address;
PCONNECTADDRESS end;
PBROADCASTSOCKET broadcastSocket;
BYTE remoteAddr[MAX_SOCKADDR];
INT remoteAddrLen;
DWORD startTick = GetTickCount();
for (;;) {
//
// Check cancel
//
if (g_StopHandle) {
rc = WaitForSingleObject (g_StopHandle, 0);
} else {
rc = WAIT_FAILED;
}
if (rc == WAIT_OBJECT_0 || IsmCheckCancel()) {
result = FALSE;
break;
}
//
// Check time to live
//
if (Args->Timeout) {
if (((GetTickCount() - startTick) / 1000) >= Args->Timeout) {
DEBUGMSG ((DBG_HOMENET, "Name search timed out"));
break;
}
}
if (waitCycle > -1) {
waitCycle--;
if (!waitCycle) {
break;
}
}
//
// Check for a message
//
FD_ZERO (&set);
for (i = 0 ; i < Args->BroadcastCount ; i++) {
FD_SET (Args->BroadcastSockets[i].Socket, &set);
}
i = select (0, &set, NULL, NULL, &zero);
if (i > 0) {
for (i = 0 ; i < Args->BroadcastCount ; i++) {
broadcastSocket = &Args->BroadcastSockets[i];
if (FD_ISSET (broadcastSocket->Socket, &set)) {
remoteAddrLen = MAX_SOCKADDR;
bytesIn = recvfrom (
broadcastSocket->Socket,
(PSTR) message,
254 * sizeof (WCHAR),
0,
(SOCKADDR *) remoteAddr,
&remoteAddrLen
);
if (bytesIn >= (MIN_MESSAGE_SIZE * sizeof (WCHAR))) {
message[bytesIn] = 0;
message[bytesIn + 1] = 0;
//
// Parse the inbound text. It must be in the format of
//
// <signature>,<tag>,<tchars>,<name>
//
// <tag> must be 64 characters, and is usmt-v2 by default
// (followed by fill numbers).
//
name = pGetNameFromMessage (message);
if (name) {
// once we receive something, wait 5 additional seconds for other inbound datagrams
if (waitCycle == -1) {
waitCycle = 20;
}
result = TRUE;
//
// Scan the address list for the name
//
address = (PCONNECTADDRESS) Args->AddressArray->Buf;
end = (PCONNECTADDRESS) (Args->AddressArray->Buf + Args->AddressArray->End);
while (address < end) {
if (StringIMatch (address->DestinationName, name)) {
if (address->Family == broadcastSocket->Family) {
break;
}
}
address++;
}
if (address >= end) {
//
// New computer name; add to the address list
//
address = (PCONNECTADDRESS) GbGrow (Args->AddressArray, sizeof (CONNECTADDRESS));
address->RemoteAddressLen = remoteAddrLen;
CopyMemory (address->RemoteAddress, remoteAddr, remoteAddrLen);
address->LocalAddressLen = MAX_SOCKADDR;
if (getsockname (
broadcastSocket->Socket,
(SOCKADDR *) address->LocalAddress,
&address->LocalAddressLen
)) {
address->LocalAddressLen = broadcastSocket->AddressLen;
ZeroMemory (address->LocalAddress, broadcastSocket->AddressLen);
DEBUGMSG ((DBG_HOMENET, "Failed to get local socket name; using nul name instead"));
}
address->Family = broadcastSocket->Family;
address->Protocol = broadcastSocket->Protocol;
address->Datagram = FALSE;
pTranslateBroadcastAddrToConnectAddr (
address->Family,
&address->Protocol,
&address->Datagram,
(SOCKADDR *) &address->RemoteAddress
);
StringCopy (address->DestinationName, name);
DEBUGMSG ((DBG_HOMENET, "Destination found: %s (protocol %i)", name, address->Family));
}
}
ELSE_DEBUGMSGW ((DBG_HOMENET, "garbage found: %s", message));
}
}
}
}
Sleep (250);
}
return result ? Args->AddressArray->End / sizeof (CONNECTADDRESS) : 0;
}
BOOL
pIsAddrFromLocalSubnet (
IN SOCKET Socket,
IN INT Family,
IN SOCKADDR *Address,
IN INT AddressLength
)
{
SOCKADDR_IPX *ipxAddr;
SOCKADDR_IN *tcpipAddr;
BOOL result = TRUE;
IPX_ADDRESS_DATA ipxLocalAddr;
INT size;
PWSAIOCTL wsaIoctlFn;
HANDLE lib;
INT rc;
INTERFACE_INFO info[32];
DWORD bytesRead;
INT i;
INT j;
SOCKADDR_IN localAddr;
PBYTE localNetPtr;
PBYTE remoteNetPtr;
PBYTE subnetMaskPtr;
INT k;
switch (Family) {
case AF_INET:
tcpipAddr = (SOCKADDR_IN *) Address;
i = sizeof (localAddr);
if (getsockname (Socket, (SOCKADDR *) &localAddr, &i)) {
DEBUGMSG ((DBG_ERROR, "Can't get local socket addr"));
break;
}
lib = LoadLibrary (TEXT("ws2_32.dll"));
if (lib) {
wsaIoctlFn = (PWSAIOCTL) GetProcAddress (lib, "WSAIoctl");
if (wsaIoctlFn) {
rc = wsaIoctlFn (
Socket,
SIO_GET_INTERFACE_LIST,
NULL,
0,
info,
sizeof (info),
&bytesRead,
NULL,
NULL
);
if (rc != SOCKET_ERROR) {
j = (INT) (bytesRead / sizeof (INTERFACE_INFO));
for (i = 0 ; i < j ; i++) {
if (!memcmp (
&localAddr.sin_addr,
&info[i].iiAddress.AddressIn.sin_addr,
sizeof (struct in_addr)
)) {
localNetPtr = (PBYTE) &localAddr.sin_addr;
remoteNetPtr = (PBYTE) &info[i].iiAddress.AddressIn.sin_addr;
subnetMaskPtr = (PBYTE) &info[i].iiNetmask.AddressIn.sin_addr;
for (k = 0 ; k < sizeof (struct in_addr) ; k++) {
localNetPtr[k] &= subnetMaskPtr[k];
remoteNetPtr[k] &= subnetMaskPtr[k];
if (localNetPtr[k] != remoteNetPtr[k]) {
break;
}
}
if (k < sizeof (struct in_addr)) {
LOG ((LOG_WARNING, (PCSTR) MSG_REFUSE_OUTSIDE_CONNECTION));
} else {
DEBUGMSG ((DBG_HOMENET, "Found interface on the same subnet!"));
}
break;
}
}
}
ELSE_DEBUGMSG ((DBG_ERROR, "WSAIoctl failed"));
}
ELSE_DEBUGMSG ((DBG_WARNING, "Can't load WSAIoctl"));
FreeLibrary (lib);
}
ELSE_DEBUGMSG ((DBG_WARNING, "Can't load ws2_32.dll"));
break;
case AF_IPX:
ipxAddr = (SOCKADDR_IPX *) Address;
//
// Compare the specified address against the local address of the socket
//
size = sizeof (ipxLocalAddr);
if (!getsockopt (Socket, NSPROTO_IPX, IPX_GETNETINFO, (PBYTE) &ipxLocalAddr, &size)) {
if (memcmp (ipxAddr->sa_netnum, ipxLocalAddr.netnum, 4)) {
if (ipxAddr->sa_netnum[0] || ipxAddr->sa_netnum[1] ||
ipxAddr->sa_netnum[2] || ipxAddr->sa_netnum[3]
) {
LOG ((LOG_WARNING, (PCSTR) MSG_REFUSE_OUTSIDE_CONNECTION_IPX));
result = FALSE;
}
}
}
break;
}
return result;
}
BOOL
pDestinationBroadcast (
IN OUT PBROADCASTARGS Args
)
/*++
Routine Description:
pDestinationBroadcast implements the name resolution mechanism for the
destination end of the connection. This involves checking for cancel, and
sending out regular datagrams to all transports to provide the server name.
At the same time, listen connections are monitored, and the datagram traffic
is stopped once one connection is accepted.
Arguments:
Args - Specifies a structure containing all of the parameters, such as the
socket array and socket addresses. Receives the connection address.
Return Value:
TRUE if a connection was accepted, or FALSE if cancel was detected.
--*/
{
INT i;
DWORD rc;
INT socketNum = 0;
WCHAR message[256];
TCHAR name[128];
UINT size;
FD_SET set;
TIMEVAL zero = {0,0};
PBROADCASTSOCKET broadcastSocket;
BOOL result = FALSE;
PCTSTR tag = S_64CHARTAG; // must be 64 chars
TCHAR alternateTag[NAME_SIZE_PLUS_NUL];
PTSTR p, q;
LINGER linger;
size = MAX_COMPUTER_NAME;
GetComputerName (name, &size);
//
// Get the tag that is registered in the environment
//
if (IsmCopyEnvironmentString (PLATFORM_DESTINATION, NULL, TRANSPORT_ENVVAR_HOMENET_TAG, alternateTag)) {
p = GetEndOfString (alternateTag);
q = alternateTag + NAME_SIZE;
if (p) {
while (p < q) {
*p++ = TEXT('@');
}
}
*q = 0;
tag = alternateTag;
}
DEBUGMSG ((DBG_HOMENET, "Broadcasting using the following tag: %s", tag));
#ifdef UNICODE
size = wsprintfW (message, L"0x%08X,%s,%u,%s", HOMENETTR_SIG, tag, TcharCount (name), name);
#else
size = wsprintfW (message, L"0x%08X,%S,%u,%S", HOMENETTR_SIG, tag, TcharCount (name), name);
#endif
size = (size + 1) * sizeof (WCHAR);
for (;;) {
//
// Check cancel
//
if (g_StopHandle) {
rc = WaitForSingleObject (g_StopHandle, 0);
} else {
rc = WAIT_FAILED;
}
if (rc == WAIT_OBJECT_0 || IsmCheckCancel()) {
break;
}
if (g_BackgroundThreadTerminate) {
rc = WaitForSingleObject (g_BackgroundThreadTerminate, 0);
if (rc == WAIT_OBJECT_0) {
break;
}
}
//
// Send out the message
//
broadcastSocket = &Args->BroadcastSockets[socketNum];
i = sendto (
broadcastSocket->Socket,
(PSTR) message,
size,
0,
(SOCKADDR *) broadcastSocket->BroadcastAddress,
broadcastSocket->AddressLen
);
if (i == SOCKET_ERROR) {
DEBUGMSG ((DBG_VERBOSE, "Error sending on socket %u: %u", socketNum, WSAGetLastError()));
} else {
Sleep (350);
DEBUGMSG ((DBG_HOMENET, "Sent data on socket %u", socketNum));
}
socketNum++;
if (socketNum >= Args->BroadcastCount) {
socketNum = 0;
}
//
// Check for an inbound connection
//
FD_ZERO (&set);
for (i = 0 ; i < Args->ListenCount ; i++) {
FD_SET (Args->ListenSockets[i].Socket, &set);
}
i = select (0, &set, NULL, NULL, &zero);
if (i > 0) {
DEBUGMSG ((DBG_HOMENET, "Connection request count = %i", i));
for (i = 0 ; i < Args->ListenCount ; i++) {
if (FD_ISSET (Args->ListenSockets[i].Socket, &set)) {
Args->ConnectionSocket.RemoteAddressLen = MAX_SOCKADDR;
if (!Args->ListenSockets[i].Datagram) {
Args->ConnectionSocket.Socket = accept (
Args->ListenSockets[i].Socket,
(SOCKADDR *) Args->ConnectionSocket.RemoteAddress,
&Args->ConnectionSocket.RemoteAddressLen
);
//
// Verify socket connection is on the subnet only
//
if (!pIsAddrFromLocalSubnet (
Args->ConnectionSocket.Socket,
Args->ListenSockets[i].Family,
(SOCKADDR *) Args->ConnectionSocket.RemoteAddress,
Args->ConnectionSocket.RemoteAddressLen
)) {
LOG ((LOG_WARNING, (PCSTR) MSG_OUTSIDE_OF_LOCAL_SUBNET));
closesocket (Args->ConnectionSocket.Socket);
Args->ConnectionSocket.Socket = INVALID_SOCKET;
} else {
linger.l_onoff = 1;
linger.l_linger = IDLE_TIMEOUT;
setsockopt (
Args->ConnectionSocket.Socket,
SOL_SOCKET,
SO_LINGER,
(PBYTE) &linger,
sizeof (linger)
);
DEBUGMSG ((DBG_HOMENET, "Connection requested"));
}
} else {
DEBUGMSG ((DBG_HOMENET, "Accepting datagram connection"));
if (DuplicateHandle (
GetCurrentProcess(),
(HANDLE) Args->ListenSockets[i].Socket,
GetCurrentProcess(),
(HANDLE *) &Args->ConnectionSocket.Socket,
0,
FALSE,
DUPLICATE_SAME_ACCESS
)) {
getpeername (
Args->ConnectionSocket.Socket,
(SOCKADDR *) Args->ConnectionSocket.RemoteAddress,
&Args->ConnectionSocket.RemoteAddressLen
);
} else {
DEBUGMSG ((DBG_ERROR, "Can't duplicate socket handle"));
Args->ConnectionSocket.Socket = INVALID_SOCKET;
}
}
if (Args->ConnectionSocket.Socket != INVALID_SOCKET) {
Args->ConnectionSocket.Family = Args->ListenSockets[i].Family;
Args->ConnectionSocket.Protocol = Args->ListenSockets[i].Protocol;
Args->ConnectionSocket.Datagram = Args->ListenSockets[i].Datagram;
ZeroMemory (&Args->ConnectionSocket.DatagramPool, sizeof (DATAGRAM_POOL));
if (Args->ConnectionSocket.Datagram) {
Args->ConnectionSocket.DatagramPool.Pool = PmCreatePool();
Args->ConnectionSocket.DatagramPool.LastPacketNumber = (UINT) -1;
}
Args->ConnectionSocket.LocalAddressLen = MAX_SOCKADDR;
if (getsockname (
Args->ConnectionSocket.Socket,
(SOCKADDR *) Args->ConnectionSocket.LocalAddress,
&Args->ConnectionSocket.LocalAddressLen
)) {
Args->ConnectionSocket.LocalAddressLen = broadcastSocket->AddressLen;
ZeroMemory (Args->ConnectionSocket.LocalAddress, broadcastSocket->AddressLen);
DEBUGMSG ((DBG_HOMENET, "Failed to get local socket name; using nul name instead"));
}
DEBUGMSG ((DBG_HOMENET, "Connection accepted"));
result = TRUE;
break;
} else {
DEBUGMSG ((DBG_ERROR, "select indicated connection, but accept failed"));
}
}
}
if (result) {
break;
}
}
}
return result;
}
BOOL
pGetDomainUserName (
OUT PTSTR UserNameBuf // 65 char buffer
)
{
HKEY domainLogonKey;
PDWORD data;
BOOL result = TRUE;
DWORD size;
NET_API_STATUS rc;
PWKSTA_INFO_102 buffer;
HANDLE netApi32Lib;
PNETWKSTAGETINFO netWkstaGetInfo;
PNETAPIBUFFERFREE netApiBufferFree;
BYTE sid[256];
DWORD sidSize;
WCHAR domain[256];
DWORD domainSize;
SID_NAME_USE use;
if (!ISNT()) {
//
// Require the Log On To Domain setting to be checked
//
SetLastError (ERROR_SUCCESS);
domainLogonKey = OpenRegKeyStr (TEXT("HKLM\\Network\\Logon"));
if (!domainLogonKey) {
DEBUGMSG ((DBG_HOMENET, "No HKLM\\Network\\Logon key"));
return FALSE;
}
data = (PDWORD) GetRegValueBinary (domainLogonKey, TEXT("LMLogon"));
if (!data) {
DEBUGMSG ((DBG_HOMENET, "No LMLogon value"));
result = FALSE;
} else {
if (!(*data)) {
DEBUGMSG ((DBG_HOMENET, "Domain logon is not enabled"));
result = FALSE;
}
FreeAlloc (data);
}
CloseRegKey (domainLogonKey);
} else {
//
// Require domain membership
//
netApi32Lib = LoadLibrary (TEXT("netapi32.dll"));
if (netApi32Lib) {
netWkstaGetInfo = (PNETWKSTAGETINFO) GetProcAddress (netApi32Lib, "NetWkstaGetInfo");
netApiBufferFree = (PNETAPIBUFFERFREE) GetProcAddress (netApi32Lib, "NetApiBufferFree");
} else {
netWkstaGetInfo = NULL;
netApiBufferFree = NULL;
}
if (!netWkstaGetInfo || !netApiBufferFree) {
DEBUGMSG ((DBG_HOMENET, "Can't get net wksta apis"));
result = FALSE;
} else {
rc = netWkstaGetInfo (NULL, 102, (PBYTE *) &buffer);
if (rc == NO_ERROR) {
result = buffer->wki102_langroup && (buffer->wki102_langroup[0] != 0);
if (result) {
DEBUGMSGW ((DBG_HOMENET, "Getting account type of %s", buffer->wki102_langroup));
sidSize = ARRAYSIZE(sid);
domainSize = ARRAYSIZE(domain);
result = LookupAccountNameW (
NULL,
buffer->wki102_langroup,
sid,
&sidSize,
domain,
&domainSize,
&use
);
DEBUGMSG ((DBG_HOMENET, "Account type result is %u (use=%u)", result, use));
}
ELSE_DEBUGMSG ((DBG_HOMENET, "No langroup specified"));
netApiBufferFree (buffer);
} else {
DEBUGMSG ((DBG_HOMENET, "Can't get net wksta info"));
result = FALSE;
}
}
if (netApi32Lib) {
FreeLibrary (netApi32Lib);
}
}
//
// Make sure a user name is specified
//
if (result) {
size = NAME_SIZE_PLUS_NUL;
if (!GetUserName (UserNameBuf, &size)) {
result = FALSE;
} else if (*UserNameBuf == 0) {
result = FALSE;
}
if (result) {
DEBUGMSG ((DBG_HOMENET, "Domain user: %s", UserNameBuf));
} else {
DEBUGMSG ((DBG_HOMENET, "Not on domain"));
}
}
return result;
}
INT
pNameResolver (
IN MIG_PLATFORMTYPEID Platform,
OUT PGROWBUFFER AddressBuffer,
IN UINT SourceTimeout,
OUT PCONNECTIONSOCKET ConnectionSocket
)
/*++
Routine Description:
pNameResolver implements the name resolution protocol. The source side
collects datagrams, looking for a destination to choose from. The
destination side sends out broadcasts to announce themselves, and accepts a
connection from the source.
At the end of name resolution, an event is signaled. This is used for
coordination with cancel.
Arguments:
AddressBuffer - Receives the array of addresses that is used on the
source side to collect a list of destinations.
This buffer must be zero-initialized by the caller.
This argument is NULL on the destination side.
SourceTimeout - Specifies the number of seconds to wait for a broadcast,
or zero to wait forever. The timeout only affects the
source side.
ConnectionSocket - Receives the connection socket and address information
that is used on the destination side. This argument
is NULL on the source side.
Return Value:
Source Side: The number of addresses in AddressBuffer, or zero if an error
occurred
Destination Side: 1 indicating that ConnectionSocket is valid, or zero if
an error occurred.
--*/
{
BROADCASTARGS args;
INT i;
INT result = 0;
BOOL b;
BOOL connected = FALSE;
GROWBUFFER broadcastSockets = INIT_GROWBUFFER;
GROWBUFFER listenSockets = INIT_GROWBUFFER;
INT broadcastSocketCount;
INT listenSocketCount = 0;
BOOL destinationMode;
TCHAR envTag[NAME_SIZE_PLUS_NUL];
__try {
//
// If tag is not set, then force it to the user name if domains are enabled
//
if (!IsmCopyEnvironmentString (Platform, NULL, TRANSPORT_ENVVAR_HOMENET_TAG, envTag)) {
if (pGetDomainUserName (envTag)) {
IsmSetEnvironmentString (Platform, NULL, TRANSPORT_ENVVAR_HOMENET_TAG, envTag);
}
}
if (!AddressBuffer && ConnectionSocket) {
destinationMode = TRUE;
} else if (AddressBuffer && !ConnectionSocket) {
destinationMode = FALSE;
} else {
MYASSERT (FALSE);
__leave;
}
//
// In source mode, we collect datagrams sent by destinations on the network. After
// the first datagram is received, collection continues for 15 seconds. At
// that point, we have a list of socket addresses, protocol, and destination names.
//
// In destination mode, we send out periodic broadcasts, and we wait until a source
// connects or the cancel event is signaled.
//
broadcastSocketCount = pOpenBroadcastSockets (&broadcastSockets);
if (!broadcastSocketCount) {
__leave;
}
if (destinationMode) {
listenSocketCount = pOpenListenSockets (&listenSockets);
if (!listenSocketCount) {
DEBUGMSG ((DBG_ERROR, "Able to set up broadcast sockets but not connection sockets"));
__leave;
}
}
// call mode-specific routine
ZeroMemory (&args, sizeof (args));
args.AddressArray = AddressBuffer;
args.BroadcastSockets = (PBROADCASTSOCKET) broadcastSockets.Buf;
args.BroadcastCount = broadcastSocketCount;
args.ListenSockets = (PLISTENSOCKET) listenSockets.Buf;
args.ListenCount = listenSocketCount;
args.Timeout = SourceTimeout;
b = destinationMode ? pDestinationBroadcast (&args) : pSourceBroadcast (&args);
//
// Clean up all sockets
//
PushError();
for (i = 0 ; i < args.BroadcastCount ; i++) {
closesocket (args.BroadcastSockets[i].Socket);
}
if (destinationMode) {
for (i = 0 ; i < args.ListenCount ; i++) {
closesocket (args.ListenSockets[i].Socket);
}
}
PopError();
if (b) {
if (destinationMode) {
CopyMemory (ConnectionSocket, &args.ConnectionSocket, sizeof (CONNECTIONSOCKET));
result = 1;
} else {
result = AddressBuffer->End / sizeof (CONNECTADDRESS);
}
}
}
__finally {
PushError();
GbFree (&broadcastSockets);
GbFree (&listenSockets);
if (g_ConnectionDone) {
SetEvent (g_ConnectionDone);
}
PopError();
}
return result;
}
INT
pSendWithTimeout (
IN SOCKET Socket,
IN PCBYTE Data,
IN UINT DataLen,
IN INT Flags
)
{
FD_SET writeSet;
FD_SET errorSet;
TIMEVAL timeout = {1,0};
UINT timeToLive = GetTickCount() + IDLE_TIMEOUT * 1000;
INT result;
//
// Wait up to IDLE_TIMEOUT seconds for the socket to be sendable
//
do {
FD_ZERO (&writeSet);
FD_SET (Socket, &writeSet);
FD_ZERO (&errorSet);
FD_SET (Socket, &errorSet);
//
// Check the ISM cancel flag
//
if (IsmCheckCancel ()) {
SetLastError (ERROR_CANCELLED);
return SOCKET_ERROR;
}
//
// Wait 1 second for the socket to be writable
//
result = select (0, NULL, &writeSet, &errorSet, &timeout);
if (result) {
if (FD_ISSET (Socket, &writeSet)) {
return send (Socket, Data, DataLen, Flags);
}
LOG ((LOG_ERROR, (PCSTR) MSG_SOCKET_HAS_ERROR));
return SOCKET_ERROR;
}
} while ((timeToLive - GetTickCount()) < IDLE_TIMEOUT * 1000);
LOG ((LOG_ERROR, (PCSTR) MSG_SOCKET_SEND_TIMEOUT));
return SOCKET_ERROR;
}
BOOL
pSendExactData (
IN SOCKET Socket,
IN PCBYTE Data,
IN UINT DataLen
)
/*++
Routine Description:
pSendExactData sends data to the specified socket.
[TODO: need to support datagram mode]
Arguments:
Socket - Specifies the socket to send data to
Data - Specifies the data to send
DataLen - Specifies the number of bytes in Data
Return Value:
TRUE if the data was sent, FALSE otherwise.
--*/
{
INT result;
PCBYTE pos;
UINT bytesLeft;
UINT packetSize;
bytesLeft = DataLen;
pos = Data;
while (bytesLeft) {
if (IsmCheckCancel()) {
return FALSE;
}
packetSize = min (1024, bytesLeft);
result = pSendWithTimeout (Socket, pos, packetSize, 0);
if (result > 0) {
bytesLeft -= (UINT) result;
pos += result;
} else {
if (GetLastError() == WSAENOBUFS) {
Sleep (100);
} else {
return FALSE;
}
}
}
return bytesLeft == 0;
}
BOOL
pSendDatagramData (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool,
IN PCBYTE Data,
IN UINT DataLen
)
/*++
Routine Description:
pSendDatagramData puts data on the wire in the form of small, numbered
packets. The packets can potentially scatter and be received out of order,
so the packets are numbered such that they can be reassembled properly.
It is assumed that the datagram protocol is reliable (datagrams are not
dropped), and that the underlying protocol implements the naggle algorithm
to cache packets for efficiency.
Arguments:
Socket - Specifies the datagram socket to send data on
DatagramPool - Specifies a structure that is used to track packets
Data - Specifies the data to send
DataLen - Specifies the length of the data to send
Return Value:
TRUE if data was sent, FALSE otherwise
--*/
{
PDATAGRAM_PACKET header;
BYTE buffer[512];
PBYTE dataPtr;
UINT bytesSent = 0;
UINT bytesToSend;
header = (PDATAGRAM_PACKET) buffer;
dataPtr = (PBYTE) (&header[1]);
do {
bytesToSend = DataLen - bytesSent;
bytesToSend = min (bytesToSend, 256);
header->PacketNumber = DatagramPool->SendSequenceNumber;
DatagramPool->SendSequenceNumber++;
header->DataLength = (WORD) bytesToSend;
CopyMemory (dataPtr, Data, bytesToSend);
if (!pSendExactData (
Socket,
(PBYTE) header,
bytesToSend + sizeof (DATAGRAM_PACKET)
)) {
break;
}
Data += bytesToSend;
bytesSent += bytesToSend;
} while (bytesSent < DataLen);
return bytesSent == DataLen;
}
BOOL
pSendData (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN PCBYTE Data,
IN UINT DataLen
)
{
if (!DatagramPool) {
return pSendExactData (Socket, Data, DataLen);
}
return pSendDatagramData (Socket, DatagramPool, Data, DataLen);
}
INT
pRecvWithTimeout (
IN SOCKET Socket,
IN PBYTE Data,
IN UINT DataLen,
IN INT Flags,
IN UINT Timeout OPTIONAL
)
/*++
Routine Description:
pRecvWithTimeout implements a basic socket recv call with a IDLE_TIMEOUT second
timeout and with a check for the ISM cancel flag.
Arguments:
Socket - Specifies the socket to recv from
Data - Specifies the data buffer
DataLen - Specifies the length of data buffer
Flags - Specifies zero for normal recv, or MSG_PEEK
Return Value:
The number of bytes read, or SOCKET_ERROR. GetLastError contains the reason
for failure.
--*/
{
FD_SET readSet;
FD_SET errorSet;
TIMEVAL timeout = {1,0};
INT timeToLive;
INT result;
if (Timeout == 0) {
Timeout = IDLE_TIMEOUT * 1000;
}
timeToLive = GetTickCount() + Timeout;
//
// Wait up to IDLE_TIMEOUT seconds for the socket to have data
//
do {
FD_ZERO (&readSet);
FD_SET (Socket, &readSet);
FD_ZERO (&errorSet);
FD_SET (Socket, &errorSet);
//
// Check the ISM cancel flag
//
if (IsmCheckCancel ()) {
SetLastError (ERROR_CANCELLED);
return SOCKET_ERROR;
}
//
// Wait 1 second for the socket to be readable
//
result = select (0, &readSet, NULL, &errorSet, &timeout);
if (result) {
if (FD_ISSET (Socket, &readSet)) {
result = recv (Socket, Data, DataLen, Flags);
return result;
}
if (FD_ISSET (Socket, &errorSet)) {
LOG ((LOG_ERROR, (PCSTR) MSG_SOCKET_HAS_ERROR));
return SOCKET_ERROR;
}
DEBUGMSG ((DBG_HOMENET, "select returned %i but socket is not in readSet or errorSet", result));
}
} while ((timeToLive - GetTickCount()) < Timeout);
LOG ((LOG_ERROR, (PCSTR) MSG_SOCKET_RECV_TIMEOUT));
return SOCKET_ERROR;
}
PBYTE
pReceiveExactData (
IN SOCKET Socket,
IN OUT PGROWBUFFER Buffer, OPTIONAL
OUT PBYTE AlternateBuffer, OPTIONAL
IN UINT BytesToReceive,
IN UINT Timeout OPTIONAL
)
/*++
Routine Description:
pReceiveExactData allocates a buffer from the caller-specified grow buffer,
and receives data until the buffer is full, or until receive fails.
Arguments:
Socket - Specifies the socket to receive data on. The socket must
be in blocking mode.
Buffer - Specifies the buffer to allocate from; the end pointer is
reset to zero. Receives the data from the wire.
AlternateBuffer - Specifies the buffer to put data into
BytesToReceive - Specifies the number of bytes to get from the socket. All
bytes must be read before this function returns.
Return Value:
TRUE if the buffer was completed, or FALSE if receive failed.
NOTE: Either Buffer or AlternateBuffer must be specified. If both are
specified, Buffer is used.
--*/
{
PBYTE recvBuf;
PBYTE bufPos;
UINT bytesSoFar = 0;
INT result;
UINT readSize;
if (Buffer) {
Buffer->End = 0;
recvBuf = GbGrow (Buffer, BytesToReceive);
} else {
recvBuf = AlternateBuffer;
}
bufPos = recvBuf;
do {
if (IsmCheckCancel()) {
return FALSE;
}
readSize = BytesToReceive - bytesSoFar;
result = pRecvWithTimeout (Socket, bufPos, readSize, 0, Timeout);
if (!result) {
// connection broken
SetLastError (ERROR_CANCELLED);
break;
}
if (result == SOCKET_ERROR) {
DEBUGMSG ((DBG_ERROR, "Error reading from socket"));
break;
}
bufPos += result;
bytesSoFar += result;
} while (bytesSoFar < BytesToReceive);
MYASSERT (bytesSoFar <= BytesToReceive);
return bytesSoFar == BytesToReceive ? recvBuf : NULL;
}
PBYTE
pReceiveDatagramData (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool,
IN OUT PGROWBUFFER Buffer, OPTIONAL
OUT PBYTE AlternateBuffer, OPTIONAL
IN UINT BytesToReceive,
IN UINT Timeout OPTIONAL
)
/*++
Routine Description:
pReceiveDatagramData checks the datagram queue for data, allocates a
receive buffer, and fills the buffer with the data from the wire. If
necessary, this function will fill the queue, until there is enough data to
fill the caller's buffer.
Arguments:
Socket - Specifies the datagram socket
DatagramPool - Specifies the structure containing the receive pool and
other sequencing info
Buffer - Specifies the buffer to allocate from; the end pointer is
reset to zero. Receives the data from the wire.
AlternateBuffer - Specifies the buffer to put data into
BytesToReceive - Specifies the number of bytes to get from the socket. All
bytes must be read before this function returns.
Return Value:
TRUE if the buffer was completed, or FALSE if receive failed.
NOTE: Either Buffer or AlternateBuffer must be specified. If both are
specified, Buffer is used.
--*/
{
PDATAGRAM_POOL_ITEM itemHeader;
PDATAGRAM_POOL_ITEM prevItem, nextItem;
BYTE buffer[512];
PBYTE dataPtr;
PBYTE recvBuf;
PBYTE bufPos;
UINT bytesSoFar = 0;
UINT bytesLeft;
INT result;
PDATAGRAM_POOL_ITEM item;
UINT newPacketNum;
UINT currentPacketNum;
ULONG available;
PBYTE bigBuf = NULL;
PBYTE p;
if (Buffer) {
Buffer->End = 0;
recvBuf = GbGrow (Buffer, BytesToReceive);
} else {
recvBuf = AlternateBuffer;
}
bufPos = recvBuf;
itemHeader = (PDATAGRAM_POOL_ITEM) buffer;
dataPtr = (PBYTE) (&itemHeader[1]);
for (;;) {
//
// Take all available data out of the pool
//
item = DatagramPool->FirstItem;
bytesLeft = BytesToReceive - bytesSoFar;
while (item) {
if (item->Header.PacketNumber == DatagramPool->RecvSequenceNumber) {
//
// Two cases:
//
// 1. Want entire packet
// 2. Want partial packet
//
if (bytesLeft >= item->Header.DataLength) {
// entire packet
CopyMemory (bufPos, item->PacketData, item->Header.DataLength);
MYASSERT (!item->Prev);
if (item->Next) {
item->Next->Prev = NULL;
}
DatagramPool->FirstItem = item->Next;
bytesSoFar += item->Header.DataLength;
PmReleaseMemory (DatagramPool->Pool, item);
DatagramPool->RecvSequenceNumber++;
} else {
// partial packet
CopyMemory (bufPos, item->PacketData, bytesLeft);
item->PacketData += bytesLeft;
item->Header.DataLength -= (WORD) bytesLeft;
bytesSoFar += bytesLeft;
}
if (BytesToReceive == bytesSoFar) {
return recvBuf;
}
}
item = item->Next;
}
//
// Data is not available in the pool. Receive one packet and then try again.
//
ioctlsocket (Socket, FIONREAD, &available);
if (!available) {
Sleep (100);
continue;
}
bigBuf = PmGetMemory (DatagramPool->Pool, available);
result = pRecvWithTimeout (Socket, bigBuf, available, 0, Timeout);
if (result == INVALID_SOCKET) {
DEBUGMSG ((DBG_ERROR, "Can't receive datagram"));
break;
}
p = bigBuf;
while (result > 0) {
if (result < sizeof (DATAGRAM_PACKET)) {
DEBUGMSG ((DBG_ERROR, "Datagram header is too small"));
break;
}
CopyMemory (&itemHeader->Header, p, sizeof (DATAGRAM_PACKET));
p += sizeof (DATAGRAM_PACKET);
result -= sizeof (DATAGRAM_PACKET);
if (itemHeader->Header.DataLength > 256) {
DEBUGMSG ((DBG_ERROR, "Datagram contains garbage"));
break;
}
if (result < itemHeader->Header.DataLength) {
DEBUGMSG ((DBG_ERROR, "Datagram data is too small"));
break;
}
CopyMemory (dataPtr, p, itemHeader->Header.DataLength);
p += itemHeader->Header.DataLength;
result -= itemHeader->Header.DataLength;
if ((UINT) itemHeader->Header.PacketNumber == DatagramPool->LastPacketNumber) {
continue;
}
DatagramPool->LastPacketNumber = itemHeader->Header.PacketNumber;
//
// Put the packet in the item linked list, sorted by packet number
//
item = (PDATAGRAM_POOL_ITEM) PmDuplicateMemory (
DatagramPool->Pool,
(PCBYTE) itemHeader,
itemHeader->Header.DataLength + sizeof (DATAGRAM_POOL_ITEM)
);
item->PacketData = (PBYTE) (&item[1]);
prevItem = NULL;
nextItem = DatagramPool->FirstItem;
while (nextItem) {
//
// Account for wrapping; assume a packet number difference no more
// than 16383 out-of-sequence packets in the queue (about 4M of
// data)
//
if (nextItem->Header.PacketNumber >= 49152 && item->Header.PacketNumber < 16384) {
newPacketNum = (UINT) item->Header.PacketNumber + 65536;
currentPacketNum = (UINT) nextItem->Header.PacketNumber;
} else if (nextItem->Header.PacketNumber < 16384 && item->Header.PacketNumber >= 49152) {
newPacketNum = (UINT) item->Header.PacketNumber;
currentPacketNum = (UINT) nextItem->Header.PacketNumber + 65536;
} else {
newPacketNum = (UINT) item->Header.PacketNumber;
currentPacketNum = (UINT) nextItem->Header.PacketNumber;
}
if (newPacketNum < currentPacketNum) {
break;
}
prevItem = nextItem;
nextItem = nextItem->Next;
}
item->Next = nextItem;
item->Prev = prevItem;
if (!prevItem) {
DatagramPool->FirstItem = item;
}
}
PmReleaseMemory (DatagramPool->Pool, bigBuf);
}
return bytesSoFar == BytesToReceive ? recvBuf : NULL;
}
PBYTE
pReceiveData (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN OUT PGROWBUFFER Buffer, OPTIONAL
OUT PBYTE AlternateBuffer, OPTIONAL
IN UINT BytesToReceive,
IN UINT Timeout OPTIONAL
)
{
if (!DatagramPool) {
return pReceiveExactData (Socket, Buffer, AlternateBuffer, BytesToReceive, Timeout);
}
return pReceiveDatagramData (Socket, DatagramPool, Buffer, AlternateBuffer, BytesToReceive, Timeout);
}
BOOL
pSendFile (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN PCTSTR LocalFileName, OPTIONAL
IN PCTSTR DestFileName OPTIONAL
)
/*++
Routine Description:
pSendFile sends a file on the wire.
Arguments:
Socket - Specifies the socket to send the file on
DatagramPool - Specifies the datagram pool for sockets that are connecitonless
LocalFileName - Specifies the path to the local file
DestFileName - Specifies the subpath that is sent to the destination. The
destination uses the subpath to construct its corresponding
file name.
Return Value:
TRUE if the file was sent, FALSE otherwise.
--*/
{
PCWSTR destFileName = NULL;
INT len;
GROWBUFFER data = INIT_GROWBUFFER;
BOOL result = FALSE;
HANDLE file = NULL;
LONGLONG fileSize;
DWORD msg;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTHASH hHash = 0;
__try {
//
// Build the encrypt stuff
//
if ((!CryptAcquireContext (&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) ||
(!CryptCreateHash (hProv, CALG_MD5, 0, 0, &hHash)) ||
(!CryptHashData (hHash, (PBYTE)g_GlobalKey, ByteCountA (g_GlobalKey), 0)) ||
(!CryptDeriveKey (hProv, CALG_RC4, hHash, CRYPT_EXPORTABLE, &hKey))
) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
//
// Try to open the file
//
fileSize = BfGetFileSize (LocalFileName);
file = BfOpenReadFile (LocalFileName);
if (!file) {
// nothing to do
__leave;
}
//
// Send the message "FILE"
//
msg = MESSAGE_FILE;
if (!pSendData (Socket, DatagramPool, (PBYTE) &msg, sizeof (msg))) {
SetLastError (ERROR_NETWORK_UNREACHABLE);
DEBUGMSG ((DBG_ERROR, "Can't send MSG_FILE"));
__leave;
}
//
// If no file was specified, send length of zero
//
if (!LocalFileName || !DestFileName) {
len = 0;
if (!pSendData (Socket, DatagramPool, (PBYTE) &len, 4)) {
SetLastError (ERROR_NETWORK_UNREACHABLE);
DEBUGMSG ((DBG_ERROR, "Can't send nul file length"));
__leave;
}
result = TRUE;
__leave;
}
//
// Send the file name and file size
//
#ifdef UNICODE
destFileName = DuplicatePathString (DestFileName, 0);
#else
destFileName = ConvertAtoW (DestFileName);
#endif
len = ByteCountW (destFileName);
if (!pSendData (Socket, DatagramPool, (PBYTE) &len, 4)) {
SetLastError (ERROR_NETWORK_UNREACHABLE);
DEBUGMSG ((DBG_ERROR, "Can't send file length"));
__leave;
}
// Encrypt the name of the file
if (!CryptEncrypt(hKey, 0, TRUE, 0, (PBYTE)destFileName, &len, len)) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
if (!pSendData (Socket, DatagramPool, (PBYTE) destFileName, len)) {
SetLastError (ERROR_NETWORK_UNREACHABLE);
DEBUGMSG ((DBG_ERROR, "Can't send file name"));
__leave;
}
if (!pSendData (Socket, DatagramPool, (PBYTE) &fileSize, 8)) {
SetLastError (ERROR_NETWORK_UNREACHABLE);
DEBUGMSG ((DBG_ERROR, "Can't send file size"));
__leave;
}
//
// Send the data 64K at a time
//
GbGrow (&data, 0x10000);
while (fileSize) {
if (fileSize > 0x10000) {
len = 0x10000;
if (!BfReadFile (file, data.Buf, len)) {
DEBUGMSG ((DBG_ERROR, "Can't read from file"));
__leave;
}
// Encrypt the buffer
if (!CryptEncrypt(hKey, 0, FALSE, 0, data.Buf, &len, len)) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
if (!pSendData (Socket, DatagramPool, data.Buf, len)) {
SetLastError (ERROR_NETWORK_UNREACHABLE);
DEBUGMSG ((DBG_ERROR, "Can't send file data"));
__leave;
}
fileSize -= 0x10000;
} else {
len = (INT)fileSize;
if (!BfReadFile (file, data.Buf, (UINT) fileSize)) {
DEBUGMSG ((DBG_ERROR, "Can't read from file"));
__leave;
}
// Encrypt the buffer (last piece so set the last to TRUE)
if (!CryptEncrypt(hKey, 0, TRUE, 0, data.Buf, &len, len)) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
if (!pSendData (Socket, DatagramPool, data.Buf, (UINT) len)) {
SetLastError (ERROR_NETWORK_UNREACHABLE);
DEBUGMSG ((DBG_ERROR, "Can't send file data"));
__leave;
}
fileSize = 0;
}
}
//
// Done!
//
result = TRUE;
DEBUGMSG ((DBG_HOMENET, "Sent %s", LocalFileName));
}
__finally {
if (hKey) {
CryptDestroyKey(hKey);
hKey = 0;
}
if (hHash) {
CryptDestroyHash(hHash);
hHash = 0;
}
if (hProv) {
CryptReleaseContext(hProv,0);
hProv = 0;
}
GbFree (&data);
if (file) {
CloseHandle (file);
}
if (destFileName) {
#ifndef UNICODE
FreeConvertedStr (destFileName);
#else
FreePathString (destFileName);
#endif
destFileName = NULL;
}
}
return result;
}
BOOL
pReceiveFile (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN PCTSTR LocalFileRoot,
IN UINT Timeout OPTIONAL
)
/*++
Routine Description:
pReceiveStreamFile obtains a file from the socket. The file is stored in
g_StorageRoot. The subpath and file name is obtained from the data on the
wire.
NOTE: The caller must pull off the message DWORD before calling
pReceiveStreamFile. This is unlike the send, which puts the message
on the wire automatically.
Arguments:
Socket - Specifies the socket to receive from.
DatagramPool - Specifies the packet pool for a datagram-based socket
LocalFileRoot - Specifies the local root path for the file
Return Value:
TRUE if the file was received, FALSE otherwise.
--*/
{
PCTSTR fileName = NULL;
INT len;
GROWBUFFER data = INIT_GROWBUFFER;
BOOL result = FALSE;
PTSTR p;
HANDLE file = NULL;
LONGLONG fileSize;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTHASH hHash = 0;
__try {
//
// Build the encrypt stuff
//
if ((!CryptAcquireContext (&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) ||
(!CryptCreateHash (hProv, CALG_MD5, 0, 0, &hHash)) ||
(!CryptHashData (hHash, (PBYTE)g_GlobalKey, ByteCountA (g_GlobalKey), 0)) ||
(!CryptDeriveKey (hProv, CALG_RC4, hHash, CRYPT_EXPORTABLE, &hKey))
) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
//
// Wait for file name
//
if (!pReceiveData (Socket, DatagramPool, &data, NULL, 4, Timeout)) {
__leave;
}
len = *((PDWORD) data.Buf);
if (!len) {
result = TRUE;
__leave;
}
if (len >= (MAX_PATH * sizeof (TCHAR))) {
__leave;
}
if (!pReceiveData (Socket, DatagramPool, &data, NULL, len, Timeout)) {
__leave;
}
// Decrypt the file name
if (!CryptDecrypt(hKey, 0, TRUE, 0, data.Buf, &len)) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
GbGrow (&data, sizeof (TCHAR) * 2);
p = (PTSTR) data.Buf;
p[len / sizeof (TCHAR)] = 0;
p[(len / sizeof (TCHAR)) + 1] = 0;
fileName = JoinPaths (LocalFileRoot, p);
if (!fileName) {
__leave;
}
//
// Get the file size
//
if (!pReceiveData (Socket, DatagramPool, &data, NULL, 8, Timeout)) {
__leave;
}
fileSize = *((PLONGLONG) data.Buf);
DEBUGMSG ((DBG_HOMENET, "Receiving %s", fileName));
//
// Create the file
//
file = BfCreateFile (fileName);
if (file == INVALID_HANDLE_VALUE) {
PushError ();
DEBUGMSG ((DBG_ERROR, "Can't create %s", fileName));
PopError ();
__leave;
}
//
// Fetch the data 64K at a time
//
while (fileSize) {
if (fileSize > 0x10000) {
if (!pReceiveData (Socket, DatagramPool, &data, NULL, 0x10000, Timeout)) {
__leave;
}
len = data.End;
// Decrypt the file name
if (!CryptDecrypt(hKey, 0, FALSE, 0, data.Buf, &len)) {
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
__leave;
}
if (!BfWriteFile (file, data.Buf, len)) {
PushError ();
DEBUGMSG ((DBG_ERROR, "Can't write to file"));
PopError ();
__leave;
}
fileSize -= data.End;
} else {
if (!pReceiveData (Socket, DatagramPool, &data, NULL, (UINT) fileSize, Timeout)) {
__leave;
}
len = data.End;
// Decrypt the file name
if (!CryptDecrypt(hKey, 0, TRUE, 0, data.Buf, &len)) {
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
__leave;
}
if (!BfWriteFile (file, data.Buf, len)) {
PushError ();
DEBUGMSG ((DBG_ERROR, "Can't write to file"));
PopError ();
__leave;
}
fileSize = 0;
}
}
//
// Done!
//
result = TRUE;
DEBUGMSG ((DBG_HOMENET, "Received %s", fileName));
}
__finally {
PushError ();
if (hKey) {
CryptDestroyKey(hKey);
hKey = 0;
}
if (hHash) {
CryptDestroyHash(hHash);
hHash = 0;
}
if (hProv) {
CryptReleaseContext(hProv,0);
hProv = 0;
}
GbFree (&data);
if (file) {
CloseHandle (file);
if (!result) {
DeleteFile (fileName);
}
}
if (fileName) {
FreePathString (fileName);
fileName = NULL;
}
PopError ();
}
return result;
}
BOOL
pSendEncryptedData (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN PCBYTE Data,
IN UINT DataSize
)
/*++
Routine Description:
pSendEncryptedFile sends a file on the wire.
Arguments:
Socket - Specifies the socket to send the file on
DatagramPool - Specifies the datagram pool for sockets that are connecitonless
Data - Buffer to be sent
DataSize - Size of data to be sent
Return Value:
TRUE if the buffer was sent, FALSE otherwise.
--*/
{
INT len;
GROWBUFFER encData = INIT_GROWBUFFER;
BOOL result = FALSE;
DWORD msg;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTHASH hHash = 0;
__try {
//
// Build the encrypt stuff
//
if ((!CryptAcquireContext (&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) ||
(!CryptCreateHash (hProv, CALG_MD5, 0, 0, &hHash)) ||
(!CryptHashData (hHash, (PBYTE)g_GlobalKey, ByteCountA (g_GlobalKey), 0)) ||
(!CryptDeriveKey (hProv, CALG_RC4, hHash, CRYPT_EXPORTABLE, &hKey))
) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
//
// Send the message "DATA"
//
msg = MESSAGE_DATA;
if (!pSendData (Socket, DatagramPool, (PBYTE) &msg, sizeof (msg))) {
DEBUGMSG ((DBG_ERROR, "Can't send MSG_FILE"));
__leave;
}
//
// Send the size of data
//
if (!pSendData (Socket, DatagramPool, (PBYTE) &DataSize, 4)) {
DEBUGMSG ((DBG_ERROR, "Can't send file length"));
__leave;
}
//
// Send the data
//
GbGrow (&encData, DataSize);
CopyMemory (encData.Buf, Data, DataSize);
// Encrypt the buffer
if (!CryptEncrypt(hKey, 0, TRUE, 0, encData.Buf, &DataSize, DataSize)) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
if (!pSendData (Socket, DatagramPool, encData.Buf, DataSize)) {
DEBUGMSG ((DBG_ERROR, "Can't send file data"));
__leave;
}
//
// Done!
//
result = TRUE;
}
__finally {
if (hKey) {
CryptDestroyKey(hKey);
hKey = 0;
}
if (hHash) {
CryptDestroyHash(hHash);
hHash = 0;
}
if (hProv) {
CryptReleaseContext(hProv,0);
hProv = 0;
}
GbFree (&encData);
}
return result;
}
BOOL
pReceiveEncryptedData (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN PBYTE *Buffer,
IN UINT Timeout OPTIONAL
)
/*++
Routine Description:
pReceiveEncryptedData obtains a buffer from the socket. The data is stored in
Buffer.
NOTE: The caller must pull off the message DWORD before calling
pReceiveEncryptedData. This is unlike the send, which puts the message
on the wire automatically.
Arguments:
Socket - Specifies the socket to receive from.
DatagramPool - Specifies the packet pool for a datagram-based socket
Buffer - Specifies a pointer to a PBYTE
Return Value:
TRUE if the file was received, FALSE otherwise.
--*/
{
GROWBUFFER data = INIT_GROWBUFFER;
DWORD dataSize;
BOOL result = FALSE;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
HCRYPTHASH hHash = 0;
__try {
//
// Build the encrypt stuff
//
if ((!CryptAcquireContext (&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) ||
(!CryptCreateHash (hProv, CALG_MD5, 0, 0, &hHash)) ||
(!CryptHashData (hHash, (PBYTE)g_GlobalKey, ByteCountA (g_GlobalKey), 0)) ||
(!CryptDeriveKey (hProv, CALG_RC4, hHash, CRYPT_EXPORTABLE, &hKey))
) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
//
// Wait for data size
//
if (!pReceiveData (Socket, DatagramPool, &data, NULL, 4, Timeout)) {
__leave;
}
dataSize = *((PDWORD) data.Buf);
if (!dataSize) {
result = TRUE;
__leave;
}
if (dataSize > MAX_DATA_PACKET_SIZE) {
// The only thing sent this way so far is the encrypted key. Obviously this
// does not exceed 64k
SetLastError (ERROR_INVALID_DATA);
__leave;
}
if (!pReceiveData (Socket, DatagramPool, &data, NULL, dataSize, Timeout)) {
__leave;
}
// Decrypt the content
if (!CryptDecrypt(hKey, 0, TRUE, 0, data.Buf, &dataSize)) {
SetLastError (ERROR_NO_SECURITY_ON_OBJECT);
LOG ((LOG_ERROR, (PCSTR) MSG_ENCRYPTION_FAILED));
__leave;
}
// Now allocate the result
*Buffer = HeapAlloc (g_hHeap, 0, dataSize);
if (*Buffer) {
CopyMemory (*Buffer, data.Buf, dataSize);
result = TRUE;
}
}
__finally {
if (hKey) {
CryptDestroyKey(hKey);
hKey = 0;
}
if (hHash) {
CryptDestroyHash(hHash);
hHash = 0;
}
if (hProv) {
CryptReleaseContext(hProv,0);
hProv = 0;
}
GbFree (&data);
}
return result;
}
BOOL
pSendMetrics (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN OUT PTRANSFERMETRICS Metrics
)
/*++
Routine Description:
pSendMetrics sends transport data (such as the number of files or number of
bytes to expect). This is the first bit of information sent to the
destination.
Arguments:
Socket - Specifies the socket to send the data to.
DatagramPool - Specifies the structure for datagram mode
Metrics - Specifies a pointer to the metrics structure to send. The
metrics structure member StructSize is updated before the struct
is sent.
Return Value:
TRUE if the metrics struct was sent, FALSE otherwise.
--*/
{
Metrics->StructSize = sizeof (TRANSFERMETRICS);
if (!pSendData (Socket, DatagramPool, (PBYTE) Metrics, sizeof (TRANSFERMETRICS))) {
DEBUGMSG ((DBG_ERROR, "Failed to send data"));
return FALSE;
}
return TRUE;
}
BOOL
pReceiveMetrics (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
OUT PTRANSFERMETRICS Metrics
)
/*++
Routine Description:
pReceiveMetrics obtains a TRANSFERMETRICS structure from the wire. This is
the first bit of information received by the destination. It provides the
number of files, total number of file bytes, and other information.
Arguments:
Socket - Specifies the socket to receive data on.
DatagramPool - Specifies the structure used for datagram data reception
Metrics - Receives the metrics from the wire.
Return Value:
TRUE if the metrics structure was received properly, FALSE otherwise.
--*/
{
GROWBUFFER data = INIT_GROWBUFFER;
BOOL result = FALSE;
__try {
if (!pReceiveData (Socket, DatagramPool, NULL, (PBYTE) Metrics, sizeof (TRANSFERMETRICS), 0)) {
__leave;
}
if (Metrics->StructSize != sizeof (TRANSFERMETRICS)) {
DEBUGMSG ((DBG_ERROR, "Invalid transfer metrics received"));
__leave;
}
if (Metrics->Signature != HOMENETTR_SIG) {
DEBUGMSG ((DBG_ERROR, "Invalid transfer signature received"));
__leave;
}
result = TRUE;
}
__finally {
GbFree (&data);
}
return result;
}
DWORD
pReceiveMessage (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN UINT Timeout OPTIONAL
)
/*++
Routine Description:
pReceiveMessage obtains the next DWORD from the socket and returns it to
the caller. This DWORD provides a message, indicating what action to
take next.
Arguments:
Socket - Specifies the socket to receive data on
DatagramPool - Specifies the structure used for datagram data reception
Return Value:
The message, or 0 if no message is available.
--*/
{
DWORD msg = 0;
if (!pReceiveData (Socket, DatagramPool, NULL, (PBYTE) &msg, sizeof (DWORD), Timeout)) {
msg = 0;
}
return msg;
}
BOOL
pConnectToDestination (
IN PCONNECTADDRESS Address,
OUT PCONNECTIONSOCKET Connection
)
{
BOOL result = FALSE;
BOOL b;
LINGER lingerStruct;
CopyMemory (Connection->LocalAddress, Address->LocalAddress, Address->LocalAddressLen);
Connection->LocalAddressLen = Address->LocalAddressLen;
CopyMemory (Connection->RemoteAddress, Address->RemoteAddress, Address->RemoteAddressLen);
Connection->RemoteAddressLen = Address->RemoteAddressLen;
Connection->Socket = socket (
Address->Family,
Address->Datagram ? SOCK_DGRAM : SOCK_STREAM,
Address->Protocol
);
if (Connection->Socket == INVALID_SOCKET) {
DEBUGMSG ((DBG_ERROR, "Can't create socket for connection"));
return FALSE;
}
__try {
b = TRUE;
setsockopt (Connection->Socket, SOL_SOCKET, SO_REUSEADDR, (PBYTE) &b, sizeof (b));
b = TRUE;
setsockopt (Connection->Socket, SOL_SOCKET, SO_KEEPALIVE, (PBYTE) &b, sizeof (b));
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 90;
setsockopt (Connection->Socket, SOL_SOCKET, SO_LINGER, (PBYTE) &lingerStruct, sizeof (lingerStruct));
pResetPort (Address->Family, (SOCKADDR *) Address->LocalAddress);
if (bind (Connection->Socket, (SOCKADDR *) Address->LocalAddress, Address->LocalAddressLen)) {
DEBUGMSG ((DBG_ERROR, "Failed to bind to connection socket"));
__leave;
}
if (connect (Connection->Socket, (SOCKADDR *) Address->RemoteAddress, Address->RemoteAddressLen)) {
DEBUGMSG ((DBG_ERROR, "Failed to connect to socket"));
__leave;
}
Connection->Family = Address->Family;
Connection->Protocol = Address->Protocol;
Connection->Datagram = Address->Datagram;
ZeroMemory (&Connection->DatagramPool, sizeof (DATAGRAM_POOL));
if (Connection->Datagram) {
Connection->DatagramPool.Pool = PmCreatePool();
Connection->DatagramPool.LastPacketNumber = (UINT) -1;
}
result = TRUE;
}
__finally {
if (!result && Connection->Socket != INVALID_SOCKET) {
closesocket (Connection->Socket);
Connection->Socket = INVALID_SOCKET;
}
}
return result;
}
BOOL
FindDestination (
OUT PCONNECTADDRESS Address,
IN UINT Timeout, OPTIONAL
IN BOOL IgnoreMultipleDests
)
/*++
Routine Description:
FindDestination invokes the name resolution algorithm to locate a
destination. It selects the best transport to communicate on, and returns
the address. The caller can use the return address to make a connection.
Arguments:
Address - Receives the address of the destination
Timeout - Specifies the number of seconds to wait for the first
destination broadcast, or zero to wait forever.
IgnoreMultipleDests - Specifies TRUE if multiple destinations should be
ignored (taking the first one as the connection),
or FALSE if a message should be passed to the UI
to resolve the conflict.
Return Value:
TRUE if a destination was found, FALSE otherwise.
--*/
{
GROWBUFFER destinationAddresses = INIT_GROWBUFFER;
INT destinationCount;
PCONNECTADDRESS addressArray;
INT i;
PCTSTR firstName;
BOOL result = FALSE;
GROWBUFFER destNames = INIT_GROWBUFFER;
MULTISZ_ENUM e;
BOOL duplicate;
BOOL oneValid;
ULONG_PTR response;
__try {
destinationCount = pNameResolver (PLATFORM_SOURCE, &destinationAddresses, Timeout, NULL);
if (!destinationCount) {
__leave;
}
addressArray = (PCONNECTADDRESS) destinationAddresses.Buf;
//
// Determine which address to use. Rules are:
//
// 1. Must have only one destination to choose from
// 2. Pick TCP/IP, then IPX. [, then NetBIOS -- no longer supported]
//
if (destinationCount > 1) {
firstName = addressArray[0].DestinationName;
for (i = 1 ; i < destinationCount ; i++) {
if (!StringIMatch (firstName, addressArray[i].DestinationName)) {
break;
}
}
if (i < destinationCount) {
DEBUGMSG ((DBG_WARNING, "Multiple destinations found on the subnet"));
//
// put all destinations in an ISM environment variable, then call the
// UI to allow it to resolve the conflict, and finally make sure
// the one remaining destination is the only one used.
//
GbMultiSzAppend (&destNames, firstName);
for (i = 1 ; i < destinationCount ; i++) {
if (EnumFirstMultiSz (&e, (PCTSTR) destNames.Buf)) {
duplicate = FALSE;
do {
if (StringIMatch (e.CurrentString, addressArray[i].DestinationName)) {
duplicate = TRUE;
break;
}
} while (EnumNextMultiSz (&e));
}
if (!duplicate) {
GbMultiSzAppend (&destNames, addressArray[i].DestinationName);
}
}
IsmSetEnvironmentMultiSz (
PLATFORM_DESTINATION,
NULL,
TRANSPORT_ENVVAR_HOMENET_DESTINATIONS,
(PCTSTR) destNames.Buf
);
//
// Tell the UI. The UI must return TRUE and also update
// TRANSPORT_ENVVAR_HOMENET_DESTINATIONS so that the selected
// destination is the only member of the multi-sz.
//
if (!IgnoreMultipleDests) {
response = IsmSendMessageToApp (TRANSPORTMESSAGE_MULTIPLE_DESTS, 0);
if (IsmCheckCancel()) {
__leave;
}
if (!response) {
DEBUGMSG ((DBG_VERBOSE, "Multiple destinations were not resolved; can't continue"));
__leave;
}
if (!IsmGetEnvironmentMultiSz (
PLATFORM_DESTINATION,
NULL,
TRANSPORT_ENVVAR_HOMENET_DESTINATIONS,
(PTSTR) destNames.Buf,
destNames.End,
NULL
)) {
DEBUGMSG ((DBG_ERROR, "Can't get resolved destinations"));
__leave;
}
}
//
// Reset all Family members for names not selected
//
oneValid = FALSE;
for (i = 0 ; i < destinationCount ; i++) {
if (!StringIMatch (addressArray[i].DestinationName, (PCTSTR) destNames.Buf)) {
addressArray[i].Family = 0;
} else {
oneValid = TRUE;
}
}
if (!oneValid) {
DEBUGMSG ((DBG_ERROR, "Resolved destination does not exist"));
__leave;
}
}
}
//
// Select the best protocol
//
for (i = 0 ; i < destinationCount ; i++) {
if (addressArray[i].Family == AF_INET) {
break;
}
}
if (i == destinationCount) {
for (i = 0 ; i < destinationCount ; i++) {
if (addressArray[i].Family == AF_IPX) {
break;
}
}
if (i == destinationCount) {
for (i = 0 ; i < destinationCount ; i++) {
if (addressArray[i].Family == AF_NETBIOS) {
//break;
}
}
if (i == destinationCount) {
DEBUGMSG ((DBG_WHOOPS, "Connection is from unsupported protocol"));
__leave;
}
}
}
DEBUGMSG ((
DBG_HOMENET,
"Destination connection is %s (protocol %i)",
addressArray[i].DestinationName,
addressArray[i].Protocol
));
CopyMemory (Address, &addressArray[i], sizeof (CONNECTADDRESS));
result = TRUE;
}
__finally {
PushError();
GbFree (&destinationAddresses);
GbFree (&destNames);
PopError();
}
return result;
}
BOOL
TestConnection (
IN PCONNECTADDRESS Address
)
/*++
Routine Description:
TestConnection establishes a connection to the destination specified
by Address. Will immediately disconnect since this was just a connection
test.
Arguments:
Address - Specifies the address of the destination, as returned by
FindDestination.
Return Value:
TRUE if a connection could be established to the destination, FALSE otherwise.
--*/
{
CONNECTIONSOCKET connection;
BOOL result = FALSE;
ZeroMemory (&connection, sizeof (CONNECTIONSOCKET));
connection.Socket = INVALID_SOCKET;
connection.KeepAliveSpacing = 30000;
connection.LastSend = GetTickCount();
__try {
if (!pConnectToDestination (Address, &connection)) {
__leave;
}
DEBUGMSG ((DBG_HOMENET, "TestConnection: Connected!"));
result = TRUE;
}
__finally {
if (connection.Socket != INVALID_SOCKET) {
closesocket (connection.Socket);
connection.Socket = INVALID_SOCKET;
}
if (connection.Datagram) {
PmDestroyPool (connection.DatagramPool.Pool);
connection.Datagram = FALSE;
}
}
return result;
}
BOOL
ConnectToDestination (
IN PCONNECTADDRESS Address,
IN PTRANSFERMETRICS Metrics,
OUT PCONNECTIONSOCKET Connection
)
/*++
Routine Description:
ConnectToDestination establishes a connection to the destination specified
by Address. Once connected, the Metrics structure is passed to the
destination. The caller receives the Connection structure for addtional
communication.
Arguments:
Address - Specifies the address of the destination, as returned by
FindDestination.
Metrics - Specifies the metrics structure that provides basic
information such as the number of files to expect.
Connection - Receives the connection to the destination, to be used in
additional data transfer.
Return Value:
TRUE if a connection was established to the destination, FALSE otherwise.
--*/
{
BOOL result = FALSE;
ZeroMemory (Connection, sizeof (CONNECTIONSOCKET));
Connection->Socket = INVALID_SOCKET;
Connection->KeepAliveSpacing = 30000;
Connection->LastSend = GetTickCount();
__try {
if (!pConnectToDestination (Address, Connection)) {
__leave;
}
DEBUGMSG ((DBG_HOMENET, "Connected!"));
if (!pSendMetrics (
Connection->Socket,
Connection->Datagram ? &Connection->DatagramPool : NULL,
Metrics
)) {
DEBUGMSG ((DBG_HOMENET, "Can't send metrics to destination"));
__leave;
}
result = TRUE;
}
__finally {
if (!result) {
if (Connection->Socket != INVALID_SOCKET) {
closesocket (Connection->Socket);
Connection->Socket = INVALID_SOCKET;
}
}
}
return result;
}
DWORD
SendMessageToDestination (
IN PCONNECTIONSOCKET Connection,
IN DWORD Message
)
{
Connection->LastSend = GetTickCount();
return pSendData (
Connection->Socket,
Connection->Datagram ? &Connection->DatagramPool : NULL,
(PBYTE) &Message,
sizeof (DWORD)
);
}
BOOL
SendFileToDestination (
IN PCONNECTIONSOCKET Connection,
IN PCTSTR LocalPath, OPTIONAL
IN PCTSTR DestSubPath OPTIONAL
)
/*++
Routine Description:
SendFileToDestination sends a file to the connection specified.
If LocalPath is NULL, then no file will be sent. This is used to skip files
that cannot be accessed locally.
If DestSubPath is NULL, then the file name in LocalPath will be used
as DestSubPath.
Arguments:
Connection - Specifies the connection to send the file to, as returned by
ConnectToDestination.
LocalPath - Specifies the local path of the file to send
DestSubPath - Specifies the sub path to send to the destination (so it can
reconstruct a path)
Return Value:
TRUE if the file was sent, FALSE otherwise.
--*/
{
if (LocalPath && !DestSubPath) {
DestSubPath = GetFileNameFromPath (LocalPath);
}
return pSendFile (
Connection->Socket,
Connection->Datagram ? &Connection->DatagramPool : NULL,
LocalPath,
DestSubPath
);
}
BOOL
SendDataToDestination (
IN PCONNECTIONSOCKET Connection,
IN PCBYTE Data,
IN UINT DataSize
)
/*++
Routine Description:
SendDataToDestination sends a buffer to the connection specified.
Arguments:
Connection - Specifies the connection to send the file to, as returned by
ConnectToDestination.
Data - Specifies the buffer to send
DataSize - Specifies the data size
Return Value:
TRUE if the file was sent, FALSE otherwise.
--*/
{
return pSendEncryptedData (
Connection->Socket,
Connection->Datagram ? &Connection->DatagramPool : NULL,
Data,
DataSize
);
}
VOID
CloseConnection (
IN PCONNECTIONSOCKET Connection
)
{
if (Connection->Socket != INVALID_SOCKET) {
closesocket (Connection->Socket);
Connection->Socket = INVALID_SOCKET;
}
if (Connection->Datagram) {
PmDestroyPool (Connection->DatagramPool.Pool);
Connection->Datagram = FALSE;
}
}
BOOL
ConnectToSource (
OUT PCONNECTIONSOCKET Connection,
OUT PTRANSFERMETRICS Metrics
)
/*++
Routine Description:
ConnectToSource locates the source machine and accepts a connection from
it. To locate the source machine, broadcast messages are sent out on all
available transports. The source machine collects the broadcasts, then
selects the best transport, and connects to the destination machine. After
the connection completes, this function returns the connection to the
caller.
Arguments:
Connection - Receives the connection to the source machine. This connection
structure is then used to obtain data from the source.
Metrics - Recieves the metrics from the source machine, indicating what
data is going to be sent.
Return Value:
TRUE if a connection was accepted, FALSE otherwise.
--*/
{
ZeroMemory (Connection, sizeof (CONNECTIONSOCKET));
Connection->Socket = INVALID_SOCKET;
for (;;) {
if (!pNameResolver (PLATFORM_DESTINATION, NULL, 0, Connection)) {
return FALSE;
}
if (pReceiveMetrics (
Connection->Socket,
Connection->Datagram ? &Connection->DatagramPool : NULL,
Metrics
)) {
return TRUE;
}
CloseConnection (Connection);
}
return TRUE;
}
DWORD
ReceiveFromSource (
IN PCONNECTIONSOCKET Connection,
IN PCTSTR LocalFileRoot,
OUT PBYTE *Buffer,
IN UINT Timeout OPTIONAL
)
/*++
Routine Description:
ReceiveFromSource obtains whatever data is being sent from the source. If the data
is a file, the file is saved into the directory indicated by LocalFileRoot.
If the data is encrypted buffer we will allocate Buffer and return the decrypted
data there.
Arguments:
Connection - Specifies the connection to send the file to, as returned by
ConnectToDestination.
LocalFileRoot - Specifies the root of the local path of the file to save. The
actual file name and optional subpath comes from the destination.
Buffer - Specifies the buffer to be allocated and filled with decrypted data.
Return Value:
The message ID received, or 0 if no message was recieved.
--*/
{
DWORD msg;
BOOL retry;
do {
retry = FALSE;
msg = pReceiveMessage (Connection->Socket, Connection->Datagram ? &Connection->DatagramPool : NULL, Timeout);
DEBUGMSG ((DBG_HOMENET, "Message from source: %u", msg));
switch (msg) {
case MESSAGE_FILE:
BfCreateDirectory (LocalFileRoot);
if (!pReceiveFile (
Connection->Socket,
Connection->Datagram ? &Connection->DatagramPool : NULL,
LocalFileRoot,
Timeout
)) {
msg = 0;
}
break;
case MESSAGE_DATA:
if (!pReceiveEncryptedData (
Connection->Socket,
Connection->Datagram ? &Connection->DatagramPool : NULL,
Buffer,
Timeout
)) {
msg = 0;
}
break;
case MESSAGE_KEEP_ALIVE:
retry = TRUE;
break;
}
} while (retry);
return msg;
}