remove old file

This commit is contained in:
he liu
2022-01-23 17:14:08 +08:00
parent ab648d6f0c
commit 4877ba185c
153 changed files with 0 additions and 31280 deletions

102
lib/cache/lru.go vendored
View File

@@ -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
}

View File

@@ -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
`
)

View File

@@ -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
}

View File

@@ -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),
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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
}

View File

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

View File

@@ -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
}

View File

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

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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")
}
}

View File

@@ -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"))
}
}()
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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))

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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
}

View File

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

View File

@@ -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)
}

View File

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

View File

@@ -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
}
}
}

View File

@@ -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
}

View File

@@ -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"
}