Path: blob/trunk/cpp/iedriverserver/IEDriverServer.cpp
2867 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the "License");5// you may not use this file except in compliance with the License.6// You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing, software11// distributed under the License is distributed on an "AS IS" BASIS,12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13// See the License for the specific language governing permissions and14// limitations under the License.1516#include "stdafx.h"17#include "resource.h"18#include "CommandLineArguments.h"19#include "IEServer.h"20#include <algorithm>21#include <iostream>22#include <map>23#include <iostream>24#include <string>25#include <vector>2627// The prototypes for these functions must match those exported28// by the .dll produced by the IEDriver project in this solution.29// The definitions of these functions can be found in WebDriver.h30// in that project.31typedef void* (__cdecl *STARTSERVERPROC)(int, const std::wstring&, const std::wstring&, const std::wstring&, const std::wstring&, const std::wstring&);32typedef void (__cdecl *STOPSERVERPROC)(void);3334#define ERR_DLL_EXTRACT_FAIL 135#define ERR_DLL_LOAD_FAIL 236#define ERR_FUNCTION_NOT_FOUND 337#define ERR_SERVER_START 43839#define RESOURCE_TYPE L"BINARY"40#define TEMP_FILE_PREFIX L"IEDriver"41#define START_SERVER_EX_API_NAME "StartServer"42#define STOP_SERVER_API_NAME "StopServer"4344#define PORT_COMMAND_LINE_ARG L"port"45#define HOST_COMMAND_LINE_ARG L"host"46#define LOGLEVEL_COMMAND_LINE_ARG L"log-level"47#define LOGFILE_COMMAND_LINE_ARG L"log-file"48#define SILENT_COMMAND_LINE_ARG L"silent"49#define EXTRACTPATH_COMMAND_LINE_ARG L"extract-path"50#define ACL_COMMAND_LINE_ARG L"whitelisted-ips"51#define BOOLEAN_COMMAND_LINE_ARG_MISSING_VALUE L"value-not-specified"5253bool ExtractResource(unsigned short resource_id,54const std::wstring& output_file_name) {55bool success = false;56try {57// First find and load the required resource58HRSRC resource_handle = ::FindResource(NULL,59MAKEINTRESOURCE(resource_id),60RESOURCE_TYPE);61HGLOBAL global_resouce_handle = ::LoadResource(NULL, resource_handle);6263// Now open and map this to a disk file64LPVOID file_pointer = ::LockResource(global_resouce_handle);65DWORD resource_size = ::SizeofResource(NULL, resource_handle);6667// Open the file and filemap68HANDLE file_handle = ::CreateFile(output_file_name.c_str(),69GENERIC_READ | GENERIC_WRITE,700,71NULL,72CREATE_ALWAYS,73FILE_ATTRIBUTE_NORMAL,74NULL);75HANDLE file_mapping_handle = ::CreateFileMapping(file_handle,76NULL,77PAGE_READWRITE,780,79resource_size,80NULL);81LPVOID base_address_pointer = ::MapViewOfFile(file_mapping_handle,82FILE_MAP_WRITE,830,840,850);8687// Write the file88::CopyMemory(base_address_pointer, file_pointer, resource_size);8990// Unmap the file and close the handles91::UnmapViewOfFile(base_address_pointer);92::CloseHandle(file_mapping_handle);93::CloseHandle(file_handle);94success = true;95} catch(...) {96// Ignore all type of errors97}98return success;99}100101std::wstring GetProcessArchitectureDescription() {102std::wstring arch_description = L"32-bit";103SYSTEM_INFO system_info;104::GetNativeSystemInfo(&system_info);105if (system_info.wProcessorArchitecture != 0) {106BOOL is_emulated;107HANDLE process_handle = ::GetCurrentProcess();108::IsWow64Process(process_handle, &is_emulated);109if (!is_emulated) {110arch_description = L"64-bit";111}112::CloseHandle(process_handle);113}114115return arch_description;116}117118std::wstring GetExecutableVersion() {119struct LANGANDCODEPAGE {120WORD language;121WORD code_page;122} *lang_info;123124// get the filename of the executable containing the version resource125std::vector<wchar_t> file_name_buffer(MAX_PATH + 1);126::GetModuleFileNameW(NULL, &file_name_buffer[0], MAX_PATH);127128DWORD dummy;129DWORD length = ::GetFileVersionInfoSizeW(&file_name_buffer[0],130&dummy);131std::vector<BYTE> version_buffer(length);132::GetFileVersionInfoW(&file_name_buffer[0],133dummy,134length,135&version_buffer[0]);136137UINT page_count;138BOOL query_result = ::VerQueryValueW(&version_buffer[0],139L"\\VarFileInfo\\Translation",140reinterpret_cast<void**>(&lang_info),141&page_count);142143wchar_t sub_block[MAX_PATH];144_snwprintf_s(sub_block,145MAX_PATH,146MAX_PATH,147L"\\StringFileInfo\\%04x%04x\\FileVersion",148lang_info->language,149lang_info->code_page);150LPVOID value = NULL;151UINT size;152query_result = ::VerQueryValueW(&version_buffer[0],153sub_block,154&value,155&size);156return static_cast<wchar_t*>(value);157}158159160void ShowUsage(void) {161std::wcout << L"Launches the WebDriver server for the Internet Explorer driver" << std::endl162<< std::endl163<< L"IEDriverServer [/port=<port>] [/host=<host>] [/log-level=<level>]" << std::endl164<< L" [/log-file=<file>] [/extract-path=<path>] [/silent]" << std::endl165<< L" [/whitelisted-ips=<whitelisted-ips>] [/version]" << std::endl166<< std::endl167<< L" /port=<port> Specifies the port on which the server will listen for" << std::endl168<< L" commands. Defaults to 5555 if not specified." << std::endl169<< L" /host=<host> Specifies the address of the host adapter on which the server" << std::endl170<< L" will listen for commands." << std::endl171<< L" /log-level=<level>" << std::endl172<< L" Specifies the log level used by the server. Valid values are:" << std::endl173<< L" TRACE, DEBUG, INFO, WARN, ERROR, and FATAL. Defaults to FATAL" << std::endl174<< L" if not specified." << std::endl175<< L" /log-file=<file>" << std::endl176<< L" Specifies the full path and file name of the log file used by" << std::endl177<< L" the server. Defaults logging to stdout if not specified. " << std::endl178<< L" /extract-path=<path>" << std::endl179<< L" Specifies the full path to the directory used to extract" << std::endl180<< L" supporting files used by the server. Defaults to the TEMP" << std::endl181<< L" directory if not specified." << std::endl182<< L" /silent Suppresses diagnostic output when the server is started." << std::endl183<< L" /whitelisted-ips=<whitelisted-ips>" << std::endl184<< L" Comma-separated whitelist of remote IPv4 addresses which" << std::endl185<< L" are allowed to connect to the WebDriver server." << std::endl186<< L" /version Displays version information and exits. All other arguments" << std::endl187<< L" are ignored." << std::endl;188}189190int _tmain(int argc, _TCHAR* argv[]) {191CommandLineArguments args(argc, argv);192if (args.is_help_requested()) {193ShowUsage();194return 0;195}196vector<TCHAR> temp_file_name_buffer(MAX_PATH);197vector<TCHAR> temp_path_buffer(MAX_PATH);198199// Gets the temp path env string (no guarantee it's a valid path).200unsigned long temp_path_length = ::GetTempPath(MAX_PATH,201&temp_path_buffer[0]);202203std::wstring extraction_path(&temp_path_buffer[0]);204205std::wstring extraction_path_arg = args.GetValue(EXTRACTPATH_COMMAND_LINE_ARG, L"");206if (extraction_path_arg.size() != 0) {207extraction_path = extraction_path_arg;208}209210if (extraction_path.size() > 0 &&211extraction_path[extraction_path.size() - 1] != L'\\') {212extraction_path.append(L"\\");213}214215std::wstring initial_file = extraction_path + TEMP_FILE_PREFIX + L".tmp";216std::wstring temp_file_name = initial_file;217WIN32_FIND_DATA find_file_data;218HANDLE file_handle = ::FindFirstFile(initial_file.c_str(), &find_file_data);219if (file_handle != INVALID_HANDLE_VALUE) {220::FindClose(file_handle);221unsigned int error_code = ::GetTempFileName(extraction_path.c_str(),222TEMP_FILE_PREFIX,2230,224&temp_file_name_buffer[0]);225226temp_file_name = &temp_file_name_buffer[0];227}228229if (!ExtractResource(IDR_DRIVER_LIBRARY, temp_file_name)) {230std::wcout << L"Failed to extract the library to temp directory: "231<< temp_file_name;232return ERR_DLL_EXTRACT_FAIL;233}234235HMODULE module_handle = ::LoadLibrary(temp_file_name.c_str());236if (module_handle == NULL) {237std::wcout << L"Failed to load the library from temp directory: "238<< temp_file_name;239return ERR_DLL_LOAD_FAIL;240}241242STARTSERVERPROC start_server_ex_proc = reinterpret_cast<STARTSERVERPROC>(243::GetProcAddress(module_handle, START_SERVER_EX_API_NAME));244STOPSERVERPROC stop_server_proc = reinterpret_cast<STOPSERVERPROC>(245::GetProcAddress(module_handle, STOP_SERVER_API_NAME));246if (start_server_ex_proc == NULL || stop_server_proc == NULL) {247std::wcout << L"Could not find entry point in extracted library: "248<< temp_file_name;249return ERR_FUNCTION_NOT_FOUND;250}251252int port = _wtoi(args.GetValue(PORT_COMMAND_LINE_ARG, L"5555").c_str());253std::wstring host_address = args.GetValue(HOST_COMMAND_LINE_ARG, L"");254std::wstring log_level = args.GetValue(LOGLEVEL_COMMAND_LINE_ARG, L"");255std::wstring log_file = args.GetValue(LOGFILE_COMMAND_LINE_ARG, L"");256bool silent = args.GetValue(SILENT_COMMAND_LINE_ARG,257BOOLEAN_COMMAND_LINE_ARG_MISSING_VALUE).size() == 0;258std::wstring executable_version = GetExecutableVersion();259std::wstring executable_architecture = GetProcessArchitectureDescription();260std::wstring implementation = L"";261std::wstring whitelist = args.GetValue(ACL_COMMAND_LINE_ARG, L"");262263// coerce log level and implementation to uppercase, making the values264// case-insensitive, to match expected values.265std::transform(log_level.begin(),266log_level.end(),267log_level.begin(),268toupper);269std::transform(implementation.begin(),270implementation.end(),271implementation.begin(),272toupper);273274if (args.is_version_requested()) {275std::wcout << L"IEDriverServer.exe"276<< L" " << executable_version277<< L" (" << executable_architecture << L")" << std::endl;278} else {279void* server_value = start_server_ex_proc(port,280host_address,281log_level,282log_file,283executable_version + L" (" + executable_architecture + L")",284whitelist);285if (server_value == NULL) {286std::wcout << L"Failed to start the server with: "287<< L"port = '" << port << L"', "288<< L"host = '" << host_address << L"', "289<< L"log level = '" << log_level << L"', "290<< L"log file = '" << log_file << L"', "291<< L"whitelisted ips = '" << whitelist << L"'.";292return ERR_SERVER_START;293}294if (!silent) {295std::wcout << L"Started InternetExplorerDriver server"296<< L" (" << executable_architecture << L")"297<< std::endl;298std::wcout << executable_version299<< std::endl;300std::wcout << L"Listening on port " << port << std::endl;301if (host_address.size() > 0) {302std::wcout << L"Bound to network adapter with IP address "303<< host_address304<< std::endl;305}306if (log_level.size() > 0) {307std::wcout << L"Log level is set to "308<< log_level309<< std::endl;310}311if (log_file.size() > 0) {312std::wcout << L"Log file is set to "313<< log_file314<< std::endl;315}316if (extraction_path_arg.size() > 0) {317std::wcout << L"Library extracted to "318<< extraction_path_arg319<< std::endl;320}321if (whitelist.size() > 0) {322std::wcout << L"IP addresses allowed to connect are "323<< whitelist324<< std::endl;325} else {326std::wcout << L"Only local connections are allowed"327<< std::endl;328}329}330331// Create the shutdown event and wait for it to be signaled.332DWORD process_id = ::GetCurrentProcessId();333vector<wchar_t> process_id_buffer(10);334_ltow_s(process_id, &process_id_buffer[0], process_id_buffer.size(), 10);335std::wstring process_id_string(&process_id_buffer[0]);336std::wstring event_name = IESERVER_SHUTDOWN_EVENT_NAME + process_id_string;337HANDLE event_handle = ::CreateEvent(NULL,338TRUE,339FALSE,340event_name.c_str());341::WaitForSingleObject(event_handle, INFINITE);342::CloseHandle(event_handle);343stop_server_proc();344}345346::FreeLibrary(module_handle);347::DeleteFile(temp_file_name.c_str());348return 0;349}350351352