- 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
62 lines
1.9 KiB
Go
62 lines
1.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"regexp"
|
|
)
|
|
|
|
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("invalid hostname %q (letters, digits, hyphen only; no leading/trailing hyphen; no domain)", name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ip4ToUint32(ip net.IP) uint32 {
|
|
b := ip.To4()
|
|
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
|
|
}
|
|
|
|
// validateIP checks that ip is a valid IPv4 address inside lanCIDR and outside
|
|
// the dynamic DHCP pool [poolStart, poolEnd] (both inclusive), so static
|
|
// reservations can never collide with dynamically leased addresses.
|
|
func validateIP(ip, lanCIDR, poolStart, poolEnd string) error {
|
|
parsed := net.ParseIP(ip)
|
|
if parsed == nil || parsed.To4() == nil {
|
|
return fmt.Errorf("invalid IPv4 address: %q", ip)
|
|
}
|
|
_, cidr, err := net.ParseCIDR(lanCIDR)
|
|
if err != nil {
|
|
return fmt.Errorf("internal error: invalid LAN CIDR %q: %w", lanCIDR, err)
|
|
}
|
|
if !cidr.Contains(parsed) {
|
|
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("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 is inside the dynamic DHCP pool (%s-%s) and is reserved, not allowed for static assignments", ip, poolStart, poolEnd)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// normalizeMAC validates a MAC address and returns it in canonical lower-case
|
|
// colon-separated form. Empty input is accepted and returned as-is.
|
|
func normalizeMAC(mac string) (string, error) {
|
|
if mac == "" {
|
|
return "", nil
|
|
}
|
|
hw, err := net.ParseMAC(mac)
|
|
if err != nil {
|
|
return "", fmt.Errorf("invalid MAC address %q: %w", mac, err)
|
|
}
|
|
return hw.String(), nil
|
|
}
|