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

View File

@ -3,9 +3,10 @@ package client
import ( import (
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/lib/pool" "github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net" "net"
"os"
"sync" "sync"
"time" "time"
) )
@ -40,11 +41,11 @@ func (s *TRPClient) Start() {
retry: retry:
c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl) c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl)
if err != nil { 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) time.Sleep(time.Second * 5)
goto retry goto retry
} }
lg.Printf("Successful connection with server %s", s.svrAddr) logs.Info("Successful connection with server %s", s.svrAddr)
s.processor(c) s.processor(c)
} }
@ -65,12 +66,13 @@ func (s *TRPClient) processor(c *conn.Conn) {
for { for {
flags, err := c.ReadFlag() flags, err := c.ReadFlag()
if err != nil { 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 break
} }
switch flags { switch flags {
case common.VERIFY_EER: 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: case common.NEW_CONN:
if link, err := c.GetLinkInfo(); err != nil { if link, err := c.GetLinkInfo(); err != nil {
break break
@ -83,12 +85,13 @@ func (s *TRPClient) processor(c *conn.Conn) {
link.Run(false) link.Run(false)
} }
case common.RES_CLOSE: 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: case common.RES_MSG:
lg.Println("Server-side return error") logs.Error("Server-side return error")
break break
default: default:
lg.Println("The error could not be resolved") logs.Warn("The error could not be resolved")
break break
} }
} }
@ -103,7 +106,7 @@ func (s *TRPClient) linkProcess(link *conn.Link, c *conn.Conn) {
if err != nil { if err != nil {
c.WriteFail(link.Id) c.WriteFail(link.Id)
lg.Println("connect to ", link.Host, "error:", err) logs.Warn("connect to ", link.Host, "error:", err)
return return
} }
c.WriteSuccess(link.Id) c.WriteSuccess(link.Id)
@ -134,7 +137,7 @@ func (s *TRPClient) getMsgStatus() {
var err error var err error
s.msgTunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_SEND_STATUS, s.proxyUrl) s.msgTunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_SEND_STATUS, s.proxyUrl)
if err != nil { if err != nil {
lg.Println("connect to ", s.svrAddr, "error:", err) logs.Error("connect to ", s.svrAddr, "error:", err)
return return
} }
go func() { go func() {
@ -160,7 +163,7 @@ func (s *TRPClient) dealChan() {
var err error var err error
s.tunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl) s.tunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl)
if err != nil { if err != nil {
lg.Println("connect to ", s.svrAddr, "error:", err) logs.Error("connect to ", s.svrAddr, "error:", err)
return return
} }
go func() { go func() {

View File

@ -1,11 +1,14 @@
package client package client
import ( import (
"encoding/binary"
"errors" "errors"
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config" "github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn" "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/github.com/xtaci/kcp"
"github.com/cnlh/nps/vender/golang.org/x/net/proxy" "github.com/cnlh/nps/vender/golang.org/x/net/proxy"
"io/ioutil" "io/ioutil"
@ -39,7 +42,7 @@ func GetTaskStatus(path string) {
if l, err := c.GetLen(); err != nil { if l, err := c.GetLen(); err != nil {
log.Fatalln(err) log.Fatalln(err)
} else if b, err := c.ReadLen(l); err != nil { } else if b, err := c.ReadLen(l); err != nil {
lg.Fatalln(err) log.Fatalln(err)
} else { } else {
arr := strings.Split(string(b), common.CONN_DATA_SEQ) arr := strings.Split(string(b), common.CONN_DATA_SEQ)
for _, v := range cnf.Hosts { 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) { func StartFromFile(path string) {
first := true first := true
cnf, err := config.NewConfig(path) cnf, err := config.NewConfig(path)
if err != nil { if err != nil || cnf.CommonConfig == nil {
lg.Fatalln(err) 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: re:
if first || cnf.CommonConfig.AutoReconnection { if first || cnf.CommonConfig.AutoReconnection {
if !first { if !first {
lg.Println("Reconnecting...") logs.Info("Reconnecting...")
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
} }
} else { } else {
@ -90,48 +95,51 @@ re:
first = false first = false
c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl) c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl)
if err != nil { if err != nil {
lg.Println(err) logs.Error(err)
goto re goto re
} }
if _, err := c.SendConfigInfo(cnf.CommonConfig.Cnf); err != nil { if _, err := c.SendConfigInfo(cnf.CommonConfig); err != nil {
lg.Println(err) logs.Error(err)
goto re goto re
} }
var b []byte var b []byte
if b, err = c.ReadLen(16); err != nil { if b, err = c.ReadLen(16); err != nil {
lg.Println(err) logs.Error(err)
goto re goto re
} else { } else {
ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(string(b)), 0600) ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(string(b)), 0600)
} }
if !c.GetAddStatus() { if !c.GetAddStatus() {
lg.Println(errAdd) logs.Error(errAdd)
goto re goto re
} }
for _, v := range cnf.Hosts { for _, v := range cnf.Hosts {
if _, err := c.SendHostInfo(v); err != nil { if _, err := c.SendHostInfo(v); err != nil {
lg.Println(err) logs.Error(err)
goto re goto re
} }
if !c.GetAddStatus() { if !c.GetAddStatus() {
lg.Println(errAdd, v.Host) logs.Error(errAdd, v.Host)
goto re goto re
} }
} }
for _, v := range cnf.Tasks { for _, v := range cnf.Tasks {
if _, err := c.SendTaskInfo(v); err != nil { if _, err := c.SendTaskInfo(v); err != nil {
lg.Println(err) logs.Error(err)
goto re goto re
} }
if !c.GetAddStatus() { if !c.GetAddStatus() {
lg.Println(errAdd, v.Ports) logs.Error(errAdd, v.Ports)
goto re goto re
} }
} }
for _, v := range cnf.LocalServer {
go StartLocalServer(v, cnf.CommonConfig)
}
c.Close() c.Close()
NewRPClient(cnf.CommonConfig.Server, string(b), cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl).Start() NewRPClient(cnf.CommonConfig.Server, string(b), cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl).Start()
CloseLocalServer()
goto re goto re
} }
@ -163,16 +171,30 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
return nil, err return nil, err
} }
c := conn.NewConn(connection) 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 { 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 { if s, err := c.ReadFlag(); err != nil {
lg.Println(err) logs.Error(err)
os.Exit(0)
} else if s == common.VERIFY_EER { } 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 { if _, err := c.Write([]byte(connType)); err != nil {
lg.Println(err) logs.Error(err)
os.Exit(0)
} }
c.SetAlive(tp) 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/client"
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/daemon" "github.com/cnlh/nps/lib/daemon"
"github.com/cnlh/nps/lib/lg" "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"os" "os"
"strings" "strings"
"time" "time"
) )
const VERSION = "v0.0.15"
var ( var (
serverAddr = flag.String("server", "", "Server addr (ip:port)") serverAddr = flag.String("server", "", "Server addr (ip:port)")
configPath = flag.String("config", "npc.conf", "Configuration file path") configPath = flag.String("config", "npc.conf", "Configuration file path")
@ -20,6 +18,7 @@ var (
logType = flag.String("log", "stdout", "Log output modestdout|file") logType = flag.String("log", "stdout", "Log output modestdout|file")
connType = flag.String("type", "tcp", "Connection type with the serverkcp|tcp") 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)") 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") registerTime = flag.Int("time", 2, "register time long /h")
) )
@ -37,14 +36,14 @@ func main() {
} }
daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath()) daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath())
if *logType == "stdout" { if *logType == "stdout" {
lg.InitLogFile("npc", true, common.GetLogPath()) logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
} else { } else {
lg.InitLogFile("npc", false, common.GetLogPath()) logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"npc_log.log"}`)
} }
if *verifyKey != "" && *serverAddr != "" { if *verifyKey != "" && *serverAddr != "" {
for { for {
client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl).Start() 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) time.Sleep(time.Second * 5)
} }
} else { } else {

View File

@ -6,19 +6,18 @@ import (
"github.com/cnlh/nps/lib/daemon" "github.com/cnlh/nps/lib/daemon"
"github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/install" "github.com/cnlh/nps/lib/install"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/server" "github.com/cnlh/nps/server"
"github.com/cnlh/nps/server/test" "github.com/cnlh/nps/server/test"
"github.com/cnlh/nps/vender/github.com/astaxie/beego" "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" _ "github.com/cnlh/nps/web/routers"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
) )
const VERSION = "v0.0.15"
var ( var (
level string
logType = flag.String("log", "stdout", "Log output modestdout|file") logType = flag.String("log", "stdout", "Log output modestdout|file")
) )
@ -37,17 +36,22 @@ func main() {
return return
} }
} }
if level = beego.AppConfig.String("logLevel"); level == "" {
level = "7"
}
logs.Reset()
if *logType == "stdout" { if *logType == "stdout" {
lg.InitLogFile("nps", true, common.GetLogPath()) logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
} else { } else {
lg.InitLogFile("nps", false, common.GetLogPath()) logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"nps_log.log"}`)
} }
task := &file.Tunnel{ task := &file.Tunnel{
Mode: "webServer", Mode: "webServer",
} }
bridgePort, err := beego.AppConfig.Int("bridgePort") bridgePort, err := beego.AppConfig.Int("bridgePort")
if err != nil { 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")) beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "app.conf"))
server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridgeType")) 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. # 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 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 vkey=123
auto_reconnection=true auto_reconnection=true
crypt=true crypt=true
[web1] [web1]
host=a.o.com host=a.o.com
host_change=www.proxy.com host_change=www.proxy.com
target=127.0.0.1:8082 target=127.0.0.1:8080
location=/ location=/
[web2] [web2]
host=a.proxy.com host=127.0.0.1
target=127.0.0.1:8080,127.0.0.1:8082
host_change=www.proxy.com host_change=www.proxy.com
header_set_proxy=nps target=127.0.0.1:8080
location=/cdn
[tcp] [ssh_1118]
mode=tcpServer mode=secretServer
target=8001-8005,8082 password=1111
port=9001-9005,9006 target=123.206.77.88:22
[socks5] [secret_ssh]
mode=socks5Server password=1111
port=9007 port=1000
[http]
mode=httpProxyServer
port=9008
[udp]
mode=udpServer
port=53
target=114.114.114.114:53

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

View File

@ -5,6 +5,8 @@ import (
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"github.com/cnlh/nps/lib/crypt" "github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/pool"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
@ -246,3 +248,10 @@ func GetIpByAddr(addr string) string {
arr := strings.Split(addr, ":") arr := strings.Split(addr, ":")
return arr[0] 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 package config
import ( import (
"errors"
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/file"
"regexp" "regexp"
@ -14,6 +15,11 @@ type CommonConfig struct {
AutoReconnection bool AutoReconnection bool
Cnf *file.Config Cnf *file.Config
ProxyUrl string ProxyUrl string
Client *file.Client
}
type LocalServer struct {
Port int
Password string
} }
type Config struct { type Config struct {
content string content string
@ -21,6 +27,7 @@ type Config struct {
CommonConfig *CommonConfig CommonConfig *CommonConfig
Hosts []*file.Host Hosts []*file.Host
Tasks []*file.Tunnel Tasks []*file.Tunnel
LocalServer []*LocalServer
} }
func NewConfig(path string) (c *Config, err error) { func NewConfig(path string) (c *Config, err error) {
@ -44,6 +51,11 @@ func NewConfig(path string) (c *Config, err error) {
nextIndex = len(c.content) nextIndex = len(c.content)
} }
nowContent = c.content[nowIndex:nextIndex] 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] { switch c.title[i] {
case "[common]": case "[common]":
c.CommonConfig = dealCommon(nowContent) c.CommonConfig = dealCommon(nowContent)
@ -68,9 +80,11 @@ func getTitleContent(s string) string {
re, _ := regexp.Compile(`[\[\]]`) re, _ := regexp.Compile(`[\[\]]`)
return re.ReplaceAllString(s, "") return re.ReplaceAllString(s, "")
} }
func dealCommon(s string) *CommonConfig { func dealCommon(s string) *CommonConfig {
c := &CommonConfig{} c := &CommonConfig{}
c.Cnf = new(file.Config) c.Cnf = new(file.Config)
c.Client = file.NewClient("", true, true)
for _, v := range strings.Split(s, "\n") { for _, v := range strings.Split(s, "\n") {
item := strings.Split(v, "=") item := strings.Split(v, "=")
if len(item) == 0 { if len(item) == 0 {
@ -97,10 +111,19 @@ func dealCommon(s string) *CommonConfig {
c.Cnf.Crypt = common.GetBoolByStr(item[1]) c.Cnf.Crypt = common.GetBoolByStr(item[1])
case "proxy_socks5_url": case "proxy_socks5_url":
c.ProxyUrl = item[1] 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 return c
} }
func dealHost(s string) *file.Host { func dealHost(s string) *file.Host {
h := &file.Host{} h := &file.Host{}
var headerChange string var headerChange string
@ -146,12 +169,35 @@ func dealTunnel(s string) *file.Tunnel {
t.Mode = item[1] t.Mode = item[1]
case "target": case "target":
t.Target = item[1] t.Target = item[1]
case "targetAddr":
t.TargetAddr = item[1]
case "password":
t.Password = item[1]
} }
} }
return t 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) { func getAllTitle(content string) (arr []string, err error) {
var re *regexp.Regexp var re *regexp.Regexp
re, err = regexp.Compile(`\[.+?\]`) re, err = regexp.Compile(`\[.+?\]`)
@ -159,5 +205,13 @@ func getAllTitle(content string) (arr []string, err error) {
return return
} }
arr = re.FindAllString(content, -1) 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 return
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -33,6 +33,8 @@ type Client struct {
Rate *rate.Rate //速度控制 Rate *rate.Rate //速度控制
NoStore bool NoStore bool
NoDisplay bool NoDisplay bool
MaxConn int //客户端最大连接数
NowConn int //当前连接数
id int id int
sync.RWMutex sync.RWMutex
} }
@ -62,6 +64,26 @@ func (s *Client) GetId() int {
return s.id 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 { type Tunnel struct {
Id int //Id Id int //Id
Port int //服务端监听端口 Port int //服务端监听端口
@ -72,7 +94,9 @@ type Tunnel struct {
Client *Client //所属客户端id Client *Client //所属客户端id
Ports string //客户端与服务端传递 Ports string //客户端与服务端传递
Flow *Flow Flow *Flow
Password string //私密模式密码,唯一
Remark string //备注 Remark string //备注
TargetAddr string
NoStore bool NoStore bool
} }
@ -114,15 +138,3 @@ func (s *Host) GetRandomTarget() string {
} }
return s.TargetArr[s.NowIndex] 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 Close() error
} }
//server base struct //Server BaseServer struct
type server struct { type BaseServer struct {
id int id int
bridge *bridge.Bridge bridge *bridge.Bridge
task *file.Tunnel task *file.Tunnel
@ -26,21 +26,30 @@ type server struct {
sync.Mutex 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() s.Lock()
defer s.Unlock() defer s.Unlock()
s.task.Flow.ExportFlow += out s.task.Flow.ExportFlow += out
s.task.Flow.InletFlow += in 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() s.Lock()
defer s.Unlock() defer s.Unlock()
host.Flow.ExportFlow += out host.Flow.ExportFlow += out
host.Flow.InletFlow += in 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 rb != nil {
if _, err := tunnel.SendMsg(rb, link); err != nil { if _, err := tunnel.SendMsg(rb, link); err != nil {
c.Close() c.Close()
@ -68,16 +77,17 @@ func (s *server) linkCopy(link *conn.Link, c *conn.Conn, rb []byte, tunnel *conn
} }
<-link.StatusCh <-link.StatusCh
} }
s.task.Client.AddConn()
pool.PutBufPoolCopy(buf) pool.PutBufPoolCopy(buf)
} }
func (s *server) writeConnFail(c net.Conn) { func (s *BaseServer) writeConnFail(c net.Conn) {
c.Write([]byte(common.ConnectionFailBytes)) c.Write([]byte(common.ConnectionFailBytes))
c.Write(s.errorContent) 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) { if u != "" && p != "" && !common.CheckAuth(r, u, p) {
c.Write([]byte(common.UnauthorizedBytes)) c.Write([]byte(common.UnauthorizedBytes))
c.Close() c.Close()
@ -86,9 +96,23 @@ func (s *server) auth(r *http.Request, c *conn.Conn, u, p string) error {
return nil 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) { 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 errors.New("Traffic exceeded")
} }
return nil 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/common"
"github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file" "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"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"sync" "sync"
) )
type httpServer struct { type httpServer struct {
server BaseServer
httpPort int //http端口 httpPort int //http端口
httpsPort int //https监听端口 httpsPort int //https监听端口
pemPath string pemPath string
@ -31,7 +32,7 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer {
pemPath := beego.AppConfig.String("pemPath") pemPath := beego.AppConfig.String("pemPath")
keyPath := beego.AppConfig.String("keyPath") keyPath := beego.AppConfig.String("keyPath")
return &httpServer{ return &httpServer{
server: server{ BaseServer: BaseServer{
task: c, task: c,
bridge: bridge, bridge: bridge,
Mutex: sync.Mutex{}, Mutex: sync.Mutex{},
@ -54,26 +55,30 @@ func (s *httpServer) Start() error {
if s.httpPort > 0 { if s.httpPort > 0 {
http = s.NewServer(s.httpPort) http = s.NewServer(s.httpPort)
go func() { go func() {
lg.Println("Start http listener, port is", s.httpPort) logs.Info("Start http listener, port is", s.httpPort)
err := http.ListenAndServe() err := http.ListenAndServe()
if err != nil { if err != nil {
lg.Fatalln(err) logs.Error(err)
os.Exit(0)
} }
}() }()
} }
if s.httpsPort > 0 { if s.httpsPort > 0 {
if !common.FileExists(s.pemPath) { 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) { 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) https = s.NewServer(s.httpsPort)
go func() { 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) err := https.ListenAndServeTLS(s.pemPath, s.keyPath)
if err != nil { 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 err error
) )
if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil { 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 goto end
} else if !host.Client.GetConn() {
logs.Notice("Connections exceed the current client %d limit", host.Client.Id)
c.Close()
return
} else { } 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 lastHost = host
} }
for { 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) 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 { if tunnel, err = s.bridge.SendLinkInfo(host.Client.Id, lk, c.Conn.RemoteAddr().String()); err != nil {
lg.Println(err) logs.Notice(err)
break break
} }
lk.Run(true) lk.Run(true)
isConn = false isConn = false
} else { } else {
r, err = http.ReadRequest(bufio.NewReader(c)) 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 { if err != nil {
break break
} }
if host, err = file.GetCsvDb().GetInfoByHost(r.Host, r); err != nil { 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 break
} else if host != lastHost { } else if host != lastHost {
lastHost = host lastHost = host
isConn = true isConn = true
host.Client.AddConn()
goto start goto start
} }
} }
@ -176,6 +188,9 @@ end:
tunnel.SendMsg([]byte(common.IO_EOF), lk) tunnel.SendMsg([]byte(common.IO_EOF), lk)
} }
c.Close() c.Close()
if host != nil {
host.Client.AddConn()
}
} }
func (s *httpServer) NewServer(port int) *http.Server { 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/common"
"github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/lg" "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"io" "io"
"net" "net"
"strconv" "strconv"
@ -48,7 +48,7 @@ const (
) )
type Sock5ModeServer struct { type Sock5ModeServer struct {
server BaseServer
listener net.Listener listener net.Listener
} }
@ -67,7 +67,7 @@ func (s *Sock5ModeServer) handleRequest(c net.Conn) {
_, err := io.ReadFull(c, header) _, err := io.ReadFull(c, header)
if err != nil { if err != nil {
lg.Println("illegal request", err) logs.Warn("illegal request", err)
c.Close() c.Close()
return return
} }
@ -165,7 +165,6 @@ func (s *Sock5ModeServer) handleBind(c net.Conn) {
//udp //udp
func (s *Sock5ModeServer) handleUDP(c net.Conn) { func (s *Sock5ModeServer) handleUDP(c net.Conn) {
lg.Println("UDP Associate")
/* /*
+----+------+------+----------+----------+----------+ +----+------+------+----------+----------+----------+
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | |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 // relay udp datagram silently, without any notification to the requesting client
if buf[2] != 0 { if buf[2] != 0 {
// does not support fragmentation, drop it // does not support fragmentation, drop it
lg.Println("does not support fragmentation, drop") logs.Warn("does not support fragmentation, drop")
dummy := make([]byte, maxUDPPacketSize) dummy := make([]byte, maxUDPPacketSize)
c.Read(dummy) c.Read(dummy)
} }
@ -190,13 +189,13 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
func (s *Sock5ModeServer) handleConn(c net.Conn) { func (s *Sock5ModeServer) handleConn(c net.Conn) {
buf := make([]byte, 2) buf := make([]byte, 2)
if _, err := io.ReadFull(c, buf); err != nil { if _, err := io.ReadFull(c, buf); err != nil {
lg.Println("negotiation err", err) logs.Warn("negotiation err", err)
c.Close() c.Close()
return return
} }
if version := buf[0]; version != 5 { 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() c.Close()
return return
} }
@ -204,7 +203,7 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) {
methods := make([]byte, nMethods) methods := make([]byte, nMethods)
if len, err := c.Read(methods); len != int(nMethods) || err != nil { if len, err := c.Read(methods); len != int(nMethods) || err != nil {
lg.Println("wrong method") logs.Warn("wrong method")
c.Close() c.Close()
return return
} }
@ -213,7 +212,7 @@ func (s *Sock5ModeServer) handleConn(c net.Conn) {
c.Write(buf) c.Write(buf)
if err := s.Auth(c); err != nil { if err := s.Auth(c); err != nil {
c.Close() c.Close()
lg.Println("Validation failed:", err) logs.Warn("Validation failed:", err)
return return
} }
} else { } else {
@ -271,9 +270,15 @@ func (s *Sock5ModeServer) Start() error {
if strings.Contains(err.Error(), "use of closed network connection") { if strings.Contains(err.Error(), "use of closed network connection") {
break 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) go s.handleConn(conn)
} else {
logs.Warn("Connections exceed the current client %d limit", s.task.Client.Id)
conn.Close()
}
} }
return nil return nil
} }

View File

@ -6,15 +6,16 @@ import (
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file" "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"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net" "net"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
) )
type TunnelModeServer struct { type TunnelModeServer struct {
server BaseServer
process process process process
listener *net.TCPListener listener *net.TCPListener
} }
@ -41,24 +42,16 @@ func (s *TunnelModeServer) Start() error {
if strings.Contains(err.Error(), "use of closed network connection") { if strings.Contains(err.Error(), "use of closed network connection") {
break break
} }
lg.Println(err) logs.Info(err)
continue 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) 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 { } else {
link.Run(true) logs.Info("Connections exceed the current client %d limit", s.task.Client.Id)
s.linkCopy(link, c, rb, tunnel, s.task.Flow) c.Close()
}
} }
return nil return nil
} }
@ -70,17 +63,18 @@ func (s *TunnelModeServer) Close() error {
//web管理方式 //web管理方式
type WebServer struct { type WebServer struct {
server BaseServer
} }
//开始 //开始
func (s *WebServer) Start() error { func (s *WebServer) Start() error {
p, _ := beego.AppConfig.Int("httpport") p, _ := beego.AppConfig.Int("httpport")
if !common.TestTcpPort(p) { 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 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.SetStaticPath("/static", filepath.Join(common.GetRunPath(), "web", "static"))
beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views")) beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views"))
beego.Run() beego.Run()
@ -102,23 +96,23 @@ type process func(c *conn.Conn, s *TunnelModeServer) error
//tcp隧道模式 //tcp隧道模式
func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error { 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代理模式 //http代理模式
func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error { func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error {
method, addr, rb, err, r := c.GetHost() _, addr, rb, err, r := c.GetHost()
if err != nil { if err != nil {
c.Close() c.Close()
lg.Println(err) logs.Info(err)
return err return err
} }
if r.Method == "CONNECT" { if r.Method == "CONNECT" {
c.Write([]byte("HTTP/1.1 200 Connection Established\r\n")) 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 { if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil {
return err 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/conn"
"github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/pool" "github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net" "net"
"strings" "strings"
) )
type UdpModeServer struct { type UdpModeServer struct {
server BaseServer
listener *net.UDPConn listener *net.UDPConn
udpMap map[string]*conn.Conn udpMap map[string]*conn.Conn
} }
@ -40,6 +41,7 @@ func (s *UdpModeServer) Start() error {
} }
continue continue
} }
logs.Trace("New ydo connection,client %d,remote address %s", s.task.Client.Id, addr)
go s.process(addr, buf[:n]) go s.process(addr, buf[:n])
} }
return nil return nil

View File

@ -5,10 +5,12 @@ import (
"github.com/cnlh/nps/bridge" "github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/lg"
"github.com/cnlh/nps/server/proxy" "github.com/cnlh/nps/server/proxy"
"github.com/cnlh/nps/server/tool" "github.com/cnlh/nps/server/tool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego" "github.com/cnlh/nps/vender/github.com/astaxie/beego"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"os"
"time"
) )
var ( var (
@ -31,7 +33,6 @@ func InitFromCsv() {
//Initialize services in server-side files //Initialize services in server-side files
for _, v := range file.GetCsvDb().Tasks { for _, v := range file.GetCsvDb().Tasks {
if v.Status { if v.Status {
lg.Println("task start info: mode", v.Mode, "port", v.Port)
AddTask(v) AddTask(v)
} }
} }
@ -45,6 +46,19 @@ func DealBridgeTask() {
case id := <-Bridge.CloseClient: case id := <-Bridge.CloseClient:
DelTunnelAndHostByClientId(id) DelTunnelAndHostByClientId(id)
file.GetCsvDb().DelClient(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) { func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ipLimit")), RunList) Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ipLimit")), RunList)
if err := Bridge.StartTunnel(); err != nil { if err := Bridge.StartTunnel(); err != nil {
lg.Fatalln("服务端开启失败", err) logs.Error("服务端开启失败", err)
os.Exit(0)
} else { } 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() go DealBridgeTask()
if svr := NewMode(Bridge, cnf); svr != nil { if svr := NewMode(Bridge, cnf); svr != nil {
if err := svr.Start(); err != nil { if err := svr.Start(); err != nil {
lg.Fatalln(err) logs.Error(err)
} }
RunList[cnf.Id] = svr RunList[cnf.Id] = svr
} else { } 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 { if err := svr.Close(); err != nil {
return err return err
} }
}
if t, err := file.GetCsvDb().GetTask(id); err != nil { if t, err := file.GetCsvDb().GetTask(id); err != nil {
return err return err
} else { } else {
t.Status = false t.Status = false
file.GetCsvDb().UpdateTask(t) file.GetCsvDb().UpdateTask(t)
} }
}
delete(RunList, id) delete(RunList, id)
return nil return nil
} }
@ -118,15 +133,24 @@ func StopServer(id int) error {
//add task //add task
func AddTask(t *file.Tunnel) error { 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" { 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") 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 { 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 RunList[t.Id] = svr
go func() { go func() {
if err := svr.Start(); err != nil { 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) delete(RunList, t.Id)
return return
} }
@ -272,5 +296,21 @@ func GetDashboardData() map[string]int {
data["udpServerCount"] += 1 data["udpServerCount"] += 1
} }
} }
tcpCount := 0
for _, v := range file.GetCsvDb().Clients {
tcpCount += v.NowConn
}
data["tcpCount"] = tcpCount
return data 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) { func TestServerPort(p int, m string) (b bool) {
if p > 65535 || p <= 0 {
return false
}
if len(ports) != 0 { if len(ports) != 0 {
if !common.InIntArr(ports, p) { if !common.InIntArr(ports, p) {
return false return false

View File

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

View File

@ -44,6 +44,12 @@ func (s *IndexController) Http() {
s.display("index/list") s.display("index/list")
} }
func (s *IndexController) Secret() {
s.SetInfo("私密代理管理")
s.SetType("secretServer")
s.display("index/list")
}
func (s *IndexController) Host() { func (s *IndexController) Host() {
s.SetInfo("host模式管理") s.SetInfo("host模式管理")
s.SetType("hostServer") s.SetType("hostServer")
@ -80,6 +86,7 @@ func (s *IndexController) Add() {
Id: file.GetCsvDb().GetTaskId(), Id: file.GetCsvDb().GetTaskId(),
Status: true, Status: true,
Remark: s.GetString("remark"), Remark: s.GetString("remark"),
Password: s.GetString("password"),
Flow: &file.Flow{}, Flow: &file.Flow{},
} }
if !tool.TestServerPort(t.Port, t.Mode) { if !tool.TestServerPort(t.Port, t.Mode) {
@ -126,6 +133,7 @@ func (s *IndexController) Edit() {
t.Port = s.GetIntNoErr("port") t.Port = s.GetIntNoErr("port")
t.Mode = s.GetString("type") t.Mode = s.GetString("type")
t.Target = s.GetString("target") t.Target = s.GetString("target")
t.Password = s.GetString("password")
t.Id = id t.Id = id
t.Remark = s.GetString("remark") t.Remark = s.GetString("remark")
if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil { 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> <label class="control-label">(KB)</label>
<input class="form-control" type="text" name="rate_limit" placeholder="为空不限制"> <input class="form-control" type="text" name="rate_limit" placeholder="为空不限制">
</div> </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"> <div class="form-group" id="u">
<label class="control-label">(socks5,web穿)</label> <label class="control-label">(socks5,web穿)</label>
<input class="form-control" type="text" name="u" placeholder="不填则无需验证"> <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" <input class="form-control" value="{{.c.RateLimit}}" type="text" name="rate_limit"
placeholder="为空不限制"> placeholder="为空不限制">
</div> </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"> <div class="form-group" id="u">
<label class="control-label">(socks5,web穿)</label> <label class="control-label">(socks5,web穿)</label>
<input class="form-control" value="{{.c.Cnf.U}}" type="text" name="u" <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 "udpServer" .type}}selected{{end}} value="udpServer">udp</option>
<option {{if eq "socks5Server" .type}}selected{{end}} value="socks5Server">socks5</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 "httpProxyServer" .type}}selected{{end}} value="httpProxyServer">http
<option {{if eq "secretServer" .type}}selected{{end}} value="secretServer">
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -30,6 +31,11 @@
<input value="{{.client_id}}" class="form-control" type="text" name="client_id" <input value="{{.client_id}}" class="form-control" type="text" name="client_id"
placeholder="客户端id"> placeholder="客户端id">
</div> </div>
<div class="form-group" id="password">
<label class="control-label"></label>
<input class="form-control" type="text" name="password"
placeholder="私密模式唯一密钥">
</div>
</form> </form>
</div> </div>
<div class="tile-footer"> <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["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["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["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"] arrClientHide = ["compress", "u", "p", "crypt", "mux"]
function resetForm() { function resetForm() {

View File

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

View File

@ -10,8 +10,8 @@
<div class="col-md-3"> <div class="col-md-3">
<div class="widget-small danger coloured-icon"><i class="icon fa fa-home fa-3x"></i> <div class="widget-small danger coloured-icon"><i class="icon fa fa-home fa-3x"></i>
<div class="info"> <div class="info">
<h4></h4> <h4>TCP</h4>
<p><b>{{.data.hostCount}}</b></p> <p><b>{{.data.tcpCount}}</b></p>
</div> </div>
</div> </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 <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> 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 <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 <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 <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> class="app-menu__icon fa fa-question-circle"></i><span class="app-menu__label">使</span></a></li>
</ul> </ul>