195 lines
4.8 KiB
Go

package main
import (
"bytes"
"errors"
`fmt`
"io"
"log"
"net"
"net/netip"
"os"
"strings"
"go4.org/netipx"
"subnetter/netsplit"
`subnetter/version`
"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 noStrict bool
var strictErr error
var splitErr = new(netsplit.SplitErr)
var 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)
}
}
if version.Ver, err = version.Version(); err != nil {
log.Panicln(err)
}
// If args.Version or args.DetailVersion are true, just print them and exit.
if args.DetailVersion || args.Version {
if args.Version {
fmt.Println(version.Ver.Short())
return
} else if args.DetailVersion {
fmt.Println(version.Ver.Detail())
return
}
}
switch parser.Active.Name {
case "table":
buf = new(bytes.Buffer)
if err = tblTpl.ExecuteTemplate(buf, "table.tpl", args.Table.tableOpts); err != nil {
log.Panicln(err)
}
os.Stdout.Write(buf.Bytes())
return
case "parse":
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{
NumberHosts: args.SplitHost.Hosts,
Strict: args.SplitHost.Strict,
BaseSplitter: new(netsplit.BaseSplitter),
}
noStrict = !args.SplitHost.Strict
strictErr = netsplit.ErrBadNumHosts
case "split-nets":
if err = validate.Struct(args.SplitSubnets); err != nil {
log.Panicln(err)
}
cmnArgs = args.SplitSubnets.common
splitter = &netsplit.SubnetSplitter{
Strict: args.SplitSubnets.Strict,
NumberSubnets: args.SplitSubnets.NumNets,
BaseSplitter: new(netsplit.BaseSplitter),
}
noStrict = !args.SplitSubnets.Strict
strictErr = netsplit.ErrNoNetSpace
case "split-cidr":
if err = validate.Struct(args.SplitCIDR); err != nil {
log.Panicln(err)
}
cmnArgs = args.SplitCIDR.common
splitter = &netsplit.CIDRSplitter{
PrefixLength: args.SplitCIDR.Prefix,
BaseSplitter: new(netsplit.BaseSplitter),
}
case "vlsm":
if err = validate.Struct(args.VLSM); err != nil {
log.Panicln(err)
}
cmnArgs = args.VLSM.common
splitter = &netsplit.VLSMSplitter{
Ascending: args.VLSM.Asc,
PrefixLengths: args.VLSM.Sizes,
BaseSplitter: new(netsplit.BaseSplitter),
}
}
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) {
if noStrict && errors.Is(splitErr.Wrapped, strictErr) {
err = nil
} else {
printSplitErr(splitErr)
os.Exit(1)
}
} else {
log.Panicln(err)
}
}
if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, splitter); err != nil {
log.Panicln(err)
}
}
}