mirror of https://github.com/qwqdanchun/nps.git
396 lines
9.1 KiB
Go
Executable File
396 lines
9.1 KiB
Go
Executable File
package proxy
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
|
|
"ehang.io/nps/lib/common"
|
|
"ehang.io/nps/lib/conn"
|
|
"ehang.io/nps/lib/file"
|
|
"github.com/astaxie/beego/logs"
|
|
)
|
|
|
|
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
|
|
)
|
|
|
|
const (
|
|
UserPassAuth = uint8(2)
|
|
userAuthVersion = uint8(1)
|
|
authSuccess = uint8(0)
|
|
authFailure = uint8(1)
|
|
)
|
|
|
|
type Sock5ModeServer struct {
|
|
BaseServer
|
|
listener net.Listener
|
|
}
|
|
|
|
//req
|
|
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 {
|
|
logs.Warn("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()
|
|
}
|
|
}
|
|
|
|
//reply
|
|
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)
|
|
}
|
|
|
|
//do conn
|
|
func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
|
|
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)
|
|
return
|
|
}
|
|
|
|
var port uint16
|
|
binary.Read(c, binary.BigEndian, &port)
|
|
// connect to host
|
|
addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
|
|
var ltype string
|
|
if command == associateMethod {
|
|
ltype = common.CONN_UDP
|
|
} else {
|
|
ltype = common.CONN_TCP
|
|
}
|
|
s.DealClient(conn.NewConn(c), s.task.Client, addr, nil, ltype, func() {
|
|
s.sendReply(c, succeeded)
|
|
}, s.task.Flow, s.task.Target.LocalProxy)
|
|
return
|
|
}
|
|
|
|
//conn
|
|
func (s *Sock5ModeServer) handleConnect(c net.Conn) {
|
|
s.doConnect(c, connectMethod)
|
|
}
|
|
|
|
// passive mode
|
|
func (s *Sock5ModeServer) handleBind(c net.Conn) {
|
|
}
|
|
func (s *Sock5ModeServer) sendUdpReply(writeConn net.Conn, c net.Conn, rep uint8, serverIp string) {
|
|
reply := []byte{
|
|
5,
|
|
rep,
|
|
0,
|
|
1,
|
|
}
|
|
localHost, localPort, _ := net.SplitHostPort(c.LocalAddr().String())
|
|
localHost = serverIp
|
|
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...)
|
|
writeConn.Write(reply)
|
|
|
|
}
|
|
|
|
func (s *Sock5ModeServer) handleUDP(c net.Conn) {
|
|
defer c.Close()
|
|
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)
|
|
return
|
|
}
|
|
//读取端口
|
|
var port uint16
|
|
binary.Read(c, binary.BigEndian, &port)
|
|
logs.Warn(host, string(port))
|
|
replyAddr, err := net.ResolveUDPAddr("udp", s.task.ServerIp+":0")
|
|
if err != nil {
|
|
logs.Error("build local reply addr error", err)
|
|
return
|
|
}
|
|
reply, err := net.ListenUDP("udp", replyAddr)
|
|
if err != nil {
|
|
s.sendReply(c, addrTypeNotSupported)
|
|
logs.Error("listen local reply udp port error")
|
|
return
|
|
}
|
|
// reply the local addr
|
|
s.sendUdpReply(c, reply, succeeded, common.GetServerIpByClientIp(c.RemoteAddr().(*net.TCPAddr).IP))
|
|
defer reply.Close()
|
|
// new a tunnel to client
|
|
link := conn.NewLink("udp5", "", s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, c.RemoteAddr().String(), false)
|
|
target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, s.task)
|
|
if err != nil {
|
|
logs.Warn("get connection from client id %d error %s", s.task.Client.Id, err.Error())
|
|
return
|
|
}
|
|
|
|
var clientAddr net.Addr
|
|
// copy buffer
|
|
go func() {
|
|
b := common.BufPoolUdp.Get().([]byte)
|
|
defer common.BufPoolUdp.Put(b)
|
|
defer c.Close()
|
|
|
|
for {
|
|
n, laddr, err := reply.ReadFrom(b)
|
|
if err != nil {
|
|
logs.Error("read data from %s err %s", reply.LocalAddr().String(), err.Error())
|
|
return
|
|
}
|
|
if clientAddr == nil {
|
|
clientAddr = laddr
|
|
}
|
|
if _, err := target.Write(b[:n]); err != nil {
|
|
logs.Error("write data to client error", err.Error())
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
var l int32
|
|
b := common.BufPoolUdp.Get().([]byte)
|
|
defer common.BufPoolUdp.Put(b)
|
|
defer c.Close()
|
|
for {
|
|
if err := binary.Read(target, binary.LittleEndian, &l); err != nil || l >= common.PoolSizeUdp || l <= 0 {
|
|
logs.Warn("read len bytes error", err.Error())
|
|
return
|
|
}
|
|
binary.Read(target, binary.LittleEndian, b[:l])
|
|
if err != nil {
|
|
logs.Warn("read data form client error", err.Error())
|
|
return
|
|
}
|
|
if _, err := reply.WriteTo(b[:l], clientAddr); err != nil {
|
|
logs.Warn("write data to user ", err.Error())
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
b := common.BufPoolUdp.Get().([]byte)
|
|
defer common.BufPoolUdp.Put(b)
|
|
defer target.Close()
|
|
for {
|
|
_, err := c.Read(b)
|
|
if err != nil {
|
|
c.Close()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
//new conn
|
|
func (s *Sock5ModeServer) handleConn(c net.Conn) {
|
|
buf := make([]byte, 2)
|
|
if _, err := io.ReadFull(c, buf); err != nil {
|
|
logs.Warn("negotiation err", err)
|
|
c.Close()
|
|
return
|
|
}
|
|
|
|
if version := buf[0]; version != 5 {
|
|
logs.Warn("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 {
|
|
logs.Warn("wrong method")
|
|
c.Close()
|
|
return
|
|
}
|
|
if (s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "") || (s.task.MultiAccount != nil && len(s.task.MultiAccount.AccountMap) > 0) {
|
|
buf[1] = UserPassAuth
|
|
c.Write(buf)
|
|
if err := s.Auth(c); err != nil {
|
|
c.Close()
|
|
logs.Warn("Validation failed:", err)
|
|
return
|
|
}
|
|
} else {
|
|
buf[1] = 0
|
|
c.Write(buf)
|
|
}
|
|
s.handleRequest(c)
|
|
}
|
|
|
|
//socks5 auth
|
|
func (s *Sock5ModeServer) Auth(c net.Conn) error {
|
|
header := []byte{0, 0}
|
|
if _, err := io.ReadAtLeast(c, header, 2); err != nil {
|
|
return err
|
|
}
|
|
if header[0] != userAuthVersion {
|
|
return errors.New("验证方式不被支持")
|
|
}
|
|
userLen := int(header[1])
|
|
user := make([]byte, userLen)
|
|
if _, err := io.ReadAtLeast(c, user, userLen); err != nil {
|
|
return err
|
|
}
|
|
if _, err := c.Read(header[:1]); err != nil {
|
|
return errors.New("密码长度获取错误")
|
|
}
|
|
passLen := int(header[0])
|
|
pass := make([]byte, passLen)
|
|
if _, err := io.ReadAtLeast(c, pass, passLen); err != nil {
|
|
return err
|
|
}
|
|
|
|
var U, P string
|
|
if s.task.MultiAccount != nil {
|
|
// enable multi user auth
|
|
U = string(user)
|
|
var ok bool
|
|
P, ok = s.task.MultiAccount.AccountMap[U]
|
|
if !ok {
|
|
return errors.New("验证不通过")
|
|
}
|
|
} else {
|
|
U = s.task.Client.Cnf.U
|
|
P = s.task.Client.Cnf.P
|
|
}
|
|
|
|
if string(user) == U && string(pass) == P {
|
|
if _, err := c.Write([]byte{userAuthVersion, authSuccess}); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
} else {
|
|
if _, err := c.Write([]byte{userAuthVersion, authFailure}); err != nil {
|
|
return err
|
|
}
|
|
return errors.New("验证不通过")
|
|
}
|
|
}
|
|
|
|
//start
|
|
func (s *Sock5ModeServer) Start() error {
|
|
return conn.NewTcpListenerAndProcess(s.task.ServerIp+":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
|
|
if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
|
|
logs.Warn("client id %d, task id %d, error %s, when socks5 connection", s.task.Client.Id, s.task.Id, err.Error())
|
|
c.Close()
|
|
return
|
|
}
|
|
logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, c.RemoteAddr())
|
|
s.handleConn(c)
|
|
s.task.Client.AddConn()
|
|
}, &s.listener)
|
|
}
|
|
|
|
//new
|
|
func NewSock5ModeServer(bridge NetBridge, task *file.Tunnel) *Sock5ModeServer {
|
|
s := new(Sock5ModeServer)
|
|
s.bridge = bridge
|
|
s.task = task
|
|
return s
|
|
}
|
|
|
|
//close
|
|
func (s *Sock5ModeServer) Close() error {
|
|
return s.listener.Close()
|
|
}
|