Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
*.coverprofile
.vscode
.idea/
.idea/

.codex
AGENTS.md
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ func main() {
fmt.Printf("Iterate through all %d IP addresses:\n", diff.Size())
ipIter := diff.IPIterator()
for {
ip := ipIter.Next()
if ip == nil {
addr := ipIter.Next()
if !addr.IsValid() {
break
}
fmt.Println(ip)
fmt.Println(addr)
}
}
```
Expand Down
70 changes: 22 additions & 48 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*
Package iprange parses IPv4/IPv6 addresses from strings in IP range
format.
Package iprange parses IPv4/IPv6 addresses from strings in IP range format.

The following IP range formats are supported:

Expand All @@ -9,64 +8,39 @@ The following IP range formats are supported:
172.18.0.1-10 fd00::1-a
172.18.0.1-172.18.1.10 fd00::1-fd00::1:a

It takes a set of IP range strings, and returns a list of start-end IP
address pairs, which can then be automatically extended and normalized,
for instance:
It takes a set of IP range strings, and returns a list of start-end IP address
pairs, which can then be automatically extended and normalized, for instance:

v4Ranges, err := iprange.Parse("172.18.0.1", "172.18.0.0/24") //
v6Ranges, err := iprange.Parse("fd00::1", "fd00::/64") //
invalid, err := iprange.Parse("Invalid IP range string") // ×
dual, err := iprange.Parse("172.18.0.1", "fd00::/64") // ×
v4Ranges, err := iprange.Parse("172.18.0.1", "172.18.0.0/24") // valid
v6Ranges, err := iprange.Parse("fd00::1", "fd00::/64") // valid
invalid, err := iprange.Parse("Invalid IP range string") // invalid
dual, err := iprange.Parse("172.18.0.1", "fd00::/64") // invalid

When parsing an invalid IP range string, error errInvalidIPRangeFormat
will be returned, and dual-stack IP ranges are not allowed because this
approach is too complex and confusing. Use the following functions to
assert the errors:
When parsing an invalid IP range string, error errInvalidIPRangeFormat will be
returned, and dual-stack IP ranges are not allowed. Use the following functions
to assert the errors:

func IsInvalidIPRangeFormat(err error) bool
func IsDualStackIPRanges(err error) bool

Use the interval methods of IPRanges to calculate the union, difference or
intersection of two IPRanges. They do not change the original parameters
(rr and rs), just calculate, and return the results.
intersection of two IPRanges. They do not change the original parameters (rs and
other), just calculate, and return the results.

func (rr *IPRanges) Union(rs *IPRanges) *IPRanges
func (rr *IPRanges) Diff(rs *IPRanges) *IPRanges
func (rr *IPRanges) Intersect(rs *IPRanges) *IPRanges
func (rs *IPRanges) Union(other *IPRanges) *IPRanges
func (rs *IPRanges) Diff(other *IPRanges) *IPRanges
func (rs *IPRanges) Intersect(other *IPRanges) *IPRanges

However, do not attempt to perform calculations on two IPRanges with
different IP versions, it won't work:

res := v4Ranges.Diff(v6Ranges) // res will be equal to v4Ranges.

The IPRanges can be converted into multiple net.IP (i.e. IP addresses)
or *net.IPNet (i.e. subnets) through their own iterators. Continuously
call the method Next() until nil is returned:
The IPRanges can be converted into multiple netip.Addr through iterator.
Continuously call the method Next() until an zero value is returned:

ipIter := ranges.IPIterator()
for {
ip := ipIter.Next()
if ip == nil {
break
}
// Do someting.
}

cidrIter := ranges.CIDRIterator()
for {
cidr := cidrIter.Next()
if cidr == nil {
break
}
// Do someting.
addr := ipIter.Next()
if !addr.IsValid() {
break
}
// TODO
}

Finally, the inspiration for writing this package comes from

CNI plugins: https://github.com/containernetworking/plugins
malfunkt/iprange: https://github.com/malfunkt/iprange
netaddr/netaddr: https://github.com/netaddr/netaddr

both of which are great!
*/
package iprange
199 changes: 15 additions & 184 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"log"
"math/big"
"net"
"net/netip"

"github.com/iiiceoo/iprange"
)
Expand All @@ -26,200 +26,51 @@ func ExampleParse() {
// [fd00::1-fd00::a fd00::1-fd00::1:a]
}

func ExampleIPRanges_Version() {
v4Ranges, err := iprange.Parse("172.18.0.1", "172.18.0.0/24")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
v6Ranges, err := iprange.Parse("fd00::1-a", "fd00::1-fd00::1:a")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
zero := iprange.IPRanges{}

fmt.Println(v4Ranges.Version())
fmt.Println(v6Ranges.Version())
fmt.Println(zero.Version())
// Output:
// IPv4
// IPv6
// Unknown
}

func ExampleIPRanges_Contains() {
ranges, err := iprange.Parse("172.18.0.0/24")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges.Contains(net.ParseIP("172.18.0.1")))
fmt.Println(ranges.Contains(net.ParseIP("172.19.0.1")))
fmt.Println(ranges.Contains(net.ParseIP("fd00::1")))
fmt.Println(ranges.Contains(netip.MustParseAddr("172.18.0.1")))
fmt.Println(ranges.Contains(netip.MustParseAddr("172.19.0.1")))
fmt.Println(ranges.Contains(netip.MustParseAddr("fd00::1")))
// Output:
// true
// false
// false
}

func ExampleIPRanges_MergeEqual() {
ranges1, err := iprange.Parse("172.18.0.0/24")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges2, err := iprange.Parse("172.18.0.100-255", "172.18.0.0-200")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges1.MergeEqual(ranges2))
// Output:
// true
}

func ExampleIPRanges_Equal() {
ranges1, err := iprange.Parse("172.18.0.0/24")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges2, err := iprange.Parse("172.18.0.0-255")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges3, err := iprange.Parse("172.18.0.100-255", "172.18.0.0-200")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges1.Equal(ranges2))
fmt.Println(ranges1.Equal(ranges3))
// Output:
// true
// false
}

func ExampleIPRanges_Size() {
ranges, err := iprange.Parse("172.18.0.0/24")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
zero := iprange.IPRanges{}

fmt.Println(ranges.Size())
fmt.Println(zero.Size())
// Output:
// 256
// 0
}

func ExampleIPRanges_Merge() {
ranges, err := iprange.Parse("172.18.0.201", "172.18.0.100-200", "172.18.0.1-150")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges)
fmt.Println(ranges.Merge())
// Output:
// [172.18.0.201 172.18.0.100-172.18.0.200 172.18.0.1-172.18.0.150]
// 172.18.0.1-172.18.0.201
}

func ExampleIPRanges_Union() {
ranges1, err := iprange.Parse("172.18.0.20-30", "172.18.0.1-25")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges2, err := iprange.Parse("172.18.0.5-25")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges1, _ := iprange.Parse("172.18.0.20-30", "172.18.0.1-25")
ranges2, _ := iprange.Parse("172.18.0.5-25")

fmt.Println(ranges1.Union(ranges2))
fmt.Println(ranges1)
// Output:
// 172.18.0.1-172.18.0.30
}

func ExampleIPRanges_Diff() {
ranges1, err := iprange.Parse("172.18.0.20-30", "172.18.0.0-25")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges2, err := iprange.Parse("172.18.0.4-26")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges1.Diff(ranges2))
// Output:
// [172.18.0.0/30 172.18.0.27-172.18.0.30]
}

func ExampleIPRanges_Intersect() {
ranges1, err := iprange.Parse("172.18.0.20-30", "172.18.0.1-25")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges2, err := iprange.Parse("172.18.0.5-25")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges1.Intersect(ranges2))
// Output:
// 172.18.0.5-172.18.0.25
}

func ExampleIPRanges_Slice() {
ranges, err := iprange.Parse("172.18.0.0-3", "172.18.0.10-14")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges.Slice(big.NewInt(2), big.NewInt(-2)))
// Output:
// [172.18.0.2/31 172.18.0.10-172.18.0.13]
}

func ExampleIPRanges_IsOverlap() {
ranges1, err := iprange.Parse("172.18.0.20-30", "172.18.0.25")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges2, err := iprange.Parse("172.18.0.0/16")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

fmt.Println(ranges1.IsOverlap())
fmt.Println(ranges2.IsOverlap())
// Output:
// true
// false
// [172.18.0.20-172.18.0.30 172.18.0.1-172.18.0.25]
}

func ExampleIPRanges_IPIterator() {
ranges, err := iprange.Parse("172.18.0.1-3")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}
ranges, _ := iprange.Parse("172.18.0.1-3")

iter := ranges.IPIterator()
for {
ip := iter.Next()
if ip == nil {
addr := iter.Next()
if !addr.IsValid() {
break
}
fmt.Println(ip)
fmt.Println(addr)
}

iter.Reset()
n := big.NewInt(2)
for {
ip := iter.NextN(n)
if ip == nil {
addr := iter.NextN(big.NewInt(2))
if !addr.IsValid() {
break
}
fmt.Println(ip)
fmt.Println(addr)
}

// Output:
Expand Down Expand Up @@ -260,23 +111,3 @@ func ExampleIPRanges_BlockIterator() {
// 172.18.0.4
// 172.18.0.4
}

func ExampleIPRanges_CIDRIterator() {
ranges, err := iprange.Parse("172.18.0.0-255", "172.18.0.1-3")
if err != nil {
log.Fatalf("error parsing IP ranges: %v", err)
}

iter := ranges.CIDRIterator()
for {
cidr := iter.Next()
if cidr == nil {
break
}
fmt.Println(cidr)
}
// Output:
// 172.18.0.0/24
// 172.18.0.1/32
// 172.18.0.2/31
}
5 changes: 0 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
module github.com/iiiceoo/iprange

go 1.22

require (
github.com/brunoga/deep v1.2.5
github.com/google/go-cmp v0.7.0
)
4 changes: 0 additions & 4 deletions go.sum

This file was deleted.

Loading
Loading