2015-11-26 00:26:10 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vi:si:et:sw=4:sts=4:ts=4
|
|
|
|
|
|
|
|
import ssl
|
|
|
|
import http.client
|
|
|
|
import urllib.request, urllib.error, urllib.parse
|
|
|
|
import logging
|
|
|
|
|
|
|
|
import socks
|
|
|
|
import socket
|
|
|
|
|
|
|
|
import settings
|
|
|
|
import state
|
2016-03-14 13:25:51 +00:00
|
|
|
from utils import get_service_id
|
2015-11-26 00:26:10 +00:00
|
|
|
|
2015-11-29 14:56:38 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2015-11-26 00:26:10 +00:00
|
|
|
|
|
|
|
class InvalidCertificateException(http.client.HTTPException, urllib.error.URLError):
|
|
|
|
def __init__(self, service_id, cert, reason):
|
|
|
|
http.client.HTTPException.__init__(self)
|
|
|
|
self._service_id = service_id
|
|
|
|
self._cert_service_id = get_service_id(cert=cert)
|
|
|
|
self.reason = reason
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return ('%s (local) != %s (remote) (%s)\n' %
|
|
|
|
(self._service_id, self._cert_service_id, self.reason))
|
|
|
|
|
|
|
|
def is_local(host):
|
2016-03-14 13:25:51 +00:00
|
|
|
return len([p for p in host.split(':')[0].split('.') if p.isdigit()]) == 4
|
2015-11-26 00:26:10 +00:00
|
|
|
|
|
|
|
def getaddrinfo(*args):
|
|
|
|
return [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (args[0], args[1]))]
|
|
|
|
|
|
|
|
def create_tor_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
|
|
|
source_address=None):
|
|
|
|
host, port = address
|
|
|
|
err = None
|
|
|
|
af = socket.AF_INET
|
|
|
|
socktype = socket.SOCK_STREAM
|
|
|
|
proto = 6
|
|
|
|
sa = address
|
|
|
|
sock = None
|
|
|
|
try:
|
|
|
|
sock = socks.socksocket(af, socktype, proto)
|
|
|
|
if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
|
|
|
|
sock.settimeout(timeout)
|
2015-11-26 11:06:01 +00:00
|
|
|
socks_port = state.tor.socks_port if state.tor else 9150
|
|
|
|
sock.set_proxy(socks.SOCKS5, "localhost", socks_port, True)
|
2015-11-26 00:26:10 +00:00
|
|
|
if source_address:
|
|
|
|
sock.bind(source_address)
|
|
|
|
sock.connect(sa)
|
|
|
|
return sock
|
|
|
|
|
|
|
|
except socket.error as _:
|
|
|
|
err = _
|
|
|
|
if sock is not None:
|
|
|
|
sock.close()
|
|
|
|
|
|
|
|
if err is not None:
|
|
|
|
raise err
|
|
|
|
else:
|
|
|
|
raise sock.error("getaddrinfo returns an empty list")
|
|
|
|
|
|
|
|
class TorHTTPSConnection(http.client.HTTPSConnection):
|
|
|
|
|
|
|
|
def __init__(self, host, port=None, service_id=None, check_hostname=None, context=None, **kwargs):
|
|
|
|
self._service_id = service_id
|
|
|
|
if self._service_id:
|
2016-06-24 12:32:20 +00:00
|
|
|
if hasattr(ssl, '_create_default_https_context'):
|
|
|
|
context = ssl._create_default_https_context()
|
|
|
|
elif hasattr(ssl, '_create_stdlib_context'):
|
|
|
|
context = ssl._create_stdlib_context()
|
|
|
|
if context:
|
|
|
|
context.check_hostname = False
|
|
|
|
context.verify_mode = ssl.CERT_NONE
|
2018-12-14 10:36:05 +00:00
|
|
|
# tor keys are still 1024 bit, debian started to require 2048 by default,
|
|
|
|
# try to lower requirements to 1024 if needed
|
|
|
|
try:
|
|
|
|
context.load_cert_chain(settings.ssl_cert_path, settings.ssl_key_path)
|
|
|
|
except ssl.SSLError:
|
|
|
|
context.set_ciphers('DEFAULT@SECLEVEL=1')
|
|
|
|
context.load_cert_chain(settings.ssl_cert_path, settings.ssl_key_path)
|
2016-06-24 12:32:20 +00:00
|
|
|
context.load_default_certs()
|
2015-11-26 00:26:10 +00:00
|
|
|
http.client.HTTPSConnection.__init__(self, host, port,
|
|
|
|
check_hostname=check_hostname, context=context, **kwargs)
|
|
|
|
|
|
|
|
if not is_local(host):
|
|
|
|
self._create_connection = create_tor_connection
|
|
|
|
|
|
|
|
def _check_service_id(self, cert):
|
|
|
|
service_id = get_service_id(cert=cert)
|
|
|
|
if service_id != self._service_id:
|
2015-11-29 16:07:01 +00:00
|
|
|
logger.debug('service_id mismatch: %s expected: %s', service_id, self._service_id)
|
2015-11-26 00:26:10 +00:00
|
|
|
return service_id == self._service_id
|
|
|
|
|
|
|
|
def connect(self):
|
|
|
|
http.client.HTTPSConnection.connect(self)
|
|
|
|
if self._service_id:
|
|
|
|
cert = self.sock.getpeercert(binary_form=True)
|
|
|
|
if not self._check_service_id(cert):
|
|
|
|
raise InvalidCertificateException(self._service_id, cert,
|
|
|
|
'service_id mismatch')
|
|
|
|
#logger.debug('CIPHER %s VERSION %s', self.sock.cipher(), self.sock.ssl_version)
|
|
|
|
|
|
|
|
class TorHTTPSHandler(urllib.request.HTTPSHandler):
|
|
|
|
def __init__(self, debuglevel=0, context=None, check_hostname=None, service_id=None):
|
|
|
|
urllib.request.AbstractHTTPHandler.__init__(self, debuglevel)
|
|
|
|
self._context = context
|
|
|
|
self._check_hostname = check_hostname
|
|
|
|
self._service_id = service_id
|
|
|
|
|
|
|
|
def https_open(self, req):
|
|
|
|
return self.do_open(TorHTTPSConnection, req,
|
|
|
|
context=self._context, check_hostname=self._check_hostname,
|
|
|
|
service_id=self._service_id)
|
|
|
|
|
|
|
|
class TorHTTPConnection(http.client.HTTPConnection):
|
|
|
|
def __init__(self, host, port=None, **kwargs):
|
|
|
|
http.client.HTTPConnection.__init__(self, host, port, **kwargs)
|
|
|
|
if not is_local(host):
|
|
|
|
self._create_connection = create_tor_connection
|
|
|
|
|
|
|
|
class TorHTTPHandler(urllib.request.HTTPHandler):
|
|
|
|
def http_open(self, req):
|
|
|
|
return self.do_open(TorHTTPConnection, req)
|
|
|
|
|
|
|
|
def get_opener(service_id=None):
|
|
|
|
handler = TorHTTPSHandler(service_id=service_id)
|
|
|
|
opener = urllib.request.build_opener(handler, TorHTTPHandler())
|
|
|
|
return opener
|