Skip to content

Commit 3d8523d

Browse files
committed
add local socks server
1 parent 8e98356 commit 3d8523d

File tree

7 files changed

+406
-8
lines changed

7 files changed

+406
-8
lines changed

cmd/agent/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ func main() {
2020
flag.StringVar(&a.AuthKey, "auth", "xxx", "Specify the authentication key")
2121
flag.BoolVar(&a.EnableTLS, "tls", true, "To enable tls between agent and server or not")
2222
flag.BoolVar(&a.EnablePprof, "pprof", false, "To enable pprof or not")
23-
flag.StringVar(&a.CaFile, "ca", "./certs/ca.pem", "Specify the trusted ca file")
24-
flag.StringVar(&a.CertFile, "cert", "./certs/client.pem", "Specify the agent cert file")
25-
flag.StringVar(&a.KeyFile, "key", "./certs/client-key.pem", "Specify the agent key file")
23+
flag.StringVar(&a.CaFile, "ca", "./ca.pem", "Specify the trusted ca file")
24+
flag.StringVar(&a.CertFile, "cert", "./agent.pem", "Specify the agent cert file")
25+
flag.StringVar(&a.KeyFile, "key", "./agent-key.pem", "Specify the agent key file")
2626
flag.StringVar(&a.LocalAddress, "local", ":16116", "Specify the local address")
2727
flag.StringVar(&a.ServerAddress, "server", "127.0.0.1:8443", "Specify the server address")
2828
flag.Parse()

cmd/server/main.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/easzlab/ezvpn/config"
1111
"github.com/easzlab/ezvpn/server"
12+
"github.com/easzlab/ezvpn/socks"
1213
)
1314

1415
func main() {
@@ -17,9 +18,9 @@ func main() {
1718
flag.BoolVar(&s.EnablePprof, "pprof", false, "To enable pprof or not")
1819
flag.StringVar(&s.ControlAddress, "listen", ":8443", "Specify the control address")
1920
flag.StringVar(&s.ConfigFile, "config", "./config/allowed-agents.yml", "Specify the config file")
20-
flag.StringVar(&s.CaFile, "ca", "./certs/ca.pem", "Specify the trusted ca file")
21-
flag.StringVar(&s.CertFile, "cert", "./certs/server.pem", "Specify the server cert file")
22-
flag.StringVar(&s.KeyFile, "key", "./certs/server-key.pem", "Specify the server key file")
21+
flag.StringVar(&s.CaFile, "ca", "./ca.pem", "Specify the trusted ca file")
22+
flag.StringVar(&s.CertFile, "cert", "./server.pem", "Specify the server cert file")
23+
flag.StringVar(&s.KeyFile, "key", "./server-key.pem", "Specify the server key file")
2324
flag.StringVar(&s.SocksServer, "socks5", "0.0.0.0:6116", "Specify the socks server address")
2425
flag.Parse()
2526

@@ -31,6 +32,11 @@ func main() {
3132
go http.ListenAndServe("0.0.0.0:6060", nil)
3233
}
3334

35+
// run socks server
36+
socksServer := socks.Server{ListenAddr: s.SocksServer}
37+
go socksServer.Run()
38+
39+
// run ezvpn server
3440
if err := server.Start(); err != nil {
3541
fmt.Fprintln(os.Stderr, "error:", err)
3642
os.Exit(1)

server/server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ func Start() error {
4242
ClientCAs: certPool,
4343
MinVersion: tls.VersionTLS12,
4444
}
45-
log.Println("https server is running on: " + config.SERVER.ControlAddress)
45+
log.Println("ezvpn server is running on: " + config.SERVER.ControlAddress)
4646
return s.ListenAndServeTLS(config.SERVER.CertFile, config.SERVER.KeyFile)
4747
} else {
48-
log.Println("http server is running on: " + config.SERVER.ControlAddress)
48+
log.Println("ezvpn server is running on: " + config.SERVER.ControlAddress)
4949
return s.ListenAndServe()
5050
}
5151
}

socks/auth.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package socks
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"io"
7+
"log"
8+
"net"
9+
)
10+
11+
func (s *Server) HandleAuth(conn net.Conn) error {
12+
cli := conn.RemoteAddr().String()
13+
14+
// auth-check
15+
reply, err := authReply(conn)
16+
if err != nil {
17+
log.Printf("auth failed from %s, error: %s", cli, err.Error())
18+
return err
19+
}
20+
21+
if n, e := conn.Write([]byte{0x05, reply}); e != nil || n != 2 {
22+
senderr := fmt.Errorf("failed to send method selection reply: %v", e)
23+
log.Printf("auth failed from %s, error: %s", cli, senderr.Error())
24+
return senderr
25+
}
26+
27+
return nil
28+
}
29+
30+
func authReply(conn net.Conn) (uint8, error) {
31+
bufConn := bufio.NewReader(conn)
32+
reply := noAcceptableMethods
33+
34+
// Read the version byte
35+
version := []byte{0}
36+
if _, err := bufConn.Read(version); err != nil {
37+
return reply, fmt.Errorf("failed to get version byte: %v", err)
38+
}
39+
40+
// Ensure socks5 version
41+
if version[0] != socks5Version {
42+
return reply, fmt.Errorf("unsupported socks version: %v", version)
43+
}
44+
45+
// Read the NMETHODS byte
46+
numMethods := []byte{0}
47+
if _, err := bufConn.Read(numMethods); err != nil {
48+
return reply, fmt.Errorf("failed to get nmethods byte: %v", err)
49+
}
50+
nMethods := int(numMethods[0])
51+
52+
// Read the METHODS bytes
53+
methods := make([]byte, nMethods)
54+
if _, err := io.ReadAtLeast(bufConn, methods, nMethods); err != nil {
55+
return reply, fmt.Errorf("failed to get methods bytes: %v", err)
56+
}
57+
58+
// Only Support NoAuth method right now
59+
for _, m := range methods {
60+
if m == noAuthRequired {
61+
reply = noAuthRequired
62+
break
63+
}
64+
}
65+
66+
if reply == noAcceptableMethods {
67+
return reply, fmt.Errorf("no acceptable methods")
68+
}
69+
70+
return reply, nil
71+
}

