github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/inbound/hysteria2.go (about)

     1  //go:build with_quic
     2  
     3  package inbound
     4  
     5  import (
     6  	"context"
     7  	"net"
     8  	"net/http"
     9  	"net/http/httputil"
    10  	"net/url"
    11  
    12  	"github.com/inazumav/sing-box/adapter"
    13  	"github.com/inazumav/sing-box/common/tls"
    14  	C "github.com/inazumav/sing-box/constant"
    15  	"github.com/inazumav/sing-box/log"
    16  	"github.com/inazumav/sing-box/option"
    17  	"github.com/inazumav/sing-box/transport/hysteria2"
    18  	"github.com/sagernet/sing/common"
    19  	"github.com/sagernet/sing/common/auth"
    20  	E "github.com/sagernet/sing/common/exceptions"
    21  	N "github.com/sagernet/sing/common/network"
    22  )
    23  
    24  var _ adapter.Inbound = (*Hysteria2)(nil)
    25  
    26  type Hysteria2 struct {
    27  	myInboundAdapter
    28  	tlsConfig tls.ServerConfig
    29  	server    *hysteria2.Server
    30  }
    31  
    32  func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (*Hysteria2, error) {
    33  	if options.TLS == nil || !options.TLS.Enabled {
    34  		return nil, C.ErrTLSRequired
    35  	}
    36  	tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	var salamanderPassword string
    41  	if options.Obfs != nil {
    42  		if options.Obfs.Password == "" {
    43  			return nil, E.New("missing obfs password")
    44  		}
    45  		switch options.Obfs.Type {
    46  		case hysteria2.ObfsTypeSalamander:
    47  			salamanderPassword = options.Obfs.Password
    48  		default:
    49  			return nil, E.New("unknown obfs type: ", options.Obfs.Type)
    50  		}
    51  	}
    52  	var masqueradeHandler http.Handler
    53  	if options.Masquerade != "" {
    54  		masqueradeURL, err := url.Parse(options.Masquerade)
    55  		if err != nil {
    56  			return nil, E.Cause(err, "parse masquerade URL")
    57  		}
    58  		switch masqueradeURL.Scheme {
    59  		case "file":
    60  			masqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path))
    61  		case "http", "https":
    62  			masqueradeHandler = &httputil.ReverseProxy{
    63  				Rewrite: func(r *httputil.ProxyRequest) {
    64  					r.SetURL(masqueradeURL)
    65  					r.Out.Host = r.In.Host
    66  				},
    67  				ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
    68  					w.WriteHeader(http.StatusBadGateway)
    69  				},
    70  			}
    71  		default:
    72  			return nil, E.New("unknown masquerade URL scheme: ", masqueradeURL.Scheme)
    73  		}
    74  	}
    75  	inbound := &Hysteria2{
    76  		myInboundAdapter: myInboundAdapter{
    77  			protocol:      C.TypeHysteria2,
    78  			network:       []string{N.NetworkUDP},
    79  			ctx:           ctx,
    80  			router:        router,
    81  			logger:        logger,
    82  			tag:           tag,
    83  			listenOptions: options.ListenOptions,
    84  		},
    85  		tlsConfig: tlsConfig,
    86  	}
    87  	server, err := hysteria2.NewServer(hysteria2.ServerOptions{
    88  		Context:            ctx,
    89  		Logger:             logger,
    90  		SendBPS:            uint64(options.UpMbps * 1024 * 1024),
    91  		ReceiveBPS:         uint64(options.DownMbps * 1024 * 1024),
    92  		SalamanderPassword: salamanderPassword,
    93  		TLSConfig:          tlsConfig,
    94  		Users: common.Map(options.Users, func(it option.Hysteria2User) hysteria2.User {
    95  			return hysteria2.User(it)
    96  		}),
    97  		IgnoreClientBandwidth: options.IgnoreClientBandwidth,
    98  		Handler:               adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, nil),
    99  		MasqueradeHandler:     masqueradeHandler,
   100  	})
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	inbound.server = server
   105  	return inbound, nil
   106  }
   107  
   108  func (h *Hysteria2) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
   109  	ctx = log.ContextWithNewID(ctx)
   110  	h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
   111  	metadata = h.createMetadata(conn, metadata)
   112  	metadata.User, _ = auth.UserFromContext[string](ctx)
   113  	return h.router.RouteConnection(ctx, conn, metadata)
   114  }
   115  
   116  func (h *Hysteria2) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
   117  	ctx = log.ContextWithNewID(ctx)
   118  	metadata = h.createPacketMetadata(conn, metadata)
   119  	metadata.User, _ = auth.UserFromContext[string](ctx)
   120  	h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
   121  	return h.router.RoutePacketConnection(ctx, conn, metadata)
   122  }
   123  
   124  func (h *Hysteria2) Start() error {
   125  	if h.tlsConfig != nil {
   126  		err := h.tlsConfig.Start()
   127  		if err != nil {
   128  			return err
   129  		}
   130  	}
   131  	packetConn, err := h.myInboundAdapter.ListenUDP()
   132  	if err != nil {
   133  		return err
   134  	}
   135  	return h.server.Start(packetConn)
   136  }
   137  
   138  func (h *Hysteria2) Close() error {
   139  	return common.Close(
   140  		&h.myInboundAdapter,
   141  		h.tlsConfig,
   142  		common.PtrOrNil(h.server),
   143  	)
   144  }