added elapsed timed and ttl colors based on the values
This commit is contained in:
parent
2669371ef4
commit
2f4438c213
Binary file not shown.
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 4.3 MiB |
@ -27,6 +27,7 @@ go install github.com/acidvegas/ptrstream@latest
|
|||||||
| `-c` | `int` | `100` | Concurrency level |
|
| `-c` | `int` | `100` | Concurrency level |
|
||||||
| `-debug` | `bool` | `false` | Show unsuccessful lookups |
|
| `-debug` | `bool` | `false` | Show unsuccessful lookups |
|
||||||
| `-dns` | `string` | | File containing DNS servers |
|
| `-dns` | `string` | | File containing DNS servers |
|
||||||
|
| `-l` | `bool` | `false` | Loop continuously after completion |
|
||||||
| `-o` | `string` | | Path to NDJSON output file |
|
| `-o` | `string` | | Path to NDJSON output file |
|
||||||
| `-r` | `int` | `2` | Number of retries for failed lookups |
|
| `-r` | `int` | `2` | Number of retries for failed lookups |
|
||||||
| `-s` | `int` | `0` | Seed for IP generation *(0 for random)* |
|
| `-s` | `int` | `0` | Seed for IP generation *(0 for random)* |
|
||||||
|
131
ptrstream.go
131
ptrstream.go
@ -33,6 +33,7 @@ type Config struct {
|
|||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
lastDNSUpdate time.Time
|
lastDNSUpdate time.Time
|
||||||
updateMu sync.Mutex
|
updateMu sync.Mutex
|
||||||
|
loop bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
@ -40,6 +41,7 @@ type Stats struct {
|
|||||||
total uint64
|
total uint64
|
||||||
lastProcessed uint64
|
lastProcessed uint64
|
||||||
lastCheckTime time.Time
|
lastCheckTime time.Time
|
||||||
|
startTime time.Time
|
||||||
success uint64
|
success uint64
|
||||||
failed uint64
|
failed uint64
|
||||||
cnames uint64
|
cnames uint64
|
||||||
@ -272,7 +274,7 @@ func colorizeIPInPtr(ptr, ip string) string {
|
|||||||
|
|
||||||
matches := re.FindAllStringIndex(ptr, -1)
|
matches := re.FindAllStringIndex(ptr, -1)
|
||||||
if matches == nil {
|
if matches == nil {
|
||||||
return "[green]" + ptr
|
return "[white]" + ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
var result strings.Builder
|
var result strings.Builder
|
||||||
@ -280,7 +282,7 @@ func colorizeIPInPtr(ptr, ip string) string {
|
|||||||
|
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
if match[0] > lastEnd {
|
if match[0] > lastEnd {
|
||||||
result.WriteString("[green]")
|
result.WriteString("[white]")
|
||||||
result.WriteString(ptr[lastEnd:match[0]])
|
result.WriteString(ptr[lastEnd:match[0]])
|
||||||
}
|
}
|
||||||
result.WriteString("[aqua]")
|
result.WriteString("[aqua]")
|
||||||
@ -289,7 +291,7 @@ func colorizeIPInPtr(ptr, ip string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if lastEnd < len(ptr) {
|
if lastEnd < len(ptr) {
|
||||||
result.WriteString("[green]")
|
result.WriteString("[white]")
|
||||||
result.WriteString(ptr[lastEnd:])
|
result.WriteString(ptr[lastEnd:])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,19 +397,19 @@ func worker(jobs <-chan string, wg *sync.WaitGroup, cfg *Config, stats *Stats, t
|
|||||||
|
|
||||||
var line string
|
var line string
|
||||||
if len(cfg.dnsServers) > 0 {
|
if len(cfg.dnsServers) > 0 {
|
||||||
line = fmt.Sprintf("[gray]%s [gray]│[-] [purple]%15s[-] [gray]│[-] [yellow]%-15s[-] [gray]│[-] %-5s [gray]│[-] [white]%-6d[-] [gray]│[-] %s\n",
|
line = fmt.Sprintf("[gray]%s [gray]│[-] [purple]%15s[-] [gray]│[-] [aqua]%-15s[-] [gray]│[-] %-5s [gray]│[-] %s [gray]│[-] %s\n",
|
||||||
timeStr,
|
timeStr,
|
||||||
ip,
|
ip,
|
||||||
response.Server,
|
response.Server,
|
||||||
recordTypeColor,
|
recordTypeColor,
|
||||||
response.TTL,
|
colorizeTTL(response.TTL),
|
||||||
colorizeIPInPtr(ptr, ip))
|
colorizeIPInPtr(ptr, ip))
|
||||||
} else {
|
} else {
|
||||||
line = fmt.Sprintf("[gray]%s [gray]│[-] [purple]%15s[-] [gray]│[-] %-5s [gray]│[-] [white]%-6d[-] [gray]│[-] %s\n",
|
line = fmt.Sprintf("[gray]%s [gray]│[-] [purple]%15s[-] [gray]│[-] %-5s [gray]│[-] %s [gray]│[-] %s\n",
|
||||||
timeStr,
|
timeStr,
|
||||||
ip,
|
ip,
|
||||||
recordTypeColor,
|
recordTypeColor,
|
||||||
response.TTL,
|
colorizeTTL(response.TTL),
|
||||||
colorizeIPInPtr(ptr, ip))
|
colorizeIPInPtr(ptr, ip))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,6 +495,7 @@ func main() {
|
|||||||
outputPath := flag.String("o", "", "Path to NDJSON output file")
|
outputPath := flag.String("o", "", "Path to NDJSON output file")
|
||||||
seed := flag.Int64("s", 0, "Seed for IP generation (0 for random)")
|
seed := flag.Int64("s", 0, "Seed for IP generation (0 for random)")
|
||||||
shard := flag.String("shard", "", "Shard specification (e.g., 1/4 for first shard of 4)")
|
shard := flag.String("shard", "", "Shard specification (e.g., 1/4 for first shard of 4)")
|
||||||
|
loop := flag.Bool("l", false, "Loop continuously after completion")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
shardNum, totalShards, err := parseShardArg(*shard)
|
shardNum, totalShards, err := parseShardArg(*shard)
|
||||||
@ -524,6 +527,7 @@ func main() {
|
|||||||
debug: *debug,
|
debug: *debug,
|
||||||
dnsServers: servers,
|
dnsServers: servers,
|
||||||
lastDNSUpdate: time.Now(),
|
lastDNSUpdate: time.Now(),
|
||||||
|
loop: *loop,
|
||||||
}
|
}
|
||||||
|
|
||||||
if *outputPath != "" {
|
if *outputPath != "" {
|
||||||
@ -554,11 +558,12 @@ func main() {
|
|||||||
flex := tview.NewFlex().
|
flex := tview.NewFlex().
|
||||||
SetDirection(tview.FlexRow).
|
SetDirection(tview.FlexRow).
|
||||||
AddItem(textView, 0, 1, false).
|
AddItem(textView, 0, 1, false).
|
||||||
AddItem(progress, 3, 0, false)
|
AddItem(progress, 4, 0, false)
|
||||||
|
|
||||||
stats := &Stats{
|
stats := &Stats{
|
||||||
total: 1 << 32,
|
total: 1 << 32,
|
||||||
lastCheckTime: time.Now(),
|
lastCheckTime: time.Now(),
|
||||||
|
startTime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -601,24 +606,24 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
statsText := fmt.Sprintf(" [aqua]Count:[:-] [white]%s [gray]│[-] [aqua]Progress:[:-] [darkgray]%7.2f%%[-] [gray]│[-] [aqua]Rate:[:-] %s [gray]│[-] [aqua]CNAMEs:[:-] [yellow]%s[-] [gray]│[-] [aqua]Successful:[:-] [green]✓%s [-][darkgray](%5.1f%%)[-] [gray]│[-] [aqua]Failed:[:-] [red]✗%s [-][darkgray](%5.1f%%)[-] ",
|
// First line: stats
|
||||||
|
statsLine := fmt.Sprintf(" [aqua]Elapsed:[:-] [white]%s [gray]│[-] [aqua]Count:[:-] [white]%s [gray]│[-] [aqua]Progress:[:-] [darkgray]%7.2f%%[-] [gray]│[-] [aqua]Rate:[:-] %s [gray]│[-] [aqua]CNAMEs:[:-] [yellow]%s [-][darkgray](%5.1f%%)[-] [gray]│[-] [aqua]Successful:[:-] [green]✓%s [-][darkgray](%5.1f%%)[-] [gray]│[-] [aqua]Failed:[:-] [red]✗%s [-][darkgray](%5.1f%%)[-]\n",
|
||||||
|
formatDuration(time.Since(stats.startTime)),
|
||||||
formatNumber(processed),
|
formatNumber(processed),
|
||||||
percent,
|
percent,
|
||||||
colorizeSpeed(avgSpeed),
|
colorizeSpeed(avgSpeed),
|
||||||
formatNumber(atomic.LoadUint64(&stats.cnames)),
|
formatNumber(atomic.LoadUint64(&stats.cnames)),
|
||||||
|
float64(atomic.LoadUint64(&stats.cnames))/float64(processed)*100,
|
||||||
formatNumber(success),
|
formatNumber(success),
|
||||||
float64(success)/float64(processed)*100,
|
float64(success)/float64(processed)*100,
|
||||||
formatNumber(failed),
|
formatNumber(failed),
|
||||||
float64(failed)/float64(processed)*100)
|
float64(failed)/float64(processed)*100)
|
||||||
|
|
||||||
textWidth := visibleLength(statsText)
|
// Second line: progress bar
|
||||||
barWidth := width - textWidth - 2 // -2 for the [] characters
|
barWidth := width - 3 // -3 for the [] and space
|
||||||
|
|
||||||
// Ensure barWidth is at least 1
|
|
||||||
if barWidth < 1 {
|
if barWidth < 1 {
|
||||||
// If there's not enough space, just show the stats without the progress bar
|
|
||||||
progress.Clear()
|
progress.Clear()
|
||||||
fmt.Fprint(progress, statsText)
|
fmt.Fprint(progress, statsLine)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,15 +632,13 @@ func main() {
|
|||||||
filled = barWidth
|
filled = barWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
bar := strings.Builder{}
|
barLine := fmt.Sprintf(" [%s%s]",
|
||||||
bar.WriteString(statsText)
|
strings.Repeat("█", filled),
|
||||||
bar.WriteString("[")
|
strings.Repeat("░", barWidth-filled))
|
||||||
bar.WriteString(strings.Repeat("█", filled))
|
|
||||||
bar.WriteString(strings.Repeat("░", barWidth-filled))
|
|
||||||
bar.WriteString("]")
|
|
||||||
|
|
||||||
|
// Combine both lines with explicit newline
|
||||||
progress.Clear()
|
progress.Clear()
|
||||||
fmt.Fprint(progress, bar.String())
|
fmt.Fprintf(progress, "%s%s", statsLine, barLine)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,13 +646,26 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
jobs := make(chan string, cfg.concurrency)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
stream, err := golcg.IPStream("0.0.0.0/0", shardNum, totalShards, int(*seed), nil)
|
stream, err := golcg.IPStream("0.0.0.0/0", shardNum, totalShards, int(*seed), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error creating IP stream: %v\n", err)
|
fmt.Printf("Error creating IP stream: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs := make(chan string, cfg.concurrency)
|
for ip := range stream {
|
||||||
|
jobs <- ip
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfg.loop {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(jobs)
|
||||||
|
}()
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for i := 0; i < cfg.concurrency; i++ {
|
for i := 0; i < cfg.concurrency; i++ {
|
||||||
@ -657,13 +673,6 @@ func main() {
|
|||||||
go worker(jobs, &wg, cfg, stats, textView, app)
|
go worker(jobs, &wg, cfg, stats, textView, app)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
|
||||||
for ip := range stream {
|
|
||||||
jobs <- ip
|
|
||||||
}
|
|
||||||
close(jobs)
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
app.Stop()
|
app.Stop()
|
||||||
@ -744,3 +753,65 @@ func writeNDJSON(cfg *Config, timestamp time.Time, ip, server, ptr, recordType,
|
|||||||
cfg.mu.Unlock()
|
cfg.mu.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatDuration(d time.Duration) string {
|
||||||
|
d = d.Round(time.Second)
|
||||||
|
|
||||||
|
days := d / (24 * time.Hour)
|
||||||
|
d -= days * 24 * time.Hour
|
||||||
|
|
||||||
|
hours := d / time.Hour
|
||||||
|
d -= hours * time.Hour
|
||||||
|
|
||||||
|
minutes := d / time.Minute
|
||||||
|
d -= minutes * time.Minute
|
||||||
|
|
||||||
|
seconds := d / time.Second
|
||||||
|
|
||||||
|
var result string
|
||||||
|
|
||||||
|
if days > 0 {
|
||||||
|
if hours > 0 && minutes > 0 {
|
||||||
|
result = fmt.Sprintf("%dd %dh %dm", days, hours, minutes)
|
||||||
|
} else if hours > 0 {
|
||||||
|
result = fmt.Sprintf("%dd %dh", days, hours)
|
||||||
|
} else {
|
||||||
|
result = fmt.Sprintf("%dd", days)
|
||||||
|
}
|
||||||
|
} else if hours > 0 {
|
||||||
|
if minutes > 0 {
|
||||||
|
result = fmt.Sprintf("%dh %dm", hours, minutes)
|
||||||
|
} else {
|
||||||
|
result = fmt.Sprintf("%dh", hours)
|
||||||
|
}
|
||||||
|
} else if minutes > 0 {
|
||||||
|
if seconds > 0 {
|
||||||
|
result = fmt.Sprintf("%dm %ds", minutes, seconds)
|
||||||
|
} else {
|
||||||
|
result = fmt.Sprintf("%dm", minutes)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = fmt.Sprintf("%ds", seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad to exactly 14 characters
|
||||||
|
for len(result) < 14 {
|
||||||
|
result = " " + result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorizeTTL(ttl uint32) string {
|
||||||
|
switch {
|
||||||
|
case ttl >= 86400: // 1 day or more
|
||||||
|
return fmt.Sprintf("[#00FF00::b]%-6d[-]", ttl) // Bright green with bold
|
||||||
|
case ttl >= 3600: // 1 hour or more
|
||||||
|
return fmt.Sprintf("[yellow]%-6d[-]", ttl)
|
||||||
|
case ttl >= 300: // 5 minutes or more
|
||||||
|
return fmt.Sprintf("[orange]%-6d[-]", ttl)
|
||||||
|
case ttl >= 60: // 1 minute or more
|
||||||
|
return fmt.Sprintf("[red]%-6d[-]", ttl)
|
||||||
|
default: // Less than 60 seconds
|
||||||
|
return fmt.Sprintf("[gray]%-6d[-]", ttl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user