212 lines
5.2 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 *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)
}
}
if version.Ver, err = version.Version(); err != nil {
log.Panicln(err)
}
switch parser.Active.Name {
case "version":
if args.Version.DetailVersion {
fmt.Println(version.Ver.Detail())
return
} else {
fmt.Println(version.Ver.Short())
return
}
case "net":
if origPfx, err = netip.ParsePrefix(args.ExplicitNetwork.Network.Network); err != nil {
log.Panicln(err)
}
pfx = netipx.PrefixIPNet(origPfx.Masked())
cmnArgs = args.ExplicitNetwork.common
nets = make([]*netip.Prefix, 1)
nets[0] = new(netip.Prefix)
*nets[0] = origPfx.Masked()
if err = printNets(&origPfx, pfx, nets, nil, &cmnArgs, nil); err != nil {
log.Panicln(err)
}
return
case "reserved":
// TODO
case "table":
// Account for a weird redundant CLI condition.
if args.Table.NoIpv4 && args.Table.NoIpv6 {
args.Table.NoIpv6 = false
args.Table.NoIpv4 = false
}
buf = new(bytes.Buffer)
if err = tblTpl.ExecuteTemplate(buf, "table.tpl", args.Table); 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 = args.Parse.common
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),
}
default:
err = flags.ErrCommandRequired
log.Println(err)
os.Exit(1)
}
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)
}
}
}