Files
flora-kit/flora/socks5.go
0x1024 fcf771e160 v0.2.5
- windows 透明代理
2017-07-02 19:21:59 +08:00

159 lines
3.7 KiB
Go

package flora
import (
"io"
"net"
"strconv"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
"encoding/binary"
"log"
)
/*
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 |
*/
//local socks server auth
func handshake(conn net.Conn,first byte ) (err error) {
const (
idVer = 0
idNmethod = 1
)
// version identification and method selection message in theory can have
// at most 256 methods, plus version and nmethod field in total 258 bytes
// the current rfc defines only 3 authentication methods (plus 2 reserved),
// so it won't be such long in practice
buf := make([]byte, 258)
buf[idVer] = first
var n int
ss.SetReadTimeout(conn)
// make sure we get the nmethod field
if n, err = io.ReadAtLeast(conn, buf[1:], idNmethod+1); err != nil {
return
}
n ++
//if buf[idVer] != socksVer5 {
// return errVer
//}
nmethod := int(buf[idNmethod])
msgLen := nmethod + 2
if n == msgLen { // handshake done, common case
// do nothing, jump directly to send confirmation
} else if n < msgLen { // has more methods to read, rare case
if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil {
log.Print(err)
return
}
} else { // error, should not get extra data
return errAuthExtraData
}
// send confirmation: version 5, no authentication required
_, err = conn.Write([]byte{socksVer5, 0})
return
}
// local socks server connect
func socks5Connect(conn net.Conn) (host string, hostType int, err error) {
const (
idVer = 0
idCmd = 1
idType = 3 // address type index
idIP0 = 4 // ip addres start index
idDmLen = 4 // domain address length index
idDm0 = 5 // domain address start index
lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port
lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port
lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen
)
// refer to getRequest in flora.go for why set buffer size to 263
buf := make([]byte, 263)
var n int
ss.SetReadTimeout(conn)
// read till we get possible domain length field
if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil {
return
}
// check version and cmd
//if buf[idVer] != socksVer5 {
// err = errVer
// return
//}
if buf[idCmd] != socksCmdConnect {
err = errCmd
return
}
reqLen := -1
hostType = int(buf[idType])
switch hostType {
case typeIPv4:
reqLen = lenIPv4
case typeIPv6:
reqLen = lenIPv6
case typeDm:
reqLen = int(buf[idDmLen]) + lenDmBase
default:
err = errAddrType
return
}
if n == reqLen {
// common case, do nothing
} else if n < reqLen { // rare case
if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil {
return
}
} else {
err = errReqExtraData
return
}
//raw := buf[idType:reqLen]
switch hostType {
case typeIPv4:
host = net.IP(buf[idIP0: idIP0+net.IPv4len]).String()
case typeIPv6:
host = net.IP(buf[idIP0: idIP0+net.IPv6len]).String()
case typeDm:
host = string(buf[idDm0: idDm0+buf[idDmLen]])
}
port := binary.BigEndian.Uint16(buf[reqLen-2: reqLen])
host = net.JoinHostPort(host, strconv.Itoa(int(port)))
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43})
return
}