go-socks5/server.go

186 lines
5.4 KiB
Go
Raw Normal View History

2014-01-23 19:28:30 +00:00
package socks5
import (
"bufio"
2020-04-19 09:08:22 +00:00
"context"
"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
"github.com/thinkgos/go-socks5/bufferpool"
2020-08-05 05:17:05 +00:00
"github.com/thinkgos/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-04-20 13:17:38 +00:00
// Server is reponsible for accepting connections and handling
// 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.
// For password-based auth use UserPassAuthenticator.
2020-04-20 13:17:38 +00:00
authCustomMethods []Authenticator
// 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
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{},
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 {
2014-01-23 19:28:30 +00:00
for {
conn, err := l.Accept()
if err != nil {
return err
}
2020-08-06 03:11:30 +00:00
sf.submit(func() {
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()
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
request, err := ParseRequest(bufConn)
if err != nil {
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)
}
}
2020-08-05 07:27:22 +00:00
return fmt.Errorf("failed to read destination address, %w", err)
}
2020-08-05 07:27:22 +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-06 03:11:30 +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-06 03:11:30 +00:00
func (sf *Server) submit(f func()) {
if sf.gPool == nil || sf.gPool.Submit(f) != nil {
2020-04-20 08:52:29 +00:00
go f()
}
}