feat/fix: support allow_insecure and fix ws path with query

This commit is contained in:
mzz2017
2023-02-12 17:17:51 +08:00
parent 16a85e3c68
commit 7656a6ecaf
11 changed files with 86 additions and 56 deletions

View File

@ -353,3 +353,11 @@ func GetTagFromLinkLikePlaintext(link string) (tag string, afterTag string) {
// Else tag is the part before colon. // Else tag is the part before colon.
return link[:iColon], link[iColon+1:] return link[:iColon], link[iColon+1:]
} }
func BoolToString(b bool) string {
if b {
return "1"
} else {
return "0"
}
}

View File

@ -39,10 +39,11 @@ type GlobalOption struct {
CheckInterval time.Duration CheckInterval time.Duration
CheckTolerance time.Duration CheckTolerance time.Duration
CheckDnsTcp bool CheckDnsTcp bool
AllowInsecure bool
} }
type InstanceOption struct { type InstanceOption struct {
CheckEnabled bool CheckEnabled bool
} }
type AliveDialerSetSet map[*AliveDialerSet]int type AliveDialerSetSet map[*AliveDialerSet]int

View File

@ -3,6 +3,7 @@ package http
import ( import (
"fmt" "fmt"
"github.com/mzz2017/softwind/protocol/http" "github.com/mzz2017/softwind/protocol/http"
"github.com/v2rayA/dae/common"
"github.com/v2rayA/dae/component/outbound/dialer" "github.com/v2rayA/dae/component/outbound/dialer"
"net" "net"
"net/url" "net/url"
@ -15,24 +16,25 @@ func init() {
} }
type HTTP struct { type HTTP struct {
Name string `json:"name"` Name string `json:"name"`
Server string `json:"server"` Server string `json:"server"`
Port int `json:"port"` Port int `json:"port"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
SNI string `json:"sni"` SNI string `json:"sni"`
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
AllowInsecure bool `json:"allowInsecure"`
} }
func NewHTTP(option *dialer.GlobalOption, iOption dialer.InstanceOption, link string) (*dialer.Dialer, error) { func NewHTTP(option *dialer.GlobalOption, iOption dialer.InstanceOption, link string) (*dialer.Dialer, error) {
s, err := ParseHTTPURL(link) s, err := ParseHTTPURL(link, option)
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %v", dialer.InvalidParameterErr, err) return nil, fmt.Errorf("%w: %v", dialer.InvalidParameterErr, err)
} }
return s.Dialer(option, iOption) return s.Dialer(option, iOption)
} }
func ParseHTTPURL(link string) (data *HTTP, err error) { func ParseHTTPURL(link string, option *dialer.GlobalOption) (data *HTTP, err error) {
u, err := url.Parse(link) u, err := url.Parse(link)
if err != nil || (u.Scheme != "http" && u.Scheme != "https") { if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
return nil, fmt.Errorf("%w: %v", dialer.InvalidParameterErr, err) return nil, fmt.Errorf("%w: %v", dialer.InvalidParameterErr, err)
@ -51,13 +53,14 @@ func ParseHTTPURL(link string) (data *HTTP, err error) {
return nil, fmt.Errorf("error when parsing port: %w", err) return nil, fmt.Errorf("error when parsing port: %w", err)
} }
return &HTTP{ return &HTTP{
Name: u.Fragment, Name: u.Fragment,
Server: u.Hostname(), Server: u.Hostname(),
Port: port, Port: port,
Username: u.User.Username(), Username: u.User.Username(),
Password: pwd, Password: pwd,
SNI: u.Query().Get("sni"), SNI: u.Query().Get("sni"),
Protocol: u.Scheme, Protocol: u.Scheme,
AllowInsecure: option.AllowInsecure,
}, nil }, nil
} }
@ -67,7 +70,7 @@ func (s *HTTP) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOption
if err != nil { if err != nil {
return nil, err return nil, err
} }
return dialer.NewDialer(d, option, iOption, s.Name, s.Protocol, u.String()), nil return dialer.NewDialer(d, option, iOption, s.Name, s.Protocol, u.String()), nil
} }
func (s *HTTP) URL() url.URL { func (s *HTTP) URL() url.URL {
@ -77,7 +80,7 @@ func (s *HTTP) URL() url.URL {
Fragment: s.Name, Fragment: s.Name,
} }
if s.SNI != "" { if s.SNI != "" {
u.RawQuery = url.Values{"sni": []string{s.SNI}}.Encode() u.RawQuery = url.Values{"sni": []string{s.SNI}, "allowInsecure": []string{common.BoolToString(s.AllowInsecure)}}.Encode()
} }
if s.Username != "" { if s.Username != "" {
if s.Password != "" { if s.Password != "" {

View File

@ -35,7 +35,7 @@ type Trojan struct {
} }
func NewTrojan(option *dialer.GlobalOption, iOption dialer.InstanceOption, link string) (*dialer.Dialer, error) { func NewTrojan(option *dialer.GlobalOption, iOption dialer.InstanceOption, link string) (*dialer.Dialer, error) {
s, err := ParseTrojanURL(link) s, err := ParseTrojanURL(link, option)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -48,7 +48,8 @@ func (s *Trojan) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOpti
Scheme: "tls", Scheme: "tls",
Host: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)), Host: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)),
RawQuery: url.Values{ RawQuery: url.Values{
"sni": []string{s.Sni}, "sni": []string{s.Sni},
"allowInsecure": []string{common.BoolToString(s.AllowInsecure)},
}.Encode(), }.Encode(),
} }
var err error var err error
@ -65,8 +66,8 @@ func (s *Trojan) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOpti
Scheme: "ws", Scheme: "ws",
Host: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)), Host: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)),
RawQuery: url.Values{ RawQuery: url.Values{
"host": []string{s.Host}, "host": []string{s.Host},
"path": []string{s.Path}, "path": []string{s.Path},
}.Encode(), }.Encode(),
} }
if d, err = ws.NewWs(u.String(), d); err != nil { if d, err = ws.NewWs(u.String(), d); err != nil {
@ -78,9 +79,10 @@ func (s *Trojan) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOpti
serviceName = "GunService" serviceName = "GunService"
} }
d = &grpc.Dialer{ d = &grpc.Dialer{
NextDialer: &protocol.DialerConverter{Dialer: d}, NextDialer: &protocol.DialerConverter{Dialer: d},
ServiceName: serviceName, ServiceName: serviceName,
ServerName: s.Sni, ServerName: s.Sni,
AllowInsecure: s.AllowInsecure,
} }
} }
if strings.HasPrefix(s.Encryption, "ss;") { if strings.HasPrefix(s.Encryption, "ss;") {
@ -101,17 +103,20 @@ func (s *Trojan) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOpti
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
return dialer.NewDialer(d, option, iOption, s.Name, s.Protocol, s.ExportToURL()), nil return dialer.NewDialer(d, option, iOption, s.Name, s.Protocol, s.ExportToURL()), nil
} }
func ParseTrojanURL(u string) (data *Trojan, err error) { func ParseTrojanURL(u string, option *dialer.GlobalOption) (data *Trojan, err error) {
//trojan://password@server:port#escape(remarks) //trojan://password@server:port#escape(remarks)
t, err := url.Parse(u) t, err := url.Parse(u)
if err != nil { if err != nil {
err = fmt.Errorf("invalid trojan format") err = fmt.Errorf("invalid trojan format")
return return
} }
allowInsecure := t.Query().Get("allowInsecure") allowInsecure, _ := strconv.ParseBool(t.Query().Get("allowInsecure"))
if !allowInsecure && option.AllowInsecure {
allowInsecure = true
}
sni := t.Query().Get("peer") sni := t.Query().Get("peer")
if sni == "" { if sni == "" {
sni = t.Query().Get("sni") sni = t.Query().Get("sni")
@ -129,7 +134,7 @@ func ParseTrojanURL(u string) (data *Trojan, err error) {
Port: port, Port: port,
Password: t.User.Username(), Password: t.User.Username(),
Sni: sni, Sni: sni,
AllowInsecure: allowInsecure == "1" || allowInsecure == "true", AllowInsecure: allowInsecure,
Protocol: "trojan", Protocol: "trojan",
} }
if t.Query().Get("type") != "" { if t.Query().Get("type") != "" {
@ -145,7 +150,6 @@ func ParseTrojanURL(u string) (data *Trojan, err error) {
if data.Type == "grpc" && data.ServiceName == "" { if data.Type == "grpc" && data.ServiceName == "" {
data.ServiceName = data.Path data.ServiceName = data.Path
} }
data.AllowInsecure = false
} }
return data, nil return data, nil
} }

View File

@ -48,7 +48,7 @@ func NewV2Ray(option *dialer.GlobalOption, iOption dialer.InstanceOption, link s
) )
switch { switch {
case strings.HasPrefix(link, "vmess://"): case strings.HasPrefix(link, "vmess://"):
s, err = ParseVmessURL(link) s, err = ParseVmessURL(link, option)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -56,7 +56,7 @@ func NewV2Ray(option *dialer.GlobalOption, iOption dialer.InstanceOption, link s
return nil, fmt.Errorf("%w: aid: %v, we only support AEAD encryption", dialer.UnexpectedFieldErr, s.Aid) return nil, fmt.Errorf("%w: aid: %v, we only support AEAD encryption", dialer.UnexpectedFieldErr, s.Aid)
} }
case strings.HasPrefix(link, "vless://"): case strings.HasPrefix(link, "vless://"):
s, err = ParseVlessURL(link) s, err = ParseVlessURL(link, option)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -92,8 +92,9 @@ func (s *V2Ray) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOptio
Host: net.JoinHostPort(s.Add, s.Port), Host: net.JoinHostPort(s.Add, s.Port),
Path: s.Path, Path: s.Path,
RawQuery: url.Values{ RawQuery: url.Values{
"host": []string{s.Host}, "host": []string{s.Host},
"sni": []string{sni}, "sni": []string{sni},
"allowInsecure": []string{common.BoolToString(s.AllowInsecure)},
}.Encode(), }.Encode(),
} }
d, err = ws.NewWs(u.String(), d) d, err = ws.NewWs(u.String(), d)
@ -110,7 +111,8 @@ func (s *V2Ray) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOptio
Scheme: "tls", Scheme: "tls",
Host: net.JoinHostPort(s.Add, s.Port), Host: net.JoinHostPort(s.Add, s.Port),
RawQuery: url.Values{ RawQuery: url.Values{
"sni": []string{sni}, "sni": []string{sni},
"allowInsecure": []string{common.BoolToString(s.AllowInsecure)},
}.Encode(), }.Encode(),
} }
d, err = tls.NewTls(u.String(), d) d, err = tls.NewTls(u.String(), d)
@ -131,9 +133,10 @@ func (s *V2Ray) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOptio
serviceName = "GunService" serviceName = "GunService"
} }
d = &grpc.Dialer{ d = &grpc.Dialer{
NextDialer: &protocol.DialerConverter{Dialer: d}, NextDialer: &protocol.DialerConverter{Dialer: d},
ServiceName: serviceName, ServiceName: serviceName,
ServerName: sni, ServerName: sni,
AllowInsecure: s.AllowInsecure,
} }
default: default:
return nil, fmt.Errorf("%w: network: %v", dialer.UnexpectedFieldErr, s.Net) return nil, fmt.Errorf("%w: network: %v", dialer.UnexpectedFieldErr, s.Net)
@ -147,10 +150,10 @@ func (s *V2Ray) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOptio
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
return dialer.NewDialer(d, option, iOption, s.Ps, s.Protocol, s.ExportToURL()), nil return dialer.NewDialer(d, option, iOption, s.Ps, s.Protocol, s.ExportToURL()), nil
} }
func ParseVlessURL(vless string) (data *V2Ray, err error) { func ParseVlessURL(vless string, option *dialer.GlobalOption) (data *V2Ray, err error) {
u, err := url.Parse(vless) u, err := url.Parse(vless)
if err != nil { if err != nil {
return nil, err return nil, err
@ -188,10 +191,13 @@ func ParseVlessURL(vless string) (data *V2Ray, err error) {
if data.Type == "mkcp" || data.Type == "kcp" { if data.Type == "mkcp" || data.Type == "kcp" {
data.Path = u.Query().Get("seed") data.Path = u.Query().Get("seed")
} }
if option.AllowInsecure {
data.AllowInsecure = true
}
return data, nil return data, nil
} }
func ParseVmessURL(vmess string) (data *V2Ray, err error) { func ParseVmessURL(vmess string, option *dialer.GlobalOption) (data *V2Ray, err error) {
var info V2Ray var info V2Ray
// perform base64 decoding and unmarshal to VmessInfo // perform base64 decoding and unmarshal to VmessInfo
raw, err := common.Base64StdDecode(vmess[8:]) raw, err := common.Base64StdDecode(vmess[8:])
@ -265,6 +271,9 @@ func ParseVmessURL(vmess string) (data *V2Ray, err error) {
info.Aid = "0" info.Aid = "0"
} }
info.Protocol = "vmess" info.Protocol = "vmess"
if option.AllowInsecure {
info.AllowInsecure = true
}
return &info, nil return &info, nil
} }

View File

@ -8,6 +8,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
) )
// Ws is a base Ws struct // Ws is a base Ws struct
@ -40,18 +41,17 @@ func NewWs(s string, d proxy.Dialer) (*Ws, error) {
wsUrl := url.URL{ wsUrl := url.URL{
Scheme: u.Scheme, Scheme: u.Scheme,
Host: u.Host, Host: u.Host,
Path: u.Path,
} }
t.wsAddr = wsUrl.String() t.wsAddr = wsUrl.String() + u.Path
t.wsDialer = &websocket.Dialer{ t.wsDialer = &websocket.Dialer{
NetDial: d.Dial, NetDial: d.Dial,
//Subprotocols: []string{"binary"}, //Subprotocols: []string{"binary"},
} }
if u.Scheme == "wss" { if u.Scheme == "wss" {
if u.Query().Get("sni") != "" { skipVerify, _ := strconv.ParseBool(u.Query().Get("allowInsecure"))
t.wsDialer.TLSClientConfig = &tls.Config{ t.wsDialer.TLSClientConfig = &tls.Config{
ServerName: u.Query().Get("sni"), ServerName: u.Query().Get("sni"),
} InsecureSkipVerify: skipVerify,
} }
} }
return t, nil return t, nil

View File

@ -18,10 +18,7 @@ type Global struct {
LogLevel string `mapstructure:"log_level" default:"info"` LogLevel string `mapstructure:"log_level" default:"info"`
// We use DirectTcpCheckUrl to check (tcp)*(ipv4/ipv6) connectivity for direct. // We use DirectTcpCheckUrl to check (tcp)*(ipv4/ipv6) connectivity for direct.
//DirectTcpCheckUrl string `mapstructure:"direct_tcp_check_url" default:"http://www.qualcomm.cn/generate_204"` //DirectTcpCheckUrl string `mapstructure:"direct_tcp_check_url" default:"http://www.qualcomm.cn/generate_204"`
// We use TcpCheckUrl to check (tcp)*(ipv4/ipv6) connectivity for non-direct and non-DNS packets. TcpCheckUrl string `mapstructure:"tcp_check_url" default:"http://cp.cloudflare.com"`
TcpCheckUrl string `mapstructure:"tcp_check_url" default:"http://cp.cloudflare.com"`
// We use UdpCheckDns to check (tcp/udp)*(ipv4/ipv6) connectivity for DNS packets,
// and udp*(ipv4/ipv6) connectivity for all other types of packets.
UdpCheckDns string `mapstructure:"udp_check_dns" default:"dns.google:53"` UdpCheckDns string `mapstructure:"udp_check_dns" default:"dns.google:53"`
CheckInterval time.Duration `mapstructure:"check_interval" default:"30s"` CheckInterval time.Duration `mapstructure:"check_interval" default:"30s"`
CheckTolerance time.Duration `mapstructure:"check_tolerance" default:"0"` CheckTolerance time.Duration `mapstructure:"check_tolerance" default:"0"`
@ -29,6 +26,7 @@ type Global struct {
LanInterface []string `mapstructure:"lan_interface"` LanInterface []string `mapstructure:"lan_interface"`
LanNatDirect bool `mapstructure:"lan_nat_direct" required:""` LanNatDirect bool `mapstructure:"lan_nat_direct" required:""`
WanInterface []string `mapstructure:"wan_interface"` WanInterface []string `mapstructure:"wan_interface"`
AllowInsecure bool `mapstructure:"allow_insecure" default:"false"`
} }
type Group struct { type Group struct {

View File

@ -200,6 +200,9 @@ func NewControlPlane(
checkDnsTcp = true checkDnsTcp = true
} }
} }
if global.AllowInsecure {
log.Warnln("AllowInsecure is enabled, but it is not recommended. Please make sure you have to turn it on.")
}
option := &dialer.GlobalOption{ option := &dialer.GlobalOption{
Log: log, Log: log,
TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: global.TcpCheckUrl}, TcpCheckOptionRaw: dialer.TcpCheckOptionRaw{Raw: global.TcpCheckUrl},
@ -207,6 +210,7 @@ func NewControlPlane(
CheckInterval: global.CheckInterval, CheckInterval: global.CheckInterval,
CheckTolerance: global.CheckTolerance, CheckTolerance: global.CheckTolerance,
CheckDnsTcp: checkDnsTcp, CheckDnsTcp: checkDnsTcp,
AllowInsecure: global.AllowInsecure,
} }
outbounds := []*outbound.DialerGroup{ outbounds := []*outbound.DialerGroup{
outbound.NewDialerGroup(option, consts.OutboundDirect.String(), outbound.NewDialerGroup(option, consts.OutboundDirect.String(),

View File

@ -42,6 +42,9 @@ global {
# The WAN interface to bind. Use it if you want to proxy localhost. # The WAN interface to bind. Use it if you want to proxy localhost.
# Multiple interfaces split by ",". # Multiple interfaces split by ",".
wan_interface: wlp5s0 wan_interface: wlp5s0
# Allow insecure TLS certificates. It is not recommended to turn it on unless you have to.
allow_insecure: false
} }
# Subscriptions defined here will be resolved as nodes and merged as a part of the global node pool. # Subscriptions defined here will be resolved as nodes and merged as a part of the global node pool.

2
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/mzz2017/softwind v0.0.0-20230212082815-dee826918b06 github.com/mzz2017/softwind v0.0.0-20230212090240-561c250bc5c4
github.com/safchain/ethtool v0.0.0-20230116090318-67cc41908669 github.com/safchain/ethtool v0.0.0-20230116090318-67cc41908669
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1

4
go.sum
View File

@ -69,8 +69,8 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mzz2017/disk-bloom v1.0.1 h1:rEF9MiXd9qMW3ibRpqcerLXULoTgRlM21yqqJl1B90M= github.com/mzz2017/disk-bloom v1.0.1 h1:rEF9MiXd9qMW3ibRpqcerLXULoTgRlM21yqqJl1B90M=
github.com/mzz2017/disk-bloom v1.0.1/go.mod h1:JLHETtUu44Z6iBmsqzkOtFlRvXSlKnxjwiBRDapizDI= github.com/mzz2017/disk-bloom v1.0.1/go.mod h1:JLHETtUu44Z6iBmsqzkOtFlRvXSlKnxjwiBRDapizDI=
github.com/mzz2017/softwind v0.0.0-20230212082815-dee826918b06 h1:cdXO7ciiP2ubeayuHQIWEK3eai/bNW4dAbNgyu8C558= github.com/mzz2017/softwind v0.0.0-20230212090240-561c250bc5c4 h1:f7nqFcMs8LaM/eLRHGeuMk/urv/U+AMNKjFylwowlwU=
github.com/mzz2017/softwind v0.0.0-20230212082815-dee826918b06/go.mod h1:K1nXwtBokwEsfOfdT/5zV6R8QabGkyhcR0iuTrRZcYY= github.com/mzz2017/softwind v0.0.0-20230212090240-561c250bc5c4/go.mod h1:K1nXwtBokwEsfOfdT/5zV6R8QabGkyhcR0iuTrRZcYY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=