374 lines
9.7 KiB
Go
374 lines
9.7 KiB
Go
package socks5
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
socks5c "github.com/snail007/goproxy/core/lib/socks5"
|
|
)
|
|
|
|
type BasicAuther interface {
|
|
CheckUserPass(username, password, fromIP, ToTarget string) bool
|
|
}
|
|
type Request struct {
|
|
ver uint8
|
|
cmd uint8
|
|
reserve uint8
|
|
addressType uint8
|
|
dstAddr string
|
|
dstPort string
|
|
dstHost string
|
|
bytes []byte
|
|
rw io.ReadWriter
|
|
}
|
|
|
|
func NewRequest(rw io.ReadWriter, header ...[]byte) (req Request, err interface{}) {
|
|
var b = make([]byte, 1024)
|
|
var n int
|
|
req = Request{rw: rw}
|
|
if header != nil && len(header) == 1 && len(header[0]) > 1 {
|
|
b = header[0]
|
|
n = len(header[0])
|
|
} else {
|
|
n, err = rw.Read(b[:])
|
|
if err != nil {
|
|
err = fmt.Errorf("read req data fail,ERR: %s", err)
|
|
return
|
|
}
|
|
}
|
|
req.ver = uint8(b[0])
|
|
req.cmd = uint8(b[1])
|
|
req.reserve = uint8(b[2])
|
|
req.addressType = uint8(b[3])
|
|
if b[0] != 0x5 {
|
|
err = fmt.Errorf("sosck version supported")
|
|
req.TCPReply(socks5c.REP_REQ_FAIL)
|
|
return
|
|
}
|
|
switch b[3] {
|
|
case 0x01: //IP V4
|
|
req.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String()
|
|
case 0x03: //域名
|
|
req.dstHost = string(b[5 : n-2]) //b[4]表示域名的长度
|
|
case 0x04: //IP V6
|
|
req.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()
|
|
}
|
|
req.dstPort = strconv.Itoa(int(b[n-2])<<8 | int(b[n-1]))
|
|
req.dstAddr = net.JoinHostPort(req.dstHost, req.dstPort)
|
|
req.bytes = b[:n]
|
|
return
|
|
}
|
|
func (s *Request) Bytes() []byte {
|
|
return s.bytes
|
|
}
|
|
func (s *Request) Addr() string {
|
|
return s.dstAddr
|
|
}
|
|
func (s *Request) Host() string {
|
|
return s.dstHost
|
|
}
|
|
func (s *Request) Port() string {
|
|
return s.dstPort
|
|
}
|
|
func (s *Request) AType() uint8 {
|
|
return s.addressType
|
|
}
|
|
func (s *Request) CMD() uint8 {
|
|
return s.cmd
|
|
}
|
|
|
|
func (s *Request) TCPReply(rep uint8) (err error) {
|
|
_, err = s.rw.Write(s.NewReply(rep, "0.0.0.0:0"))
|
|
return
|
|
}
|
|
func (s *Request) UDPReply(rep uint8, addr string) (err error) {
|
|
_, err = s.rw.Write(s.NewReply(rep, addr))
|
|
return
|
|
}
|
|
func (s *Request) NewReply(rep uint8, addr string) []byte {
|
|
var response bytes.Buffer
|
|
host, port, _ := net.SplitHostPort(addr)
|
|
ip := net.ParseIP(host)
|
|
ipb := ip.To4()
|
|
atyp := socks5c.ATYP_IPV4
|
|
ipv6 := ip.To16()
|
|
zeroiIPv6 := fmt.Sprintf("%d%d%d%d%d%d%d%d%d%d%d%d",
|
|
ipv6[0], ipv6[1], ipv6[2], ipv6[3],
|
|
ipv6[4], ipv6[5], ipv6[6], ipv6[7],
|
|
ipv6[8], ipv6[9], ipv6[10], ipv6[11],
|
|
)
|
|
if ipb == nil && ipv6 != nil && "0000000000255255" != zeroiIPv6 {
|
|
atyp = socks5c.ATYP_IPV6
|
|
ipb = ip.To16()
|
|
}
|
|
porti, _ := strconv.Atoi(port)
|
|
portb := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(portb, uint16(porti))
|
|
// log.Printf("atyp : %v", atyp)
|
|
// log.Printf("ip : %v", []byte(ip))
|
|
response.WriteByte(socks5c.VERSION_V5)
|
|
response.WriteByte(rep)
|
|
response.WriteByte(socks5c.RSV)
|
|
response.WriteByte(atyp)
|
|
response.Write(ipb)
|
|
response.Write(portb)
|
|
return response.Bytes()
|
|
}
|
|
|
|
type MethodsRequest struct {
|
|
ver uint8
|
|
methodsCount uint8
|
|
methods []uint8
|
|
bytes []byte
|
|
rw *io.ReadWriter
|
|
}
|
|
|
|
func NewMethodsRequest(r io.ReadWriter, header ...[]byte) (s MethodsRequest, err interface{}) {
|
|
defer func() {
|
|
if err == nil {
|
|
err = recover()
|
|
}
|
|
}()
|
|
s = MethodsRequest{}
|
|
s.rw = &r
|
|
var buf = make([]byte, 300)
|
|
var n int
|
|
if header != nil && len(header) == 1 && len(header[0]) > 1 {
|
|
buf = header[0]
|
|
n = len(header[0])
|
|
} else {
|
|
n, err = r.Read(buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
if buf[0] != 0x05 {
|
|
err = fmt.Errorf("socks version not supported")
|
|
return
|
|
}
|
|
if n != int(buf[1])+int(2) {
|
|
err = fmt.Errorf("socks methods data length error")
|
|
return
|
|
}
|
|
s.ver = buf[0]
|
|
s.methodsCount = buf[1]
|
|
s.methods = buf[2:n]
|
|
s.bytes = buf[:n]
|
|
return
|
|
}
|
|
func (s *MethodsRequest) Version() uint8 {
|
|
return s.ver
|
|
}
|
|
func (s *MethodsRequest) MethodsCount() uint8 {
|
|
return s.methodsCount
|
|
}
|
|
func (s *MethodsRequest) Methods() []uint8 {
|
|
return s.methods
|
|
}
|
|
func (s *MethodsRequest) Select(method uint8) bool {
|
|
for _, m := range s.methods {
|
|
if m == method {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
func (s *MethodsRequest) Reply(method uint8) (err error) {
|
|
_, err = (*s.rw).Write([]byte{byte(socks5c.VERSION_V5), byte(method)})
|
|
return
|
|
}
|
|
func (s *MethodsRequest) Bytes() []byte {
|
|
return s.bytes
|
|
}
|
|
|
|
type ServerConn struct {
|
|
target string
|
|
user string
|
|
password string
|
|
conn *net.Conn
|
|
timeout time.Duration
|
|
auth *BasicAuther
|
|
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 *BasicAuther, udpAddress string, header []byte) *ServerConn {
|
|
if udpAddress == "" {
|
|
udpAddress = "0.0.0.0:16666"
|
|
}
|
|
s := &ServerConn{
|
|
conn: conn,
|
|
timeout: timeout,
|
|
auth: auth,
|
|
header: header,
|
|
ver: socks5c.VERSION_V5,
|
|
udpAddress: udpAddress,
|
|
}
|
|
return s
|
|
|
|
}
|
|
func (s *ServerConn) Close() {
|
|
(*s.conn).Close()
|
|
}
|
|
func (s *ServerConn) AuthData() socks5c.UsernamePassword {
|
|
return socks5c.UsernamePassword{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(socks5c.Method_NONE_ACCEPTABLE)
|
|
(*s.conn).SetReadDeadline(time.Time{})
|
|
err = fmt.Errorf("new methods request fail,ERR: %s", e)
|
|
return
|
|
}
|
|
//log.Printf("%v,s.auth == %v && methodReq.Select(Method_NO_AUTH) %v", methodReq.methods, s.auth, methodReq.Select(Method_NO_AUTH))
|
|
if s.auth == nil && methodReq.Select(socks5c.Method_NO_AUTH) && !methodReq.Select(socks5c.Method_USER_PASS) {
|
|
// 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 = socks5c.Method_NO_AUTH
|
|
//method select reply
|
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
|
err = methodReq.Reply(socks5c.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(socks5c.Method_USER_PASS) {
|
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
|
methodReq.Reply(socks5c.Method_NONE_ACCEPTABLE)
|
|
(*s.conn).SetReadDeadline(time.Time{})
|
|
err = fmt.Errorf("none method found : Method_USER_PASS")
|
|
return
|
|
}
|
|
s.method = socks5c.Method_USER_PASS
|
|
//method reply need auth
|
|
(*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout))
|
|
err = methodReq.Reply(socks5c.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 == nil || (*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 socks5c.CMD_BIND:
|
|
err = request.TCPReply(socks5c.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 socks5c.CMD_CONNECT:
|
|
err = request.TCPReply(socks5c.REP_SUCCESS)
|
|
if err != nil {
|
|
err = fmt.Errorf("TCPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err)
|
|
return
|
|
}
|
|
case socks5c.CMD_ASSOCIATE:
|
|
err = request.UDPReply(socks5c.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
|
|
}
|