Merge pull request #109 from Snawoot/dp_export

DP export
This commit is contained in:
Vladislav Yarmak
2025-11-20 01:03:14 +02:00
committed by GitHub
2 changed files with 67 additions and 12 deletions
+1
View File
@@ -101,6 +101,7 @@ eu3.sec-tunnel.com,77.111.244.22,443
| cafile | String | use custom CA certificate bundle file | | cafile | String | use custom CA certificate bundle file |
| certchain-workaround | Boolean | add bundled cross-signed intermediate cert to certchain to make it check out on old systems (default true) | | certchain-workaround | Boolean | add bundled cross-signed intermediate cert to certchain to make it check out on old systems (default true) |
| country | String | desired proxy location (default "EU") | | country | String | desired proxy location (default "EU") |
| dp-export | - | export configuration for dumbproxy |
| fake-SNI | String | domain name to use as SNI in communications with servers | | fake-SNI | String | domain name to use as SNI in communications with servers |
| init-retries | Number | number of attempts for initialization steps, zero for unlimited retry | | init-retries | Number | number of attempts for initialization steps, zero for unlimited retry |
| init-retry-interval | Duration | delay between initialization retries (default 5s) | | init-retry-interval | Duration | delay between initialization retries (default 5s) |
+66 -12
View File
@@ -16,6 +16,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@@ -102,6 +103,7 @@ type CLIArgs struct {
country string country string
listCountries bool listCountries bool
listProxies bool listProxies bool
dpExport bool
bindAddress string bindAddress string
socksMode bool socksMode bool
verbosity int verbosity int
@@ -150,6 +152,7 @@ func parse_args() *CLIArgs {
flag.StringVar(&args.country, "country", "EU", "desired proxy location") flag.StringVar(&args.country, "country", "EU", "desired proxy location")
flag.BoolVar(&args.listCountries, "list-countries", false, "list available countries and exit") flag.BoolVar(&args.listCountries, "list-countries", false, "list available countries and exit")
flag.BoolVar(&args.listProxies, "list-proxies", false, "output proxy list and exit") flag.BoolVar(&args.listProxies, "list-proxies", false, "output proxy list and exit")
flag.BoolVar(&args.dpExport, "dp-export", false, "export configuration for dumbproxy")
flag.StringVar(&args.bindAddress, "bind-address", "127.0.0.1:18080", "proxy listen address") flag.StringVar(&args.bindAddress, "bind-address", "127.0.0.1:18080", "proxy listen address")
flag.BoolVar(&args.socksMode, "socks-mode", false, "listen for SOCKS requests instead of HTTP") flag.BoolVar(&args.socksMode, "socks-mode", false, "listen for SOCKS requests instead of HTTP")
flag.IntVar(&args.verbosity, "verbosity", 20, "logging verbosity "+ flag.IntVar(&args.verbosity, "verbosity", 20, "logging verbosity "+
@@ -188,8 +191,8 @@ func parse_args() *CLIArgs {
if args.country == "" { if args.country == "" {
arg_fail("Country can't be empty string.") arg_fail("Country can't be empty string.")
} }
if args.listCountries && args.listProxies { if args.listCountries && args.listProxies || args.listCountries && args.dpExport || args.listProxies && args.dpExport {
arg_fail("list-countries and list-proxies flags are mutually exclusive") arg_fail("mutually exclusive output arguments were provided")
} }
return args return args
} }
@@ -338,6 +341,31 @@ func run() int {
return printCountries(try, mainLogger, args.timeout, seclient) return printCountries(try, mainLogger, args.timeout, seclient)
} }
var ips []se.SEIPEntry
if args.listProxies || args.dpExport {
err = try("discover", func() error {
ctx, cl := context.WithTimeout(context.Background(), args.timeout)
defer cl()
ips, err = seclient.Discover(ctx, fmt.Sprintf("\"%s\",,", args.country))
if err != nil {
return err
}
if len(ips) == 0 {
return errors.New("empty endpoints list!")
}
return nil
})
if err != nil {
return 12
}
if args.listProxies {
return printProxies(ips, seclient)
}
if args.dpExport {
return dpExport(ips, seclient)
}
}
handlerDialerFactory := func(endpointAddr string) dialer.ContextDialer { handlerDialerFactory := func(endpointAddr string) dialer.ContextDialer {
return dialer.NewProxyDialer( return dialer.NewProxyDialer(
dialer.WrapStringToCb(endpointAddr), dialer.WrapStringToCb(endpointAddr),
@@ -351,10 +379,9 @@ func run() int {
d) d)
} }
var ips []se.SEIPEntry
var handlerDialer dialer.ContextDialer var handlerDialer dialer.ContextDialer
if args.overrideProxyAddress == "" || args.listProxies { if args.overrideProxyAddress == "" {
err = try("discover", func() error { err = try("discover", func() error {
ctx, cl := context.WithTimeout(context.Background(), args.timeout) ctx, cl := context.WithTimeout(context.Background(), args.timeout)
defer cl() defer cl()
@@ -365,10 +392,6 @@ func run() int {
if len(res) == 0 { if len(res) == 0 {
return errors.New("empty endpoints list!") return errors.New("empty endpoints list!")
} }
if args.listProxies {
ips = res
return nil
}
mainLogger.Info("Discovered endpoints: %v. Starting server selection routine %q.", res, args.serverSelection.value) mainLogger.Info("Discovered endpoints: %v. Starting server selection routine %q.", res, args.serverSelection.value)
var ss dialer.SelectionFunc var ss dialer.SelectionFunc
@@ -414,10 +437,6 @@ func run() int {
mainLogger.Info("Endpoint override: %s", sanitizedEndpoint) mainLogger.Info("Endpoint override: %s", sanitizedEndpoint)
} }
if args.listProxies {
return printProxies(ips, seclient)
}
clock.RunTicker(context.Background(), args.refresh, args.refreshRetry, func(ctx context.Context) error { clock.RunTicker(context.Background(), args.refresh, args.refreshRetry, func(ctx context.Context) error {
mainLogger.Info("Refreshing login...") mainLogger.Info("Refreshing login...")
reqCtx, cl := context.WithTimeout(ctx, args.timeout) reqCtx, cl := context.WithTimeout(ctx, args.timeout)
@@ -503,6 +522,41 @@ func printProxies(ips []se.SEIPEntry, seclient *se.SEClient) int {
return 0 return 0
} }
func dpExport(ips []se.SEIPEntry, seclient *se.SEClient) int {
wr := csv.NewWriter(os.Stdout)
wr.Comma = ' '
defer wr.Flush()
creds := url.UserPassword(seclient.GetProxyCredentials())
var gotOne bool
for i, ip := range ips {
if len(ip.Ports) == 0 {
continue
}
u := url.URL{
Scheme: "https",
User: creds,
Host: net.JoinHostPort(
ip.IP,
strconv.Itoa(int(ip.Ports[0])),
),
RawQuery: url.Values{
"sni": []string{""},
"peername": []string{fmt.Sprintf("%s%d.%s", strings.ToLower(ip.Geo.CountryCode), i, PROXY_SUFFIX)},
}.Encode(),
}
key := "proxy"
if gotOne {
key = "#proxy"
}
wr.Write([]string{
key,
u.String(),
})
gotOne = true
}
return 0
}
func sanitizeFixedProxyAddress(addr string) string { func sanitizeFixedProxyAddress(addr string) string {
if _, _, err := net.SplitHostPort(addr); err == nil { if _, _, err := net.SplitHostPort(addr); err == nil {
return addr return addr