github.com/yaling888/clash@v1.53.0/adapter/outbound/trojan.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"strconv"
    10  
    11  	"golang.org/x/net/http2"
    12  
    13  	"github.com/yaling888/clash/common/convert"
    14  	"github.com/yaling888/clash/component/dialer"
    15  	C "github.com/yaling888/clash/constant"
    16  	"github.com/yaling888/clash/transport/crypto"
    17  	"github.com/yaling888/clash/transport/gun"
    18  	"github.com/yaling888/clash/transport/header"
    19  	"github.com/yaling888/clash/transport/quic"
    20  	"github.com/yaling888/clash/transport/trojan"
    21  )
    22  
    23  var _ C.ProxyAdapter = (*Trojan)(nil)
    24  
    25  type Trojan struct {
    26  	*Base
    27  	instance *trojan.Trojan
    28  	option   *TrojanOption
    29  
    30  	// for gun mux
    31  	gunTLSConfig *tls.Config
    32  	gunConfig    *gun.Config
    33  	transport    *http2.Transport
    34  
    35  	quicAEAD *crypto.AEAD
    36  }
    37  
    38  type TrojanOption struct {
    39  	BasicOption
    40  	Name             string            `proxy:"name"`
    41  	Server           string            `proxy:"server"`
    42  	Port             int               `proxy:"port"`
    43  	Password         string            `proxy:"password"`
    44  	ALPN             []string          `proxy:"alpn,omitempty"`
    45  	SNI              string            `proxy:"sni,omitempty"`
    46  	SkipCertVerify   bool              `proxy:"skip-cert-verify,omitempty"`
    47  	UDP              bool              `proxy:"udp,omitempty"`
    48  	Network          string            `proxy:"network,omitempty"`
    49  	GrpcOpts         GrpcOptions       `proxy:"grpc-opts,omitempty"`
    50  	WSOpts           WSOptions         `proxy:"ws-opts,omitempty"`
    51  	HTTP2Opts        HTTP2Options      `proxy:"h2-opts,omitempty"`
    52  	QUICOpts         QUICOptions       `proxy:"quic-opts,omitempty"`
    53  	AEADOpts         crypto.AEADOption `proxy:"aead-opts,omitempty"`
    54  	RemoteDnsResolve bool              `proxy:"remote-dns-resolve,omitempty"`
    55  }
    56  
    57  func (t *Trojan) plainStream(conn net.Conn) (net.Conn, error) {
    58  	switch t.option.Network {
    59  	case "ws":
    60  		host, port, _ := net.SplitHostPort(t.addr)
    61  		wsOpts := &trojan.WebsocketOption{
    62  			Host:    host,
    63  			Port:    port,
    64  			Path:    t.option.WSOpts.Path,
    65  			Headers: http.Header{},
    66  		}
    67  
    68  		if t.option.SNI != "" {
    69  			wsOpts.Host = t.option.SNI
    70  		}
    71  
    72  		if len(t.option.WSOpts.Headers) != 0 {
    73  			for key, value := range t.option.WSOpts.Headers {
    74  				wsOpts.Headers.Add(key, value)
    75  			}
    76  		}
    77  
    78  		if wsOpts.Headers.Get("User-Agent") == "" {
    79  			wsOpts.Headers.Set("User-Agent", convert.RandUserAgent())
    80  		}
    81  
    82  		return t.instance.StreamWebsocketConn(conn, wsOpts)
    83  	case "h2":
    84  		h2Opts := &trojan.HTTPOptions{
    85  			Hosts:   t.option.HTTP2Opts.Host,
    86  			Path:    t.option.HTTP2Opts.Path,
    87  			Headers: http.Header{},
    88  		}
    89  
    90  		if len(t.option.HTTP2Opts.Headers) != 0 {
    91  			for key, value := range t.option.HTTP2Opts.Headers {
    92  				h2Opts.Headers.Add(key, value)
    93  			}
    94  		}
    95  
    96  		if h2Opts.Headers.Get("User-Agent") == "" {
    97  			h2Opts.Headers.Set("User-Agent", convert.RandUserAgent())
    98  		}
    99  
   100  		return t.instance.StreamH2Conn(conn, h2Opts)
   101  	case "quic":
   102  		quicOpts := &quic.Config{
   103  			Host:           t.option.Server,
   104  			Port:           t.option.Port,
   105  			ALPN:           t.option.ALPN,
   106  			ServerName:     t.option.Server,
   107  			SkipCertVerify: t.option.SkipCertVerify,
   108  			Header:         t.option.QUICOpts.Header,
   109  			AEAD:           t.quicAEAD,
   110  		}
   111  
   112  		if t.option.SNI != "" {
   113  			quicOpts.ServerName = t.option.SNI
   114  		}
   115  
   116  		return quic.StreamQUICConn(conn, quicOpts)
   117  	}
   118  
   119  	return t.instance.StreamConn(conn)
   120  }
   121  
   122  func (t *Trojan) trojanStream(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
   123  	var err error
   124  	if t.transport != nil {
   125  		c, err = gun.StreamGunWithConn(c, t.gunTLSConfig, t.gunConfig)
   126  	} else {
   127  		c, err = t.plainStream(c)
   128  		if err != nil {
   129  			return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   130  		}
   131  		c, err = crypto.StreamAEADConnOrNot(c, t.option.AEADOpts)
   132  	}
   133  
   134  	if err != nil {
   135  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   136  	}
   137  
   138  	if metadata.NetWork == C.UDP {
   139  		err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
   140  		return c, err
   141  	}
   142  
   143  	err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata))
   144  	return c, err
   145  }
   146  
   147  // StreamConn implements C.ProxyAdapter
   148  func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
   149  	return t.trojanStream(c, metadata)
   150  }
   151  
   152  // StreamPacketConn implements C.ProxyAdapter
   153  func (t *Trojan) StreamPacketConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
   154  	var err error
   155  	c, err = t.trojanStream(c, metadata)
   156  	if err != nil {
   157  		return c, err
   158  	}
   159  
   160  	pc := t.instance.PacketConn(c)
   161  	return WrapConn(pc), nil
   162  }
   163  
   164  // DialContext implements C.ProxyAdapter
   165  func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
   166  	var c net.Conn
   167  
   168  	// gun transport
   169  	if t.transport != nil && len(opts) == 0 {
   170  		c, err = gun.StreamGunWithTransport(t.transport, t.gunConfig)
   171  		if err != nil {
   172  			return nil, err
   173  		}
   174  
   175  		defer func(cc net.Conn, e error) {
   176  			safeConnClose(cc, e)
   177  		}(c, err)
   178  
   179  		if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil {
   180  			return nil, err
   181  		}
   182  
   183  		return NewConn(c, t), nil
   184  	}
   185  
   186  	c, err = t.dialContext(ctx, opts...)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	tcpKeepAlive(c)
   191  
   192  	defer func(cc net.Conn, e error) {
   193  		safeConnClose(cc, e)
   194  	}(c, err)
   195  
   196  	c, err = t.StreamConn(c, metadata)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	return NewConn(c, t), err
   202  }
   203  
   204  // ListenPacketContext implements C.ProxyAdapter
   205  func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
   206  	var c net.Conn
   207  
   208  	// gun transport
   209  	if t.transport != nil && len(opts) == 0 {
   210  		c, err = gun.StreamGunWithTransport(t.transport, t.gunConfig)
   211  		if err != nil {
   212  			return nil, err
   213  		}
   214  
   215  		defer func(cc net.Conn, e error) {
   216  			safeConnClose(cc, e)
   217  		}(c, err)
   218  
   219  		if err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)); err != nil {
   220  			return nil, err
   221  		}
   222  
   223  		pc := t.instance.PacketConn(c)
   224  
   225  		return NewPacketConn(pc, t), nil
   226  	}
   227  
   228  	c, err = t.dialContext(ctx, opts...)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	tcpKeepAlive(c)
   234  
   235  	defer func(cc net.Conn, e error) {
   236  		safeConnClose(cc, e)
   237  	}(c, err)
   238  
   239  	c, err = t.StreamPacketConn(c, metadata)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	return NewPacketConn(c.(net.PacketConn), t), nil
   245  }
   246  
   247  func (t *Trojan) dialContext(ctx context.Context, opts ...dialer.Option) (net.Conn, error) {
   248  	switch t.option.Network {
   249  	case "quic":
   250  		c, err := dialer.ListenPacket(ctx, "udp", "", t.Base.DialOptions(opts...)...)
   251  		if err != nil {
   252  			return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   253  		}
   254  		return c.(*net.UDPConn), nil
   255  	}
   256  
   257  	c, err := dialer.DialContext(ctx, "tcp", t.addr, t.Base.DialOptions(opts...)...)
   258  	if err != nil {
   259  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   260  	}
   261  	return c, nil
   262  }
   263  
   264  func NewTrojan(option TrojanOption) (*Trojan, error) {
   265  	if _, err := crypto.VerifyAEADOption(option.AEADOpts, true); err != nil {
   266  		return nil, err
   267  	}
   268  
   269  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   270  
   271  	tOption := &trojan.Option{
   272  		Password:       option.Password,
   273  		ALPN:           option.ALPN,
   274  		ServerName:     option.Server,
   275  		SkipCertVerify: option.SkipCertVerify,
   276  	}
   277  
   278  	if option.SNI != "" {
   279  		tOption.ServerName = option.SNI
   280  	}
   281  
   282  	t := &Trojan{
   283  		Base: &Base{
   284  			name:  option.Name,
   285  			addr:  addr,
   286  			tp:    C.Trojan,
   287  			udp:   option.UDP,
   288  			iface: option.Interface,
   289  			rmark: option.RoutingMark,
   290  			dns:   option.RemoteDnsResolve,
   291  		},
   292  		instance: trojan.New(tOption),
   293  		option:   &option,
   294  	}
   295  
   296  	switch option.Network {
   297  	case "h2":
   298  		if len(option.HTTP2Opts.Host) == 0 {
   299  			option.HTTP2Opts.Host = append(option.HTTP2Opts.Host, tOption.ServerName)
   300  		}
   301  	case "grpc":
   302  		dialFn := func(network, addr string) (net.Conn, error) {
   303  			c, err := dialer.DialContext(context.Background(), "tcp", t.addr, t.Base.DialOptions()...)
   304  			if err != nil {
   305  				return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   306  			}
   307  			tcpKeepAlive(c)
   308  			return c, nil
   309  		}
   310  
   311  		tlsConfig := &tls.Config{
   312  			NextProtos:         option.ALPN,
   313  			MinVersion:         tls.VersionTLS12,
   314  			InsecureSkipVerify: tOption.SkipCertVerify,
   315  			ServerName:         tOption.ServerName,
   316  		}
   317  
   318  		t.transport = gun.NewHTTP2Client(dialFn, tlsConfig)
   319  
   320  		t.gunTLSConfig = tlsConfig
   321  		t.gunConfig = &gun.Config{
   322  			ServiceName: option.GrpcOpts.GrpcServiceName,
   323  			Host:        tOption.ServerName,
   324  		}
   325  	case "quic":
   326  		quicAEAD, err := crypto.NewAEAD(t.option.QUICOpts.Security, t.option.QUICOpts.Key, "v2ray-quic-salt")
   327  		if err != nil {
   328  			return nil, fmt.Errorf("invalid quic-opts: %w", err)
   329  		}
   330  		t.quicAEAD = quicAEAD
   331  		_, err = header.New(t.option.QUICOpts.Header)
   332  		if err != nil {
   333  			return nil, fmt.Errorf("invalid quic-opts: %w", err)
   334  		}
   335  	}
   336  
   337  	return t, nil
   338  }