From 1a39f54858c7d018d9a41e6e35e841ad42578535 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 11:49:47 +0200 Subject: [PATCH] ipadm: translate to English, fix help alignment - All ipadm-internal messages (usage, prompts, errors) are now English - Usage text is built programmatically from a (left, desc) row list with computed column width instead of hand-aligned spaces, so it can't drift out of alignment again when rows are added/changed - README's literal usage block updated to match; rest of the repo docs stay German --- tools/ipadm/README.md | 12 +-- tools/ipadm/dnsmasq.go | 8 +- tools/ipadm/host.go | 2 +- tools/ipadm/interactive.go | 18 ++--- tools/ipadm/main.go | 148 +++++++++++++++++++++++-------------- tools/ipadm/nftpf.go | 12 +-- tools/ipadm/portfwd.go | 16 ++-- tools/ipadm/store.go | 6 +- tools/ipadm/validate.go | 14 ++-- 9 files changed, 137 insertions(+), 99 deletions(-) diff --git a/tools/ipadm/README.md b/tools/ipadm/README.md index e6d7ba1..54ac4fe 100644 --- a/tools/ipadm/README.md +++ b/tools/ipadm/README.md @@ -44,7 +44,7 @@ Installiert auf dem Server unter `/usr/local/bin/ipadm`. ## Usage ``` -ipadm add/edit host (interaktiv) +ipadm add/edit host (interactive) ipadm -l list hosts ipadm -a [-f] [mac address] add host ipadm -c set comment @@ -53,13 +53,15 @@ ipadm -m change mac address ipadm -r rename host ipadm -d delete host ipadm -pa [-f] [lan-port] [tcp|udp|both] - add port-forward zu bekanntem Host -ipadm -pl list port-forwards -ipadm -pd [tcp|udp|both] delete port-forward -ipadm -u update (dnsmasq+nftables-Config regenerieren + reload) + add port-forward to a known host +ipadm -pl list port-forwards +ipadm -pd [tcp|udp|both] delete port-forward +ipadm -u update (regenerate dnsmasq+nftables config, reload) ipadm -h this help ``` +(Output/messages of the tool itself are English; this README stays German like the rest of the repo.) + Beispiel: ```sh diff --git a/tools/ipadm/dnsmasq.go b/tools/ipadm/dnsmasq.go index 5d0ec46..9712087 100644 --- a/tools/ipadm/dnsmasq.go +++ b/tools/ipadm/dnsmasq.go @@ -45,11 +45,11 @@ func applyDnsmasqConfig(path string, content string) error { backup = b hadFile = true } else if !os.IsNotExist(err) { - return fmt.Errorf("kann bestehende %s nicht lesen: %w", path, err) + return fmt.Errorf("cannot read existing %s: %w", path, err) } if err := os.WriteFile(path, []byte(content), 0644); err != nil { - return fmt.Errorf("kann %s nicht schreiben: %w", path, err) + return fmt.Errorf("cannot write %s: %w", path, err) } if err := runDnsmasqTest(); err != nil { @@ -59,11 +59,11 @@ func applyDnsmasqConfig(path string, content string) error { } else { os.Remove(path) } - return fmt.Errorf("dnsmasq-Konfiguration ungültig, Änderung zurückgerollt: %w", err) + return fmt.Errorf("dnsmasq config invalid, change rolled back: %w", err) } if err := reloadDnsmasq(); err != nil { - return fmt.Errorf("%s geschrieben, aber Reload fehlgeschlagen: %w", path, err) + return fmt.Errorf("%s written, but reload failed: %w", path, err) } return nil } diff --git a/tools/ipadm/host.go b/tools/ipadm/host.go index 2b2e7c4..3be9bed 100644 --- a/tools/ipadm/host.go +++ b/tools/ipadm/host.go @@ -22,7 +22,7 @@ const hostHeader = "# ipadm host database - managed with `ipadm`, do not edit wh func parseHostLine(line string) (Host, error) { parts := strings.SplitN(line, "\t", 4) if len(parts) < 3 { - return Host{}, fmt.Errorf("ungültige Zeile (erwarte mind. 3 Tab-getrennte Felder): %q", line) + return Host{}, fmt.Errorf("invalid line (expected at least 3 tab-separated fields): %q", line) } mac := parts[2] if mac == "-" { diff --git a/tools/ipadm/interactive.go b/tools/ipadm/interactive.go index 826b166..8432c98 100644 --- a/tools/ipadm/interactive.go +++ b/tools/ipadm/interactive.go @@ -37,16 +37,16 @@ func interactiveEdit(hosts []Host, name string, cfg netConfig) ([]Host, error) { var cur Host if idx >= 0 { cur = hosts[idx] - fmt.Printf("Bearbeite bestehenden Host %q (Enter = Wert behalten)\n", name) + fmt.Printf("Editing existing host %q (press enter to keep a value)\n", name) } else { cur = Host{Name: name} - fmt.Printf("Neuer Host %q (Enter bei MAC/Comment = leer lassen)\n", name) + fmt.Printf("New host %q (press enter to leave MAC/comment empty)\n", name) } for { - ip := prompt("IP-Adresse", cur.IP) + ip := prompt("IP address", cur.IP) if err := validateIP(ip, cfg.lanCIDR, cfg.poolStart, cfg.poolEnd); err != nil { - fmt.Fprintln(os.Stderr, "Fehler:", err) + fmt.Fprintln(os.Stderr, "error:", err) continue } cur.IP = ip @@ -54,26 +54,26 @@ func interactiveEdit(hosts []Host, name string, cfg netConfig) ([]Host, error) { } for { - mac := prompt("MAC-Adresse (optional, '-' zum Löschen)", macOrDash(cur.MAC)) + mac := prompt("MAC address (optional, '-' to clear)", macOrDash(cur.MAC)) if mac == "-" { mac = "" } norm, err := normalizeMAC(mac) if err != nil { - fmt.Fprintln(os.Stderr, "Fehler:", err) + fmt.Fprintln(os.Stderr, "error:", err) continue } cur.MAC = norm break } - cur.Comment = prompt("Kommentar", cur.Comment) + cur.Comment = prompt("Comment", cur.Comment) if other := findByIP(hosts, cur.IP, idx); other >= 0 { - return nil, fmt.Errorf("IP %s ist bereits Host %q zugewiesen", cur.IP, hosts[other].Name) + return nil, fmt.Errorf("IP %s is already assigned to host %q", cur.IP, hosts[other].Name) } if other := findByMAC(hosts, cur.MAC, idx); other >= 0 { - return nil, fmt.Errorf("MAC %s ist bereits Host %q zugewiesen", cur.MAC, hosts[other].Name) + return nil, fmt.Errorf("MAC %s is already assigned to host %q", cur.MAC, hosts[other].Name) } if idx >= 0 { diff --git a/tools/ipadm/main.go b/tools/ipadm/main.go index 317ee1d..4149376 100644 --- a/tools/ipadm/main.go +++ b/tools/ipadm/main.go @@ -16,23 +16,59 @@ import ( const version = "1.1.0" -const usage = `ipadm - dhcp/dns management (dnsmasq backend) v` + version + ` +type usageRow struct { + left string + desc string + wrap bool // put desc on its own line, indented to the description column +} -usage: ipadm add/edit host (interactive) - ipadm -l list hosts - ipadm -a [-f] [mac address] add host - ipadm -c set comment - ipadm -i change ip address - ipadm -m change mac address - ipadm -r rename host - ipadm -d delete host - ipadm -pa [-f] [lan-port] [tcp|udp|both] - add port-forward to a known host - ipadm -pl list port-forwards - ipadm -pd [tcp|udp|both] delete port-forward - ipadm -u update (regenerate dnsmasq+nftables config, reload) - ipadm -h this help -` +var usageRows = []usageRow{ + {"ipadm ", "add/edit host (interactive)", false}, + {"ipadm -l", "list hosts", false}, + {"ipadm -a [-f] [mac address]", "add host", false}, + {"ipadm -c ", "set comment", false}, + {"ipadm -i ", "change ip address", false}, + {"ipadm -m ", "change mac address", false}, + {"ipadm -r ", "rename host", false}, + {"ipadm -d ", "delete host", false}, + {"ipadm -pa [-f] [lan-port] [tcp|udp|both]", "add port-forward to a known host", true}, + {"ipadm -pl", "list port-forwards", false}, + {"ipadm -pd [tcp|udp|both]", "delete port-forward", false}, + {"ipadm -u", "update (regenerate dnsmasq+nftables config, reload)", false}, + {"ipadm -h", "this help", false}, +} + +// buildUsage lays out usageRows in two aligned columns. Rows too long to +// share a line with their description (marked wrap) get the description on +// the next line instead, so one oversized entry can't drag every other +// line's alignment along with it. +func buildUsage() string { + const gap = 3 + width := 0 + for _, r := range usageRows { + if !r.wrap && len(r.left) > width { + width = len(r.left) + } + } + + var b strings.Builder + fmt.Fprintf(&b, "ipadm - dhcp/dns management (dnsmasq backend) v%s\n\n", version) + for i, r := range usageRows { + prefix := " " + if i == 0 { + prefix = "usage: " + } + if r.wrap { + fmt.Fprintf(&b, "%s%s\n", prefix, r.left) + fmt.Fprintf(&b, "%s%s%s\n", prefix, strings.Repeat(" ", width+gap), r.desc) + } else { + fmt.Fprintf(&b, "%s%-*s%s%s\n", prefix, width, r.left, strings.Repeat(" ", gap), r.desc) + } + } + return b.String() +} + +var usage = buildUsage() // netConfig describes the LAN/WAN the tool validates against. Overridable // via env vars, mainly so this can be tested without touching the real @@ -118,7 +154,7 @@ func main() { cmdPortFwdDelete(args[1:]) default: if strings.HasPrefix(args[0], "-") { - fail("unbekannte Option %q\n\n%s", args[0], usage) + fail("unknown option %q\n\n%s", args[0], usage) } requireArgs(args[1:], 0, "ipadm ") cmdInteractive(args[0]) @@ -127,7 +163,7 @@ func main() { func requireArgs(rest []string, want int, form string) { if len(rest) != want { - fail("falsche Anzahl Argumente für %q\n\n%s", form, usage) + fail("wrong number of arguments for %q\n\n%s", form, usage) } } @@ -153,10 +189,10 @@ func withStore(fn func(hosts []Host) ([]Host, string, error)) { sortHosts(newHosts) if err := s.Save(newHosts); err != nil { - fail("kann %s nicht schreiben: %s", dbPath(), err) + fail("cannot write %s: %s", dbPath(), err) } fmt.Println(summary) - fmt.Println("Hinweis: `ipadm -u` ausführen, um dnsmasq/nftables zu aktualisieren.") + fmt.Println("Note: run `ipadm -u` to apply this to dnsmasq/nftables.") } // withPortFwdStore mirrors withStore for the port-forward DB. It also loads @@ -190,10 +226,10 @@ func withPortFwdStore(fn func(hosts []Host, fwds []PortForward) ([]PortForward, sortPortFwds(newFwds) if err := s.Save(newFwds); err != nil { - fail("kann %s nicht schreiben: %s", portFwdDBPath(), err) + fail("cannot write %s: %s", portFwdDBPath(), err) } fmt.Println(summary) - fmt.Println("Hinweis: `ipadm -u` ausführen, um nftables zu aktualisieren.") + fmt.Println("Note: run `ipadm -u` to apply this to nftables.") } func cmdInteractive(name string) { @@ -203,7 +239,7 @@ func cmdInteractive(name string) { if err != nil { return nil, "", err } - return newHosts, fmt.Sprintf("Host %q gespeichert.", name), nil + return newHosts, fmt.Sprintf("Host %q saved.", name), nil }) } @@ -218,7 +254,7 @@ func cmdList() { fail("%s", err) } if len(hosts) == 0 { - fmt.Println("keine Hosts eingetragen") + fmt.Println("no hosts configured") return } sortHosts(hosts) @@ -264,22 +300,22 @@ func cmdAdd(args []string) { idx := findHost(hosts, name) if idx >= 0 && !force { - return nil, "", fmt.Errorf("Host %q existiert bereits (benutze -f zum Überschreiben oder `ipadm %s` zum Bearbeiten)", name, name) + return nil, "", fmt.Errorf("host %q already exists (use -f to overwrite, or `ipadm %s` to edit it)", name, name) } if other := findByIP(hosts, ip, idx); other >= 0 { - return nil, "", fmt.Errorf("IP %s ist bereits Host %q zugewiesen", ip, hosts[other].Name) + return nil, "", fmt.Errorf("IP %s is already assigned to host %q", ip, hosts[other].Name) } if other := findByMAC(hosts, normMac, idx); other >= 0 { - return nil, "", fmt.Errorf("MAC %s ist bereits Host %q zugewiesen", normMac, hosts[other].Name) + return nil, "", fmt.Errorf("MAC %s is already assigned to host %q", normMac, hosts[other].Name) } h := Host{Name: name, IP: ip, MAC: normMac} if idx >= 0 { hosts[idx] = h - return hosts, fmt.Sprintf("Host %q überschrieben (-f).", name), nil + return hosts, fmt.Sprintf("Host %q overwritten (-f).", name), nil } hosts = append(hosts, h) - return hosts, fmt.Sprintf("Host %q hinzugefügt.", name), nil + return hosts, fmt.Sprintf("Host %q added.", name), nil }) } @@ -292,10 +328,10 @@ func cmdComment(args []string) { withStore(func(hosts []Host) ([]Host, string, error) { idx := findHost(hosts, name) if idx < 0 { - return nil, "", fmt.Errorf("Host %q nicht gefunden", name) + return nil, "", fmt.Errorf("host %q not found", name) } hosts[idx].Comment = comment - return hosts, fmt.Sprintf("Kommentar von %q gesetzt.", name), nil + return hosts, fmt.Sprintf("Comment set for %q.", name), nil }) } @@ -308,16 +344,16 @@ func cmdChangeIP(args []string) { withStore(func(hosts []Host) ([]Host, string, error) { idx := findHost(hosts, name) if idx < 0 { - return nil, "", fmt.Errorf("Host %q nicht gefunden", name) + return nil, "", fmt.Errorf("host %q not found", name) } if err := validateIP(ip, cfg.lanCIDR, cfg.poolStart, cfg.poolEnd); err != nil { return nil, "", err } if other := findByIP(hosts, ip, idx); other >= 0 { - return nil, "", fmt.Errorf("IP %s ist bereits Host %q zugewiesen", ip, hosts[other].Name) + return nil, "", fmt.Errorf("IP %s is already assigned to host %q", ip, hosts[other].Name) } hosts[idx].IP = ip - return hosts, fmt.Sprintf("IP von %q auf %s geändert.", name, ip), nil + return hosts, fmt.Sprintf("IP of %q changed to %s.", name, ip), nil }) } @@ -332,21 +368,21 @@ func cmdChangeMAC(args []string) { withStore(func(hosts []Host) ([]Host, string, error) { idx := findHost(hosts, name) if idx < 0 { - return nil, "", fmt.Errorf("Host %q nicht gefunden", name) + return nil, "", fmt.Errorf("host %q not found", name) } normMac, err := normalizeMAC(mac) if err != nil { return nil, "", err } if other := findByMAC(hosts, normMac, idx); other >= 0 { - return nil, "", fmt.Errorf("MAC %s ist bereits Host %q zugewiesen", normMac, hosts[other].Name) + return nil, "", fmt.Errorf("MAC %s is already assigned to host %q", normMac, hosts[other].Name) } hosts[idx].MAC = normMac label := normMac if label == "" { - label = "(entfernt)" + label = "(removed)" } - return hosts, fmt.Sprintf("MAC von %q auf %s geändert.", name, label), nil + return hosts, fmt.Sprintf("MAC of %q changed to %s.", name, label), nil }) } @@ -361,13 +397,13 @@ func cmdRename(args []string) { } idx := findHost(hosts, oldName) if idx < 0 { - return nil, "", fmt.Errorf("Host %q nicht gefunden", oldName) + return nil, "", fmt.Errorf("host %q not found", oldName) } if existing := findHost(hosts, newName); existing >= 0 && existing != idx { - return nil, "", fmt.Errorf("Host %q existiert bereits", newName) + return nil, "", fmt.Errorf("host %q already exists", newName) } hosts[idx].Name = newName - return hosts, fmt.Sprintf("Host %q umbenannt in %q.", oldName, newName), nil + return hosts, fmt.Sprintf("Host %q renamed to %q.", oldName, newName), nil }) } @@ -379,10 +415,10 @@ func cmdDelete(args []string) { withStore(func(hosts []Host) ([]Host, string, error) { idx := findHost(hosts, name) if idx < 0 { - return nil, "", fmt.Errorf("Host %q nicht gefunden", name) + return nil, "", fmt.Errorf("host %q not found", name) } hosts = append(hosts[:idx], hosts[idx+1:]...) - return hosts, fmt.Sprintf("Host %q gelöscht.", name), nil + return hosts, fmt.Sprintf("Host %q deleted.", name), nil }) } @@ -403,7 +439,7 @@ func cmdPortFwdAdd(args []string) { withPortFwdStore(func(hosts []Host, fwds []PortForward) ([]PortForward, string, error) { if findHost(hosts, name) < 0 { - return nil, "", fmt.Errorf("Host %q ist nicht in der ipadm-Datenbank (erst mit `ipadm %s` oder `ipadm -a` anlegen)", name, name) + return nil, "", fmt.Errorf("host %q is not in the ipadm database (add it first with `ipadm %s` or `ipadm -a`)", name, name) } lanPort, proto, err := classifyPortFwdArgs(args[2:], wanPort) if err != nil { @@ -418,14 +454,14 @@ func cmdPortFwdAdd(args []string) { conflicts := findConflictingPortFwds(fwds, wanPort, proto, -1) if len(conflicts) > 0 && !force { - return nil, "", fmt.Errorf("wan-port %d/%s kollidiert mit bestehendem Port-Forward (benutze -f zum Überschreiben)", wanPort, proto) + return nil, "", fmt.Errorf("wan-port %d/%s conflicts with an existing port-forward (use -f to overwrite)", wanPort, proto) } for i := len(conflicts) - 1; i >= 0; i-- { fwds = append(fwds[:conflicts[i]], fwds[conflicts[i]+1:]...) } fwds = append(fwds, PortForward{WanPort: wanPort, Proto: proto, Host: name, LanPort: lanPort}) - return fwds, fmt.Sprintf("Port-Forward %d/%s -> %s:%d angelegt.", wanPort, proto, name, lanPort), nil + return fwds, fmt.Sprintf("Port-forward %d/%s -> %s:%d added.", wanPort, proto, name, lanPort), nil }) } @@ -454,7 +490,7 @@ func cmdPortFwdList() { fail("%s", err) } if len(fwds) == 0 { - fmt.Println("keine Port-Forwards eingetragen") + fmt.Println("no port-forwards configured") return } sortPortFwds(fwds) @@ -463,7 +499,7 @@ func cmdPortFwdList() { for _, f := range fwds { ip, ok := ipByHost[strings.ToLower(f.Host)] if !ok { - ip = "??? (Host fehlt)" + ip = "??? (host missing)" } fmt.Fprintf(tw, "%d\t%s\t%s\t%s\t%d\n", f.WanPort, f.Proto, f.Host, ip, f.LanPort) } @@ -482,33 +518,33 @@ func cmdPortFwdDelete(args []string) { if len(args) == 2 { proto = args[1] if !validProtos[proto] { - fail("ungültiges Protokoll %q (erwarte tcp/udp/both)", proto) + fail("invalid protocol %q (expected tcp/udp/both)", proto) } } withPortFwdStore(func(hosts []Host, fwds []PortForward) ([]PortForward, string, error) { matches := findPortFwd(fwds, wanPort, proto) if len(matches) == 0 { - return nil, "", fmt.Errorf("kein Port-Forward für wan-port %d gefunden", wanPort) + return nil, "", fmt.Errorf("no port-forward found for wan-port %d", wanPort) } if len(matches) > 1 { var protos []string for _, i := range matches { protos = append(protos, fwds[i].Proto) } - return nil, "", fmt.Errorf("wan-port %d ist mehrdeutig (Protokolle: %s) — bitte Protokoll angeben", wanPort, strings.Join(protos, ", ")) + return nil, "", fmt.Errorf("wan-port %d is ambiguous (protocols: %s) — please specify a protocol", wanPort, strings.Join(protos, ", ")) } idx := matches[0] deleted := fwds[idx] fwds = append(fwds[:idx], fwds[idx+1:]...) - return fwds, fmt.Sprintf("Port-Forward %d/%s (-> %s:%d) gelöscht.", deleted.WanPort, deleted.Proto, deleted.Host, deleted.LanPort), nil + return fwds, fmt.Sprintf("Port-forward %d/%s (-> %s:%d) deleted.", deleted.WanPort, deleted.Proto, deleted.Host, deleted.LanPort), nil }) } func parsePort(s string) (int, error) { var port int if _, err := fmt.Sscanf(s, "%d", &port); err != nil { - return 0, fmt.Errorf("ungültiger Port: %q", s) + return 0, fmt.Errorf("invalid port: %q", s) } return port, nil } @@ -530,7 +566,7 @@ func cmdUpdate() { if err := applyDnsmasqConfig(dnsmasqHostsPath(), dnsContent); err != nil { fail("%s", err) } - fmt.Printf("%s aktualisiert, dnsmasq neu geladen (%d Hosts).\n", dnsmasqHostsPath(), len(hosts)) + fmt.Printf("%s updated, dnsmasq reloaded (%d hosts).\n", dnsmasqHostsPath(), len(hosts)) fs, err := openPortFwdStore(portFwdDBPath()) if err != nil { @@ -544,10 +580,10 @@ func cmdUpdate() { nftContent, warnings := renderPortForwards(hosts, fwds, cfg.wanIface) for _, w := range warnings { - fmt.Fprintln(os.Stderr, "warnung:", w) + fmt.Fprintln(os.Stderr, "warning:", w) } if err := applyPortForwards(nftPortFwdPath(), nftContent); err != nil { fail("%s", err) } - fmt.Printf("%s aktualisiert, nftables neu geladen (%d Port-Forwards).\n", nftPortFwdPath(), len(fwds)-len(warnings)) + fmt.Printf("%s updated, nftables reloaded (%d port-forwards).\n", nftPortFwdPath(), len(fwds)-len(warnings)) } diff --git a/tools/ipadm/nftpf.go b/tools/ipadm/nftpf.go index 24a1c0e..8eb0da2 100644 --- a/tools/ipadm/nftpf.go +++ b/tools/ipadm/nftpf.go @@ -25,7 +25,7 @@ func renderPortForwards(hosts []Host, fwds []PortForward, wanIface string) (cont for _, f := range sorted { ip, ok := ipByHost[strings.ToLower(f.Host)] if !ok { - warnings = append(warnings, fmt.Sprintf("Port-Forward %d/%s -> %q übersprungen: Host nicht (mehr) in der ipadm-Datenbank", f.WanPort, f.Proto, f.Host)) + warnings = append(warnings, fmt.Sprintf("port-forward %d/%s -> %q skipped: host no longer in the ipadm database", f.WanPort, f.Proto, f.Host)) continue } switch f.Proto { @@ -58,14 +58,14 @@ func applyPortForwards(path string, content string) error { backup = b hadFile = true } else if !os.IsNotExist(err) { - return fmt.Errorf("kann bestehende %s nicht lesen: %w", path, err) + return fmt.Errorf("cannot read existing %s: %w", path, err) } if err := os.MkdirAll(dirOf(path), 0755); err != nil { - return fmt.Errorf("kann Verzeichnis für %s nicht anlegen: %w", path, err) + return fmt.Errorf("cannot create directory for %s: %w", path, err) } if err := os.WriteFile(path, []byte(content), 0644); err != nil { - return fmt.Errorf("kann %s nicht schreiben: %w", path, err) + return fmt.Errorf("cannot write %s: %w", path, err) } if err := runNftTest(); err != nil { @@ -74,11 +74,11 @@ func applyPortForwards(path string, content string) error { } else { os.Remove(path) } - return fmt.Errorf("nftables-Konfiguration ungültig, Änderung zurückgerollt: %w", err) + return fmt.Errorf("nftables config invalid, change rolled back: %w", err) } if err := reloadNftables(); err != nil { - return fmt.Errorf("%s geschrieben, aber Reload fehlgeschlagen: %w", path, err) + return fmt.Errorf("%s written, but reload failed: %w", path, err) } return nil } diff --git a/tools/ipadm/portfwd.go b/tools/ipadm/portfwd.go index 4939eac..670e69b 100644 --- a/tools/ipadm/portfwd.go +++ b/tools/ipadm/portfwd.go @@ -26,19 +26,19 @@ const portFwdHeader = "# ipadm port-forward database - managed with `ipadm`, do func parsePortFwdLine(line string) (PortForward, error) { parts := strings.Split(line, "\t") if len(parts) != 4 { - return PortForward{}, fmt.Errorf("ungültige Zeile (erwarte 4 Tab-getrennte Felder): %q", line) + return PortForward{}, fmt.Errorf("invalid line (expected 4 tab-separated fields): %q", line) } wanPort, err := strconv.Atoi(parts[0]) if err != nil { - return PortForward{}, fmt.Errorf("ungültiger wan-port: %q", parts[0]) + return PortForward{}, fmt.Errorf("invalid wan-port: %q", parts[0]) } lanPort, err := strconv.Atoi(parts[3]) if err != nil { - return PortForward{}, fmt.Errorf("ungültiger lan-port: %q", parts[3]) + return PortForward{}, fmt.Errorf("invalid lan-port: %q", parts[3]) } proto := parts[1] if !validProtos[proto] { - return PortForward{}, fmt.Errorf("ungültiges Protokoll: %q", proto) + return PortForward{}, fmt.Errorf("invalid protocol: %q", proto) } return PortForward{WanPort: wanPort, Proto: proto, Host: parts[2], LanPort: lanPort}, nil } @@ -62,7 +62,7 @@ func sortPortFwds(fwds []PortForward) { func validatePort(label string, port int) error { if port < 1 || port > 65535 { - return fmt.Errorf("ungültiger %s: %d (erlaubt: 1-65535)", label, port) + return fmt.Errorf("invalid %s: %d (allowed: 1-65535)", label, port) } return nil } @@ -111,7 +111,7 @@ func classifyPortFwdArgs(args []string, wanPort int) (lanPort int, proto string, for _, a := range args { if validProtos[a] { if protoSet { - return 0, "", fmt.Errorf("Protokoll mehrfach angegeben: %q", a) + return 0, "", fmt.Errorf("protocol given more than once: %q", a) } proto = a protoSet = true @@ -119,13 +119,13 @@ func classifyPortFwdArgs(args []string, wanPort int) (lanPort int, proto string, } if n, convErr := strconv.Atoi(a); convErr == nil { if lanPortSet { - return 0, "", fmt.Errorf("lan-port mehrfach angegeben: %q", a) + return 0, "", fmt.Errorf("lan-port given more than once: %q", a) } lanPort = n lanPortSet = true continue } - return 0, "", fmt.Errorf("unbekanntes Argument %q (erwarte lan-port oder tcp/udp/both)", a) + return 0, "", fmt.Errorf("unrecognized argument %q (expected lan-port or tcp/udp/both)", a) } return lanPort, proto, nil } diff --git a/tools/ipadm/store.go b/tools/ipadm/store.go index 61dbfc9..db0185f 100644 --- a/tools/ipadm/store.go +++ b/tools/ipadm/store.go @@ -21,15 +21,15 @@ type LineStore[T any] struct { func openLineStore[T any](path, header string, parse func(string) (T, error), format func(T) string) (*LineStore[T], error) { if err := os.MkdirAll(dirOf(path), 0755); err != nil { - return nil, fmt.Errorf("kann Verzeichnis für %s nicht anlegen: %w", path, err) + return nil, fmt.Errorf("cannot create directory for %s: %w", path, err) } f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644) if err != nil { - return nil, fmt.Errorf("kann %s nicht öffnen: %w", path, err) + return nil, fmt.Errorf("cannot open %s: %w", path, err) } if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil { f.Close() - return nil, fmt.Errorf("kann Lock auf %s nicht setzen: %w", path, err) + return nil, fmt.Errorf("cannot lock %s: %w", path, err) } return &LineStore[T]{path: path, file: f, header: header, parse: parse, format: format}, nil } diff --git a/tools/ipadm/validate.go b/tools/ipadm/validate.go index 3a63695..444ee8d 100644 --- a/tools/ipadm/validate.go +++ b/tools/ipadm/validate.go @@ -10,7 +10,7 @@ var hostnameRe = regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9]) func validateHostname(name string) error { if !hostnameRe.MatchString(name) { - return fmt.Errorf("ungültiger Hostname %q (nur Buchstaben, Ziffern, Bindestrich, kein führender/abschließender Bindestrich, keine Domain)", name) + return fmt.Errorf("invalid hostname %q (letters, digits, hyphen only; no leading/trailing hyphen; no domain)", name) } return nil } @@ -26,23 +26,23 @@ func ip4ToUint32(ip net.IP) uint32 { func validateIP(ip, lanCIDR, poolStart, poolEnd string) error { parsed := net.ParseIP(ip) if parsed == nil || parsed.To4() == nil { - return fmt.Errorf("ungültige IPv4-Adresse: %q", ip) + return fmt.Errorf("invalid IPv4 address: %q", ip) } _, cidr, err := net.ParseCIDR(lanCIDR) if err != nil { - return fmt.Errorf("internes Problem: ungültiges LAN-CIDR %q: %w", lanCIDR, err) + return fmt.Errorf("internal error: invalid LAN CIDR %q: %w", lanCIDR, err) } if !cidr.Contains(parsed) { - return fmt.Errorf("IP %s liegt nicht im LAN-Netz %s", ip, lanCIDR) + return fmt.Errorf("IP %s is not inside LAN network %s", ip, lanCIDR) } start := net.ParseIP(poolStart) end := net.ParseIP(poolEnd) if start == nil || end == nil { - return fmt.Errorf("internes Problem: ungültiger DHCP-Pool %q-%q", poolStart, poolEnd) + return fmt.Errorf("internal error: invalid DHCP pool %q-%q", poolStart, poolEnd) } v, s, e := ip4ToUint32(parsed), ip4ToUint32(start), ip4ToUint32(end) if v >= s && v <= e { - return fmt.Errorf("IP %s liegt im dynamischen DHCP-Pool (%s-%s) und ist für statische Reservierungen gesperrt", ip, poolStart, poolEnd) + return fmt.Errorf("IP %s is inside the dynamic DHCP pool (%s-%s) and is reserved, not allowed for static assignments", ip, poolStart, poolEnd) } return nil } @@ -55,7 +55,7 @@ func normalizeMAC(mac string) (string, error) { } hw, err := net.ParseMAC(mac) if err != nil { - return "", fmt.Errorf("ungültige MAC-Adresse %q: %w", mac, err) + return "", fmt.Errorf("invalid MAC address %q: %w", mac, err) } return hw.String(), nil }