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
This commit is contained in:
@@ -44,7 +44,7 @@ Installiert auf dem Server unter `/usr/local/bin/ipadm`.
|
||||
## Usage
|
||||
|
||||
```
|
||||
ipadm <hostname> add/edit host (interaktiv)
|
||||
ipadm <hostname> add/edit host (interactive)
|
||||
ipadm -l list hosts
|
||||
ipadm -a [-f] <hostname> <ip address> [mac address] add host
|
||||
ipadm -c <hostname> <comment> set comment
|
||||
@@ -53,13 +53,15 @@ ipadm -m <hostname> <mac address> change mac address
|
||||
ipadm -r <old hostname> <new hostname> rename host
|
||||
ipadm -d <hostname> delete host
|
||||
ipadm -pa [-f] <hostname> <wan-port> [lan-port] [tcp|udp|both]
|
||||
add port-forward zu bekanntem Host
|
||||
add port-forward to a known host
|
||||
ipadm -pl list port-forwards
|
||||
ipadm -pd <wan-port> [tcp|udp|both] delete port-forward
|
||||
ipadm -u update (dnsmasq+nftables-Config regenerieren + reload)
|
||||
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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -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 == "-" {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+92
-56
@@ -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 <hostname> add/edit host (interactive)
|
||||
ipadm -l list hosts
|
||||
ipadm -a [-f] <hostname> <ip address> [mac address] add host
|
||||
ipadm -c <hostname> <comment> set comment
|
||||
ipadm -i <hostname> <ip address> change ip address
|
||||
ipadm -m <hostname> <mac address> change mac address
|
||||
ipadm -r <old hostname> <new hostname> rename host
|
||||
ipadm -d <hostname> delete host
|
||||
ipadm -pa [-f] <hostname> <wan-port> [lan-port] [tcp|udp|both]
|
||||
add port-forward to a known host
|
||||
ipadm -pl list port-forwards
|
||||
ipadm -pd <wan-port> [tcp|udp|both] delete port-forward
|
||||
ipadm -u update (regenerate dnsmasq+nftables config, reload)
|
||||
ipadm -h this help
|
||||
`
|
||||
var usageRows = []usageRow{
|
||||
{"ipadm <hostname>", "add/edit host (interactive)", false},
|
||||
{"ipadm -l", "list hosts", false},
|
||||
{"ipadm -a [-f] <hostname> <ip address> [mac address]", "add host", false},
|
||||
{"ipadm -c <hostname> <comment>", "set comment", false},
|
||||
{"ipadm -i <hostname> <ip address>", "change ip address", false},
|
||||
{"ipadm -m <hostname> <mac address>", "change mac address", false},
|
||||
{"ipadm -r <old hostname> <new hostname>", "rename host", false},
|
||||
{"ipadm -d <hostname>", "delete host", false},
|
||||
{"ipadm -pa [-f] <hostname> <wan-port> [lan-port] [tcp|udp|both]", "add port-forward to a known host", true},
|
||||
{"ipadm -pl", "list port-forwards", false},
|
||||
{"ipadm -pd <wan-port> [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 <hostname>")
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user