This commit is contained in:
刘河 2019-10-17 09:21:01 +08:00
parent 5e5df224b7
commit b6a9001d43
9 changed files with 371 additions and 86 deletions

50
core/const.go Normal file
View File

@ -0,0 +1,50 @@
package core
const (
CONN_DATA_SEQ = "*#*" //Separator
VERIFY_EER = "vkey"
VERIFY_SUCCESS = "sucs"
WORK_MAIN = "main"
WORK_CHAN = "chan"
WORK_CONFIG = "conf"
WORK_REGISTER = "rgst"
WORK_SECRET = "sert"
WORK_FILE = "file"
WORK_P2P = "p2pm"
WORK_P2P_VISITOR = "p2pv"
WORK_P2P_PROVIDER = "p2pp"
WORK_P2P_CONNECT = "p2pc"
WORK_P2P_SUCCESS = "p2ps"
WORK_P2P_END = "p2pe"
WORK_P2P_LAST = "p2pl"
WORK_STATUS = "stus"
RES_MSG = "msg0"
RES_CLOSE = "clse"
NEW_UDP_CONN = "udpc" //p2p udp conn
NEW_TASK = "task"
NEW_CONF = "conf"
NEW_HOST = "host"
CONN_TCP = "tcp"
CONN_UDP = "udp"
CONN_TEST = "TST"
UnauthorizedBytes = `HTTP/1.1 401 Unauthorized
Content-Type: text/plain; charset=utf-8
WWW-Authenticate: Basic realm="easyProxy"
401 Unauthorized`
ConnectionFailBytes = `HTTP/1.1 404 Not Found
`
)
const (
MUX_PING_FLAG uint8 = iota
MUX_NEW_CONN_OK
MUX_NEW_CONN_Fail
MUX_NEW_MSG
MUX_MSG_SEND_OK
MUX_NEW_CONN
MUX_CONN_CLOSE
MUX_PING_RETURN
MUX_PING int32 = -1
)

209
core/netpackager.go Normal file
View File

