This repository has been archived by the owner on Apr 3, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 434
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
110d887
commit a77d387
Showing
7 changed files
with
294 additions
and
28 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// +build d | ||
|
||
package main | ||
|
||
import ( | ||
"flag" | ||
"net" | ||
"strings" | ||
|
||
"github.com/eycorsican/go-tun2socks/common/log" | ||
"github.com/eycorsican/go-tun2socks/core" | ||
"github.com/eycorsican/go-tun2socks/proxy/d" | ||
"github.com/eycorsican/go-tun2socks/proxy/socks" | ||
) | ||
|
||
func init() { | ||
args.addFlag(fProxyServer) | ||
args.addFlag(fUdpTimeout) | ||
args.addFlag(fApplog) | ||
|
||
args.ExceptionApps = flag.String("exceptionApps", "", "Exception app list separated by commas") | ||
args.ExceptionSendThrough = flag.String("exceptionSendThrough", "192.168.1.101:0", "Exception send through address") | ||
|
||
registerHandlerCreater("d", func() { | ||
// Verify proxy server address. | ||
proxyAddr, err := net.ResolveTCPAddr("tcp", *args.ProxyServer) | ||
if err != nil { | ||
log.Fatalf("invalid proxy server address: %v", err) | ||
} | ||
proxyHost := proxyAddr.IP.String() | ||
proxyPort := uint16(proxyAddr.Port) | ||
|
||
proxyTCPHandler := socks.NewTCPHandler(proxyHost, proxyPort, fakeDns) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
eycorsican
via email
Author
Owner
|
||
proxyUDPHandler := socks.NewUDPHandler(proxyHost, proxyPort, *args.UdpTimeout, dnsCache, fakeDns) | ||
|
||
sendThrough, err := net.ResolveTCPAddr("tcp", *args.ExceptionSendThrough) | ||
if err != nil { | ||
log.Fatalf("invalid exception send through address: %v", err) | ||
} | ||
apps := strings.Split(*args.ExceptionApps, ",") | ||
tcpHandler := d.NewTCPHandler(proxyTCPHandler, apps, sendThrough) | ||
udpHandler := d.NewUDPHandler(proxyUDPHandler, apps, sendThrough, *args.UdpTimeout) | ||
|
||
core.RegisterTCPConnHandler(tcpHandler) | ||
core.RegisterUDPConnHandler(udpHandler) | ||
}) | ||
} |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package d | ||
|
||
import ( | ||
"io" | ||
"net" | ||
"strconv" | ||
|
||
"github.com/eycorsican/go-tun2socks/common/log" | ||
"github.com/eycorsican/go-tun2socks/common/lsof" | ||
"github.com/eycorsican/go-tun2socks/core" | ||
) | ||
|
||
// This handler allows you chain another proxy behind tun2socks locally, typically a rule-based proxy client, e.g. V2Ray. | ||
// | ||
// Rule-based proxy clients are very useful, they are able to dispatch requests to different servers based on powerful rule filters. | ||
// By using this setup, you are able to make all your TCP/UDP traffic under control with your favorite rule-based proxy client. | ||
// | ||
// Here's an example setup on macOS: | ||
// | ||
// tun2socks -tunGw 10.255.0.1 -fakeDns -proxyType d -proxyServer 127.0.0.1:1086 -exceptionSendThrough 192.168.1.189:0 -exceptionApps "v2ray" | ||
// | ||
// route delete default | ||
// route add default 10.255.0.1 | ||
// route add default 192.168.1.1 -ifscope en0 | ||
// | ||
// Where 192.168.1.189 is the default interface address, in my case, it's the WiFi interface and it's en0. | ||
// 192.168.1.1 is the default gateway. | ||
// It's very important to have two default routes, and the default route to TUN should has the highest priority. | ||
// | ||
// Start v2ray (or any other chainable proxy clients) and has SOCKS inbound listen on 127.0.0.1:1086. | ||
// | ||
// Optinally with all outbounds have sendThrough set to 192.168.1.189, if applicable. | ||
// https://v2ray.com/chapter_02/01_overview.html#outboundobject | ||
|
||
type tcpHandler struct { | ||
proxyHandler core.TCPConnHandler | ||
exceptionApps []string | ||
sendThrough net.Addr | ||
} | ||
|
||
func NewTCPHandler(proxyHandler core.TCPConnHandler, exceptionApps []string, sendThrough net.Addr) core.TCPConnHandler { | ||
return &tcpHandler{ | ||
proxyHandler, | ||
exceptionApps, | ||
sendThrough, | ||
} | ||
} | ||
|
||
func (h *tcpHandler) isExceptionApp(name string) bool { | ||
for _, app := range h.exceptionApps { | ||
if name == app { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (h *tcpHandler) relay(lhs, rhs net.Conn) { | ||
cls := func() { | ||
rhs.Close() | ||
lhs.Close() | ||
} | ||
|
||
go func() { | ||
io.Copy(rhs, lhs) | ||
cls() | ||
}() | ||
|
||
io.Copy(lhs, rhs) | ||
cls() | ||
} | ||
|
||
func (h *tcpHandler) Handle(conn net.Conn, target net.Addr) error { | ||
localHost, localPortStr, _ := net.SplitHostPort(conn.LocalAddr().String()) | ||
localPortInt, _ := strconv.Atoi(localPortStr) | ||
cmd, err := lsof.GetCommandNameBySocket("tcp", localHost, uint16(localPortInt)) | ||
if err != nil { | ||
cmd = "unknown process" | ||
} | ||
|
||
if h.isExceptionApp(cmd) { | ||
dialer := net.Dialer{LocalAddr: h.sendThrough} | ||
rc, err := dialer.Dial("tcp", target.String()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
go h.relay(conn, rc) | ||
|
||
log.Access("direct", target.Network(), conn.LocalAddr().String(), target.String()) | ||
|
||
return nil | ||
} else { | ||
return h.proxyHandler.Handle(conn, target) | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package d | ||
|
||
import ( | ||
"net" | ||
"strconv" | ||
"sync" | ||
"time" | ||
|
||
"github.com/eycorsican/go-tun2socks/common/log" | ||
"github.com/eycorsican/go-tun2socks/common/lsof" | ||
"github.com/eycorsican/go-tun2socks/core" | ||
) | ||
|
||
type udpHandler struct { | ||
sync.Mutex | ||
|
||
proxyHandler core.UDPConnHandler | ||
exceptionApps []string | ||
sendThrough net.Addr | ||
exceptionConns map[core.UDPConn]*net.UDPConn | ||
timeout time.Duration | ||
} | ||
|
||
func (h *udpHandler) isExceptionApp(name string) bool { | ||
for _, app := range h.exceptionApps { | ||
if name == app { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func NewUDPHandler(proxyHandler core.UDPConnHandler, exceptionApps []string, sendThrough net.Addr, timeout time.Duration) core.UDPConnHandler { | ||
return &udpHandler{ | ||
proxyHandler: proxyHandler, | ||
exceptionApps: exceptionApps, | ||
sendThrough: sendThrough, | ||
exceptionConns: make(map[core.UDPConn]*net.UDPConn), | ||
timeout: timeout, | ||
} | ||
} | ||
|
||
func (h *udpHandler) handleInput(conn core.UDPConn, pc net.PacketConn) { | ||
buf := core.NewBytes(core.BufSize) | ||
|
||
defer func() { | ||
h.Close(conn) | ||
core.FreeBytes(buf) | ||
}() | ||
|
||
for { | ||
pc.SetDeadline(time.Now().Add(h.timeout)) | ||
n, addr, err := pc.ReadFrom(buf) | ||
if err != nil { | ||
return | ||
} | ||
|
||
_, err = conn.WriteFrom(buf[:n], addr) | ||
if err != nil { | ||
return | ||
} | ||
} | ||
} | ||
|
||
func (h *udpHandler) Connect(conn core.UDPConn, target net.Addr) error { | ||
localHost, localPortStr, _ := net.SplitHostPort(conn.LocalAddr().String()) | ||
localPortInt, _ := strconv.Atoi(localPortStr) | ||
cmd, err := lsof.GetCommandNameBySocket("udp", localHost, uint16(localPortInt)) | ||
if err != nil { | ||
cmd = "unknown process" | ||
} | ||
|
||
if h.isExceptionApp(cmd) { | ||
bindAddr, _ := net.ResolveUDPAddr( | ||
"udp", | ||
h.sendThrough.String(), | ||
) | ||
pc, err := net.ListenUDP("udp", bindAddr) | ||
if err != nil { | ||
return err | ||
} | ||
h.Lock() | ||
h.exceptionConns[conn] = pc | ||
h.Unlock() | ||
|
||
go h.handleInput(conn, pc) | ||
|
||
log.Access("direct", target.Network(), conn.LocalAddr().String(), target.String()) | ||
|
||
return nil | ||
} else { | ||
return h.proxyHandler.Connect(conn, target) | ||
} | ||
} | ||
|
||
func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr net.Addr) error { | ||
h.Lock() | ||
defer h.Unlock() | ||
|
||
if pc, found := h.exceptionConns[conn]; found { | ||
_, err := pc.WriteTo(data, addr) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} else { | ||
return h.proxyHandler.ReceiveTo(conn, data, addr) | ||
} | ||
} | ||
|
||
func (h *udpHandler) Close(conn core.UDPConn) { | ||
conn.Close() | ||
|
||
h.Lock() | ||
defer h.Unlock() | ||
|
||
if pc, ok := h.exceptionConns[conn]; ok { | ||
pc.Close() | ||
delete(h.exceptionConns, conn) | ||
} | ||
} |
This file contains 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
This file contains 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
@eycorsican
Thank you so much for all the improvements you've been making!
While I don't have the bandwidth to contribute, I've worked with this things and my team is using your code for Outline and Intra.
The code for chaining proxies can be simplified and made more composable and testable if instead of working with Handlers you work with Dialers.
Take, for example, socks.TCPHandler:
go-tun2socks/proxy/socks/tcp.go
Line 50 in 3c7dcb5
It could, instead, be a generic dialer handler that takes a dialer in the construction and all it does is to use the Dialer to dial to the target and plug it to the lwip connection.
Then if you want a SOCKS handler, all you need to do is create a SOCKS Dialer and pass that to the generic TCPHandler that takes a dialer.
You can them make the SOCKS Dialer itself take a dialer that is used to connect to the proxy, and that will give you chaining of proxies.
Another example of simplification: you can have a Shadowsocks Dialer, rather than a Shadowsocks handler.
Testing dialers are a lot easier than testing handlers. Conversely, testing a handler with a fake dialer is also easier.
By having developers only need to implement handlers, you will remove all the boilerplate in the handlers found in the proxy/ directory. They currently all need to reproduce the logic to plug the target connection to the lwip connection.
@alalamav @bemasc FYI