全面修订代码;完成 grpcSimple包;使用 tag选择编译quic 和 grpc

grpcSimple包的服务端和客户端现在都已完成,且兼容v2ray等内核。
grpcSimple包 简洁、高效,更加科学。暂不支持multiMode。

若 grpc_full 给出,则使用grpc包,否则默认使用 grpcSimple包。
若 noquic给出,则不使用 quic,否则 默认使用 quic。

修复 ws early 失效问题;
This commit is contained in:
e1732a364fed
2022-04-28 05:41:56 +08:00
parent 4e700d7475
commit cc758dec66
31 changed files with 613 additions and 222 deletions

View File

@@ -1,7 +1,7 @@
![GoVersion][10] [![GoDoc][1]][2] [![MIT licensed][3]][4] [![Go Report Card][5]][6] [![Downloads][7]][8] [![release][9]][8] ![GoVersion][10] [![GoDoc][1]][2] [![MIT licensed][3]][4] [![Go Report Card][5]][6] [![Downloads][7]][8] [![release][9]][8]
[1]: https://godoc.org/github.com/e1732a364fed/v2ray_simple?status.svg [1]: https://pkg.go.dev/badge/github.com/e1732a364fed/v2ray_simple.svg
[2]: https://godoc.org/github.com/e1732a364fed/v2ray_simple [2]: https://pkg.go.dev/github.com/e1732a364fed/v2ray_simple#section-readme
[3]: https://img.shields.io/badge/license-MIT-blue.svg [3]: https://img.shields.io/badge/license-MIT-blue.svg
[4]: LICENSE [4]: LICENSE
[5]: https://goreportcard.com/badge/github.com/e1732a364fed/v2ray_simple [5]: https://goreportcard.com/badge/github.com/e1732a364fed/v2ray_simple

View File

