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.
 
 
 
 
 
 

1864 lines
47 KiB

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
main.c
Abstract:
homenet.exe is a proof-of-concept tool for the protocol-independent
home networking transport.
Author:
Jim Schmidt (jimschm) 01-Jul-2000
Revision History:
<full name> (<alias>) <date> <comments>
--*/
#include "pch.h"
#include <initguid.h>
#include <winsock2.h>
#include <wsipx.h>
#include <wsnwlink.h>
#include <wsnetbs.h>
#include <nb30.h>
#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 MAX_SOCKADDR (max(sizeof(SOCKADDR_IN),max(sizeof(SOCKADDR_IPX),sizeof(SOCKADDR_NB))))
#define DBG_HOMENET "HomeNet"
VOID
pDoSource (
VOID
);
VOID
pDoDestination (
VOID
);
// 36E4BE8D-0766-46E9-8679-8546529A90EE
DEFINE_GUID(g_MyGuid, 0x36E4BE8D, 0x0766, 0x46e9, 0x86, 0x79, 0X85, 0x46, 0x52, 0X9A, 0x90, 0XEE);
HANDLE g_StopHandle;
HANDLE g_ConnectionDone;
TCHAR g_StoragePath[MAX_PATH];
#pragma pack(push,1)
typedef struct {
WORD PacketNumber;
WORD DataLength;
} DATAGRAM_PACKET, *PDATAGRAM_PACKET;
#pragma pack(pop)
typedef struct TAG_DATAGRAM_POOL_ITEM {
struct TAG_DATAGRAM_POOL_ITEM *Next, *Prev;
DATAGRAM_PACKET Header;
PCBYTE PacketData;
// PacketData follows
} DATAGRAM_POOL_ITEM, *PDATAGRAM_POOL_ITEM;
typedef struct {
PMHANDLE Pool;
SOCKET Socket;
PDATAGRAM_POOL_ITEM FirstItem;
WORD SendSequenceNumber;
WORD RecvSequenceNumber;
} DATAGRAM_POOL, *PDATAGRAM_POOL;
BOOL
pCallEntryPoints (
DWORD Reason
)
{
switch (Reason) {
case DLL_PROCESS_ATTACH:
UtInitialize (NULL);
break;
case DLL_PROCESS_DETACH:
UtTerminate ();
break;
}
return TRUE;
}
BOOL
Init (
VOID
)
{
return pCallEntryPoints (DLL_PROCESS_ATTACH);
}
VOID
Terminate (
VOID
)
{
pCallEntryPoints (DLL_PROCESS_DETACH);
}
VOID
HelpAndExit (
VOID
)
{
//
// This routine is called whenever command line args are wrong
//
fprintf (
stderr,
"Command Line Syntax:\n\n"
//
// Describe command line syntax(es), indent 2 spaces
//
" homenet /S|/D\n"
"\nDescription:\n\n"
//
// Describe tool, indent 2 spaces
//
" HomeNet.exe is a proof-of-concept tool for the home networking transport.\n"
"\nArguments:\n\n"
//
// Describe args, indent 2 spaces, say optional if necessary
//
" /S Executes the tool in source mode\n"
" /D Executes the tool in destination mode\n"
);
exit (1);
}
BOOL
pCtrlCRoutine (
IN DWORD CtrlType
)
{
SetEvent (g_StopHandle);
WaitForSingleObject (g_ConnectionDone, INFINITE);
return FALSE;
}
INT
__cdecl
_tmain (
INT argc,
PCTSTR argv[]
)
{
INT i;
BOOL destination = FALSE;
BOOL source = FALSE;
WSADATA startupData;
INT result;
g_StopHandle = CreateEvent (NULL, TRUE, FALSE, NULL);
g_ConnectionDone = CreateEvent (NULL, TRUE, FALSE, NULL);
SetConsoleCtrlHandler (pCtrlCRoutine, TRUE);
//
// Parse command line here
//
for (i = 1 ; i < argc ; i++) {
if (argv[i][0] == TEXT('/') || argv[i][0] == TEXT('-')) {
switch (_totlower (_tcsnextc (&argv[i][1]))) {
case TEXT('s'):
if (source || destination) {
HelpAndExit();
}
source = TRUE;
break;
case TEXT('d'):
if (source || destination) {
HelpAndExit();
}
destination = TRUE;
break;
default:
HelpAndExit();
}
} else {
//
// Parse other args that don't require / or -
//
// None
HelpAndExit();
}
}
//
// Begin processing
//
if (!Init()) {
return 0;
}
//
// Start sockets
//
result = WSAStartup (2, &startupData);
if (result) {
printf ("Can't start sockets. Code=%u\n", result);
exit (1);
}
//
// Do work
//
if (source) {
pDoSource();
} else {
pDoDestination();
}
//
// Shut down sockets
//
WSACleanup();
//
// End of processing
//
Terminate();
return 0;
}
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 {
SOCKET Socket;
BYTE LocalAddress[MAX_SOCKADDR];
INT LocalAddressLen;
BYTE RemoteAddress[MAX_SOCKADDR];
INT RemoteAddressLen;
INT Family;
INT Protocol;
BOOL Datagram;
DATAGRAM_POOL DatagramPool;
} CONNECTIONSOCKET, *PCONNECTIONSOCKET;
typedef struct {
BYTE LocalAddress[MAX_SOCKADDR];
INT LocalAddressLen;
BYTE RemoteAddress[MAX_SOCKADDR];
INT RemoteAddressLen;
INT Family;
INT Protocol;
BOOL Datagram;
TCHAR DestinationName[MAX_COMPUTER_NAME];
} CONNECTADDRESS, *PCONNECTADDRESS;
typedef struct {
PBROADCASTSOCKET BroadcastSockets;
INT BroadcastCount;
PLISTENSOCKET ListenSockets;
INT ListenCount;
CONNECTIONSOCKET ConnectionSocket;
PGROWBUFFER AddressArray;
} BROADCASTARGS, *PBROADCASTARGS;
typedef struct {
UINT StructSize;
UINT FileCount;
LONGLONG TotalSize;
} TRANSFERMETRICS, *PTRANSFERMETRICS;
PBROADCASTSOCKET
pOpenOneBroadcastSocket (
IN OUT PGROWBUFFER BroadcastSockets,
IN SOCKADDR *SockAddr,
IN INT SockAddrLen,
IN INT Family,
IN INT Protocol,
IN PCTSTR DebugText
)
{
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
)
{
SOCKADDR_IPX ipxAddr;
SOCKADDR_IN tcpipAddr;
SOCKADDR_NB netbiosAddr;
NCB ncbEnum;
LANA_ENUM leBuf;
INT rc;
INT i;
PBROADCASTSOCKET broadcastSocket;
BOOL b;
TCHAR netbiosDebugText[32];
MYASSERT (!BroadcastSockets->Buf && !BroadcastSockets->End);
//
// Open sockets for broadcasts
//
// IPX
ZeroMemory (&ipxAddr, sizeof (ipxAddr));
ipxAddr.sa_family = AF_IPX;
memset (ipxAddr.sa_nodenum, 0xFF, 6);
ipxAddr.sa_socket = IPX_BROADCAST_PORT;
pOpenOneBroadcastSocket (
BroadcastSockets,
(SOCKADDR *) &ipxAddr,
sizeof (ipxAddr),
AF_IPX,
NSPROTO_IPX,
TEXT("IPX")
);
// 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));
}
// NetBIOS
ZeroMemory (&ncbEnum, sizeof (NCB));
ncbEnum.ncb_command = NCBENUM;
ncbEnum.ncb_buffer = (PBYTE) &leBuf;
ncbEnum.ncb_length = sizeof (LANA_ENUM);
rc = Netbios (&ncbEnum);
if (rc == NRC_GOODRET) {
for (i = 0 ; i < leBuf.length ; i++) {
SET_NETBIOS_SOCKADDR (&netbiosAddr, NETBIOS_GROUP_NAME, "usmt", NETBIOS_BROADCAST_PORT);
wsprintf (netbiosDebugText, TEXT("NETBIOS cli lana %u"), leBuf.lana[i]);
pOpenOneBroadcastSocket (
BroadcastSockets,
(SOCKADDR *) &netbiosAddr,
sizeof (netbiosAddr),
AF_NETBIOS,
-leBuf.lana[i],
netbiosDebugText
);
}
}
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
)
{
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
)
{
SOCKADDR_IPX ipxAddr;
SOCKADDR_IN tcpipAddr;
SOCKADDR_NB netbiosAddr;
NCB ncbEnum;
LANA_ENUM leBuf;
INT rc;
INT i;
TCHAR netbiosDebugText[32];
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")
);
// NetBIOS
ZeroMemory (&ncbEnum, sizeof (NCB));
ncbEnum.ncb_command = NCBENUM;
ncbEnum.ncb_buffer = (PBYTE) &leBuf;
ncbEnum.ncb_length = sizeof (LANA_ENUM);
rc = Netbios (&ncbEnum);
if (rc == NRC_GOODRET) {
for (i = 0 ; i < leBuf.length ; i++) {
SET_NETBIOS_SOCKADDR (&netbiosAddr, NETBIOS_GROUP_NAME, "usmt", NETBIOS_CONNECT_PORT);
wsprintf (netbiosDebugText, TEXT("NETBIOS srv lana %u"), leBuf.lana[i]);
pOpenOneListenSocket (
ListenSockets,
(SOCKADDR *) &netbiosAddr,
sizeof (netbiosAddr),
AF_NETBIOS,
TRUE,
-leBuf.lana[i],
netbiosDebugText
);
}
}
return ListenSockets->End / sizeof (LISTENSOCKET);
}
PCTSTR
pGetNameFromMessage (
IN PCTSTR Message
)
{
PCTSTR p;
PCTSTR name = NULL;
INT len;
CHARTYPE ch;
if (_tcsprefixcmp (Message, TEXT("usmt-v2,"))) {
p = Message + 8;
len = 0;
while (*p) {
ch = _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;
}
}
}
return name;
}
VOID
pTranslateBroadcastAddrToConnectAddr (
IN INT Family,
IN OUT PINT Protocol,
IN OUT PBOOL Datagram,
IN OUT SOCKADDR *SockAddr
)
{
SOCKADDR_IPX *ipxAddr;
SOCKADDR_IN *tcpipAddr;
SOCKADDR_NB *netbiosAddr;
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;
case AF_NETBIOS:
netbiosAddr = (SOCKADDR_NB *) SockAddr;
netbiosAddr->snb_name[NETBIOS_NAME_LENGTH - 1] = NETBIOS_CONNECT_PORT;
*Datagram = TRUE;
break;
}
}
VOID
pResetPort (
IN INT Family,
IN OUT SOCKADDR *SockAddr
)
{
SOCKADDR_IN *tcpipAddr;
switch (Family) {
case AF_INET:
tcpipAddr = (SOCKADDR_IN *) SockAddr;
tcpipAddr->sin_port = 0;
break;
}
}
INT
pSourceBroadcast (
PBROADCASTARGS Args
)
{
INT i;
INT bytesIn;
DWORD rc;
TCHAR message[256];
UINT size;
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;
for (;;) {
//
// Check cancel
//
rc = WaitForSingleObject (g_StopHandle, 250);
if (rc == WAIT_OBJECT_0) {
result = FALSE;
break;
}
//
// Check time to live
//
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) {
// once we receive something, wait 15 additional seconds for other inbound datagrams
if (waitCycle == -1) {
waitCycle = 60;
}
for (i = 0 ; i < Args->BroadcastCount ; i++) {
broadcastSocket = &Args->BroadcastSockets[i];
if (FD_ISSET (broadcastSocket->Socket, &set)) {
remoteAddrLen = MAX_SOCKADDR;
bytesIn = recvfrom (
broadcastSocket->Socket,
message,
254,
0,
(SOCKADDR *) remoteAddr,
&remoteAddrLen
);
if (bytesIn > (10 * sizeof (TCHAR))) {
message[bytesIn] = 0;
message[bytesIn + 1] = 0;
//
// Parse the inbound text. It must be in the format of
//
// usmt-v2,<tchars>,<name>
//
name = pGetNameFromMessage (message);
if (name) {
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_DEBUGMSG ((DBG_HOMENET, "garbage found: %s", message));
}
}
}
}
}
return result ? Args->AddressArray->End / sizeof (CONNECTADDRESS) : 0;
}
BOOL
pDestinationBroadcast (
PBROADCASTARGS Args
)
{
INT i;
DWORD rc;
INT socketNum = 0;
TCHAR message[256];
TCHAR name[128];
UINT size;
FD_SET set;
TIMEVAL zero = {0,0};
PBROADCASTSOCKET broadcastSocket;
BOOL result = FALSE;
BYTE sockAddr[MAX_SOCKADDR];
INT sockAddrLen;
size = MAX_COMPUTER_NAME;
GetComputerName (name, &size);
size = wsprintf (message, TEXT("USMT-v2,%u,%s"), TcharCount (name), name);
size = (size + 1) * sizeof (TCHAR);
for (;;) {
//
// Check cancel
//
rc = WaitForSingleObject (g_StopHandle, 250);
if (rc == WAIT_OBJECT_0) {
break;
}
//
// Send out the message
//
broadcastSocket = &Args->BroadcastSockets[socketNum];
i = sendto (
broadcastSocket->Socket,
message,
size,
0,
(SOCKADDR *) broadcastSocket->BroadcastAddress,
broadcastSocket->AddressLen
);
if (i == SOCKET_ERROR) {
DEBUGMSG ((DBG_VERBOSE, "Error sending on socket %u: %u", socketNum, WSAGetLastError()));
} else {
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;
Args->ConnectionSocket.Socket = accept (
Args->ListenSockets[i].Socket,
(SOCKADDR *) Args->ConnectionSocket.RemoteAddress,
&Args->ConnectionSocket.RemoteAddressLen
);
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.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"));
}
result = TRUE;
break;
} else {
DEBUGMSG ((DBG_ERROR, "select indicated connection, but accept failed"));
}
}
}
if (result) {
break;
}
}
}
return result;
}
INT
pNameResolver (
OUT PGROWBUFFER AddressBuffer,
IN BOOL DestinationMode,
OUT PCONNECTIONSOCKET ConnectionSocket
)
{
INT size;
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;
PLISTENSOCKET connection;
__try {
//
// 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;
b = DestinationMode ? pDestinationBroadcast (&args) : pSourceBroadcast (&args);
//
// Clean up all sockets
//
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);
}
}
if (b) {
if (DestinationMode) {
CopyMemory (ConnectionSocket, &args.ConnectionSocket, sizeof (CONNECTIONSOCKET));
result = 1;
} else {
result = AddressBuffer->End / sizeof (CONNECTADDRESS);
}
}
}
__finally {
GbFree (&broadcastSockets);
GbFree (&listenSockets);
SetEvent (g_ConnectionDone);
}
return result;
}
BOOL
pSendExactData (
IN SOCKET Socket,
IN PBYTE Data,
IN UINT DataLen
)
{
INT result;
result = send (Socket, Data, DataLen, 0);
return result == (INT) DataLen;
}
BOOL
pSendDatagramData (
IN PDATAGRAM_POOL DatagramPool,
IN PCBYTE Data,
IN UINT DataLen
)
{
PDATAGRAM_PACKET header;
BYTE buffer[512];
PBYTE dataPtr;
UINT bytesSent = 0;
UINT bytesToSend;
INT result;
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);
result = send (
DatagramPool->Socket,
(PBYTE) header,
header->DataLength + sizeof (DATAGRAM_PACKET),
0
);
if (result == SOCKET_ERROR) {
break;
}
bytesToSend = (UINT) result - sizeof (DATAGRAM_PACKET);
Data += bytesToSend;
bytesSent += bytesToSend;
} while (bytesSent < DataLen);
return bytesSent == DataLen;
}
PBYTE
pReceiveExactData (
IN SOCKET Socket,
IN OUT PGROWBUFFER Buffer,
IN UINT BytesToReceive
)
{
PBYTE recvBuf;
PBYTE bufPos;
UINT bytesSoFar = 0;
INT result;
UINT readSize;
Buffer->End = 0;
recvBuf = GbGrow (Buffer, BytesToReceive);
bufPos = recvBuf;
do {
readSize = BytesToReceive - bytesSoFar;
result = recv (Socket, bufPos, (INT) readSize, 0);
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;
}
BOOL
pReceiveDatagramData (
IN PDATAGRAM_POOL DatagramPool,
IN OUT PGROWBUFFER Buffer, OPTIONAL
OUT PBYTE AlternateBuffer, OPTIONAL
IN UINT BytesToReceive
)
{
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;
UINT readSize;
PDATAGRAM_POOL_ITEM item;
UINT newPacketNum;
UINT currentPacketNum;
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 TRUE;
}
}
}
//
// Data is not available in the pool. Receive one packet and then try again.
//
// header
if (!pReceiveExactData (
DatagramPool->Socket,
NULL,
(PBYTE) &itemHeader->Header,
sizeof (DATAGRAM_PACKET)
)) {
break;
}
if (itemHeader->Header.DataLength > 256) {
break;
}
// data
if (!pReceiveExactData (
DatagramPool->Socket,
NULL,
(PBYTE) dataPtr,
itemHeader->Header.DataLength
)) {
break;
}
//
// 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_PACKET)
);
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;
}
}
return bytesSoFar == BytesToReceive;
}
BOOL
pSendFile (
IN SOCKET Socket,
IN PDATAGRAM_POOL DatagramPool, OPTIONAL
IN PCTSTR LocalFileName, OPTIONAL
IN PCTSTR DestFileName OPTIONAL
)
{
INT len;
GROWBUFFER data = INIT_GROWBUFFER;
BOOL result = FALSE;
HANDLE file = NULL;
LONGLONG fileSize;
__try {
//
// If no file was specified, send length of zero
//
if (!LocalFileName || !DestFileName) {
len = 0;
if (!pSendExactData (Socket, (PBYTE) &len, 4)) {
__leave;
}
result = TRUE;
__leave;
}
//
// Try to open the file
//
fileSize = BfGetFileSize (LocalFileName);
file = BfOpenFile (LocalFileName);
if (!file) {
__leave;
}
//
// Send the file name and file size
//
len = ByteCount (DestFileName);
if (!pSendExactData (Socket, (PBYTE) &len, 4)) {
__leave;
}
if (!pSendExactData (Socket, (PBYTE) DestFileName, len)) {
__leave;
}
if (!pSendExactData (Socket, (PBYTE) &fileSize, 8)) {
__leave;
}
//
// Send the data 64K at a time
//
GbGrow (&data, 0x10000);
while (fileSize) {
if (fileSize >= 0x10000) {
if (!BfReadFile (file, data.Buf, 0x10000)) {
DEBUGMSG ((DBG_ERROR, "Can't read from file"));
__leave;
}
if (!pSendExactData (Socket, data.Buf, 0x10000)) {
__leave;
}
fileSize -= 0x10000;
} else {
if (!BfReadFile (file, data.Buf, (UINT) fileSize)) {
DEBUGMSG ((DBG_ERROR, "Can't read from file"));
__leave;
}
if (!pSendExactData (Socket, data.Buf, (UINT) fileSize)) {
__leave;
}
fileSize = 0;
}
}
//
// Done!
//
result = TRUE;
}
__finally {
GbFree (&data);
if (file) {
CloseHandle (file);
}
}
return result;
}
BOOL
pReceiveStreamFile (
IN SOCKET Socket
)
{
TCHAR fileName[MAX_PATH * 2];
INT len;
INT bytesIn;
GROWBUFFER data = INIT_GROWBUFFER;
BOOL result = FALSE;
PTSTR p;
HANDLE file = NULL;
LONGLONG fileSize;
__try {
//
// Wait for file name
//
if (!pReceiveExactData (Socket, &data, 4)) {
__leave;
}
len = *((PDWORD) data.Buf);
if (!len) {
result = TRUE;
__leave;
}
if (len >= (MAX_PATH * sizeof (TCHAR))) {
__leave;
}
if (!pReceiveExactData (Socket, &data, len)) {
__leave;
}
StringCopy (fileName, g_StoragePath);
GbGrow (&data, sizeof (TCHAR) * 2);
p = (PTSTR) data.Buf;
p[len] = 0;
p[len + 1] = 0;
StringCopy (AppendWack (fileName), p);
//
// Get the file size
//
if (!pReceiveExactData (Socket, &data, 8)) {
__leave;
}
fileSize = *((PLONGLONG) data.Buf);
DEBUGMSG ((DBG_HOMENET, "Receiving %s", fileName));
//
// Create the file
//
file = BfCreateFile (fileName);
if (!file) {
__leave;
}
//
// Fetch the data 64K at a time
//
while (fileSize) {
if (fileSize >= 0x10000) {
if (!pReceiveExactData (Socket, &data, 0x10000)) {
__leave;
}
if (!BfWriteFile (file, data.Buf, data.End)) {
DEBUGMSG ((DBG_ERROR, "Can't write to file"));
__leave;
}
fileSize -= 0x10000;
} else {
if (!pReceiveExactData (Socket, &data, (UINT) fileSize)) {
__leave;
}
if (!BfWriteFile (file, data.Buf, data.End)) {
DEBUGMSG ((DBG_ERROR, "Can't write to file"));
__leave;
}
fileSize = 0;
}
}
//
// Done!
//
result = TRUE;
}
__finally {
GbFree (&data);
if (file) {
CloseHandle (file);
if (!result) {
DeleteFile (fileName);
}
}
}
return result;
}
BOOL
pReceiveFile (
IN SOCKET Socket,
IN BOOL Datagram
)
{
if (Datagram) {
return FALSE;
}
return pReceiveStreamFile (Socket);
}
BOOL
pSendMetrics (
IN SOCKET Socket,
IN BOOL Datagram,
IN PTRANSFERMETRICS Metrics
)
{
if (Datagram) {
return FALSE;
}
Metrics->StructSize = sizeof (TRANSFERMETRICS);
if (!pSendExactData (Socket, (PBYTE) Metrics, sizeof (TRANSFERMETRICS))) {
return FALSE;
}
return TRUE;
}
BOOL
pReceiveMetrics (
IN SOCKET Socket,
OUT PTRANSFERMETRICS Metrics
)
{
GROWBUFFER data = INIT_GROWBUFFER;
BOOL result = FALSE;
__try {
if (!pReceiveExactData (Socket, &data, sizeof (TRANSFERMETRICS))) {
__leave;
}
CopyMemory (Metrics, data.Buf, data.End);
if (Metrics->StructSize != sizeof (TRANSFERMETRICS)) {
DEBUGMSG ((DBG_ERROR, "Invalid transfer metrics received"));
__leave;
}
result = TRUE;
}
__finally {
GbFree (&data);
}
return result;
}
VOID
pDoDestination (
VOID
)
{
GROWBUFFER sourceAddress = INIT_GROWBUFFER;
CONNECTIONSOCKET connection;
TRANSFERMETRICS metrics;
UINT u;
ZeroMemory (&connection, sizeof (CONNECTIONSOCKET));
connection.Socket = INVALID_SOCKET;
__try {
GetTempPath (MAX_PATH, g_StoragePath);
if (!pNameResolver (&sourceAddress, TRUE, &connection)) {
__leave;
}
printf ("Connected!\n");
if (!pReceiveMetrics (connection.Socket, &metrics)) {
__leave;
}
for (u = 0 ; u < metrics.FileCount ; u++) {
if (!pReceiveFile (connection.Socket, connection.Datagram)) {
__leave;
}
}
}
__finally {
GbFree (&sourceAddress);
if (connection.Socket != INVALID_SOCKET) {
closesocket (connection.Socket);
}
}
}
BOOL
pConnectToDestination (
IN PCONNECTADDRESS Address,
OUT PCONNECTIONSOCKET Connection
)
{
BOOL result = FALSE;
BOOL b;
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));
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;
result = TRUE;
}
__finally {
if (!result && Connection->Socket != INVALID_SOCKET) {
closesocket (Connection->Socket);
Connection->Socket = INVALID_SOCKET;
}
}
return result;
}
VOID
pDoSource (
VOID
)
{
GROWBUFFER destinationAddresses = INIT_GROWBUFFER;
INT destinationCount;
PCTSTR firstName;
PCONNECTADDRESS addressArray;
PCONNECTADDRESS address;
CONNECTIONSOCKET connection;
INT i;
TRANSFERMETRICS metrics;
PCTSTR localFileName = TEXT("c:\\debug.inf");
PCTSTR destFileName = TEXT("foo.inf");
ZeroMemory (&connection, sizeof (CONNECTIONSOCKET));
connection.Socket = INVALID_SOCKET;
__try {
destinationCount = pNameResolver (&destinationAddresses, FALSE, 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
//
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_ERROR, "Multiple destinations found on the subnet; can't continue"));
__leave;
}
}
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;
}
}
}
//
// Now connect to destination (at index i)
//
DEBUGMSG ((
DBG_HOMENET,
"Attempting connection to %s (protocol %i)",
addressArray[i].DestinationName,
addressArray[i].Protocol
));
if (!pConnectToDestination (&addressArray[i], &connection)) {
__leave;
}
printf ("Connected!\n");
ZeroMemory (&metrics, sizeof (metrics));
metrics.FileCount = 1;
metrics.TotalSize = 0;
if (!pSendMetrics (connection.Socket, connection.Datagram, &metrics)) {
__leave;
}
if (!pSendFile (
connection.Socket,
connection.Datagram ? &connection.DatagramPool : NULL,
localFileName,
destFileName
)) {
__leave;
}
}
__finally {
GbFree (&destinationAddresses);
if (connection.Socket != INVALID_SOCKET) {
closesocket (connection.Socket);
}
}
}