mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-05 08:46:59 +08:00
修订代码, 默认loglevel 改为 Log_info.
对一般用户而言,还是需要使用Info等级 来了解一下 一般的 日志情况,等到使用熟练之后,且确认运行没有错误后, 可以自行调为 warning 来提升性能 发现 bubble包 还自己引入了 命令行参数,这十分不可取,所以我们还是直接使用其代码。 将其它包中 的 命令行参数 统一 移动 到 cmd/verysimple 中;tls lazy 特性因为还在 调试阶段,所以 命令行参数 仍然放到 v2ray_simple 包中。
This commit is contained in:
@@ -23,8 +23,8 @@ type Client struct {
|
|||||||
useHysteria, hysteria_manual, early bool
|
useHysteria, hysteria_manual, early bool
|
||||||
maxbyteCount int
|
maxbyteCount int
|
||||||
|
|
||||||
clientconns map[[16]byte]*sessionState
|
clientconns map[[16]byte]*connState
|
||||||
sessionMapMutex sync.RWMutex
|
connMapMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(addr *netLayer.Addr, alpnList []string, host string, insecure bool, useHysteria bool, maxbyteCount int, hysteria_manual, early bool) *Client {
|
func NewClient(addr *netLayer.Addr, alpnList []string, host string, insecure bool, useHysteria bool, maxbyteCount int, hysteria_manual, early bool) *Client {
|
||||||
@@ -42,8 +42,8 @@ func NewClient(addr *netLayer.Addr, alpnList []string, host string, insecure boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//trimSessions移除不Active的session, 并试图返回一个 最佳的可用于新stream的session
|
//trimBadConns removes non-Active sessions, 并试图返回一个 最佳的可用于新stream的session
|
||||||
func (c *Client) trimSessions(ss map[[16]byte]*sessionState) (s *sessionState) {
|
func (c *Client) trimBadConns(ss map[[16]byte]*connState) (s *connState) {
|
||||||
minSessionNum := 10000
|
minSessionNum := 10000
|
||||||
for id, thisState := range ss {
|
for id, thisState := range ss {
|
||||||
if isActive(thisState) {
|
if isActive(thisState) {
|
||||||
@@ -84,24 +84,24 @@ func (c *Client) DialCommonConn(openBecausePreviousFull bool, previous any) any
|
|||||||
|
|
||||||
if !openBecausePreviousFull {
|
if !openBecausePreviousFull {
|
||||||
|
|
||||||
c.sessionMapMutex.Lock()
|
c.connMapMutex.Lock()
|
||||||
var theSession *sessionState
|
var theState *connState
|
||||||
if len(c.clientconns) > 0 {
|
if len(c.clientconns) > 0 {
|
||||||
theSession = c.trimSessions(c.clientconns)
|
theState = c.trimBadConns(c.clientconns)
|
||||||
}
|
}
|
||||||
if len(c.clientconns) > 0 {
|
if len(c.clientconns) > 0 {
|
||||||
c.sessionMapMutex.Unlock()
|
c.connMapMutex.Unlock()
|
||||||
if theSession != nil {
|
if theState != nil {
|
||||||
return theSession
|
return theState
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.clientconns = make(map[[16]byte]*sessionState)
|
c.clientconns = make(map[[16]byte]*connState)
|
||||||
c.sessionMapMutex.Unlock()
|
c.connMapMutex.Unlock()
|
||||||
}
|
}
|
||||||
} else if previous != nil && c.knownServerMaxStreamCount == 0 {
|
} else if previous != nil && c.knownServerMaxStreamCount == 0 {
|
||||||
|
|
||||||
ps, ok := previous.(*sessionState)
|
ps, ok := previous.(*connState)
|
||||||
if !ok {
|
if !ok {
|
||||||
if ce := utils.CanLogDebug("QUIC: 'previous' parameter was given but with wrong type "); ce != nil {
|
if ce := utils.CanLogDebug("QUIC: 'previous' parameter was given but with wrong type "); ce != nil {
|
||||||
ce.Write(zap.String("type", reflect.TypeOf(previous).String()))
|
ce.Write(zap.String("type", reflect.TypeOf(previous).String()))
|
||||||
@@ -154,16 +154,16 @@ func (c *Client) DialCommonConn(openBecausePreviousFull bool, previous any) any
|
|||||||
|
|
||||||
id := utils.GenerateUUID()
|
id := utils.GenerateUUID()
|
||||||
|
|
||||||
var result = &sessionState{Connection: conn, id: id}
|
var result = &connState{Connection: conn, id: id}
|
||||||
c.sessionMapMutex.Lock()
|
c.connMapMutex.Lock()
|
||||||
c.clientconns[id] = result
|
c.clientconns[id] = result
|
||||||
c.sessionMapMutex.Unlock()
|
c.connMapMutex.Unlock()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DialSubConn(thing any) (net.Conn, error) {
|
func (c *Client) DialSubConn(thing any) (net.Conn, error) {
|
||||||
theState, ok := thing.(*sessionState)
|
theState, ok := thing.(*connState)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, utils.ErrNilOrWrongParameter
|
return nil, utils.ErrNilOrWrongParameter
|
||||||
}
|
}
|
||||||
@@ -176,5 +176,5 @@ func (c *Client) DialSubConn(thing any) (net.Conn, error) {
|
|||||||
|
|
||||||
atomic.AddInt32(&theState.openedStreamCount, 1)
|
atomic.AddInt32(&theState.openedStreamCount, 1)
|
||||||
|
|
||||||
return StreamConn{Stream: stream, laddr: theState.LocalAddr(), raddr: theState.RemoteAddr(), relatedSessionState: theState}, nil
|
return StreamConn{Stream: stream, laddr: theState.LocalAddr(), raddr: theState.RemoteAddr(), relatedConnState: theState}, nil
|
||||||
}
|
}
|
||||||
|
@@ -7,8 +7,9 @@ import (
|
|||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
//用于 跟踪 一个 session 中 所开启的 stream的数量
|
// 对 quic.Connection 的一个包装。
|
||||||
type sessionState struct {
|
//用于 跟踪 一个 session 中 所开启的 stream的数量.
|
||||||
|
type connState struct {
|
||||||
quic.Connection
|
quic.Connection
|
||||||
id [16]byte
|
id [16]byte
|
||||||
|
|
||||||
@@ -20,9 +21,9 @@ type sessionState struct {
|
|||||||
// 因为它是通过 StreamID 来识别连接. 不过session是有的。
|
// 因为它是通过 StreamID 来识别连接. 不过session是有的。
|
||||||
type StreamConn struct {
|
type StreamConn struct {
|
||||||
quic.Stream
|
quic.Stream
|
||||||
laddr, raddr net.Addr
|
laddr, raddr net.Addr
|
||||||
relatedSessionState *sessionState
|
relatedConnState *connState
|
||||||
isclosed bool
|
isclosed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc StreamConn) LocalAddr() net.Addr {
|
func (sc StreamConn) LocalAddr() net.Addr {
|
||||||
@@ -42,7 +43,7 @@ func (sc StreamConn) Close() error {
|
|||||||
sc.isclosed = true
|
sc.isclosed = true
|
||||||
sc.CancelRead(quic.StreamErrorCode(quic.ConnectionRefused))
|
sc.CancelRead(quic.StreamErrorCode(quic.ConnectionRefused))
|
||||||
sc.CancelWrite(quic.StreamErrorCode(quic.ConnectionRefused))
|
sc.CancelWrite(quic.StreamErrorCode(quic.ConnectionRefused))
|
||||||
if rss := sc.relatedSessionState; rss != nil {
|
if rss := sc.relatedConnState; rss != nil {
|
||||||
|
|
||||||
atomic.AddInt32(&rss.openedStreamCount, -1)
|
atomic.AddInt32(&rss.openedStreamCount, -1)
|
||||||
|
|
||||||
|
@@ -1,22 +1,16 @@
|
|||||||
//Package quic defines functions to listen and dial quic, with some customizable congestion settings.
|
//Package quic defines functions to listen and dial quic, with some customizable congestion settings.
|
||||||
//
|
//
|
||||||
// 这里我们使用 hysteria的 brutal阻控.
|
// 这里我们 还选择性 使用 hysteria的 brutal阻控.
|
||||||
// 见 https://github.com/tobyxdd/quic-go 中 toby的 *-mod 分支, 里面会多一个 congestion 文件夹.
|
// 见 https://github.com/tobyxdd/quic-go 中 toby的 *-mod 分支, 里面会多一个 congestion 文件夹.
|
||||||
package quic
|
package quic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/e1732a364fed/v2ray_simple/advLayer"
|
"github.com/e1732a364fed/v2ray_simple/advLayer"
|
||||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
"github.com/lucas-clemente/quic-go/congestion"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -38,10 +32,10 @@ const (
|
|||||||
//100mbps
|
//100mbps
|
||||||
Default_hysteriaMaxByteCount = 1024 * 1024 / 8 * 100
|
Default_hysteriaMaxByteCount = 1024 * 1024 / 8 * 100
|
||||||
|
|
||||||
common_maxidletimeout = time.Second * 45
|
common_maxidletimeout = time.Second * 45
|
||||||
common_HandshakeIdleTimeout = time.Second * 8
|
common_HandshakeIdleTimeout = time.Second * 8
|
||||||
common_ConnectionIDLength = 12
|
common_ConnectionIDLength = 12
|
||||||
server_maxStreamCountInOneSession = 4 //一个session中 stream越多, 性能越低, 因此我们这里限制为4
|
server_maxStreamCountInOneConn = 4 //一个 Connection 中 stream越多, 性能越低, 因此我们这里限制为4
|
||||||
)
|
)
|
||||||
|
|
||||||
func isActive(s quic.Connection) bool {
|
func isActive(s quic.Connection) bool {
|
||||||
@@ -69,7 +63,7 @@ var (
|
|||||||
ConnectionIDLength: common_ConnectionIDLength,
|
ConnectionIDLength: common_ConnectionIDLength,
|
||||||
HandshakeIdleTimeout: common_HandshakeIdleTimeout,
|
HandshakeIdleTimeout: common_HandshakeIdleTimeout,
|
||||||
MaxIdleTimeout: common_maxidletimeout,
|
MaxIdleTimeout: common_maxidletimeout,
|
||||||
MaxIncomingStreams: server_maxStreamCountInOneSession,
|
MaxIncomingStreams: server_maxStreamCountInOneConn,
|
||||||
MaxIncomingUniStreams: -1,
|
MaxIncomingUniStreams: -1,
|
||||||
KeepAlive: true,
|
KeepAlive: true,
|
||||||
}
|
}
|
||||||
@@ -81,126 +75,3 @@ var (
|
|||||||
KeepAlive: true,
|
KeepAlive: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListenInitialLayers(addr string, tlsConf tls.Config, useHysteria bool, hysteriaMaxByteCount int, hysteria_manual, early bool, customMaxStreamCountInOneSession int64) (newConnChan chan net.Conn, baseConn any) {
|
|
||||||
|
|
||||||
thisConfig := common_ListenConfig
|
|
||||||
if customMaxStreamCountInOneSession > 0 {
|
|
||||||
thisConfig.MaxIncomingStreams = customMaxStreamCountInOneSession
|
|
||||||
}
|
|
||||||
|
|
||||||
var listener quic.Listener
|
|
||||||
var elistener quic.EarlyListener
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if early {
|
|
||||||
utils.Info("quic Listen Early")
|
|
||||||
elistener, err = quic.ListenAddrEarly(addr, &tlsConf, &thisConfig)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
listener, err = quic.ListenAddr(addr, &tlsConf, &thisConfig)
|
|
||||||
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if ce := utils.CanLogErr("quic listen"); ce != nil {
|
|
||||||
ce.Write(zap.Error(err))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if useHysteria {
|
|
||||||
if hysteriaMaxByteCount <= 0 {
|
|
||||||
hysteriaMaxByteCount = Default_hysteriaMaxByteCount
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
newConnChan = make(chan net.Conn, 10)
|
|
||||||
|
|
||||||
if early {
|
|
||||||
go loopAcceptEarly(elistener, newConnChan, useHysteria, hysteria_manual, hysteriaMaxByteCount)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
go loopAccept(listener, newConnChan, useHysteria, hysteria_manual, hysteriaMaxByteCount)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//阻塞
|
|
||||||
func loopAccept(l quic.Listener, theChan chan net.Conn, useHysteria bool, hysteria_manual bool, hysteriaMaxByteCount int) {
|
|
||||||
for {
|
|
||||||
conn, err := l.Accept(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
if ce := utils.CanLogErr("quic session accept"); ce != nil {
|
|
||||||
ce.Write(zap.Error(err))
|
|
||||||
}
|
|
||||||
//close(theChan) //不应关闭chan,因为listen虽然不好使但是也许现存的stream还是好使的...
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if useHysteria {
|
|
||||||
configHyForConn(conn, hysteria_manual, hysteriaMaxByteCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
dealNewSession(conn, theChan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//阻塞
|
|
||||||
func loopAcceptEarly(el quic.EarlyListener, theChan chan net.Conn, useHysteria bool, hysteria_manual bool, hysteriaMaxByteCount int) {
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := el.Accept(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
if ce := utils.CanLogErr("quic session accept"); ce != nil {
|
|
||||||
ce.Write(zap.Error(err))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if useHysteria {
|
|
||||||
configHyForConn(conn, hysteria_manual, hysteriaMaxByteCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
dealNewSession(conn, theChan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func configHyForConn(conn quic.Connection, hysteria_manual bool, hysteriaMaxByteCount int) {
|
|
||||||
if hysteria_manual {
|
|
||||||
bs := NewBrutalSender_M(congestion.ByteCount(hysteriaMaxByteCount))
|
|
||||||
|
|
||||||
conn.SetCongestionControl(bs)
|
|
||||||
} else {
|
|
||||||
bs := NewBrutalSender(congestion.ByteCount(hysteriaMaxByteCount))
|
|
||||||
|
|
||||||
conn.SetCongestionControl(bs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//非阻塞
|
|
||||||
func dealNewSession(session quic.Connection, theChan chan net.Conn) {
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
stream, err := session.AcceptStream(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
if ce := utils.CanLogDebug("quic stream accept failed"); ce != nil {
|
|
||||||
//只要某个连接idle时间一长,超过了idleTimeout,服务端就会出现此错误:
|
|
||||||
// timeout: no recent network activity,即 quic.IdleTimeoutError
|
|
||||||
//这不能说是错误, 而是quic的udp特性所致,所以放到debug 输出中.
|
|
||||||
//这也同时说明, keep alive功能并不会更新 idle的最后期限.
|
|
||||||
|
|
||||||
//我们为了性能,不必将该err转成 net.Error然后判断是否是timeout
|
|
||||||
//如果要排错那就开启debug日志即可.
|
|
||||||
|
|
||||||
ce.Write(zap.Error(err))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
theChan <- StreamConn{stream, session.LocalAddr(), session.RemoteAddr(), nil, false}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
132
advLayer/quic/server.go
Normal file
132
advLayer/quic/server.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||||
|
"github.com/lucas-clemente/quic-go"
|
||||||
|
"github.com/lucas-clemente/quic-go/congestion"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListenInitialLayers(addr string, tlsConf tls.Config, useHysteria bool, hysteriaMaxByteCount int, hysteria_manual, early bool, customMaxStreamCountInOneConn int64) (newConnChan chan net.Conn, baseConn any) {
|
||||||
|
|
||||||
|
thisConfig := common_ListenConfig
|
||||||
|
if customMaxStreamCountInOneConn > 0 {
|
||||||
|
thisConfig.MaxIncomingStreams = customMaxStreamCountInOneConn
|
||||||
|
}
|
||||||
|
|
||||||
|
var listener quic.Listener
|
||||||
|
var elistener quic.EarlyListener
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if early {
|
||||||
|
utils.Info("quic Listen Early")
|
||||||
|
elistener, err = quic.ListenAddrEarly(addr, &tlsConf, &thisConfig)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
listener, err = quic.ListenAddr(addr, &tlsConf, &thisConfig)
|
||||||
|
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if ce := utils.CanLogErr("quic listen"); ce != nil {
|
||||||
|
ce.Write(zap.Error(err))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if useHysteria {
|
||||||
|
if hysteriaMaxByteCount <= 0 {
|
||||||
|
hysteriaMaxByteCount = Default_hysteriaMaxByteCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newConnChan = make(chan net.Conn, 10)
|
||||||
|
|
||||||
|
if early {
|
||||||
|
go loopAcceptEarly(elistener, newConnChan, useHysteria, hysteria_manual, hysteriaMaxByteCount)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
go loopAccept(listener, newConnChan, useHysteria, hysteria_manual, hysteriaMaxByteCount)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//阻塞
|
||||||
|
func loopAccept(l quic.Listener, theChan chan net.Conn, useHysteria bool, hysteria_manual bool, hysteriaMaxByteCount int) {
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
if ce := utils.CanLogErr("quic accept failed"); ce != nil {
|
||||||
|
ce.Write(zap.Error(err))
|
||||||
|
}
|
||||||
|
//close(theChan) //不应关闭chan,因为listen虽然不好使但是也许现存的stream还是好使的...
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if useHysteria {
|
||||||
|
configHyForConn(conn, hysteria_manual, hysteriaMaxByteCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
go dealNewConn(conn, theChan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//阻塞
|
||||||
|
func loopAcceptEarly(el quic.EarlyListener, theChan chan net.Conn, useHysteria bool, hysteria_manual bool, hysteriaMaxByteCount int) {
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := el.Accept(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
if ce := utils.CanLogErr("quic early accept failed"); ce != nil {
|
||||||
|
ce.Write(zap.Error(err))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if useHysteria {
|
||||||
|
configHyForConn(conn, hysteria_manual, hysteriaMaxByteCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
go dealNewConn(conn, theChan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configHyForConn(conn quic.Connection, hysteria_manual bool, hysteriaMaxByteCount int) {
|
||||||
|
if hysteria_manual {
|
||||||
|
bs := NewBrutalSender_M(congestion.ByteCount(hysteriaMaxByteCount))
|
||||||
|
|
||||||
|
conn.SetCongestionControl(bs)
|
||||||
|
} else {
|
||||||
|
bs := NewBrutalSender(congestion.ByteCount(hysteriaMaxByteCount))
|
||||||
|
|
||||||
|
conn.SetCongestionControl(bs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//阻塞
|
||||||
|
func dealNewConn(conn quic.Connection, theChan chan net.Conn) {
|
||||||
|
|
||||||
|
for {
|
||||||
|
stream, err := conn.AcceptStream(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
if ce := utils.CanLogDebug("quic stream accept failed"); ce != nil {
|
||||||
|
//只要某个连接idle时间一长,超过了idleTimeout,服务端就会出现此错误:
|
||||||
|
// timeout: no recent network activity,即 quic.IdleTimeoutError
|
||||||
|
//这不能说是错误, 而是quic的udp特性所致,所以放到debug 输出中.
|
||||||
|
//这也同时说明, keep alive功能并不会更新 idle的最后期限.
|
||||||
|
|
||||||
|
//我们为了性能,不必将该err转成 net.Error然后判断是否是timeout
|
||||||
|
//如果要排错,开启debug日志即可.
|
||||||
|
|
||||||
|
ce.Write(zap.Error(err))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
theChan <- StreamConn{stream, conn.LocalAddr(), conn.RemoteAddr(), nil, false}
|
||||||
|
}
|
||||||
|
}
|
@@ -53,6 +53,16 @@ func init() {
|
|||||||
flag.StringVar(&listenURL, "L", "", "listen URL (i.e. the listen part in config file), only enbled when config file is not provided.")
|
flag.StringVar(&listenURL, "L", "", "listen URL (i.e. the listen part in config file), only enbled when config file is not provided.")
|
||||||
flag.StringVar(&dialURL, "D", "", "dial URL (i.e. the dial part in config file), only enbled when config file is not provided.")
|
flag.StringVar(&dialURL, "D", "", "dial URL (i.e. the dial part in config file), only enbled when config file is not provided.")
|
||||||
|
|
||||||
|
//other packages
|
||||||
|
|
||||||
|
flag.IntVar(&utils.LogLevel, "ll", utils.DefaultLL, "log level,0=debug, 1=info, 2=warning, 3=error, 4=dpanic, 5=panic, 6=fatal")
|
||||||
|
|
||||||
|
flag.StringVar(&utils.LogOutFileName, "lf", "vs_log", "output file for log; If empty, no log file will be used.")
|
||||||
|
|
||||||
|
flag.BoolVar(&netLayer.UseReadv, "readv", netLayer.DefaultReadvOption, "toggle the use of 'readv' syscall")
|
||||||
|
|
||||||
|
flag.StringVar(&netLayer.GeoipFileName, "geoip", "GeoLite2-Country.mmdb", "geoip maxmind file name")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -69,6 +79,8 @@ func mainFunc() (result int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result = -3
|
result = -3
|
||||||
|
|
||||||
|
cleanup()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -91,7 +103,9 @@ func mainFunc() (result int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.ShouldLogToFile = true
|
if utils.LogOutFileName != "" {
|
||||||
|
utils.ShouldLogToFile = true
|
||||||
|
}
|
||||||
|
|
||||||
utils.InitLog()
|
utils.InitLog()
|
||||||
|
|
||||||
@@ -103,7 +117,7 @@ func mainFunc() (result int) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
if startMProf {
|
if startMProf {
|
||||||
//若不使用 NoShutdownHook, 我们ctrl+c退出时不会产生 pprof文件
|
//若不使用 NoShutdownHook, 则 我们ctrl+c退出时不会产生 pprof文件
|
||||||
p := profile.Start(profile.MemProfile, profile.MemProfileRate(1), profile.NoShutdownHook)
|
p := profile.Start(profile.MemProfile, profile.MemProfileRate(1), profile.NoShutdownHook)
|
||||||
|
|
||||||
defer p.Stop()
|
defer p.Stop()
|
||||||
@@ -126,8 +140,21 @@ func mainFunc() (result int) {
|
|||||||
netLayer.Prepare()
|
netLayer.Prepare()
|
||||||
|
|
||||||
fmt.Printf("Log Level:%d\n", utils.LogLevel)
|
fmt.Printf("Log Level:%d\n", utils.LogLevel)
|
||||||
fmt.Printf("UseReadv:%t\n", netLayer.UseReadv)
|
|
||||||
fmt.Printf("tls_lazy_encrypt:%t\n", vs.Tls_lazy_encrypt)
|
if ce := utils.CanLogInfo("Options"); ce != nil {
|
||||||
|
|
||||||
|
ce.Write(
|
||||||
|
zap.String("Log Level", utils.LogLevelStr(utils.LogLevel)),
|
||||||
|
zap.Bool("UseReadv", netLayer.UseReadv),
|
||||||
|
zap.Bool("tls_lazy_encrypt", vs.Tls_lazy_encrypt),
|
||||||
|
)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fmt.Printf("UseReadv:%t\n", netLayer.UseReadv)
|
||||||
|
fmt.Printf("tls_lazy_encrypt:%t\n", vs.Tls_lazy_encrypt)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
runPreCommands()
|
runPreCommands()
|
||||||
|
|
||||||
@@ -138,7 +165,7 @@ func mainFunc() (result int) {
|
|||||||
RoutingEnv.MainFallback = mainFallback
|
RoutingEnv.MainFallback = mainFallback
|
||||||
}
|
}
|
||||||
|
|
||||||
//load inServers and vs.RoutePolicy
|
//load inServers and RoutingEnv
|
||||||
switch mode {
|
switch mode {
|
||||||
case proxy.SimpleMode:
|
case proxy.SimpleMode:
|
||||||
var hase bool
|
var hase bool
|
||||||
@@ -294,19 +321,22 @@ func mainFunc() (result int) {
|
|||||||
|
|
||||||
utils.Info("Program got close signal.")
|
utils.Info("Program got close signal.")
|
||||||
|
|
||||||
//在程序ctrl+C关闭时, 会主动Close所有的监听端口. 主要是被报告windows有时退出程序之后, 端口还是处于占用状态.
|
cleanup()
|
||||||
// 用下面代码以试图解决端口占用问题.
|
|
||||||
|
|
||||||
for _, listener := range ListenerArray {
|
|
||||||
if listener != nil {
|
|
||||||
listener.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tm := range TproxyList {
|
|
||||||
tm.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanup() {
|
||||||
|
//在程序ctrl+C关闭时, 会主动Close所有的监听端口. 主要是被报告windows有时退出程序之后, 端口还是处于占用状态.
|
||||||
|
// 用下面代码以试图解决端口占用问题.
|
||||||
|
|
||||||
|
for _, listener := range ListenerArray {
|
||||||
|
if listener != nil {
|
||||||
|
listener.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tm := range TproxyList {
|
||||||
|
tm.Stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2
doc.go
2
doc.go
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
Package v2ray_simple provides a way to set up a proxy.
|
Package v2ray_simple provides a way to set up a proxy.
|
||||||
|
|
||||||
|
|
||||||
Structure 本项目结构
|
Structure 本项目结构
|
||||||
|
|
||||||
utils -> netLayer-> tlsLayer -> httpLayer -> advLayer -> proxy -> v2ray_simple -> cmd/verysimple
|
utils -> netLayer-> tlsLayer -> httpLayer -> advLayer -> proxy -> v2ray_simple -> cmd/verysimple
|
||||||
|
|
||||||
|
根项目 v2ray_simple 仅研究实际转发过程.
|
||||||
|
|
||||||
Chain
|
Chain
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[app] # app 项是可选的
|
[app] # app 项是可选的
|
||||||
|
|
||||||
# 日志等级,默认为2. 0=debug, 1=info, 2=warning, 3=error, 4=dpanic, 5=panic, 6=fatal,
|
# 日志等级,默认为1. 0=debug, 1=info, 2=warning, 3=error, 4=dpanic, 5=panic, 6=fatal,
|
||||||
# 推荐开发时用0, 测试时使用1, 日常使用时 使用2或3; 显然日志越少越快, 设为6或者更大值的话性能是最好的.
|
# 推荐开发时用0, 测试时使用1, 日常使用时 使用2或3; 显然日志越少越快, 设为6或者更大值的话性能是最好的.
|
||||||
#loglevel = 1
|
#loglevel = 1
|
||||||
|
|
||||||
|
8
go.mod
8
go.mod
@@ -5,10 +5,12 @@ go 1.18
|
|||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.0.0
|
github.com/BurntSushi/toml v1.0.0
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||||
|
github.com/biter777/countries v1.3.4
|
||||||
github.com/gobwas/ws v1.1.0
|
github.com/gobwas/ws v1.1.0
|
||||||
github.com/lucas-clemente/quic-go v0.0.0-00010101000000-000000000000
|
github.com/lucas-clemente/quic-go v0.0.0-00010101000000-000000000000
|
||||||
github.com/manifoldco/promptui v0.9.0
|
github.com/manifoldco/promptui v0.9.0
|
||||||
github.com/miekg/dns v1.1.47
|
github.com/miekg/dns v1.1.47
|
||||||
|
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||||
github.com/oschwald/maxminddb-golang v1.8.0
|
github.com/oschwald/maxminddb-golang v1.8.0
|
||||||
github.com/pkg/profile v1.6.0
|
github.com/pkg/profile v1.6.0
|
||||||
github.com/refraction-networking/utls v1.0.0
|
github.com/refraction-networking/utls v1.0.0
|
||||||
@@ -16,13 +18,13 @@ require (
|
|||||||
github.com/yl2chen/cidranger v1.0.2
|
github.com/yl2chen/cidranger v1.0.2
|
||||||
go.uber.org/zap v1.21.0
|
go.uber.org/zap v1.21.0
|
||||||
golang.org/x/exp v0.0.0-20220407100705-7b9b53b0aca4
|
golang.org/x/exp v0.0.0-20220407100705-7b9b53b0aca4
|
||||||
|
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
|
||||||
gonum.org/v1/gonum v0.11.0
|
gonum.org/v1/gonum v0.11.0
|
||||||
google.golang.org/grpc v1.45.0
|
google.golang.org/grpc v1.45.0
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/biter777/countries v1.3.4 // indirect
|
|
||||||
github.com/cheekybits/genny v1.0.0 // indirect
|
github.com/cheekybits/genny v1.0.0 // indirect
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
@@ -33,20 +35,18 @@ require (
|
|||||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||||
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
|
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||||
github.com/tjarratt/babble v0.0.0-20210505082055-cbca2a4833c1 // indirect
|
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
|
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/tools v0.1.9 // indirect
|
golang.org/x/tools v0.1.9 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
4
go.sum
4
go.sum
@@ -202,8 +202,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||||
github.com/tjarratt/babble v0.0.0-20210505082055-cbca2a4833c1 h1:j8whCiEmvLCXI3scVn+YnklCU8mwJ9ZJ4/DGAKqQbRE=
|
|
||||||
github.com/tjarratt/babble v0.0.0-20210505082055-cbca2a4833c1/go.mod h1:O5hBrCGqzfb+8WyY8ico2AyQau7XQwAfEQeEQ5/5V9E=
|
|
||||||
github.com/tobyxdd/quic-go v0.27.1-0.20220414074155-271e5f3ac478 h1:/6nptMH0dGAsaE4/ai70AyOmhRlPBy+lAcvw3x8T0m4=
|
github.com/tobyxdd/quic-go v0.27.1-0.20220414074155-271e5f3ac478 h1:/6nptMH0dGAsaE4/ai70AyOmhRlPBy+lAcvw3x8T0m4=
|
||||||
github.com/tobyxdd/quic-go v0.27.1-0.20220414074155-271e5f3ac478/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
github.com/tobyxdd/quic-go v0.27.1-0.20220414074155-271e5f3ac478/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI=
|
||||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||||
@@ -381,6 +379,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
27
main.go
27
main.go
@@ -48,7 +48,7 @@ var (
|
|||||||
Tls_lazy_secure bool
|
Tls_lazy_secure bool
|
||||||
|
|
||||||
//有时需要测试到单一网站的流量,此时为了避免其它干扰,可声明 一下 该域名,然后程序里会进行过滤
|
//有时需要测试到单一网站的流量,此时为了避免其它干扰,可声明 一下 该域名,然后程序里会进行过滤
|
||||||
uniqueTestDomain string
|
//uniqueTestDomain string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -56,12 +56,12 @@ func init() {
|
|||||||
flag.BoolVar(&Tls_lazy_encrypt, "lazy", false, "tls lazy encrypt (splice)")
|
flag.BoolVar(&Tls_lazy_encrypt, "lazy", false, "tls lazy encrypt (splice)")
|
||||||
flag.BoolVar(&Tls_lazy_secure, "ls", false, "tls lazy secure, use special techs to ensure the tls lazy encrypt data can't be detected. Only valid at client end.")
|
flag.BoolVar(&Tls_lazy_secure, "ls", false, "tls lazy secure, use special techs to ensure the tls lazy encrypt data can't be detected. Only valid at client end.")
|
||||||
|
|
||||||
flag.StringVar(&uniqueTestDomain, "td", "", "test a single domain, like www.domain.com. Only valid when loglevel=0")
|
//flag.StringVar(&uniqueTestDomain, "td", "", "test a single domain, like www.domain.com. Only valid when loglevel=0")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//非阻塞. 可以 直接使用 ListenSer 函数 来手动开启新的转发流程。
|
//非阻塞. 可以 直接使用 ListenSer 函数 来手动开启新的转发流程。
|
||||||
// 若 not_temporary 为 false, 则不会使用 RoutingEnv进行路由或回落
|
// 若 env 为 nil, 则不会 进行路由或回落
|
||||||
func ListenSer(inServer proxy.Server, defaultOutClientForThis proxy.Client, env *proxy.RoutingEnv) (thisListener net.Listener) {
|
func ListenSer(inServer proxy.Server, defaultOutClientForThis proxy.Client, env *proxy.RoutingEnv) (thisListener net.Listener) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -878,19 +878,22 @@ func dialClient(targetAddr netLayer.Addr,
|
|||||||
// 而其它代理的话, realTargetAddr会被设成实际配置的代理的地址
|
// 而其它代理的话, realTargetAddr会被设成实际配置的代理的地址
|
||||||
realTargetAddr = targetAddr
|
realTargetAddr = targetAddr
|
||||||
|
|
||||||
if ce := utils.CanLogDebug("request isn't the appointed domain"); ce != nil {
|
/*
|
||||||
|
if ce := utils.CanLogDebug("request isn't the appointed domain"); ce != nil {
|
||||||
|
|
||||||
if uniqueTestDomain != "" && uniqueTestDomain != targetAddr.Name {
|
|
||||||
|
|
||||||
ce.Write(
|
if uniqueTestDomain != "" && uniqueTestDomain != targetAddr.Name {
|
||||||
zap.String("request", targetAddr.String()),
|
|
||||||
zap.String("uniqueTestDomain", uniqueTestDomain),
|
|
||||||
)
|
|
||||||
result = -1
|
|
||||||
return
|
|
||||||
|
|
||||||
|
ce.Write(
|
||||||
|
zap.String("request", targetAddr.String()),
|
||||||
|
zap.String("uniqueTestDomain", uniqueTestDomain),
|
||||||
|
)
|
||||||
|
result = -1
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
|
|
||||||
if ce := utils.CanLogInfo("Request"); ce != nil {
|
if ce := utils.CanLogInfo("Request"); ce != nil {
|
||||||
|
|
||||||
|
@@ -2,7 +2,6 @@ package netLayer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"flag"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@@ -16,14 +15,9 @@ var (
|
|||||||
the_geoipdb *maxminddb.Reader
|
the_geoipdb *maxminddb.Reader
|
||||||
embedGeoip bool
|
embedGeoip bool
|
||||||
|
|
||||||
GeoipFileName string //若运行程序指定了 geoip 参数,则该值为给定值;否则默认会被init为 GeoLite2-Country.mmdb
|
GeoipFileName string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.StringVar(&GeoipFileName, "geoip", "GeoLite2-Country.mmdb", "geoip maxmind file name")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func HasEmbedGeoip() bool {
|
func HasEmbedGeoip() bool {
|
||||||
return embedGeoip
|
return embedGeoip
|
||||||
}
|
}
|
||||||
@@ -42,7 +36,7 @@ func LoadMaxmindGeoipFile(fn string) {
|
|||||||
if fn == "" {
|
if fn == "" {
|
||||||
fn = GeoipFileName
|
fn = GeoipFileName
|
||||||
}
|
}
|
||||||
if fn == "" { //因为 GeoipFileName 是共有变量,所以可能会被设成"", 不排除脑残
|
if fn == "" { //因为 GeoipFileName 是公有变量,所以可能会被设成""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bs, e := os.ReadFile(fn)
|
bs, e := os.ReadFile(fn)
|
||||||
|
@@ -10,12 +10,15 @@ import (
|
|||||||
/* go test -bench "CheckMMDB_country" . -v
|
/* go test -bench "CheckMMDB_country" . -v
|
||||||
BenchmarkCheckMMDB_country-8 3631854 315.3 ns/op
|
BenchmarkCheckMMDB_country-8 3631854 315.3 ns/op
|
||||||
|
|
||||||
总之一次mmdb查询比map查询慢了十倍多 (见 utils/container_test.go.bak)
|
总之一次mmdb查询比map查询慢了十倍多 (见 以前代码的 utils/container_test.go.bak, 新代码已经删掉了,可以找老 tag 找到。)
|
||||||
|
|
||||||
有必要设置一个 国别-ip 的map缓存; 不过这种纳秒级别的优化就无所谓了; 也不好说,谁知道客户端的cpu有多垃圾
|
有必要设置一个 国别-ip 的map缓存; 不过这种纳秒级别的优化就无所谓了; 也不好说,谁知道客户端的cpu有多垃圾
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func BenchmarkCheckMMDB_country(b *testing.B) {
|
func BenchmarkCheckMMDB_country(b *testing.B) {
|
||||||
|
|
||||||
|
GeoipFileName = "GeoLite2-Country.mmdb"
|
||||||
|
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
LoadMaxmindGeoipFile(utils.GetFilePath("../" + GeoipFileName))
|
LoadMaxmindGeoipFile(utils.GetFilePath("../" + GeoipFileName))
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package netLayer
|
package netLayer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -29,7 +28,6 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVar(&UseReadv, "readv", DefaultReadvOption, "toggle the use of 'readv' syscall")
|
|
||||||
|
|
||||||
readvPool = sync.Pool{
|
readvPool = sync.Pool{
|
||||||
New: newReadvMem,
|
New: newReadvMem,
|
||||||
|
@@ -1,3 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Package tproxy implements tproxy.
|
||||||
|
|
||||||
|
透明代理只能用于linux。
|
||||||
|
|
||||||
|
About TProxy 关于透明代理
|
||||||
|
|
||||||
|
透明代理原理
|
||||||
|
https://www.kernel.org/doc/html/latest/networking/tproxy.html
|
||||||
|
|
||||||
|
golang 示例
|
||||||
|
https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_tcp.go
|
||||||
|
|
||||||
|
c 语言 示例
|
||||||
|
https://github.com/FarFetchd/simple_tproxy_example/blob/master/tproxy_captive_portal.c
|
||||||
|
|
||||||
|
|
||||||
|
关键点在于
|
||||||
|
|
||||||
|
1. 要使用 syscall.IP_TRANSPARENT 监听
|
||||||
|
|
||||||
|
2. 监听到的 连接 的 localAddr实际上是 真实的目标地址, 而不是我们监听的地址;
|
||||||
|
|
||||||
|
|
||||||
|
我们在本包里要做的事情就是 模仿 上面的 golang示例,
|
||||||
|
|
||||||
|
但是,上面的go示例有一个特点, 它是直接利用客户端自己的地址+reuse端口的方法去拨号实际地址的,而我们不需要那样做。
|
||||||
|
|
||||||
|
而且, udp 的过程更加特殊。
|
||||||
|
|
||||||
|
总之,这种情况完全不适配 proxy.Server 的接口, 应该单独拿出来, 属于网络层的特殊情况.
|
||||||
|
|
||||||
|
另外就是,偶然发现,trojan-go也是使用的 上面的示例的代码。
|
||||||
|
|
||||||
|
同时,trojan-go还使用了
|
||||||
|
https://github.com/cybozu-go/transocks/blob/master/original_dst_linux.go
|
||||||
|
|
||||||
|
Iptables
|
||||||
|
|
||||||
|
iptables配置教程:
|
||||||
|
https://toutyrater.github.io/app/tproxy.html
|
||||||
|
|
||||||
|
下面把该教程的重要部分搬过来。
|
||||||
|
|
||||||
|
|
||||||
|
ip rule add fwmark 1 table 100
|
||||||
|
ip route add local 0.0.0.0/0 dev lo table 100
|
||||||
|
|
||||||
|
iptables -t mangle -N V2RAY
|
||||||
|
iptables -t mangle -A V2RAY -d 127.0.0.1/32 -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY -d 224.0.0.0/4 -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY -d 255.255.255.255/32 -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p tcp -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
|
||||||
|
iptables -t mangle -A V2RAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
|
||||||
|
iptables -t mangle -A PREROUTING -j V2RAY
|
||||||
|
|
||||||
|
iptables -t mangle -N V2RAY_MASK
|
||||||
|
iptables -t mangle -A V2RAY_MASK -d 224.0.0.0/4 -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY_MASK -d 255.255.255.255/32 -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p tcp -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
|
||||||
|
iptables -t mangle -A V2RAY_MASK -j RETURN -m mark --mark 0xff
|
||||||
|
iptables -t mangle -A V2RAY_MASK -p udp -j MARK --set-mark 1
|
||||||
|
iptables -t mangle -A V2RAY_MASK -p tcp -j MARK --set-mark 1
|
||||||
|
iptables -t mangle -A OUTPUT -j V2RAY_MASK
|
||||||
|
|
||||||
|
|
||||||
|
Persistant iptables
|
||||||
|
|
||||||
|
单独设置iptables,重启后会消失. 下面是持久化方法
|
||||||
|
|
||||||
|
mkdir -p /etc/iptables && iptables-save > /etc/iptables/rules.v4
|
||||||
|
|
||||||
|
vi /etc/systemd/system/tproxyrule.service
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Tproxy rule
|
||||||
|
After=network.target
|
||||||
|
Wants=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/sbin/ip rule add fwmark 1 table 100 ; /sbin/ip route add local 0.0.0.0/0 dev lo table 100 ; /sbin/iptables-restore /etc/iptables/rules.v4
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
|
||||||
|
systemctl enable tproxyrule
|
||||||
|
|
||||||
|
*/
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@@ -1,97 +1,3 @@
|
|||||||
/*
|
|
||||||
Package tproxy implements tproxy.
|
|
||||||
|
|
||||||
透明代理只能用于linux。
|
|
||||||
|
|
||||||
About TProxy 关于透明代理
|
|
||||||
|
|
||||||
透明代理原理
|
|
||||||
https://www.kernel.org/doc/html/latest/networking/tproxy.html
|
|
||||||
|
|
||||||
golang 示例
|
|
||||||
https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_tcp.go
|
|
||||||
|
|
||||||
c 语言 示例
|
|
||||||
https://github.com/FarFetchd/simple_tproxy_example/blob/master/tproxy_captive_portal.c
|
|
||||||
|
|
||||||
|
|
||||||
关键点在于
|
|
||||||
|
|
||||||
1. 要使用 syscall.IP_TRANSPARENT 监听
|
|
||||||
|
|
||||||
2. 监听到的 连接 的 localAddr实际上是 真实的目标地址, 而不是我们监听的地址;
|
|
||||||
|
|
||||||
|
|
||||||
我们在本包里要做的事情就是 模仿 上面的 golang示例,
|
|
||||||
|
|
||||||
但是,上面的go示例有一个特点, 它是直接利用客户端自己的地址+reuse端口的方法去拨号实际地址的,而我们不需要那样做。
|
|
||||||
|
|
||||||
而且, udp 的过程更加特殊。
|
|
||||||
|
|
||||||
总之,这种情况完全不适配 proxy.Server 的接口, 应该单独拿出来, 属于网络层的特殊情况.
|
|
||||||
|
|
||||||
另外就是,偶然发现,trojan-go也是使用的 上面的示例的代码。
|
|
||||||
|
|
||||||
同时,trojan-go还使用了
|
|
||||||
https://github.com/cybozu-go/transocks/blob/master/original_dst_linux.go
|
|
||||||
|
|
||||||
Iptables
|
|
||||||
|
|
||||||
iptables配置教程:
|
|
||||||
https://toutyrater.github.io/app/tproxy.html
|
|
||||||
|
|
||||||
下面把该教程的重要部分搬过来。
|
|
||||||
|
|
||||||
|
|
||||||
ip rule add fwmark 1 table 100
|
|
||||||
ip route add local 0.0.0.0/0 dev lo table 100
|
|
||||||
|
|
||||||
iptables -t mangle -N V2RAY
|
|
||||||
iptables -t mangle -A V2RAY -d 127.0.0.1/32 -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY -d 224.0.0.0/4 -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY -d 255.255.255.255/32 -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p tcp -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
|
|
||||||
iptables -t mangle -A V2RAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
|
|
||||||
iptables -t mangle -A PREROUTING -j V2RAY
|
|
||||||
|
|
||||||
iptables -t mangle -N V2RAY_MASK
|
|
||||||
iptables -t mangle -A V2RAY_MASK -d 224.0.0.0/4 -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY_MASK -d 255.255.255.255/32 -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p tcp -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN
|
|
||||||
iptables -t mangle -A V2RAY_MASK -j RETURN -m mark --mark 0xff
|
|
||||||
iptables -t mangle -A V2RAY_MASK -p udp -j MARK --set-mark 1
|
|
||||||
iptables -t mangle -A V2RAY_MASK -p tcp -j MARK --set-mark 1
|
|
||||||
iptables -t mangle -A OUTPUT -j V2RAY_MASK
|
|
||||||
|
|
||||||
|
|
||||||
Persistant iptables
|
|
||||||
|
|
||||||
单独设置iptables,重启后会消失. 下面是持久化方法
|
|
||||||
|
|
||||||
mkdir -p /etc/iptables && iptables-save > /etc/iptables/rules.v4
|
|
||||||
|
|
||||||
vi /etc/systemd/system/tproxyrule.service
|
|
||||||
|
|
||||||
[Unit]
|
|
||||||
Description=Tproxy rule
|
|
||||||
After=network.target
|
|
||||||
Wants=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/sbin/ip rule add fwmark 1 table 100 ; /sbin/ip route add local 0.0.0.0/0 dev lo table 100 ; /sbin/iptables-restore /etc/iptables/rules.v4
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
||||||
|
|
||||||
systemctl enable tproxyrule
|
|
||||||
|
|
||||||
*/
|
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@@ -3,6 +3,8 @@ package proxy
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/e1732a364fed/v2ray_simple/httpLayer"
|
"github.com/e1732a364fed/v2ray_simple/httpLayer"
|
||||||
@@ -51,3 +53,50 @@ func LoadSimpleConfigFromStr(str string) (config SimpleConf, hasE bool, E utils.
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadSimpleConf_byFile(fpath string) (simpleConf SimpleConf, mainFallback *httpLayer.ClassicFallback, err error) {
|
||||||
|
//默认认为所有其他后缀的都是json格式,因为有时会用 server.json.vless 这种写法
|
||||||
|
// 默认所有json格式的文件都为 极简模式
|
||||||
|
|
||||||
|
var hasE bool
|
||||||
|
simpleConf, hasE, err = LoadSimpleConfigFile(fpath)
|
||||||
|
if hasE {
|
||||||
|
|
||||||
|
log.Printf("can not load simple config file: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if simpleConf.Fallbacks != nil {
|
||||||
|
mainFallback = httpLayer.NewClassicFallbackFromConfList(simpleConf.Fallbacks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if simpleConf.Client_ThatDialRemote_Url == "" {
|
||||||
|
simpleConf.Client_ThatDialRemote_Url = "direct://"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSimpleConf_byUrl(listenURL, dialURL string) (simpleConf SimpleConf, err error) {
|
||||||
|
|
||||||
|
_, err = url.Parse(listenURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("listenURL given but invalid %s %s\n", listenURL, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleConf = SimpleConf{
|
||||||
|
Server_ThatListenPort_Url: listenURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dialURL != "" {
|
||||||
|
|
||||||
|
_, err = url.Parse(dialURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("dialURL given but invalid %s %s\n", dialURL, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleConf.Client_ThatDialRemote_Url = dialURL
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@@ -27,9 +26,9 @@ type AppConf struct {
|
|||||||
UDP_timeout *int `toml:"udp_timeout"`
|
UDP_timeout *int `toml:"udp_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//标准配置。默认使用toml格式
|
//标准配置,使用toml格式。
|
||||||
// toml:https://toml.io/cn/
|
// toml:https://toml.io/cn/
|
||||||
// english: https://toml.io/en/
|
// English: https://toml.io/en/
|
||||||
type StandardConf struct {
|
type StandardConf struct {
|
||||||
App *AppConf `toml:"app"`
|
App *AppConf `toml:"app"`
|
||||||
DnsConf *netLayer.DnsConf `toml:"dns"`
|
DnsConf *netLayer.DnsConf `toml:"dns"`
|
||||||
@@ -43,7 +42,6 @@ type StandardConf struct {
|
|||||||
|
|
||||||
func LoadTomlConfStr(str string) (c StandardConf, err error) {
|
func LoadTomlConfStr(str string) (c StandardConf, err error) {
|
||||||
_, err = toml.Decode(str, &c)
|
_, err = toml.Decode(str, &c)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +57,8 @@ func LoadTomlConfFile(fileNamePath string) (StandardConf, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先检查configFileName是否存在,存在就尝试加载文件到 standardConf 或者 simpleConf,否则尝试 -L参数
|
// 先检查configFileName是否存在,存在就尝试加载文件到 standardConf 或者 simpleConf,否则尝试 listenURL, dialURL 参数.
|
||||||
|
// 若 返回的是 simpleConf, 则还可能返回 mainFallback.
|
||||||
func LoadConfig(configFileName, listenURL, dialURL string) (standardConf StandardConf, simpleConf SimpleConf, confMode int, mainFallback *httpLayer.ClassicFallback, err error) {
|
func LoadConfig(configFileName, listenURL, dialURL string) (standardConf StandardConf, simpleConf SimpleConf, confMode int, mainFallback *httpLayer.ClassicFallback, err error) {
|
||||||
|
|
||||||
fpath := utils.GetFilePath(configFileName)
|
fpath := utils.GetFilePath(configFileName)
|
||||||
@@ -113,54 +112,6 @@ func LoadConfig(configFileName, listenURL, dialURL string) (standardConf Standar
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSimpleConf_byFile(fpath string) (simpleConf SimpleConf, mainFallback *httpLayer.ClassicFallback, err error) {
|
|
||||||
//默认认为所有其他后缀的都是json格式,因为有时会用 server.json.vless 这种写法
|
|
||||||
// 默认所有json格式的文件都为 极简模式
|
|
||||||
|
|
||||||
var hasE bool
|
|
||||||
simpleConf, hasE, err = LoadSimpleConfigFile(fpath)
|
|
||||||
if hasE {
|
|
||||||
|
|
||||||
log.Printf("can not load simple config file: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if simpleConf.Fallbacks != nil {
|
|
||||||
mainFallback = httpLayer.NewClassicFallbackFromConfList(simpleConf.Fallbacks)
|
|
||||||
}
|
|
||||||
//ConfMode = 0
|
|
||||||
|
|
||||||
if simpleConf.Client_ThatDialRemote_Url == "" {
|
|
||||||
simpleConf.Client_ThatDialRemote_Url = "direct://"
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadSimpleConf_byUrl(listenURL, dialURL string) (simpleConf SimpleConf, err error) {
|
|
||||||
|
|
||||||
_, err = url.Parse(listenURL)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("listenURL given but invalid %s %s\n", listenURL, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
simpleConf = SimpleConf{
|
|
||||||
Server_ThatListenPort_Url: listenURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
if dialURL != "" {
|
|
||||||
|
|
||||||
_, err = url.Parse(dialURL)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("dialURL given but invalid %s %s\n", dialURL, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
simpleConf.Client_ThatDialRemote_Url = dialURL
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type RoutingEnv struct {
|
type RoutingEnv struct {
|
||||||
RoutePolicy *netLayer.RoutePolicy //used in passToOutClient
|
RoutePolicy *netLayer.RoutePolicy //used in passToOutClient
|
||||||
MainFallback *httpLayer.ClassicFallback //used in checkFallback in passToOutClient
|
MainFallback *httpLayer.ClassicFallback //used in checkFallback in passToOutClient
|
||||||
|
@@ -57,7 +57,7 @@ type Client interface {
|
|||||||
//规定, firstPayload 由 utils.GetMTU或者 GetPacket获取, 所以写入之后可以用 utils.PutBytes 放回
|
//规定, firstPayload 由 utils.GetMTU或者 GetPacket获取, 所以写入之后可以用 utils.PutBytes 放回
|
||||||
Handshake(underlay net.Conn, firstPayload []byte, target netLayer.Addr) (wrappedConn io.ReadWriteCloser, err error)
|
Handshake(underlay net.Conn, firstPayload []byte, target netLayer.Addr) (wrappedConn io.ReadWriteCloser, err error)
|
||||||
|
|
||||||
//建立一个通道, 然后在这个通道上 不断地申请发送到 各个远程udp地址 的连接。
|
//建立一个通道, 然后在这个通道上 不断地申请发送到 各个远程udp地址 的连接。理论上target可为空值。
|
||||||
EstablishUDPChannel(underlay net.Conn, target netLayer.Addr) (netLayer.MsgConn, error)
|
EstablishUDPChannel(underlay net.Conn, target netLayer.Addr) (netLayer.MsgConn, error)
|
||||||
|
|
||||||
//udp的拨号是否使用了多信道方式
|
//udp的拨号是否使用了多信道方式
|
||||||
|
14
tcp_test.go
14
tcp_test.go
@@ -14,19 +14,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestTCP_vless(t *testing.T) {
|
func TestTCP_vless(t *testing.T) {
|
||||||
testTCP("vless", 0, "tcp", false, t)
|
testTCP(t, "vless", 0, "tcp", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTCP_trojan(t *testing.T) {
|
func TestTCP_trojan(t *testing.T) {
|
||||||
testTCP("trojan", 0, "tcp", false, t)
|
testTCP(t, "trojan", 0, "tcp", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTCP_trojan_mux(t *testing.T) {
|
func TestTCP_trojan_mux(t *testing.T) {
|
||||||
testTCP("trojan", 0, "tcp", true, t)
|
testTCP(t, "trojan", 0, "tcp", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
//tcp测试我们直接使用http请求来测试
|
//tcp测试我们直接使用http请求来测试
|
||||||
func testTCP(protocol string, version int, network string, innermux bool, t *testing.T) {
|
func testTCP(t *testing.T, protocol string, version int, network string, innermux bool) {
|
||||||
utils.LogLevel = utils.Log_debug
|
utils.LogLevel = utils.Log_debug
|
||||||
utils.InitLog()
|
utils.InitLog()
|
||||||
|
|
||||||
@@ -133,14 +133,14 @@ protocol = "direct"
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tryGetHttp(client, "http://captive.apple.com", t)
|
tryGetHttp(t, client, "http://captive.apple.com")
|
||||||
tryGetHttp(client, "http://www.msftconnecttest.com/connecttest.txt", t)
|
tryGetHttp(t, client, "http://www.msftconnecttest.com/connecttest.txt")
|
||||||
|
|
||||||
//联通性测试 可参考 https://imldy.cn/posts/99d42f85/
|
//联通性测试 可参考 https://imldy.cn/posts/99d42f85/
|
||||||
// 用这种 captive 测试 不容易遇到 网站无法在 某些地区 如 github action 所在的地区 访问 或者卡顿等情况.
|
// 用这种 captive 测试 不容易遇到 网站无法在 某些地区 如 github action 所在的地区 访问 或者卡顿等情况.
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryGetHttp(client *http.Client, path string, t *testing.T) {
|
func tryGetHttp(t *testing.T, client *http.Client, path string) {
|
||||||
t.Log("start dial", path)
|
t.Log("start dial", path)
|
||||||
resp, err := client.Get(path)
|
resp, err := client.Get(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
13
tls_lazy.go
13
tls_lazy.go
@@ -28,17 +28,18 @@ func canLazyEncryptClient(outClient proxy.Client) bool {
|
|||||||
return outClient.IsUseTLS() && canNetwork_tlsLazy(outClient.Network()) && outClient.AdvancedLayer() == ""
|
return outClient.IsUseTLS() && canNetwork_tlsLazy(outClient.Network()) && outClient.AdvancedLayer() == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func canNetwork_tlsLazy(nw string) bool {
|
func canNetwork_tlsLazy(n string) bool {
|
||||||
switch nw {
|
switch n {
|
||||||
case "", "tcp", "tcp4", "tcp6", "unix":
|
case "", "tcp", "tcp4", "tcp6", "unix":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryTlsLazyRawCopy 尝试能否直接对拷,对拷 直接使用 原始 TCPConn,也就是裸奔转发
|
// tryTlsLazyRawCopy 尝试能否直接对拷,对拷 直接使用 原始 TCPConn,也就是裸奔转发.
|
||||||
// 如果在linux上,则和 xtls的splice 含义相同. 在其他系统时,与xtls-direct含义相同。
|
// 如果在linux上,则和 xtls的splice 含义相同. 在其他系统时,与xtls-direct含义相同。
|
||||||
// 我们内部先 使用 DetectConn进行过滤分析,然后再判断进化为splice 或者退化为普通拷贝
|
// 我们内部先 使用 DetectConn进行过滤分析,然后再判断进化为splice 或者退化为普通拷贝.
|
||||||
|
//
|
||||||
// 第一个参数仅用于 tls_lazy_secure
|
// 第一个参数仅用于 tls_lazy_secure
|
||||||
func tryTlsLazyRawCopy(useSecureMethod bool, proxy_client proxy.UserClient, proxy_server proxy.UserServer, targetAddr netLayer.Addr, wrc, wlc io.ReadWriteCloser, localConn net.Conn, isclient bool, theRecorder *tlsLayer.Recorder) {
|
func tryTlsLazyRawCopy(useSecureMethod bool, proxy_client proxy.UserClient, proxy_server proxy.UserServer, targetAddr netLayer.Addr, wrc, wlc io.ReadWriteCloser, localConn net.Conn, isclient bool, theRecorder *tlsLayer.Recorder) {
|
||||||
if ce := utils.CanLogDebug("trying tls lazy copy"); ce != nil {
|
if ce := utils.CanLogDebug("trying tls lazy copy"); ce != nil {
|
||||||
@@ -362,7 +363,9 @@ func tryTlsLazyRawCopy(useSecureMethod bool, proxy_client proxy.UserClient, prox
|
|||||||
//
|
//
|
||||||
//这是因为, 触发上一段类似代码的原因是tls 0-rtt,而 tls 0-rtt 总是由 客户端发起的
|
//这是因为, 触发上一段类似代码的原因是tls 0-rtt,而 tls 0-rtt 总是由 客户端发起的
|
||||||
log.Println("有问题, nextI > len(bs)", nextI, len(bs))
|
log.Println("有问题, nextI > len(bs)", nextI, len(bs))
|
||||||
//os.Exit(-1) //这里就暂时不要退出程序了,毕竟理论上有可能由一些黑客来触发这里。
|
|
||||||
|
//这里不应 用 os.Exit 退出程序,理论上有可能由一些黑客来触发这里, 直接退出转发过程即可。
|
||||||
|
|
||||||
localConn.Close()
|
localConn.Close()
|
||||||
rawWRC.Close()
|
rawWRC.Close()
|
||||||
return
|
return
|
||||||
|
@@ -9,6 +9,8 @@ import (
|
|||||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
//监听透明代理, 返回一个 值 用于 关闭.
|
||||||
func ListenTproxy(addr string) (tm *tproxy.Machine) {
|
func ListenTproxy(addr string) (tm *tproxy.Machine) {
|
||||||
utils.Info("Start running Tproxy")
|
utils.Info("Start running Tproxy")
|
||||||
|
|
||||||
@@ -16,7 +18,7 @@ func ListenTproxy(addr string) (tm *tproxy.Machine) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
//tproxy因为比较特殊, 不属于 proxy.Server, 需要独特的转发过程去处理.
|
//因为 tproxy比较特殊, 不属于 proxy.Server, 所以 需要 独立的 转发过程去处理.
|
||||||
lis, err := startLoopTCP(ad)
|
lis, err := startLoopTCP(ad)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ce := utils.CanLogErr("TProxy startLoopTCP failed"); ce != nil {
|
if ce := utils.CanLogErr("TProxy startLoopTCP failed"); ce != nil {
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListenTproxy(addr string) (tm *tproxy.Machine) {
|
func ListenTproxy(addr string) (_ *tproxy.Machine) {
|
||||||
utils.Warn("Tproxy not possible on non-linux device")
|
utils.Warn("Tproxy not possible on non-linux device")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
20
udp_test.go
20
udp_test.go
@@ -13,45 +13,45 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestUDP_vless(t *testing.T) {
|
func TestUDP_vless(t *testing.T) {
|
||||||
testUDP("vless", 0, "tcp", false, false, false, t)
|
testUDP(t, "vless", 0, "tcp", false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
//v0 没有fullcone
|
//v0 没有fullcone
|
||||||
|
|
||||||
func TestUDP_vless_v1(t *testing.T) {
|
func TestUDP_vless_v1(t *testing.T) {
|
||||||
testUDP("vless", 1, "tcp", false, false, false, t)
|
testUDP(t, "vless", 1, "tcp", false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_vless_v1_fullcone(t *testing.T) {
|
func TestUDP_vless_v1_fullcone(t *testing.T) {
|
||||||
testUDP("vless", 1, "tcp", false, true, false, t)
|
testUDP(t, "vless", 1, "tcp", false, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_vless_v1_udpMulti(t *testing.T) {
|
func TestUDP_vless_v1_udpMulti(t *testing.T) {
|
||||||
testUDP("vless", 1, "tcp", true, false, false, t)
|
testUDP(t, "vless", 1, "tcp", true, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_vless_v1_udpMulti_fullcone(t *testing.T) {
|
func TestUDP_vless_v1_udpMulti_fullcone(t *testing.T) {
|
||||||
testUDP("vless", 1, "tcp", true, true, false, t)
|
testUDP(t, "vless", 1, "tcp", true, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_trojan(t *testing.T) {
|
func TestUDP_trojan(t *testing.T) {
|
||||||
testUDP("trojan", 0, "tcp", false, false, false, t)
|
testUDP(t, "trojan", 0, "tcp", false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_trojan_mux(t *testing.T) {
|
func TestUDP_trojan_mux(t *testing.T) {
|
||||||
testUDP("trojan", 0, "tcp", false, false, true, t)
|
testUDP(t, "trojan", 0, "tcp", false, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_trojan_fullcone(t *testing.T) {
|
func TestUDP_trojan_fullcone(t *testing.T) {
|
||||||
testUDP("trojan", 0, "tcp", false, true, false, t)
|
testUDP(t, "trojan", 0, "tcp", false, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_trojan_through_udp(t *testing.T) {
|
func TestUDP_trojan_through_udp(t *testing.T) {
|
||||||
testUDP("trojan", 0, "udp", false, false, false, t)
|
testUDP(t, "trojan", 0, "udp", false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
//udp测试我们直接使用dns请求来测试.
|
//udp测试我们直接使用dns请求来测试.
|
||||||
func testUDP(protocol string, version int, network string, multi bool, fullcone bool, mux bool, t *testing.T) {
|
func testUDP(t *testing.T, protocol string, version int, network string, multi bool, fullcone bool, mux bool) {
|
||||||
utils.LogLevel = utils.Log_debug
|
utils.LogLevel = utils.Log_debug
|
||||||
utils.InitLog()
|
utils.InitLog()
|
||||||
|
|
||||||
|
18
utils/log.go
18
utils/log.go
@@ -1,7 +1,6 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -15,12 +14,12 @@ const (
|
|||||||
Log_info
|
Log_info
|
||||||
Log_warning
|
Log_warning
|
||||||
Log_error //error一般用于输出一些 连接错误或者客户端协议错误之类的, 但不致命
|
Log_error //error一般用于输出一些 连接错误或者客户端协议错误之类的, 但不致命
|
||||||
log_dpanic
|
Log_dpanic
|
||||||
log_panic
|
Log_panic
|
||||||
Log_fatal
|
Log_fatal
|
||||||
log_off //不支持不打印致命输出。既然致命我们一定要尸检然后查看病因啊
|
log_off //不支持不打印致命输出。既然致命 那么我们一定要尸检然后查看病因
|
||||||
|
|
||||||
DefaultLL = Log_warning
|
DefaultLL = Log_info
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogLevel 值越小越唠叨, 废话越多,值越大打印的越少,见log_开头的常量;
|
// LogLevel 值越小越唠叨, 废话越多,值越大打印的越少,见log_开头的常量;
|
||||||
@@ -37,13 +36,6 @@ var (
|
|||||||
ShouldLogToFile bool
|
ShouldLogToFile bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
flag.IntVar(&LogLevel, "ll", DefaultLL, "log level,0=debug, 1=info, 2=warning, 3=error, 4=dpanic, 5=panic, 6=fatal")
|
|
||||||
|
|
||||||
flag.StringVar(&LogOutFileName, "lf", "vs_log", "output file for log; If empty, no log file will be used.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogLevelStrList() (sl []string) {
|
func LogLevelStrList() (sl []string) {
|
||||||
sl = make([]string, 0, log_off)
|
sl = make([]string, 0, log_off)
|
||||||
for i := 0; i < log_off; i++ {
|
for i := 0; i < log_off; i++ {
|
||||||
@@ -107,7 +99,7 @@ func InitLog() {
|
|||||||
|
|
||||||
if ShouldLogToFile && LogOutFileName != "" {
|
if ShouldLogToFile && LogOutFileName != "" {
|
||||||
jsonConf := zap.NewProductionEncoderConfig()
|
jsonConf := zap.NewProductionEncoderConfig()
|
||||||
jsonConf.EncodeTime = zapcore.TimeEncoderOfLayout("060102 150405.000") //用一种比较简短的方式输出时间,年月日 时分秒.毫秒。 年只需输出后两位数字即可, 不管Y2K问题, 80年后要是还没实现网络自由那这个世界完蛋了.
|
jsonConf.EncodeTime = zapcore.TimeEncoderOfLayout("060102 150405.000") //用一种比较简短的方式输出时间,年月日 时分秒.毫秒。 年只需输出后两位数字即可, 不管Y2K问题
|
||||||
jsonConf.LevelKey = "L"
|
jsonConf.LevelKey = "L"
|
||||||
jsonConf.TimeKey = "T"
|
jsonConf.TimeKey = "T"
|
||||||
jsonConf.MessageKey = "M"
|
jsonConf.MessageKey = "M"
|
||||||
|
@@ -3,8 +3,15 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
//保证我们随机种子每次都不一样, 这样可以保证go test中使用随机端口时, 不同的test会使用不同的端口, 防止端口冲突
|
||||||
|
// 因为我们所有的包应该都引用了 utils包, 所以可以保证这一点.
|
||||||
|
rand.Seed(time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
//6-11 字节的字符串
|
//6-11 字节的字符串
|
||||||
func GenerateRandomString() string {
|
func GenerateRandomString() string {
|
||||||
|
|
@@ -4,25 +4,42 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tjarratt/babble"
|
"io/ioutil"
|
||||||
"go.uber.org/zap"
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var words []string
|
||||||
|
var getWordListFailed bool
|
||||||
|
|
||||||
func GetRandomWord() (result string) {
|
func GetRandomWord() (result string) {
|
||||||
//babbler包 在 系统中 没有 /usr/share/dict/words 且不是windows 时,会panic
|
|
||||||
defer func() {
|
|
||||||
|
|
||||||
if r := recover(); r != nil {
|
if len(words) == 0 && !getWordListFailed {
|
||||||
if ce := CanLogErr("getRandomWord babble panic"); ce != nil {
|
words = readAvailableDictionary()
|
||||||
ce.Write(zap.Any("err:", r))
|
}
|
||||||
}
|
|
||||||
|
|
||||||
result = GenerateRandomString()
|
if theLen := len(words); theLen == 0 {
|
||||||
}
|
getWordListFailed = true
|
||||||
}()
|
result = GenerateRandomString()
|
||||||
babbler := babble.NewBabbler()
|
} else {
|
||||||
babbler.Count = 1
|
result = words[rand.Int()%theLen]
|
||||||
result = babbler.Babble()
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readAvailableDictionary() (words []string) {
|
||||||
|
file, err := os.Open("/usr/share/dict/words")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
words = strings.Split(string(bytes), "\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@@ -3,19 +3,11 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"math/rand"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
//保证我们随机种子每次都不一样, 这样可以保证go test中使用随机端口时, 不同的test会使用不同的端口, 防止端口冲突
|
|
||||||
// 因为我们所有的包应该都引用了 utils包, 所以可以保证这一点.
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
// bufio.Reader 和 bytes.Buffer 都实现了 ByteReader
|
// bufio.Reader 和 bytes.Buffer 都实现了 ByteReader
|
||||||
type ByteReader interface {
|
type ByteReader interface {
|
||||||
ReadByte() (byte, error)
|
ReadByte() (byte, error)
|
||||||
|
Reference in New Issue
Block a user