initial commit
This commit is contained in:
87
includes/misc.py
Normal file
87
includes/misc.py
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import time, random, sys, os, string
|
||||
#import sockschain as socks
|
||||
sys.path.append('./includes')
|
||||
import rocksock
|
||||
|
||||
""" return formatted timestamp """
|
||||
def timestamp():
|
||||
return time.strftime('%H:%M:%S', time.gmtime())
|
||||
|
||||
""" return some random string """
|
||||
def random_string(strlen=20):
|
||||
return ''.join([random.choice(string.letters) for x in xrange(strlen)])
|
||||
|
||||
def _log(strng, level='info'):
|
||||
print '%s/%s\t%s' % (timestamp(), level, strng)
|
||||
|
||||
def option_matches_options(strng, items):
|
||||
try: return [item for item in items if re.match(strng, item)]
|
||||
except: return False
|
||||
|
||||
def prepare_socksocket(self, destination, path, path_item):
|
||||
if path_item in self.paths and self.paths[path_item]['path'] == path:
|
||||
self.paths[path_item]['path'] = False
|
||||
|
||||
#socks.setdefaultproxy()
|
||||
# relay to i2p http proxy if *.i2p domain
|
||||
if destination.endswith('i2p'):
|
||||
proxy = random.choice(self.i2p_host).split(':')
|
||||
path = False
|
||||
# or go with tor
|
||||
else:
|
||||
proxies = [ rocksock.RocksockProxyFromURL('socks5://%s' % random.choice(self.tor_host)) ]
|
||||
#socks.adddefaultproxy(*socks.parseproxy('tor://%s' % random.choice(self.tor_host)))
|
||||
# add 'clearnet' proxies to the chain ?
|
||||
if self.proxify and (not destination.endswith('onion') and not destination.endswith('.exit')):
|
||||
|
||||
# get a proxy path
|
||||
path = build_path(self, path_item, path)
|
||||
|
||||
# if path isn't long enough, break
|
||||
if not len(path): return False, False, False
|
||||
|
||||
# add chain...
|
||||
#for inc in xrange(len(path) - 1): socks.adddefaultproxy(*socks.parseproxy('http://%s' % path[inc]))
|
||||
#for inc in xrange(len(path) - 1): socks.adddefaultproxy(*socks.parseproxy('%s://%s' % (path[inc][1], path[inc][0])))
|
||||
for inc in xrange(len(path)): proxies.append( rocksock.RocksockProxyFromURL('%s://%s' % (path[inc][1], path[inc][0])))
|
||||
|
||||
#return True, socks.socksocket, path
|
||||
return True, proxies, path
|
||||
|
||||
def build_path(self, path_item, path):
|
||||
|
||||
chainlen = random.randint( self.path_len, (self.path_len + self.path_randomlen))
|
||||
# if not enough proxies
|
||||
# FIXME: try to get a proxylist from database
|
||||
if len(self.proxylist) < chainlen: return []
|
||||
|
||||
# valid path already available
|
||||
elif (path_item in self.paths and
|
||||
self.paths[path_item]['path'] and
|
||||
(time.time() - self.paths[path_item]['ticks']) < self.path_duration):
|
||||
|
||||
# take available path if any
|
||||
if path != self.paths[path_item]['path']: path = self.paths[path_item]['path']
|
||||
|
||||
# or nope, none available
|
||||
# build a new one from scratch
|
||||
else:
|
||||
path = []
|
||||
avail = []
|
||||
|
||||
# dec chainlen if we have to select the exit proxy
|
||||
if self.exitcountry is not None: chainlen -= 1
|
||||
#avail = [item[0] for item in self.proxylist if not item[0] in avail and item[1] != str(self.exitcountry).upper()]
|
||||
avail = [[item[0],item[2]] for item in self.proxylist if not item[0] in avail and item[1] != str(self.exitcountry).upper()]
|
||||
path = random.sample(avail, chainlen)
|
||||
|
||||
# choose the exit proxy
|
||||
if self.exitcountry is not None:
|
||||
#avail = [item[0] for item in self.proxylist if not item[0] in path and item[1] == str(self.exitcountry).upper()]
|
||||
avail = [[item[0],item[2]] for item in self.proxylist if not item[0] in path and item[1] == str(self.exitcountry).upper()]
|
||||
if not len(avail): return []
|
||||
path.append(random.choice(avail))
|
||||
|
||||
return path
|
||||
33
includes/mysqlite.py
Normal file
33
includes/mysqlite.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import time, random, sys
|
||||
import sqlite3 as lite
|
||||
|
||||
class mysqlite:
|
||||
|
||||
def execute(self, query, args = None, rmin = 1.5, rmax = 7.0):
|
||||
|
||||
while True:
|
||||
try: return self.cursor.execute(query,args) if args else self.cursor.execute(query)
|
||||
except:
|
||||
print '%s\nquery: %s\nargs: %s' % (str(sys.exc_info()), str(query), str(args))
|
||||
time.sleep(random.uniform(rmin, rmax))
|
||||
|
||||
def executemany(self, query, args, rmin = 1.5, rmax = 7.0):
|
||||
|
||||
while True:
|
||||
try: return self.cursor.executemany(query,args)
|
||||
except:
|
||||
print '%s\nquery: %s\nargs: %s' % (str(sys.exc_info()), str(query), str(args))
|
||||
time.sleep(random.uniform(rmin, rmax))
|
||||
|
||||
def commit(self, rmin = 1.5, rmax = 7.0):
|
||||
while True:
|
||||
try: return self.handle.commit()
|
||||
except: time.sleep(random.uniform(rmin, rmax))
|
||||
|
||||
def close(self):
|
||||
self.handle.close()
|
||||
|
||||
def __init__(self, database, factory = None):
|
||||
self.handle = lite.connect(database)
|
||||
if factory: self.handle.text_factory = factory
|
||||
self.cursor = self.handle.cursor()
|
||||
165
includes/proxywatchd.py
Normal file
165
includes/proxywatchd.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from threading import Thread
|
||||
import threading, commands
|
||||
import socket, time, random, sys, string, re
|
||||
#import sockschain as socks
|
||||
import requests
|
||||
#from geoip import geolite2
|
||||
|
||||
from ConfigParser import SafeConfigParser
|
||||
|
||||
sys.path.append('./includes')
|
||||
import mysqlite
|
||||
from misc import _log
|
||||
import rocksock
|
||||
|
||||
class Proxywatchd(Thread):
|
||||
|
||||
def stop(self):
|
||||
_log('Requesting proxywatchd to halt (%d thread(s))' % len([item for item in self.threads if item.isAlive()]))
|
||||
self.running = 0
|
||||
|
||||
def __init__(self, config_file):
|
||||
Thread.__init__(self)
|
||||
|
||||
self.threads = []
|
||||
self.running = 1
|
||||
self.parser = SafeConfigParser()
|
||||
self.parser.read(config_file)
|
||||
|
||||
self.maxfail = self.parser.getint('global', 'proxy_max_fail')
|
||||
self.maxthreads = self.parser.getint('watcherd', 'threads')
|
||||
self.checktime = self.parser.getint('watcherd', 'checktime')
|
||||
self.timeout = self.parser.getint('watcherd', 'timeout')
|
||||
self.database = self.parser.get('global', 'database')
|
||||
self.torhosts = [ str(i).strip() for i in self.parser.get('global', 'tor_host').split(',') ]
|
||||
self.read_timeout = self.parser.getint('watcherd', 'read_timeout')
|
||||
|
||||
# create table if needed
|
||||
self.mysqlite = mysqlite.mysqlite(self.database, str)
|
||||
self.mysqlite.execute('CREATE TABLE IF NOT EXISTS proxylist (proxy BLOB, country BLOB, added INT, failed INT, tested INT, source BLOB, dronebl INT, proto TEXT, duration INT)')
|
||||
self.mysqlite.commit()
|
||||
self.echoise = time.time() - 3600;
|
||||
self.ticks = time.time() - 3600;
|
||||
|
||||
with open('servers.txt', 'r') as handle: self.servers = handle.read().split('\n')
|
||||
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
_log('Starting proxywatchd..', 'notice')
|
||||
|
||||
threads = []
|
||||
self.mysqlite = mysqlite.mysqlite(self.database, str)
|
||||
|
||||
while self.running:
|
||||
|
||||
if len(threads) < self.maxthreads:
|
||||
t = threading.Thread(target=self.daemon, args=(self.servers,))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
time.sleep( random.choice( xrange(1,3)))
|
||||
|
||||
else: time.sleep(1)
|
||||
|
||||
if (time.time() - self.echoise) >= 180:
|
||||
_log('Proxywatchd threads: %d/%d' % (len(threads), self.maxthreads))
|
||||
self.echoise = time.time()
|
||||
|
||||
self.mysqlite.close()
|
||||
|
||||
def is_drone_bl(self, proxy):
|
||||
p = proxy.split(':')[0]
|
||||
proxies = {'http':'socks4://%s:%s@%s' % (p,p,random.choice(self.torhosts))}
|
||||
resp = requests.get('http://dronebl.org/lookup?ip=%s' % p, proxies=proxies)
|
||||
if 'No incidents regarding' in resp.text: return 0
|
||||
else: return 1
|
||||
|
||||
def connect_socket(self, proxy, servers, proto = None):
|
||||
proto = ['http', 'socks5', 'socks4']
|
||||
|
||||
for p in proto:
|
||||
torhost = random.choice(self.torhosts)
|
||||
duration = time.time()
|
||||
proxies = [ rocksock.RocksockProxyFromURL('socks4://%s' % torhost),
|
||||
rocksock.RocksockProxyFromURL('%s://%s' % (p, proxy[0])),
|
||||
]
|
||||
|
||||
tst = random.choice(servers).strip()
|
||||
try:
|
||||
sock = rocksock.Rocksock(host=tst, port=6667, ssl=False, proxies=proxies, timeout=self.timeout)
|
||||
sock.connect()
|
||||
sock.send('%s\n' % random.choice(['NICK', 'USER', 'JOIN', 'MODE', 'PART', 'INVITE', 'KNOCK', 'WHOIS', 'WHO', 'NOTICE', 'PRIVMSG', 'PING', 'QUIT']))
|
||||
return sock, p, duration, torhost, tst
|
||||
|
||||
except:
|
||||
#print('socket error, disconnecting')
|
||||
sock.disconnect()
|
||||
|
||||
return False, False, False, False, False
|
||||
|
||||
def daemon(self, servers):
|
||||
sqlite = mysqlite.mysqlite(self.database, str)
|
||||
threadid = ''.join( [ random.choice(string.letters) for x in range(5) ] )
|
||||
|
||||
q = 'SELECT proxy,failed,country,proto FROM proxylist WHERE failed<? and tested<? ORDER BY RANDOM() LIMIT ?'
|
||||
|
||||
while self.running:
|
||||
|
||||
time.sleep(0.1)
|
||||
sqlite_requests = []
|
||||
rows = sqlite.execute(q, (self.maxfail, time.time(), random.randint(10,20))).fetchall()
|
||||
|
||||
if not len(rows):
|
||||
time.sleep(random.randint(10,20))
|
||||
continue
|
||||
|
||||
abc = ' OR proxy='.join( [ '?' for x in xrange(0, len(rows)) ] )
|
||||
args = [ (time.time() + 180) ]
|
||||
e = [ args.append(i[0]) for i in rows ]
|
||||
sqlite.executemany('UPDATE proxylist SET tested=? WHERE proxy=%s' % abc, (args,))
|
||||
sqlite.commit()
|
||||
|
||||
for proxy in rows:
|
||||
time.sleep(0.1)
|
||||
nextcheck = (time.time() + 1800 + ((1+int(proxy[1])) * 3600))
|
||||
|
||||
sock, proto, duration, tor, tst = self.connect_socket(proxy, servers, proto=proxy[3])
|
||||
if not sock:
|
||||
sqlite_requests.append(((proxy[1]+1), nextcheck, 1, 'unknown', 'http', 0, proxy[0],))
|
||||
continue
|
||||
|
||||
try:
|
||||
recv = sock.recv(6)
|
||||
|
||||
# good data
|
||||
if re.match('^(:|ERROR|PING|PONG|NOTICE|\*\*\*)', recv, re.IGNORECASE):
|
||||
duration = (time.time() - duration)
|
||||
nextcheck = (time.time() + 1800)
|
||||
|
||||
#match = geolite2.lookup(proxy[0].split(':')[0])
|
||||
match = None
|
||||
if match is not None: match = match.country
|
||||
else: match = 'unknown'
|
||||
|
||||
#dronebl = self.is_drone_bl(proxy[0])
|
||||
sqlite_requests.append( (0, time.time(), 1, match, proto, duration, proxy[0],))
|
||||
_log('%s://%s; c: %s; d: %d sec(s); tor: %s; irc: %s; recv: %s' % (proto, proxy[0], match, duration, tor, tst, recv), threadid)
|
||||
|
||||
# bad data
|
||||
else:
|
||||
sqlite_requests.append(( (proxy[1]+1), nextcheck, 1, 'unknown', 'http', 0, proxy[0],))
|
||||
|
||||
# also bad
|
||||
except:
|
||||
sqlite_requests.append(( (proxy[1]+1), nextcheck, 1, 'unknown', 'http', 0, proxy[0],))
|
||||
|
||||
finally:
|
||||
sock.disconnect()
|
||||
|
||||
for r in sqlite_requests:
|
||||
sqlite.execute('UPDATE proxylist SET failed=?,tested=?,dronebl=?,country=?,proto=?,duration=? WHERE proxy=?', r)
|
||||
sqlite.commit()
|
||||
|
||||
sqlite.close()
|
||||
437
includes/rocksock.py
Normal file
437
includes/rocksock.py
Normal file
@@ -0,0 +1,437 @@
|
||||
import socket, ssl, select
|
||||
|
||||
# rs_proxyType
|
||||
RS_PT_NONE = 0
|
||||
RS_PT_SOCKS4 = 1
|
||||
RS_PT_SOCKS5 = 2
|
||||
RS_PT_HTTP = 3
|
||||
|
||||
# rs_errorType
|
||||
RS_ET_OWN = 0 # rocksock-specific error
|
||||
RS_ET_SYS = 1 # system error with errno
|
||||
RS_ET_GAI = 2 # dns resolution subsystem error
|
||||
RS_ET_SSL = 3 # ssl subsystem error
|
||||
|
||||
# rs_error
|
||||
RS_E_NO_ERROR = 0
|
||||
RS_E_NULL = 1
|
||||
RS_E_EXCEED_PROXY_LIMIT = 2
|
||||
RS_E_NO_SSL = 3
|
||||
RS_E_NO_SOCKET = 4
|
||||
RS_E_HIT_TIMEOUT = 5
|
||||
RS_E_OUT_OF_BUFFER = 6
|
||||
RS_E_SSL_GENERIC = 7
|
||||
RS_E_SOCKS4_NOAUTH = 8
|
||||
RS_E_SOCKS5_AUTH_EXCEEDSIZE = 9
|
||||
RS_E_SOCKS4_NO_IP6 = 10
|
||||
RS_E_PROXY_UNEXPECTED_RESPONSE = 11
|
||||
RS_E_TARGETPROXY_CONNECT_FAILED = 12
|
||||
RS_E_PROXY_AUTH_FAILED = 13
|
||||
RS_E_HIT_READTIMEOUT = 14
|
||||
RS_E_HIT_WRITETIMEOUT = 15
|
||||
RS_E_HIT_CONNECTTIMEOUT = 16
|
||||
RS_E_PROXY_GENERAL_FAILURE = 17
|
||||
RS_E_TARGETPROXY_NET_UNREACHABLE = 18
|
||||
RS_E_TARGETPROXY_HOST_UNREACHABLE = 19
|
||||
RS_E_TARGETPROXY_CONN_REFUSED = 20
|
||||
RS_E_TARGETPROXY_TTL_EXPIRED = 21
|
||||
RS_E_PROXY_COMMAND_NOT_SUPPORTED = 22
|
||||
RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED = 23
|
||||
RS_E_REMOTE_DISCONNECTED = 24
|
||||
RS_E_NO_PROXYSTORAGE = 25
|
||||
RS_E_HOSTNAME_TOO_LONG = 26
|
||||
RS_E_INVALID_PROXY_URL = 27
|
||||
|
||||
|
||||
class RocksockException(Exception):
|
||||
def __init__(self, error, failedproxy=None, errortype=RS_ET_OWN, *args, **kwargs):
|
||||
Exception.__init__(self,*args,**kwargs)
|
||||
self.error = error
|
||||
self.errortype = errortype
|
||||
self.failedproxy = failedproxy
|
||||
|
||||
def get_failedproxy(self):
|
||||
return self.failedproxy
|
||||
|
||||
def get_error(self):
|
||||
return self.error
|
||||
|
||||
def reraise(self):
|
||||
import sys
|
||||
ei = sys.exc_info()
|
||||
raise ei[0], ei[1], ei[2]
|
||||
# import traceback, sys
|
||||
# traceback.print_exc(file=sys.stderr)
|
||||
# raise self
|
||||
|
||||
def get_errormessage(self):
|
||||
errordict = {
|
||||
RS_E_NO_ERROR : "no error",
|
||||
RS_E_NULL: "NULL pointer passed",
|
||||
RS_E_EXCEED_PROXY_LIMIT: "exceeding maximum number of proxies",
|
||||
RS_E_NO_SSL: "can not establish SSL connection, since library was not compiled with USE_SSL define",
|
||||
RS_E_NO_SOCKET: "socket is not set up, maybe you should call connect first",
|
||||
RS_E_HIT_TIMEOUT: "timeout reached on operation",
|
||||
RS_E_OUT_OF_BUFFER: "supplied buffer is too small",
|
||||
RS_E_SSL_GENERIC: "generic SSL error", # the C version uses this error when the SSL library does not report any specific error, otherwise errortype SSL will be set and the SSL errorcode be used
|
||||
RS_E_SOCKS4_NOAUTH:"SOCKS4 authentication not implemented",
|
||||
RS_E_SOCKS5_AUTH_EXCEEDSIZE: "maximum length for SOCKS5 servername/password/username is 255",
|
||||
RS_E_SOCKS4_NO_IP6: "SOCKS4 is not compatible with IPv6",
|
||||
RS_E_PROXY_UNEXPECTED_RESPONSE: "the proxy sent an unexpected response",
|
||||
RS_E_TARGETPROXY_CONNECT_FAILED: "could not connect to target proxy",
|
||||
RS_E_PROXY_AUTH_FAILED: "proxy authentication failed or authd not enabled",
|
||||
RS_E_HIT_READTIMEOUT : "timeout reached on read operation",
|
||||
RS_E_HIT_WRITETIMEOUT : "timeout reached on write operation",
|
||||
RS_E_HIT_CONNECTTIMEOUT : "timeout reached on connect operation",
|
||||
RS_E_PROXY_GENERAL_FAILURE : "proxy general failure",
|
||||
RS_E_TARGETPROXY_NET_UNREACHABLE : "proxy-target: net unreachable",
|
||||
RS_E_TARGETPROXY_HOST_UNREACHABLE : "proxy-target: host unreachable",
|
||||
RS_E_TARGETPROXY_CONN_REFUSED : "proxy-target: connection refused",
|
||||
RS_E_TARGETPROXY_TTL_EXPIRED : "proxy-target: TTL expired",
|
||||
RS_E_PROXY_COMMAND_NOT_SUPPORTED : "proxy: command not supported",
|
||||
RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED : "proxy: addresstype not supported",
|
||||
RS_E_REMOTE_DISCONNECTED : "remote socket closed connection",
|
||||
RS_E_NO_PROXYSTORAGE : "no proxy storage assigned",
|
||||
RS_E_HOSTNAME_TOO_LONG : "hostname exceeds 255 chars",
|
||||
RS_E_INVALID_PROXY_URL : "invalid proxy URL string"
|
||||
}
|
||||
if self.errortype == RS_ET_SYS:
|
||||
import errno
|
||||
if self.error in errno.errorcode:
|
||||
msg = "ERRNO: " + errno.errorcode[self.error]
|
||||
else:
|
||||
msg = "ERRNO: invalid errno: " + str(self.error)
|
||||
elif self.errortype == RS_ET_GAI:
|
||||
msg = "GAI: " + self.failedproxy
|
||||
elif self.errortype == RS_ET_SSL:
|
||||
msg = errordict[self.error]
|
||||
if self.error == RS_E_SSL_GENERIC and self.failedproxy != None:
|
||||
msg += ': ' + self.failedproxy #failedproxy is repurposed for SSL exceptions
|
||||
else: #RS_ET_OWN
|
||||
msg = errordict[self.error]
|
||||
return msg
|
||||
|
||||
|
||||
class RocksockHostinfo():
|
||||
def __init__(self, host, port):
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
class RocksockProxy():
|
||||
def __init__(self, host, port, type, username = None, password=None, **kwargs):
|
||||
typemap = { 'none' : RS_PT_NONE,
|
||||
'socks4' : RS_PT_SOCKS4,
|
||||
'socks5' : RS_PT_SOCKS5,
|
||||
'http' : RS_PT_HTTP }
|
||||
self.type = typemap[type] if type in typemap else type
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.hostinfo = RocksockHostinfo(host, port)
|
||||
|
||||
def RocksockProxyFromURL(url):
|
||||
# valid URL: socks5://[user:pass@]hostname:port
|
||||
x = url.find('://')
|
||||
if x == -1: return None
|
||||
t = url[:x]
|
||||
url = url[x+len('://'):]
|
||||
x = url.rfind(':')
|
||||
if x == -1: return None # port is obligatory
|
||||
port = int(url[x+len(':'):]) #TODO: catch exception when port is non-numeric
|
||||
url = url[:x]
|
||||
x = url.find('@')
|
||||
if x != -1:
|
||||
u, p = url[:x].split(':')
|
||||
url = url[x+len('@'):]
|
||||
else:
|
||||
u, p = (None, None)
|
||||
return RocksockProxy(host=url, port=port, type=t, username=u, password=p)
|
||||
|
||||
|
||||
class Rocksock():
|
||||
def __init__(self, host=None, port=0, verifycert=False, timeout=0, proxies=None, **kwargs):
|
||||
if 'ssl' in kwargs and kwargs['ssl'] == True:
|
||||
self.sslcontext = ssl.create_default_context()
|
||||
self.sslcontext.check_hostname = False
|
||||
if not verifycert: self.sslcontext.verify_mode = ssl.CERT_NONE
|
||||
else:
|
||||
self.sslcontext = None
|
||||
self.proxychain = proxies if proxies else []
|
||||
target = RocksockProxy(host, port, RS_PT_NONE)
|
||||
self.proxychain.append(target)
|
||||
self.sock = None
|
||||
self.timeout = timeout
|
||||
|
||||
def connect(self):
|
||||
|
||||
af, sa = self._resolve(self.proxychain[0].hostinfo, True)
|
||||
try:
|
||||
x = af+1
|
||||
except TypeError:
|
||||
raise RocksockException(-3, "unexpected problem resolving DNS, try again", errortype=RS_ET_GAI)
|
||||
# print "GOT A WEIRD AF"
|
||||
# print af
|
||||
# raise RocksockException(-6666, af, errortype=RS_ET_GAI)
|
||||
|
||||
self.sock = socket.socket(af, socket.SOCK_STREAM)
|
||||
self.sock.settimeout(None if self.timeout == 0 else self.timeout)
|
||||
try:
|
||||
self.sock.connect((sa[0], sa[1]))
|
||||
except socket.timeout:
|
||||
raise RocksockException(RS_E_HIT_TIMEOUT)
|
||||
except socket.error as e:
|
||||
raise RocksockException(e.errno, errortype=RS_ET_SYS)
|
||||
|
||||
for pnum in xrange(1, len(self.proxychain)):
|
||||
curr = self.proxychain[pnum]
|
||||
prev = self.proxychain[pnum-1]
|
||||
self._connect_step(pnum)
|
||||
|
||||
if self.sslcontext:
|
||||
try:
|
||||
self.sock = self.sslcontext.wrap_socket(self.sock, server_hostname=self.proxychain[len(self.proxychain)-1].hostinfo.host)
|
||||
except ssl.SSLError as e:
|
||||
reason = self._get_ssl_exception_reason(e)
|
||||
#if hasattr(e, 'library'): subsystem = e.library
|
||||
raise RocksockException(RS_E_SSL_GENERIC, reason, errortype=RS_ET_SSL)
|
||||
except socket.error as e:
|
||||
raise RocksockException(e.errno, errortype=RS_ET_SYS)
|
||||
except Exception as e:
|
||||
raise e
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
self.sock.do_handshake()
|
||||
break
|
||||
except ssl.SSLWantReadError:
|
||||
select.select([self.sock], [], [])
|
||||
except ssl.SSLWantWriteError:
|
||||
select.select([], [self.sock], [])
|
||||
"""
|
||||
|
||||
|
||||
def disconnect(self):
|
||||
if self.sock is None: return
|
||||
try:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
except socket.error:
|
||||
pass
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
||||
def canread(self):
|
||||
return select.select([self.sock], [], [], 0)[0]
|
||||
|
||||
def send(self, buf):
|
||||
if self.sock is None:
|
||||
raise RocksockException(RS_E_NO_SOCKET)
|
||||
return self.sock.sendall(buf)
|
||||
|
||||
def _get_ssl_exception_reason(self, e):
|
||||
s = ''
|
||||
if hasattr(e, 'reason'): s = e.reason
|
||||
elif hasattr(e, 'message'): s = e.message
|
||||
elif hasattr(e, 'args'): s = e.args[0]
|
||||
return s
|
||||
|
||||
def recv(self, count=-1):
|
||||
data = ''
|
||||
while count:
|
||||
try:
|
||||
n = count if count != -1 else 4096
|
||||
if n >= 1024*1024: n = 1024*1024
|
||||
chunk = self.sock.recv(n)
|
||||
except socket.timeout:
|
||||
raise RocksockException(RS_E_HIT_TIMEOUT)
|
||||
except ssl.SSLError as e:
|
||||
s = self._get_ssl_exception_reason(e)
|
||||
if s == 'The read operation timed out':
|
||||
raise RocksockException(RS_E_HIT_READTIMEOUT)
|
||||
else:
|
||||
raise RocksockException(RS_E_SSL_GENERIC, s, errortype=RS_ET_SSL)
|
||||
if len(chunk) == 0:
|
||||
raise RocksockException(RS_E_REMOTE_DISCONNECTED)
|
||||
data += chunk
|
||||
if count == -1: break
|
||||
else: count -= len(chunk)
|
||||
return data
|
||||
|
||||
def recvline(self):
|
||||
s = ''
|
||||
c = '\0'
|
||||
while c != '\n':
|
||||
c = self.recv(1)
|
||||
if c == '': return s
|
||||
s += c
|
||||
return s
|
||||
|
||||
def recvuntil(self, until):
|
||||
s = self.recv(len(until))
|
||||
endc = until[-1:]
|
||||
while not (s[-1:] == endc and s.endswith(until)):
|
||||
s += self.recv(1)
|
||||
return s
|
||||
|
||||
def _isnumericipv4(self, ip):
|
||||
try:
|
||||
a,b,c,d = ip.split('.')
|
||||
if int(a) < 256 and int(b) < 256 and int(c) < 256 and int(d) < 256:
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def _resolve(self, hostinfo, want_v4=True):
|
||||
if self._isnumericipv4(hostinfo.host):
|
||||
return socket.AF_INET, (hostinfo.host, hostinfo.port)
|
||||
try:
|
||||
for res in socket.getaddrinfo(hostinfo.host, hostinfo.port, \
|
||||
socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
if want_v4 and af != socket.AF_INET: continue
|
||||
if af != socket.AF_INET and af != socket.AF_INET6: continue
|
||||
else: return af, sa
|
||||
|
||||
except socket.gaierror as e:
|
||||
eno, str = e.args
|
||||
raise RocksockException(eno, str, errortype=RS_ET_GAI)
|
||||
|
||||
return None, None
|
||||
|
||||
def _ip_to_int(self, ip):
|
||||
a,b,c,d = ip.split('.')
|
||||
h = "0x%.2X%.2X%.2X%.2X"%(int(a),int(b),int(c),int(d))
|
||||
return int(h, 16)
|
||||
|
||||
def _ip_to_bytes(self, ip):
|
||||
ip = self._ip_to_int(ip)
|
||||
a = (ip & 0xff000000) >> 24
|
||||
b = (ip & 0x00ff0000) >> 16
|
||||
c = (ip & 0x0000ff00) >> 8
|
||||
d = (ip & 0x000000ff) >> 0
|
||||
return chr(a) + chr(b) + chr(c) + chr(d)
|
||||
|
||||
def _setup_socks4_header(self, v4a, dest):
|
||||
buf = '\x04\x01'
|
||||
buf += chr(dest.hostinfo.port / 256)
|
||||
buf += chr(dest.hostinfo.port % 256)
|
||||
if v4a:
|
||||
buf += '\0\0\0\x01'
|
||||
else:
|
||||
af, sa = self._resolve(dest, True)
|
||||
if af != socket.AF_INET: raise RocksockException(RS_E_SOCKS4_NO_IP6)
|
||||
buf += self._ip_to_bytes(sa[0])
|
||||
buf += '\0'
|
||||
if v4a: buf += dest.hostinfo.host + '\0'
|
||||
return buf
|
||||
|
||||
def _connect_socks4(self, header, pnum):
|
||||
self.send(header)
|
||||
res = self.recv(8)
|
||||
if len(res) < 8 or ord(res[0]) != 0:
|
||||
raise RocksockException(RS_E_PROXY_UNEXPECTED_RESPONSE, pnum-1)
|
||||
ch = ord(res[1])
|
||||
if ch == 0x5a:
|
||||
pass
|
||||
elif ch == 0x5b:
|
||||
raise RocksockException(RS_E_TARGETPROXY_CONNECT_FAILED, pnum-1)
|
||||
elif ch == 0x5c or ch == 0x5d:
|
||||
return RocksockException(RS_E_PROXY_AUTH_FAILED, pnum-1)
|
||||
else:
|
||||
raise RocksockException(RS_E_PROXY_UNEXPECTED_RESPONSE, pnum-1)
|
||||
|
||||
def _setup_socks5_header(self, proxy):
|
||||
buf = '\x05'
|
||||
if proxy.username and proxy.password:
|
||||
buf += '\x02\x00\x02'
|
||||
else:
|
||||
buf += '\x01\x00'
|
||||
return buf
|
||||
|
||||
def _connect_socks5(self, header, pnum):
|
||||
self.send(header)
|
||||
res = self.recv(2)
|
||||
if len(res) != 2 or res[0] != '\x05':
|
||||
raise RocksockException(RS_E_PROXY_UNEXPECTED_RESPONSE, pnum-1)
|
||||
if res[1] == '\xff':
|
||||
raise RocksockException(RS_E_PROXY_AUTH_FAILED, pnum-1)
|
||||
|
||||
if ord(res[1]) == 2:
|
||||
px = self.proxychain[pnum-1]
|
||||
if px.username and px.password:
|
||||
pkt = '\x01%c%s%c%s'%(len(px.username),px.username,len(px.password),px.password)
|
||||
self.send(pkt)
|
||||
res = self.recv(2)
|
||||
if len(res) < 2 or res[1] != '\0':
|
||||
raise RocksockException(RS_E_PROXY_AUTH_FAILED, pnum-1)
|
||||
else: raise RocksockException(RS_E_PROXY_AUTH_FAILED, pnum-1)
|
||||
dst = self.proxychain[pnum]
|
||||
numeric = self._isnumericipv4(dst.hostinfo.host)
|
||||
if numeric:
|
||||
dstaddr = self._ip_to_bytes(dst.hostinfo.host)
|
||||
else:
|
||||
dstaddr = chr(len(dst.hostinfo.host)) + dst.hostinfo.host
|
||||
|
||||
pkt = '\x05\x01\x00%c%s%c%c'% (1 if numeric else 3, dstaddr, dst.hostinfo.port / 256, dst.hostinfo.port % 256)
|
||||
self.send(pkt)
|
||||
res = self.recv()
|
||||
if len(res) < 2 or res[0] != '\x05':
|
||||
raise RocksockException(RS_E_PROXY_UNEXPECTED_RESPONSE, pnum-1)
|
||||
ch = ord(res[1])
|
||||
if ch == 0: pass
|
||||
elif ch == 1: raise RocksockException(RS_E_PROXY_GENERAL_FAILURE, pnum-1)
|
||||
elif ch == 2: raise RocksockException(RS_E_PROXY_AUTH_FAILED, pnum-1)
|
||||
elif ch == 3: raise RocksockException(RS_E_TARGETPROXY_NET_UNREACHABLE, pnum-1)
|
||||
elif ch == 4: raise RocksockException(RS_E_TARGETPROXY_HOST_UNREACHABLE, pnum-1)
|
||||
elif ch == 5: raise RocksockException(RS_E_TARGETPROXY_CONN_REFUSED, pnum-1)
|
||||
elif ch == 6: raise RocksockException(RS_E_TARGETPROXY_TTL_EXPIRED, pnum-1)
|
||||
elif ch == 7: raise RocksockException(RS_E_PROXY_COMMAND_NOT_SUPPORTED, pnum-1)
|
||||
elif ch == 8: raise RocksockException(RS_E_PROXY_ADDRESSTYPE_NOT_SUPPORTED, pnum-1)
|
||||
else: raise RocksockException(RS_E_PROXY_UNEXPECTED_RESPONSE, pnum-1)
|
||||
|
||||
|
||||
def _connect_step(self, pnum):
|
||||
prev = self.proxychain[pnum -1]
|
||||
curr = self.proxychain[pnum]
|
||||
if prev.type == RS_PT_SOCKS4:
|
||||
s4a = self._setup_socks4_header(True, curr)
|
||||
try:
|
||||
self._connect_socks4(s4a, pnum)
|
||||
except RocksockException as e:
|
||||
if e.get_error() == RS_E_TARGETPROXY_CONNECT_FAILED:
|
||||
s4 = self._setup_socks4_header(self, False, curr)
|
||||
self._connect_socks4(s4a, pnum)
|
||||
else: raise e
|
||||
elif prev.type == RS_PT_SOCKS5:
|
||||
s5 = self._setup_socks5_header(prev)
|
||||
self._connect_socks5(s5, pnum)
|
||||
elif prev.type == RS_PT_HTTP:
|
||||
dest = self.proxychain[pnum]
|
||||
self.send("CONNECT %s:%d HTTP/1.1\r\n\r\n"%(dest.hostinfo.host, dest.hostinfo.port))
|
||||
resp = self.recv()
|
||||
if len(resp) <12:
|
||||
raise RocksockException(RS_E_PROXY_UNEXPECTED_RESPONSE, pnum-1)
|
||||
if resp[9] != '2':
|
||||
raise RocksockException(RS_E_TARGETPROXY_CONNECT_FAILED, pnum-1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
proxies = [
|
||||
# RocksockProxyFromURL("socks5://foo:bar@localhost:1080"),
|
||||
# RocksockProxyFromURL("socks5://10.0.0.3:1080"),
|
||||
RocksockProxyFromURL("socks5://127.0.0.1:31339"),
|
||||
]
|
||||
proxies = None
|
||||
#rs = Rocksock(host='googleff242342423f.com', port=443, ssl=True, proxies=proxies)
|
||||
rs = Rocksock(host='google.com', port=80, ssl=False, proxies=proxies)
|
||||
try:
|
||||
rs.connect()
|
||||
except RocksockException as e:
|
||||
print e.get_errormessage()
|
||||
e.reraise()
|
||||
rs.send('GET / HTTP/1.0\r\n\r\n')
|
||||
print rs.recvline()
|
||||
rs.disconnect()
|
||||
rs.connect()
|
||||
rs.send('GET / HTTP/1.0\r\n\r\n')
|
||||
print rs.recvline()
|
||||
|
||||
Reference in New Issue
Block a user