goipgrep: refactor into module; pure-Go ping/resolve; cache+CI; drop binary

This commit is contained in:
tobias
2026-02-17 09:26:30 +01:00
parent 27760b0bf1
commit a931be4707
20 changed files with 1214 additions and 376 deletions

View File

@@ -0,0 +1,79 @@
package extract
import (
"bufio"
"context"
"errors"
"io"
"regexp"
)
type Mode int
const (
ModeIP Mode = iota
ModeMAC
)
type Options struct {
Mode Mode
IPv6 bool
}
// Grep scans r line-by-line and emits raw matches via emit.
// It avoids reading the full input into memory.
func Grep(ctx context.Context, r io.Reader, opts Options, emit func(string)) error {
if emit == nil {
return errors.New("emit func is nil")
}
var re *regexp.Regexp
switch opts.Mode {
case ModeMAC:
re = macRE
case ModeIP:
if opts.IPv6 {
re = ipAnyRE
} else {
re = ipv4RE
}
default:
return errors.New("unknown mode")
}
sc := bufio.NewScanner(r)
// Large-ish buffer for long lines (logs, JSON blobs, etc.).
buf := make([]byte, 64*1024)
sc.Buffer(buf, 1024*1024)
for sc.Scan() {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
line := sc.Bytes()
matches := re.FindAll(line, -1)
for _, m := range matches {
emit(string(m))
}
}
if err := sc.Err(); err != nil {
return err
}
return nil
}
// Note: These regexes intentionally find candidates only; callers should
// validate/normalize as needed.
var (
// \b based candidate matcher, validated later.
ipv4RE = regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`)
// Rough IPv6 candidate matcher (validation happens later if caller chooses to).
ipv6RE = regexp.MustCompile(`(?i)\b(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}\b`)
ipAnyRE = regexp.MustCompile(`(?i)(?:` + `\b(?:\d{1,3}\.){3}\d{1,3}\b` + `|` + `\b(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}\b` + `)`)
macRE = regexp.MustCompile(`(?i)\b(?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}\b|\b(?:[0-9a-f]{4}\.){2}[0-9a-f]{4}\b`)
)

View File

@@ -0,0 +1,36 @@
package extract
import (
"context"
"strings"
"testing"
)
func TestGrepIPv4(t *testing.T) {
in := "a 1.2.3.4 b 999.2.3.4 c\nx 10.0.0.1 y"
var got []string
err := Grep(context.Background(), strings.NewReader(in), Options{Mode: ModeIP}, func(s string) {
got = append(got, s)
})
if err != nil {
t.Fatalf("Grep: %v", err)
}
// Candidate matcher intentionally returns 999.2.3.4 too; validation happens elsewhere.
if len(got) != 3 {
t.Fatalf("got %d matches: %#v", len(got), got)
}
}
func TestGrepMAC(t *testing.T) {
in := "aa:bb:cc:dd:ee:ff aabb.ccdd.eeff 00-11-22-33-44-55"
var got []string
err := Grep(context.Background(), strings.NewReader(in), Options{Mode: ModeMAC}, func(s string) {
got = append(got, s)
})
if err != nil {
t.Fatalf("Grep: %v", err)
}
if len(got) != 3 {
t.Fatalf("got %d matches: %#v", len(got), got)
}
}