Merge remote-tracking branch 'origin/Alpha' into Alpha
# Conflicts: # component/sniffer/http_sniffer.go
This commit is contained in:
commit
f2d7db4a41
6 changed files with 194 additions and 142 deletions
45
common/convert/base64.go
Normal file
45
common/convert/base64.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package convert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
encRaw = base64.RawStdEncoding
|
||||||
|
enc = base64.StdEncoding
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodeBase64 try to decode content from the given bytes,
|
||||||
|
// which can be in base64.RawStdEncoding, base64.StdEncoding or just plaintext.
|
||||||
|
func DecodeBase64(buf []byte) []byte {
|
||||||
|
result, err := tryDecodeBase64(buf)
|
||||||
|
if err != nil {
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryDecodeBase64(buf []byte) ([]byte, error) {
|
||||||
|
dBuf := make([]byte, encRaw.DecodedLen(len(buf)))
|
||||||
|
n, err := encRaw.Decode(dBuf, buf)
|
||||||
|
if err != nil {
|
||||||
|
n, err = enc.Decode(dBuf, buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dBuf[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSafe(data string) string {
|
||||||
|
return strings.NewReplacer("+", "-", "/", "_").Replace(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeUrlSafe(data string) string {
|
||||||
|
dcBuf, err := base64.RawURLEncoding.DecodeString(data)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(dcBuf)
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package convert
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -10,23 +9,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
encRaw = base64.RawStdEncoding
|
|
||||||
enc = base64.StdEncoding
|
|
||||||
)
|
|
||||||
|
|
||||||
func DecodeBase64(buf []byte) []byte {
|
|
||||||
dBuf := make([]byte, encRaw.DecodedLen(len(buf)))
|
|
||||||
n, err := encRaw.Decode(dBuf, buf)
|
|
||||||
if err != nil {
|
|
||||||
n, err = enc.Decode(dBuf, buf)
|
|
||||||
if err != nil {
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dBuf[:n]
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
|
// ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config
|
||||||
func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
data := DecodeBase64(buf)
|
data := DecodeBase64(buf)
|
||||||
|
@ -132,102 +114,37 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
proxies = append(proxies, trojan)
|
proxies = append(proxies, trojan)
|
||||||
|
|
||||||
case "vless":
|
case "vless":
|
||||||
urlVless, err := url.Parse(line)
|
urlVLess, err := url.Parse(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
query := urlVLess.Query()
|
||||||
query := urlVless.Query()
|
|
||||||
|
|
||||||
name := uniqueName(names, urlVless.Fragment)
|
|
||||||
vless := make(map[string]any, 20)
|
vless := make(map[string]any, 20)
|
||||||
|
handleVShareLink(names, urlVLess, scheme, vless)
|
||||||
vless["name"] = name
|
if flow := query.Get("flow"); flow != "" {
|
||||||
vless["type"] = scheme
|
vless["flow"] = strings.ToLower(flow)
|
||||||
vless["server"] = urlVless.Hostname()
|
|
||||||
vless["port"] = urlVless.Port()
|
|
||||||
vless["uuid"] = urlVless.User.Username()
|
|
||||||
vless["udp"] = true
|
|
||||||
vless["skip-cert-verify"] = false
|
|
||||||
vless["tls"] = false
|
|
||||||
tls := strings.ToLower(query.Get("security"))
|
|
||||||
if strings.HasSuffix(tls, "tls") {
|
|
||||||
vless["tls"] = true
|
|
||||||
}
|
}
|
||||||
sni := query.Get("sni")
|
|
||||||
if sni != "" {
|
|
||||||
vless["servername"] = sni
|
|
||||||
}
|
|
||||||
|
|
||||||
flow := strings.ToLower(query.Get("flow"))
|
|
||||||
if flow != "" {
|
|
||||||
vless["flow"] = flow
|
|
||||||
}
|
|
||||||
|
|
||||||
network := strings.ToLower(query.Get("type"))
|
|
||||||
fakeType := strings.ToLower(query.Get("headerType"))
|
|
||||||
if fakeType == "http" {
|
|
||||||
network = "http"
|
|
||||||
} else if network == "http" {
|
|
||||||
network = "h2"
|
|
||||||
}
|
|
||||||
vless["network"] = network
|
|
||||||
switch network {
|
|
||||||
case "tcp":
|
|
||||||
if fakeType != "none" {
|
|
||||||
headers := make(map[string]any)
|
|
||||||
httpOpts := make(map[string]any)
|
|
||||||
httpOpts["path"] = []string{"/"}
|
|
||||||
|
|
||||||
if query.Get("host") != "" {
|
|
||||||
headers["Host"] = []string{query.Get("host")}
|
|
||||||
}
|
|
||||||
|
|
||||||
if query.Get("method") != "" {
|
|
||||||
httpOpts["method"] = query.Get("method")
|
|
||||||
}
|
|
||||||
|
|
||||||
if query.Get("path") != "" {
|
|
||||||
httpOpts["path"] = []string{query.Get("path")}
|
|
||||||
}
|
|
||||||
httpOpts["headers"] = headers
|
|
||||||
vless["http-opts"] = httpOpts
|
|
||||||
}
|
|
||||||
|
|
||||||
case "http":
|
|
||||||
headers := make(map[string]any)
|
|
||||||
h2Opts := make(map[string]any)
|
|
||||||
h2Opts["path"] = []string{"/"}
|
|
||||||
if query.Get("path") != "" {
|
|
||||||
h2Opts["path"] = []string{query.Get("path")}
|
|
||||||
}
|
|
||||||
if query.Get("host") != "" {
|
|
||||||
h2Opts["host"] = []string{query.Get("host")}
|
|
||||||
}
|
|
||||||
h2Opts["headers"] = headers
|
|
||||||
vless["h2-opts"] = h2Opts
|
|
||||||
|
|
||||||
case "ws":
|
|
||||||
headers := make(map[string]any)
|
|
||||||
wsOpts := make(map[string]any)
|
|
||||||
headers["User-Agent"] = RandUserAgent()
|
|
||||||
headers["Host"] = query.Get("host")
|
|
||||||
wsOpts["path"] = query.Get("path")
|
|
||||||
wsOpts["headers"] = headers
|
|
||||||
|
|
||||||
vless["ws-opts"] = wsOpts
|
|
||||||
|
|
||||||
case "grpc":
|
|
||||||
grpcOpts := make(map[string]any)
|
|
||||||
grpcOpts["grpc-service-name"] = query.Get("serviceName")
|
|
||||||
vless["grpc-opts"] = grpcOpts
|
|
||||||
}
|
|
||||||
|
|
||||||
proxies = append(proxies, vless)
|
proxies = append(proxies, vless)
|
||||||
|
|
||||||
case "vmess":
|
case "vmess":
|
||||||
dcBuf, err := encRaw.DecodeString(body)
|
// V2RayN-styled share link
|
||||||
|
// https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
|
||||||
|
dcBuf, err := tryDecodeBase64([]byte(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Xray VMessAEAD share link
|
||||||
|
urlVMess, err := url.Parse(line)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
query := urlVMess.Query()
|
||||||
|
vmess := make(map[string]any, 20)
|
||||||
|
handleVShareLink(names, urlVMess, scheme, vmess)
|
||||||
|
vmess["alterId"] = 0
|
||||||
|
vmess["cipher"] = "auto"
|
||||||
|
if encryption := query.Get("encryption"); encryption != "" {
|
||||||
|
vmess["cipher"] = encryption
|
||||||
|
}
|
||||||
|
proxies = append(proxies, vmess)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,17 +168,16 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
} else {
|
} else {
|
||||||
vmess["alterId"] = 0
|
vmess["alterId"] = 0
|
||||||
}
|
}
|
||||||
vmess["cipher"] = "auto"
|
|
||||||
vmess["udp"] = true
|
vmess["udp"] = true
|
||||||
vmess["tls"] = false
|
vmess["tls"] = false
|
||||||
vmess["skip-cert-verify"] = false
|
vmess["skip-cert-verify"] = false
|
||||||
|
|
||||||
if values["cipher"] != nil && values["cipher"] != "" {
|
vmess["cipher"] = "auto"
|
||||||
vmess["cipher"] = values["cipher"]
|
if cipher, ok := values["scy"]; ok && cipher != "" {
|
||||||
|
vmess["cipher"] = cipher
|
||||||
}
|
}
|
||||||
|
|
||||||
sni := values["sni"]
|
if sni, ok := values["sni"]; ok && sni != "" {
|
||||||
if sni != "" {
|
|
||||||
vmess["servername"] = sni
|
vmess["servername"] = sni
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +190,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
vmess["network"] = network
|
vmess["network"] = network
|
||||||
|
|
||||||
tls := strings.ToLower(values["tls"].(string))
|
tls := strings.ToLower(values["tls"].(string))
|
||||||
if strings.Contains(tls, "tls") {
|
if strings.HasSuffix(tls, "tls") {
|
||||||
vmess["tls"] = true
|
vmess["tls"] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,12 +198,12 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
case "http":
|
case "http":
|
||||||
headers := make(map[string]any)
|
headers := make(map[string]any)
|
||||||
httpOpts := make(map[string]any)
|
httpOpts := make(map[string]any)
|
||||||
if values["host"] != "" && values["host"] != nil {
|
if host, ok := values["host"]; ok && host != "" {
|
||||||
headers["Host"] = []string{values["host"].(string)}
|
headers["Host"] = []string{host.(string)}
|
||||||
}
|
}
|
||||||
httpOpts["path"] = []string{"/"}
|
httpOpts["path"] = []string{"/"}
|
||||||
if values["path"] != "" && values["path"] != nil {
|
if path, ok := values["path"]; ok && path != "" {
|
||||||
httpOpts["path"] = []string{values["path"].(string)}
|
httpOpts["path"] = []string{path.(string)}
|
||||||
}
|
}
|
||||||
httpOpts["headers"] = headers
|
httpOpts["headers"] = headers
|
||||||
|
|
||||||
|
@ -296,8 +212,8 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
case "h2":
|
case "h2":
|
||||||
headers := make(map[string]any)
|
headers := make(map[string]any)
|
||||||
h2Opts := make(map[string]any)
|
h2Opts := make(map[string]any)
|
||||||
if values["host"] != "" && values["host"] != nil {
|
if host, ok := values["host"]; ok && host != "" {
|
||||||
headers["Host"] = []string{values["host"].(string)}
|
headers["Host"] = []string{host.(string)}
|
||||||
}
|
}
|
||||||
|
|
||||||
h2Opts["path"] = values["path"]
|
h2Opts["path"] = values["path"]
|
||||||
|
@ -309,11 +225,11 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
headers := make(map[string]any)
|
headers := make(map[string]any)
|
||||||
wsOpts := make(map[string]any)
|
wsOpts := make(map[string]any)
|
||||||
wsOpts["path"] = []string{"/"}
|
wsOpts["path"] = []string{"/"}
|
||||||
if values["host"] != "" && values["host"] != nil {
|
if host, ok := values["host"]; ok && host != "" {
|
||||||
headers["Host"] = values["host"].(string)
|
headers["Host"] = host.(string)
|
||||||
}
|
}
|
||||||
if values["path"] != "" && values["path"] != nil {
|
if path, ok := values["path"]; ok && path != "" {
|
||||||
wsOpts["path"] = values["path"].(string)
|
wsOpts["path"] = path.(string)
|
||||||
}
|
}
|
||||||
wsOpts["headers"] = headers
|
wsOpts["headers"] = headers
|
||||||
vmess["ws-opts"] = wsOpts
|
vmess["ws-opts"] = wsOpts
|
||||||
|
@ -450,18 +366,6 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||||
return proxies, nil
|
return proxies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func urlSafe(data string) string {
|
|
||||||
return strings.ReplaceAll(strings.ReplaceAll(data, "+", "-"), "/", "_")
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeUrlSafe(data string) string {
|
|
||||||
dcBuf, err := base64.RawURLEncoding.DecodeString(data)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return string(dcBuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func uniqueName(names map[string]int, name string) string {
|
func uniqueName(names map[string]int, name string) string {
|
||||||
if index, ok := names[name]; ok {
|
if index, ok := names[name]; ok {
|
||||||
index++
|
index++
|
||||||
|
|
89
common/convert/v.go
Normal file
89
common/convert/v.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package convert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy map[string]any) {
|
||||||
|
// Xray VMessAEAD / VLESS share link standard
|
||||||
|
// https://github.com/XTLS/Xray-core/discussions/716
|
||||||
|
query := url.Query()
|
||||||
|
proxy["name"] = uniqueName(names, url.Fragment)
|
||||||
|
proxy["type"] = scheme
|
||||||
|
proxy["server"] = url.Hostname()
|
||||||
|
proxy["port"] = url.Port()
|
||||||
|
proxy["uuid"] = url.User.Username()
|
||||||
|
proxy["udp"] = true
|
||||||
|
proxy["skip-cert-verify"] = false
|
||||||
|
proxy["tls"] = false
|
||||||
|
tls := strings.ToLower(query.Get("security"))
|
||||||
|
if strings.HasSuffix(tls, "tls") {
|
||||||
|
proxy["tls"] = true
|
||||||
|
}
|
||||||
|
if sni := query.Get("sni"); sni != "" {
|
||||||
|
proxy["servername"] = sni
|
||||||
|
}
|
||||||
|
|
||||||
|
network := strings.ToLower(query.Get("type"))
|
||||||
|
if network == "" {
|
||||||
|
network = "tcp"
|
||||||
|
}
|
||||||
|
fakeType := strings.ToLower(query.Get("headerType"))
|
||||||
|
if fakeType == "http" {
|
||||||
|
network = "http"
|
||||||
|
} else if network == "http" {
|
||||||
|
network = "h2"
|
||||||
|
}
|
||||||
|
proxy["network"] = network
|
||||||
|
switch network {
|
||||||
|
case "tcp":
|
||||||
|
if fakeType != "none" {
|
||||||
|
headers := make(map[string]any)
|
||||||
|
httpOpts := make(map[string]any)
|
||||||
|
httpOpts["path"] = []string{"/"}
|
||||||
|
|
||||||
|
if host := query.Get("host"); host != "" {
|
||||||
|
headers["Host"] = []string{host}
|
||||||
|
}
|
||||||
|
|
||||||
|
if method := query.Get("method"); method != "" {
|
||||||
|
httpOpts["method"] = method
|
||||||
|
}
|
||||||
|
|
||||||
|
if path := query.Get("path"); path != "" {
|
||||||
|
httpOpts["path"] = []string{path}
|
||||||
|
}
|
||||||
|
httpOpts["headers"] = headers
|
||||||
|
proxy["http-opts"] = httpOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
case "http":
|
||||||
|
headers := make(map[string]any)
|
||||||
|
h2Opts := make(map[string]any)
|
||||||
|
h2Opts["path"] = []string{"/"}
|
||||||
|
if path := query.Get("path"); path != "" {
|
||||||
|
h2Opts["path"] = []string{path}
|
||||||
|
}
|
||||||
|
if host := query.Get("host"); host != "" {
|
||||||
|
h2Opts["host"] = []string{host}
|
||||||
|
}
|
||||||
|
h2Opts["headers"] = headers
|
||||||
|
proxy["h2-opts"] = h2Opts
|
||||||
|
|
||||||
|
case "ws":
|
||||||
|
headers := make(map[string]any)
|
||||||
|
wsOpts := make(map[string]any)
|
||||||
|
headers["User-Agent"] = RandUserAgent()
|
||||||
|
headers["Host"] = query.Get("host")
|
||||||
|
wsOpts["path"] = query.Get("path")
|
||||||
|
wsOpts["headers"] = headers
|
||||||
|
|
||||||
|
proxy["ws-opts"] = wsOpts
|
||||||
|
|
||||||
|
case "grpc":
|
||||||
|
grpcOpts := make(map[string]any)
|
||||||
|
grpcOpts["grpc-service-name"] = query.Get("serviceName")
|
||||||
|
proxy["grpc-opts"] = grpcOpts
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ import (
|
||||||
|
|
||||||
CN "github.com/Dreamacro/clash/common/net"
|
CN "github.com/Dreamacro/clash/common/net"
|
||||||
"github.com/Dreamacro/clash/common/utils"
|
"github.com/Dreamacro/clash/common/utils"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
@ -84,8 +83,7 @@ func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string) {
|
||||||
|
|
||||||
metadata.AddrType = C.AtypDomainName
|
metadata.AddrType = C.AtypDomainName
|
||||||
metadata.Host = host
|
metadata.Host = host
|
||||||
metadata.DNSMode = C.DNSMapping
|
metadata.DNSMode = C.DNSNormal
|
||||||
resolver.InsertHostByIP(metadata.DstIP, host)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *SnifferDispatcher) Enable() bool {
|
func (sd *SnifferDispatcher) Enable() bool {
|
||||||
|
|
|
@ -89,17 +89,32 @@ func SniffHTTP(b []byte) (*string, error) {
|
||||||
host, _, err := net.SplitHostPort(rawHost)
|
host, _, err := net.SplitHostPort(rawHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
|
if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
|
||||||
if host, _, err = net.SplitHostPort(net.JoinHostPort(rawHost, "80")); err == nil {
|
return parseHost(rawHost)
|
||||||
if net.ParseIP(host) != nil {
|
|
||||||
return nil, fmt.Errorf("host is ip")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if net.ParseIP(host) != nil {
|
||||||
|
return nil, fmt.Errorf("host is ip")
|
||||||
|
}
|
||||||
|
|
||||||
return &host, nil
|
return &host, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, ErrNoClue
|
return nil, ErrNoClue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseHost(host string) (*string, error) {
|
||||||
|
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||||
|
if net.ParseIP(host[1:len(host)-1]) != nil {
|
||||||
|
return nil, fmt.Errorf("host is ip")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if net.ParseIP(host) != nil {
|
||||||
|
return nil, fmt.Errorf("host is ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &host, nil
|
||||||
|
}
|
||||||
|
|
|
@ -983,7 +983,8 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
|
||||||
|
|
||||||
var ports []utils.Range[uint16]
|
var ports []utils.Range[uint16]
|
||||||
if len(snifferRaw.Ports) == 0 {
|
if len(snifferRaw.Ports) == 0 {
|
||||||
ports = append(ports, *utils.NewRange[uint16](0, 65535))
|
ports = append(ports, *utils.NewRange[uint16](80, 80))
|
||||||
|
ports = append(ports, *utils.NewRange[uint16](443, 443))
|
||||||
} else {
|
} else {
|
||||||
for _, portRange := range snifferRaw.Ports {
|
for _, portRange := range snifferRaw.Ports {
|
||||||
portRaws := strings.Split(portRange, "-")
|
portRaws := strings.Split(portRange, "-")
|
||||||
|
|
Loading…
Reference in a new issue