mirror of
https://github.com/ehang-io/nps.git
synced 2025-09-01 02:46:52 +00:00
Multiple HTTPS certificate support
This commit is contained in:
@@ -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 {
|
||||
|
147
server/proxy/https.go
Normal file
147
server/proxy/https.go
Normal file
@@ -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]
|
||||
}
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user