mirror of
https://github.com/apernet/hysteria.git
synced 2025-04-04 21:17:47 +03:00
298 lines
8.8 KiB
Go
298 lines
8.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/tls"
|
|
"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"
|
|
"github.com/tobyxdd/hysteria/pkg/obfs"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
func proxyServer(args []string) {
|
|
var config proxyServerConfig
|
|
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")
|
|
// Load cert
|
|
cert, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"cert": config.CertFile,
|
|
"key": config.KeyFile,
|
|
}).Fatal("Unable to load the certificate")
|
|
}
|
|
tlsConfig := &tls.Config{
|
|
Certificates: []tls.Certificate{cert},
|
|
NextProtos: []string{proxyTLSProtocol},
|
|
MinVersion: tls.VersionTLS13,
|
|
}
|
|
|
|
quicConfig := &quic.Config{
|
|
MaxReceiveStreamFlowControlWindow: config.ReceiveWindowConn,
|
|
MaxReceiveConnectionFlowControlWindow: config.ReceiveWindowClient,
|
|
MaxIncomingStreams: int64(config.MaxConnClient),
|
|
KeepAlive: true,
|
|
}
|
|
if quicConfig.MaxReceiveStreamFlowControlWindow == 0 {
|
|
quicConfig.MaxReceiveStreamFlowControlWindow = DefaultMaxReceiveStreamFlowControlWindow
|
|
}
|
|
if quicConfig.MaxReceiveConnectionFlowControlWindow == 0 {
|
|
quicConfig.MaxReceiveConnectionFlowControlWindow = DefaultMaxReceiveConnectionFlowControlWindow
|
|
}
|
|
if quicConfig.MaxIncomingStreams == 0 {
|
|
quicConfig.MaxIncomingStreams = DefaultMaxIncomingStreams
|
|
}
|
|
|
|
if len(config.AuthFile) == 0 {
|
|
logrus.Warn("No authentication configured, this server can be used by anyone")
|
|
}
|
|
|
|
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")
|
|
}
|
|
aclEngine.DefaultAction = acl.ActionDirect
|
|
}
|
|
|
|
server, err := core.NewServer(config.ListenAddr, tlsConfig, quicConfig,
|
|
uint64(config.UpMbps)*mbpsToBps, uint64(config.DownMbps)*mbpsToBps,
|
|
func(refBPS uint64) congestion.ExternalSendAlgorithm {
|
|
return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))
|
|
},
|
|
obfuscator,
|
|
func(addr net.Addr, username string, password string, sSend uint64, sRecv uint64) (core.AuthResult, string) {
|
|
if len(config.AuthFile) == 0 {
|
|
logrus.WithFields(logrus.Fields{
|
|
"addr": addr.String(),
|
|
"username": username,
|
|
"up": sSend / mbpsToBps,
|
|
"down": sRecv / mbpsToBps,
|
|
}).Info("Client connected")
|
|
return core.AuthResultSuccess, ""
|
|
} else {
|
|
// Need auth
|
|
ok, err := checkAuth(config.AuthFile, username, password)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err.Error(),
|
|
"addr": addr.String(),
|
|
"username": username,
|
|
}).Error("Client authentication error")
|
|
return core.AuthResultInternalError, "Server auth error"
|
|
}
|
|
if ok {
|
|
logrus.WithFields(logrus.Fields{
|
|
"addr": addr.String(),
|
|
"username": username,
|
|
"up": sSend / mbpsToBps,
|
|
"down": sRecv / mbpsToBps,
|
|
}).Info("Client authenticated")
|
|
return core.AuthResultSuccess, ""
|
|
} else {
|
|
logrus.WithFields(logrus.Fields{
|
|
"addr": addr.String(),
|
|
"username": username,
|
|
"up": sSend / mbpsToBps,
|
|
"down": sRecv / mbpsToBps,
|
|
}).Info("Client rejected due to invalid credential")
|
|
return core.AuthResultInvalidCred, "Invalid credential"
|
|
}
|
|
}
|
|
},
|
|
func(addr net.Addr, username string, err error) {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err.Error(),
|
|
"addr": addr.String(),
|
|
"username": username,
|
|
}).Info("Client disconnected")
|
|
},
|
|
func(addr net.Addr, username string, id int, reqType core.ConnectionType, reqAddr string) (core.ConnectResult, string, io.ReadWriteCloser) {
|
|
packet := reqType == core.ConnectionTypePacket
|
|
if packet && config.DisableUDP {
|
|
return core.ConnectResultBlocked, "UDP disabled", nil
|
|
}
|
|
host, port, err := net.SplitHostPort(reqAddr)
|
|
if err != nil {
|
|
return core.ConnectResultFailed, err.Error(), nil
|
|
}
|
|
ip := net.ParseIP(host)
|
|
if ip != nil {
|
|
// IP request, clear host for ACL engine
|
|
host = ""
|
|
}
|
|
action, arg := acl.ActionDirect, ""
|
|
if aclEngine != nil {
|
|
action, arg = aclEngine.Lookup(host, ip)
|
|
}
|
|
switch action {
|
|
case acl.ActionDirect, acl.ActionProxy: // Treat proxy as direct on server side
|
|
if !packet {
|
|
// TCP
|
|
logrus.WithFields(logrus.Fields{
|
|
"action": "direct",
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
}).Debug("New TCP request")
|
|
conn, err := net.DialTimeout("tcp", reqAddr, dialTimeout)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"dst": reqAddr,
|
|
}).Error("TCP error")
|
|
return core.ConnectResultFailed, err.Error(), nil
|
|
}
|
|
return core.ConnectResultSuccess, "", conn
|
|
} else {
|
|
// UDP
|
|
logrus.WithFields(logrus.Fields{
|
|
"action": "direct",
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
}).Debug("New UDP request")
|
|
conn, err := net.Dial("udp", reqAddr)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"dst": reqAddr,
|
|
}).Error("UDP error")
|
|
return core.ConnectResultFailed, err.Error(), nil
|
|
}
|
|
return core.ConnectResultSuccess, "", conn
|
|
}
|
|
case acl.ActionBlock:
|
|
if !packet {
|
|
// TCP
|
|
logrus.WithFields(logrus.Fields{
|
|
"action": "block",
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
}).Debug("New TCP request")
|
|
return core.ConnectResultBlocked, "blocked by ACL", nil
|
|
} else {
|
|
// UDP
|
|
logrus.WithFields(logrus.Fields{
|
|
"action": "block",
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
}).Debug("New UDP request")
|
|
return core.ConnectResultBlocked, "blocked by ACL", nil
|
|
}
|
|
case acl.ActionHijack:
|
|
hijackAddr := net.JoinHostPort(arg, port)
|
|
if !packet {
|
|
// TCP
|
|
logrus.WithFields(logrus.Fields{
|
|
"action": "hijack",
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
"rdst": arg,
|
|
}).Debug("New TCP request")
|
|
conn, err := net.DialTimeout("tcp", hijackAddr, dialTimeout)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"dst": hijackAddr,
|
|
}).Error("TCP error")
|
|
return core.ConnectResultFailed, err.Error(), nil
|
|
}
|
|
return core.ConnectResultSuccess, "", conn
|
|
} else {
|
|
// UDP
|
|
logrus.WithFields(logrus.Fields{
|
|
"action": "hijack",
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
"rdst": arg,
|
|
}).Debug("New UDP request")
|
|
conn, err := net.Dial("udp", hijackAddr)
|
|
if err != nil {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"dst": hijackAddr,
|
|
}).Error("UDP error")
|
|
return core.ConnectResultFailed, err.Error(), nil
|
|
}
|
|
return core.ConnectResultSuccess, "", conn
|
|
}
|
|
default:
|
|
return core.ConnectResultFailed, "server ACL error", nil
|
|
}
|
|
},
|
|
func(addr net.Addr, username string, id int, reqType core.ConnectionType, reqAddr string, err error) {
|
|
packet := reqType == core.ConnectionTypePacket
|
|
if !packet {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
}).Debug("TCP request closed")
|
|
} else {
|
|
logrus.WithFields(logrus.Fields{
|
|
"error": err,
|
|
"username": username,
|
|
"src": addr.String(),
|
|
"dst": reqAddr,
|
|
}).Debug("UDP request closed")
|
|
}
|
|
},
|
|
)
|
|
if err != nil {
|
|
logrus.WithField("error", err).Fatal("Server initialization failed")
|
|
}
|
|
defer server.Close()
|
|
logrus.WithField("addr", config.ListenAddr).Info("Server up and running")
|
|
|
|
err = server.Serve()
|
|
logrus.WithField("error", err).Fatal("Server shutdown")
|
|
}
|
|
|
|
func checkAuth(authFile, username, password string) (bool, error) {
|
|
f, err := os.Open(authFile)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer f.Close()
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
pair := strings.Fields(scanner.Text())
|
|
if len(pair) != 2 {
|
|
// Invalid format
|
|
continue
|
|
}
|
|
if username == pair[0] && password == pair[1] {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|