// Package main provides a utility named ct-sans. // // Install: // // go install git.cs.kau.se/rasmoste/ct-sans@latest // // Usage: // // $ ct-sans -h package main import ( "flag" "fmt" "log" "os" "time" "git.cs.kau.se/rasmoste/ct-sans/internal/ctflag" ) const usage = `ct-sans collects SANs in CT-logged certificates Usage: ct-sans snapshot [Options...] Refresh log lists, signed tree heads, and timestamps ct-sans collect [Options...] Collect SANs with regards to the current snapshot ct-sans assemble [Options...] Assemble a dataset manifest and print a command that combines, sorts, and removes duplicate SANs that were collected. Help: ct-sans [-h] [--help] Options: -d, --directory: The ct-sans working directory (Default: "ct-sans") -w, --workers: Number of parallel download workers per log (Default: 2) -k, --batch-disk: Certificate batch size before persisting (Default: 16384) -q, --batch-req: Certificate batch size to use in request (Default: 512) -a, --http-agent: HTTP agent to use in all request (Default: "git.cs.kau.se/rasmoste/ct-sans") -m, --metrics: How often to emit metrics to stderr (Default: 15s) ` type options struct { Directory string WorkersPerLog uint64 PersistSize uint64 BatchSize uint64 HTTPAgent string MetricsInterval time.Duration logDirectory string metadataFile string metadataSignatureFile string metadataTimestampFile string sthFile string stateFile string sansFile string } func main() { log.SetOutput(os.Stdout) log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) if ctflag.WantHelp(os.Args, 1) { fmt.Fprintf(os.Stdout, usage) os.Exit(1) } // Define command-line options fs := ctflag.NewFlagSet() opts := options{} ctflag.String(&fs, &opts.Directory, "directory", "d", "ct-sans") ctflag.Uint64(&fs, &opts.WorkersPerLog, "workers", "w", 2) ctflag.Uint64(&fs, &opts.PersistSize, "batch-disk", "k", 16384) ctflag.Uint64(&fs, &opts.BatchSize, "batch-req", "q", 512) ctflag.String(&fs, &opts.HTTPAgent, "http-agent", "a", "git.cs.kau.se/rasmoste/ct-sans") ctflag.Duration(&fs, &opts.MetricsInterval, "metrics", "m", 15*time.Second) // Parse command-line options and hardcode additional values if err := ctflag.Parse(fs, os.Args[2:]); err != nil { if err == flag.ErrHelp { fmt.Fprintf(os.Stdout, usage) os.Exit(1) } fmt.Fprintf(os.Stdout, "error: %v\n\n", err) os.Exit(1) } opts.logDirectory = opts.Directory + "/" + "logs" opts.metadataFile = "metadata.json" opts.metadataSignatureFile = "metadata.sig" opts.metadataTimestampFile = "metadata.timestamp" opts.sthFile = "sth.json" opts.stateFile = "th.json" opts.sansFile = "sans.lst" // Hand-over to the respective subcommands var err error switch cmd := os.Args[1]; cmd { case "snapshot": err = snapshot(opts) case "collect": err = collect(opts) case "assemble": err = assemble(opts) default: fmt.Fprintf(os.Stdout, "ct-sans: unknown command %q\n\n", cmd) os.Exit(1) } if err != nil { fmt.Fprintf(os.Stdout, "ct-sans %s: error: %v\n", os.Args[1], err) os.Exit(1) } }