-
Notifications
You must be signed in to change notification settings - Fork 9
Code Examples 1: Creating, Parsing, Extracting Address Subnet Info
- Parse IP Address or Subnet String
- Parse IP Address String with Prefix Length as Subnet or IP Address
- Check if String is an IP Address or Host Name
- Parse Socket Address
- Convert to/from bytes, uint32, big.Int, net.IP, net.IPAddr, net.IPMask, net.IPNet, netip.Addr, or netip.Prefix
- Zero Values for Address and Range Types
- Get Prefix Length, Network Mask, Network Address, Version, Subnet Size and Segments from CIDR Address or Subnet
- Apply Masks and Prefix Lengths
- Check if Multicast
- Control Parsing of IP Addresses with Fewer Segments than Standard
Parses representations of IPv6 and IPv4 addresses and subnets. For a list of supported formats, see godoc for IPAddressString or the docs and docs examples
Starting with address or subnet strings, use errors to handle invalid formats:
ipv6AddrStr := ipaddr.NewIPAddressString("a:b:c:d::a:b/64")
if ipAddr, err := ipv6AddrStr.ToAddress(); err != nil {
// err.Error() has validation error
} else {
// use the address
}
...or avoid errors, checking for nil:
str := ipaddr.NewIPAddressString("a:b:c:d:e-f:f:1.2-3.3.4/64")
addr := str.GetAddress()
if addr != nil {
// use address
}
In the IPAddress library, an IPAddress instance can be either an address or a subnet, which differs from most other libraries, which typically use separate types. Using a single type simplifies code.
By default, CIDR IP addresses with a host of zero (or IPAddress ranges whose boundaries have hosts of zero) are treated as subnets, while CIDR IP addresses with non-zero hosts are treated as single addresses. The bits beyond the prefix (as determined by the prefix length) form the host. For example:
parse("1.2.0.0/16") // this is a subnet of 65536 addresses, the 0.0 host makes it a network
parse("1.2.3.4/16") // this is a single address, since the host is 3.4
func parse(addressStr string) {
addr := ipaddr.NewIPAddressString(addressStr).GetAddress()
fmt.Printf("count of %v is %v\n", addressStr, addr.GetCount())
}
Output:
count of 1.2.0.0/16 is 65536
count of 1.2.3.4/16 is 1
This convention follows the same convention in common usage by network engineers.
With this convention, the address 1.2.0.0/16, with a zero host, often called the network address, is the entire network of addresses with the same 16-bit prefix 1.2. Meanwhile, the address 1.2.3.4/16, with a non-zero host, is the single address 1.2.3.4 in that same network. Sometimes it is called an interface address.
The same convention applies to IPv6 in this library. In fact, with IPv6, the address with a zero host is specified to be the subnet router anycast address and is generally not intended to be used for a host, but to be used to communicate with a router for the given subnet.
If you wish to see the network and host bits, use the method ToBinaryString()
Use GetLower
parseAddress("1.2.0.0/16")
parseAddress("1.2.3.4/16")
func parseAddress(addressStr string) {
addr := ipaddr.NewIPAddressString(addressStr).GetAddress().GetLower()
fmt.Printf("count of %v is %v\n", addr, addr.GetCount())
}
Output:
count of 1.2.0.0/16 is 1
count of 1.2.3.4/16 is 1
Or use ToZeroHost
parseAddressHost("1.2.0.0/16")
parseAddressHost("1.2.3.4/16")
func parseAddressHost(addressStr string) {
addr := ipaddr.NewIPAddressString(addressStr).GetAddress()
if addr.IsSinglePrefixBlock() {
addr, _ = addr.ToZeroHost()
fmt.Print("zero host ")
}
fmt.Printf("count of %v is %v\n", addr, addr.GetCount())
}
Output:
zero host count of 1.2.0.0/16 is 1
count of 1.2.3.4/16 is 1
Or parse separately from the prefix length:
parseHostAddress("1.2.0.0/16")
parseHostAddress("1.2.3.4/16")
func parseHostAddress(addressStr string) {
addrStr := ipaddr.NewIPAddressString(addressStr)
addr := addrStr.GetHostAddress() // ignores prefix length
addr = addr.SetPrefixLen(addrStr.GetNetworkPrefixLen().Len())
fmt.Printf("count of %v is %v\n", addr, addr.GetCount())
}
Output:
count of 1.2.0.0/16 is 1
count of 1.2.3.4/16 is 1
Use ToPrefixBlock
parseSubnet("1.2.0.0/16")
parseSubnet("1.2.3.4/16")
func parseSubnet(str string) {
addr := ipaddr.NewIPAddressString(str).GetAddress().ToPrefixBlock()
fmt.Printf("count of %v is %v\n", addr, addr.GetCount())
}
Output:
count of 1.2.0.0/16 is 65536
count of 1.2.0.0/16 is 65536
The code above works the same way for IPv6 address strings with prefix lengths.
This code will not trigger a DNS lookup.
check("1.2.3.4")
check("1.2.a.4")
check("::1")
check("[::1]")
check("1.2.?.4")
func check(hostStr string) {
host := ipaddr.NewHostName(hostStr)
err := host.Validate()
if err == nil {
if host.IsAddress() {
fmt.Println("address: " + host.AsAddress().String())
} else {
fmt.Println("host name: " + host.String())
}
} else {
fmt.Println(err.Error())
}
}
Output:
address: 1.2.3.4
host name: 1.2.a.4
address: ::1
address: ::1
1.2.?.4 Host error: invalid character at index 4
A socket address, or TCP address, consists of address and port, or possibly a host name and port. Use HostName to parse.
hostName := "[a:b:c:d:e:f:a:b]:8080"
hostName2 := "1.2.3.4:8080"
hostName3 := "github.com:8080"
host := ipaddr.NewHostName(hostName)
err := host.Validate()
var address, address2, address3 *net.TCPAddr
if err == nil {
address = host.ToNetTCPAddr()
}
host2 := ipaddr.NewHostName(hostName2)
err2 := host2.Validate()
if err2 == nil {
address2 = host2.ToNetTCPAddr()
}
host3 := ipaddr.NewHostName(hostName3)
err3 := host3.Validate()
if err3 == nil {
address3 = host3.ToNetTCPAddr()
}
if err != nil || err2 != nil || err3 != nil {
// handle improperly formatted host name or address string
}
// use socket addresses
fmt.Println(address, address2, address3)
Output:
[a:b:c:d:e:f:a:b]:8080 1.2.3.4:8080 140.82.114.4:8080
Convert to/from bytes, uint32, big.Int, net.IP, net.IPAddr, net.IPMask, net.IPNet, netip.Addr, or netip.Prefix
Errors are returned when input slices and values are not the correct size, which is not the case in these examples.
loopback := ipaddr.NewIPAddressString("::1").GetAddress()
bytes := loopback.Bytes() // same as for getting net.IP or net.IPMask
fmt.Println(bytes)
// you can use NewIPAddressFromBytes, NewIPAddressFromNetIPMask for []byte or net.IPMask,
// which skip an IPv4-mapping check but are otherwise identical to NewIPAddressFromNetIP
backAgain, _ := ipaddr.NewIPAddressFromNetIP(bytes)
fmt.Println(backAgain)
Output:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
::1
loopbackv6 := ipaddr.NewIPAddressString("::1").GetAddress()
bigVal := loopbackv6.GetValue()
fmt.Println(bigVal)
backAgainv6, _ := ipaddr.NewIPv6AddressFromInt(bigVal)
fmt.Println(backAgainv6)
Output:
1
::1
loopbackv4 := ipaddr.NewIPAddressString("127.0.0.1").GetAddress().ToIPv4()
val := loopbackv4.Uint32Value()
fmt.Println(val)
backAgainv4 := ipaddr.NewIPv4AddressFromUint32(val)
fmt.Println(backAgainv4)
Output:
2130706433
127.0.0.1
loopback := ipaddr.NewIPAddressString("::1").GetAddress()
value := loopback.GetValue()
fmt.Println(value)
var andAgain *ipaddr.IPAddress
if loopback.IsIPv4() {
andAgain = ipaddr.NewIPv4AddressFromUint32(uint32(value.Uint64())).ToIP()
} else {
andAgainv6, _ := ipaddr.NewIPv6AddressFromInt(value)
andAgain = andAgainv6.ToIP()
}
fmt.Println(andAgain)
Output:
1
::1
loopback := ipaddr.NewIPAddressString("::1").GetAddress()
value := loopback.GetValue()
fmt.Println(value)
bigIntBytes := value.Bytes()
valBytes := make([]byte, loopback.GetByteCount())
copy(valBytes[len(valBytes)-len(bigIntBytes):], bigIntBytes)
andAgain, _ := ipaddr.NewIPAddressFromNetIP(valBytes) // keys on byte length for version
fmt.Println(andAgain)
Output:
1
::1
linkLocal := ipaddr.NewIPAddressString("fe80::a%en1").GetAddress()
addr := linkLocal.GetNetIPAddr()
fmt.Println(addr)
backAgain, _ := ipaddr.NewIPAddressFromNetIPAddr(addr)
fmt.Println(backAgain)
Output:
fe80::a%en1
fe80::a%en1
block := ipaddr.NewIPAddressString("1.2.0.0/255.255.0.0").GetAddress()
fmt.Println(block)
ipNet := &net.IPNet{
IP: block.GetNetIP(),
Mask: block.GetNetworkMask().Bytes(),
}
fmt.Println(ipNet)
backAgain, _ := ipaddr.NewIPAddressFromNetIPNet(ipNet)
fmt.Println(backAgain)
Output:
1.2.0.0/16
1.2.0.0/16
1.2.0.0/16
loopback := ipaddr.NewIPAddressString("::1").GetAddress()
addr := loopback.GetNetNetIPAddr()
fmt.Println(addr)
backAgain := ipaddr.NewIPAddressFromNetNetIPAddr(addr)
fmt.Println(backAgain)
Output:
::1
::1
addr := ipaddr.NewIPAddressString("1.2.0.0/16").GetAddress()
netIpAddr := addr.GetNetNetIPAddr()
prefixLen := addr.GetNetworkPrefixLen()
prefix := netip.PrefixFrom(netIpAddr, prefixLen.Len())
fmt.Println(prefix)
backAgain, _ := ipaddr.NewIPAddressFromNetNetIPPrefix(prefix)
fmt.Println(backAgain)
Output:
1.2.0.0/16
1.2.0.0/16
Get Prefix Length, Network Mask, Network Address, Version, Subnet Size and Segments from CIDR Address or Subnet
The code is entirely polymorphic. Note the addresses are subnets since hosts are zero.
cidr("192.168.10.0/24")
cidr("2001:db8:abcd:0012::/64")
func cidr(str string) {
addrString := ipaddr.NewIPAddressString(str)
addr := addrString.GetAddress()
version := addrString.GetIPVersion()
segments := addr.GetSegments()
bitLength := addr.GetBitCount()
size := addr.GetCount()
prefixLen := addr.GetNetworkPrefixLen()
mask := addr.GetNetworkMask()
// three different ways to get the network address
networkAddr, _ := addr.Mask(mask)
networkAddr = networkAddr.WithoutPrefixLen()
networkAddrAnotherWay := addr.GetLower().WithoutPrefixLen()
zeroHost, _ := addr.ToZeroHost()
networkAddrOneMoreWay := zeroHost.WithoutPrefixLen()
fmt.Printf("%v address: %v\nprefix length: %v\nbit length: %v\nsegments: %v\n"+
"size: %v\nnetwork mask: %v\nnetwork address: %v\n\n",
version, addr, prefixLen, bitLength, segments, size, mask, networkAddr)
_, _ = networkAddrAnotherWay, networkAddrOneMoreWay
}
Output:
IPv4 address: 192.168.10.0/24
prefix length: 24
bit length: 32
segments: [192 168 10 0-255]
size: 256
network mask: 255.255.255.0
network address: 192.168.10.0
IPv6 address: 2001:db8:abcd:12::/64
prefix length: 64
bit length: 128
segments: [0x2001 0xdb8 0xabcd 0x12 0x0-0xffff 0x0-0xffff 0x0-0xffff 0x0-0xffff]
size: 18446744073709551616
network mask: ffff:ffff:ffff:ffff::
network address: 2001:db8:abcd:12::
Starting with a mask string, we convert directly to a prefix length:
maskStr := "255.255.255.0"
mask := ipaddr.NewIPAddressString(maskStr).GetAddress()
prefixLen := mask.GetBlockMaskPrefixLen(true)
fmt.Println(maskStr, "is the network mask for prefix length", prefixLen)
Output:
255.255.255.0 is the network mask for prefix length 24
Or we can parse addresses paired with masks, which converts the mask to a prefix length and pairs it with the address:
addrStr := "1.2.3.4"
fullAddrStr := addrStr + ipaddr.PrefixLenSeparatorStr + maskStr
addr := ipaddr.NewIPAddressString(fullAddrStr).GetAddress()
prefixLen = addr.GetPrefixLen()
fmt.Println(fullAddrStr, "parsed as", addr, "has prefix length", prefixLen)
Output:
1.2.3.4/255.255.255.0 parsed as 1.2.3.4/24 has prefix length 24
We obtain the enclosing block, the network for that mask:
block := addr.ToPrefixBlock()
blockNoPrefix := block.WithoutPrefixLen()
fmt.Println("Enclosing block is", block.String()+", without prefix length it is",
blockNoPrefix)
Output:
Enclosing block is 1.2.3.0/24, without prefix length it is 1.2.3.*
In the reverse direction, we can obtain the network mask corresponding to the prefix length:
networkMaskAgain := ipaddr.IPv4Network.
GetNetworkMask(prefixLen.Len()).WithoutPrefixLen()
fmt.Println("Mask for prefix length", prefixLen, "is", networkMaskAgain)
Output:
Mask for prefix length 24 is 255.255.255.0
And we can apply that network mask to an address:
ipAddr := ipaddr.NewIPAddressString(addrStr).GetAddress()
masked, _ := ipAddr.Mask(mask)
fmt.Println(ipAddr, "masked by", mask, "is", masked)
Output:
1.2.3.4 masked by 255.255.255.0 is 1.2.3.0
Equivalently, we can zero-out the host with the prefix length:
zeroHost, _ := ipAddr.ToZeroHostLen(prefixLen.Len())
fmt.Println(ipAddr, "with zero host for prefix length", prefixLen, "is", zeroHost)
Output:
1.2.3.4 with zero host for prefix length 24 is 1.2.3.0
Or we can simply pair the prefix length with the address:
addr = ipAddr.SetPrefixLen(prefixLen.Len())
fmt.Println(ipAddr, "with prefix length", prefixLen, "is", addr)
Output:
1.2.3.4 with prefix length 24 is 1.2.3.4/24
func isMulticast(addressStr string) {
addrString := ipaddr.NewIPAddressString(addressStr)
addr := addrString.GetAddress()
result := addr.IsMulticast()
fmt.Printf("%v is multicast %t\n", addr, result)
}
isMulticast("224.0.0.0/4")
isMulticast("ff00::/64")
isMulticast("224-239.0.0.1-5")
isMulticast("1.0.0.0/4")
isMulticast("fe::/64")
Output:
224.0.0.0/4 is multicast true
ff00::/64 is multicast true
224-239.0.0.1-5 is multicast true
1.0.0.0/4 is multicast false
fe::/64 is multicast false
If you type 23.23.43 into the address bar of some browsers, you will see it is interpreted as 23.23.0.43. If you do the same with http://2222, you will see it interpreted as the IPv4 address 0.0.8.174. Those strings are IPv4 string formats originally introduced by the inet_aton utility in BSD Unix. Those formats have persisted with inet_aton in the various Unix and Linux flavours, while spreading elsewhere into browsers and other software.
If you wish, you can treat such addresses as invalid, using validation parameters. You can either do it on a case-by-case basis or make it the default behaviour.
IP address strings:
var strParams = new(addrstrparam.IPAddressStringParamsBuilder).
Allow_inet_aton(false).ToParams()
str := "23.23.43"
// do not allow it this time
addrStr := ipaddr.NewIPAddressStringParams(str, strParams)
checkStr(addrStr.Wrap())
// otherwise allowed, since the default validation options are permissive
addrStr = ipaddr.NewIPAddressString(str)
checkStr(addrStr.Wrap())
// or make it default behaviour using your own function
addrStr = NewIPAddressString(str)
checkStr(addrStr.Wrap())
func NewIPAddressString(str string) *ipaddr.IPAddressString {
return ipaddr.NewIPAddressStringParams(str, strParams)
}
func checkStr(addrStr ipaddr.ExtendedIdentifierString) {
if addr, err := addrStr.ToAddress(); err == nil {
fmt.Println("address: " + addr.String())
} else {
fmt.Println(err.Error())
}
}
Output:
23.23.43 IP Address error: options do not allow IPv4 address with less than four segments
address: 23.23.0.43
23.23.43 IP Address error: options do not allow IPv4 address with less than four segments
Host names:
var hostParams = new(addrstrparam.HostNameParamsBuilder).GetIPAddressParamsBuilder().
Allow_inet_aton(false).GetParentBuilder().ToParams()
str := "23.23.43"
// do not allow it this time
hostName := ipaddr.NewHostNameParams(str, hostParams)
checkStr(hostName.Wrap())
// otherwise allowed
hostName = ipaddr.NewHostName(str)
checkStr(hostName.Wrap())
// or make it default behaviour using fixed params with your own class
hostName = NewHostName(str)
checkStr(hostName.Wrap())
func NewHostName(str string) *ipaddr.HostName {
return ipaddr.NewHostNameParams(str, hostParams)
}
Output:
23.23.43 Host error: invalid host
address: 23.23.0.43
23.23.43 Host error: invalid host
You can also be more specific, using allow_inet_aton_joined_segments, allow_inet_aton_hex, allow_inet_aton_octal, or allowSingleSegment.
The allowSingleSegment parameter also applies to IPv6, for which 32 hex characters can be parsed an an IPv6 address.
strParams := new(addrstrparam.IPAddressStringParamsBuilder).
AllowSingleSegment(false).ToParams()
str := "aaaabbbbccccddddeeeeffffaaaabbbb"
// do not allow it this time
addrStr := ipaddr.NewIPAddressStringParams(str, strParams)
checkStr(addrStr.Wrap())
// otherwise allowed
addrStr = ipaddr.NewIPAddressString(str)
checkStr(addrStr.Wrap())
Output:
aaaabbbbccccddddeeeeffffaaaabbbb IP Address error: validation options do not allow you to specify a non-segmented single value
address: aaaa:bbbb:cccc:dddd:eeee:ffff:aaaa:bbbb