@ -0,0 +1,209 @@
package core
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"io"
"strings"
)
type NetPackager interface {
Pack(writer io.Writer) (err error)
UnPack(reader io.Reader) (err error)
}
type BasePackager struct {
Length uint32
Content []byte
}
func (Self *BasePackager) NewPac(contents ...interface{}) (err error) {
Self.clean()
for _, content := range contents {
switch content.(type) {
case nil:
Self.Content = Self.Content[:0]
case []byte:
err = Self.appendByte(content.([]byte))
case string:
err = Self.appendByte([]byte(content.(string)))
if err != nil {
return
}
err = Self.appendByte([]byte(CONN_DATA_SEQ))
default:
err = Self.marshal(content)
}
}
Self.setLength()
return
}
func (Self *BasePackager) appendByte(data []byte) (err error) {
m := len(Self.Content)
n := m + len(data)
if n <= cap(Self.Content) {
Self.Content = Self.Content[0:n] // grow the length for copy
copy(Self.Content[m:n], data)
return nil
} else {
return errors.New("pack content too large")
}
}
//似乎这里涉及到父类作用域问题当子类调用父类的方法时其struct仅仅为父类的
func (Self *BasePackager) Pack(writer io.Writer) (err error) {
err = binary.Write(writer, binary.LittleEndian, Self.Length)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, Self.Content)
return
}
//Unpack 会导致传入的数字类型转化成float64
//主要原因是json unmarshal并未传入正确的数据类型
func (Self *BasePackager) UnPack(reader io.Reader) (err error) {
Self.clean()
err = binary.Read(reader, binary.LittleEndian, &Self.Length)
if err != nil {
return
}
if int(Self.Length) > cap(Self.Content) {
err = errors.New("unpack err, content length too large")
}
Self.Content = Self.Content[:int(Self.Length)]
//n, err := io.ReadFull(reader, Self.Content)
//if n != int(Self.Length) {
// err = io.ErrUnexpectedEOF
//}
err = binary.Read(reader, binary.LittleEndian, Self.Content)
return
}
func (Self *BasePackager) marshal(content interface{}) (err error) {
tmp, err := json.Marshal(content)
if err != nil {
return err
}
err = Self.appendByte(tmp)
return
}
func (Self *BasePackager) Unmarshal(content interface{}) (err error) {
err = json.Unmarshal(Self.Content, content)
if err != nil {
return err
}
return
}
func (Self *BasePackager) setLength() {
Self.Length = uint32(len(Self.Content))
return
}
func (Self *BasePackager) clean() {
Self.Length = 0
Self.Content = Self.Content[:0] // reset length
}
func (Self *BasePackager) Split() (strList []string) {
n := bytes.IndexByte(Self.Content, 0)
strList = strings.Split(string(Self.Content[:n]), CONN_DATA_SEQ)
strList = strList[0 : len(strList)-1]
return
}
type ConnPackager struct { // Todo
ConnType uint8
BasePackager
}
func (Self *ConnPackager) NewPac(connType uint8, content ...interface{}) (err error) {
Self.ConnType = connType
err = Self.BasePackager.NewPac(content...)
return
}
func (Self *ConnPackager) Pack(writer io.Writer) (err error) {
err = binary.Write(writer, binary.LittleEndian, Self.ConnType)
if err != nil {
return
}
err = Self.BasePackager.Pack(writer)
return
}
func (Self *ConnPackager) UnPack(reader io.Reader) (err error) {
err = binary.Read(reader, binary.LittleEndian, &Self.ConnType)
if err != nil && err != io.EOF {
return
}
err = Self.BasePackager.UnPack(reader)
return
}
type MuxPackager struct {
Flag uint8
Id int32
Window uint16
BasePackager
}
func (Self *MuxPackager) NewPac(flag uint8, id int32, content ...interface{}) (err error) {
Self.Flag = flag
Self.Id = id
if flag == MUX_NEW_MSG {
err = Self.BasePackager.NewPac(content...)
}
if flag == MUX_MSG_SEND_OK {
// MUX_MSG_SEND_OK only allows one data
switch content[0].(type) {
case int:
Self.Window = uint16(content[0].(int))
case uint16:
Self.Window = content[0].(uint16)
}
}
return
}
func (Self *MuxPackager) Pack(writer io.Writer) (err error) {
err = binary.Write(writer, binary.LittleEndian, Self.Flag)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, Self.Id)
if err != nil {
return
}
if Self.Flag == MUX_NEW_MSG {
err = Self.BasePackager.Pack(writer)
}
if Self.Flag == MUX_MSG_SEND_OK {
err = binary.Write(writer, binary.LittleEndian, Self.Window)
}
return
}
func (Self *MuxPackager) UnPack(reader io.Reader) (err error) {
Self.BasePackager.clean() // also clean the content
err = binary.Read(reader, binary.LittleEndian, &Self.Flag)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &Self.Id)
if err != nil {
return
}
if Self.Flag == MUX_NEW_MSG {
err = Self.BasePackager.UnPack(reader)
}
if Self.Flag == MUX_MSG_SEND_OK {
err = binary.Read(reader, binary.LittleEndian, &Self.Window)
}
return
}

View File

@ -9,7 +9,7 @@ import (
// Plugin interface, all plugins must implement those functions.
type Plugin interface {
GetConfigName() *NpsConfigs
InitConfig(globalConfig, clientConfig, pluginConfig map[string]string)
InitConfig(globalConfig, clientConfig, pluginConfig map[string]string, pgCnf []*Config)
GetStage() []Stage
Start(ctx context.Context) (context.Context, error)
Run(ctx context.Context) (context.Context, error)
@ -25,9 +25,9 @@ func (npsPlugin *NpsPlugin) GetConfigName() *NpsConfigs {
return nil
}
func (npsPlugin *NpsPlugin) InitConfig(globalConfig, clientConfig, pluginConfig map[string]string) {
func (npsPlugin *NpsPlugin) InitConfig(globalConfig, clientConfig, pluginConfig map[string]string, pgCnf []*Config) {
npsPlugin.Configs = make(map[string]string)
for _, cfg := range npsPlugin.GetConfigName().GetAll() {
for _, cfg := range pgCnf {
switch cfg.ConfigLevel {
case CONFIG_LEVEL_PLUGIN:
npsPlugin.Configs[cfg.ConfigName] = pluginConfig[cfg.ConfigName]
@ -105,13 +105,20 @@ func (pl *Plugins) Add(plugins ...Plugin) {
}
}
func RunPlugin(ctx context.Context, pgs []Plugin) error {
func RunPlugin(ctx context.Context, pgs []Plugin, stage Stage) (context.Context, error) {
var err error
for _, pg := range pgs {
ctx, err = pg.Start(ctx)
switch stage {
case STAGE_RUN:
ctx, err = pg.Run(ctx)
case STAGE_START:
ctx, err = pg.Start(ctx)
case STAGE_END:
ctx, err = pg.End(ctx)
}
if err != nil {
return err
return ctx, err
}
}
return nil
return ctx, nil
}

View File

@ -2,14 +2,14 @@ package common
import (
"context"
"fmt"
"github.com/cnlh/nps/core"
"net"
"strconv"
)
type Proxy struct {
core.NpsPlugin
clientConn net.Conn
ctx context.Context
}
func (proxy *Proxy) GetConfigName() *core.NpsConfigs {
@ -17,10 +17,9 @@ func (proxy *Proxy) GetConfigName() *core.NpsConfigs {
}
func (proxy *Proxy) Run(ctx context.Context) (context.Context, error) {
proxy.ctx = ctx
proxy.clientConn = proxy.GetClientConn(ctx)
clientId := proxy.GetClientId(ctx)
brg := proxy.GetBridge(ctx)
clientConn := proxy.GetClientConn(ctx)
//clientId := proxy.GetClientId(ctx)
//brg := proxy.GetBridge(ctx)
//severConn, err := brg.GetConnByClientId(clientId)
//if err != nil {
@ -31,12 +30,22 @@ func (proxy *Proxy) Run(ctx context.Context) (context.Context, error) {
//if _, err := core.SendInfo(severConn, nil); err != nil {
// return ctx, err
//}
severConn, err := net.Dial(ctx.Value(core.PROXY_CONNECTION_TYPE).(string), ctx.Value(core.PROXY_CONNECTION_ADDR).(string)+":"+ctx.Value(core.PROXY_CONNECTION_PORT).(string))
connType := ctx.Value(core.PROXY_CONNECTION_TYPE).(string)
connAddr := ctx.Value(core.PROXY_CONNECTION_ADDR).(string)
connPort := strconv.Itoa(int(ctx.Value(core.PROXY_CONNECTION_PORT).(uint16)))
fmt.Println(connType, connAddr, connPort, clientConn.RemoteAddr().String())
serverConn, err := net.Dial(connType, connAddr+":"+connPort)
if err != nil {
return ctx, err
}
// data exchange
go core.CopyBuffer(severConn, proxy.clientConn)
core.CopyBuffer(proxy.clientConn, severConn)
go func() {
core.CopyBuffer(serverConn, clientConn)
serverConn.Close()
clientConn.Close()
}()
core.CopyBuffer(clientConn, serverConn)
serverConn.Close()
clientConn.Close()
return ctx, core.REQUEST_EOF
}

View File

@ -3,15 +3,13 @@ package socks5
import (
"context"
"errors"
"fmt"
"github.com/cnlh/nps/core"
"net"
)
type CheckAccess struct {
core.NpsPlugin
clientConn net.Conn
clientUsername string
clientPassword string
configUsername string
configPassword string
}
@ -24,19 +22,23 @@ func (check *CheckAccess) GetConfigName() *core.NpsConfigs {
}
func (check *CheckAccess) Run(ctx context.Context) (context.Context, error) {
check.clientConn = check.GetClientConn(ctx)
check.configUsername = check.Configs["socks5_access_username"]
check.configPassword = check.Configs["socks5_access_password"]
clientConn := check.GetClientConn(ctx)
check.configUsername = check.Configs["socks5_simple_access_username"]
check.configPassword = check.Configs["socks5_simple_access_password"]
if check.Configs["socks5_simple_access_check"] == "true" {
connUsername := ctx.Value("socks_client_username").(string)
connPassword := ctx.Value("socks_client_password").(string)
return ctx, check.checkAuth(clientConn, connUsername, connPassword)
}
return ctx, nil
}
func (check *CheckAccess) checkAuth(configUserName, configPassword string) error {
if check.clientUsername == configUserName && check.clientPassword == configPassword {
_, err := check.clientConn.Write([]byte{userAuthVersion, authSuccess})
func (check *CheckAccess) checkAuth(clientConn net.Conn, connUserName, connPassword string) error {
if check.configUsername == connUserName && check.configPassword == connPassword {
_, err := clientConn.Write([]byte{userAuthVersion, authSuccess})
return err
} else {
_, err := check.clientConn.Write([]byte{userAuthVersion, authFailure})
_, err := clientConn.Write([]byte{userAuthVersion, authFailure})
if err != nil {
return err
}

View File

@ -18,24 +18,23 @@ const (
type Access struct {
core.NpsPlugin
clientConn net.Conn
}
func (access *Access) GetConfigName() *core.NpsConfigs {
return core.NewNpsConfigs("socks5_check_access_check", "need check the permission simply",core.CONFIG_LEVEL_PLUGIN)
return core.NewNpsConfigs("socks5_check_access", "need check the permission simply", core.CONFIG_LEVEL_PLUGIN)
}
func (access *Access) Run(ctx context.Context) (context.Context, error) {
access.clientConn = access.GetClientConn(ctx)
clientConn := access.GetClientConn(ctx)
if access.Configs["socks5_check_access"] != "true" {
return ctx, access.sendAccessMsgToClient(UserNoAuth)
return ctx, access.sendAccessMsgToClient(clientConn, UserNoAuth)
}
// need auth
if err := access.sendAccessMsgToClient(UserPassAuth); err != nil {
if err := access.sendAccessMsgToClient(clientConn, UserPassAuth); err != nil {
return ctx, err
}
// send auth reply to client ,and get the auth information
username, password, err := access.getAuthInfoFromClient()
username, password, err := access.getAuthInfoFromClient(clientConn)
if err != nil {
return ctx, err
}
@ -45,20 +44,20 @@ func (access *Access) Run(ctx context.Context) (context.Context, error) {
return ctx, nil
}
func (access *Access) sendAccessMsgToClient(auth uint8) error {
func (access *Access) sendAccessMsgToClient(clientConn net.Conn, auth uint8) error {
buf := make([]byte, 2)
buf[0] = 5
buf[1] = auth
n, err := access.clientConn.Write(buf)
n, err := clientConn.Write(buf)
if err != nil || n != 2 {
return errors.New("write access message to client error " + err.Error())
}
return nil
}
func (access *Access) getAuthInfoFromClient() (username string, password string, err error) {
func (access *Access) getAuthInfoFromClient(clientConn net.Conn) (username string, password string, err error) {
header := []byte{0, 0}
if _, err = io.ReadAtLeast(access.clientConn, header, 2); err != nil {
if _, err = io.ReadAtLeast(clientConn, header, 2); err != nil {
return
}
if header[0] != userAuthVersion {
@ -67,16 +66,16 @@ func (access *Access) getAuthInfoFromClient() (username string, password string,
}
userLen := int(header[1])
user := make([]byte, userLen)
if _, err = io.ReadAtLeast(access.clientConn, user, userLen); err != nil {
if _, err = io.ReadAtLeast(clientConn, user, userLen); err != nil {
return
}
if _, err := access.clientConn.Read(header[:1]); err != nil {
if _, err = clientConn.Read(header[:1]); err != nil {
err = errors.New("get password length error" + err.Error())
return
}
passLen := int(header[0])
pass := make([]byte, passLen)
if _, err := io.ReadAtLeast(access.clientConn, pass, passLen); err != nil {
if _, err = io.ReadAtLeast(clientConn, pass, passLen); err != nil {
err = errors.New("get password error" + err.Error())
return
}

View File

@ -12,8 +12,6 @@ import (
type Request struct {
core.NpsPlugin
clientConn net.Conn
ctx context.Context
}
const (
@ -30,11 +28,11 @@ const (
maxUDPPacketSize = 1476
commandNotSupported = 7
addrTypeNotSupported = 8
succeeded = 0
)
func (request *Request) Run(ctx context.Context) (context.Context, error) {
request.clientConn = request.GetClientConn(ctx)
request.ctx = ctx
clientConn := request.GetClientConn(ctx)
/*
The SOCKS request is formed as follows:
@ -46,36 +44,36 @@ func (request *Request) Run(ctx context.Context) (context.Context, error) {
*/
header := make([]byte, 3)
_, err := io.ReadFull(request.clientConn, header)
_, err := io.ReadFull(clientConn, header)
if err != nil {
return request.ctx, errors.New("illegal request" + err.Error())
return ctx, errors.New("illegal request" + err.Error())
}
switch header[1] {
case connectMethod:
request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "tcp")
return request.ctx, request.doConnect()
ctx = context.WithValue(ctx, core.PROXY_CONNECTION_TYPE, "tcp")
return request.doConnect(ctx, clientConn)
case bindMethod:
return request.ctx, request.handleBind()
return ctx, request.handleBind()
case associateMethod:
request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "udp")
return request.ctx, request.handleUDP()
ctx = context.WithValue(ctx, core.PROXY_CONNECTION_TYPE, "udp")
return request.handleUDP(ctx, clientConn)
default:
request.sendReply(commandNotSupported)
return request.ctx, errors.New("command not supported")
request.sendReply(clientConn, commandNotSupported)
return ctx, errors.New("command not supported")
}
return request.ctx, nil
return ctx, nil
}
func (request *Request) sendReply(rep uint8) error {
func (request *Request) sendReply(clientConn net.Conn, rep uint8) error {
reply := []byte{
5,
rep,
0,
1,
}
localAddr := request.clientConn.LocalAddr().String()
localAddr := clientConn.LocalAddr().String()
localHost, localPort, _ := net.SplitHostPort(localAddr)
ipBytes := net.ParseIP(localHost).To4()
nPort, _ := strconv.Atoi(localPort)
@ -83,42 +81,41 @@ func (request *Request) sendReply(rep uint8) error {
portBytes := make([]byte, 2)
binary.BigEndian.PutUint16(portBytes, uint16(nPort))
reply = append(reply, portBytes...)
_, err := request.clientConn.Write(reply)
_, err := clientConn.Write(reply)
return err
}
//do conn
func (request *Request) doConnect() error {
func (request *Request) doConnect(ctx context.Context, clientConn net.Conn) (context.Context, error) {
addrType := make([]byte, 1)
request.clientConn.Read(addrType)
clientConn.Read(addrType)
var host string
switch addrType[0] {
case ipV4:
ipv4 := make(net.IP, net.IPv4len)
request.clientConn.Read(ipv4)
clientConn.Read(ipv4)
host = ipv4.String()
case ipV6:
ipv6 := make(net.IP, net.IPv6len)
request.clientConn.Read(ipv6)
clientConn.Read(ipv6)
host = ipv6.String()
case domainName:
var domainLen uint8
binary.Read(request.clientConn, binary.BigEndian, &domainLen)
binary.Read(clientConn, binary.BigEndian, &domainLen)
domain := make([]byte, domainLen)
request.clientConn.Read(domain)
clientConn.Read(domain)
host = string(domain)
default:
request.sendReply(addrTypeNotSupported)
return errors.New("target address type is not support")
request.sendReply(clientConn, addrTypeNotSupported)
return ctx, errors.New("target address type is not support")
}
var port uint16
binary.Read(request.clientConn, binary.BigEndian, &port)
request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_ADDR, host)
request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_PORT, port)
return nil
binary.Read(clientConn, binary.BigEndian, &port)
ctx = context.WithValue(ctx, core.PROXY_CONNECTION_ADDR, host)
ctx = context.WithValue(ctx, core.PROXY_CONNECTION_PORT, port)
request.sendReply(clientConn, succeeded)
return ctx, nil
}
// passive mode
@ -127,7 +124,7 @@ func (request *Request) handleBind() error {
}
//udp
func (request *Request) handleUDP() error {
func (request *Request) handleUDP(ctx context.Context, clientConn net.Conn) (context.Context, error) {
/*
+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
@ -136,13 +133,13 @@ func (request *Request) handleUDP() error {
+----+------+------+----------+----------+----------+
*/
buf := make([]byte, 3)
request.clientConn.Read(buf)
clientConn.Read(buf)
// relay udp datagram silently, without any notification to the requesting client
if buf[2] != 0 {
// does not support fragmentation, drop it
dummy := make([]byte, maxUDPPacketSize)
request.clientConn.Read(dummy)
return errors.New("does not support fragmentation, drop")
clientConn.Read(dummy)
return ctx, errors.New("does not support fragmentation, drop")
}
return request.doConnect()
return request.doConnect(ctx, clientConn)
}

View File

@ -35,25 +35,33 @@ func NewS5Server(globalConfig, clientConfig, pluginConfig map[string]string, Ser
func (s5 *S5Server) Start(ctx context.Context) error {
// init config of plugin
for _, pg := range s5.plugins.AllPgs {
pg.InitConfig(s5.globalConfig, s5.clientConfig, s5.pluginConfig)
}
// run the plugin contains start
if core.RunPlugin(ctx, s5.plugins.StartPgs) != nil {
return nil
if pg.GetConfigName() != nil {
pg.InitConfig(s5.globalConfig, s5.clientConfig, s5.pluginConfig, pg.GetConfigName().GetAll())
}
}
return core.NewTcpListenerAndProcess(s5.ServerIp+":"+strconv.Itoa(s5.ServerPort), func(c net.Conn) {
core.NewTcpListenerAndProcess(s5.ServerIp+":"+strconv.Itoa(s5.ServerPort), func(c net.Conn) {
// init ctx value clientConn
ctx = context.WithValue(ctx, core.CLIENT_CONNECTION, c)
// start run the plugin run
if err := core.RunPlugin(ctx, s5.plugins.RunPgs); err != nil {
connCtx := context.WithValue(ctx, core.CLIENT_CONNECTION, c)
var err error
// run the plugin contains start
if connCtx, err = core.RunPlugin(connCtx, s5.plugins.StartPgs, core.STAGE_START); err != nil {
fmt.Println(err)
return
}
// start run the plugin run
if connCtx, err = core.RunPlugin(connCtx, s5.plugins.RunPgs, core.STAGE_RUN); err != nil {
fmt.Println(err)
return
}
// start run the plugin end
if err := core.RunPlugin(ctx, s5.plugins.EndPgs); err != nil {
if connCtx, err = core.RunPlugin(connCtx, s5.plugins.EndPgs, core.STAGE_END); err != nil {
fmt.Println(err)
return
}
}, &s5.listener)
return nil
}

View File

@ -9,6 +9,10 @@ func TestNewS5Server(t *testing.T) {
g := make(map[string]string)
c := make(map[string]string)
p := make(map[string]string)
p["socks5_check_access"] = "true"
p["socks5_simple_access_check"] = "true"
p["socks5_simple_access_username"] = "111"
p["socks5_simple_access_password"] = "222"
s5 := NewS5Server(g, c, p, "", 1099)
ctx := context.Background()
s5.Start(ctx)