| Index: tools/telemetry/third_party/webpagereplay/platformsettings.py
|
| diff --git a/tools/telemetry/third_party/webpagereplay/platformsettings.py b/tools/telemetry/third_party/webpagereplay/platformsettings.py
|
| deleted file mode 100644
|
| index 81cb56c760ec7863220ebd15c3dd7297e62654c5..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/third_party/webpagereplay/platformsettings.py
|
| +++ /dev/null
|
| @@ -1,794 +0,0 @@
|
| -#!/usr/bin/env python
|
| -# Copyright 2010 Google Inc. All Rights Reserved.
|
| -#
|
| -# Licensed 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.
|
| -
|
| -"""Provides cross-platform utility functions.
|
| -
|
| -Example:
|
| - import platformsettings
|
| - ip = platformsettings.get_server_ip_address()
|
| -
|
| -Functions with "_temporary_" in their name automatically clean-up upon
|
| -termination (via the atexit module).
|
| -
|
| -For the full list of functions, see the bottom of the file.
|
| -"""
|
| -
|
| -import atexit
|
| -import distutils.spawn
|
| -import distutils.version
|
| -import fileinput
|
| -import logging
|
| -import os
|
| -import platform
|
| -import re
|
| -import socket
|
| -import stat
|
| -import subprocess
|
| -import sys
|
| -import time
|
| -import urlparse
|
| -
|
| -
|
| -class PlatformSettingsError(Exception):
|
| - """Module catch-all error."""
|
| - pass
|
| -
|
| -
|
| -class DnsReadError(PlatformSettingsError):
|
| - """Raised when unable to read DNS settings."""
|
| - pass
|
| -
|
| -
|
| -class DnsUpdateError(PlatformSettingsError):
|
| - """Raised when unable to update DNS settings."""
|
| - pass
|
| -
|
| -
|
| -class NotAdministratorError(PlatformSettingsError):
|
| - """Raised when not running as administrator."""
|
| - pass
|
| -
|
| -
|
| -class CalledProcessError(PlatformSettingsError):
|
| - """Raised when a _check_output() process returns a non-zero exit status."""
|
| - def __init__(self, returncode, cmd):
|
| - super(CalledProcessError, self).__init__()
|
| - self.returncode = returncode
|
| - self.cmd = cmd
|
| -
|
| - def __str__(self):
|
| - return 'Command "%s" returned non-zero exit status %d' % (
|
| - ' '.join(self.cmd), self.returncode)
|
| -
|
| -
|
| -def FindExecutable(executable):
|
| - """Finds the given executable in PATH.
|
| -
|
| - Since WPR may be invoked as sudo, meaning PATH is empty, we also hardcode a
|
| - few common paths.
|
| -
|
| - Returns:
|
| - The fully qualified path with .exe appended if appropriate or None if it
|
| - doesn't exist.
|
| - """
|
| - return distutils.spawn.find_executable(executable,
|
| - os.pathsep.join([os.environ['PATH'],
|
| - '/sbin',
|
| - '/usr/bin',
|
| - '/usr/sbin/',
|
| - '/usr/local/sbin',
|
| - ]))
|
| -
|
| -def HasSniSupport():
|
| - try:
|
| - import OpenSSL
|
| - return (distutils.version.StrictVersion(OpenSSL.__version__) >=
|
| - distutils.version.StrictVersion('0.13'))
|
| - except ImportError:
|
| - return False
|
| -
|
| -
|
| -def SupportsFdLimitControl():
|
| - """Whether the platform supports changing the process fd limit."""
|
| - return os.name is 'posix'
|
| -
|
| -
|
| -def GetFdLimit():
|
| - """Returns a tuple of (soft_limit, hard_limit)."""
|
| - import resource
|
| - return resource.getrlimit(resource.RLIMIT_NOFILE)
|
| -
|
| -
|
| -def AdjustFdLimit(new_soft_limit, new_hard_limit):
|
| - """Sets a new soft and hard limit for max number of fds."""
|
| - import resource
|
| - resource.setrlimit(resource.RLIMIT_NOFILE, (new_soft_limit, new_hard_limit))
|
| -
|
| -
|
| -class SystemProxy(object):
|
| - """A host/port pair for a HTTP or HTTPS proxy configuration."""
|
| -
|
| - def __init__(self, host, port):
|
| - """Initialize a SystemProxy instance.
|
| -
|
| - Args:
|
| - host: a host name or IP address string (e.g. "example.com" or "1.1.1.1").
|
| - port: a port string or integer (e.g. "8888" or 8888).
|
| - """
|
| - self.host = host
|
| - self.port = int(port) if port else None
|
| -
|
| - def __nonzero__(self):
|
| - """True if the host is set."""
|
| - return bool(self.host)
|
| -
|
| - @classmethod
|
| - def from_url(cls, proxy_url):
|
| - """Create a SystemProxy instance.
|
| -
|
| - If proxy_url is None, an empty string, or an invalid URL, the
|
| - SystemProxy instance with have None and None for the host and port
|
| - (no exception is raised).
|
| -
|
| - Args:
|
| - proxy_url: a proxy url string such as "http://proxy.com:8888/".
|
| - Returns:
|
| - a System proxy instance.
|
| - """
|
| - if proxy_url:
|
| - parse_result = urlparse.urlparse(proxy_url)
|
| - return cls(parse_result.hostname, parse_result.port)
|
| - return cls(None, None)
|
| -
|
| -
|
| -class _BasePlatformSettings(object):
|
| -
|
| - def get_system_logging_handler(self):
|
| - """Return a handler for the logging module (optional)."""
|
| - return None
|
| -
|
| - def rerun_as_administrator(self):
|
| - """If needed, rerun the program with administrative privileges.
|
| -
|
| - Raises NotAdministratorError if unable to rerun.
|
| - """
|
| - pass
|
| -
|
| - def timer(self):
|
| - """Return the current time in seconds as a floating point number."""
|
| - return time.time()
|
| -
|
| - def get_server_ip_address(self, is_server_mode=False):
|
| - """Returns the IP address to use for dnsproxy and ipfw."""
|
| - if is_server_mode:
|
| - return socket.gethostbyname(socket.gethostname())
|
| - return '127.0.0.1'
|
| -
|
| - def get_httpproxy_ip_address(self, is_server_mode=False):
|
| - """Returns the IP address to use for httpproxy."""
|
| - if is_server_mode:
|
| - return '0.0.0.0'
|
| - return '127.0.0.1'
|
| -
|
| - def get_system_proxy(self, use_ssl):
|
| - """Returns the system HTTP(S) proxy host, port."""
|
| - del use_ssl
|
| - return SystemProxy(None, None)
|
| -
|
| - def _ipfw_cmd(self):
|
| - raise NotImplementedError
|
| -
|
| - def ipfw(self, *args):
|
| - ipfw_cmd = (self._ipfw_cmd(), ) + args
|
| - return self._check_output(*ipfw_cmd, elevate_privilege=True)
|
| -
|
| - def has_ipfw(self):
|
| - try:
|
| - self.ipfw('list')
|
| - return True
|
| - except AssertionError as e:
|
| - logging.warning('Failed to start ipfw command. '
|
| - 'Error: %s' % e.message)
|
| - return False
|
| -
|
| - def _get_cwnd(self):
|
| - return None
|
| -
|
| - def _set_cwnd(self, args):
|
| - pass
|
| -
|
| - def _elevate_privilege_for_cmd(self, args):
|
| - return args
|
| -
|
| - def _check_output(self, *args, **kwargs):
|
| - """Run Popen(*args) and return its output as a byte string.
|
| -
|
| - Python 2.7 has subprocess.check_output. This is essentially the same
|
| - except that, as a convenience, all the positional args are used as
|
| - command arguments and the |elevate_privilege| kwarg is supported.
|
| -
|
| - Args:
|
| - *args: sequence of program arguments
|
| - elevate_privilege: Run the command with elevated privileges.
|
| - Raises:
|
| - CalledProcessError if the program returns non-zero exit status.
|
| - Returns:
|
| - output as a byte string.
|
| - """
|
| - command_args = [str(a) for a in args]
|
| -
|
| - if os.path.sep not in command_args[0]:
|
| - qualified_command = FindExecutable(command_args[0])
|
| - assert qualified_command, 'Failed to find %s in path' % command_args[0]
|
| - command_args[0] = qualified_command
|
| -
|
| - if kwargs.get('elevate_privilege'):
|
| - command_args = self._elevate_privilege_for_cmd(command_args)
|
| -
|
| - logging.debug(' '.join(command_args))
|
| - process = subprocess.Popen(
|
| - command_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
| - output = process.communicate()[0]
|
| - retcode = process.poll()
|
| - if retcode:
|
| - raise CalledProcessError(retcode, command_args)
|
| - return output
|
| -
|
| - def set_temporary_tcp_init_cwnd(self, cwnd):
|
| - cwnd = int(cwnd)
|
| - original_cwnd = self._get_cwnd()
|
| - if original_cwnd is None:
|
| - raise PlatformSettingsError('Unable to get current tcp init_cwnd.')
|
| - if cwnd == original_cwnd:
|
| - logging.info('TCP init_cwnd already set to target value: %s', cwnd)
|
| - else:
|
| - self._set_cwnd(cwnd)
|
| - if self._get_cwnd() == cwnd:
|
| - logging.info('Changed cwnd to %s', cwnd)
|
| - atexit.register(self._set_cwnd, original_cwnd)
|
| - else:
|
| - logging.error('Unable to update cwnd to %s', cwnd)
|
| -
|
| - def setup_temporary_loopback_config(self):
|
| - """Setup the loopback interface similar to real interface.
|
| -
|
| - We use loopback for much of our testing, and on some systems, loopback
|
| - behaves differently from real interfaces.
|
| - """
|
| - logging.error('Platform does not support loopback configuration.')
|
| -
|
| - def _save_primary_interface_properties(self):
|
| - self._orig_nameserver = self.get_original_primary_nameserver()
|
| -
|
| - def _restore_primary_interface_properties(self):
|
| - self._set_primary_nameserver(self._orig_nameserver)
|
| -
|
| - def _get_primary_nameserver(self):
|
| - raise NotImplementedError
|
| -
|
| - def _set_primary_nameserver(self, _):
|
| - raise NotImplementedError
|
| -
|
| - def get_original_primary_nameserver(self):
|
| - if not hasattr(self, '_original_nameserver'):
|
| - self._original_nameserver = self._get_primary_nameserver()
|
| - logging.info('Saved original primary DNS nameserver: %s',
|
| - self._original_nameserver)
|
| - return self._original_nameserver
|
| -
|
| - def set_temporary_primary_nameserver(self, nameserver):
|
| - self._save_primary_interface_properties()
|
| - self._set_primary_nameserver(nameserver)
|
| - if self._get_primary_nameserver() == nameserver:
|
| - logging.info('Changed temporary primary nameserver to %s', nameserver)
|
| - atexit.register(self._restore_primary_interface_properties)
|
| - else:
|
| - raise self._get_dns_update_error()
|
| -
|
| -
|
| -class _PosixPlatformSettings(_BasePlatformSettings):
|
| -
|
| - # pylint: disable=abstract-method
|
| - # Suppress lint check for _get_primary_nameserver & _set_primary_nameserver
|
| -
|
| - def rerun_as_administrator(self):
|
| - """If needed, rerun the program with administrative privileges.
|
| -
|
| - Raises NotAdministratorError if unable to rerun.
|
| - """
|
| - if os.geteuid() != 0:
|
| - logging.warn('Rerunning with sudo: %s', sys.argv)
|
| - os.execv('/usr/bin/sudo', ['--'] + sys.argv)
|
| -
|
| - def _elevate_privilege_for_cmd(self, args):
|
| - def IsSetUID(path):
|
| - return (os.stat(path).st_mode & stat.S_ISUID) == stat.S_ISUID
|
| -
|
| - def IsElevated():
|
| - p = subprocess.Popen(
|
| - ['sudo', '-nv'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
| - stderr=subprocess.STDOUT)
|
| - stdout = p.communicate()[0]
|
| - # Some versions of sudo set the returncode based on whether sudo requires
|
| - # a password currently. Other versions return output when password is
|
| - # required and no output when the user is already authenticated.
|
| - return not p.returncode and not stdout
|
| -
|
| - if not IsSetUID(args[0]):
|
| - args = ['sudo'] + args
|
| -
|
| - if not IsElevated():
|
| - print 'WPR needs to run %s under sudo. Please authenticate.' % args[1]
|
| - subprocess.check_call(['sudo', '-v']) # Synchronously authenticate.
|
| -
|
| - prompt = ('Would you like to always allow %s to run without sudo '
|
| - '(via `sudo chmod +s %s`)? (y/N)' % (args[1], args[1]))
|
| - if raw_input(prompt).lower() == 'y':
|
| - subprocess.check_call(['sudo', 'chmod', '+s', args[1]])
|
| - return args
|
| -
|
| - def get_system_proxy(self, use_ssl):
|
| - """Returns the system HTTP(S) proxy host, port."""
|
| - proxy_url = os.environ.get('https_proxy' if use_ssl else 'http_proxy')
|
| - return SystemProxy.from_url(proxy_url)
|
| -
|
| - def _ipfw_cmd(self):
|
| - return 'ipfw'
|
| -
|
| - def _get_dns_update_error(self):
|
| - return DnsUpdateError('Did you run under sudo?')
|
| -
|
| - def _sysctl(self, *args, **kwargs):
|
| - sysctl_args = [FindExecutable('sysctl')]
|
| - if kwargs.get('use_sudo'):
|
| - sysctl_args = self._elevate_privilege_for_cmd(sysctl_args)
|
| - sysctl_args.extend(str(a) for a in args)
|
| - sysctl = subprocess.Popen(
|
| - sysctl_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
| - stdout = sysctl.communicate()[0]
|
| - return sysctl.returncode, stdout
|
| -
|
| - def has_sysctl(self, name):
|
| - if not hasattr(self, 'has_sysctl_cache'):
|
| - self.has_sysctl_cache = {}
|
| - if name not in self.has_sysctl_cache:
|
| - self.has_sysctl_cache[name] = self._sysctl(name)[0] == 0
|
| - return self.has_sysctl_cache[name]
|
| -
|
| - def set_sysctl(self, name, value):
|
| - rv = self._sysctl('%s=%s' % (name, value), use_sudo=True)[0]
|
| - if rv != 0:
|
| - logging.error('Unable to set sysctl %s: %s', name, rv)
|
| -
|
| - def get_sysctl(self, name):
|
| - rv, value = self._sysctl('-n', name)
|
| - if rv == 0:
|
| - return value
|
| - else:
|
| - logging.error('Unable to get sysctl %s: %s', name, rv)
|
| - return None
|
| -
|
| -
|
| -class _OsxPlatformSettings(_PosixPlatformSettings):
|
| - LOCAL_SLOWSTART_MIB_NAME = 'net.inet.tcp.local_slowstart_flightsize'
|
| -
|
| - def _scutil(self, cmd):
|
| - scutil = subprocess.Popen([FindExecutable('scutil')],
|
| - stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
| - return scutil.communicate(cmd)[0]
|
| -
|
| - def _ifconfig(self, *args):
|
| - return self._check_output('ifconfig', *args, elevate_privilege=True)
|
| -
|
| - def set_sysctl(self, name, value):
|
| - rv = self._sysctl('-w', '%s=%s' % (name, value), use_sudo=True)[0]
|
| - if rv != 0:
|
| - logging.error('Unable to set sysctl %s: %s', name, rv)
|
| -
|
| - def _get_cwnd(self):
|
| - return int(self.get_sysctl(self.LOCAL_SLOWSTART_MIB_NAME))
|
| -
|
| - def _set_cwnd(self, size):
|
| - self.set_sysctl(self.LOCAL_SLOWSTART_MIB_NAME, size)
|
| -
|
| - def _get_loopback_mtu(self):
|
| - config = self._ifconfig('lo0')
|
| - match = re.search(r'\smtu\s+(\d+)', config)
|
| - return int(match.group(1)) if match else None
|
| -
|
| - def setup_temporary_loopback_config(self):
|
| - """Configure loopback to temporarily use reasonably sized frames.
|
| -
|
| - OS X uses jumbo frames by default (16KB).
|
| - """
|
| - TARGET_LOOPBACK_MTU = 1500
|
| - original_mtu = self._get_loopback_mtu()
|
| - if original_mtu is None:
|
| - logging.error('Unable to read loopback mtu. Setting left unchanged.')
|
| - return
|
| - if original_mtu == TARGET_LOOPBACK_MTU:
|
| - logging.debug('Loopback MTU already has target value: %d', original_mtu)
|
| - else:
|
| - self._ifconfig('lo0', 'mtu', TARGET_LOOPBACK_MTU)
|
| - if self._get_loopback_mtu() == TARGET_LOOPBACK_MTU:
|
| - logging.debug('Set loopback MTU to %d (was %d)',
|
| - TARGET_LOOPBACK_MTU, original_mtu)
|
| - atexit.register(self._ifconfig, 'lo0', 'mtu', original_mtu)
|
| - else:
|
| - logging.error('Unable to change loopback MTU from %d to %d',
|
| - original_mtu, TARGET_LOOPBACK_MTU)
|
| -
|
| - def _get_dns_service_key(self):
|
| - output = self._scutil('show State:/Network/Global/IPv4')
|
| - lines = output.split('\n')
|
| - for line in lines:
|
| - key_value = line.split(' : ')
|
| - if key_value[0] == ' PrimaryService':
|
| - return 'State:/Network/Service/%s/DNS' % key_value[1]
|
| - raise DnsReadError('Unable to find DNS service key: %s', output)
|
| -
|
| - def _get_primary_nameserver(self):
|
| - output = self._scutil('show %s' % self._get_dns_service_key())
|
| - match = re.search(
|
| - br'ServerAddresses\s+:\s+<array>\s+{\s+0\s+:\s+((\d{1,3}\.){3}\d{1,3})',
|
| - output)
|
| - if match:
|
| - return match.group(1)
|
| - else:
|
| - raise DnsReadError('Unable to find primary DNS server: %s', output)
|
| -
|
| - def _set_primary_nameserver(self, dns):
|
| - command = '\n'.join([
|
| - 'd.init',
|
| - 'd.add ServerAddresses * %s' % dns,
|
| - 'set %s' % self._get_dns_service_key()
|
| - ])
|
| - self._scutil(command)
|
| -
|
| -
|
| -class _FreeBSDPlatformSettings(_PosixPlatformSettings):
|
| - """Partial implementation for FreeBSD. Does not allow a DNS server to be
|
| - launched nor ipfw to be used.
|
| - """
|
| - RESOLV_CONF = '/etc/resolv.conf'
|
| -
|
| - def _get_default_route_line(self):
|
| - raise NotImplementedError
|
| -
|
| - def _set_cwnd(self, cwnd):
|
| - raise NotImplementedError
|
| -
|
| - def _get_cwnd(self):
|
| - raise NotImplementedError
|
| -
|
| - def setup_temporary_loopback_config(self):
|
| - raise NotImplementedError
|
| -
|
| - def _write_resolve_conf(self, dns):
|
| - raise NotImplementedError
|
| -
|
| - def _get_primary_nameserver(self):
|
| - try:
|
| - resolv_file = open(self.RESOLV_CONF)
|
| - except IOError:
|
| - raise DnsReadError()
|
| - for line in resolv_file:
|
| - if line.startswith('nameserver '):
|
| - return line.split()[1]
|
| - raise DnsReadError()
|
| -
|
| - def _set_primary_nameserver(self, dns):
|
| - raise NotImplementedError
|
| -
|
| -
|
| -class _LinuxPlatformSettings(_PosixPlatformSettings):
|
| - """The following thread recommends a way to update DNS on Linux:
|
| -
|
| - http://ubuntuforums.org/showthread.php?t=337553
|
| -
|
| - sudo cp /etc/dhcp3/dhclient.conf /etc/dhcp3/dhclient.conf.bak
|
| - sudo gedit /etc/dhcp3/dhclient.conf
|
| - #prepend domain-name-servers 127.0.0.1;
|
| - prepend domain-name-servers 208.67.222.222, 208.67.220.220;
|
| -
|
| - prepend domain-name-servers 208.67.222.222, 208.67.220.220;
|
| - request subnet-mask, broadcast-address, time-offset, routers,
|
| - domain-name, domain-name-servers, host-name,
|
| - netbios-name-servers, netbios-scope;
|
| - #require subnet-mask, domain-name-servers;
|
| -
|
| - sudo /etc/init.d/networking restart
|
| -
|
| - The code below does not try to change dchp and does not restart networking.
|
| - Update this as needed to make it more robust on more systems.
|
| - """
|
| - RESOLV_CONF = '/etc/resolv.conf'
|
| - ROUTE_RE = re.compile('initcwnd (\d+)')
|
| - TCP_BASE_MSS = 'net.ipv4.tcp_base_mss'
|
| - TCP_MTU_PROBING = 'net.ipv4.tcp_mtu_probing'
|
| -
|
| - def _get_default_route_line(self):
|
| - stdout = self._check_output('ip', 'route')
|
| - for line in stdout.split('\n'):
|
| - if line.startswith('default'):
|
| - return line
|
| - return None
|
| -
|
| - def _set_cwnd(self, cwnd):
|
| - default_line = self._get_default_route_line()
|
| - self._check_output(
|
| - 'ip', 'route', 'change', default_line, 'initcwnd', str(cwnd))
|
| -
|
| - def _get_cwnd(self):
|
| - default_line = self._get_default_route_line()
|
| - m = self.ROUTE_RE.search(default_line)
|
| - if m:
|
| - return int(m.group(1))
|
| - # If 'initcwnd' wasn't found, then 0 means it's the system default.
|
| - return 0
|
| -
|
| - def setup_temporary_loopback_config(self):
|
| - """Setup Linux to temporarily use reasonably sized frames.
|
| -
|
| - Linux uses jumbo frames by default (16KB), using the combination
|
| - of MTU probing and a base MSS makes it use normal sized packets.
|
| -
|
| - The reason this works is because tcp_base_mss is only used when MTU
|
| - probing is enabled. And since we're using the max value, it will
|
| - always use the reasonable size. This is relevant for server-side realism.
|
| - The client-side will vary depending on the client TCP stack config.
|
| - """
|
| - ENABLE_MTU_PROBING = 2
|
| - original_probing = self.get_sysctl(self.TCP_MTU_PROBING)
|
| - self.set_sysctl(self.TCP_MTU_PROBING, ENABLE_MTU_PROBING)
|
| - atexit.register(self.set_sysctl, self.TCP_MTU_PROBING, original_probing)
|
| -
|
| - TCP_FULL_MSS = 1460
|
| - original_mss = self.get_sysctl(self.TCP_BASE_MSS)
|
| - self.set_sysctl(self.TCP_BASE_MSS, TCP_FULL_MSS)
|
| - atexit.register(self.set_sysctl, self.TCP_BASE_MSS, original_mss)
|
| -
|
| - def _write_resolve_conf(self, dns):
|
| - is_first_nameserver_replaced = False
|
| - # The fileinput module uses sys.stdout as the edited file output.
|
| - for line in fileinput.input(self.RESOLV_CONF, inplace=1, backup='.bak'):
|
| - if line.startswith('nameserver ') and not is_first_nameserver_replaced:
|
| - print 'nameserver %s' % dns
|
| - is_first_nameserver_replaced = True
|
| - else:
|
| - print line,
|
| - if not is_first_nameserver_replaced:
|
| - raise DnsUpdateError('Could not find a suitable nameserver entry in %s' %
|
| - self.RESOLV_CONF)
|
| -
|
| - def _get_primary_nameserver(self):
|
| - try:
|
| - resolv_file = open(self.RESOLV_CONF)
|
| - except IOError:
|
| - raise DnsReadError()
|
| - for line in resolv_file:
|
| - if line.startswith('nameserver '):
|
| - return line.split()[1]
|
| - raise DnsReadError()
|
| -
|
| - def _set_primary_nameserver(self, dns):
|
| - """Replace the first nameserver entry with the one given."""
|
| - try:
|
| - self._write_resolve_conf(dns)
|
| - except OSError, e:
|
| - if 'Permission denied' in e:
|
| - raise self._get_dns_update_error()
|
| - raise
|
| -
|
| -
|
| -class _WindowsPlatformSettings(_BasePlatformSettings):
|
| -
|
| - # pylint: disable=abstract-method
|
| - # Suppress lint check for _ipfw_cmd
|
| -
|
| - def get_system_logging_handler(self):
|
| - """Return a handler for the logging module (optional).
|
| -
|
| - For Windows, output can be viewed with DebugView.
|
| - http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
|
| - """
|
| - import ctypes
|
| - output_debug_string = ctypes.windll.kernel32.OutputDebugStringA
|
| - output_debug_string.argtypes = [ctypes.c_char_p]
|
| - class DebugViewHandler(logging.Handler):
|
| - def emit(self, record):
|
| - output_debug_string('[wpr] ' + self.format(record))
|
| - return DebugViewHandler()
|
| -
|
| - def rerun_as_administrator(self):
|
| - """If needed, rerun the program with administrative privileges.
|
| -
|
| - Raises NotAdministratorError if unable to rerun.
|
| - """
|
| - import ctypes
|
| - if not ctypes.windll.shell32.IsUserAnAdmin():
|
| - raise NotAdministratorError('Rerun with administrator privileges.')
|
| - #os.execv('runas', sys.argv) # TODO: replace needed Windows magic
|
| -
|
| - def timer(self):
|
| - """Return the current time in seconds as a floating point number.
|
| -
|
| - From time module documentation:
|
| - On Windows, this function [time.clock()] returns wall-clock
|
| - seconds elapsed since the first call to this function, as a
|
| - floating point number, based on the Win32 function
|
| - QueryPerformanceCounter(). The resolution is typically better
|
| - than one microsecond.
|
| - """
|
| - return time.clock()
|
| -
|
| - def _arp(self, *args):
|
| - return self._check_output('arp', *args)
|
| -
|
| - def _route(self, *args):
|
| - return self._check_output('route', *args)
|
| -
|
| - def _ipconfig(self, *args):
|
| - return self._check_output('ipconfig', *args)
|
| -
|
| - def _get_mac_address(self, ip):
|
| - """Return the MAC address for the given ip."""
|
| - ip_re = re.compile(r'^\s*IP(?:v4)? Address[ .]+:\s+([0-9.]+)')
|
| - for line in self._ipconfig('/all').splitlines():
|
| - if line[:1].isalnum():
|
| - current_ip = None
|
| - current_mac = None
|
| - elif ':' in line:
|
| - line = line.strip()
|
| - ip_match = ip_re.match(line)
|
| - if ip_match:
|
| - current_ip = ip_match.group(1)
|
| - elif line.startswith('Physical Address'):
|
| - current_mac = line.split(':', 1)[1].lstrip()
|
| - if current_ip == ip and current_mac:
|
| - return current_mac
|
| - return None
|
| -
|
| - def setup_temporary_loopback_config(self):
|
| - """On Windows, temporarily route the server ip to itself."""
|
| - ip = self.get_server_ip_address()
|
| - mac_address = self._get_mac_address(ip)
|
| - if self.mac_address:
|
| - self._arp('-s', ip, self.mac_address)
|
| - self._route('add', ip, ip, 'mask', '255.255.255.255')
|
| - atexit.register(self._arp, '-d', ip)
|
| - atexit.register(self._route, 'delete', ip, ip, 'mask', '255.255.255.255')
|
| - else:
|
| - logging.warn('Unable to configure loopback: MAC address not found.')
|
| - # TODO(slamm): Configure cwnd, MTU size
|
| -
|
| - def _get_dns_update_error(self):
|
| - return DnsUpdateError('Did you run as administrator?')
|
| -
|
| - def _netsh_show_dns(self):
|
| - """Return DNS information:
|
| -
|
| - Example output:
|
| - Configuration for interface "Local Area Connection 3"
|
| - DNS servers configured through DHCP: None
|
| - Register with which suffix: Primary only
|
| -
|
| - Configuration for interface "Wireless Network Connection 2"
|
| - DNS servers configured through DHCP: 192.168.1.1
|
| - Register with which suffix: Primary only
|
| - """
|
| - return self._check_output('netsh', 'interface', 'ip', 'show', 'dns')
|
| -
|
| - def _netsh_set_dns(self, iface_name, addr):
|
| - """Modify DNS information on the primary interface."""
|
| - output = self._check_output('netsh', 'interface', 'ip', 'set', 'dns',
|
| - iface_name, 'static', addr)
|
| -
|
| - def _netsh_set_dns_dhcp(self, iface_name):
|
| - """Modify DNS information on the primary interface."""
|
| - output = self._check_output('netsh', 'interface', 'ip', 'set', 'dns',
|
| - iface_name, 'dhcp')
|
| -
|
| - def _get_interfaces_with_dns(self):
|
| - output = self._netsh_show_dns()
|
| - lines = output.split('\n')
|
| - iface_re = re.compile(r'^Configuration for interface \"(?P<name>.*)\"')
|
| - dns_re = re.compile(r'(?P<kind>.*):\s+(?P<dns>\d+\.\d+\.\d+\.\d+)')
|
| - iface_name = None
|
| - iface_dns = None
|
| - iface_kind = None
|
| - ifaces = []
|
| - for line in lines:
|
| - iface_match = iface_re.match(line)
|
| - if iface_match:
|
| - iface_name = iface_match.group('name')
|
| - dns_match = dns_re.match(line)
|
| - if dns_match:
|
| - iface_dns = dns_match.group('dns')
|
| - iface_dns_config = dns_match.group('kind').strip()
|
| - if iface_dns_config == "Statically Configured DNS Servers":
|
| - iface_kind = "static"
|
| - elif iface_dns_config == "DNS servers configured through DHCP":
|
| - iface_kind = "dhcp"
|
| - if iface_name and iface_dns and iface_kind:
|
| - ifaces.append((iface_dns, iface_name, iface_kind))
|
| - iface_name = None
|
| - iface_dns = None
|
| - return ifaces
|
| -
|
| - def _save_primary_interface_properties(self):
|
| - # TODO(etienneb): On windows, an interface can have multiple DNS server
|
| - # configured. We should save/restore all of them.
|
| - ifaces = self._get_interfaces_with_dns()
|
| - self._primary_interfaces = ifaces
|
| -
|
| - def _restore_primary_interface_properties(self):
|
| - for iface in self._primary_interfaces:
|
| - (iface_dns, iface_name, iface_kind) = iface
|
| - self._netsh_set_dns(iface_name, iface_dns)
|
| - if iface_kind == "dhcp":
|
| - self._netsh_set_dns_dhcp(iface_name)
|
| -
|
| - def _get_primary_nameserver(self):
|
| - ifaces = self._get_interfaces_with_dns()
|
| - if not len(ifaces):
|
| - raise DnsUpdateError("Interface with valid DNS configured not found.")
|
| - (iface_dns, iface_name, iface_kind) = ifaces[0]
|
| - return iface_dns
|
| -
|
| - def _set_primary_nameserver(self, dns):
|
| - for iface in self._primary_interfaces:
|
| - (iface_dns, iface_name, iface_kind) = iface
|
| - self._netsh_set_dns(iface_name, dns)
|
| -
|
| -
|
| -class _WindowsXpPlatformSettings(_WindowsPlatformSettings):
|
| - def _ipfw_cmd(self):
|
| - return (r'third_party\ipfw_win32\ipfw.exe',)
|
| -
|
| -
|
| -def _new_platform_settings(system, release):
|
| - """Make a new instance of PlatformSettings for the current system."""
|
| - if system == 'Darwin':
|
| - return _OsxPlatformSettings()
|
| - if system == 'Linux':
|
| - return _LinuxPlatformSettings()
|
| - if system == 'Windows' and release == 'XP':
|
| - return _WindowsXpPlatformSettings()
|
| - if system == 'Windows':
|
| - return _WindowsPlatformSettings()
|
| - if system == 'FreeBSD':
|
| - return _FreeBSDPlatformSettings()
|
| - raise NotImplementedError('Sorry %s %s is not supported.' % (system, release))
|
| -
|
| -
|
| -# Create one instance of the platform-specific settings and
|
| -# make the functions available at the module-level.
|
| -_inst = _new_platform_settings(platform.system(), platform.release())
|
| -
|
| -get_system_logging_handler = _inst.get_system_logging_handler
|
| -rerun_as_administrator = _inst.rerun_as_administrator
|
| -timer = _inst.timer
|
| -
|
| -get_server_ip_address = _inst.get_server_ip_address
|
| -get_httpproxy_ip_address = _inst.get_httpproxy_ip_address
|
| -get_system_proxy = _inst.get_system_proxy
|
| -ipfw = _inst.ipfw
|
| -has_ipfw = _inst.has_ipfw
|
| -set_temporary_tcp_init_cwnd = _inst.set_temporary_tcp_init_cwnd
|
| -setup_temporary_loopback_config = _inst.setup_temporary_loopback_config
|
| -
|
| -get_original_primary_nameserver = _inst.get_original_primary_nameserver
|
| -set_temporary_primary_nameserver = _inst.set_temporary_primary_nameserver
|
|
|