1package socks5
2
3import (
4	"bufio"
5	"fmt"
6	"log"
7	"net"
8	"os"
9
10	"github.com/ooni/psiphon/oopsi/golang.org/x/net/context"
11)
12
13const (
14	socks5Version = uint8(5)
15)
16
17// Config is used to setup and configure a Server
18type Config struct {
19	// AuthMethods can be provided to implement custom authentication
20	// By default, "auth-less" mode is enabled.
21	// For password-based auth use UserPassAuthenticator.
22	AuthMethods []Authenticator
23
24	// If provided, username/password authentication is enabled,
25	// by appending a UserPassAuthenticator to AuthMethods. If not provided,
26	// and AUthMethods is nil, then "auth-less" mode is enabled.
27	Credentials CredentialStore
28
29	// Resolver can be provided to do custom name resolution.
30	// Defaults to DNSResolver if not provided.
31	Resolver NameResolver
32
33	// Rules is provided to enable custom logic around permitting
34	// various commands. If not provided, PermitAll is used.
35	Rules RuleSet
36
37	// Rewriter can be used to transparently rewrite addresses.
38	// This is invoked before the RuleSet is invoked.
39	// Defaults to NoRewrite.
40	Rewriter AddressRewriter
41
42	// BindIP is used for bind or udp associate
43	BindIP net.IP
44
45	// Logger can be used to provide a custom log target.
46	// Defaults to stdout.
47	Logger *log.Logger
48
49	// Optional function for dialing out
50	Dial func(ctx context.Context, network, addr string) (net.Conn, error)
51}
52
53// Server is reponsible for accepting connections and handling
54// the details of the SOCKS5 protocol
55type Server struct {
56	config      *Config
57	authMethods map[uint8]Authenticator
58}
59
60// New creates a new Server and potentially returns an error
61func New(conf *Config) (*Server, error) {
62	// Ensure we have at least one authentication method enabled
63	if len(conf.AuthMethods) == 0 {
64		if conf.Credentials != nil {
65			conf.AuthMethods = []Authenticator{&UserPassAuthenticator{conf.Credentials}}
66		} else {
67			conf.AuthMethods = []Authenticator{&NoAuthAuthenticator{}}
68		}
69	}
70
71	// Ensure we have a DNS resolver
72	if conf.Resolver == nil {
73		conf.Resolver = DNSResolver{}
74	}
75
76	// Ensure we have a rule set
77	if conf.Rules == nil {
78		conf.Rules = PermitAll()
79	}
80
81	// Ensure we have a log target
82	if conf.Logger == nil {
83		conf.Logger = log.New(os.Stdout, "", log.LstdFlags)
84	}
85
86	server := &Server{
87		config: conf,
88	}
89
90	server.authMethods = make(map[uint8]Authenticator)
91
92	for _, a := range conf.AuthMethods {
93		server.authMethods[a.GetCode()] = a
94	}
95
96	return server, nil
97}
98
99// ListenAndServe is used to create a listener and serve on it
100func (s *Server) ListenAndServe(network, addr string) error {
101	l, err := net.Listen(network, addr)
102	if err != nil {
103		return err
104	}
105	return s.Serve(l)
106}
107
108// Serve is used to serve connections from a listener
109func (s *Server) Serve(l net.Listener) error {
110	for {
111		conn, err := l.Accept()
112		if err != nil {
113			return err
114		}
115		go s.ServeConn(conn)
116	}
117	return nil
118}
119
120// ServeConn is used to serve a single connection.
121func (s *Server) ServeConn(conn net.Conn) error {
122	defer conn.Close()
123	bufConn := bufio.NewReader(conn)
124
125	// Read the version byte
126	version := []byte{0}
127	if _, err := bufConn.Read(version); err != nil {
128		s.config.Logger.Printf("[ERR] socks: Failed to get version byte: %v", err)
129		return err
130	}
131
132	// Ensure we are compatible
133	if version[0] != socks5Version {
134		err := fmt.Errorf("Unsupported SOCKS version: %v", version)
135		s.config.Logger.Printf("[ERR] socks: %v", err)
136		return err
137	}
138
139	// Authenticate the connection
140	authContext, err := s.authenticate(conn, bufConn)
141	if err != nil {
142		err = fmt.Errorf("Failed to authenticate: %v", err)
143		s.config.Logger.Printf("[ERR] socks: %v", err)
144		return err
145	}
146
147	request, err := NewRequest(bufConn)
148	if err != nil {
149		if err == unrecognizedAddrType {
150			if err := sendReply(conn, addrTypeNotSupported, nil); err != nil {
151				return fmt.Errorf("Failed to send reply: %v", err)
152			}
153		}
154		return fmt.Errorf("Failed to read destination address: %v", err)
155	}
156	request.AuthContext = authContext
157	if client, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
158		request.RemoteAddr = &AddrSpec{IP: client.IP, Port: client.Port}
159	}
160
161	// Process the client request
162	if err := s.handleRequest(request, conn); err != nil {
163		err = fmt.Errorf("Failed to handle request: %v", err)
164		s.config.Logger.Printf("[ERR] socks: %v", err)
165		return err
166	}
167
168	return nil
169}
170