2014-01-23 19:28:30 +00:00
|
|
|
package socks5
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2020-04-19 09:08:22 +00:00
|
|
|
"context"
|
2020-08-06 00:19:31 +00:00
|
|
|
"errors"
|
2014-01-23 19:28:30 +00:00
|
|
|
"fmt"
|
2020-04-22 02:15:40 +00:00
|
|
|
"io"
|
2020-04-19 09:47:39 +00:00
|
|
|
"io/ioutil"
|
2014-01-23 19:28:30 +00:00
|
|
|
"log"
|
|
|
|
"net"
|
2020-08-05 05:17:05 +00:00
|
|
|
|
2022-10-17 01:36:23 +00:00
|
|
|
"git.tcp.direct/kayos/go-socks5/bufferpool"
|
|
|
|
"git.tcp.direct/kayos/go-socks5/statute"
|
2014-01-23 19:28:30 +00:00
|
|
|
)
|
|
|
|
|
2020-04-20 08:52:29 +00:00
|
|
|
// GPool is used to implement custom goroutine pool default use goroutine
|
|
|
|
type GPool interface {
|
|
|
|
Submit(f func()) error
|
|
|
|
}
|
|
|
|
|
2020-08-30 03:15:05 +00:00
|
|
|
// Server is responsible for accepting connections and handling
|
2020-04-20 13:17:38 +00:00
|
|
|
// the details of the SOCKS5 protocol
|
|
|
|
type Server struct {
|
|
|
|
authMethods map[uint8]Authenticator
|
2014-02-18 14:21:03 +00:00
|
|
|
// AuthMethods can be provided to implement custom authentication
|
2020-08-06 08:29:38 +00:00
|
|
|
// By default, "no-auth" mode is enabled.
|
2014-02-22 20:31:12 +00:00
|
|
|
// For password-based auth use UserPassAuthenticator.
|
2020-04-20 13:17:38 +00:00
|
|
|
authCustomMethods []Authenticator
|
2014-02-22 20:31:12 +00:00
|
|
|
// If provided, username/password authentication is enabled,
|
|
|
|
// by appending a UserPassAuthenticator to AuthMethods. If not provided,
|
2020-08-06 08:29:38 +00:00
|
|
|
// and authCustomMethods is nil, then "no-auth" mode is enabled.
|
2020-04-20 13:17:38 +00:00
|
|
|
credentials CredentialStore
|
|
|
|
// resolver can be provided to do custom name resolution.
|
2014-01-23 19:28:30 +00:00
|
|
|
// Defaults to DNSResolver if not provided.
|
2020-04-20 13:17:38 +00:00
|
|
|
resolver NameResolver
|
|
|
|
// rules is provided to enable custom logic around permitting
|
2020-08-05 06:40:07 +00:00
|
|
|
// various commands. If not provided, NewPermitAll is used.
|
2020-04-20 13:17:38 +00:00
|
|
|
rules RuleSet
|
|
|
|
// rewriter can be used to transparently rewrite addresses.
|
2014-01-24 00:55:08 +00:00
|
|
|
// This is invoked before the RuleSet is invoked.
|
|
|
|
// Defaults to NoRewrite.
|
2020-04-20 13:17:38 +00:00
|
|
|
rewriter AddressRewriter
|
|
|
|
// bindIP is used for bind or udp associate
|
|
|
|
bindIP net.IP
|
|
|
|
// logger can be used to provide a custom log target.
|
2020-04-19 09:47:39 +00:00
|
|
|
// Defaults to ioutil.Discard.
|
2020-04-20 13:17:38 +00:00
|
|
|
logger Logger
|
2016-02-04 19:25:27 +00:00
|
|
|
// Optional function for dialing out
|
2020-04-20 13:17:38 +00:00
|
|
|
dial func(ctx context.Context, network, addr string) (net.Conn, error)
|
|
|
|
// buffer pool
|
2020-08-06 06:31:36 +00:00
|
|
|
bufferPool bufferpool.BufPool
|
2020-04-20 13:17:38 +00:00
|
|
|
// goroutine pool
|
|
|
|
gPool GPool
|
2020-04-22 02:15:40 +00:00
|
|
|
// user's handle
|
2020-04-24 07:52:47 +00:00
|
|
|
userConnectHandle func(ctx context.Context, writer io.Writer, request *Request) error
|
|
|
|
userBindHandle func(ctx context.Context, writer io.Writer, request *Request) error
|
|
|
|
userAssociateHandle func(ctx context.Context, writer io.Writer, request *Request) error
|
2014-01-23 19:28:30 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 08:29:38 +00:00
|
|
|
// NewServer creates a new Server
|
2020-08-05 09:32:37 +00:00
|
|
|
func NewServer(opts ...Option) *Server {
|
2020-08-06 08:29:38 +00:00
|
|
|
srv := &Server{
|
2020-04-20 13:17:38 +00:00
|
|
|
authMethods: make(map[uint8]Authenticator),
|
2020-08-06 08:29:38 +00:00
|
|
|
authCustomMethods: []Authenticator{},
|
2020-08-06 06:31:36 +00:00
|
|
|
bufferPool: bufferpool.NewPool(32 * 1024),
|
2020-04-20 13:17:38 +00:00
|
|
|
resolver: DNSResolver{},
|
2020-08-05 06:40:07 +00:00
|
|
|
rules: NewPermitAll(),
|
2020-04-20 13:17:38 +00:00
|
|
|
logger: NewLogger(log.New(ioutil.Discard, "socks5: ", log.LstdFlags)),
|
|
|
|
dial: func(ctx context.Context, net_, addr string) (net.Conn, error) {
|
2020-04-20 08:19:51 +00:00
|
|
|
return net.Dial(net_, addr)
|
2020-04-20 13:17:38 +00:00
|
|
|
},
|
2020-04-20 08:19:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-20 13:17:38 +00:00
|
|
|
for _, opt := range opts {
|
2020-08-06 08:29:38 +00:00
|
|
|
opt(srv)
|
2014-01-23 19:28:30 +00:00
|
|
|
}
|
2014-02-18 14:21:03 +00:00
|
|
|
|
2020-04-20 13:17:38 +00:00
|
|
|
// Ensure we have at least one authentication method enabled
|
2020-08-06 08:29:38 +00:00
|
|
|
if (len(srv.authCustomMethods) == 0) && srv.credentials != nil {
|
|
|
|
srv.authCustomMethods = []Authenticator{&UserPassAuthenticator{srv.credentials}}
|
2020-04-20 13:17:38 +00:00
|
|
|
}
|
2014-02-18 14:21:03 +00:00
|
|
|
|
2020-08-06 08:29:38 +00:00
|
|
|
if len(srv.authCustomMethods) == 0 {
|
|
|
|
srv.authCustomMethods = []Authenticator{&NoAuthAuthenticator{}}
|
2014-02-18 14:21:03 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 08:29:38 +00:00
|
|
|
for _, v := range srv.authCustomMethods {
|
|
|
|
srv.authMethods[v.GetCode()] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return srv
|
2014-01-23 19:28:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListenAndServe is used to create a listener and serve on it
|
2020-08-06 03:11:30 +00:00
|
|
|
func (sf *Server) ListenAndServe(network, addr string) error {
|
2014-01-23 19:28:30 +00:00
|
|
|
l, err := net.Listen(network, addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-06 03:11:30 +00:00
|
|
|
return sf.Serve(l)
|
2014-01-23 19:28:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Serve is used to serve connections from a listener
|
2020-08-06 03:11:30 +00:00
|
|
|
func (sf *Server) Serve(l net.Listener) error {
|
2020-08-30 03:15:05 +00:00
|
|
|
defer l.Close()
|
2014-01-23 19:28:30 +00:00
|
|
|
for {
|
|
|
|
conn, err := l.Accept()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-10 00:34:34 +00:00
|
|
|
sf.goFunc(func() {
|
2020-08-06 03:11:30 +00:00
|
|
|
if err := sf.ServeConn(conn); err != nil {
|
2020-08-06 07:59:48 +00:00
|
|
|
sf.logger.Errorf("server: %v", err)
|
2020-04-22 02:32:03 +00:00
|
|
|
}
|
2020-04-20 08:52:29 +00:00
|
|
|
})
|
2014-01-23 19:28:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServeConn is used to serve a single connection.
|
2020-08-06 03:11:30 +00:00
|
|
|
func (sf *Server) ServeConn(conn net.Conn) error {
|
2020-08-05 07:27:22 +00:00
|
|
|
var authContext *AuthContext
|
|
|
|
|
2014-01-23 19:28:30 +00:00
|
|
|
defer conn.Close()
|
2020-08-06 00:19:31 +00:00
|
|
|
|
2014-01-23 19:28:30 +00:00
|
|
|
bufConn := bufio.NewReader(conn)
|
|
|
|
|
2020-08-05 05:54:05 +00:00
|
|
|
mr, err := statute.ParseMethodRequest(bufConn)
|
|
|
|
if err != nil {
|
2014-01-23 19:28:30 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-08-05 05:54:05 +00:00
|
|
|
if mr.Ver != statute.VersionSocks5 {
|
|
|
|
return statute.ErrNotSupportVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
// Authenticate the connection
|
2020-08-06 03:11:30 +00:00
|
|
|
authContext, err = sf.authenticate(conn, bufConn, conn.RemoteAddr().String(), mr.Methods)
|
2020-08-05 05:54:05 +00:00
|
|
|
if err != nil {
|
2020-08-05 07:27:22 +00:00
|
|
|
return fmt.Errorf("failed to authenticate: %w", err)
|
2014-01-23 19:28:30 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 02:34:29 +00:00
|
|
|
// The client request detail
|
2020-08-06 00:19:31 +00:00
|
|
|
request, err := ParseRequest(bufConn)
|
2016-01-11 08:24:54 +00:00
|
|
|
if err != nil {
|
2020-08-06 00:19:31 +00:00
|
|
|
if errors.Is(err, statute.ErrUnrecognizedAddrType) {
|
|
|
|
if err := SendReply(conn, statute.RepAddrTypeNotSupported, nil); err != nil {
|
2020-08-05 07:27:22 +00:00
|
|
|
return fmt.Errorf("failed to send reply %w", err)
|
2016-01-11 08:24:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-05 07:27:22 +00:00
|
|
|
return fmt.Errorf("failed to read destination address, %w", err)
|
2016-01-11 08:24:54 +00:00
|
|
|
}
|
2020-08-05 07:27:22 +00:00
|
|
|
|
2020-08-06 00:19:31 +00:00
|
|
|
if request.Request.Command != statute.CommandConnect &&
|
|
|
|
request.Request.Command != statute.CommandBind &&
|
|
|
|
request.Request.Command != statute.CommandAssociate {
|
|
|
|
if err := SendReply(conn, statute.RepCommandNotSupported, nil); err != nil {
|
|
|
|
return fmt.Errorf("failed to send reply, %v", err)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("unrecognized command[%d]", request.Request.Command)
|
|
|
|
}
|
|
|
|
|
2020-08-05 07:27:22 +00:00
|
|
|
request.AuthContext = authContext
|
2020-04-23 02:16:26 +00:00
|
|
|
request.LocalAddr = conn.LocalAddr()
|
2020-04-23 02:15:02 +00:00
|
|
|
request.RemoteAddr = conn.RemoteAddr()
|
2014-01-23 19:28:30 +00:00
|
|
|
// Process the client request
|
2020-08-06 03:11:30 +00:00
|
|
|
return sf.handleRequest(conn, request)
|
2014-01-23 19:28:30 +00:00
|
|
|
}
|
2020-04-20 08:52:29 +00:00
|
|
|
|
2020-08-05 06:40:07 +00:00
|
|
|
// authenticate is used to handle connection authentication
|
2020-08-30 03:15:05 +00:00
|
|
|
func (sf *Server) authenticate(conn io.Writer, bufConn io.Reader,
|
|
|
|
userAddr string, methods []byte) (*AuthContext, error) {
|
2020-08-05 06:40:07 +00:00
|
|
|
// Select a usable method
|
|
|
|
for _, method := range methods {
|
2020-08-06 03:11:30 +00:00
|
|
|
if cator, found := sf.authMethods[method]; found {
|
2020-08-05 06:40:07 +00:00
|
|
|
return cator.Authenticate(bufConn, conn, userAddr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// No usable method found
|
|
|
|
conn.Write([]byte{statute.VersionSocks5, statute.MethodNoAcceptable}) // nolint: errcheck
|
|
|
|
return nil, statute.ErrNoSupportedAuth
|
|
|
|
}
|
|
|
|
|
2020-08-10 00:34:34 +00:00
|
|
|
func (sf *Server) goFunc(f func()) {
|
2020-08-06 03:11:30 +00:00
|
|
|
if sf.gPool == nil || sf.gPool.Submit(f) != nil {
|
2020-04-20 08:52:29 +00:00
|
|
|
go f()
|
|
|
|
}
|
|
|
|
}
|