diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..bffec80 --- /dev/null +++ b/errors.go @@ -0,0 +1,124 @@ +package mmdbwriter + +import ( + "errors" + "fmt" + "net" + "net/netip" +) + +// AliasedNetworkError is returned when inserting a aliased network into +// a Tree where DisableIPv4Aliasing in Options is false. +type AliasedNetworkError struct { + // AliasedNetwork is the aliased network being inserted into. + AliasedNetwork netip.Prefix + // InsertedNetwork is the network being inserted into the Tree. + InsertedNetwork netip.Prefix +} + +func newAliasedNetworkError(netIP net.IP, curPrefixLen, recPrefixLen int) error { + anErr := &AliasedNetworkError{} + ip, ok := netip.AddrFromSlice(netIP) + if !ok { + return errors.Join( + fmt.Errorf("creating netip.Addr from %s", netIP), + anErr, + ) + } + var err error + // We are using netip here despite using net.IP/net.IPNet internally as + // it seems quite likely that we will switch to netip throughout. + anErr.InsertedNetwork, err = ip.Prefix(recPrefixLen) + if err != nil { + return errors.Join( + fmt.Errorf( + "creating prefix from addr %s and prefix length %d: %w", + ip, + recPrefixLen, + err, + ), + anErr, + ) + } + + anErr.AliasedNetwork, err = ip.Prefix(curPrefixLen) + if err != nil { + return errors.Join( + fmt.Errorf( + "creating prefix from addr %s and prefix length %d: %w", + ip, + curPrefixLen, + err, + ), + anErr, + ) + } + return anErr +} + +func (r *AliasedNetworkError) Error() string { + return fmt.Sprintf( + "attempt to insert %s into %s, which is an aliased network", + r.InsertedNetwork, + r.AliasedNetwork, + ) +} + +// ReservedNetworkError is returned when inserting a reserved network into +// a Tree where IncludeReservedNetworks in Options is false. +type ReservedNetworkError struct { + // InsertedNetwork is the network being inserted into the Tree. + InsertedNetwork netip.Prefix + // ReservedNetwork is the reserved network being inserted into. + ReservedNetwork netip.Prefix +} + +var _ error = &ReservedNetworkError{} + +func newReservedNetworkError(netIP net.IP, curPrefixLen, recPrefixLen int) error { + rnErr := &ReservedNetworkError{} + ip, ok := netip.AddrFromSlice(netIP) + if !ok { + return errors.Join( + fmt.Errorf("creating netip.Addr from %s", netIP), + rnErr, + ) + } + var err error + // We are using netip here despite using net.IP/net.IPNet internally as + // it seems quite likely that we will switch to netip throughout. + rnErr.InsertedNetwork, err = ip.Prefix(recPrefixLen) + if err != nil { + return errors.Join( + fmt.Errorf( + "creating prefix from addr %s and prefix length %d: %w", + ip, + recPrefixLen, + err, + ), + rnErr, + ) + } + + rnErr.ReservedNetwork, err = ip.Prefix(curPrefixLen) + if err != nil { + return errors.Join( + fmt.Errorf( + "creating prefix from addr %s and prefix length %d: %w", + ip, + curPrefixLen, + err, + ), + rnErr, + ) + } + return rnErr +} + +func (r *ReservedNetworkError) Error() string { + return fmt.Sprintf( + "attempt to insert %s into %s, which is a reserved network", + r.InsertedNetwork, + r.ReservedNetwork, + ) +} diff --git a/node.go b/node.go index c2148d5..49a19ec 100644 --- a/node.go +++ b/node.go @@ -117,11 +117,7 @@ func (r *record) insert( return r.maybeMergeChildren(iRec) case recordTypeReserved: if iRec.prefixLen >= newDepth { - return fmt.Errorf( - "attempt to insert %s/%d, which is in a reserved network", - iRec.ip, - iRec.prefixLen, - ) + return newReservedNetworkError(iRec.ip, newDepth, iRec.prefixLen) } // If we are inserting a network that contains a reserved network, // we silently remove the reserved network. @@ -133,11 +129,7 @@ func (r *record) insert( return nil } // attempting to insert _into_ an aliased network - return fmt.Errorf( - "attempt to insert %s/%d, which is in an aliased network", - iRec.ip, - iRec.prefixLen, - ) + return newAliasedNetworkError(iRec.ip, newDepth, iRec.prefixLen) default: return fmt.Errorf("inserting into record type %d is not implemented", r.recordType) } diff --git a/tree_test.go b/tree_test.go index b2fa3f5..2e34744 100644 --- a/tree_test.go +++ b/tree_test.go @@ -341,19 +341,19 @@ func TestTreeInsertAndGet(t *testing.T) { network: "10.0.0.0/8", start: "10.0.0.0", end: "10.255.255.255", - expectedErrorMsg: "attempt to insert ::a00:0/104, which is in a reserved network", + expectedErrorMsg: "attempt to insert ::a00:0/104 into ::a00:0/104, which is a reserved network", }, { network: "10.0.0.1/32", start: "10.0.0.1", end: "10.0.0.1", - expectedErrorMsg: "attempt to insert ::a00:1/128, which is in a reserved network", + expectedErrorMsg: "attempt to insert ::a00:1/128 into ::a00:0/104, which is a reserved network", }, { network: "2002:100::/24", start: "2002:100::", end: "2002:1ff:ffff:ffff:ffff:ffff:ffff:ffff", - expectedErrorMsg: "attempt to insert 2002:100::/24, which is in an aliased network", + expectedErrorMsg: "attempt to insert 2002:100::/24 into 2002::/16, which is an aliased network", }, }, gets: []testGet{