mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-10-12 01:58:46 +02:00
83 lines
2.3 KiB
Go
83 lines
2.3 KiB
Go
package cli
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"maps"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// parsePFAnchorNames returns the anchor names that the running ruleset refers to,
|
|
// unique and sorted. It takes both pfctl show commands because each one holds half
|
|
// of the list: "pfctl -sr" the scrub and filter anchors, "pfctl -sn" the
|
|
// translation anchors.
|
|
func parsePFAnchorNames(rules, nat io.Reader) []string {
|
|
unique := make(map[string]struct{})
|
|
// A read that stopped early gives a shorter list, and a shorter list reads
|
|
// as an anchor that went away.
|
|
if !collectPFAnchorNames(rules, unique) || !collectPFAnchorNames(nat, unique) {
|
|
return nil
|
|
}
|
|
return slices.Sorted(maps.Keys(unique))
|
|
}
|
|
|
|
// collectPFAnchorNames reports whether it read the whole output.
|
|
func collectPFAnchorNames(r io.Reader, into map[string]struct{}) bool {
|
|
if r == nil {
|
|
return true
|
|
}
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
name := pfAnchorNameOf(scanner.Text())
|
|
if name == "" {
|
|
continue
|
|
}
|
|
into[name] = struct{}{}
|
|
}
|
|
return scanner.Err() == nil
|
|
}
|
|
|
|
// pfAnchorNameOf returns the anchor name of one pfctl rule line. The rule verb is
|
|
// anchor, nat-anchor, rdr-anchor, scrub-anchor or dummynet-anchor, and the quoted
|
|
// name follows it. Every other line, including the merged ALTQ warnings, gives "".
|
|
func pfAnchorNameOf(line string) string {
|
|
verb, rest, ok := strings.Cut(strings.TrimSpace(line), " ")
|
|
if !ok || !strings.HasSuffix(verb, "anchor") {
|
|
return ""
|
|
}
|
|
_, quoted, ok := strings.Cut(rest, `"`)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
name, _, ok := strings.Cut(quoted, `"`)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return name
|
|
}
|
|
|
|
// parsePFStatus reads "pfctl -si" output and reports whether pf runs, with the
|
|
// uptime text that pfctl prints beside it.
|
|
func parsePFStatus(r io.Reader) (enabled bool, since string) {
|
|
if r == nil {
|
|
return false, ""
|
|
}
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
status, ok := strings.CutPrefix(strings.TrimSpace(scanner.Text()), "Status:")
|
|
if !ok {
|
|
continue
|
|
}
|
|
// pfctl pads the status into a column and prints the debug level next to it.
|
|
status, _, _ = strings.Cut(status, "Debug:")
|
|
state, uptime, _ := strings.Cut(strings.TrimSpace(status), " for ")
|
|
if state != "Enabled" {
|
|
return false, ""
|
|
}
|
|
return true, strings.TrimSpace(uptime)
|
|
}
|
|
// A read that stopped early may have stopped before the status line.
|
|
return false, ""
|
|
}
|