@@ -4,7 +4,6 @@ package advLayer
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -13,10 +12,11 @@ import (
"github.com/e1732a364fed/v2ray_simple/utils" "github.com/e1732a364fed/v2ray_simple/utils"
) )
var ErrPreviousFull = errors.New("previous conn full") //var ErrPreviousFull = errors.New("previous conn full")
var ProtocolsMap = make(map[string]Creator) var ProtocolsMap = make(map[string]Creator)
//为了避免黑客攻击,我们固定earlydata最大值为2048
var MaxEarlyDataLen = 2048 //for ws early data var MaxEarlyDataLen = 2048 //for ws early data
func PrintAllProtocolNames() { func PrintAllProtocolNames() {
@@ -31,6 +31,9 @@ type Creator interface {
//NewClientFromURL(url *url.URL) (Client, error) //NewClientFromURL(url *url.URL) (Client, error)
NewClientFromConf(conf *Conf) (Client, error) NewClientFromConf(conf *Conf) (Client, error)
NewServerFromConf(conf *Conf) (Server, error) NewServerFromConf(conf *Conf) (Server, error)
GetDefaultAlpn() (alpn string, mustUse bool)
PackageID() string
} }
type Conf struct { type Conf struct {
@@ -45,12 +48,12 @@ type Conf struct {
} }
type Client interface { type Client interface {
GetPath() string
IsMux() bool //quic and grpc. if IsMux, then Client is a MuxClient, or it's a SingleClient IsMux() bool //quic and grpc. if IsMux, then Client is a MuxClient, or it's a SingleClient
IsEarly() bool //is 0-rtt or not.
IsSuper() bool // quic handles transport layer dialing and tls layer handshake directly. IsSuper() bool // quic handles transport layer dialing and tls layer handshake directly.
GetPath() string
IsEarly() bool //is 0-rtt or not.
} }
// ws (h1.1) // ws (h1.1)
@@ -68,6 +71,7 @@ type MuxClient interface {
// If IsSuper, underlay should be nil; // If IsSuper, underlay should be nil;
// //
// If not IsSuper and underlay == nil, it will return error if it can't find any extablished connection. // If not IsSuper and underlay == nil, it will return error if it can't find any extablished connection.
// Usually underlay is tls.Conn.
GetCommonConn(underlay net.Conn) (conn any, err error) GetCommonConn(underlay net.Conn) (conn any, err error)
DialSubConn(underlay any) (net.Conn, error) DialSubConn(underlay any) (net.Conn, error)
@@ -76,11 +80,11 @@ type MuxClient interface {
} }
type Server interface { type Server interface {
IsMux() bool //quic and grpc. if IsMux, then Server is a MuxServer, or it's a SingleServer
IsSuper() bool //quic
GetPath() string //for ws and grpc GetPath() string //for ws and grpc
IsMux() bool //quic and grpc. if IsMux, then Server is a MuxServer, or it's a SingleServer
IsSuper() bool //quic
} }
//ws //ws

View File

@@ -166,12 +166,14 @@ func (c *streamClient) multitun_withName(ctx context.Context, name string, opts
type Client struct { type Client struct {
ServerAddr netLayer.Addr ServerAddr netLayer.Addr
Path string Path string
ismulti bool
} }
func NewClient(addr netLayer.Addr, path string) (*Client, error) { func NewClient(addr netLayer.Addr, path string, ismulti bool) (*Client, error) {
return &Client{ return &Client{
ServerAddr: addr, ServerAddr: addr,
Path: path, Path: path,
ismulti: ismulti,
}, nil }, nil
} }
@@ -211,7 +213,7 @@ func (c *Client) DialSubConn(underlay any) (net.Conn, error) {
return nil, utils.ErrNilParameter return nil, utils.ErrNilParameter
} }
if cc, ok := underlay.(ClientConn); ok { if cc, ok := underlay.(ClientConn); ok {
return DialNewSubConn(c.Path, cc, &c.ServerAddr, false) return DialNewSubConn(c.Path, cc, &c.ServerAddr, c.ismulti)
} else { } else {
return nil, utils.ErrInvalidParameter return nil, utils.ErrInvalidParameter
} }

View File

@@ -24,7 +24,9 @@ https://github.com/v2fly/v2ray-core/pull/757
*/ */
package grpc package grpc
import "github.com/e1732a364fed/v2ray_simple/advLayer" import (
"github.com/e1732a364fed/v2ray_simple/advLayer"
)
func init() { func init() {
advLayer.ProtocolsMap["grpc"] = Creator{} advLayer.ProtocolsMap["grpc"] = Creator{}
@@ -32,8 +34,27 @@ func init() {
type Creator struct{} type Creator struct{}
func (Creator) PackageID() string {
return "grpc"
}
func (Creator) GetDefaultAlpn() (alpn string, mustUse bool) {
// v2ray 和 xray 的grpc 因为没有自己处理tls直接用grpc包处理的tls而grpc包对alpn有严格要求, 要用h2.
return "h2", true
}
func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) { func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
return NewClient(conf.Addr, conf.Path) grpc_multi := false
if extra := conf.Extra; len(extra) > 0 {
if thing := extra["grpc_multi"]; thing != nil {
if use_multi, ok := thing.(bool); ok {
grpc_multi = use_multi
}
}
}
return NewClient(conf.Addr, conf.Path, grpc_multi)
} }
func (Creator) NewServerFromConf(conf *advLayer.Conf) (advLayer.Server, error) { func (Creator) NewServerFromConf(conf *advLayer.Conf) (advLayer.Server, error) {

View File

@@ -5,20 +5,19 @@ package grpcSimple
import ( import (
"bufio" "bufio"
"context"
"crypto/tls" "crypto/tls"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"net/http" "net/http"
"net/url" "strings"
"sync" "sync"
"time" "time"
"github.com/e1732a364fed/v2ray_simple/utils" "github.com/e1732a364fed/v2ray_simple/utils"
"go.uber.org/atomic" "go.uber.org/atomic"
"go.uber.org/zap"
"golang.org/x/net/http2" "golang.org/x/net/http2"
) )
@@ -26,9 +25,10 @@ var (
ErrInvalidLength = errors.New("invalid length") ErrInvalidLength = errors.New("invalid length")
) )
var defaultHeader = http.Header{ var defaultClientHeader = http.Header{
"content-type": []string{"application/grpc"}, "content-type": []string{"application/grpc"},
"user-agent": []string{"grpc-go/1.36.0"}, "user-agent": []string{"grpc-go/1.41.0"},
"Te": []string{"trailers"},
} }
//implements net.Conn //implements net.Conn
@@ -44,7 +44,9 @@ type ClientConn struct {
br *bufio.Reader br *bufio.Reader
// deadlines // deadlines
deadline *time.Timer timeouter
client *Client
} }
type Config struct { type Config struct {
@@ -52,7 +54,7 @@ type Config struct {
Host string Host string
} }
func (g *ClientConn) initRequest() { func (g *ClientConn) handshake() {
response, err := g.transport.RoundTrip(g.request) response, err := g.transport.RoundTrip(g.request)
if err != nil { if err != nil {
g.err = err g.err = err
@@ -61,15 +63,33 @@ func (g *ClientConn) initRequest() {
} }
if !g.close.Load() { if !g.close.Load() {
//log.Println("response headers", response.Header)
if ct := response.Header.Get("Content-Type"); ct != "application/grpc" {
if ce := utils.CanLogWarn("GRPC Client got wrong Content-Type"); ce != nil {
ce.Write(zap.String("type", ct))
}
g.client.cachedTransport = nil
response.Body.Close()
return
}
g.response = response g.response = response
g.br = bufio.NewReader(response.Body) g.br = bufio.NewReader(response.Body)
} else { } else {
g.client.cachedTransport = nil
response.Body.Close() response.Body.Close()
} }
} }
func (g *ClientConn) Read(b []byte) (n int, err error) { func (g *ClientConn) Read(b []byte) (n int, err error) {
g.once.Do(g.initRequest)
g.once.Do(g.handshake)
if g.err != nil { if g.err != nil {
return 0, g.err return 0, g.err
} }
@@ -133,6 +153,10 @@ func (g *ClientConn) Write(b []byte) (n int, err error) {
if err == io.ErrClosedPipe && g.err != nil { if err == io.ErrClosedPipe && g.err != nil {
err = g.err err = g.err
} }
if err != nil {
g.client.dealErr(err)
}
return len(b), err return len(b), err
} }
@@ -146,110 +170,101 @@ func (g *ClientConn) Close() error {
return g.writer.Close() return g.writer.Close()
} }
func (g *ClientConn) LocalAddr() net.Addr { return nil }
func (g *ClientConn) RemoteAddr() net.Addr { return nil }
func (g *ClientConn) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) }
func (g *ClientConn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }
func (g *ClientConn) SetDeadline(t time.Time) error {
d := time.Until(t)
if g.deadline != nil {
g.deadline.Reset(d)
return nil
}
g.deadline = time.AfterFunc(d, func() {
g.Close()
})
return nil
}
const tlsTimeout = time.Second * 5 const tlsTimeout = time.Second * 5
func NewHTTP2Client( type Client struct {
rawTCPConn net.Conn, Config
tlsConfig *tls.Config,
) *http2.Transport {
dialFunc := func(_, _ string, cfg *tls.Config) (net.Conn, error) { curBaseConn net.Conn //一般为 tlsConn
cn := tls.Client(rawTCPConn, cfg) theRequest http.Request
ctx, cancel := context.WithTimeout(context.Background(), tlsTimeout) cachedTransport *http2.Transport
defer cancel()
if err := cn.HandshakeContext(ctx); err != nil {
rawTCPConn.Close()
return nil, err
}
state := cn.ConnectionState()
if p := state.NegotiatedProtocol; p != http2.NextProtoTLS {
cn.Close()
return nil, utils.ErrInErr{
ErrDesc: "grpcHardcore, http2: unexpected ALPN protocol",
ErrDetail: utils.ErrInvalidData,
Data: p,
}
}
return cn, nil
}
return &http2.Transport{ path string
DialTLS: dialFunc, }
TLSClientConfig: tlsConfig,
AllowHTTP: false, func (g *Client) dealErr(err error) {
DisableCompression: true, //use of closed connection
PingTimeout: 0, if strings.Contains(err.Error(), "use of closed") {
g.cachedTransport = nil
} }
} }
func StreamGunWithTransport(transport *http2.Transport, cfg *Config) (net.Conn, error) { func (c *Client) GetPath() string {
serviceName := "GunService" return c.ServiceName
if cfg.ServiceName != "" { }
serviceName = cfg.ServiceName
func (c *Client) IsSuper() bool {
return false
}
func (c *Client) IsMux() bool {
return true
}
func (c *Client) IsEarly() bool {
return false
}
// 由于 本包应用了 http2包, 无法获取特定连接, 所以返回 underlay 本身
func (c *Client) GetCommonConn(underlay net.Conn) (any, error) {
if underlay == nil {
if c.cachedTransport != nil {
return c.cachedTransport, nil
} else {
return nil, nil
}
} else {
return underlay, nil
}
}
func (c *Client) ProcessWhenFull(underlay any) {}
func (c *Client) DialSubConn(underlay any) (net.Conn, error) {
if underlay == nil {
return nil, utils.ErrNilParameter
}
var transport *http2.Transport
if t, ok := underlay.(*http2.Transport); ok && t != nil {
transport = t
} else {
transport = &http2.Transport{
DialTLS: func(_, _ string, cfg *tls.Config) (net.Conn, error) {
return underlay.(net.Conn), nil
},
AllowHTTP: false,
DisableCompression: true,
PingTimeout: 0,
}
c.cachedTransport = transport
} }
reader, writer := io.Pipe() reader, writer := io.Pipe()
request := &http.Request{
Method: http.MethodPost, request := c.theRequest
Body: reader, request.Body = reader
URL: &url.URL{
Scheme: "https",
Host: cfg.Host,
Path: fmt.Sprintf("/%s/Tun", serviceName),
// for unescape path
Opaque: fmt.Sprintf("//%s/%s/Tun", cfg.Host, serviceName),
},
Proto: "HTTP/2",
ProtoMajor: 2,
ProtoMinor: 0,
Header: defaultHeader,
}
conn := &ClientConn{ conn := &ClientConn{
request: request, request: &request,
transport: transport, transport: transport,
writer: writer, writer: writer,
close: atomic.NewBool(false), close: atomic.NewBool(false),
client: c,
}
conn.timeouter = timeouter{
closeFunc: func() {
conn.Close()
},
} }
go conn.once.Do(conn.initRequest) go conn.once.Do(conn.handshake) //necessary
return conn, nil return conn, nil
} }
func GetNewClientStream(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) {
transport := NewHTTP2Client(conn, tlsConfig)
return StreamGunWithTransport(transport, cfg)
}
func GetNewClientStream_withTlsConn(conn net.Conn, cfg *Config) (net.Conn, error) {
transport := &http2.Transport{
DialTLS: func(_, _ string, cfg *tls.Config) (net.Conn, error) {
return conn, nil
},
AllowHTTP: false,
DisableCompression: true,
PingTimeout: 0,
}
return StreamGunWithTransport(transport, cfg)
}

View File

@@ -1,7 +0,0 @@
// Package grpcHardcore implements grpc tunnel without importing google.golang.org/grpc.
//
//Reference
//
// https://github.com/Dreamacro/clash/blob/master/transport/gun/gun.go, which is under MIT license
//
package grpcSimple

View File

@@ -0,0 +1,79 @@
// Package grpcHardcore implements grpc tunnel without importing google.golang.org/grpc.
//
//Reference
//
// https://github.com/Dreamacro/clash/blob/master/transport/gun/gun.go, which is under MIT license
//
// 在 clash的客户端实现 的 基础上 继续用 golang的 http2包 实现了 grpc 的 基本服务端,并改进了 原代码。
//
// grpcSimple包 比grpc包 小很多替代grpc包的话可以减小 4MB 左右的可执行文件大小。但是目前不支持 multiMode。
//
// grpcSimple包 是很棒 很有用的 实现,未来可以添加 针对 grpc的ServiceName的回落。
package grpcSimple
import (
"fmt"
"net/http"
"net/url"
"github.com/e1732a364fed/v2ray_simple/advLayer"
)
func init() {
advLayer.ProtocolsMap["grpc"] = Creator{}
}
type Creator struct{}
func (Creator) PackageID() string {
return "grpcSimple"
}
func (Creator) GetDefaultAlpn() (alpn string, mustUse bool) {
// v2ray 和 xray 的grpc 因为没有自己处理tls直接用grpc包处理的tls而grpc包对alpn有严格要求, 要用h2.
return "h2", true
}
func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
serviceName := "GunService"
if conf.Path != "" {
serviceName = conf.Path
}
c := &Client{
Config: Config{
ServiceName: serviceName,
Host: conf.Host,
},
path: fmt.Sprintf("/%s/Tun", conf.Path),
}
c.theRequest = http.Request{
Method: http.MethodPost,
URL: &url.URL{
Scheme: "https",
Host: c.Host,
Path: c.path,
// for unescape path
Opaque: fmt.Sprintf("//%s/%s/Tun", c.Host, c.ServiceName),
},
Proto: "HTTP/2",
ProtoMajor: 2,
ProtoMinor: 0,
Header: defaultClientHeader,
}
return c, nil
}
func (Creator) NewServerFromConf(conf *advLayer.Conf) (advLayer.Server, error) {
s := &Server{
Config: Config{
ServiceName: conf.Path,
Host: conf.Host,
},
path: fmt.Sprintf("/%s/Tun", conf.Path),
}
return s, nil
}

View File

@@ -1 +1,169 @@
package grpcSimple package grpcSimple
import (
"bufio"
"encoding/binary"
"io"
"net"
"net/http"
"sync"
"github.com/e1732a364fed/v2ray_simple/utils"
"go.uber.org/zap"
"golang.org/x/net/http2"
)
type Server struct {
Config
http2.Server
path string
}
func (s *Server) GetPath() string {
return s.ServiceName
}
func (*Server) IsMux() bool {
return true
}
func (*Server) IsSuper() bool {
return false
}
func (s *Server) StartHandle(underlay net.Conn, newSubConnChan chan net.Conn) {
go s.Server.ServeConn(underlay, &http2.ServeConnOpts{
Handler: http.HandlerFunc(func(rw http.ResponseWriter, rq *http.Request) {
//log.Println("request headers", rq.Header)
//TODO: support fallback
if rq.URL.Path != s.path {
if ce := utils.CanLogWarn("grpc Server got wrong path"); ce != nil {
ce.Write(zap.String("path", rq.URL.Path))
}
return
}
if ct := rq.Header.Get("Content-Type"); ct != "application/grpc" {
if ce := utils.CanLogWarn("GRPC Server got wrong Content-Type"); ce != nil {
ce.Write(zap.String("type", ct))
}
return
}
//https://dzone.com/articles/learning-about-the-headers-used-for-grpc-over-http
headerMap := rw.Header()
headerMap.Add("Content-Type", "application/grpc") //necessary
rw.WriteHeader(http.StatusOK)
cc := make(chan int)
sc := &ServerConn{
br: bufio.NewReader(rq.Body),
Writer: rw,
Closer: rq.Body,
closeChan: cc,
}
sc.timeouter = timeouter{
closeFunc: func() {
sc.Close()
},
}
newSubConnChan <- sc
<-cc //necessary
}),
})
}
type ServerConn struct {
io.Closer
io.Writer
remain int
br *bufio.Reader
once sync.Once
closeChan chan int
timeouter
}
func (g *ServerConn) Close() error {
g.once.Do(func() {
close(g.closeChan)
g.Closer.Close()
})
return nil
}
func (g *ServerConn) Read(b []byte) (n int, err error) {
if g.remain > 0 {
size := g.remain
if len(b) < size {
size = len(b)
}
n, err = io.ReadFull(g.br, b[:size])
g.remain -= n
return
}
_, err = g.br.Discard(6)
if err != nil {
return 0, err
}
protobufPayloadLen, err := binary.ReadUvarint(g.br)
if err != nil {
return 0, ErrInvalidLength
}
size := int(protobufPayloadLen)
if len(b) < size {
size = len(b)
}
n, err = io.ReadFull(g.br, b[:size])
if err != nil {
return
}
remain := int(protobufPayloadLen) - n
if remain > 0 {
g.remain = remain
}
return n, nil
}
func (g *ServerConn) Write(b []byte) (n int, err error) {
protobufHeader := [binary.MaxVarintLen64 + 1]byte{0x0A}
varuintSize := binary.PutUvarint(protobufHeader[1:], uint64(len(b)))
grpcHeader := make([]byte, 5)
grpcPayloadLen := uint32(varuintSize + 1 + len(b))
binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen)
buf := utils.GetBuf()
defer utils.PutBuf(buf)
buf.Write(grpcHeader)
buf.Write(protobufHeader[:varuintSize+1])
buf.Write(b)
_, err = g.Writer.Write(buf.Bytes())
if err == nil {
g.Writer.(http.Flusher).Flush() //necessary
}
return len(b), err
}

View File

@@ -0,0 +1,42 @@
package grpcSimple
import (
"net"
"time"
)
type timeouter struct {
deadline *time.Timer
closeFunc func()
}
func (g *timeouter) LocalAddr() net.Addr { return nil }
func (g *timeouter) RemoteAddr() net.Addr { return nil }
func (g *timeouter) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) }
func (g *timeouter) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }
func (g *timeouter) SetDeadline(t time.Time) error {
var d time.Duration
if g.deadline != nil {
if t == (time.Time{}) {
g.deadline.Stop()
return nil
}
g.deadline.Reset(d)
return nil
} else {
if t == (time.Time{}) {
return nil
}
d = time.Until(t)
}
g.deadline = time.AfterFunc(d, g.closeFunc)
return nil
}