socks/connect.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package socks
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"log"
8+
"net"
9+
10+
"github.com/easzlab/ezvpn/config"
11+
)
12+
13+
// CMD CONNECT
14+
func (s *Server) handleConnect(ctx context.Context, conn net.Conn, req *Request) error {
15+
cli := conn.RemoteAddr().String()
16+
17+
// Try to connect the destination
18+
target, err := net.DialTimeout("tcp", req.DestAddr.Address(), config.NetDialTimeout)
19+
if err != nil {
20+
SendReply(conn, hostUnreachable, nil)
21+
errcon := fmt.Errorf("connect to target failed: %v", err)
22+
log.Printf("target unreachable, error: %s, client: %s, target: %s", errcon.Error(), cli, req.DestAddr.String())
23+
return errcon
24+
}
25+
defer target.Close()
26+
27+
// Send success
28+
local, ok := target.LocalAddr().(*net.TCPAddr)
29+
if !ok {
30+
msg := fmt.Sprintf("expect *net.TCPAddr, not %t", target.LocalAddr())
31+
log.Printf("unknown type, error: %s, client: %s, target: %s", msg, cli, req.DestAddr.String())
32+
}
33+
bind := AddrSpec{IP: local.IP, Port: local.Port}
34+
if err := SendReply(conn, successReply, &bind); err != nil {
35+
log.Printf("failed to send reply, error: %s, client: %s, target: %s", err.Error(), cli, req.DestAddr.String())
36+
return fmt.Errorf("failed to send reply: %v", err)
37+
}
38+
39+
// Start proxying
40+
errCh := make(chan error, 2)
41+
go proxy(target, conn, errCh)
42+
go proxy(conn, target, errCh)
43+
44+
// Wait
45+
for i := 0; i < 2; i++ {
46+
err := <-errCh
47+
if err != nil {
48+
// return from this function closes target (and conn).
49+
return err
50+
}
51+
}
52+
return nil
53+
}
54+
55+
// proxy is used to suffle data from src to destination, and sends errors
56+
// down a dedicated channel
57+
func proxy(dst net.Conn, src net.Conn, errCh chan error) {
58+
_, err := io.Copy(dst, src)
59+
dst.Close()
60+
errCh <- err
61+
}

