diff --git a/app/app.go b/app/app.go index 4c6ba4df3..1b1020350 100644 --- a/app/app.go +++ b/app/app.go @@ -6,10 +6,12 @@ import ( "fmt" "log/slog" "net/netip" + "path" "github.com/bepass-org/warp-plus/psiphon" "github.com/bepass-org/warp-plus/warp" "github.com/bepass-org/warp-plus/wiresocks" + "github.com/go-ini/ini" ) const singleMTU = 1330 @@ -22,6 +24,7 @@ type WarpOptions struct { Psiphon *PsiphonOptions Gool bool Scan *wiresocks.ScanOptions + CacheDir string } type PsiphonOptions struct { @@ -38,7 +41,7 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error { } // create identities - if err := createPrimaryAndSecondaryIdentities(l.With("subsystem", "warp/account"), opts.License); err != nil { + if err := createPrimaryAndSecondaryIdentities(l.With("subsystem", "warp/account"), opts); err != nil { return err } @@ -46,6 +49,17 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error { endpoints := []string{opts.Endpoint, opts.Endpoint} if opts.Scan != nil { + cfg, err := ini.Load(path.Join(opts.CacheDir, "primary", "wgcf-profile.ini")) + if err != nil { + return fmt.Errorf("failed to read file: %w", err) + } + + // Reading the private key from the 'Interface' section + opts.Scan.PrivateKey = cfg.Section("Interface").Key("PrivateKey").String() + + // Reading the public key from the 'Peer' section + opts.Scan.PublicKey = cfg.Section("Peer").Key("PublicKey").String() + res, err := wiresocks.RunScan(ctx, l, *opts.Scan) if err != nil { return err @@ -65,22 +79,22 @@ func RunWarp(ctx context.Context, l *slog.Logger, opts WarpOptions) error { case opts.Psiphon != nil: l.Info("running in Psiphon (cfon) mode") // run primary warp on a random tcp port and run psiphon on bind address - warpErr = runWarpWithPsiphon(ctx, l, opts.Bind, endpoints[0], opts.Psiphon.Country) + warpErr = runWarpWithPsiphon(ctx, l, opts, endpoints[0]) case opts.Gool: l.Info("running in warp-in-warp (gool) mode") // run warp in warp - warpErr = runWarpInWarp(ctx, l, opts.Bind, endpoints) + warpErr = runWarpInWarp(ctx, l, opts, endpoints) default: l.Info("running in normal warp mode") // just run primary warp on bindAddress - warpErr = runWarp(ctx, l, opts.Bind, endpoints[0]) + warpErr = runWarp(ctx, l, opts, endpoints[0]) } return warpErr } -func runWarp(ctx context.Context, l *slog.Logger, bind netip.AddrPort, endpoint string) error { - conf, err := wiresocks.ParseConfig("./stuff/primary/wgcf-profile.ini", endpoint) +func runWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint string) error { + conf, err := wiresocks.ParseConfig(path.Join(opts.CacheDir, "primary", "wgcf-profile.ini"), endpoint) if err != nil { return err } @@ -97,18 +111,18 @@ func runWarp(ctx context.Context, l *slog.Logger, bind netip.AddrPort, endpoint return err } - _, err = tnet.StartProxy(bind) + _, err = tnet.StartProxy(opts.Bind) if err != nil { return err } - l.Info("serving proxy", "address", bind) + l.Info("serving proxy", "address", opts.Bind) return nil } -func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, bind netip.AddrPort, endpoint string, country string) error { - conf, err := wiresocks.ParseConfig("./stuff/primary/wgcf-profile.ini", endpoint) +func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoint string) error { + conf, err := wiresocks.ParseConfig(path.Join(opts.CacheDir, "primary", "wgcf-profile.ini"), endpoint) if err != nil { return err } @@ -131,19 +145,19 @@ func runWarpWithPsiphon(ctx context.Context, l *slog.Logger, bind netip.AddrPort } // run psiphon - err = psiphon.RunPsiphon(ctx, l.With("subsystem", "psiphon"), warpBind.String(), bind.String(), country) + err = psiphon.RunPsiphon(ctx, l.With("subsystem", "psiphon"), warpBind.String(), opts.Bind.String(), opts.Psiphon.Country) if err != nil { return fmt.Errorf("unable to run psiphon %w", err) } - l.Info("serving proxy", "address", bind) + l.Info("serving proxy", "address", opts.Bind) return nil } -func runWarpInWarp(ctx context.Context, l *slog.Logger, bind netip.AddrPort, endpoints []string) error { +func runWarpInWarp(ctx context.Context, l *slog.Logger, opts WarpOptions, endpoints []string) error { // Run outer warp - conf, err := wiresocks.ParseConfig("./stuff/primary/wgcf-profile.ini", endpoints[0]) + conf, err := wiresocks.ParseConfig(path.Join(opts.CacheDir, "primary", "wgcf-profile.ini"), endpoints[0]) if err != nil { return err } @@ -167,7 +181,7 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, bind netip.AddrPort, end } // Run inner warp - conf, err = wiresocks.ParseConfig("./stuff/secondary/wgcf-profile.ini", addr.String()) + conf, err = wiresocks.ParseConfig(path.Join(opts.CacheDir, "secondary", "wgcf-profile.ini"), addr.String()) if err != nil { return err } @@ -183,25 +197,25 @@ func runWarpInWarp(ctx context.Context, l *slog.Logger, bind netip.AddrPort, end return err } - _, err = tnet.StartProxy(bind) + _, err = tnet.StartProxy(opts.Bind) if err != nil { return err } - l.Info("serving proxy", "address", bind) + l.Info("serving proxy", "address", opts.Bind) return nil } -func createPrimaryAndSecondaryIdentities(l *slog.Logger, license string) error { +func createPrimaryAndSecondaryIdentities(l *slog.Logger, opts WarpOptions) error { // make primary identity - err := warp.LoadOrCreateIdentity(l, "./stuff/primary", license) + err := warp.LoadOrCreateIdentity(l, path.Join(opts.CacheDir, "primary"), opts.License) if err != nil { l.Error("couldn't load primary warp identity") return err } // make secondary - err = warp.LoadOrCreateIdentity(l, "./stuff/secondary", license) + err = warp.LoadOrCreateIdentity(l, path.Join(opts.CacheDir, "secondary"), opts.License) if err != nil { l.Error("couldn't load secondary warp identity") return err diff --git a/example_config.json b/example_config.json index 6d24f3da7..599791482 100644 --- a/example_config.json +++ b/example_config.json @@ -7,5 +7,6 @@ "cfon": false, "country": "DE", "scan": true, - "rtt": "1000ms" + "rtt": "1000ms", + "cache-dir": "" } diff --git a/go.mod b/go.mod index bbc54c0fc..7ac9209f5 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464 // indirect github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240305020009-09f917290799 // indirect github.com/Psiphon-Labs/quic-go v0.0.0-20240305203241-7c4a760d03cc // indirect + github.com/adrg/xdg v0.4.0 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f // indirect github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61 // indirect diff --git a/go.sum b/go.sum index 678cc3077..49baf783f 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240305020009-09f917290799 h1:dHFQz6 github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240305020009-09f917290799/go.mod h1:ECTyVpleBW9oR/iHi185js4Fs7YD5T8A6tujOUzltxs= github.com/Psiphon-Labs/quic-go v0.0.0-20240305203241-7c4a760d03cc h1:o9jpHz1Vuum0oasqBX4kKB8VQrR+VJzEJsBg6XAz5YU= github.com/Psiphon-Labs/quic-go v0.0.0-20240305203241-7c4a760d03cc/go.mod h1:1gvBCJ18gsMqvZXkPkq0u9/BQKvjNS5RFWwF5uLl2Ys= +github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= +github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f h1:SaJ6yqg936TshyeFZqQE+N+9hYkIeL9AMr7S4voCl10= @@ -243,6 +245,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/main.go b/main.go index 9e6cf8390..20c567cce 100644 --- a/main.go +++ b/main.go @@ -8,11 +8,13 @@ import ( "net/netip" "os" "os/signal" + "path" "syscall" "time" _ "net/http/pprof" + "github.com/adrg/xdg" "github.com/bepass-org/warp-plus/app" "github.com/bepass-org/warp-plus/warp" "github.com/bepass-org/warp-plus/wiresocks" @@ -22,6 +24,8 @@ import ( "github.com/peterbourgon/ff/v4/ffjson" ) +const appName = "warp-plus" + var psiphonCountries = []string{ "AT", "BE", @@ -56,7 +60,7 @@ var psiphonCountries = []string{ } func main() { - fs := ff.NewFlagSet("warp-plus") + fs := ff.NewFlagSet(appName) var ( v4 = fs.BoolShort('4', "only use IPv4 for random warp endpoint") v6 = fs.BoolShort('6', "only use IPv6 for random warp endpoint") @@ -69,6 +73,7 @@ func main() { country = fs.StringEnumLong("country", fmt.Sprintf("psiphon country code (valid values: %s)", psiphonCountries), psiphonCountries...) scan = fs.BoolLong("scan", "enable warp scanning") rtt = fs.DurationLong("rtt", 1000*time.Millisecond, "scanner rtt limit") + cacheDir = fs.StringLong("cache-dir", "", "directory to store generated profiles") _ = fs.String('c', "config", "", "path to config file") ) @@ -117,6 +122,17 @@ func main() { Gool: *gool, } + switch { + case *cacheDir != "": + opts.CacheDir = *cacheDir + case xdg.CacheHome != "": + opts.CacheDir = path.Join(xdg.CacheHome, appName) + case os.Getenv("HOME") != "": + opts.CacheDir = path.Join(os.Getenv("HOME"), ".cache", appName) + default: + opts.CacheDir = "warp_plus_cache" + } + if *psiphon { l.Info("psiphon mode enabled", "country", *country) opts.Psiphon = &app.PsiphonOptions{Country: *country} diff --git a/wiresocks/scanner.go b/wiresocks/scanner.go index 3263b154d..193bcf055 100644 --- a/wiresocks/scanner.go +++ b/wiresocks/scanner.go @@ -3,39 +3,28 @@ package wiresocks import ( "context" "errors" - "fmt" "log/slog" "time" "github.com/bepass-org/warp-plus/ipscanner" "github.com/bepass-org/warp-plus/warp" - "github.com/go-ini/ini" ) type ScanOptions struct { - V4 bool - V6 bool - MaxRTT time.Duration + V4 bool + V6 bool + MaxRTT time.Duration + PrivateKey string + PublicKey string } func RunScan(ctx context.Context, l *slog.Logger, opts ScanOptions) (result []ipscanner.IPInfo, err error) { - cfg, err := ini.Load("./stuff/primary/wgcf-profile.ini") - if err != nil { - return nil, fmt.Errorf("failed to read file: %w", err) - } - - // Reading the private key from the 'Interface' section - privateKey := cfg.Section("Interface").Key("PrivateKey").String() - - // Reading the public key from the 'Peer' section - publicKey := cfg.Section("Peer").Key("PublicKey").String() - // new scanner scanner := ipscanner.NewScanner( ipscanner.WithLogger(l.With(slog.String("subsystem", "scanner"))), ipscanner.WithWarpPing(), - ipscanner.WithWarpPrivateKey(privateKey), - ipscanner.WithWarpPeerPublicKey(publicKey), + ipscanner.WithWarpPrivateKey(opts.PrivateKey), + ipscanner.WithWarpPeerPublicKey(opts.PublicKey), ipscanner.WithUseIPv4(opts.V4), ipscanner.WithUseIPv6(opts.V6), ipscanner.WithMaxDesirableRTT(opts.MaxRTT),