package main import ( `encoding/binary` `fmt` `net` `net/netip` `reflect` `strconv` `strings` `github.com/TwiN/go-color` `github.com/projectdiscovery/mapcidr` `go4.org/netipx` `subnetter/netsplit` ) /* tplClass4Iter should only be called if legacy info is enabled. It returns a tableLegacy4Sizer and a slice of tableLegacy4. It takes no input. */ func tplClass4Iter() (legacySpec *tableLegacy4Ret, err error) { // This whole thing feels dirty. // It's like adding a microcontroller to a rock. // But it works. var pfx *net.IPNet var classNets []*netip.Prefix var netRange netipx.IPRange var v *netsplit.VLSMSplitter = &netsplit.VLSMSplitter{ Ascending: false, PrefixLengths: []uint8{ 1, // A 2, // B 3, // C 4, // D 4, // E }, BaseSplitter: new(netsplit.BaseSplitter), } if _, pfx, err = net.ParseCIDR("0.0.0.0/0"); err != nil { return } v.SetParent(*pfx) if classNets, _, err = v.Split(); err != nil { return } legacySpec = &tableLegacy4Ret{ Sizer: &tableLegacy4Sizer{ Class: 5, // "CLASS" CIDR: 4, // "BITS" Start: 5, // "START" End: 3, // "END" }, Rows: make([]tableLegacy4, 5), } for idx, cls := range []string{ "A", "B", "C", "D", "E", } { legacySpec.Rows[idx] = tableLegacy4{ Class: cls, CIDR: classNets[idx].String(), NetCIDR: *classNets[idx], } netRange = netipx.RangeOfPrefix(legacySpec.Rows[idx].NetCIDR) legacySpec.Rows[idx].NetStart = netRange.From() legacySpec.Rows[idx].NetEnd = netRange.To() legacySpec.Rows[idx].Start = legacySpec.Rows[idx].NetStart.String() legacySpec.Rows[idx].End = legacySpec.Rows[idx].NetEnd.String() } return } /* tplAddrIter takes a 4 or 6 for inet family/version and returns a tableAddrSizer and slice of tableAddr. tableAddr is sorted from smallest prefix/largest network to largest prefix/smallest network. */ func tplAddrIter(ipVer uint8) (addrs *tableAddrRet, err error) { var dummyAddr netip.Addr var dummyNet *net.IPNet var l int addrs = &tableAddrRet{ Sizer: &tableAddrSizer{ Prefix: 6, // "PREFIX" Bits: 4, // "BITS" Addresses: 9, // "ADDRESSES" Hosts: 5, // "HOSTS" }, } switch ipVer { case 4: if dummyAddr, err = netip.ParseAddr("0.0.0.0"); err != nil { return } case 6: if dummyAddr, err = netip.ParseAddr("::"); err != nil { return } default: err = errBadNet return } // Before we size, we generate the tableAddrs. addrs.Rows = make([]tableAddr, dummyAddr.BitLen()+1) for i := 0; i <= dummyAddr.BitLen(); i++ { addrs.Rows[i] = tableAddr{ Prefix: uint8(i), Bits: uint8(dummyAddr.BitLen() - i), } if addrs.Rows[i].NetPrefix, err = dummyAddr.Prefix(i); err != nil { return } dummyNet = netipx.PrefixIPNet(addrs.Rows[i].NetPrefix.Masked()) addrs.Rows[i].Addresses = mapcidr.CountIPsInCIDR(true, true, dummyNet) addrs.Rows[i].Hosts = mapcidr.CountIPsInCIDR(false, false, dummyNet) } // Now the sizer. The padding itself is handled in different logic, just need the length of the longest value as a string. for _, addr := range addrs.Rows { // I *abhor* walrus operators in anything but loops. l = len(strconv.Itoa(int(addr.Prefix))) if int(addrs.Sizer.Prefix) < l { addrs.Sizer.Prefix = uint8(l) } l = len(strconv.Itoa(int(addr.Bits))) if int(addrs.Sizer.Bits) < l { addrs.Sizer.Bits = uint8(l) } // Use the full numeric length. l = len(addr.Addresses.String()) if int(addrs.Sizer.Addresses) < l { addrs.Sizer.Addresses = uint8(l) } l = len(addr.Hosts.String()) if int(addrs.Sizer.Hosts) < l { addrs.Sizer.Hosts = uint8(l) } } return } /* tplMaskIter4 returns a slice of IPv4 netmasks and returns a slice of tableMask4. Sorted from smallest prefix/largest network to largest prefix/smallest network. */ func tplMaskIter4() (masks *tableMask4Ret, err error) { var dummyAddr netip.Addr var pfx netip.Prefix var dummyNet *net.IPNet var l int masks = &tableMask4Ret{ Sizer: &tableMask4Sizer{ Prefix: 6, // "PREFIX" Netmask: 7, // "NETMASK" Hex: 3, // "HEX" Dec: 3, // "DEC" Bin: 3, // "BIN" }, } if dummyAddr, err = netip.ParseAddr("0.0.0.0"); err != nil { return } masks.Rows = make([]tableMask4, dummyAddr.BitLen()+1) for i := 0; i <= dummyAddr.BitLen(); i++ { if pfx, err = dummyAddr.Prefix(i); err != nil { return } dummyNet = netipx.PrefixIPNet(pfx.Masked()) masks.Rows[i] = tableMask4{ Prefix: uint8(i), Netmask: netsplit.MaskFmt( dummyNet.Mask, "d", ".", "", 1, 0, ), Hex: dummyNet.Mask.String(), Dec: binary.BigEndian.Uint32(dummyNet.Mask), Bin: netsplit.MaskFmt( dummyNet.Mask, "08b", ".", "", 1, 0, ), Mask: dummyNet.Mask, } } // Now the sizer. for _, mask := range masks.Rows { l = len(strconv.Itoa(int(mask.Prefix))) if int(masks.Sizer.Prefix) < l { masks.Sizer.Prefix = uint8(l) } l = len(mask.Netmask) if int(masks.Sizer.Netmask) < l { masks.Sizer.Netmask = uint8(l) } l = len(mask.Hex) if int(masks.Sizer.Hex) < l { masks.Sizer.Hex = uint8(l) } l = len(strconv.FormatUint(uint64(mask.Dec), 10)) if int(masks.Sizer.Dec) < l { masks.Sizer.Dec = uint8(l) } l = len(mask.Bin) if int(masks.Sizer.Bin) < l { masks.Sizer.Bin = uint8(l) } } return } // do not include in template funcs; used externally func hdrRender(hdrVal reflect.Value, indent string, plain bool) (out string) { var val reflect.Value var field reflect.StructField var fieldVal reflect.Value var colLen uint8 var colTitle string var lastField int var valType reflect.Type var tfmt *tableFormatter = tblFmts[plain] var sb *strings.Builder = new(strings.Builder) val = hdrVal valType = val.Type() // Avoid the edge case where a struct's last field is skipped rendering for i := val.NumField(); i > 0; i-- { field = valType.Field(i - 1) if field.Tag.Get("render") == "-" { continue } lastField = i break } // Top-most line. sb.WriteString(indent) sb.WriteString(tfmt.TopLeftHdr) for i := 0; i < val.NumField(); i++ { field = valType.Field(i) if field.Tag.Get("render") == "-" { continue } fieldVal = val.Field(i) colLen = uint8(fieldVal.Uint()) sb.WriteString(strings.Repeat(tfmt.TopFillHdr, int(colLen)+fixedPad)) if i == lastField { sb.WriteString(tfmt.TopRightHdr) } else { sb.WriteString(tfmt.TopColSepHdr) } } sb.WriteString("\n") // Column titles sb.WriteString(indent) sb.WriteString(tfmt.Left) for i := 0; i < val.NumField(); i++ { field = valType.Field(i) if field.Tag.Get("render") == "-" { continue } fieldVal = val.Field(i) colLen = uint8(fieldVal.Uint()) + uint8(fixedPad) colTitle = field.Name if !tfmt.NoUpperTitle { colTitle = strings.ToUpper(colTitle) } if !tfmt.NoBoldTitle { sb.WriteString(color.InBold(padStr(colTitle, colLen))) } else { sb.WriteString(padStr(colTitle, colLen)) } if i == lastField { sb.WriteString(tfmt.Right) } else { sb.WriteString(tfmt.ColSepHdr) } } sb.WriteString("\n") // Header bottom line; headers always include bottom separators. sb.WriteString(indent) sb.WriteString(tfmt.BottomLeftHdr) for i := 0; i < val.NumField(); i++ { field = valType.Field(i) if field.Tag.Get("render") == "-" { continue } fieldVal = val.Field(i) colLen = uint8(fieldVal.Uint()) sb.WriteString(strings.Repeat(tfmt.BottomFillHdr, int(colLen)+fixedPad)) if i == lastField { sb.WriteString(tfmt.BottomRightHdr) } else { sb.WriteString(tfmt.BottomColSepHdr) } } sb.WriteString("\n") out = sb.String() return } // do not include in template funcs; used externally func hdrLineRender(hdrVal reflect.Value, indent string, plain bool, rowIdx int, numRows int) (out string) { var val reflect.Value var field reflect.StructField var fieldVal reflect.Value var colLen uint8 var lastField int var isLastLine bool var valType reflect.Type var tfmt *tableFormatter = tblFmts[plain] var sb *strings.Builder = new(strings.Builder) isLastLine = rowIdx == (numRows - 1) if !isLastLine && tfmt.SuppressLineSep { return } val = hdrVal valType = val.Type() lastField = valType.NumField() - 1 for i := val.NumField(); i >= 0; i-- { field = valType.Field(i - 1) if field.Tag.Get("render") == "-" { continue } lastField = i break } sb.WriteString(indent) if isLastLine { sb.WriteString(tfmt.LastLeft) } else { sb.WriteString(tfmt.LineLeft) } for i := 0; i < val.NumField(); i++ { field = valType.Field(i) if field.Tag.Get("render") == "-" { continue } fieldVal = val.Field(i) colLen = uint8(fieldVal.Uint()) if isLastLine { sb.WriteString(strings.Repeat(tfmt.LastFill, int(colLen)+fixedPad)) } else { sb.WriteString(strings.Repeat(tfmt.Fill, int(colLen)+fixedPad)) } if i == lastField { if isLastLine { sb.WriteString(tfmt.LastRight) } else { sb.WriteString(tfmt.LineRight) } } else { if isLastLine { sb.WriteString(tfmt.LastSep) } else { sb.WriteString(tfmt.LineColSep) } } } sb.WriteString("\n") out = sb.String() return } // do not include in template funcs; used externally func rowRender(val reflect.Value, sizerVal reflect.Value, indent string, plain bool) (out string) { var field reflect.StructField var fieldVal reflect.Value var colLen uint8 var sizerName string var sizerField reflect.Value var callVal string var valType reflect.Type = val.Type() var tfmt *tableFormatter = tblFmts[plain] var sb *strings.Builder = new(strings.Builder) sb.WriteString(indent) for i := 0; i < val.NumField(); i++ { field = valType.Field(i) if field.Tag.Get("render") == "-" { continue } sb.WriteString(tfmt.Left) fieldVal = val.Field(i) sizerName = field.Tag.Get("renderSizeName") if sizerName == "" { sizerName = field.Name } sizerField = sizerVal.FieldByName(sizerName) colLen = uint8(sizerField.Uint()) + uint8(fixedPad) switch fieldVal.Kind() { // This is tailored specifically to this implementation. case reflect.String: sb.WriteString(fieldVal.String()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: sb.WriteString(padStr(fmt.Sprintf("%d", fieldVal.Int()), colLen)) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: sb.WriteString(padStr(fmt.Sprintf("%d", fieldVal.Uint()), colLen)) case reflect.Ptr: // It's a *big.Int. if fieldVal.IsNil() { sb.WriteString(padStr(strings.Repeat(padChars, int(colLen)), colLen)) } else { // TIL you can even *do* this in reflection. fieldVal = fieldVal.MethodByName("String").Call(nil)[0] callVal = fieldVal.String() sb.WriteString(padStr(callVal, colLen)) } } } sb.WriteString("\n") out = sb.String() return }