diff --git a/bridge/bridge.go b/bridge/bridge.go index b42e32d..283ac6a 100755 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -162,7 +162,7 @@ func (s *Bridge) verifySuccess(c *conn.Conn) { func (s *Bridge) cliProcess(c *conn.Conn) { //read test flag if _, err := c.GetShortContent(3); err != nil { - logs.Info("The client %s connect error", c.Conn.RemoteAddr()) + logs.Info("The client %s connect error", c.Conn.RemoteAddr(), err.Error()) return } //version check @@ -173,7 +173,7 @@ func (s *Bridge) cliProcess(c *conn.Conn) { } //write server version to client c.Write([]byte(crypt.Md5(version.GetVersion()))) - c.SetReadDeadline(5, s.tunnelType) + c.SetReadDeadlineByType(5, s.tunnelType) var buf []byte var err error //get vKey from client diff --git a/client/control.go b/client/control.go index b8420cf..36a56f3 100644 --- a/client/control.go +++ b/client/control.go @@ -1,6 +1,7 @@ package client import ( + "encoding/base64" "encoding/binary" "errors" "github.com/cnlh/nps/lib/common" @@ -14,6 +15,8 @@ import ( "io/ioutil" "log" "net" + "net/http" + "net/http/httputil" "net/url" "os" "path/filepath" @@ -180,11 +183,16 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st if er != nil { return nil, er } - n, er := proxy.FromURL(u, nil) - if er != nil { - return nil, er + switch u.Scheme { + case "socks5": + n, er := proxy.FromURL(u, nil) + if er != nil { + return nil, er + } + connection, err = n.Dial("tcp", server) + case "http": + connection, err = NewHttpProxyConn(u, server) } - connection, err = n.Dial("tcp", server) } else { connection, err = net.Dial("tcp", server) } @@ -230,3 +238,36 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st return c, nil } + +func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) { + req := &http.Request{ + Method: "CONNECT", + URL: url, + Host: remoteAddr, + Header: http.Header{}, + Proto: "HTTP/1.1", + } + password, _ := url.User.Password() + req.Header.Set("Proxy-Authorization", "Basic "+basicAuth(url.User.Username(), password)) + b, err := httputil.DumpRequest(req, false) + if err != nil { + return nil, err + } + proxyConn, err := net.Dial("tcp", url.Host) + if err != nil { + return nil, err + } + if _, err := proxyConn.Write(b); err != nil { + return nil, err + } + buf := make([]byte, 1024) + if _, err := proxyConn.Read(buf); err != nil { + return nil, err + } + return proxyConn, nil +} + +func basicAuth(username, password string) string { + auth := username + ":" + password + return base64.StdEncoding.EncodeToString([]byte(auth)) +} diff --git a/conf/nps.conf b/conf/nps.conf index 6ff9258..1269f0d 100755 --- a/conf/nps.conf +++ b/conf/nps.conf @@ -7,10 +7,6 @@ http_proxy_port=80 https_proxy_port=443 https_just_proxy=true http_proxy_ip=0.0.0.0 -#certFile absolute path -#pem_path=conf/server.pem -#KeyFile absolute path -#key_path=conf/server.key ##bridge bridge_type=tcp diff --git a/lib/config/config.go b/lib/config/config.go index d9e7f4c..d771b2e 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -130,7 +130,7 @@ func dealCommon(s string) *CommonConfig { c.Cnf.Compress = common.GetBoolByStr(item[1]) case "crypt": c.Cnf.Crypt = common.GetBoolByStr(item[1]) - case "proxy_socks5_url": + case "proxy_url": c.ProxyUrl = item[1] case "rate_limit": c.Client.RateLimit = common.GetIntNoErrByStr(item[1]) diff --git a/lib/conn/conn.go b/lib/conn/conn.go index 38458d4..7763dbc 100755 --- a/lib/conn/conn.go +++ b/lib/conn/conn.go @@ -25,6 +25,7 @@ import ( type Conn struct { Conn net.Conn + Rb []byte } //new conn @@ -83,6 +84,26 @@ func (s *Conn) GetShortContent(l int) (b []byte, err error) { return buf, binary.Read(s, binary.LittleEndian, &buf) } +func (s *Conn) LocalAddr() net.Addr { + return s.Conn.LocalAddr() +} + +func (s *Conn) RemoteAddr() net.Addr { + return s.Conn.RemoteAddr() +} + +func (s *Conn) SetDeadline(t time.Time) error { + return s.Conn.SetDeadline(t) +} + +func (s *Conn) SetWriteDeadline(t time.Time) error { + return s.Conn.SetWriteDeadline(t) +} + +func (s *Conn) SetReadDeadline(t time.Time) error { + return s.Conn.SetReadDeadline(t) +} + //读取指定长度内容 func (s *Conn) ReadLen(cLen int, buf []byte) (int, error) { if cLen > len(buf) { @@ -130,7 +151,7 @@ func (s *Conn) SetAlive(tp string) { } //set read deadline -func (s *Conn) SetReadDeadline(t time.Duration, tp string) { +func (s *Conn) SetReadDeadlineByType(t time.Duration, tp string) { switch s.Conn.(type) { case *kcp.UDPSession: s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second)) @@ -340,7 +361,15 @@ func (s *Conn) Write(b []byte) (int, error) { } //read -func (s *Conn) Read(b []byte) (int, error) { +func (s *Conn) Read(b []byte) (n int, err error) { + if s.Rb != nil { + if len(s.Rb) > 0 { + n = copy(b, s.Rb) + s.Rb = s.Rb[n:] + return + } + s.Rb = nil + } return s.Conn.Read(b) } diff --git a/lib/crypt/clientHello.go b/lib/crypt/clientHello.go new file mode 100644 index 0000000..3b5a272 --- /dev/null +++ b/lib/crypt/clientHello.go @@ -0,0 +1,253 @@ +package crypt + +import ( + "strings" +) + +type CurveID uint16 +type SignatureScheme uint16 + +const ( + statusTypeOCSP uint8 = 1 + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 + extensionSupportedPoints uint16 = 11 + extensionSignatureAlgorithms uint16 = 13 + extensionALPN uint16 = 16 + extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6 + extensionSessionTicket uint16 = 35 + extensionNextProtoNeg uint16 = 13172 // not IANA assigned + extensionRenegotiationInfo uint16 = 0xff01 + scsvRenegotiation uint16 = 0x00ff +) + +type ClientHelloMsg struct { + raw []byte + vers uint16 + random []byte + sessionId []byte + cipherSuites []uint16 + compressionMethods []uint8 + nextProtoNeg bool + serverName string + ocspStapling bool + scts bool + supportedCurves []CurveID + supportedPoints []uint8 + ticketSupported bool + sessionTicket []uint8 + supportedSignatureAlgorithms []SignatureScheme + secureRenegotiation []byte + secureRenegotiationSupported bool + alpnProtocols []string +} + +func (m *ClientHelloMsg) GetServerName() string { + return m.serverName +} + +func (m *ClientHelloMsg) Unmarshal(data []byte) bool { + if len(data) < 42 { + return false + } + m.raw = data + m.vers = uint16(data[4])<<8 | uint16(data[5]) + m.random = data[6:38] + sessionIdLen := int(data[38]) + if sessionIdLen > 32 || len(data) < 39+sessionIdLen { + return false + } + m.sessionId = data[39 : 39+sessionIdLen] + data = data[39+sessionIdLen:] + if len(data) < 2 { + return false + } + // cipherSuiteLen is the number of bytes of cipher suite numbers. Since + // they are uint16s, the number must be even. + cipherSuiteLen := int(data[0])<<8 | int(data[1]) + if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { + return false + } + numCipherSuites := cipherSuiteLen / 2 + m.cipherSuites = make([]uint16, numCipherSuites) + for i := 0; i < numCipherSuites; i++ { + m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i]) + if m.cipherSuites[i] == scsvRenegotiation { + m.secureRenegotiationSupported = true + } + } + data = data[2+cipherSuiteLen:] + if len(data) < 1 { + return false + } + compressionMethodsLen := int(data[0]) + if len(data) < 1+compressionMethodsLen { + return false + } + m.compressionMethods = data[1 : 1+compressionMethodsLen] + data = data[1+compressionMethodsLen:] + + m.nextProtoNeg = false + m.serverName = "" + m.ocspStapling = false + m.ticketSupported = false + m.sessionTicket = nil + m.supportedSignatureAlgorithms = nil + m.alpnProtocols = nil + m.scts = false + + if len(data) == 0 { + // ClientHello is optionally followed by extension data + return true + } + if len(data) < 2 { + return false + } + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if extensionsLength != len(data) { + return false + } + + for len(data) != 0 { + if len(data) < 4 { + return false + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return false + } + + switch extension { + case extensionServerName: + d := data[:length] + if len(d) < 2 { + return false + } + namesLen := int(d[0])<<8 | int(d[1]) + d = d[2:] + if len(d) != namesLen { + return false + } + for len(d) > 0 { + if len(d) < 3 { + return false + } + nameType := d[0] + nameLen := int(d[1])<<8 | int(d[2]) + d = d[3:] + if len(d) < nameLen { + return false + } + if nameType == 0 { + m.serverName = string(d[:nameLen]) + // An SNI value may not include a + // trailing dot. See + // https://tools.ietf.org/html/rfc6066#section-3. + if strings.HasSuffix(m.serverName, ".") { + return false + } + break + } + d = d[nameLen:] + } + case extensionNextProtoNeg: + if length > 0 { + return false + } + m.nextProtoNeg = true + case extensionStatusRequest: + m.ocspStapling = length > 0 && data[0] == statusTypeOCSP + case extensionSupportedCurves: + // https://tools.ietf.org/html/rfc4492#section-5.5.1 + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l%2 == 1 || length != l+2 { + return false + } + numCurves := l / 2 + m.supportedCurves = make([]CurveID, numCurves) + d := data[2:] + for i := 0; i < numCurves; i++ { + m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1]) + d = d[2:] + } + case extensionSupportedPoints: + // https://tools.ietf.org/html/rfc4492#section-5.5.2 + if length < 1 { + return false + } + l := int(data[0]) + if length != l+1 { + return false + } + m.supportedPoints = make([]uint8, l) + copy(m.supportedPoints, data[1:]) + case extensionSessionTicket: + // https://tools.ietf.org/html/rfc5077#section-3.2 + m.ticketSupported = true + m.sessionTicket = data[:length] + case extensionSignatureAlgorithms: + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + if length < 2 || length&1 != 0 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 { + return false + } + n := l / 2 + d := data[2:] + m.supportedSignatureAlgorithms = make([]SignatureScheme, n) + for i := range m.supportedSignatureAlgorithms { + m.supportedSignatureAlgorithms[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1]) + d = d[2:] + } + case extensionRenegotiationInfo: + if length == 0 { + return false + } + d := data[:length] + l := int(d[0]) + d = d[1:] + if l != len(d) { + return false + } + + m.secureRenegotiation = d + m.secureRenegotiationSupported = true + case extensionALPN: + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 { + return false + } + d := data[2:length] + for len(d) != 0 { + stringLen := int(d[0]) + d = d[1:] + if stringLen == 0 || stringLen > len(d) { + return false + } + m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen])) + d = d[stringLen:] + } + case extensionSCT: + m.scts = true + if length != 0 { + return false + } + } + data = data[length:] + } + + return true +} diff --git a/lib/file/obj.go b/lib/file/obj.go index 6a21a40..cbd916d 100644 --- a/lib/file/obj.go +++ b/lib/file/obj.go @@ -152,6 +152,8 @@ type Host struct { Location string //url router Remark string //remark Scheme string //http https all + CertFilePath string + KeyFilePath string NoStore bool IsClose bool Flow *Flow diff --git a/lib/version/version.go b/lib/version/version.go index 115bce9..edd78ef 100644 --- a/lib/version/version.go +++ b/lib/version/version.go @@ -1,8 +1,8 @@ package version -const VERSION = "0.20.2" +const VERSION = "0.21.0" // Compulsory minimum version, Minimum downward compatibility to this version func GetVersion() string { - return "0.19.0" + return "0.21.0" } diff --git a/server/proxy/http.go b/server/proxy/http.go index c2d5d19..95834b4 100644 --- a/server/proxy/http.go +++ b/server/proxy/http.go @@ -2,7 +2,6 @@ package proxy import ( "bufio" - "bytes" "crypto/tls" "github.com/cnlh/nps/bridge" "github.com/cnlh/nps/lib/common" @@ -15,7 +14,6 @@ import ( "net" "net/http" "net/http/httputil" - "net/url" "os" "path/filepath" "strconv" @@ -24,10 +22,8 @@ import ( type httpServer struct { BaseServer - httpPort int //http端口 - httpsPort int //https监听端口 - pemPath string - keyPath string + httpPort int + httpsPort int httpServer *http.Server httpsServer *http.Server httpsListener net.Listener @@ -36,8 +32,6 @@ type httpServer struct { func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer { httpPort, _ := beego.AppConfig.Int("http_proxy_port") httpsPort, _ := beego.AppConfig.Int("https_proxy_port") - pemPath := beego.AppConfig.String("pem_path") - keyPath := beego.AppConfig.String("key_path") return &httpServer{ BaseServer: BaseServer{ task: c, @@ -46,57 +40,9 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer { }, httpPort: httpPort, httpsPort: httpsPort, - pemPath: pemPath, - keyPath: keyPath, } } -func (s *httpServer) processHttps(c net.Conn) { - buf := make([]byte, 2048) - n, err := c.Read(buf) - if err != nil { - return - } - var host *file.Host - file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool { - v := value.(*file.Host) - if v.Scheme != "https" && v.Scheme != "all" { - return true - } - if bytes.Index(buf[:n], []byte(v.Host)) >= 0 && (host == nil || len(host.Host) < len(v.Host)) { - host = v - return false - } - return true - }) - if host == nil { - logs.Error("new https connection can't be parsed!", c.RemoteAddr().String()) - c.Close() - return - } - var targetAddr string - r := new(http.Request) - r.RequestURI = "/" - r.URL = new(url.URL) - r.URL.Scheme = "https" - r.Host = host.Host - if err := s.CheckFlowAndConnNum(host.Client); err != nil { - logs.Warn("client id %d, host id %d, error %s, when https connection", host.Client.Id, host.Id, err.Error()) - c.Close() - return - } - defer host.Client.AddConn() - if err = s.auth(r, conn.NewConn(c), host.Client.Cnf.U, host.Client.Cnf.P); err != nil { - logs.Warn("auth error", err, r.RemoteAddr) - return - } - if targetAddr, err = host.Target.GetRandomTarget(); err != nil { - logs.Warn(err.Error()) - } - logs.Trace("new https connection,clientId %d,host %s,remote address %s", host.Client.Id, r.Host, c.RemoteAddr().String()) - s.DealClient(conn.NewConn(c), host.Client, targetAddr, buf[:n], common.CONN_TCP, nil, host.Flow) -} - func (s *httpServer) Start() error { var err error if s.errorContent, err = common.ReadAllFromFile(filepath.Join(common.GetRunPath(), "web", "static", "page", "error.html")); err != nil { @@ -125,30 +71,7 @@ func (s *httpServer) Start() error { logs.Error(err) os.Exit(0) } - if b, err := beego.AppConfig.Bool("https_just_proxy"); err == nil && b { - for { - c, err := s.httpsListener.Accept() - if err != nil { - logs.Error(err) - break - } - go s.processHttps(c) - } - } else { - if !common.FileExists(s.pemPath) { - logs.Error("ssl certFile %s exist", s.keyPath) - os.Exit(0) - } - if !common.FileExists(s.keyPath) { - logs.Error("ssl keyFile %s exist", s.keyPath) - os.Exit(0) - } - err = s.httpsServer.ServeTLS(s.httpsListener, s.pemPath, s.keyPath) - if err != nil { - logs.Error(err) - os.Exit(0) - } - } + logs.Error(NewHttpsServer(s.httpsListener, s.bridge).Start()) }() } return nil @@ -255,7 +178,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { goto start } } - //根据设定,修改header和host + //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 { diff --git a/server/proxy/https.go b/server/proxy/https.go new file mode 100644 index 0000000..d2c160a --- /dev/null +++ b/server/proxy/https.go @@ -0,0 +1,147 @@ +package proxy + +import ( + "github.com/cnlh/nps/bridge" + "github.com/cnlh/nps/lib/common" + "github.com/cnlh/nps/lib/conn" + "github.com/cnlh/nps/lib/crypt" + "github.com/cnlh/nps/lib/file" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" + "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" + "github.com/pkg/errors" + "net" + "net/http" + "net/url" + "sync" +) + +type HttpsServer struct { + httpServer + listener net.Listener + httpsListenerMap sync.Map +} + +func NewHttpsServer(l net.Listener, bridge *bridge.Bridge) *HttpsServer { + https := &HttpsServer{listener: l} + https.bridge = bridge + return https +} + +func (https *HttpsServer) Start() error { + if b, err := beego.AppConfig.Bool("https_just_proxy"); err == nil && b { + conn.Accept(https.listener, func(c net.Conn) { + https.handleHttps(c) + }) + } else { + conn.Accept(https.listener, func(c net.Conn) { + serverName, rb := GetServerNameFromClientHello(c) + var l *HttpsListener + if v, ok := https.httpsListenerMap.Load(serverName); ok { + l = v.(*HttpsListener) + } else { + r := new(http.Request) + r.RequestURI = "/" + r.URL = new(url.URL) + r.URL.Scheme = "https" + if host, err := file.GetDb().GetInfoByHost(serverName, r); err != nil { + c.Close() + logs.Notice("the url %s can't be parsed!", serverName) + return + } else { + if !common.FileExists(host.CertFilePath) || !common.FileExists(host.KeyFilePath) { + c.Close() + logs.Error("the key %s cert %s file is not exist", host.KeyFilePath, host.CertFilePath) + return + } + l = NewHttpsListener(https.listener) + https.NewHttps(l, host.CertFilePath, host.KeyFilePath) + https.httpsListenerMap.Store(serverName, l) + } + } + acceptConn := conn.NewConn(c) + acceptConn.Rb = rb + l.acceptConn <- acceptConn + }) + } + return nil +} + +func (https *HttpsServer) Close() error { + return https.listener.Close() +} + +func (https *HttpsServer) NewHttps(l net.Listener, certFile string, keyFile string) { + go func() { + logs.Error(https.NewServer(0, "https").ServeTLS(l, certFile, keyFile)) + }() +} + +func (https *HttpsServer) handleHttps(c net.Conn) { + hostName, rb := GetServerNameFromClientHello(c) + var targetAddr string + r := new(http.Request) + r.RequestURI = "/" + r.URL = new(url.URL) + r.URL.Scheme = "https" + r.Host = hostName + var host *file.Host + var err error + if host, err = file.GetDb().GetInfoByHost(hostName, r); err != nil { + c.Close() + logs.Notice("the url %s can't be parsed!", hostName) + return + } + if err := https.CheckFlowAndConnNum(host.Client); err != nil { + logs.Warn("client id %d, host id %d, error %s, when https connection", host.Client.Id, host.Id, err.Error()) + c.Close() + return + } + defer host.Client.AddConn() + if err = https.auth(r, conn.NewConn(c), host.Client.Cnf.U, host.Client.Cnf.P); err != nil { + logs.Warn("auth error", err, r.RemoteAddr) + return + } + if targetAddr, err = host.Target.GetRandomTarget(); err != nil { + 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) +} + +type HttpsListener struct { + acceptConn chan *conn.Conn + parentListener net.Listener +} + +func NewHttpsListener(l net.Listener) *HttpsListener { + return &HttpsListener{parentListener: l, acceptConn: make(chan *conn.Conn)} +} + +func (httpsListener *HttpsListener) Accept() (net.Conn, error) { + httpsConn := <-httpsListener.acceptConn + if httpsConn == nil { + return nil, errors.New("get connection error") + } + return httpsConn, nil +} + +func (httpsListener *HttpsListener) Close() error { + return nil +} + +func (httpsListener *HttpsListener) Addr() net.Addr { + return httpsListener.parentListener.Addr() +} + +func GetServerNameFromClientHello(c net.Conn) (string, []byte) { + buf := make([]byte, 4096) + data := make([]byte, 4096) + n, err := c.Read(buf) + if err != nil { + return "", nil + } + copy(data, buf[:n]) + clientHello := new(crypt.ClientHelloMsg) + clientHello.Unmarshal(data[5:n]) + return clientHello.GetServerName(), buf[:n] +} diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go index 55973d6..cb275f0 100755 --- a/server/proxy/tcp.go +++ b/server/proxy/tcp.go @@ -86,7 +86,7 @@ func NewWebServer(bridge *bridge.Bridge) *WebServer { type process func(c *conn.Conn, s *TunnelModeServer) error -//tcp隧道模式 +//tcp proxy func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error { targetAddr, err := s.task.Target.GetRandomTarget() if err != nil { @@ -97,7 +97,7 @@ func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error { return s.DealClient(c, s.task.Client, targetAddr, nil, common.CONN_TCP, nil, s.task.Flow) } -//http代理模式 +//http proxy func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error { _, addr, rb, err, r := c.GetHost() if err != nil { diff --git a/web/controllers/base.go b/web/controllers/base.go index 907b359..18ab0a9 100755 --- a/web/controllers/base.go +++ b/web/controllers/base.go @@ -43,6 +43,7 @@ func (s *BaseController) Prepare() { } else { s.Data["isAdmin"] = true } + s.Data["https_just_proxy"], _ = beego.AppConfig.Bool("https_just_proxy") } //加载模板 diff --git a/web/controllers/index.go b/web/controllers/index.go index 7a24443..651f8b2 100755 --- a/web/controllers/index.go +++ b/web/controllers/index.go @@ -251,10 +251,12 @@ func (s *IndexController) AddHost() { Location: s.GetString("location"), Flow: &file.Flow{}, Scheme: s.GetString("scheme"), + KeyFilePath: s.GetString("key_file_path"), + CertFilePath: s.GetString("cert_file_path"), } var err error if h.Client, err = file.GetDb().GetClient(s.GetIntNoErr("client_id")); err != nil { - s.AjaxErr("add error") + s.AjaxErr("add error the client can not be found") } if err := file.GetDb().NewHost(h); err != nil { s.AjaxErr("add fail" + err.Error()) @@ -300,6 +302,8 @@ func (s *IndexController) EditHost() { h.Remark = s.GetString("remark") h.Location = s.GetString("location") h.Scheme = s.GetString("scheme") + h.KeyFilePath = s.GetString("key_file_path") + h.CertFilePath = s.GetString("cert_file_path") file.GetDb().JsonDb.StoreHostToJsonFile() } s.AjaxOk("modified success") diff --git a/web/views/index/hadd.html b/web/views/index/hadd.html index f51d747..1e5ef25 100755 --- a/web/views/index/hadd.html +++ b/web/views/index/hadd.html @@ -19,13 +19,29 @@
-
+ {{if eq false .https_just_proxy}} +
+ +
+ +
+
+
+ +
+ +
+
+ {{end}}
@@ -60,7 +76,7 @@
-
+
\ No newline at end of file diff --git a/web/views/index/hedit.html b/web/views/index/hedit.html index 679c597..10e9840 100644 --- a/web/views/index/hedit.html +++ b/web/views/index/hedit.html @@ -22,13 +22,29 @@
-
+ {{if eq false .https_just_proxy}} +
+ +
+ +
+
+
+ +
+ +
+
+ {{end}}
@@ -99,5 +115,15 @@ } }) }) + $("#scheme_select").on("change", function () { + if ($("#scheme_select").val() == "all" || $("#scheme_select").val() == "https") { + $("#cert_file").css("display", "block") + $("#key_file").css("display", "block") + } else { + $("#cert_file").css("display", "none") + $("#key_file").css("display", "none") + } + }) }) + \ No newline at end of file diff --git a/web/views/index/hlist.html b/web/views/index/hlist.html index a6b6b85..6d2ccaa 100755 --- a/web/views/index/hlist.html +++ b/web/views/index/hlist.html @@ -68,6 +68,8 @@ + '压缩:' + row.Client.Cnf.Compress + `       ` + "

