(g *globalFlags)
| 123 | } |
| 124 | |
| 125 | func newScanIPCmd(g *globalFlags) *cobra.Command { |
| 126 | var ( |
| 127 | port uint16 |
| 128 | conc int |
| 129 | timeout time.Duration |
| 130 | topN int |
| 131 | jsonOut bool |
| 132 | ) |
| 133 | cmd := &cobra.Command{ |
| 134 | Use: "ip", |
| 135 | Short: "Probe Cloudflare IPs for reachability and latency", |
| 136 | RunE: func(cmd *cobra.Command, args []string) error { |
| 137 | ctx, cancel := installSignalCancel(cmd.Context()) |
| 138 | defer cancel() |
| 139 | |
| 140 | ips := scanner.DefaultCloudflareIPs |
| 141 | fmt.Fprintf(cmd.OutOrStdout(), |
| 142 | "snix scan ip: probing %d Cloudflare IPs on port %d (conc=%d, timeout=%s)\n", |
| 143 | len(ips), port, conc, timeout) |
| 144 | |
| 145 | var done atomic.Int32 |
| 146 | total := len(ips) |
| 147 | start := time.Now() |
| 148 | onResult := func(r scanner.IPResult) { |
| 149 | n := done.Add(1) |
| 150 | mark := "✓" |
| 151 | if !r.Reachable { |
| 152 | mark = "✗" |
| 153 | } |
| 154 | if !jsonOut { |
| 155 | fmt.Fprintf(cmd.ErrOrStderr(), |
| 156 | "\r[%3d/%3d] %s %-18s rtt=%s", |
| 157 | n, total, mark, r.IP, r.RTT.Round(time.Millisecond)) |
| 158 | } |
| 159 | } |
| 160 | results := scanner.ProbeIPs(ctx, ips, port, timeout, conc, onResult) |
| 161 | if !jsonOut { |
| 162 | fmt.Fprintf(cmd.ErrOrStderr(), "\r%*s\r", 80, "") |
| 163 | fmt.Fprintf(cmd.OutOrStdout(), "done in %s\n\n", time.Since(start).Round(time.Millisecond)) |
| 164 | } |
| 165 | |
| 166 | ranked := scanner.RankIPs(results) |
| 167 | if topN > 0 && topN < len(ranked) { |
| 168 | ranked = ranked[:topN] |
| 169 | } |
| 170 | if jsonOut { |
| 171 | return json.NewEncoder(cmd.OutOrStdout()).Encode(results) |
| 172 | } |
| 173 | return writeIPTable(cmd.OutOrStdout(), results, ranked) |
| 174 | }, |
| 175 | } |
| 176 | cmd.Flags().Uint16VarP(&port, "port", "p", 443, "port to probe") |
| 177 | cmd.Flags().IntVarP(&conc, "concurrency", "j", 16, "max concurrent probes") |
| 178 | cmd.Flags().DurationVar(&timeout, "timeout", 2*time.Second, "per-probe timeout") |
| 179 | cmd.Flags().IntVarP(&topN, "top", "n", 10, "show only top-N by latency") |
| 180 | cmd.Flags().BoolVar(&jsonOut, "json", false, "emit JSON instead of a table") |
| 181 | return cmd |
| 182 | } |
no test coverage detected