You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
4.7 KiB
Go

9 years ago
package socks
import (
"errors"
9 years ago
"fmt"
"io"
//"log"
9 years ago
"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
9 years ago
type socks5Conn struct {
//addr string
8 years ago
clientConn net.Conn
serverConn net.Conn
7 years ago
dial DialFunc
auth AuthService
9 years ago
}
func (s5 *socks5Conn) Serve(b []byte, n int) (err error) {
9 years ago
defer s5.Close()
if err = s5.handshake(b, n); err != nil {
//log.Println(err)
9 years ago
return
}
if err = s5.processRequest(); err != nil {
//log.Println(err)
9 years ago
return
}
return
9 years ago
}
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
9 years ago
}
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:])
9 years ago
if err != nil {
return err
}
n += n1
9 years ago
}
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})
9 years ago
return errors.New("access denied")
}
return errors.New("no auth method")
9 years ago
}
func (s5 *socks5Conn) processRequest() error {
buf := make([]byte, 258)
// read header
8 years ago
n, err := io.ReadAtLeast(s5.clientConn, buf, 10)
9 years ago
if err != nil {
return err
}
9 years ago
if buf[0] != socks5Version {
return fmt.Errorf("error version %d", buf[0])
9 years ago
}
// command only support connect
9 years ago
if buf[1] != cmdConnect {
8 years ago
return fmt.Errorf("unsupported command %d", buf[1])
9 years ago
}
hlen := 0 // target address length
host := "" // target address
msglen := 0 // header length
9 years ago
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
8 years ago
_, err := io.ReadFull(s5.clientConn, buf[n:msglen])
9 years ago
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
9 years ago
port := binary.BigEndian.Uint16(buf[msglen-2 : msglen])
// target address
9 years ago
target := net.JoinHostPort(host, fmt.Sprintf("%d", port))
//log.Printf("connecing to %s\r\n", target)
9 years ago
// connect to the target
8 years ago
s5.serverConn, err = s5.dial("tcp", target)
9 years ago
if err != nil {
// connection failed
s5.clientConn.Write([]byte{0x05, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01})
9 years ago
return err
}
// connection success
s5.clientConn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01})
9 years ago
// enter data exchange
forward(s5.clientConn, s5.serverConn)
9 years ago
return nil
}
func (s5 *socks5Conn) Close() {
8 years ago
if s5.serverConn != nil {
s5.serverConn.Close()
9 years ago
}
8 years ago
if s5.clientConn != nil {
s5.clientConn.Close()
9 years ago
}
}