socks/request.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package socks
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"log"
7+
"net"
8+
"strconv"
9+
)
10+
11+
// AddrSpec is used to return the target AddrSpec
12+
// which may be specified as IPv4, IPv6, or a FQDN
13+
type AddrSpec struct {
14+
FQDN string
15+
IP net.IP
16+
Port int
17+
}
18+
19+
func (a *AddrSpec) String() string {
20+
if a.FQDN != "" {
21+
return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port)
22+
}
23+
return fmt.Sprintf("%s:%d", a.IP, a.Port)
24+
}
25+
26+
// Address returns a string suitable to dial; prefer returning IP-based
27+
// address, fallback to FQDN
28+
func (a AddrSpec) Address() string {
29+
if len(a.IP) > 0 {
30+
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
31+
}
32+
return net.JoinHostPort(a.FQDN, strconv.Itoa(a.Port))
33+
}
34+
35+
// A Request represents request received by a server
36+
type Request struct {
37+
// Protocol version
38+
Version uint8
39+
// Requested command
40+
Command uint8
41+
// AddrSpec of the the network that sent the request
42+
RemoteAddr *AddrSpec
43+
// AddrSpec of the desired destination
44+
DestAddr *AddrSpec
45+
}
46+
47+
func (r *Request) ParseRequest(conn net.Conn) error {
48+
header := []byte{0, 0, 0}
49+
if _, err := io.ReadAtLeast(conn, header, 3); err != nil {
50+
return fmt.Errorf("read 3 bytes header error:%s", err.Error())
51+
}
52+
53+
if header[0] != 0x05 {
54+
return fmt.Errorf("unsupported proxy version")
55+
}
56+
57+
// Read in the destination address
58+
dest, err := readAddrSpec(conn)
59+
if err != nil {
60+
return err
61+
}
62+
remote, _ := conn.RemoteAddr().(*net.TCPAddr)
63+
64+
r.Version = 0x05
65+
r.Command = header[1]
66+
r.DestAddr = dest
67+
r.RemoteAddr = &AddrSpec{IP: remote.IP, Port: remote.Port}
68+
69+
return nil
70+
}
71+
72+
// readAddrSpec is used to read AddrSpec.
73+
// Expects an address type byte, follwed by the address and port
74+
func readAddrSpec(r io.Reader) (*AddrSpec, error) {
75+
d := &AddrSpec{}
76+
77+
// Get the address type
78+
addrType := []byte{0}
79+
if _, err := r.Read(addrType); err != nil {
80+
return nil, fmt.Errorf("read addr type error:%s", err.Error())
81+
}
82+
83+
// Handle on a per type basis
84+
switch addrType[0] {
85+
case ipv4Address:
86+
addr := make([]byte, 4)
87+
if _, err := io.ReadAtLeast(r, addr, 4); err != nil {
88+
return nil, fmt.Errorf("read ipv4 add error:%s", err.Error())
89+
}
90+
d.IP = net.IP(addr)
91+
92+
case ipv6Address:
93+
addr := make([]byte, 16)
94+
if _, err := io.ReadAtLeast(r, addr, 16); err != nil {
95+
return nil, fmt.Errorf("read ipv6 add error:%s", err.Error())
96+
}
97+
d.IP = net.IP(addr)
98+
99+
case fqdnAddress:
100+
buf := []byte{0}
101+
if _, err := r.Read(buf); err != nil {
102+
return nil, fmt.Errorf("read fqdn len error:%s", err.Error())
103+
}
104+
addrLen := int(buf[0])
105+
fqdn := make([]byte, addrLen)
106+
if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil {
107+
return nil, fmt.Errorf("read fqdn %d bytes error:%s", addrLen, err.Error())
108+
}
109+
d.FQDN = string(fqdn)
110+
111+
default:
112+
return nil, fmt.Errorf("unrecognized address type")
113+
}
114+
115+
// Read the port
116+
port := []byte{0, 0}
117+
if _, err := io.ReadAtLeast(r, port, 2); err != nil {
118+
return nil, fmt.Errorf("read 2 bytes port error:%s", err.Error())
119+
}
120+
d.Port = (int(port[0]) << 8) | int(port[1])
121+
122+
return d, nil
123+
}
124+
125+
// SendReply is used to send a reply message
126+
func SendReply(w io.Writer, rep uint8, addr *AddrSpec) error {
127+
// Format the address
128+
var addrType uint8
129+
var addrBody []byte
130+
var addrPort uint16
131+
switch {
132+
case addr == nil:
133+
addrType = ipv4Address
134+
addrBody = []byte{0, 0, 0, 0}
135+
addrPort = 0
136+
137+
case addr.FQDN != "":
138+
addrType = fqdnAddress
139+
addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...)
140+
addrPort = uint16(addr.Port)
141+
142+
case addr.IP.To4() != nil:
143+
addrType = ipv4Address
144+
addrBody = []byte(addr.IP.To4())
145+
addrPort = uint16(addr.Port)
146+
147+
case addr.IP.To16() != nil:
148+
addrType = ipv6Address
149+
addrBody = []byte(addr.IP.To16())
150+
addrPort = uint16(addr.Port)
151+
152+
default:
153+
err := fmt.Errorf("failed to format address: %v", addr)
154+
log.Printf("failed to send reply, error: %s, client: , target: ", err.Error())
155+
return err
156+
}
157+
158+
// Format the message
159+
reply := make([]byte, 6+len(addrBody))
160+
reply[0] = socks5Version
161+
reply[1] = rep
162+
reply[2] = 0 // Reserved
163+
reply[3] = addrType
164+
copy(reply[4:], addrBody)
165+
reply[4+len(addrBody)] = byte(addrPort >> 8)
166+
reply[4+len(addrBody)+1] = byte(addrPort & 0xff)
167+
168+
// Send the message
169+
_, err := w.Write(reply)
170+
if err != nil {
171+
log.Printf("failed to send reply, error: %s, client: , target: ", err.Error())
172+
}
173+
return err
174+
}

0 commit comments

Comments
 (0)