chore: cleanup — untrack binaries, consolidate Go dirs, dedupe tools
- Untrack and delete compiled binaries (tarsum, gosoft.exe, rust uniq/uniq2);
ignore build outputs (dist/, bin/, *.exe, *.test, .ruff_cache/)
- Merge tools/go/ and projects/go-tools/go/ into projects/go-tools/<name>/
- Fix goipgrep .gitignore: bare 'ipgrep' pattern was ignoring cmd/ipgrep/,
so the main entrypoint was never tracked; now anchored to /ipgrep
- Archive duplicate implementations to archive/experimental/{rust,go}/
(uniq, between, tarsum rewrites); canonical versions stay in tools/
- Update README tool catalog to match new layout
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+11
@@ -6,3 +6,14 @@ Icon?
|
||||
._*
|
||||
__MACOSX/
|
||||
__pycache__/
|
||||
|
||||
# build outputs
|
||||
dist/
|
||||
bin/
|
||||
*.exe
|
||||
*.test
|
||||
.ruff_cache/
|
||||
|
||||
# compiled binaries that live next to their source
|
||||
projects/go-tools/gosoft/gosoft
|
||||
archive/experimental/go/tarsum/tarsum
|
||||
|
||||
@@ -87,9 +87,9 @@ Format: `path | goal | usage`. This section is intentionally compact so `what` c
|
||||
- `tools/data/vba_chr_decode.py` | goal: decode VBA `Chr(...)` obfuscation patterns | usage: `python3 tools/data/vba_chr_decode.py macro.txt`
|
||||
- `tools/data/concat.py` | goal: concatenate structured text inputs in a repeatable way | usage: `python3 tools/data/concat.py file1 file2`
|
||||
- `tools/data/split_linewise.py` | goal: split text into line-based chunks | usage: `python3 tools/data/split_linewise.py input.txt`
|
||||
- `tools/data/uniq.py` | goal: remove duplicate lines while preserving first occurrence order | usage: `python3 tools/data/uniq.py file.txt`
|
||||
- `tools/data/uniq.py` | goal: remove duplicate lines while preserving first occurrence order (canonical; Rust/Go versions archived) | usage: `python3 tools/data/uniq.py file.txt`
|
||||
- `tools/data/urldecode.py` | goal: URL-decode strings from stdin or files | usage: `python3 tools/data/urldecode.py`
|
||||
- `tools/data/between` | goal: print text between delimiters | usage: `tools/data/between START END < file.txt`
|
||||
- `tools/data/between` | goal: print text between delimiters (canonical; Rust/Go versions archived) | usage: `tools/data/between START END < file.txt`
|
||||
- `tools/data/csv_get` | goal: extract selected CSV fields quickly | usage: `tools/data/csv_get file.csv column`
|
||||
- `tools/data/csv2dot` | goal: turn CSV relationships into Graphviz dot edges | usage: `tools/data/csv2dot`
|
||||
- `config/visidata/plugins/ioc.py` | goal: VisiData plugin for IOC types (domains, URLs, hashes) with VT/MB integration | usage: `vd --plugin config/visidata/plugins/ioc.py ...`
|
||||
@@ -103,13 +103,13 @@ Format: `path | goal | usage`. This section is intentionally compact so `what` c
|
||||
- `tools/hashing/libarchivesum.py` | goal: hash files inside archives without full extraction | usage: `python3 tools/hashing/libarchivesum.py archive.zip`
|
||||
- `tools/hashing/scatterhash.py` | goal: hash very large files by sparse sampling when you need a fingerprint rather than a comparison | usage: `python3 tools/hashing/scatterhash.py huge.img`
|
||||
- `tools/hashing/hashzip.py` | goal: hash ZIP contents or metadata for comparison | usage: `python3 tools/hashing/hashzip.py sample.zip`
|
||||
- `tools/hashing/tarsum.py` | goal: compute tar-oriented checksums in Python | usage: `python3 tools/hashing/tarsum.py archive.tar`
|
||||
- `tools/hashing/tarsum.py` | goal: compute per-file checksums inside a tar archive (canonical; Go version archived) | usage: `python3 tools/hashing/tarsum.py archive.tar`
|
||||
- `tools/hashing/sparsecmp.sh` | goal: compare very large files or block devices by sampling chunks at fixed offsets | usage: `tools/hashing/sparsecmp.sh source target 100`
|
||||
- `tools/hashing/trunc_by_hash.py` | goal: find the byte length where a rolling hash matches a target digest | usage: `python3 tools/hashing/trunc_by_hash.py HASH file.bin`
|
||||
|
||||
### Network And Cloud
|
||||
|
||||
- `tools/network/ipgrep` | goal: extract IP or MAC indicators from text | usage: `cat file.txt | tools/network/ipgrep`
|
||||
- `tools/network/ipgrep` | goal: extract IP or MAC indicators from text (quick script; `projects/go-tools/goipgrep/` is the full-featured version) | usage: `cat file.txt | tools/network/ipgrep`
|
||||
- `tools/network/fritzshark.sh` | goal: inspect or capture FritzBox traffic workflows | usage: `tools/network/fritzshark.sh`
|
||||
- `tools/network/fritzshark2.sh` | goal: alternate FritzBox traffic workflow | usage: `tools/network/fritzshark2.sh`
|
||||
- `tools/network/get_ntp.py` | goal: query NTP information from remote systems | usage: `python3 tools/network/get_ntp.py host`
|
||||
@@ -172,18 +172,12 @@ Format: `path | goal | usage`. This section is intentionally compact so `what` c
|
||||
|
||||
### Go Tools And Small Projects
|
||||
|
||||
- `tools/go/bincmp/gobincmp.go` | goal: compare files or directories with fuzzy hashing | usage: `go run tools/go/bincmp/gobincmp.go left right`
|
||||
- `tools/go/gopname/pname.go` | goal: demo process-title renaming with `gspt` | usage: `go run tools/go/gopname/pname.go`
|
||||
- `tools/go/tarsum/tarsum.go` | goal: print a SHA-256 checksum for a tar file | usage: `go run tools/go/tarsum/tarsum.go archive.tar`
|
||||
- `projects/go-tools/go/goipgrep/` | goal: production-grade IP and MAC extractor with ping, DNS, and lookup support | usage: `projects/go-tools/go/goipgrep/scripts/build.sh`
|
||||
- `projects/go-tools/go/csv2json/csv2json.go` | goal: convert CSV input to JSON | usage: `go run projects/go-tools/go/csv2json/csv2json.go`
|
||||
- `projects/go-tools/go/gobetween/gobetween.go` | goal: extract text between delimiters in Go | usage: `go run projects/go-tools/go/gobetween/gobetween.go`
|
||||
- `projects/go-tools/go/goinfo/goinfo.go` | goal: inspect file or system information in Go | usage: `go run projects/go-tools/go/goinfo/goinfo.go`
|
||||
- `projects/go-tools/go/gosoft/gosoft.go` | goal: enumerate installed software from multiple package sources | usage: `go run projects/go-tools/go/gosoft/gosoft.go`
|
||||
- `projects/go-tools/go/gouniq/gouniq.go` | goal: remove duplicate lines in Go | usage: `go run projects/go-tools/go/gouniq/gouniq.go < file.txt`
|
||||
- `projects/rust-tools/between.rs` | goal: Rust version of between-delimiter extraction | usage: `rustc projects/rust-tools/between.rs && ./between`
|
||||
- `projects/rust-tools/uniq.rs` | goal: Rust uniq implementation preserving first occurrences | usage: `rustc projects/rust-tools/uniq.rs && ./uniq file.txt`
|
||||
- `projects/rust-tools/uniq2.rs` | goal: alternate Rust uniq implementation | usage: `rustc projects/rust-tools/uniq2.rs && ./uniq2 file.txt`
|
||||
- `projects/go-tools/goipgrep/` | goal: production-grade IP and MAC extractor with ping, DNS, and lookup support | usage: `projects/go-tools/goipgrep/scripts/build.sh`
|
||||
- `projects/go-tools/bincmp/gobincmp.go` | goal: compare files or directories with fuzzy hashing | usage: `go run projects/go-tools/bincmp/gobincmp.go left right`
|
||||
- `projects/go-tools/gopname/pname.go` | goal: demo process-title renaming with `gspt` | usage: `go run projects/go-tools/gopname/pname.go`
|
||||
- `projects/go-tools/csv2json/csv2json.go` | goal: convert CSV input to JSON | usage: `go run projects/go-tools/csv2json/csv2json.go`
|
||||
- `projects/go-tools/goinfo/goinfo.go` | goal: inspect file or system information in Go | usage: `go run projects/go-tools/goinfo/goinfo.go`
|
||||
- `projects/go-tools/gosoft/gosoft.go` | goal: enumerate installed software from multiple package sources | usage: `go run projects/go-tools/gosoft/gosoft.go`
|
||||
- `projects/puzzlebox/` | goal: solve voxel and puzzlebox search problems with several solver variants | usage: `python3 projects/puzzlebox/solve.py`
|
||||
- `projects/timesketch/deploy_timesketch.sh` | goal: deploy a Timesketch environment | usage: `projects/timesketch/deploy_timesketch.sh`
|
||||
|
||||
@@ -230,6 +224,8 @@ Format: `path | goal | usage`. This section is intentionally compact so `what` c
|
||||
- `archive/experimental/lpic.sh` | goal: keep an old system experiment available for salvage | usage: `archive/experimental/lpic.sh`
|
||||
- `archive/experimental/matplottest.py` | goal: keep an old plotting experiment available for salvage | usage: `python3 archive/experimental/matplottest.py`
|
||||
- `archive/experimental/rootshell.c` | goal: keep a dangerous historical C example archived rather than active | usage: `do not run; reference only`
|
||||
- `archive/experimental/rust/` | goal: keep Rust rewrites of `uniq` and `between` (superseded by `tools/data/` versions) | usage: `rustc archive/experimental/rust/uniq.rs`
|
||||
- `archive/experimental/go/` | goal: keep Go rewrites of `uniq`, `between`, and `tarsum` (superseded by `tools/data/` and `tools/hashing/` versions) | usage: `go run archive/experimental/go/gouniq/gouniq.go`
|
||||
|
||||
## `tools/`: Standalone Utilities
|
||||
|
||||
@@ -274,7 +270,7 @@ Format: `path | goal | usage`. This section is intentionally compact so `what` c
|
||||
|
||||
### Networking / Cloud
|
||||
|
||||
- `tools/network/ipgrep`: network indicator extractor for IPs and MACs; there is a fuller Go project version under `projects/go-tools/go/goipgrep/`.
|
||||
- `tools/network/ipgrep`: network indicator extractor for IPs and MACs; there is a fuller Go project version under `projects/go-tools/goipgrep/`.
|
||||
- `tools/network/fritzshark.sh`, `fritzshark2.sh`: FritzBox traffic / packet capture helpers.
|
||||
- `tools/network/get_ntp.py`, `get_stp.sh`: NTP and STP inspection scripts.
|
||||
- `tools/cloud/cloudsend.py`, `cloudsend.sh`: Nextcloud/OwnCloud share upload helpers.
|
||||
@@ -295,18 +291,15 @@ Format: `path | goal | usage`. This section is intentionally compact so `what` c
|
||||
- `tools/ctf/`: small CTF-solving utilities such as text filtering, JS extraction, guessing, search, transpose, and flag submission helpers.
|
||||
- `tools/text/probability.py`, `tools/text/depth`: text-analysis experiments.
|
||||
|
||||
### Go Tools Under `tools/go/`
|
||||
|
||||
- `tools/go/bincmp/`: fuzzy binary and directory comparison using `ssdeep`.
|
||||
- `tools/go/gopname/`: process-title renaming demo using `gspt`.
|
||||
- `tools/go/tarsum/`: Go tar checksum utility; currently behaves as a raw SHA-256 of the tar file contents.
|
||||
|
||||
## `projects/`: Larger Multi-File Work
|
||||
|
||||
- `projects/go-tools/go/goipgrep/`: the most complete standalone project in the repo. It extracts IPs and MACs from text and can optionally do ping checks, reverse DNS, and IP info lookups. Read its local README first.
|
||||
- `projects/go-tools/go/csv2json/`, `gobetween/`, `goinfo/`, `gosoft/`, `gouniq/`: smaller Go experiments and utilities.
|
||||
All Go code lives under `projects/go-tools/` (one directory per tool).
|
||||
|
||||
- `projects/go-tools/goipgrep/`: the most complete standalone project in the repo. It extracts IPs and MACs from text and can optionally do ping checks, reverse DNS, and IP info lookups. Read its local README first.
|
||||
- `projects/go-tools/bincmp/`: fuzzy binary and directory comparison using `ssdeep`.
|
||||
- `projects/go-tools/gopname/`: process-title renaming demo using `gspt`.
|
||||
- `projects/go-tools/csv2json/`, `goinfo/`, `gosoft/`: smaller Go experiments and utilities.
|
||||
- `projects/puzzlebox/`: puzzle-solving and voxel-based search experiments with several solver variants.
|
||||
- `projects/rust-tools/`: small Rust utilities and built binaries.
|
||||
- `projects/timesketch/deploy_timesketch.sh`: deployment helper for Timesketch.
|
||||
|
||||
## `config/`: Shell, Desktop, and VisiData
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dist/
|
||||
ipgrep
|
||||
/ipgrep
|
||||
*.test
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.ktf.ninja/tabledevil/gists/projects/go-tools/go/goipgrep/internal/cache"
|
||||
"git.ktf.ninja/tabledevil/gists/projects/go-tools/go/goipgrep/internal/extract"
|
||||
"git.ktf.ninja/tabledevil/gists/projects/go-tools/go/goipgrep/internal/ipinfo"
|
||||
"git.ktf.ninja/tabledevil/gists/projects/go-tools/go/goipgrep/internal/normalize"
|
||||
"git.ktf.ninja/tabledevil/gists/projects/go-tools/go/goipgrep/internal/ping"
|
||||
"git.ktf.ninja/tabledevil/gists/projects/go-tools/go/goipgrep/internal/resolve"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "unknown"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
type stringSlice []string
|
||||
|
||||
func (s *stringSlice) String() string { return strings.Join(*s, ",") }
|
||||
func (s *stringSlice) Set(v string) error {
|
||||
*s = append(*s, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
// legacy short flags (kept for compatibility)
|
||||
shortUniq bool
|
||||
shortSort bool
|
||||
shortMAC bool
|
||||
shortPing bool
|
||||
shortResolve bool
|
||||
shortLookup bool
|
||||
shortFile string
|
||||
|
||||
// long flags
|
||||
longUniq bool
|
||||
longSort bool
|
||||
longMAC bool
|
||||
longPing bool
|
||||
longResolve bool
|
||||
longLookup bool
|
||||
longFiles stringSlice
|
||||
|
||||
format string
|
||||
jobs int
|
||||
timeout time.Duration
|
||||
pingMode string
|
||||
ipv6 bool
|
||||
cachePath string
|
||||
noCache bool
|
||||
clearCache bool
|
||||
cacheTTL time.Duration
|
||||
cacheMax int
|
||||
ipinfoToken string
|
||||
showVersion bool
|
||||
)
|
||||
|
||||
flag.BoolVar(&shortUniq, "u", false, "only show uniq IPs/MACs (implies -s)")
|
||||
flag.BoolVar(&shortSort, "s", false, "sort output")
|
||||
flag.BoolVar(&shortMAC, "m", false, "grep MAC addresses instead of IP addresses")
|
||||
flag.BoolVar(&shortPing, "p", false, "only show entries considered reachable (see --ping-mode)")
|
||||
flag.BoolVar(&shortResolve, "r", false, "resolve: IP reverse DNS; MAC best-effort via neighbor/ARP table (Linux only)")
|
||||
flag.BoolVar(&shortLookup, "l", false, "lookup ip info using ipinfo.io (IP only)")
|
||||
flag.StringVar(&shortFile, "f", "", "input file (legacy; prefer --file)")
|
||||
|
||||
flag.BoolVar(&longUniq, "uniq", false, "only show uniq IPs/MACs (implies --sort)")
|
||||
flag.BoolVar(&longSort, "sort", false, "sort output")
|
||||
flag.BoolVar(&longMAC, "mac", false, "grep MAC addresses instead of IP addresses")
|
||||
flag.BoolVar(&longPing, "pingable", false, "only show entries considered reachable (see --ping-mode)")
|
||||
flag.BoolVar(&longResolve, "resolve", false, "resolve: IP reverse DNS; MAC best-effort via neighbor/ARP table (Linux only)")
|
||||
flag.BoolVar(&longLookup, "lookup", false, "lookup ip info using ipinfo.io (IP only)")
|
||||
flag.Var(&longFiles, "file", "input file (repeatable)")
|
||||
|
||||
flag.StringVar(&format, "format", "text", "output format: text|csv|json")
|
||||
flag.IntVar(&jobs, "jobs", runtime.GOMAXPROCS(0), "max concurrent workers for network operations")
|
||||
flag.DurationVar(&timeout, "timeout", 2*time.Second, "per-operation timeout for -p/-r/-l")
|
||||
flag.StringVar(&pingMode, "ping-mode", "auto", "reachability mode for -p: auto|icmp|tcp")
|
||||
flag.BoolVar(&ipv6, "ipv6", false, "also match IPv6 addresses (default: IPv4 only)")
|
||||
|
||||
flag.StringVar(&cachePath, "cache-path", "", "cache path for ipinfo lookups (default: OS cache dir)")
|
||||
flag.BoolVar(&noCache, "no-cache", false, "disable reading/writing the ipinfo cache")
|
||||
flag.BoolVar(&clearCache, "clear-cache", false, "clear the ipinfo cache then exit")
|
||||
flag.DurationVar(&cacheTTL, "cache-ttl", 30*24*time.Hour, "ipinfo cache TTL (0 disables expiry)")
|
||||
flag.IntVar(&cacheMax, "cache-max-entries", 50000, "max cached IPs for ipinfo lookups (0 disables pruning)")
|
||||
flag.StringVar(&ipinfoToken, "ipinfo-token", "", "ipinfo.io token (also read from IPINFO_TOKEN)")
|
||||
|
||||
flag.BoolVar(&showVersion, "version", false, "print version and exit")
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [flags] [file...]\n\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
if showVersion {
|
||||
fmt.Printf("ipgrep %s (%s) %s\n", version, commit, date)
|
||||
return
|
||||
}
|
||||
|
||||
uniqFlag := shortUniq || longUniq
|
||||
sortFlag := shortSort || longSort
|
||||
macFlag := shortMAC || longMAC
|
||||
pingable := shortPing || longPing
|
||||
resolveFlag := shortResolve || longResolve
|
||||
lookupFlag := shortLookup || longLookup
|
||||
|
||||
// keep old behavior: pingable/lookup implies uniq+sort
|
||||
if pingable || lookupFlag {
|
||||
sortFlag = true
|
||||
uniqFlag = true
|
||||
}
|
||||
if uniqFlag {
|
||||
sortFlag = true
|
||||
}
|
||||
|
||||
if lookupFlag && macFlag {
|
||||
fatalf("lookup mode (-l/--lookup) only works for IP addresses, not MAC addresses")
|
||||
}
|
||||
|
||||
format = strings.ToLower(strings.TrimSpace(format))
|
||||
switch format {
|
||||
case "text", "csv", "json":
|
||||
default:
|
||||
fatalf("invalid --format %q (want text|csv|json)", format)
|
||||
}
|
||||
pingMode = strings.ToLower(strings.TrimSpace(pingMode))
|
||||
switch pingMode {
|
||||
case "auto", "icmp", "tcp":
|
||||
default:
|
||||
fatalf("invalid --ping-mode %q (want auto|icmp|tcp)", pingMode)
|
||||
}
|
||||
if jobs < 1 {
|
||||
fatalf("--jobs must be >= 1")
|
||||
}
|
||||
if timeout <= 0 {
|
||||
fatalf("--timeout must be > 0")
|
||||
}
|
||||
|
||||
var files []string
|
||||
if shortFile != "" {
|
||||
files = append(files, shortFile)
|
||||
}
|
||||
files = append(files, longFiles...)
|
||||
files = append(files, flag.Args()...)
|
||||
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
// cache management for lookup mode
|
||||
if lookupFlag && ipinfoToken == "" {
|
||||
ipinfoToken = os.Getenv("IPINFO_TOKEN")
|
||||
}
|
||||
|
||||
// Resolve cache default path.
|
||||
if cachePath == "" {
|
||||
cachePath = cache.DefaultPath()
|
||||
}
|
||||
|
||||
if clearCache {
|
||||
if err := cache.Clear(cachePath); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
fatalf("clear cache: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
mode := extract.ModeIP
|
||||
if macFlag {
|
||||
mode = extract.ModeMAC
|
||||
}
|
||||
|
||||
// Extraction stage (stream input, collect matches as needed).
|
||||
var matches []string
|
||||
seen := map[string]struct{}{}
|
||||
emit := func(s string) {
|
||||
if uniqFlag {
|
||||
if _, ok := seen[s]; ok {
|
||||
return
|
||||
}
|
||||
seen[s] = struct{}{}
|
||||
}
|
||||
matches = append(matches, s)
|
||||
}
|
||||
|
||||
exOpts := extract.Options{Mode: mode, IPv6: ipv6}
|
||||
|
||||
if len(files) == 0 {
|
||||
if err := extract.Grep(ctx, os.Stdin, exOpts, func(raw string) {
|
||||
if mode == extract.ModeMAC {
|
||||
if norm, ok := normalize.NormalizeMAC(raw); ok {
|
||||
emit(norm)
|
||||
}
|
||||
return
|
||||
}
|
||||
if norm, ok := normalize.NormalizeIP(raw, ipv6); ok {
|
||||
emit(norm)
|
||||
}
|
||||
}); err != nil {
|
||||
fatalf("read stdin: %v", err)
|
||||
}
|
||||
} else {
|
||||
for _, f := range files {
|
||||
r, err := os.Open(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ipgrep: open %s: %v\n", f, err)
|
||||
continue
|
||||
}
|
||||
err = extract.Grep(ctx, r, exOpts, func(raw string) {
|
||||
if mode == extract.ModeMAC {
|
||||
if norm, ok := normalize.NormalizeMAC(raw); ok {
|
||||
emit(norm)
|
||||
}
|
||||
return
|
||||
}
|
||||
if norm, ok := normalize.NormalizeIP(raw, ipv6); ok {
|
||||
emit(norm)
|
||||
}
|
||||
})
|
||||
_ = r.Close()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ipgrep: read %s: %v\n", f, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if sortFlag {
|
||||
sort.Strings(matches)
|
||||
}
|
||||
|
||||
// -p reachability filter
|
||||
if pingable {
|
||||
popts := ping.Options{
|
||||
Mode: pingMode,
|
||||
Timeout: timeout,
|
||||
Jobs: jobs,
|
||||
}
|
||||
if macFlag {
|
||||
var err error
|
||||
matches, err = ping.FilterMACs(ctx, matches, popts)
|
||||
if err != nil {
|
||||
fatalf("%v", err)
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
matches, err = ping.FilterIPs(ctx, matches, popts)
|
||||
if err != nil {
|
||||
fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
if sortFlag {
|
||||
sort.Strings(matches)
|
||||
}
|
||||
}
|
||||
|
||||
// -l ipinfo lookup
|
||||
if lookupFlag {
|
||||
if format == "csv" {
|
||||
w := csv.NewWriter(os.Stdout)
|
||||
must(w.Write([]string{"ip", "country", "region", "city", "org", "hostname"}))
|
||||
client := ipinfo.Client{
|
||||
Token: ipinfoToken,
|
||||
Timeout: timeout,
|
||||
}
|
||||
var store *cache.Store
|
||||
if !noCache {
|
||||
s, err := cache.Load(cachePath, cacheTTL, cacheMax)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ipgrep: cache load: %v\n", err)
|
||||
} else {
|
||||
store = s
|
||||
}
|
||||
}
|
||||
results := ipinfo.LookupAll(ctx, matches, client, store, jobs)
|
||||
sort.Slice(results, func(i, j int) bool { return results[i].IP < results[j].IP })
|
||||
for _, info := range results {
|
||||
must(w.Write([]string{info.IP, info.Country, info.Region, info.City, info.Org, info.Hostname}))
|
||||
}
|
||||
w.Flush()
|
||||
if err := w.Error(); err != nil {
|
||||
fatalf("csv: %v", err)
|
||||
}
|
||||
if store != nil && store.Changed() {
|
||||
if err := store.Save(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ipgrep: cache save: %v\n", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
client := ipinfo.Client{
|
||||
Token: ipinfoToken,
|
||||
Timeout: timeout,
|
||||
}
|
||||
var store *cache.Store
|
||||
if !noCache {
|
||||
s, err := cache.Load(cachePath, cacheTTL, cacheMax)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ipgrep: cache load: %v\n", err)
|
||||
} else {
|
||||
store = s
|
||||
}
|
||||
}
|
||||
results := ipinfo.LookupAll(ctx, matches, client, store, jobs)
|
||||
sort.Slice(results, func(i, j int) bool { return results[i].IP < results[j].IP })
|
||||
|
||||
if format == "json" {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
must(enc.Encode(results))
|
||||
} else {
|
||||
for _, info := range results {
|
||||
fmt.Printf("%s\t%s\t%s\t%s\t%s\t%s\n", info.IP, info.Country, info.Region, info.City, info.Org, info.Hostname)
|
||||
}
|
||||
}
|
||||
if store != nil && store.Changed() {
|
||||
if err := store.Save(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ipgrep: cache save: %v\n", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -r resolution
|
||||
if resolveFlag {
|
||||
if macFlag {
|
||||
res, err := resolve.ResolveMACs(matches)
|
||||
if err != nil {
|
||||
fatalf("%v", err)
|
||||
}
|
||||
if format == "json" {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
must(enc.Encode(res))
|
||||
return
|
||||
}
|
||||
for _, row := range res {
|
||||
// one line per mapping
|
||||
for _, ip := range row.IPs {
|
||||
fmt.Printf("%s\t%s\n", row.MAC, ip)
|
||||
}
|
||||
if len(row.IPs) == 0 {
|
||||
fmt.Println(row.MAC)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
res := resolve.ReverseLookupAll(ctx, matches, timeout, jobs)
|
||||
if format == "json" {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
must(enc.Encode(res))
|
||||
return
|
||||
}
|
||||
for _, row := range res {
|
||||
if row.Hostname != "" {
|
||||
fmt.Printf("%s\t%s\n", row.IP, row.Hostname)
|
||||
} else {
|
||||
fmt.Println(row.IP)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// plain output
|
||||
if format == "json" {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
must(enc.Encode(matches))
|
||||
return
|
||||
}
|
||||
if format == "csv" {
|
||||
// csv doesn't make much sense for plain extraction, but keep it consistent.
|
||||
w := csv.NewWriter(os.Stdout)
|
||||
must(w.Write([]string{"value"}))
|
||||
for _, m := range matches {
|
||||
must(w.Write([]string{m}))
|
||||
}
|
||||
w.Flush()
|
||||
if err := w.Error(); err != nil {
|
||||
fatalf("csv: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, m := range matches {
|
||||
fmt.Println(m)
|
||||
}
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func fatalf(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, "ipgrep: "+format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
Reference in New Issue
Block a user