github.com/ipfans/trojan-go@v0.11.0/tunnel/freedom/client.go (about) 1 package freedom 2 3 import ( 4 "context" 5 "net" 6 7 "github.com/txthinking/socks5" 8 "golang.org/x/net/proxy" 9 10 "github.com/ipfans/trojan-go/common" 11 "github.com/ipfans/trojan-go/config" 12 "github.com/ipfans/trojan-go/tunnel" 13 ) 14 15 type Client struct { 16 preferIPv4 bool 17 noDelay bool 18 keepAlive bool 19 ctx context.Context 20 cancel context.CancelFunc 21 forwardProxy bool 22 proxyAddr *tunnel.Address 23 username string 24 password string 25 } 26 27 func (c *Client) DialConn(addr *tunnel.Address, _ tunnel.Tunnel) (tunnel.Conn, error) { 28 // forward proxy 29 if c.forwardProxy { 30 var auth *proxy.Auth 31 if c.username != "" { 32 auth = &proxy.Auth{ 33 User: c.username, 34 Password: c.password, 35 } 36 } 37 dialer, err := proxy.SOCKS5("tcp", c.proxyAddr.String(), auth, proxy.Direct) 38 if err != nil { 39 return nil, common.NewError("freedom failed to init socks dialer") 40 } 41 conn, err := dialer.Dial("tcp", addr.String()) 42 if err != nil { 43 return nil, common.NewError("freedom failed to dial target address via socks proxy " + addr.String()).Base(err) 44 } 45 return &Conn{ 46 Conn: conn, 47 }, nil 48 } 49 network := "tcp" 50 if c.preferIPv4 { 51 network = "tcp4" 52 } 53 dialer := new(net.Dialer) 54 tcpConn, err := dialer.DialContext(c.ctx, network, addr.String()) 55 if err != nil { 56 return nil, common.NewError("freedom failed to dial " + addr.String()).Base(err) 57 } 58 59 tcpConn.(*net.TCPConn).SetKeepAlive(c.keepAlive) 60 tcpConn.(*net.TCPConn).SetNoDelay(c.noDelay) 61 return &Conn{ 62 Conn: tcpConn, 63 }, nil 64 } 65 66 func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) { 67 if c.forwardProxy { 68 socksClient, err := socks5.NewClient(c.proxyAddr.String(), c.username, c.password, 0, 0) 69 common.Must(err) 70 if err := socksClient.Negotiate(&net.TCPAddr{}); err != nil { 71 return nil, common.NewError("freedom failed to negotiate socks").Base(err) 72 } 73 a, addr, port, err := socks5.ParseAddress("1.1.1.1:53") // useless address 74 common.Must(err) 75 resp, err := socksClient.Request(socks5.NewRequest(socks5.CmdUDP, a, addr, port)) 76 if err != nil { 77 return nil, common.NewError("freedom failed to dial udp to socks").Base(err) 78 } 79 // TODO fix hardcoded localhost 80 packetConn, err := net.ListenPacket("udp", "127.0.0.1:0") 81 if err != nil { 82 return nil, common.NewError("freedom failed to listen udp").Base(err) 83 } 84 socksAddr, err := net.ResolveUDPAddr("udp", resp.Address()) 85 if err != nil { 86 return nil, common.NewError("freedom recv invalid socks bind addr").Base(err) 87 } 88 return &SocksPacketConn{ 89 PacketConn: packetConn, 90 socksAddr: socksAddr, 91 socksClient: socksClient, 92 }, nil 93 } 94 network := "udp" 95 if c.preferIPv4 { 96 network = "udp4" 97 } 98 udpConn, err := net.ListenPacket(network, "") 99 if err != nil { 100 return nil, common.NewError("freedom failed to listen udp socket").Base(err) 101 } 102 return &PacketConn{ 103 UDPConn: udpConn.(*net.UDPConn), 104 }, nil 105 } 106 107 func (c *Client) Close() error { 108 c.cancel() 109 return nil 110 } 111 112 func NewClient(ctx context.Context, _ tunnel.Client) (*Client, error) { 113 cfg := config.FromContext(ctx, Name).(*Config) 114 addr := tunnel.NewAddressFromHostPort("tcp", cfg.ForwardProxy.ProxyHost, cfg.ForwardProxy.ProxyPort) 115 ctx, cancel := context.WithCancel(ctx) 116 return &Client{ 117 ctx: ctx, 118 cancel: cancel, 119 noDelay: cfg.TCP.NoDelay, 120 keepAlive: cfg.TCP.KeepAlive, 121 preferIPv4: cfg.TCP.PreferIPV4, 122 forwardProxy: cfg.ForwardProxy.Enabled, 123 proxyAddr: addr, 124 username: cfg.ForwardProxy.Username, 125 password: cfg.ForwardProxy.Password, 126 }, nil 127 }