mirror of
https://github.com/ehang-io/nps.git
synced 2025-09-08 00:26:52 +00:00
add new file
This commit is contained in:
138
core/rule/list.go
Normal file
138
core/rule/list.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"ehang.io/nps/core/action"
|
||||
"ehang.io/nps/core/handler"
|
||||
"ehang.io/nps/core/limiter"
|
||||
"ehang.io/nps/core/process"
|
||||
"ehang.io/nps/core/server"
|
||||
"github.com/fatih/structtag"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var orderMap map[string]int
|
||||
var nowOrder = 2<<8 - 1
|
||||
|
||||
type children map[string]*List
|
||||
|
||||
var chains children
|
||||
var limiters children
|
||||
|
||||
func init() {
|
||||
orderMap = make(map[string]int, 0)
|
||||
chains = make(map[string]*List, 0)
|
||||
limiters = make(map[string]*List, 0)
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.HttpHandler{}).Append(&process.HttpServeProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{}, &action.AdminAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.HttpsHandler{}).Append(&process.HttpsServeProcess{HttpServeProcess: process.HttpServeProcess{}}).AppendMany(&action.NpcAction{}, &action.LocalAction{}, &action.AdminAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.HttpsHandler{}).Append(&process.HttpsRedirectProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.HttpHandler{}).Append(&process.HttpProxyProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.HttpsHandler{}).Append(&process.HttpsProxyProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.Socks5Handler{}).Append(&process.Socks5Process{}).AppendMany(&action.LocalAction{}, &action.NpcAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.TransparentHandler{}).Append(&process.TransparentProcess{}).AppendMany(&action.NpcAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.RdpHandler{}).Append(&process.DefaultProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{})
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.RedisHandler{}).Append(&process.DefaultProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{})
|
||||
|
||||
chains.Append(&server.UdpServer{}).Append(&handler.DnsHandler{}).Append(&process.DefaultProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{})
|
||||
// TODO p2p
|
||||
chains.Append(&server.UdpServer{}).Append(&handler.P2PHandler{}).Append(&process.DefaultProcess{}).AppendMany(&action.NpcAction{}, &action.LocalAction{})
|
||||
chains.Append(&server.UdpServer{}).Append(&handler.QUICHandler{}).Append(&process.DefaultProcess{}).AppendMany(&action.BridgeAction{})
|
||||
chains.Append(&server.UdpServer{}).Append(&handler.Socks5UdpHandler{}).Append(&process.Socks5Process{}).AppendMany(&action.LocalAction{}, &action.NpcAction{})
|
||||
|
||||
chains.Append(&server.TcpServer{}).Append(&handler.DefaultHandler{}).Append(&process.DefaultProcess{}).AppendMany(&action.BridgeAction{}, &action.AdminAction{}, &action.NpcAction{}, &action.LocalAction{})
|
||||
|
||||
limiters.AppendMany(&limiter.RateLimiter{}, &limiter.ConnNumLimiter{}, &limiter.FlowLimiter{}, &limiter.IpConnNumLimiter{})
|
||||
}
|
||||
|
||||
func GetLimiters() children {
|
||||
return limiters
|
||||
}
|
||||
|
||||
func GetChains() children {
|
||||
return chains
|
||||
}
|
||||
|
||||
type NameInterface interface {
|
||||
GetName() string
|
||||
GetZhName() string
|
||||
}
|
||||
|
||||
type List struct {
|
||||
ZhName string `json:"zh_name"`
|
||||
Self interface{} `json:"-"`
|
||||
Field []field `json:"field"`
|
||||
Children children `json:"children"`
|
||||
}
|
||||
|
||||
func (c children) AppendMany(child ...NameInterface) {
|
||||
for _, cd := range child {
|
||||
c.Append(cd)
|
||||
}
|
||||
}
|
||||
|
||||
func (c children) Append(child NameInterface) children {
|
||||
if v, ok := c[child.GetName()]; ok {
|
||||
return v.Children
|
||||
}
|
||||
if _, ok := orderMap[child.GetName()]; !ok {
|
||||
orderMap[child.GetName()] = nowOrder
|
||||
nowOrder--
|
||||
}
|
||||
cd := &List{Self: child, Field: getFieldName(child), Children: make(map[string]*List, 0), ZhName: child.GetZhName()}
|
||||
c[child.GetName()] = cd
|
||||
return cd.Children
|
||||
}
|
||||
|
||||
type field struct {
|
||||
FiledType string `json:"field_type"`
|
||||
FieldName string `json:"field_name"`
|
||||
FieldZhName string `json:"field_zh_name"`
|
||||
FieldRequired bool `json:"field_required"`
|
||||
FieldExample string `json:"field_example"`
|
||||
}
|
||||
|
||||
func getFieldName(structName interface{}, child ...bool) []field {
|
||||
result := make([]field, 0)
|
||||
t := reflect.TypeOf(structName)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return result
|
||||
}
|
||||
fieldNum := t.NumField()
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
if len(child) == 0 && t.Field(i).Type.Kind() == reflect.Struct {
|
||||
value := reflect.ValueOf(structName)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
if value.Field(i).CanInterface() {
|
||||
result = append(result, getFieldName(value.Field(i).Interface(), true)...)
|
||||
}
|
||||
}
|
||||
tags, err := structtag.Parse(string(t.Field(i).Tag))
|
||||
if err == nil {
|
||||
tag, err := tags.Get("json")
|
||||
if err == nil {
|
||||
f := field{}
|
||||
f.FiledType = t.Field(i).Type.Kind().String()
|
||||
f.FieldName = tag.Name
|
||||
tag, err = tags.Get("required")
|
||||
if err == nil {
|
||||
f.FieldRequired, _ = strconv.ParseBool(tag.Name)
|
||||
}
|
||||
tag, err = tags.Get("placeholder")
|
||||
if err == nil {
|
||||
f.FieldExample = tag.Name
|
||||
}
|
||||
tag, err = tags.Get("zh_name")
|
||||
if err == nil {
|
||||
f.FieldZhName = tag.Name
|
||||
}
|
||||
result = append(result, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
13
core/rule/list_test.go
Normal file
13
core/rule/list_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"ehang.io/nps/core/process"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetFields(t *testing.T) {
|
||||
h := process.HttpsServeProcess{HttpServeProcess: process.HttpServeProcess{}}
|
||||
if len(getFieldName(h)) < 3 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
71
core/rule/rule.go
Normal file
71
core/rule/rule.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"ehang.io/nps/core/action"
|
||||
"ehang.io/nps/core/handler"
|
||||
"ehang.io/nps/core/limiter"
|
||||
"ehang.io/nps/core/process"
|
||||
"ehang.io/nps/core/server"
|
||||
"ehang.io/nps/lib/enet"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
Server server.Server `json:"server"`
|
||||
Handler handler.Handler `json:"handler"`
|
||||
Process process.Process `json:"process"`
|
||||
Action action.Action `json:"action"`
|
||||
Limiters []limiter.Limiter `json:"limiters"`
|
||||
}
|
||||
|
||||
var servers map[string]server.Server
|
||||
|
||||
func init() {
|
||||
servers = make(map[string]server.Server, 0)
|
||||
}
|
||||
|
||||
func (r *Rule) GetHandler() handler.Handler {
|
||||
return r.Handler
|
||||
}
|
||||
|
||||
func (r *Rule) Init() error {
|
||||
s := r.Server
|
||||
var ok bool
|
||||
if s, ok = servers[r.Server.GetName()+":"+r.Server.GetServerAddr()]; !ok {
|
||||
s = r.Server
|
||||
err := s.Init()
|
||||
servers[r.Server.GetName()+":"+r.Server.GetServerAddr()] = s
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.Serve()
|
||||
}
|
||||
s.RegisterHandle(r)
|
||||
r.Handler.AddRule(r)
|
||||
if err := r.Action.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, l := range r.Limiters {
|
||||
if err := l.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return r.Process.Init(r.Action)
|
||||
}
|
||||
|
||||
func (r *Rule) RunConn(c enet.Conn) (bool, error) {
|
||||
var err error
|
||||
for _, lm := range r.Limiters {
|
||||
if c, err = lm.DoLimit(c); err != nil {
|
||||
return true, errors.Wrap(err, "rule run")
|
||||
}
|
||||
}
|
||||
if err = c.Reset(0); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return r.Process.ProcessConn(c)
|
||||
}
|
||||
|
||||
func (r *Rule) RunPacketConn(pc enet.PacketConn) (bool, error) {
|
||||
return r.Process.ProcessPacketConn(pc)
|
||||
}
|
92
core/rule/rule_json.go
Normal file
92
core/rule/rule_json.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"ehang.io/nps/core/action"
|
||||
"ehang.io/nps/core/handler"
|
||||
"ehang.io/nps/core/limiter"
|
||||
"ehang.io/nps/core/process"
|
||||
"ehang.io/nps/core/server"
|
||||
"encoding/json"
|
||||
"github.com/pkg/errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type JsonData struct {
|
||||
ObjType string `json:"obj_type"`
|
||||
ObjData string `json:"obj_data"`
|
||||
}
|
||||
|
||||
type JsonRule struct {
|
||||
Name string `json:"name"`
|
||||
Uuid string `json:"uuid"`
|
||||
Status int `json:"status"`
|
||||
Extend int `json:"extend"`
|
||||
Server JsonData `json:"server"`
|
||||
Handler JsonData `json:"handler"`
|
||||
Process JsonData `json:"process"`
|
||||
Action JsonData `json:"action"`
|
||||
Limiters []JsonData `json:"limiters"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
var NotFoundError = errors.New("not found")
|
||||
|
||||
func (jd *JsonRule) ToRule() (*Rule, error) {
|
||||
r := &Rule{Limiters: make([]limiter.Limiter, 0)}
|
||||
s, ok := chains[jd.Server.ObjType]
|
||||
if !ok {
|
||||
return nil, NotFoundError
|
||||
}
|
||||
r.Server = clone(s.Self).(server.Server)
|
||||
err := json.Unmarshal([]byte(jd.Server.ObjData), r.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, ok := s.Children[jd.Handler.ObjType]
|
||||
if !ok {
|
||||
return nil, NotFoundError
|
||||
}
|
||||
r.Handler = clone(h.Self).(handler.Handler)
|
||||
err = json.Unmarshal([]byte(jd.Handler.ObjData), r.Handler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, ok := h.Children[jd.Process.ObjType]
|
||||
if !ok {
|
||||
return nil, NotFoundError
|
||||
}
|
||||
r.Process = clone(p.Self).(process.Process)
|
||||
err = json.Unmarshal([]byte(jd.Process.ObjData), r.Process)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, ok := p.Children[jd.Action.ObjType]
|
||||
if !ok {
|
||||
return nil, NotFoundError
|
||||
}
|
||||
r.Action = clone(a.Self).(action.Action)
|
||||
err = json.Unmarshal([]byte(jd.Action.ObjData), r.Action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range jd.Limiters {
|
||||
l, ok := limiters[v.ObjType]
|
||||
if !ok {
|
||||
return nil, NotFoundError
|
||||
}
|
||||
lm := clone(l.Self).(limiter.Limiter)
|
||||
err = json.Unmarshal([]byte(v.ObjData), lm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Limiters = append(r.Limiters, lm)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func clone(i interface{}) interface{} {
|
||||
v := reflect.ValueOf(i).Elem()
|
||||
vNew := reflect.New(v.Type())
|
||||
vNew.Elem().Set(v)
|
||||
return vNew.Interface()
|
||||
}
|
58
core/rule/rule_json_test.go
Normal file
58
core/rule/rule_json_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"ehang.io/nps/core/action"
|
||||
"ehang.io/nps/core/handler"
|
||||
"ehang.io/nps/core/process"
|
||||
"ehang.io/nps/core/server"
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
type person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
a := &person{
|
||||
Name: "ALice",
|
||||
Age: 20,
|
||||
}
|
||||
b := clone(a).(*person)
|
||||
assert.Equal(t, a.Name, b.Name)
|
||||
assert.Equal(t, a.Age, b.Age)
|
||||
a.Name = "Bob"
|
||||
a.Age = 21
|
||||
assert.NotEqual(t, a.Name, b.Name)
|
||||
assert.NotEqual(t, a.Age, b.Age)
|
||||
assert.NotEqual(t, reflect.ValueOf(a).Pointer(), reflect.ValueOf(b).Pointer())
|
||||
}
|
||||
|
||||
func getJson(t *testing.T, i interface{}) string {
|
||||
b, err := json.Marshal(i)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, string(b))
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func TestJsonRule(t *testing.T) {
|
||||
s := &server.TcpServer{ ServerAddr: "127.0.0.1:0"}
|
||||
h := &handler.HttpHandler{}
|
||||
p := &process.HttpServeProcess{}
|
||||
a := &action.LocalAction{}
|
||||
js := JsonRule{
|
||||
Uuid: "",
|
||||
Server: JsonData{s.GetName(), getJson(t, s)},
|
||||
Handler: JsonData{h.GetName(), getJson(t, h)},
|
||||
Process: JsonData{p.GetName(), getJson(t, p)},
|
||||
Action: JsonData{a.GetName(), getJson(t, a)},
|
||||
Limiters: make([]JsonData, 0),
|
||||
}
|
||||
rl, err := js.ToRule()
|
||||
assert.NoError(t, err)
|
||||
err = rl.Init()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rl.Server.(*server.TcpServer).ServerAddr, "127.0.0.1:0")
|
||||
}
|
45
core/rule/rule_test.go
Normal file
45
core/rule/rule_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"ehang.io/nps/core/action"
|
||||
"ehang.io/nps/core/handler"
|
||||
"ehang.io/nps/core/limiter"
|
||||
"ehang.io/nps/core/process"
|
||||
"ehang.io/nps/core/server"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRule(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
assert.NoError(t, err)
|
||||
r := &Rule{
|
||||
Server: &server.TcpServer{ServerAddr: "127.0.0.1:0"},
|
||||
Handler: &handler.DefaultHandler{},
|
||||
Process: &process.DefaultProcess{},
|
||||
Action: &action.LocalAction{TargetAddr: []string{ln.Addr().String()}},
|
||||
Limiters: make([]limiter.Limiter, 0),
|
||||
}
|
||||
err = r.Init()
|
||||
assert.NoError(t, err)
|
||||
data := []byte("test")
|
||||
go func() {
|
||||
conn, err := ln.Accept()
|
||||
assert.NoError(t, err)
|
||||
b := make([]byte, 1024)
|
||||
n, err := conn.Read(b)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, b[:n])
|
||||
_, err = conn.Write(b[:n])
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
conn, err := net.Dial(r.Server.GetName(), r.Server.GetServerAddr())
|
||||
assert.NoError(t, err)
|
||||
_, err = conn.Write(data)
|
||||
assert.NoError(t, err)
|
||||
b := make([]byte, 1024)
|
||||
n, err := conn.Read(b)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, b[:n], data)
|
||||
}
|
30
core/rule/sort.go
Normal file
30
core/rule/sort.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package rule
|
||||
|
||||
import "ehang.io/nps/core/process"
|
||||
|
||||
type Sort []*Rule
|
||||
|
||||
func (s Sort) Len() int { return len(s) }
|
||||
func (s Sort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
// Less rule sort by
|
||||
func (s Sort) Less(i, j int) bool {
|
||||
iHandlerSort := orderMap[s[i].Handler.GetName()]
|
||||
iProcessSort := orderMap[s[i].Process.GetName()]
|
||||
jHandlerSort := orderMap[s[j].Handler.GetName()]
|
||||
jProcessSort := orderMap[s[j].Process.GetName()]
|
||||
iSort := iHandlerSort<<16 | iProcessSort<<8
|
||||
jSort := jHandlerSort<<16 | jProcessSort<<8
|
||||
if vi, ok := s[i].Process.(*process.HttpServeProcess); ok {
|
||||
if vj, ok := s[j].Process.(*process.HttpServeProcess); ok {
|
||||
iSort = iSort | (len(vj.RouteUrl) & (2 ^ 8 - 1))
|
||||
jSort = jSort | (len(vi.RouteUrl) & (2 ^ 8 - 1))
|
||||
}
|
||||
}
|
||||
if vi, ok := s[i].Process.(*process.HttpsServeProcess); ok {
|
||||
if vj, ok := s[j].Process.(*process.HttpsServeProcess); ok {
|
||||
iSort = iSort | (len(vj.RouteUrl) & (2 ^ 8 - 1))
|
||||
jSort = jSort | (len(vi.RouteUrl) & (2 ^ 8 - 1))
|
||||
}
|
||||
}
|
||||
return iSort > jSort
|
||||
}
|
27
core/rule/sort_test.go
Normal file
27
core/rule/sort_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"ehang.io/nps/core/handler"
|
||||
"ehang.io/nps/core/process"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSort_Len(t *testing.T) {
|
||||
r1 := &Rule{Handler: &handler.DefaultHandler{}, Process: &process.TransparentProcess{}}
|
||||
r2 := &Rule{Handler: &handler.DefaultHandler{}, Process: &process.DefaultProcess{}}
|
||||
r3 := &Rule{Handler: &handler.DefaultHandler{}, Process: &process.HttpServeProcess{RouteUrl: "/test/aaa"}}
|
||||
r4 := &Rule{Handler: &handler.DefaultHandler{}, Process: &process.Socks5Process{}}
|
||||
r5 := &Rule{Handler: &handler.DefaultHandler{}, Process: &process.HttpServeProcess{RouteUrl: "/test"}}
|
||||
r6 := &Rule{Handler: &handler.HttpsHandler{}, Process: &process.HttpsProxyProcess{}}
|
||||
s := make(Sort, 0)
|
||||
s = append(s, r1, r2, r3, r4, r5, r6)
|
||||
sort.Sort(s)
|
||||
expected := make(Sort, 0)
|
||||
expected = append(expected, r6, r5, r3, r4, r1, r2)
|
||||
for k, v := range expected {
|
||||
if v != s[k] {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user