socks5 client server done
Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
This commit is contained in:
@ -599,7 +599,7 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid
|
|||||||

|

|
||||||
使用本地端口8090,假设上级SOCKS5代理是`22.22.22.22:8080`
|
使用本地端口8090,假设上级SOCKS5代理是`22.22.22.22:8080`
|
||||||
`./proxy socks -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
|
`./proxy socks -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
|
||||||
我们还可以指定网站域名的黑白名单文件,一行一个域名,匹配规则是最右匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理.
|
我们还可以指定网站域名的黑白名单文件,一行一个域名,匹配规则是最右匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理;如果域名即在黑名单又在白名单中,那么黑名单起作用.
|
||||||
`./proxy socks -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
|
`./proxy socks -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
|
||||||
|
|
||||||
#### **5.3.SOCKS二级代理(加密)**
|
#### **5.3.SOCKS二级代理(加密)**
|
||||||
|
|||||||
253
utils/socks/client.go
Normal file
253
utils/socks/client.go
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var socks5Errors = []string{
|
||||||
|
"",
|
||||||
|
"general failure",
|
||||||
|
"connection forbidden",
|
||||||
|
"network unreachable",
|
||||||
|
"host unreachable",
|
||||||
|
"connection refused",
|
||||||
|
"TTL expired",
|
||||||
|
"command not supported",
|
||||||
|
"address type not supported",
|
||||||
|
}
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
User, Password string
|
||||||
|
}
|
||||||
|
type ClientConn struct {
|
||||||
|
user string
|
||||||
|
password string
|
||||||
|
conn *net.Conn
|
||||||
|
header []byte
|
||||||
|
timeout time.Duration
|
||||||
|
addr string
|
||||||
|
network string
|
||||||
|
udpAddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
||||||
|
// with an optional username and password. See RFC 1928 and RFC 1929.
|
||||||
|
// target must be a canonical address with a host and port.
|
||||||
|
// network : tcp udp
|
||||||
|
func NewClientConn(conn *net.Conn, network, target string, timeout time.Duration, auth *Auth, header []byte) *ClientConn {
|
||||||
|
s := &ClientConn{
|
||||||
|
conn: conn,
|
||||||
|
network: network,
|
||||||
|
timeout: timeout,
|
||||||
|
}
|
||||||
|
if auth != nil {
|
||||||
|
s.user = auth.User
|
||||||
|
s.password = auth.Password
|
||||||
|
}
|
||||||
|
if header != nil && len(header) > 0 {
|
||||||
|
s.header = header
|
||||||
|
}
|
||||||
|
if network == "udp" && target == "" {
|
||||||
|
target = "0.0.0.0:1"
|
||||||
|
}
|
||||||
|
s.addr = target
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect takes an existing connection to a socks5 proxy server,
|
||||||
|
// and commands the server to extend that connection to target,
|
||||||
|
// which must be a canonical address with a host and port.
|
||||||
|
func (s *ClientConn) Connect() error {
|
||||||
|
host, portStr, err := net.SplitHostPort(s.addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
port, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||||
|
}
|
||||||
|
if port < 1 || port > 0xffff {
|
||||||
|
return errors.New("proxy: port number out of range: " + portStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.handshake(host); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf := []byte{}
|
||||||
|
if s.network == "tcp" {
|
||||||
|
buf = append(buf, VERSION_V5, CMD_CONNECT, 0 /* reserved */)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
buf = append(buf, VERSION_V5, CMD_ASSOCIATE, 0 /* reserved */)
|
||||||
|
}
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
buf = append(buf, ATYP_IPV4)
|
||||||
|
ip = ip4
|
||||||
|
} else {
|
||||||
|
buf = append(buf, ATYP_IPV6)
|
||||||
|
}
|
||||||
|
buf = append(buf, ip...)
|
||||||
|
} else {
|
||||||
|
if len(host) > 255 {
|
||||||
|
return errors.New("proxy: destination host name too long: " + host)
|
||||||
|
}
|
||||||
|
buf = append(buf, ATYP_DOMAIN)
|
||||||
|
buf = append(buf, byte(len(host)))
|
||||||
|
buf = append(buf, host...)
|
||||||
|
}
|
||||||
|
buf = append(buf, byte(port>>8), byte(port))
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := (*s.conn).Write(buf); err != nil {
|
||||||
|
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := io.ReadFull((*s.conn), buf[:4]); err != nil {
|
||||||
|
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
failure := "unknown error"
|
||||||
|
if int(buf[1]) < len(socks5Errors) {
|
||||||
|
failure = socks5Errors[buf[1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(failure) > 0 {
|
||||||
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesToDiscard := 0
|
||||||
|
switch buf[3] {
|
||||||
|
case ATYP_IPV4:
|
||||||
|
bytesToDiscard = net.IPv4len
|
||||||
|
case ATYP_IPV6:
|
||||||
|
bytesToDiscard = net.IPv6len
|
||||||
|
case ATYP_DOMAIN:
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
_, err := io.ReadFull((*s.conn), buf[:1])
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
bytesToDiscard = int(buf[0])
|
||||||
|
default:
|
||||||
|
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(buf) < bytesToDiscard {
|
||||||
|
buf = make([]byte, bytesToDiscard)
|
||||||
|
} else {
|
||||||
|
buf = buf[:bytesToDiscard]
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := io.ReadFull((*s.conn), buf); err != nil {
|
||||||
|
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
var ip net.IP
|
||||||
|
ip = buf
|
||||||
|
ipStr := ""
|
||||||
|
if bytesToDiscard == net.IPv4len || bytesToDiscard == net.IPv6len {
|
||||||
|
if ipv4 := ip.To4(); ipv4 != nil {
|
||||||
|
ipStr = ipv4.String()
|
||||||
|
} else {
|
||||||
|
ipStr = ip.To16().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//log.Printf("%v", ipStr)
|
||||||
|
// Also need to discard the port number
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
|
||||||
|
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
p := binary.BigEndian.Uint16([]byte{buf[0], buf[1]})
|
||||||
|
//log.Printf("%v", p)
|
||||||
|
s.udpAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p))
|
||||||
|
//log.Printf("%v", s.udpAddr)
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (s *ClientConn) SendUDP(data []byte, addr string) (respData []byte, err error) {
|
||||||
|
|
||||||
|
c, err := net.DialTimeout("udp", s.udpAddr, s.timeout)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn := c.(*net.UDPConn)
|
||||||
|
|
||||||
|
p := NewPacketUDP()
|
||||||
|
p.Build(addr, data)
|
||||||
|
conn.SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
conn.Write(p.Bytes())
|
||||||
|
conn.SetDeadline(time.Time{})
|
||||||
|
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
conn.SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
n, _, err := conn.ReadFrom(buf)
|
||||||
|
conn.SetDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
respData = buf[:n]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *ClientConn) handshake(host string) error {
|
||||||
|
|
||||||
|
// the size here is just an estimate
|
||||||
|
buf := make([]byte, 0, 6+len(host))
|
||||||
|
|
||||||
|
buf = append(buf, VERSION_V5)
|
||||||
|
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||||
|
buf = append(buf, 2 /* num auth methods */, Method_NO_AUTH, Method_USER_PASS)
|
||||||
|
} else {
|
||||||
|
buf = append(buf, 1 /* num auth methods */, Method_NO_AUTH)
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := (*s.conn).Write(buf); err != nil {
|
||||||
|
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
|
||||||
|
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
|
||||||
|
if buf[0] != 5 {
|
||||||
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||||
|
}
|
||||||
|
if buf[1] == 0xff {
|
||||||
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||||
|
}
|
||||||
|
|
||||||
|
// See RFC 1929
|
||||||
|
if buf[1] == Method_USER_PASS {
|
||||||
|
buf = buf[:0]
|
||||||
|
buf = append(buf, 1 /* password protocol version */)
|
||||||
|
buf = append(buf, uint8(len(s.user)))
|
||||||
|
buf = append(buf, s.user...)
|
||||||
|
buf = append(buf, uint8(len(s.password)))
|
||||||
|
buf = append(buf, s.password...)
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := (*s.conn).Write(buf); err != nil {
|
||||||
|
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(s.timeout))
|
||||||
|
if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil {
|
||||||
|
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
|
}
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
if buf[1] != 0 {
|
||||||
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
225
utils/socks/server.go
Normal file
225
utils/socks/server.go
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"snail007/proxy/utils"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Method_NO_AUTH = uint8(0x00)
|
||||||
|
Method_GSSAPI = uint8(0x01)
|
||||||
|
Method_USER_PASS = uint8(0x02)
|
||||||
|
Method_IANA = uint8(0x7F)
|
||||||
|
Method_RESVERVE = uint8(0x80)
|
||||||
|
Method_NONE_ACCEPTABLE = uint8(0xFF)
|
||||||
|
VERSION_V5 = uint8(0x05)
|
||||||
|
CMD_CONNECT = uint8(0x01)
|
||||||
|
CMD_BIND = uint8(0x02)
|
||||||
|
CMD_ASSOCIATE = uint8(0x03)
|
||||||
|
ATYP_IPV4 = uint8(0x01)
|
||||||
|
ATYP_DOMAIN = uint8(0x03)
|
||||||
|
ATYP_IPV6 = uint8(0x04)
|
||||||
|
REP_SUCCESS = uint8(0x00)
|
||||||
|
REP_REQ_FAIL = uint8(0x01)
|
||||||
|
REP_RULE_FORBIDDEN = uint8(0x02)
|
||||||
|
REP_NETWOR_UNREACHABLE = uint8(0x03)
|
||||||
|
REP_HOST_UNREACHABLE = uint8(0x04)
|
||||||
|
REP_CONNECTION_REFUSED = uint8(0x05)
|
||||||
|
REP_TTL_TIMEOUT = uint8(0x06)
|
||||||
|
REP_CMD_UNSUPPORTED = uint8(0x07)
|
||||||
|
REP_ATYP_UNSUPPORTED = uint8(0x08)
|
||||||
|
REP_UNKNOWN = uint8(0x09)
|
||||||
|
RSV = uint8(0x00)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ZERO_IP = []byte{0x00, 0x00, 0x00, 0x00}
|
||||||
|
ZERO_PORT = []byte{0x00, 0x00}
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerConn struct {
|
||||||
|
target string
|
||||||
|
user string
|
||||||
|
password string
|
||||||
|
conn *net.Conn
|
||||||
|
timeout time.Duration
|
||||||
|
auth *utils.BasicAuth
|
||||||
|
header []byte
|
||||||
|
ver uint8
|
||||||
|
//method
|
||||||
|
methodsCount uint8
|
||||||
|
methods []uint8
|
||||||
|
method uint8
|
||||||
|
//request
|
||||||
|
cmd uint8
|
||||||
|
reserve uint8
|
||||||
|
addressType uint8
|
||||||
|
dstAddr string
|
||||||
|
dstPort string
|
||||||
|
dstHost string
|
||||||
|
udpAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerConn(conn *net.Conn, timeout time.Duration, auth *utils.BasicAuth, udpAddress string, header []byte) *ServerConn {
|
||||||
|
if udpAddress == "" {
|
||||||
|
udpAddress = "0.0.0.0:16666"
|
||||||
|
}
|
||||||
|
s := &ServerConn{
|
||||||
|
conn: conn,
|
||||||
|
timeout: timeout,
|
||||||
|
auth: auth,
|
||||||
|
header: header,
|
||||||
|
ver: VERSION_V5,
|
||||||
|
udpAddress: udpAddress,
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
|
||||||
|
}
|
||||||
|
func (s *ServerConn) Close() {
|
||||||
|
utils.CloseConn(s.conn)
|
||||||
|
}
|
||||||
|
func (s *ServerConn) AuthData() Auth {
|
||||||
|
return Auth{s.user, s.password}
|
||||||
|
}
|
||||||
|
func (s *ServerConn) Method() uint8 {
|
||||||
|
return s.method
|
||||||
|
}
|
||||||
|
func (s *ServerConn) Target() string {
|
||||||
|
return s.target
|
||||||
|
}
|
||||||
|
func (s *ServerConn) Handshake() (err error) {
|
||||||
|
remoteAddr := (*s.conn).RemoteAddr()
|
||||||
|
//协商开始
|
||||||
|
//method select request
|
||||||
|
var methodReq MethodsRequest
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
|
||||||
|
methodReq, e := NewMethodsRequest((*s.conn), s.header)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
if e != nil {
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
methodReq.Reply(Method_NONE_ACCEPTABLE)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
err = fmt.Errorf("new methods request fail,ERR: %s", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.auth == nil {
|
||||||
|
if !methodReq.Select(Method_NO_AUTH) {
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
methodReq.Reply(Method_NONE_ACCEPTABLE)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
err = fmt.Errorf("none method found : Method_NO_AUTH")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.method = Method_NO_AUTH
|
||||||
|
//method select reply
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
err = methodReq.Reply(Method_NO_AUTH)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("reply answer data fail,ERR: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// err = fmt.Errorf("% x", methodReq.Bytes())
|
||||||
|
} else {
|
||||||
|
//auth
|
||||||
|
if !methodReq.Select(Method_USER_PASS) {
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
methodReq.Reply(Method_NONE_ACCEPTABLE)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
err = fmt.Errorf("none method found : Method_USER_PASS")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.method = Method_USER_PASS
|
||||||
|
//method reply need auth
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
err = methodReq.Reply(Method_USER_PASS)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("reply answer data fail,ERR: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//read auth
|
||||||
|
buf := make([]byte, 500)
|
||||||
|
var n int
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
n, err = (*s.conn).Read(buf)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("read auth info fail,ERR: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r := buf[:n]
|
||||||
|
s.user = string(r[2 : r[1]+2])
|
||||||
|
s.password = string(r[2+r[1]+1:])
|
||||||
|
//err = fmt.Errorf("user:%s,pass:%s", user, pass)
|
||||||
|
//auth
|
||||||
|
_addr := strings.Split(remoteAddr.String(), ":")
|
||||||
|
if s.auth.CheckUserPass(s.user, s.password, _addr[0], "") {
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
|
||||||
|
_, err = (*s.conn).Write([]byte{0x01, 0x00})
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("answer auth success to %s fail,ERR: %s", remoteAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout)))
|
||||||
|
_, err = (*s.conn).Write([]byte{0x01, 0x01})
|
||||||
|
(*s.conn).SetDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("answer auth fail to %s fail,ERR: %s", remoteAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("auth fail from %s", remoteAddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//request detail
|
||||||
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
||||||
|
request, e := NewRequest(*s.conn)
|
||||||
|
(*s.conn).SetReadDeadline(time.Time{})
|
||||||
|
if e != nil {
|
||||||
|
err = fmt.Errorf("read request data fail,ERR: %s", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//协商结束
|
||||||
|
|
||||||
|
switch request.CMD() {
|
||||||
|
case CMD_BIND:
|
||||||
|
err = request.TCPReply(REP_UNKNOWN)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("TCPReply REP_UNKNOWN to %s fail,ERR: %s", remoteAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("cmd bind not supported, form: %s", remoteAddr)
|
||||||
|
return
|
||||||
|
case CMD_CONNECT:
|
||||||
|
err = request.TCPReply(REP_SUCCESS)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("TCPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case CMD_ASSOCIATE:
|
||||||
|
err = request.UDPReply(REP_SUCCESS, s.udpAddress)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("UDPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fill socks info
|
||||||
|
s.target = request.Addr()
|
||||||
|
s.methodsCount = methodReq.MethodsCount()
|
||||||
|
s.methods = methodReq.Methods()
|
||||||
|
s.cmd = request.CMD()
|
||||||
|
s.reserve = request.reserve
|
||||||
|
s.addressType = request.addressType
|
||||||
|
s.dstAddr = request.dstAddr
|
||||||
|
s.dstHost = request.dstHost
|
||||||
|
s.dstPort = request.dstPort
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -3,44 +3,13 @@ package socks
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
Method_NO_AUTH = uint8(0x00)
|
|
||||||
Method_GSSAPI = uint8(0x01)
|
|
||||||
Method_USER_PASS = uint8(0x02)
|
|
||||||
Method_IANA = uint8(0x7F)
|
|
||||||
Method_RESVERVE = uint8(0x80)
|
|
||||||
Method_NONE_ACCEPTABLE = uint8(0xFF)
|
|
||||||
VERSION_V5 = uint8(0x05)
|
|
||||||
CMD_CONNECT = uint8(0x01)
|
|
||||||
CMD_BIND = uint8(0x02)
|
|
||||||
CMD_ASSOCIATE = uint8(0x03)
|
|
||||||
ATYP_IPV4 = uint8(0x01)
|
|
||||||
ATYP_DOMAIN = uint8(0x03)
|
|
||||||
ATYP_IPV6 = uint8(0x04)
|
|
||||||
REP_SUCCESS = uint8(0x00)
|
|
||||||
REP_REQ_FAIL = uint8(0x01)
|
|
||||||
REP_RULE_FORBIDDEN = uint8(0x02)
|
|
||||||
REP_NETWOR_UNREACHABLE = uint8(0x03)
|
|
||||||
REP_HOST_UNREACHABLE = uint8(0x04)
|
|
||||||
REP_CONNECTION_REFUSED = uint8(0x05)
|
|
||||||
REP_TTL_TIMEOUT = uint8(0x06)
|
|
||||||
REP_CMD_UNSUPPORTED = uint8(0x07)
|
|
||||||
REP_ATYP_UNSUPPORTED = uint8(0x08)
|
|
||||||
REP_UNKNOWN = uint8(0x09)
|
|
||||||
RSV = uint8(0x00)
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ZERO_IP = []byte{0x00, 0x00, 0x00, 0x00}
|
|
||||||
ZERO_PORT = []byte{0x00, 0x00}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
ver uint8
|
ver uint8
|
||||||
cmd uint8
|
cmd uint8
|
||||||
@ -57,7 +26,7 @@ func NewRequest(rw io.ReadWriter, header ...[]byte) (req Request, err interface{
|
|||||||
var b = make([]byte, 1024)
|
var b = make([]byte, 1024)
|
||||||
var n int
|
var n int
|
||||||
req = Request{rw: rw}
|
req = Request{rw: rw}
|
||||||
if len(header) == 1 {
|
if header != nil && len(header) == 1 && len(header[0]) > 1 {
|
||||||
b = header[0]
|
b = header[0]
|
||||||
n = len(header[0])
|
n = len(header[0])
|
||||||
} else {
|
} else {
|
||||||
@ -71,7 +40,6 @@ func NewRequest(rw io.ReadWriter, header ...[]byte) (req Request, err interface{
|
|||||||
req.cmd = uint8(b[1])
|
req.cmd = uint8(b[1])
|
||||||
req.reserve = uint8(b[2])
|
req.reserve = uint8(b[2])
|
||||||
req.addressType = uint8(b[3])
|
req.addressType = uint8(b[3])
|
||||||
|
|
||||||
if b[0] != 0x5 {
|
if b[0] != 0x5 {
|
||||||
err = fmt.Errorf("sosck version supported")
|
err = fmt.Errorf("sosck version supported")
|
||||||
req.TCPReply(REP_REQ_FAIL)
|
req.TCPReply(REP_REQ_FAIL)
|
||||||
@ -129,7 +97,7 @@ func (s *Request) NewReply(rep uint8, addr string) []byte {
|
|||||||
ipv6[4], ipv6[5], ipv6[6], ipv6[7],
|
ipv6[4], ipv6[5], ipv6[6], ipv6[7],
|
||||||
ipv6[8], ipv6[9], ipv6[10], ipv6[11],
|
ipv6[8], ipv6[9], ipv6[10], ipv6[11],
|
||||||
)
|
)
|
||||||
if ipv6 != nil && "0000000000255255" != zeroiIPv6 {
|
if ipb == nil && ipv6 != nil && "0000000000255255" != zeroiIPv6 {
|
||||||
atyp = ATYP_IPV6
|
atyp = ATYP_IPV6
|
||||||
ipb = ip.To16()
|
ipb = ip.To16()
|
||||||
}
|
}
|
||||||
@ -165,7 +133,7 @@ func NewMethodsRequest(r io.ReadWriter, header ...[]byte) (s MethodsRequest, err
|
|||||||
s.rw = &r
|
s.rw = &r
|
||||||
var buf = make([]byte, 300)
|
var buf = make([]byte, 300)
|
||||||
var n int
|
var n int
|
||||||
if len(header) == 1 {
|
if header != nil && len(header) == 1 && len(header[0]) > 1 {
|
||||||
buf = header[0]
|
buf = header[0]
|
||||||
n = len(header[0])
|
n = len(header[0])
|
||||||
} else {
|
} else {
|
||||||
@ -182,7 +150,6 @@ func NewMethodsRequest(r io.ReadWriter, header ...[]byte) (s MethodsRequest, err
|
|||||||
err = fmt.Errorf("socks methods data length error")
|
err = fmt.Errorf("socks methods data length error")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ver = buf[0]
|
s.ver = buf[0]
|
||||||
s.methodsCount = buf[1]
|
s.methodsCount = buf[1]
|
||||||
s.methods = buf[2:n]
|
s.methods = buf[2:n]
|
||||||
@ -195,6 +162,9 @@ func (s *MethodsRequest) Version() uint8 {
|
|||||||
func (s *MethodsRequest) MethodsCount() uint8 {
|
func (s *MethodsRequest) MethodsCount() uint8 {
|
||||||
return s.methodsCount
|
return s.methodsCount
|
||||||
}
|
}
|
||||||
|
func (s *MethodsRequest) Methods() []uint8 {
|
||||||
|
return s.methods
|
||||||
|
}
|
||||||
func (s *MethodsRequest) Select(method uint8) bool {
|
func (s *MethodsRequest) Select(method uint8) bool {
|
||||||
for _, m := range s.methods {
|
for _, m := range s.methods {
|
||||||
if m == method {
|
if m == method {
|
||||||
@ -211,17 +181,6 @@ func (s *MethodsRequest) Bytes() []byte {
|
|||||||
return s.bytes
|
return s.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
type UDPPacket struct {
|
|
||||||
rsv uint16
|
|
||||||
frag uint8
|
|
||||||
atype uint8
|
|
||||||
dstHost string
|
|
||||||
dstPort string
|
|
||||||
data []byte
|
|
||||||
header []byte
|
|
||||||
bytes []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseUDPPacket(b []byte) (p UDPPacket, err error) {
|
func ParseUDPPacket(b []byte) (p UDPPacket, err error) {
|
||||||
p = UDPPacket{}
|
p = UDPPacket{}
|
||||||
p.frag = uint8(b[2])
|
p.frag = uint8(b[2])
|
||||||
@ -249,6 +208,18 @@ func ParseUDPPacket(b []byte) (p UDPPacket, err error) {
|
|||||||
p.header = b[:portIndex+2]
|
p.header = b[:portIndex+2]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UDPPacket struct {
|
||||||
|
rsv uint16
|
||||||
|
frag uint8
|
||||||
|
atype uint8
|
||||||
|
dstHost string
|
||||||
|
dstPort string
|
||||||
|
data []byte
|
||||||
|
header []byte
|
||||||
|
bytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
func (s *UDPPacket) Header() []byte {
|
func (s *UDPPacket) Header() []byte {
|
||||||
return s.header
|
return s.header
|
||||||
}
|
}
|
||||||
@ -268,3 +239,104 @@ func (s *UDPPacket) Port() string {
|
|||||||
func (s *UDPPacket) Data() []byte {
|
func (s *UDPPacket) Data() []byte {
|
||||||
return s.data
|
return s.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketUDP struct {
|
||||||
|
rsv uint16
|
||||||
|
frag uint8
|
||||||
|
atype uint8
|
||||||
|
dstHost string
|
||||||
|
dstPort string
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPacketUDP() (p PacketUDP) {
|
||||||
|
return PacketUDP{}
|
||||||
|
}
|
||||||
|
func (p *PacketUDP) Build(destAddr string, data []byte) (err error) {
|
||||||
|
host, port, err := net.SplitHostPort(destAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.rsv = 0
|
||||||
|
p.frag = 0
|
||||||
|
p.dstHost = host
|
||||||
|
p.dstPort = port
|
||||||
|
p.atype = ATYP_IPV4
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
p.atype = ATYP_IPV4
|
||||||
|
ip = ip4
|
||||||
|
} else {
|
||||||
|
p.atype = ATYP_IPV6
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(host) > 255 {
|
||||||
|
err = errors.New("proxy: destination host name too long: " + host)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.atype = ATYP_DOMAIN
|
||||||
|
}
|
||||||
|
p.data = data
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (p *PacketUDP) Parse(b []byte) (err error) {
|
||||||
|
p.frag = uint8(b[2])
|
||||||
|
if p.frag != 0 {
|
||||||
|
err = fmt.Errorf("FRAG only support for 0 , %v ,%v", p.frag, b[:4])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
portIndex := 0
|
||||||
|
p.atype = b[3]
|
||||||
|
switch p.atype {
|
||||||
|
case ATYP_IPV4: //IP V4
|
||||||
|
p.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String()
|
||||||
|
portIndex = 8
|
||||||
|
case ATYP_DOMAIN: //域名
|
||||||
|
domainLen := uint8(b[4])
|
||||||
|
p.dstHost = string(b[5 : 5+domainLen]) //b[4]表示域名的长度
|
||||||
|
portIndex = int(5 + domainLen)
|
||||||
|
case ATYP_IPV6: //IP V6
|
||||||
|
p.dstHost = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String()
|
||||||
|
portIndex = 20
|
||||||
|
}
|
||||||
|
p.dstPort = strconv.Itoa(int(b[portIndex])<<8 | int(b[portIndex+1]))
|
||||||
|
p.data = b[portIndex+2:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (p *PacketUDP) Header() []byte {
|
||||||
|
header := new(bytes.Buffer)
|
||||||
|
header.Write([]byte{0x00, 0x00, p.frag, p.atype})
|
||||||
|
if p.atype == ATYP_IPV4 {
|
||||||
|
ip := net.ParseIP(p.dstHost)
|
||||||
|
header.Write(ip.To4())
|
||||||
|
} else if p.atype == ATYP_IPV6 {
|
||||||
|
ip := net.ParseIP(p.dstHost)
|
||||||
|
header.Write(ip.To16())
|
||||||
|
} else if p.atype == ATYP_DOMAIN {
|
||||||
|
hBytes := []byte(p.dstHost)
|
||||||
|
header.WriteByte(byte(len(hBytes)))
|
||||||
|
header.Write(hBytes)
|
||||||
|
}
|
||||||
|
port, _ := strconv.ParseUint(p.dstPort, 10, 64)
|
||||||
|
portBytes := new(bytes.Buffer)
|
||||||
|
binary.Write(portBytes, binary.BigEndian, port)
|
||||||
|
header.Write(portBytes.Bytes()[portBytes.Len()-2:])
|
||||||
|
return header.Bytes()
|
||||||
|
}
|
||||||
|
func (p *PacketUDP) Bytes() []byte {
|
||||||
|
packBytes := new(bytes.Buffer)
|
||||||
|
packBytes.Write(p.Header())
|
||||||
|
packBytes.Write(p.data)
|
||||||
|
return packBytes.Bytes()
|
||||||
|
}
|
||||||
|
func (p *PacketUDP) Host() string {
|
||||||
|
return p.dstHost
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PacketUDP) Port() string {
|
||||||
|
return p.dstPort
|
||||||
|
}
|
||||||
|
func (p *PacketUDP) Data() []byte {
|
||||||
|
return p.data
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user