The package "net" defines many types, functions and methods of use in Go network programming. It is described at Go Package net
The type IP
is defined as an array of bytes
type IP []byte
There are several functions to manipulate a variable of type
IP
, but you are likely to use only some of them in
practice. The function ParseIP(String)
will take a dotted IPv4 address or a colon IPv6 address,
while the IP
method String
will
return a string. Note that you may not get back what you
started with: the string form of 0:0:0:0:0:0:0:1 is ::1.
A program to illustrate this is IP.go:
/* IP
*/
package main
import (
"fmt"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s ip-addr\n", os.Args[0])
os.Exit(1)
}
name := os.Args[1]
addr := net.ParseIP(name)
if addr == nil {
fmt.Println("Invalid address")
} else {
fmt.Println("The address is ", addr.String())
}
os.Exit(0)
}
If this is compiled to the executable IP
then it
can run for example as
with response
IP 127.0.0.1
or as
The address is 127.0.0.1
with response
IP 0:0:0:0:0:0:0:1
The address is ::1
There are functions isLinkLocalUnicast()
,
isMulticast()
and isGlobalUnicast()
,
but no function to check IPv6 addresses as unique local.
Many of the other functions and methods in the net package return
a pointer to an IPAddr
. This is simply a structure
containing an IP
.
type IPAddr {
IP IP
}
A primary use of this type is to perform DNS lookups on IP host names.
func ResolveIPAddr(net, addr string) (*IPAddr, os.Error)
where net
is one of "ip", "ip4" or "ip6".
This is shown in the program
ResolveIP.go
/* ResolveIP
*/
package main
import (
"fmt"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s hostname\n", os.Args[0])
fmt.Println("Usage: ", os.Args[0], "hostname")
os.Exit(1)
}
name := os.Args[1]
addr, err := net.ResolveIPAddr("ip6", name)
if err != nil {
fmt.Println("Resolution error", err.Error())
os.Exit(1)
}
fmt.Println("Resolved address is ", addr.String())
os.Exit(0)
}
Running ResolveIP www.google.com
returns
If the net parameter is given as "ip6" instead of "ip",
I get
Resolved address is 172.217.25.164
Resolved address is 2404:6800:4006:801::2004
Youo may get different results, depending on where Google appears
to live to you.
The function ResolveIPAddr
will perform a DNS lookup
on a hostname, and return a single IP address. However, hosts may
have multiple IP addresses, usually from multiple network interface
cards. They may also have multiple host names, acting as aliases.
func LookupHost(name string) (cname string, addrs []string, err os.Error)
One of these addresses will be labelled as the "canonical" host name.
If you wish to find the canonical name, use
func LookupCNAME(name string) (cname string, err os.Error)
.
This is shown in the following program
/* LookupHost
*/
package main
import (
"fmt"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s hostname\n", os.Args[0])
os.Exit(1)
}
name := os.Args[1]
addrs, err := net.LookupHost(name)
if err != nil {
fmt.Println("Error: ", err.Error())
os.Exit(2)
}
for _, s := range addrs {
fmt.Println(s)
}
os.Exit(0)
}
Note that this function returns strings, not IPAddress
values.
For www.google.com
it prints both the IPv4 and IPv6 addresses
172.217.25.164
2404:6800:4006:806::2004
Similar to the Java program, a Go program to get all addresses for all interfaces is Interfaces.go
// From https://www.socketloop.com/tutorials/golang-get-the-ipv4-and-ipv6-addresses-for-a-specific-network-interface
package main
import (
"fmt"
"net"
)
func main() {
// get available network interfaces for
// this machine
interfaces, err := net.Interfaces()
if err != nil {
fmt.Print(err)
return
}
for _, i := range interfaces {
fmt.Printf("Name : %v \n", i.Name)
byNameInterface, err := net.InterfaceByName(i.Name)
if err != nil {
fmt.Println(err)
}
//fmt.Println("Interface by Name : ", byNameInterface)
addresses, err := byNameInterface.Addrs()
for k, v := range addresses {
fmt.Printf("Interface Address #%v : %v\n", k, v.String())
}
fmt.Println("------------------------------------")
}
}
When run on my server, it gives
Name : lo
Interface Address #0 : 127.0.0.1/8
Interface Address #1 : ::1/128
------------------------------------
Name : eth0
Interface Address #0 : 192.168.2.216/24
Interface Address #1 : fd5a:1870:766c::260/128
Interface Address #2 : 2400:3740:200:d900::260/128
Interface Address #3 : fd5a:1870:766c:0:cc2f:3de0:9947:6895/64
Interface Address #4 : fd5a:1870:766c:0:461e:a1ff:fe3b:7531/64
Interface Address #5 : 2400:3740:200:d900:cc2f:3de0:9947:6895/64
Interface Address #6 : 2400:3740:200:d900:461e:a1ff:fe3b:7531/64
Interface Address #7 : fe80::461e:a1ff:fe3b:7531/64
------------------------------------
Name : docker0
Interface Address #0 : 172.17.42.1/16
An IP address is typically divided into the components of a network address, a subnet and a device portion. The network address and subnet form a prefix to the device portion. The mask is an IP address of all binary ones to match the prefix length, followed by all zeroes.
In order to handle masking operations, there is the type
type IPMask []byte
The simplest function to create a netmask uses the CIDR
notation of ones followed by zeroes upto the number of bits:
func CIDRMask(ones, bits int) IPMask
A mask can then be used by a method of an IP address to find the network for that IP address
func (ip IP) Mask(mask IPMask) IP
An example of the use of this is the following program Mask.go:
/* Mask
*/
package main
import (
"fmt"
"net"
"os"
"strconv"
)
func main() {
if len(os.Args) != 4 {
fmt.Fprintf(os.Stderr, "Usage: %s dotted-ip-addr ones bits\n", os.Args[0])
os.Exit(1)
}
dotAddr := os.Args[1]
ones, _ := strconv.Atoi(os.Args[2])
bits, _ := strconv.Atoi(os.Args[3])
addr := net.ParseIP(dotAddr)
if addr == nil {
fmt.Println("Invalid address")
os.Exit(1)
}
mask := net.CIDRMask(ones, bits)
network := addr.Mask(mask)
fmt.Println("Address is ", addr.String(),
"\nMask length is ", bits,
"\nLeading ones count is ", ones,
"\nMask is (hex) ", mask.String(),
"\nNetwork is ", network.String())
os.Exit(0)
}
This can be compiled to Mask
and run by
For an IPv4 address of
Mask <ip-address> <ones> <zeroes>
103.232.159.187
on a /24 network
we get
For an IPv6 address
go run Mask.go 103.232.159.187 24 32
Address is 103.232.159.187
Mask length is 32
Leading ones count is 24
Mask is (hex) ffffff00
Network is 103.232.159.0
fda3:97c:1eb:fff0:5444:903a:33f0:3a6b
where the network component is fda3:97c:1eb
,
the subnet is fff0
and the device part
is 5444:903a:33f0:3a6b
it gives
go run Mask.go fda3:97c:1eb:fff0:5444:903a:33f0:3a6b 52 128
Address is fda3:97c:1eb:fff0:5444:903a:33f0:3a6b
Mask length is 128
Leading ones count is 52
Mask is (hex) fffffffffffff0000000000000000000
Network is fda3:97c:1eb:f000:
IPv4 netmasks are often given in the 4-byte dotted notation
such as 255.255.255.0
for a /24 network.
There is a function to create a mask from such a 4-byte
IPv4 address
func IPv4Mask(a, b, c, d byte) IPMask
Also, there is a method of IP
which returns
the default mask for IPv4
func (ip IP) DefaultMask() IPMask
Note that the string form of a mask is a hex number such as ffffff00
for a /24 mask.
The following program IPv4Mask.go illustrates these:
/* IPv4Mask
*/
package main
import (
"fmt"
"net"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s dotted-ip-addr\n", os.Args[0])
os.Exit(1)
}
dotAddr := os.Args[1]
addr := net.ParseIP(dotAddr)
if addr == nil {
fmt.Println("Invalid address")
os.Exit(1)
}
mask := addr.DefaultMask()
network := addr.Mask(mask)
ones, bits := mask.Size()
fmt.Println("Address is ", addr.String(),
"\nDefault mask length is ", bits,
"\nLeading ones count is ", ones,
"\nMask is (hex) ", mask.String(),
"\nNetwork is ", network.String())
os.Exit(0)
}
Copyright © Jan Newmarch, jan@newmarch.name
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia"
by
Jan Newmarch
is licensed under a
Creative Commons Attribution-ShareAlike 4.0 International License
.
Based on a work at
https://jan.newmarch.name/NetworkProgramming/
.