add new file

This commit is contained in:
he liu
2022-01-23 17:30:38 +08:00
parent 05b2e55f39
commit c482967f8c
125 changed files with 9688 additions and 0 deletions

138
core/rule/list.go Normal file
View 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
View 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
View 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
View 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()
}

View 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
View 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
View 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
View 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()
}
}
}