|
|
/*++
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); } } }
|