" + 'basic权限认证用户名:' + row.Client.Cnf.U + `       ` + 'basic权限认证密码:' + row.Client.Cnf.P + `       ` + "

" + + 'cert file路径:' + row.CertFilePath + `       ` + + 'key file路径:' + row.KeyFilePath + `       ` + "

" + 'request header修改:' + row.HeaderChange + `       ` + "

" + 'request host 修改:' + row.HostChange + `       ` }, @@ -77,13 +79,13 @@ field: 'Id',//域值 title: 'id',//标题 visible: true,//false表示不显示 - + }, { field: 'Id',//域值 title: 'client id',//标题 visible: true,//false表示不显示 - + formatter: function (value, row, index) { return row.Client.Id } @@ -92,25 +94,25 @@ field: 'Remark',//域值 title: 'remark',//标题 visible: true,//false表示不显示 - + }, { field: 'Host',//域值 title: 'host',//标题 visible: true,//false表示不显示 - + }, { field: 'Scheme',//域值 title: 'scheme',//标题 visible: true,//false表示不显示 - + }, { field: 'Target',//域值 title: 'target',//标题 visible: true,//false表示不显示 - + formatter: function (value, row, index) { return row.Target.TargetStr } @@ -119,13 +121,13 @@ field: 'Location',//域值 title: 'location',//标题 visible: true,//false表示不显示 - + }, { field: '',//域值 title: 'client status',//内容 visible: true,//false表示不显示 - + formatter: function (value, row, index) { if (row.Client.IsConnect) { return 'online' @@ -138,7 +140,7 @@ field: 'option',//域值 title: 'option',//内容 visible: true,//false表示不显示 - + formatter: function (value, row, index) { btn_group = '
' btn = `
`