Last active
August 17, 2022 15:32
-
-
Save u8sand/0df0f75190717c3963e2bcb6e47e02dc to your computer and use it in GitHub Desktop.
A wrapper script for generating proxychains config and running proxychains with a sane command-line interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/python | |
import sys | |
import click | |
import socket | |
import contextlib | |
import urllib.parse | |
import tempfile | |
from subprocess import Popen | |
def free_port(): | |
with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: | |
s.bind(('127.0.0.1', 0)) | |
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
return s.getsockname()[1] | |
@click.command( | |
context_settings=dict( | |
ignore_unknown_options=True, | |
allow_interspersed_args=False, | |
) | |
) | |
@click.option( | |
'-t', '--chain-type', | |
default='strict', | |
show_default=True, | |
type=click.Choice([ | |
'strict', | |
'dynamic', | |
'round_robin', | |
'random', | |
]), | |
help='Chain type as described by proxychains config' | |
) | |
@click.option( | |
'-l', '--chain-len', | |
type=int, | |
help='Length of the chain (for random_chain/round_robin_chain)', | |
) | |
@click.option( | |
'--remote-dns-subnet', | |
type=int, | |
default=224, | |
show_default=True, | |
help='remote_dns_subnet as described by proxychains' | |
) | |
@click.option( | |
'-d', '--proxy-dns', | |
type=bool, | |
is_flag=True, | |
help='proxy_dns as described by proxychains' | |
) | |
@click.option( | |
'-e', '--localnet', | |
type=str, | |
multiple=True, | |
help='localnet as described by proxychains', | |
) | |
@click.option( | |
'-q', '--quiet', | |
type=bool, | |
is_flag=True, | |
help='Quiet mode', | |
) | |
@click.option( | |
'--tcp-read-time-out', | |
type=int, | |
default=15000, | |
show_default=True, | |
help='tcp_read_time_out as described by proxychains', | |
) | |
@click.option( | |
'--tcp-connect-time-out', | |
type=int, | |
default=8000, | |
show_default=True, | |
help='tcp_connect_time_out as described by proxychains', | |
) | |
@click.option( | |
'-p', '--proxy', | |
multiple=True, | |
type=str, | |
help='Any socks uris for the proxy list e.g. `-p socks5://host:1002 -p socks4://user:passs@host2:1000`', | |
) | |
@click.option( | |
'-J', '--jump', | |
help='Jump host(s) for using ssh to setup the proxy server', | |
) | |
@click.argument( | |
'command', | |
nargs=-1, | |
type=click.UNPROCESSED, | |
) | |
def proxychains( | |
command, | |
chain_type, | |
chain_len, | |
remote_dns_subnet, | |
proxy_dns, | |
localnet, | |
quiet, | |
tcp_read_time_out, | |
tcp_connect_time_out, | |
proxy, | |
jump, | |
): | |
assert proxy is not None or jump is not None, '-p, --proxy or -J, --jump must be provided' | |
assert proxy is None or jump is None, 'proxy and jump are mutually exclusive' | |
if proxy is None: | |
port = free_port() | |
*prelim_jumps, final_jump = jump.split(',') | |
proxy_proc = Popen([ | |
'ssh', | |
*((f"-J{','.join(prelim_jumps)}",) if prelim_jumps else tuple()), | |
f"-ND{port}", | |
final_jump, | |
], stdout=sys.stdout, stderr=sys.stderr) | |
proxy = [f"socks5://127.0.0.1:{port}"] | |
else: | |
proxy_proc = None | |
# | |
config_lines = [] | |
config_lines.append('%s_chain' % (chain_type)) | |
if chain_len is not None: config_lines.append('chain_len %d' % (chain_len)) | |
if remote_dns_subnet is not None: config_lines.append('remote_dns_subnet %d' % (remote_dns_subnet)) | |
if tcp_read_time_out is not None: config_lines.append('tcp_read_time_out %d' % (tcp_read_time_out)) | |
if tcp_connect_time_out is not None: config_lines.append('tcp_connect_time_out %d' % (tcp_connect_time_out)) | |
if localnet is not None: config_lines += ['localnet %s' % (line) for line in localnet] | |
if proxy_dns: config_lines.append('proxy_dns') | |
if quiet: config_lines.append('quiet') | |
config_lines.append('[ProxyList]') | |
for proxy in proxy: | |
proxy_uri = urllib.parse.urlparse(proxy) | |
config_lines.append( | |
' '.join(map(str, filter(None, ( | |
proxy_uri.scheme, | |
proxy_uri.hostname, | |
proxy_uri.port, | |
proxy_uri.username, | |
proxy_uri.password, | |
)))) | |
) | |
exit_code = 1 | |
with tempfile.NamedTemporaryFile('w') as proxy_config: | |
print(*config_lines, sep='\n', file=proxy_config.file) | |
proxy_config.file.close() | |
with Popen(['proxychains', '-f', proxy_config.name, *command], | |
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) as proc: | |
exit_code = proc.wait() | |
if proxy_proc is not None: | |
proxy_proc.terminate() | |
sys.exit(exit_code) | |
if __name__ == '__main__': | |
proxychains() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment