mirror of
https://github.com/ehang-io/nps.git
synced 2025-07-04 13:50:42 +00:00
pugin init
This commit is contained in:
parent
8bcf5313f4
commit
e11511637b
@ -0,0 +1,2 @@
|
|||||||
|
{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"m3ue28maxy3pgqp8","Addr":"","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"","WebPassword":"","ConfigConnAllow":true,"MaxTunnelNum":0}
|
||||||
|
*#*
|
@ -0,0 +1,2 @@
|
|||||||
|
{"Id":1,"Host":"a.o.com","HeaderChange":"","HostChange":"","Location":"/","Remark":"","Scheme":"all","CertFilePath":"","KeyFilePath":"","NoStore":false,"IsClose":false,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Client":{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"m3ue28maxy3pgqp8","Addr":"127.0.0.1","Remark":"","Status":true,"IsConnect":true,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"","WebPassword":"","ConfigConnAllow":true,"MaxTunnelNum":0},"Target":{"TargetStr":"127.0.0.1:8080","TargetArr":null,"LocalProxy":false}}
|
||||||
|
*#*
|
@ -4,7 +4,7 @@ runmode = dev
|
|||||||
|
|
||||||
#HTTP(S) proxy port, no startup if empty
|
#HTTP(S) proxy port, no startup if empty
|
||||||
http_proxy_ip=0.0.0.0
|
http_proxy_ip=0.0.0.0
|
||||||
http_proxy_port=80
|
http_proxy_port=809
|
||||||
https_proxy_port=443
|
https_proxy_port=443
|
||||||
https_just_proxy=true
|
https_just_proxy=true
|
||||||
#default https certificate setting
|
#default https certificate setting
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{"Id":2,"Port":1234,"ServerIp":"","Mode":"tcp","Status":true,"RunStatus":true,"Client":{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"m3ue28maxy3pgqp8","Addr":"","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"","WebPassword":"","ConfigConnAllow":true,"MaxTunnelNum":0},"Ports":"","Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Password":"","Remark":"","TargetAddr":"","NoStore":false,"LocalPath":"","StripPre":"","Target":{"TargetStr":"152.136.18.138:22","TargetArr":null,"LocalProxy":false},"HealthCheckTimeout":0,"HealthMaxFail":0,"HealthCheckInterval":0,"HealthNextTime":"0001-01-01T00:00:00Z","HealthMap":null,"HttpHealthUrl":"","HealthRemoveArr":null,"HealthCheckType":"","HealthCheckTarget":""}
|
||||||
|
*#*{"Id":3,"Port":1111,"ServerIp":"","Mode":"socks5","Status":true,"RunStatus":false,"Client":{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"m3ue28maxy3pgqp8","Addr":"","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"","WebPassword":"","ConfigConnAllow":true,"MaxTunnelNum":0},"Ports":"","Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Password":"","Remark":"","TargetAddr":"","NoStore":false,"LocalPath":"","StripPre":"","Target":{"TargetStr":"","TargetArr":null,"LocalProxy":false},"HealthCheckTimeout":0,"HealthMaxFail":0,"HealthCheckInterval":0,"HealthNextTime":"0001-01-01T00:00:00Z","HealthMap":null,"HttpHealthUrl":"","HealthRemoveArr":null,"HealthCheckType":"","HealthCheckTarget":""}
|
||||||
|
*#*
|
34
core/struct.go
Normal file
34
core/struct.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This structure is used to describe the plugin configuration item name and description.
|
||||||
|
type Config struct {
|
||||||
|
ConfigName string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stage uint8
|
||||||
|
|
||||||
|
// These constants are meant to describe the stage in which the plugin is running.
|
||||||
|
const (
|
||||||
|
STAGE_START_RUN_END Stage = iota
|
||||||
|
STAGE_START_RUN
|
||||||
|
STAGE_START_END
|
||||||
|
STAGE_RUN_END
|
||||||
|
STAGE_START
|
||||||
|
STAGE_END
|
||||||
|
STAGE_RUN
|
||||||
|
)
|
||||||
|
|
||||||
|
// Plugin interface, all plugins must implement those functions.
|
||||||
|
type Plugin interface {
|
||||||
|
GetConfigName() []*Config
|
||||||
|
GetBeforePlugin() Plugin
|
||||||
|
GetStage() Stage
|
||||||
|
Start(ctx context.Context, config map[string]string) error
|
||||||
|
Run(ctx context.Context, config map[string]string) error
|
||||||
|
End(ctx context.Context, config map[string]string) error
|
||||||
|
}
|
36
module.md
Normal file
36
module.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
主干程序模块化编写
|
||||||
|
|
||||||
|
假设socks5来说
|
||||||
|
首先需要定一个一个socks5 struct {
|
||||||
|
包含
|
||||||
|
客户端地址
|
||||||
|
服务端地址
|
||||||
|
读到的用户名
|
||||||
|
读到的密码
|
||||||
|
连接类型(tcp、udp)
|
||||||
|
远程地址(ipv4 ipv6)
|
||||||
|
远程端口
|
||||||
|
}
|
||||||
|
|
||||||
|
每个模块需要一个获取配置参数的func
|
||||||
|
|
||||||
|
定义好了之后在每每个模块中对该结=结构体进行操作
|
||||||
|
|
||||||
|
调用每一个模块的时候,需要如下参数
|
||||||
|
ctx上线文
|
||||||
|
socks5结构体
|
||||||
|
该module的配置
|
||||||
|
当前的connection
|
||||||
|
预分配的connection
|
||||||
|
每个模块根据自己的业务逻辑进行处理
|
||||||
|
|
||||||
|
|
||||||
|
其他技术:
|
||||||
|
零拷贝
|
||||||
|
事件驱动
|
||||||
|
回调
|
||||||
|
触发器
|
||||||
|
|
||||||
|
|
||||||
|
插件启动范围
|
||||||
|
插件优先级
|
@ -199,6 +199,7 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) {
|
|||||||
c.Close()
|
c.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "" {
|
if s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "" {
|
||||||
buf[1] = UserPassAuth
|
buf[1] = UserPassAuth
|
||||||
c.Write(buf)
|
c.Write(buf)
|
||||||
|
128
server/socks5/socks5_access_handle.go
Normal file
128
server/socks5/socks5_access_handle.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package socks5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/cnlh/nps/core"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserPassAuth = uint8(2)
|
||||||
|
userAuthVersion = uint8(1)
|
||||||
|
authSuccess = uint8(0)
|
||||||
|
authFailure = uint8(1)
|
||||||
|
UserNoAuth = uint8(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Access struct {
|
||||||
|
clientConn net.Conn
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (access *Access) GetConfigName() []*core.Config {
|
||||||
|
c := make([]*core.Config, 0)
|
||||||
|
c = append(c, &core.Config{ConfigName: "socks5_check_access", Description: "need check the permission?"})
|
||||||
|
c = append(c, &core.Config{ConfigName: "socks5_access_username", Description: "auth username"})
|
||||||
|
c = append(c, &core.Config{ConfigName: "socks5_access_password", Description: "auth password"})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (access *Access) GetStage() core.Stage {
|
||||||
|
return core.STAGE_RUN
|
||||||
|
}
|
||||||
|
|
||||||
|
func (access *Access) GetBeforePlugin() core.Plugin {
|
||||||
|
return &Handshake{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (access *Access) Start(ctx context.Context, config map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (access *Access) End(ctx context.Context, config map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (access *Access) Run(ctx context.Context, config map[string]string) error {
|
||||||
|
clientCtxConn := ctx.Value("clientConn")
|
||||||
|
if clientCtxConn == nil {
|
||||||
|
return errors.New("the client access.clientConnection is not exist")
|
||||||
|
}
|
||||||
|
access.clientConn = clientCtxConn.(net.Conn)
|
||||||
|
if config["socks5_check_access"] != "true" {
|
||||||
|
return access.sendAccessMsgToClient(UserNoAuth)
|
||||||
|
}
|
||||||
|
configUsername := config["socks5_access_username"]
|
||||||
|
configPassword := config["socks5_access_password"]
|
||||||
|
if configUsername == "" || configPassword == "" {
|
||||||
|
return access.sendAccessMsgToClient(UserNoAuth)
|
||||||
|
}
|
||||||
|
// need auth
|
||||||
|
if err := access.sendAccessMsgToClient(UserPassAuth); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// send auth reply to client ,and get the auth information
|
||||||
|
var err error
|
||||||
|
access.username, access.password, err = access.getAuthInfoFromClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
context.WithValue(ctx, access.username, access.password)
|
||||||
|
// check
|
||||||
|
return access.checkAuth(configUsername, configPassword)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (access *Access) sendAccessMsgToClient(auth uint8) error {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
buf[0] = 5
|
||||||
|
buf[1] = auth
|
||||||
|
n, err := access.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) {
|
||||||
|
header := []byte{0, 0}
|
||||||
|
if _, err = io.ReadAtLeast(access.clientConn, header, 2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if header[0] != userAuthVersion {
|
||||||
|
err = errors.New("authentication method is not supported")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userLen := int(header[1])
|
||||||
|
user := make([]byte, userLen)
|
||||||
|
if _, err = io.ReadAtLeast(access.clientConn, user, userLen); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := access.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 {
|
||||||
|
err = errors.New("get password error" + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
username = string(user)
|
||||||
|
password = string(pass)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (access *Access) checkAuth(configUserName, configPassword string) error {
|
||||||
|
if access.username == configUserName && access.password == configPassword {
|
||||||
|
_, err := access.clientConn.Write([]byte{userAuthVersion, authSuccess})
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
_, err := access.clientConn.Write([]byte{userAuthVersion, authFailure})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return errors.New("auth check error,username or password does not match")
|
||||||
|
}
|
||||||
|
}
|
58
server/socks5/socks5_handshake_handle.go
Normal file
58
server/socks5/socks5_handshake_handle.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package socks5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/cnlh/nps/core"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handshake struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handshake *Handshake) GetConfigName() []*core.Config {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (handshake *Handshake) GetStage() core.Stage {
|
||||||
|
return core.STAGE_RUN
|
||||||
|
}
|
||||||
|
func (handshake *Handshake) GetBeforePlugin() core.Plugin {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (handshake *Handshake) Start(ctx context.Context, config map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handshake *Handshake) Run(ctx context.Context, config map[string]string) error {
|
||||||
|
clientCtxConn := ctx.Value("clientConn")
|
||||||
|
if clientCtxConn == nil {
|
||||||
|
return errors.New("the client connection is not exist")
|
||||||
|
}
|
||||||
|
clientConn := clientCtxConn.(net.Conn)
|
||||||
|
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
if _, err := io.ReadFull(clientConn, buf); err != nil {
|
||||||
|
return errors.New("negotiation err while read 2 bytes from client connection: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if version := buf[0]; version != 5 {
|
||||||
|
return errors.New("only support socks5")
|
||||||
|
}
|
||||||
|
nMethods := buf[1]
|
||||||
|
|
||||||
|
methods := make([]byte, nMethods)
|
||||||
|
|
||||||
|
if n, err := clientConn.Read(methods); n != int(nMethods) || err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("read methods error, need %d , read %d, error %s", nMethods, n, err.Error()))
|
||||||
|
} else {
|
||||||
|
context.WithValue(ctx, "methods", methods[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handshake *Handshake) End(ctx context.Context, config map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user