From d85162ea44ff99d9aad6e7117fcef41e950fda8b Mon Sep 17 00:00:00 2001 From: eMeab <32988354+eMeab@users.noreply.github.com> Date: Wed, 13 Jan 2021 23:13:51 +0800 Subject: [PATCH] Add SNI shunt support for VLESS (#141) --- infra/conf/vless.go | 2 + proxy/vless/inbound/config.pb.go | 70 ++++++++++++++++++-------------- proxy/vless/inbound/config.proto | 11 ++--- proxy/vless/inbound/inbound.go | 36 ++++++++++++---- 4 files changed, 77 insertions(+), 42 deletions(-) diff --git a/infra/conf/vless.go b/infra/conf/vless.go index 22e5645f..91233703 100644 --- a/infra/conf/vless.go +++ b/infra/conf/vless.go @@ -18,6 +18,7 @@ import ( ) type VLessInboundFallback struct { + Name string `json:"name"` Alpn string `json:"alpn"` Path string `json:"path"` Type string `json:"type"` @@ -85,6 +86,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { _ = json.Unmarshal(fb.Dest, &s) } config.Fallbacks = append(config.Fallbacks, &inbound.Fallback{ + Name: fb.Name, Alpn: fb.Alpn, Path: fb.Path, Type: fb.Type, diff --git a/proxy/vless/inbound/config.pb.go b/proxy/vless/inbound/config.pb.go index a2be91a2..bb253d69 100644 --- a/proxy/vless/inbound/config.pb.go +++ b/proxy/vless/inbound/config.pb.go @@ -31,11 +31,12 @@ type Fallback struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Alpn string `protobuf:"bytes,1,opt,name=alpn,proto3" json:"alpn,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` - Dest string `protobuf:"bytes,4,opt,name=dest,proto3" json:"dest,omitempty"` - Xver uint64 `protobuf:"varint,5,opt,name=xver,proto3" json:"xver,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Alpn string `protobuf:"bytes,2,opt,name=alpn,proto3" json:"alpn,omitempty"` + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` + Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"` + Dest string `protobuf:"bytes,5,opt,name=dest,proto3" json:"dest,omitempty"` + Xver uint64 `protobuf:"varint,6,opt,name=xver,proto3" json:"xver,omitempty"` } func (x *Fallback) Reset() { @@ -70,6 +71,13 @@ func (*Fallback) Descriptor() ([]byte, []int) { return file_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{0} } +func (x *Fallback) GetName() string { + if x != nil { + return x.Name + } + return "" +} + func (x *Fallback) GetAlpn() string { if x != nil { return x.Alpn @@ -178,31 +186,33 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{ 0x74, 0x6f, 0x12, 0x18, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x1a, 0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x08, 0x46, 0x61, 0x6c, 0x6c, - 0x62, 0x61, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xa0, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, - 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x09, 0x66, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, - 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, - 0x52, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x42, 0x6a, 0x0a, 0x1c, 0x63, - 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, - 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, - 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, - 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, - 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, - 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x82, 0x01, 0x0a, 0x08, 0x46, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x6c, 0x70, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x6c, 0x70, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xa0, 0x01, + 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, + 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, + 0x0a, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, + 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, + 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, + 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, + 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proxy/vless/inbound/config.proto b/proxy/vless/inbound/config.proto index ee24161f..94b5551c 100644 --- a/proxy/vless/inbound/config.proto +++ b/proxy/vless/inbound/config.proto @@ -9,11 +9,12 @@ option java_multiple_files = true; import "common/protocol/user.proto"; message Fallback { - string alpn = 1; - string path = 2; - string type = 3; - string dest = 4; - uint64 xver = 5; + string name = 1; + string alpn = 2; + string path = 3; + string type = 4; + string dest = 5; + uint64 xver = 6; } message Config { diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index a79455d0..59de471e 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -6,6 +6,7 @@ import ( "context" "io" "strconv" + "strings" "syscall" "time" @@ -63,7 +64,7 @@ type Handler struct { policyManager policy.Manager validator *vless.Validator dns dns.Client - fallbacks map[string]map[string]*Fallback // or nil + fallbacks map[string]map[string]map[string]*Fallback // or nil // regexps map[string]*regexp.Regexp // or nil } @@ -88,13 +89,16 @@ func New(ctx context.Context, config *Config, dc dns.Client) (*Handler, error) { } if config.Fallbacks != nil { - handler.fallbacks = make(map[string]map[string]*Fallback) + handler.fallbacks = make(map[string]map[string]map[string]*Fallback) // handler.regexps = make(map[string]*regexp.Regexp) for _, fb := range config.Fallbacks { - if handler.fallbacks[fb.Alpn] == nil { - handler.fallbacks[fb.Alpn] = make(map[string]*Fallback) + if handler.fallbacks[fb.Name] == nil { + handler.fallbacks[fb.Name] = make(map[string]map[string]*Fallback) } - handler.fallbacks[fb.Alpn][fb.Path] = fb + if handler.fallbacks[fb.Name][fb.Alpn] == nil { + handler.fallbacks[fb.Name][fb.Alpn] = make(map[string]*Fallback) + } + handler.fallbacks[fb.Name][fb.Alpn][fb.Path] = fb /* if fb.Path != "" { if r, err := regexp.Compile(fb.Path); err != nil { @@ -187,20 +191,38 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i } newError("fallback starts").Base(err).AtInfo().WriteToLog(sid) + name := "" alpn := "" if len(apfb) > 1 || apfb[""] == nil { if tlsConn, ok := iConn.(*tls.Conn); ok { + name = tlsConn.ConnectionState().ServerName alpn = tlsConn.ConnectionState().NegotiatedProtocol + newError("realServerName = " + name).AtInfo().WriteToLog(sid) newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } else if xtlsConn, ok := iConn.(*xtls.Conn); ok { + name = xtlsConn.ConnectionState().ServerName alpn = xtlsConn.ConnectionState().NegotiatedProtocol + newError("realServerName = " + name).AtInfo().WriteToLog(sid) newError("realAlpn = " + alpn).AtInfo().WriteToLog(sid) } - if apfb[alpn] == nil { + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + if apfb[candidate] != nil { + name = candidate + break + } + } + if apfb[name] == nil { + name = "" + } + if apfb[name][alpn] == nil { alpn = "" } + } - pfb := apfb[alpn] + pfb := apfb[name][alpn] if pfb == nil { return newError(`failed to find the default "alpn" config`).AtWarning() }