Relay & better logging

This commit is contained in:
Toby 2021-02-05 01:00:44 -08:00
parent 7d280393a3
commit 565d659338
6 changed files with 270 additions and 117 deletions

View File

@ -11,7 +11,9 @@ import (
"github.com/tobyxdd/hysteria/pkg/core" "github.com/tobyxdd/hysteria/pkg/core"
hyHTTP "github.com/tobyxdd/hysteria/pkg/http" hyHTTP "github.com/tobyxdd/hysteria/pkg/http"
"github.com/tobyxdd/hysteria/pkg/obfs" "github.com/tobyxdd/hysteria/pkg/obfs"
"github.com/tobyxdd/hysteria/pkg/relay"
"github.com/tobyxdd/hysteria/pkg/socks5" "github.com/tobyxdd/hysteria/pkg/socks5"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
@ -101,46 +103,28 @@ func client(config *clientConfig) {
return config.SOCKS5.User == user && config.SOCKS5.Password == password return config.SOCKS5.User == user && config.SOCKS5.Password == password
} }
} }
socks5server, err := socks5.NewServer(client, config.SOCKS5.Listen, authFunc, config.SOCKS5.Timeout, aclEngine, socks5server, err := socks5.NewServer(client, config.SOCKS5.Listen, authFunc,
config.SOCKS5.DisableUDP, time.Duration(config.SOCKS5.Timeout)*time.Second, aclEngine, config.SOCKS5.DisableUDP,
func(addr net.Addr, reqAddr string, action acl.Action, arg string) { func(addr net.Addr, reqAddr string, action acl.Action, arg string) {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"action": actionToString(action, arg), "action": actionToString(action, arg),
"src": addr.String(), "src": addr.String(),
"dst": reqAddr, "dst": reqAddr,
}).Debug("New SOCKS5 TCP request") }).Debug("SOCKS5 TCP request")
}, },
func(addr net.Addr, reqAddr string, err error) { func(addr net.Addr, reqAddr string, err error) {
if err != io.EOF {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"error": err, "error": err,
"src": addr.String(), "src": addr.String(),
"dst": reqAddr, "dst": reqAddr,
}).Debug("SOCKS5 TCP request closed") }).Info("SOCKS5 TCP error")
}, } else {
func(addr net.Addr) {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"src": addr.String(), "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, "dst": reqAddr,
}).Debug("New SOCKS5 UDP tunnel") }).Debug("SOCKS5 TCP EOF")
}, }
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 { if err != nil {
logrus.WithField("error", err).Fatal("Failed to initialize SOCKS5 server") logrus.WithField("error", err).Fatal("Failed to initialize SOCKS5 server")
@ -163,7 +147,7 @@ func client(config *clientConfig) {
logrus.WithFields(logrus.Fields{ logrus.WithFields(logrus.Fields{
"action": actionToString(action, arg), "action": actionToString(action, arg),
"dst": reqAddr, "dst": reqAddr,
}).Debug("New HTTP request") }).Debug("HTTP request")
}, },
authFunc) authFunc)
if err != nil { if err != nil {
@ -179,21 +163,36 @@ func client(config *clientConfig) {
}() }()
} }
if len(config.Relay.Listen) > 0 {
go func() {
rl, err := relay.NewRelay(client, config.Relay.Listen, config.Relay.Remote,
time.Duration(config.Relay.Timeout)*time.Second,
func(addr net.Addr) {
logrus.WithFields(logrus.Fields{
"src": addr.String(),
}).Debug("TCP relay request")
},
func(addr net.Addr, err error) {
if err != io.EOF {
logrus.WithFields(logrus.Fields{
"error": err,
"src": addr.String(),
}).Info("TCP relay error")
} else {
logrus.WithFields(logrus.Fields{
"src": addr.String(),
}).Debug("TCP relay EOF")
}
})
if err != nil {
logrus.WithField("error", err).Fatal("Failed to initialize TCP relay")
}
logrus.WithField("addr", config.Relay.Listen).Info("TCP relay up and running")
errChan <- rl.ListenAndServe()
}()
}
err = <-errChan err = <-errChan
logrus.WithField("error", err).Fatal("Client shutdown") 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"
}
}

View File

@ -9,6 +9,7 @@ import (
hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion" hyCongestion "github.com/tobyxdd/hysteria/pkg/congestion"
"github.com/tobyxdd/hysteria/pkg/core" "github.com/tobyxdd/hysteria/pkg/core"
"github.com/tobyxdd/hysteria/pkg/obfs" "github.com/tobyxdd/hysteria/pkg/obfs"
"io"
"net" "net"
"strings" "strings"
) )
@ -78,16 +79,7 @@ func server(config *serverConfig) {
uint64(config.UpMbps)*mbpsToBps, uint64(config.DownMbps)*mbpsToBps, uint64(config.UpMbps)*mbpsToBps, uint64(config.DownMbps)*mbpsToBps,
func(refBPS uint64) congestion.CongestionControl { func(refBPS uint64) congestion.CongestionControl {
return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS)) return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))
}, aclEngine, obfuscator, authFunc, func(addr net.Addr, auth []byte, udp bool, reqAddr string) { }, aclEngine, obfuscator, authFunc, tcpRequestFunc, tcpErrorFunc)
if !udp {
logrus.WithFields(logrus.Fields{
"src": addr.String(),
"dst": reqAddr,
}).Debug("New TCP request")
} else {
// TODO
}
})
if err != nil { if err != nil {
logrus.WithField("error", err).Fatal("Failed to initialize server") logrus.WithField("error", err).Fatal("Failed to initialize server")
} }
@ -97,3 +89,41 @@ func server(config *serverConfig) {
err = server.Serve() err = server.Serve()
logrus.WithField("error", err).Fatal("Server shutdown") logrus.WithField("error", err).Fatal("Server shutdown")
} }
func tcpRequestFunc(addr net.Addr, auth []byte, reqAddr string, action acl.Action, arg string) {
logrus.WithFields(logrus.Fields{
"src": addr.String(),
"dst": 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": addr.String(),
"dst": reqAddr,
"error": err,
}).Info("TCP error")
} else {
logrus.WithFields(logrus.Fields{
"src": addr.String(),
"dst": reqAddr,
}).Debug("TCP 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"
}
}

View File

@ -15,21 +15,24 @@ import (
const dialTimeout = 10 * time.Second const dialTimeout = 10 * time.Second
type AuthFunc func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string) type AuthFunc func(addr net.Addr, auth []byte, sSend uint64, sRecv uint64) (bool, string)
type RequestFunc func(addr net.Addr, auth []byte, udp bool, reqAddr string) type TCPRequestFunc func(addr net.Addr, auth []byte, reqAddr string, action acl.Action, arg string)
type TCPErrorFunc func(addr net.Addr, auth []byte, reqAddr string, err error)
type Server struct { type Server struct {
sendBPS, recvBPS uint64 sendBPS, recvBPS uint64
congestionFactory CongestionFactory congestionFactory CongestionFactory
authFunc AuthFunc
requestFunc RequestFunc
aclEngine *acl.Engine aclEngine *acl.Engine
authFunc AuthFunc
tcpRequestFunc TCPRequestFunc
tcpErrorFunc TCPErrorFunc
listener quic.Listener listener quic.Listener
} }
func NewServer(addr string, tlsConfig *tls.Config, quicConfig *quic.Config, func NewServer(addr string, tlsConfig *tls.Config, quicConfig *quic.Config,
sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, aclEngine *acl.Engine, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, aclEngine *acl.Engine,
obfuscator Obfuscator, authFunc AuthFunc, requestFunc RequestFunc) (*Server, error) { obfuscator Obfuscator, authFunc AuthFunc, tcpRequestFunc TCPRequestFunc, tcpErrorFunc TCPErrorFunc) (*Server, error) {
packetConn, err := net.ListenPacket("udp", addr) packetConn, err := net.ListenPacket("udp", addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -50,9 +53,10 @@ func NewServer(addr string, tlsConfig *tls.Config, quicConfig *quic.Config,
sendBPS: sendBPS, sendBPS: sendBPS,
recvBPS: recvBPS, recvBPS: recvBPS,
congestionFactory: congestionFactory, congestionFactory: congestionFactory,
authFunc: authFunc,
requestFunc: requestFunc,
aclEngine: aclEngine, aclEngine: aclEngine,
authFunc: authFunc,
tcpRequestFunc: tcpRequestFunc,
tcpErrorFunc: tcpErrorFunc,
} }
return s, nil return s, nil
} }
@ -148,23 +152,23 @@ func (s *Server) handleStream(remoteAddr net.Addr, auth []byte, stream quic.Stre
if err != nil { if err != nil {
return return
} }
s.requestFunc(remoteAddr, auth, req.UDP, req.Address)
if !req.UDP { if !req.UDP {
// TCP connection // TCP connection
s.handleTCP(stream, req.Address) s.handleTCP(remoteAddr, auth, stream, req.Address)
} else { } else {
// UDP connection // UDP connection
// TODO // TODO
} }
} }
func (s *Server) handleTCP(stream quic.Stream, reqAddr string) { func (s *Server) handleTCP(remoteAddr net.Addr, auth []byte, stream quic.Stream, reqAddr string) {
host, port, err := net.SplitHostPort(reqAddr) host, port, err := net.SplitHostPort(reqAddr)
if err != nil { if err != nil {
_ = struc.Pack(stream, &serverResponse{ _ = struc.Pack(stream, &serverResponse{
OK: false, OK: false,
Message: "invalid address", Message: "invalid address",
}) })
s.tcpErrorFunc(remoteAddr, auth, reqAddr, err)
return return
} }
ip := net.ParseIP(host) ip := net.ParseIP(host)
@ -176,6 +180,7 @@ func (s *Server) handleTCP(stream quic.Stream, reqAddr string) {
if s.aclEngine != nil { if s.aclEngine != nil {
action, arg = s.aclEngine.Lookup(host, ip) action, arg = s.aclEngine.Lookup(host, ip)
} }
s.tcpRequestFunc(remoteAddr, auth, reqAddr, action, arg)
var conn net.Conn // Connection to be piped var conn net.Conn // Connection to be piped
switch action { switch action {
@ -186,6 +191,7 @@ func (s *Server) handleTCP(stream quic.Stream, reqAddr string) {
OK: false, OK: false,
Message: err.Error(), Message: err.Error(),
}) })
s.tcpErrorFunc(remoteAddr, auth, reqAddr, err)
return return
} }
case acl.ActionBlock: case acl.ActionBlock:
@ -202,6 +208,7 @@ func (s *Server) handleTCP(stream quic.Stream, reqAddr string) {
OK: false, OK: false,
Message: err.Error(), Message: err.Error(),
}) })
s.tcpErrorFunc(remoteAddr, auth, reqAddr, err)
return return
} }
default: default:
@ -212,11 +219,13 @@ func (s *Server) handleTCP(stream quic.Stream, reqAddr string) {
return return
} }
// So far so good if we reach here // So far so good if we reach here
defer conn.Close()
err = struc.Pack(stream, &serverResponse{ err = struc.Pack(stream, &serverResponse{
OK: true, OK: true,
}) })
if err != nil { if err != nil {
return return
} }
_ = utils.Pipe2Way(stream, conn) err = utils.Pipe2Way(stream, conn)
s.tcpErrorFunc(remoteAddr, auth, reqAddr, err)
} }

