package main import ( "crypto/tls" "crypto/x509" "io/ioutil" "net" "net/http" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/congestion" "github.com/sirupsen/logrus" "github.com/tobyxdd/hysteria/pkg/acl" hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion" "github.com/tobyxdd/hysteria/pkg/core" hyHTTP "github.com/tobyxdd/hysteria/pkg/http" "github.com/tobyxdd/hysteria/pkg/obfs" "github.com/tobyxdd/hysteria/pkg/socks5" ) func proxyClient(args []string) { var config proxyClientConfig err := loadConfig(&config, args) if err != nil { logrus.WithField("error", err).Fatal("Unable to load configuration") } if err := config.Check(); err != nil { logrus.WithField("error", err).Fatal("Configuration error") } logrus.WithField("config", config.String()).Info("Configuration loaded") tlsConfig := &tls.Config{ InsecureSkipVerify: config.Insecure, NextProtos: []string{proxyTLSProtocol}, MinVersion: tls.VersionTLS13, } // Load CA if len(config.CustomCAFile) > 0 { bs, err := ioutil.ReadFile(config.CustomCAFile) if err != nil { logrus.WithFields(logrus.Fields{ "error": err, "file": config.CustomCAFile, }).Fatal("Unable to load CA file") } cp := x509.NewCertPool() if !cp.AppendCertsFromPEM(bs) { logrus.WithFields(logrus.Fields{ "file": config.CustomCAFile, }).Fatal("Unable to parse CA file") } tlsConfig.RootCAs = cp } quicConfig := &quic.Config{ MaxReceiveStreamFlowControlWindow: config.ReceiveWindowConn, MaxReceiveConnectionFlowControlWindow: config.ReceiveWindow, KeepAlive: true, } if quicConfig.MaxReceiveStreamFlowControlWindow == 0 { quicConfig.MaxReceiveStreamFlowControlWindow = DefaultMaxReceiveStreamFlowControlWindow } if quicConfig.MaxReceiveConnectionFlowControlWindow == 0 { quicConfig.MaxReceiveConnectionFlowControlWindow = DefaultMaxReceiveConnectionFlowControlWindow } var obfuscator core.Obfuscator if len(config.Obfs) > 0 { obfuscator = obfs.XORObfuscator(config.Obfs) } var aclEngine *acl.Engine if len(config.ACLFile) > 0 { aclEngine, err = acl.LoadFromFile(config.ACLFile) if err != nil { logrus.WithFields(logrus.Fields{ "error": err, "file": config.ACLFile, }).Fatal("Unable to parse ACL") } } client, err := core.NewClient(config.ServerAddr, config.Username, config.Password, tlsConfig, quicConfig, uint64(config.UpMbps)*mbpsToBps, uint64(config.DownMbps)*mbpsToBps, func(refBPS uint64) congestion.ExternalSendAlgorithm { return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS)) }, obfuscator) if err != nil { logrus.WithField("error", err).Fatal("Client initialization failed") } defer client.Close() logrus.WithField("addr", config.ServerAddr).Info("Connected") errChan := make(chan error) if len(config.SOCKS5Addr) > 0 { go func() { var authFunc func(user, password string) bool if config.SOCKS5User != "" && config.SOCKS5Password != "" { authFunc = func(user, password string) bool { return config.SOCKS5User == user && config.SOCKS5Password == password } } socks5server, err := socks5.NewServer(client, config.SOCKS5Addr, authFunc, config.SOCKS5Timeout, aclEngine, config.SOCKS5DisableUDP, func(addr net.Addr, reqAddr string, action acl.Action, arg string) { logrus.WithFields(logrus.Fields{ "action": actionToString(action, arg), "src": addr.String(), "dst": reqAddr, }).Debug("New SOCKS5 TCP request") }, func(addr net.Addr, reqAddr string, err error) { logrus.WithFields(logrus.Fields{ "error": err, "src": addr.String(), "dst": reqAddr, }).Debug("SOCKS5 TCP request closed") }, func(addr net.Addr) { logrus.WithFields(logrus.Fields{ "src": addr.String(), }).Debug("New SOCKS5 UDP associate request") }, func(addr net.Addr, err error) { logrus.WithFields(logrus.Fields{ "error": err, "src": addr.String(), }).Debug("SOCKS5 UDP associate request closed") }, func(addr net.Addr, reqAddr string, action acl.Action, arg string) { logrus.WithFields(logrus.Fields{ "action": actionToString(action, arg), "src": addr.String(), "dst": reqAddr, }).Debug("New SOCKS5 UDP tunnel") }, func(addr net.Addr, reqAddr string, err error) { logrus.WithFields(logrus.Fields{ "error": err, "src": addr.String(), "dst": reqAddr, }).Debug("SOCKS5 UDP tunnel closed") }) if err != nil { logrus.WithField("error", err).Fatal("SOCKS5 server initialization failed") } logrus.WithField("addr", config.SOCKS5Addr).Info("SOCKS5 server up and running") errChan <- socks5server.ListenAndServe() }() } if len(config.HTTPAddr) > 0 { go func() { var authFunc func(user, password string) bool if config.HTTPUser != "" && config.HTTPPassword != "" { authFunc = func(user, password string) bool { return config.HTTPUser == user && config.HTTPPassword == password } } proxy, err := hyHTTP.NewProxyHTTPServer(client, time.Duration(config.HTTPTimeout)*time.Second, aclEngine, func(reqAddr string, action acl.Action, arg string) { logrus.WithFields(logrus.Fields{ "action": actionToString(action, arg), "dst": reqAddr, }).Debug("New HTTP request") }, authFunc) if err != nil { logrus.WithField("error", err).Fatal("HTTP server initialization failed") } if config.HTTPSCert != "" && config.HTTPSKey != "" { logrus.WithField("addr", config.HTTPAddr).Info("HTTPS server up and running") errChan <- http.ListenAndServeTLS(config.HTTPAddr, config.HTTPSCert, config.HTTPSKey, proxy) } else { logrus.WithField("addr", config.HTTPAddr).Info("HTTP server up and running") errChan <- http.ListenAndServe(config.HTTPAddr, proxy) } }() } err = <-errChan logrus.WithField("error", err).Fatal("Client shutdown") } 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" } }