python-proxy icon indicating copy to clipboard operation
python-proxy copied to clipboard

Rules seem to be ignored with 'direct'

Open MartinusR opened this issue 1 year ago • 3 comments

I want to exclude local addresses from going through the proxy.

When I try to use direct with some rules, it seems that the rules are ignored and everything goes through direct:

pproxy -r direct://?rules -r socks5://server

With rules containing:

localhost

Then for instance wget google.com goes through direct:

http ::1:50386 -> google.com:80

If I create another local socks5 (or http) proxy that has no remote (direct only), this works: pproxy -r socks5://direct_proxy?rules -r socks5://server

When i do wget google.com, this is now working:

http ::1:50538 -> socks5 server -> google.com:80

MartinusR avatar Jan 25 '24 14:01 MartinusR

It works with older (2.2.0) version, but not with recent one. Unfortunately in 2.2.0 HTTP2 is broken (or not implemented). It would be really nice if this could be fixed.

gordon-quad avatar Mar 09 '24 11:03 gordon-quad

Simple but ugly workaround

diff --git a/pproxy/server.py b/pproxy/server.py
index dc3e4e5..26963ce 100644
--- a/pproxy/server.py
+++ b/pproxy/server.py
@@ -153,11 +153,12 @@ async def check_server_alive(interval, rserver, verbose):
                 pass

 class ProxyDirect(object):
-    def __init__(self, lbind=None):
+    def __init__(self, rule=None, lbind=None):
         self.bind = 'DIRECT'
         self.lbind = lbind
         self.unix = False
         self.alive = True
+        self.rule = compile_rule(rule) if rule else None
         self.connections = 0
         self.udpmap = {}
     @property
@@ -166,7 +167,7 @@ class ProxyDirect(object):
     def logtext(self, host, port):
         return '' if host == 'tunnel' else f' -> {host}:{port}'
     def match_rule(self, host, port):
-        return True
+        return (self.rule is None) or self.rule(host) or self.rule(str(port))
     def connection_change(self, delta):
         self.connections += delta
     def udp_packet_unpack(self, data):
@@ -827,7 +828,7 @@ def proxy_by_uri(uri, jump):
         auth = url.fragment.encode()
     users = [i.rstrip() for i in auth.split(b'\n')] if auth else None
     if 'direct' in protonames:
-        return ProxyDirect(lbind=lbind)
+        return ProxyDirect(lbind=lbind, rule=url.query)
     else:
         params = dict(jump=jump, protos=protos, cipher=cipher, users=users, rule=url.query, bind=loc or urlpath,
                       host_name=host_name, port=port, unix=not loc, lbind=lbind, sslclient=sslclient, sslserver=sslserver)

gordon-quad avatar Apr 19 '24 23:04 gordon-quad

This is my solution, no need to modify any code. Just write your own code.

import asyncio
import uvloop
import signal
from pproxy import Server,Connection
from pproxy.server import ProxyDirect

HOSTS=(
    'google.com',
    'x.com',
    'facebook.com',
)
LOCAL='http+socks5://127.0.0.1:8080'
REMOTE='ss://cipher:[email protected]:port'

def rule(host,port):
    for item in HOSTS:
        if host.endswith(item):
            return True

async def main():
    direct=ProxyDirect()
    direct.match_rule=rule
    server=Server(LOCAL)
    handler=await server.start_server({
        'rserver':(direct,Connection(REMOTE)),
        'verbose':print,
        'ruport':True,
    })
    stop=asyncio.Event()
    signal.signal(signal.SIGINT,lambda sig,frame:stop.set())
    await stop.wait()
    handler.close()
    if hasattr(handler,'wait_closed'):
        await handler.wait_closed()
    await asyncio.get_event_loop().shutdown_asyncgens()

if __name__=='__main__':
    uvloop.install()
    asyncio.run(main())

Jonney avatar Apr 25 '24 08:04 Jonney