1690 lines
40 KiB
C
1690 lines
40 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1992-1996 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
tftpd.c
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Sam Patton (sampa) 08-Apr-1992
|
||
|
|
||
|
History:
|
||
|
|
||
|
MohsinA, 02-Dec-96. - Winsock2 asynchronous version with
|
||
|
synchronisation events, graceful termination,
|
||
|
event logging, debugging info.
|
||
|
Credits: Anirudh Sahni - Info on SC.
|
||
|
AdamBa, 11-Jun-97 - add security
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "tftpd.h"
|
||
|
|
||
|
|
||
|
char USAGE[] =
|
||
|
" ======================================================================== \n"
|
||
|
"Abstract: \n"
|
||
|
" This implements an RFC 783 tftp daemon. \n"
|
||
|
" It listens on port 69 for requests \n"
|
||
|
" and spawns a thread to process each request. \n"
|
||
|
" \n"
|
||
|
"TFTPD USAGE and Installation: \n"
|
||
|
" \n"
|
||
|
" md d:/tftpd (the StartDirectory). \n"
|
||
|
" copy //MohsinA_p90/test/tftpd.exe . \n"
|
||
|
" sc create tftpd binPath= d:/tftpd/tftpd.exe (give full path). \n"
|
||
|
" sc query tftpd (check if installed). \n"
|
||
|
" \n"
|
||
|
"Start: \n"
|
||
|
" sc start tftpd -f (creates a log file). \n"
|
||
|
"or sc start tftpd \n"
|
||
|
"or net start tftpd \n"
|
||
|
"or sc start tftpd [-dStartDirectory] [-e] [-f] \n"
|
||
|
" Options: -e use event log. \n"
|
||
|
" -f log to file. \n"
|
||
|
" -dStartDirectory \n"
|
||
|
"Info: \n"
|
||
|
" sc interrogate tftpd (logs will be updated). \n"
|
||
|
" sc query tftpd Check whether running. \n"
|
||
|
"Stop: \n"
|
||
|
" sc stop tftpd \n"
|
||
|
" net stop tftpd \n"
|
||
|
" \n"
|
||
|
"Variables that control what files can be read/written and by whom: \n"
|
||
|
" StartDirectory - only files there will be accessible. \n"
|
||
|
" LogFile is created here. \n"
|
||
|
" ValidClients - Clients matching this ip address can read files. \n"
|
||
|
" eg. you can set it to \"157.55.8?.*\" \n"
|
||
|
" ValidMasters - clients matching this can write and read files. \n"
|
||
|
" eg. you can set it to \"\" and no one can write. \n"
|
||
|
" ValidReadFiles - only matching files will be served out, eg. \"r*.t?t\"\n"
|
||
|
" ValidWriteFiles- only matching files will be accepted, eg. \"w*.txt\" \n"
|
||
|
" \n"
|
||
|
"Client: \n"
|
||
|
" tftp [-i] servername {get|put} src_file dest_file \n"
|
||
|
" -i from binary mode, else ascii mode is used. \n"
|
||
|
" \n"
|
||
|
" ======================================================================== \n"
|
||
|
;
|
||
|
|
||
|
#define NUM_INITIAL_THREADS 1
|
||
|
#define MAX_THREAD_COUNT 25
|
||
|
#define RCV_WAIT_INTERVAL_MSEC 5
|
||
|
|
||
|
#define RECEIVE_HEAP_THREASHOLD1 10
|
||
|
#define RECEIVE_HEAP_THREASHOLD2 3
|
||
|
|
||
|
#define RECEIVE_NUM_FREE 2
|
||
|
|
||
|
#define MAX_SOCKET_INIT_ATTEMPT 10
|
||
|
|
||
|
#define LOOPBACK 0x100007f
|
||
|
|
||
|
#define SE_SOCKET_REOPEN 0x1
|
||
|
|
||
|
#define MAX_LOCK_TRIES 125
|
||
|
|
||
|
|
||
|
SERVICE_STATUS TftpdServiceStatus;
|
||
|
SERVICE_STATUS_HANDLE TftpdServiceStatusHandle;
|
||
|
char TftpdServiceName[] = TEXT("Tftpd");
|
||
|
|
||
|
HANDLE MasterThreadHandle;
|
||
|
DWORD MasterThreadId;
|
||
|
|
||
|
|
||
|
WSADATA WsaData;
|
||
|
|
||
|
FILE * LogFile = NULL;
|
||
|
|
||
|
BOOL LoggingEvent = FALSE;
|
||
|
BOOL LoggingFile = FALSE;
|
||
|
|
||
|
HANDLE eMasterTerminate = NULL; // Event to stop main thread.
|
||
|
HANDLE eSock = NULL; // Event to start WSARecvFrom
|
||
|
|
||
|
DWORD TotalThreadCount=0; // total worker threads
|
||
|
DWORD AvailableThreads=0; // number of idle worker threads
|
||
|
|
||
|
TFTP_GLOBALS Globals;
|
||
|
HANDLE MasterSocketEvent; // set when data available on TFTPD socket
|
||
|
HANDLE MasterSocketWait; // handle to RtlRegisterWait for above event
|
||
|
|
||
|
LIST_ENTRY ReceiveList;
|
||
|
CRITICAL_SECTION ReceiveLock;
|
||
|
|
||
|
LIST_ENTRY SocketList;
|
||
|
CRITICAL_SECTION SocketLock;
|
||
|
|
||
|
HANDLE ReceiveHeap=0;
|
||
|
HANDLE ReaperWait;
|
||
|
|
||
|
DWORD ReceiveHeapSize=0;
|
||
|
DWORD ActiveReceive=0;
|
||
|
|
||
|
OVERLAPPED AddrChangeOverlapped;
|
||
|
HANDLE AddrChangeEvent=NULL;
|
||
|
HANDLE AddrChangeWaitHandle=NULL;
|
||
|
HANDLE AddrChangeHandle=NULL;
|
||
|
|
||
|
const char BuildString[] =
|
||
|
__FILE__ " built " __DATE__ " " __TIME__ "\n";
|
||
|
|
||
|
struct TFTPD_STAT tftpd_stat;
|
||
|
|
||
|
// See sdk/inc/winsvc.h
|
||
|
|
||
|
SERVICE_TABLE_ENTRY TftpdDispatchTable[] = {
|
||
|
{
|
||
|
TftpdServiceName, // TEXT "Tftpd"
|
||
|
TftpdStart // main function.
|
||
|
},
|
||
|
{ NULL, NULL }
|
||
|
};
|
||
|
|
||
|
#if defined(REMOTE_BOOT_SECURITY)
|
||
|
BOOL
|
||
|
TftpdInitSecurityArray(
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
TftpdUninitSecurityArray(
|
||
|
VOID
|
||
|
);
|
||
|
#endif //defined(REMOTE_BOOT_SECURITY)
|
||
|
|
||
|
// ========================================================================
|
||
|
|
||
|
void
|
||
|
__cdecl
|
||
|
main(
|
||
|
int argc,
|
||
|
char * argv[]
|
||
|
)
|
||
|
{
|
||
|
int ok;
|
||
|
|
||
|
DbgPrint( "main: %s"
|
||
|
"Starting service \"%s\".\n",
|
||
|
BuildString,
|
||
|
TftpdDispatchTable[0].lpServiceName );
|
||
|
|
||
|
if( argc > 1 && !strcmp( argv[1], "-?" ) ){
|
||
|
printf( USAGE );
|
||
|
printf( " TFTPD_DEFAULT_DIR is %s\n", TFTPD_DEFAULT_DIR );
|
||
|
printf( " TFTPD_LOGFILE is %s\n\n", TFTPD_LOGFILE );
|
||
|
|
||
|
printf( "Registry key names, all strings: HKEY_LOCAL_MACHINE %s\n",
|
||
|
TFTPD_REGKEY );
|
||
|
printf( " o StartDirectory keyname \"%s\"\n", TFTPD_REGKEY_DIR );
|
||
|
|
||
|
printf( "These keys are shell patterns with * and ? (see examples above):\n");
|
||
|
printf( " o ValidClients keyname \"%s\"\n", TFTPD_REGKEY_CLIENTS );
|
||
|
printf( " o ValidMasters keyname \"%s\"\n", TFTPD_REGKEY_MASTERS );
|
||
|
printf( " o Readable files keyname \"%s\"\n", TFTPD_REGKEY_READABLE );
|
||
|
printf( " o writable files keyname \"%s\"\n", TFTPD_REGKEY_WRITEABLE);
|
||
|
exit( -1 );
|
||
|
}
|
||
|
|
||
|
|
||
|
ok =
|
||
|
StartServiceCtrlDispatcher(TftpdDispatchTable);
|
||
|
|
||
|
if( ok ){
|
||
|
DbgPrint("tftpd StartServiceCtrlDispatcher ok.\n" );
|
||
|
}else{
|
||
|
DbgPrint("tftpd StartServiceCtrlDispatcher error=%d\n",
|
||
|
GetLastError() );
|
||
|
}
|
||
|
|
||
|
ExitProcess(0);
|
||
|
}
|
||
|
|
||
|
// ========================================================================
|
||
|
// Main function called by service controller.
|
||
|
|
||
|
DWORD
|
||
|
TftpdStart(
|
||
|
IN DWORD argc,
|
||
|
IN LPTSTR argv[]
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This sits waiting on the tftpd well-known port (69) until it gets a request
|
||
|
Then it spawns a thread to either TftpdHandleRead or TftpdHandleWrite which
|
||
|
processes the request.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
argc:
|
||
|
argv: [-dStartDirectory] option.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
Error?
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
int Status;
|
||
|
struct sockaddr_in TftpdAddress;
|
||
|
int err, ok, i;
|
||
|
DWORD WaitStatus;
|
||
|
|
||
|
//
|
||
|
// Initialize all the status fields so that subsequent calls to
|
||
|
// SetServiceStatus need to only update fields that changed.
|
||
|
//
|
||
|
|
||
|
TftpdServiceStatus.dwServiceType = SERVICE_WIN32;
|
||
|
TftpdServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
||
|
TftpdServiceStatus.dwControlsAccepted = 0;
|
||
|
TftpdServiceStatus.dwCheckPoint = 1;
|
||
|
TftpdServiceStatus.dwWaitHint = 20000; // 20 seconds
|
||
|
|
||
|
|
||
|
SET_SERVICE_EXITCODE(
|
||
|
NO_ERROR,
|
||
|
TftpdServiceStatus.dwWin32ExitCode,
|
||
|
TftpdServiceStatus.dwServiceSpecificExitCode
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Initialize server to receive service requests by registering the
|
||
|
// control handler.
|
||
|
//
|
||
|
|
||
|
TftpdServiceStatusHandle =
|
||
|
RegisterServiceCtrlHandler(
|
||
|
TftpdServiceName,
|
||
|
TftpdControlHandler
|
||
|
);
|
||
|
|
||
|
if ( TftpdServiceStatusHandle == 0 ) {
|
||
|
DbgPrint("TftpdStart: RegisterServiceCtrlHandler failed: %lx\n",
|
||
|
GetLastError());
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
ok =
|
||
|
SetServiceStatus( TftpdServiceStatusHandle, &TftpdServiceStatus);
|
||
|
|
||
|
if( !ok ){
|
||
|
DbgPrint("TftpdStart: SetServiceStatus failed=%d\n",
|
||
|
GetLastError() );
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Create the main synchronisation events.
|
||
|
//
|
||
|
|
||
|
eMasterTerminate =
|
||
|
CreateEvent(
|
||
|
NULL, // LPSECURITY_ATTRIBUTES.
|
||
|
FALSE, // bManualReset,
|
||
|
FALSE, // bInitialState
|
||
|
NULL // LPCTSTR pointer to event-object name
|
||
|
);
|
||
|
|
||
|
eSock = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||
|
|
||
|
if( ! eMasterTerminate || ! eSock ){
|
||
|
DbgPrint("TftpdStart: CreateEvent failed.\n");
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Start Winsock
|
||
|
//
|
||
|
|
||
|
err =
|
||
|
WSAStartup( 0x0101, &WsaData );
|
||
|
|
||
|
if (err == SOCKET_ERROR ) {
|
||
|
DbgPrint("TftpdStart: WSAStartup failed, Error=%d.\n",
|
||
|
WSAGetLastError() );
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
DbgPrint("TftpdStart: winsock: %x/%x, %s\n %s\n",
|
||
|
WsaData.wVersion, WsaData.wHighVersion,
|
||
|
WsaData.szDescription, WsaData.szSystemStatus );
|
||
|
|
||
|
#if defined(REMOTE_BOOT_SECURITY)
|
||
|
//
|
||
|
// Initialize security-related info.
|
||
|
//
|
||
|
|
||
|
if (!TftpdInitSecurityArray()) {
|
||
|
DbgPrint("TftpdStart: cannot initialize security array\n");
|
||
|
goto failed;
|
||
|
}
|
||
|
#endif //defined(REMOTE_BOOT_SECURITY)
|
||
|
|
||
|
|
||
|
//
|
||
|
// Startup worked - announce that we're all done.
|
||
|
//
|
||
|
|
||
|
TftpdServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
||
|
|
||
|
TftpdServiceStatus.dwControlsAccepted =
|
||
|
SERVICE_ACCEPT_STOP
|
||
|
| SERVICE_ACCEPT_PAUSE_CONTINUE
|
||
|
| SERVICE_ACCEPT_SHUTDOWN;
|
||
|
|
||
|
TftpdServiceStatus.dwCheckPoint = 0;
|
||
|
TftpdServiceStatus.dwWaitHint = 0;
|
||
|
|
||
|
ok =
|
||
|
SetServiceStatus(
|
||
|
TftpdServiceStatusHandle,
|
||
|
&TftpdServiceStatus
|
||
|
);
|
||
|
|
||
|
if( !ok ){
|
||
|
DbgPrint("TftpdStart: SetServiceStatus failed=%d\n",
|
||
|
GetLastError() );
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Clear the stats.
|
||
|
//
|
||
|
|
||
|
memset( &tftpd_stat, '\0', sizeof( struct TFTPD_STAT ) );
|
||
|
time( &tftpd_stat.started_at );
|
||
|
|
||
|
|
||
|
//
|
||
|
// Process Command line arguments/options.
|
||
|
//
|
||
|
|
||
|
#ifdef DBG
|
||
|
DbgPrint("TftpdStart: argc=%d\n", argc);
|
||
|
for( ok = 0; ok < (int) argc; ok++ ){
|
||
|
DbgPrint("\targv[%d]=%s.\n", ok, argv[ok] );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
while( --argc > 0 && argv[argc][0] == '-' ){
|
||
|
switch( argv[argc][1] ){
|
||
|
case 'd' : // ie. -ddirectory.
|
||
|
strcpy( StartDirectory, &argv[argc][2] );
|
||
|
DbgPrint("TftpdStart: StartDirectory=%s\n", StartDirectory );
|
||
|
break;
|
||
|
case 'e' :
|
||
|
LoggingEvent = TRUE;
|
||
|
break;
|
||
|
case 'f' :
|
||
|
LoggingFile = TRUE;
|
||
|
break;
|
||
|
default:
|
||
|
DbgPrint("TftpdStart: invalid option %s\n", argv[0] );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ReadRegistryValues();
|
||
|
|
||
|
Set_StartDirectory();
|
||
|
|
||
|
//
|
||
|
// Start in the dir from where we want to export files.
|
||
|
//
|
||
|
|
||
|
err = _chdir( StartDirectory );
|
||
|
|
||
|
if( err == -1 ){
|
||
|
DbgPrint( "TftpdStart: _chdir(%s) failed, errno=%d\n",
|
||
|
StartDirectory, errno
|
||
|
);
|
||
|
err = _mkdir(StartDirectory);
|
||
|
if (err == ERROR_SUCCESS) {
|
||
|
err = _chdir( StartDirectory );
|
||
|
}
|
||
|
|
||
|
if (err != ERROR_SUCCESS) {
|
||
|
goto failed;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Open Logfile only after chdir.
|
||
|
//
|
||
|
|
||
|
if( LoggingFile ){
|
||
|
LogFile = fopen( TFTPD_LOGFILE, "a+" );
|
||
|
if( LogFile == NULL ){
|
||
|
LoggingFile = FALSE;
|
||
|
DbgPrint( "Cannot open TFTPD_LOGFILE=%s\n", TFTPD_LOGFILE );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Only the risque and successful come here.
|
||
|
//
|
||
|
|
||
|
DbgPrint("TftpdStart in %s at %s.\n",
|
||
|
StartDirectory,
|
||
|
ctime( &tftpd_stat.started_at )
|
||
|
);
|
||
|
|
||
|
|
||
|
// Initialize Thread Pool
|
||
|
|
||
|
TftpdInitializeThreadPool();
|
||
|
TftpdInitializeReceiveHeap();
|
||
|
|
||
|
|
||
|
WaitStatus=WaitForSingleObject(eMasterTerminate,INFINITE);
|
||
|
if (WaitStatus != WAIT_OBJECT_0) {
|
||
|
DbgPrint("Wait for termination error %d",GetLastError());
|
||
|
}
|
||
|
|
||
|
// Thread pool thread still active, but oh well ... exiting process.
|
||
|
|
||
|
return(0);
|
||
|
|
||
|
//
|
||
|
// Failures jump here.
|
||
|
//
|
||
|
|
||
|
failed:
|
||
|
TftpdServiceExit(ERROR_GEN_FAILURE);
|
||
|
exit(1);
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
HANDLE RegisterSocket(SOCKET Sock, HANDLE Event, DWORD Flag)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Schedule calling of TftpdReceive when data received on socket Sock
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Handle to registered function, NULL on failure
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
HANDLE MasterSocketWait;
|
||
|
|
||
|
DbgPrint("RegisterSocket: Socket %d, Event %d\n",Sock,Event);
|
||
|
|
||
|
if (WSAEventSelect(Sock,Event,FD_READ|FD_WRITE) != 0) {
|
||
|
DbgPrint("EventSelect failed %d",GetLastError());
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (Flag & REG_NEW_SOCKET) {
|
||
|
status = RtlRegisterWait(&MasterSocketWait,
|
||
|
Event,
|
||
|
TftpdNewReceive,
|
||
|
(PVOID)Sock,
|
||
|
INFINITE,
|
||
|
0);
|
||
|
} else {
|
||
|
status = RtlRegisterWait(&MasterSocketWait,
|
||
|
Event,
|
||
|
TftpdContinueReceive,
|
||
|
(PVOID)Sock,
|
||
|
INFINITE,
|
||
|
0);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
DbgPrint("Failed to register wait %d",status);
|
||
|
}
|
||
|
|
||
|
|
||
|
return MasterSocketWait;
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
DWORD TftpdInitializeThreadPool()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Create initial pool of thread
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Exit status
|
||
|
0 == success
|
||
|
1 == failure
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
DWORD i;
|
||
|
HANDLE ThreadHandle;
|
||
|
DWORD ThreadId;
|
||
|
NTSTATUS status;
|
||
|
PMIB_IPADDRTABLE IpAddrTable;
|
||
|
|
||
|
InitializeCriticalSection(&Globals.Lock);
|
||
|
InitializeCriticalSection(&SocketLock);
|
||
|
InitializeListHead(&Globals.WorkList);
|
||
|
|
||
|
InitializeListHead(&SocketList);
|
||
|
if (GetIpTable(&IpAddrTable) == ERROR_SUCCESS) {
|
||
|
|
||
|
for (i=0; i < IpAddrTable->dwNumEntries; i++) {
|
||
|
if ( (IpAddrTable->table[i].dwAddr != 0) &&
|
||
|
(IpAddrTable->table[i].dwAddr != LOOPBACK)) {
|
||
|
AddSocket(IpAddrTable->table[i].dwAddr);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
free(IpAddrTable);
|
||
|
|
||
|
}
|
||
|
|
||
|
status=RtlCreateTimerQueue(&Globals.TimerQueueHandle);
|
||
|
|
||
|
if (status != ERROR_SUCCESS) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
status=RtlCreateTimer(Globals.TimerQueueHandle,
|
||
|
&ReaperWait,
|
||
|
TftpdReaper,
|
||
|
(PVOID)NULL,
|
||
|
REAPER_INTERVAL_SEC*1000,
|
||
|
REAPER_INTERVAL_SEC*1000,
|
||
|
0);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
DbgPrint("Failed to Arm Timer %d",status);
|
||
|
}
|
||
|
|
||
|
AddrChangeEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
|
||
|
|
||
|
if (!AddrChangeEvent) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
status =
|
||
|
RtlRegisterWait(
|
||
|
&AddrChangeWaitHandle,
|
||
|
AddrChangeEvent,
|
||
|
InterfaceChange,
|
||
|
NULL,
|
||
|
INFINITE,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if (status != ERROR_SUCCESS) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
memset(&AddrChangeOverlapped,0,sizeof(OVERLAPPED));
|
||
|
AddrChangeOverlapped.hEvent=AddrChangeEvent;
|
||
|
|
||
|
status = NotifyAddrChange(&AddrChangeHandle,&AddrChangeOverlapped);
|
||
|
|
||
|
if (status != ERROR_SUCCESS && status != ERROR_IO_PENDING) {
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
return ERROR_SUCCESS;
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID TftpdInitializeReceiveHeap()
|
||
|
{
|
||
|
DWORD size;
|
||
|
|
||
|
InitializeListHead(&ReceiveList);
|
||
|
InitializeCriticalSection(&ReceiveLock);
|
||
|
size = 5 * (sizeof(TFTP_REQUEST));
|
||
|
ReceiveHeap = HeapCreate(0, size ,0);
|
||
|
ASSERT(ReceiveHeap);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
Called on the reaper interval. Cleanup extra heap entries
|
||
|
|
||
|
*/
|
||
|
VOID TftpdCleanHeap()
|
||
|
{
|
||
|
|
||
|
DWORD i=0;
|
||
|
PLIST_ENTRY pEntry;
|
||
|
PTFTP_REQUEST Request=NULL;
|
||
|
DWORD NumToFree=0;
|
||
|
|
||
|
|
||
|
EnterCriticalSection(&ReceiveLock);
|
||
|
|
||
|
if (ReceiveHeapSize - ActiveReceive > RECEIVE_HEAP_THREASHOLD1) {
|
||
|
NumToFree = ((ReceiveHeapSize - ActiveReceive) / 2);
|
||
|
} else if ((ReceiveHeapSize - ActiveReceive > RECEIVE_HEAP_THREASHOLD2)) {
|
||
|
NumToFree = RECEIVE_NUM_FREE;
|
||
|
}
|
||
|
|
||
|
while(i < NumToFree) {
|
||
|
pEntry=RemoveHeadList(&ReceiveList);
|
||
|
Request=CONTAINING_RECORD(pEntry,TFTP_REQUEST,RequestLinkage);
|
||
|
|
||
|
CloseHandle(Request->RcvEvent);
|
||
|
HeapFree(ReceiveHeap,0,Request);
|
||
|
i++;
|
||
|
ReceiveHeapSize--;
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&ReceiveLock);
|
||
|
|
||
|
}
|
||
|
|
||
|
DWORD HandleRequest(SOCKET Sock, IPAddr MyAddr)
|
||
|
{
|
||
|
|
||
|
unsigned short Opcode;
|
||
|
struct sockaddr_in PeerAddress;
|
||
|
int PeerAddressLength;
|
||
|
PTFTP_REQUEST Request;
|
||
|
char RequestPacket[MAX_TFTP_DATAGRAM + 1];
|
||
|
int Status;
|
||
|
struct sockaddr_in TftpdAddress;
|
||
|
HANDLE ThreadHandle;
|
||
|
DWORD ThreadId;
|
||
|
LPTHREAD_START_ROUTINE ThreadRoutine=NULL;
|
||
|
|
||
|
WSABUF RecvBuf[1];
|
||
|
WSAOVERLAPPED Overlap;
|
||
|
DWORD dwNumberBytes = 0;
|
||
|
DWORD dwFlags = 0;
|
||
|
int err, ok;
|
||
|
HANDLE handle_list[2];
|
||
|
DWORD handle_ready;
|
||
|
PLIST_ENTRY pEntry;
|
||
|
DWORD size;
|
||
|
|
||
|
DWORD IterCount=0;
|
||
|
SocketEntry *SE;
|
||
|
|
||
|
IterCount=0;
|
||
|
|
||
|
while(1) {
|
||
|
|
||
|
// loop while data available on this socket
|
||
|
|
||
|
{
|
||
|
|
||
|
int ret;
|
||
|
DWORD DataAvail;
|
||
|
|
||
|
ret=ioctlsocket(Sock,
|
||
|
FIONREAD,
|
||
|
&DataAvail);
|
||
|
|
||
|
if (ret != 0) {
|
||
|
DbgPrint("ioctlsocket failed %d",WSAGetLastError());
|
||
|
return 0;
|
||
|
}
|
||
|
if (DataAvail == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
err = 0;
|
||
|
|
||
|
handle_ready = 0;
|
||
|
memset( &Overlap, 0, sizeof(WSAOVERLAPPED));
|
||
|
|
||
|
EnterCriticalSection(&ReceiveLock);
|
||
|
if (!IsListEmpty(&ReceiveList)) {
|
||
|
pEntry=RemoveHeadList(&ReceiveList);
|
||
|
Request=CONTAINING_RECORD(pEntry,TFTP_REQUEST,RequestLinkage);
|
||
|
ResetEvent(Request->RcvEvent);
|
||
|
Overlap.hEvent = Request->RcvEvent;
|
||
|
} else {
|
||
|
size = sizeof(TFTP_REQUEST);
|
||
|
Request = HeapAlloc(ReceiveHeap, HEAP_ZERO_MEMORY, size);
|
||
|
if (Request == NULL) {
|
||
|
DWORD bytesReceived = 0, flags = 0;
|
||
|
// Failed to allocate REQUEST structure, perform no-op winsock recv
|
||
|
// so as to re-enable its event-signalling mechanism.
|
||
|
if (WSARecvFrom(Sock, NULL, 0, &bytesReceived, &flags,
|
||
|
NULL, NULL, NULL, NULL) == SOCKET_ERROR)
|
||
|
DbgPrint("HandleRequest: Failed to allocate REQUEST structure, "
|
||
|
"and failed to re-enable socket event.\n");
|
||
|
LeaveCriticalSection(&ReceiveLock);
|
||
|
return 0;
|
||
|
}
|
||
|
ReceiveHeapSize++;
|
||
|
Request->RcvEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
if (Request->RcvEvent == NULL) {
|
||
|
DWORD bytesReceived = 0, flags = 0;
|
||
|
HeapFree(ReceiveHeap, 0, Request);
|
||
|
ReceiveHeapSize--;
|
||
|
// Failed to create event, perform no-op winsock recv
|
||
|
// so as to re-enable its event-signalling mechanism.
|
||
|
if (WSARecvFrom(Sock, NULL, 0, &bytesReceived, &flags,
|
||
|
NULL, NULL, NULL, NULL) == SOCKET_ERROR)
|
||
|
DbgPrint("HandleRequest: Failed to allocate REQUEST structure, "
|
||
|
"and failed to re-enable socket event.\n");
|
||
|
LeaveCriticalSection(&ReceiveLock);
|
||
|
return 0;
|
||
|
}
|
||
|
Overlap.hEvent = Request->RcvEvent;
|
||
|
}
|
||
|
LeaveCriticalSection(&ReceiveLock);
|
||
|
|
||
|
ActiveReceive++;
|
||
|
|
||
|
// Zero the request packet, to streamline option negotiation code.
|
||
|
memset( Request->Packet1, 0, MAX_TFTP_DATAGRAM + 1);
|
||
|
|
||
|
RecvBuf[0].buf = (char*)&Request->Packet1;
|
||
|
RecvBuf[0].len = MAX_TFTP_DATAGRAM + 1;
|
||
|
|
||
|
PeerAddressLength = sizeof(PeerAddress);
|
||
|
|
||
|
Request->MyAddr=MyAddr;
|
||
|
|
||
|
|
||
|
Status =
|
||
|
WSARecvFrom(
|
||
|
Sock,
|
||
|
RecvBuf,
|
||
|
1,
|
||
|
&Request->DataSize,
|
||
|
&dwFlags,
|
||
|
(struct sockaddr *) &Request->ForeignAddress,
|
||
|
&PeerAddressLength,
|
||
|
&Overlap,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
DbgPrint("Received from Peer Addr: %x Port %d\n",Request->ForeignAddress.sin_addr.s_addr,
|
||
|
ntohs(Request->ForeignAddress.sin_port));
|
||
|
|
||
|
if( Status ){
|
||
|
err = WSAGetLastError();
|
||
|
|
||
|
DbgPrint("err %d\n",err);
|
||
|
|
||
|
|
||
|
if( err == WSA_IO_PENDING){
|
||
|
DbgPrint("Operation pending\n");
|
||
|
handle_list[0] = eMasterTerminate;
|
||
|
handle_list[1] = Overlap.hEvent;
|
||
|
handle_ready =
|
||
|
WaitForMultipleObjects(
|
||
|
2,
|
||
|
handle_list,
|
||
|
FALSE,
|
||
|
INFINITE
|
||
|
);
|
||
|
if( handle_ready == WAIT_FAILED ){
|
||
|
DbgPrint("TftpdMasterThread: WAIT_FAILED\n");
|
||
|
goto failed;
|
||
|
}
|
||
|
if (handle_ready == WAIT_TIMEOUT) {
|
||
|
DbgPrint("Wait timed out Socket %d\n",Sock);
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( handle_ready == WAIT_OBJECT_0 ){
|
||
|
goto terminated;
|
||
|
} else {
|
||
|
ok =
|
||
|
WSAGetOverlappedResult(
|
||
|
Sock, // SOCKET s,
|
||
|
&Overlap, // LPWSAOVERLAPPED lpOverlapped,
|
||
|
&Request->DataSize, // LPDWORD lpcbTransfer,
|
||
|
FALSE, // BOOL fWait,
|
||
|
&dwFlags // LPDWORD lpdwFlags
|
||
|
);
|
||
|
if( ! ok ){
|
||
|
DbgPrint("WSAGetOverlappedResult failed=%d",
|
||
|
WSAGetLastError() );
|
||
|
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
}else{
|
||
|
DbgPrint("TftpdMasterThread: WSARecvFrom failed=%d.\n", err );
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
IterCount++;
|
||
|
|
||
|
if( WaitForSingleObject( eMasterTerminate, 0 ) == WAIT_OBJECT_0 ){
|
||
|
goto terminated;
|
||
|
}
|
||
|
|
||
|
Status = Request->DataSize; // winsock1 <= winsock2.
|
||
|
|
||
|
if( Status >= 2 ){
|
||
|
//
|
||
|
// Process the packet
|
||
|
//
|
||
|
|
||
|
if (MyAddr != 0) {
|
||
|
|
||
|
// New request
|
||
|
|
||
|
ThreadRoutine=NULL;
|
||
|
Opcode = *((unsigned short *) &Request->Packet1[0]);
|
||
|
Opcode = htons(Opcode);
|
||
|
|
||
|
switch(Opcode) {
|
||
|
|
||
|
case TFTPD_RRQ:
|
||
|
case TFTPD_WRQ:
|
||
|
#if defined(REMOTE_BOOT_SECURITY)
|
||
|
case TFTPD_LOGIN:
|
||
|
case TFTPD_KEY:
|
||
|
#endif // defined(REMOTE_BOOT_SECURITY)
|
||
|
|
||
|
if ( Opcode == TFTPD_RRQ ) {
|
||
|
|
||
|
tftpd_stat.req_read++;
|
||
|
ThreadRoutine = TftpdHandleRead;
|
||
|
|
||
|
} else if ( Opcode == TFTPD_WRQ ) {
|
||
|
|
||
|
tftpd_stat.req_write++;
|
||
|
ThreadRoutine = TftpdHandleWrite;
|
||
|
|
||
|
}
|
||
|
#if defined(REMOTE_BOOT_SECURITY)
|
||
|
else if ( Opcode == TFTPD_LOGIN ) {
|
||
|
|
||
|
tftpd_stat.req_login++;
|
||
|
ThreadRoutine = TftpdHandleLogin;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
tftpd_stat.req_key++;
|
||
|
ThreadRoutine = TftpdHandleKey;
|
||
|
|
||
|
}
|
||
|
#endif //defined(REMOTE_BOOT_SECURITY)
|
||
|
|
||
|
Request->TftpdPort = Sock;
|
||
|
|
||
|
DbgPrint("Posting work");
|
||
|
|
||
|
if (ThreadRoutine) {
|
||
|
(*ThreadRoutine)(Request);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case TFTPD_ERROR:
|
||
|
break;
|
||
|
|
||
|
case TFTPD_ACK:
|
||
|
case TFTPD_DATA:
|
||
|
case TFTPD_OACK:
|
||
|
|
||
|
default:
|
||
|
DbgPrint("TftpdMasterThread: invalid TFTPD_(%d).\n", Opcode );
|
||
|
|
||
|
tftpd_stat.req_error++;
|
||
|
|
||
|
TftpdErrorPacket(
|
||
|
(struct sockaddr *) &PeerAddress,
|
||
|
RequestPacket,
|
||
|
Sock,
|
||
|
TFTPD_ERROR_ILLEGAL_OPERATION,
|
||
|
NULL);
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
// Continue serving existing request
|
||
|
Request->TftpdPort=Sock;
|
||
|
|
||
|
TftpdResumeProcessing(Request);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
cleanup:
|
||
|
EnterCriticalSection(&ReceiveLock);
|
||
|
pEntry=&Request->RequestLinkage;
|
||
|
InsertHeadList(&ReceiveList,pEntry);
|
||
|
InterlockedIncrement(&AvailableThreads);
|
||
|
ActiveReceive--;
|
||
|
LeaveCriticalSection(&ReceiveLock);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
terminated:
|
||
|
DbgPrint("TftpdReceive: exiting\n");
|
||
|
|
||
|
|
||
|
|
||
|
failed:
|
||
|
EnterCriticalSection(&ReceiveLock);
|
||
|
pEntry=&Request->RequestLinkage;
|
||
|
InsertHeadList(&ReceiveList,pEntry);
|
||
|
InterlockedIncrement(&AvailableThreads);
|
||
|
ActiveReceive--;
|
||
|
LeaveCriticalSection(&ReceiveLock);
|
||
|
|
||
|
return (0);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
// ========================================================================
|
||
|
|
||
|
DWORD
|
||
|
TftpdNewReceive(
|
||
|
PVOID Argument,
|
||
|
BYTE Flags
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This handles all incoming requests and dispatches them to appropriate
|
||
|
worker threads.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Argument - not used
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Exit status
|
||
|
0 == success
|
||
|
1 == failure, stop service and exit if severe error.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
IPAddr MyAddr=0;
|
||
|
DWORD IterCount=0;
|
||
|
BOOL LockHeld=FALSE;
|
||
|
SocketEntry *SE;
|
||
|
|
||
|
DbgPrint("TftpdReceive: entered. Socket %d\n", (DWORD)((DWORD_PTR)Argument));
|
||
|
|
||
|
LockHeld=TryEnterCriticalSection(&SocketLock);
|
||
|
while(IterCount < MAX_LOCK_TRIES && !LockHeld) {
|
||
|
//Potential for deadlock on interface change if this socket is being deleted.
|
||
|
// Break the deadlock by not waiting forever
|
||
|
Sleep(200);
|
||
|
LockHeld=TryEnterCriticalSection(&SocketLock);
|
||
|
IterCount++;
|
||
|
}
|
||
|
if (!LockHeld) {
|
||
|
DbgPrint("TftpdReceived: SocketLock held. Dropping packet");
|
||
|
return 0;
|
||
|
}
|
||
|
if (LookupSocketEntryBySock((SOCKET)Argument,&SE) == ERROR_SUCCESS) {
|
||
|
MyAddr=SE->IPAddress;
|
||
|
DbgPrint("TftpdReceive: Found Addr in SocketList %x\n",MyAddr);
|
||
|
}
|
||
|
LeaveCriticalSection(&SocketLock);
|
||
|
|
||
|
HandleRequest((SOCKET)Argument,MyAddr);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
TftpdContinueReceive(
|
||
|
PVOID Argument,
|
||
|
BYTE Flags
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This handles all incoming requests and dispatches them to appropriate
|
||
|
worker threads.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Argument - not used
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Exit status
|
||
|
0 == success
|
||
|
1 == failure, stop service and exit if severe error.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
|
||
|
DbgPrint("TftpdReceive: entered. Socket %d\n", (DWORD)((DWORD_PTR)Argument));
|
||
|
|
||
|
HandleRequest((SOCKET)Argument,0);
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
// ========================================================================
|
||
|
|
||
|
VOID
|
||
|
TftpdControlHandler(
|
||
|
DWORD Opcode)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This does a write with the appropriate conversions for netascii or octet
|
||
|
modes.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
WriteFd - file to read from
|
||
|
Buffer - Buffer to read into
|
||
|
BufferSize - size of buffer
|
||
|
WriteMode - O_TEXT or O_BINARY
|
||
|
O_TEXT means the netascii conversions must be done
|
||
|
O_BINARY means octet mode
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BytesWritten
|
||
|
Error?
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
int ok;
|
||
|
time_t current_time;
|
||
|
|
||
|
time( ¤t_time );
|
||
|
|
||
|
TftpdServiceStatus.dwCheckPoint++;
|
||
|
|
||
|
|
||
|
DbgPrint("TftpdControlHandler(%d) dwCheckPoint=%d, at %s.\n",
|
||
|
Opcode,
|
||
|
TftpdServiceStatus.dwCheckPoint,
|
||
|
ctime( ¤t_time ) );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
switch (Opcode) {
|
||
|
|
||
|
case SERVICE_CONTROL_PAUSE:
|
||
|
|
||
|
// Actually can we pause listening?
|
||
|
|
||
|
DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_PAUSE\n");
|
||
|
SuspendThread(MasterThreadHandle);
|
||
|
TftpdServiceStatus.dwCurrentState = SERVICE_PAUSED;
|
||
|
break;
|
||
|
|
||
|
case SERVICE_CONTROL_CONTINUE:
|
||
|
|
||
|
DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_CONTINUE\n");
|
||
|
ResumeThread(MasterThreadHandle);
|
||
|
TftpdServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
||
|
break;
|
||
|
|
||
|
case SERVICE_CONTROL_STOP:
|
||
|
case SERVICE_CONTROL_SHUTDOWN:
|
||
|
|
||
|
DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_STOP/SHUTDOWN\n");
|
||
|
|
||
|
// TerminateThread(MasterThreadHandle, 0);
|
||
|
|
||
|
TftpdServiceExit(NO_ERROR);
|
||
|
goto done;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SERVICE_CONTROL_INTERROGATE:
|
||
|
DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_INTERROGATE\n");
|
||
|
|
||
|
DbgPrint( " req_read=%d, req_write=%d, req_error=%d",
|
||
|
tftpd_stat.req_read,
|
||
|
tftpd_stat.req_write,
|
||
|
tftpd_stat.req_error);
|
||
|
|
||
|
#if defined(REMOTE_BOOT_SECURITY)
|
||
|
|
||
|
DbgPrint("req_login=%d, req_key=%d.\n",
|
||
|
tftpd_stat.req_login,
|
||
|
tftpd_stat.req_key );
|
||
|
#endif //defined(REMOTE_BOOT_SECURITY)
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_(%d)?\n", Opcode );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/** Send a status response **/
|
||
|
|
||
|
ok =
|
||
|
SetServiceStatus( TftpdServiceStatusHandle,
|
||
|
&TftpdServiceStatus );
|
||
|
|
||
|
if( !ok ){
|
||
|
DbgPrint("TftpdControlHandler: SetServiceStatus failed=%d\n",
|
||
|
GetLastError() );
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// ========================================================================
|
||
|
//
|
||
|
// Announce that we're going down, clean up, stop master thread.
|
||
|
//
|
||
|
|
||
|
VOID
|
||
|
TftpdServiceExit(
|
||
|
IN ULONG Error
|
||
|
)
|
||
|
{
|
||
|
int ok;
|
||
|
|
||
|
DbgPrint("TftpdServiceExit(%d)\n", Error );
|
||
|
|
||
|
|
||
|
// ====================
|
||
|
// stage one, stop pending, signal master thread.
|
||
|
// ====================
|
||
|
|
||
|
TftpdServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||
|
|
||
|
ok =
|
||
|
SetServiceStatus( TftpdServiceStatusHandle,
|
||
|
&TftpdServiceStatus );
|
||
|
|
||
|
if( !ok ){
|
||
|
DbgPrint("TftpdServiceExit: SetServiceStatus failed=%d\n",
|
||
|
GetLastError() );
|
||
|
}
|
||
|
|
||
|
|
||
|
SetEvent( eMasterTerminate );
|
||
|
|
||
|
|
||
|
// ====================
|
||
|
// stage two, stop.
|
||
|
// ====================
|
||
|
|
||
|
|
||
|
TftpdServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||
|
TftpdServiceStatus.dwCheckPoint = 0;
|
||
|
TftpdServiceStatus.dwWaitHint = 0;
|
||
|
|
||
|
SET_SERVICE_EXITCODE(
|
||
|
Error,
|
||
|
TftpdServiceStatus.dwWin32ExitCode,
|
||
|
TftpdServiceStatus.dwServiceSpecificExitCode
|
||
|
);
|
||
|
|
||
|
ok =
|
||
|
SetServiceStatus( TftpdServiceStatusHandle,
|
||
|
&TftpdServiceStatus );
|
||
|
|
||
|
if( !ok ){
|
||
|
DbgPrint("TftpdServiceExit: SetServiceStatus failed=%d\n",
|
||
|
GetLastError() );
|
||
|
}
|
||
|
|
||
|
// ====================
|
||
|
// stage three, cleanup.
|
||
|
// ====================
|
||
|
|
||
|
|
||
|
if( eSock ){
|
||
|
CloseHandle( eSock );
|
||
|
eSock = NULL;
|
||
|
}
|
||
|
|
||
|
if( eMasterTerminate ){
|
||
|
CloseHandle( eMasterTerminate );
|
||
|
eMasterTerminate = NULL;
|
||
|
}
|
||
|
|
||
|
if( LogFile ){
|
||
|
fclose( LogFile );
|
||
|
LogFile = NULL;
|
||
|
}
|
||
|
|
||
|
#if defined(REMOTE_BOOT_SECURITY)
|
||
|
TftpdUninitSecurityArray();
|
||
|
#endif //defined(REMOTE_BOOT_SECURITY)
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Remove Entry from list, and free it. SocketLock must be held by caller.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
VOID DeleteSocketEntry(SocketEntry *SE)
|
||
|
{
|
||
|
|
||
|
|
||
|
DbgPrint("Deregister wait %x",SE->WaitHandle);
|
||
|
|
||
|
RtlDeregisterWaitEx(SE->WaitHandle,INVALID_HANDLE_VALUE);
|
||
|
closesocket(SE->Sock);
|
||
|
WSACloseEvent(SE->WaitEvent);
|
||
|
|
||
|
|
||
|
if (SE->Linkage.Flink == SE->Linkage.Blink) {
|
||
|
// single entry case
|
||
|
RemoveHeadList(&SocketList);
|
||
|
} else {
|
||
|
|
||
|
SE->Linkage.Blink->Flink=SE->Linkage.Flink;
|
||
|
SE->Linkage.Flink->Blink=SE->Linkage.Blink;
|
||
|
}
|
||
|
|
||
|
DbgPrint("removing socket to %x",SE->IPAddress);
|
||
|
|
||
|
free(SE);
|
||
|
|
||
|
|
||
|
}
|
||
|
DWORD GetIpTable(PMIB_IPADDRTABLE *AddrTable)
|
||
|
{
|
||
|
|
||
|
|
||
|
HRESULT hr;
|
||
|
|
||
|
DWORD IpAddrTableSize=0;
|
||
|
PMIB_IPADDRTABLE IpAddrTable=NULL, TmpAddrTable=NULL;
|
||
|
DWORD ReturnValue=STATUS_NO_MEMORY;
|
||
|
|
||
|
*AddrTable=NULL;
|
||
|
|
||
|
hr=GetIpAddrTable(NULL,
|
||
|
&IpAddrTableSize,
|
||
|
FALSE);
|
||
|
|
||
|
if (hr != ERROR_SUCCESS && hr != ERROR_INSUFFICIENT_BUFFER) {
|
||
|
DbgPrint("GetIpAddrTable failed with %x",hr);
|
||
|
goto ret;
|
||
|
}
|
||
|
|
||
|
IpAddrTable=(PMIB_IPADDRTABLE)malloc(IpAddrTableSize);
|
||
|
|
||
|
if (IpAddrTable == NULL) {
|
||
|
goto ret;
|
||
|
}
|
||
|
|
||
|
while (1) {
|
||
|
|
||
|
hr=GetIpAddrTable(IpAddrTable,
|
||
|
&IpAddrTableSize,
|
||
|
FALSE);
|
||
|
|
||
|
|
||
|
if (hr == ERROR_SUCCESS) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (hr == ERROR_INSUFFICIENT_BUFFER) {
|
||
|
TmpAddrTable=realloc(IpAddrTable,IpAddrTableSize);
|
||
|
if (TmpAddrTable == NULL) {
|
||
|
free(IpAddrTable);
|
||
|
goto ret;
|
||
|
} else {
|
||
|
IpAddrTable = TmpAddrTable;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
DbgPrint("GetIpAddrTable failed with %x",hr);
|
||
|
goto ret;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
ReturnValue=ERROR_SUCCESS;
|
||
|
*AddrTable=IpAddrTable;
|
||
|
|
||
|
|
||
|
ret:
|
||
|
|
||
|
return ReturnValue;
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID inet_ntoa_copy(struct in_addr IP, BYTE* IPString)
|
||
|
{
|
||
|
|
||
|
BYTE *Tmp;
|
||
|
|
||
|
Tmp=inet_ntoa(IP);
|
||
|
if (Tmp) {
|
||
|
strcpy(IPString,Tmp);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Addr : IP Addr to bind socket to.
|
||
|
|
||
|
Return: NULL of failure. Pointer to SocketEntry that is added to SocketList on success.
|
||
|
SocketLock must be held by caller.
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
SocketEntry* AddSocket(IPAddr Addr)
|
||
|
{
|
||
|
|
||
|
struct sockaddr_in TftpdAddress;
|
||
|
SOCKET Sock;
|
||
|
HANDLE SocketEvent;
|
||
|
SocketEntry *SE;
|
||
|
struct servent * serventry;
|
||
|
|
||
|
|
||
|
int Count=0;
|
||
|
DWORD ErrStatus;
|
||
|
|
||
|
do {
|
||
|
|
||
|
Sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||
|
|
||
|
if (Sock != INVALID_SOCKET) {
|
||
|
memset(&TftpdAddress, 0, sizeof(struct sockaddr_in));
|
||
|
serventry = getservbyname("tftp", "udp");
|
||
|
|
||
|
if (serventry == NULL) {
|
||
|
DbgPrint("TftpdStart: getservbyname cannot find tftp port.\n");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
TftpdAddress.sin_family = AF_INET;
|
||
|
TftpdAddress.sin_port = serventry->s_port;
|
||
|
TftpdAddress.sin_addr.s_addr = Addr;
|
||
|
|
||
|
if (bind(Sock, (struct sockaddr *)&TftpdAddress, sizeof(struct sockaddr_in))) {
|
||
|
DWORD err=GetLastError();
|
||
|
DbgPrint("Bind failed %d\n",err);
|
||
|
return NULL;
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ErrStatus=WSAGetLastError();
|
||
|
DbgPrint("WSASocket failed with %d\n",ErrStatus);
|
||
|
Sleep(750);
|
||
|
Count++;
|
||
|
} while (Count < MAX_SOCKET_INIT_ATTEMPT);
|
||
|
|
||
|
if (Sock == INVALID_SOCKET) {
|
||
|
DbgPrint("Failed to create socket for addr %x\n",Addr);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
SE=(SocketEntry*)malloc(sizeof(SocketEntry));
|
||
|
if (!SE) {
|
||
|
goto out;
|
||
|
} else {
|
||
|
BYTE TmpAddr[20];
|
||
|
|
||
|
memset(SE,0,sizeof(SocketEntry));
|
||
|
|
||
|
|
||
|
SE->Sock=Sock;
|
||
|
SE->IPAddress=Addr;
|
||
|
|
||
|
inet_ntoa_copy(*((struct in_addr*)&Addr),TmpAddr);
|
||
|
|
||
|
SocketEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
|
||
|
if (SocketEvent == NULL) {
|
||
|
goto out;
|
||
|
}
|
||
|
SE->WaitEvent=SocketEvent;
|
||
|
SE->WaitHandle=RegisterSocket(Sock,SocketEvent,REG_NEW_SOCKET);
|
||
|
if (SE->WaitHandle == 0) {
|
||
|
goto out;
|
||
|
}
|
||
|
InsertHeadList(&SocketList,&SE->Linkage);
|
||
|
DbgPrint("Adding socket: %d addr: %s\n",Sock,TmpAddr);
|
||
|
|
||
|
}
|
||
|
|
||
|
return SE;
|
||
|
out:
|
||
|
closesocket(Sock);
|
||
|
if (SocketEvent) {
|
||
|
CloseHandle(SocketEvent);
|
||
|
}
|
||
|
if (SE) {
|
||
|
free(SE);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*++
|
||
|
Walk through SocketList, deleting unreferenced entries, cleaning reference on referenced entries.
|
||
|
SocketList lock must be held by caller.
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
DWORD CleanSocketList()
|
||
|
{
|
||
|
PLIST_ENTRY pEntry, pNextEntry;
|
||
|
SocketEntry *SE;
|
||
|
BOOL DeletedEntry=FALSE;
|
||
|
|
||
|
pEntry = SocketList.Flink;
|
||
|
|
||
|
while ( pEntry != &SocketList) {
|
||
|
|
||
|
SE = CONTAINING_RECORD(pEntry,
|
||
|
SocketEntry,
|
||
|
Linkage);
|
||
|
|
||
|
pNextEntry=pEntry->Flink;
|
||
|
|
||
|
if (!SE->Referenced) {
|
||
|
DeleteSocketEntry(SE);
|
||
|
DeletedEntry=TRUE;
|
||
|
} else {
|
||
|
SE->Referenced = FALSE;
|
||
|
}
|
||
|
pEntry=pNextEntry;
|
||
|
|
||
|
}
|
||
|
|
||
|
return DeletedEntry;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
SocketLock must be held by caller.
|
||
|
|
||
|
Returns: S_OK if found. SE contains pointer.
|
||
|
S_FALSE if failed. SE contains NULL.
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
DWORD LookupInterfaceEntry(IPAddr Addr,SocketEntry **SE)
|
||
|
{
|
||
|
|
||
|
PLIST_ENTRY pEntry;
|
||
|
SocketEntry *LocalSE;
|
||
|
|
||
|
*SE=NULL;
|
||
|
|
||
|
for ( pEntry = SocketList.Flink;
|
||
|
pEntry != &SocketList;
|
||
|
pEntry = pEntry->Flink) {
|
||
|
|
||
|
LocalSE = CONTAINING_RECORD(pEntry,
|
||
|
SocketEntry,
|
||
|
Linkage);
|
||
|
|
||
|
if (LocalSE->IPAddress == Addr) {
|
||
|
*SE=LocalSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
|
||
|
}
|
||
|
/*++
|
||
|
|
||
|
SocketLock must be held by caller.
|
||
|
|
||
|
Returns: S_OK if found. SE contains pointer.
|
||
|
S_FALSE if failed. SE contains NULL.
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
DWORD LookupSocketEntryBySock(SOCKET Sock, SocketEntry **SE)
|
||
|
{
|
||
|
|
||
|
PLIST_ENTRY pEntry;
|
||
|
SocketEntry *LocalSE;
|
||
|
|
||
|
*SE=NULL;
|
||
|
|
||
|
for ( pEntry = SocketList.Flink;
|
||
|
pEntry != &SocketList;
|
||
|
pEntry = pEntry->Flink) {
|
||
|
|
||
|
LocalSE = CONTAINING_RECORD(pEntry,
|
||
|
SocketEntry,
|
||
|
Linkage);
|
||
|
|
||
|
if (LocalSE->Sock == Sock) {
|
||
|
*SE=LocalSE;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (*SE) {
|
||
|
return ERROR_SUCCESS;
|
||
|
} else {
|
||
|
return ERROR_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Handle interface change event signalled by PA.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
VOID NTAPI InterfaceChange(PVOID Context, BOOLEAN Flag)
|
||
|
{
|
||
|
|
||
|
|
||
|
PMIB_IPADDRTABLE IpAddrTable;
|
||
|
DWORD i;
|
||
|
DWORD EntryCount=0;
|
||
|
|
||
|
PLIST_ENTRY pEntry,pNextEntry;
|
||
|
SocketEntry *SE,*NewSE;
|
||
|
int Count=0;
|
||
|
DWORD ErrStatus;
|
||
|
DWORD Added=FALSE, Removed=FALSE;
|
||
|
IPAddr IPAddress;
|
||
|
|
||
|
DbgPrint("InterfaceChange\n");
|
||
|
|
||
|
EnterCriticalSection(&SocketLock);
|
||
|
|
||
|
|
||
|
if (GetIpTable(&IpAddrTable) == ERROR_SUCCESS) {
|
||
|
|
||
|
for (i=0; i < IpAddrTable->dwNumEntries; i++) {
|
||
|
|
||
|
if ( (IpAddrTable->table[i].dwAddr != 0) &&
|
||
|
(IpAddrTable->table[i].dwAddr != LOOPBACK)) {
|
||
|
|
||
|
if (LookupInterfaceEntry(IpAddrTable->table[i].dwAddr,&SE)) {
|
||
|
SE->Referenced=TRUE;
|
||
|
} else {
|
||
|
Added=TRUE;
|
||
|
SE=AddSocket(IpAddrTable->table[i].dwAddr);
|
||
|
if (SE) {
|
||
|
SE->Referenced=TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
DbgPrint("Referenced Socket %x\n",IpAddrTable->table[i].dwAddr);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
Removed=CleanSocketList();
|
||
|
free(IpAddrTable);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!Added && !Removed) {
|
||
|
// Got a notify, and didn't detect any differences. Could be a race condition, and
|
||
|
// addr got removed, and readded before we could process the notify
|
||
|
// Reopen all sockets to handle this case
|
||
|
|
||
|
pEntry=SocketList.Flink;
|
||
|
while (pEntry != &SocketList) {
|
||
|
|
||
|
|
||
|
SE = CONTAINING_RECORD(pEntry,
|
||
|
SocketEntry,
|
||
|
Linkage);
|
||
|
|
||
|
pNextEntry=pEntry->Flink;
|
||
|
|
||
|
if (!(SE->Flags & SE_SOCKET_REOPEN)) {
|
||
|
IPAddress=SE->IPAddress;
|
||
|
DeleteSocketEntry(SE);
|
||
|
NewSE=AddSocket(IPAddress);
|
||
|
if (NewSE) {
|
||
|
NewSE->Flags |= SE_SOCKET_REOPEN;
|
||
|
}
|
||
|
}
|
||
|
pEntry=pNextEntry;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
ErrStatus = NotifyAddrChange(&AddrChangeHandle,&AddrChangeOverlapped);
|
||
|
if (ErrStatus != ERROR_SUCCESS && ErrStatus != ERROR_IO_PENDING) {
|
||
|
DbgPrint("NotifyAddrChange failed %d",ErrStatus);
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&SocketLock);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// ========================================================================
|
||
|
// EOF.
|
||
|
|
||
|
|
||
|
|