|
|
|
package socks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
//"strconv"
|
|
|
|
"encoding/binary"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
socks5 protocol
|
|
|
|
|
|
|
|
initial
|
|
|
|
|
|
|
|
byte | 0 | 1 | 2 | ...... | n |
|
|
|
|
|0x05|num auth| auth methods |
|
|
|
|
|
|
|
|
|
|
|
|
reply
|
|
|
|
|
|
|
|
byte | 0 | 1 |
|
|
|
|
|0x05| auth|
|
|
|
|
|
|
|
|
|
|
|
|
username/password auth request
|
|
|
|
|
|
|
|
byte | 0 | 1 | | 1 byte | |
|
|
|
|
|0x01|username_len| username | password_len | password |
|
|
|
|
|
|
|
|
username/password auth reponse
|
|
|
|
|
|
|
|
byte | 0 | 1 |
|
|
|
|
|0x01|status|
|
|
|
|
|
|
|
|
request
|
|
|
|
|
|
|
|
byte | 0 | 1 | 2 | 3 | 4 | .. | n-2 | n-1| n |
|
|
|
|
|0x05|cmd|0x00|addrtype| addr | port |
|
|
|
|
|
|
|
|
response
|
|
|
|
byte |0 | 1 | 2 | 3 | 4 | .. | n-2 | n-1 | n |
|
|
|
|
|0x05|status|0x00|addrtype| addr | port |
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Socks5AuthRequired means socks5 server need auth or not
|
|
|
|
|
|
|
|
type socks5Conn struct {
|
|
|
|
//addr string
|
|
|
|
clientConn net.Conn
|
|
|
|
serverConn net.Conn
|
|
|
|
dial DialFunc
|
|
|
|
auth AuthService
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s5 *socks5Conn) Serve(b []byte, n int) {
|
|
|
|
defer s5.Close()
|
|
|
|
|
|
|
|
if err := s5.handshake(b, n); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s5.processRequest(); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s5 *socks5Conn) handshake(buf []byte, n int) (err error) {
|
|
|
|
|
|
|
|
// read auth methods
|
|
|
|
if n < 2 {
|
|
|
|
n1, err := io.ReadAtLeast(s5.clientConn, buf[1:], 1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
n += n1
|
|
|
|
}
|
|
|
|
|
|
|
|
l := int(buf[1])
|
|
|
|
if n != (l + 2) {
|
|
|
|
// read remains data
|
|
|
|
n1, err := io.ReadFull(s5.clientConn, buf[n:l+2+1])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
n += n1
|
|
|
|
}
|
|
|
|
|
|
|
|
if s5.auth == nil {
|
|
|
|
// no auth required
|
|
|
|
s5.clientConn.Write([]byte{0x05, 0x00})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
hasPassAuth := false
|
|
|
|
var passAuth byte = 0x02
|
|
|
|
|
|
|
|
// check auth method
|
|
|
|
// only password(0x02) supported
|
|
|
|
for i := 2; i < n; i++ {
|
|
|
|
if buf[i] == passAuth {
|
|
|
|
hasPassAuth = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !hasPassAuth {
|
|
|
|
s5.clientConn.Write([]byte{0x05, 0xff})
|
|
|
|
return errors.New("no supported auth method")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = s5.passwordAuth()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s5 *socks5Conn) passwordAuth() error {
|
|
|
|
buf := make([]byte, 32)
|
|
|
|
|
|
|
|
// username/password required
|
|
|
|
s5.clientConn.Write([]byte{0x05, 0x02})
|
|
|
|
n, err := io.ReadAtLeast(s5.clientConn, buf, 2)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
//log.Printf("%+v", buf[:n])
|
|
|
|
|
|
|
|
// check auth version
|
|
|
|
if buf[0] != 0x01 {
|
|
|
|
return errors.New("unsupported auth version")
|
|
|
|
}
|
|
|
|
|
|
|
|
usernameLen := int(buf[1])
|
|
|
|
|
|
|
|
p0 := 2
|
|
|
|
p1 := p0 + usernameLen
|
|
|
|
|
|
|
|
if n < p1 {
|
|
|
|
n1, err := s5.clientConn.Read(buf[n:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
n += n1
|
|
|
|
}
|
|
|
|
|
|
|
|
username := buf[p0:p1]
|
|
|
|
passwordLen := int(buf[p1])
|
|
|
|
|
|
|
|
p3 := p1 + 1
|
|
|
|
p4 := p3 + passwordLen
|
|
|
|
|
|
|
|
if n < p4 {
|
|
|
|
n1, err := s5.clientConn.Read(buf[n:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
n += n1
|
|
|
|
}
|
|
|
|
|
|
|
|
password := buf[p3:p4]
|
|
|
|
|
|
|
|
// log.Printf("get username: %s, password: %s", username, password)
|
|
|
|
|
|
|
|
if s5.auth != nil {
|
|
|
|
ret := s5.auth.Authenticate(
|
|
|
|
string(username), string(password),
|
|
|
|
s5.clientConn.RemoteAddr())
|
|
|
|
if ret {
|
|
|
|
s5.clientConn.Write([]byte{0x01, 0x00})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s5.clientConn.Write([]byte{0x01, 0x01})
|
|
|
|
|
|
|
|
return errors.New("access denied")
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.New("no auth method")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s5 *socks5Conn) processRequest() error {
|
|
|
|
buf := make([]byte, 258)
|
|
|
|
|
|
|
|
// read header
|
|
|
|
n, err := io.ReadAtLeast(s5.clientConn, buf, 10)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if buf[0] != socks5Version {
|
|
|
|
return fmt.Errorf("error version %d", buf[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
// command only support connect
|
|
|
|
if buf[1] != cmdConnect {
|
|
|
|
return fmt.Errorf("unsupported command %d", buf[1])
|
|
|
|
}
|
|
|
|
|
|
|
|
hlen := 0 // target address length
|
|
|
|
host := "" // target address
|
|
|
|
msglen := 0 // header length
|
|
|
|
|
|
|
|
switch buf[3] {
|
|
|
|
case addrTypeIPv4:
|
|
|
|
hlen = 4
|
|
|
|
case addrTypeDomain:
|
|
|
|
hlen = int(buf[4]) + 1
|
|
|
|
case addrTypeIPv6:
|
|
|
|
hlen = 16
|
|
|
|
}
|
|
|
|
|
|
|
|
msglen = 6 + hlen
|
|
|
|
|
|
|
|
if n < msglen {
|
|
|
|
// read remains header
|
|
|
|
_, err := io.ReadFull(s5.clientConn, buf[n:msglen])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get target address
|
|
|
|
addr := buf[4 : 4+hlen]
|
|
|
|
if buf[3] == addrTypeDomain {
|
|
|
|
host = string(addr[1:])
|
|
|
|
} else {
|
|
|
|
host = net.IP(addr).String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// get target port
|
|
|
|
port := binary.BigEndian.Uint16(buf[msglen-2 : msglen])
|
|
|
|
|
|
|
|
// target address
|
|
|
|
target := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
|
|
|
|
|
|
|
//log.Printf("connecing to %s\r\n", target)
|
|
|
|
|
|
|
|
// connect to the target
|
|
|
|
s5.serverConn, err = s5.dial("tcp", target)
|
|
|
|
if err != nil {
|
|
|
|
// connection failed
|
|
|
|
s5.clientConn.Write([]byte{0x05, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// connection success
|
|
|
|
s5.clientConn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01})
|
|
|
|
|
|
|
|
// enter data exchange
|
|
|
|
forward(s5.clientConn, s5.serverConn)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s5 *socks5Conn) Close() {
|
|
|
|
if s5.serverConn != nil {
|
|
|
|
s5.serverConn.Close()
|
|
|
|
}
|
|
|
|
if s5.clientConn != nil {
|
|
|
|
s5.clientConn.Close()
|
|
|
|
}
|
|
|
|
}
|