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 @@ + '<b langtag="info-web-auth-username">basic权限认证用户名</b>:' + row.Client.Cnf.U + `       ` + '<b langtag="info-web-auth-password">basic权限认证密码</b>:' + row.Client.Cnf.P + `       ` if (row.Mode == "p2p") { - return tmp + "<br/><br>" + '<b langtag="info-command">访问端命令</b>:' + "<code>./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -target=" + row.Target.TargetStr + "</code>" + return tmp + "<br/><br>" + + '<b langtag="info-command">访问端命令(tcp)</b>:' + "<code>./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -target=" + row.Target.TargetStr + "</code>" + "<br/><br>" + + '<b langtag="info-command">访问端命令(socks5)</b>:' + "<code>./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -local_type=p2ps" + "</code>" + "<br/><br>" + + '<b langtag="info-command">访问端命令(透明代理)</b>:' + "<code>./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -local_type=p2pt" + "</code>" + } if (row.Mode = "secret") { return tmp + "<br/><br>" + '<b langtag="info-command">访问端命令</b>:' + "<code>./npc{{.win}} -server={{.ip}}:{{.p}} -vkey=" + row.Client.VerifyKey + " -type=" +{{.bridgeType}} +" -password=" + row.Password + " -local_type=secret" + "</code>"