mirror of
https://github.com/zhigang1992/cow.git
synced 2026-04-29 09:55:42 +08:00
143 lines
3.6 KiB
Go
143 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
)
|
|
|
|
// For socks documentation, refer to rfc 1928 http://www.ietf.org/rfc/rfc1928.txt
|
|
|
|
var socksError = [...]string{
|
|
1: "General SOCKS server failure",
|
|
2: "Connection not allowed by ruleset",
|
|
3: "Network unreachable",
|
|
4: "Host unreachable",
|
|
5: "Connection refused",
|
|
6: "TTL expired",
|
|
7: "Command not supported",
|
|
8: "Address type not supported",
|
|
9: "to X'FF' unassigned",
|
|
}
|
|
|
|
var socksProtocolErr = errors.New("socks protocol error")
|
|
|
|
var socksMsgVerMethodSelection = []byte{
|
|
0x5, // version 5
|
|
1, // n method
|
|
0, // no authorization required
|
|
}
|
|
|
|
var hasSocksServer = false
|
|
|
|
func initSocksServer() {
|
|
hasSocksServer = (config.SocksAddr != "")
|
|
if hasSocksServer {
|
|
debug.Println("has socks server:", config.SocksAddr)
|
|
}
|
|
}
|
|
|
|
func createctSocksConnection(hostFull string) (cn conn, err error) {
|
|
c, err := net.Dial("tcp", config.SocksAddr)
|
|
if err != nil {
|
|
debug.Printf("Can't connect to socks server %v\n", err)
|
|
return
|
|
}
|
|
hasErr := false
|
|
defer func() {
|
|
if hasErr {
|
|
c.Close()
|
|
}
|
|
}()
|
|
// debug.Println("Connected to socks server")
|
|
|
|
var n int
|
|
if n, err = c.Write(socksMsgVerMethodSelection); n != 3 || err != nil {
|
|
errl.Printf("sending ver/method selection msg %v n = %v\n", err, n)
|
|
hasErr = true
|
|
return
|
|
}
|
|
|
|
// version/method selection
|
|
repBuf := make([]byte, 2, 2)
|
|
_, err = c.Read(repBuf)
|
|
if err != nil {
|
|
errl.Printf("read ver/method selection error %v\n", err)
|
|
hasErr = true
|
|
return
|
|
}
|
|
if repBuf[0] != 5 || repBuf[1] != 0 {
|
|
errl.Printf("socks ver/method selection reply error ver %d method %d",
|
|
repBuf[0], repBuf[1])
|
|
hasErr = true
|
|
return
|
|
}
|
|
// debug.Println("Socks version selection done")
|
|
|
|
// send connect request
|
|
host, portStr := splitHostPort(hostFull)
|
|
port, err := strconv.Atoi(portStr)
|
|
if err != nil {
|
|
errl.Printf("Should not happen, port error %v\n", port)
|
|
hasErr = true
|
|
return
|
|
}
|
|
|
|
hostLen := len(host)
|
|
bufLen := 5 + hostLen + 2 // last 2 is port
|
|
reqBuf := make([]byte, bufLen, bufLen)
|
|
reqBuf[0] = 5 // version 5
|
|
reqBuf[1] = 1 // cmd: connect
|
|
// reqBuf[2] = 0 // rsv: set to 0 when initializing
|
|
reqBuf[3] = 3 // atyp: domain name
|
|
reqBuf[4] = byte(hostLen)
|
|
copy(reqBuf[5:], host)
|
|
binary.BigEndian.PutUint16(reqBuf[5+hostLen:5+hostLen+2], uint16(port))
|
|
|
|
/*
|
|
if debug {
|
|
debug.Println("Send socks connect request", (host + ":" + portStr))
|
|
}
|
|
*/
|
|
|
|
if n, err = c.Write(reqBuf); err != nil || n != bufLen {
|
|
errl.Printf("Send socks request err %v n %d\n", err, n)
|
|
hasErr = true
|
|
return
|
|
}
|
|
|
|
// I'm not clear why the buffer is fixed at 10. The rfc document does not say this.
|
|
// Polipo set this to 10 and I also observed the reply is always 10.
|
|
replyBuf := make([]byte, 10, 10)
|
|
if n, err = c.Read(replyBuf); err != nil {
|
|
// Seems that socks server will close connection if it can't find host
|
|
if err != io.EOF {
|
|
errl.Printf("Read socks reply err %v n %d\n", err, n)
|
|
}
|
|
hasErr = true
|
|
return zeroConn, errors.New("Connection failed (by socks server). No such host?")
|
|
}
|
|
// debug.Printf("Socks reply length %d\n", n)
|
|
|
|
if replyBuf[0] != 5 {
|
|
errl.Printf("Socks reply connect %s VER %d not supported\n", hostFull, replyBuf[0])
|
|
hasErr = true
|
|
return zeroConn, socksProtocolErr
|
|
}
|
|
if replyBuf[1] != 0 {
|
|
errl.Printf("Socks reply connect %s error %d\n", hostFull, socksError[replyBuf[1]])
|
|
hasErr = true
|
|
return zeroConn, socksProtocolErr
|
|
}
|
|
if replyBuf[3] != 1 {
|
|
errl.Printf("Socks reply connect %s ATYP %d\n", hostFull, replyBuf[3])
|
|
hasErr = true
|
|
return zeroConn, socksProtocolErr
|
|
}
|
|
|
|
// Now the socket can be used to pass data.
|
|
return conn{c, ctSocksConn}, nil
|
|
}
|