From 1c38e1f5f64c5da8a39a890d9205dd49297a9914 Mon Sep 17 00:00:00 2001 From: schlamar Date: Thu, 20 Apr 2017 16:19:58 +0200 Subject: [PATCH] proxy bypass on Windows without DNS lookups --- requests/compat.py | 6 ++++-- requests/utils.py | 52 ++++++++++++++++++++++++++++++++++++++++++++- tests/test_utils.py | 27 +++++++++++++---------- 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/requests/compat.py b/requests/compat.py index f88e600d..a6452c87 100644 --- a/requests/compat.py +++ b/requests/compat.py @@ -37,7 +37,9 @@ except (ImportError, SyntaxError): # --------- if is_py2: - from urllib import quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, proxy_bypass + from urllib import ( + quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, + proxy_bypass, proxy_bypass_environment, getproxies_environment) from urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefrag from urllib2 import parse_http_list import cookielib @@ -54,7 +56,7 @@ if is_py2: elif is_py3: from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag - from urllib.request import parse_http_list, getproxies, proxy_bypass + from urllib.request import parse_http_list, getproxies, proxy_bypass, proxy_bypass_environment, getproxies_environment from http import cookiejar as cookielib from http.cookies import Morsel from io import StringIO diff --git a/requests/utils.py b/requests/utils.py index 6f783cf1..6feaaf38 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -14,6 +14,7 @@ import collections import contextlib import io import os +import platform import re import socket import struct @@ -26,7 +27,8 @@ from ._internal_utils import to_native_string from .compat import parse_http_list as _parse_list_header from .compat import ( quote, urlparse, bytes, str, OrderedDict, unquote, getproxies, - proxy_bypass, urlunparse, basestring, integer_types) + proxy_bypass, urlunparse, basestring, integer_types, is_py3, + proxy_bypass_environment, getproxies_environment) from .cookies import cookiejar_from_dict from .structures import CaseInsensitiveDict from .exceptions import ( @@ -37,6 +39,54 @@ NETRC_FILES = ('.netrc', '_netrc') DEFAULT_CA_BUNDLE_PATH = certs.where() +if platform.system() == 'Windows': + # provide a proxy_bypass version on Windows without DNS lookups + + def proxy_bypass_registry(host): + if is_py3: + import winreg + else: + import _winreg as winreg + try: + internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, + r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') + proxyEnable = winreg.QueryValueEx(internetSettings, + 'ProxyEnable')[0] + proxyOverride = winreg.QueryValueEx(internetSettings, + 'ProxyOverride')[0] + except OSError: + return False + if not proxyEnable or not proxyOverride: + return False + + # make a check value list from the registry entry: replace the + # '' string by the localhost entry and the corresponding + # canonical entry. + proxyOverride = proxyOverride.split(';') + # now check if we match one of the registry values. + for test in proxyOverride: + if test == '': + if '.' not in host: + return True + test = test.replace(".", r"\.") # mask dots + test = test.replace("*", r".*") # change glob sequence + test = test.replace("?", r".") # change glob char + if re.match(test, host, re.I): + return True + return False + + def proxy_bypass(host): # noqa + """Return True, if the host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, + or the registry. + """ + if getproxies_environment(): + return proxy_bypass_environment(host) + else: + return proxy_bypass_registry(host) + + def dict_to_sequence(d): """Returns an internal sequence dictionary update.""" diff --git a/tests/test_utils.py b/tests/test_utils.py index fa5da071..0b37e57f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -604,20 +604,25 @@ def test_should_bypass_proxies_no_proxy( @pytest.mark.skipif(os.name != 'nt', reason='Test only on Windows') @pytest.mark.parametrize( - 'url, expected', ( - ('http://192.168.0.1:5000/', True), - ('http://192.168.0.1/', True), - ('http://172.16.1.1/', True), - ('http://172.16.1.1:5000/', True), - ('http://localhost.localdomain:5000/v1.0/', True), - ('http://172.16.1.22/', False), - ('http://172.16.1.22:5000/', False), - ('http://google.com:5000/v1.0/', False), + 'url, expected, override', ( + ('http://192.168.0.1:5000/', True, None), + ('http://192.168.0.1/', True, None), + ('http://172.16.1.1/', True, None), + ('http://172.16.1.1:5000/', True, None), + ('http://localhost.localdomain:5000/v1.0/', True, None), + ('http://172.16.1.22/', False, None), + ('http://172.16.1.22:5000/', False, None), + ('http://google.com:5000/v1.0/', False, None), + ('http://mylocalhostname:5000/v1.0/', True, ''), + ('http://192.168.0.1/', False, ''), )) -def test_should_bypass_proxies_win_registry(url, expected, monkeypatch): +def test_should_bypass_proxies_win_registry(url, expected, override, + monkeypatch): """Tests for function should_bypass_proxies to check if proxy can be bypassed or not with Windows registry settings """ + if override is None: + override = '192.168.*;127.0.0.1;localhost.localdomain;172.16.1.1' if compat.is_py3: import winreg else: @@ -637,7 +642,7 @@ def test_should_bypass_proxies_win_registry(url, expected, monkeypatch): if value_name == 'ProxyEnable': return [1] elif value_name == 'ProxyOverride': - return ['192.168.*;127.0.0.1;localhost.localdomain;172.16.1.1'] + return [override] monkeypatch.setenv('http_proxy', '') monkeypatch.setenv('https_proxy', '')