()
| 131 | ` |
| 132 | |
| 133 | func main() { |
| 134 | configPath := flag.String("config", "client_config.json", "path to client config JSON") |
| 135 | showVersion := flag.Bool("version", false, "show version and exit") |
| 136 | dumpDiag := flag.Bool("dump-diag", false, "write a redacted diagnostics zip and exit") |
| 137 | diagOutput := flag.String("diag-output", "", "path for --dump-diag output (default: goose-diagnostics-YYYYMMDD-HHMMSS.zip)") |
| 138 | flag.Parse() |
| 139 | |
| 140 | if *showVersion { |
| 141 | fmt.Println(version) |
| 142 | return |
| 143 | } |
| 144 | if *dumpDiag { |
| 145 | out, err := writeDiagnosticsZip(*diagOutput, *configPath, version) |
| 146 | if err != nil { |
| 147 | fmt.Fprintf(os.Stderr, "diagnostics failed: %v\n", err) |
| 148 | os.Exit(1) |
| 149 | } |
| 150 | fmt.Printf("diagnostics written to %s\n", out) |
| 151 | return |
| 152 | } |
| 153 | |
| 154 | fmt.Print(gooseBanner) |
| 155 | setupClientLogging() |
| 156 | |
| 157 | cfg, err := config.LoadClient(*configPath) |
| 158 | if err != nil { |
| 159 | log.Fatalf("%v", err) |
| 160 | } |
| 161 | log.Printf("[client] GooseRelayVPN client starting") |
| 162 | log.Printf("[client] config loaded from %s", *configPath) |
| 163 | log.Printf("[client] SOCKS5 proxy: socks5://%s", cfg.ListenAddr) |
| 164 | if cfg.UseFronting { |
| 165 | log.Printf("[client] mode: fronting") |
| 166 | if len(cfg.SNIHosts) == 1 { |
| 167 | log.Printf("[client] fronting via %s (sni=%s)", cfg.GoogleIP, cfg.SNIHosts[0]) |
| 168 | } else { |
| 169 | log.Printf("[client] fronting via %s (sni hosts: %s — %d throttle buckets)", cfg.GoogleIP, strings.Join(cfg.SNIHosts, ", "), len(cfg.SNIHosts)) |
| 170 | } |
| 171 | } else { |
| 172 | log.Printf("[client] mode: direct relay_urls (fronting disabled)") |
| 173 | } |
| 174 | log.Printf("[client] relay endpoints: %d (%s)", len(cfg.ScriptURLs), summarizeScriptURLs(cfg.ScriptURLs)) |
| 175 | if cfg.DebugTiming { |
| 176 | log.Printf("[client] debug_timing enabled — per-session TTFB and per-poll RTT will be logged") |
| 177 | } |
| 178 | if cfg.CoalesceStepMs > 0 { |
| 179 | log.Printf("[client] uplink coalescing: step=%dms (internal safety cap %dms; bursts of TX collapse into a single poll)", cfg.CoalesceStepMs, cfg.CoalesceMaxMs) |
| 180 | } |
| 181 | carr, err := carrier.New(carrier.Config{ |
| 182 | ScriptURLs: cfg.ScriptURLs, |
| 183 | ScriptAccounts: cfg.ScriptAccounts, |
| 184 | AESKeyHex: cfg.AESKeyHex, |
| 185 | DebugTiming: cfg.DebugTiming, |
| 186 | ClientVersion: version, |
| 187 | CoalesceStep: time.Duration(cfg.CoalesceStepMs) * time.Millisecond, |
| 188 | CoalesceMax: time.Duration(cfg.CoalesceMaxMs) * time.Millisecond, |
| 189 | IdleSlotsPerBucket: cfg.IdleSlotsPerBucket, |
| 190 | Fronting: carrier.FrontingConfig{ |
nothing calls this directly
no test coverage detected