add functions

pull/59/head
刘河 2019-02-23 23:29:48 +08:00
parent 2c608ddb7f
commit 750ecb824a
36 changed files with 607 additions and 289 deletions

View File

@ -8,11 +8,11 @@ import (
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/lib/version"
"github.com/cnlh/nps/server/tool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"log"
"net"
"strconv"
"sync"
@ -48,6 +48,7 @@ type Bridge struct {
tunnelType string //bridge type kcp or tcp
OpenTask chan *file.Tunnel
CloseClient chan int
SecretChan chan *conn.Secret
clientLock sync.RWMutex
Register map[string]time.Time
registerLock sync.RWMutex
@ -65,6 +66,7 @@ func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList map[int
t.Register = make(map[string]time.Time)
t.ipVerify = ipVerify
t.runList = runList
t.SecretChan = make(chan *conn.Secret)
return t
}
@ -80,7 +82,7 @@ func (s *Bridge) StartTunnel() error {
c, err := s.kcpListener.AcceptKCP()
conn.SetUdpSession(c)
if err != nil {
lg.Println(err)
logs.Warn(err)
continue
}
go s.cliProcess(conn.NewConn(c))
@ -95,7 +97,7 @@ func (s *Bridge) StartTunnel() error {
for {
c, err := s.tcpListener.Accept()
if err != nil {
lg.Println(err)
logs.Warn(err)
continue
}
go s.cliProcess(conn.NewConn(c))
@ -116,6 +118,12 @@ func (s *Bridge) verifySuccess(c *conn.Conn) {
}
func (s *Bridge) cliProcess(c *conn.Conn) {
c.Write([]byte(crypt.Md5(version.GetVersion())))
if b, err := c.ReadFlag(); err != nil || string(b) != version.VERSION_OK {
logs.Info("The client %s version does not match", c.Conn.RemoteAddr())
c.Close()
return
}
c.SetReadDeadline(5, s.tunnelType)
var buf []byte
var err error
@ -126,7 +134,7 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
//验证
id, err := file.GetCsvDb().GetIdByVerifyKey(string(buf), c.Conn.RemoteAddr().String())
if err != nil {
lg.Println("当前客户端连接校验错误,关闭此客户端:", c.Conn.RemoteAddr())
logs.Info("Current client connection validation error, close this client:", c.Conn.RemoteAddr())
s.verifyError(c)
return
} else {
@ -136,7 +144,7 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
if flag, err := c.ReadFlag(); err == nil {
s.typeDeal(flag, c, id)
} else {
log.Println(err, flag)
logs.Warn(err, flag)
}
return
}
@ -182,7 +190,7 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
s.Client[id] = NewClient(nil, c, nil)
s.clientLock.Unlock()
}
lg.Printf("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
logs.Info("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
go s.GetStatus(id)
case common.WORK_CHAN:
s.clientLock.Lock()
@ -200,6 +208,10 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
go s.GetConfig(c)
case common.WORK_REGISTER:
go s.register(c)
case common.WORD_SECRET:
if b, err := c.ReadLen(32); err == nil {
s.SecretChan <- conn.NewSecret(string(b), c)
}
case common.WORK_SEND_STATUS:
s.clientLock.Lock()
if v, ok := s.Client[id]; ok {
@ -293,7 +305,7 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (t
v.signal.SendLinkInfo(link)
if err != nil {
lg.Println("send link information error:", err, link.Id)
logs.Warn("send link information error:", err, link.Id)
s.DelClient(clientId)
return
}
@ -314,7 +326,7 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (t
}
} else {
s.clientLock.Unlock()
err = errors.New("the connection is not connect")
err = errors.New(fmt.Sprintf("the client %d is not connect", clientId))
}
return
}
@ -328,6 +340,7 @@ func (s *Bridge) DelClient(id int) {
func (s *Bridge) GetConfig(c *conn.Conn) {
var client *file.Client
var fail bool
for {
flag, err := c.ReadFlag()
if err != nil {
@ -357,19 +370,15 @@ func (s *Bridge) GetConfig(c *conn.Conn) {
binary.Write(c, binary.LittleEndian, []byte(str))
}
case common.NEW_CONF:
//new client ,Set the client not to store to the file
client = file.NewClient(crypt.GetRandomString(16), true, false)
client.Remark = "public veky"
//Send the key to the client
file.GetCsvDb().NewClient(client)
var err error
if client, err = c.GetConfigInfo(); err != nil {
c.Write([]byte(client.VerifyKey))
if config, err := c.GetConfigInfo(); err != nil {
fail = true
c.WriteAddFail()
break
} else {
client.Cnf = config
c.Write([]byte(client.VerifyKey))
file.GetCsvDb().NewClient(client)
c.WriteAddOk()
}
case common.NEW_HOST:
@ -380,6 +389,7 @@ func (s *Bridge) GetConfig(c *conn.Conn) {
} else if file.GetCsvDb().IsHostExist(h) {
fail = true
c.WriteAddFail()
break
} else {
h.Client = client
file.GetCsvDb().NewHost(h)
@ -397,6 +407,13 @@ func (s *Bridge) GetConfig(c *conn.Conn) {
fail = true
c.WriteAddFail()
break
} else if t.Mode == "secretServer" {
ports = append(ports, 0)
}
if len(ports) == 0 {
fail = true
c.WriteAddFail()
break
}
for i := 0; i < len(ports); i++ {
tl := new(file.Tunnel)
@ -407,17 +424,24 @@ func (s *Bridge) GetConfig(c *conn.Conn) {
tl.Remark = t.Remark
} else {
tl.Remark = t.Remark + "_" + strconv.Itoa(tl.Port)
tl.Target = strconv.Itoa(targets[i])
tl.Target = t.TargetAddr + ":" + strconv.Itoa(targets[i])
}
tl.Id = file.GetCsvDb().GetTaskId()
tl.Status = true
tl.Flow = new(file.Flow)
tl.NoStore = true
tl.Client = client
file.GetCsvDb().NewTask(tl)
if b := tool.TestServerPort(tl.Port, tl.Mode); !b {
tl.Password = t.Password
if err := file.GetCsvDb().NewTask(tl); err != nil {
logs.Notice("Add task error ", err.Error())
fail = true
c.WriteAddFail()
break
}
if b := tool.TestServerPort(tl.Port, tl.Mode); !b && t.Mode != "secretServer" {
fail = true
c.WriteAddFail()
break
} else {
s.OpenTask <- tl
}
@ -460,7 +484,7 @@ func (s *Bridge) clientCopy(clientId int) {
for {
if id, err := client.tunnel.GetLen(); err != nil {
lg.Println("read msg content length error close client")
logs.Info("read msg content length error close client")
s.delClient(clientId)
break
} else {
@ -470,7 +494,7 @@ func (s *Bridge) clientCopy(clientId int) {
if content, err := client.tunnel.GetMsgContent(link); err != nil {
pool.PutBufPoolCopy(content)
s.delClient(clientId)
lg.Println("read msg content error", err, "close client")
logs.Notice("read msg content error", err, "close client")
break
} else {
link.MsgCh <- content

View File

@ -3,9 +3,10 @@ package client
import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net"
"os"
"sync"
"time"
)
@ -40,11 +41,11 @@ func (s *TRPClient) Start() {
retry:
c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl)
if err != nil {
lg.Println("The connection server failed and will be reconnected in five seconds")
logs.Error("The connection server failed and will be reconnected in five seconds")
time.Sleep(time.Second * 5)
goto retry
}
lg.Printf("Successful connection with server %s", s.svrAddr)
logs.Info("Successful connection with server %s", s.svrAddr)
s.processor(c)
}
@ -65,12 +66,13 @@ func (s *TRPClient) processor(c *conn.Conn) {
for {
flags, err := c.ReadFlag()
if err != nil {
lg.Printf("Accept server data error %s, end this service", err.Error())
logs.Error("Accept server data error %s, end this service", err.Error())
break
}
switch flags {
case common.VERIFY_EER:
lg.Fatalf("VKey:%s is incorrect, the server refuses to connect, please check", s.vKey)
logs.Error("VKey:%s is incorrect, the server refuses to connect, please check", s.vKey)
os.Exit(0)
case common.NEW_CONN:
if link, err := c.GetLinkInfo(); err != nil {
break
@ -83,12 +85,13 @@ func (s *TRPClient) processor(c *conn.Conn) {
link.Run(false)
}
case common.RES_CLOSE:
lg.Fatalln("The authentication key is connected by another client or the server closes the client.")
logs.Error("The authentication key is connected by another client or the server closes the client.")
os.Exit(0)
case common.RES_MSG:
lg.Println("Server-side return error")
logs.Error("Server-side return error")
break
default:
lg.Println("The error could not be resolved")
logs.Warn("The error could not be resolved")
break
}
}
@ -103,7 +106,7 @@ func (s *TRPClient) linkProcess(link *conn.Link, c *conn.Conn) {
if err != nil {
c.WriteFail(link.Id)
lg.Println("connect to ", link.Host, "error:", err)
logs.Warn("connect to ", link.Host, "error:", err)
return
}
c.WriteSuccess(link.Id)
@ -134,7 +137,7 @@ func (s *TRPClient) getMsgStatus() {
var err error
s.msgTunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_SEND_STATUS, s.proxyUrl)
if err != nil {
lg.Println("connect to ", s.svrAddr, "error:", err)
logs.Error("connect to ", s.svrAddr, "error:", err)
return
}
go func() {
@ -160,7 +163,7 @@ func (s *TRPClient) dealChan() {
var err error
s.tunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl)
if err != nil {
lg.Println("connect to ", s.svrAddr, "error:", err)
logs.Error("connect to ", s.svrAddr, "error:", err)
return
}
go func() {

View File

@ -1,11 +1,14 @@
package client
import (
"encoding/binary"
"errors"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/version"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"github.com/cnlh/nps/vender/golang.org/x/net/proxy"
"io/ioutil"
@ -39,7 +42,7 @@ func GetTaskStatus(path string) {
if l, err := c.GetLen(); err != nil {
log.Fatalln(err)
} else if b, err := c.ReadLen(l); err != nil {
lg.Fatalln(err)
log.Fatalln(err)
} else {
arr := strings.Split(string(b), common.CONN_DATA_SEQ)
for _, v := range cnf.Hosts {
@ -74,14 +77,16 @@ var errAdd = errors.New("The server returned an error, which port or host may ha
func StartFromFile(path string) {
first := true
cnf, err := config.NewConfig(path)
if err != nil {
lg.Fatalln(err)
if err != nil || cnf.CommonConfig == nil {
logs.Error("Config file %s loading error", path)
os.Exit(0)
}
lg.Printf("Loading configuration file %s successfully", path)
logs.Info("Loading configuration file %s successfully", path)
re:
if first || cnf.CommonConfig.AutoReconnection {
if !first {
lg.Println("Reconnecting...")
logs.Info("Reconnecting...")
time.Sleep(time.Second * 5)
}
} else {
@ -90,48 +95,51 @@ re:
first = false
c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl)
if err != nil {
lg.Println(err)
logs.Error(err)
goto re
}
if _, err := c.SendConfigInfo(cnf.CommonConfig.Cnf); err != nil {
lg.Println(err)
if _, err := c.SendConfigInfo(cnf.CommonConfig); err != nil {
logs.Error(err)
goto re
}
var b []byte
if b, err = c.ReadLen(16); err != nil {
lg.Println(err)
logs.Error(err)
goto re
} else {
ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(string(b)), 0600)
}
if !c.GetAddStatus() {
lg.Println(errAdd)
logs.Error(errAdd)
goto re
}
for _, v := range cnf.Hosts {
if _, err := c.SendHostInfo(v); err != nil {
lg.Println(err)
logs.Error(err)
goto re
}
if !c.GetAddStatus() {
lg.Println(errAdd, v.Host)
logs.Error(errAdd, v.Host)
goto re
}
}
for _, v := range cnf.Tasks {
if _, err := c.SendTaskInfo(v); err != nil {
lg.Println(err)
logs.Error(err)
goto re
}
if !c.GetAddStatus() {
lg.Println(errAdd, v.Ports)
logs.Error(errAdd, v.Ports)
goto re
}
}
for _, v := range cnf.LocalServer {
go StartLocalServer(v, cnf.CommonConfig)
}
c.Close()
NewRPClient(cnf.CommonConfig.Server, string(b), cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl).Start()
CloseLocalServer()
goto re
}
@ -163,16 +171,30 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
return nil, err
}
c := conn.NewConn(connection)
if b, err := c.ReadLen(32); err != nil {
logs.Error(err)
os.Exit(0)
} else if crypt.Md5(version.GetVersion()) != string(b) {
logs.Error("The client does not match the server version. The current version of the client is", version.GetVersion())
os.Exit(0)
} else if binary.Write(c, binary.LittleEndian, []byte(version.VERSION_OK)); err != nil {
logs.Error(err)
os.Exit(0)
}
if _, err := c.Write([]byte(common.Getverifyval(vkey))); err != nil {
lg.Println(err)
logs.Error(err)
os.Exit(0)
}
if s, err := c.ReadFlag(); err != nil {
lg.Println(err)
logs.Error(err)
os.Exit(0)
} else if s == common.VERIFY_EER {
lg.Fatalf("Validation key %s incorrect", vkey)
logs.Error("Validation key %s incorrect", vkey)
os.Exit(0)
}
if _, err := c.Write([]byte(connType)); err != nil {
lg.Println(err)
logs.Error(err)
os.Exit(0)
}
c.SetAlive(tp)

54
client/local.go Normal file
View File

@ -0,0 +1,54 @@
package client
import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net"
"strings"
)
var LocalServer []*net.TCPListener
func CloseLocalServer() {
for _, v := range LocalServer {
v.Close()
}
}
func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error {
listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), l.Port, ""})
if err != nil {
logs.Error("Local listener startup failed port %d, error %s", l.Port, err.Error())
return err
}
LocalServer = append(LocalServer, listener)
logs.Info("Successful start-up of local monitoring, port", l.Port)
for {
c, err := listener.AcceptTCP()
if err != nil {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
logs.Info(err)
continue
}
go process(c, config, l)
}
return nil
}
func process(conn net.Conn, config *config.CommonConfig, l *config.LocalServer) {
c, err := NewConn(config.Tp, config.VKey, config.Server, common.WORD_SECRET, config.ProxyUrl)
if err != nil {
logs.Error("Local connection server failed ", err.Error())
}
if _, err := c.Write([]byte(crypt.Md5(l.Password))); err != nil {
logs.Error("Local connection server failed ", err.Error())
}
go common.CopyBuffer(c, conn)
common.CopyBuffer(conn, c)
c.Close()
conn.Close()
}

View File

@ -5,14 +5,12 @@ import (
"github.com/cnlh/nps/client"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/daemon"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"os"
"strings"
"time"
)
const VERSION = "v0.0.15"
var (
serverAddr = flag.String("server", "", "Server addr (ip:port)")
configPath = flag.String("config", "npc.conf", "Configuration file path")
@ -20,6 +18,7 @@ var (
logType = flag.String("log", "stdout", "Log output modestdout|file")
connType = flag.String("type", "tcp", "Connection type with the serverkcp|tcp")
proxyUrl = flag.String("proxy", "", "proxy socks5 url(eg:socks5://111:222@127.0.0.1:9007)")
logLevel = flag.String("log_level", "7", "log level 0~7")
registerTime = flag.Int("time", 2, "register time long /h")
)
@ -37,14 +36,14 @@ func main() {
}
daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath())
if *logType == "stdout" {
lg.InitLogFile("npc", true, common.GetLogPath())
logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
} else {
lg.InitLogFile("npc", false, common.GetLogPath())
logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"npc_log.log"}`)
}
if *verifyKey != "" && *serverAddr != "" {
for {
client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl).Start()
lg.Println("It will be reconnected in five seconds")
logs.Info("It will be reconnected in five seconds")
time.Sleep(time.Second * 5)
}
} else {

View File

@ -6,19 +6,18 @@ import (
"github.com/cnlh/nps/lib/daemon"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/install"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/server"
"github.com/cnlh/nps/server/test"
"github.com/cnlh/nps/vender/github.com/astaxie/beego"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
_ "github.com/cnlh/nps/web/routers"
"log"
"os"
"path/filepath"
)
const VERSION = "v0.0.15"
var (
level string
logType = flag.String("log", "stdout", "Log output modestdout|file")
)
@ -37,17 +36,22 @@ func main() {
return
}
}
if level = beego.AppConfig.String("logLevel"); level == "" {
level = "7"
}
logs.Reset()
if *logType == "stdout" {
lg.InitLogFile("nps", true, common.GetLogPath())
logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
} else {
lg.InitLogFile("nps", false, common.GetLogPath())
logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"nps_log.log"}`)
}
task := &file.Tunnel{
Mode: "webServer",
}
bridgePort, err := beego.AppConfig.Int("bridgePort")
if err != nil {
lg.Fatalln("Getting bridgePort error", err)
logs.Error("Getting bridgePort error", err)
os.Exit(0)
}
beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "app.conf"))
server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridgeType"))

View File

@ -36,4 +36,11 @@ bridgeType=tcp
# After the connection, the server will be able to open relevant ports and parse related domain names according to its own configuration file.
publicVkey=123
#Traffic data persistence interval(minute)
#Ignorance means no persistence
flowStoreInterval=1
#log level
#LevelEmergency->0 LevelAlert->1 LevelCritical->2 LevelError->3 LevelWarning->4
#LevelNotice->5 LevelInformational->6 LevelDebug->7
logLevel=7

View File

@ -1 +1 @@
7,7hv3avgeg2ldzvx7,,true,,,0,,0,0
7,7hv3avgeg2ldzvx7,,true,,,1,,0,999,10

1 7 7hv3avgeg2ldzvx7 true 1 0 0 0 999 10

View File

@ -0,0 +1 @@
b.o.com,127.0.0.1:8082,7,,,,/,3,12995709,38827
1 b.o.com 127.0.0.1:8082 7 / 3 12995709 38827
1 b.o.com 127.0.0.1:8082 7 / 3 12995709 38827

View File

@ -4,32 +4,24 @@ tp=tcp
vkey=123
auto_reconnection=true
crypt=true
[web1]
host=a.o.com
host_change=www.proxy.com
target=127.0.0.1:8082
target=127.0.0.1:8080
location=/
[web2]
host=a.proxy.com
target=127.0.0.1:8080,127.0.0.1:8082
host=127.0.0.1
host_change=www.proxy.com
header_set_proxy=nps
target=127.0.0.1:8080
location=/cdn
[tcp]
mode=tcpServer
target=8001-8005,8082
port=9001-9005,9006
[ssh_1118]
mode=secretServer
password=1111
target=123.206.77.88:22
[socks5]
mode=socks5Server
port=9007
[http]
mode=httpProxyServer
port=9008
[udp]
mode=udpServer
port=53
target=114.114.114.114:53
[secret_ssh]
password=1111
port=1000

View File

@ -1 +1,3 @@
9010,socks5Server,,1,27,7,
9010,socks5Server,,1,27,7,,0,0,
9002,tcpServer,127.0.0.1:8082,1,28,7,,9175971,17054,
0,secretServer,123.206.77.88:22,1,30,7,私密隧道测试,15620,12628,tests

1 9010 socks5Server 1 27 7 0 0
2 9002 tcpServer 127.0.0.1:8082 1 28 7 9175971 17054
3 0 secretServer 123.206.77.88:22 1 30 7 私密隧道测试 15620 12628 tests

View File

@ -13,6 +13,7 @@ const (
WORK_SEND_STATUS = "sdst"
WORK_CONFIG = "conf"
WORK_REGISTER = "rgst"
WORD_SECRET = "sert"
WORK_STATUS = "stus"
RES_SIGN = "sign"
RES_MSG = "msg0"
@ -21,7 +22,6 @@ const (
NEW_TASK = "task" //新连接标志
NEW_CONF = "conf" //新连接标志
NEW_HOST = "host" //新连接标志
CONN_TCP = "tcp"
CONN_UDP = "udp"
UnauthorizedBytes = `HTTP/1.1 401 Unauthorized

View File

@ -5,6 +5,8 @@ import (
"encoding/base64"
"encoding/binary"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/pool"
"io"
"io/ioutil"
"net"
"net/http"
@ -246,3 +248,10 @@ func GetIpByAddr(addr string) string {
arr := strings.Split(addr, ":")
return arr[0]
}
func CopyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
buf := pool.BufPoolCopy.Get().([]byte)
io.CopyBuffer(dst, src, buf)
pool.PutBufPoolCopy(buf)
return written, err
}

View File

@ -1,6 +1,7 @@
package config
import (
"errors"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"regexp"
@ -14,6 +15,11 @@ type CommonConfig struct {
AutoReconnection bool
Cnf *file.Config
ProxyUrl string
Client *file.Client
}
type LocalServer struct {
Port int
Password string
}
type Config struct {
content string
@ -21,6 +27,7 @@ type Config struct {
CommonConfig *CommonConfig
Hosts []*file.Host
Tasks []*file.Tunnel
LocalServer []*LocalServer
}
func NewConfig(path string) (c *Config, err error) {
@ -44,6 +51,11 @@ func NewConfig(path string) (c *Config, err error) {
nextIndex = len(c.content)
}
nowContent = c.content[nowIndex:nextIndex]
if strings.Index(getTitleContent(c.title[i]), "secret") == 0 {
c.LocalServer = append(c.LocalServer, delLocalService(nowContent))
continue
}
switch c.title[i] {
case "[common]":
c.CommonConfig = dealCommon(nowContent)
@ -68,9 +80,11 @@ func getTitleContent(s string) string {
re, _ := regexp.Compile(`[\[\]]`)
return re.ReplaceAllString(s, "")
}
func dealCommon(s string) *CommonConfig {
c := &CommonConfig{}
c.Cnf = new(file.Config)
c.Client = file.NewClient("", true, true)
for _, v := range strings.Split(s, "\n") {
item := strings.Split(v, "=")
if len(item) == 0 {
@ -97,10 +111,19 @@ func dealCommon(s string) *CommonConfig {
c.Cnf.Crypt = common.GetBoolByStr(item[1])
case "proxy_socks5_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]
}
}
return c
}
func dealHost(s string) *file.Host {
h := &file.Host{}
var headerChange string
@ -146,12 +169,35 @@ func dealTunnel(s string) *file.Tunnel {
t.Mode = item[1]
case "target":
t.Target = item[1]
case "targetAddr":
t.TargetAddr = item[1]
case "password":
t.Password = item[1]
}
}
return t
}
func delLocalService(s string) *LocalServer {
l := new(LocalServer)
for _, v := range strings.Split(s, "\n") {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch item[0] {
case "port":
l.Port = common.GetIntNoErrByStr(item[1])
case "password":
l.Password = item[1]
}
}
return l
}
func getAllTitle(content string) (arr []string, err error) {
var re *regexp.Regexp
re, err = regexp.Compile(`\[.+?\]`)
@ -159,5 +205,13 @@ func getAllTitle(content string) (arr []string, err error) {
return
}
arr = re.FindAllString(content, -1)
m := make(map[string]bool)
for _, v := range arr {
if _, ok := m[v]; ok {
err = errors.New("Item names are not allowed to be duplicated")
return
}
m[v] = true
}
return
}

View File

@ -6,6 +6,8 @@ import (
"encoding/binary"
"errors"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/lib/rate"
@ -317,7 +319,7 @@ func (s *Conn) GetHostInfo() (h *file.Host, err error) {
}
//send task info
func (s *Conn) SendConfigInfo(c *file.Config) (int, error) {
func (s *Conn) SendConfigInfo(c *config.CommonConfig) (int, error) {
/*
The task info is formed as follows:
+----+-----+---------+
@ -328,14 +330,15 @@ func (s *Conn) SendConfigInfo(c *file.Config) (int, error) {
*/
raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, []byte(common.NEW_CONF))
common.BinaryWrite(raw, c.U, c.P, common.GetStrByBool(c.Crypt), c.Compress)
common.BinaryWrite(raw, c.Cnf.U, c.Cnf.P, common.GetStrByBool(c.Cnf.Crypt), c.Cnf.Compress, strconv.Itoa(c.Client.RateLimit),
strconv.Itoa(int(c.Client.Flow.FlowLimit)), strconv.Itoa(c.Client.MaxConn), c.Client.Remark)
s.Lock()
defer s.Unlock()
return s.Write(raw.Bytes())
}
//get task info
func (s *Conn) GetConfigInfo() (c *file.Config, err error) {
func (s *Conn) GetConfigInfo() (c *file.Client, err error) {
var l int
var b []byte
if l, err = s.GetLen(); err != nil {
@ -344,12 +347,16 @@ func (s *Conn) GetConfigInfo() (c *file.Config, err error) {
return
} else {
arr := strings.Split(string(b), common.CONN_DATA_SEQ)
c = new(file.Config)
c.U = arr[0]
c.P = arr[1]
c.Crypt = common.GetBoolByStr(arr[2])
c.Compress = arr[3]
c.CompressDecode, c.CompressDecode = common.GetCompressType(arr[3])
c = file.NewClient(crypt.GetRandomString(16), true, false)
c.Cnf.U = arr[0]
c.Cnf.P = arr[1]
c.Cnf.Crypt = common.GetBoolByStr(arr[2])
c.Cnf.Compress = arr[3]
c.RateLimit = common.GetIntNoErrByStr(arr[4])
c.Flow.FlowLimit = int64(common.GetIntNoErrByStr(arr[5]))
c.MaxConn = common.GetIntNoErrByStr(arr[6])
c.Remark = arr[7]
c.Cnf.CompressDecode, c.Cnf.CompressDecode = common.GetCompressType(arr[3])
}
return
}
@ -366,7 +373,7 @@ func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) {
*/
raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK))
common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark)
common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark, t.TargetAddr, t.Password)
s.Lock()
defer s.Unlock()
return s.Write(raw.Bytes())
@ -390,6 +397,8 @@ func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
t.Status = true
t.Flow = new(file.Flow)
t.Remark = arr[3]
t.TargetAddr = arr[4]
t.Password = arr[5]
t.NoStore = true
}
return

View File

@ -8,6 +8,18 @@ import (
"net"
)
type Secret struct {
Password string
Conn *Conn
}
func NewSecret(p string, conn *Conn) *Secret {
return &Secret{
Password: p,
Conn: conn,
}
}
type Link struct {
Id int //id
ConnType string //连接类型

View File

@ -14,7 +14,9 @@ var (
func GetCsvDb() *Csv {
once.Do(func() {
CsvDb = NewCsv(common.GetRunPath())
CsvDb.Init()
CsvDb.LoadClientFromCsv()
CsvDb.LoadTaskFromCsv()
CsvDb.LoadHostFromCsv()
})
return CsvDb
}

View File

@ -3,9 +3,11 @@ package file
import (
"encoding/csv"
"errors"
"fmt"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/rate"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net/http"
"os"
"path/filepath"
@ -33,17 +35,11 @@ type Csv struct {
sync.Mutex
}
func (s *Csv) Init() {
s.LoadClientFromCsv()
s.LoadTaskFromCsv()
s.LoadHostFromCsv()
}
func (s *Csv) StoreTasksToCsv() {
// 创建文件
csvFile, err := os.Create(filepath.Join(s.RunPath, "conf", "tasks.csv"))
if err != nil {
lg.Fatalf(err.Error())
logs.Error(err.Error())
}
defer csvFile.Close()
writer := csv.NewWriter(csvFile)
@ -59,10 +55,13 @@ func (s *Csv) StoreTasksToCsv() {
strconv.Itoa(task.Id),
strconv.Itoa(task.Client.Id),
task.Remark,
strconv.Itoa(int(task.Flow.ExportFlow)),
strconv.Itoa(int(task.Flow.InletFlow)),
task.Password,
}
err := writer.Write(record)
if err != nil {
lg.Fatalf(err.Error())
logs.Error(err.Error())
}
}
writer.Flush()
@ -90,7 +89,8 @@ func (s *Csv) LoadTaskFromCsv() {
path := filepath.Join(s.RunPath, "conf", "tasks.csv")
records, err := s.openFile(path)
if err != nil {
lg.Fatalln("配置文件打开错误:", path)
logs.Error("Profile Opening Error:", path)
os.Exit(0)
}
var tasks []*Tunnel
// 将每一行数据保存到内存slice中
@ -102,8 +102,11 @@ func (s *Csv) LoadTaskFromCsv() {
Status: common.GetBoolByStr(item[3]),
Id: common.GetIntNoErrByStr(item[4]),
Remark: item[6],
Password: item[9],
}
post.Flow = new(Flow)
post.Flow.ExportFlow = int64(common.GetIntNoErrByStr(item[7]))
post.Flow.InletFlow = int64(common.GetIntNoErrByStr(item[8]))
if post.Client, err = s.GetClient(common.GetIntNoErrByStr(item[5])); err != nil {
continue
}
@ -142,10 +145,16 @@ func (s *Csv) GetIdByVerifyKey(vKey string, addr string) (int, error) {
return 0, errors.New("not found")
}
func (s *Csv) NewTask(t *Tunnel) {
func (s *Csv) NewTask(t *Tunnel) error {
for _, v := range s.Tasks {
if v.Mode == "secretServer" && v.Password == t.Password {
return errors.New(fmt.Sprintf("Secret mode keys %s must be unique", t.Password))
}
}
t.Flow = new(Flow)
s.Tasks = append(s.Tasks, t)
s.StoreTasksToCsv()
return nil
}
func (s *Csv) UpdateTask(t *Tunnel) error {
@ -171,6 +180,16 @@ func (s *Csv) DelTask(id int) error {
return errors.New("不存在")
}
//md5 password
func (s *Csv) GetSecretTask(p string) *Tunnel {
for _, v := range s.Tasks {
if crypt.Md5(v.Password) == p {
return v
}
}
return nil
}
func (s *Csv) GetTask(id int) (v *Tunnel, err error) {
for _, v = range s.Tasks {
if v.Id == id {
@ -205,6 +224,8 @@ func (s *Csv) StoreHostToCsv() {
host.Remark,
host.Location,
strconv.Itoa(host.Id),
strconv.Itoa(int(host.Flow.ExportFlow)),
strconv.Itoa(int(host.Flow.InletFlow)),
}
err1 := writer.Write(record)
if err1 != nil {
@ -219,7 +240,8 @@ func (s *Csv) LoadClientFromCsv() {
path := filepath.Join(s.RunPath, "conf", "clients.csv")
records, err := s.openFile(path)
if err != nil {
lg.Fatalln("配置文件打开错误:", path)
logs.Error("Profile Opening Error:", path)
os.Exit(0)
}
var clients []*Client
// 将每一行数据保存到内存slice中
@ -236,6 +258,7 @@ func (s *Csv) LoadClientFromCsv() {
Crypt: common.GetBoolByStr(item[6]),
Compress: item[7],
},
MaxConn: common.GetIntNoErrByStr(item[10]),
}
if post.Id > s.ClientIncreaseId {
s.ClientIncreaseId = post.Id
@ -255,7 +278,8 @@ func (s *Csv) LoadHostFromCsv() {
path := filepath.Join(s.RunPath, "conf", "hosts.csv")
records, err := s.openFile(path)
if err != nil {
lg.Fatalln("配置文件打开错误:", path)
logs.Error("Profile Opening Error:", path)
os.Exit(0)
}
var hosts []*Host
// 将每一行数据保存到内存slice中
@ -273,6 +297,8 @@ func (s *Csv) LoadHostFromCsv() {
continue
}
post.Flow = new(Flow)
post.Flow.ExportFlow = int64(common.GetIntNoErrByStr(item[8]))
post.Flow.InletFlow = int64(common.GetIntNoErrByStr(item[9]))
hosts = append(hosts, post)
if post.Id > s.HostIncreaseId {
s.HostIncreaseId = post.Id
@ -350,7 +376,9 @@ func (s *Csv) NewClient(c *Client) {
if c.Id == 0 {
c.Id = s.GetClientId()
}
if c.Flow == nil {
c.Flow = new(Flow)
}
s.Lock()
defer s.Unlock()
s.Clients = append(s.Clients, c)
@ -433,6 +461,8 @@ func (s *Csv) GetHostById(id int) (h *Host, err error) {
//get key by host from x
func (s *Csv) GetInfoByHost(host string, r *http.Request) (h *Host, err error) {
var hosts []*Host
//Handling Ported Access
host = common.GetIpByAddr(host)
for _, v := range s.Hosts {
//Remove http(s) http(s)://a.proxy.com
//*.proxy.com *.a.proxy.com Do some pan-parsing
@ -467,7 +497,7 @@ func (s *Csv) StoreClientsToCsv() {
// 创建文件
csvFile, err := os.Create(filepath.Join(s.RunPath, "conf", "clients.csv"))
if err != nil {
lg.Fatalln(err.Error())
logs.Error(err.Error())
}
defer csvFile.Close()
writer := csv.NewWriter(csvFile)
@ -486,10 +516,11 @@ func (s *Csv) StoreClientsToCsv() {
client.Cnf.Compress,
strconv.Itoa(client.RateLimit),
strconv.Itoa(int(client.Flow.FlowLimit)),
strconv.Itoa(int(client.MaxConn)),
}
err := writer.Write(record)
if err != nil {
lg.Fatalln(err.Error())
logs.Error(err.Error())
}
}
writer.Flush()

View File

@ -33,6 +33,8 @@ type Client struct {
Rate *rate.Rate //速度控制
NoStore bool
NoDisplay bool
MaxConn int //客户端最大连接数
NowConn int //当前连接数
id int
sync.RWMutex
}
@ -62,6 +64,26 @@ func (s *Client) GetId() int {
return s.id
}
func (s *Client) CutConn() {
s.Lock()
defer s.Unlock()
s.NowConn++
}
func (s *Client) AddConn() {
s.Lock()
defer s.Unlock()
s.NowConn--
}
func (s *Client) GetConn() bool {
s.CutConn()
if s.MaxConn == 0 || s.NowConn < s.MaxConn {
return true
}
return false
}
type Tunnel struct {
Id int //Id
Port int //服务端监听端口
@ -72,7 +94,9 @@ type Tunnel struct {
Client *Client //所属客户端id
Ports string //客户端与服务端传递
Flow *Flow
Password string //私密模式密码,唯一
Remark string //备注
TargetAddr string
NoStore bool
}
@ -114,15 +138,3 @@ func (s *Host) GetRandomTarget() string {
}
return s.TargetArr[s.NowIndex]
}
//深拷贝Config
func DeepCopyConfig(c *Config) *Config {
return &Config{
U: c.U,
P: c.P,
Compress: c.Compress,
Crypt: c.Crypt,
CompressEncode: c.CompressEncode,
CompressDecode: c.CompressDecode,
}
}

View File

@ -1,45 +0,0 @@
package lg
import (
"log"
"os"
"path/filepath"
"runtime"
)
var Log *log.Logger
func InitLogFile(f string, isStdout bool, logPath string) {
var prefix string
if !isStdout {
logFile, err := os.OpenFile(filepath.Join(logPath, f+"_log.txt"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
if err != nil {
log.Fatalln("open file error !", err)
}
if runtime.GOOS == "windows" {
prefix = "\r\n"
}
Log = log.New(logFile, prefix, log.Ldate|log.Ltime)
} else {
Log = log.New(os.Stdout, "", log.Ldate|log.Ltime)
}
}
func Println(v ...interface{}) {
Log.Println(v...)
}
func Fatalln(v ...interface{}) {
Log.SetPrefix("error ")
Log.Fatalln(v...)
Log.SetPrefix("")
}
func Fatalf(format string, v ...interface{}) {
Log.SetPrefix("error ")
Log.Fatalf(format, v...)
Log.SetPrefix("")
}
func Printf(format string, v ...interface{}) {
Log.Printf(format, v...)
}

8
lib/version/version.go Normal file
View File

@ -0,0 +1,8 @@
package version
const VERSION = "0.0.16"
const VERSION_OK = "vrok"
func GetVersion() string {
return VERSION
}

View File

@ -17,8 +17,8 @@ type Service interface {
Close() error
}
//server base struct
type server struct {
//Server BaseServer struct
type BaseServer struct {
id int
bridge *bridge.Bridge
task *file.Tunnel
@ -26,21 +26,30 @@ type server struct {
sync.Mutex
}
func (s *server) FlowAdd(in, out int64) {
func NewBaseServer(bridge *bridge.Bridge, task *file.Tunnel) *BaseServer {
return &BaseServer{
bridge: bridge,
task: task,
errorContent: nil,
Mutex: sync.Mutex{},
}
}
func (s *BaseServer) FlowAdd(in, out int64) {
s.Lock()
defer s.Unlock()
s.task.Flow.ExportFlow += out
s.task.Flow.InletFlow += in
}
func (s *server) FlowAddHost(host *file.Host, in, out int64) {
func (s *BaseServer) FlowAddHost(host *file.Host, in, out int64) {
s.Lock()
defer s.Unlock()
host.Flow.ExportFlow += out
host.Flow.InletFlow += in
}
func (s *server) linkCopy(link *conn.Link, c *conn.Conn, rb []byte, tunnel *conn.Conn, flow *file.Flow) {
func (s *BaseServer) linkCopy(link *conn.Link, c *conn.Conn, rb []byte, tunnel *conn.Conn, flow *file.Flow) {
if rb != nil {
if _, err := tunnel.SendMsg(rb, link); err != nil {
c.Close()
@ -68,16 +77,17 @@ func (s *server) linkCopy(link *conn.Link, c *conn.Conn, rb []byte, tunnel *conn
}
<-link.StatusCh
}
s.task.Client.AddConn()
pool.PutBufPoolCopy(buf)
}
func (s *server) writeConnFail(c net.Conn) {
func (s *BaseServer) writeConnFail(c net.Conn) {
c.Write([]byte(common.ConnectionFailBytes))
c.Write(s.errorContent)
}
//权限认证
func (s *server) auth(r *http.Request, c *conn.Conn, u, p string) error {
func (s *BaseServer) auth(r *http.Request, c *conn.Conn, u, p string) error {
if u != "" && p != "" && !common.CheckAuth(r, u, p) {
c.Write([]byte(common.UnauthorizedBytes))
c.Close()
@ -86,9 +96,23 @@ func (s *server) auth(r *http.Request, c *conn.Conn, u, p string) error {
return nil
}
func (s *server) checkFlow() error {
func (s *BaseServer) checkFlow() error {
if s.task.Client.Flow.FlowLimit > 0 && (s.task.Client.Flow.FlowLimit<<20) < (s.task.Client.Flow.ExportFlow+s.task.Client.Flow.InletFlow) {
return errors.New("Traffic exceeded")
}
return nil
}
//与客户端建立通道
func (s *BaseServer) DealClient(c *conn.Conn, addr string, rb []byte) error {
link := conn.NewLink(s.task.Client.GetId(), common.CONN_TCP, addr, s.task.Client.Cnf.CompressEncode, s.task.Client.Cnf.CompressDecode, s.task.Client.Cnf.Crypt, c, s.task.Flow, nil, s.task.Client.Rate, nil)
if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.Conn.RemoteAddr().String()); err != nil {
c.Close()
return err
} else {
link.Run(true)
s.linkCopy(link, c, rb, tunnel, s.task.Flow)
}
return nil
}

View File

@ -7,17 +7,18 @@ import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/vender/github.com/astaxie/beego"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net/http"
"net/http/httputil"
"os"
"path/filepath"
"strconv"
"sync"
)
type httpServer struct {
server
BaseServer
httpPort int //http端口
httpsPort int //https监听端口
pemPath string
@ -31,7 +32,7 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer {
pemPath := beego.AppConfig.String("pemPath")
keyPath := beego.AppConfig.String("keyPath")
return &httpServer{
server: server{
BaseServer: BaseServer{
task: c,
bridge: bridge,
Mutex: sync.Mutex{},
@ -54,26 +55,30 @@ func (s *httpServer) Start() error {
if s.httpPort > 0 {
http = s.NewServer(s.httpPort)
go func() {
lg.Println("Start http listener, port is", s.httpPort)
logs.Info("Start http listener, port is", s.httpPort)
err := http.ListenAndServe()
if err != nil {
lg.Fatalln(err)
logs.Error(err)
os.Exit(0)
}
}()
}
if s.httpsPort > 0 {
if !common.FileExists(s.pemPath) {
lg.Fatalf("ssl certFile %s is not exist", s.pemPath)
logs.Error("ssl certFile %s is not exist", s.pemPath)
os.Exit(0)
}
if !common.FileExists(s.keyPath) {
lg.Fatalf("ssl keyFile %s exist", s.keyPath)
logs.Error("ssl keyFile %s exist", s.keyPath)
os.Exit(0)
}
https = s.NewServer(s.httpsPort)
go func() {
lg.Println("Start https listener, port is", s.httpsPort)
logs.Info("Start https listener, port is", s.httpsPort)
err := https.ListenAndServeTLS(s.pemPath, s.keyPath)
if err != nil {
lg.Fatalln(err)
logs.Error(err)
os.Exit(0)
}
}()
}
@ -118,9 +123,14 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) {
err error
)
if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil {
lg.Printf("the url %s %s can't be parsed!", r.Host, r.RequestURI)
logs.Notice("the url %s %s can't be parsed!", r.Host, r.RequestURI)
goto end
} else if !host.Client.GetConn() {
logs.Notice("Connections exceed the current client %d limit", host.Client.Id)
c.Close()
return
} else {
logs.Trace("New http(s) connection,clientId %d,host %s,url %s,remote address %s", host.Client.Id, r.Host, r.URL, r.RemoteAddr)
lastHost = host
}
for {
@ -137,22 +147,24 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) {
}
lk = conn.NewLink(host.Client.GetId(), common.CONN_TCP, host.GetRandomTarget(), host.Client.Cnf.CompressEncode, host.Client.Cnf.CompressDecode, host.Client.Cnf.Crypt, c, host.Flow, nil, host.Client.Rate, nil)
if tunnel, err = s.bridge.SendLinkInfo(host.Client.Id, lk, c.Conn.RemoteAddr().String()); err != nil {
lg.Println(err)
logs.Notice(err)
break
}
lk.Run(true)
isConn = false
} else {
r, err = http.ReadRequest(bufio.NewReader(c))
logs.Trace("New http(s) connection,clientId %d,host %s,url %s,remote address %s", host.Client.Id, r.Host, r.URL, r.RemoteAddr)
if err != nil {
break
}
if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil {
lg.Printf("the url %s %s is not found !", r.Host, r.RequestURI)
logs.Notice("the url %s %s can't be parsed!", r.Host, r.RequestURI)
break
} else if host != lastHost {
lastHost = host
isConn = true
host.Client.AddConn()
goto start
}
}
@ -176,6 +188,9 @@ end:
tunnel.SendMsg([]byte(common.IO_EOF), lk)
}
c.Close()
if host != nil {
host.Client.AddConn()
}
}
func (s *httpServer) NewServer(port int) *http.Server {

View File

@ -7,7 +7,7 @@ import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"io"
"net"
"strconv"
@ -48,7 +48,7 @@ const (
)
type Sock5ModeServer struct {
server
BaseServer
listener net.Listener
}
@ -67,7 +67,7 @@ func (s *Sock5ModeServer) handleRequest(c net.Conn) {
_, err := io.ReadFull(c, header)
if err != nil {
lg.Println("illegal request", err)
logs.Warn("illegal request", err)
c.Close()
return
}
@ -165,7 +165,6 @@ func (s *Sock5ModeServer) handleBind(c net.Conn) {
//udp
func (s *Sock5ModeServer) handleUDP(c net.Conn) {
lg.Println("UDP Associate")
/*
+----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
@ -178,7 +177,7 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
// relay udp datagram silently, without any notification to the requesting client
if buf[2] != 0 {
// does not support fragmentation, drop it
lg.Println("does not support fragmentation, drop")
logs.Warn("does not support fragmentation, drop")
dummy := make([]byte, maxUDPPacketSize)
c.Read(dummy)
}
@ -190,13 +189,13 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
func (s *Sock5ModeServer) handleConn(c net.Conn) {
buf := make([]byte, 2)
if _, err := io.ReadFull(c, buf); err != nil {
lg.Println("negotiation err", err)
logs.Warn("negotiation err", err)
c.Close()
return
}
if version := buf[0]; version != 5 {
lg.Println("only support socks5, request from: ", c.RemoteAddr())
logs.Warn("only support socks5, request from: ", c.RemoteAddr())
c.Close()
return
}
@ -204,7 +203,7 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) {
methods := make([]byte, nMethods)
if len, err := c.Read(methods); len != int(nMethods) || err != nil {
lg.Println("wrong method")
logs.Warn("wrong method")
c.Close()
return
}
@ -213,7 +212,7 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) {
c.Write(buf)
if err := s.Auth(c); err != nil {
c.Close()
lg.Println("Validation failed:", err)
logs.Warn("Validation failed:", err)
return
}
} else {
@ -271,9 +270,15 @@ func (s *Sock5ModeServer) Start() error {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
lg.Fatalln("accept error: ", err)
logs.Warn("accept error: ", err)
}
if s.task.Client.GetConn() {
logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, conn.RemoteAddr())
go s.handleConn(conn)
} else {
logs.Warn("Connections exceed the current client %d limit", s.task.Client.Id)
conn.Close()
}
}
return nil
}

View File

@ -6,15 +6,16 @@ import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/vender/github.com/astaxie/beego"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net"
"os"
"path/filepath"
"strings"
)
type TunnelModeServer struct {
server
BaseServer
process process
listener *net.TCPListener
}
@ -41,24 +42,16 @@ func (s *TunnelModeServer) Start() error {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
lg.Println(err)
logs.Info(err)
continue
}
if s.task.Client.GetConn() {
logs.Trace("New tcp connection,client %d,remote address %s", s.task.Client.Id, c.RemoteAddr())
go s.process(conn.NewConn(c), s)
}
return nil
}
//与客户端建立通道
func (s *TunnelModeServer) dealClient(c *conn.Conn, cnf *file.Config, addr string, method string, rb []byte) error {
link := conn.NewLink(s.task.Client.GetId(), common.CONN_TCP, addr, cnf.CompressEncode, cnf.CompressDecode, cnf.Crypt, c, s.task.Flow, nil, s.task.Client.Rate, nil)
if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.Conn.RemoteAddr().String()); err != nil {
c.Close()
return err
} else {
link.Run(true)
s.linkCopy(link, c, rb, tunnel, s.task.Flow)
logs.Info("Connections exceed the current client %d limit", s.task.Client.Id)
c.Close()
}
}
return nil
}
@ -70,17 +63,18 @@ func (s *TunnelModeServer) Close() error {
//web管理方式
type WebServer struct {
server
BaseServer
}
//开始
func (s *WebServer) Start() error {
p, _ := beego.AppConfig.Int("httpport")
if !common.TestTcpPort(p) {
lg.Fatalf("Web management port %d is occupied", p)
logs.Error("Web management port %d is occupied", p)
os.Exit(0)
}
beego.BConfig.WebConfig.Session.SessionOn = true
lg.Println("Web management start, access port is", p)
logs.Info("Web management start, access port is", p)
beego.SetStaticPath("/static", filepath.Join(common.GetRunPath(), "web", "static"))
beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views"))
beego.Run()
@ -102,23 +96,23 @@ type process func(c *conn.Conn, s *TunnelModeServer) error
//tcp隧道模式
func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error {
return s.dealClient(c, s.task.Client.Cnf, s.task.Target, "", nil)
return s.DealClient(c, s.task.Target, nil)
}
//http代理模式
func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error {
method, addr, rb, err, r := c.GetHost()
_, addr, rb, err, r := c.GetHost()
if err != nil {
c.Close()
lg.Println(err)
logs.Info(err)
return err
}
if r.Method == "CONNECT" {
c.Write([]byte("HTTP/1.1 200 Connection Established\r\n"))
rb = nil //reset
rb = nil
}
if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil {
return err
}
return s.dealClient(c, s.task.Client.Cnf, addr, method, rb)
return s.DealClient(c, addr, rb)
}

View File

@ -6,12 +6,13 @@ import (
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net"
"strings"
)
type UdpModeServer struct {
server
BaseServer
listener *net.UDPConn
udpMap map[string]*conn.Conn
}
@ -40,6 +41,7 @@ func (s *UdpModeServer) Start() error {
}
continue
}
logs.Trace("New ydo connection,client %d,remote address %s", s.task.Client.Id, addr)
go s.process(addr, buf[:n])
}
return nil

View File

@ -5,10 +5,12 @@ import (
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/server/proxy"
"github.com/cnlh/nps/server/tool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"os"
"time"
)
var (
@ -31,7 +33,6 @@ func InitFromCsv() {
//Initialize services in server-side files
for _, v := range file.GetCsvDb().Tasks {
if v.Status {
lg.Println("task start info: mode", v.Mode, "port", v.Port)
AddTask(v)
}
}
@ -45,6 +46,19 @@ func DealBridgeTask() {
case id := <-Bridge.CloseClient:
DelTunnelAndHostByClientId(id)
file.GetCsvDb().DelClient(id)
case s := <-Bridge.SecretChan:
logs.Trace("New secret connection, addr", s.Conn.Conn.RemoteAddr())
if t := file.GetCsvDb().GetSecretTask(s.Password); t != nil {
if !t.Client.GetConn() {
logs.Info("Connections exceed the current client %d limit", t.Client.Id)
s.Conn.Close()
} else {
go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Target, nil)
}
} else {
logs.Trace("This key %s cannot be processed", s.Password)
s.Conn.Close()
}
}
}
}
@ -53,18 +67,19 @@ func DealBridgeTask() {
func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ipLimit")), RunList)
if err := Bridge.StartTunnel(); err != nil {
lg.Fatalln("服务端开启失败", err)
logs.Error("服务端开启失败", err)
os.Exit(0)
} else {
lg.Printf("Server startup, the bridge type is %s, the bridge port is %d", bridgeType, bridgePort)
logs.Info("Server startup, the bridge type is %s, the bridge port is %d", bridgeType, bridgePort)
}
go DealBridgeTask()
if svr := NewMode(Bridge, cnf); svr != nil {
if err := svr.Start(); err != nil {
lg.Fatalln(err)
logs.Error(err)
}
RunList[cnf.Id] = svr
} else {
lg.Fatalln("启动模式%s不正确", cnf.Mode)
logs.Error("Incorrect startup mode %s", cnf.Mode)
}
}
@ -103,13 +118,13 @@ func StopServer(id int) error {
if err := svr.Close(); err != nil {
return err
}
}
if t, err := file.GetCsvDb().GetTask(id); err != nil {
return err
} else {
t.Status = false
file.GetCsvDb().UpdateTask(t)
}
}
delete(RunList, id)
return nil
}
@ -118,15 +133,24 @@ func StopServer(id int) error {
//add task
func AddTask(t *file.Tunnel) error {
if t.Mode == "secretServer" {
logs.Info("secret task %s start ", t.Remark)
RunList[t.Id] = nil
return nil
}
if b := tool.TestServerPort(t.Port, t.Mode); !b && t.Mode != "httpHostServer" {
lg.Printf("taskId %d start error port %d Open Failed", t.Id, t.Port)
logs.Error("taskId %d start error port %d open failed", t.Id, t.Port)
return errors.New("the port open error")
}
if minute, err := beego.AppConfig.Int("flowStoreInterval"); err == nil && minute > 0 {
go flowSession(time.Minute * time.Duration(minute))
}
if svr := NewMode(Bridge, t); svr != nil {
logs.Info("tunnel task %s start mode%s port %d", t.Remark, t.Mode, t.Port)
RunList[t.Id] = svr
go func() {
if err := svr.Start(); err != nil {
lg.Println("clientId %d taskId %d start error %s", t.Client.Id, t.Id, err)
logs.Error("clientId %d taskId %d start error %s", t.Client.Id, t.Id, err)
delete(RunList, t.Id)
return
}
@ -272,5 +296,21 @@ func GetDashboardData() map[string]int {
data["udpServerCount"] += 1
}
}
tcpCount := 0
for _, v := range file.GetCsvDb().Clients {
tcpCount += v.NowConn
}
data["tcpCount"] = tcpCount
return data
}
func flowSession(m time.Duration) {
ticker := time.NewTicker(m)
for {
select {
case <-ticker.C:
file.GetCsvDb().StoreHostToCsv()
file.GetCsvDb().StoreTasksToCsv()
}
}
}

View File

@ -13,6 +13,9 @@ func init() {
}
func TestServerPort(p int, m string) (b bool) {
if p > 65535 || p <= 0 {
return false
}
if len(ports) != 0 {
if !common.InIntArr(ports, p) {
return false

View File

@ -42,6 +42,7 @@ func (s *ClientController) Add() {
Crypt: s.GetBoolNoErr("crypt"),
},
RateLimit: s.GetIntNoErr("rate_limit"),
MaxConn: s.GetIntNoErr("max_conn"),
Flow: &file.Flow{
ExportFlow: 0,
InletFlow: 0,
@ -94,6 +95,7 @@ func (s *ClientController) Edit() {
c.Cnf.Crypt = s.GetBoolNoErr("crypt")
c.Flow.FlowLimit = int64(s.GetIntNoErr("flow_limit"))
c.RateLimit = s.GetIntNoErr("rate_limit")
c.MaxConn = s.GetIntNoErr("max_conn")
if c.Rate != nil {
c.Rate.Stop()
}

View File

@ -44,6 +44,12 @@ func (s *IndexController) Http() {
s.display("index/list")
}
func (s *IndexController) Secret() {
s.SetInfo("私密代理管理")
s.SetType("secretServer")
s.display("index/list")
}
func (s *IndexController) Host() {
s.SetInfo("host模式管理")
s.SetType("hostServer")
@ -80,6 +86,7 @@ func (s *IndexController) Add() {
Id: file.GetCsvDb().GetTaskId(),
Status: true,
Remark: s.GetString("remark"),
Password: s.GetString("password"),
Flow: &file.Flow{},
}
if !tool.TestServerPort(t.Port, t.Mode) {
@ -126,6 +133,7 @@ func (s *IndexController) Edit() {
t.Port = s.GetIntNoErr("port")
t.Mode = s.GetString("type")
t.Target = s.GetString("target")
t.Password = s.GetString("password")
t.Id = id
t.Remark = s.GetString("remark")
if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {

View File

@ -16,6 +16,10 @@
<label class="control-label">(KB)</label>
<input class="form-control" type="text" name="rate_limit" placeholder="为空不限制">
</div>
<div class="form-group" id="max_conn">
<label class="control-label"></label>
<input class="form-control" type="text" name="max_conn" placeholder="为空不限制">
</div>
<div class="form-group" id="u">
<label class="control-label">(socks5,web穿)</label>
<input class="form-control" type="text" name="u" placeholder="不填则无需验证">

View File

@ -19,6 +19,11 @@
<input class="form-control" value="{{.c.RateLimit}}" type="text" name="rate_limit"
placeholder="为空不限制">
</div>
<div class="form-group" id="max_conn">
<label class="control-label"></label>
<input class="form-control" value="{{.c.MaxConn}}" type="text" name="max_conn"
placeholder="为空不限制">
</div>
<div class="form-group" id="u">
<label class="control-label">(socks5,web穿)</label>
<input class="form-control" value="{{.c.Cnf.U}}" type="text" name="u"

View File

@ -11,6 +11,7 @@
<option {{if eq "udpServer" .type}}selected{{end}} value="udpServer">udp</option>
<option {{if eq "socks5Server" .type}}selected{{end}} value="socks5Server">socks5</option>
<option {{if eq "httpProxyServer" .type}}selected{{end}} value="httpProxyServer">http
<option {{if eq "secretServer" .type}}selected{{end}} value="secretServer">
</select>
</div>
<div class="form-group">
@ -30,6 +31,11 @@
<input value="{{.client_id}}" class="form-control" type="text" name="client_id"
placeholder="客户端id">
</div>
<div class="form-group" id="password">
<label class="control-label"></label>
<input class="form-control" type="text" name="password"
placeholder="私密模式唯一密钥">
</div>
</form>
</div>
<div class="tile-footer">
@ -48,6 +54,7 @@
arr["udpServer"] = ["type", "port", "target", "compress", "udp隧道模式提供一条udp隧道适用于dns、内网dns访问等添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后访问公网服务器的设定端口则相当于访问内网目标地址的udp目标端口"]
arr["socks5Server"] = ["type", "port", "compress", "u", "p", "socks5代理模式内网socks5代理配合proxifer可如同使用vpn一样访问内网设备或资源添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后在外网环境下本机配置socks5代理即访问内网设备或者资源 "]
arr["httpProxyServer"] = ["type", "port", "compress", "u", "p", " http代理模式内网http代理可访问内网网站添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后在外网环境下本机配置http代理即访问内网站点"]
arr["secretServer"] = ["type", "target", "compress", "password", "u", "p", " http代理模式内网http代理可访问内网网站添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后在外网环境下本机配置http代理即访问内网站点"]
arrClientHide = ["compress", "u", "p", "crypt", "mux"]
function resetForm() {

View File

@ -13,6 +13,7 @@
<option {{if eq "socks5Server" .t.Mode}}selected{{end}} value="socks5Server">socks5
</option>
<option {{if eq "httpProxyServer" .t.Mode}}selected{{end}} value="httpProxyServer">http
<option {{if eq "secretServer" .t.Mode}}selected{{end}} value="secretServer">
</select>
</div>
<div class="form-group">
@ -34,6 +35,11 @@
<input class="form-control" value="{{.t.Client.Id}}" type="text" name="client_id"
placeholder="客户端id">
</div>
<div class="form-group" id="password">
<label class="control-label"></label>
<input class="form-control" value="{{.t.Password}}" type="text" name="password"
placeholder="私密模式唯一密钥">
</div>
</form>
</div>
<div class="tile-footer">
@ -52,6 +58,7 @@
arr["udpServer"] = ["type", "port", "target", "compress"]
arr["socks5Server"] = ["type", "port", "compress", "u", "p"]
arr["httpProxyServer"] = ["type", "port", "compress", "u", "p"]
arr["secretServer"] = ["type", "target", "compress", "u", "p","password"]
arrClientHide = ["compress", "u", "p", "crypt", "mux"]
function resetForm() {

View File

@ -10,8 +10,8 @@
<div class="col-md-3">
<div class="widget-small danger coloured-icon"><i class="icon fa fa-home fa-3x"></i>
<div class="info">
<h4></h4>
<p><b>{{.data.hostCount}}</b></p>
<h4>TCP</h4>
<p><b>{{.data.tcpCount}}</b></p>
</div>
</div>
</div>

View File

@ -51,9 +51,11 @@
<li><a class="app-menu__item {{if eq "udp" .menu}}active{{end}}" href="/index/udp"><i
class="app-menu__icon fa fa-laptop"></i><span class="app-menu__label">udp</span></a></li>
<li><a class="app-menu__item {{if eq "socks5" .menu}}active{{end}}" href="/index/socks5"><i
class="app-menu__icon fa fa-lightbulb-o"></i><span class="app-menu__label">socks5</span></a></li>
class="app-menu__icon fa fa-lightbulb-o"></i><span class="app-menu__label">socks5</span></a></li>
<li><a class="app-menu__item {{if eq "http" .menu}}active{{end}}" href="/index/http"><i
class="app-menu__icon fa fa-magic"></i><span class="app-menu__label">http</span></a></li>
class="app-menu__icon fa fa-magic"></i><span class="app-menu__label">http</span></a></li>
<li><a class="app-menu__item {{if eq "secret" .menu}}active{{end}}" href="/index/secret"><i
class="app-menu__icon fa fa-dashcube"></i><span class="app-menu__label"></span></a></li>
<li><a class="app-menu__item {{if eq "help" .menu}}active{{end}}" href="/index/help"><i
class="app-menu__icon fa fa-question-circle"></i><span class="app-menu__label">使</span></a></li>
</ul>