package main import ( "bytes" "errors" "go4.org/netipx" "io" "log" "net" "net/netip" "os" "strings" "subnetter/netsplit" "github.com/jessevdk/go-flags" "r00t2.io/sysutils/paths" ) func main() { var err error var b []byte var pfx *net.IPNet var resPfx *netip.Prefix var origPfx netip.Prefix var splitter netsplit.NetSplitter var cmnArgs common var nets []*netip.Prefix var remaining *netipx.IPSet var buf *bytes.Buffer var res *netsplit.StructuredResults var splitErr *netsplit.SplitErr = new(netsplit.SplitErr) var parser *flags.Parser = flags.NewParser(args, flags.Default) if _, err = parser.Parse(); err != nil { switch flagsErr := err.(type) { case *flags.Error: switch flagsErr.Type { case flags.ErrHelp, flags.ErrCommandRequired, flags.ErrRequired: // These print their relevant messages by themselves. return default: log.Panicln(err) } default: log.Panicln(err) } } switch parser.Active.Name { case "table": // TODO: print table and exit return case "parse": // TODO: parse file/bytes, unmarshal, and render with new options then exit if strings.TrimSpace(args.Parse.InFile) == "-" { buf = new(bytes.Buffer) if _, err = io.Copy(buf, os.Stdin); err != nil { log.Panicln(err) } b = buf.Bytes() } else { if err = paths.RealPath(&args.Parse.InFile); err != nil { log.Panicln(err) } if b, err = os.ReadFile(args.Parse.InFile); err != nil { log.Panicln(err) } } if res, err = netsplit.Parse(b); err != nil { log.Panicln(err) } if resPfx, nets, remaining, splitter, err = res.Uncontain(); err != nil { log.Panicln(err) } if resPfx != nil { origPfx = *resPfx } pfx = netipx.PrefixIPNet(origPfx.Masked()) cmnArgs = common{ outputOpts: args.Parse.outputOpts, AllowReserved: args.Parse.AllowReserved, AllowHostNet: args.Parse.AllowHostNet, } if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, res.GetSplitter()); err != nil { log.Panicln(err) } return default: // Actually subnet (and print results). /* A netsplit.NetSplitter is needed, along with: * prefix * verbosity * disable showing remaining * formatter These are all handily-dandily enclosed in a `common` struct type. */ switch parser.Active.Name { case "split-hosts": if err = validate.Struct(args.SplitHost); err != nil { log.Panicln(err) } cmnArgs = args.SplitHost.common splitter = &netsplit.HostSplitter{ BaseSplitter: new(netsplit.BaseSplitter), NumberHosts: args.SplitHost.Hosts, } case "split-nets": if err = validate.Struct(args.SplitSubnets); err != nil { log.Panicln(err) } cmnArgs = args.SplitSubnets.common splitter = &netsplit.SubnetSplitter{ BaseSplitter: new(netsplit.BaseSplitter), NumberSubnets: args.SplitSubnets.NumNets, } case "split-cidr": if err = validate.Struct(args.SplitCIDR); err != nil { log.Panicln(err) } cmnArgs = args.SplitCIDR.common splitter = &netsplit.CIDRSplitter{ BaseSplitter: new(netsplit.BaseSplitter), PrefixLength: args.SplitCIDR.Prefix, } case "vlsm": if err = validate.Struct(args.VLSM); err != nil { log.Panicln(err) } cmnArgs = args.VLSM.common splitter = &netsplit.VLSMSplitter{ BaseSplitter: new(netsplit.BaseSplitter), Ascending: args.VLSM.Asc, PrefixLengths: args.VLSM.Sizes, } } if origPfx, err = netip.ParsePrefix(cmnArgs.Network.Network); err != nil { log.Panicln(err) } // This can be a direct conversion. We have to make sure we mask off the host bits to avoid errors, though. /* if _, pfx, err = net.ParseCIDR(cmnArgs.network.network); err != nil { log.Panicln(err) } */ pfx = netipx.PrefixIPNet(origPfx.Masked()) splitter.SetParent(*pfx) if nets, remaining, err = splitter.Split(); err != nil { if errors.As(err, &splitErr) { printSplitErr(splitErr) os.Exit(1) } else { log.Panicln(err) } } if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, splitter); err != nil { log.Panicln(err) } } }