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.
 
 
 
 
 
 

624 lines
23 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
service.c
Abstract:
This module contains functions to implement TFTP
as a NT system service. It contains the startup
and cleanup code for the service.
Author:
Jeffrey C. Venable, Sr. (jeffv) 01-Jun-2001
Revision History:
--*/
#include "precomp.h"
#include <winsvc.h>
TFTPD_GLOBALS globals;
void
TftpdServiceCleanup() {
NTSTATUS status;
UINT x;
TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceCleanup().\n"));
// Destroy the hash table.
ASSERT(globals.hash.numEntries == 0);
if (globals.initialized.contextHashTable) {
for (x = 0; x < globals.parameters.hashEntries; x++)
DeleteCriticalSection(&globals.hash.table[x].cs);
HeapFree(globals.hServiceHeap, 0, globals.hash.table);
globals.hash.table = NULL;
globals.initialized.contextHashTable = FALSE;
}
// Destroy the timeout timer queue.
if (!DeleteTimerQueueEx(globals.io.hTimerQueue, INVALID_HANDLE_VALUE)) {
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"TftpdServiceCleanup(): DeleteTimerQueueEx() failed, "
"error = 0x%08X.\n",
GetLastError()));
}
globals.io.hTimerQueue = NULL;
// Undo initialized things.
if (globals.initialized.ioCS) {
DeleteCriticalSection(&globals.io.cs);
globals.initialized.ioCS = FALSE;
}
if (globals.initialized.reaperContextCS) {
DeleteCriticalSection(&globals.reaper.contextCS);
globals.initialized.reaperContextCS = FALSE;
}
if (globals.initialized.reaperSocketCS) {
DeleteCriticalSection(&globals.reaper.socketCS);
globals.initialized.reaperSocketCS = FALSE;
}
if (globals.initialized.winsock) {
WSACleanup();
globals.initialized.winsock = FALSE;
}
// Clean up the service heap.
if (globals.hServiceHeap != GetProcessHeap()) {
// We used a private heap, destroying it will automatically deallocate
// everything we used, including connections.
HeapDestroy(globals.hServiceHeap);
} else {
// We had to use the process heap instead of a private heap,
// cleanup remaining allocations.
} // if (globals.hServiceHeap != GetProcessHeap())
globals.hServiceHeap = NULL;
// Unregister event-logging if necessary.
if (globals.service.hEventLogSource != NULL)
DeregisterEventSource(globals.service.hEventLogSource), globals.service.hEventLogSource = NULL;
// Notify the service control manager that we've stopped.
globals.service.status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(globals.service.hStatus, &globals.service.status);
if (globals.reaper.numLeakedContexts) {
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"TftpdServiceCleanup(): Leaked %d contexts.\n",
globals.reaper.numLeakedContexts));
}
if (globals.reaper.numLeakedSockets) {
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"TftpdServiceCleanup(): Leaked %d sockets.\n",
globals.reaper.numLeakedSockets));
}
TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceCleanup(): Service stopped.\n"));
} // TftpdServiceCleanup()
void
TftpdServiceAttemptCleanup() {
TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceAttemptCleanup().\n"));
InterlockedIncrement((PLONG)&globals.service.status.dwCheckPoint);
SetServiceStatus(globals.service.hStatus, &globals.service.status);
if ((globals.io.numBuffers != -1) || (globals.io.numContexts != -1))
return;
if (InterlockedCompareExchange((PLONG)&globals.service.shutdown, 2, 1) != 1)
return;
TftpdServiceCleanup();
} // TftpdServiceAttemptCleanup()
void
TftpdShutdownService() {
UINT x;
TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdShutdownService().\n"));
ASSERT(globals.service.status.dwCurrentState != SERVICE_STOPPED);
// Set the shutdown flag.
InterlockedExchange((PLONG)&globals.service.shutdown, 1);
// Notify the service control manager that we're going to stop.
globals.service.status.dwCurrentState = SERVICE_STOP_PENDING;
globals.service.status.dwWaitHint = 5000;
globals.service.status.dwCheckPoint = 0;
SetServiceStatus(globals.service.hStatus, &globals.service.status);
// Close the sockets.
EnterCriticalSection(&globals.io.cs); {
if (globals.io.master.s != INVALID_SOCKET)
TftpdIoDestroySocketContext(&globals.io.master);
if (globals.io.def.s != INVALID_SOCKET)
TftpdIoDestroySocketContext(&globals.io.def);
if (globals.io.mtu.s != INVALID_SOCKET)
TftpdIoDestroySocketContext(&globals.io.mtu);
if (globals.io.max.s != INVALID_SOCKET)
TftpdIoDestroySocketContext(&globals.io.max);
} LeaveCriticalSection(&globals.io.cs);
// Empty out all the contexts from the hash table.
for (x = 0; x < globals.parameters.hashEntries; x++) {
EnterCriticalSection(&globals.hash.table[x].cs); {
while (globals.hash.table[x].bucket.Flink != &globals.hash.table[x].bucket)
TftpdContextKill(CONTAINING_RECORD(globals.hash.table[x].bucket.Flink,
TFTPD_CONTEXT, linkage));
} LeaveCriticalSection(&globals.hash.table[x].cs);
} // for (unsigned int x = 0; x < globals.parameters.hashEntries; x++)
// Empty out all the contexts from the leak list.
EnterCriticalSection(&globals.reaper.contextCS); {
PLIST_ENTRY entry;
while ((entry = RemoveHeadList(&globals.reaper.leakedContexts)) !=
&globals.reaper.leakedContexts) {
PTFTPD_CONTEXT context = CONTAINING_RECORD(entry, TFTPD_CONTEXT, linkage);
if (!TftpdContextFree(context)) {
// Free the reference from it having been on the leak list.
TftpdContextRelease(context);
}
globals.reaper.numLeakedContexts--;
}
} LeaveCriticalSection(&globals.reaper.contextCS);
// Empty out all the sockets from the leak list.
EnterCriticalSection(&globals.reaper.socketCS); {
PLIST_ENTRY entry;
while ((entry = RemoveHeadList(&globals.reaper.leakedSockets)) !=
&globals.reaper.leakedSockets) {
TftpdIoDestroySocketContext(CONTAINING_RECORD(entry, TFTPD_SOCKET, linkage));
globals.reaper.numLeakedSockets--;
}
} LeaveCriticalSection(&globals.reaper.socketCS);
InterlockedDecrement(&globals.io.numBuffers);
InterlockedDecrement(&globals.io.numContexts);
TftpdServiceAttemptCleanup();
} // TftpdShutdownService()
BOOL
TftpdSetStartDirectory(char *path) {
char expanded[MAX_PATH];
int length;
if (path == NULL)
path = "\\tftpdroot";
// Expand the string and leave room to insert a trailing '\\'.
if ((length = ExpandEnvironmentStrings(path, expanded, sizeof(expanded) - 1)) == 0)
return (FALSE);
CopyMemory(globals.parameters.rootDirectory, expanded, length);
if ((globals.parameters.rootDirectory[length - 1] != '\\') && (length < MAX_PATH))
strcat(globals.parameters.rootDirectory, "\\");
return (TRUE);
} // TftpdSetStartDirectory()
void
TftpdReadRegistryParameters() {
DWORD keyType, valueSize;
char path[MAX_PATH];
HKEY parameters = NULL;
// Open the registry key which contains all the adjustable parameters
// to the service. We will register for notification on it later incase
// anything changes while we're running.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\Tftpd\\Parameters",
0, KEY_QUERY_VALUE, &parameters) != ERROR_SUCCESS)
parameters = NULL;
if (parameters == NULL) {
globals.parameters.hashEntries = 256;
globals.parameters.lowWaterMark = 5;
globals.parameters.highWaterMark = 256;
globals.parameters.maxRetries = 10;
TftpdSetStartDirectory(NULL);
strcpy(globals.parameters.validClients, "*.*.*.*");
strcpy(globals.parameters.validReadFiles, "*");
strcpy(globals.parameters.validMasters, "*.*.*.*");
strcpy(globals.parameters.validWriteFiles, "*");
return;
}
#if (DBG)
// Initialize debug settings (if applicable) :
keyType = 0;
valueSize = sizeof(globals.parameters.debugFlags);
if ((RegQueryValueEx(parameters, "DebugFlags", NULL, &keyType,
(LPBYTE)&globals.parameters.debugFlags, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_DWORD)) {
globals.parameters.debugFlags = 0x00000000;
}
#endif // (DBG)
keyType = 0;
valueSize = sizeof(globals.parameters.hashEntries);
if ((RegQueryValueEx(parameters, "HashEntries", NULL, &keyType,
(LPBYTE)&globals.parameters.hashEntries, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_DWORD) || !globals.parameters.hashEntries)
globals.parameters.hashEntries = 256;
if (globals.parameters.hashEntries < 1)
globals.parameters.hashEntries = 1;
keyType = 0;
valueSize = sizeof(globals.parameters.lowWaterMark);
if ((RegQueryValueEx(parameters, "LowWaterMark", NULL, &keyType,
(LPBYTE)&globals.parameters.lowWaterMark, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_DWORD) || !globals.parameters.lowWaterMark)
globals.parameters.lowWaterMark = 5;
keyType = 0;
valueSize = sizeof(globals.parameters.highWaterMark);
if ((RegQueryValueEx(parameters, "HighWaterMark", NULL, &keyType,
(LPBYTE)&globals.parameters.highWaterMark, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_DWORD) || !globals.parameters.highWaterMark)
globals.parameters.highWaterMark = 256;
keyType = 0;
valueSize = sizeof(globals.parameters.maxRetries);
if ((RegQueryValueEx(parameters, "MaxRetries", NULL, &keyType,
(LPBYTE)&globals.parameters.maxRetries, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_DWORD) || !globals.parameters.maxRetries)
globals.parameters.maxRetries = 4;
path[0] = '\0';
keyType = 0;
valueSize = sizeof(globals.parameters.rootDirectory);
if ((RegQueryValueEx(parameters, "directory", NULL, &keyType, (LPBYTE)path, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_SZ))
TftpdSetStartDirectory(NULL);
else
TftpdSetStartDirectory(path);
keyType = 0;
valueSize = sizeof(globals.parameters.validClients);
if ((RegQueryValueEx(parameters, "clients", NULL, &keyType,
(LPBYTE)&globals.parameters.validClients, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_SZ))
strcpy(globals.parameters.validClients, "*.*.*.*");
keyType = 0;
valueSize = sizeof(globals.parameters.validReadFiles);
if ((RegQueryValueEx(parameters, "readable", NULL, &keyType,
(LPBYTE)&globals.parameters.validReadFiles, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_SZ))
strcpy(globals.parameters.validReadFiles, "*");
keyType = 0;
valueSize = sizeof(globals.parameters.validMasters);
if ((RegQueryValueEx(parameters, "masters", NULL, &keyType,
(LPBYTE)&globals.parameters.validMasters, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_SZ))
strcpy(globals.parameters.validMasters, "*.*.*.*");
keyType = 0;
valueSize = sizeof(globals.parameters.validWriteFiles);
if ((RegQueryValueEx(parameters, "writable", NULL, &keyType,
(LPBYTE)&globals.parameters.validWriteFiles, &valueSize) != ERROR_SUCCESS) ||
(keyType != REG_SZ))
strcpy(globals.parameters.validWriteFiles, "*");
RegCloseKey(parameters);
} // TftpdReadRegistryParameters()
void WINAPI
TftpdServiceHandler(
DWORD dwOpcode
);
void WINAPI
TftpdServiceMain(DWORD argc, PWSTR argv[]) {
WSADATA wsaData;
PSERVENT servent;
SOCKADDR_IN addr;
NTSTATUS status;
UINT x;
TftpdReadRegistryParameters();
// Register the service control handler.
if ((globals.service.hStatus = RegisterServiceCtrlHandler(TEXT("Tftpd"), TftpdServiceHandler)) == 0) {
globals.service.status.dwWin32ExitCode = GetLastError();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"ServiceMain: RegisterServiceCtrlHandler() failed, error 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
// Immediately report that we are beginning to start up.
globals.service.status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
globals.service.status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
globals.service.status.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(globals.service.hStatus, &globals.service.status);
TFTPD_DEBUG((TFTPD_TRACE_SERVICE,
"\nTftpdServiceMain: Parameters...\n"
"\tDebug flags : 0x%08X\n"
"\tHash table size : %d buckets\n"
"\tBuffer low water-mark : %d buffers\n"
"\tBuffer high water-mark : %d buffers\n"
"\tMax retries : %d attempts\n"
"\tRoot directory : %s\n"
"\tValid client mask : %s\n"
"\tValid read file mask : %s\n"
"\tValid master mask : %s\n"
"\tValid write file mask : %s\n",
globals.parameters.debugFlags,
globals.parameters.hashEntries,
globals.parameters.lowWaterMark,
globals.parameters.highWaterMark,
globals.parameters.maxRetries,
globals.parameters.rootDirectory,
globals.parameters.validClients,
globals.parameters.validReadFiles,
globals.parameters.validMasters,
globals.parameters.validWriteFiles));
// Attempt to create the service's private heap.
if ((globals.hServiceHeap = HeapCreate(0, 0, 0)) == NULL)
globals.hServiceHeap = GetProcessHeap();
__try { InitializeCriticalSection(&globals.io.cs); }
__except (EXCEPTION_EXECUTE_HANDLER) {
globals.service.status.dwWin32ExitCode = _exception_code();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"InitializeCriticalSection() raised exception 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
globals.initialized.ioCS = TRUE;
__try { InitializeCriticalSection(&globals.reaper.contextCS); }
__except (EXCEPTION_EXECUTE_HANDLER) {
globals.service.status.dwWin32ExitCode = _exception_code();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"InitializeCriticalSection() raised exception 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
globals.initialized.reaperContextCS = TRUE;
InitializeListHead(&globals.reaper.leakedContexts);
__try { InitializeCriticalSection(&globals.reaper.socketCS); }
__except (EXCEPTION_EXECUTE_HANDLER) {
globals.service.status.dwWin32ExitCode = _exception_code();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"InitializeCriticalSection() raised exception 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
globals.initialized.reaperSocketCS = TRUE;
InitializeListHead(&globals.reaper.leakedSockets);
// Initialize Winsock.
if (globals.service.status.dwWin32ExitCode = WSAStartup(MAKEWORD(2, 0), &wsaData)) {
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"ServiceMain: WSAStartup() failed, error 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
globals.initialized.winsock = TRUE;
// Initialize the socket contexts.
globals.io.master.s = INVALID_SOCKET;
globals.io.master.buffersize = TFTPD_DEF_BUFFER;
globals.io.master.datasize = TFTPD_DEF_DATA;
globals.io.master.lowWaterMark = globals.parameters.lowWaterMark;
globals.io.master.highWaterMark = globals.parameters.highWaterMark;
globals.io.def.s = INVALID_SOCKET;
globals.io.def.buffersize = TFTPD_DEF_BUFFER;
globals.io.def.datasize = TFTPD_DEF_DATA;
globals.io.def.lowWaterMark = globals.parameters.lowWaterMark;
globals.io.def.highWaterMark = globals.parameters.highWaterMark;
globals.io.mtu.s = INVALID_SOCKET;
globals.io.mtu.buffersize = TFTPD_MTU_BUFFER;
globals.io.mtu.datasize = TFTPD_MTU_DATA;
globals.io.mtu.lowWaterMark = globals.parameters.lowWaterMark;
globals.io.mtu.highWaterMark = globals.parameters.highWaterMark;
globals.io.max.s = INVALID_SOCKET;
globals.io.max.buffersize = TFTPD_MAX_BUFFER;
globals.io.max.datasize = TFTPD_MAX_DATA;
globals.io.max.lowWaterMark = globals.parameters.lowWaterMark;
globals.io.max.highWaterMark = globals.parameters.highWaterMark;
// Initialize the context hash table.
globals.hash.table =
(PTFTPD_HASH_BUCKET)HeapAlloc(globals.hServiceHeap,
HEAP_ZERO_MEMORY,
(globals.parameters.hashEntries *
sizeof(TFTPD_HASH_BUCKET)));
if (globals.hash.table == NULL)
goto stop_service;
for (x = 0; x < globals.parameters.hashEntries; x++) {
__try { InitializeCriticalSection(&globals.hash.table[x].cs); }
__except (EXCEPTION_EXECUTE_HANDLER) {
int y;
globals.service.status.dwWin32ExitCode = _exception_code();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"InitializeCriticalSection() raised exception 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
for (y = (x - 1); y >= 0; y--)
DeleteCriticalSection(&globals.hash.table[y].cs);
HeapFree(globals.hServiceHeap, 0, globals.hash.table);
globals.hash.table = NULL;
goto stop_service;
}
InitializeListHead(&globals.hash.table[x].bucket);
}
globals.initialized.contextHashTable = TRUE;
//
// Start the thread pool :
//
// Create the timer queue for timeouts.
globals.io.hTimerQueue = CreateTimerQueue();
if (globals.io.hTimerQueue == NULL) {
globals.service.status.dwWin32ExitCode = GetLastError();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"ServiceMain: CreateTimerQueue() failed, error 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
// Obtain the udp tftp service port to bind the master UDP service socket to.
if ((servent = getservbyname("tftp", "udp")) == NULL) {
globals.service.status.dwWin32ExitCode = WSAGetLastError();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"ServiceMain: getservbyname() failed, error 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = servent->s_port;
// Create the master UDP service socket.
TftpdIoInitializeSocketContext(&globals.io.master, &addr, NULL);
if (globals.io.master.s == INVALID_SOCKET) {
globals.service.status.dwWin32ExitCode = GetLastError();
TFTPD_DEBUG((TFTPD_DBG_SERVICE,
"ServiceMain: TftpdIoInitializeSocketContext() failed, error 0x%08X.\n",
globals.service.status.dwWin32ExitCode));
TftpdLogEvent(EVENTLOG_ERROR_TYPE, 0, 0);
goto stop_service;
}
// Notify the service control manager that we're ready to go.
globals.service.status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(globals.service.hStatus, &globals.service.status);
TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceMain(): Service running.\n"));
return;
stop_service :
TftpdShutdownService();
} // TftpdServiceMain()
void WINAPI
TftpdServiceHandler(DWORD dwOpcode) {
switch (dwOpcode) {
case SERVICE_CONTROL_INTERROGATE :
SetServiceStatus(globals.service.hStatus, &globals.service.status);
TFTPD_DEBUG((TFTPD_TRACE_SERVICE,
"TftpdServiceHandler(SERVICE_CONTROL_INTERROGATE)...\n"
"\tMax Clients : %d\n"
"\tTimeouts : %d\n"
"\tDrops : %d\n"
"\tPrivate sockets : %d\n"
"\tSorcerer's Apprentice : %d\n",
globals.performance.maxClients,
globals.performance.timeouts,
globals.performance.drops,
globals.performance.privateSockets,
globals.performance.sorcerersApprentice));
break;
case SERVICE_CONTROL_STOP :
TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceHandler: SERVICE_CONTROL_STOP.\n"));
if (globals.service.shutdown) {
if (globals.service.shutdown == 1)
SetServiceStatus(globals.service.hStatus, &globals.service.status);
return;
}
TftpdShutdownService();
break;
default :
TFTPD_DEBUG((TFTPD_TRACE_SERVICE, "TftpdServiceHandler: Unknown request 0x%08X.\n", dwOpcode));
} // switch (dwOpcode)
} // TftpdServiceHandler()
void __cdecl
main(int argc, char *argv[]) {
SERVICE_TABLE_ENTRY dispatch[] = {
{ TEXT("Tftpd"), (LPSERVICE_MAIN_FUNCTION)TftpdServiceMain },
{ NULL, NULL }
};
StartServiceCtrlDispatcher(dispatch);
} // main()