Https kcp

This commit is contained in:
刘河 2019-03-19 22:41:40 +08:00
parent 7ec3e82b0f
commit a66b465046
13 changed files with 129 additions and 74 deletions

View File

@ -16,6 +16,7 @@ import (
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp" "github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"net" "net"
"os"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -70,15 +71,14 @@ func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList map[int
func (s *Bridge) StartTunnel() error { func (s *Bridge) StartTunnel() error {
go s.ping() go s.ping()
l, err := connection.GetBridgeListener(s.tunnelType)
if err != nil {
return err
}
if s.tunnelType == "kcp" { if s.tunnelType == "kcp" {
listener, ok := l.(*kcp.Listener) listener, err := kcp.ListenWithOptions(beego.AppConfig.String("bridge_ip")+":"+beego.AppConfig.String("bridge_port"), nil, 150, 3)
if !ok { if err != nil {
logs.Error(err)
os.Exit(0)
return err return err
} }
logs.Info("server start, the bridge type is %s, the bridge port is %d", s.tunnelType, s.TunnelPort)
go func() { go func() {
for { for {
c, err := listener.AcceptKCP() c, err := listener.AcceptKCP()
@ -91,8 +91,10 @@ func (s *Bridge) StartTunnel() error {
} }
}() }()
} else { } else {
listener, ok := l.(net.Listener) listener, err := connection.GetBridgeListener(s.tunnelType)
if !ok { if err != nil {
logs.Error(err)
os.Exit(0)
return err return err
} }
go func() { go func() {
@ -253,10 +255,10 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
if v, ok := s.Client[id]; ok { if v, ok := s.Client[id]; ok {
s.clientLock.Unlock() s.clientLock.Unlock()
v.Lock() v.Lock()
v.tunnel = mux.NewMux(c.Conn) v.tunnel = mux.NewMux(c.Conn, s.tunnelType)
v.Unlock() v.Unlock()
} else { } else {
s.Client[id] = NewClient(mux.NewMux(c.Conn), nil, nil) s.Client[id] = NewClient(mux.NewMux(c.Conn, s.tunnelType), nil, nil)
s.clientLock.Unlock() s.clientLock.Unlock()
} }
case common.WORK_CONFIG: case common.WORK_CONFIG:
@ -282,10 +284,10 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
if v, ok := s.Client[id]; ok { if v, ok := s.Client[id]; ok {
s.clientLock.Unlock() s.clientLock.Unlock()
v.Lock() v.Lock()
v.file = mux.NewMux(c.Conn) v.file = mux.NewMux(c.Conn, s.tunnelType)
v.Unlock() v.Unlock()
} else { } else {
s.Client[id] = NewClient(nil, mux.NewMux(c.Conn), nil) s.Client[id] = NewClient(nil, mux.NewMux(c.Conn, s.tunnelType), nil)
s.clientLock.Unlock() s.clientLock.Unlock()
} }
case common.WORK_P2P: case common.WORK_P2P:

View File

@ -15,11 +15,11 @@ import (
type TRPClient struct { type TRPClient struct {
svrAddr string svrAddr string
bridgeConnType string bridgeConnType string
stop chan bool
proxyUrl string proxyUrl string
vKey string vKey string
tunnel *mux.Mux tunnel *mux.Mux
signal *conn.Conn signal *conn.Conn
ticker *time.Ticker
cnf *config.Config cnf *config.Config
} }
@ -29,7 +29,6 @@ func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl st
svrAddr: svraddr, svrAddr: svraddr,
vKey: vKey, vKey: vKey,
bridgeConnType: bridgeConnType, bridgeConnType: bridgeConnType,
stop: make(chan bool),
proxyUrl: proxyUrl, proxyUrl: proxyUrl,
cnf: cnf, cnf: cnf,
} }
@ -51,8 +50,9 @@ retry:
} }
func (s *TRPClient) Close() { func (s *TRPClient) Close() {
s.stop <- true s.tunnel.Close()
s.signal.Close() s.signal.Close()
s.ticker.Stop()
} }
//处理 //处理
@ -168,7 +168,7 @@ func (s *TRPClient) newUdpConn(rAddr string, md5Password string) {
if udpTunnel.RemoteAddr().String() == string(b) { if udpTunnel.RemoteAddr().String() == string(b) {
conn.SetUdpSession(udpTunnel) conn.SetUdpSession(udpTunnel)
//读取link,设置msgCh 设置msgConn消息回传响应机制 //读取link,设置msgCh 设置msgConn消息回传响应机制
l := mux.NewMux(udpTunnel) l := mux.NewMux(udpTunnel, s.bridgeConnType)
for { for {
connMux, err := l.Accept() connMux, err := l.Accept()
if err != nil { if err != nil {
@ -187,18 +187,16 @@ func (s *TRPClient) dealChan() {
logs.Error("connect to ", s.svrAddr, "error:", err) logs.Error("connect to ", s.svrAddr, "error:", err)
return return
} }
go func() { s.tunnel = mux.NewMux(tunnel.Conn, s.bridgeConnType)
s.tunnel = mux.NewMux(tunnel.Conn) for {
for { src, err := s.tunnel.Accept()
src, err := s.tunnel.Accept() if err != nil {
if err != nil { logs.Warn(err)
logs.Warn(err) s.Close()
break break
}
go s.srcProcess(src)
} }
}() go s.srcProcess(src)
<-s.stop }
} }
func (s *TRPClient) srcProcess(src net.Conn) { func (s *TRPClient) srcProcess(src net.Conn) {
@ -221,14 +219,14 @@ func (s *TRPClient) srcProcess(src net.Conn) {
} }
func (s *TRPClient) ping() { func (s *TRPClient) ping() {
ticker := time.NewTicker(time.Second * 5) s.ticker = time.NewTicker(time.Second * 5)
loop: loop:
for { for {
select { select {
case <-ticker.C: case <-s.ticker.C:
if s.tunnel.IsClose { if s.tunnel.IsClose {
s.Close() s.Close()
ticker.Stop() s.ticker.Stop()
break loop break loop
} }
} }

View File

@ -39,7 +39,7 @@ 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) 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) fileServer = append(fileServer, srv)
listener := mux.NewMux(remoteConn.Conn) listener := mux.NewMux(remoteConn.Conn, common.CONN_TCP)
logs.Warn(srv.Serve(listener)) logs.Warn(srv.Serve(listener))
} }
@ -88,7 +88,7 @@ func processP2P(localTcpConn net.Conn, config *config.CommonConfig, l *config.Lo
if udpConn == nil { if udpConn == nil {
return return
} }
muxSession = mux.NewMux(udpConn) muxSession = mux.NewMux(udpConn, "kcp")
} }
nowConn, err := muxSession.NewConn() nowConn, err := muxSession.NewConn()
if err != nil { if err != nil {

View File

@ -6,7 +6,8 @@ runmode = dev
#HTTP(S) proxy port, no startup if empty #HTTP(S) proxy port, no startup if empty
http_proxy_port=80 http_proxy_port=80
#https_proxy_port=8024 #https_proxy_port=443
https_just_proxy=true
#certFile absolute path #certFile absolute path
pem_path=conf/server.pem pem_path=conf/server.pem
#KeyFile absolute path #KeyFile absolute path

View File

@ -335,3 +335,12 @@ func RemoveArrVal(arr []string, val string) []string {
} }
return arr return arr
} }
func BytesToNum(b []byte) int {
var str string
for i := 0; i < len(b); i++ {
str += strconv.Itoa(int(b[i]))
}
x, _ := strconv.Atoi(str)
return int(x)
}

View File

@ -561,7 +561,7 @@ func (s *Csv) GetInfoByHost(host string, r *http.Request) (h *Host, err error) {
v.Location = "/" v.Location = "/"
} }
if strings.Index(r.RequestURI, v.Location) == 0 { if strings.Index(r.RequestURI, v.Location) == 0 {
if h == nil || (len(v.Location) > len(h.Location)) { if h == nil || (len(v.Location) < len(h.Location)) {
h = v h = v
} }
} }

View File

@ -5,7 +5,6 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"github.com/cnlh/nps/lib/pool" "github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"math" "math"
"net" "net"
"sync" "sync"
@ -33,10 +32,12 @@ type Mux struct {
id int32 id int32
closeChan chan struct{} closeChan chan struct{}
IsClose bool IsClose bool
pingOk int
connType string
sync.Mutex sync.Mutex
} }
func NewMux(c net.Conn) *Mux { func NewMux(c net.Conn, connType string) *Mux {
m := &Mux{ m := &Mux{
conn: c, conn: c,
connMap: NewConnMap(), connMap: NewConnMap(),
@ -44,6 +45,7 @@ func NewMux(c net.Conn) *Mux {
closeChan: make(chan struct{}), closeChan: make(chan struct{}),
newConnCh: make(chan *conn), newConnCh: make(chan *conn),
IsClose: false, IsClose: false,
connType: connType,
} }
//read session by flag //read session by flag
go m.readSession() go m.readSession()
@ -85,7 +87,11 @@ func (s *Mux) Accept() (net.Conn, error) {
if s.IsClose { if s.IsClose {
return nil, errors.New("accpet error,the conn has closed") return nil, errors.New("accpet error,the conn has closed")
} }
return <-s.newConnCh, nil conn := <-s.newConnCh
if conn == nil {
return nil, errors.New("accpet error,the conn has closed")
}
return conn, nil
} }
func (s *Mux) Addr() net.Addr { func (s *Mux) Addr() net.Addr {
@ -118,11 +124,11 @@ func (s *Mux) ping() {
if (math.MaxInt32 - s.id) < 10000 { if (math.MaxInt32 - s.id) < 10000 {
s.id = 0 s.id = 0
} }
if err := s.sendInfo(MUX_PING_FLAG, MUX_PING, nil); err != nil { if err := s.sendInfo(MUX_PING_FLAG, MUX_PING, nil); err != nil || (s.pingOk > 10 && s.connType == "kcp") {
logs.Error("ping error,close the connection")
s.Close() s.Close()
break break
} }
s.pingOk++
} }
}() }()
select { select {
@ -141,6 +147,7 @@ func (s *Mux) readSession() {
if binary.Read(s.conn, binary.LittleEndian, &i) != nil { if binary.Read(s.conn, binary.LittleEndian, &i) != nil {
break break
} }
s.pingOk = 0
switch flag { switch flag {
case MUX_NEW_CONN: //new conn case MUX_NEW_CONN: //new conn
conn := NewConn(i, s) conn := NewConn(i, s)
@ -187,7 +194,6 @@ func (s *Mux) readSession() {
pool.PutBufPoolCopy(buf) pool.PutBufPoolCopy(buf)
} }
} else { } else {
logs.Error("read or send error")
break break
} }
} }
@ -210,6 +216,7 @@ func (s *Mux) Close() error {
select { select {
case s.closeChan <- struct{}{}: case s.closeChan <- struct{}{}:
} }
close(s.newConnCh)
return s.conn.Close() return s.conn.Close()
} }

View File

@ -88,7 +88,7 @@ func (pMux *PortMux) process(conn net.Conn) {
var ch chan *PortConn var ch chan *PortConn
var rs []byte var rs []byte
var buffer bytes.Buffer var buffer bytes.Buffer
switch bytesToNum(buf) { switch common.BytesToNum(buf) {
case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager
buffer.Reset() buffer.Reset()
r := bufio.NewReader(conn) r := bufio.NewReader(conn)
@ -161,12 +161,3 @@ func (pMux *PortMux) GetHttpsListener() net.Listener {
func (pMux *PortMux) GetManagerListener() net.Listener { func (pMux *PortMux) GetManagerListener() net.Listener {
return NewPortListener(pMux.managerConn, pMux.Listener.Addr()) return NewPortListener(pMux.managerConn, pMux.Listener.Addr())
} }
func bytesToNum(b []byte) int {
var str string
for i := 0; i < len(b); i++ {
str += strconv.Itoa(int(b[i]))
}
x, _ := strconv.Atoi(str)
return int(x)
}

View File

@ -4,7 +4,6 @@ import (
"github.com/cnlh/nps/lib/mux" "github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/vender/github.com/astaxie/beego" "github.com/cnlh/nps/vender/github.com/astaxie/beego"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"net" "net"
"os" "os"
"strconv" "strconv"
@ -32,7 +31,7 @@ func InitConnectionService() {
} }
} }
func GetBridgeListener(tp string) (interface{}, error) { func GetBridgeListener(tp string) (net.Listener, error) {
logs.Info("server start, the bridge type is %s, the bridge port is %s", tp, bridgePort) logs.Info("server start, the bridge type is %s, the bridge port is %s", tp, bridgePort)
var p int var p int
var err error var err error
@ -41,13 +40,6 @@ func GetBridgeListener(tp string) (interface{}, error) {
} }
if pMux != nil { if pMux != nil {
return pMux.GetClientListener(), nil return pMux.GetClientListener(), nil
} else if tp == "udp" {
if p, err = beego.AppConfig.Int("bridge_port"); err != nil {
logs.Error(err)
os.Exit(0)
} else {
return kcp.ListenWithOptions(":"+strconv.Itoa(p), nil, 150, 3)
}
} }
return net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP(beego.AppConfig.String("bridge_ip")), p, ""}) return net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP(beego.AppConfig.String("bridge_ip")), p, ""})
} }

View File

@ -72,21 +72,21 @@ func (s *BaseServer) checkFlow() error {
} }
//与客户端建立通道 //与客户端建立通道
func (s *BaseServer) DealClient(c *conn.Conn, addr string, rb []byte, tp string) error { func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string) error {
link := conn.NewLink(tp, addr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, c.Conn.RemoteAddr().String()) link := conn.NewLink(tp, addr, client.Cnf.Crypt, client.Cnf.Compress, c.Conn.RemoteAddr().String())
if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.Conn.RemoteAddr().String(), s.task); err != nil { if target, err := s.bridge.SendLinkInfo(client.Id, link, c.Conn.RemoteAddr().String(), s.task); err != nil {
logs.Warn("task id %d get connection from client id %d error %s", s.task.Id, s.task.Client.Id, err.Error()) logs.Warn("task id %d get connection from client id %d error %s", s.task.Id, client.Id, err.Error())
c.Close() c.Close()
return err return err
} else { } else {
if rb != nil { if rb != nil {
//HTTP proxy crypt or compress //HTTP proxy crypt or compress
conn.GetConn(target, link.Crypt, link.Compress, s.task.Client.Rate, true).Write(rb) conn.GetConn(target, link.Crypt, link.Compress, client.Rate, true).Write(rb)
} }
conn.CopyWaitGroup(target, c.Conn, link.Crypt, link.Compress, s.task.Client.Rate, s.task.Flow, true) conn.CopyWaitGroup(target, c.Conn, link.Crypt, link.Compress, client.Rate, s.task.Flow, true)
} }
s.task.Client.AddConn() client.AddConn()
return nil return nil
} }

View File

@ -2,6 +2,7 @@ package proxy
import ( import (
"bufio" "bufio"
"bytes"
"crypto/tls" "crypto/tls"
"github.com/cnlh/nps/bridge" "github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
@ -14,6 +15,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -49,6 +51,49 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel) *httpServer {
} }
} }
func (s *httpServer) processHttps(c net.Conn) {
buf := make([]byte, 2<<10)
n, err := c.Read(buf)
if err != nil {
return
}
var host *file.Host
file.GetCsvDb().Lock()
for _, host = range file.GetCsvDb().Hosts {
if bytes.Index(buf[:n], []byte(host.Host)) >= 0 {
break
}
}
file.GetCsvDb().Unlock()
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
//read the host form connection
if !host.Client.GetConn() { //conn num limit
logs.Notice("connections exceed the current client %d limit %d ,now connection num %d", host.Client.Id, host.Client.MaxConn, host.Client.NowConn)
c.Close()
return
}
//流量限制
if host.Client.Flow.FlowLimit > 0 && (host.Client.Flow.FlowLimit<<20) < (host.Client.Flow.ExportFlow+host.Client.Flow.InletFlow) {
logs.Warn("Traffic exceeded client id %s", host.Client.Id)
return
}
if targetAddr, err = host.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)
}
func (s *httpServer) Start() error { func (s *httpServer) Start() error {
var err error var err error
var httpSrv, httpsSrv *http.Server var httpSrv, httpsSrv *http.Server
@ -81,16 +126,26 @@ func (s *httpServer) Start() error {
} }
httpsSrv = s.NewServer(s.httpsPort, "https") httpsSrv = s.NewServer(s.httpsPort, "https")
go func() { go func() {
logs.Info("Start https listener, port is", s.httpsPort)
l, err := connection.GetHttpsListener() l, err := connection.GetHttpsListener()
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
os.Exit(0) os.Exit(0)
} }
err = httpsSrv.ServeTLS(l, s.pemPath, s.keyPath) if b, err := beego.AppConfig.Bool("https_just_proxy"); err == nil && b {
if err != nil { for {
logs.Error(err) c, err := l.Accept()
os.Exit(0) if err != nil {
logs.Error(err)
break
}
go s.processHttps(c)
}
} else {
err = httpsSrv.ServeTLS(l, s.pemPath, s.keyPath)
if err != nil {
logs.Error(err)
os.Exit(0)
}
} }
}() }()
} }

View File

@ -115,7 +115,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()) 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 err
} }
return s.DealClient(c, targetAddr, nil, common.CONN_TCP) return s.DealClient(c, s.task.Client, targetAddr, nil, common.CONN_TCP)
} }
//http代理模式 //http代理模式
@ -133,5 +133,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 { if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil {
return err return err
} }
return s.DealClient(c, addr, rb, common.CONN_TCP) return s.DealClient(c, s.task.Client, addr, rb, common.CONN_TCP)
} }

View File

@ -66,7 +66,7 @@ func DealBridgeTask() {
logs.Info("Connections exceed the current client %d limit", t.Client.Id) logs.Info("Connections exceed the current client %d limit", t.Client.Id)
s.Conn.Close() s.Conn.Close()
} else if t.Status { } else if t.Status {
go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Target, nil, common.CONN_TCP) go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target, nil, common.CONN_TCP)
} else { } else {
s.Conn.Close() s.Conn.Close()
logs.Trace("This key %s cannot be processed,status is close", s.Password) logs.Trace("This key %s cannot be processed,status is close", s.Password)