IP

Go

The package "net" defines many types, functions and methods of use in Go network programming. It is described at Go Package net

IP types

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

	  IP 127.0.0.1
	
with response
	  The address is 127.0.0.1
	
or as
	  IP 0:0:0:0:0:0:0:1
	
with response
	  The address is ::1
	

There are functions isLinkLocalUnicast(), isMulticast() and isGlobalUnicast(), but no function to check IPv6 addresses as unique local.

Get one DNS address

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

	  Resolved address is 172.217.25.164
	
If the net parameter is given as "ip6" instead of "ip", I get
	
	  Resolved address is  2404:6800:4006:801::2004
	
      
Youo may get different results, depending on where Google appears to live to you.

Get all DNS addresses

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
	
      

Get all addresses for an interface

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
      

The type IPmask

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

	  Mask <ip-address> <ones> <zeroes>
	
For an IPv4 address of 103.232.159.187 on a /24 network we get
	  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
	
For an IPv6 address 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
Creative Commons License
" 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/ .

If you like this book, please contribute using PayPal