Port mux| https|tls crypt

This commit is contained in:
刘河
2019-03-05 09:23:18 +08:00
parent a29a7d4923
commit f81fb7760e
39 changed files with 706 additions and 251 deletions

View File

@@ -22,6 +22,7 @@ const (
NEW_HOST = "host"
CONN_TCP = "tcp"
CONN_UDP = "udp"
CONN_TEST = "TST"
UnauthorizedBytes = `HTTP/1.1 401 Unauthorized
Content-Type: text/plain; charset=utf-8
WWW-Authenticate: Basic realm="easyProxy"

View File

@@ -7,7 +7,9 @@ import (
"errors"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/lib/rate"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
@@ -119,46 +121,31 @@ func (s *Conn) ReadFlag() (string, error) {
//设置连接为长连接
func (s *Conn) SetAlive(tp string) {
if tp == "kcp" {
s.setKcpAlive()
} else {
s.setTcpAlive()
switch s.Conn.(type) {
case *kcp.UDPSession:
s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Time{})
case *net.TCPConn:
conn := s.Conn.(*net.TCPConn)
conn.SetReadDeadline(time.Time{})
conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
case *mux.PortConn:
s.Conn.(*mux.PortConn).SetReadDeadline(time.Time{})
}
}
//设置连接为长连接
func (s *Conn) setTcpAlive() {
conn := s.Conn.(*net.TCPConn)
conn.SetReadDeadline(time.Time{})
conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
}
//设置连接为长连接
func (s *Conn) setKcpAlive() {
conn := s.Conn.(*kcp.UDPSession)
conn.SetReadDeadline(time.Time{})
}
//设置连接为长连接
func (s *Conn) SetReadDeadline(t time.Duration, tp string) {
if tp == "kcp" {
s.SetKcpReadDeadline(t)
} else {
s.SetTcpReadDeadline(t)
switch s.Conn.(type) {
case *kcp.UDPSession:
s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
case *net.TCPConn:
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
case *mux.PortConn:
s.Conn.(*mux.PortConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
}
}
//set read dead time
func (s *Conn) SetTcpReadDeadline(t time.Duration) {
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
}
//set read dead time
func (s *Conn) SetKcpReadDeadline(t time.Duration) {
s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
}
//send info for link
func (s *Conn) SendLinkInfo(link *Link) (int, error) {
raw := bytes.NewBuffer([]byte{})
@@ -402,19 +389,19 @@ func SetUdpSession(sess *kcp.UDPSession) {
}
//conn1 mux conn
func CopyWaitGroup(conn1, conn2 io.ReadWriteCloser, crypt bool, snappy bool, rate *rate.Rate, flow *file.Flow) {
func CopyWaitGroup(conn1, conn2 net.Conn, crypt bool, snappy bool, rate *rate.Rate, flow *file.Flow, isServer bool) {
var in, out int64
var wg sync.WaitGroup
conn1 = GetConn(conn1, crypt, snappy, rate)
connHandle := GetConn(conn1, crypt, snappy, rate, isServer)
go func(in *int64) {
wg.Add(1)
*in, _ = common.CopyBuffer(conn1, conn2)
conn1.Close()
*in, _ = common.CopyBuffer(connHandle, conn2)
connHandle.Close()
conn2.Close()
wg.Done()
}(&in)
out, _ = common.CopyBuffer(conn2, conn1)
conn1.Close()
out, _ = common.CopyBuffer(conn2, connHandle)
connHandle.Close()
conn2.Close()
wg.Wait()
if flow != nil {
@@ -423,11 +410,14 @@ func CopyWaitGroup(conn1, conn2 io.ReadWriteCloser, crypt bool, snappy bool, rat
}
//get crypt or snappy conn
func GetConn(conn io.ReadWriteCloser, crypt, snappy bool, rate *rate.Rate) (io.ReadWriteCloser) {
if crypt {
conn = NewCryptConn(conn, true, rate)
func GetConn(conn net.Conn, cpt, snappy bool, rate *rate.Rate, isServer bool) (io.ReadWriteCloser) {
if cpt {
if isServer {
return crypt.NewTlsServerConn(conn)
}
return crypt.NewTlsClientConn(conn)
} else if snappy {
conn = NewSnappyConn(conn, crypt, rate)
return NewSnappyConn(conn, cpt, rate)
}
return conn
}

View File

@@ -1,72 +0,0 @@
package conn
import (
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/pool"
"github.com/cnlh/nps/lib/rate"
"io"
)
type CryptConn struct {
conn io.ReadWriteCloser
crypt bool
rate *rate.Rate
}
func NewCryptConn(conn io.ReadWriteCloser, crypt bool, rate *rate.Rate) *CryptConn {
c := new(CryptConn)
c.conn = conn
c.crypt = crypt
c.rate = rate
return c
}
//加密写
func (s *CryptConn) Write(b []byte) (n int, err error) {
n = len(b)
if s.crypt {
if b, err = crypt.AesEncrypt(b, []byte(cryptKey)); err != nil {
return
}
}
if b, err = GetLenBytes(b); err != nil {
return
}
_, err = s.conn.Write(b)
if s.rate != nil {
s.rate.Get(int64(n))
}
return
}
//解密读
func (s *CryptConn) Read(b []byte) (n int, err error) {
var lens int
var buf []byte
var rb []byte
if lens, err = GetLen(s.conn); err != nil || lens > len(b) || lens < 0 {
return
}
buf = pool.BufPool.Get().([]byte)
defer pool.BufPool.Put(buf)
if n, err = io.ReadFull(s.conn, buf[:lens]); err != nil {
return
}
if s.crypt {
if rb, err = crypt.AesDecrypt(buf[:lens], []byte(cryptKey)); err != nil {
return
}
} else {
rb = buf[:lens]
}
copy(b, rb)
n = len(rb)
if s.rate != nil {
s.rate.Get(int64(n))
}
return
}
func (s *CryptConn) Close() error {
return s.conn.Close()
}

28
lib/crypt/tls.go Normal file
View File

@@ -0,0 +1,28 @@
package crypt
import (
"crypto/tls"
"github.com/cnlh/nps/vender/github.com/astaxie/beego"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net"
"os"
"path/filepath"
)
func NewTlsServerConn(conn net.Conn) net.Conn {
cert, err := tls.LoadX509KeyPair(filepath.Join(beego.AppPath, "conf", "server.pem"), filepath.Join(beego.AppPath, "conf", "server.key"))
if err != nil {
logs.Error(err)
os.Exit(0)
return nil
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
return tls.Server(conn, config)
}
func NewTlsClientConn(conn net.Conn) net.Conn {
conf := &tls.Config{
InsecureSkipVerify: true,
}
return tls.Client(conn, conf)
}

View File

@@ -225,6 +225,7 @@ func (s *Csv) StoreHostToCsv() {
strconv.Itoa(host.Id),
strconv.Itoa(int(host.Flow.ExportFlow)),
strconv.Itoa(int(host.Flow.InletFlow)),
host.Scheme,
}
err1 := writer.Write(record)
if err1 != nil {
@@ -298,6 +299,11 @@ func (s *Csv) LoadHostFromCsv() {
post.Flow = new(Flow)
post.Flow.ExportFlow = int64(common.GetIntNoErrByStr(item[8]))
post.Flow.InletFlow = int64(common.GetIntNoErrByStr(item[9]))
if len(item) > 10 {
post.Scheme = item[10]
} else {
post.Scheme = "all"
}
hosts = append(hosts, post)
if post.Id > s.HostIncreaseId {
s.HostIncreaseId = post.Id
@@ -319,7 +325,7 @@ func (s *Csv) DelHost(id int) error {
func (s *Csv) IsHostExist(h *Host) bool {
for _, v := range s.Hosts {
if v.Host == h.Host && h.Location == v.Location {
if v.Host == h.Host && h.Location == v.Location && (v.Scheme == "all" || v.Scheme == h.Scheme) {
return true
}
}
@@ -497,7 +503,7 @@ func (s *Csv) GetInfoByHost(host string, r *http.Request) (h *Host, err error) {
if re, err = regexp.Compile(tmp); err != nil {
return
}
if len(re.FindAllString(host, -1)) > 0 {
if len(re.FindAllString(host, -1)) > 0 && (v.Scheme == "all" || v.Scheme == r.URL.Scheme) {
//URL routing
hosts = append(hosts, v)
}

View File

@@ -133,6 +133,7 @@ type Host struct {
NowIndex int
TargetArr []string
NoStore bool
Scheme string //http https all
sync.RWMutex
}

63
lib/mux/pconn.go Normal file
View File

@@ -0,0 +1,63 @@
package mux
import (
"net"
"time"
)
type PortConn struct {
Conn net.Conn
rs []byte
start int
}
func newPortConn(conn net.Conn, rs []byte) *PortConn {
return &PortConn{
Conn: conn,
rs: rs,
}
}
func (pConn *PortConn) Read(b []byte) (n int, err error) {
if len(b) < len(pConn.rs)-pConn.start {
defer func() {
pConn.start = pConn.start + len(b)
}()
return copy(b, pConn.rs), nil
}
if pConn.start < len(pConn.rs) {
defer func() {
pConn.start = len(pConn.rs)
}()
return copy(b, pConn.rs[pConn.start:]), nil
}
return pConn.Conn.Read(b)
}
func (pConn *PortConn) Write(b []byte) (n int, err error) {
return pConn.Conn.Write(b)
}
func (pConn *PortConn) Close() error {
return pConn.Conn.Close()
}
func (pConn *PortConn) LocalAddr() net.Addr {
return pConn.Conn.LocalAddr()
}
func (pConn *PortConn) RemoteAddr() net.Addr {
return pConn.Conn.RemoteAddr()
}
func (pConn *PortConn) SetDeadline(t time.Time) error {
return pConn.Conn.SetDeadline(t)
}
func (pConn *PortConn) SetReadDeadline(t time.Time) error {
return pConn.Conn.SetReadDeadline(t)
}
func (pConn *PortConn) SetWriteDeadline(t time.Time) error {
return pConn.Conn.SetWriteDeadline(t)
}

44
lib/mux/plistener.go Normal file
View File

@@ -0,0 +1,44 @@
package mux
import (
"errors"
"net"
)
type PortListener struct {
net.Listener
connCh chan *PortConn
addr net.Addr
isClose bool
}
func NewPortListener(connCh chan *PortConn, addr net.Addr) *PortListener {
return &PortListener{
connCh: connCh,
addr: addr,
}
}
func (pListener *PortListener) Accept() (net.Conn, error) {
if pListener.isClose {
return nil, errors.New("the listener has closed")
}
conn := <-pListener.connCh
if conn != nil {
return conn, nil
}
return nil, errors.New("the listener has closed")
}
func (pListener *PortListener) Close() error {
//close
if pListener.isClose {
return errors.New("the listener has closed")
}
pListener.isClose = true
return nil
}
func (pListener *PortListener) Addr() net.Addr {
return pListener.addr
}

163
lib/mux/pmux.go Normal file
View File

@@ -0,0 +1,163 @@
// This module is used for port reuse
// Distinguish client, web manager , HTTP and HTTPS according to the difference of protocol
package mux
import (
"bufio"
"bytes"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/pkg/errors"
"io"
"net"
"strconv"
"strings"
)
const (
HTTP_GET = 716984
HTTP_POST = 807983
HTTP_HEAD = 726965
HTTP_PUT = 808585
HTTP_DELETE = 686976
HTTP_CONNECT = 677978
HTTP_OPTIONS = 798084
HTTP_TRACE = 848265
CLIENT = 848384
)
type PortMux struct {
net.Listener
port int
isClose bool
managerHost string
clientConn chan *PortConn
httpConn chan *PortConn
httpsConn chan *PortConn
managerConn chan *PortConn
}
func NewPortMux(port int, managerHost string) *PortMux {
pMux := &PortMux{
managerHost: managerHost,
port: port,
clientConn: make(chan *PortConn),
httpConn: make(chan *PortConn),
httpsConn: make(chan *PortConn),
managerConn: make(chan *PortConn),
}
pMux.Start()
return pMux
}
func (pMux *PortMux) Start() error {
// Port multiplexing is based on TCP only
tcpAddr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+strconv.Itoa(pMux.port))
if err != nil {
return err
}
pMux.Listener, err = net.ListenTCP("tcp", tcpAddr)
if err != nil {
return err
}
go func() {
for {
conn, err := pMux.Listener.Accept()
if err != nil {
logs.Warn(err)
//close
pMux.Close()
}
go pMux.process(conn)
}
}()
return nil
}
func (pMux *PortMux) process(conn net.Conn) {
// Recognition according to different signs
// read 3 byte
buf := make([]byte, 3)
if n, err := io.ReadFull(conn, buf); err != nil || n != 3 {
return
}
var ch chan *PortConn
var rs []byte
var buffer bytes.Buffer
switch bytesToNum(buf) {
case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager
buffer.Reset()
r := bufio.NewReader(conn)
buffer.Write(buf)
for {
b, _, err := r.ReadLine()
if err != nil {
logs.Warn("read line error", err.Error())
conn.Close()
break
}
buffer.Write(b)
buffer.Write([]byte("\r\n"))
if strings.Index(string(b), "Host:") == 0 || strings.Index(string(b), "host:") == 0 {
// Remove host and space effects
str := strings.Replace(string(b), "Host:", "", -1)
str = strings.Replace(str, "host:", "", -1)
str = strings.TrimSpace(str)
// Determine whether it is the same as the manager domain name
if str == pMux.managerHost {
ch = pMux.managerConn
} else {
ch = pMux.httpConn
}
b, _ := r.Peek(r.Buffered())
buffer.Write(b)
rs = buffer.Bytes()
break
}
}
case CLIENT: // client connection
ch = pMux.clientConn
default: // https
ch = pMux.httpsConn
}
if len(rs) == 0 {
rs = buf
}
ch <- newPortConn(conn, rs)
}
func (pMux *PortMux) Close() error {
if pMux.isClose {
return errors.New("the port mux has closed")
}
pMux.isClose = true
close(pMux.clientConn)
close(pMux.httpsConn)
close(pMux.httpConn)
close(pMux.managerConn)
return pMux.Listener.Close()
}
func (pMux *PortMux) GetClientListener() net.Listener {
return NewPortListener(pMux.clientConn, pMux.Listener.Addr())
}
func (pMux *PortMux) GetHttpListener() net.Listener {
return NewPortListener(pMux.httpConn, pMux.Listener.Addr())
}
func (pMux *PortMux) GetHttpsListener() net.Listener {
return NewPortListener(pMux.httpsConn, pMux.Listener.Addr())
}
func (pMux *PortMux) GetManagerListener() net.Listener {
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)
}

39
lib/mux/pmux_test.go Normal file
View File

@@ -0,0 +1,39 @@
package mux
import (
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"testing"
"time"
)
func TestPortMux_Close(t *testing.T) {
logs.Reset()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
pMux := NewPortMux(8888)
go func() {
if pMux.Start() != nil {
logs.Warn("Error")
}
}()
time.Sleep(time.Second * 3)
go func() {
l := pMux.GetHttpsAccept()
conn, err := l.Accept()
logs.Warn(conn, err)
}()
go func() {
l := pMux.GetHttpAccept()
conn, err := l.Accept()
logs.Warn(conn, err)
}()
go func() {
l := pMux.GetClientAccept()
conn, err := l.Accept()
logs.Warn(conn, err)
}()
l := pMux.GetManagerAccept()
conn, err := l.Accept()
logs.Warn(conn, err)
}

View File

@@ -1,7 +1,8 @@
package version
const VERSION = "0.17.1"
const VERSION = "0.18.0"
// Compulsory minimum version, Minimum downward compatibility to this version
func GetVersion() string {
return VERSION
return "0.18.0"
}