2015-04-13 12:09:26 -04:00
|
|
|
// 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");
|
2013-01-11 22:18:32 +01:00
|
|
|
// 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"
|
2014-09-05 02:00:00 +00:00
|
|
|
#include "IEServer.h"
|
2013-01-11 22:18:32 +01:00
|
|
|
#include <algorithm>
|
2014-09-05 02:00:00 +00:00
|
|
|
#include <iostream>
|
2013-01-11 22:18:32 +01:00
|
|
|
#include <map>
|
2014-08-19 21:15:00 +00:00
|
|
|
#include <iostream>
|
2013-01-11 22:18:32 +01:00
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2013-04-10 00:02:15 +04:00
|
|
|
// 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.
|
2017-02-22 07:12:09 -08:00
|
|
|
typedef void* (__cdecl *STARTSERVERPROC)(int, const std::wstring&, const std::wstring&, const std::wstring&, const std::wstring&, const std::wstring&);
|
2013-01-11 22:18:32 +01:00
|
|
|
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"
|
2013-03-20 10:59:48 -04:00
|
|
|
#define START_SERVER_EX_API_NAME "StartServer"
|
2013-01-11 22:18:32 +01:00
|
|
|
#define STOP_SERVER_API_NAME "StopServer"
|
|
|
|
|
|
2013-03-20 10:59:48 -04:00
|
|
|
#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"
|
2015-10-30 15:47:54 -04:00
|
|
|
#define ACL_COMMAND_LINE_ARG L"whitelisted-ips"
|
2013-03-20 10:59:48 -04:00
|
|
|
#define BOOLEAN_COMMAND_LINE_ARG_MISSING_VALUE L"value-not-specified"
|
2013-01-11 22:18:32 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-20 10:59:48 -04:00
|
|
|
std::wstring GetProcessArchitectureDescription() {
|
|
|
|
|
std::wstring arch_description = L"32-bit";
|
2013-01-11 22:18:32 +01:00
|
|
|
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) {
|
2013-03-20 10:59:48 -04:00
|
|
|
arch_description = L"64-bit";
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
::CloseHandle(process_handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return arch_description;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-20 10:59:48 -04:00
|
|
|
std::wstring GetExecutableVersion() {
|
2013-01-11 22:18:32 +01:00
|
|
|
struct LANGANDCODEPAGE {
|
|
|
|
|
WORD language;
|
|
|
|
|
WORD code_page;
|
|
|
|
|
} *lang_info;
|
|
|
|
|
|
|
|
|
|
// get the filename of the executable containing the version resource
|
2013-03-20 10:59:48 -04:00
|
|
|
std::vector<wchar_t> file_name_buffer(MAX_PATH + 1);
|
|
|
|
|
::GetModuleFileNameW(NULL, &file_name_buffer[0], MAX_PATH);
|
2013-01-11 22:18:32 +01:00
|
|
|
|
|
|
|
|
DWORD dummy;
|
2013-03-20 10:59:48 -04:00
|
|
|
DWORD length = ::GetFileVersionInfoSizeW(&file_name_buffer[0],
|
2013-01-11 22:18:32 +01:00
|
|
|
&dummy);
|
|
|
|
|
std::vector<BYTE> version_buffer(length);
|
2013-03-20 10:59:48 -04:00
|
|
|
::GetFileVersionInfoW(&file_name_buffer[0],
|
2013-01-11 22:18:32 +01:00
|
|
|
dummy,
|
|
|
|
|
length,
|
|
|
|
|
&version_buffer[0]);
|
|
|
|
|
|
|
|
|
|
UINT page_count;
|
2013-03-20 10:59:48 -04:00
|
|
|
BOOL query_result = ::VerQueryValueW(&version_buffer[0],
|
|
|
|
|
L"\\VarFileInfo\\Translation",
|
2013-01-11 22:18:32 +01:00
|
|
|
reinterpret_cast<void**>(&lang_info),
|
|
|
|
|
&page_count);
|
|
|
|
|
|
2013-03-20 10:59:48 -04:00
|
|
|
wchar_t sub_block[MAX_PATH];
|
|
|
|
|
_snwprintf_s(sub_block,
|
2013-01-11 22:18:32 +01:00
|
|
|
MAX_PATH,
|
|
|
|
|
MAX_PATH,
|
2013-03-20 10:59:48 -04:00
|
|
|
L"\\StringFileInfo\\%04x%04x\\FileVersion",
|
2013-01-11 22:18:32 +01:00
|
|
|
lang_info->language,
|
|
|
|
|
lang_info->code_page);
|
|
|
|
|
LPVOID value = NULL;
|
|
|
|
|
UINT size;
|
2013-03-20 10:59:48 -04:00
|
|
|
query_result = ::VerQueryValueW(&version_buffer[0],
|
2013-01-11 22:18:32 +01:00
|
|
|
sub_block,
|
|
|
|
|
&value,
|
|
|
|
|
&size);
|
2013-03-20 10:59:48 -04:00
|
|
|
return static_cast<wchar_t*>(value);
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
2015-10-30 15:47:54 -04:00
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
void ShowUsage(void) {
|
|
|
|
|
std::wcout << L"Launches the WebDriver server for the Internet Explorer driver" << std::endl
|
|
|
|
|
<< std::endl
|
|
|
|
|
<< L"IEDriverServer [/port=<port>] [/host=<host>] [/log-level=<level>]" << std::endl
|
|
|
|
|
<< L" [/log-file=<file>] [/extract-path=<path>] [/silent]" << std::endl
|
2016-06-29 05:02:58 -07:00
|
|
|
<< L" [/whitelisted-ips=<whitelisted-ips>] [/version]" << std::endl
|
2013-01-11 22:18:32 +01:00
|
|
|
<< std::endl
|
|
|
|
|
<< L" /port=<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=<host> Specifies the address of the host adapter on which the server" << std::endl
|
|
|
|
|
<< L" will listen for commands." << std::endl
|
|
|
|
|
<< L" /log-level=<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=<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=<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
|
2015-10-30 15:47:54 -04:00
|
|
|
<< L" /silent Suppresses diagnostic output when the server is started." << std::endl
|
|
|
|
|
<< L" /whitelisted-ips=<whitelisted-ips>" << std::endl
|
|
|
|
|
<< L" Comma-separated whitelist of remote IPv4 addresses which" << std::endl
|
2016-06-29 05:02:58 -07:00
|
|
|
<< 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;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int _tmain(int argc, _TCHAR* argv[]) {
|
|
|
|
|
CommandLineArguments args(argc, argv);
|
|
|
|
|
if (args.is_help_requested()) {
|
|
|
|
|
ShowUsage();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
vector<TCHAR> temp_file_name_buffer(MAX_PATH);
|
|
|
|
|
vector<TCHAR> 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]);
|
|
|
|
|
|
2013-03-20 10:59:48 -04:00
|
|
|
std::wstring extraction_path_arg = args.GetValue(EXTRACTPATH_COMMAND_LINE_ARG, L"");
|
2013-01-11 22:18:32 +01:00
|
|
|
if (extraction_path_arg.size() != 0) {
|
2013-03-20 10:59:48 -04:00
|
|
|
extraction_path = extraction_path_arg;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-23 14:59:49 -07:00
|
|
|
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) {
|
2018-09-17 08:12:25 -07:00
|
|
|
::FindClose(file_handle);
|
2018-07-23 14:59:49 -07:00
|
|
|
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];
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-20 10:59:48 -04:00
|
|
|
STARTSERVERPROC start_server_ex_proc = reinterpret_cast<STARTSERVERPROC>(
|
2013-01-11 22:18:32 +01:00
|
|
|
::GetProcAddress(module_handle, START_SERVER_EX_API_NAME));
|
|
|
|
|
STOPSERVERPROC stop_server_proc = reinterpret_cast<STOPSERVERPROC>(
|
|
|
|
|
::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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-20 10:59:48 -04:00
|
|
|
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"");
|
2013-01-11 22:18:32 +01:00
|
|
|
bool silent = args.GetValue(SILENT_COMMAND_LINE_ARG,
|
|
|
|
|
BOOLEAN_COMMAND_LINE_ARG_MISSING_VALUE).size() == 0;
|
2013-03-20 10:59:48 -04:00
|
|
|
std::wstring executable_version = GetExecutableVersion();
|
2015-10-30 15:47:54 -04:00
|
|
|
std::wstring executable_architecture = GetProcessArchitectureDescription();
|
2017-02-22 07:12:09 -08:00
|
|
|
std::wstring implementation = L"";
|
2015-10-30 15:47:54 -04:00
|
|
|
std::wstring whitelist = args.GetValue(ACL_COMMAND_LINE_ARG, L"");
|
2014-09-09 15:12:11 -04:00
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
2016-06-29 05:02:58 -07:00
|
|
|
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;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
2016-06-29 05:02:58 -07:00
|
|
|
if (!silent) {
|
|
|
|
|
std::wcout << L"Started InternetExplorerDriver server"
|
|
|
|
|
<< L" (" << executable_architecture << L")"
|
2013-03-20 10:59:48 -04:00
|
|
|
<< std::endl;
|
2016-06-29 05:02:58 -07:00
|
|
|
std::wcout << executable_version
|
2013-03-20 10:59:48 -04:00
|
|
|
<< std::endl;
|
2016-06-29 05:02:58 -07:00
|
|
|
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;
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-29 05:02:58 -07:00
|
|
|
// Create the shutdown event and wait for it to be signaled.
|
|
|
|
|
DWORD process_id = ::GetCurrentProcessId();
|
|
|
|
|
vector<wchar_t> 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();
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
|
|
|
|
|
::FreeLibrary(module_handle);
|
|
|
|
|
::DeleteFile(temp_file_name.c_str());
|
|
|
|
|
return 0;
|
|
|
|
|
}
|