View File

@ -52,7 +52,7 @@ func NewProxyHTTPServer(hyClient *core.Client, idleTimeout time.Duration, aclEng
} }
}, },
IdleConnTimeout: idleTimeout, IdleConnTimeout: idleTimeout,
// TODO: Disable HTTP2 support? ref: https://github.com/elazarl/goproxy/issues/361 // Disable HTTP2 support? ref: https://github.com/elazarl/goproxy/issues/361
} }
proxy.ConnectDial = nil proxy.ConnectDial = nil
if basicAuthFunc != nil { if basicAuthFunc != nil {

112
pkg/relay/relay.go Normal file
View File

@ -0,0 +1,112 @@
package relay
import (
"github.com/tobyxdd/hysteria/pkg/core"
"github.com/tobyxdd/hysteria/pkg/utils"
"io"
"net"
"time"
)
type Relay struct {
HyClient *core.Client
ListenAddr *net.TCPAddr
Remote string
Timeout time.Duration
ConnFunc func(addr net.Addr)
ErrorFunc func(addr net.Addr, err error)
tcpListener *net.TCPListener
}
func NewRelay(hyClient *core.Client, listen, remote string, timeout time.Duration,
connFunc func(addr net.Addr), errorFunc func(addr net.Addr, err error)) (*Relay, error) {
tAddr, err := net.ResolveTCPAddr("tcp", listen)
if err != nil {
return nil, err
}
r := &Relay{
HyClient: hyClient,
ListenAddr: tAddr,
Remote: remote,
Timeout: timeout,
ConnFunc: connFunc,
ErrorFunc: errorFunc,
}
return r, nil
}
func (r *Relay) ListenAndServe() error {
var err error
r.tcpListener, err = net.ListenTCP("tcp", r.ListenAddr)
if err != nil {
return err
}
defer r.tcpListener.Close()
for {
c, err := r.tcpListener.AcceptTCP()
if err != nil {
return err
}
go func(c *net.TCPConn) {
defer c.Close()
r.ConnFunc(c.RemoteAddr())
rc, err := r.HyClient.DialTCP(r.Remote)
if err != nil {
r.ErrorFunc(c.RemoteAddr(), err)
return
}
defer rc.Close()
err = pipePair(c, rc, r.Timeout)
r.ErrorFunc(c.RemoteAddr(), err)
}(c)
}
}
func pipePair(conn *net.TCPConn, stream io.ReadWriteCloser, timeout time.Duration) error {
errChan := make(chan error, 2)
// TCP to stream
go func() {
buf := make([]byte, utils.PipeBufferSize)
for {
if timeout != 0 {
_ = conn.SetDeadline(time.Now().Add(timeout))
}
rn, err := conn.Read(buf)
if rn > 0 {
_, err := stream.Write(buf[:rn])
if err != nil {
errChan <- err
return
}
}
if err != nil {
errChan <- err
return
}
}
}()
// Stream to TCP
go func() {
buf := make([]byte, utils.PipeBufferSize)
for {
rn, err := stream.Read(buf)
if rn > 0 {
_, err := conn.Write(buf[:rn])
if err != nil {
errChan <- err
return
}
if timeout != 0 {
_ = conn.SetDeadline(time.Now().Add(timeout))
}
}
if err != nil {
errChan <- err
return
}
}
}()
return <-errChan
}

View File

@ -27,30 +27,21 @@ type Server struct {
AuthFunc func(username, password string) bool AuthFunc func(username, password string) bool
Method byte Method byte
TCPAddr *net.TCPAddr TCPAddr *net.TCPAddr
TCPDeadline int TCPTimeout time.Duration
ACLEngine *acl.Engine ACLEngine *acl.Engine
DisableUDP bool DisableUDP bool
NewRequestFunc func(addr net.Addr, reqAddr string, action acl.Action, arg string) TCPRequestFunc func(addr net.Addr, reqAddr string, action acl.Action, arg string)
RequestClosedFunc func(addr net.Addr, reqAddr string, err error) TCPErrorFunc func(addr net.Addr, reqAddr string, err error)
NewUDPAssociateFunc func(addr net.Addr)
UDPAssociateClosedFunc func(addr net.Addr, err error)
NewUDPTunnelFunc func(addr net.Addr, reqAddr string, action acl.Action, arg string)
UDPTunnelClosedFunc func(addr net.Addr, reqAddr string, err error)
tcpListener *net.TCPListener tcpListener *net.TCPListener
} }
func NewServer(hyClient *core.Client, addr string, authFunc func(username, password string) bool, tcpDeadline int, func NewServer(hyClient *core.Client, addr string, authFunc func(username, password string) bool, tcpTimeout time.Duration,
aclEngine *acl.Engine, disableUDP bool, aclEngine *acl.Engine, disableUDP bool,
newReqFunc func(addr net.Addr, reqAddr string, action acl.Action, arg string), tcpReqFunc func(addr net.Addr, reqAddr string, action acl.Action, arg string),
reqClosedFunc func(addr net.Addr, reqAddr string, err error), tcpErrorFunc func(addr net.Addr, reqAddr string, err error)) (*Server, error) {
newUDPAssociateFunc func(addr net.Addr), tAddr, err := net.ResolveTCPAddr("tcp", addr)
udpAssociateClosedFunc func(addr net.Addr, err error),
newUDPTunnelFunc func(addr net.Addr, reqAddr string, action acl.Action, arg string),
udpTunnelClosedFunc func(addr net.Addr, reqAddr string, err error)) (*Server, error) {
taddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,16 +53,12 @@ func NewServer(hyClient *core.Client, addr string, authFunc func(username, passw
HyClient: hyClient, HyClient: hyClient,
AuthFunc: authFunc, AuthFunc: authFunc,
Method: m, Method: m,
TCPAddr: taddr, TCPAddr: tAddr,
TCPDeadline: tcpDeadline, TCPTimeout: tcpTimeout,
ACLEngine: aclEngine, ACLEngine: aclEngine,
DisableUDP: disableUDP, DisableUDP: disableUDP,
NewRequestFunc: newReqFunc, TCPRequestFunc: tcpReqFunc,
RequestClosedFunc: reqClosedFunc, TCPErrorFunc: tcpErrorFunc,
NewUDPAssociateFunc: newUDPAssociateFunc,
UDPAssociateClosedFunc: udpAssociateClosedFunc,
NewUDPTunnelFunc: newUDPTunnelFunc,
UDPTunnelClosedFunc: udpTunnelClosedFunc,
} }
return s, nil return s, nil
} }
@ -133,8 +120,8 @@ func (s *Server) ListenAndServe() error {
} }
go func(c *net.TCPConn) { go func(c *net.TCPConn) {
defer c.Close() defer c.Close()
if s.TCPDeadline != 0 { if s.TCPTimeout != 0 {
if err := c.SetDeadline(time.Now().Add(time.Duration(s.TCPDeadline) * time.Second)); err != nil { if err := c.SetDeadline(time.Now().Add(s.TCPTimeout)); err != nil {
return return
} }
} }
@ -170,10 +157,10 @@ func (s *Server) handleTCP(c *net.TCPConn, r *socks5.Request) error {
if s.ACLEngine != nil { if s.ACLEngine != nil {
action, arg = s.ACLEngine.Lookup(domain, ip) action, arg = s.ACLEngine.Lookup(domain, ip)
} }
s.NewRequestFunc(c.RemoteAddr(), addr, action, arg) s.TCPRequestFunc(c.RemoteAddr(), addr, action, arg)
var closeErr error var closeErr error
defer func() { defer func() {
s.RequestClosedFunc(c.RemoteAddr(), addr, closeErr) s.TCPErrorFunc(c.RemoteAddr(), addr, closeErr)
}() }()
// Handle according to the action // Handle according to the action
switch action { switch action {
@ -186,7 +173,7 @@ func (s *Server) handleTCP(c *net.TCPConn, r *socks5.Request) error {
} }
defer rc.Close() defer rc.Close()
_ = sendReply(c, socks5.RepSuccess) _ = sendReply(c, socks5.RepSuccess)
closeErr = pipePair(c, rc, s.TCPDeadline) closeErr = pipePair(c, rc, s.TCPTimeout)
return nil return nil
case acl.ActionProxy: case acl.ActionProxy:
rc, err := s.HyClient.DialTCP(addr) rc, err := s.HyClient.DialTCP(addr)
@ -197,7 +184,7 @@ func (s *Server) handleTCP(c *net.TCPConn, r *socks5.Request) error {
} }
defer rc.Close() defer rc.Close()
_ = sendReply(c, socks5.RepSuccess) _ = sendReply(c, socks5.RepSuccess)
closeErr = pipePair(c, rc, s.TCPDeadline) closeErr = pipePair(c, rc, s.TCPTimeout)
return nil return nil
case acl.ActionBlock: case acl.ActionBlock:
_ = sendReply(c, socks5.RepHostUnreachable) _ = sendReply(c, socks5.RepHostUnreachable)
@ -212,7 +199,7 @@ func (s *Server) handleTCP(c *net.TCPConn, r *socks5.Request) error {
} }
defer rc.Close() defer rc.Close()
_ = sendReply(c, socks5.RepSuccess) _ = sendReply(c, socks5.RepSuccess)
closeErr = pipePair(c, rc, s.TCPDeadline) closeErr = pipePair(c, rc, s.TCPTimeout)
return nil return nil
default: default:
_ = sendReply(c, socks5.RepServerFailure) _ = sendReply(c, socks5.RepServerFailure)
@ -237,15 +224,14 @@ func parseRequestAddress(r *socks5.Request) (domain string, ip net.IP, port stri
} }
} }
func pipePair(conn *net.TCPConn, stream io.ReadWriteCloser, deadline int) error { func pipePair(conn *net.TCPConn, stream io.ReadWriteCloser, timeout time.Duration) error {
deadlineDuration := time.Duration(deadline) * time.Second
errChan := make(chan error, 2) errChan := make(chan error, 2)
// TCP to stream // TCP to stream
go func() { go func() {
buf := make([]byte, utils.PipeBufferSize) buf := make([]byte, utils.PipeBufferSize)
for { for {
if deadline != 0 { if timeout != 0 {
_ = conn.SetDeadline(time.Now().Add(deadlineDuration)) _ = conn.SetDeadline(time.Now().Add(timeout))
} }
rn, err := conn.Read(buf) rn, err := conn.Read(buf)
if rn > 0 { if rn > 0 {
@ -263,7 +249,24 @@ func pipePair(conn *net.TCPConn, stream io.ReadWriteCloser, deadline int) error
}() }()
// Stream to TCP // Stream to TCP
go func() { go func() {
errChan <- utils.Pipe(stream, conn) buf := make([]byte, utils.PipeBufferSize)
for {
rn, err := stream.Read(buf)
if rn > 0 {
_, err := conn.Write(buf[:rn])
if err != nil {
errChan <- err
return
}
if timeout != 0 {
_ = conn.SetDeadline(time.Now().Add(timeout))
}
}
if err != nil {
errChan <- err
return
}
}
}() }()
return <-errChan return <-errChan
} }