diff --git a/Resource.rc b/Resource.rc new file mode 100644 index 0000000..49888b8 Binary files /dev/null and b/Resource.rc differ diff --git a/Service.cpp b/Service.cpp new file mode 100644 index 0000000..a6e9aea --- /dev/null +++ b/Service.cpp @@ -0,0 +1,783 @@ +// TinyWebRedirector +// Joseph Ryan Ries, 2015 +// Vista/2008 and above. +// Warning - This code is not portable to non-Unicode builds. + +#include +#include +#include // includes windows.h, among others. +#include + +#pragma comment(lib, "Ws2_32.lib") + +#define SERVICE_NAME L"TinyWebRedirector" +#define SERVICE_VERSION L"1.0" +#define SERVICE_DESC L"Simply listens for and redirects HTTP requests. Configurable via registry in HKLM\\SYSTEM\\CurrentControlSet\\Services\\"SERVICE_NAME +#define EVENTLOG_REG_PATH L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"SERVICE_NAME +#define SERVICE_REG_PATH L"SYSTEM\\CurrentControlSet\\Services\\"SERVICE_NAME +#define MAX_EVENT_CHARS 1024 + +wchar_t g_EventBuffer[MAX_EVENT_CHARS]; +wchar_t* g_EventText[1] = { g_EventBuffer }; +CRITICAL_SECTION g_EventCritSec; + +SERVICE_STATUS g_ServiceStatus; +SERVICE_STATUS_HANDLE g_StatusHandle; +HANDLE g_ServiceStopEvent; +HANDLE g_EventLogHandle; +HANDLE g_WorkerThreadHandle; + +DWORD g_Port; +wchar_t g_URL[256]; + +HRESULT WriteEventW(_In_ WORD EventType, _In_ DWORD EventId, _In_ const wchar_t* EventText, _In_ ...) +{ + EnterCriticalSection(&g_EventCritSec); + wchar_t FormattedEventText[MAX_EVENT_CHARS] = { 0 }; + va_list ArgPointer = NULL; + + if (wcslen(EventText) > (sizeof(g_EventBuffer) / sizeof(wchar_t)) - sizeof(wchar_t)) + { + LeaveCriticalSection(&g_EventCritSec); + return E_INVALIDARG; + } + + if (g_EventLogHandle == NULL) + { + LeaveCriticalSection(&g_EventCritSec); + return E_HANDLE; + } + + DWORD Result = 0; + + va_start(ArgPointer, EventText); + Result = _vsnwprintf_s(FormattedEventText, MAX_EVENT_CHARS - sizeof(wchar_t), EventText, ArgPointer); + va_end(ArgPointer); + + if (Result < 0) + { + LeaveCriticalSection(&g_EventCritSec); + return E_FAIL; + } + + wcscpy_s(g_EventBuffer, FormattedEventText); + + Result = ReportEvent(g_EventLogHandle, EventType, 0, EventId, NULL, 1, 0, (LPCWSTR*)g_EventText, 0); + LeaveCriticalSection(&g_EventCritSec); + + if (Result == 0) + { + return E_FAIL; + } + else + { + return ERROR_SUCCESS; + } +} + +void PrintUsage() +{ + wprintf(L"\n%s v%s - Redirects HTTP Requests\n", SERVICE_NAME, SERVICE_VERSION); + wprintf(L"Copyright (C) 2015 Joseph Ryan Ries\n"); + wprintf(L"www.myotherpcisacloud.com\n\n"); + wprintf(L"Usage:\n"); + wprintf(L"Install: %s -install\n", SERVICE_NAME); + wprintf(L"Uninstall: %s -uninstall\n\n", SERVICE_NAME); +} + +const wchar_t* ErrorCodeToStringW(_In_ DWORD ErrorCode) +{ + if (ErrorCode == NO_ERROR) + { + return L"NONE"; + } + + static wchar_t ErrorString[256] = { 0 }; + + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), ErrorString, 255, NULL); + + return ErrorString; +} + +// NOTE(Ryan): This function returns true if the string ends with the specified Suffix/substring. +// Uses wide characters. Case sensitive. +int StringEndsWithW(_In_opt_ const wchar_t* String, _In_opt_ const wchar_t* Suffix) +{ + if (String == NULL || Suffix == NULL) + { + return 0; + } + + size_t StringLength = wcslen(String); + size_t SuffixLength = wcslen(Suffix); + + if (SuffixLength > StringLength) + { + return 0; + } + + return 0 == wcsncmp(String + StringLength - SuffixLength, Suffix, SuffixLength); +} + +BOOL FileExists(_In_ LPCWSTR FilePath) +{ + DWORD FileAttributes = GetFileAttributes(FilePath); + return (FileAttributes != INVALID_FILE_ATTRIBUTES && !(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)); +} + +DWORD AddAceToObjectSecurityDescriptor(LPTSTR ObjectName, SE_OBJECT_TYPE ObjectType, LPTSTR Trustee, TRUSTEE_FORM TrusteeForm, DWORD AccessRights, ACCESS_MODE AccessMode, DWORD Inheritance) +{ + DWORD Result = 0; + PACL OldDACL = NULL; + PACL NewDACL = NULL; + PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; + EXPLICIT_ACCESS ExplicitAccess; + + if (NULL == ObjectName) + { + return ERROR_INVALID_PARAMETER; + } + + // Get a pointer to the existing DACL. + Result = GetNamedSecurityInfo(ObjectName, ObjectType, DACL_SECURITY_INFORMATION, NULL, NULL, &OldDACL, NULL, &SecurityDescriptor); + if (ERROR_SUCCESS != Result) + { + wprintf(L"GetNamedSecurityInfo Error 0x%x %s\n", Result, ErrorCodeToStringW(Result)); + goto Cleanup; + } + + // Initialize an EXPLICIT_ACCESS structure for the new ACE. + + ZeroMemory(&ExplicitAccess, sizeof(EXPLICIT_ACCESS)); + ExplicitAccess.grfAccessPermissions = AccessRights; + ExplicitAccess.grfAccessMode = AccessMode; + ExplicitAccess.grfInheritance = Inheritance; + ExplicitAccess.Trustee.TrusteeForm = TrusteeForm; + ExplicitAccess.Trustee.ptstrName = Trustee; + + // Create a new ACL that merges the new ACE + // into the existing DACL. + + Result = SetEntriesInAcl(1, &ExplicitAccess, OldDACL, &NewDACL); + if (ERROR_SUCCESS != Result) + { + wprintf(L"SetEntriesInAcl Error 0x%x %s\n", Result, ErrorCodeToStringW(Result)); + goto Cleanup; + } + + // Attach the new ACL as the object's DACL. + + Result = SetNamedSecurityInfo(ObjectName, ObjectType, DACL_SECURITY_INFORMATION, NULL, NULL, NewDACL, NULL); + if (ERROR_SUCCESS != Result) + { + wprintf(L"SetNamedSecurityInfo Error 0x%x %s\n", Result, ErrorCodeToStringW(Result)); + goto Cleanup; + } + +Cleanup: + + if (SecurityDescriptor != NULL) + { + LocalFree((HLOCAL)SecurityDescriptor); + } + if (NewDACL != NULL) + { + LocalFree((HLOCAL)NewDACL); + } + //if (OldDACL != NULL) + //{ + // LocalFree((HLOCAL)OldDACL); + //} + + return Result; +} + +void InstallService() +{ + wchar_t PathToEventCreate[MAX_PATH] = { 0 }; + + DWORD EnvironmentVarLength = 0; + + EnvironmentVarLength = GetEnvironmentVariable(L"SystemRoot", PathToEventCreate, MAX_PATH); + + if (EnvironmentVarLength != 0 && (GetLastError() != ERROR_ENVVAR_NOT_FOUND) && (EnvironmentVarLength < MAX_PATH)) + { + if (!StringEndsWithW(PathToEventCreate, L"\\")) + { + wcscat_s(PathToEventCreate, L"\\"); + } + } + else + { + wprintf(L"ERROR: Failed to locate the SystemRoot environment variable!\n"); + return; + } + + wcscat_s(PathToEventCreate, L"System32\\Eventcreate.exe"); + + if (!FileExists(PathToEventCreate)) + { + wprintf(L"ERROR: %s not found!\n", PathToEventCreate); + return; + } + + HKEY EventLogKeyHandle = NULL; + LONG LResult = 0; + + LResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, EVENTLOG_REG_PATH, 0, KEY_READ, &EventLogKeyHandle); + + if (LResult != ERROR_FILE_NOT_FOUND) + { + wprintf(L"ERROR: It appears the service is already installed. Uninstall first then try again.\n"); + return; + } + + LResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, EVENTLOG_REG_PATH, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &EventLogKeyHandle, NULL); + + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to create registry key! (Check Admin privileges.) Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + DWORD Value = 1; + LResult = RegSetValueEx(EventLogKeyHandle, L"CustomSource", 0, REG_DWORD, (const BYTE*)&Value, sizeof(DWORD)); + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to set registry value! Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + Value = 7; + LResult = RegSetValueEx(EventLogKeyHandle, L"TypesSupported", 0, REG_DWORD, (const BYTE*)&Value, sizeof(DWORD)); + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to set registry value! Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + LResult = RegSetValueEx(EventLogKeyHandle, L"EventMessageFile", 0, REG_EXPAND_SZ, (const BYTE*)&PathToEventCreate, (DWORD)(wcslen(PathToEventCreate) * sizeof(wchar_t))); + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to set registry value! Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + if (EventLogKeyHandle) + { + RegCloseKey(EventLogKeyHandle); + } + + wprintf(L"Eventlog source registered.\n"); + + + SC_HANDLE ServiceController = NULL; + SC_HANDLE MyService = NULL; + + ServiceController = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + + if (ServiceController == NULL) + { + wprintf(L"ERROR: Failed to open Service Controller! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + wchar_t ImageFilePath[MAX_PATH] = { 0 }; + + DWORD PathLength = 0; + PathLength = GetModuleFileName(NULL, ImageFilePath, MAX_PATH); + if (PathLength != 0 && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) + { + MyService = CreateService( + ServiceController, + SERVICE_NAME, + SERVICE_NAME, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + (LPCWSTR)ImageFilePath, + NULL, + NULL, + NULL, + L"NT AUTHORITY\\LocalService", + NULL); + } + + if (MyService == NULL) + { + wprintf(L"ERROR: Failed to create service! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + SERVICE_DESCRIPTION ServiceDescription = { SERVICE_DESC }; + + if (ChangeServiceConfig2(MyService, SERVICE_CONFIG_DESCRIPTION, &ServiceDescription) == 0) + { + wprintf(L"ERROR: Failed to set service description! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + SERVICE_DELAYED_AUTO_START_INFO AutoStartInfo = { 0 }; + + AutoStartInfo.fDelayedAutostart = TRUE; + + if (ChangeServiceConfig2(MyService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &AutoStartInfo) == 0) + { + wprintf(L"ERROR: Failed to set service to delayed auto start! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + wprintf(L"%s service installed.\n", SERVICE_NAME); + + // Need to make sure 'Local Service' has read permissions to the executable image. + DWORD AddACEResult = AddAceToObjectSecurityDescriptor(ImageFilePath, SE_FILE_OBJECT, L"NT AUTHORITY\\LocalService", TRUSTEE_IS_NAME, GENERIC_READ | GENERIC_EXECUTE, GRANT_ACCESS, NO_INHERITANCE); + if (AddACEResult != ERROR_SUCCESS) + { + wprintf(L"WARNING: Failed to set read access on the executable! Code: 0x%x %s\n", AddACEResult, ErrorCodeToStringW(AddACEResult)); + } + + HKEY ServiceRegKey = NULL; + + LResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SERVICE_REG_PATH, 0, KEY_ALL_ACCESS, &ServiceRegKey); + + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to open service registry key! Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + Value = 80; + LResult = RegSetValueEx(ServiceRegKey, L"Port", 0, REG_DWORD, (const BYTE*)&Value, sizeof(DWORD)); + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to set registry value! Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + wchar_t RedirectURL[] = L"www.myotherpcisacloud.com"; + + LResult = RegSetValueEx(ServiceRegKey, L"RedirectURL", 0, REG_SZ, (const BYTE*)&RedirectURL, (DWORD)(wcslen(RedirectURL) * sizeof(wchar_t) + 1)); + + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to set registry value! Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + if (ServiceRegKey) + { + RegCloseKey(ServiceRegKey); + } + + if (StartService(MyService, 0, NULL) == 0) + { + wprintf(L"WARNING: StartService failed! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + } + + if (ServiceController) + { + CloseServiceHandle(ServiceController); + } + + wprintf(L"\nYou may choose to customize the port number and URL in the registry:\n"); + wprintf(L"HKLM\\%s\n", SERVICE_REG_PATH); + wprintf(L"Restart the service for changes to take effect.\n"); +} + +void UninstallService() +{ + SC_HANDLE ServiceController = NULL; + SC_HANDLE MyService = NULL; + + ServiceController = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (ServiceController == NULL) + { + wprintf(L"ERROR: Failed to open Service Controller! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + MyService = OpenService(ServiceController, SERVICE_NAME, SERVICE_ALL_ACCESS); + if (MyService == NULL) + { + wprintf(L"ERROR: Failed to open handle to service! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + goto DeleteEventLogRegistryKey; + } + + wprintf(L"Waiting for service to stop...\n"); + + SERVICE_STATUS_PROCESS ServiceStatus; + DWORD StopTimeout = 0; + DWORD BytesNeeded = 0; + + ControlService(MyService, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ServiceStatus); + + while (ServiceStatus.dwCurrentState != SERVICE_STOPPED && StopTimeout < 6) + { + Sleep(4000); + StopTimeout++; + if (QueryServiceStatusEx(MyService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ServiceStatus, sizeof(SERVICE_STATUS_PROCESS), &BytesNeeded) == 0) + { + wprintf(L"ERROR: QueryServiceStatusEx failed! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + } + } + + if (StopTimeout >= 6) + { + wprintf(L"WARNING: Service did not stop in a timely manner.\n"); + } + + if (DeleteService(MyService) == 0) + { + wprintf(L"ERROR: Failed to delete service! Code: 0x%x %s\n", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + else + { + wprintf(L"Service uninstalled.\n"); + } + +DeleteEventLogRegistryKey: + + if (ServiceController) + { + CloseServiceHandle(ServiceController); + } + + HKEY EventLogKeyHandle = NULL; + LONG LResult = 0; + + LResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\", 0, KEY_ALL_ACCESS, &EventLogKeyHandle); + + if (LResult != ERROR_SUCCESS) + { + wprintf(L"ERROR: Failed to open eventlog registry key. Check admin privileges. Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + return; + } + + LResult = RegDeleteTree(EventLogKeyHandle, SERVICE_NAME); + + if (LResult != ERROR_SUCCESS) + { + wprintf(L"WARNING: Failed to delete eventlog registry key! Code: 0x%x %s\n", LResult, ErrorCodeToStringW(LResult)); + } + else + { + wprintf(L"Eventlog registry key deleted.\n"); + } +} + +DWORD WINAPI ServiceWorkerThread(_In_ LPVOID) +{ + WSADATA WinsockData = { 0 }; + + if (WSAStartup(MAKEWORD(2, 2), &WinsockData) != 0) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 108, L"ServiceWorkerThread: Failed to initialize Winsock! Code: 0x%x %s", WSAGetLastError(), ErrorCodeToStringW(WSAGetLastError())); + return WSAGetLastError(); + } + + SOCKET ServerSocket = { 0 }; + + if ((ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 109, L"ServiceWorkerThread: Failed to create socket! Code: 0x%x %s", WSAGetLastError(), ErrorCodeToStringW(WSAGetLastError())); + return WSAGetLastError(); + } + + struct sockaddr_in Server; + + Server.sin_family = AF_INET; + Server.sin_addr.s_addr = INADDR_ANY; + Server.sin_port = htons((u_short)g_Port); + + if (bind(ServerSocket, (struct sockaddr *)&Server, sizeof(Server)) == SOCKET_ERROR) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 110, L"ServiceWorkerThread: Failed to bind to port %d! Code: 0x%x %s", g_Port, WSAGetLastError(), ErrorCodeToStringW(WSAGetLastError())); + return WSAGetLastError(); + } + + listen(ServerSocket, 3); + + int AddressLength = sizeof(struct sockaddr_in); + + SOCKET ClientSocket = { 0 }; + + struct sockaddr_in Client; + + char UnicodeURLtoASCII[128] = { 0 }; + + size_t CharactersConverted = 0; + wcstombs_s(&CharactersConverted, UnicodeURLtoASCII, g_URL, wcslen(g_URL)); + + while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0) + { + int Result = 0; + char ReceiveBuffer[512] = { 0 }; + + ClientSocket = accept(ServerSocket, (struct sockaddr *)&Client, &AddressLength); + Result = recv(ClientSocket, ReceiveBuffer, sizeof(ReceiveBuffer), 0); + + char Response[512] = { 0 }; + char HTML[512] = { 0 }; + + _snprintf_s( + HTML, + sizeof(HTML), + "Click here if you are not automatically redirected.", + UnicodeURLtoASCII, + UnicodeURLtoASCII); + + _snprintf_s( + Response, + sizeof(Response), + "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n%s", + (int)strlen(HTML), + HTML); + + send(ClientSocket, Response, (int)strlen(Response), 0); + + shutdown(ClientSocket, SD_SEND); + closesocket(ClientSocket); + } + + closesocket(ServerSocket); + + return ERROR_SUCCESS; +} + +VOID WINAPI ServiceControlHandler(_In_ DWORD ControlCode) +{ + switch (ControlCode) + { + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + { + if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) + { + break; + } + + WSACleanup(); + + if (TerminateThread(g_WorkerThreadHandle, 0) == 0) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 120, L"WARNING: TerminateThread failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + } + + // Send yourself a "kill signal" to keep the service from blocking so it will stop + //SOCKET ClientSocket = { 0 }; + //struct sockaddr_in Server; + + //if ((ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + //{ + // WriteEventW(EVENTLOG_ERROR_TYPE, 115, L"ServiceControlHandler: Failed to create socket for kill signal! Code: 0x%x %s", g_Port, WSAGetLastError(), ErrorCodeToStringW(WSAGetLastError())); + //} + + //InetPton(AF_INET, L"127.0.0.1", &Server.sin_addr.s_addr); + //Server.sin_family = AF_INET; + //Server.sin_port = htons((u_short)g_Port); + + //if (connect(ClientSocket, (struct sockaddr *)&Server, sizeof(Server)) < 0) + //{ + // WriteEventW(EVENTLOG_ERROR_TYPE, 116, L"ServiceControlHandler: Failed to connect to send kill signal! Code: 0x%x %s", g_Port, WSAGetLastError(), ErrorCodeToStringW(WSAGetLastError())); + //} + //else + //{ + // char Kill[5] = "kill"; + // send(ClientSocket, Kill, (int)strlen(Kill), 0); + // closesocket(ClientSocket); + //} + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 4; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 107, L"ServiceControlHandler: SetServiceStatus (STOP_PENDING) failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + } + + SetEvent(g_ServiceStopEvent); + break; + } + default: + { + break; + } + } +} + +VOID WINAPI ServiceMain(_In_ DWORD, _In_ LPTSTR*) +{ + g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceControlHandler); + if (g_StatusHandle == NULL) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 101, L"ServiceMain: RegisterServiceCtrlHandler failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus)); + + g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + g_ServiceStatus.dwControlsAccepted = NULL; + g_ServiceStatus.dwWin32ExitCode = NO_ERROR; + g_ServiceStatus.dwServiceSpecificExitCode = NO_ERROR; + g_ServiceStatus.dwWaitHint = 5000; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == NULL) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 102, L"ServiceMain: SetServiceStatus (PENDING) failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (g_ServiceStopEvent == NULL) + { + g_ServiceStatus.dwControlsAccepted = NULL; + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = GetLastError(); + g_ServiceStatus.dwCheckPoint = 1; + SetServiceStatus(g_StatusHandle, &g_ServiceStatus); + WriteEventW(EVENTLOG_ERROR_TYPE, 103, L"ServiceMain: Failed to create stop event! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; + g_ServiceStatus.dwWin32ExitCode = NO_ERROR; + g_ServiceStatus.dwCheckPoint = 0; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 104, L"ServiceMain: SetServiceStatus (RUNNING) failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + g_WorkerThreadHandle = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL); + + if (g_WorkerThreadHandle == NULL) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 105, L"ServiceMain: CreateThread failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + return; + } + + WaitForSingleObject(g_WorkerThreadHandle, INFINITE); + + WriteEventW(EVENTLOG_INFORMATION_TYPE, 302, L"ServiceMain: Service is stopping."); + + CloseHandle(g_ServiceStopEvent); + + g_ServiceStatus.dwControlsAccepted = NULL; + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = NO_ERROR; + g_ServiceStatus.dwCheckPoint = 3; + + if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 106, L"ServiceMain: SetServiceStatus (STOPPED) failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + } +} + +// Program entry point. +int wmain(_In_ int argc, _In_ wchar_t* argv[]) +{ + if (argc > 2) + { + PrintUsage(); + return(0); + } + + if (argc > 1) + { + if (_wcsicmp(argv[1], L"-install") == 0) + { + InstallService(); + return(0); + } + else if (_wcsicmp(argv[1], L"-uninstall") == 0) + { + UninstallService(); + return(0); + } + else + { + PrintUsage(); + return(0); + } + } + + g_EventLogHandle = RegisterEventSource(NULL, SERVICE_NAME); + if (g_EventLogHandle == NULL) + { + OutputDebugString(L"RegisterEventSource failed!\n"); + return(0); + } + + InitializeCriticalSection(&g_EventCritSec); + + WriteEventW(EVENTLOG_INFORMATION_TYPE, 300, L"%s v%s service is starting.", SERVICE_NAME, SERVICE_VERSION); + + HKEY ServiceRegKey = NULL; + LONG LResult = 0; + DWORD RegBuffer = sizeof(DWORD); + + LResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SERVICE_REG_PATH, 0, KEY_READ, &ServiceRegKey); + + if (LResult != ERROR_SUCCESS) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 111, L"wmain: RegOpenKeyEx failed! Code: 0x%x %s", LResult, ErrorCodeToStringW(LResult)); + return(0); + } + + LResult = RegQueryValueEx(ServiceRegKey, L"Port", NULL, NULL, (LPBYTE)&g_Port, &RegBuffer); + + if (LResult != ERROR_SUCCESS) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 112, L"wmain: RegQueryValueEx failed! Code: 0x%x %s", LResult, ErrorCodeToStringW(LResult)); + return(0); + } + + RegBuffer = sizeof(g_URL); + LResult = RegQueryValueEx(ServiceRegKey, L"RedirectURL", NULL, NULL, (LPBYTE)&g_URL, &RegBuffer); + + if (LResult != ERROR_SUCCESS) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 113, L"wmain: RegQueryValueEx failed! Code: 0x%x %s", LResult, ErrorCodeToStringW(LResult)); + return(0); + } + + + if (ServiceRegKey) + { + RegCloseKey(ServiceRegKey); + } + + if (g_Port < 1 || g_Port > 65535) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 114, L"Port number in registry was out of range!"); + return(0); + } + + WriteEventW(EVENTLOG_INFORMATION_TYPE, 303, L"Redirect URL: %s\nPort: %d", g_URL, g_Port); + + SERVICE_TABLE_ENTRY ServiceTable[] = + { + { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcher(ServiceTable) == 0) + { + WriteEventW(EVENTLOG_ERROR_TYPE, 115, L"wmain: StartServiceCtrlDispatcher failed! Code: 0x%x %s", GetLastError(), ErrorCodeToStringW(GetLastError())); + return GetLastError(); + } + + if (g_EventLogHandle) + { + DeregisterEventSource(g_EventLogHandle); + } + return(0); +} \ No newline at end of file diff --git a/TinyWebRedirector.sln b/TinyWebRedirector.sln new file mode 100644 index 0000000..f4b9803 --- /dev/null +++ b/TinyWebRedirector.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TinyWebRedirector", "TinyWebRedirector.vcxproj", "{33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Debug|Win32.Build.0 = Debug|Win32 + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Debug|x64.ActiveCfg = Debug|x64 + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Debug|x64.Build.0 = Debug|x64 + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Release|Win32.ActiveCfg = Release|Win32 + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Release|Win32.Build.0 = Release|Win32 + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Release|x64.ActiveCfg = Release|x64 + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TinyWebRedirector.vcxproj b/TinyWebRedirector.vcxproj new file mode 100644 index 0000000..4ff00de --- /dev/null +++ b/TinyWebRedirector.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {33ECE474-6693-4B77-98F8-A7CE2C8F5DE3} + TinyWebRedirector + + + + Application + true + v120 + Unicode + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + $(SolutionDir)$(Platform)\$(Configuration)\Temp\ + + + + Level4 + Disabled + true + MultiThreadedDebug + + + true + Console + 6.0 + + + + + Level4 + Disabled + true + MultiThreadedDebug + + + true + Console + 6.0 + + + + + Level4 + MaxSpeed + true + true + true + MultiThreaded + + + true + true + true + Console + 6.0 + + + + + Level4 + MaxSpeed + true + true + true + MultiThreaded + + + true + true + true + Console + 6.0 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TinyWebRedirector.vcxproj.filters b/TinyWebRedirector.vcxproj.filters new file mode 100644 index 0000000..67ab417 --- /dev/null +++ b/TinyWebRedirector.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..7ca31da --- /dev/null +++ b/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Resource.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif