Update PySocks to 1.6.7

This commit is contained in:
shortcutme 2017-04-06 19:20:27 +02:00
parent 9921fc07dd
commit fa65a6dc05
No known key found for this signature in database
GPG key ID: 5B63BAE6CB9613AE
15 changed files with 349 additions and 539 deletions

View file

@ -1,7 +1,7 @@
PySocks PySocks
======= =======
Updated version of SocksiPy. Many old bugs fixed, and overall code cleanup. Updated and semi-actively maintained version of [SocksiPy](http://socksipy.sourceforge.net/), with bug fixes and extra features.
Acts as a drop-in replacement to the socket module. Acts as a drop-in replacement to the socket module.
@ -10,23 +10,10 @@ Acts as a drop-in replacement to the socket module.
Features Features
======== ========
* Fully supports Python 2.6 - 3.4 * SOCKS proxy client for Python 2.6 - 3.x
* TCP and UDP both supported
* UDP support * HTTP proxy client included but not supported or recommended (you should use urllib2's or requests' own HTTP proxy interface)
* urllib2 handler included. `pip install` / `setup.py install` will automatically install the `sockshandler` module.
* SocksiPyHandler, courtesy e000, was also added as an example of how this module can be used with urllib2. See example code in sockshandler.py. `pip install` and `setup.py install` will automatically install the `sockshandler` module.
* Bugs in the original SocksiPy were fixed, including two that could lead to infinite hanging when communicating with bad proxy servers.
* urllib3, which powers the requests module, is working on integrating SOCKS proxy support based on this branch
* `SOCKS5`, `SOCKS4`, and `HTTP` are now aliases for `PROXY_TYPE_SOCKS5`, `PROXY_TYPE_SOCKS4`, and `PROXY_TYPE_HTTP`
* Tests added
* Various style and performance improvements; codebase simplified
* Actively maintained
Installation Installation
============ ============
@ -43,18 +30,18 @@ Alternatively, include just `socks.py` in your project.
-------------------------------------------- --------------------------------------------
*Warning:* PySocks/SocksiPy only supports HTTP proxies that use CONNECT tunneling. Certain HTTP proxies may not work with this library. If you wish to use HTTP proxies (and not SOCKS proxies), it is recommended that you rely on your HTTP client's native proxy support (`proxies` dict for `requests`, or `urllib2.ProxyHandler` for `urllib2`) instead. *Warning:* PySocks/SocksiPy only supports HTTP proxies that use CONNECT tunneling. Certain HTTP proxies may not work with this library. If you wish to use HTTP (not SOCKS) proxies, it is recommended that you rely on your HTTP client's native proxy support (`proxies` dict for `requests`, or `urllib2.ProxyHandler` for `urllib2`) instead.
-------------------------------------------- --------------------------------------------
Usage Usage
===== =====
## Example ## ## socks.socksocket ##
import socks import socks
s = socks.socksocket() s = socks.socksocket() # Same API as socket.socket in the standard lib
s.set_proxy(socks.SOCKS5, "localhost") # SOCKS4 and SOCKS5 use port 1080 by default s.set_proxy(socks.SOCKS5, "localhost") # SOCKS4 and SOCKS5 use port 1080 by default
# Or # Or
@ -63,23 +50,35 @@ Usage
s.set_proxy(socks.HTTP, "5.5.5.5", 8888) s.set_proxy(socks.HTTP, "5.5.5.5", 8888)
# Can be treated identical to a regular socket object # Can be treated identical to a regular socket object
s.connect(("www.test.com", 80)) s.connect(("www.somesite.com", 80))
s.sendall("GET / ...") s.sendall("GET / HTTP/1.1 ...")
print s.recv(4096) print s.recv(4096)
## Monkeypatching ##
To monkeypatch the entire standard library with a single default proxy: To monkeypatch the entire standard library with a single default proxy:
import urllib2
import socket import socket
import socks import socks
import urllib2
socks.set_default_proxy(socks.SOCKS5, "localhost") socks.set_default_proxy(socks.SOCKS5, "localhost")
socket.socket = socks.socksocket socket.socket = socks.socksocket
urllib2.urlopen("http://...") # All requests will pass through the SOCKS proxy urllib2.urlopen("http://www.somesite.com/") # All requests will pass through the SOCKS proxy
Note that monkeypatching may not work for all standard modules or for all third party modules, and generally isn't recommended. Note that monkeypatching may not work for all standard modules or for all third party modules, and generally isn't recommended. Monkeypatching is usually an anti-pattern in Python.
## urllib2 Handler ##
Example use case with the `sockshandler` urllib2 handler. Note that you must import both `socks` and `sockshandler`, as the handler is its own module separate from PySocks. The module is included in the PyPI package.
import urllib2
import socks
from sockshandler import SocksiPyHandler
opener = urllib2.build_opener(SocksiPyHandler(socks.SOCKS5, "127.0.0.1", 9050))
print opener.open("http://www.somesite.com/") # All requests made by the opener will pass through the SOCKS proxy
-------------------------------------------- --------------------------------------------
@ -87,7 +86,7 @@ Original SocksiPy README attached below, amended to reflect API changes.
-------------------------------------------- --------------------------------------------
SocksiPy - version 1.5.0 SocksiPy
A Python SOCKS module. A Python SOCKS module.
@ -277,6 +276,7 @@ class `HTTPError` - This will be raised for HTTP errors. The message will contai
the HTTP status code and provided error message. the HTTP status code and provided error message.
After establishing the connection, the object behaves like a standard socket. After establishing the connection, the object behaves like a standard socket.
Methods like `makefile()` and `settimeout()` should behave just like regular sockets. Methods like `makefile()` and `settimeout()` should behave just like regular sockets.
Call the `close()` method to close the connection. Call the `close()` method to close the connection.

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
from distutils.core import setup from setuptools import setup
VERSION = "1.5.3" VERSION = "1.6.7"
setup( setup(
name = "PySocks", name = "PySocks",
@ -14,4 +14,3 @@ setup(
keywords = ["socks", "proxy"], keywords = ["socks", "proxy"],
py_modules=["socks", "sockshandler"] py_modules=["socks", "sockshandler"]
) )

View file

@ -1,6 +1,5 @@
""" """
SocksiPy - Python SOCKS module. SocksiPy - Python SOCKS module.
Version 1.5.3
Copyright 2006 Dan-Haim. All rights reserved. Copyright 2006 Dan-Haim. All rights reserved.
@ -52,14 +51,28 @@ Modifications made by Anorov (https://github.com/Anorov)
-Various small bug fixes -Various small bug fixes
""" """
__version__ = "1.5.3" __version__ = "1.6.7"
import socket import socket
import struct import struct
from errno import EOPNOTSUPP, EINVAL, EAGAIN from errno import EOPNOTSUPP, EINVAL, EAGAIN
from io import BytesIO from io import BytesIO
from os import SEEK_CUR from os import SEEK_CUR
import os
import sys
import functools
import logging
from collections import Callable from collections import Callable
from base64 import b64encode
if os.name == "nt" and sys.version_info < (3, 0):
try:
import win_inet_pton
except ImportError:
raise ImportError("To run PySocks on Windows you must install win_inet_pton")
log = logging.getLogger(__name__)
PROXY_TYPE_SOCKS4 = SOCKS4 = 1 PROXY_TYPE_SOCKS4 = SOCKS4 = 1
PROXY_TYPE_SOCKS5 = SOCKS5 = 2 PROXY_TYPE_SOCKS5 = SOCKS5 = 2
@ -70,6 +83,25 @@ PRINTABLE_PROXY_TYPES = dict(zip(PROXY_TYPES.values(), PROXY_TYPES.keys()))
_orgsocket = _orig_socket = socket.socket _orgsocket = _orig_socket = socket.socket
def set_self_blocking(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
self = args[0]
try:
_is_blocking = self.gettimeout()
if _is_blocking == 0:
self.setblocking(True)
return function(*args, **kwargs)
except Exception as e:
raise
finally:
# set orgin blcoking
if _is_blocking == 0:
self.setblocking(False)
return wrapper
class ProxyError(IOError): class ProxyError(IOError):
""" """
socket_err contains original socket.error exception. socket_err contains original socket.error exception.
@ -122,7 +154,10 @@ def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, username
username.encode() if username else None, username.encode() if username else None,
password.encode() if password else None) password.encode() if password else None)
setdefaultproxy = set_default_proxy def setdefaultproxy(*args, **kwargs):
if 'proxytype' in kwargs:
kwargs['proxy_type'] = kwargs.pop('proxytype')
return set_default_proxy(*args, **kwargs)
def get_default_proxy(): def get_default_proxy():
""" """
@ -147,9 +182,10 @@ def wrap_module(module):
wrapmodule = wrap_module wrapmodule = wrap_module
def create_connection(dest_pair, proxy_type=None, proxy_addr=None, def create_connection(dest_pair, proxy_type=None, proxy_addr=None,
proxy_port=None, proxy_username=None, proxy_port=None, proxy_rdns=True,
proxy_password=None, timeout=None, proxy_username=None, proxy_password=None,
source_address=None): timeout=None, source_address=None,
socket_options=None):
"""create_connection(dest_pair, *[, timeout], **proxy_args) -> socket object """create_connection(dest_pair, *[, timeout], **proxy_args) -> socket object
Like socket.create_connection(), but connects to proxy Like socket.create_connection(), but connects to proxy
@ -161,14 +197,48 @@ def create_connection(dest_pair, proxy_type=None, proxy_addr=None,
source_address - tuple (host, port) for the socket to bind to as its source source_address - tuple (host, port) for the socket to bind to as its source
address before connecting (only for compatibility) address before connecting (only for compatibility)
""" """
sock = socksocket() # Remove IPv6 brackets on the remote address and proxy address.
if isinstance(timeout, (int, float)): remote_host, remote_port = dest_pair
sock.settimeout(timeout) if remote_host.startswith('['):
if proxy_type is not None: remote_host = remote_host.strip('[]')
sock.set_proxy(proxy_type, proxy_addr, proxy_port, if proxy_addr and proxy_addr.startswith('['):
proxy_username, proxy_password) proxy_addr = proxy_addr.strip('[]')
sock.connect(dest_pair)
return sock err = None
# Allow the SOCKS proxy to be on IPv4 or IPv6 addresses.
for r in socket.getaddrinfo(proxy_addr, proxy_port, 0, socket.SOCK_STREAM):
family, socket_type, proto, canonname, sa = r
sock = None
try:
sock = socksocket(family, socket_type, proto)
if socket_options:
for opt in socket_options:
sock.setsockopt(*opt)
if isinstance(timeout, (int, float)):
sock.settimeout(timeout)
if proxy_type:
sock.set_proxy(proxy_type, proxy_addr, proxy_port, proxy_rdns,
proxy_username, proxy_password)
if source_address:
sock.bind(source_address)
sock.connect((remote_host, remote_port))
return sock
except (socket.error, ProxyConnectionError) as e:
err = e
if sock:
sock.close()
sock = None
if err:
raise err
raise socket.error("gai returned empty list.")
class _BaseSocket(socket.socket): class _BaseSocket(socket.socket):
"""Allows Python 2's "delegated" methods such as send() to be overridden """Allows Python 2's "delegated" methods such as send() to be overridden
@ -207,12 +277,12 @@ class socksocket(_BaseSocket):
default_proxy = None default_proxy = None
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, *args, **kwargs):
if type not in (socket.SOCK_STREAM, socket.SOCK_DGRAM): if type not in (socket.SOCK_STREAM, socket.SOCK_DGRAM):
msg = "Socket type must be stream or datagram, not {!r}" msg = "Socket type must be stream or datagram, not {!r}"
raise ValueError(msg.format(type)) raise ValueError(msg.format(type))
_BaseSocket.__init__(self, family, type, proto, _sock) super(socksocket, self).__init__(family, type, proto, *args, **kwargs)
self._proxyconn = None # TCP connection to keep UDP relay alive self._proxyconn = None # TCP connection to keep UDP relay alive
if self.default_proxy: if self.default_proxy:
@ -222,6 +292,8 @@ class socksocket(_BaseSocket):
self.proxy_sockname = None self.proxy_sockname = None
self.proxy_peername = None self.proxy_peername = None
self._timeout = None
def _readall(self, file, count): def _readall(self, file, count):
""" """
Receive EXACTLY the number of bytes requested from the file object. Receive EXACTLY the number of bytes requested from the file object.
@ -235,6 +307,24 @@ class socksocket(_BaseSocket):
data += d data += d
return data return data
def settimeout(self, timeout):
self._timeout = timeout
try:
# test if we're connected, if so apply timeout
peer = self.get_proxy_peername()
super(socksocket, self).settimeout(self._timeout)
except socket.error:
pass
def gettimeout(self):
return self._timeout
def setblocking(self, v):
if v:
self.settimeout(None)
else:
self.settimeout(0.0)
def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None): def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None):
"""set_proxy(proxy_type, addr[, port[, rdns[, username[, password]]]]) """set_proxy(proxy_type, addr[, port[, rdns[, username[, password]]]])
Sets the proxy to be used. Sets the proxy to be used.
@ -257,7 +347,10 @@ class socksocket(_BaseSocket):
username.encode() if username else None, username.encode() if username else None,
password.encode() if password else None) password.encode() if password else None)
setproxy = set_proxy def setproxy(self, *args, **kwargs):
if 'proxytype' in kwargs:
kwargs['proxy_type'] = kwargs.pop('proxytype')
return self.set_proxy(*args, **kwargs)
def bind(self, *pos, **kw): def bind(self, *pos, **kw):
""" """
@ -273,7 +366,7 @@ class socksocket(_BaseSocket):
if proxy_type != SOCKS5: if proxy_type != SOCKS5:
msg = "UDP only supported by SOCKS5 proxy type" msg = "UDP only supported by SOCKS5 proxy type"
raise socket.error(EOPNOTSUPP, msg) raise socket.error(EOPNOTSUPP, msg)
_BaseSocket.bind(self, *pos, **kw) super(socksocket, self).bind(*pos, **kw)
# Need to specify actual local port because # Need to specify actual local port because
# some relays drop packets if a port of zero is specified. # some relays drop packets if a port of zero is specified.
@ -292,12 +385,13 @@ class socksocket(_BaseSocket):
# but some proxies return a private IP address (10.x.y.z) # but some proxies return a private IP address (10.x.y.z)
host, _ = proxy host, _ = proxy
_, port = relay _, port = relay
_BaseSocket.connect(self, (host, port)) super(socksocket, self).connect((host, port))
super(socksocket, self).settimeout(self._timeout)
self.proxy_sockname = ("0.0.0.0", 0) # Unknown self.proxy_sockname = ("0.0.0.0", 0) # Unknown
def sendto(self, bytes, *args, **kwargs): def sendto(self, bytes, *args, **kwargs):
if self.type != socket.SOCK_DGRAM: if self.type != socket.SOCK_DGRAM:
return _BaseSocket.sendto(self, bytes, *args, **kwargs) return super(socksocket, self).sendto(bytes, *args, **kwargs)
if not self._proxyconn: if not self._proxyconn:
self.bind(("", 0)) self.bind(("", 0))
@ -311,23 +405,23 @@ class socksocket(_BaseSocket):
header.write(STANDALONE) header.write(STANDALONE)
self._write_SOCKS5_address(address, header) self._write_SOCKS5_address(address, header)
sent = _BaseSocket.send(self, header.getvalue() + bytes, *flags, **kwargs) sent = super(socksocket, self).send(header.getvalue() + bytes, *flags, **kwargs)
return sent - header.tell() return sent - header.tell()
def send(self, bytes, flags=0, **kwargs): def send(self, bytes, flags=0, **kwargs):
if self.type == socket.SOCK_DGRAM: if self.type == socket.SOCK_DGRAM:
return self.sendto(bytes, flags, self.proxy_peername, **kwargs) return self.sendto(bytes, flags, self.proxy_peername, **kwargs)
else: else:
return _BaseSocket.send(self, bytes, flags, **kwargs) return super(socksocket, self).send(bytes, flags, **kwargs)
def recvfrom(self, bufsize, flags=0): def recvfrom(self, bufsize, flags=0):
if self.type != socket.SOCK_DGRAM: if self.type != socket.SOCK_DGRAM:
return _BaseSocket.recvfrom(self, bufsize, flags) return super(socksocket, self).recvfrom(bufsize, flags)
if not self._proxyconn: if not self._proxyconn:
self.bind(("", 0)) self.bind(("", 0))
buf = BytesIO(_BaseSocket.recv(self, bufsize, flags)) buf = BytesIO(super(socksocket, self).recv(bufsize + 1024, flags))
buf.seek(+2, SEEK_CUR) buf.seek(2, SEEK_CUR)
frag = buf.read(1) frag = buf.read(1)
if ord(frag): if ord(frag):
raise NotImplementedError("Received UDP packet fragment") raise NotImplementedError("Received UDP packet fragment")
@ -338,7 +432,7 @@ class socksocket(_BaseSocket):
if fromhost != peerhost or peerport not in (0, fromport): if fromhost != peerhost or peerport not in (0, fromport):
raise socket.error(EAGAIN, "Packet filtered") raise socket.error(EAGAIN, "Packet filtered")
return (buf.read(), (fromhost, fromport)) return (buf.read(bufsize), (fromhost, fromport))
def recv(self, *pos, **kw): def recv(self, *pos, **kw):
bytes, _ = self.recvfrom(*pos, **kw) bytes, _ = self.recvfrom(*pos, **kw)
@ -347,7 +441,7 @@ class socksocket(_BaseSocket):
def close(self): def close(self):
if self._proxyconn: if self._proxyconn:
self._proxyconn.close() self._proxyconn.close()
return _BaseSocket.close(self) return super(socksocket, self).close()
def get_proxy_sockname(self): def get_proxy_sockname(self):
""" """
@ -361,7 +455,7 @@ class socksocket(_BaseSocket):
""" """
Returns the IP and port number of the proxy. Returns the IP and port number of the proxy.
""" """
return _BaseSocket.getpeername(self) return super(socksocket, self).getpeername()
getproxypeername = get_proxy_peername getproxypeername = get_proxy_peername
@ -459,6 +553,8 @@ class socksocket(_BaseSocket):
# Get the bound address/port # Get the bound address/port
bnd = self._read_SOCKS5_address(reader) bnd = self._read_SOCKS5_address(reader)
super(socksocket, self).settimeout(self._timeout)
return (resolved, bnd) return (resolved, bnd)
finally: finally:
reader.close() reader.close()
@ -471,25 +567,38 @@ class socksocket(_BaseSocket):
""" """
host, port = addr host, port = addr
proxy_type, _, _, rdns, username, password = self.proxy proxy_type, _, _, rdns, username, password = self.proxy
family_to_byte = {socket.AF_INET: b"\x01", socket.AF_INET6: b"\x04"}
# If the given destination address is an IP address, we'll # If the given destination address is an IP address, we'll
# use the IPv4 address request even if remote resolving was specified. # use the IP address request even if remote resolving was specified.
try: # Detect whether the address is IPv4/6 directly.
addr_bytes = socket.inet_aton(host) for family in (socket.AF_INET, socket.AF_INET6):
file.write(b"\x01" + addr_bytes) try:
host = socket.inet_ntoa(addr_bytes) addr_bytes = socket.inet_pton(family, host)
except socket.error: file.write(family_to_byte[family] + addr_bytes)
# Well it's not an IP number, so it's probably a DNS name. host = socket.inet_ntop(family, addr_bytes)
if rdns: file.write(struct.pack(">H", port))
# Resolve remotely return host, port
host_bytes = host.encode('idna') except socket.error:
file.write(b"\x03" + chr(len(host_bytes)).encode() + host_bytes) continue
else:
# Resolve locally
addr_bytes = socket.inet_aton(socket.gethostbyname(host))
file.write(b"\x01" + addr_bytes)
host = socket.inet_ntoa(addr_bytes)
# Well it's not an IP number, so it's probably a DNS name.
if rdns:
# Resolve remotely
host_bytes = host.encode('idna')
file.write(b"\x03" + chr(len(host_bytes)).encode() + host_bytes)
else:
# Resolve locally
addresses = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_ADDRCONFIG)
# We can't really work out what IP is reachable, so just pick the
# first.
target_addr = addresses[0]
family = target_addr[0]
host = target_addr[4][0]
addr_bytes = socket.inet_pton(family, host)
file.write(family_to_byte[family] + addr_bytes)
host = socket.inet_ntop(family, addr_bytes)
file.write(struct.pack(">H", port)) file.write(struct.pack(">H", port))
return host, port return host, port
@ -500,6 +609,8 @@ class socksocket(_BaseSocket):
elif atyp == b"\x03": elif atyp == b"\x03":
length = self._readall(file, 1) length = self._readall(file, 1)
addr = self._readall(file, ord(length)) addr = self._readall(file, ord(length))
elif atyp == b"\x04":
addr = socket.inet_ntop(socket.AF_INET6, self._readall(file, 16))
else: else:
raise GeneralProxyError("SOCKS5 proxy server sent invalid data") raise GeneralProxyError("SOCKS5 proxy server sent invalid data")
@ -575,8 +686,17 @@ class socksocket(_BaseSocket):
# If we need to resolve locally, we do this now # If we need to resolve locally, we do this now
addr = dest_addr if rdns else socket.gethostbyname(dest_addr) addr = dest_addr if rdns else socket.gethostbyname(dest_addr)
self.sendall(b"CONNECT " + addr.encode('idna') + b":" + str(dest_port).encode() + http_headers = [
b" HTTP/1.1\r\n" + b"Host: " + dest_addr.encode('idna') + b"\r\n\r\n") b"CONNECT " + addr.encode('idna') + b":" + str(dest_port).encode() + b" HTTP/1.1",
b"Host: " + dest_addr.encode('idna')
]
if username and password:
http_headers.append(b"Proxy-Authorization: basic " + b64encode(username + b":" + password))
http_headers.append(b"\r\n")
self.sendall(b"\r\n".join(http_headers))
# We just need the first line to check if the connection was successful # We just need the first line to check if the connection was successful
fobj = self.makefile() fobj = self.makefile()
@ -616,7 +736,7 @@ class socksocket(_BaseSocket):
HTTP: _negotiate_HTTP HTTP: _negotiate_HTTP
} }
@set_self_blocking
def connect(self, dest_pair): def connect(self, dest_pair):
""" """
Connects to the specified destination through a proxy. Connects to the specified destination through a proxy.
@ -625,6 +745,12 @@ class socksocket(_BaseSocket):
dest_pair - 2-tuple of (IP/hostname, port). dest_pair - 2-tuple of (IP/hostname, port).
""" """
if len(dest_pair) != 2 or dest_pair[0].startswith("["):
# Probably IPv6, not supported -- raise an error, and hope
# Happy Eyeballs (RFC6555) makes sure at least the IPv4
# connection works...
raise socket.error("PySocks doesn't support IPv6: %s" % str(dest_pair))
dest_addr, dest_port = dest_pair dest_addr, dest_port = dest_pair
if self.type == socket.SOCK_DGRAM: if self.type == socket.SOCK_DGRAM:
@ -650,17 +776,22 @@ class socksocket(_BaseSocket):
raise GeneralProxyError("Invalid destination-connection (host, port) pair") raise GeneralProxyError("Invalid destination-connection (host, port) pair")
# We set the timeout here so that we don't hang in connection or during
# negotiation.
super(socksocket, self).settimeout(self._timeout)
if proxy_type is None: if proxy_type is None:
# Treat like regular socket object # Treat like regular socket object
self.proxy_peername = dest_pair self.proxy_peername = dest_pair
_BaseSocket.connect(self, (dest_addr, dest_port)) super(socksocket, self).settimeout(self._timeout)
super(socksocket, self).connect((dest_addr, dest_port))
return return
proxy_addr = self._proxy_addr() proxy_addr = self._proxy_addr()
try: try:
# Initial connection to proxy server # Initial connection to proxy server.
_BaseSocket.connect(self, proxy_addr) super(socksocket, self).connect(proxy_addr)
except socket.error as error: except socket.error as error:
# Error while connecting to proxy # Error while connecting to proxy
@ -671,6 +802,7 @@ class socksocket(_BaseSocket):
msg = "Error connecting to {0} proxy {1}".format(printable_type, msg = "Error connecting to {0} proxy {1}".format(printable_type,
proxy_server) proxy_server)
log.debug("%s due to: %s", msg, error)
raise ProxyConnectionError(msg, error) raise ProxyConnectionError(msg, error)
else: else:

View file

@ -1,5 +0,0 @@
Very rudimentary tests for Python 2 and Python 3.
Requirements: tornado, twisted (available through pip)
./test.sh

View file

@ -1,137 +0,0 @@
#!/usr/bin/env python
#
# Simple asynchronous HTTP proxy with tunnelling (CONNECT).
#
# GET/POST proxying based on
# http://groups.google.com/group/python-tornado/msg/7bea08e7a049cf26
#
# Copyright (C) 2012 Senko Rasic <senko.rasic@dobarkod.hr>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import socket
import tornado.httpserver
import tornado.ioloop
import tornado.iostream
import tornado.web
import tornado.httpclient
__all__ = ['ProxyHandler', 'run_proxy']
class ProxyHandler(tornado.web.RequestHandler):
SUPPORTED_METHODS = ['GET', 'POST', 'CONNECT']
@tornado.web.asynchronous
def get(self):
def handle_response(response):
if response.error and not isinstance(response.error,
tornado.httpclient.HTTPError):
self.set_status(500)
self.write('Internal server error:\n' + str(response.error))
self.finish()
else:
self.set_status(response.code)
for header in ('Date', 'Cache-Control', 'Server',
'Content-Type', 'Location'):
v = response.headers.get(header)
if v:
self.set_header(header, v)
if response.body:
self.write(response.body)
self.finish()
req = tornado.httpclient.HTTPRequest(url=self.request.uri,
method=self.request.method, body=self.request.body,
headers=self.request.headers, follow_redirects=False,
allow_nonstandard_methods=True)
client = tornado.httpclient.AsyncHTTPClient()
try:
client.fetch(req, handle_response)
except tornado.httpclient.HTTPError as e:
if hasattr(e, 'response') and e.response:
self.handle_response(e.response)
else:
self.set_status(500)
self.write('Internal server error:\n' + str(e))
self.finish()
@tornado.web.asynchronous
def post(self):
return self.get()
@tornado.web.asynchronous
def connect(self):
host, port = self.request.uri.split(':')
client = self.request.connection.stream
def read_from_client(data):
upstream.write(data)
def read_from_upstream(data):
client.write(data)
def client_close(data=None):
if upstream.closed():
return
if data:
upstream.write(data)
upstream.close()
def upstream_close(data=None):
if client.closed():
return
if data:
client.write(data)
client.close()
def start_tunnel():
client.read_until_close(client_close, read_from_client)
upstream.read_until_close(upstream_close, read_from_upstream)
client.write(b'HTTP/1.0 200 Connection established\r\n\r\n')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
upstream = tornado.iostream.IOStream(s)
upstream.connect((host, int(port)), start_tunnel)
def run_proxy(port=8080, start_ioloop=True):
"""
Run proxy on the specified port. If start_ioloop is True (default),
the tornado IOLoop will be started immediately.
"""
app = tornado.web.Application([
(r'.*', ProxyHandler),
])
app.listen(port, address="127.0.0.1")
ioloop = tornado.ioloop.IOLoop.instance()
if start_ioloop:
ioloop.start()
if __name__ == '__main__':
port = 8081
if len(sys.argv) > 1:
port = int(sys.argv[1])
print ("Running HTTP proxy server")
run_proxy(port)

Binary file not shown.

View file

@ -1,104 +0,0 @@
#################################################
# #
# Sample configuration file for MOCKS 0.0.2 #
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
# #
# I recommend reading the examples in this file #
# and then extending it to suite your needs. #
# #
#################################################
#########################
#
# General daemon config
# ~~~~~~~~~~~~~~~~~~~~~
#
#########################
PORT = 1081 # Port MOCKS is to listen to
MOCKS_ADDR = 127.0.0.1 # IP adress MOCKS is to bind to
LOG_FILE = mocks.log # MOCKS log file
PID_FILE = mocks.pid # File holding MOCKS's process ID
BUFFER_SIZE = 65536 # Traffic buffer size in bytes
BACKLOG = 5 # Backlog for listen()
NEGOTIATION_TIMEOUT = 5
CONNECTION_IDLE_TIMEOUT = 300
BIND_TIMEOUT = 30
SHUTDOWN_TIMEOUT = 3
MAX_CONNECTIONS = 50
##########################################################################
#
# Client filter config
# ~~~~~~~~~~~~~~~~~~~~
#
# Client filtering means sorting out which clients are allowed
# connection and which are not. This is basically done like this:
# MOCKS has a default behaviour regarding filtering client
# connections. This behaviour is called the 'policy' and can either
# be to ALLOW or to DENY the connection. After setting the policy
# you can specify a list of exceptions. The action MOCKS takes
# for a client matching any of these exceptions is the opposite
# of the policy (that is, if the policy is set to ALLOW the exceptions
# are denied and if the policy is set to DENY the exceptions are allowed).
# An exception is specified in the form ip_address/mask, where mask
# is optional and is an integer ranging from 0 to 32 identifying the
# number of common heading bits that ip_address and the client's IP
# address must have in order to yield a match. If mask is missing,
# 32 will be assumed. For instance, 192.168.1.0/24 will match any IP
# ranging from 192.168.1.1 to 192.168.1.255.
#
# Let's take two examples, one for each type of policy. Let's say we
# only want to allow IPs 10.12.0.0 through 10.12.255.255, 172.23.2.5 and
# 192.168.52.26 to use MOCKS. What we have to to is this:
#
# FILTER_POLICY = DENY
# FILTER_EXCEPTION = 10.12.0.0/16
# FILTER_EXCEPTION = 172.23.2.5 # implied /32
# FILTER_EXCEPTION = 192.168.52.26 # implied /32
#
# Now, let's say this is a public proxy server, but for some reason
# you don't want to let any IP ranging from 192.168.1.1 to 192.168.1.255
# and neither 10.2.5.13 to connect to it:
#
# FILTER_POLICY = ALLOW
# FILTER_EXCEPTION = 192.168.1.0/24
# FILTER_EXCEPTION = 10.2.5.13
#
###########################################################################
FILTER_POLICY = ALLOW
#############################################################################
#
# Upstream proxy config
# ~~~~~~~~~~~~~~~~~~~~~
#
# You can choose to further relay traffic through another proxy server.
# MOCKS supports upstream HTTP CONNECT, SOCKS4 and SOCKS5 proxies. You
# must specify the proxy type (one of HTTPCONNECT, SOCKS4 or SOCKS5), the
# proxy address and the proxy port. Optionally you can specify an user
# name and a password used to authenicate to the upstream proxy. This is
# pretty straight forward, so let's just take an example. Let's say you
# want to use the HTTP CONNECT server at httpconnectproxy.com, on port 3128,
# using the username 'foo' and the password 'bar'. You do it like this:
#
# UP_PROXY_TYPE = HTTPCONNECT
# UP_PROXY_ADDR = httpconnectproxy.com
# UP_PROXY_PORT = 3128
# UP_PROXY_USER = foo # These two can be missing if you
# UP_PROXY_PASSWD = bar # are not required to authenticate
#
#############################################################################
# UP_PROXY_TYPE = HTTPCONNECT
# UP_PROXY_ADDR = 192.168.1.12
# UP_PROXY_PORT = 3128

View file

@ -1,14 +0,0 @@
#!/usr/bin/env python
from twisted.internet import reactor
from twisted.protocols.socks import SOCKSv4Factory
def run_proxy():
reactor.listenTCP(1080, SOCKSv4Factory("/dev/null"), interface="127.0.0.1")
try:
reactor.run()
except (KeyboardInterrupt, SystemExit):
reactor.stop()
if __name__ == "__main__":
print "Running SOCKS4 proxy server"
run_proxy()

View file

@ -1,174 +0,0 @@
import sys
sys.path.append("..")
import socks
import socket
PY3K = sys.version_info[0] == 3
if PY3K:
import urllib.request as urllib2
else:
import sockshandler
import urllib2
def raw_HTTP_request():
req = "GET /ip HTTP/1.1\r\n"
req += "Host: ifconfig.me\r\n"
req += "User-Agent: Mozilla\r\n"
req += "Accept: text/html\r\n"
req += "\r\n"
return req.encode()
def socket_HTTP_test():
s = socks.socksocket()
s.set_proxy(socks.HTTP, "127.0.0.1", 8081)
s.connect(("ifconfig.me", 80))
s.sendall(raw_HTTP_request())
status = s.recv(2048).splitlines()[0]
assert status.startswith(b"HTTP/1.1 200")
def socket_SOCKS4_test():
s = socks.socksocket()
s.set_proxy(socks.SOCKS4, "127.0.0.1", 1080)
s.connect(("ifconfig.me", 80))
s.sendall(raw_HTTP_request())
status = s.recv(2048).splitlines()[0]
assert status.startswith(b"HTTP/1.1 200")
def socket_SOCKS5_test():
s = socks.socksocket()
s.set_proxy(socks.SOCKS5, "127.0.0.1", 1081)
s.connect(("ifconfig.me", 80))
s.sendall(raw_HTTP_request())
status = s.recv(2048).splitlines()[0]
assert status.startswith(b"HTTP/1.1 200")
def SOCKS5_connect_timeout_test():
s = socks.socksocket()
s.settimeout(0.0001)
s.set_proxy(socks.SOCKS5, "8.8.8.8", 80)
try:
s.connect(("ifconfig.me", 80))
except socks.ProxyConnectionError as e:
assert str(e.socket_err) == "timed out"
def SOCKS5_timeout_test():
s = socks.socksocket()
s.settimeout(0.0001)
s.set_proxy(socks.SOCKS5, "127.0.0.1", 1081)
try:
s.connect(("ifconfig.me", 4444))
except socks.GeneralProxyError as e:
assert str(e.socket_err) == "timed out"
def socket_SOCKS5_auth_test():
# TODO: add support for this test. Will need a better SOCKS5 server.
s = socks.socksocket()
s.set_proxy(socks.SOCKS5, "127.0.0.1", 1081, username="a", password="b")
s.connect(("ifconfig.me", 80))
s.sendall(raw_HTTP_request())
status = s.recv(2048).splitlines()[0]
assert status.startswith(b"HTTP/1.1 200")
def socket_HTTP_IP_test():
s = socks.socksocket()
s.set_proxy(socks.HTTP, "127.0.0.1", 8081)
s.connect(("133.242.129.236", 80))
s.sendall(raw_HTTP_request())
status = s.recv(2048).splitlines()[0]
assert status.startswith(b"HTTP/1.1 200")
def socket_SOCKS4_IP_test():
s = socks.socksocket()
s.set_proxy(socks.SOCKS4, "127.0.0.1", 1080)
s.connect(("133.242.129.236", 80))
s.sendall(raw_HTTP_request())
status = s.recv(2048).splitlines()[0]
assert status.startswith(b"HTTP/1.1 200")
def socket_SOCKS5_IP_test():
s = socks.socksocket()
s.set_proxy(socks.SOCKS5, "127.0.0.1", 1081)
s.connect(("133.242.129.236", 80))
s.sendall(raw_HTTP_request())
status = s.recv(2048).splitlines()[0]
assert status.startswith(b"HTTP/1.1 200")
def urllib2_HTTP_test():
socks.set_default_proxy(socks.HTTP, "127.0.0.1", 8081)
socks.wrap_module(urllib2)
status = urllib2.urlopen("http://ifconfig.me/ip").getcode()
assert status == 200
def urllib2_SOCKS5_test():
socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 1081)
socks.wrap_module(urllib2)
status = urllib2.urlopen("http://ifconfig.me/ip").getcode()
assert status == 200
def urllib2_handler_HTTP_test():
opener = urllib2.build_opener(sockshandler.SocksiPyHandler(socks.HTTP, "127.0.0.1", 8081))
status = opener.open("http://ifconfig.me/ip").getcode()
assert status == 200
def urllib2_handler_SOCKS5_test():
opener = urllib2.build_opener(sockshandler.SocksiPyHandler(socks.SOCKS5, "127.0.0.1", 1081))
status = opener.open("http://ifconfig.me/ip").getcode()
assert status == 200
def global_override_HTTP_test():
socks.set_default_proxy(socks.HTTP, "127.0.0.1", 8081)
good = socket.socket
socket.socket = socks.socksocket
status = urllib2.urlopen("http://ifconfig.me/ip").getcode()
socket.socket = good
assert status == 200
def global_override_SOCKS5_test():
default_proxy = (socks.SOCKS5, "127.0.0.1", 1081)
socks.set_default_proxy(*default_proxy)
good = socket.socket
socket.socket = socks.socksocket
status = urllib2.urlopen("http://ifconfig.me/ip").getcode()
socket.socket = good
assert status == 200
assert socks.get_default_proxy()[1].decode() == default_proxy[1]
def main():
print("Running tests...")
socket_HTTP_test()
print("1/12")
socket_SOCKS4_test()
print("2/12")
socket_SOCKS5_test()
print("3/12")
if not PY3K:
urllib2_handler_HTTP_test()
print("3.33/12")
urllib2_handler_SOCKS5_test()
print("3.66/12")
socket_HTTP_IP_test()
print("4/12")
socket_SOCKS4_IP_test()
print("5/12")
socket_SOCKS5_IP_test()
print("6/12")
SOCKS5_connect_timeout_test()
print("7/12")
SOCKS5_timeout_test()
print("8/12")
urllib2_HTTP_test()
print("9/12")
urllib2_SOCKS5_test()
print("10/12")
global_override_HTTP_test()
print("11/12")
global_override_SOCKS5_test()
print("12/12")
print("All tests ran successfully")
if __name__ == "__main__":
main()

View file

@ -1,25 +0,0 @@
#!/bin/bash
shopt -s expand_aliases
type python2 >/dev/null 2>&1 || alias python2='python'
echo "Starting proxy servers..."
python2 socks4server.py > /dev/null &
python2 httpproxy.py > /dev/null &
./mocks start
sleep 2
echo "Python 2.6 tests"
python2.6 sockstest.py
exit
sleep 2
echo "Python 2.7 tests"
python2.7 sockstest.py
sleep 2
echo "Python 3.x tests"
python3 sockstest.py
pkill python2 > /dev/null
./mocks shutdown
echo "Finished tests"

View file

@ -0,0 +1,4 @@
This software released into the public domain. Anyone is free to copy,
modify, publish, use, compile, sell, or distribute this software,
either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.

View file

@ -0,0 +1,29 @@
win_inet_pton
=============
Native inet_pton and inet_ntop implementation for Python on Windows (with ctypes).
Credit Where Credit Is Due
--------------------------
This package is based on code that was originally written by https://github.com/nnemkin here: https://gist.github.com/nnemkin/4966028
Why?
----
I needed this functionality in https://github.com/SerenitySoftwareLLC/cahoots to get full windows support. I figured, since there were other people looking for a solution to this on the net, I should publish it.
Usage
-----
Just import it, and it will auto-add the methods to the socket library.
import win_inet_pton
import socket
socket.inet_pton(...)
socket.inet_ntop(...)

View file

@ -0,0 +1 @@
from win_inet_pton import *

View file

@ -0,0 +1,20 @@
from distutils.core import setup
setup (
name = 'win_inet_pton',
version = '1.0.1',
py_modules = ['win_inet_pton'],
url = 'https://github.com/hickeroar/win_inet_pton',
author = 'Ryan Vennell',
author_email = 'ryan.vennell@gmail.com',
description = 'Native inet_pton and inet_ntop implementation for Python on Windows (with ctypes).',
license = open('LICENSE', 'r').read(),
classifiers = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Operating System :: OS Independent',
'Operating System :: Microsoft :: Windows',
'Programming Language :: Python :: 2.7',
'Topic :: Utilities'
]
)

View file

@ -0,0 +1,84 @@
# This software released into the public domain. Anyone is free to copy,
# modify, publish, use, compile, sell, or distribute this software,
# either in source code form or as a compiled binary, for any purpose,
# commercial or non-commercial, and by any means.
import socket
import ctypes
import os
class sockaddr(ctypes.Structure):
_fields_ = [("sa_family", ctypes.c_short),
("__pad1", ctypes.c_ushort),
("ipv4_addr", ctypes.c_byte * 4),
("ipv6_addr", ctypes.c_byte * 16),
("__pad2", ctypes.c_ulong)]
if hasattr(ctypes, 'windll'):
WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA
WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringA
else:
def not_windows():
raise SystemError(
"Invalid platform. ctypes.windll must be available."
)
WSAStringToAddressA = not_windows
WSAAddressToStringA = not_windows
def inet_pton(address_family, ip_string):
addr = sockaddr()
addr.sa_family = address_family
addr_size = ctypes.c_int(ctypes.sizeof(addr))
if WSAStringToAddressA(
ip_string,
address_family,
None,
ctypes.byref(addr),
ctypes.byref(addr_size)
) != 0:
raise socket.error(ctypes.FormatError())
if address_family == socket.AF_INET:
return ctypes.string_at(addr.ipv4_addr, 4)
if address_family == socket.AF_INET6:
return ctypes.string_at(addr.ipv6_addr, 16)
raise socket.error('unknown address family')
def inet_ntop(address_family, packed_ip):
addr = sockaddr()
addr.sa_family = address_family
addr_size = ctypes.c_int(ctypes.sizeof(addr))
ip_string = ctypes.create_string_buffer(128)
ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string))
if address_family == socket.AF_INET:
if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr):
raise socket.error('packed IP wrong length for inet_ntoa')
ctypes.memmove(addr.ipv4_addr, packed_ip, 4)
elif address_family == socket.AF_INET6:
if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr):
raise socket.error('packed IP wrong length for inet_ntoa')
ctypes.memmove(addr.ipv6_addr, packed_ip, 16)
else:
raise socket.error('unknown address family')
if WSAAddressToStringA(
ctypes.byref(addr),
addr_size,
None,
ip_string,
ctypes.byref(ip_string_size)
) != 0:
raise socket.error(ctypes.FormatError())
return ip_string[:ip_string_size.value - 1]
# Adding our two functions to the socket library
if os.name == 'nt':
socket.inet_pton = inet_pton
socket.inet_ntop = inet_ntop