diff options
author | binji <binji@chromium.org> | 2015-02-04 15:11:21 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-04 23:12:17 +0000 |
commit | 6d97ebda5078f7f62e6dd47f316ad43fabb64d07 (patch) | |
tree | 4c264ddcd7467299d18124ee8b6a571a0e8654ef /native_client_sdk/src | |
parent | a5a10a846c2cab558a556af0e7ec0cd1028ef737 (diff) | |
download | chromium_src-6d97ebda5078f7f62e6dd47f316ad43fabb64d07.zip chromium_src-6d97ebda5078f7f62e6dd47f316ad43fabb64d07.tar.gz chromium_src-6d97ebda5078f7f62e6dd47f316ad43fabb64d07.tar.bz2 |
[NaCl SDK] Update fancy_urllib to more recent version.
This includes a fix for Python 2.7.7, which removed the
HTTPSConnection._set_hostport function.
BUG=410545
R=sbc@chromium.org
Review URL: https://codereview.chromium.org/904513005
Cr-Commit-Position: refs/heads/master@{#314672}
Diffstat (limited to 'native_client_sdk/src')
-rw-r--r-- | native_client_sdk/src/build_tools/sdk_tools/third_party/fancy_urllib/__init__.py | 162 |
1 files changed, 112 insertions, 50 deletions
diff --git a/native_client_sdk/src/build_tools/sdk_tools/third_party/fancy_urllib/__init__.py b/native_client_sdk/src/build_tools/sdk_tools/third_party/fancy_urllib/__init__.py index dfbad4d..d4e7b27 100644 --- a/native_client_sdk/src/build_tools/sdk_tools/third_party/fancy_urllib/__init__.py +++ b/native_client_sdk/src/build_tools/sdk_tools/third_party/fancy_urllib/__init__.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python -# # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software # Foundation; All Rights Reserved @@ -14,13 +12,12 @@ __author__ = "{frew,nick.johnson}@google.com (Fred Wulff and Nick Johnson)" import base64 import httplib import logging -import re import socket -import urllib2 - +from urllib import splitpasswd from urllib import splittype from urllib import splituser -from urllib import splitpasswd +import urllib2 + class InvalidCertificateException(httplib.HTTPException): """Raised when a certificate is provided with an invalid hostname.""" @@ -31,6 +28,7 @@ class InvalidCertificateException(httplib.HTTPException): Args: host: The hostname the connection was made to. cert: The SSL certificate (as a dictionary) the host returned. + reason: user readable error reason. """ httplib.HTTPException.__init__(self) self.host = host @@ -38,21 +36,35 @@ class InvalidCertificateException(httplib.HTTPException): self.reason = reason def __str__(self): - return ('Host %s returned an invalid certificate (%s): %s\n' - 'To learn more, see ' - 'http://code.google.com/appengine/kb/general.html#rpcssl' % + return ("Host %s returned an invalid certificate (%s): %s\n" + "To learn more, see " + "http://code.google.com/appengine/kb/general.html#rpcssl" % (self.host, self.reason, self.cert)) + +try: + import ssl + _CAN_VALIDATE_CERTS = True +except ImportError: + _CAN_VALIDATE_CERTS = False + + def can_validate_certs(): """Return True if we have the SSL package and can validate certificates.""" - try: - import ssl - return True - except ImportError: - return False - -def _create_fancy_connection(tunnel_host=None, key_file=None, - cert_file=None, ca_certs=None): + return _CAN_VALIDATE_CERTS + + +# Reexport SSLError so clients don't have to to do their own checking for ssl's +# existence. +if can_validate_certs(): + SSLError = ssl.SSLError +else: + SSLError = None + + +def create_fancy_connection(tunnel_host=None, key_file=None, + cert_file=None, ca_certs=None, + proxy_authorization=None): # This abomination brought to you by the fact that # the HTTPHandler creates the connection instance in the middle # of do_open so we need to add the tunnel host to the class. @@ -70,28 +82,64 @@ def _create_fancy_connection(tunnel_host=None, key_file=None, self.key_file = key_file self.cert_file = cert_file self.ca_certs = ca_certs - try: - import ssl + if can_validate_certs(): if self.ca_certs: self.cert_reqs = ssl.CERT_REQUIRED else: self.cert_reqs = ssl.CERT_NONE - except ImportError: - pass + + def _get_hostport(self, host, port): + # Python 2.7.7rc1 (hg r90728:568041fd8090), 3.4.1 and 3.5 rename + # _set_hostport to _get_hostport and changes it's functionality. The + # Python 2.7.7rc1 version of this method is included here for + # compatibility with earlier versions of Python. Without this, HTTPS over + # HTTP CONNECT proxies cannot be used. + + # This method may be removed if compatibility with Python <2.7.7rc1 is not + # required. + + # Python bug: http://bugs.python.org/issue7776 + if port is None: + i = host.rfind(":") + j = host.rfind("]") # ipv6 addresses have [...] + if i > j: + try: + port = int(host[i+1:]) + except ValueError: + if host[i+1:] == "": # http://foo.com:/ == http://foo.com/ + port = self.default_port + else: + raise httplib.InvalidURL("nonnumeric port: '%s'" % host[i+1:]) + host = host[:i] + else: + port = self.default_port + if host and host[0] == "[" and host[-1] == "]": + host = host[1:-1] + + return (host, port) def _tunnel(self): - self._set_hostport(self._tunnel_host, None) + self.host, self.port = self._get_hostport(self._tunnel_host, None) logging.info("Connecting through tunnel to: %s:%d", self.host, self.port) - self.send("CONNECT %s:%d HTTP/1.0\r\n\r\n" % (self.host, self.port)) + + self.send("CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port)) + + if proxy_authorization: + self.send("Proxy-Authorization: %s\r\n" % proxy_authorization) + + # blank line + self.send("\r\n") + response = self.response_class(self.sock, strict=self.strict, method=self._method) + # pylint: disable=protected-access (_, code, message) = response._read_status() if code != 200: self.close() - raise socket.error, "Tunnel connection failed: %d %s" % ( - code, message.strip()) + raise socket.error("Tunnel connection failed: %d %s" % + (code, message.strip())) while True: line = response.fp.readline() @@ -106,15 +154,15 @@ def _create_fancy_connection(tunnel_host=None, key_file=None, Returns: list: A list of valid host globs. """ - if 'subjectAltName' in cert: - return [x[1] for x in cert['subjectAltName'] if x[0].lower() == 'dns'] + if "subjectAltName" in cert: + return [x[1] for x in cert["subjectAltName"] if x[0].lower() == "dns"] else: # Return a list of commonName fields - return [x[0][1] for x in cert['subject'] - if x[0][0].lower() == 'commonname'] + return [x[0][1] for x in cert["subject"] + if x[0][0].lower() == "commonname"] def _validate_certificate_hostname(self, cert, hostname): - """Validates that a given hostname is valid for an SSL certificate. + """Perform RFC2818/6125 validation against a cert and hostname. Args: cert: A dictionary representing an SSL certificate. @@ -124,14 +172,19 @@ def _create_fancy_connection(tunnel_host=None, key_file=None, """ hosts = self._get_valid_hosts_for_cert(cert) for host in hosts: - # Convert the glob-style hostname expression (eg, '*.google.com') into a - # valid regular expression. - host_re = host.replace('.', '\.').replace('*', '[^.]*') - if re.search('^%s$' % (host_re,), hostname, re.I): + # Wildcards are only valid when the * exists at the end of the last + # (left-most) label, and there are at least 3 labels in the expression. + if ("*." in host and host.count("*") == 1 and + host.count(".") > 1 and "." in hostname): + left_expected, right_expected = host.split("*.") + left_hostname, right_hostname = hostname.split(".", 1) + if (left_hostname.startswith(left_expected) and + right_expected == right_hostname): + return True + elif host == hostname: return True return False - def connect(self): # TODO(frew): When we drop support for <2.6 (in the far distant future), # change this to socket.create_connection. @@ -141,9 +194,11 @@ def _create_fancy_connection(tunnel_host=None, key_file=None, self._tunnel() # ssl and FakeSocket got deprecated. Try for the new hotness of wrap_ssl, - # with fallback. - try: - import ssl + # with fallback. Note: Since can_validate_certs() just checks for the + # ssl module, it's equivalent to attempting to import ssl from + # the function, but doesn't require a dynamic import, which doesn't + # play nicely with dev_appserver. + if can_validate_certs(): self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, certfile=self.cert_file, @@ -152,15 +207,15 @@ def _create_fancy_connection(tunnel_host=None, key_file=None, if self.cert_reqs & ssl.CERT_REQUIRED: cert = self.sock.getpeercert() - hostname = self.host.split(':', 0)[0] + hostname = self.host.split(":", 0)[0] if not self._validate_certificate_hostname(cert, hostname): raise InvalidCertificateException(hostname, cert, - 'hostname mismatch') - except ImportError: - ssl = socket.ssl(self.sock, - keyfile=self.key_file, - certfile=self.cert_file) - self.sock = httplib.FakeSocket(self.sock, ssl) + "hostname mismatch") + else: + ssl_socket = socket.ssl(self.sock, + keyfile=self.key_file, + certfile=self.cert_file) + self.sock = httplib.FakeSocket(self.sock, ssl_socket) return PresetProxyHTTPSConnection @@ -330,15 +385,22 @@ class FancyHTTPSHandler(urllib2.HTTPSHandler): """An HTTPSHandler that works with CONNECT-enabled proxies.""" def do_open(self, http_class, req, **kwargs): + proxy_authorization = None + for header in req.headers: + if header.lower() == "proxy-authorization": + proxy_authorization = req.headers[header] + break + # Intentionally very specific so as to opt for false negatives # rather than false positives. try: return urllib2.HTTPSHandler.do_open( self, - _create_fancy_connection(req._tunnel_host, - req._key_file, - req._cert_file, - req._ca_certs), + create_fancy_connection(req._tunnel_host, + req._key_file, + req._cert_file, + req._ca_certs, + proxy_authorization), req, **kwargs) except urllib2.URLError, url_error: @@ -348,7 +410,7 @@ class FancyHTTPSHandler(urllib2.HTTPSHandler): url_error.reason.args[0] == 1): # Display the reason to the user. Need to use args for python2.5 # compat. - raise InvalidCertificateException(req.host, '', + raise InvalidCertificateException(req.host, "", url_error.reason.args[1]) except ImportError: pass |