hysteria/cmd/proxy_client.go

200 lines
6.2 KiB
Go

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"
}
}