mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-08 18:20:47 +08:00
全面修订代码;完成 grpcSimple包;使用 tag选择编译quic 和 grpc
grpcSimple包的服务端和客户端现在都已完成,且兼容v2ray等内核。 grpcSimple包 简洁、高效,更加科学。暂不支持multiMode。 若 grpc_full 给出,则使用grpc包,否则默认使用 grpcSimple包。 若 noquic给出,则不使用 quic,否则 默认使用 quic。 修复 ws early 失效问题;
This commit is contained in:
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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)
|
|
||||||
}
|
|
||||||
|
@@ -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
|
|
79
advLayer/grpcSimple/grpcSimple.go
Normal file
79
advLayer/grpcSimple/grpcSimple.go
Normal 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
|
||||||
|
}
|
@@ -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
|
||||||
|
}
|
||||||
|
42
advLayer/grpcSimple/timeouter.go
Normal file
42
advLayer/grpcSimple/timeouter.go
Normal 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
|
||||||
|
}
|
@@ -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 {
|
||||||
|
@@ -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,所以我们直接提供包装好的即可
|
||||||
|
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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"
|
||||||
}
|
}
|
||||||
|
@@ -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
8
adv_grpc.go
Normal 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
5
adv_grpcSimple.go
Normal 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
7
adv_quic.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build !noquic
|
||||||
|
|
||||||
|
package v2ray_simple
|
||||||
|
|
||||||
|
import _ "github.com/e1732a364fed/v2ray_simple/advLayer/quic"
|
||||||
|
|
||||||
|
// 如果不引用 quic,go build 编译出的可执行文件 的大小 可以减小 2MB 。
|
@@ -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 {
|
||||||
|
49
cmd/verysimple/cli_quic.go
Normal file
49
cmd/verysimple/cli_quic.go
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
@@ -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() {
|
||||||
|
@@ -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 即可.
|
@@ -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
4
go.mod
@@ -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
23
main.go
@@ -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]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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 是平台相关的.
|
|
||||||
|
@@ -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) {
|
||||||
}
|
}
|
||||||
|
@@ -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]
|
||||||
|
@@ -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 {
|
||||||
|
Reference in New Issue
Block a user