mirror of
https://github.com/ehang-io/nps.git
synced 2025-09-08 00:26:52 +00:00
remove old file
This commit is contained in:
102
lib/cache/lru.go
vendored
102
lib/cache/lru.go
vendored
@@ -1,102 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Cache is an LRU cache. It is safe for concurrent access.
|
||||
type Cache struct {
|
||||
// MaxEntries is the maximum number of cache entries before
|
||||
// an item is evicted. Zero means no limit.
|
||||
MaxEntries int
|
||||
|
||||
//Execute this callback function when an element is culled
|
||||
OnEvicted func(key Key, value interface{})
|
||||
|
||||
ll *list.List //list
|
||||
cache sync.Map
|
||||
}
|
||||
|
||||
// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
|
||||
type Key interface{}
|
||||
|
||||
type entry struct {
|
||||
key Key
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// New creates a new Cache.
|
||||
// If maxEntries is 0, the cache has no length limit.
|
||||
// that eviction is done by the caller.
|
||||
func New(maxEntries int) *Cache {
|
||||
return &Cache{
|
||||
MaxEntries: maxEntries,
|
||||
ll: list.New(),
|
||||
//cache: make(map[interface{}]*list.Element),
|
||||
}
|
||||
}
|
||||
|
||||
// If the key value already exists, move the key to the front
|
||||
func (c *Cache) Add(key Key, value interface{}) {
|
||||
if ee, ok := c.cache.Load(key); ok {
|
||||
c.ll.MoveToFront(ee.(*list.Element)) // move to the front
|
||||
ee.(*list.Element).Value.(*entry).value = value
|
||||
return
|
||||
}
|
||||
ele := c.ll.PushFront(&entry{key, value})
|
||||
c.cache.Store(key, ele)
|
||||
if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { // Remove the oldest element if the limit is exceeded
|
||||
c.RemoveOldest()
|
||||
}
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *Cache) Get(key Key) (value interface{}, ok bool) {
|
||||
if ele, hit := c.cache.Load(key); hit {
|
||||
c.ll.MoveToFront(ele.(*list.Element))
|
||||
return ele.(*list.Element).Value.(*entry).value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache.
|
||||
func (c *Cache) Remove(key Key) {
|
||||
if ele, hit := c.cache.Load(key); hit {
|
||||
c.removeElement(ele.(*list.Element))
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveOldest removes the oldest item from the cache.
|
||||
func (c *Cache) RemoveOldest() {
|
||||
ele := c.ll.Back()
|
||||
if ele != nil {
|
||||
c.removeElement(ele)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) removeElement(e *list.Element) {
|
||||
c.ll.Remove(e)
|
||||
kv := e.Value.(*entry)
|
||||
c.cache.Delete(kv.key)
|
||||
if c.OnEvicted != nil {
|
||||
c.OnEvicted(kv.key, kv.value)
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (c *Cache) Len() int {
|
||||
return c.ll.Len()
|
||||
}
|
||||
|
||||
// Clear purges all stored items from the cache.
|
||||
func (c *Cache) Clear() {
|
||||
if c.OnEvicted != nil {
|
||||
c.cache.Range(func(key, value interface{}) bool {
|
||||
kv := value.(*list.Element).Value.(*entry)
|
||||
c.OnEvicted(kv.key, kv.value)
|
||||
return true
|
||||
})
|
||||
}
|
||||
c.ll = nil
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
package common
|
||||
|
||||
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
|
||||
|
||||
`
|
||||
)
|
@@ -1,48 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/logs"
|
||||
"time"
|
||||
)
|
||||
|
||||
const MaxMsgLen = 5000
|
||||
|
||||
var logMsgs string
|
||||
|
||||
func init() {
|
||||
logs.Register("store", func() logs.Logger {
|
||||
return new(StoreMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func GetLogMsg() string {
|
||||
return logMsgs
|
||||
}
|
||||
|
||||
type StoreMsg struct {
|
||||
}
|
||||
|
||||
func (lg *StoreMsg) Init(config string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lg *StoreMsg) WriteMsg(when time.Time, msg string, level int) error {
|
||||
m := when.Format("2006-01-02 15:04:05") + " " + msg + "\r\n"
|
||||
if len(logMsgs) > MaxMsgLen {
|
||||
start := MaxMsgLen - len(m)
|
||||
if start <= 0 {
|
||||
start = MaxMsgLen
|
||||
}
|
||||
logMsgs = logMsgs[start:]
|
||||
}
|
||||
logMsgs += m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lg *StoreMsg) Destroy() {
|
||||
return
|
||||
}
|
||||
|
||||
func (lg *StoreMsg) Flush() {
|
||||
return
|
||||
}
|
@@ -1,219 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type NetPackager interface {
|
||||
Pack(writer io.Writer) (err error)
|
||||
UnPack(reader io.Reader) (err error)
|
||||
}
|
||||
|
||||
const (
|
||||
ipV4 = 1
|
||||
domainName = 3
|
||||
ipV6 = 4
|
||||
)
|
||||
|
||||
type UDPHeader struct {
|
||||
Rsv uint16
|
||||
Frag uint8
|
||||
Addr *Addr
|
||||
}
|
||||
|
||||
func NewUDPHeader(rsv uint16, frag uint8, addr *Addr) *UDPHeader {
|
||||
return &UDPHeader{
|
||||
Rsv: rsv,
|
||||
Frag: frag,
|
||||
Addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
type Addr struct {
|
||||
Type uint8
|
||||
Host string
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func (addr *Addr) String() string {
|
||||
return net.JoinHostPort(addr.Host, strconv.Itoa(int(addr.Port)))
|
||||
}
|
||||
|
||||
func (addr *Addr) Decode(b []byte) error {
|
||||
addr.Type = b[0]
|
||||
pos := 1
|
||||
switch addr.Type {
|
||||
case ipV4:
|
||||
addr.Host = net.IP(b[pos : pos+net.IPv4len]).String()
|
||||
pos += net.IPv4len
|
||||
case ipV6:
|
||||
addr.Host = net.IP(b[pos : pos+net.IPv6len]).String()
|
||||
pos += net.IPv6len
|
||||
case domainName:
|
||||
addrlen := int(b[pos])
|
||||
pos++
|
||||
addr.Host = string(b[pos : pos+addrlen])
|
||||
pos += addrlen
|
||||
default:
|
||||
return errors.New("decode error")
|
||||
}
|
||||
|
||||
addr.Port = binary.BigEndian.Uint16(b[pos:])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (addr *Addr) Encode(b []byte) (int, error) {
|
||||
b[0] = addr.Type
|
||||
pos := 1
|
||||
switch addr.Type {
|
||||
case ipV4:
|
||||
ip4 := net.ParseIP(addr.Host).To4()
|
||||
if ip4 == nil {
|
||||
ip4 = net.IPv4zero.To4()
|
||||
}
|
||||
pos += copy(b[pos:], ip4)
|
||||
case domainName:
|
||||
b[pos] = byte(len(addr.Host))
|
||||
pos++
|
||||
pos += copy(b[pos:], []byte(addr.Host))
|
||||
case ipV6:
|
||||
ip16 := net.ParseIP(addr.Host).To16()
|
||||
if ip16 == nil {
|
||||
ip16 = net.IPv6zero.To16()
|
||||
}
|
||||
pos += copy(b[pos:], ip16)
|
||||
default:
|
||||
b[0] = ipV4
|
||||
copy(b[pos:pos+4], net.IPv4zero.To4())
|
||||
pos += 4
|
||||
}
|
||||
binary.BigEndian.PutUint16(b[pos:], addr.Port)
|
||||
pos += 2
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
func (h *UDPHeader) Write(w io.Writer) error {
|
||||
b := BufPoolUdp.Get().([]byte)
|
||||
defer BufPoolUdp.Put(b)
|
||||
|
||||
binary.BigEndian.PutUint16(b[:2], h.Rsv)
|
||||
b[2] = h.Frag
|
||||
|
||||
addr := h.Addr
|
||||
if addr == nil {
|
||||
addr = &Addr{}
|
||||
}
|
||||
length, _ := addr.Encode(b[3:])
|
||||
|
||||
_, err := w.Write(b[:3+length])
|
||||
return err
|
||||
}
|
||||
|
||||
type UDPDatagram struct {
|
||||
Header *UDPHeader
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func ReadUDPDatagram(r io.Reader) (*UDPDatagram, error) {
|
||||
b := BufPoolUdp.Get().([]byte)
|
||||
defer BufPoolUdp.Put(b)
|
||||
|
||||
// when r is a streaming (such as TCP connection), we may read more than the required data,
|
||||
// but we don't know how to handle it. So we use io.ReadFull to instead of io.ReadAtLeast
|
||||
// to make sure that no redundant data will be discarded.
|
||||
n, err := io.ReadFull(r, b[:5])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header := &UDPHeader{
|
||||
Rsv: binary.BigEndian.Uint16(b[:2]),
|
||||
Frag: b[2],
|
||||
}
|
||||
|
||||
atype := b[3]
|
||||
hlen := 0
|
||||
switch atype {
|
||||
case ipV4:
|
||||
hlen = 10
|
||||
case ipV6:
|
||||
hlen = 22
|
||||
case domainName:
|
||||
hlen = 7 + int(b[4])
|
||||
default:
|
||||
return nil, errors.New("addr not support")
|
||||
}
|
||||
dlen := int(header.Rsv)
|
||||
if dlen == 0 { // standard SOCKS5 UDP datagram
|
||||
extra, err := ioutil.ReadAll(r) // we assume no redundant data
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[n:], extra)
|
||||
n += len(extra) // total length
|
||||
dlen = n - hlen // data length
|
||||
} else { // extended feature, for UDP over TCP, using reserved field as data length
|
||||
if _, err := io.ReadFull(r, b[n:hlen+dlen]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n = hlen + dlen
|
||||
}
|
||||
header.Addr = new(Addr)
|
||||
if err := header.Addr.Decode(b[3:hlen]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]byte, dlen)
|
||||
copy(data, b[hlen:n])
|
||||
d := &UDPDatagram{
|
||||
Header: header,
|
||||
Data: data,
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func NewUDPDatagram(header *UDPHeader, data []byte) *UDPDatagram {
|
||||
return &UDPDatagram{
|
||||
Header: header,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *UDPDatagram) Write(w io.Writer) error {
|
||||
h := d.Header
|
||||
if h == nil {
|
||||
h = &UDPHeader{}
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
if err := h.Write(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := buf.Write(d.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := buf.WriteTo(w)
|
||||
return err
|
||||
}
|
||||
|
||||
func ToSocksAddr(addr net.Addr) *Addr {
|
||||
host := "0.0.0.0"
|
||||
port := 0
|
||||
if addr != nil {
|
||||
h, p, _ := net.SplitHostPort(addr.String())
|
||||
host = h
|
||||
port, _ = strconv.Atoi(p)
|
||||
}
|
||||
return &Addr{
|
||||
Type: ipV4,
|
||||
Host: host,
|
||||
Port: uint16(port),
|
||||
}
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const PoolSize = 64 * 1024
|
||||
const PoolSizeSmall = 100
|
||||
const PoolSizeUdp = 1472 + 200
|
||||
const PoolSizeCopy = 32 << 10
|
||||
|
||||
var BufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, PoolSize)
|
||||
},
|
||||
}
|
||||
|
||||
var BufPoolUdp = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, PoolSizeUdp)
|
||||
},
|
||||
}
|
||||
var BufPoolMax = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, PoolSize)
|
||||
},
|
||||
}
|
||||
var BufPoolSmall = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, PoolSizeSmall)
|
||||
},
|
||||
}
|
||||
var BufPoolCopy = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, PoolSizeCopy)
|
||||
},
|
||||
}
|
||||
|
||||
func PutBufPoolUdp(buf []byte) {
|
||||
if cap(buf) == PoolSizeUdp {
|
||||
BufPoolUdp.Put(buf[:PoolSizeUdp])
|
||||
}
|
||||
}
|
||||
|
||||
func PutBufPoolCopy(buf []byte) {
|
||||
if cap(buf) == PoolSizeCopy {
|
||||
BufPoolCopy.Put(buf[:PoolSizeCopy])
|
||||
}
|
||||
}
|
||||
|
||||
func GetBufPoolCopy() []byte {
|
||||
return (BufPoolCopy.Get().([]byte))[:PoolSizeCopy]
|
||||
}
|
||||
|
||||
func PutBufPoolMax(buf []byte) {
|
||||
if cap(buf) == PoolSize {
|
||||
BufPoolMax.Put(buf[:PoolSize])
|
||||
}
|
||||
}
|
||||
|
||||
type copyBufferPool struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
func (Self *copyBufferPool) New() {
|
||||
Self.pool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, PoolSizeCopy, PoolSizeCopy)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (Self *copyBufferPool) Get() []byte {
|
||||
buf := Self.pool.Get().([]byte)
|
||||
return buf[:PoolSizeCopy] // just like make a new slice, but data may not be 0
|
||||
}
|
||||
|
||||
func (Self *copyBufferPool) Put(x []byte) {
|
||||
if len(x) == PoolSizeCopy {
|
||||
Self.pool.Put(x)
|
||||
} else {
|
||||
x = nil // buf is not full, not allowed, New method returns a full buf
|
||||
}
|
||||
}
|
||||
|
||||
var once = sync.Once{}
|
||||
var CopyBuff = copyBufferPool{}
|
||||
|
||||
func newPool() {
|
||||
CopyBuff.New()
|
||||
}
|
||||
|
||||
func init() {
|
||||
once.Do(newPool)
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
func InitPProfFromFile() {
|
||||
ip := beego.AppConfig.String("pprof_ip")
|
||||
p := beego.AppConfig.String("pprof_port")
|
||||
if len(ip) > 0 && len(p) > 0 && IsPort(p) {
|
||||
runPProf(ip + ":" + p)
|
||||
}
|
||||
}
|
||||
|
||||
func InitPProfFromArg(arg string) {
|
||||
if len(arg) > 0 {
|
||||
runPProf(arg)
|
||||
}
|
||||
}
|
||||
|
||||
func runPProf(ipPort string) {
|
||||
go func() {
|
||||
_ = http.ListenAndServe(ipPort, nil)
|
||||
}()
|
||||
logs.Info("PProf debug listen on", ipPort)
|
||||
}
|
@@ -1,89 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
//Get the currently selected configuration file directory
|
||||
//For non-Windows systems, select the /etc/nps as config directory if exist, or select ./
|
||||
//windows system, select the C:\Program Files\nps as config directory if exist, or select ./
|
||||
func GetRunPath() string {
|
||||
var path string
|
||||
if path = GetInstallPath(); !FileExists(path) {
|
||||
return GetAppPath()
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
//Different systems get different installation paths
|
||||
func GetInstallPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = `C:\Program Files\nps`
|
||||
} else {
|
||||
path = "/etc/nps"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
//Get the absolute path to the running directory
|
||||
func GetAppPath() string {
|
||||
if path, err := filepath.Abs(filepath.Dir(os.Args[0])); err == nil {
|
||||
return path
|
||||
}
|
||||
return os.Args[0]
|
||||
}
|
||||
|
||||
//Determine whether the current system is a Windows system?
|
||||
func IsWindows() bool {
|
||||
if runtime.GOOS == "windows" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//interface log file path
|
||||
func GetLogPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = filepath.Join(GetAppPath(), "nps.log")
|
||||
} else {
|
||||
path = "/var/log/nps.log"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
//interface npc log file path
|
||||
func GetNpcLogPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = filepath.Join(GetAppPath(), "npc.log")
|
||||
} else {
|
||||
path = "/var/log/npc.log"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
//interface pid file path
|
||||
func GetTmpPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = GetAppPath()
|
||||
} else {
|
||||
path = "/tmp"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
//config file path
|
||||
func GetConfigPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = filepath.Join(GetAppPath(), "conf/npc.conf")
|
||||
} else {
|
||||
path = "conf/npc.conf"
|
||||
}
|
||||
return path
|
||||
}
|
@@ -1,469 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"ehang.io/nps/lib/version"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"ehang.io/nps/lib/crypt"
|
||||
)
|
||||
|
||||
//Get the corresponding IP address through domain name
|
||||
func GetHostByName(hostname string) string {
|
||||
if !DomainCheck(hostname) {
|
||||
return hostname
|
||||
}
|
||||
ips, _ := net.LookupIP(hostname)
|
||||
if ips != nil {
|
||||
for _, v := range ips {
|
||||
if v.To4() != nil {
|
||||
return v.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
//Check the legality of domain
|
||||
func DomainCheck(domain string) bool {
|
||||
var match bool
|
||||
IsLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/)"
|
||||
NotLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}"
|
||||
match, _ = regexp.MatchString(IsLine, domain)
|
||||
if !match {
|
||||
match, _ = regexp.MatchString(NotLine, domain)
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
//Check if the Request request is validated
|
||||
func CheckAuth(r *http.Request, user, passwd string) bool {
|
||||
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||
if len(s) != 2 {
|
||||
s = strings.SplitN(r.Header.Get("Proxy-Authorization"), " ", 2)
|
||||
if len(s) != 2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pair := strings.SplitN(string(b), ":", 2)
|
||||
if len(pair) != 2 {
|
||||
return false
|
||||
}
|
||||
return pair[0] == user && pair[1] == passwd
|
||||
}
|
||||
|
||||
//get bool by str
|
||||
func GetBoolByStr(s string) bool {
|
||||
switch s {
|
||||
case "1", "true":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//get str by bool
|
||||
func GetStrByBool(b bool) string {
|
||||
if b {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
//int
|
||||
func GetIntNoErrByStr(str string) int {
|
||||
i, _ := strconv.Atoi(strings.TrimSpace(str))
|
||||
return i
|
||||
}
|
||||
|
||||
//Get verify value
|
||||
func Getverifyval(vkey string) string {
|
||||
return crypt.Md5(vkey)
|
||||
}
|
||||
|
||||
//Change headers and host of request
|
||||
func ChangeHostAndHeader(r *http.Request, host string, header string, addr string, addOrigin bool) {
|
||||
if host != "" {
|
||||
r.Host = host
|
||||
}
|
||||
if header != "" {
|
||||
h := strings.Split(header, "\n")
|
||||
for _, v := range h {
|
||||
hd := strings.Split(v, ":")
|
||||
if len(hd) == 2 {
|
||||
r.Header.Set(hd[0], hd[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
addr = strings.Split(addr, ":")[0]
|
||||
if prior, ok := r.Header["X-Forwarded-For"]; ok {
|
||||
addr = strings.Join(prior, ", ") + ", " + addr
|
||||
}
|
||||
if addOrigin {
|
||||
r.Header.Set("X-Forwarded-For", addr)
|
||||
r.Header.Set("X-Real-IP", addr)
|
||||
}
|
||||
}
|
||||
|
||||
//Read file content by file path
|
||||
func ReadAllFromFile(filePath string) ([]byte, error) {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
|
||||
// FileExists reports whether the named file or directory exists.
|
||||
func FileExists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//Judge whether the TCP port can open normally
|
||||
func TestTcpPort(port int) bool {
|
||||
l, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), port, ""})
|
||||
defer func() {
|
||||
if l != nil {
|
||||
l.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//Judge whether the UDP port can open normally
|
||||
func TestUdpPort(port int) bool {
|
||||
l, err := net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), port, ""})
|
||||
defer func() {
|
||||
if l != nil {
|
||||
l.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//Write length and individual byte data
|
||||
//Length prevents sticking
|
||||
//# Characters are used to separate data
|
||||
func BinaryWrite(raw *bytes.Buffer, v ...string) {
|
||||
b := GetWriteStr(v...)
|
||||
binary.Write(raw, binary.LittleEndian, int32(len(b)))
|
||||
binary.Write(raw, binary.LittleEndian, b)
|
||||
}
|
||||
|
||||
// get seq str
|
||||
func GetWriteStr(v ...string) []byte {
|
||||
buffer := new(bytes.Buffer)
|
||||
var l int32
|
||||
for _, v := range v {
|
||||
l += int32(len([]byte(v))) + int32(len([]byte(CONN_DATA_SEQ)))
|
||||
binary.Write(buffer, binary.LittleEndian, []byte(v))
|
||||
binary.Write(buffer, binary.LittleEndian, []byte(CONN_DATA_SEQ))
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
//inArray str interface
|
||||
func InStrArr(arr []string, val string) bool {
|
||||
for _, v := range arr {
|
||||
if v == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//inArray int interface
|
||||
func InIntArr(arr []int, val int) bool {
|
||||
for _, v := range arr {
|
||||
if v == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//format ports str to a int array
|
||||
func GetPorts(p string) []int {
|
||||
var ps []int
|
||||
arr := strings.Split(p, ",")
|
||||
for _, v := range arr {
|
||||
fw := strings.Split(v, "-")
|
||||
if len(fw) == 2 {
|
||||
if IsPort(fw[0]) && IsPort(fw[1]) {
|
||||
start, _ := strconv.Atoi(fw[0])
|
||||
end, _ := strconv.Atoi(fw[1])
|
||||
for i := start; i <= end; i++ {
|
||||
ps = append(ps, i)
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if IsPort(v) {
|
||||
p, _ := strconv.Atoi(v)
|
||||
ps = append(ps, p)
|
||||
}
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
//is the string a port
|
||||
func IsPort(p string) bool {
|
||||
pi, err := strconv.Atoi(p)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if pi > 65536 || pi < 1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//if the s is just a port,return 127.0.0.1:s
|
||||
func FormatAddress(s string) string {
|
||||
if strings.Contains(s, ":") {
|
||||
return s
|
||||
}
|
||||
return "127.0.0.1:" + s
|
||||
}
|
||||
|
||||
//get address from the complete address
|
||||
func GetIpByAddr(addr string) string {
|
||||
arr := strings.Split(addr, ":")
|
||||
return arr[0]
|
||||
}
|
||||
|
||||
//get port from the complete address
|
||||
func GetPortByAddr(addr string) int {
|
||||
arr := strings.Split(addr, ":")
|
||||
if len(arr) < 2 {
|
||||
return 0
|
||||
}
|
||||
p, err := strconv.Atoi(arr[1])
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func CopyBuffer(dst io.Writer, src io.Reader, label ...string) (written int64, err error) {
|
||||
buf := CopyBuff.Get()
|
||||
defer CopyBuff.Put(buf)
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
//if len(pr)>0 && pr[0] && nr > 50 {
|
||||
// logs.Warn(string(buf[:50]))
|
||||
//}
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
err = er
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
}
|
||||
|
||||
//send this ip forget to get a local udp port
|
||||
func GetLocalUdpAddr() (net.Conn, error) {
|
||||
tmpConn, err := net.Dial("udp", "114.114.114.114:53")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tmpConn, tmpConn.Close()
|
||||
}
|
||||
|
||||
//parse template
|
||||
func ParseStr(str string) (string, error) {
|
||||
tmp := template.New("npc")
|
||||
var err error
|
||||
w := new(bytes.Buffer)
|
||||
if tmp, err = tmp.Parse(str); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = tmp.Execute(w, GetEnvMap()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return w.String(), nil
|
||||
}
|
||||
|
||||
//get env
|
||||
func GetEnvMap() map[string]string {
|
||||
m := make(map[string]string)
|
||||
environ := os.Environ()
|
||||
for i := range environ {
|
||||
tmp := strings.Split(environ[i], "=")
|
||||
if len(tmp) == 2 {
|
||||
m[tmp[0]] = tmp[1]
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
//throw the empty element of the string array
|
||||
func TrimArr(arr []string) []string {
|
||||
newArr := make([]string, 0)
|
||||
for _, v := range arr {
|
||||
if v != "" {
|
||||
newArr = append(newArr, v)
|
||||
}
|
||||
}
|
||||
return newArr
|
||||
}
|
||||
|
||||
//
|
||||
func IsArrContains(arr []string, val string) bool {
|
||||
if arr == nil {
|
||||
return false
|
||||
}
|
||||
for _, v := range arr {
|
||||
if v == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//remove value from string array
|
||||
func RemoveArrVal(arr []string, val string) []string {
|
||||
for k, v := range arr {
|
||||
if v == val {
|
||||
arr = append(arr[:k], arr[k+1:]...)
|
||||
return arr
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
//convert bytes to num
|
||||
func BytesToNum(b []byte) int {
|
||||
var str string
|
||||
for i := 0; i < len(b); i++ {
|
||||
str += strconv.Itoa(int(b[i]))
|
||||
}
|
||||
x, _ := strconv.Atoi(str)
|
||||
return int(x)
|
||||
}
|
||||
|
||||
//get the length of the sync map
|
||||
func GeSynctMapLen(m sync.Map) int {
|
||||
var c int
|
||||
m.Range(func(key, value interface{}) bool {
|
||||
c++
|
||||
return true
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
func GetExtFromPath(path string) string {
|
||||
s := strings.Split(path, ".")
|
||||
re, err := regexp.Compile(`(\w+)`)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(re.Find([]byte(s[0])))
|
||||
}
|
||||
|
||||
var externalIp string
|
||||
|
||||
func GetExternalIp() string {
|
||||
if externalIp != "" {
|
||||
return externalIp
|
||||
}
|
||||
resp, err := http.Get("http://myexternalip.com/raw")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
content, _ := ioutil.ReadAll(resp.Body)
|
||||
externalIp = string(content)
|
||||
return externalIp
|
||||
}
|
||||
|
||||
func GetIntranetIp() (error, string) {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return nil, ""
|
||||
}
|
||||
for _, address := range addrs {
|
||||
// 检查ip地址判断是否回环地址
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return nil, ipnet.IP.To4().String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("get intranet ip error"), ""
|
||||
}
|
||||
|
||||
func IsPublicIP(IP net.IP) bool {
|
||||
if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
|
||||
return false
|
||||
}
|
||||
if ip4 := IP.To4(); ip4 != nil {
|
||||
switch true {
|
||||
case ip4[0] == 10:
|
||||
return false
|
||||
case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
|
||||
return false
|
||||
case ip4[0] == 192 && ip4[1] == 168:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetServerIpByClientIp(clientIp net.IP) string {
|
||||
if IsPublicIP(clientIp) {
|
||||
return GetExternalIp()
|
||||
}
|
||||
_, ip := GetIntranetIp()
|
||||
return ip
|
||||
}
|
||||
|
||||
func PrintVersion() {
|
||||
fmt.Printf("Version: %s\nCore version: %s\nSame core version of client and server can connect each other\n", version.VERSION, version.GetVersion())
|
||||
}
|
@@ -1,329 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
)
|
||||
|
||||
type CommonConfig struct {
|
||||
Server string
|
||||
VKey string
|
||||
Tp string //bridgeType kcp or tcp
|
||||
AutoReconnection bool
|
||||
ProxyUrl string
|
||||
Client *file.Client
|
||||
DisconnectTime int
|
||||
}
|
||||
|
||||
type LocalServer struct {
|
||||
Type string
|
||||
Port int
|
||||
Ip string
|
||||
Password string
|
||||
Target string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
content string
|
||||
title []string
|
||||
CommonConfig *CommonConfig
|
||||
Hosts []*file.Host
|
||||
Tasks []*file.Tunnel
|
||||
Healths []*file.Health
|
||||
LocalServer []*LocalServer
|
||||
}
|
||||
|
||||
func NewConfig(path string) (c *Config, err error) {
|
||||
c = new(Config)
|
||||
var b []byte
|
||||
if b, err = common.ReadAllFromFile(path); err != nil {
|
||||
return
|
||||
} else {
|
||||
if c.content, err = common.ParseStr(string(b)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.title, err = getAllTitle(c.content); err != nil {
|
||||
return
|
||||
}
|
||||
var nowIndex int
|
||||
var nextIndex int
|
||||
var nowContent string
|
||||
for i := 0; i < len(c.title); i++ {
|
||||
nowIndex = strings.Index(c.content, c.title[i]) + len(c.title[i])
|
||||
if i < len(c.title)-1 {
|
||||
nextIndex = strings.Index(c.content, c.title[i+1])
|
||||
} else {
|
||||
nextIndex = len(c.content)
|
||||
}
|
||||
nowContent = c.content[nowIndex:nextIndex]
|
||||
|
||||
if strings.Index(getTitleContent(c.title[i]), "secret") == 0 && !strings.Contains(nowContent, "mode") {
|
||||
local := delLocalService(nowContent)
|
||||
local.Type = "secret"
|
||||
c.LocalServer = append(c.LocalServer, local)
|
||||
continue
|
||||
}
|
||||
//except mode
|
||||
if strings.Index(getTitleContent(c.title[i]), "p2p") == 0 && !strings.Contains(nowContent, "mode") {
|
||||
local := delLocalService(nowContent)
|
||||
local.Type = "p2p"
|
||||
c.LocalServer = append(c.LocalServer, local)
|
||||
continue
|
||||
}
|
||||
//health set
|
||||
if strings.Index(getTitleContent(c.title[i]), "health") == 0 {
|
||||
c.Healths = append(c.Healths, dealHealth(nowContent))
|
||||
continue
|
||||
}
|
||||
switch c.title[i] {
|
||||
case "[common]":
|
||||
c.CommonConfig = dealCommon(nowContent)
|
||||
default:
|
||||
if strings.Index(nowContent, "host") > -1 {
|
||||
h := dealHost(nowContent)
|
||||
h.Remark = getTitleContent(c.title[i])
|
||||
c.Hosts = append(c.Hosts, h)
|
||||
} else {
|
||||
t := dealTunnel(nowContent)
|
||||
t.Remark = getTitleContent(c.title[i])
|
||||
c.Tasks = append(c.Tasks, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTitleContent(s string) string {
|
||||
re, _ := regexp.Compile(`[\[\]]`)
|
||||
return re.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
func dealCommon(s string) *CommonConfig {
|
||||
c := &CommonConfig{}
|
||||
c.Client = file.NewClient("", true, true)
|
||||
c.Client.Cnf = new(file.Config)
|
||||
for _, v := range splitStr(s) {
|
||||
item := strings.Split(v, "=")
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
} else if len(item) == 1 {
|
||||
item = append(item, "")
|
||||
}
|
||||
switch item[0] {
|
||||
case "server_addr":
|
||||
c.Server = item[1]
|
||||
case "vkey":
|
||||
c.VKey = item[1]
|
||||
case "conn_type":
|
||||
c.Tp = item[1]
|
||||
case "auto_reconnection":
|
||||
c.AutoReconnection = common.GetBoolByStr(item[1])
|
||||
case "basic_username":
|
||||
c.Client.Cnf.U = item[1]
|
||||
case "basic_password":
|
||||
c.Client.Cnf.P = item[1]
|
||||
case "web_password":
|
||||
c.Client.WebPassword = item[1]
|
||||
case "web_username":
|
||||
c.Client.WebUserName = item[1]
|
||||
case "compress":
|
||||
c.Client.Cnf.Compress = common.GetBoolByStr(item[1])
|
||||
case "crypt":
|
||||
c.Client.Cnf.Crypt = common.GetBoolByStr(item[1])
|
||||
case "proxy_url":
|
||||
c.ProxyUrl = item[1]
|
||||
case "rate_limit":
|
||||
c.Client.RateLimit = common.GetIntNoErrByStr(item[1])
|
||||
case "flow_limit":
|
||||
c.Client.Flow.FlowLimit = int64(common.GetIntNoErrByStr(item[1]))
|
||||
case "max_conn":
|
||||
c.Client.MaxConn = common.GetIntNoErrByStr(item[1])
|
||||
case "remark":
|
||||
c.Client.Remark = item[1]
|
||||
case "pprof_addr":
|
||||
common.InitPProfFromArg(item[1])
|
||||
case "disconnect_timeout":
|
||||
c.DisconnectTime = common.GetIntNoErrByStr(item[1])
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func dealHost(s string) *file.Host {
|
||||
h := &file.Host{}
|
||||
h.Target = new(file.Target)
|
||||
h.Scheme = "all"
|
||||
var headerChange string
|
||||
for _, v := range splitStr(s) {
|
||||
item := strings.Split(v, "=")
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
} else if len(item) == 1 {
|
||||
item = append(item, "")
|
||||
}
|
||||
switch strings.TrimSpace(item[0]) {
|
||||
case "host":
|
||||
h.Host = item[1]
|
||||
case "target_addr":
|
||||
h.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1)
|
||||
case "host_change":
|
||||
h.HostChange = item[1]
|
||||
case "scheme":
|
||||
h.Scheme = item[1]
|
||||
case "location":
|
||||
h.Location = item[1]
|
||||
default:
|
||||
if strings.Contains(item[0], "header") {
|
||||
headerChange += strings.Replace(item[0], "header_", "", -1) + ":" + item[1] + "\n"
|
||||
}
|
||||
h.HeaderChange = headerChange
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func dealHealth(s string) *file.Health {
|
||||
h := &file.Health{}
|
||||
for _, v := range splitStr(s) {
|
||||
item := strings.Split(v, "=")
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
} else if len(item) == 1 {
|
||||
item = append(item, "")
|
||||
}
|
||||
switch strings.TrimSpace(item[0]) {
|
||||
case "health_check_timeout":
|
||||
h.HealthCheckTimeout = common.GetIntNoErrByStr(item[1])
|
||||
case "health_check_max_failed":
|
||||
h.HealthMaxFail = common.GetIntNoErrByStr(item[1])
|
||||
case "health_check_interval":
|
||||
h.HealthCheckInterval = common.GetIntNoErrByStr(item[1])
|
||||
case "health_http_url":
|
||||
h.HttpHealthUrl = item[1]
|
||||
case "health_check_type":
|
||||
h.HealthCheckType = item[1]
|
||||
case "health_check_target":
|
||||
h.HealthCheckTarget = item[1]
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func dealTunnel(s string) *file.Tunnel {
|
||||
t := &file.Tunnel{}
|
||||
t.Target = new(file.Target)
|
||||
for _, v := range splitStr(s) {
|
||||
item := strings.Split(v, "=")
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
} else if len(item) == 1 {
|
||||
item = append(item, "")
|
||||
}
|
||||
switch strings.TrimSpace(item[0]) {
|
||||
case "server_port":
|
||||
t.Ports = item[1]
|
||||
case "server_ip":
|
||||
t.ServerIp = item[1]
|
||||
case "mode":
|
||||
t.Mode = item[1]
|
||||
case "target_addr":
|
||||
t.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1)
|
||||
case "target_port":
|
||||
t.Target.TargetStr = item[1]
|
||||
case "target_ip":
|
||||
t.TargetAddr = item[1]
|
||||
case "password":
|
||||
t.Password = item[1]
|
||||
case "local_path":
|
||||
t.LocalPath = item[1]
|
||||
case "strip_pre":
|
||||
t.StripPre = item[1]
|
||||
case "multi_account":
|
||||
t.MultiAccount = &file.MultiAccount{}
|
||||
if common.FileExists(item[1]) {
|
||||
if b, err := common.ReadAllFromFile(item[1]); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
if content, err := common.ParseStr(string(b)); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
t.MultiAccount.AccountMap = dealMultiUser(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return t
|
||||
|
||||
}
|
||||
|
||||
func dealMultiUser(s string) map[string]string {
|
||||
multiUserMap := make(map[string]string)
|
||||
for _, v := range splitStr(s) {
|
||||
item := strings.Split(v, "=")
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
} else if len(item) == 1 {
|
||||
item = append(item, "")
|
||||
}
|
||||
multiUserMap[strings.TrimSpace(item[0])] = item[1]
|
||||
}
|
||||
return multiUserMap
|
||||
}
|
||||
|
||||
func delLocalService(s string) *LocalServer {
|
||||
l := new(LocalServer)
|
||||
for _, v := range splitStr(s) {
|
||||
item := strings.Split(v, "=")
|
||||
if len(item) == 0 {
|
||||
continue
|
||||
} else if len(item) == 1 {
|
||||
item = append(item, "")
|
||||
}
|
||||
switch item[0] {
|
||||
case "local_port":
|
||||
l.Port = common.GetIntNoErrByStr(item[1])
|
||||
case "local_ip":
|
||||
l.Ip = item[1]
|
||||
case "password":
|
||||
l.Password = item[1]
|
||||
case "target_addr":
|
||||
l.Target = item[1]
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func getAllTitle(content string) (arr []string, err error) {
|
||||
var re *regexp.Regexp
|
||||
re, err = regexp.Compile(`(?m)^\[[^\[\]\r\n]+\]`)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
arr = re.FindAllString(content, -1)
|
||||
m := make(map[string]bool)
|
||||
for _, v := range arr {
|
||||
if _, ok := m[v]; ok {
|
||||
err = errors.New(fmt.Sprintf("Item names %s are not allowed to be duplicated", v))
|
||||
return
|
||||
}
|
||||
m[v] = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func splitStr(s string) (configDataArr []string) {
|
||||
if common.IsWindows() {
|
||||
configDataArr = strings.Split(s, "\r\n")
|
||||
}
|
||||
if len(configDataArr) < 3 {
|
||||
configDataArr = strings.Split(s, "\n")
|
||||
}
|
||||
return
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReg(t *testing.T) {
|
||||
content := `
|
||||
[common]
|
||||
server=127.0.0.1:8284
|
||||
tp=tcp
|
||||
vkey=123
|
||||
[web2]
|
||||
host=www.baidu.com
|
||||
host_change=www.sina.com
|
||||
target=127.0.0.1:8080,127.0.0.1:8082
|
||||
header_cookkile=122123
|
||||
header_user-Agent=122123
|
||||
[web2]
|
||||
host=www.baidu.com
|
||||
host_change=www.sina.com
|
||||
target=127.0.0.1:8080,127.0.0.1:8082
|
||||
header_cookkile="122123"
|
||||
header_user-Agent=122123
|
||||
[tunnel1]
|
||||
type=udp
|
||||
target=127.0.0.1:8080
|
||||
port=9001
|
||||
compress=snappy
|
||||
crypt=true
|
||||
u=1
|
||||
p=2
|
||||
[tunnel2]
|
||||
type=tcp
|
||||
target=127.0.0.1:8080
|
||||
port=9001
|
||||
compress=snappy
|
||||
crypt=true
|
||||
u=1
|
||||
p=2
|
||||
`
|
||||
re, err := regexp.Compile(`\[.+?\]`)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
log.Println(re.FindAllString(content, -1))
|
||||
}
|
||||
|
||||
func TestDealCommon(t *testing.T) {
|
||||
s := `server=127.0.0.1:8284
|
||||
tp=tcp
|
||||
vkey=123`
|
||||
f := new(CommonConfig)
|
||||
f.Server = "127.0.0.1:8284"
|
||||
f.Tp = "tcp"
|
||||
f.VKey = "123"
|
||||
if c := dealCommon(s); *c != *f {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTitleContent(t *testing.T) {
|
||||
s := "[common]"
|
||||
if getTitleContent(s) != "common" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
431
lib/conn/conn.go
431
lib/conn/conn.go
@@ -1,431 +0,0 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"ehang.io/nps/lib/goroutine"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/lib/pmux"
|
||||
"ehang.io/nps/lib/rate"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
Conn net.Conn
|
||||
Rb []byte
|
||||
}
|
||||
|
||||
//new conn
|
||||
func NewConn(conn net.Conn) *Conn {
|
||||
return &Conn{Conn: conn}
|
||||
}
|
||||
|
||||
func (s *Conn) readRequest(buf []byte) (n int, err error) {
|
||||
var rd int
|
||||
for {
|
||||
rd, err = s.Read(buf[n:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += rd
|
||||
if n < 4 {
|
||||
continue
|
||||
}
|
||||
if string(buf[n-4:n]) == "\r\n\r\n" {
|
||||
return
|
||||
}
|
||||
// buf is full, can't contain the request
|
||||
if n == cap(buf) {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//get host 、connection type、method...from connection
|
||||
func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) {
|
||||
var b [32 * 1024]byte
|
||||
var n int
|
||||
if n, err = s.readRequest(b[:]); err != nil {
|
||||
return
|
||||
}
|
||||
rb = b[:n]
|
||||
r, err = http.ReadRequest(bufio.NewReader(bytes.NewReader(rb)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
hostPortURL, err := url.Parse(r.Host)
|
||||
if err != nil {
|
||||
address = r.Host
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
if hostPortURL.Opaque == "443" {
|
||||
if strings.Index(r.Host, ":") == -1 {
|
||||
address = r.Host + ":443"
|
||||
} else {
|
||||
address = r.Host
|
||||
}
|
||||
} else {
|
||||
if strings.Index(r.Host, ":") == -1 {
|
||||
address = r.Host + ":80"
|
||||
} else {
|
||||
address = r.Host
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Conn) GetShortLenContent() (b []byte, err error) {
|
||||
var l int
|
||||
if l, err = s.GetLen(); err != nil {
|
||||
return
|
||||
}
|
||||
if l < 0 || l > 32<<10 {
|
||||
err = errors.New("read length error")
|
||||
return
|
||||
}
|
||||
return s.GetShortContent(l)
|
||||
}
|
||||
|
||||
func (s *Conn) GetShortContent(l int) (b []byte, err error) {
|
||||
buf := make([]byte, l)
|
||||
return buf, binary.Read(s, binary.LittleEndian, &buf)
|
||||
}
|
||||
|
||||
//读取指定长度内容
|
||||
func (s *Conn) ReadLen(cLen int, buf []byte) (int, error) {
|
||||
if cLen > len(buf) || cLen <= 0 {
|
||||
return 0, errors.New("长度错误" + strconv.Itoa(cLen))
|
||||
}
|
||||
if n, err := io.ReadFull(s, buf[:cLen]); err != nil || n != cLen {
|
||||
return n, errors.New("Error reading specified length " + err.Error())
|
||||
}
|
||||
return cLen, nil
|
||||
}
|
||||
|
||||
func (s *Conn) GetLen() (int, error) {
|
||||
var l int32
|
||||
err := binary.Read(s, binary.LittleEndian, &l)
|
||||
return int(l), err
|
||||
}
|
||||
|
||||
func (s *Conn) WriteLenContent(buf []byte) (err error) {
|
||||
var b []byte
|
||||
if b, err = GetLenBytes(buf); err != nil {
|
||||
return
|
||||
}
|
||||
return binary.Write(s.Conn, binary.LittleEndian, b)
|
||||
}
|
||||
|
||||
//read flag
|
||||
func (s *Conn) ReadFlag() (string, error) {
|
||||
buf := make([]byte, 4)
|
||||
return string(buf), binary.Read(s, binary.LittleEndian, &buf)
|
||||
}
|
||||
|
||||
//set alive
|
||||
func (s *Conn) SetAlive(tp string) {
|
||||
switch s.Conn.(type) {
|
||||
case *kcp.UDPSession:
|
||||
s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Time{})
|
||||
case *net.TCPConn:
|
||||
conn := s.Conn.(*net.TCPConn)
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
//conn.SetKeepAlive(false)
|
||||
//conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
|
||||
case *pmux.PortConn:
|
||||
s.Conn.(*pmux.PortConn).SetReadDeadline(time.Time{})
|
||||
}
|
||||
}
|
||||
|
||||
//set read deadline
|
||||
func (s *Conn) SetReadDeadlineBySecond(t time.Duration) {
|
||||
switch s.Conn.(type) {
|
||||
case *kcp.UDPSession:
|
||||
s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
|
||||
case *net.TCPConn:
|
||||
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
|
||||
case *pmux.PortConn:
|
||||
s.Conn.(*pmux.PortConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
|
||||
}
|
||||
}
|
||||
|
||||
//get link info from conn
|
||||
func (s *Conn) GetLinkInfo() (lk *Link, err error) {
|
||||
err = s.getInfo(&lk)
|
||||
return
|
||||
}
|
||||
|
||||
//send info for link
|
||||
func (s *Conn) SendHealthInfo(info, status string) (int, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
common.BinaryWrite(raw, info, status)
|
||||
return s.Write(raw.Bytes())
|
||||
}
|
||||
|
||||
//get health info from conn
|
||||
func (s *Conn) GetHealthInfo() (info string, status bool, err error) {
|
||||
var l int
|
||||
buf := common.BufPoolMax.Get().([]byte)
|
||||
defer common.PutBufPoolMax(buf)
|
||||
if l, err = s.GetLen(); err != nil {
|
||||
return
|
||||
} else if _, err = s.ReadLen(l, buf); err != nil {
|
||||
return
|
||||
} else {
|
||||
arr := strings.Split(string(buf[:l]), common.CONN_DATA_SEQ)
|
||||
if len(arr) >= 2 {
|
||||
return arr[0], common.GetBoolByStr(arr[1]), nil
|
||||
}
|
||||
}
|
||||
return "", false, errors.New("receive health info error")
|
||||
}
|
||||
|
||||
//get task info
|
||||
func (s *Conn) GetHostInfo() (h *file.Host, err error) {
|
||||
err = s.getInfo(&h)
|
||||
h.Id = int(file.GetDb().JsonDb.GetHostId())
|
||||
h.Flow = new(file.Flow)
|
||||
h.NoStore = true
|
||||
return
|
||||
}
|
||||
|
||||
//get task info
|
||||
func (s *Conn) GetConfigInfo() (c *file.Client, err error) {
|
||||
err = s.getInfo(&c)
|
||||
c.NoStore = true
|
||||
c.Status = true
|
||||
if c.Flow == nil {
|
||||
c.Flow = new(file.Flow)
|
||||
}
|
||||
c.NoDisplay = false
|
||||
return
|
||||
}
|
||||
|
||||
//get task info
|
||||
func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
|
||||
err = s.getInfo(&t)
|
||||
t.Id = int(file.GetDb().JsonDb.GetTaskId())
|
||||
t.NoStore = true
|
||||
t.Flow = new(file.Flow)
|
||||
return
|
||||
}
|
||||
|
||||
//send info
|
||||
func (s *Conn) SendInfo(t interface{}, flag string) (int, error) {
|
||||
/*
|
||||
The task info is formed as follows:
|
||||
+----+-----+---------+
|
||||
|type| len | content |
|
||||
+----+---------------+
|
||||
| 4 | 4 | ... |
|
||||
+----+---------------+
|
||||
*/
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
if flag != "" {
|
||||
binary.Write(raw, binary.LittleEndian, []byte(flag))
|
||||
}
|
||||
b, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
lenBytes, err := GetLenBytes(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
binary.Write(raw, binary.LittleEndian, lenBytes)
|
||||
return s.Write(raw.Bytes())
|
||||
}
|
||||
|
||||
//get task info
|
||||
func (s *Conn) getInfo(t interface{}) (err error) {
|
||||
var l int
|
||||
buf := common.BufPoolMax.Get().([]byte)
|
||||
defer common.PutBufPoolMax(buf)
|
||||
if l, err = s.GetLen(); err != nil {
|
||||
return
|
||||
} else if _, err = s.ReadLen(l, buf); err != nil {
|
||||
return
|
||||
} else {
|
||||
json.Unmarshal(buf[:l], &t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//close
|
||||
func (s *Conn) Close() error {
|
||||
return s.Conn.Close()
|
||||
}
|
||||
|
||||
//write
|
||||
func (s *Conn) Write(b []byte) (int, error) {
|
||||
return s.Conn.Write(b)
|
||||
}
|
||||
|
||||
//read
|
||||
func (s *Conn) Read(b []byte) (n int, err error) {
|
||||
if s.Rb != nil {
|
||||
//if the rb is not nil ,read rb first
|
||||
if len(s.Rb) > 0 {
|
||||
n = copy(b, s.Rb)
|
||||
s.Rb = s.Rb[n:]
|
||||
return
|
||||
}
|
||||
s.Rb = nil
|
||||
}
|
||||
return s.Conn.Read(b)
|
||||
}
|
||||
|
||||
//write sign flag
|
||||
func (s *Conn) WriteClose() (int, error) {
|
||||
return s.Write([]byte(common.RES_CLOSE))
|
||||
}
|
||||
|
||||
//write main
|
||||
func (s *Conn) WriteMain() (int, error) {
|
||||
return s.Write([]byte(common.WORK_MAIN))
|
||||
}
|
||||
|
||||
//write main
|
||||
func (s *Conn) WriteConfig() (int, error) {
|
||||
return s.Write([]byte(common.WORK_CONFIG))
|
||||
}
|
||||
|
||||
//write chan
|
||||
func (s *Conn) WriteChan() (int, error) {
|
||||
return s.Write([]byte(common.WORK_CHAN))
|
||||
}
|
||||
|
||||
//get task or host result of add
|
||||
func (s *Conn) GetAddStatus() (b bool) {
|
||||
binary.Read(s.Conn, binary.LittleEndian, &b)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Conn) WriteAddOk() error {
|
||||
return binary.Write(s.Conn, binary.LittleEndian, true)
|
||||
}
|
||||
|
||||
func (s *Conn) WriteAddFail() error {
|
||||
defer s.Close()
|
||||
return binary.Write(s.Conn, binary.LittleEndian, false)
|
||||
}
|
||||
|
||||
func (s *Conn) LocalAddr() net.Addr {
|
||||
return s.Conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (s *Conn) RemoteAddr() net.Addr {
|
||||
return s.Conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (s *Conn) SetDeadline(t time.Time) error {
|
||||
return s.Conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (s *Conn) SetWriteDeadline(t time.Time) error {
|
||||
return s.Conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (s *Conn) SetReadDeadline(t time.Time) error {
|
||||
return s.Conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
//get the assembled amount data(len 4 and content)
|
||||
func GetLenBytes(buf []byte) (b []byte, err error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
if err = binary.Write(raw, binary.LittleEndian, int32(len(buf))); err != nil {
|
||||
return
|
||||
}
|
||||
if err = binary.Write(raw, binary.LittleEndian, buf); err != nil {
|
||||
return
|
||||
}
|
||||
b = raw.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
//udp connection setting
|
||||
func SetUdpSession(sess *kcp.UDPSession) {
|
||||
sess.SetStreamMode(true)
|
||||
sess.SetWindowSize(1024, 1024)
|
||||
sess.SetReadBuffer(64 * 1024)
|
||||
sess.SetWriteBuffer(64 * 1024)
|
||||
sess.SetNoDelay(1, 10, 2, 1)
|
||||
sess.SetMtu(1600)
|
||||
sess.SetACKNoDelay(true)
|
||||
sess.SetWriteDelay(false)
|
||||
}
|
||||
|
||||
//conn1 mux conn
|
||||
func CopyWaitGroup(conn1, conn2 net.Conn, crypt bool, snappy bool, rate *rate.Rate, flow *file.Flow, isServer bool, rb []byte) {
|
||||
//var in, out int64
|
||||
//var wg sync.WaitGroup
|
||||
connHandle := GetConn(conn1, crypt, snappy, rate, isServer)
|
||||
if rb != nil {
|
||||
connHandle.Write(rb)
|
||||
}
|
||||
//go func(in *int64) {
|
||||
// wg.Add(1)
|
||||
// *in, _ = common.CopyBuffer(connHandle, conn2)
|
||||
// connHandle.Close()
|
||||
// conn2.Close()
|
||||
// wg.Done()
|
||||
//}(&in)
|
||||
//out, _ = common.CopyBuffer(conn2, connHandle)
|
||||
//connHandle.Close()
|
||||
//conn2.Close()
|
||||
//wg.Wait()
|
||||
//if flow != nil {
|
||||
// flow.Add(in, out)
|
||||
//}
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(1)
|
||||
err := goroutine.CopyConnsPool.Invoke(goroutine.NewConns(connHandle, conn2, flow, wg))
|
||||
wg.Wait()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
//get crypt or snappy conn
|
||||
func GetConn(conn net.Conn, cpt, snappy bool, rt *rate.Rate, isServer bool) io.ReadWriteCloser {
|
||||
if cpt {
|
||||
if isServer {
|
||||
return rate.NewRateConn(crypt.NewTlsServerConn(conn), rt)
|
||||
}
|
||||
return rate.NewRateConn(crypt.NewTlsClientConn(conn), rt)
|
||||
} else if snappy {
|
||||
return rate.NewRateConn(NewSnappyConn(conn), rt)
|
||||
}
|
||||
return rate.NewRateConn(conn, rt)
|
||||
}
|
||||
|
||||
type LenConn struct {
|
||||
conn io.Writer
|
||||
Len int
|
||||
}
|
||||
|
||||
func NewLenConn(conn io.Writer) *LenConn {
|
||||
return &LenConn{conn: conn}
|
||||
}
|
||||
func (c *LenConn) Write(p []byte) (n int, err error) {
|
||||
n, err = c.conn.Write(p)
|
||||
c.Len += n
|
||||
return
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
package conn
|
||||
|
||||
import "time"
|
||||
|
||||
type Secret struct {
|
||||
Password string
|
||||
Conn *Conn
|
||||
}
|
||||
|
||||
func NewSecret(p string, conn *Conn) *Secret {
|
||||
return &Secret{
|
||||
Password: p,
|
||||
Conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
ConnType string //连接类型
|
||||
Host string //目标
|
||||
Crypt bool //加密
|
||||
Compress bool
|
||||
LocalProxy bool
|
||||
RemoteAddr string
|
||||
Option Options
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
type Options struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
var defaultTimeOut = time.Second * 5
|
||||
|
||||
func NewLink(connType string, host string, crypt bool, compress bool, remoteAddr string, localProxy bool, opts ...Option) *Link {
|
||||
options := newOptions(opts...)
|
||||
|
||||
return &Link{
|
||||
RemoteAddr: remoteAddr,
|
||||
ConnType: connType,
|
||||
Host: host,
|
||||
Crypt: crypt,
|
||||
Compress: compress,
|
||||
LocalProxy: localProxy,
|
||||
Option: options,
|
||||
}
|
||||
}
|
||||
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{
|
||||
Timeout: defaultTimeOut,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
return opt
|
||||
}
|
||||
|
||||
func LinkTimeout(t time.Duration) Option {
|
||||
return func(opt *Options) {
|
||||
opt.Timeout = t
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
func NewTcpListenerAndProcess(addr string, f func(c net.Conn), listener *net.Listener) error {
|
||||
var err error
|
||||
*listener, err = net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Accept(*listener, f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewKcpListenerAndProcess(addr string, f func(c net.Conn)) error {
|
||||
kcpListener, err := kcp.ListenWithOptions(addr, nil, 150, 3)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return err
|
||||
}
|
||||
for {
|
||||
c, err := kcpListener.AcceptKCP()
|
||||
SetUdpSession(c)
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
continue
|
||||
}
|
||||
go f(c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Accept(l net.Listener, f func(c net.Conn)) {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
break
|
||||
}
|
||||
if strings.Contains(err.Error(), "the mux has closed") {
|
||||
break
|
||||
}
|
||||
logs.Warn(err)
|
||||
continue
|
||||
}
|
||||
if c == nil {
|
||||
logs.Warn("nil connection")
|
||||
break
|
||||
}
|
||||
go f(c)
|
||||
}
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
)
|
||||
|
||||
type SnappyConn struct {
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
c io.Closer
|
||||
}
|
||||
|
||||
func NewSnappyConn(conn io.ReadWriteCloser) *SnappyConn {
|
||||
c := new(SnappyConn)
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
c.c = conn.(io.Closer)
|
||||
return c
|
||||
}
|
||||
|
||||
//snappy压缩写
|
||||
func (s *SnappyConn) Write(b []byte) (n int, err error) {
|
||||
if n, err = s.w.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
if err = s.w.Flush(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//snappy压缩读
|
||||
func (s *SnappyConn) Read(b []byte) (n int, err error) {
|
||||
return s.r.Read(b)
|
||||
}
|
||||
|
||||
func (s *SnappyConn) Close() error {
|
||||
err := s.w.Close()
|
||||
err2 := s.c.Close()
|
||||
if err != nil && err2 == nil {
|
||||
return err
|
||||
}
|
||||
if err == nil && err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil && err2 != nil {
|
||||
return errors.New(err.Error() + err2.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -1,253 +0,0 @@
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CurveID uint16
|
||||
type SignatureScheme uint16
|
||||
|
||||
const (
|
||||
statusTypeOCSP uint8 = 1
|
||||
extensionServerName uint16 = 0
|
||||
extensionStatusRequest uint16 = 5
|
||||
extensionSupportedCurves uint16 = 10
|
||||
extensionSupportedPoints uint16 = 11
|
||||
extensionSignatureAlgorithms uint16 = 13
|
||||
extensionALPN uint16 = 16
|
||||
extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6
|
||||
extensionSessionTicket uint16 = 35
|
||||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
|
||||
extensionRenegotiationInfo uint16 = 0xff01
|
||||
scsvRenegotiation uint16 = 0x00ff
|
||||
)
|
||||
|
||||
type ClientHelloMsg struct {
|
||||
raw []byte
|
||||
vers uint16
|
||||
random []byte
|
||||
sessionId []byte
|
||||
cipherSuites []uint16
|
||||
compressionMethods []uint8
|
||||
nextProtoNeg bool
|
||||
serverName string
|
||||
ocspStapling bool
|
||||
scts bool
|
||||
supportedCurves []CurveID
|
||||
supportedPoints []uint8
|
||||
ticketSupported bool
|
||||
sessionTicket []uint8
|
||||
supportedSignatureAlgorithms []SignatureScheme
|
||||
secureRenegotiation []byte
|
||||
secureRenegotiationSupported bool
|
||||
alpnProtocols []string
|
||||
}
|
||||
|
||||
func (m *ClientHelloMsg) GetServerName() string {
|
||||
return m.serverName
|
||||
}
|
||||
|
||||
func (m *ClientHelloMsg) Unmarshal(data []byte) bool {
|
||||
if len(data) < 42 {
|
||||
return false
|
||||
}
|
||||
m.raw = data
|
||||
m.vers = uint16(data[4])<<8 | uint16(data[5])
|
||||
m.random = data[6:38]
|
||||
sessionIdLen := int(data[38])
|
||||
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
|
||||
return false
|
||||
}
|
||||
m.sessionId = data[39 : 39+sessionIdLen]
|
||||
data = data[39+sessionIdLen:]
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
|
||||
// they are uint16s, the number must be even.
|
||||
cipherSuiteLen := int(data[0])<<8 | int(data[1])
|
||||
if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
|
||||
return false
|
||||
}
|
||||
numCipherSuites := cipherSuiteLen / 2
|
||||
m.cipherSuites = make([]uint16, numCipherSuites)
|
||||
for i := 0; i < numCipherSuites; i++ {
|
||||
m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
|
||||
if m.cipherSuites[i] == scsvRenegotiation {
|
||||
m.secureRenegotiationSupported = true
|
||||
}
|
||||
}
|
||||
data = data[2+cipherSuiteLen:]
|
||||
if len(data) < 1 {
|
||||
return false
|
||||
}
|
||||
compressionMethodsLen := int(data[0])
|
||||
if len(data) < 1+compressionMethodsLen {
|
||||
return false
|
||||
}
|
||||
m.compressionMethods = data[1 : 1+compressionMethodsLen]
|
||||
data = data[1+compressionMethodsLen:]
|
||||
|
||||
m.nextProtoNeg = false
|
||||
m.serverName = ""
|
||||
m.ocspStapling = false
|
||||
m.ticketSupported = false
|
||||
m.sessionTicket = nil
|
||||
m.supportedSignatureAlgorithms = nil
|
||||
m.alpnProtocols = nil
|
||||
m.scts = false
|
||||
|
||||
if len(data) == 0 {
|
||||
// ClientHello is optionally followed by extension data
|
||||
return true
|
||||
}
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
extensionsLength := int(data[0])<<8 | int(data[1])
|
||||
data = data[2:]
|
||||
if extensionsLength != len(data) {
|
||||
return false
|
||||
}
|
||||
|
||||
for len(data) != 0 {
|
||||
if len(data) < 4 {
|
||||
return false
|
||||
}
|
||||
extension := uint16(data[0])<<8 | uint16(data[1])
|
||||
length := int(data[2])<<8 | int(data[3])
|
||||
data = data[4:]
|
||||
if len(data) < length {
|
||||
return false
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionServerName:
|
||||
d := data[:length]
|
||||
if len(d) < 2 {
|
||||
return false
|
||||
}
|
||||
namesLen := int(d[0])<<8 | int(d[1])
|
||||
d = d[2:]
|
||||
if len(d) != namesLen {
|
||||
return false
|
||||
}
|
||||
for len(d) > 0 {
|
||||
if len(d) < 3 {
|
||||
return false
|
||||
}
|
||||
nameType := d[0]
|
||||
nameLen := int(d[1])<<8 | int(d[2])
|
||||
d = d[3:]
|
||||
if len(d) < nameLen {
|
||||
return false
|
||||
}
|
||||
if nameType == 0 {
|
||||
m.serverName = string(d[:nameLen])
|
||||
// An SNI value may not include a
|
||||
// trailing dot. See
|
||||
// https://tools.ietf.org/html/rfc6066#section-3.
|
||||
if strings.HasSuffix(m.serverName, ".") {
|
||||
return false
|
||||
}
|
||||
break
|
||||
}
|
||||
d = d[nameLen:]
|
||||
}
|
||||
case extensionNextProtoNeg:
|
||||
if length > 0 {
|
||||
return false
|
||||
}
|
||||
m.nextProtoNeg = true
|
||||
case extensionStatusRequest:
|
||||
m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
|
||||
case extensionSupportedCurves:
|
||||
// https://tools.ietf.org/html/rfc4492#section-5.5.1
|
||||
if length < 2 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])<<8 | int(data[1])
|
||||
if l%2 == 1 || length != l+2 {
|
||||
return false
|
||||
}
|
||||
numCurves := l / 2
|
||||
m.supportedCurves = make([]CurveID, numCurves)
|
||||
d := data[2:]
|
||||
for i := 0; i < numCurves; i++ {
|
||||
m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
|
||||
d = d[2:]
|
||||
}
|
||||
case extensionSupportedPoints:
|
||||
// https://tools.ietf.org/html/rfc4492#section-5.5.2
|
||||
if length < 1 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])
|
||||
if length != l+1 {
|
||||
return false
|
||||
}
|
||||
m.supportedPoints = make([]uint8, l)
|
||||
copy(m.supportedPoints, data[1:])
|
||||
case extensionSessionTicket:
|
||||
// https://tools.ietf.org/html/rfc5077#section-3.2
|
||||
m.ticketSupported = true
|
||||
m.sessionTicket = data[:length]
|
||||
case extensionSignatureAlgorithms:
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
||||
if length < 2 || length&1 != 0 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])<<8 | int(data[1])
|
||||
if l != length-2 {
|
||||
return false
|
||||
}
|
||||
n := l / 2
|
||||
d := data[2:]
|
||||
m.supportedSignatureAlgorithms = make([]SignatureScheme, n)
|
||||
for i := range m.supportedSignatureAlgorithms {
|
||||
m.supportedSignatureAlgorithms[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1])
|
||||
d = d[2:]
|
||||
}
|
||||
case extensionRenegotiationInfo:
|
||||
if length == 0 {
|
||||
return false
|
||||
}
|
||||
d := data[:length]
|
||||
l := int(d[0])
|
||||
d = d[1:]
|
||||
if l != len(d) {
|
||||
return false
|
||||
}
|
||||
|
||||
m.secureRenegotiation = d
|
||||
m.secureRenegotiationSupported = true
|
||||
case extensionALPN:
|
||||
if length < 2 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])<<8 | int(data[1])
|
||||
if l != length-2 {
|
||||
return false
|
||||
}
|
||||
d := data[2:length]
|
||||
for len(d) != 0 {
|
||||
stringLen := int(d[0])
|
||||
d = d[1:]
|
||||
if stringLen == 0 || stringLen > len(d) {
|
||||
return false
|
||||
}
|
||||
m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen]))
|
||||
d = d[stringLen:]
|
||||
}
|
||||
case extensionSCT:
|
||||
m.scts = true
|
||||
if length != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
//en
|
||||
func AesEncrypt(origData, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockSize := block.BlockSize()
|
||||
origData = PKCS5Padding(origData, blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
|
||||
crypted := make([]byte, len(origData))
|
||||
blockMode.CryptBlocks(crypted, origData)
|
||||
return crypted, nil
|
||||
}
|
||||
|
||||
//de
|
||||
func AesDecrypt(crypted, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockSize := block.BlockSize()
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||
origData := make([]byte, len(crypted))
|
||||
blockMode.CryptBlocks(origData, crypted)
|
||||
err, origData = PKCS5UnPadding(origData)
|
||||
return origData, err
|
||||
}
|
||||
|
||||
//Completion when the length is insufficient
|
||||
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
//Remove excess
|
||||
func PKCS5UnPadding(origData []byte) (error, []byte) {
|
||||
length := len(origData)
|
||||
unpadding := int(origData[length-1])
|
||||
if (length - unpadding) < 0 {
|
||||
return errors.New("len error"), nil
|
||||
}
|
||||
return nil, origData[:(length - unpadding)]
|
||||
}
|
||||
|
||||
//Generate 32-bit MD5 strings
|
||||
func Md5(s string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
//Generating Random Verification Key
|
||||
func GetRandomString(l int) string {
|
||||
str := "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
bytes := []byte(str)
|
||||
result := []byte{}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := 0; i < l; i++ {
|
||||
result = append(result, bytes[r.Intn(len(bytes))])
|
||||
}
|
||||
return string(result)
|
||||
}
|
@@ -1,87 +0,0 @@
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
)
|
||||
|
||||
var (
|
||||
cert tls.Certificate
|
||||
)
|
||||
|
||||
func InitTls() {
|
||||
c, k, err := generateKeyPair("NPS Org")
|
||||
if err == nil {
|
||||
cert, err = tls.X509KeyPair(c, k)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalln("Error initializing crypto certs", err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewTlsServerConn(conn net.Conn) net.Conn {
|
||||
var err error
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
config := &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
return tls.Server(conn, config)
|
||||
}
|
||||
|
||||
func NewTlsClientConn(conn net.Conn) net.Conn {
|
||||
conf := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
return tls.Client(conn, conf)
|
||||
}
|
||||
|
||||
func generateKeyPair(CommonName string) (rawCert, rawKey []byte, err error) {
|
||||
// Create private key and self-signed certificate
|
||||
// Adapted from https://golang.org/src/crypto/tls/generate_cert.go
|
||||
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
validFor := time.Hour * 24 * 365 * 10 // ten years
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(validFor)
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"My Company Name LTD."},
|
||||
CommonName: CommonName,
|
||||
Country: []string{"US"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rawCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
rawKey = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
|
||||
return
|
||||
}
|
@@ -1,126 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
)
|
||||
|
||||
func InitDaemon(f string, runPath string, pidPath string) {
|
||||
if len(os.Args) < 2 {
|
||||
return
|
||||
}
|
||||
var args []string
|
||||
args = append(args, os.Args[0])
|
||||
if len(os.Args) >= 2 {
|
||||
args = append(args, os.Args[2:]...)
|
||||
}
|
||||
args = append(args, "-log=file")
|
||||
switch os.Args[1] {
|
||||
case "start":
|
||||
start(args, f, pidPath, runPath)
|
||||
os.Exit(0)
|
||||
case "stop":
|
||||
stop(f, args[0], pidPath)
|
||||
os.Exit(0)
|
||||
case "restart":
|
||||
stop(f, args[0], pidPath)
|
||||
start(args, f, pidPath, runPath)
|
||||
os.Exit(0)
|
||||
case "status":
|
||||
if status(f, pidPath) {
|
||||
log.Printf("%s is running", f)
|
||||
} else {
|
||||
log.Printf("%s is not running", f)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "reload":
|
||||
reload(f, pidPath)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func reload(f string, pidPath string) {
|
||||
if f == "nps" && !common.IsWindows() && !status(f, pidPath) {
|
||||
log.Println("reload fail")
|
||||
return
|
||||
}
|
||||
var c *exec.Cmd
|
||||
var err error
|
||||
b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid"))
|
||||
if err == nil {
|
||||
c = exec.Command("/bin/bash", "-c", `kill -30 `+string(b))
|
||||
} else {
|
||||
log.Fatalln("reload error,pid file does not exist")
|
||||
}
|
||||
if c.Run() == nil {
|
||||
log.Println("reload success")
|
||||
} else {
|
||||
log.Println("reload fail")
|
||||
}
|
||||
}
|
||||
|
||||
func status(f string, pidPath string) bool {
|
||||
var cmd *exec.Cmd
|
||||
b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid"))
|
||||
if err == nil {
|
||||
if !common.IsWindows() {
|
||||
cmd = exec.Command("/bin/sh", "-c", "ps -ax | awk '{ print $1 }' | grep "+string(b))
|
||||
} else {
|
||||
cmd = exec.Command("tasklist")
|
||||
}
|
||||
out, _ := cmd.Output()
|
||||
if strings.Index(string(out), string(b)) > -1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func start(osArgs []string, f string, pidPath, runPath string) {
|
||||
if status(f, pidPath) {
|
||||
log.Printf(" %s is running", f)
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(osArgs[0], osArgs[1:]...)
|
||||
cmd.Start()
|
||||
if cmd.Process.Pid > 0 {
|
||||
log.Println("start ok , pid:", cmd.Process.Pid, "config path:", runPath)
|
||||
d1 := []byte(strconv.Itoa(cmd.Process.Pid))
|
||||
ioutil.WriteFile(filepath.Join(pidPath, f+".pid"), d1, 0600)
|
||||
} else {
|
||||
log.Println("start error")
|
||||
}
|
||||
}
|
||||
|
||||
func stop(f string, p string, pidPath string) {
|
||||
if !status(f, pidPath) {
|
||||
log.Printf(" %s is not running", f)
|
||||
return
|
||||
}
|
||||
var c *exec.Cmd
|
||||
var err error
|
||||
if common.IsWindows() {
|
||||
p := strings.Split(p, `\`)
|
||||
c = exec.Command("taskkill", "/F", "/IM", p[len(p)-1])
|
||||
} else {
|
||||
b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid"))
|
||||
if err == nil {
|
||||
c = exec.Command("/bin/bash", "-c", `kill -9 `+string(b))
|
||||
} else {
|
||||
log.Fatalln("stop error,pid file does not exist")
|
||||
}
|
||||
}
|
||||
err = c.Run()
|
||||
if err != nil {
|
||||
log.Println("stop error,", err)
|
||||
} else {
|
||||
log.Println("stop ok")
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
func init() {
|
||||
s := make(chan os.Signal, 1)
|
||||
signal.Notify(s, syscall.SIGUSR1)
|
||||
go func() {
|
||||
for {
|
||||
<-s
|
||||
beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf"))
|
||||
}
|
||||
}()
|
||||
}
|
361
lib/file/db.go
361
lib/file/db.go
@@ -1,361 +0,0 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/rate"
|
||||
)
|
||||
|
||||
type DbUtils struct {
|
||||
JsonDb *JsonDb
|
||||
}
|
||||
|
||||
var (
|
||||
Db *DbUtils
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
//init csv from file
|
||||
func GetDb() *DbUtils {
|
||||
once.Do(func() {
|
||||
jsonDb := NewJsonDb(common.GetRunPath())
|
||||
jsonDb.LoadClientFromJsonFile()
|
||||
jsonDb.LoadTaskFromJsonFile()
|
||||
jsonDb.LoadHostFromJsonFile()
|
||||
Db = &DbUtils{JsonDb: jsonDb}
|
||||
})
|
||||
return Db
|
||||
}
|
||||
|
||||
func GetMapKeys(m sync.Map, isSort bool, sortKey, order string) (keys []int) {
|
||||
if sortKey != "" && isSort {
|
||||
return sortClientByKey(m, sortKey, order)
|
||||
}
|
||||
m.Range(func(key, value interface{}) bool {
|
||||
keys = append(keys, key.(int))
|
||||
return true
|
||||
})
|
||||
sort.Ints(keys)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *DbUtils) GetClientList(start, length int, search, sort, order string, clientId int) ([]*Client, int) {
|
||||
list := make([]*Client, 0)
|
||||
var cnt int
|
||||
keys := GetMapKeys(s.JsonDb.Clients, true, sort, order)
|
||||
for _, key := range keys {
|
||||
if value, ok := s.JsonDb.Clients.Load(key); ok {
|
||||
v := value.(*Client)
|
||||
if v.NoDisplay {
|
||||
continue
|
||||
}
|
||||
if clientId != 0 && clientId != v.Id {
|
||||
continue
|
||||
}
|
||||
if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.VerifyKey, search) || strings.Contains(v.Remark, search)) {
|
||||
continue
|
||||
}
|
||||
cnt++
|
||||
if start--; start < 0 {
|
||||
if length--; length >= 0 {
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list, cnt
|
||||
}
|
||||
|
||||
func (s *DbUtils) GetIdByVerifyKey(vKey string, addr string) (id int, err error) {
|
||||
var exist bool
|
||||
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Client)
|
||||
if common.Getverifyval(v.VerifyKey) == vKey && v.Status {
|
||||
v.Addr = common.GetIpByAddr(addr)
|
||||
id = v.Id
|
||||
exist = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if exist {
|
||||
return
|
||||
}
|
||||
return 0, errors.New("not found")
|
||||
}
|
||||
|
||||
func (s *DbUtils) NewTask(t *Tunnel) (err error) {
|
||||
s.JsonDb.Tasks.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Tunnel)
|
||||
if (v.Mode == "secret" || v.Mode == "p2p") && v.Password == t.Password {
|
||||
err = errors.New(fmt.Sprintf("secret mode keys %s must be unique", t.Password))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.Flow = new(Flow)
|
||||
s.JsonDb.Tasks.Store(t.Id, t)
|
||||
s.JsonDb.StoreTasksToJsonFile()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *DbUtils) UpdateTask(t *Tunnel) error {
|
||||
s.JsonDb.Tasks.Store(t.Id, t)
|
||||
s.JsonDb.StoreTasksToJsonFile()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DbUtils) DelTask(id int) error {
|
||||
s.JsonDb.Tasks.Delete(id)
|
||||
s.JsonDb.StoreTasksToJsonFile()
|
||||
return nil
|
||||
}
|
||||
|
||||
//md5 password
|
||||
func (s *DbUtils) GetTaskByMd5Password(p string) (t *Tunnel) {
|
||||
s.JsonDb.Tasks.Range(func(key, value interface{}) bool {
|
||||
if crypt.Md5(value.(*Tunnel).Password) == p {
|
||||
t = value.(*Tunnel)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *DbUtils) GetTask(id int) (t *Tunnel, err error) {
|
||||
if v, ok := s.JsonDb.Tasks.Load(id); ok {
|
||||
t = v.(*Tunnel)
|
||||
return
|
||||
}
|
||||
err = errors.New("not found")
|
||||
return
|
||||
}
|
||||
|
||||
func (s *DbUtils) DelHost(id int) error {
|
||||
s.JsonDb.Hosts.Delete(id)
|
||||
s.JsonDb.StoreHostToJsonFile()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DbUtils) IsHostExist(h *Host) bool {
|
||||
var exist bool
|
||||
s.JsonDb.Hosts.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Host)
|
||||
if v.Id != h.Id && v.Host == h.Host && h.Location == v.Location && (v.Scheme == "all" || v.Scheme == h.Scheme) {
|
||||
exist = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return exist
|
||||
}
|
||||
|
||||
func (s *DbUtils) NewHost(t *Host) error {
|
||||
if t.Location == "" {
|
||||
t.Location = "/"
|
||||
}
|
||||
if s.IsHostExist(t) {
|
||||
return errors.New("host has exist")
|
||||
}
|
||||
t.Flow = new(Flow)
|
||||
s.JsonDb.Hosts.Store(t.Id, t)
|
||||
s.JsonDb.StoreHostToJsonFile()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DbUtils) GetHost(start, length int, id int, search string) ([]*Host, int) {
|
||||
list := make([]*Host, 0)
|
||||
var cnt int
|
||||
keys := GetMapKeys(s.JsonDb.Hosts, false, "", "")
|
||||
for _, key := range keys {
|
||||
if value, ok := s.JsonDb.Hosts.Load(key); ok {
|
||||
v := value.(*Host)
|
||||
if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.Host, search) || strings.Contains(v.Remark, search)) {
|
||||
continue
|
||||
}
|
||||
if id == 0 || v.Client.Id == id {
|
||||
cnt++
|
||||
if start--; start < 0 {
|
||||
if length--; length >= 0 {
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list, cnt
|
||||
}
|
||||
|
||||
func (s *DbUtils) DelClient(id int) error {
|
||||
s.JsonDb.Clients.Delete(id)
|
||||
s.JsonDb.StoreClientsToJsonFile()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DbUtils) NewClient(c *Client) error {
|
||||
var isNotSet bool
|
||||
if c.WebUserName != "" && !s.VerifyUserName(c.WebUserName, c.Id) {
|
||||
return errors.New("web login username duplicate, please reset")
|
||||
}
|
||||
reset:
|
||||
if c.VerifyKey == "" || isNotSet {
|
||||
isNotSet = true
|
||||
c.VerifyKey = crypt.GetRandomString(16)
|
||||
}
|
||||
if c.RateLimit == 0 {
|
||||
c.Rate = rate.NewRate(int64(2 << 23))
|
||||
} else if c.Rate == nil {
|
||||
c.Rate = rate.NewRate(int64(c.RateLimit * 1024))
|
||||
}
|
||||
c.Rate.Start()
|
||||
if !s.VerifyVkey(c.VerifyKey, c.Id) {
|
||||
if isNotSet {
|
||||
goto reset
|
||||
}
|
||||
return errors.New("Vkey duplicate, please reset")
|
||||
}
|
||||
if c.Id == 0 {
|
||||
c.Id = int(s.JsonDb.GetClientId())
|
||||
}
|
||||
if c.Flow == nil {
|
||||
c.Flow = new(Flow)
|
||||
}
|
||||
s.JsonDb.Clients.Store(c.Id, c)
|
||||
s.JsonDb.StoreClientsToJsonFile()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DbUtils) VerifyVkey(vkey string, id int) (res bool) {
|
||||
res = true
|
||||
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Client)
|
||||
if v.VerifyKey == vkey && v.Id != id {
|
||||
res = false
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *DbUtils) VerifyUserName(username string, id int) (res bool) {
|
||||
res = true
|
||||
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Client)
|
||||
if v.WebUserName == username && v.Id != id {
|
||||
res = false
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *DbUtils) UpdateClient(t *Client) error {
|
||||
s.JsonDb.Clients.Store(t.Id, t)
|
||||
if t.RateLimit == 0 {
|
||||
t.Rate = rate.NewRate(int64(2 << 23))
|
||||
t.Rate.Start()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DbUtils) IsPubClient(id int) bool {
|
||||
client, err := s.GetClient(id)
|
||||
if err == nil {
|
||||
return client.NoDisplay
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *DbUtils) GetClient(id int) (c *Client, err error) {
|
||||
if v, ok := s.JsonDb.Clients.Load(id); ok {
|
||||
c = v.(*Client)
|
||||
return
|
||||
}
|
||||
err = errors.New("未找到客户端")
|
||||
return
|
||||
}
|
||||
|
||||
func (s *DbUtils) GetClientIdByVkey(vkey string) (id int, err error) {
|
||||
var exist bool
|
||||
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Client)
|
||||
if crypt.Md5(v.VerifyKey) == vkey {
|
||||
exist = true
|
||||
id = v.Id
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if exist {
|
||||
return
|
||||
}
|
||||
err = errors.New("未找到客户端")
|
||||
return
|
||||
}
|
||||
|
||||
func (s *DbUtils) GetHostById(id int) (h *Host, err error) {
|
||||
if v, ok := s.JsonDb.Hosts.Load(id); ok {
|
||||
h = v.(*Host)
|
||||
return
|
||||
}
|
||||
err = errors.New("The host could not be parsed")
|
||||
return
|
||||
}
|
||||
|
||||
//get key by host from x
|
||||
func (s *DbUtils) GetInfoByHost(host string, r *http.Request) (h *Host, err error) {
|
||||
var hosts []*Host
|
||||
//Handling Ported Access
|
||||
host = common.GetIpByAddr(host)
|
||||
s.JsonDb.Hosts.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Host)
|
||||
if v.IsClose {
|
||||
return true
|
||||
}
|
||||
//Remove http(s) http(s)://a.proxy.com
|
||||
//*.proxy.com *.a.proxy.com Do some pan-parsing
|
||||
if v.Scheme != "all" && v.Scheme != r.URL.Scheme {
|
||||
return true
|
||||
}
|
||||
tmpHost := v.Host
|
||||
if strings.Contains(tmpHost, "*") {
|
||||
tmpHost = strings.Replace(tmpHost, "*", "", -1)
|
||||
if strings.Contains(host, tmpHost) {
|
||||
hosts = append(hosts, v)
|
||||
}
|
||||
} else if v.Host == host {
|
||||
hosts = append(hosts, v)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
for _, v := range hosts {
|
||||
//If not set, default matches all
|
||||
if v.Location == "" {
|
||||
v.Location = "/"
|
||||
}
|
||||
if strings.Index(r.RequestURI, v.Location) == 0 {
|
||||
if h == nil || (len(v.Location) > len(h.Location)) {
|
||||
h = v
|
||||
}
|
||||
}
|
||||
}
|
||||
if h != nil {
|
||||
return
|
||||
}
|
||||
err = errors.New("The host could not be parsed")
|
||||
return
|
||||
}
|
201
lib/file/file.go
201
lib/file/file.go
@@ -1,201 +0,0 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/rate"
|
||||
)
|
||||
|
||||
func NewJsonDb(runPath string) *JsonDb {
|
||||
return &JsonDb{
|
||||
RunPath: runPath,
|
||||
TaskFilePath: filepath.Join(runPath, "conf", "tasks.json"),
|
||||
HostFilePath: filepath.Join(runPath, "conf", "hosts.json"),
|
||||
ClientFilePath: filepath.Join(runPath, "conf", "clients.json"),
|
||||
}
|
||||
}
|
||||
|
||||
type JsonDb struct {
|
||||
Tasks sync.Map
|
||||
Hosts sync.Map
|
||||
HostsTmp sync.Map
|
||||
Clients sync.Map
|
||||
RunPath string
|
||||
ClientIncreaseId int32 //client increased id
|
||||
TaskIncreaseId int32 //task increased id
|
||||
HostIncreaseId int32 //host increased id
|
||||
TaskFilePath string //task file path
|
||||
HostFilePath string //host file path
|
||||
ClientFilePath string //client file path
|
||||
}
|
||||
|
||||
func (s *JsonDb) LoadTaskFromJsonFile() {
|
||||
loadSyncMapFromFile(s.TaskFilePath, func(v string) {
|
||||
var err error
|
||||
post := new(Tunnel)
|
||||
if json.Unmarshal([]byte(v), &post) != nil {
|
||||
return
|
||||
}
|
||||
if post.Client, err = s.GetClient(post.Client.Id); err != nil {
|
||||
return
|
||||
}
|
||||
s.Tasks.Store(post.Id, post)
|
||||
if post.Id > int(s.TaskIncreaseId) {
|
||||
s.TaskIncreaseId = int32(post.Id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *JsonDb) LoadClientFromJsonFile() {
|
||||
loadSyncMapFromFile(s.ClientFilePath, func(v string) {
|
||||
post := new(Client)
|
||||
if json.Unmarshal([]byte(v), &post) != nil {
|
||||
return
|
||||
}
|
||||
if post.RateLimit > 0 {
|
||||
post.Rate = rate.NewRate(int64(post.RateLimit * 1024))
|
||||
} else {
|
||||
post.Rate = rate.NewRate(int64(2 << 23))
|
||||
}
|
||||
post.Rate.Start()
|
||||
post.NowConn = 0
|
||||
s.Clients.Store(post.Id, post)
|
||||
if post.Id > int(s.ClientIncreaseId) {
|
||||
s.ClientIncreaseId = int32(post.Id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *JsonDb) LoadHostFromJsonFile() {
|
||||
loadSyncMapFromFile(s.HostFilePath, func(v string) {
|
||||
var err error
|
||||
post := new(Host)
|
||||
if json.Unmarshal([]byte(v), &post) != nil {
|
||||
return
|
||||
}
|
||||
if post.Client, err = s.GetClient(post.Client.Id); err != nil {
|
||||
return
|
||||
}
|
||||
s.Hosts.Store(post.Id, post)
|
||||
if post.Id > int(s.HostIncreaseId) {
|
||||
s.HostIncreaseId = int32(post.Id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *JsonDb) GetClient(id int) (c *Client, err error) {
|
||||
if v, ok := s.Clients.Load(id); ok {
|
||||
c = v.(*Client)
|
||||
return
|
||||
}
|
||||
err = errors.New("未找到客户端")
|
||||
return
|
||||
}
|
||||
|
||||
var hostLock sync.Mutex
|
||||
|
||||
func (s *JsonDb) StoreHostToJsonFile() {
|
||||
hostLock.Lock()
|
||||
storeSyncMapToFile(s.Hosts, s.HostFilePath)
|
||||
hostLock.Unlock()
|
||||
}
|
||||
|
||||
var taskLock sync.Mutex
|
||||
|
||||
func (s *JsonDb) StoreTasksToJsonFile() {
|
||||
taskLock.Lock()
|
||||
storeSyncMapToFile(s.Tasks, s.TaskFilePath)
|
||||
taskLock.Unlock()
|
||||
}
|
||||
|
||||
var clientLock sync.Mutex
|
||||
|
||||
func (s *JsonDb) StoreClientsToJsonFile() {
|
||||
clientLock.Lock()
|
||||
storeSyncMapToFile(s.Clients, s.ClientFilePath)
|
||||
clientLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *JsonDb) GetClientId() int32 {
|
||||
return atomic.AddInt32(&s.ClientIncreaseId, 1)
|
||||
}
|
||||
|
||||
func (s *JsonDb) GetTaskId() int32 {
|
||||
return atomic.AddInt32(&s.TaskIncreaseId, 1)
|
||||
}
|
||||
|
||||
func (s *JsonDb) GetHostId() int32 {
|
||||
return atomic.AddInt32(&s.HostIncreaseId, 1)
|
||||
}
|
||||
|
||||
func loadSyncMapFromFile(filePath string, f func(value string)) {
|
||||
b, err := common.ReadAllFromFile(filePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range strings.Split(string(b), "\n"+common.CONN_DATA_SEQ) {
|
||||
f(v)
|
||||
}
|
||||
}
|
||||
|
||||
func storeSyncMapToFile(m sync.Map, filePath string) {
|
||||
file, err := os.Create(filePath + ".tmp")
|
||||
// first create a temporary file to store
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.Range(func(key, value interface{}) bool {
|
||||
var b []byte
|
||||
var err error
|
||||
switch value.(type) {
|
||||
case *Tunnel:
|
||||
obj := value.(*Tunnel)
|
||||
if obj.NoStore {
|
||||
return true
|
||||
}
|
||||
b, err = json.Marshal(obj)
|
||||
case *Host:
|
||||
obj := value.(*Host)
|
||||
if obj.NoStore {
|
||||
return true
|
||||
}
|
||||
b, err = json.Marshal(obj)
|
||||
case *Client:
|
||||
obj := value.(*Client)
|
||||
if obj.NoStore {
|
||||
return true
|
||||
}
|
||||
b, err = json.Marshal(obj)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
_, err = file.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = file.Write([]byte("\n" + common.CONN_DATA_SEQ))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return true
|
||||
})
|
||||
_ = file.Sync()
|
||||
_ = file.Close()
|
||||
// must close file first, then rename it
|
||||
err = os.Rename(filePath+".tmp", filePath)
|
||||
if err != nil {
|
||||
logs.Error(err, "store to file err, data will lost")
|
||||
}
|
||||
// replace the file, maybe provides atomic operation
|
||||
}
|
210
lib/file/obj.go
210
lib/file/obj.go
@@ -1,210 +0,0 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/rate"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Flow struct {
|
||||
ExportFlow int64
|
||||
InletFlow int64
|
||||
FlowLimit int64
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *Flow) Add(in, out int64) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.InletFlow += int64(in)
|
||||
s.ExportFlow += int64(out)
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
U string
|
||||
P string
|
||||
Compress bool
|
||||
Crypt bool
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
Cnf *Config
|
||||
Id int //id
|
||||
VerifyKey string //verify key
|
||||
Addr string //the ip of client
|
||||
Remark string //remark
|
||||
Status bool //is allow connect
|
||||
IsConnect bool //is the client connect
|
||||
RateLimit int //rate /kb
|
||||
Flow *Flow //flow setting
|
||||
Rate *rate.Rate //rate limit
|
||||
NoStore bool //no store to file
|
||||
NoDisplay bool //no display on web
|
||||
MaxConn int //the max connection num of client allow
|
||||
NowConn int32 //the connection num of now
|
||||
WebUserName string //the username of web login
|
||||
WebPassword string //the password of web login
|
||||
ConfigConnAllow bool //is allow connected by config file
|
||||
MaxTunnelNum int
|
||||
Version string
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func NewClient(vKey string, noStore bool, noDisplay bool) *Client {
|
||||
return &Client{
|
||||
Cnf: new(Config),
|
||||
Id: 0,
|
||||
VerifyKey: vKey,
|
||||
Addr: "",
|
||||
Remark: "",
|
||||
Status: true,
|
||||
IsConnect: false,
|
||||
RateLimit: 0,
|
||||
Flow: new(Flow),
|
||||
Rate: nil,
|
||||
NoStore: noStore,
|
||||
RWMutex: sync.RWMutex{},
|
||||
NoDisplay: noDisplay,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Client) CutConn() {
|
||||
atomic.AddInt32(&s.NowConn, 1)
|
||||
}
|
||||
|
||||
func (s *Client) AddConn() {
|
||||
atomic.AddInt32(&s.NowConn, -1)
|
||||
}
|
||||
|
||||
func (s *Client) GetConn() bool {
|
||||
if s.MaxConn == 0 || int(s.NowConn) < s.MaxConn {
|
||||
s.CutConn()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Client) HasTunnel(t *Tunnel) (exist bool) {
|
||||
GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Tunnel)
|
||||
if v.Client.Id == s.Id && v.Port == t.Port && t.Port != 0 {
|
||||
exist = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Client) GetTunnelNum() (num int) {
|
||||
GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Tunnel)
|
||||
if v.Client.Id == s.Id {
|
||||
num++
|
||||
}
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Client) HasHost(h *Host) bool {
|
||||
var has bool
|
||||
GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool {
|
||||
v := value.(*Host)
|
||||
if v.Client.Id == s.Id && v.Host == h.Host && h.Location == v.Location {
|
||||
has = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return has
|
||||
}
|
||||
|
||||
type Tunnel struct {
|
||||
Id int
|
||||
Port int
|
||||
ServerIp string
|
||||
Mode string
|
||||
Status bool
|
||||
RunStatus bool
|
||||
Client *Client
|
||||
Ports string
|
||||
Flow *Flow
|
||||
Password string
|
||||
Remark string
|
||||
TargetAddr string
|
||||
NoStore bool
|
||||
LocalPath string
|
||||
StripPre string
|
||||
Target *Target
|
||||
MultiAccount *MultiAccount
|
||||
Health
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type Health struct {
|
||||
HealthCheckTimeout int
|
||||
HealthMaxFail int
|
||||
HealthCheckInterval int
|
||||
HealthNextTime time.Time
|
||||
HealthMap map[string]int
|
||||
HttpHealthUrl string
|
||||
HealthRemoveArr []string
|
||||
HealthCheckType string
|
||||
HealthCheckTarget string
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type Host struct {
|
||||
Id int
|
||||
Host string //host
|
||||
HeaderChange string //header change
|
||||
HostChange string //host change
|
||||
Location string //url router
|
||||
Remark string //remark
|
||||
Scheme string //http https all
|
||||
CertFilePath string
|
||||
KeyFilePath string
|
||||
NoStore bool
|
||||
IsClose bool
|
||||
Flow *Flow
|
||||
Client *Client
|
||||
Target *Target //目标
|
||||
Health `json:"-"`
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
nowIndex int
|
||||
TargetStr string
|
||||
TargetArr []string
|
||||
LocalProxy bool
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type MultiAccount struct {
|
||||
AccountMap map[string]string // multi account and pwd
|
||||
}
|
||||
|
||||
func (s *Target) GetRandomTarget() (string, error) {
|
||||
if s.TargetArr == nil {
|
||||
s.TargetArr = strings.Split(s.TargetStr, "\n")
|
||||
}
|
||||
if len(s.TargetArr) == 1 {
|
||||
return s.TargetArr[0], nil
|
||||
}
|
||||
if len(s.TargetArr) == 0 {
|
||||
return "", errors.New("all inward-bending targets are offline")
|
||||
}
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if s.nowIndex >= len(s.TargetArr)-1 {
|
||||
s.nowIndex = -1
|
||||
}
|
||||
s.nowIndex++
|
||||
return s.TargetArr[s.nowIndex], nil
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A data structure to hold a key/value pair.
|
||||
type Pair struct {
|
||||
key string //sort key
|
||||
cId int
|
||||
order string
|
||||
clientFlow *Flow
|
||||
}
|
||||
|
||||
// A slice of Pairs that implements sort.Interface to sort by Value.
|
||||
type PairList []*Pair
|
||||
|
||||
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
func (p PairList) Len() int { return len(p) }
|
||||
func (p PairList) Less(i, j int) bool {
|
||||
if p[i].order == "desc" {
|
||||
return reflect.ValueOf(*p[i].clientFlow).FieldByName(p[i].key).Int() < reflect.ValueOf(*p[j].clientFlow).FieldByName(p[j].key).Int()
|
||||
}
|
||||
return reflect.ValueOf(*p[i].clientFlow).FieldByName(p[i].key).Int() > reflect.ValueOf(*p[j].clientFlow).FieldByName(p[j].key).Int()
|
||||
}
|
||||
|
||||
// A function to turn a map into a PairList, then sort and return it.
|
||||
func sortClientByKey(m sync.Map, sortKey, order string) (res []int) {
|
||||
p := make(PairList, 0)
|
||||
m.Range(func(key, value interface{}) bool {
|
||||
p = append(p, &Pair{sortKey, value.(*Client).Id, order, value.(*Client).Flow})
|
||||
return true
|
||||
})
|
||||
sort.Sort(p)
|
||||
for _, v := range p {
|
||||
res = append(res, v.cId)
|
||||
}
|
||||
return
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
package goroutine
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type connGroup struct {
|
||||
src io.ReadWriteCloser
|
||||
dst io.ReadWriteCloser
|
||||
wg *sync.WaitGroup
|
||||
n *int64
|
||||
}
|
||||
|
||||
func newConnGroup(dst, src io.ReadWriteCloser, wg *sync.WaitGroup, n *int64) connGroup {
|
||||
return connGroup{
|
||||
src: src,
|
||||
dst: dst,
|
||||
wg: wg,
|
||||
n: n,
|
||||
}
|
||||
}
|
||||
|
||||
func copyConnGroup(group interface{}) {
|
||||
cg, ok := group.(connGroup)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var err error
|
||||
*cg.n, err = common.CopyBuffer(cg.dst, cg.src)
|
||||
if err != nil {
|
||||
cg.src.Close()
|
||||
cg.dst.Close()
|
||||
//logs.Warn("close npc by copy from nps", err, c.connId)
|
||||
}
|
||||
cg.wg.Done()
|
||||
}
|
||||
|
||||
type Conns struct {
|
||||
conn1 io.ReadWriteCloser // mux connection
|
||||
conn2 net.Conn // outside connection
|
||||
flow *file.Flow
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewConns(c1 io.ReadWriteCloser, c2 net.Conn, flow *file.Flow, wg *sync.WaitGroup) Conns {
|
||||
return Conns{
|
||||
conn1: c1,
|
||||
conn2: c2,
|
||||
flow: flow,
|
||||
wg: wg,
|
||||
}
|
||||
}
|
||||
|
||||
func copyConns(group interface{}) {
|
||||
conns := group.(Conns)
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(2)
|
||||
var in, out int64
|
||||
_ = connCopyPool.Invoke(newConnGroup(conns.conn1, conns.conn2, wg, &in))
|
||||
// outside to mux : incoming
|
||||
_ = connCopyPool.Invoke(newConnGroup(conns.conn2, conns.conn1, wg, &out))
|
||||
// mux to outside : outgoing
|
||||
wg.Wait()
|
||||
if conns.flow != nil {
|
||||
conns.flow.Add(in, out)
|
||||
}
|
||||
conns.wg.Done()
|
||||
}
|
||||
|
||||
var connCopyPool, _ = ants.NewPoolWithFunc(200000, copyConnGroup, ants.WithNonblocking(false))
|
||||
var CopyConnsPool, _ = ants.NewPoolWithFunc(100000, copyConns, ants.WithNonblocking(false))
|
@@ -1,363 +0,0 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/c4milo/unpackit"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Keep it in sync with the template from service_sysv_linux.go file
|
||||
// Use "ps | grep -v grep | grep $(get_pid)" because "ps PID" may not work on OpenWrt
|
||||
const SysvScript = `#!/bin/sh
|
||||
# For RedHat and cousins:
|
||||
# chkconfig: - 99 01
|
||||
# description: {{.Description}}
|
||||
# processname: {{.Path}}
|
||||
### BEGIN INIT INFO
|
||||
# Provides: {{.Path}}
|
||||
# Required-Start:
|
||||
# Required-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: {{.DisplayName}}
|
||||
# Description: {{.Description}}
|
||||
### END INIT INFO
|
||||
cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
|
||||
name=$(basename $(readlink -f $0))
|
||||
pid_file="/var/run/$name.pid"
|
||||
stdout_log="/var/log/$name.log"
|
||||
stderr_log="/var/log/$name.err"
|
||||
[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
|
||||
get_pid() {
|
||||
cat "$pid_file"
|
||||
}
|
||||
is_running() {
|
||||
[ -f "$pid_file" ] && ps | grep -v grep | grep $(get_pid) > /dev/null 2>&1
|
||||
}
|
||||
case "$1" in
|
||||
start)
|
||||
if is_running; then
|
||||
echo "Already started"
|
||||
else
|
||||
echo "Starting $name"
|
||||
{{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}}
|
||||
$cmd >> "$stdout_log" 2>> "$stderr_log" &
|
||||
echo $! > "$pid_file"
|
||||
if ! is_running; then
|
||||
echo "Unable to start, see $stdout_log and $stderr_log"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
if is_running; then
|
||||
echo -n "Stopping $name.."
|
||||
kill $(get_pid)
|
||||
for i in $(seq 1 10)
|
||||
do
|
||||
if ! is_running; then
|
||||
break
|
||||
fi
|
||||
echo -n "."
|
||||
sleep 1
|
||||
done
|
||||
echo
|
||||
if is_running; then
|
||||
echo "Not stopped; may still be shutting down or shutdown may have failed"
|
||||
exit 1
|
||||
else
|
||||
echo "Stopped"
|
||||
if [ -f "$pid_file" ]; then
|
||||
rm "$pid_file"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Not running"
|
||||
fi
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
if is_running; then
|
||||
echo "Unable to stop, will not attempt to start"
|
||||
exit 1
|
||||
fi
|
||||
$0 start
|
||||
;;
|
||||
status)
|
||||
if is_running; then
|
||||
echo "Running"
|
||||
else
|
||||
echo "Stopped"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
`
|
||||
|
||||
const SystemdScript = `[Unit]
|
||||
Description={{.Description}}
|
||||
ConditionFileIsExecutable={{.Path|cmdEscape}}
|
||||
{{range $i, $dep := .Dependencies}}
|
||||
{{$dep}} {{end}}
|
||||
[Service]
|
||||
LimitNOFILE=65536
|
||||
StartLimitInterval=5
|
||||
StartLimitBurst=10
|
||||
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
|
||||
{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
|
||||
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
|
||||
{{if .UserName}}User={{.UserName}}{{end}}
|
||||
{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}}
|
||||
{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}}
|
||||
{{if and .LogOutput .HasOutputFileSupport -}}
|
||||
StandardOutput=file:/var/log/{{.Name}}.out
|
||||
StandardError=file:/var/log/{{.Name}}.err
|
||||
{{- end}}
|
||||
Restart=always
|
||||
RestartSec=120
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`
|
||||
|
||||
func UpdateNps() {
|
||||
destPath := downloadLatest("server")
|
||||
//复制文件到对应目录
|
||||
copyStaticFile(destPath, "nps")
|
||||
fmt.Println("Update completed, please restart")
|
||||
}
|
||||
|
||||
func UpdateNpc() {
|
||||
destPath := downloadLatest("client")
|
||||
//复制文件到对应目录
|
||||
copyStaticFile(destPath, "npc")
|
||||
fmt.Println("Update completed, please restart")
|
||||
}
|
||||
|
||||
type release struct {
|
||||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
func downloadLatest(bin string) string {
|
||||
// get version
|
||||
data, err := http.Get("https://api.github.com/repos/ehang-io/nps/releases/latest")
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
b, err := ioutil.ReadAll(data.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rl := new(release)
|
||||
json.Unmarshal(b, &rl)
|
||||
version := rl.TagName
|
||||
fmt.Println("the latest version is", version)
|
||||
filename := runtime.GOOS + "_" + runtime.GOARCH + "_" + bin + ".tar.gz"
|
||||
// download latest package
|
||||
downloadUrl := fmt.Sprintf("https://ehang.io/nps/releases/download/%s/%s", version, filename)
|
||||
fmt.Println("download package from ", downloadUrl)
|
||||
resp, err := http.Get(downloadUrl)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
destPath, err := unpackit.Unpack(resp.Body, "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if bin == "server" {
|
||||
destPath = strings.Replace(destPath, "/web", "", -1)
|
||||
destPath = strings.Replace(destPath, `\web`, "", -1)
|
||||
destPath = strings.Replace(destPath, "/views", "", -1)
|
||||
destPath = strings.Replace(destPath, `\views`, "", -1)
|
||||
} else {
|
||||
destPath = strings.Replace(destPath, `\conf`, "", -1)
|
||||
destPath = strings.Replace(destPath, "/conf", "", -1)
|
||||
}
|
||||
return destPath
|
||||
}
|
||||
|
||||
func copyStaticFile(srcPath, bin string) string {
|
||||
path := common.GetInstallPath()
|
||||
if bin == "nps" {
|
||||
//复制文件到对应目录
|
||||
if err := CopyDir(filepath.Join(srcPath, "web", "views"), filepath.Join(path, "web", "views")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
chMod(filepath.Join(path, "web", "views"), 0766)
|
||||
if err := CopyDir(filepath.Join(srcPath, "web", "static"), filepath.Join(path, "web", "static")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
chMod(filepath.Join(path, "web", "static"), 0766)
|
||||
}
|
||||
binPath, _ := filepath.Abs(os.Args[0])
|
||||
if !common.IsWindows() {
|
||||
if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin); err != nil {
|
||||
if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin); err != nil {
|
||||
log.Fatalln(err)
|
||||
} else {
|
||||
copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin+"-update")
|
||||
chMod("/usr/local/bin/"+bin+"-update", 0755)
|
||||
binPath = "/usr/local/bin/" + bin
|
||||
}
|
||||
} else {
|
||||
copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin+"-update")
|
||||
chMod("/usr/bin/"+bin+"-update", 0755)
|
||||
binPath = "/usr/bin/" + bin
|
||||
}
|
||||
} else {
|
||||
copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+"-update.exe"))
|
||||
copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+".exe"))
|
||||
}
|
||||
chMod(binPath, 0755)
|
||||
return binPath
|
||||
}
|
||||
|
||||
func InstallNpc() {
|
||||
path := common.GetInstallPath()
|
||||
if !common.FileExists(path) {
|
||||
err := os.Mkdir(path, 0755)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
copyStaticFile(common.GetAppPath(), "npc")
|
||||
}
|
||||
|
||||
func InstallNps() string {
|
||||
path := common.GetInstallPath()
|
||||
if common.FileExists(path) {
|
||||
MkidrDirAll(path, "web/static", "web/views")
|
||||
} else {
|
||||
MkidrDirAll(path, "conf", "web/static", "web/views")
|
||||
// not copy config if the config file is exist
|
||||
if err := CopyDir(filepath.Join(common.GetAppPath(), "conf"), filepath.Join(path, "conf")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
chMod(filepath.Join(path, "conf"), 0766)
|
||||
}
|
||||
binPath := copyStaticFile(common.GetAppPath(), "nps")
|
||||
log.Println("install ok!")
|
||||
log.Println("Static files and configuration files in the current directory will be useless")
|
||||
log.Println("The new configuration file is located in", path, "you can edit them")
|
||||
if !common.IsWindows() {
|
||||
log.Println(`You can start with:
|
||||
nps start|stop|restart|uninstall|update or nps-update update
|
||||
anywhere!`)
|
||||
} else {
|
||||
log.Println(`You can copy executable files to any directory and start working with:
|
||||
nps.exe start|stop|restart|uninstall|update or nps-update.exe update
|
||||
now!`)
|
||||
}
|
||||
chMod(common.GetLogPath(), 0777)
|
||||
return binPath
|
||||
}
|
||||
func MkidrDirAll(path string, v ...string) {
|
||||
for _, item := range v {
|
||||
if err := os.MkdirAll(filepath.Join(path, item), 0755); err != nil {
|
||||
log.Fatalf("Failed to create directory %s error:%s", path, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CopyDir(srcPath string, destPath string) error {
|
||||
//检测目录正确性
|
||||
if srcInfo, err := os.Stat(srcPath); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return err
|
||||
} else {
|
||||
if !srcInfo.IsDir() {
|
||||
e := errors.New("SrcPath is not the right directory!")
|
||||
return e
|
||||
}
|
||||
}
|
||||
if destInfo, err := os.Stat(destPath); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if !destInfo.IsDir() {
|
||||
e := errors.New("DestInfo is not the right directory!")
|
||||
return e
|
||||
}
|
||||
}
|
||||
err := filepath.Walk(srcPath, func(path string, f os.FileInfo, err error) error {
|
||||
if f == nil {
|
||||
return err
|
||||
}
|
||||
if !f.IsDir() {
|
||||
destNewPath := strings.Replace(path, srcPath, destPath, -1)
|
||||
log.Println("copy file ::" + path + " to " + destNewPath)
|
||||
copyFile(path, destNewPath)
|
||||
if !common.IsWindows() {
|
||||
chMod(destNewPath, 0766)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
//生成目录并拷贝文件
|
||||
func copyFile(src, dest string) (w int64, err error) {
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer srcFile.Close()
|
||||
//分割path目录
|
||||
destSplitPathDirs := strings.Split(dest, string(filepath.Separator))
|
||||
|
||||
//检测时候存在目录
|
||||
destSplitPath := ""
|
||||
for index, dir := range destSplitPathDirs {
|
||||
if index < len(destSplitPathDirs)-1 {
|
||||
destSplitPath = destSplitPath + dir + string(filepath.Separator)
|
||||
b, _ := pathExists(destSplitPath)
|
||||
if b == false {
|
||||
log.Println("mkdir:" + destSplitPath)
|
||||
//创建目录
|
||||
err := os.Mkdir(destSplitPath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dstFile, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
return io.Copy(dstFile, srcFile)
|
||||
}
|
||||
|
||||
//检测文件夹路径时候存在
|
||||
func pathExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func chMod(name string, mode os.FileMode) {
|
||||
if !common.IsWindows() {
|
||||
os.Chmod(name, mode)
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
package pmux
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PortConn struct {
|
||||
Conn net.Conn
|
||||
rs []byte
|
||||
readMore bool
|
||||
start int
|
||||
}
|
||||
|
||||
func newPortConn(conn net.Conn, rs []byte, readMore bool) *PortConn {
|
||||
return &PortConn{
|
||||
Conn: conn,
|
||||
rs: rs,
|
||||
readMore: readMore,
|
||||
}
|
||||
}
|
||||
|
||||
func (pConn *PortConn) Read(b []byte) (n int, err error) {
|
||||
if len(b) < len(pConn.rs)-pConn.start {
|
||||
defer func() {
|
||||
pConn.start = pConn.start + len(b)
|
||||
}()
|
||||
return copy(b, pConn.rs), nil
|
||||
}
|
||||
if pConn.start < len(pConn.rs) {
|
||||
defer func() {
|
||||
pConn.start = len(pConn.rs)
|
||||
}()
|
||||
n = copy(b, pConn.rs[pConn.start:])
|
||||
if !pConn.readMore {
|
||||
return
|
||||
}
|
||||
}
|
||||
var n2 = 0
|
||||
n2, err = pConn.Conn.Read(b[n:])
|
||||
n = n + n2
|
||||
return
|
||||
}
|
||||
|
||||
func (pConn *PortConn) Write(b []byte) (n int, err error) {
|
||||
return pConn.Conn.Write(b)
|
||||
}
|
||||
|
||||
func (pConn *PortConn) Close() error {
|
||||
return pConn.Conn.Close()
|
||||
}
|
||||
|
||||
func (pConn *PortConn) LocalAddr() net.Addr {
|
||||
return pConn.Conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (pConn *PortConn) RemoteAddr() net.Addr {
|
||||
return pConn.Conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (pConn *PortConn) SetDeadline(t time.Time) error {
|
||||
return pConn.Conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (pConn *PortConn) SetReadDeadline(t time.Time) error {
|
||||
return pConn.Conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (pConn *PortConn) SetWriteDeadline(t time.Time) error {
|
||||
return pConn.Conn.SetWriteDeadline(t)
|
||||
}
|
@@ -1,44 +0,0 @@
|
||||
package pmux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
type PortListener struct {
|
||||
net.Listener
|
||||
connCh chan *PortConn
|
||||
addr net.Addr
|
||||
isClose bool
|
||||
}
|
||||
|
||||
func NewPortListener(connCh chan *PortConn, addr net.Addr) *PortListener {
|
||||
return &PortListener{
|
||||
connCh: connCh,
|
||||
addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
func (pListener *PortListener) Accept() (net.Conn, error) {
|
||||
if pListener.isClose {
|
||||
return nil, errors.New("the listener has closed")
|
||||
}
|
||||
conn := <-pListener.connCh
|
||||
if conn != nil {
|
||||
return conn, nil
|
||||
}
|
||||
return nil, errors.New("the listener has closed")
|
||||
}
|
||||
|
||||
func (pListener *PortListener) Close() error {
|
||||
//close
|
||||
if pListener.isClose {
|
||||
return errors.New("the listener has closed")
|
||||
}
|
||||
pListener.isClose = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pListener *PortListener) Addr() net.Addr {
|
||||
return pListener.addr
|
||||
}
|
166
lib/pmux/pmux.go
166
lib/pmux/pmux.go
@@ -1,166 +0,0 @@
|
||||
// This module is used for port reuse
|
||||
// Distinguish client, web manager , HTTP and HTTPS according to the difference of protocol
|
||||
package pmux
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
HTTP_GET = 716984
|
||||
HTTP_POST = 807983
|
||||
HTTP_HEAD = 726965
|
||||
HTTP_PUT = 808585
|
||||
HTTP_DELETE = 686976
|
||||
HTTP_CONNECT = 677978
|
||||
HTTP_OPTIONS = 798084
|
||||
HTTP_TRACE = 848265
|
||||
CLIENT = 848384
|
||||
ACCEPT_TIME_OUT = 10
|
||||
)
|
||||
|
||||
type PortMux struct {
|
||||
net.Listener
|
||||
port int
|
||||
isClose bool
|
||||
managerHost string
|
||||
clientConn chan *PortConn
|
||||
httpConn chan *PortConn
|
||||
httpsConn chan *PortConn
|
||||
managerConn chan *PortConn
|
||||
}
|
||||
|
||||
func NewPortMux(port int, managerHost string) *PortMux {
|
||||
pMux := &PortMux{
|
||||
managerHost: managerHost,
|
||||
port: port,
|
||||
clientConn: make(chan *PortConn),
|
||||
httpConn: make(chan *PortConn),
|
||||
httpsConn: make(chan *PortConn),
|
||||
managerConn: make(chan *PortConn),
|
||||
}
|
||||
pMux.Start()
|
||||
return pMux
|
||||
}
|
||||
|
||||
func (pMux *PortMux) Start() error {
|
||||
// Port multiplexing is based on TCP only
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+strconv.Itoa(pMux.port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pMux.Listener, err = net.ListenTCP("tcp", tcpAddr)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
os.Exit(0)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
conn, err := pMux.Listener.Accept()
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
//close
|
||||
pMux.Close()
|
||||
}
|
||||
go pMux.process(conn)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pMux *PortMux) process(conn net.Conn) {
|
||||
// Recognition according to different signs
|
||||
// read 3 byte
|
||||
buf := make([]byte, 3)
|
||||
if n, err := io.ReadFull(conn, buf); err != nil || n != 3 {
|
||||
return
|
||||
}
|
||||
var ch chan *PortConn
|
||||
var rs []byte
|
||||
var buffer bytes.Buffer
|
||||
var readMore = false
|
||||
switch common.BytesToNum(buf) {
|
||||
case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager
|
||||
buffer.Reset()
|
||||
r := bufio.NewReader(conn)
|
||||
buffer.Write(buf)
|
||||
for {
|
||||
b, _, err := r.ReadLine()
|
||||
if err != nil {
|
||||
logs.Warn("read line error", err.Error())
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
buffer.Write(b)
|
||||
buffer.Write([]byte("\r\n"))
|
||||
if strings.Index(string(b), "Host:") == 0 || strings.Index(string(b), "host:") == 0 {
|
||||
// Remove host and space effects
|
||||
str := strings.Replace(string(b), "Host:", "", -1)
|
||||
str = strings.Replace(str, "host:", "", -1)
|
||||
str = strings.TrimSpace(str)
|
||||
// Determine whether it is the same as the manager domain name
|
||||
if common.GetIpByAddr(str) == pMux.managerHost {
|
||||
ch = pMux.managerConn
|
||||
} else {
|
||||
ch = pMux.httpConn
|
||||
}
|
||||
b, _ := r.Peek(r.Buffered())
|
||||
buffer.Write(b)
|
||||
rs = buffer.Bytes()
|
||||
break
|
||||
}
|
||||
}
|
||||
case CLIENT: // client connection
|
||||
ch = pMux.clientConn
|
||||
default: // https
|
||||
readMore = true
|
||||
ch = pMux.httpsConn
|
||||
}
|
||||
if len(rs) == 0 {
|
||||
rs = buf
|
||||
}
|
||||
timer := time.NewTimer(ACCEPT_TIME_OUT)
|
||||
select {
|
||||
case <-timer.C:
|
||||
case ch <- newPortConn(conn, rs, readMore):
|
||||
}
|
||||
}
|
||||
|
||||
func (pMux *PortMux) Close() error {
|
||||
if pMux.isClose {
|
||||
return errors.New("the port pmux has closed")
|
||||
}
|
||||
pMux.isClose = true
|
||||
close(pMux.clientConn)
|
||||
close(pMux.httpsConn)
|
||||
close(pMux.httpConn)
|
||||
close(pMux.managerConn)
|
||||
return pMux.Listener.Close()
|
||||
}
|
||||
|
||||
func (pMux *PortMux) GetClientListener() net.Listener {
|
||||
return NewPortListener(pMux.clientConn, pMux.Listener.Addr())
|
||||
}
|
||||
|
||||
func (pMux *PortMux) GetHttpListener() net.Listener {
|
||||
return NewPortListener(pMux.httpConn, pMux.Listener.Addr())
|
||||
}
|
||||
|
||||
func (pMux *PortMux) GetHttpsListener() net.Listener {
|
||||
return NewPortListener(pMux.httpsConn, pMux.Listener.Addr())
|
||||
}
|
||||
|
||||
func (pMux *PortMux) GetManagerListener() net.Listener {
|
||||
return NewPortListener(pMux.managerConn, pMux.Listener.Addr())
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
package pmux
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
)
|
||||
|
||||
func TestPortMux_Close(t *testing.T) {
|
||||
logs.Reset()
|
||||
logs.EnableFuncCallDepth(true)
|
||||
logs.SetLogFuncCallDepth(3)
|
||||
|
||||
pMux := NewPortMux(8888, "Ds")
|
||||
go func() {
|
||||
if pMux.Start() != nil {
|
||||
logs.Warn("Error")
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Second * 3)
|
||||
go func() {
|
||||
l := pMux.GetHttpListener()
|
||||
conn, err := l.Accept()
|
||||
logs.Warn(conn, err)
|
||||
}()
|
||||
go func() {
|
||||
l := pMux.GetHttpListener()
|
||||
conn, err := l.Accept()
|
||||
logs.Warn(conn, err)
|
||||
}()
|
||||
go func() {
|
||||
l := pMux.GetHttpListener()
|
||||
conn, err := l.Accept()
|
||||
logs.Warn(conn, err)
|
||||
}()
|
||||
l := pMux.GetHttpListener()
|
||||
conn, err := l.Accept()
|
||||
logs.Warn(conn, err)
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
package rate
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type rateConn struct {
|
||||
conn io.ReadWriteCloser
|
||||
rate *Rate
|
||||
}
|
||||
|
||||
func NewRateConn(conn io.ReadWriteCloser, rate *Rate) io.ReadWriteCloser {
|
||||
return &rateConn{
|
||||
conn: conn,
|
||||
rate: rate,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *rateConn) Read(b []byte) (n int, err error) {
|
||||
n, err = s.conn.Read(b)
|
||||
if s.rate != nil {
|
||||
s.rate.Get(int64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *rateConn) Write(b []byte) (n int, err error) {
|
||||
n, err = s.conn.Write(b)
|
||||
if s.rate != nil {
|
||||
s.rate.Get(int64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *rateConn) Close() error {
|
||||
return s.conn.Close()
|
||||
}
|
@@ -1,81 +0,0 @@
|
||||
package rate
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Rate struct {
|
||||
bucketSize int64
|
||||
bucketSurplusSize int64
|
||||
bucketAddSize int64
|
||||
stopChan chan bool
|
||||
NowRate int64
|
||||
}
|
||||
|
||||
func NewRate(addSize int64) *Rate {
|
||||
return &Rate{
|
||||
bucketSize: addSize * 2,
|
||||
bucketSurplusSize: 0,
|
||||
bucketAddSize: addSize,
|
||||
stopChan: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Rate) Start() {
|
||||
go s.session()
|
||||
}
|
||||
|
||||
func (s *Rate) add(size int64) {
|
||||
if res := s.bucketSize - s.bucketSurplusSize; res < s.bucketAddSize {
|
||||
atomic.AddInt64(&s.bucketSurplusSize, res)
|
||||
return
|
||||
}
|
||||
atomic.AddInt64(&s.bucketSurplusSize, size)
|
||||
}
|
||||
|
||||
//回桶
|
||||
func (s *Rate) ReturnBucket(size int64) {
|
||||
s.add(size)
|
||||
}
|
||||
|
||||
//停止
|
||||
func (s *Rate) Stop() {
|
||||
s.stopChan <- true
|
||||
}
|
||||
|
||||
func (s *Rate) Get(size int64) {
|
||||
if s.bucketSurplusSize >= size {
|
||||
atomic.AddInt64(&s.bucketSurplusSize, -size)
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if s.bucketSurplusSize >= size {
|
||||
atomic.AddInt64(&s.bucketSurplusSize, -size)
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Rate) session() {
|
||||
ticker := time.NewTicker(time.Second * 1)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if rs := s.bucketAddSize - s.bucketSurplusSize; rs > 0 {
|
||||
s.NowRate = rs
|
||||
} else {
|
||||
s.NowRate = s.bucketSize - s.bucketSurplusSize
|
||||
}
|
||||
s.add(s.bucketAddSize)
|
||||
case <-s.stopChan:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
package sheap
|
||||
|
||||
type IntHeap []int64
|
||||
|
||||
func (h IntHeap) Len() int { return len(h) }
|
||||
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
|
||||
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
|
||||
func (h *IntHeap) Push(x interface{}) {
|
||||
// Push and Pop use pointer receivers because they modify the slice's length,
|
||||
// not just its contents.
|
||||
*h = append(*h, x.(int64))
|
||||
}
|
||||
|
||||
func (h *IntHeap) Pop() interface{} {
|
||||
old := *h
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
*h = old[0 : n-1]
|
||||
return x
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
package version
|
||||
|
||||
const VERSION = "0.26.10"
|
||||
|
||||
// Compulsory minimum version, Minimum downward compatibility to this version
|
||||
func GetVersion() string {
|
||||
return "0.26.0"
|
||||
}
|
Reference in New Issue
Block a user