From 14b9760386bd9c5b8bc6fcb14bfb251f8dad5c72 Mon Sep 17 00:00:00 2001 From: "Arthur K." Date: Sun, 8 Mar 2026 09:46:13 +0300 Subject: [PATCH] feat: proxy rotation for 9proxy.com --- .env.example | 5 +++ .gitignore | 1 + main.go | 114 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 92 insertions(+), 28 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0fbd72c --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +PROXY_USER=user-ssid- +PROXY_PASS=123 +PROXY_HOST=localhost +PROXY_PORT=8080 +PORT=80 diff --git a/.gitignore b/.gitignore index 95423cb..61ded9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /pp +.env diff --git a/main.go b/main.go index 97b534e..3c50431 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "crypto/rand" "encoding/base64" "fmt" "io" @@ -11,9 +12,18 @@ import ( "net/url" "os" "strings" + "sync/atomic" "time" ) +var ( + proxyUser string + proxyPass string + proxyHost string + sessionID string + rotateCounter atomic.Int64 +) + func getEnvDefault(name, def string) string { v := os.Getenv(name) if v != "" { @@ -22,37 +32,85 @@ func getEnvDefault(name, def string) string { return def } -func parseProxy() *url.URL { - raw := strings.TrimPrefix(strings.TrimSpace(os.Getenv("PROXY")), "http://") - if raw == "" { - log.Fatal("missing PROXY (format: login:password@ip:port)") +func generateSessionID(length int) string { + b := make([]byte, length) + if _, err := rand.Read(b); err != nil { + log.Fatalf("failed to generate session ID: %v", err) } - - creds, hostPort, ok := strings.Cut(raw, "@") - if !ok { - log.Fatal("expected login:password@ip:port") - } - - login, password, ok := strings.Cut(creds, ":") - if !ok || strings.TrimSpace(login) == "" || strings.TrimSpace(password) == "" { - log.Fatal("expected login:password before @") - } - - hostPort = strings.TrimSpace(hostPort) - if _, _, err := net.SplitHostPort(hostPort); err != nil { - log.Fatalf("invalid ip:port: %v", err) - } - - return &url.URL{Scheme: "http", User: url.UserPassword(login, password), Host: hostPort} + return base64.RawURLEncoding.EncodeToString(b)[:length] } -func proxyHandler(transport *http.Transport, upstream *url.URL) http.HandlerFunc { +func getUpstreamURL() *url.URL { + counter := rotateCounter.Load() + user := fmt.Sprintf("%s%s%d", proxyUser, sessionID, counter) + return &url.URL{ + Scheme: "http", + User: url.UserPassword(user, proxyPass), + Host: proxyHost, + } +} + +func parseProxyConfig() { + proxyUser = os.Getenv("PROXY_USER") + if proxyUser == "" { + log.Fatal("missing PROXY_USER") + } + + proxyPass = os.Getenv("PROXY_PASS") + if proxyPass == "" { + log.Fatal("missing PROXY_PASS") + } + + proxyHost = os.Getenv("PROXY_HOST") + if proxyHost == "" { + log.Fatal("missing PROXY_HOST") + } + + port := os.Getenv("PROXY_PORT") + if port == "" { + log.Fatal("missing PROXY_PORT") + } + proxyHost = net.JoinHostPort(proxyHost, port) + + sessionID = generateSessionID(11) + rotateCounter.Store(0) +} + +func getCurrentIP() string { + upstream := getUpstreamURL() + transport := &http.Transport{Proxy: http.ProxyURL(upstream)} + client := &http.Client{Transport: transport, Timeout: 10 * time.Second} + + resp, err := client.Get("https://checkip.amazonaws.com") + if err != nil { + return fmt.Sprintf("error: %v", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Sprintf("error reading: %v", err) + } + return strings.TrimSpace(string(body)) +} + +func proxyHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/health" { + switch r.URL.Path { + case "/health": w.Write([]byte("ok")) return + case "/rotate": + newCounter := rotateCounter.Add(1) + ip := getCurrentIP() + log.Printf("Rotated to counter=%d, IP=%s", newCounter, ip) + w.Write([]byte(fmt.Sprintf("%s\n", ip))) + return } + upstream := getUpstreamURL() + transport := &http.Transport{Proxy: http.ProxyURL(upstream)} + if r.Method == http.MethodConnect { handleTunneling(w, r, upstream) return @@ -173,10 +231,10 @@ func copyHeader(dst, src http.Header) { func main() { listenAddr := ":" + getEnvDefault("PORT", "80") - upstream := parseProxy() + parseProxyConfig() - transport := &http.Transport{Proxy: http.ProxyURL(upstream)} - - log.Printf("Proxy running on %s -> http://%s@%s", listenAddr, upstream.User.Username(), upstream.Host) - log.Fatal(http.ListenAndServe(listenAddr, proxyHandler(transport, upstream))) + upstream := getUpstreamURL() + ip := getCurrentIP() + log.Printf("started listen=%s upstream=http://%s ip=%s", listenAddr, upstream.Host, ip) + log.Fatal(http.ListenAndServe(listenAddr, proxyHandler())) }