New Upstream Release - golang-github-c-robinson-iplib

Ready changes

Summary

Merged new upstream version: 1.0.6 (was: 1.0.4).

Resulting package

Built on 2023-03-28T03:43 (took 4m18s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-c-robinson-iplib-dev

Lintian Result

Diff

diff --git a/README.md b/README.md
index bbb211f..228084f 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ It includes:
 Some simple tools for performing common tasks against IP objects:
 
 - compare two addresses
+- make a copy of a net.IP address
 - get the delta between two addresses
 - sort
 - decrement or increment addresses
diff --git a/debian/changelog b/debian/changelog
index ebedf3e..c05f369 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+golang-github-c-robinson-iplib (1.0.6-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Tue, 28 Mar 2023 03:39:44 -0000
+
 golang-github-c-robinson-iplib (1.0.3-3) unstable; urgency=medium
 
   * Add patch to avoid test failures on 32-bit archs (adding an explicit
diff --git a/debian/patches/0001-workaround-test-failure-on-32-bit-archs.patch b/debian/patches/0001-workaround-test-failure-on-32-bit-archs.patch
index 60cc045..ada8ed8 100644
--- a/debian/patches/0001-workaround-test-failure-on-32-bit-archs.patch
+++ b/debian/patches/0001-workaround-test-failure-on-32-bit-archs.patch
@@ -13,9 +13,11 @@ Add an explicit cast to avoid the issue, until the upstream issue has been
 commented on.
 
 Bug: https://github.com/c-robinson/iplib/issues/11
---- a/iplib_test.go
-+++ b/iplib_test.go
-@@ -359,7 +359,7 @@ func TestDeltaIP(t *testing.T) {
+Index: golang-github-c-robinson-iplib.git/iplib_test.go
+===================================================================
+--- golang-github-c-robinson-iplib.git.orig/iplib_test.go
++++ golang-github-c-robinson-iplib.git/iplib_test.go
+@@ -371,7 +371,7 @@ func TestDeltaIP(t *testing.T) {
  func TestDeltaIPMaxValue(t *testing.T) {
  	i := DeltaIP(net.ParseIP("2001:db8::"), net.ParseIP("2001:db8:1234:5678::"))
  	if i != MaxIPv4 {
diff --git a/hostmask.go b/hostmask.go
index 12faab3..f00c83a 100644
--- a/hostmask.go
+++ b/hostmask.go
@@ -18,25 +18,25 @@ import (
 // 2001:db8:1234:5678::1. Here is a Net6 object eing initialized without a
 // hostmask:
 //
-//   n := NewNet6(2001:db8::, 56, 0)
-//   Address            2001:db8::
-//   Netmask            ffff:ffff:ffff:ff00:0000:0000:0000:0000
-//   Hostmask           0000:0000:0000:0000:0000:0000:0000:0000
-//   First              2001:0db8:0000:0000:0000:0000:0000:0000
-//   Last               2001:0db8:0000:00ff:ffff:ffff:ffff:ffff
-//   Count              4722366482869645213696
+//	n := NewNet6(2001:db8::, 56, 0)
+//	Address            2001:db8::
+//	Netmask            ffff:ffff:ffff:ff00:0000:0000:0000:0000
+//	Hostmask           0000:0000:0000:0000:0000:0000:0000:0000
+//	First              2001:0db8:0000:0000:0000:0000:0000:0000
+//	Last               2001:0db8:0000:00ff:ffff:ffff:ffff:ffff
+//	Count              4722366482869645213696
 //
 // This creates a block with 4.7 sextillion usable addresses. Below is he same
 // block with a hostmask of /60. The mask is applied from the rightmost byte,
 // leaving 12 unmasked bits for a total of 4096 allocatable addresses:
 //
-//   n:= NewNet6(2001:db8::, 56, 60)
-//   Address            2001:db8::
-//   Netmask            ffff:ffff:ffff:ff00:0000:0000:0000:0000
-//   Hostmask           0000:0000:0000:0000:0fff:ffff:ffff:ffff
-//   First              2001:0db8:0000:0000:0000:0000:0000:0000
-//   Last               2001:0db8:0000:00ff:f000:0000:0000:0000
-//   Count              4096
+//	n:= NewNet6(2001:db8::, 56, 60)
+//	Address            2001:db8::
+//	Netmask            ffff:ffff:ffff:ff00:0000:0000:0000:0000
+//	Hostmask           0000:0000:0000:0000:0fff:ffff:ffff:ffff
+//	First              2001:0db8:0000:0000:0000:0000:0000:0000
+//	Last               2001:0db8:0000:00ff:f000:0000:0000:0000
+//	Count              4096
 //
 // In the first example the second IP address of the netblock is 2001:db8::1,
 // in the second example it is 2001:db8:0:1::
@@ -45,15 +45,15 @@ import (
 // within those bytes are still blocked out left-to-right, so that address
 // incrementing/decrementing makes sense to the end user, as shown here:
 //
-//   BINARY      Base16  Base10  Example Max16  Max10
-//   0000 0000     0x00       0      /56  0xFF    255
-//   1000 0000     0x80     128      /57  0x7F    127
-//   1100 0000     0xC0     192      /58  0x3F     63
-//   1110 0000     0xE0     224      /59  0x1F     31
-//   1111 0000     0xF0     240      /60  0x0F     15
-//   1111 1000     0xF8     248      /61  0x07      7
-//   1111 1100     0xFC     252      /62  0x03      3
-//   1111 1110     0xFE     254      /63  0x01      1
+//	BINARY      Base16  Base10  Example Max16  Max10
+//	0000 0000     0x00       0      /56  0xFF    255
+//	1000 0000     0x80     128      /57  0x7F    127
+//	1100 0000     0xC0     192      /58  0x3F     63
+//	1110 0000     0xE0     224      /59  0x1F     31
+//	1111 0000     0xF0     240      /60  0x0F     15
+//	1111 1000     0xF8     248      /61  0x07      7
+//	1111 1100     0xFC     252      /62  0x03      3
+//	1111 1110     0xFE     254      /63  0x01      1
 //
 // A hostmask of /1 will block out the left-most bit of the 16th byte
 // while a /8 will block the entire 16th byte.
@@ -209,7 +209,7 @@ func IncrementIP6WithinHostmask(ip net.IP, hm HostMask, count *big.Int) (net.IP,
 // inside the hostmask are set, an empty net.IP{} and an ErrAddressOutOfRange
 // will be returned
 func NextIP6WithinHostmask(ip net.IP, hm HostMask) (net.IP, error) {
-	xip := getCloneIP(ip)
+	xip := CopyIP(ip)
 
 	for i := len(xip) - 1; i >= 0; i-- {
 		if hm[i] == 0xff {
@@ -237,7 +237,7 @@ func NextIP6WithinHostmask(ip net.IP, hm HostMask) (net.IP, error) {
 // If bits inside the hostmask are set, an empty net.IP{} and an
 // ErrAddressOutOfRange will be returned
 func PreviousIP6WithinHostmask(ip net.IP, hm HostMask) (net.IP, error) {
-	xip := getCloneIP(ip)
+	xip := CopyIP(ip)
 	bb, bbpos := hm.BoundaryByte()
 	bbmax := 0xff - bb
 
diff --git a/iplib.go b/iplib.go
index a90b24a..696f715 100644
--- a/iplib.go
+++ b/iplib.go
@@ -47,7 +47,7 @@ import (
 
 const (
 	// MaxIPv4 is the max size of a uint32, also the IPv4 address space
-	MaxIPv4 = 1<<32 - 1
+	MaxIPv4 uint32 = 1<<32 - 1
 
 	// IP4Version is the label returned by IPv4 addresses
 	IP4Version = 4
@@ -138,6 +138,15 @@ func CompareNets(a, b Net) int {
 	return 1
 }
 
+// CopyIP creates a new net.IP object containing the same data as the supplied
+// net.IP (e.g. creates a new array and duplicates the contents)
+func CopyIP(ip net.IP) net.IP {
+	var xip []byte
+	xip = make([]byte, len(ip))
+	copy(xip, ip)
+	return xip
+}
+
 // DecrementIPBy returns a net.IP that is lower than the supplied net.IP by
 // the supplied integer value. If you underflow the IP space it will return
 // the zero address.
@@ -445,9 +454,9 @@ func IsAllZeroes(ip net.IP) bool {
 func NextIP(ip net.IP) net.IP {
 	var xip []byte
 	if EffectiveVersion(ip) == IP4Version {
-		xip = getCloneIP(ForceIP4(ip))
+		xip = CopyIP(ForceIP4(ip))
 	} else {
-		xip = getCloneIP(ip)
+		xip = CopyIP(ip)
 	}
 
 	for i := len(xip) - 1; i >= 0; i-- {
@@ -466,9 +475,9 @@ func NextIP(ip net.IP) net.IP {
 func PreviousIP(ip net.IP) net.IP {
 	var xip []byte
 	if EffectiveVersion(ip) == IP4Version {
-		xip = getCloneIP(ForceIP4(ip))
+		xip = CopyIP(ForceIP4(ip))
 	} else {
-		xip = getCloneIP(ip)
+		xip = CopyIP(ip)
 	}
 
 	for i := len(xip) - 1; i >= 0; i-- {
@@ -530,10 +539,3 @@ func getCloneBigInt(z *big.Int) *big.Int {
 	nz := new(big.Int)
 	return nz.Set(z)
 }
-
-func getCloneIP(ip net.IP) net.IP {
-	var xip []byte
-	xip = make([]byte, len(ip))
-	copy(xip, ip)
-	return xip
-}
diff --git a/iplib_test.go b/iplib_test.go
index 8593b3b..8a2bb68 100644
--- a/iplib_test.go
+++ b/iplib_test.go
@@ -3,10 +3,22 @@ package iplib
 import (
 	"math/big"
 	"net"
+	"reflect"
 	"sort"
 	"testing"
 )
 
+func TestCopyIP(t *testing.T) {
+	ipa := net.ParseIP("192.168.23.5")
+	ipb := CopyIP(ipa)
+	if reflect.ValueOf(ipa).Pointer() == reflect.ValueOf(ipb).Pointer() {
+		t.Errorf("CopyIP() failed to copy (copied IP shares pointer with original)!")
+	}
+	if CompareIPs(ipa, ipb) != 0 {
+		t.Errorf("CopyIP() failed to copy (value of copied IP does not match original)!")
+	}
+}
+
 var IPTests = []struct {
 	ipaddr net.IP
 	next   net.IP
diff --git a/net4.go b/net4.go
index 60d2921..33e74a3 100644
--- a/net4.go
+++ b/net4.go
@@ -1,7 +1,9 @@
 package iplib
 
 import (
+	"crypto/rand"
 	"math"
+	"math/big"
 	"net"
 	"sync"
 )
@@ -106,7 +108,7 @@ func (n Net4) Enumerate(size, offset int) []net.IP {
 
 	// Handle edge-case mask sizes
 	if count == 1 { // Count() returns 1 if host-bits == 0
-		return []net.IP{getCloneIP(n.IPNet.IP)}
+		return []net.IP{CopyIP(n.IPNet.IP)}
 	}
 
 	addrs := make([]net.IP, size)
@@ -209,7 +211,12 @@ func (n Net4) NextIP(ip net.IP) (net.IP, error) {
 // NextNet takes a CIDR mask-size as an argument and attempts to create a new
 // Net object just after the current Net, at the requested mask length
 func (n Net4) NextNet(masklen int) Net4 {
-	return NewNet4(NextIP(n.BroadcastAddress()), masklen)
+	l, _ := n.Mask().Size()
+	nextIP := NextIP(n.BroadcastAddress())
+	if masklen < l {
+		nextIP = IncrementIP4By(nextIP, uint32(math.Pow(2, 32-float64(masklen)))-2)
+	}
+	return NewNet4(nextIP, masklen)
 }
 
 // PreviousIP takes a net.IP as an argument and attempts to decrement it by
@@ -245,6 +252,13 @@ func (n Net4) PreviousNet(masklen int) Net4 {
 	return NewNet4(PreviousIP(n.IP()), masklen)
 }
 
+// RandomIP returns a random address from this Net4. It uses crypto/rand and
+// *big.Int so is not the most performant implementation possible
+func (n Net4) RandomIP() net.IP {
+	z, _ := rand.Int(rand.Reader, big.NewInt(int64(n.Count())))
+	return IncrementIP4By(n.IP(), uint32(z.Uint64()))
+}
+
 // String returns the CIDR notation of the enclosed network e.g. 192.168.0.1/24
 func (n Net4) String() string {
 	return n.IPNet.String()
diff --git a/net4_test.go b/net4_test.go
index cc599e4..fd8d176 100644
--- a/net4_test.go
+++ b/net4_test.go
@@ -389,9 +389,11 @@ var incr4SubnetTests = []struct {
 	netmask  int
 	next     Net4
 }{
+	{Net4FromStr("192.168.0.0/24"), 21, Net4FromStr("192.168.8.0/21")},
+	{Net4FromStr("192.168.0.0/24"), 22, Net4FromStr("192.168.4.0/22")},
+	{Net4FromStr("192.168.0.0/24"), 23, Net4FromStr("192.168.2.0/23")},
 	{Net4FromStr("192.168.0.0/24"), 24, Net4FromStr("192.168.1.0/24")},
 	{Net4FromStr("192.168.0.0/24"), 25, Net4FromStr("192.168.1.0/25")},
-	{Net4FromStr("192.168.0.0/24"), 23, Net4FromStr("192.168.0.0/23")},
 	{Net4FromStr("255.255.255.0/24"), 24, Net4FromStr("255.255.255.0/24")},
 }
 
@@ -626,9 +628,17 @@ func TestNet4_ContainsNet(t *testing.T) {
 	}
 }
 
+func TestNet4_RandomIP(t *testing.T) {
+	for i, tt := range containsNet4Tests {
+		rip := tt.ipn1.RandomIP()
+		if !tt.ipn1.Contains(rip) {
+			t.Errorf("[%d] address %s not in %s", i, rip, tt.ipn1)
+		}
+	}
+}
+
 func TestNet4_Is4in6(t *testing.T) {
 	nf := Net4FromStr("192.168.0.0./16")
-	//nf := NewNet4(ForceIP4(net.ParseIP("192.168.0.0")), 16)
 	if nf.Is4in6() != false {
 		t.Errorf("should be false for '192.168.0.0/16'")
 	}
diff --git a/net6.go b/net6.go
index d5257e4..584f421 100644
--- a/net6.go
+++ b/net6.go
@@ -1,6 +1,7 @@
 package iplib
 
 import (
+	"crypto/rand"
 	"math"
 	"math/big"
 	"net"
@@ -147,13 +148,13 @@ func (n Net6) Enumerate(size, offset int) []net.IP {
 	// workload in this way simply ensures that we [a] can dynamically expand
 	// our worker-pool based on request size; and [b] don't have to worry
 	// about exhausting some upper bound of goroutines -- enumerate requests
-	// are limited to MaxInt32, so we won't generate more than 32768
+	// are limited to MaxUint32, so we won't generate more than 65536
 	limit := uint32(65535)
 	pos := uint32(0)
 	wg := sync.WaitGroup{}
 	for pos < count {
 		incr := limit
-		if limit > count - pos {
+		if limit > count-pos {
 			incr = count - pos
 		}
 		wg.Add(1)
@@ -173,7 +174,7 @@ func (n Net6) Enumerate(size, offset int) []net.IP {
 
 // FirstAddress returns the first usable address for the represented network
 func (n Net6) FirstAddress() net.IP {
-	return getCloneIP(n.IP())
+	return CopyIP(n.IP())
 }
 
 // LastAddress returns the last usable address for the represented network
@@ -248,6 +249,13 @@ func (n Net6) PreviousNet(masklen int) Net6 {
 	return NewNet6(xip, masklen, hmlen)
 }
 
+// RandomIP returns a random address from this Net6. It uses crypto/rand and
+// so is not the most performant implementation possible
+func (n Net6) RandomIP() net.IP {
+	z, _ := rand.Int(rand.Reader, n.Count())
+	return IncrementIP6By(n.FirstAddress(), z)
+}
+
 // String returns the CIDR notation of the enclosed network e.g. 2001:db8::/16
 func (n Net6) String() string {
 	return n.IPNet.String()
diff --git a/net6_test.go b/net6_test.go
index 408908f..01b7fe3 100644
--- a/net6_test.go
+++ b/net6_test.go
@@ -752,6 +752,16 @@ func TestNet6_ContainsNet(t *testing.T) {
 	}
 }
 
+func TestNet6_RandomIP(t *testing.T) {
+	for i, tt := range containsNet6Tests {
+		_, ipn, _ := ParseCIDR(tt.netblock1)
+		rip := ipn.(Net6).RandomIP()
+		if !ipn.Contains(rip) {
+			t.Errorf("[%d] address %s not in %s", i, rip, ipn)
+		}
+	}
+}
+
 var controlsTests = []struct {
 	ipn   Net6
 	addrs map[string]bool

Debdiff

File lists identical (after any substitutions)

No differences were encountered in the control files

More details

Full run details