全面修订代码;完成 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]
[1]: https://godoc.org/github.com/e1732a364fed/v2ray_simple?status.svg
[2]: https://godoc.org/github.com/e1732a364fed/v2ray_simple
[1]: https://pkg.go.dev/badge/github.com/e1732a364fed/v2ray_simple.svg
[2]: https://pkg.go.dev/github.com/e1732a364fed/v2ray_simple#section-readme
[3]: https://img.shields.io/badge/license-MIT-blue.svg
[4]: LICENSE
[5]: https://goreportcard.com/badge/github.com/e1732a364fed/v2ray_simple

View File

@@ -4,7 +4,6 @@ package advLayer
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
@@ -13,10 +12,11 @@ import (
"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)
//为了避免黑客攻击,我们固定earlydata最大值为2048
var MaxEarlyDataLen = 2048 //for ws early data
func PrintAllProtocolNames() {
@@ -31,6 +31,9 @@ type Creator interface {
//NewClientFromURL(url *url.URL) (Client, error)
NewClientFromConf(conf *Conf) (Client, error)
NewServerFromConf(conf *Conf) (Server, error)
GetDefaultAlpn() (alpn string, mustUse bool)
PackageID() string
}
type Conf struct {
@@ -45,12 +48,12 @@ type Conf struct {
}
type Client interface {
GetPath() string
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.
GetPath() string
IsEarly() bool //is 0-rtt or not.
}
// ws (h1.1)
@@ -68,6 +71,7 @@ type MuxClient interface {
// If IsSuper, underlay should be nil;
//
// 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)
DialSubConn(underlay any) (net.Conn, error)
@@ -76,11 +80,11 @@ type MuxClient 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
IsMux() bool //quic and grpc. if IsMux, then Server is a MuxServer, or it's a SingleServer
IsSuper() bool //quic
}
//ws

View File

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

View File

@@ -24,7 +24,9 @@ https://github.com/v2fly/v2ray-core/pull/757
*/
package grpc
import "github.com/e1732a364fed/v2ray_simple/advLayer"
import (
"github.com/e1732a364fed/v2ray_simple/advLayer"
)
func init() {
advLayer.ProtocolsMap["grpc"] = Creator{}
@@ -32,8 +34,27 @@ func init() {
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) {
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) {

View File

@@ -5,20 +5,19 @@ package grpcSimple
import (
"bufio"
"context"
"crypto/tls"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"sync"
"time"
"github.com/e1732a364fed/v2ray_simple/utils"
"go.uber.org/atomic"
"go.uber.org/zap"
"golang.org/x/net/http2"
)
@@ -26,9 +25,10 @@ var (
ErrInvalidLength = errors.New("invalid length")
)
var defaultHeader = http.Header{
var defaultClientHeader = http.Header{
"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
@@ -44,7 +44,9 @@ type ClientConn struct {
br *bufio.Reader
// deadlines
deadline *time.Timer
timeouter
client *Client
}
type Config struct {
@@ -52,7 +54,7 @@ type Config struct {
Host string
}
func (g *ClientConn) initRequest() {
func (g *ClientConn) handshake() {
response, err := g.transport.RoundTrip(g.request)
if err != nil {
g.err = err
@@ -61,15 +63,33 @@ func (g *ClientConn) initRequest() {
}
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.br = bufio.NewReader(response.Body)
} else {
g.client.cachedTransport = nil
response.Body.Close()
}
}
func (g *ClientConn) Read(b []byte) (n int, err error) {
g.once.Do(g.initRequest)
g.once.Do(g.handshake)
if g.err != nil {
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 {
err = g.err
}
if err != nil {
g.client.dealErr(err)
}
return len(b), err
}
@@ -146,110 +170,101 @@ func (g *ClientConn) Close() error {
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
func NewHTTP2Client(
rawTCPConn net.Conn,
tlsConfig *tls.Config,
) *http2.Transport {
type Client struct {
Config
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)
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
}
cachedTransport *http2.Transport
return &http2.Transport{
DialTLS: dialFunc,
TLSClientConfig: tlsConfig,
AllowHTTP: false,
DisableCompression: true,
PingTimeout: 0,
path string
}
func (g *Client) dealErr(err error) {
//use of closed connection
if strings.Contains(err.Error(), "use of closed") {
g.cachedTransport = nil
}
}
func StreamGunWithTransport(transport *http2.Transport, cfg *Config) (net.Conn, error) {
serviceName := "GunService"
if cfg.ServiceName != "" {
serviceName = cfg.ServiceName
func (c *Client) GetPath() string {
return c.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()
request := &http.Request{
Method: http.MethodPost,
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,
}
request := c.theRequest
request.Body = reader
conn := &ClientConn{
request: request,
request: &request,
transport: transport,
writer: writer,
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
}
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
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 (
//h3
DefaultAlpnList = []string{"h3"}
common_ListenConfig = quic.Config{
@@ -78,6 +79,14 @@ var (
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) {
var alpn []string
if conf.TlsConf != nil {

View File

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

View File

@@ -28,12 +28,13 @@ type Server struct {
}
// 这里默认: 传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查.
func NewServer(path string, headers map[string][]string) *Server {
func NewServer(path string, headers map[string][]string, UseEarlyData bool) *Server {
return &Server{
//upgrader: upgrader,
Thepath: path,
headers: headers,
Thepath: path,
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) {
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
}
s := ws.NewServer(wsPath, nil)
s := ws.NewServer(wsPath, nil, false)
wsConn, err := s.Handshake(nil, conn)
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"
"github.com/asaskevich/govalidator"
"github.com/e1732a364fed/v2ray_simple/advLayer/quic"
"github.com/e1732a364fed/v2ray_simple/netLayer"
"github.com/e1732a364fed/v2ray_simple/proxy"
"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 {

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"
"runtime"
"github.com/e1732a364fed/v2ray_simple/advLayer"
"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就是用这种方式确定版本号
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() {

View File

@@ -5,7 +5,7 @@ port = 10800
[[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了
ip = "127.0.0.1"
host = "your-domain-name.com" # trojan-go 的服务端要求指定一个sni 并与服务端的配置相匹配, 否则会trojan-go 会拒绝连接
@@ -13,6 +13,9 @@ port = 4434
insecure = 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 即可.

View File

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

4
go.mod
View File

@@ -16,8 +16,10 @@ require (
github.com/refraction-networking/utls v1.0.0
github.com/xtaci/smux v1.5.16
github.com/yl2chen/cidranger v1.0.2
go.uber.org/atomic v1.7.0
go.uber.org/zap v1.21.0
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
gonum.org/v1/gonum v0.11.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/nxadm/tail v1.4.8 // 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
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/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.9 // 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/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/proxy/dokodemo"
@@ -1030,6 +1028,8 @@ func dialClient(targetAddr netLayer.Addr,
advLayerHandshakeStep:
//var firstPayloadAlreadyDealt bool
if adv != "" {
switch adv {
case "quic":
@@ -1116,7 +1116,11 @@ advLayerHandshakeStep:
//若配置了 MaxEarlyDataLen则我们先读一段;
edBuf := utils.GetPacket()
edBuf = edBuf[:advLayer.MaxEarlyDataLen]
wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
n, e := wlc.Read(edBuf)
wlc.SetReadDeadline(time.Time{})
if e != nil {
if ce := utils.CanLogErr("failed to read ws early data"); ce != nil {
ce.Write(zap.Error(e))
@@ -1124,7 +1128,11 @@ advLayerHandshakeStep:
result = -1
return
}
ed = edBuf[:n]
if n > 0 {
//firstPayloadAlreadyDealt = true
ed = edBuf[:n]
}
if ce := utils.CanLogDebug("will send early data"); ce != nil {
ce.Write(
@@ -1165,11 +1173,16 @@ advLayerHandshakeStep:
//udp但是有innermux时 依然用handshake, 而不是 EstablishUDPChannel
var firstPayload []byte
if !hasInnerMux { //如果有内层mux要在dialInnerProxy函数里再读
//读取firstPayload
if !hasInnerMux {
//如果有内层mux要在dialInnerProxy函数里再读, 而不是在这里读
firstPayload = utils.GetMTU()
wlc.SetReadDeadline(time.Now().Add(proxy.FirstPayloadTimeout))
n, err := wlc.Read(firstPayload)
wlc.SetReadDeadline(time.Time{})
if err != nil {
if !errors.Is(err, os.ErrDeadlineExceeded) {
@@ -1194,7 +1207,7 @@ advLayerHandshakeStep:
}
}
wlc.SetReadDeadline(time.Time{})
firstPayload = firstPayload[:n]
}

View File

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

View File

@@ -1,9 +1,9 @@
/*
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
@@ -54,7 +54,12 @@ func HasIpv6Interface() bool {
// According to godoc, If ip is not an IPv4 address, To4 returns nil.
// This means it's ipv6
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
}

View File

@@ -154,6 +154,7 @@ func ReadBuffersFrom(c io.Reader, rawReadConn syscall.RawConn, mr utils.MultiRea
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) {
rawReadConn = GetRawConn(c)
var ok bool

View File

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

View File

@@ -3,5 +3,6 @@
package netLayer
//SetSockOpt 是平台相关的.
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),
)
}
return nil
}
return smuxSession
}
@@ -319,6 +320,7 @@ func (pcs *ProxyCommonStruct) GetClientInnerMuxSession(wrc io.ReadWriteCloser) *
zap.Error(err),
)
}
return nil
}
pcs.innermux = smuxSession
return smuxSession
@@ -429,8 +431,11 @@ func (s *ProxyCommonStruct) GetAdvServer() advLayer.Server {
}
func (s *ProxyCommonStruct) InitAdvLayer() {
if s.AdvancedL == "" {
switch s.AdvancedL {
case "":
return
case "quic":
s.setNetwork("udp")
}
creator := advLayer.ProtocolsMap[s.AdvancedL]

View File

@@ -4,68 +4,57 @@ import (
"net"
"net/url"
"github.com/e1732a364fed/v2ray_simple/httpLayer"
"github.com/e1732a364fed/v2ray_simple/advLayer"
"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.
func prepareTLS_forClient(com ProxyCommon, dc *DialConf) error {
alpnList := dc.Alpn
alpnList := updateAlpnListByAdvLayer(com, dc.Alpn)
clic := com.getCommon()
if clic == 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))
return nil
}
//use lc.Host, lc.TLSCert, lc.TLSKey, lc.Insecure, lc.Alpn.
func prepareTLS_forServer(com ProxyCommon, lc *ListenConf) error {
// 这里直接不检查 字符串就直接传给 tlsLayer.NewServer
// 所以要求 cert和 key 不在程序本身目录 的话,就要给出完整路径
serc := com.getCommon()
if serc == nil {
return nil
}
alpnList := 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...)
}
}
alpnList := updateAlpnListByAdvLayer(com, lc.Alpn)
tlsserver, err := tlsLayer.NewServer(lc.Host, lc.TLSCert, lc.TLSKey, lc.Insecure, alpnList)
if err == nil {