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  }