diff --git a/client/client.go b/client/client.go
index 1659b5c..f27fb16 100755
--- a/client/client.go
+++ b/client/client.go
@@ -1,13 +1,17 @@
package client
import (
+ "bufio"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
+ "github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"net"
+ "net/http"
+ "strconv"
"time"
)
@@ -16,6 +20,7 @@ type TRPClient struct {
bridgeConnType string
proxyUrl string
vKey string
+ p2pAddr map[string]string
tunnel *mux.Mux
signal *conn.Conn
ticker *time.Ticker
@@ -26,6 +31,7 @@ type TRPClient struct {
func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl string, cnf *config.Config) *TRPClient {
return &TRPClient{
svrAddr: svraddr,
+ p2pAddr: make(map[string]string, 0),
vKey: vKey,
bridgeConnType: bridgeConnType,
proxyUrl: proxyUrl,
@@ -71,18 +77,30 @@ func (s *TRPClient) handleMain() {
logs.Warn(err)
return
} else if pwd, err := s.signal.GetShortLenContent(); err == nil {
- go s.newUdpConn(string(lAddr), string(pwd))
+ var localAddr string
+ //The local port remains unchanged for a certain period of time
+ if v, ok := s.p2pAddr[crypt.Md5(string(pwd)+strconv.Itoa(int(time.Now().Unix()/100)))]; !ok {
+ tmpConn, err := common.GetLocalUdpAddr()
+ if err != nil {
+ logs.Error(err)
+ return
+ }
+ localAddr = tmpConn.LocalAddr().String()
+ } else {
+ localAddr = v
+ }
+ go s.newUdpConn(localAddr, string(lAddr), string(pwd))
}
}
}
s.Close()
}
-func (s *TRPClient) newUdpConn(rAddr string, md5Password string) {
+func (s *TRPClient) newUdpConn(localAddr, rAddr string, md5Password string) {
var localConn net.PacketConn
var err error
var remoteAddress string
- if remoteAddress, localConn, err = handleP2PUdp(rAddr, md5Password, common.WORK_P2P_PROVIDER); err != nil {
+ if remoteAddress, localConn, err = handleP2PUdp(localAddr, rAddr, md5Password, common.WORK_P2P_PROVIDER); err != nil {
logs.Error(err)
return
}
@@ -92,7 +110,6 @@ func (s *TRPClient) newUdpConn(rAddr string, md5Password string) {
return
}
logs.Trace("start local p2p udp listen, local address", localConn.LocalAddr().String())
- //接收新的监听,得到conn,
for {
udpTunnel, err := l.AcceptKCP()
if err != nil {
@@ -104,14 +121,10 @@ func (s *TRPClient) newUdpConn(rAddr string, md5Password string) {
conn.SetUdpSession(udpTunnel)
logs.Trace("successful connection with client ,address %s", udpTunnel.RemoteAddr().String())
//read link info from remote
- l := mux.NewMux(udpTunnel, s.bridgeConnType)
- for {
- connMux, err := l.Accept()
- if err != nil {
- continue
- }
- go s.handleChan(connMux)
- }
+ conn.Accept(mux.NewMux(udpTunnel, s.bridgeConnType), func(c net.Conn) {
+ go s.handleChan(c)
+ })
+ break
}
}
}
@@ -144,7 +157,31 @@ func (s *TRPClient) handleChan(src net.Conn) {
}
//host for target processing
lk.Host = common.FormatAddress(lk.Host)
- //connect to target
+ //if Conn type is http, read the request and log
+ if lk.ConnType == "http" {
+ if targetConn, err := net.Dial(common.CONN_TCP, lk.Host); err != nil {
+ logs.Warn("connect to %s error %s", lk.Host, err.Error())
+ src.Close()
+ } else {
+ go func() {
+ common.CopyBuffer(src, targetConn)
+ src.Close()
+ targetConn.Close()
+ }()
+ for {
+ if r, err := http.ReadRequest(bufio.NewReader(src)); err != nil {
+ src.Close()
+ targetConn.Close()
+ break
+ } else {
+ logs.Trace("http request, method %s, host %s, url %s, remote address %s", r.Method, r.Host, r.URL.Path, r.RemoteAddr)
+ r.Write(targetConn)
+ }
+ }
+ }
+ return
+ }
+ //connect to target if conn type is tcp or udp
if targetConn, err := net.Dial(lk.ConnType, lk.Host); err != nil {
logs.Warn("connect to %s error %s", lk.Host, err.Error())
src.Close()
@@ -154,6 +191,7 @@ func (s *TRPClient) handleChan(src net.Conn) {
}
}
+// Whether the monitor channel is closed
func (s *TRPClient) ping() {
s.ticker = time.NewTicker(time.Second * 5)
loop:
diff --git a/client/control.go b/client/control.go
index 6613c84..53f1958 100644
--- a/client/control.go
+++ b/client/control.go
@@ -4,17 +4,19 @@ import (
"encoding/base64"
"encoding/binary"
"errors"
+ "fmt"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"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/ccding/go-stun/stun"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"github.com/cnlh/nps/vender/golang.org/x/net/proxy"
"io/ioutil"
"log"
+ "math"
+ "math/rand"
"net"
"net/http"
"net/http/httputil"
@@ -276,108 +278,137 @@ func basicAuth(username, password string) string {
return base64.StdEncoding.EncodeToString([]byte(auth))
}
-func handleP2PUdp(rAddr, md5Password, role string) (remoteAddress string, c net.PacketConn, err error) {
- tmpConn, err := common.GetLocalUdpAddr()
+func getRemoteAddressFromServer(rAddr string, localConn *net.UDPConn, md5Password, role string, add int) error {
+ rAddr, err := getNextAddr(rAddr, add)
+ if err != nil {
+ logs.Error(err)
+ return err
+ }
+ addr, err := net.ResolveUDPAddr("udp", rAddr)
+ if err != nil {
+ return err
+ }
+ if _, err := localConn.WriteTo(common.GetWriteStr(md5Password, role), addr); err != nil {
+ return err
+ }
+ return nil
+}
+
+func handleP2PUdp(localAddr, rAddr, md5Password, role string) (remoteAddress string, c net.PacketConn, err error) {
+ localConn, err := newUdpConnByAddr(localAddr)
+ if err != nil {
+ return
+ }
+ err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 0)
if err != nil {
logs.Error(err)
return
}
- localConn, err := newUdpConnByAddr(tmpConn.LocalAddr().String())
+ err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 1)
if err != nil {
logs.Error(err)
return
}
- localKcpConn, err := kcp.NewConn(rAddr, nil, 150, 3, localConn)
+ err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 2)
if err != nil {
logs.Error(err)
return
}
- conn.SetUdpSession(localKcpConn)
- localToolConn := conn.NewConn(localKcpConn)
- //get local nat type
- //localNatType, host, err := stun.NewClient().Discover()
- //if err != nil || host == nil {
- // err = errors.New("get nat type error")
- // return
- //}
- localNatType := stun.NATRestricted
- //write password
- if _, err = localToolConn.Write([]byte(md5Password)); err != nil {
+ var remoteAddr1, remoteAddr2, remoteAddr3 string
+ for {
+ buf := make([]byte, 1024)
+ if n, addr, er := localConn.ReadFromUDP(buf); er != nil {
+ err = er
+ return
+ } else {
+ rAddr2, _ := getNextAddr(rAddr, 1)
+ rAddr3, _ := getNextAddr(rAddr, 2)
+ switch addr.String() {
+ case rAddr:
+ remoteAddr1 = string(buf[:n])
+ case rAddr2:
+ remoteAddr2 = string(buf[:n])
+ case rAddr3:
+ remoteAddr3 = string(buf[:n])
+ }
+ }
+ if remoteAddr1 != "" && remoteAddr2 != "" && remoteAddr3 != "" {
+ break
+ }
+ }
+ if remoteAddress, err = sendP2PTestMsg(localConn, remoteAddr1, remoteAddr2, remoteAddr3); err != nil {
return
}
- //write role
- if _, err = localToolConn.Write([]byte(role)); err != nil {
- return
- }
- if err = binary.Write(localToolConn, binary.LittleEndian, int32(localNatType)); err != nil {
- return
- }
- //get another type address and nat type from server
- var remoteAddr []byte
- var remoteNatType int32
- if remoteAddr, err = localToolConn.GetShortLenContent(); err != nil {
- return
- }
- if err = binary.Read(localToolConn, binary.LittleEndian, &remoteNatType); err != nil {
- return
- }
- localConn.Close()
- //logs.Trace("remote nat type %d,local nat type %s", remoteNatType, localNatType)
- if remoteAddress, err = sendP2PTestMsg(string(remoteAddr), tmpConn.LocalAddr().String()); err != nil {
- return
- }
- c, err = newUdpConnByAddr(tmpConn.LocalAddr().String())
+ c, err = newUdpConnByAddr(localAddr)
return
}
-func handleP2P(natType1, natType2 int, addr1, addr2 string, role string) (string, error) {
- switch natType1 {
- case int(stun.NATFull):
- return sendP2PTestMsg(addr2, addr1)
- case int(stun.NATRestricted):
- switch natType2 {
- case int(stun.NATFull), int(stun.NATRestricted), int(stun.NATPortRestricted), int(stun.NATSymetric):
- return sendP2PTestMsg(addr2, addr1)
- }
- case int(stun.NATPortRestricted):
- switch natType2 {
- case int(stun.NATFull), int(stun.NATRestricted), int(stun.NATPortRestricted):
- return sendP2PTestMsg(addr2, addr1)
- }
- case int(stun.NATSymetric):
- switch natType2 {
- case int(stun.NATFull), int(stun.NATRestricted):
- return sendP2PTestMsg(addr2, addr1)
- }
- }
- return "", errors.New("not support p2p")
-}
-
-func sendP2PTestMsg(remoteAddr string, localAddr string) (string, error) {
- remoteUdpAddr, err := net.ResolveUDPAddr("udp", remoteAddr)
- if err != nil {
- return "", err
- }
- localConn, err := newUdpConnByAddr(localAddr)
- if err != nil {
- return "", err
- }
+func sendP2PTestMsg(localConn *net.UDPConn, remoteAddr1, remoteAddr2, remoteAddr3 string) (string, error) {
+ logs.Trace(remoteAddr3, remoteAddr2, remoteAddr1)
defer localConn.Close()
- ticker := time.NewTicker(time.Millisecond * 500)
- go func(ticker *time.Ticker) {
+ isClose := false
+ defer func() { isClose = true }()
+ interval, err := getAddrInterval(remoteAddr1, remoteAddr2, remoteAddr3)
+ if err != nil {
+ return "", err
+ }
+ go func() {
+ addr, err := getNextAddr(remoteAddr3, interval)
+ if err != nil {
+ return
+ }
+ remoteUdpAddr, err := net.ResolveUDPAddr("udp", addr)
+ if err != nil {
+ return
+ }
+ logs.Trace("try send test packet to target %s", addr)
+ ticker := time.NewTicker(time.Millisecond * 500)
for {
select {
case <-ticker.C:
- logs.Trace("try send test packet to target %s", remoteAddr)
+ if isClose {
+ return
+ }
if _, err := localConn.WriteTo([]byte(common.WORK_P2P_CONNECT), remoteUdpAddr); err != nil {
return
}
}
}
- }(ticker)
+ }()
+ if interval != 0 {
+ ip := common.GetIpByAddr(remoteAddr2)
+ go func() {
+ ports := getRandomPortArr(common.GetPortByAddr(remoteAddr3), common.GetPortByAddr(remoteAddr3)+interval*50)
+ for i := 0; i <= 50; i ++ {
+ go func(port int) {
+ trueAddress := ip + ":" + strconv.Itoa(port)
+ logs.Trace("try send test packet to target %s", trueAddress)
+ remoteUdpAddr, err := net.ResolveUDPAddr("udp", trueAddress)
+ if err != nil {
+ return
+ }
+ ticker := time.NewTicker(time.Second * 2)
+ for {
+ select {
+ case <-ticker.C:
+ if isClose {
+ return
+ }
+ if _, err := localConn.WriteTo([]byte(common.WORK_P2P_CONNECT), remoteUdpAddr); err != nil {
+ return
+ }
+ }
+ }
+ }(ports[i])
+ time.Sleep(time.Millisecond * 10)
+ }
+ }()
+
+ }
+
buf := make([]byte, 10)
for {
- localConn.SetReadDeadline(time.Now().Add(time.Second * 30))
+ localConn.SetReadDeadline(time.Now().Add(time.Second * 10))
n, addr, err := localConn.ReadFromUDP(buf)
localConn.SetReadDeadline(time.Time{})
if err != nil {
@@ -397,7 +428,7 @@ func sendP2PTestMsg(remoteAddr string, localAddr string) (string, error) {
case common.WORK_P2P_CONNECT:
go func() {
for i := 20; i > 0; i-- {
- logs.Trace("try send receive success packet to target %s", remoteAddr)
+ logs.Trace("try send receive success packet to target %s", addr.String())
if _, err = localConn.WriteTo([]byte(common.WORK_P2P_SUCCESS), addr); err != nil {
return
}
@@ -407,9 +438,7 @@ func sendP2PTestMsg(remoteAddr string, localAddr string) (string, error) {
default:
continue
}
- ticker.Stop()
}
- ticker.Stop()
return "", errors.New("connect to the target failed, maybe the nat type is not support p2p")
}
@@ -424,3 +453,66 @@ func newUdpConnByAddr(addr string) (*net.UDPConn, error) {
}
return udpConn, nil
}
+
+func getNextAddr(addr string, n int) (string, error) {
+ arr := strings.Split(addr, ":")
+ if len(arr) != 2 {
+ return "", errors.New(fmt.Sprintf("the format of %s incorrect", addr))
+ }
+ if p, err := strconv.Atoi(arr[1]); err != nil {
+ return "", err
+ } else {
+ return arr[0] + ":" + strconv.Itoa(p+n), nil
+ }
+}
+
+func getAddrInterval(addr1, addr2, addr3 string) (int, error) {
+ arr1 := strings.Split(addr1, ":")
+ if len(arr1) != 2 {
+ return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr1))
+ }
+ arr2 := strings.Split(addr2, ":")
+ if len(arr2) != 2 {
+ return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr2))
+ }
+ arr3 := strings.Split(addr3, ":")
+ if len(arr3) != 2 {
+ return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr3))
+ }
+ p1, err := strconv.Atoi(arr1[1])
+ if err != nil {
+ return 0, err
+ }
+ p2, err := strconv.Atoi(arr2[1])
+ if err != nil {
+ return 0, err
+ }
+ p3, err := strconv.Atoi(arr3[1])
+ if err != nil {
+ return 0, err
+ }
+ interVal := int(math.Floor(math.Min(math.Abs(float64(p3-p2)), math.Abs(float64(p2-p1)))))
+ if p3-p1 < 0 {
+ return -interVal, nil
+ }
+ return interVal, nil
+}
+
+func getRandomPortArr(min, max int) []int {
+ if min > max {
+ min, max = max, min
+ }
+ addrAddr := make([]int, max-min+1)
+ for i := min; i <= max; i++ {
+ addrAddr[max-i] = i
+ }
+ rand.Seed(time.Now().UnixNano())
+ var r, temp int
+ for i := max - min; i > 0; i-- {
+ r = rand.Int() % i
+ temp = addrAddr[i]
+ addrAddr[i] = addrAddr[r]
+ addrAddr[r] = temp
+ }
+ return addrAddr
+}
diff --git a/client/local.go b/client/local.go
index 480e0a9..b9afe5c 100644
--- a/client/local.go
+++ b/client/local.go
@@ -7,22 +7,36 @@ import (
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
+ "github.com/cnlh/nps/server/proxy"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"net"
"net/http"
- "sync"
)
var (
- LocalServer []*net.TCPListener
- udpConn net.Conn
- muxSession *mux.Mux
- fileServer []*http.Server
- lock sync.Mutex
- hasP2PTry bool
+ LocalServer []*net.TCPListener
+ udpConn net.Conn
+ muxSession *mux.Mux
+ fileServer []*http.Server
+ p2pNetBridge *p2pBridge
)
+type p2pBridge struct {
+}
+
+func (p2pBridge *p2pBridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) {
+ nowConn, err := muxSession.NewConn()
+ if err != nil {
+ udpConn = nil
+ return nil, err
+ }
+ if _, err := conn.NewConn(nowConn).SendInfo(link, ""); err != nil {
+ return nil, err
+ }
+ return nowConn, nil
+}
+
func CloseLocalServer() {
for _, v := range LocalServer {
v.Close()
@@ -48,20 +62,58 @@ func startLocalFileServer(config *config.CommonConfig, t *file.Tunnel, vkey stri
}
func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error {
- listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), l.Port, ""})
+ tmpConn, err := common.GetLocalUdpAddr()
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)
- conn.Accept(listener, func(c net.Conn) {
- if l.Type == "secret" {
- handleSecret(c, config, l)
- } else {
- handleP2PVisitor(c, config, l)
+ for i := 0; i < 10; i++ {
+ logs.Notice("try to connect to the server", i+1)
+ newUdpConn(tmpConn.LocalAddr().String(), config, l)
+ if udpConn != nil {
+ break
}
- })
+ }
+ task := &file.Tunnel{
+ Port: l.Port,
+ ServerIp: "0.0.0.0",
+ Status: true,
+ Client: &file.Client{
+ Cnf: &file.Config{
+ U: "",
+ P: "",
+ Compress: config.Client.Cnf.Compress,
+ },
+ Status: true,
+ RateLimit: 0,
+ Flow: &file.Flow{},
+ },
+ Flow: &file.Flow{},
+ Target: &file.Target{},
+ }
+ switch l.Type {
+ case "p2ps":
+ logs.Info("successful start-up of local socks5 monitoring, port", l.Port)
+ return proxy.NewSock5ModeServer(p2pNetBridge, task).Start()
+ case "p2pt":
+ logs.Info("successful start-up of local tcp trans monitoring, port", l.Port)
+ return proxy.NewTunnelModeServer(proxy.HandleTrans, p2pNetBridge, task).Start()
+ case "p2p", "secret":
+ 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 tcp monitoring, port", l.Port)
+ conn.Accept(listener, func(c net.Conn) {
+ logs.Trace("new %s connection", l.Type)
+ if l.Type == "secret" {
+ handleSecret(c, config, l)
+ } else if l.Type == "p2p" {
+ handleP2PVisitor(c, config, l)
+ }
+ })
+ }
return nil
}
@@ -79,41 +131,22 @@ func handleSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config.
}
func handleP2PVisitor(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) {
-restart:
- lock.Lock()
if udpConn == nil {
- if !hasP2PTry {
- hasP2PTry = true
- newUdpConn(config, l)
- }
- if udpConn == nil {
- lock.Unlock()
- logs.Notice("new conn, P2P can not penetrate successfully, traffic will be transferred through the server")
- handleSecret(localTcpConn, config, l)
- return
- } else {
- muxSession = mux.NewMux(udpConn, "kcp")
- }
+ logs.Notice("new conn, P2P can not penetrate successfully, traffic will be transferred through the server")
+ handleSecret(localTcpConn, config, l)
}
- lock.Unlock()
logs.Trace("start trying to connect with the server")
- nowConn, err := muxSession.NewConn()
- if err != nil {
- udpConn = nil
- logs.Error(err, "reconnect......")
- goto restart
- return
- }
//TODO just support compress now because there is not tls file in client packages
link := conn.NewLink(common.CONN_TCP, l.Target, false, config.Client.Cnf.Compress, localTcpConn.LocalAddr().String(), false)
- if _, err := conn.NewConn(nowConn).SendInfo(link, ""); err != nil {
+ if target, err := p2pNetBridge.SendLinkInfo(0, link, nil); err != nil {
logs.Error(err)
return
+ } else {
+ conn.CopyWaitGroup(target, localTcpConn, false, config.Client.Cnf.Compress, nil, nil, false, nil)
}
- conn.CopyWaitGroup(nowConn, localTcpConn, false, config.Client.Cnf.Compress, nil, nil, false, nil)
}
-func newUdpConn(config *config.CommonConfig, l *config.LocalServer) {
+func newUdpConn(localAddr string, config *config.CommonConfig, l *config.LocalServer) {
remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_P2P, config.ProxyUrl)
if err != nil {
logs.Error("Local connection server failed ", err.Error())
@@ -131,7 +164,7 @@ func newUdpConn(config *config.CommonConfig, l *config.LocalServer) {
}
var localConn net.PacketConn
var remoteAddress string
- if remoteAddress, localConn, err = handleP2PUdp(string(rAddr), crypt.Md5(l.Password), common.WORK_P2P_VISITOR); err != nil {
+ if remoteAddress, localConn, err = handleP2PUdp(localAddr, string(rAddr), crypt.Md5(l.Password), common.WORK_P2P_VISITOR); err != nil {
logs.Error(err)
return
}
@@ -143,4 +176,6 @@ func newUdpConn(config *config.CommonConfig, l *config.LocalServer) {
logs.Trace("successful create a connection with server", remoteAddress)
conn.SetUdpSession(udpTunnel)
udpConn = udpTunnel
+ muxSession = mux.NewMux(udpConn, "kcp")
+ p2pNetBridge = &p2pBridge{}
}
diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go
index ffadb9d..f2c7b7c 100644
--- a/cmd/npc/npc.go
+++ b/cmd/npc/npc.go
@@ -60,7 +60,7 @@ func main() {
if *logType == "stdout" {
logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
} else {
- logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"color":true}`)
+ logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"maxlines":100000,"color":true}`)
}
//p2p or secret command
if *password != "" {
diff --git a/cmd/nps/nps.go b/cmd/nps/nps.go
index e9b4d37..2cb9a28 100644
--- a/cmd/nps/nps.go
+++ b/cmd/nps/nps.go
@@ -50,7 +50,7 @@ func main() {
if *logType == "stdout" {
logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
} else {
- logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+beego.AppConfig.String("log_path")+`","daily":false,"color":true}`)
+ logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+beego.AppConfig.String("log_path")+`","daily":false,"maxlines":100000,"color":true}`)
}
task := &file.Tunnel{
Mode: "webServer",
diff --git a/lib/common/util.go b/lib/common/util.go
index c0e101c..ce4381d 100755
--- a/lib/common/util.go
+++ b/lib/common/util.go
@@ -163,6 +163,13 @@ func TestUdpPort(port int) bool {
//Length prevents sticking
//# Characters are used to separate data
func BinaryWrite(raw *bytes.Buffer, v ...string) {
+ b := GetWriteStr(v...)
+ binary.Write(raw, binary.LittleEndian, int32(len(b)))
+ binary.Write(raw, binary.LittleEndian, b)
+}
+
+// get seq str
+func GetWriteStr(v ...string) []byte {
buffer := new(bytes.Buffer)
var l int32
for _, v := range v {
@@ -170,8 +177,7 @@ func BinaryWrite(raw *bytes.Buffer, v ...string) {
binary.Write(buffer, binary.LittleEndian, []byte(v))
binary.Write(buffer, binary.LittleEndian, []byte(CONN_DATA_SEQ))
}
- binary.Write(raw, binary.LittleEndian, l)
- binary.Write(raw, binary.LittleEndian, buffer.Bytes())
+ return buffer.Bytes()
}
//inArray str interface
@@ -244,6 +250,19 @@ func GetIpByAddr(addr string) string {
return arr[0]
}
+//get port from the complete address
+func GetPortByAddr(addr string) int {
+ arr := strings.Split(addr, ":")
+ if len(arr) < 2 {
+ return 0
+ }
+ p, err := strconv.Atoi(arr[1])
+ if err != nil {
+ return 0
+ }
+ return p
+}
+
func CopyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
buf := pool.GetBufPoolCopy()
defer pool.PutBufPoolCopy(buf)
diff --git a/lib/install/install.go b/lib/install/install.go
index 323d563..5b2a515 100644
--- a/lib/install/install.go
+++ b/lib/install/install.go
@@ -13,6 +13,9 @@ import (
func InstallNps() {
path := common.GetInstallPath()
+ if common.FileExists(path) {
+ log.Fatalf("the path %s has exist, does not support install", path)
+ }
MkidrDirAll(path, "conf", "web/static", "web/views")
//复制文件到对应目录
if err := CopyDir(filepath.Join(common.GetAppPath(), "web", "views"), filepath.Join(path, "web", "views")); err != nil {
diff --git a/lib/version/version.go b/lib/version/version.go
index a8e21dd..6a7d0d5 100644
--- a/lib/version/version.go
+++ b/lib/version/version.go
@@ -1,6 +1,6 @@
package version
-const VERSION = "0.22.5"
+const VERSION = "0.23.0"
// Compulsory minimum version, Minimum downward compatibility to this version
func GetVersion() string {
diff --git a/server/proxy/base.go b/server/proxy/base.go
index ba7568f..2509dbd 100644
--- a/server/proxy/base.go
+++ b/server/proxy/base.go
@@ -17,10 +17,14 @@ type Service interface {
Close() error
}
+type NetBridge interface {
+ SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error)
+}
+
//BaseServer struct
type BaseServer struct {
id int
- bridge *bridge.Bridge
+ bridge NetBridge
task *file.Tunnel
errorContent []byte
sync.Mutex
diff --git a/server/proxy/http.go b/server/proxy/http.go
index f8e0a24..84026ff 100644
--- a/server/proxy/http.go
+++ b/server/proxy/http.go
@@ -147,7 +147,7 @@ func (s *httpServer) httpHandle(c *conn.Conn, r *http.Request) {
logs.Warn(err.Error())
break
}
- lk = conn.NewLink(common.CONN_TCP, targetAddr, host.Client.Cnf.Crypt, host.Client.Cnf.Compress, r.RemoteAddr, host.Target.LocalProxy)
+ lk = conn.NewLink("http", targetAddr, host.Client.Cnf.Crypt, host.Client.Cnf.Compress, r.RemoteAddr, host.Target.LocalProxy)
if target, err = s.bridge.SendLinkInfo(host.Client.Id, lk, nil); err != nil {
logs.Notice("connect to target %s error %s", lk.Host, err)
break
diff --git a/server/proxy/https.go b/server/proxy/https.go
index 7fc3ee9..9ccce29 100644
--- a/server/proxy/https.go
+++ b/server/proxy/https.go
@@ -1,7 +1,6 @@
package proxy
import (
- "github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/cache"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
@@ -22,7 +21,7 @@ type HttpsServer struct {
httpsListenerMap sync.Map
}
-func NewHttpsServer(l net.Listener, bridge *bridge.Bridge, useCache bool, cacheLen int) *HttpsServer {
+func NewHttpsServer(l net.Listener, bridge NetBridge, useCache bool, cacheLen int) *HttpsServer {
https := &HttpsServer{listener: l}
https.bridge = bridge
https.useCache = useCache
diff --git a/server/proxy/p2p.go b/server/proxy/p2p.go
index e553ffb..44cdea3 100644
--- a/server/proxy/p2p.go
+++ b/server/proxy/p2p.go
@@ -1,28 +1,24 @@
package proxy
import (
- "encoding/binary"
"github.com/cnlh/nps/lib/common"
- "github.com/cnlh/nps/lib/conn"
+ "github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net"
- "strconv"
+ "strings"
"time"
)
type P2PServer struct {
BaseServer
- p2pPort int
- p2p map[string]*p2p
+ p2pPort int
+ p2p map[string]*p2p
+ listener *net.UDPConn
}
type p2p struct {
- provider *conn.Conn
- visitor *conn.Conn
- visitorAddr string
- providerAddr string
- providerNatType int32
- visitorNatType int32
+ visitorAddr *net.UDPAddr
+ providerAddr *net.UDPAddr
}
func NewP2PServer(p2pPort int) *P2PServer {
@@ -33,62 +29,52 @@ func NewP2PServer(p2pPort int) *P2PServer {
}
func (s *P2PServer) Start() error {
- return conn.NewKcpListenerAndProcess(":"+strconv.Itoa(s.p2pPort), func(c net.Conn) {
- s.p2pProcess(conn.NewConn(c))
- })
+ logs.Info("start p2p server port", s.p2pPort)
+ var err error
+ s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.p2pPort, ""})
+ if err != nil {
+ return err
+ }
+ for {
+ buf := pool.BufPoolUdp.Get().([]byte)
+ n, addr, err := s.listener.ReadFromUDP(buf)
+ if err != nil {
+ if strings.Contains(err.Error(), "use of closed network connection") {
+ break
+ }
+ continue
+ }
+ go s.handleP2P(addr, string(buf[:n]))
+ }
+ return nil
}
-func (s *P2PServer) p2pProcess(c *conn.Conn) {
+func (s *P2PServer) handleP2P(addr *net.UDPAddr, str string) {
var (
- f string
- b []byte
- err error
- v *p2p
- ok bool
- natType int32
+ v *p2p
+ ok bool
)
- if b, err = c.GetShortContent(32); err != nil {
+ arr := strings.Split(str, common.CONN_DATA_SEQ)
+ if len(arr) < 2 {
return
}
- //get role
- if f, err = c.ReadFlag(); err != nil {
- return
- }
- //get nat type
- if err := binary.Read(c, binary.LittleEndian, &natType); err != nil {
- return
- }
- if v, ok = s.p2p[string(b)]; !ok {
+ if v, ok = s.p2p[arr[0]]; !ok {
v = new(p2p)
- s.p2p[string(b)] = v
+ s.p2p[arr[0]] = v
}
- logs.Trace("new p2p connection ,role %s , password %s, nat type %s ,local address %s", f, string(b), strconv.Itoa(int(natType)), c.RemoteAddr().String())
- //存储
- if f == common.WORK_P2P_VISITOR {
- v.visitorAddr = c.Conn.RemoteAddr().String()
- v.visitorNatType = natType
- v.visitor = c
+ logs.Trace("new p2p connection ,role %s , password %s ,local address %s", arr[1], arr[0], addr.String())
+ if arr[1] == common.WORK_P2P_VISITOR {
+ v.visitorAddr = addr
for i := 20; i > 0; i-- {
- if v.provider != nil {
- v.provider.WriteLenContent([]byte(v.visitorAddr))
- binary.Write(v.provider, binary.LittleEndian, v.visitorNatType)
+ if v.providerAddr != nil {
+ s.listener.WriteTo([]byte(v.providerAddr.String()), v.visitorAddr)
+ s.listener.WriteTo([]byte(v.visitorAddr.String()), v.providerAddr)
break
}
time.Sleep(time.Second)
}
- v.provider = nil
+ delete(s.p2p, arr[0])
} else {
- v.providerAddr = c.Conn.RemoteAddr().String()
- v.providerNatType = natType
- v.provider = c
- for i := 20; i > 0; i-- {
- if v.visitor != nil {
- v.visitor.WriteLenContent([]byte(v.providerAddr))
- binary.Write(v.visitor, binary.LittleEndian, v.providerNatType)
- break
- }
- time.Sleep(time.Second)
- }
- v.visitor = nil
+ v.providerAddr = addr
}
}
diff --git a/server/proxy/socks5.go b/server/proxy/socks5.go
index 1ab20a9..5af021b 100755
--- a/server/proxy/socks5.go
+++ b/server/proxy/socks5.go
@@ -3,7 +3,6 @@ package proxy
import (
"encoding/binary"
"errors"
- "github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
@@ -264,7 +263,7 @@ func (s *Sock5ModeServer) Start() error {
}
//new
-func NewSock5ModeServer(bridge *bridge.Bridge, task *file.Tunnel) *Sock5ModeServer {
+func NewSock5ModeServer(bridge NetBridge, task *file.Tunnel) *Sock5ModeServer {
s := new(Sock5ModeServer)
s.bridge = bridge
s.task = task
@@ -274,4 +273,4 @@ func NewSock5ModeServer(bridge *bridge.Bridge, task *file.Tunnel) *Sock5ModeServ
//close
func (s *Sock5ModeServer) Close() error {
return s.listener.Close()
-}
+}
\ No newline at end of file
diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go
index 89f4edd..0cda27a 100755
--- a/server/proxy/tcp.go
+++ b/server/proxy/tcp.go
@@ -13,6 +13,7 @@ import (
"net/http"
"path/filepath"
"strconv"
+ "syscall"
)
type TunnelModeServer struct {
@@ -22,7 +23,7 @@ type TunnelModeServer struct {
}
//tcp|http|host
-func NewTunnelModeServer(process process, bridge *bridge.Bridge, task *file.Tunnel) *TunnelModeServer {
+func NewTunnelModeServer(process process, bridge NetBridge, task *file.Tunnel) *TunnelModeServer {
s := new(TunnelModeServer)
s.bridge = bridge
s.process = process
@@ -114,3 +115,35 @@ func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error {
}
return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy)
}
+
+func HandleTrans(c *conn.Conn, s *TunnelModeServer) error {
+ if addr, err := getAddress(c.Conn); err != nil {
+ return err
+ } else {
+ return s.DealClient(c, s.task.Client, addr, nil, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy)
+ }
+}
+
+const SO_ORIGINAL_DST = 80
+
+func getAddress(conn net.Conn) (string, error) {
+ sysrawconn, f := conn.(syscall.Conn)
+ if !f {
+ return "", nil
+ }
+ rawConn, err := sysrawconn.SyscallConn()
+ if err != nil {
+ return "", nil
+ }
+ var ip string
+ var port uint16
+ err = rawConn.Control(func(fd uintptr) {
+ addr, err := syscall.GetsockoptIPv6Mreq(int(fd), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
+ if err != nil {
+ return
+ }
+ ip = net.IP(addr.Multiaddr[4:8]).String()
+ port = uint16(addr.Multiaddr[2])<<8 + uint16(addr.Multiaddr[3])
+ })
+ return ip + ":" + strconv.Itoa(int(port)), nil
+}
diff --git a/server/server.go b/server/server.go
index d5d0f63..31e6f18 100644
--- a/server/server.go
+++ b/server/server.go
@@ -90,8 +90,9 @@ func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
}
}()
if p, err := beego.AppConfig.Int("p2p_port"); err == nil {
- logs.Info("start p2p server port", p)
go proxy.NewP2PServer(p).Start()
+ go proxy.NewP2PServer(p + 1).Start()
+ go proxy.NewP2PServer(p + 2).Start()
}
go DealBridgeTask()
go dealClientFlow()
@@ -125,6 +126,8 @@ func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) proxy.Service {
service = proxy.NewSock5ModeServer(Bridge, c)
case "httpProxy":
service = proxy.NewTunnelModeServer(proxy.ProcessHttp, Bridge, c)
+ case "tcpTrans":
+ service = proxy.NewTunnelModeServer(proxy.HandleTrans, Bridge, c)
case "udp":
service = proxy.NewUdpModeServer(Bridge, c)
case "webServer":
diff --git a/web/views/index/list.html b/web/views/index/list.html
index 90d6742..5537f67 100755
--- a/web/views/index/list.html
+++ b/web/views/index/list.html
@@ -70,7 +70,11 @@
+ 'basic权限认证用户名:' + row.Client.Cnf.U + `       `
+ 'basic权限认证密码:' + row.Client.Cnf.P + `       `
if (row.Mode == "p2p") {
- return tmp + "
" + '访问端命令:' + "./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -target=" + row.Target.TargetStr + "
"
+ return tmp + "
"
+ + '访问端命令(tcp):' + "./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -target=" + row.Target.TargetStr + "
" + "
"
+ + '访问端命令(socks5):' + "./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -local_type=p2ps" + "
" + "
"
+ + '访问端命令(透明代理):' + "./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -local_type=p2pt" + "
"
+
}
if (row.Mode = "secret") {
return tmp + "
" + '访问端命令:' + "./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -local_type=secret" + "
"