Skip to content

Commit 3e20d86

Browse files
Update main.go
1 parent 231883a commit 3e20d86

1 file changed

Lines changed: 135 additions & 10 deletions

File tree

main.go

Lines changed: 135 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ type ClashProxy struct {
128128
Plugin string `yaml:"plugin"`
129129
PluginOpts map[string]interface{} `yaml:"plugin-opts"`
130130

131-
AuthStr string `yaml:"auth-str"`
132-
Auth string `yaml:"auth"`
131+
AuthStr string `yaml:"auth-str"`
132+
AuthStrAlt string `yaml:"auth_str"`
133+
Auth string `yaml:"auth"`
133134
Up interface{} `yaml:"up"`
134135
Down interface{} `yaml:"down"`
135136

@@ -945,6 +946,9 @@ func clashHyToURI(p ClashProxy) string {
945946
return ""
946947
}
947948
auth := p.AuthStr
949+
if auth == "" {
950+
auth = p.AuthStrAlt
951+
}
948952
if auth == "" {
949953
auth = p.Auth
950954
}
@@ -964,6 +968,12 @@ func clashHyToURI(p ClashProxy) string {
964968
if len(p.ALPN) > 0 {
965969
q.Set("alpn", strings.Join(p.ALPN, ","))
966970
}
971+
if p.Obfs != "" {
972+
q.Set("obfs", p.Obfs)
973+
}
974+
if p.Protocol != "" {
975+
q.Set("protocol", p.Protocol)
976+
}
967977

968978
return fmt.Sprintf("hy://%s@%s:%s?%s#%s",
969979
url.PathEscape(auth), p.Server, portStr, q.Encode(), url.PathEscape(p.Name))
@@ -986,6 +996,10 @@ func clashTUICToURI(p ClashProxy) string {
986996
if len(p.ALPN) > 0 {
987997
q.Set("alpn", strings.Join(p.ALPN, ","))
988998
}
999+
// Encode congestion-controller if set (non-default)
1000+
if congestion, ok := p.PluginOpts["congestion-controller"].(string); ok && congestion != "" {
1001+
q.Set("congestion_control", congestion)
1002+
}
9891003

9901004
return fmt.Sprintf("tuic://%s:%s@%s:%s?%s#%s",
9911005
url.PathEscape(p.UUID), url.PathEscape(password),
@@ -2736,8 +2750,14 @@ func parseHysteria2(raw string) (string, string) {
27362750
return "", "missing server"
27372751
}
27382752
q, _ := url.ParseQuery(queryStr)
2739-
return fmt.Sprintf(`{"type":"hysteria2","tag":"proxy","server":%q,"server_port":%d,"password":%q,"tls":{"enabled":true,"insecure":true,"server_name":%q}}`,
2740-
server, port, password, first(q.Get("sni"), server)), ""
2753+
obfsJSON := ""
2754+
if obfs := q.Get("obfs"); obfs == "salamander" {
2755+
if obfsPwd := q.Get("obfs-password"); obfsPwd != "" {
2756+
obfsJSON = fmt.Sprintf(`,"obfs":{"type":"salamander","password":%q}`, obfsPwd)
2757+
}
2758+
}
2759+
return fmt.Sprintf(`{"type":"hysteria2","tag":"proxy","server":%q,"server_port":%d,"password":%q%s,"tls":{"enabled":true,"insecure":true,"server_name":%q}}`,
2760+
server, port, password, obfsJSON, first(q.Get("sni"), server)), ""
27412761
}
27422762

27432763
func parseHysteria(raw string) (string, string) {
@@ -2766,8 +2786,13 @@ func parseHysteria(raw string) (string, string) {
27662786
if down <= 0 {
27672787
down = 50
27682788
}
2769-
return fmt.Sprintf(`{"type":"hysteria","tag":"proxy","server":%q,"server_port":%d,"up_mbps":%d,"down_mbps":%d,"auth_str":%q,"tls":{"enabled":true,"insecure":true,"server_name":%q}}`,
2770-
server, port, up, down, auth, first(q.Get("peer"), q.Get("sni"), server)), ""
2789+
obfs := q.Get("obfs")
2790+
obfsJSON := ""
2791+
if obfs != "" {
2792+
obfsJSON = fmt.Sprintf(`,"obfs":%q`, obfs)
2793+
}
2794+
return fmt.Sprintf(`{"type":"hysteria","tag":"proxy","server":%q,"server_port":%d,"up_mbps":%d,"down_mbps":%d,"auth_str":%q%s,"tls":{"enabled":true,"insecure":true,"server_name":%q}}`,
2795+
server, port, up, down, auth, obfsJSON, first(q.Get("peer"), q.Get("sni"), server)), ""
27712796
}
27722797

27732798
func parseTUIC(raw string) (string, string) {
@@ -2788,8 +2813,16 @@ func parseTUIC(raw string) (string, string) {
27882813
if err != nil {
27892814
return "", "port: " + err.Error()
27902815
}
2791-
return fmt.Sprintf(`{"type":"tuic","tag":"proxy","server":%q,"server_port":%d,"uuid":%q,"password":%q,"tls":{"enabled":true,"insecure":true,"server_name":%q}}`,
2792-
server, port, uuid, password, first(u.Query().Get("sni"), server)), ""
2816+
q := u.Query()
2817+
sni := first(q.Get("sni"), server)
2818+
congestion := first(q.Get("congestion_control"), q.Get("congestion-controller"), "bbr")
2819+
udpRelayMode := q.Get("udp_relay_mode")
2820+
udpJSON := ""
2821+
if udpRelayMode != "" {
2822+
udpJSON = fmt.Sprintf(`,"udp_relay_mode":%q`, udpRelayMode)
2823+
}
2824+
return fmt.Sprintf(`{"type":"tuic","tag":"proxy","server":%q,"server_port":%d,"uuid":%q,"password":%q,"congestion_control":%q%s,"tls":{"enabled":true,"insecure":true,"server_name":%q}}`,
2825+
server, port, uuid, password, congestion, udpJSON, sni), ""
27932826
}
27942827

27952828
func parseSSR(raw string) (string, string) {
@@ -3202,6 +3235,16 @@ func trojanClashYAML(raw, name string) (string, bool) {
32023235

32033236
func ssClashYAML(raw, name string) (string, bool) {
32043237
trimmed := strings.TrimPrefix(raw, "ss://")
3238+
// Capture query string before stripping fragment
3239+
queryStr := ""
3240+
if idx := strings.Index(trimmed, "?"); idx != -1 {
3241+
qEnd := len(trimmed)
3242+
if fragIdx := strings.Index(trimmed[idx:], "#"); fragIdx != -1 {
3243+
qEnd = idx + fragIdx
3244+
}
3245+
queryStr = trimmed[idx+1 : qEnd]
3246+
trimmed = trimmed[:idx] + trimmed[qEnd:]
3247+
}
32053248
if idx := strings.Index(trimmed, "#"); idx != -1 {
32063249
trimmed = trimmed[:idx]
32073250
}
@@ -3249,9 +3292,66 @@ func ssClashYAML(raw, name string) (string, bool) {
32493292
var sb strings.Builder
32503293
fmt.Fprintf(&sb, " - name: %s\n type: ss\n server: %s\n port: %d\n cipher: %s\n password: %s\n udp: true\n",
32513294
yamlQuote(name), yamlQuote(server), port, yamlQuote(parts[0]), yamlQuote(parts[1]))
3295+
3296+
// Parse plugin info from query string
3297+
if queryStr != "" {
3298+
q, _ := url.ParseQuery(queryStr)
3299+
if pluginParam := q.Get("plugin"); pluginParam != "" {
3300+
// pluginParam format: "pluginName;opt1=val1;opt2=val2"
3301+
pluginParts := strings.SplitN(pluginParam, ";", 2)
3302+
pluginName := pluginParts[0]
3303+
// Map URI plugin names to Clash plugin names
3304+
switch {
3305+
case pluginName == "obfs-local" || pluginName == "obfs":
3306+
fmt.Fprintf(&sb, " plugin: obfs\n plugin-opts:\n")
3307+
if len(pluginParts) > 1 {
3308+
opts := parsePluginOpts(pluginParts[1])
3309+
if mode, ok := opts["obfs"]; ok {
3310+
fmt.Fprintf(&sb, " mode: %s\n", yamlQuote(mode))
3311+
}
3312+
if host, ok := opts["obfs-host"]; ok {
3313+
fmt.Fprintf(&sb, " host: %s\n", yamlQuote(host))
3314+
}
3315+
}
3316+
case pluginName == "v2ray-plugin":
3317+
fmt.Fprintf(&sb, " plugin: v2ray-plugin\n plugin-opts:\n")
3318+
if len(pluginParts) > 1 {
3319+
opts := parsePluginOpts(pluginParts[1])
3320+
mode := first(opts["mode"], "websocket")
3321+
fmt.Fprintf(&sb, " mode: %s\n", yamlQuote(mode))
3322+
if path, ok := opts["path"]; ok {
3323+
fmt.Fprintf(&sb, " path: %s\n", yamlQuote(path))
3324+
}
3325+
if host, ok := opts["host"]; ok {
3326+
fmt.Fprintf(&sb, " host: %s\n", yamlQuote(host))
3327+
}
3328+
if _, hasTLS := opts["tls"]; hasTLS {
3329+
fmt.Fprintf(&sb, " tls: true\n")
3330+
}
3331+
}
3332+
}
3333+
}
3334+
}
32523335
return sb.String(), true
32533336
}
32543337

3338+
// parsePluginOpts parses "key=val;key2=val2;flag" into a map.
3339+
func parsePluginOpts(s string) map[string]string {
3340+
opts := make(map[string]string)
3341+
for _, part := range strings.Split(s, ";") {
3342+
part = strings.TrimSpace(part)
3343+
if part == "" {
3344+
continue
3345+
}
3346+
if idx := strings.Index(part, "="); idx != -1 {
3347+
opts[part[:idx]] = part[idx+1:]
3348+
} else {
3349+
opts[part] = "" // flag without value (e.g. "tls")
3350+
}
3351+
}
3352+
return opts
3353+
}
3354+
32553355
func hy2ClashYAML(raw, name string) (string, bool) {
32563356
trimmed := strings.TrimPrefix(raw, "hy2://")
32573357
if i := strings.LastIndex(trimmed, "#"); i != -1 {
@@ -3287,6 +3387,12 @@ func hy2ClashYAML(raw, name string) (string, bool) {
32873387
var sb strings.Builder
32883388
fmt.Fprintf(&sb, " - name: %s\n type: hysteria2\n server: %s\n port: %d\n password: %s\n sni: %s\n skip-cert-verify: true\n udp: true\n",
32893389
yamlQuote(name), yamlQuote(server), port, yamlQuote(password), yamlQuote(first(q.Get("sni"), server)))
3390+
if obfs := q.Get("obfs"); obfs != "" {
3391+
fmt.Fprintf(&sb, " obfs: %s\n", yamlQuote(obfs))
3392+
if obfsPwd := q.Get("obfs-password"); obfsPwd != "" {
3393+
fmt.Fprintf(&sb, " obfs-password: %s\n", yamlQuote(obfsPwd))
3394+
}
3395+
}
32903396
return sb.String(), true
32913397
}
32923398

@@ -3320,6 +3426,20 @@ func hyClashYAML(raw, name string) (string, bool) {
33203426
fmt.Fprintf(&sb, " - name: %s\n type: hysteria\n server: %s\n port: %d\n auth-str: %s\n up: %d\n down: %d\n sni: %s\n skip-cert-verify: true\n udp: true\n",
33213427
yamlQuote(name), yamlQuote(server), port, yamlQuote(auth), up, down,
33223428
yamlQuote(first(q.Get("peer"), q.Get("sni"), server)))
3429+
if obfs := q.Get("obfs"); obfs != "" {
3430+
fmt.Fprintf(&sb, " obfs: %s\n", yamlQuote(obfs))
3431+
}
3432+
if proto := q.Get("protocol"); proto != "" {
3433+
fmt.Fprintf(&sb, " protocol: %s\n", yamlQuote(proto))
3434+
}
3435+
if alpnStr := q.Get("alpn"); alpnStr != "" {
3436+
parts := strings.Split(alpnStr, ",")
3437+
quoted := make([]string, len(parts))
3438+
for i, a := range parts {
3439+
quoted[i] = yamlQuote(strings.TrimSpace(a))
3440+
}
3441+
fmt.Fprintf(&sb, " alpn: [%s]\n", strings.Join(quoted, ", "))
3442+
}
33233443
return sb.String(), true
33243444
}
33253445

@@ -3335,10 +3455,15 @@ func tuicClashYAML(raw, name string) (string, bool) {
33353455
if err != nil || uuid == "" || server == "" {
33363456
return "", false
33373457
}
3458+
q := u.Query()
3459+
congestion := first(q.Get("congestion_control"), q.Get("congestion-controller"), "bbr")
33383460
var sb strings.Builder
3339-
fmt.Fprintf(&sb, " - name: %s\n type: tuic\n server: %s\n port: %d\n uuid: %s\n password: %s\n sni: %s\n skip-cert-verify: true\n udp: true\n congestion-controller: bbr\n reduce-rtt: true\n",
3461+
fmt.Fprintf(&sb, " - name: %s\n type: tuic\n server: %s\n port: %d\n uuid: %s\n password: %s\n sni: %s\n skip-cert-verify: true\n udp: true\n congestion-controller: %s\n reduce-rtt: true\n",
33403462
yamlQuote(name), yamlQuote(server), port, yamlQuote(uuid), yamlQuote(password),
3341-
yamlQuote(first(u.Query().Get("sni"), server)))
3463+
yamlQuote(first(q.Get("sni"), server)), congestion)
3464+
if udpRelay := first(q.Get("udp_relay_mode"), q.Get("udp-relay-mode")); udpRelay != "" {
3465+
fmt.Fprintf(&sb, " udp-relay-mode: %s\n", yamlQuote(udpRelay))
3466+
}
33423467
return sb.String(), true
33433468
}
33443469

0 commit comments

Comments
 (0)