mirror of
https://github.com/zhigang1992/flora-kit.git
synced 2026-01-12 22:47:28 +08:00
159 lines
3.7 KiB
Go
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
|
|
}
|