github.com/ipfans/trojan-go@v0.11.0/tunnel/transport/client.go (about) 1 package transport 2 3 import ( 4 "context" 5 "os" 6 "os/exec" 7 "strconv" 8 9 "github.com/ipfans/trojan-go/common" 10 "github.com/ipfans/trojan-go/config" 11 "github.com/ipfans/trojan-go/log" 12 "github.com/ipfans/trojan-go/tunnel" 13 "github.com/ipfans/trojan-go/tunnel/freedom" 14 ) 15 16 // Client implements tunnel.Client 17 type Client struct { 18 serverAddress *tunnel.Address 19 cmd *exec.Cmd 20 ctx context.Context 21 cancel context.CancelFunc 22 direct *freedom.Client 23 } 24 25 func (c *Client) Close() error { 26 c.cancel() 27 if c.cmd != nil && c.cmd.Process != nil { 28 c.cmd.Process.Kill() 29 } 30 return nil 31 } 32 33 func (c *Client) DialPacket(tunnel.Tunnel) (tunnel.PacketConn, error) { 34 panic("not supported") 35 } 36 37 // DialConn implements tunnel.Client. It will ignore the params and directly dial to the remote server 38 func (c *Client) DialConn(*tunnel.Address, tunnel.Tunnel) (tunnel.Conn, error) { 39 conn, err := c.direct.DialConn(c.serverAddress, nil) 40 if err != nil { 41 return nil, common.NewError("transport failed to connect to remote server").Base(err) 42 } 43 return &Conn{ 44 Conn: conn, 45 }, nil 46 } 47 48 // NewClient creates a transport layer client 49 func NewClient(ctx context.Context, _ tunnel.Client) (*Client, error) { 50 cfg := config.FromContext(ctx, Name).(*Config) 51 52 var cmd *exec.Cmd 53 serverAddress := tunnel.NewAddressFromHostPort("tcp", cfg.RemoteHost, cfg.RemotePort) 54 55 if cfg.TransportPlugin.Enabled { 56 log.Warn("trojan-go will use transport plugin and work in plain text mode") 57 switch cfg.TransportPlugin.Type { 58 case "shadowsocks": 59 pluginHost := "127.0.0.1" 60 pluginPort := common.PickPort("tcp", pluginHost) 61 cfg.TransportPlugin.Env = append( 62 cfg.TransportPlugin.Env, 63 "SS_LOCAL_HOST="+pluginHost, 64 "SS_LOCAL_PORT="+strconv.FormatInt(int64(pluginPort), 10), 65 "SS_REMOTE_HOST="+cfg.RemoteHost, 66 "SS_REMOTE_PORT="+strconv.FormatInt(int64(cfg.RemotePort), 10), 67 "SS_PLUGIN_OPTIONS="+cfg.TransportPlugin.Option, 68 ) 69 cfg.RemoteHost = pluginHost 70 cfg.RemotePort = pluginPort 71 serverAddress = tunnel.NewAddressFromHostPort("tcp", cfg.RemoteHost, cfg.RemotePort) 72 log.Debug("plugin address", serverAddress.String()) 73 log.Debug("plugin env", cfg.TransportPlugin.Env) 74 75 cmd = exec.Command(cfg.TransportPlugin.Command, cfg.TransportPlugin.Arg...) 76 cmd.Env = append(cmd.Env, cfg.TransportPlugin.Env...) 77 cmd.Stdout = os.Stdout 78 cmd.Stderr = os.Stdout 79 cmd.Start() 80 case "other": 81 cmd = exec.Command(cfg.TransportPlugin.Command, cfg.TransportPlugin.Arg...) 82 cmd.Env = append(cmd.Env, cfg.TransportPlugin.Env...) 83 cmd.Stdout = os.Stdout 84 cmd.Stderr = os.Stdout 85 cmd.Start() 86 case "plaintext": 87 // do nothing 88 default: 89 return nil, common.NewError("invalid plugin type: " + cfg.TransportPlugin.Type) 90 } 91 } 92 93 direct, err := freedom.NewClient(ctx, nil) 94 common.Must(err) 95 ctx, cancel := context.WithCancel(ctx) 96 client := &Client{ 97 serverAddress: serverAddress, 98 cmd: cmd, 99 ctx: ctx, 100 cancel: cancel, 101 direct: direct, 102 } 103 return client, nil 104 }