mirror of
https://github.com/apernet/hysteria.git
synced 2025-04-03 04:27:39 +03:00
Proxy timeout & auth
This commit is contained in:
parent
d5640efd7e
commit
e699a5560c
5 changed files with 91 additions and 23 deletions
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/tobyxdd/hysteria/pkg/socks5"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os/user"
|
||||
)
|
||||
|
||||
func proxyClient(args []string) {
|
||||
|
@ -22,12 +21,6 @@ func proxyClient(args []string) {
|
|||
if err := config.Check(); err != nil {
|
||||
log.Fatalln("Configuration error:", err)
|
||||
}
|
||||
if len(config.Name) == 0 {
|
||||
usr, err := user.Current()
|
||||
if err == nil {
|
||||
config.Name = usr.Name
|
||||
}
|
||||
}
|
||||
log.Printf("Configuration loaded: %+v\n", config)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -59,7 +52,7 @@ func proxyClient(args []string) {
|
|||
quicConfig.MaxReceiveConnectionFlowControlWindow = DefaultMaxReceiveConnectionFlowControlWindow
|
||||
}
|
||||
|
||||
client, err := core.NewClient(config.ServerAddr, config.Name, "", tlsConfig, quicConfig,
|
||||
client, err := core.NewClient(config.ServerAddr, config.Username, config.Password, tlsConfig, quicConfig,
|
||||
uint64(config.UpMbps)*mbpsToBps, uint64(config.DownMbps)*mbpsToBps,
|
||||
func(refBPS uint64) congestion.SendAlgorithmWithDebugInfos {
|
||||
return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))
|
||||
|
@ -70,7 +63,7 @@ func proxyClient(args []string) {
|
|||
defer client.Close()
|
||||
log.Println("Connected to", config.ServerAddr)
|
||||
|
||||
socks5server, err := socks5.NewServer(config.SOCKS5Addr, "", nil, 0, 0, 0)
|
||||
socks5server, err := socks5.NewServer(config.SOCKS5Addr, "", nil, config.SOCKS5Timeout, 0, 0)
|
||||
if err != nil {
|
||||
log.Fatalln("SOCKS5 server initialization failed:", err)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ type proxyClientConfig struct {
|
|||
SOCKS5Addr string `json:"socks5_addr" desc:"SOCKS5 listen address"`
|
||||
SOCKS5Timeout int `json:"socks5_timeout" desc:"SOCKS5 connection timeout in seconds"`
|
||||
ServerAddr string `json:"server" desc:"Server address"`
|
||||
Name string `json:"name" desc:"Client name presented to the server"`
|
||||
Username string `json:"username" desc:"Authentication username"`
|
||||
Password string `json:"password" desc:"Authentication password"`
|
||||
Insecure bool `json:"insecure" desc:"Ignore TLS certificate errors"`
|
||||
CustomCAFile string `json:"ca" desc:"Specify a trusted CA file"`
|
||||
UpMbps int `json:"up_mbps" desc:"Upload speed in Mbps"`
|
||||
|
@ -41,6 +42,7 @@ type proxyServerConfig struct {
|
|||
ListenAddr string `json:"listen" desc:"Server listen address"`
|
||||
CertFile string `json:"cert" desc:"TLS certificate file"`
|
||||
KeyFile string `json:"key" desc:"TLS key file"`
|
||||
AuthFile string `json:"auth" desc:"Authentication file"`
|
||||
UpMbps int `json:"up_mbps" desc:"Max upload speed per client in Mbps"`
|
||||
DownMbps int `json:"down_mbps" desc:"Max download speed per client in Mbps"`
|
||||
ReceiveWindowConn uint64 `json:"recv_window_conn" desc:"Max receive window size per connection"`
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/lucas-clemente/quic-go/congestion"
|
||||
|
@ -9,6 +10,8 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func proxyServer(args []string) {
|
||||
|
@ -48,16 +51,36 @@ func proxyServer(args []string) {
|
|||
quicConfig.MaxIncomingStreams = DefaultMaxIncomingStreams
|
||||
}
|
||||
|
||||
if len(config.AuthFile) == 0 {
|
||||
log.Println("WARNING: No authentication configured. This server can be used by anyone!")
|
||||
}
|
||||
|
||||
server, err := core.NewServer(config.ListenAddr, tlsConfig, quicConfig,
|
||||
uint64(config.UpMbps)*mbpsToBps, uint64(config.DownMbps)*mbpsToBps,
|
||||
func(refBPS uint64) congestion.SendAlgorithmWithDebugInfos {
|
||||
return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))
|
||||
},
|
||||
func(addr net.Addr, username string, password string, sSend uint64, sRecv uint64) (core.AuthResult, string) {
|
||||
// No authentication logic in relay, just log username and speed
|
||||
log.Printf("%s (%s) connected, negotiated speed (Mbps): Up %d / Down %d\n",
|
||||
addr.String(), username, sSend/mbpsToBps, sRecv/mbpsToBps)
|
||||
return core.AuthSuccess, ""
|
||||
if len(config.AuthFile) == 0 {
|
||||
log.Printf("%s (%s) connected, negotiated speed (Mbps): Up %d / Down %d\n",
|
||||
addr.String(), username, sSend/mbpsToBps, sRecv/mbpsToBps)
|
||||
return core.AuthSuccess, ""
|
||||
} else {
|
||||
// Need auth
|
||||
ok, err := checkAuth(config.AuthFile, username, password)
|
||||
if err != nil {
|
||||
log.Printf("%s (%s) auth error: %s\n", addr.String(), username, err.Error())
|
||||
return core.AuthInternalError, "Server auth error"
|
||||
}
|
||||
if ok {
|
||||
log.Printf("%s (%s) authenticated, negotiated speed (Mbps): Up %d / Down %d\n",
|
||||
addr.String(), username, sSend/mbpsToBps, sRecv/mbpsToBps)
|
||||
return core.AuthSuccess, ""
|
||||
} else {
|
||||
log.Printf("%s (%s) auth failed (invalid credential)\n", addr.String(), username)
|
||||
return core.AuthInvalidCred, "Invalid credential"
|
||||
}
|
||||
}
|
||||
},
|
||||
func(addr net.Addr, username string, err error) {
|
||||
log.Printf("%s (%s) disconnected: %s\n", addr.String(), username, err.Error())
|
||||
|
@ -99,3 +122,23 @@ func proxyServer(args []string) {
|
|||
|
||||
log.Fatalln(server.Serve())
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ import (
|
|||
"sync/atomic"
|
||||
)
|
||||
|
||||
const pipeBufferSize = 65536
|
||||
const PipeBufferSize = 65536
|
||||
|
||||
func Pipe(src, dst io.ReadWriter, atomicCounter *uint64) error {
|
||||
buf := make([]byte, pipeBufferSize)
|
||||
buf := make([]byte, PipeBufferSize)
|
||||
for {
|
||||
rn, err := src.Read(buf)
|
||||
if rn > 0 {
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"github.com/tobyxdd/hysteria/internal/utils"
|
||||
"github.com/tobyxdd/hysteria/pkg/core"
|
||||
"github.com/txthinking/socks5"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HyHandler struct {
|
||||
|
@ -22,19 +24,17 @@ func (h *HyHandler) TCPHandle(server *Server, conn *net.TCPConn, request *socks5
|
|||
}()
|
||||
rc, err := h.Client.Dial(false, request.Address())
|
||||
if err != nil {
|
||||
_ = sendFailed(request, conn, socks5.RepHostUnreachable)
|
||||
_ = sendReply(request, conn, socks5.RepHostUnreachable)
|
||||
closeErr = err
|
||||
return err
|
||||
}
|
||||
// All good
|
||||
p := socks5.NewReply(socks5.RepSuccess, socks5.ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
_, _ = p.WriteTo(conn)
|
||||
_ = sendReply(request, conn, socks5.RepSuccess)
|
||||
defer rc.Close()
|
||||
closeErr = utils.PipePair(conn, rc, nil, nil)
|
||||
closeErr = pipePair(conn, rc, server.TCPDeadline)
|
||||
return nil
|
||||
} else {
|
||||
p := socks5.NewReply(socks5.RepCommandNotSupported, socks5.ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
_, _ = p.WriteTo(conn)
|
||||
_ = sendReply(request, conn, socks5.RepCommandNotSupported)
|
||||
return ErrUnsupportedCmd
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ func (h *HyHandler) UDPHandle(server *Server, addr *net.UDPAddr, datagram *socks
|
|||
return nil
|
||||
}
|
||||
|
||||
func sendFailed(request *socks5.Request, conn *net.TCPConn, rep byte) error {
|
||||
func sendReply(request *socks5.Request, conn *net.TCPConn, rep byte) error {
|
||||
var p *socks5.Reply
|
||||
if request.Atyp == socks5.ATYPIPv4 || request.Atyp == socks5.ATYPDomain {
|
||||
p = socks5.NewReply(rep, socks5.ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
|
@ -54,3 +54,33 @@ func sendFailed(request *socks5.Request, conn *net.TCPConn, rep byte) error {
|
|||
_, err := p.WriteTo(conn)
|
||||
return err
|
||||
}
|
||||
|
||||
func pipePair(conn *net.TCPConn, stream io.ReadWriteCloser, deadline int) error {
|
||||
errChan := make(chan error, 2)
|
||||
// TCP to stream
|
||||
go func() {
|
||||
buf := make([]byte, utils.PipeBufferSize)
|
||||
for {
|
||||
if deadline != 0 {
|
||||
_ = conn.SetDeadline(time.Now().Add(time.Duration(deadline) * time.Second))
|
||||
}
|
||||
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() {
|
||||
errChan <- utils.Pipe(stream, conn, nil)
|
||||
}()
|
||||
return <-errChan
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue