github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/outbound/trojan.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"encoding/json"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"strconv"
    11  
    12  	C "github.com/xxf098/lite-proxy/constant"
    13  	"github.com/xxf098/lite-proxy/stats"
    14  	"github.com/xxf098/lite-proxy/transport/dialer"
    15  	"github.com/xxf098/lite-proxy/transport/gun"
    16  	"github.com/xxf098/lite-proxy/transport/trojan"
    17  	"golang.org/x/net/http2"
    18  )
    19  
    20  type Trojan struct {
    21  	*Base
    22  	instance *trojan.Trojan
    23  	option   *TrojanOption
    24  
    25  	// for gun mux
    26  	gunTLSConfig *tls.Config
    27  	gunConfig    *gun.Config
    28  	transport    *http2.Transport
    29  }
    30  
    31  type TrojanOption struct {
    32  	Name           string      `proxy:"name,omitempty"`
    33  	Server         string      `proxy:"server"`
    34  	Port           int         `proxy:"port"`
    35  	Password       string      `proxy:"password"`
    36  	ALPN           []string    `proxy:"alpn,omitempty"`
    37  	SNI            string      `proxy:"sni,omitempty"`
    38  	SkipCertVerify bool        `proxy:"skip-cert-verify,omitempty"`
    39  	UDP            bool        `proxy:"udp,omitempty"`
    40  	Remarks        string      `proxy:"remarks,omitempty"`
    41  	Network        string      `proxy:"network,omitempty"`
    42  	GrpcOpts       GrpcOptions `proxy:"grpc-opts,omitempty"`
    43  	WSOpts         WSOptions   `proxy:"ws-opts,omitempty"`
    44  }
    45  
    46  func (t *Trojan) plainStream(c net.Conn) (net.Conn, error) {
    47  	if t.option.Network == "ws" {
    48  		host, port, _ := net.SplitHostPort(t.addr)
    49  		wsOpts := &trojan.WebsocketOption{
    50  			Host: host,
    51  			Port: port,
    52  			Path: t.option.WSOpts.Path,
    53  		}
    54  
    55  		if t.option.SNI != "" {
    56  			wsOpts.Host = t.option.SNI
    57  		}
    58  
    59  		if len(t.option.WSOpts.Headers) != 0 {
    60  			header := http.Header{}
    61  			for key, value := range t.option.WSOpts.Headers {
    62  				header.Add(key, value)
    63  			}
    64  			wsOpts.Headers = header
    65  		}
    66  
    67  		return t.instance.StreamWebsocketConn(c, wsOpts)
    68  	}
    69  
    70  	return t.instance.StreamConn(c)
    71  }
    72  
    73  func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    74  	c, err := t.plainStream(c)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
    77  	}
    78  
    79  	err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata))
    80  	return c, err
    81  }
    82  
    83  func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) {
    84  	// gun transport
    85  	if t.transport != nil {
    86  		c, err := gun.StreamGunWithTransport(t.transport, t.gunConfig)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  
    91  		if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil {
    92  			c.Close()
    93  			return nil, err
    94  		}
    95  		sc := stats.NewStatsConn(c)
    96  		return t.StreamConn(sc, metadata)
    97  	}
    98  
    99  	c, err := dialer.DialContext(ctx, "tcp", t.addr)
   100  	if err != nil {
   101  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   102  	}
   103  	tcpKeepAlive(c)
   104  	sc := stats.NewStatsConn(c)
   105  	return t.StreamConn(sc, metadata)
   106  }
   107  
   108  // TODO: grpc transport
   109  func (t *Trojan) DialUDP(metadata *C.Metadata) (_ net.PacketConn, err error) {
   110  	var c net.Conn
   111  
   112  	// grpc transport
   113  	if t.transport != nil {
   114  		c, err = gun.StreamGunWithTransport(t.transport, t.gunConfig)
   115  		if err != nil {
   116  			return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   117  		}
   118  	} else {
   119  		ctx, cancel := context.WithTimeout(context.Background(), tcpTimeout)
   120  		defer cancel()
   121  		c, err := dialer.DialContext(ctx, "tcp", t.addr)
   122  		if err != nil {
   123  			return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   124  		}
   125  		tcpKeepAlive(c)
   126  		c, err = t.instance.StreamConn(c)
   127  		if err != nil {
   128  			return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   129  		}
   130  	}
   131  
   132  	err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	pc := t.instance.PacketConn(c)
   138  	return pc, err
   139  }
   140  
   141  func (t *Trojan) MarshalJSON() ([]byte, error) {
   142  	return json.Marshal(map[string]string{
   143  		"type": "Trojan",
   144  	})
   145  }
   146  
   147  func NewTrojan(option *TrojanOption) (*Trojan, error) {
   148  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   149  
   150  	tOption := &trojan.Option{
   151  		Password:           option.Password,
   152  		ALPN:               option.ALPN,
   153  		ServerName:         option.Server,
   154  		SkipCertVerify:     option.SkipCertVerify,
   155  		ClientSessionCache: getClientSessionCache(),
   156  	}
   157  
   158  	if option.SNI != "" {
   159  		tOption.ServerName = option.SNI
   160  	}
   161  
   162  	t := &Trojan{
   163  		Base: &Base{
   164  			name: option.Name,
   165  			addr: addr,
   166  			udp:  option.UDP,
   167  		},
   168  		instance: trojan.New(tOption),
   169  		option:   option,
   170  	}
   171  
   172  	// if option.Network == "grpc" {
   173  	// 	dialFn := func(network, addr string) (net.Conn, error) {
   174  	// 		c, err := dialer.DialContext(context.Background(), "tcp", t.addr)
   175  	// 		if err != nil {
   176  	// 			return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error())
   177  	// 		}
   178  	// 		tcpKeepAlive(c)
   179  	// 		return c, nil
   180  	// 	}
   181  
   182  	// 	tlsConfig := &tls.Config{
   183  	// 		NextProtos:         option.ALPN,
   184  	// 		MinVersion:         tls.VersionTLS12,
   185  	// 		InsecureSkipVerify: tOption.SkipCertVerify,
   186  	// 		ServerName:         tOption.ServerName,
   187  	// 	}
   188  	// 	t.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
   189  	// 	t.gunTLSConfig = tlsConfig
   190  	// 	t.gunConfig = &gun.Config{
   191  	// 		ServiceName: option.GrpcOpts.GrpcServiceName,
   192  	// 		Host:        tOption.ServerName,
   193  	// 	}
   194  	// }
   195  
   196  	return t, nil
   197  }