github.com/metacubex/mihomo@v1.18.5/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  	N "github.com/metacubex/mihomo/common/net"
    12  	"github.com/metacubex/mihomo/component/ca"
    13  	"github.com/metacubex/mihomo/component/dialer"
    14  	"github.com/metacubex/mihomo/component/proxydialer"
    15  	tlsC "github.com/metacubex/mihomo/component/tls"
    16  	C "github.com/metacubex/mihomo/constant"
    17  	"github.com/metacubex/mihomo/transport/gun"
    18  	"github.com/metacubex/mihomo/transport/trojan"
    19  )
    20  
    21  type Trojan struct {
    22  	*Base
    23  	instance *trojan.Trojan
    24  	option   *TrojanOption
    25  
    26  	// for gun mux
    27  	gunTLSConfig *tls.Config
    28  	gunConfig    *gun.Config
    29  	transport    *gun.TransportWrap
    30  
    31  	realityConfig *tlsC.RealityConfig
    32  }
    33  
    34  type TrojanOption struct {
    35  	BasicOption
    36  	Name              string         `proxy:"name"`
    37  	Server            string         `proxy:"server"`
    38  	Port              int            `proxy:"port"`
    39  	Password          string         `proxy:"password"`
    40  	ALPN              []string       `proxy:"alpn,omitempty"`
    41  	SNI               string         `proxy:"sni,omitempty"`
    42  	SkipCertVerify    bool           `proxy:"skip-cert-verify,omitempty"`
    43  	Fingerprint       string         `proxy:"fingerprint,omitempty"`
    44  	UDP               bool           `proxy:"udp,omitempty"`
    45  	Network           string         `proxy:"network,omitempty"`
    46  	RealityOpts       RealityOptions `proxy:"reality-opts,omitempty"`
    47  	GrpcOpts          GrpcOptions    `proxy:"grpc-opts,omitempty"`
    48  	WSOpts            WSOptions      `proxy:"ws-opts,omitempty"`
    49  	ClientFingerprint string         `proxy:"client-fingerprint,omitempty"`
    50  }
    51  
    52  func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) {
    53  	if t.option.Network == "ws" {
    54  		host, port, _ := net.SplitHostPort(t.addr)
    55  		wsOpts := &trojan.WebsocketOption{
    56  			Host:                     host,
    57  			Port:                     port,
    58  			Path:                     t.option.WSOpts.Path,
    59  			V2rayHttpUpgrade:         t.option.WSOpts.V2rayHttpUpgrade,
    60  			V2rayHttpUpgradeFastOpen: t.option.WSOpts.V2rayHttpUpgradeFastOpen,
    61  			Headers:                  http.Header{},
    62  		}
    63  
    64  		if t.option.SNI != "" {
    65  			wsOpts.Host = t.option.SNI
    66  		}
    67  
    68  		if len(t.option.WSOpts.Headers) != 0 {
    69  			for key, value := range t.option.WSOpts.Headers {
    70  				wsOpts.Headers.Add(key, value)
    71  			}
    72  		}
    73  
    74  		return t.instance.StreamWebsocketConn(ctx, c, wsOpts)
    75  	}
    76  
    77  	return t.instance.StreamConn(ctx, c)
    78  }
    79  
    80  // StreamConnContext implements C.ProxyAdapter
    81  func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    82  	var err error
    83  
    84  	if tlsC.HaveGlobalFingerprint() && len(t.option.ClientFingerprint) == 0 {
    85  		t.option.ClientFingerprint = tlsC.GetGlobalFingerprint()
    86  	}
    87  
    88  	if t.transport != nil {
    89  		c, err = gun.StreamGunWithConn(c, t.gunTLSConfig, t.gunConfig, t.realityConfig)
    90  	} else {
    91  		c, err = t.plainStream(ctx, c)
    92  	}
    93  
    94  	if err != nil {
    95  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
    96  	}
    97  
    98  	if metadata.NetWork == C.UDP {
    99  		err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
   100  		return c, err
   101  	}
   102  	err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata))
   103  	return c, err
   104  }
   105  
   106  // DialContext implements C.ProxyAdapter
   107  func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
   108  	// gun transport
   109  	if t.transport != nil && len(opts) == 0 {
   110  		c, err := gun.StreamGunWithTransport(t.transport, t.gunConfig)
   111  		if err != nil {
   112  			return nil, err
   113  		}
   114  
   115  		if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil {
   116  			c.Close()
   117  			return nil, err
   118  		}
   119  
   120  		return NewConn(c, t), nil
   121  	}
   122  	return t.DialContextWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata)
   123  }
   124  
   125  // DialContextWithDialer implements C.ProxyAdapter
   126  func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
   127  	if len(t.option.DialerProxy) > 0 {
   128  		dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer)
   129  		if err != nil {
   130  			return nil, err
   131  		}
   132  	}
   133  	c, err := dialer.DialContext(ctx, "tcp", t.addr)
   134  	if err != nil {
   135  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   136  	}
   137  	N.TCPKeepAlive(c)
   138  
   139  	defer func(c net.Conn) {
   140  		safeConnClose(c, err)
   141  	}(c)
   142  
   143  	c, err = t.StreamConnContext(ctx, c, metadata)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	return NewConn(c, t), err
   149  }
   150  
   151  // ListenPacketContext implements C.ProxyAdapter
   152  func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
   153  	var c net.Conn
   154  
   155  	// grpc transport
   156  	if t.transport != nil && len(opts) == 0 {
   157  		c, err = gun.StreamGunWithTransport(t.transport, t.gunConfig)
   158  		if err != nil {
   159  			return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   160  		}
   161  		defer func(c net.Conn) {
   162  			safeConnClose(c, err)
   163  		}(c)
   164  		err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  
   169  		pc := t.instance.PacketConn(c)
   170  		return newPacketConn(pc, t), err
   171  	}
   172  	return t.ListenPacketWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata)
   173  }
   174  
   175  // ListenPacketWithDialer implements C.ProxyAdapter
   176  func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
   177  	if len(t.option.DialerProxy) > 0 {
   178  		dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer)
   179  		if err != nil {
   180  			return nil, err
   181  		}
   182  	}
   183  	c, err := dialer.DialContext(ctx, "tcp", t.addr)
   184  	if err != nil {
   185  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   186  	}
   187  	defer func(c net.Conn) {
   188  		safeConnClose(c, err)
   189  	}(c)
   190  	N.TCPKeepAlive(c)
   191  	c, err = t.plainStream(ctx, c)
   192  	if err != nil {
   193  		return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
   194  	}
   195  
   196  	err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata))
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	pc := t.instance.PacketConn(c)
   202  	return newPacketConn(pc, t), err
   203  }
   204  
   205  // SupportWithDialer implements C.ProxyAdapter
   206  func (t *Trojan) SupportWithDialer() C.NetWork {
   207  	return C.ALLNet
   208  }
   209  
   210  // ListenPacketOnStreamConn implements C.ProxyAdapter
   211  func (t *Trojan) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
   212  	pc := t.instance.PacketConn(c)
   213  	return newPacketConn(pc, t), err
   214  }
   215  
   216  // SupportUOT implements C.ProxyAdapter
   217  func (t *Trojan) SupportUOT() bool {
   218  	return true
   219  }
   220  
   221  func NewTrojan(option TrojanOption) (*Trojan, error) {
   222  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   223  
   224  	tOption := &trojan.Option{
   225  		Password:          option.Password,
   226  		ALPN:              option.ALPN,
   227  		ServerName:        option.Server,
   228  		SkipCertVerify:    option.SkipCertVerify,
   229  		Fingerprint:       option.Fingerprint,
   230  		ClientFingerprint: option.ClientFingerprint,
   231  	}
   232  
   233  	if option.SNI != "" {
   234  		tOption.ServerName = option.SNI
   235  	}
   236  
   237  	t := &Trojan{
   238  		Base: &Base{
   239  			name:   option.Name,
   240  			addr:   addr,
   241  			tp:     C.Trojan,
   242  			udp:    option.UDP,
   243  			tfo:    option.TFO,
   244  			mpTcp:  option.MPTCP,
   245  			iface:  option.Interface,
   246  			rmark:  option.RoutingMark,
   247  			prefer: C.NewDNSPrefer(option.IPVersion),
   248  		},
   249  		instance: trojan.New(tOption),
   250  		option:   &option,
   251  	}
   252  
   253  	var err error
   254  	t.realityConfig, err = option.RealityOpts.Parse()
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  	tOption.Reality = t.realityConfig
   259  
   260  	if option.Network == "grpc" {
   261  		dialFn := func(network, addr string) (net.Conn, error) {
   262  			var err error
   263  			var cDialer C.Dialer = dialer.NewDialer(t.Base.DialOptions()...)
   264  			if len(t.option.DialerProxy) > 0 {
   265  				cDialer, err = proxydialer.NewByName(t.option.DialerProxy, cDialer)
   266  				if err != nil {
   267  					return nil, err
   268  				}
   269  			}
   270  			c, err := cDialer.DialContext(context.Background(), "tcp", t.addr)
   271  			if err != nil {
   272  				return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error())
   273  			}
   274  			N.TCPKeepAlive(c)
   275  			return c, nil
   276  		}
   277  
   278  		tlsConfig := &tls.Config{
   279  			NextProtos:         option.ALPN,
   280  			MinVersion:         tls.VersionTLS12,
   281  			InsecureSkipVerify: tOption.SkipCertVerify,
   282  			ServerName:         tOption.ServerName,
   283  		}
   284  
   285  		var err error
   286  		tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
   287  		if err != nil {
   288  			return nil, err
   289  		}
   290  
   291  		t.transport = gun.NewHTTP2Client(dialFn, tlsConfig, tOption.ClientFingerprint, t.realityConfig)
   292  
   293  		t.gunTLSConfig = tlsConfig
   294  		t.gunConfig = &gun.Config{
   295  			ServiceName: option.GrpcOpts.GrpcServiceName,
   296  			Host:        tOption.ServerName,
   297  		}
   298  	}
   299  
   300  	return t, nil
   301  }