nps/sock5.go

237 lines
5.2 KiB
Go

package main
import (
"encoding/binary"
"errors"
"io"
"log"
"net"
"strconv"
)
const (
ipV4 = 1
domainName = 3
ipV6 = 4
connectMethod = 1
bindMethod = 2
associateMethod = 3
// The maximum packet size of any udp Associate packet, based on ethernet's max size,
// minus the IP and UDP headers. IPv4 has a 20 byte header, UDP adds an
// additional 4 bytes. This is a total overhead of 24 bytes. Ethernet's
// max packet size is 1500 bytes, 1500 - 24 = 1476.
maxUDPPacketSize = 1476
)
const (
succeeded uint8 = iota
serverFailure
notAllowed
networkUnreachable
hostUnreachable
connectionRefused
ttlExpired
commandNotSupported
addrTypeNotSupported
)
type Sock5ModeServer struct {
Tunnel
httpPort int
}
func (s *Sock5ModeServer) handleRequest(c net.Conn) {
/*
The SOCKS request is formed as follows:
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
*/
header := make([]byte, 3)
_, err := io.ReadFull(c, header)
if err != nil {
log.Println("illegal request", err)
c.Close()
return
}
switch header[1] {
case connectMethod:
s.handleConnect(c)
case bindMethod:
s.handleBind(c)
case associateMethod:
s.handleUDP(c)
default:
s.sendReply(c, commandNotSupported)
c.Close()
}
}
func (s *Sock5ModeServer) sendReply(c net.Conn, rep uint8) {
reply := []byte{
5,
rep,
0,
1,
}
localAddr := c.LocalAddr().String()
localHost, localPort, _ := net.SplitHostPort(localAddr)
ipBytes := net.ParseIP(localHost).To4()
nPort, _ := strconv.Atoi(localPort)
reply = append(reply, ipBytes...)
portBytes := make([]byte, 2)
binary.BigEndian.PutUint16(portBytes, uint16(nPort))
reply = append(reply, portBytes...)
c.Write(reply)
}
func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *Conn, err error) {
addrType := make([]byte, 1)
c.Read(addrType)
var host string
switch addrType[0] {
case ipV4:
ipv4 := make(net.IP, net.IPv4len)
c.Read(ipv4)
host = ipv4.String()
case ipV6:
ipv6 := make(net.IP, net.IPv6len)
c.Read(ipv6)
host = ipv6.String()
case domainName:
var domainLen uint8
binary.Read(c, binary.BigEndian, &domainLen)
domain := make([]byte, domainLen)
c.Read(domain)
host = string(domain)
default:
s.sendReply(c, addrTypeNotSupported)
err = errors.New("Address type not supported")
return nil, err
}
var port uint16
binary.Read(c, binary.BigEndian, &port)
// connect to host
addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
//取出一个连接
if len(s.tunnelList) < 10 { //新建通道
go s.newChan()
}
client := <-s.tunnelList
s.sendReply(c, succeeded)
_, err = client.WriteHost(addr)
return client, nil
}
func (s *Sock5ModeServer) handleConnect(c net.Conn) {
proxyConn, err := s.doConnect(c, connectMethod)
if err != nil {
c.Close()
} else {
go io.Copy(c, proxyConn)
go io.Copy(proxyConn, c)
}
}
func (s *Sock5ModeServer) relay(in, out net.Conn) {
if _, err := io.Copy(in, out); err != nil {
log.Println("copy error", err)
}
in.Close() // will trigger an error in the other relay, then call out.Close()
}
// passive mode
func (s *Sock5ModeServer) handleBind(c net.Conn) {
}
func (s *Sock5ModeServer) handleUDP(c net.Conn) {
log.Println("UDP Associate")
/*
+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
+----+------+------+----------+----------+----------+
| 2 | 1 | 1 | Variable | 2 | Variable |
+----+------+------+----------+----------+----------+
*/
buf := make([]byte, 3)
c.Read(buf)
// relay udp datagram silently, without any notification to the requesting client
if buf[2] != 0 {
// does not support fragmentation, drop it
log.Println("does not support fragmentation, drop")
dummy := make([]byte, maxUDPPacketSize)
c.Read(dummy)
}
proxyConn, err := s.doConnect(c, associateMethod)
if err != nil {
c.Close()
} else {
go io.Copy(c, proxyConn)
go io.Copy(proxyConn, c)
}
}
func (s *Sock5ModeServer) handleNewConn(c net.Conn) {
buf := make([]byte, 2)
if _, err := io.ReadFull(c, buf); err != nil {
log.Println("negotiation err", err)
c.Close()
return
}
if version := buf[0]; version != 5 {
log.Println("only support socks5, request from: ", c.RemoteAddr())
c.Close()
return
}
nMethods := buf[1]
methods := make([]byte, nMethods)
if len, err := c.Read(methods); len != int(nMethods) || err != nil {
log.Println("wrong method")
c.Close()
return
}
// no authentication required for now
buf[1] = 0
// send a METHOD selection message
c.Write(buf)
s.handleRequest(c)
}
func (s *Sock5ModeServer) Start() {
l, err := net.Listen("tcp", ":"+strconv.Itoa(s.httpPort))
if err != nil {
log.Fatal("listen error: ", err)
}
s.StartTunnel()
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error: ", err)
}
go s.handleNewConn(conn)
}
}
func NewSock5ModeServer(tcpPort, httpPort int) *Sock5ModeServer {
s := new(Sock5ModeServer)
s.tunnelPort = tcpPort
s.httpPort = httpPort
s.tunnelList = make(chan *Conn, 1000)
s.signalList = make(chan *Conn, 10)
return s
}