mirror of https://github.com/ehang-io/nps
				
				
				
			Modular 、Functional enhancement
							parent
							
								
									0c87b4119a
								
							
						
					
					
						commit
						824b12a2f8
					
				|  | @ -152,7 +152,6 @@ func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) { | |||
| //验证失败,返回错误验证flag,并且关闭连接
 | ||||
| func (s *Bridge) verifyError(c *conn.Conn) { | ||||
| 	c.Write([]byte(common.VERIFY_EER)) | ||||
| 	c.Conn.Close() | ||||
| } | ||||
| 
 | ||||
| func (s *Bridge) verifySuccess(c *conn.Conn) { | ||||
|  | @ -291,11 +290,16 @@ func (s *Bridge) register(c *conn.Conn) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string, t *file.Tunnel) (target net.Conn, err error) { | ||||
| func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) { | ||||
| 	//if the proxy type is local
 | ||||
| 	if link.LocalProxy { | ||||
| 		target, err = net.Dial(link.ConnType, link.Host) | ||||
| 		return | ||||
| 	} | ||||
| 	if v, ok := s.Client.Load(clientId); ok { | ||||
| 		//If ip is restricted to do ip verification
 | ||||
| 		if s.ipVerify { | ||||
| 			ip := common.GetIpByAddr(linkAddr) | ||||
| 			ip := common.GetIpByAddr(link.RemoteAddr) | ||||
| 			if v, ok := s.Register.Load(ip); !ok { | ||||
| 				return nil, errors.New(fmt.Sprintf("The ip %s is not in the validation list", ip)) | ||||
| 			} else { | ||||
|  |  | |||
							
								
								
									
										113
									
								
								client/client.go
								
								
								
								
							
							
						
						
									
										113
									
								
								client/client.go
								
								
								
								
							|  | @ -8,7 +8,6 @@ import ( | |||
| 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" | ||||
| 	"github.com/cnlh/nps/vender/github.com/xtaci/kcp" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
|  | @ -43,106 +42,47 @@ retry: | |||
| 		time.Sleep(time.Second * 5) | ||||
| 		goto retry | ||||
| 	} | ||||
| 
 | ||||
| 	logs.Info("Successful connection with server %s", s.svrAddr) | ||||
| 	//monitor the connection
 | ||||
| 	go s.ping() | ||||
| 	s.processor(c) | ||||
| } | ||||
| 
 | ||||
| //处理
 | ||||
| func (s *TRPClient) processor(c *conn.Conn) { | ||||
| 	s.signal = c | ||||
| 	go s.dealChan() | ||||
| 	//start a channel connection
 | ||||
| 	go s.newChan() | ||||
| 	//start health check if the it's open
 | ||||
| 	if s.cnf != nil && len(s.cnf.Healths) > 0 { | ||||
| 		go heathCheck(s.cnf.Healths, s.signal) | ||||
| 	} | ||||
| 	//msg connection, eg udp
 | ||||
| 	s.handleMain() | ||||
| } | ||||
| 
 | ||||
| //handle main connection
 | ||||
| func (s *TRPClient) handleMain() { | ||||
| 	for { | ||||
| 		flags, err := c.ReadFlag() | ||||
| 		flags, err := s.signal.ReadFlag() | ||||
| 		if err != nil { | ||||
| 			logs.Error("Accept server data error %s, end this service", err.Error()) | ||||
| 			break | ||||
| 		} | ||||
| 		switch flags { | ||||
| 		case common.VERIFY_EER: | ||||
| 			logs.Error("VKey:%s is incorrect, the server refuses to connect, please check", s.vKey) | ||||
| 			os.Exit(0) | ||||
| 		case common.RES_CLOSE: | ||||
| 			logs.Error("The authentication key is connected by another client or the server closes the client.") | ||||
| 			os.Exit(0) | ||||
| 		case common.RES_MSG: | ||||
| 			logs.Error("Server-side return error") | ||||
| 			break | ||||
| 		case common.NEW_UDP_CONN: | ||||
| 			//读取服务端地址、密钥 继续做处理
 | ||||
| 			if lAddr, err := c.GetShortLenContent(); err != nil { | ||||
| 			//read server udp addr and password
 | ||||
| 			if lAddr, err := s.signal.GetShortLenContent(); err != nil { | ||||
| 				logs.Warn(err) | ||||
| 				return | ||||
| 			} else if pwd, err := c.GetShortLenContent(); err == nil { | ||||
| 			} else if pwd, err := s.signal.GetShortLenContent(); err == nil { | ||||
| 				go s.newUdpConn(string(lAddr), string(pwd)) | ||||
| 			} | ||||
| 		default: | ||||
| 			logs.Warn("The error could not be resolved") | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	c.Close() | ||||
| 	s.Close() | ||||
| } | ||||
| 
 | ||||
| func (s *TRPClient) newUdpConn(rAddr string, md5Password string) { | ||||
| 	tmpConn, err := common.GetLocalUdpAddr() | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	localAddr, _ := net.ResolveUDPAddr("udp", tmpConn.LocalAddr().String()) | ||||
| 	localConn, err := net.ListenUDP("udp", localAddr) | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	localKcpConn, err := kcp.NewConn(rAddr, nil, 150, 3, localConn) | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	conn.SetUdpSession(localKcpConn) | ||||
| 	localToolConn := conn.NewConn(localKcpConn) | ||||
| 	//写入密钥、provider身份
 | ||||
| 	if _, err := localToolConn.Write([]byte(md5Password)); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	if _, err := localToolConn.Write([]byte(common.WORK_P2P_PROVIDER)); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//接收服务端传的visitor地址
 | ||||
| 	var b []byte | ||||
| 	if b, err = localToolConn.GetShortLenContent(); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//向visitor地址发送测试消息
 | ||||
| 	visitorAddr, err := net.ResolveUDPAddr("udp", string(b)) | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//向目标IP发送探测包
 | ||||
| 	if _, err := localConn.WriteTo([]byte("test"), visitorAddr); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//给服务端发反馈
 | ||||
| 	if _, err := localToolConn.Write([]byte(common.VERIFY_SUCCESS)); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//关闭与服务端的连接
 | ||||
| 	localConn.Close() | ||||
| 	//关闭与服务端udp conn,建立新的监听
 | ||||
| 	if localConn, err = net.ListenUDP("udp", localAddr); err != nil { | ||||
| 	var localConn net.PacketConn | ||||
| 	var err error | ||||
| 	var remoteAddress string | ||||
| 	if remoteAddress, localConn, err = handleP2PUdp(rAddr, md5Password, common.WORK_P2P_PROVIDER); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
|  | @ -151,6 +91,7 @@ func (s *TRPClient) newUdpConn(rAddr string, md5Password string) { | |||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	logs.Trace("start local p2p udp listen, local address", localConn.LocalAddr().String()) | ||||
| 	//接收新的监听,得到conn,
 | ||||
| 	for { | ||||
| 		udpTunnel, err := l.AcceptKCP() | ||||
|  | @ -159,23 +100,24 @@ func (s *TRPClient) newUdpConn(rAddr string, md5Password string) { | |||
| 			l.Close() | ||||
| 			return | ||||
| 		} | ||||
| 		if udpTunnel.RemoteAddr().String() == string(b) { | ||||
| 		if udpTunnel.RemoteAddr().String() == string(remoteAddress) { | ||||
| 			conn.SetUdpSession(udpTunnel) | ||||
| 			//读取link,设置msgCh 设置msgConn消息回传响应机制
 | ||||
| 			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.srcProcess(connMux) | ||||
| 				go s.handleChan(connMux) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //mux tunnel
 | ||||
| func (s *TRPClient) dealChan() { | ||||
| func (s *TRPClient) newChan() { | ||||
| 	tunnel, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl) | ||||
| 	if err != nil { | ||||
| 		logs.Error("connect to ", s.svrAddr, "error:", err) | ||||
|  | @ -189,11 +131,11 @@ func (s *TRPClient) dealChan() { | |||
| 			s.Close() | ||||
| 			break | ||||
| 		} | ||||
| 		go s.srcProcess(src) | ||||
| 		go s.handleChan(src) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *TRPClient) srcProcess(src net.Conn) { | ||||
| func (s *TRPClient) handleChan(src net.Conn) { | ||||
| 	lk, err := conn.NewConn(src).GetLinkInfo() | ||||
| 	if err != nil { | ||||
| 		src.Close() | ||||
|  | @ -218,9 +160,8 @@ loop: | |||
| 	for { | ||||
| 		select { | ||||
| 		case <-s.ticker.C: | ||||
| 			if s.tunnel.IsClose { | ||||
| 			if s.tunnel != nil && s.tunnel.IsClose { | ||||
| 				s.Close() | ||||
| 				s.ticker.Stop() | ||||
| 				break loop | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import ( | |||
| 	"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" | ||||
|  | @ -162,7 +163,7 @@ re: | |||
| 
 | ||||
| 	//create local server secret or p2p
 | ||||
| 	for _, v := range cnf.LocalServer { | ||||
| 		go StartLocalServer(v, cnf.CommonConfig) | ||||
| 		go startLocalServer(v, cnf.CommonConfig) | ||||
| 	} | ||||
| 
 | ||||
| 	c.Close() | ||||
|  | @ -238,6 +239,7 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st | |||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| //http proxy connection
 | ||||
| func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) { | ||||
| 	req := &http.Request{ | ||||
| 		Method: "CONNECT", | ||||
|  | @ -266,7 +268,143 @@ func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) { | |||
| 	return proxyConn, nil | ||||
| } | ||||
| 
 | ||||
| //get a basic auth string
 | ||||
| func basicAuth(username, password string) string { | ||||
| 	auth := username + ":" + password | ||||
| 	return base64.StdEncoding.EncodeToString([]byte(auth)) | ||||
| } | ||||
| 
 | ||||
| func handleP2PUdp(rAddr, md5Password, role string) (remoteAddress string, c net.PacketConn, err error) { | ||||
| 	tmpConn, err := common.GetLocalUdpAddr() | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	localConn, err := newUdpConnByAddr(tmpConn.LocalAddr().String()) | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	localKcpConn, err := kcp.NewConn(rAddr, nil, 150, 3, localConn) | ||||
| 	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 { | ||||
| 		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()) | ||||
| 	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) | ||||
| 	defer localConn.Close() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	buf := make([]byte, 10) | ||||
| 	for i := 20; i > 0; i-- { | ||||
| 		logs.Trace("try send test packet to target %s", remoteAddr) | ||||
| 		if _, err := localConn.WriteTo([]byte(common.WORK_P2P_CONNECT), remoteUdpAddr); err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		localConn.SetReadDeadline(time.Now().Add(time.Millisecond * 500)) | ||||
| 		n, addr, err := localConn.ReadFromUDP(buf) | ||||
| 		localConn.SetReadDeadline(time.Time{}) | ||||
| 		switch string(buf[:n]) { | ||||
| 		case common.WORK_P2P_SUCCESS: | ||||
| 			for i := 20; i > 0; i-- { | ||||
| 				if _, err = localConn.WriteTo([]byte(common.WORK_P2P_END), addr); err != nil { | ||||
| 					return "", err | ||||
| 				} | ||||
| 			} | ||||
| 			return addr.String(), nil | ||||
| 		case common.WORK_P2P_END: | ||||
| 			logs.Trace("Remotely Address %s Reply Packet Successfully Received", addr.String()) | ||||
| 			return addr.String(), nil | ||||
| 		case common.WORK_P2P_CONNECT: | ||||
| 			go func() { | ||||
| 				for i := 20; i > 0; i-- { | ||||
| 					logs.Trace("try send receive success packet to target %s", remoteAddr) | ||||
| 					if _, err = localConn.WriteTo([]byte(common.WORK_P2P_SUCCESS), addr); err != nil { | ||||
| 						return | ||||
| 					} | ||||
| 					time.Sleep(time.Second) | ||||
| 				} | ||||
| 			}() | ||||
| 		} | ||||
| 	} | ||||
| 	localConn.Close() | ||||
| 	return "", errors.New("connect to the target failed, maybe the nat type is not support p2p") | ||||
| } | ||||
| 
 | ||||
| func newUdpConnByAddr(addr string) (*net.UDPConn, error) { | ||||
| 	udpAddr, err := net.ResolveUDPAddr("udp", addr) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	udpConn, err := net.ListenUDP("udp", udpAddr) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return udpConn, nil | ||||
| } | ||||
|  |  | |||
|  | @ -11,12 +11,16 @@ import ( | |||
| 	"github.com/cnlh/nps/vender/github.com/xtaci/kcp" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| var LocalServer []*net.TCPListener | ||||
| var udpConn net.Conn | ||||
| var muxSession *mux.Mux | ||||
| var fileServer []*http.Server | ||||
| var ( | ||||
| 	LocalServer []*net.TCPListener | ||||
| 	udpConn     net.Conn | ||||
| 	muxSession  *mux.Mux | ||||
| 	fileServer  []*http.Server | ||||
| 	lock        sync.Mutex | ||||
| ) | ||||
| 
 | ||||
| func CloseLocalServer() { | ||||
| 	for _, v := range LocalServer { | ||||
|  | @ -39,10 +43,10 @@ func startLocalFileServer(config *config.CommonConfig, t *file.Tunnel, vkey stri | |||
| 	logs.Info("start local file system, local path %s, strip prefix %s ,remote port %s ", t.LocalPath, t.StripPre, t.Ports) | ||||
| 	fileServer = append(fileServer, srv) | ||||
| 	listener := mux.NewMux(remoteConn.Conn, common.CONN_TCP) | ||||
| 	logs.Warn(srv.Serve(listener)) | ||||
| 	logs.Error(srv.Serve(listener)) | ||||
| } | ||||
| 
 | ||||
| func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error { | ||||
| 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()) | ||||
|  | @ -52,15 +56,15 @@ func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error | |||
| 	logs.Info("successful start-up of local monitoring, port", l.Port) | ||||
| 	conn.Accept(listener, func(c net.Conn) { | ||||
| 		if l.Type == "secret" { | ||||
| 			processSecret(c, config, l) | ||||
| 			handleSecret(c, config, l) | ||||
| 		} else { | ||||
| 			processP2P(c, config, l) | ||||
| 			handleP2PVisitor(c, config, l) | ||||
| 		} | ||||
| 	}) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func processSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) { | ||||
| func handleSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) { | ||||
| 	remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_SECRET, config.ProxyUrl) | ||||
| 	if err != nil { | ||||
| 		logs.Error("Local connection server failed ", err.Error()) | ||||
|  | @ -73,21 +77,28 @@ func processSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config | |||
| 	conn.CopyWaitGroup(remoteConn.Conn, localTcpConn, false, false, nil, nil, false, nil) | ||||
| } | ||||
| 
 | ||||
| func processP2P(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) { | ||||
| func handleP2PVisitor(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) { | ||||
| restart: | ||||
| 	lock.Lock() | ||||
| 	if udpConn == nil { | ||||
| 		newUdpConn(config, l) | ||||
| 		if udpConn == nil { | ||||
| 			lock.Unlock() | ||||
| 			return | ||||
| 		} | ||||
| 		muxSession = mux.NewMux(udpConn, "kcp") | ||||
| 	} | ||||
| 	lock.Unlock() | ||||
| 	logs.Trace("start trying to connect with the server") | ||||
| 	nowConn, err := muxSession.NewConn() | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		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()) | ||||
| 	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 { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
|  | @ -111,49 +122,18 @@ func newUdpConn(config *config.CommonConfig, l *config.LocalServer) { | |||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//与服务端udp建立连接
 | ||||
| 	tmpConn, err := common.GetLocalUdpAddr() | ||||
| 	if err != nil { | ||||
| 	var localConn net.PacketConn | ||||
| 	var remoteAddress string | ||||
| 	if remoteAddress, localConn, err = handleP2PUdp(string(rAddr), crypt.Md5(l.Password), common.WORK_P2P_VISITOR); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	udpTunnel, err := kcp.NewConn(remoteAddress, nil, 150, 3, localConn) | ||||
| 	if err != nil || udpTunnel == nil { | ||||
| 		logs.Warn(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//与服务端建立udp连接
 | ||||
| 	localAddr, _ := net.ResolveUDPAddr("udp", tmpConn.LocalAddr().String()) | ||||
| 	localConn, err := net.ListenUDP("udp", localAddr) | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	localKcpConn, err := kcp.NewConn(string(rAddr), nil, 150, 3, localConn) | ||||
| 	if err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	conn.SetUdpSession(localKcpConn) | ||||
| 	//写入密钥、provider身份
 | ||||
| 	if _, err := localKcpConn.Write([]byte(crypt.Md5(l.Password))); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	if _, err := localKcpConn.Write([]byte(common.WORK_P2P_VISITOR)); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 	//接收服务端传的visitor地址
 | ||||
| 	if b, err := conn.NewConn(localKcpConn).GetShortLenContent(); err != nil { | ||||
| 		logs.Error(err) | ||||
| 		return | ||||
| 	} else { | ||||
| 		//关闭与服务端连接
 | ||||
| 		localConn.Close() | ||||
| 		//建立新的连接
 | ||||
| 		localConn, err = net.ListenUDP("udp", localAddr) | ||||
| 		udpTunnel, err := kcp.NewConn(string(b), nil, 150, 3, localConn) | ||||
| 		if err != nil || udpTunnel == nil { | ||||
| 			logs.Warn(err) | ||||
| 			return | ||||
| 		} | ||||
| 		conn.SetUdpSession(udpTunnel) | ||||
| 		udpConn = udpTunnel | ||||
| 	} | ||||
| 	logs.Trace("successful create a connection with server", remoteAddress) | ||||
| 	conn.SetUdpSession(udpTunnel) | ||||
| 	udpConn = udpTunnel | ||||
| } | ||||
|  |  | |||
|  | @ -2,11 +2,15 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"github.com/cnlh/nps/client" | ||||
| 	"github.com/cnlh/nps/lib/common" | ||||
| 	"github.com/cnlh/nps/lib/config" | ||||
| 	"github.com/cnlh/nps/lib/daemon" | ||||
| 	"github.com/cnlh/nps/lib/file" | ||||
| 	"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" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | @ -21,18 +25,33 @@ var ( | |||
| 	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") | ||||
| 	localPort    = flag.Int("local_port", 2000, "p2p local port") | ||||
| 	password     = flag.String("password", "", "p2p password flag") | ||||
| 	target       = flag.String("target", "", "p2p target") | ||||
| 	localType    = flag.String("local_type", "p2p", "p2p target") | ||||
| 	logPath      = flag.String("log_path", "npc.log", "npc log path") | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	if len(os.Args) > 2 { | ||||
| 	if len(os.Args) >= 2 { | ||||
| 		switch os.Args[1] { | ||||
| 		case "status": | ||||
| 			path := strings.Replace(os.Args[2], "-config=", "", -1) | ||||
| 			client.GetTaskStatus(path) | ||||
| 			if len(os.Args) > 2 { | ||||
| 				path := strings.Replace(os.Args[2], "-config=", "", -1) | ||||
| 				client.GetTaskStatus(path) | ||||
| 			} | ||||
| 		case "register": | ||||
| 			flag.CommandLine.Parse(os.Args[2:]) | ||||
| 			client.RegisterLocalIp(*serverAddr, *verifyKey, *connType, *proxyUrl, *registerTime) | ||||
| 		case "nat": | ||||
| 			nat, host, err := stun.NewClient().Discover() | ||||
| 			if err != nil || host == nil { | ||||
| 				logs.Error("get nat type error", err) | ||||
| 				return | ||||
| 			} | ||||
| 			fmt.Printf("nat type: %s \npublic address: %s\n", nat.String(), host.String()) | ||||
| 			os.Exit(0) | ||||
| 		} | ||||
| 	} | ||||
| 	daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath()) | ||||
|  | @ -41,7 +60,23 @@ func main() { | |||
| 	if *logType == "stdout" { | ||||
| 		logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`) | ||||
| 	} else { | ||||
| 		logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"npc_log.log","daily":false,"color":true}`) | ||||
| 		logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"color":true}`) | ||||
| 	} | ||||
| 	//p2p or secret command
 | ||||
| 	if *password != "" { | ||||
| 		commonConfig := new(config.CommonConfig) | ||||
| 		commonConfig.Server = *serverAddr | ||||
| 		commonConfig.VKey = *verifyKey | ||||
| 		commonConfig.Tp = *connType | ||||
| 		localServer := new(config.LocalServer) | ||||
| 		localServer.Type = *localType | ||||
| 		localServer.Password = *password | ||||
| 		localServer.Target = *target | ||||
| 		localServer.Port = *localPort | ||||
| 		commonConfig.Client = new(file.Client) | ||||
| 		commonConfig.Client.Cnf = new(file.Config) | ||||
| 		client.StartLocalServer(localServer, commonConfig) | ||||
| 		return | ||||
| 	} | ||||
| 	env := common.GetEnvMap() | ||||
| 	if *serverAddr == "" { | ||||
|  | @ -50,7 +85,7 @@ func main() { | |||
| 	if *verifyKey == "" { | ||||
| 		*verifyKey, _ = env["NPC_SERVER_VKEY"] | ||||
| 	} | ||||
| 	logs.Info("the version of client is %s", version.VERSION) | ||||
| 	logs.Info("the version of client is %s, the core version of client is %s", version.VERSION, version.GetVersion()) | ||||
| 	if *verifyKey != "" && *serverAddr != "" && *configPath == "" { | ||||
| 		for { | ||||
| 			client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil).Start() | ||||
|  |  | |||
|  | @ -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":"nps_log.log","daily":false,"color":true}`) | ||||
| 		logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+beego.AppConfig.String("log_path")+`","daily":false,"color":true}`) | ||||
| 	} | ||||
| 	task := &file.Tunnel{ | ||||
| 		Mode: "webServer", | ||||
|  | @ -62,7 +62,8 @@ func main() { | |||
| 	} | ||||
| 	logs.Info("the version of server is %s ,allow client version to be %s", version.VERSION, version.GetVersion()) | ||||
| 	connection.InitConnectionService() | ||||
| 	crypt.InitTls(filepath.Join(beego.AppPath, "conf", "server.pem"), filepath.Join(beego.AppPath, "conf", "server.key")) | ||||
| 	crypt.InitTls(filepath.Join(common.GetRunPath(), "conf", "server.pem"), filepath.Join(common.GetRunPath(), "conf", "server.key")) | ||||
| 	tool.InitAllowPort() | ||||
| 	tool.StartSystemInfo() | ||||
| 	server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type")) | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,4 @@ | |||
| {"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":4,"VerifyKey":"6h7x7tjvkocgltep","Addr":"","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"admin22","WebPassword":"123","ConfigConnAllow":false,"MaxTunnelNum":0} | ||||
| *#*{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":6,"VerifyKey":"xf3nwghskyw4e7g4","Addr":"","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"admin3","WebPassword":"123","ConfigConnAllow":false,"MaxTunnelNum":0} | ||||
| *#*{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"06j3twjj9vjy2kdg","Addr":"","Remark":"","Status":true,"IsConnect":false,"RateLimit":0,"Flow":{"ExportFlow":2298489,"InletFlow":92324,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":0,"WebUserName":"admin55","WebPassword":"123","ConfigConnAllow":false,"MaxTunnelNum":0} | ||||
| *#* | ||||
|  | @ -0,0 +1,2 @@ | |||
| {"Id":1,"Host":"a.o.com","HeaderChange":"","HostChange":"","Location":"/","Remark":"","Scheme":"all","CertFilePath":"","KeyFilePath":"","NoStore":false,"IsClose":false,"Flow":{"ExportFlow":2298489,"InletFlow":92303,"FlowLimit":0},"Client":{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"06j3twjj9vjy2kdg","Addr":"127.0.0.1","Remark":"","Status":true,"IsConnect":true,"RateLimit":0,"Flow":{"ExportFlow":2298489,"InletFlow":92324,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":4,"WebUserName":"admin2","WebPassword":"123","ConfigConnAllow":false,"MaxTunnelNum":0},"Target":{"TargetStr":"127.0.0.1:8082","TargetArr":null,"LocalProxy":false}} | ||||
| *#* | ||||
|  | @ -1,5 +1,5 @@ | |||
| [common] | ||||
| server_addr=127.0.0.1:8024 | ||||
| server_addr=123.206.77.88:8024 | ||||
| conn_type=tcp | ||||
| vkey=123 | ||||
| auto_reconnection=true | ||||
|  | @ -44,7 +44,7 @@ server_port=19009 | |||
| [file] | ||||
| mode=file | ||||
| server_port=19008 | ||||
| local_path=./ | ||||
| local_path=/Users/liuhe/Downloads | ||||
| strip_pre=/web/ | ||||
| 
 | ||||
| [http] | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| appname = nps | ||||
| #Boot mode(dev|pro) | ||||
| runmode = pro | ||||
| runmode = dev | ||||
| 
 | ||||
| #HTTP(S) proxy port, no startup if empty | ||||
| http_proxy_ip=0.0.0.0 | ||||
|  | @ -26,13 +26,14 @@ public_vkey=123 | |||
| 
 | ||||
| # log level LevelEmergency->0  LevelAlert->1 LevelCritical->2 LevelError->3 LevelWarning->4 LevelNotice->5 LevelInformational->6 LevelDebug->7 | ||||
| log_level=7 | ||||
| log_path=nps.log | ||||
| 
 | ||||
| #Whether to restrict IP access, true or false or ignore | ||||
| #ip_limit=true | ||||
| 
 | ||||
| #p2p | ||||
| #p2p_ip=127.0.0.1 | ||||
| #p2p_port=6000 | ||||
| p2p_ip=127.0.0.1 | ||||
| p2p_port=6000 | ||||
| 
 | ||||
| #web | ||||
| web_host=a.o.com | ||||
|  | @ -48,12 +49,22 @@ auth_crypt_key =1234567812345678 | |||
| #allow_ports=9001-9009,10001,11000-12000 | ||||
| 
 | ||||
| #Web management multi-user login | ||||
| #allow_user_login=true | ||||
| allow_user_login=false | ||||
| allow_user_register=false | ||||
| allow_user_change_username=false | ||||
| 
 | ||||
| 
 | ||||
| #extension | ||||
| allow_flow_limit=false | ||||
| allow_rate_limit=false | ||||
| allow_tunnel_num_limit=false | ||||
| allow_local_proxy=false | ||||
| allow_connection_num_limit=false | ||||
| allow_multi_ip=false | ||||
| system_info_display=false | ||||
| 
 | ||||
| #cache | ||||
| http_cache=true | ||||
| http_cache_length=100 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| {"Id":1,"Port":0,"ServerIp":"","Mode":"p2p","Status":true,"RunStatus":true,"Client":{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"06j3twjj9vjy2kdg","Addr":"127.0.0.1","Remark":"","Status":true,"IsConnect":true,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":1,"WebUserName":"admin2","WebPassword":"123","ConfigConnAllow":false,"MaxTunnelNum":0},"Ports":"","Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Password":"p2ptest","Remark":"","TargetAddr":"","NoStore":false,"LocalPath":"","StripPre":"","Target":{"TargetStr":"","TargetArr":null,"LocalProxy":false},"HealthCheckTimeout":0,"HealthMaxFail":0,"HealthCheckInterval":0,"HealthNextTime":"0001-01-01T00:00:00Z","HealthMap":null,"HttpHealthUrl":"","HealthRemoveArr":null,"HealthCheckType":"","HealthCheckTarget":""} | ||||
| *#*{"Id":2,"Port":0,"ServerIp":"","Mode":"secret","Status":true,"RunStatus":true,"Client":{"Cnf":{"U":"","P":"","Compress":false,"Crypt":false},"Id":2,"VerifyKey":"06j3twjj9vjy2kdg","Addr":"127.0.0.1","Remark":"","Status":true,"IsConnect":true,"RateLimit":0,"Flow":{"ExportFlow":0,"InletFlow":0,"FlowLimit":0},"Rate":{"NowRate":0},"NoStore":false,"NoDisplay":false,"MaxConn":0,"NowConn":1,"WebUserName":"admin2","WebPassword":"123","ConfigConnAllow":false,"MaxTunnelNum":0},"Ports":"","Flow":{"ExportFlow":0,"InletFlow":21,"FlowLimit":0},"Password":"secrettest","Remark":"","TargetAddr":"","NoStore":false,"LocalPath":"","StripPre":"","Target":{"TargetStr":"118.89.159.126:22","TargetArr":null,"LocalProxy":false},"HealthCheckTimeout":0,"HealthMaxFail":0,"HealthCheckInterval":0,"HealthNextTime":"0001-01-01T00:00:00Z","HealthMap":null,"HttpHealthUrl":"","HealthRemoveArr":null,"HealthCheckType":"","HealthCheckTarget":""} | ||||
| *#* | ||||
|  | @ -0,0 +1,102 @@ | |||
| package cache | ||||
| 
 | ||||
| import ( | ||||
| 	"container/list" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| // Cache is an LRU cache. It is safe for concurrent access.
 | ||||
| type Cache struct { | ||||
| 	// MaxEntries is the maximum number of cache entries before
 | ||||
| 	// an item is evicted. Zero means no limit.
 | ||||
| 	MaxEntries int | ||||
| 
 | ||||
| 	//Execute this callback function when an element is culled
 | ||||
| 	OnEvicted func(key Key, value interface{}) | ||||
| 
 | ||||
| 	ll    *list.List //list
 | ||||
| 	cache sync.Map | ||||
| } | ||||
| 
 | ||||
| // A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
 | ||||
| type Key interface{} | ||||
| 
 | ||||
| type entry struct { | ||||
| 	key   Key | ||||
| 	value interface{} | ||||
| } | ||||
| 
 | ||||
| // New creates a new Cache.
 | ||||
| // If maxEntries is 0, the cache has no length limit.
 | ||||
| // that eviction is done by the caller.
 | ||||
| func New(maxEntries int) *Cache { | ||||
| 	return &Cache{ | ||||
| 		MaxEntries: maxEntries, | ||||
| 		ll:         list.New(), | ||||
| 		//cache:      make(map[interface{}]*list.Element),
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // If the key value already exists, move the key to the front
 | ||||
| func (c *Cache) Add(key Key, value interface{}) { | ||||
| 	if ee, ok := c.cache.Load(key); ok { | ||||
| 		c.ll.MoveToFront(ee.(*list.Element)) // move to the front
 | ||||
| 		ee.(*list.Element).Value.(*entry).value = value | ||||
| 		return | ||||
| 	} | ||||
| 	ele := c.ll.PushFront(&entry{key, value}) | ||||
| 	c.cache.Store(key, ele) | ||||
| 	if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { // Remove the oldest element if the limit is exceeded
 | ||||
| 		c.RemoveOldest() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Get looks up a key's value from the cache.
 | ||||
| func (c *Cache) Get(key Key) (value interface{}, ok bool) { | ||||
| 	if ele, hit := c.cache.Load(key); hit { | ||||
| 		c.ll.MoveToFront(ele.(*list.Element)) | ||||
| 		return ele.(*list.Element).Value.(*entry).value, true | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Remove removes the provided key from the cache.
 | ||||
| func (c *Cache) Remove(key Key) { | ||||
| 	if ele, hit := c.cache.Load(key); hit { | ||||
| 		c.removeElement(ele.(*list.Element)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RemoveOldest removes the oldest item from the cache.
 | ||||
| func (c *Cache) RemoveOldest() { | ||||
| 	ele := c.ll.Back() | ||||
| 	if ele != nil { | ||||
| 		c.removeElement(ele) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Cache) removeElement(e *list.Element) { | ||||
| 	c.ll.Remove(e) | ||||
| 	kv := e.Value.(*entry) | ||||
| 	c.cache.Delete(kv.key) | ||||
| 	if c.OnEvicted != nil { | ||||
| 		c.OnEvicted(kv.key, kv.value) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Len returns the number of items in the cache.
 | ||||
| func (c *Cache) Len() int { | ||||
| 	return c.ll.Len() | ||||
| } | ||||
| 
 | ||||
| // Clear purges all stored items from the cache.
 | ||||
| func (c *Cache) Clear() { | ||||
| 	if c.OnEvicted != nil { | ||||
| 		c.cache.Range(func(key, value interface{}) bool { | ||||
| 			kv := value.(*list.Element).Value.(*entry) | ||||
| 			c.OnEvicted(kv.key, kv.value) | ||||
| 			return true | ||||
| 		}) | ||||
| 	} | ||||
| 	c.ll = nil | ||||
| } | ||||
|  | @ -13,6 +13,10 @@ const ( | |||
| 	WORK_P2P          = "p2pm" | ||||
| 	WORK_P2P_VISITOR  = "p2pv" | ||||
| 	WORK_P2P_PROVIDER = "p2pp" | ||||
| 	WORK_P2P_CONNECT  = "p2pc" | ||||
| 	WORK_P2P_SUCCESS  = "p2ps" | ||||
| 	WORK_P2P_END      = "p2pe" | ||||
| 	WORK_P2P_LAST     = "p2pl" | ||||
| 	WORK_STATUS       = "stus" | ||||
| 	RES_MSG           = "msg0" | ||||
| 	RES_CLOSE         = "clse" | ||||
|  |  | |||
|  | @ -218,6 +218,7 @@ func GetPorts(p string) []int { | |||
| 	return ps | ||||
| } | ||||
| 
 | ||||
| //is the string a port
 | ||||
| func IsPort(p string) bool { | ||||
| 	pi, err := strconv.Atoi(p) | ||||
| 	if err != nil { | ||||
|  | @ -229,6 +230,7 @@ func IsPort(p string) bool { | |||
| 	return true | ||||
| } | ||||
| 
 | ||||
| //if the s is just a port,return 127.0.0.1:s
 | ||||
| func FormatAddress(s string) string { | ||||
| 	if strings.Contains(s, ":") { | ||||
| 		return s | ||||
|  | @ -236,6 +238,7 @@ func FormatAddress(s string) string { | |||
| 	return "127.0.0.1:" + s | ||||
| } | ||||
| 
 | ||||
| //get address from the complete address
 | ||||
| func GetIpByAddr(addr string) string { | ||||
| 	arr := strings.Split(addr, ":") | ||||
| 	return arr[0] | ||||
|  | @ -279,6 +282,7 @@ func GetLocalUdpAddr() (net.Conn, error) { | |||
| 	return tmpConn, tmpConn.Close() | ||||
| } | ||||
| 
 | ||||
| //parse template
 | ||||
| func ParseStr(str string) (string, error) { | ||||
| 	tmp := template.New("npc") | ||||
| 	var err error | ||||
|  | @ -305,6 +309,7 @@ func GetEnvMap() map[string]string { | |||
| 	return m | ||||
| } | ||||
| 
 | ||||
| //throw the empty element of the string array
 | ||||
| func TrimArr(arr []string) []string { | ||||
| 	newArr := make([]string, 0) | ||||
| 	for _, v := range arr { | ||||
|  | @ -315,6 +320,7 @@ func TrimArr(arr []string) []string { | |||
| 	return newArr | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
| func IsArrContains(arr []string, val string) bool { | ||||
| 	if arr == nil { | ||||
| 		return false | ||||
|  | @ -327,6 +333,7 @@ func IsArrContains(arr []string, val string) bool { | |||
| 	return false | ||||
| } | ||||
| 
 | ||||
| //remove value from string array
 | ||||
| func RemoveArrVal(arr []string, val string) []string { | ||||
| 	for k, v := range arr { | ||||
| 		if v == val { | ||||
|  | @ -337,6 +344,7 @@ func RemoveArrVal(arr []string, val string) []string { | |||
| 	return arr | ||||
| } | ||||
| 
 | ||||
| //convert bytes to num
 | ||||
| func BytesToNum(b []byte) int { | ||||
| 	var str string | ||||
| 	for i := 0; i < len(b); i++ { | ||||
|  | @ -346,6 +354,7 @@ func BytesToNum(b []byte) int { | |||
| 	return int(x) | ||||
| } | ||||
| 
 | ||||
| //get the length of the sync map
 | ||||
| func GeSynctMapLen(m sync.Map) int { | ||||
| 	var c int | ||||
| 	m.Range(func(key, value interface{}) bool { | ||||
|  | @ -354,3 +363,12 @@ func GeSynctMapLen(m sync.Map) int { | |||
| 	}) | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| func GetExtFromPath(path string) string { | ||||
| 	s := strings.Split(path, ".") | ||||
| 	re, err := regexp.Compile(`(\w+)`) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return string(re.Find([]byte(s[0]))) | ||||
| } | ||||
|  |  | |||
|  | @ -17,15 +17,17 @@ type Link struct { | |||
| 	Host       string //目标
 | ||||
| 	Crypt      bool   //加密
 | ||||
| 	Compress   bool | ||||
| 	LocalProxy bool | ||||
| 	RemoteAddr string | ||||
| } | ||||
| 
 | ||||
| func NewLink(connType string, host string, crypt bool, compress bool, remoteAddr string) *Link { | ||||
| func NewLink(connType string, host string, crypt bool, compress bool, remoteAddr string, localProxy bool) *Link { | ||||
| 	return &Link{ | ||||
| 		RemoteAddr: remoteAddr, | ||||
| 		ConnType:   connType, | ||||
| 		Host:       host, | ||||
| 		Crypt:      crypt, | ||||
| 		Compress:   compress, | ||||
| 		LocalProxy: localProxy, | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ type Client struct { | |||
| 	WebUserName     string     //the username of web login
 | ||||
| 	WebPassword     string     //the password of web login
 | ||||
| 	ConfigConnAllow bool       //is allow connected by config file
 | ||||
| 	MaxTunnelNum    int | ||||
| 	sync.RWMutex | ||||
| } | ||||
| 
 | ||||
|  | @ -97,6 +98,17 @@ func (s *Client) HasTunnel(t *Tunnel) (exist bool) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (s *Client) GetTunnelNum() (num int) { | ||||
| 	GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool { | ||||
| 		v := value.(*Tunnel) | ||||
| 		if v.Client.Id == s.Id { | ||||
| 			num++ | ||||
| 		} | ||||
| 		return true | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (s *Client) HasHost(h *Host) bool { | ||||
| 	var has bool | ||||
| 	GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { | ||||
|  | @ -164,9 +176,10 @@ type Host struct { | |||
| } | ||||
| 
 | ||||
| type Target struct { | ||||
| 	nowIndex  int | ||||
| 	TargetStr string | ||||
| 	TargetArr []string | ||||
| 	nowIndex   int | ||||
| 	TargetStr  string | ||||
| 	TargetArr  []string | ||||
| 	LocalProxy bool | ||||
| 	sync.RWMutex | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -75,9 +75,9 @@ func (s *BaseServer) CheckFlowAndConnNum(client *file.Client) error { | |||
| } | ||||
| 
 | ||||
| //与客户端建立通道
 | ||||
| func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string, f func(), flow *file.Flow) error { | ||||
| 	link := conn.NewLink(tp, addr, client.Cnf.Crypt, client.Cnf.Compress, c.Conn.RemoteAddr().String()) | ||||
| 	if target, err := s.bridge.SendLinkInfo(client.Id, link, c.Conn.RemoteAddr().String(), s.task); err != nil { | ||||
| func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string, f func(), flow *file.Flow, localProxy bool) error { | ||||
| 	link := conn.NewLink(tp, addr, client.Cnf.Crypt, client.Cnf.Compress, c.Conn.RemoteAddr().String(), localProxy) | ||||
| 	if target, err := s.bridge.SendLinkInfo(client.Id, link, s.task); err != nil { | ||||
| 		logs.Warn("get connection from client id %d  error %s", client.Id, err.Error()) | ||||
| 		c.Close() | ||||
| 		return err | ||||
|  |  | |||
|  | @ -4,11 +4,11 @@ import ( | |||
| 	"bufio" | ||||
| 	"crypto/tls" | ||||
| 	"github.com/cnlh/nps/bridge" | ||||
| 	"github.com/cnlh/nps/lib/cache" | ||||
| 	"github.com/cnlh/nps/lib/common" | ||||
| 	"github.com/cnlh/nps/lib/conn" | ||||
| 	"github.com/cnlh/nps/lib/file" | ||||
| 	"github.com/cnlh/nps/server/connection" | ||||
| 	"github.com/cnlh/nps/vender/github.com/astaxie/beego" | ||||
| 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" | ||||
| 	"io" | ||||
| 	"net" | ||||
|  | @ -17,6 +17,7 @@ import ( | |||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
|  | @ -27,12 +28,13 @@ type httpServer struct { | |||
| 	httpServer    *http.Server | ||||
| 	httpsServer   *http.Server | ||||
| 	httpsListener net.Listener | ||||
| 	useCache      bool | ||||
| 	cache         *cache.Cache | ||||
| 	cacheLen      int | ||||
| } | ||||
| 
 | ||||
| func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer { | ||||
| 	httpPort, _ := beego.AppConfig.Int("http_proxy_port") | ||||
| 	httpsPort, _ := beego.AppConfig.Int("https_proxy_port") | ||||
| 	return &httpServer{ | ||||
| func NewHttp(bridge *bridge.Bridge, c *file.Tunnel, httpPort, httpsPort int, useCache bool, cacheLen int) *httpServer { | ||||
| 	httpServer := &httpServer{ | ||||
| 		BaseServer: BaseServer{ | ||||
| 			task:   c, | ||||
| 			bridge: bridge, | ||||
|  | @ -40,7 +42,13 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer { | |||
| 		}, | ||||
| 		httpPort:  httpPort, | ||||
| 		httpsPort: httpsPort, | ||||
| 		useCache:  useCache, | ||||
| 		cacheLen:  cacheLen, | ||||
| 	} | ||||
| 	if useCache { | ||||
| 		httpServer.cache = cache.New(cacheLen) | ||||
| 	} | ||||
| 	return httpServer | ||||
| } | ||||
| 
 | ||||
| func (s *httpServer) Start() error { | ||||
|  | @ -71,7 +79,7 @@ func (s *httpServer) Start() error { | |||
| 				logs.Error(err) | ||||
| 				os.Exit(0) | ||||
| 			} | ||||
| 			logs.Error(NewHttpsServer(s.httpsListener, s.bridge).Start()) | ||||
| 			logs.Error(NewHttpsServer(s.httpsListener, s.bridge, s.useCache, s.cacheLen).Start()) | ||||
| 		}() | ||||
| 	} | ||||
| 	return nil | ||||
|  | @ -100,12 +108,12 @@ func (s *httpServer) handleTunneling(w http.ResponseWriter, r *http.Request) { | |||
| 	if err != nil { | ||||
| 		http.Error(w, err.Error(), http.StatusServiceUnavailable) | ||||
| 	} | ||||
| 	s.process(conn.NewConn(c), r) | ||||
| 	s.httpHandle(conn.NewConn(c), r) | ||||
| } | ||||
| 
 | ||||
| func (s *httpServer) process(c *conn.Conn, r *http.Request) { | ||||
| func (s *httpServer) httpHandle(c *conn.Conn, r *http.Request) { | ||||
| 	var ( | ||||
| 		isConn     = true | ||||
| 		isConn     = false | ||||
| 		host       *file.Host | ||||
| 		target     net.Conn | ||||
| 		lastHost   *file.Host | ||||
|  | @ -114,7 +122,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { | |||
| 		scheme     = r.URL.Scheme | ||||
| 		lk         *conn.Link | ||||
| 		targetAddr string | ||||
| 		wg         sync.WaitGroup | ||||
| 		readReq    bool | ||||
| 	) | ||||
| 	if host, err = file.GetDb().GetInfoByHost(r.Host, r); err != nil { | ||||
| 		logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI) | ||||
|  | @ -126,7 +134,6 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { | |||
| 		return | ||||
| 	} | ||||
| 	defer host.Client.AddConn() | ||||
| 	logs.Trace("new %s connection,clientId %d,host %s,url %s,remote address %s", r.URL.Scheme, host.Client.Id, r.Host, r.URL, r.RemoteAddr) | ||||
| 	lastHost = host | ||||
| 	for { | ||||
| 	start: | ||||
|  | @ -139,22 +146,43 @@ func (s *httpServer) process(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) | ||||
| 			if target, err = s.bridge.SendLinkInfo(host.Client.Id, lk, c.Conn.RemoteAddr().String(), nil); err != nil { | ||||
| 			lk = conn.NewLink(common.CONN_TCP, 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 | ||||
| 			} | ||||
| 			connClient = conn.GetConn(target, lk.Crypt, lk.Compress, host.Client.Rate, true) | ||||
| 			isConn = false | ||||
| 			go func() { | ||||
| 				wg.Add(1) | ||||
| 				w, _ := common.CopyBuffer(c, connClient) | ||||
| 				host.Flow.Add(0, w) | ||||
| 				c.Close() | ||||
| 				target.Close() | ||||
| 				wg.Done() | ||||
| 				defer connClient.Close() | ||||
| 				defer c.Close() | ||||
| 				if resp, err := http.ReadResponse(bufio.NewReader(connClient), r); err != nil { | ||||
| 					return | ||||
| 				} else { | ||||
| 					//if the cache is start and the response is in the extension,store the response to the cache list
 | ||||
| 					if s.useCache && strings.Contains(r.URL.Path, ".") { | ||||
| 						b, err := httputil.DumpResponse(resp, true) | ||||
| 						if err != nil { | ||||
| 							return | ||||
| 						} | ||||
| 						c.Write(b) | ||||
| 						host.Flow.Add(0, int64(len(b))) | ||||
| 						s.cache.Add(filepath.Join(host.Host, r.URL.Path), b) | ||||
| 					} else { | ||||
| 						b, err := httputil.DumpResponse(resp, false) | ||||
| 						if err != nil { | ||||
| 							return | ||||
| 						} | ||||
| 						c.Write(b) | ||||
| 						if bodyLen, err := common.CopyBuffer(c, resp.Body); err != nil { | ||||
| 							return | ||||
| 						} else { | ||||
| 							host.Flow.Add(0, int64(len(b))+bodyLen) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			}() | ||||
| 		} else { | ||||
| 		} else if readReq { | ||||
| 			r, err = http.ReadRequest(bufio.NewReader(c)) | ||||
| 			if err != nil { | ||||
| 				break | ||||
|  | @ -167,7 +195,6 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { | |||
| 			if r.Method == "OST" { | ||||
| 				r.Method = "POST" | ||||
| 			} | ||||
| 			logs.Trace("new %s connection,clientId %d,host %s,url %s,remote address %s", r.URL.Scheme, host.Client.Id, r.Host, r.URL, r.RemoteAddr) | ||||
| 			if hostTmp, err := file.GetDb().GetInfoByHost(r.Host, r); err != nil { | ||||
| 				logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI) | ||||
| 				break | ||||
|  | @ -178,13 +205,36 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { | |||
| 				goto start | ||||
| 			} | ||||
| 		} | ||||
| 		//if the cache start and the request is in the cache list, return the cache
 | ||||
| 		if s.useCache { | ||||
| 			if v, ok := s.cache.Get(filepath.Join(host.Host, r.URL.Path)); ok { | ||||
| 				n, err := c.Write(v.([]byte)) | ||||
| 				if err != nil { | ||||
| 					break | ||||
| 				} | ||||
| 				logs.Trace("%s request, method %s, host %s, url %s, remote address %s, return cache", r.URL.Scheme, r.Method, r.Host, r.URL.Path, c.RemoteAddr().String()) | ||||
| 				host.Flow.Add(0, int64(n)) | ||||
| 				//if return cache and does not create a new conn with client and Connection is not set or close, close the connection.
 | ||||
| 				if connClient == nil && (strings.ToLower(r.Header.Get("Connection")) == "close" || strings.ToLower(r.Header.Get("Connection")) == "") { | ||||
| 					c.Close() | ||||
| 					break | ||||
| 				} | ||||
| 				readReq = true | ||||
| 				goto start | ||||
| 			} | ||||
| 		} | ||||
| 		if connClient == nil { | ||||
| 			isConn = true | ||||
| 			goto start | ||||
| 		} | ||||
| 		readReq = true | ||||
| 		//change the host and header and set proxy setting
 | ||||
| 		common.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String()) | ||||
| 		b, err := httputil.DumpRequest(r, false) | ||||
| 		if err != nil { | ||||
| 			break | ||||
| 		} | ||||
| 		logs.Trace("%s request, method %s, host %s, url %s, remote address %s, target %s", r.URL.Scheme, r.Method, r.Host, r.RequestURI, r.RemoteAddr, lk.Host) | ||||
| 		logs.Trace("%s request, method %s, host %s, url %s, remote address %s, target %s", r.URL.Scheme, r.Method, r.Host, r.URL.Path, c.RemoteAddr().String(), lk.Host) | ||||
| 		//write
 | ||||
| 		connClient.Write(b) | ||||
| 		if bodyLen, err := common.CopyBuffer(connClient, r.Body); err != nil { | ||||
|  | @ -201,7 +251,6 @@ end: | |||
| 	if target != nil { | ||||
| 		target.Close() | ||||
| 	} | ||||
| 	wg.Wait() | ||||
| } | ||||
| 
 | ||||
| func (s *httpServer) NewServer(port int, scheme string) *http.Server { | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ 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" | ||||
| 	"github.com/cnlh/nps/lib/crypt" | ||||
|  | @ -21,9 +22,13 @@ type HttpsServer struct { | |||
| 	httpsListenerMap sync.Map | ||||
| } | ||||
| 
 | ||||
| func NewHttpsServer(l net.Listener, bridge *bridge.Bridge) *HttpsServer { | ||||
| func NewHttpsServer(l net.Listener, bridge *bridge.Bridge, useCache bool, cacheLen int) *HttpsServer { | ||||
| 	https := &HttpsServer{listener: l} | ||||
| 	https.bridge = bridge | ||||
| 	https.useCache = useCache | ||||
| 	if useCache { | ||||
| 		https.cache = cache.New(cacheLen) | ||||
| 	} | ||||
| 	return https | ||||
| } | ||||
| 
 | ||||
|  | @ -116,7 +121,7 @@ func (https *HttpsServer) handleHttps(c net.Conn) { | |||
| 		logs.Warn(err.Error()) | ||||
| 	} | ||||
| 	logs.Trace("new https connection,clientId %d,host %s,remote address %s", host.Client.Id, r.Host, c.RemoteAddr().String()) | ||||
| 	https.DealClient(conn.NewConn(c), host.Client, targetAddr, rb, common.CONN_TCP, nil, host.Flow) | ||||
| 	https.DealClient(conn.NewConn(c), host.Client, targetAddr, rb, common.CONN_TCP, nil, host.Flow, host.Target.LocalProxy) | ||||
| } | ||||
| 
 | ||||
| type HttpsListener struct { | ||||
|  |  | |||
|  | @ -1,8 +1,10 @@ | |||
| package proxy | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"github.com/cnlh/nps/lib/common" | ||||
| 	"github.com/cnlh/nps/lib/conn" | ||||
| 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" | ||||
| 	"net" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | @ -15,10 +17,12 @@ type P2PServer struct { | |||
| } | ||||
| 
 | ||||
| type p2p struct { | ||||
| 	provider     *conn.Conn | ||||
| 	visitor      *conn.Conn | ||||
| 	visitorAddr  string | ||||
| 	providerAddr string | ||||
| 	provider        *conn.Conn | ||||
| 	visitor         *conn.Conn | ||||
| 	visitorAddr     string | ||||
| 	providerAddr    string | ||||
| 	providerNatType int32 | ||||
| 	visitorNatType  int32 | ||||
| } | ||||
| 
 | ||||
| func NewP2PServer(p2pPort int) *P2PServer { | ||||
|  | @ -35,49 +39,57 @@ func (s *P2PServer) Start() error { | |||
| } | ||||
| 
 | ||||
| func (s *P2PServer) p2pProcess(c *conn.Conn) { | ||||
| 	//获取密钥
 | ||||
| 	var ( | ||||
| 		f   string | ||||
| 		b   []byte | ||||
| 		err error | ||||
| 		v   *p2p | ||||
| 		ok  bool | ||||
| 		f       string | ||||
| 		b       []byte | ||||
| 		err     error | ||||
| 		v       *p2p | ||||
| 		ok      bool | ||||
| 		natType int32 | ||||
| 	) | ||||
| 	if b, err = c.GetShortContent(32); err != nil { | ||||
| 		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 { | ||||
| 		v = new(p2p) | ||||
| 		s.p2p[string(b)] = 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 { | ||||
| 		logs.Warn("try visitor") | ||||
| 		v.visitorAddr = c.Conn.RemoteAddr().String() | ||||
| 		v.visitorNatType = natType | ||||
| 		v.visitor = c | ||||
| 		for { | ||||
| 			time.Sleep(time.Second) | ||||
| 		for i := 20; i > 0; i-- { | ||||
| 			if v.provider != nil { | ||||
| 				v.provider.WriteLenContent([]byte(v.visitorAddr)) | ||||
| 				binary.Write(v.provider, binary.LittleEndian, v.visitorNatType) | ||||
| 				break | ||||
| 			} | ||||
| 			time.Sleep(time.Second) | ||||
| 		} | ||||
| 		if _, err := v.provider.ReadFlag(); err == nil { | ||||
| 			v.visitor.WriteLenContent([]byte(v.providerAddr)) | ||||
| 			delete(s.p2p, string(b)) | ||||
| 		} else { | ||||
| 		} | ||||
| 		v.provider = nil | ||||
| 	} else { | ||||
| 		v.providerAddr = c.Conn.RemoteAddr().String() | ||||
| 		v.providerNatType = natType | ||||
| 		v.provider = c | ||||
| 		for { | ||||
| 			time.Sleep(time.Second) | ||||
| 		for i := 20; i > 0; i-- { | ||||
| 			if v.visitor != nil { | ||||
| 				v.provider.WriteLenContent([]byte(v.visitorAddr)) | ||||
| 				v.visitor.WriteLenContent([]byte(v.providerAddr)) | ||||
| 				binary.Write(v.visitor, binary.LittleEndian, v.providerNatType) | ||||
| 				break | ||||
| 			} | ||||
| 			time.Sleep(time.Second) | ||||
| 		} | ||||
| 		v.visitor = nil | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -142,7 +142,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) { | |||
| 	} | ||||
| 	s.DealClient(conn.NewConn(c), s.task.Client, addr, nil, ltype, func() { | ||||
| 		s.sendReply(c, succeeded) | ||||
| 	}, s.task.Flow) | ||||
| 	}, s.task.Flow, s.task.Target.LocalProxy) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -94,7 +94,7 @@ func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error { | |||
| 		logs.Warn("tcp port %d ,client id %d,task id %d connect error %s", s.task.Port, s.task.Client.Id, s.task.Id, err.Error()) | ||||
| 		return err | ||||
| 	} | ||||
| 	return s.DealClient(c, s.task.Client, targetAddr, nil, common.CONN_TCP, nil, s.task.Flow) | ||||
| 	return s.DealClient(c, s.task.Client, targetAddr, nil, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy) | ||||
| } | ||||
| 
 | ||||
| //http proxy
 | ||||
|  | @ -112,5 +112,5 @@ func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error { | |||
| 	if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP, nil, s.task.Flow) | ||||
| 	return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP, nil, s.task.Flow, s.task.Target.LocalProxy) | ||||
| } | ||||
|  |  | |||
|  | @ -54,8 +54,8 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) { | |||
| 		return | ||||
| 	} | ||||
| 	defer s.task.Client.AddConn() | ||||
| 	link := conn.NewLink(common.CONN_UDP, s.task.Target.TargetStr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String()) | ||||
| 	if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, addr.String(), s.task); err != nil { | ||||
| 	link := conn.NewLink(common.CONN_UDP, s.task.Target.TargetStr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String(), s.task.Target.LocalProxy) | ||||
| 	if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, s.task); err != nil { | ||||
| 		return | ||||
| 	} else { | ||||
| 		s.task.Flow.Add(int64(len(data)), 0) | ||||
|  |  | |||
|  | @ -70,7 +70,7 @@ func DealBridgeTask() { | |||
| 					logs.Info("Connections exceed the current client %d limit", t.Client.Id) | ||||
| 					s.Conn.Close() | ||||
| 				} else if t.Status { | ||||
| 					go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target.TargetStr, nil, common.CONN_TCP, nil, t.Flow) | ||||
| 					go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target.TargetStr, nil, common.CONN_TCP, nil, t.Flow, t.Target.LocalProxy) | ||||
| 				} else { | ||||
| 					s.Conn.Close() | ||||
| 					logs.Trace("This key %s cannot be processed,status is close", s.Password) | ||||
|  | @ -140,7 +140,11 @@ func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) proxy.Service { | |||
| 		AddTask(t) | ||||
| 		service = proxy.NewWebServer(Bridge) | ||||
| 	case "httpHostServer": | ||||
| 		service = proxy.NewHttp(Bridge, c) | ||||
| 		httpPort, _ := beego.AppConfig.Int("http_proxy_port") | ||||
| 		httpsPort, _ := beego.AppConfig.Int("https_proxy_port") | ||||
| 		useCache, _ := beego.AppConfig.Bool("http_cache") | ||||
| 		cacheLen, _ := beego.AppConfig.Int("http_cache_length") | ||||
| 		service = proxy.NewHttp(Bridge, c, httpPort, httpsPort, useCache, cacheLen) | ||||
| 	} | ||||
| 	return service | ||||
| } | ||||
|  |  | |||
|  | @ -51,10 +51,10 @@ func TestServerConfig() { | |||
| 			if port, err := strconv.Atoi(p); err != nil { | ||||
| 				log.Fatalln("get https port error", err) | ||||
| 			} else { | ||||
| 				if !common.FileExists(filepath.Join(beego.AppPath, beego.AppConfig.String("pemPath"))) { | ||||
| 				if !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("pemPath"))) { | ||||
| 					log.Fatalf("ssl certFile %s is not exist", beego.AppConfig.String("pemPath")) | ||||
| 				} | ||||
| 				if !common.FileExists(filepath.Join(beego.AppPath, beego.AppConfig.String("ketPath"))) { | ||||
| 				if !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("ketPath"))) { | ||||
| 					log.Fatalf("ssl keyFile %s is not exist", beego.AppConfig.String("pemPath")) | ||||
| 				} | ||||
| 				isInArr(&postTcpArr, port, "http port", "tcp") | ||||
|  |  | |||
|  | @ -17,9 +17,11 @@ var ( | |||
| 	ServerStatus []map[string]interface{} | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	ServerStatus = make([]map[string]interface{}, 0, 1500) | ||||
| 	go getSeverStatus() | ||||
| func StartSystemInfo() { | ||||
| 	if b, err := beego.AppConfig.Bool("system_info_display"); err == nil && b { | ||||
| 		ServerStatus = make([]map[string]interface{}, 0, 1500) | ||||
| 		go getSeverStatus() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func InitAllowPort() { | ||||
|  | @ -86,5 +88,3 @@ func getSeverStatus() { | |||
| 		ServerStatus = append(ServerStatus, m) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -49,6 +49,10 @@ func (s *BaseController) Prepare() { | |||
| 	s.Data["allow_rate_limit"], _ = beego.AppConfig.Bool("allow_rate_limit") | ||||
| 	s.Data["allow_connection_num_limit"], _ = beego.AppConfig.Bool("allow_connection_num_limit") | ||||
| 	s.Data["allow_multi_ip"], _ = beego.AppConfig.Bool("allow_multi_ip") | ||||
| 	s.Data["system_info_display"], _ = beego.AppConfig.Bool("system_info_display") | ||||
| 	s.Data["allow_tunnel_num_limit"], _ = beego.AppConfig.Bool("allow_tunnel_num_limit") | ||||
| 	s.Data["allow_local_proxy"], _ = beego.AppConfig.Bool("allow_local_proxy") | ||||
| 	s.Data["allow_user_change_username"], _ = beego.AppConfig.Bool("allow_user_change_username") | ||||
| } | ||||
| 
 | ||||
| //加载模板
 | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ func (s *ClientController) Add() { | |||
| 			MaxConn:         s.GetIntNoErr("max_conn"), | ||||
| 			WebUserName:     s.GetString("web_username"), | ||||
| 			WebPassword:     s.GetString("web_password"), | ||||
| 			MaxTunnelNum:    s.GetIntNoErr("max_tunnel"), | ||||
| 			Flow: &file.Flow{ | ||||
| 				ExportFlow: 0, | ||||
| 				InletFlow:  0, | ||||
|  | @ -116,13 +117,17 @@ func (s *ClientController) Edit() { | |||
| 				c.Flow.FlowLimit = int64(s.GetIntNoErr("flow_limit")) | ||||
| 				c.RateLimit = s.GetIntNoErr("rate_limit") | ||||
| 				c.MaxConn = s.GetIntNoErr("max_conn") | ||||
| 				c.MaxTunnelNum = s.GetIntNoErr("max_tunnel") | ||||
| 			} | ||||
| 			c.Remark = s.GetString("remark") | ||||
| 			c.Cnf.U = s.GetString("u") | ||||
| 			c.Cnf.P = s.GetString("p") | ||||
| 			c.Cnf.Compress = common.GetBoolByStr(s.GetString("compress")) | ||||
| 			c.Cnf.Crypt = s.GetBoolNoErr("crypt") | ||||
| 			c.WebUserName = s.GetString("web_username") | ||||
| 			b, err := beego.AppConfig.Bool("allow_user_change_username") | ||||
| 			if s.GetSession("isAdmin").(bool) || (err == nil && b) { | ||||
| 				c.WebUserName = s.GetString("web_username") | ||||
| 			} | ||||
| 			c.WebPassword = s.GetString("web_password") | ||||
| 			c.ConfigConnAllow = s.GetBoolNoErr("config_conn_allow") | ||||
| 			if c.Rate != nil { | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ func (s *IndexController) Add() { | |||
| 			Port:      s.GetIntNoErr("port"), | ||||
| 			ServerIp:  s.GetString("server_ip"), | ||||
| 			Mode:      s.GetString("type"), | ||||
| 			Target:    &file.Target{TargetStr: s.GetString("target")}, | ||||
| 			Target:    &file.Target{TargetStr: s.GetString("target"), LocalProxy: s.GetBoolNoErr("local_proxy")}, | ||||
| 			Id:        int(file.GetDb().JsonDb.GetTaskId()), | ||||
| 			Status:    true, | ||||
| 			Remark:    s.GetString("remark"), | ||||
|  | @ -109,6 +109,9 @@ func (s *IndexController) Add() { | |||
| 		if t.Client, err = file.GetDb().GetClient(s.GetIntNoErr("client_id")); err != nil { | ||||
| 			s.AjaxErr(err.Error()) | ||||
| 		} | ||||
| 		if t.Client.MaxTunnelNum != 0 && t.Client.GetTunnelNum() >= t.Client.MaxTunnelNum { | ||||
| 			s.AjaxErr("The number of tunnels exceeds the limit") | ||||
| 		} | ||||
| 		if err := file.GetDb().NewTask(t); err != nil { | ||||
| 			s.AjaxErr(err.Error()) | ||||
| 		} | ||||
|  | @ -166,6 +169,7 @@ func (s *IndexController) Edit() { | |||
| 			t.LocalPath = s.GetString("local_path") | ||||
| 			t.StripPre = s.GetString("strip_pre") | ||||
| 			t.Remark = s.GetString("remark") | ||||
| 			t.Target.LocalProxy = s.GetBoolNoErr("local_proxy") | ||||
| 			file.GetDb().UpdateTask(t) | ||||
| 			server.StopServer(t.Id) | ||||
| 			server.StartTask(t.Id) | ||||
|  | @ -244,7 +248,7 @@ func (s *IndexController) AddHost() { | |||
| 		h := &file.Host{ | ||||
| 			Id:           int(file.GetDb().JsonDb.GetHostId()), | ||||
| 			Host:         s.GetString("host"), | ||||
| 			Target:       &file.Target{TargetStr: s.GetString("target")}, | ||||
| 			Target:       &file.Target{TargetStr: s.GetString("target"), LocalProxy: s.GetBoolNoErr("local_proxy")}, | ||||
| 			HeaderChange: s.GetString("header"), | ||||
| 			HostChange:   s.GetString("hostchange"), | ||||
| 			Remark:       s.GetString("remark"), | ||||
|  | @ -304,6 +308,7 @@ func (s *IndexController) EditHost() { | |||
| 			h.Scheme = s.GetString("scheme") | ||||
| 			h.KeyFilePath = s.GetString("key_file_path") | ||||
| 			h.CertFilePath = s.GetString("cert_file_path") | ||||
| 			h.Target.LocalProxy = s.GetBoolNoErr("local_proxy") | ||||
| 			file.GetDb().JsonDb.StoreHostToJsonFile() | ||||
| 		} | ||||
| 		s.AjaxOk("modified success") | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ type LoginController struct { | |||
| } | ||||
| 
 | ||||
| func (self *LoginController) Index() { | ||||
| 	self.Data["register_allow"], _ = beego.AppConfig.Bool("allow_user_register") | ||||
| 	self.TplName = "login/index.html" | ||||
| } | ||||
| func (self *LoginController) Verify() { | ||||
|  | @ -55,6 +56,37 @@ func (self *LoginController) Verify() { | |||
| 	} | ||||
| 	self.ServeJSON() | ||||
| } | ||||
| func (self *LoginController) Register() { | ||||
| 	if self.Ctx.Request.Method == "GET" { | ||||
| 		self.TplName = "login/register.html" | ||||
| 	} else { | ||||
| 		if b, err := beego.AppConfig.Bool("allow_user_register"); err != nil || !b { | ||||
| 			self.Data["json"] = map[string]interface{}{"status": 0, "msg": "register is not allow"} | ||||
| 			self.ServeJSON() | ||||
| 			return | ||||
| 		} | ||||
| 		if self.GetString("username") == "" || self.GetString("password") == "" || self.GetString("username") == beego.AppConfig.String("web_username") { | ||||
| 			self.Data["json"] = map[string]interface{}{"status": 0, "msg": "please check your input"} | ||||
| 			self.ServeJSON() | ||||
| 			return | ||||
| 		} | ||||
| 		t := &file.Client{ | ||||
| 			Id:          int(file.GetDb().JsonDb.GetClientId()), | ||||
| 			Status:      true, | ||||
| 			Cnf:         &file.Config{}, | ||||
| 			WebUserName: self.GetString("username"), | ||||
| 			WebPassword: self.GetString("password"), | ||||
| 			Flow:        &file.Flow{}, | ||||
| 		} | ||||
| 		if err := file.GetDb().NewClient(t); err != nil { | ||||
| 			self.Data["json"] = map[string]interface{}{"status": 0, "msg": err.Error()} | ||||
| 		} else { | ||||
| 			self.Data["json"] = map[string]interface{}{"status": 1, "msg": "register success"} | ||||
| 		} | ||||
| 		self.ServeJSON() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (self *LoginController) Out() { | ||||
| 	self.SetSession("auth", false) | ||||
| 	self.Redirect("/login/index", 302) | ||||
|  |  | |||
|  | @ -39,6 +39,15 @@ | |||
|                                    placeholder="empty means to be unrestricted"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                 {{if eq true .allow_tunnel_num_limit}} | ||||
|                     <div class="form-group" id="max_tunnel"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-max-tunnel-num">客户端最大隧道数量</label> | ||||
|                         <div class="col-sm-10"> | ||||
|                             <input class="form-control" type="text" name="max_tunnel" | ||||
|                                    placeholder="empty means to be unrestricted"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                     <div class="form-group" id="u"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-web-auth-username">basic权限认证用户名</label> | ||||
|  |  | |||
|  | @ -44,6 +44,15 @@ | |||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                 {{if eq true .allow_tunnel_num_limit}} | ||||
|                     <div class="form-group" id="max_tunnel"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-max-tunnel-num">客户端最大隧道数量</label> | ||||
|                         <div class="col-sm-10"> | ||||
|                             <input class="form-control" value="{{.c.MaxTunnelNum}}" type="text" name="max_tunnel" | ||||
|                                    placeholder="empty means to be unrestricted"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                 {{end}} | ||||
|                     <div class="form-group" id="u"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-web-auth-username">basic权限认证用户名</label> | ||||
|  | @ -72,6 +81,7 @@ | |||
|                     </div> | ||||
|                 {{end}} | ||||
|                 {{if eq true .allow_user_login}} | ||||
|                 {{if or (eq true .allow_user_change_username) (eq true .isAdmin)}} | ||||
|                     <div class="form-group" id="web_username"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-client-web-username">web登陆用户名</label> | ||||
|                         <div class="col-sm-10"> | ||||
|  | @ -79,6 +89,7 @@ | |||
|                                    placeholder="empty means to be unrestricted"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                     <div class="form-group" id="web_password"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-client-web-password">web登陆密码</label> | ||||
|                         <div class="col-sm-10"> | ||||
|  |  | |||
|  | @ -127,6 +127,7 @@ | |||
|                     + '<b langtag="info-now-conn-num">当前连接数</b>:' + row.NowConn + `       ` | ||||
|                     + '<b langtag="info-flow-limit">流量限制</b>:' + row.Flow.FlowLimit + `m       ` | ||||
|                     + '<b langtag="info-rate-limit">带宽限制</b>:' + row.RateLimit + `kb/s       ` | ||||
|                     + '<b langtag="info-max-tunnel-num">隧道数限制</b>:' + row.MaxTunnelNum + `       ` | ||||
|                     + '<b langtag="info-client-web-username">web登陆用户名</b>:' + row.WebUserName + `       ` | ||||
|                     + '<b langtag="info-client-web-password">web登陆密码</b>:' + row.WebPassword + `       ` | ||||
|                     + `       ` + "<br/><br>" | ||||
|  |  | |||
|  | @ -43,8 +43,20 @@ | |||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                 {{if eq true .allow_local_proxy}} | ||||
|                     <div class="form-group" id="local_proxy"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-local-proxy">是否代理为服务端本地</label> | ||||
|                         <div class="col-sm-10"> | ||||
|                             <select class="form-control" name="local_proxy"> | ||||
|                                 <option value="0">no</option> | ||||
|                                 <option value="1">yes</option> | ||||
|                             </select> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
| 
 | ||||
|                     <div class="form-group" id="target"> | ||||
|                         <label class="col-sm-2 control-label" langtag="info-target">内网目标(ip:端口)</label> | ||||
|                         <label class="col-sm-2 control-label" langtag="info-target">目标(ip:端口)</label> | ||||
|                         <div class="col-sm-10"> | ||||
|                             <textarea class="form-control" name="target" rows="4" placeholder="10.1.50.203:22 | ||||
| 10.1.50.202:22"></textarea> | ||||
|  | @ -103,9 +115,9 @@ | |||
| </div> | ||||
| <script> | ||||
|     var arr = [] | ||||
|     arr["all"] = ["type", "port", "compress", "u", "p", "target", "password", "strip_pre", "local_path"] | ||||
|     arr["tcp"] = ["type", "port", "target", "compress", "u", "p", "tcp隧道模式,提供一条tcp隧道,适用于ssh、远程桌面等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的目标端口"] | ||||
|     arr["udp"] = ["type", "port", "target", "compress", "udp隧道模式,提供一条udp隧道,适用于dns、内网dns访问等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的udp目标端口"] | ||||
|     arr["all"] = ["type", "port", "compress", "u", "p", "target", "password", "strip_pre", "local_path", "local_proxy"] | ||||
|     arr["tcp"] = ["type", "port", "target", "compress", "u", "p", "local_proxy", "tcp隧道模式,提供一条tcp隧道,适用于ssh、远程桌面等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的目标端口"] | ||||
|     arr["udp"] = ["type", "port", "target", "compress", "local_proxy", "udp隧道模式,提供一条udp隧道,适用于dns、内网dns访问等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的udp目标端口"] | ||||
|     arr["socks5"] = ["type", "port", "compress", "u", "p", "socks5代理模式,内网socks5代理,配合proxifer,可如同使用vpn一样访问内网设备或资源,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置socks5代理,即访问内网设备或者资源 "] | ||||
|     arr["httpProxy"] = ["type", "port", "compress", "u", "p", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置http代理,即访问内网站点"] | ||||
|     arr["secret"] = ["type", "target", "compress", "password", "u", "p", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置http代理,即访问内网站点"] | ||||
|  |  | |||
|  | @ -44,6 +44,17 @@ | |||
|                                    placeholder="such as 8024"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{if eq true .allow_local_proxy}} | ||||
|                     <div class="form-group" id="local_proxy"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-local-proxy">是否代理为服务端本地</label> | ||||
|                         <div class="col-sm-10"> | ||||
|                             <select class="form-control" name="local_proxy"> | ||||
|                                 <option  {{if eq false .t.Target.LocalProxy}}selected{{end}} value="0">no</option> | ||||
|                                 <option  {{if eq true .t.Target.LocalProxy}}selected{{end}} value="1">yes</option> | ||||
|                             </select> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                     <div class="form-group" id="target"> | ||||
|                         <label class="col-sm-2 control-label" langtag="info-target">内网目标(ip:端口)</label> | ||||
|                         <div class="col-sm-10"> | ||||
|  | @ -103,9 +114,9 @@ | |||
| </div> | ||||
| <script> | ||||
|     var arr = [] | ||||
|     arr["all"] = ["type", "port", "compress", "u", "p", "target", "password", "local_path", "strip_pre"] | ||||
|     arr["tcp"] = ["type", "port", "target", "u", "p", "compress"] | ||||
|     arr["udp"] = ["type", "port", "target", "compress"] | ||||
|     arr["all"] = ["type", "port", "compress", "u", "p", "target", "password", "local_path", "strip_pre", "local_proxy"] | ||||
|     arr["tcp"] = ["type", "port", "target", "u", "p", "compress", "local_proxy"] | ||||
|     arr["udp"] = ["type", "port", "target", "compress", "local_proxy"] | ||||
|     arr["socks5"] = ["type", "port", "compress", "u", "p"] | ||||
|     arr["httpProxy"] = ["type", "port", "compress", "u", "p"] | ||||
|     arr["secret"] = ["type", "target", "compress", "u", "p", "password"] | ||||
|  |  | |||
|  | @ -56,6 +56,17 @@ | |||
|                                    placeholder="client id"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{if eq true .allow_local_proxy}} | ||||
|                     <div class="form-group" id="local_proxy"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-local-proxy">是否代理为服务端本地</label> | ||||
|                         <div class="col-sm-10"> | ||||
|                             <select class="form-control" name="local_proxy"> | ||||
|                                 <option value="0">no</option> | ||||
|                                 <option value="1">yes</option> | ||||
|                             </select> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                     <div class="form-group"> | ||||
|                         <label class="col-sm-2 control-label" langtag="info-target">内网目标(ip:端口)</label> | ||||
|                         <div class="col-sm-10"> | ||||
|  |  | |||
|  | @ -59,6 +59,17 @@ | |||
|                                    placeholder="client id"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{if eq true .allow_local_proxy}} | ||||
|                     <div class="form-group" id="local_proxy"> | ||||
|                         <label class="control-label col-sm-2" langtag="info-local-proxy">是否代理为服务端本地</label> | ||||
|                         <div class="col-sm-10"> | ||||
|                             <select class="form-control" name="local_proxy"> | ||||
|                                 <option  {{if eq false .h.Target.LocalProxy}}selected{{end}} value="0">no</option> | ||||
|                                 <option  {{if eq true .h.Target.LocalProxy}}selected{{end}} value="1">yes</option> | ||||
|                             </select> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 {{end}} | ||||
|                     <div class="form-group"> | ||||
|                         <label class="col-sm-2 control-label" langtag="info-target">内网目标(ip:端口)</label> | ||||
|                         <div class="col-sm-10"> | ||||
|  |  | |||
|  | @ -242,7 +242,7 @@ | |||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
| 
 | ||||
| {{if eq true .system_info_display}} | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-6"> | ||||
|             <div class="ibox float-e-margins"> | ||||
|  | @ -340,6 +340,7 @@ | |||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| {{end}} | ||||
| 
 | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-6"> | ||||
|  |  | |||
|  | @ -63,12 +63,19 @@ | |||
|         detailView: true, | ||||
|         smartDisplay: true, // 智能显示 pagination 和 cardview 等
 | ||||
|         detailFormatter: function (index, row, element) { | ||||
|             return '<b langtag="info-export-flow">出口流量</b>:' + change(row.Flow.ExportFlow) + `       ` | ||||
|                     + '<b langtag="info-inlet-flow">入口流量</b>:' + change(row.Flow.InletFlow) + `       ` + "<br/><br>" | ||||
|             tmp = '<b langtag="info-export-flow">出口流量</b>:' + change(row.Flow.ExportFlow) + `       ` | ||||
|                     + '<b langtag="info-inlet-flow">入口流量</b>:' + change(row.Flow.InletFlow) + `       ` | ||||
|                     + '<b langtag="info-crypt">加密</b>:' + row.Client.Cnf.Crypt + `       ` | ||||
|                     + '<b langtag="info-compress">压缩</b>:' + row.Client.Cnf.Compress + `       ` | ||||
|                     + '<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=your target" + "</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>" | ||||
|             } | ||||
|             return tmp | ||||
|         }, | ||||
|         //表格的列
 | ||||
|         columns: [ | ||||
|  | @ -112,13 +119,13 @@ | |||
|                 field: 'Password',//域值
 | ||||
|                 title: 'secret',//标题
 | ||||
|                 visible: true,//false表示不显示
 | ||||
|                  | ||||
| 
 | ||||
|             }, | ||||
|             { | ||||
|                 field: 'Status',//域值
 | ||||
|                 title: 'setting',//内容
 | ||||
|                 visible: true,//false表示不显示
 | ||||
|                  | ||||
| 
 | ||||
|                 formatter: function (value, row, index) { | ||||
|                     if (value) { | ||||
|                         return '<span class="badge badge-primary">open</span>' | ||||
|  | @ -131,7 +138,7 @@ | |||
|                 field: 'RunStatus',//域值
 | ||||
|                 title: 'run',//内容
 | ||||
|                 visible: true,//false表示不显示
 | ||||
|                  | ||||
| 
 | ||||
|                 formatter: function (value, row, index) { | ||||
|                     if (value) { | ||||
|                         return '<span class="badge badge-primary">open</span>' | ||||
|  | @ -144,7 +151,7 @@ | |||
|                 field: '',//域值
 | ||||
|                 title: 'client',//内容
 | ||||
|                 visible: true,//false表示不显示
 | ||||
|                  | ||||
| 
 | ||||
|                 formatter: function (value, row, index) { | ||||
|                     if (row.Client.IsConnect) { | ||||
|                         return '<span class="badge badge-primary">online</span>' | ||||
|  | @ -157,7 +164,7 @@ | |||
|                 field: 'option',//域值
 | ||||
|                 title: 'option',//内容
 | ||||
|                 visible: true,//false表示不显示
 | ||||
|                  | ||||
| 
 | ||||
|                 formatter: function (value, row, index) { | ||||
|                     btn_group = '<div class="btn-group">' | ||||
|                     btn = `<button onclick="del(` + row.Id + `)" class="btn-danger"><i class="fa fa-trash"></i></button><button onclick="edit(` + row.Id + `)" class="btn-primary"><i class="fa fa-edit"></i></button></div>` | ||||
|  |  | |||
|  | @ -48,6 +48,9 @@ | |||
|                         <input name="password" type="password" class="form-control" placeholder="password" required=""> | ||||
|                     </div> | ||||
|                     <button onclick="login()" class="btn btn-primary block full-width m-b">login</button> | ||||
|                 {{if eq true .register_allow}} | ||||
|                     <a class="btn btn-sm btn-white btn-block" href="/login/register">register</a> | ||||
|                 {{end}} | ||||
|                 </form> | ||||
|             </div> | ||||
|         </div> | ||||
|  |  | |||
|  | @ -0,0 +1,61 @@ | |||
| <!DOCTYPE html> | ||||
| <html> | ||||
| 
 | ||||
| <head> | ||||
| 
 | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
| 
 | ||||
|     <title>nps register</title> | ||||
| 
 | ||||
|     <link href="/static/css/bootstrap.min.css" rel="stylesheet"> | ||||
|     <link href="/static/font-awesome/css/font-awesome.css" rel="stylesheet"> | ||||
| 
 | ||||
|     <link href="/static/css/style.css" rel="stylesheet"> | ||||
| 
 | ||||
| </head> | ||||
| 
 | ||||
| <body class="gray-bg"> | ||||
| 
 | ||||
| <div class="middle-box text-center loginscreen   animated fadeInDown"> | ||||
|     <div> | ||||
|         <div> | ||||
| 
 | ||||
|             <h1 class="logo-name">NPS</h1> | ||||
| 
 | ||||
|         </div> | ||||
|         <h3>register NPS</h3> | ||||
|         <form class="m-t" role="form" onsubmit="return false"> | ||||
|             <div class="form-group"> | ||||
|                 <input type="text" class="form-control" placeholder="username" name="username" required=""> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                 <input type="password" class="form-control" placeholder="password" name="password" required=""> | ||||
|             </div> | ||||
|             <button onclick="register()" type="submit" class="btn btn-primary block full-width m-b">register</button> | ||||
| 
 | ||||
|             <a class="btn btn-sm btn-white btn-block" href="/login/index">login</a> | ||||
|         </form> | ||||
|     </div> | ||||
| </div> | ||||
| 
 | ||||
| <script src="/static/js/jquery-2.1.1.js"></script> | ||||
| <script> | ||||
|     function register() { | ||||
|         $.ajax({ | ||||
|             type: "POST", | ||||
|             url: "/login/register", | ||||
|             data: $("form").serializeArray(), | ||||
|             success: function (res) { | ||||
|                 alert(res.msg) | ||||
|                 if (res.status) { | ||||
|                     window.location.href = "/login/index" | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         return false | ||||
|     } | ||||
| </script> | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
		Loading…
	
		Reference in New Issue
	
	 刘河
						刘河