win 0cda0e0b96
Some checks failed
CI / test (push) Failing after 8s
CI / golangci-lint (push) Failing after 5s
Security Scan / backend-security (push) Failing after 7s
Security Scan / frontend-security (push) Failing after 6s
feat: add dockerized antigravity ls worker mode
2026-03-30 23:57:25 +08:00

139 lines
3.5 KiB
Go

package lspool
import (
"fmt"
"net/url"
"os"
"os/exec"
"strings"
"github.com/Wei-Shaw/sub2api/internal/pkg/proxyurl"
)
type lsLaunchPlan struct {
cmd *exec.Cmd
effectiveProxyURL string
proxyMode string
cleanup func()
}
func prepareLSLaunchPlan(binPath string, args []string, rawProxyURL string) (*lsLaunchPlan, error) {
normalized, parsed, err := proxyurl.Parse(rawProxyURL)
if err != nil {
return nil, err
}
plan := &lsLaunchPlan{
cmd: exec.Command(binPath, args...),
proxyMode: "direct",
}
if parsed == nil {
return plan, nil
}
switch strings.ToLower(parsed.Scheme) {
case "http", "https":
plan.effectiveProxyURL = normalized
plan.proxyMode = "env-http-proxy"
return plan, nil
case "socks5", "socks5h":
if proxychainsPath, err := exec.LookPath("proxychains4"); err == nil {
cfgPath, err := writeProxychainsConfig(parsed)
if err != nil {
return nil, err
}
plan.cmd = exec.Command(proxychainsPath, append([]string{"-f", cfgPath, binPath}, args...)...)
plan.proxyMode = "proxychains4"
plan.cleanup = func() {
_ = os.Remove(cfgPath)
}
return plan, nil
}
effectiveProxyURL, err := prepareLSProxyURL(normalized)
if err != nil {
return nil, err
}
plan.effectiveProxyURL = effectiveProxyURL
plan.proxyMode = "http-connect-bridge"
return plan, nil
default:
return nil, fmt.Errorf("unsupported LS proxy scheme: %s", parsed.Scheme)
}
}
func writeProxychainsConfig(proxyURL *url.URL) (string, error) {
content, err := buildProxychainsConfig(proxyURL)
if err != nil {
return "", err
}
file, err := os.CreateTemp("", "sub2api-proxychains-*.conf")
if err != nil {
return "", fmt.Errorf("create proxychains config: %w", err)
}
if _, err := file.WriteString(content); err != nil {
_ = file.Close()
_ = os.Remove(file.Name())
return "", fmt.Errorf("write proxychains config: %w", err)
}
if err := file.Close(); err != nil {
_ = os.Remove(file.Name())
return "", fmt.Errorf("close proxychains config: %w", err)
}
return file.Name(), nil
}
func buildProxychainsConfig(proxyURL *url.URL) (string, error) {
if proxyURL == nil {
return "", fmt.Errorf("proxy url is nil")
}
if scheme := strings.ToLower(proxyURL.Scheme); scheme != "socks5" && scheme != "socks5h" {
return "", fmt.Errorf("proxychains only supports socks5/socks5h, got %s", proxyURL.Scheme)
}
host := strings.TrimSpace(proxyURL.Hostname())
port := strings.TrimSpace(proxyURL.Port())
if host == "" {
return "", fmt.Errorf("proxy host is empty")
}
if port == "" {
port = "1080"
}
username := proxyURL.User.Username()
password, _ := proxyURL.User.Password()
if strings.ContainsAny(username, " \t\r\n") || strings.ContainsAny(password, " \t\r\n") {
return "", fmt.Errorf("proxychains credentials cannot contain whitespace")
}
var builder strings.Builder
builder.WriteString("strict_chain\n")
builder.WriteString("proxy_dns\n")
builder.WriteString("remote_dns_subnet 224\n")
builder.WriteString("tcp_connect_time_out 8000\n")
builder.WriteString("tcp_read_time_out 15000\n")
builder.WriteString("localnet 127.0.0.0/255.0.0.0\n")
builder.WriteString("localnet ::1/128\n")
builder.WriteString("[ProxyList]\n")
builder.WriteString("socks5 ")
builder.WriteString(host)
builder.WriteString(" ")
builder.WriteString(port)
if username != "" {
builder.WriteString(" ")
builder.WriteString(username)
if password != "" {
builder.WriteString(" ")
builder.WriteString(password)
}
}
builder.WriteString("\n")
return builder.String(), nil
}