View File

@@ -57,6 +57,7 @@ func CloseConn(baseC any) {
} }
var ( var (
//h3
DefaultAlpnList = []string{"h3"} DefaultAlpnList = []string{"h3"}
common_ListenConfig = quic.Config{ common_ListenConfig = quic.Config{
@@ -78,6 +79,14 @@ var (
type Creator struct{} type Creator struct{}
func (Creator) GetDefaultAlpn() (alpn string, mustUse bool) {
return "h3", false
}
func (Creator) PackageID() string {
return "quic"
}
func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) { func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
var alpn []string var alpn []string
if conf.TlsConf != nil { if conf.TlsConf != nil {

View File

@@ -15,9 +15,6 @@ import (
"github.com/gobwas/ws/wsutil" "github.com/gobwas/ws/wsutil"
) )
//为了避免黑客攻击,我们固定earlydata最大值为2048
const MaxEarlyDataLen = 2048
//implements advLayer.Client //implements advLayer.Client
type Client struct { type Client struct {
requestURL *url.URL //因为调用gobwas/ws.Dialer.Upgrade 时要传入url所以我们直接提供包装好的即可 requestURL *url.URL //因为调用gobwas/ws.Dialer.Upgrade 时要传入url所以我们直接提供包装好的即可

View File

@@ -28,12 +28,13 @@ type Server struct {
} }
// 这里默认: 传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查. // 这里默认: 传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查.
func NewServer(path string, headers map[string][]string) *Server { func NewServer(path string, headers map[string][]string, UseEarlyData bool) *Server {
return &Server{ return &Server{
//upgrader: upgrader, //upgrader: upgrader,
Thepath: path, Thepath: path,
headers: headers, headers: headers,
UseEarlyData: UseEarlyData,
} }
} }

View File

@@ -48,5 +48,11 @@ func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
} }
func (Creator) NewServerFromConf(conf *advLayer.Conf) (advLayer.Server, error) { func (Creator) NewServerFromConf(conf *advLayer.Conf) (advLayer.Server, error) {
return NewServer(conf.Path, conf.Headers), nil return NewServer(conf.Path, conf.Headers, conf.IsEarly), nil
}
func (Creator) GetDefaultAlpn() (alpn string, mustUse bool) {
return
}
func (Creator) PackageID() string {
return "ws"
} }

View File

@@ -46,7 +46,7 @@ func TestWs(t *testing.T) {
return return
} }
s := ws.NewServer(wsPath, nil) s := ws.NewServer(wsPath, nil, false)
wsConn, err := s.Handshake(nil, conn) wsConn, err := s.Handshake(nil, conn)
if err != nil { if err != nil {

8
adv_grpc.go Normal file
View File

@@ -0,0 +1,8 @@
//go:build grpc_full
package v2ray_simple
import _ "github.com/e1732a364fed/v2ray_simple/advLayer/grpc"
//默认使用 grpcSimple除非编译时 使用 grpc_full 这个 tag, 才会使用 grpc 包。
// go build -tags=grpc_full

5
adv_grpcSimple.go Normal file
View File

@@ -0,0 +1,5 @@
//go:build !grpc_full
package v2ray_simple
import _ "github.com/e1732a364fed/v2ray_simple/advLayer/grpcSimple"

7
adv_quic.go Normal file
View File

@@ -0,0 +1,7 @@
//go:build !noquic
package v2ray_simple
import _ "github.com/e1732a364fed/v2ray_simple/advLayer/quic"
// 如果不引用 quicgo build 编译出的可执行文件 的大小 可以减小 2MB 。

View File

@@ -9,7 +9,6 @@ import (
"strings" "strings"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
"github.com/e1732a364fed/v2ray_simple/advLayer/quic"
"github.com/e1732a364fed/v2ray_simple/netLayer" "github.com/e1732a364fed/v2ray_simple/netLayer"
"github.com/e1732a364fed/v2ray_simple/proxy" "github.com/e1732a364fed/v2ray_simple/proxy"
"github.com/e1732a364fed/v2ray_simple/proxy/trojan" "github.com/e1732a364fed/v2ray_simple/proxy/trojan"
@@ -80,41 +79,6 @@ func init() {
}, },
}) })
cliCmdList = append(cliCmdList, CliCmd{
"调节hy手动挡", func() {
var arr = []string{"加速", "减速", "当前状态", "讲解"}
Select := promptui.Select{
Label: "请选择",
Items: arr,
}
for {
i, result, err := Select.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("你选择了 %s\n", result)
switch i {
case 0:
quic.TheCustomRate -= 0.1
fmt.Printf("调好了!当前rate %f\n", quic.TheCustomRate)
case 1:
quic.TheCustomRate += 0.1
fmt.Printf("调好了!当前rate %f\n", quic.TheCustomRate)
case 2:
fmt.Printf("当前rate %f\n", quic.TheCustomRate)
case 3:
fmt.Printf("rate越小越加速, rate越大越减速. 最小0.2最大1.5。实际速度倍率为 1.5/rate \n")
}
}
},
})
} }
type CliCmd struct { type CliCmd struct {

View File

@@ -0,0 +1,49 @@
//go:build !noquic
package main
import (
"fmt"
"github.com/e1732a364fed/v2ray_simple/advLayer/quic"
"github.com/manifoldco/promptui"
)
func init() {
cliCmdList = append(cliCmdList, CliCmd{
"调节hy手动挡", func() {
var arr = []string{"加速", "减速", "当前状态", "讲解"}
Select := promptui.Select{
Label: "请选择",
Items: arr,
}
for {
i, result, err := Select.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("你选择了 %s\n", result)
switch i {
case 0:
quic.TheCustomRate -= 0.1
fmt.Printf("调好了!当前rate %f\n", quic.TheCustomRate)
case 1:
quic.TheCustomRate += 0.1
fmt.Printf("调好了!当前rate %f\n", quic.TheCustomRate)
case 2:
fmt.Printf("当前rate %f\n", quic.TheCustomRate)
case 3:
fmt.Printf("rate越小越加速, rate越大越减速. 最小0.2最大1.5。实际速度倍率为 1.5/rate \n")
}
}
},
})
}

View File

@@ -14,6 +14,7 @@ import (
"fmt" "fmt"
"runtime" "runtime"
"github.com/e1732a364fed/v2ray_simple/advLayer"
"github.com/e1732a364fed/v2ray_simple/netLayer" "github.com/e1732a364fed/v2ray_simple/netLayer"
) )
@@ -22,7 +23,13 @@ const delimiter = "===============================\n"
var Version string = "[version_undefined]" //版本号可由 -ldflags "-X 'main.Version=v1.x.x'" 指定, 本项目的Makefile就是用这种方式确定版本号 var Version string = "[version_undefined]" //版本号可由 -ldflags "-X 'main.Version=v1.x.x'" 指定, 本项目的Makefile就是用这种方式确定版本号
func versionStr() string { func versionStr() string {
return fmt.Sprintf("verysimple %s, %s %s %s", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) //verysimple 可以用 noquic, grpc_full tag 来选择性加载 advLayer的一些包所以需要注明编译使用了哪些包
var advList []string
for _, c := range advLayer.ProtocolsMap {
advList = append(advList, c.PackageID())
}
return fmt.Sprintf("verysimple %s, %s %s %s, with advLayer packages: %v", Version, runtime.Version(), runtime.GOOS, runtime.GOARCH, advList)
} }
func printVersion_simple() { func printVersion_simple() {

View File

@@ -5,7 +5,7 @@ port = 10800
[[dial]] [[dial]]
protocol = "trojans" # 还是为了简便直接加了尾缀s 表示使用tls. 虽然trojan强制tls, 但是我们很灵活, 自行可以选择是否开启tls. protocol = "trojans" # 还是为了简便直接加了尾缀s 表示使用tls. 虽然trojan强制tls, 但是vs很灵活, 自行可以选择是否开启tls.
uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # trojan的"password",我们填写到uuid项里. 实际上trojan这个password不要求格式, 所以你可以乱写,甚至可以写成一个中文字符串, 不过我们作为示例就统一用 示例的uuid了 uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # trojan的"password",我们填写到uuid项里. 实际上trojan这个password不要求格式, 所以你可以乱写,甚至可以写成一个中文字符串, 不过我们作为示例就统一用 示例的uuid了
ip = "127.0.0.1" ip = "127.0.0.1"
host = "your-domain-name.com" # trojan-go 的服务端要求指定一个sni 并与服务端的配置相匹配, 否则会trojan-go 会拒绝连接 host = "your-domain-name.com" # trojan-go 的服务端要求指定一个sni 并与服务端的配置相匹配, 否则会trojan-go 会拒绝连接
@@ -13,6 +13,9 @@ port = 4434
insecure = true insecure = true
utls = true utls = true
# use_mux = true # 只需要客户端指明 use_mux 即可开启mux, 服务端自动适配. #advancedLayer = "ws"
#path = "/ohmygod_verysimple_is_very_simple"
#early = true # websocket early data 功能 即0-rtt
#use_mux = true # 只需要客户端指明 use_mux 即可开启mux, 服务端自动适配.
# 备注: trojan 也是一样可以应用 ws/grpc/quic 的,具体你只要参考对应示例文件即可,然后把 vlesss 改成 trojans 即可. # 备注: trojan 也是一样可以应用 ws/grpc/quic 的,具体你只要参考对应示例文件即可,然后把 vlesss 改成 trojans 即可.

View File

@@ -8,6 +8,9 @@ insecure = true
fallback = ":80" fallback = ":80"
cert = "cert.pem" cert = "cert.pem"
key = "cert.key" key = "cert.key"
#advancedLayer = "ws"
#path = "/ohmygod_verysimple_is_very_simple"
#early = true
[[dial]] [[dial]]
protocol = "direct" protocol = "direct"

4
go.mod
View File

@@ -16,8 +16,10 @@ require (
github.com/refraction-networking/utls v1.0.0 github.com/refraction-networking/utls v1.0.0
github.com/xtaci/smux v1.5.16 github.com/xtaci/smux v1.5.16
github.com/yl2chen/cidranger v1.0.2 github.com/yl2chen/cidranger v1.0.2
go.uber.org/atomic v1.7.0
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/net v0.0.0-20211112202133-69e39bad7dc2
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 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
@@ -37,11 +39,9 @@ require (
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.1 // 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
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/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

23
main.go
View File

@@ -21,8 +21,6 @@ import (
"github.com/e1732a364fed/v2ray_simple/tlsLayer" "github.com/e1732a364fed/v2ray_simple/tlsLayer"
"github.com/e1732a364fed/v2ray_simple/utils" "github.com/e1732a364fed/v2ray_simple/utils"
_ "github.com/e1732a364fed/v2ray_simple/advLayer/grpc"
_ "github.com/e1732a364fed/v2ray_simple/advLayer/quic"
_ "github.com/e1732a364fed/v2ray_simple/advLayer/ws" _ "github.com/e1732a364fed/v2ray_simple/advLayer/ws"
_ "github.com/e1732a364fed/v2ray_simple/proxy/dokodemo" _ "github.com/e1732a364fed/v2ray_simple/proxy/dokodemo"
@@ -1030,6 +1028,8 @@ func dialClient(targetAddr netLayer.Addr,
advLayerHandshakeStep: advLayerHandshakeStep:
//var firstPayloadAlreadyDealt bool
if adv != "" { if adv != "" {
switch adv { switch adv {
case "quic": case "quic":
@@ -1116,7 +1116,11 @@ advLayerHandshakeStep:
//若配置了 MaxEarlyDataLen则我们先读一段; //若配置了 MaxEarlyDataLen则我们先读一段;
edBuf := utils.GetPacket() edBuf := utils.GetPacket()
edBuf = edBuf[:advLayer.MaxEarlyDataLen] edBuf = edBuf[:advLayer.MaxEarlyDataLen]
wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
n, e := wlc.Read(edBuf) n, e := wlc.Read(edBuf)
wlc.SetReadDeadline(time.Time{})
if e != nil { if e != nil {
if ce := utils.CanLogErr("failed to read ws early data"); ce != nil { if ce := utils.CanLogErr("failed to read ws early data"); ce != nil {
ce.Write(zap.Error(e)) ce.Write(zap.Error(e))
@@ -1124,7 +1128,11 @@ advLayerHandshakeStep:
result = -1 result = -1
return return
} }
ed = edBuf[:n] if n > 0 {
//firstPayloadAlreadyDealt = true
ed = edBuf[:n]
}
if ce := utils.CanLogDebug("will send early data"); ce != nil { if ce := utils.CanLogDebug("will send early data"); ce != nil {
ce.Write( ce.Write(
@@ -1165,11 +1173,16 @@ advLayerHandshakeStep:
//udp但是有innermux时 依然用handshake, 而不是 EstablishUDPChannel //udp但是有innermux时 依然用handshake, 而不是 EstablishUDPChannel
var firstPayload []byte var firstPayload []byte
if !hasInnerMux { //如果有内层mux要在dialInnerProxy函数里再读 //读取firstPayload
if !hasInnerMux {
//如果有内层mux要在dialInnerProxy函数里再读, 而不是在这里读
firstPayload = utils.GetMTU() firstPayload = utils.GetMTU()
wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout)) wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
n, err := wlc.Read(firstPayload) n, err := wlc.Read(firstPayload)
wlc.SetReadDeadline(time.Time{})
if err != nil { if err != nil {
if !errors.Is(err, os.ErrDeadlineExceeded) { if !errors.Is(err, os.ErrDeadlineExceeded) {
@@ -1194,7 +1207,7 @@ advLayerHandshakeStep:
} }
} }
wlc.SetReadDeadline(time.Time{})
firstPayload = firstPayload[:n] firstPayload = firstPayload[:n]
} }

View File

@@ -37,16 +37,8 @@ func (addr *Addr) Dial() (net.Conn, error) {
tcp: tcp:
//dialer := &net.Dialer{
// Timeout: time.Second * 16,
//}
//本以为直接用 DialTCP 可以加速拨号结果发现go官方包内部依然还是把地址转换回字符串再拨号 //本以为直接用 DialTCP 可以加速拨号结果发现go官方包内部依然还是把地址转换回字符串再拨号
//另外,为了为以后支持 tproxy、bindToDevice、SO_MARK 作准备,我们还是要选择性使用 net.Dialer.
//fastopen 不予支持, 因为自己客户端在重重网关之下不可能让层层网关都支持tcp fast open
// 而自己的远程节点的话因为本来网速就很快, 也不需要fastopen总之 因为木桶原理,慢的地方在我们层层网关, 所以fastopen 意义不大.
if addr.IP != nil { if addr.IP != nil {
if addr.IP.To4() == nil { if addr.IP.To4() == nil {
if !machineCanConnectToIpv6 { if !machineCanConnectToIpv6 {

View File

@@ -1,9 +1,9 @@
/* /*
Package netLayer contains definitions in network layer AND transport layer. Package netLayer contains definitions in network layer AND transport layer.
本包有 geoip, geosite, udp, readv, splice, relay, route, dns 等相关功能。 本包有 geoip, geosite, route, udp, readv, splice, relay, dns, listen/dial/sockopt 等相关功能。
以后如果要添加 kcp 或 raw socket 等底层协议时,或者要控制tcp/udp拨号的细节时也要在此包里实现. 以后如果要添加 kcp 或 raw socket 等底层协议时,也要在此包里实现.
*/ */
package netLayer package netLayer
@@ -54,7 +54,12 @@ func HasIpv6Interface() bool {
// According to godoc, If ip is not an IPv4 address, To4 returns nil. // According to godoc, If ip is not an IPv4 address, To4 returns nil.
// This means it's ipv6 // This means it's ipv6
if ipnet.IP.To4() == nil { if ipnet.IP.To4() == nil {
utils.Debug("Has Ipv6Interface!")
if ce := utils.CanLogDebug("Has Ipv6Interface!"); ce != nil {
ce.Write()
} else {
log.Println("Has Ipv6Interface!")
}
return true return true
} }

View File

@@ -154,6 +154,7 @@ func ReadBuffersFrom(c io.Reader, rawReadConn syscall.RawConn, mr utils.MultiRea
return return
} }
// if r!=0, then it means c can be used in readv. 1 means syscall.RawConn, 2 means utils.MultiReader
func IsConnGoodForReadv(c net.Conn) (r int, rawReadConn syscall.RawConn, mr utils.MultiReader) { func IsConnGoodForReadv(c net.Conn) (r int, rawReadConn syscall.RawConn, mr utils.MultiReader) {
rawReadConn = GetRawConn(c) rawReadConn = GetRawConn(c)
var ok bool var ok bool

View File

@@ -10,6 +10,10 @@ type Sockopt struct {
TProxy bool `toml:"tproxy"` TProxy bool `toml:"tproxy"`
Somark int `toml:"mark"` Somark int `toml:"mark"`
Device string `toml:"device"` Device string `toml:"device"`
//fastopen 不予支持, 因为自己客户端在重重网关之下不可能让层层网关都支持tcp fast open
// 而自己的远程节点的话因为本来网速就很快, 也不需要fastopen总之 因为木桶原理,慢的地方在我们层层网关, 所以fastopen 意义不大.
} }
//net.TCPListener, net.UnixListener //net.TCPListener, net.UnixListener
@@ -32,5 +36,3 @@ func SetSockOptForListener(tcplistener ListenerWithFile, sockopt *Sockopt, isudp
defer fileDescriptorSource.Close() defer fileDescriptorSource.Close()
SetSockOpt(int(fileDescriptorSource.Fd()), sockopt, isudp, isipv6) SetSockOpt(int(fileDescriptorSource.Fd()), sockopt, isudp, isipv6)
} }
//SetSockOpt 是平台相关的.

View File

@@ -3,5 +3,6 @@
package netLayer package netLayer
//SetSockOpt 是平台相关的.
func SetSockOpt(fd int, sockopt *Sockopt, isudp bool, isipv6 bool) { func SetSockOpt(fd int, sockopt *Sockopt, isudp bool, isipv6 bool) {
} }

View File

@@ -296,6 +296,7 @@ func (*ProxyCommonStruct) GetServerInnerMuxSession(wlc io.ReadWriteCloser) *smux
zap.Error(err), zap.Error(err),
) )
} }
return nil
} }
return smuxSession return smuxSession
} }
@@ -319,6 +320,7 @@ func (pcs *ProxyCommonStruct) GetClientInnerMuxSession(wrc io.ReadWriteCloser) *
zap.Error(err), zap.Error(err),
) )
} }
return nil
} }
pcs.innermux = smuxSession pcs.innermux = smuxSession
return smuxSession return smuxSession
@@ -429,8 +431,11 @@ func (s *ProxyCommonStruct) GetAdvServer() advLayer.Server {
} }
func (s *ProxyCommonStruct) InitAdvLayer() { func (s *ProxyCommonStruct) InitAdvLayer() {
if s.AdvancedL == "" { switch s.AdvancedL {
case "":
return return
case "quic":
s.setNetwork("udp")
} }
creator := advLayer.ProtocolsMap[s.AdvancedL] creator := advLayer.ProtocolsMap[s.AdvancedL]

View File

@@ -4,68 +4,57 @@ import (
"net" "net"
"net/url" "net/url"
"github.com/e1732a364fed/v2ray_simple/httpLayer" "github.com/e1732a364fed/v2ray_simple/advLayer"
"github.com/e1732a364fed/v2ray_simple/tlsLayer" "github.com/e1732a364fed/v2ray_simple/tlsLayer"
) )
func updateAlpnListByAdvLayer(com ProxyCommon, alpnList []string) (result []string) {
result = alpnList
if adv := com.AdvancedLayer(); adv != "" {
if creator := advLayer.ProtocolsMap[adv]; creator != nil {
if alpn, must := creator.GetDefaultAlpn(); must {
has_alpn := false
for _, a := range alpnList {
if a == alpn {
has_alpn = true
break
}
}
if !has_alpn {
result = append([]string{alpn}, alpnList...)
}
}
}
}
return
}
//use dc.Host, dc.Insecure, dc.Utls, dc.Alpn. //use dc.Host, dc.Insecure, dc.Utls, dc.Alpn.
func prepareTLS_forClient(com ProxyCommon, dc *DialConf) error { func prepareTLS_forClient(com ProxyCommon, dc *DialConf) error {
alpnList := dc.Alpn alpnList := updateAlpnListByAdvLayer(com, dc.Alpn)
clic := com.getCommon() clic := com.getCommon()
if clic == nil { if clic == nil {
return nil return nil
} }
switch com.AdvancedLayer() {
case "quic":
clic.setNetwork("udp")
return nil
case "grpc":
has_h2 := false
for _, a := range alpnList {
if a == httpLayer.H2_Str {
has_h2 = true
break
}
}
if !has_h2 {
alpnList = append([]string{httpLayer.H2_Str}, alpnList...)
}
}
clic.setTLS_Client(tlsLayer.NewClient(dc.Host, dc.Insecure, dc.Utls, alpnList)) clic.setTLS_Client(tlsLayer.NewClient(dc.Host, dc.Insecure, dc.Utls, alpnList))
return nil return nil
} }
//use lc.Host, lc.TLSCert, lc.TLSKey, lc.Insecure, lc.Alpn. //use lc.Host, lc.TLSCert, lc.TLSKey, lc.Insecure, lc.Alpn.
func prepareTLS_forServer(com ProxyCommon, lc *ListenConf) error { func prepareTLS_forServer(com ProxyCommon, lc *ListenConf) error {
// 这里直接不检查 字符串就直接传给 tlsLayer.NewServer
// 所以要求 cert和 key 不在程序本身目录 的话,就要给出完整路径
serc := com.getCommon() serc := com.getCommon()
if serc == nil { if serc == nil {
return nil return nil
} }
alpnList := lc.Alpn alpnList := updateAlpnListByAdvLayer(com, lc.Alpn)
switch com.AdvancedLayer() {
case "quic":
serc.setNetwork("udp")
return nil
case "grpc":
has_h2 := false
for _, a := range alpnList {
if a == httpLayer.H2_Str {
has_h2 = true
break
}
}
if !has_h2 {
alpnList = append([]string{httpLayer.H2_Str}, alpnList...)
}
}
tlsserver, err := tlsLayer.NewServer(lc.Host, lc.TLSCert, lc.TLSKey, lc.Insecure, alpnList) tlsserver, err := tlsLayer.NewServer(lc.Host, lc.TLSCert, lc.TLSKey, lc.Insecure, alpnList)
if err == nil { if err == nil {