mirror of
https://github.com/wyx2685/V2bX.git
synced 2025-01-23 10:28:13 -05:00
d76c6a73eb
add conditional compilation support add multi core support
307 lines
9.5 KiB
Go
307 lines
9.5 KiB
Go
package hy
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/oschwald/geoip2-golang"
|
|
"github.com/quic-go/quic-go"
|
|
|
|
"github.com/apernet/hysteria/app/auth"
|
|
"github.com/apernet/hysteria/core/acl"
|
|
"github.com/apernet/hysteria/core/cs"
|
|
"github.com/apernet/hysteria/core/pktconns"
|
|
"github.com/apernet/hysteria/core/pmtud"
|
|
"github.com/apernet/hysteria/core/sockopt"
|
|
"github.com/apernet/hysteria/core/transport"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/yosuke-furukawa/json5/encoding/json5"
|
|
)
|
|
|
|
var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFactory{
|
|
"": pktconns.NewServerUDPConnFunc,
|
|
"udp": pktconns.NewServerUDPConnFunc,
|
|
"wechat": pktconns.NewServerWeChatConnFunc,
|
|
"wechat-video": pktconns.NewServerWeChatConnFunc,
|
|
"faketcp": pktconns.NewServerFakeTCPConnFunc,
|
|
}
|
|
|
|
func server(config *serverConfig) {
|
|
logrus.WithField("config", config.String()).Info("Server configuration loaded")
|
|
config.Fill() // Fill default values
|
|
// Resolver
|
|
if len(config.Resolver) > 0 {
|
|
err := setResolver(config.Resolver)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
}).Fatal("Failed to set resolver")
|
|
}
|
|
}
|
|
// Load TLS config
|
|
var tlsConfig *tls.Config
|
|
if len(config.ACME.Domains) > 0 {
|
|
// ACME mode
|
|
tc, err := acmeTLSConfig(config.ACME.Domains, config.ACME.Email,
|
|
config.ACME.DisableHTTPChallenge, config.ACME.DisableTLSALPNChallenge,
|
|
config.ACME.AltHTTPPort, config.ACME.AltTLSALPNPort)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
}).Fatal("Failed to get a certificate with ACME")
|
|
}
|
|
tc.NextProtos = []string{config.ALPN}
|
|
tc.MinVersion = tls.VersionTLS13
|
|
tlsConfig = tc
|
|
} else {
|
|
// Local cert mode
|
|
kpl, err := newKeypairLoader(config.CertFile, config.KeyFile)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"cert": config.CertFile,
|
|
"key": config.KeyFile,
|
|
}).Fatal("Failed to load the certificate")
|
|
}
|
|
tlsConfig = &tls.Config{
|
|
GetCertificate: kpl.GetCertificateFunc(),
|
|
NextProtos: []string{config.ALPN},
|
|
MinVersion: tls.VersionTLS13,
|
|
}
|
|
}
|
|
// QUIC config
|
|
quicConfig := &quic.Config{
|
|
InitialStreamReceiveWindow: config.ReceiveWindowConn,
|
|
MaxStreamReceiveWindow: config.ReceiveWindowConn,
|
|
InitialConnectionReceiveWindow: config.ReceiveWindowClient,
|
|
MaxConnectionReceiveWindow: config.ReceiveWindowClient,
|
|
MaxIncomingStreams: int64(config.MaxConnClient),
|
|
MaxIdleTimeout: ServerMaxIdleTimeoutSec * time.Second,
|
|
KeepAlivePeriod: 0, // Keep alive should solely be client's responsibility
|
|
DisablePathMTUDiscovery: config.DisableMTUDiscovery,
|
|
EnableDatagrams: true,
|
|
}
|
|
if !quicConfig.DisablePathMTUDiscovery && pmtud.DisablePathMTUDiscovery {
|
|
logrus.Info("Path MTU Discovery is not yet supported on this platform")
|
|
}
|
|
// Auth
|
|
var authFunc cs.ConnectFunc
|
|
var err error
|
|
switch authMode := config.Auth.Mode; authMode {
|
|
case "", "none":
|
|
if len(config.Obfs) == 0 {
|
|
logrus.Warn("Neither authentication nor obfuscation is turned on. " +
|
|
"Your server could be used by anyone! Are you sure this is what you want?")
|
|
}
|
|
authFunc = func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
|
|
return true, "Welcome"
|
|
}
|
|
case "password", "passwords":
|
|
authFunc, err = auth.PasswordAuthFunc(config.Auth.Config)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
}).Fatal("Failed to enable password authentication")
|
|
} else {
|
|
logrus.Info("Password authentication enabled")
|
|
}
|
|
case "external":
|
|
authFunc, err = auth.ExternalAuthFunc(config.Auth.Config)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
}).Fatal("Failed to enable external authentication")
|
|
} else {
|
|
logrus.Info("External authentication enabled")
|
|
}
|
|
default:
|
|
logrus.WithField("mode", config.Auth.Mode).Fatal("Unsupported authentication mode")
|
|
}
|
|
connectFunc := func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) {
|
|
ok, msg := authFunc(addr, auth, sSend, sRecv)
|
|
if !ok {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"msg": msg,
|
|
}).Info("Authentication failed, client rejected")
|
|
} else {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
}).Info("Client connected")
|
|
}
|
|
return ok, msg
|
|
}
|
|
// Resolve preference
|
|
if len(config.ResolvePreference) > 0 {
|
|
pref, err := transport.ResolvePreferenceFromString(config.ResolvePreference)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
}).Fatal("Failed to parse the resolve preference")
|
|
}
|
|
transport.DefaultServerTransport.ResolvePreference = pref
|
|
}
|
|
// SOCKS5 outbound
|
|
if config.SOCKS5Outbound.Server != "" {
|
|
transport.DefaultServerTransport.SOCKS5Client = transport.NewSOCKS5Client(config.SOCKS5Outbound.Server,
|
|
config.SOCKS5Outbound.User, config.SOCKS5Outbound.Password)
|
|
}
|
|
// Bind outbound
|
|
if config.BindOutbound.Device != "" {
|
|
iface, err := net.InterfaceByName(config.BindOutbound.Device)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
}).Fatal("Failed to find the interface")
|
|
}
|
|
transport.DefaultServerTransport.LocalUDPIntf = iface
|
|
sockopt.BindDialer(transport.DefaultServerTransport.Dialer, iface)
|
|
}
|
|
if config.BindOutbound.Address != "" {
|
|
ip := net.ParseIP(config.BindOutbound.Address)
|
|
if ip == nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
}).Fatal("Failed to parse the address")
|
|
}
|
|
transport.DefaultServerTransport.Dialer.LocalAddr = &net.TCPAddr{IP: ip}
|
|
transport.DefaultServerTransport.LocalUDPAddr = &net.UDPAddr{IP: ip}
|
|
}
|
|
// ACL
|
|
var aclEngine *acl.Engine
|
|
if len(config.ACL) > 0 {
|
|
aclEngine, err = acl.LoadFromFile(config.ACL, func(addr string) (*net.IPAddr, error) {
|
|
ipAddr, _, err := transport.DefaultServerTransport.ResolveIPAddr(addr)
|
|
return ipAddr, err
|
|
},
|
|
func() (*geoip2.Reader, error) {
|
|
return loadMMDBReader(config.MMDB)
|
|
})
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"file": config.ACL,
|
|
}).Fatal("Failed to parse ACL")
|
|
}
|
|
aclEngine.DefaultAction = acl.ActionDirect
|
|
}
|
|
// Prometheus
|
|
var trafficCounter cs.TrafficCounter
|
|
if len(config.PrometheusListen) > 0 {
|
|
promReg := prometheus.NewRegistry()
|
|
trafficCounter = NewPrometheusTrafficCounter(promReg)
|
|
go func() {
|
|
http.Handle("/metrics", promhttp.HandlerFor(promReg, promhttp.HandlerOpts{}))
|
|
err := http.ListenAndServe(config.PrometheusListen, nil)
|
|
logrus.WithField("error", err).Fatal("Prometheus HTTP server error")
|
|
}()
|
|
}
|
|
// Packet conn
|
|
pktConnFuncFactory := serverPacketConnFuncFactoryMap[config.Protocol]
|
|
if pktConnFuncFactory == nil {
|
|
logrus.WithField("protocol", config.Protocol).Fatal("Unsupported protocol")
|
|
}
|
|
pktConnFunc := pktConnFuncFactory(config.Obfs)
|
|
pktConn, err := pktConnFunc(config.Listen)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"addr": config.Listen,
|
|
}).Fatal("Failed to listen on the UDP address")
|
|
}
|
|
// Server
|
|
up, down, _ := config.Speed()
|
|
server, err := cs.NewServer(tlsConfig, quicConfig, pktConn,
|
|
transport.DefaultServerTransport, up, down, config.DisableUDP, aclEngine,
|
|
connectFunc, disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, trafficCounter)
|
|
if err != nil {
|
|
logrus.WithField("error", err).Fatal("Failed to initialize server")
|
|
}
|
|
defer server.Close()
|
|
logrus.WithField("addr", config.Listen).Info("Server up and running")
|
|
|
|
err = server.Serve()
|
|
logrus.WithField("error", err).Fatal("Server shutdown")
|
|
}
|
|
|
|
func disconnectFunc(addr net.Addr, auth []byte, err error) {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"error": err,
|
|
}).Info("Client disconnected")
|
|
}
|
|
|
|
func tcpRequestFunc(addr net.Addr, auth []byte, reqAddr string, action acl.Action, arg string) {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"dst": defaultIPMasker.Mask(reqAddr),
|
|
"action": actionToString(action, arg),
|
|
}).Debug("TCP request")
|
|
}
|
|
|
|
func tcpErrorFunc(addr net.Addr, auth []byte, reqAddr string, err error) {
|
|
if err != io.EOF {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"dst": defaultIPMasker.Mask(reqAddr),
|
|
"error": err,
|
|
}).Info("TCP error")
|
|
} else {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"dst": defaultIPMasker.Mask(reqAddr),
|
|
}).Debug("TCP EOF")
|
|
}
|
|
}
|
|
|
|
func udpRequestFunc(addr net.Addr, auth []byte, sessionID uint32) {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"session": sessionID,
|
|
}).Debug("UDP request")
|
|
}
|
|
|
|
func udpErrorFunc(addr net.Addr, auth []byte, sessionID uint32, err error) {
|
|
if err != io.EOF {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"session": sessionID,
|
|
"error": err,
|
|
}).Info("UDP error")
|
|
} else {
|
|
logrus.WithFields(logrus.Fields{
|
|
"src": defaultIPMasker.Mask(addr.String()),
|
|
"session": sessionID,
|
|
}).Debug("UDP EOF")
|
|
}
|
|
}
|
|
|
|
func actionToString(action acl.Action, arg string) string {
|
|
switch action {
|
|
case acl.ActionDirect:
|
|
return "Direct"
|
|
case acl.ActionProxy:
|
|
return "Proxy"
|
|
case acl.ActionBlock:
|
|
return "Block"
|
|
case acl.ActionHijack:
|
|
return "Hijack to " + arg
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
func parseServerConfig(cb []byte) (*serverConfig, error) {
|
|
var c serverConfig
|
|
err := json5.Unmarshal(cb, &c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &c, c.Check()
|
|
}
|