package preset import ( "fmt" "net/netip" "strconv" "strings" ) // ParsePrefix accepts every spelling of a subnet mask the editor is required to // take — "24", "/24" and "255.255.255.0" — and returns the prefix length. // // Only contiguous masks are accepted. 255.255.0.255 is a typo, not a netmask, // and letting it through would produce a netsh call that fails confusingly. func ParsePrefix(s string) (int, error) { s = strings.TrimSpace(s) if s == "" { return 0, fmt.Errorf("empty mask") } s = strings.TrimPrefix(s, "/") if strings.Contains(s, ".") { addr, err := netip.ParseAddr(s) if err != nil || !addr.Is4() { return 0, fmt.Errorf("%q is not a dotted-quad netmask", s) } b := addr.As4() mask := uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) ones := 0 for ones < 32 && mask&(1< 32 { return 0, fmt.Errorf("prefix /%d out of range", n) } return n, nil } // MaskString renders a prefix length as the dotted-quad netmask netsh expects // on the command line. netsh will not take "/24". func MaskString(prefix int) string { if prefix < 0 || prefix > 32 { prefix = 32 } var mask uint32 if prefix > 0 { mask = ^uint32(0) << uint(32-prefix) } return fmt.Sprintf("%d.%d.%d.%d", mask>>24&0xff, mask>>16&0xff, mask>>8&0xff, mask&0xff) } // FormatPrefix renders a prefix in whichever notation the user last chose. // dotted controls the style; see config.Settings.MaskStyle. func FormatPrefix(prefix int, dotted bool) string { if dotted { return MaskString(prefix) } return "/" + strconv.Itoa(prefix) }