Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Route vpn traffic through socks proxy? #32

Open
nyu-macroman opened this issue Nov 2, 2024 · 4 comments
Open

Route vpn traffic through socks proxy? #32

nyu-macroman opened this issue Nov 2, 2024 · 4 comments

Comments

@nyu-macroman
Copy link

nyu-macroman commented Nov 2, 2024

Hi, can’t thank you enough for this awesome script! It works amazingly. One thing I was wondering though if you could help me understand how to do is to route my vpn to my home network like pivpn or tail scale so that way I can use the unrestricted hotspot speeds with something like sunshine/moonlight for fast game streaming?

It seems when I turn on vpn on either the phone or iPad, it will not go through the socks proxy and be limited as usual.

@nneonneo
Copy link
Owner

nneonneo commented Nov 3, 2024

Good question. Support for this depends entirely on your VPN software; some VPN software will bypass the system proxy settings, which prevents this approach from working. Other VPN software will use UDP, which is supported by this proxy but not supported by many clients.

If your VPN has a fixed endpoint (i.e. you know the IP/port that your VPN will connect to), one approach can be to set up a forwarder which forwards a specific listening port on your iPhone running the proxy to the remote VPN over the cellular connection, then tell your VPN software to connect to your iPhone. Concretely, it would look something like this:

  • iPhone runs a forwarder (e.g. Python script) which forwards incoming connections port 1337 to my.vpn.site:443 over the cellular connection
  • Other device VPN is configured to connect to iPhone:1337 instead of the default server

The forwarder doesn't have to be especially complicated; the following script would basically do the trick (for TCP):

import socket
import ifaddrs
from collections import defaultdict
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
from select import select

PROXY_HOST = "172.20.10.1"
PROXY_PORT = 1337
CONNECT_HOST = "0.0.0.0"
# Time out connections after being idle for this long (in seconds)
IDLE_TIMEOUT = 1800
TARGET = ("ip.me", 443)

interfaces = ifaddrs.get_interfaces()
iftypes = defaultdict(list)
initial_output = ""
for iface in interfaces:
    if not iface.addr:
        continue
    if iface.name.startswith('lo'):
        continue
    # XXX implement better classification of interfaces
    if iface.name.startswith('en'):
        iftypes['en'].append(iface)
    elif iface.name.startswith('bridge'):
        iftypes['bridge'].append(iface)
    else:
        iftypes['cell'].append(iface)

if iftypes['bridge']:
    iface = next((iface for iface in iftypes['bridge'] if iface.addr.family == socket.AF_INET), None)
    if iface:
        initial_output = "Assuming proxy will be accessed over hotspot (%s) at %s:%d\n" % (iface.name, iface.addr.address, PROXY_PORT)
        PROXY_HOST = iface.addr.address
elif iftypes['en']:
    iface = next((iface for iface in iftypes['en'] if iface.addr.family == socket.AF_INET), None)
    if iface:
        initial_output += "Assuming proxy will be accessed over WiFi (%s) at %s\n" % (iface.name, iface.addr.address)
        PROXY_HOST = iface.addr.address
else:
    initial_output += 'Warning: could not get WiFi address; assuming %s\n' % PROXY_HOST

if iftypes['cell']:
    iface = next((iface for iface in iftypes['cell'] if iface.addr.family == socket.AF_INET), None)
    if iface:
        initial_output += "Will connect to %s:%d over interface %s at %s\n" % (TARGET[0], TARGET[1], iface.name, iface.addr.address)
        CONNECT_HOST = iface.addr.address

print(initial_output)

def tcp_loop(csock, ssock):
    while True:
        r, _, _ = select([csock, ssock], [], [], IDLE_TIMEOUT)
        if not r:
            raise socket.timeout()

        if csock in r:
            data = csock.recv(4096)
            if not data:
                break
            ssock.sendall(data)

        if ssock in r:
            data = ssock.recv(4096)
            if not data:
                break
            csock.sendall(data)

class ThreadingTCPForwarder(ThreadingMixIn, TCPServer):
    daemon_threads = True
    allow_reuse_address = True

    def __init__(self, target, *args, **kwargs):
        self.target = target
        super().__init__(*args, **kwargs)

class TCPForwarderHandler(StreamRequestHandler):

    def handle(self):
        remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        remote.bind((CONNECT_HOST, 0))
        remote.connect(self.server.target)

        tcp_loop(self.connection, remote)

if __name__ == '__main__':
    server = ThreadingTCPForwarder(TARGET, (PROXY_HOST, PROXY_PORT), TCPForwarderHandler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("Shutting down.")
        server.shutdown()

Change TARGET as appropriate.

@nyu-macroman
Copy link
Author

nyu-macroman commented Nov 3, 2024

This is very detailed and I sort of follow but I’m still a noob getting into python 😅 I think NordVPN’s meshnet uses UDP so I could tweak your example to use UDPServer instead. Does the rest stay true? And it also looks like I could implement the example code you have in the original sock5? Or how would I go about adding this forwarding alongside the proxy?

I believe the meshnet local address and port I want to ultimately access the sunshine gaming computer is 100.69.184.241:47984

@nneonneo
Copy link
Owner

nneonneo commented Nov 3, 2024

Yeah, I think you can tweak it to work with UDPServer. You have to change send to sendto (and recv to recvfrom), and some other small details, but it should work.

If you want to use the SOCKS proxy at the same time as a dedicated forwarding proxy, it's as easy as throwing the server.serve_forever into a thread (i.e. vpnproxy_thread = threading.Thread(target=server.serve_forever, daemon=True) then vpnproxy_thread.start()). In fact, the code can be simplified somewhat because the SOCKS proxy already takes care of all the interface detection logic (I duplicated that in the script above to make it stand alone).

@nyu-macroman
Copy link
Author

Thanks for all that info! I’ve been playing around with it and hopefully can get it to work. Been googling a lot and trying to learn as much as I can :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants