// Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "stdafx.h" #include "resource.h" #include "CommandLineArguments.h" #include "IEServer.h" #include #include #include #include #include #include // The prototypes for these functions must match those exported // by the .dll produced by the IEDriver project in this solution. // The definitions of these functions can be found in WebDriver.h // in that project. typedef void* (__cdecl *STARTSERVERPROC)(int, const std::wstring&, const std::wstring&, const std::wstring&, const std::wstring&, const std::wstring&); typedef void (__cdecl *STOPSERVERPROC)(void); #define ERR_DLL_EXTRACT_FAIL 1 #define ERR_DLL_LOAD_FAIL 2 #define ERR_FUNCTION_NOT_FOUND 3 #define ERR_SERVER_START 4 #define RESOURCE_TYPE L"BINARY" #define TEMP_FILE_PREFIX L"IEDriver" #define START_SERVER_EX_API_NAME "StartServer" #define STOP_SERVER_API_NAME "StopServer" #define PORT_COMMAND_LINE_ARG L"port" #define HOST_COMMAND_LINE_ARG L"host" #define LOGLEVEL_COMMAND_LINE_ARG L"log-level" #define LOGFILE_COMMAND_LINE_ARG L"log-file" #define SILENT_COMMAND_LINE_ARG L"silent" #define EXTRACTPATH_COMMAND_LINE_ARG L"extract-path" #define ACL_COMMAND_LINE_ARG L"whitelisted-ips" #define BOOLEAN_COMMAND_LINE_ARG_MISSING_VALUE L"value-not-specified" bool ExtractResource(unsigned short resource_id, const std::wstring& output_file_name) { bool success = false; try { // First find and load the required resource HRSRC resource_handle = ::FindResource(NULL, MAKEINTRESOURCE(resource_id), RESOURCE_TYPE); HGLOBAL global_resouce_handle = ::LoadResource(NULL, resource_handle); // Now open and map this to a disk file LPVOID file_pointer = ::LockResource(global_resouce_handle); DWORD resource_size = ::SizeofResource(NULL, resource_handle); // Open the file and filemap HANDLE file_handle = ::CreateFile(output_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); HANDLE file_mapping_handle = ::CreateFileMapping(file_handle, NULL, PAGE_READWRITE, 0, resource_size, NULL); LPVOID base_address_pointer = ::MapViewOfFile(file_mapping_handle, FILE_MAP_WRITE, 0, 0, 0); // Write the file ::CopyMemory(base_address_pointer, file_pointer, resource_size); // Unmap the file and close the handles ::UnmapViewOfFile(base_address_pointer); ::CloseHandle(file_mapping_handle); ::CloseHandle(file_handle); success = true; } catch(...) { // Ignore all type of errors } return success; } std::wstring GetProcessArchitectureDescription() { std::wstring arch_description = L"32-bit"; SYSTEM_INFO system_info; ::GetNativeSystemInfo(&system_info); if (system_info.wProcessorArchitecture != 0) { BOOL is_emulated; HANDLE process_handle = ::GetCurrentProcess(); ::IsWow64Process(process_handle, &is_emulated); if (!is_emulated) { arch_description = L"64-bit"; } ::CloseHandle(process_handle); } return arch_description; } std::wstring GetExecutableVersion() { struct LANGANDCODEPAGE { WORD language; WORD code_page; } *lang_info; // get the filename of the executable containing the version resource std::vector file_name_buffer(MAX_PATH + 1); ::GetModuleFileNameW(NULL, &file_name_buffer[0], MAX_PATH); DWORD dummy; DWORD length = ::GetFileVersionInfoSizeW(&file_name_buffer[0], &dummy); std::vector version_buffer(length); ::GetFileVersionInfoW(&file_name_buffer[0], dummy, length, &version_buffer[0]); UINT page_count; BOOL query_result = ::VerQueryValueW(&version_buffer[0], L"\\VarFileInfo\\Translation", reinterpret_cast(&lang_info), &page_count); wchar_t sub_block[MAX_PATH]; _snwprintf_s(sub_block, MAX_PATH, MAX_PATH, L"\\StringFileInfo\\%04x%04x\\FileVersion", lang_info->language, lang_info->code_page); LPVOID value = NULL; UINT size; query_result = ::VerQueryValueW(&version_buffer[0], sub_block, &value, &size); return static_cast(value); } void ShowUsage(void) { std::wcout << L"Launches the WebDriver server for the Internet Explorer driver" << std::endl << std::endl << L"IEDriverServer [/port=] [/host=] [/log-level=]" << std::endl << L" [/log-file=] [/extract-path=] [/silent]" << std::endl << L" [/whitelisted-ips=] [/version]" << std::endl << std::endl << L" /port= Specifies the port on which the server will listen for" << std::endl << L" commands. Defaults to 5555 if not specified." << std::endl << L" /host= Specifies the address of the host adapter on which the server" << std::endl << L" will listen for commands." << std::endl << L" /log-level=" << std::endl << L" Specifies the log level used by the server. Valid values are:" << std::endl << L" TRACE, DEBUG, INFO, WARN, ERROR, and FATAL. Defaults to FATAL" << std::endl << L" if not specified." << std::endl << L" /log-file=" << std::endl << L" Specifies the full path and file name of the log file used by" << std::endl << L" the server. Defaults logging to stdout if not specified. " << std::endl << L" /extract-path=" << std::endl << L" Specifies the full path to the directory used to extract" << std::endl << L" supporting files used by the server. Defaults to the TEMP" << std::endl << L" directory if not specified." << std::endl << L" /silent Suppresses diagnostic output when the server is started." << std::endl << L" /whitelisted-ips=" << std::endl << L" Comma-separated whitelist of remote IPv4 addresses which" << std::endl << L" are allowed to connect to the WebDriver server." << std::endl << L" /version Displays version information and exits. All other arguments" << std::endl << L" are ignored." << std::endl; } int _tmain(int argc, _TCHAR* argv[]) { CommandLineArguments args(argc, argv); if (args.is_help_requested()) { ShowUsage(); return 0; } vector temp_file_name_buffer(MAX_PATH); vector temp_path_buffer(MAX_PATH); // Gets the temp path env string (no guarantee it's a valid path). unsigned long temp_path_length = ::GetTempPath(MAX_PATH, &temp_path_buffer[0]); std::wstring extraction_path(&temp_path_buffer[0]); std::wstring extraction_path_arg = args.GetValue(EXTRACTPATH_COMMAND_LINE_ARG, L""); if (extraction_path_arg.size() != 0) { extraction_path = extraction_path_arg; } if (extraction_path.size() > 0 && extraction_path[extraction_path.size() - 1] != L'\\') { extraction_path.append(L"\\"); } std::wstring initial_file = extraction_path + TEMP_FILE_PREFIX + L".tmp"; std::wstring temp_file_name = initial_file; WIN32_FIND_DATA find_file_data; HANDLE file_handle = ::FindFirstFile(initial_file.c_str(), &find_file_data); if (file_handle != INVALID_HANDLE_VALUE) { ::FindClose(file_handle); unsigned int error_code = ::GetTempFileName(extraction_path.c_str(), TEMP_FILE_PREFIX, 0, &temp_file_name_buffer[0]); temp_file_name = &temp_file_name_buffer[0]; } if (!ExtractResource(IDR_DRIVER_LIBRARY, temp_file_name)) { std::wcout << L"Failed to extract the library to temp directory: " << temp_file_name; return ERR_DLL_EXTRACT_FAIL; } HMODULE module_handle = ::LoadLibrary(temp_file_name.c_str()); if (module_handle == NULL) { std::wcout << L"Failed to load the library from temp directory: " << temp_file_name; return ERR_DLL_LOAD_FAIL; } STARTSERVERPROC start_server_ex_proc = reinterpret_cast( ::GetProcAddress(module_handle, START_SERVER_EX_API_NAME)); STOPSERVERPROC stop_server_proc = reinterpret_cast( ::GetProcAddress(module_handle, STOP_SERVER_API_NAME)); if (start_server_ex_proc == NULL || stop_server_proc == NULL) { std::wcout << L"Could not find entry point in extracted library: " << temp_file_name; return ERR_FUNCTION_NOT_FOUND; } int port = _wtoi(args.GetValue(PORT_COMMAND_LINE_ARG, L"5555").c_str()); std::wstring host_address = args.GetValue(HOST_COMMAND_LINE_ARG, L""); std::wstring log_level = args.GetValue(LOGLEVEL_COMMAND_LINE_ARG, L""); std::wstring log_file = args.GetValue(LOGFILE_COMMAND_LINE_ARG, L""); bool silent = args.GetValue(SILENT_COMMAND_LINE_ARG, BOOLEAN_COMMAND_LINE_ARG_MISSING_VALUE).size() == 0; std::wstring executable_version = GetExecutableVersion(); std::wstring executable_architecture = GetProcessArchitectureDescription(); std::wstring implementation = L""; std::wstring whitelist = args.GetValue(ACL_COMMAND_LINE_ARG, L""); // coerce log level and implementation to uppercase, making the values // case-insensitive, to match expected values. std::transform(log_level.begin(), log_level.end(), log_level.begin(), toupper); std::transform(implementation.begin(), implementation.end(), implementation.begin(), toupper); if (args.is_version_requested()) { std::wcout << L"IEDriverServer.exe" << L" " << executable_version << L" (" << executable_architecture << L")" << std::endl; } else { void* server_value = start_server_ex_proc(port, host_address, log_level, log_file, executable_version + L" (" + executable_architecture + L")", whitelist); if (server_value == NULL) { std::wcout << L"Failed to start the server with: " << L"port = '" << port << L"', " << L"host = '" << host_address << L"', " << L"log level = '" << log_level << L"', " << L"log file = '" << log_file << L"', " << L"whitelisted ips = '" << whitelist << L"'."; return ERR_SERVER_START; } if (!silent) { std::wcout << L"Started InternetExplorerDriver server" << L" (" << executable_architecture << L")" << std::endl; std::wcout << executable_version << std::endl; std::wcout << L"Listening on port " << port << std::endl; if (host_address.size() > 0) { std::wcout << L"Bound to network adapter with IP address " << host_address << std::endl; } if (log_level.size() > 0) { std::wcout << L"Log level is set to " << log_level << std::endl; } if (log_file.size() > 0) { std::wcout << L"Log file is set to " << log_file << std::endl; } if (extraction_path_arg.size() > 0) { std::wcout << L"Library extracted to " << extraction_path_arg << std::endl; } if (whitelist.size() > 0) { std::wcout << L"IP addresses allowed to connect are " << whitelist << std::endl; } else { std::wcout << L"Only local connections are allowed" << std::endl; } } // Create the shutdown event and wait for it to be signaled. DWORD process_id = ::GetCurrentProcessId(); vector process_id_buffer(10); _ltow_s(process_id, &process_id_buffer[0], process_id_buffer.size(), 10); std::wstring process_id_string(&process_id_buffer[0]); std::wstring event_name = IESERVER_SHUTDOWN_EVENT_NAME + process_id_string; HANDLE event_handle = ::CreateEvent(NULL, TRUE, FALSE, event_name.c_str()); ::WaitForSingleObject(event_handle, INFINITE); ::CloseHandle(event_handle); stop_server_proc(); } ::FreeLibrary(module_handle); ::DeleteFile(temp_file_name.c_str()); return 0; }