IP

General

Introduction

There are many kinds of networks in the world. These range from the very old such as serial links, through to wide area networks made from copper and fibre, to wireless networks of various kinds, both for computers and for telecommunications devices such as phones. These networks obviously differ at the physical link layer, but in many cases they also differed at higher layers of the OSI stack.

Over the years there has been a convergence to the "internet stack" of IP and TCP/UDP. For example, Bluetooth defines physical layers and protocol layers, but on top of that is an IP stack so that the same internet programming techniques can be employed on many Bluetooth devices. Similarly, developing 4G wireless phone technologies such as LTE (Long Term Evolution) will also use an IP stack.

While IP provides the networking layer 3 of the OSI stack, TCP and UDP deal with layer 4. These are not the final word, even in the interenet world: SCTP has come from the telecommunications to challenge both TCP and UDP, while to provide internet services in interplanetary space requires new, under development protocols such as DTN. Nevertheless, IP, TCP and UDP hold sway as principal networking technologies now and at least for a considerable time into the future. Each langauge has some measure of support for this style of programming.

This chapter discusses the IP layer as this is fundamental to all IP networking programs.

The TCP/IP stack

The OSI model was devised using a committee process wherein the standard was set up and then implemented. Some parts of the OSI standard are obscure, some parts cannot easily be implemented, some parts have not been implemented.

The TCP/IP protocol was devised through a long-running DARPA project. This worked by implementation followed by RFCs (Request For Comment). TCP/IP is the principal Unix networking protocol. TCP/IP = Transmission Control Protocol/Internet Protocol.

The TCP/IP stack is shorter than the OSI one:

TCP is a connection-oriented protocol, UDP (User Datagram Protocol) is a connectionless protocol.

IP datagrams

The IP layer provides a connectionless and unreliable delivery system. It considers each datagram independently of the others. Any association between datagrams must be supplied by the higher layers.

The IP layer supplies a checksum that includes its own header. The header includes the source and destination addresses.

The IP layer handles routing through an Internet. It is also responsible for breaking up large datagrams into smaller ones for transmission and reassembling them at the other end.

Internet adddresses

In order to use a service you must be able to find it. The Internet uses an address scheme for devices such as computers so that they can be located. This addressing scheme was originally devised when there were only a handful of connected computers, and very generously allowed upto 2^32 addresses, using a 32 bit unsigned integer. These are the so-called IPv4 addresses. In recent years, the number of connected (or at least directly addressable) devices has threatened to exceed this number, and so "any day now" we will switch to IPv6 addressing which will allow upto 2^128 addresses, using an unsigned 128 bit integer. The changeover is most likely to be forced by emerging countries, as the developed world has already taken nearly all of the pool of IPv4 addresses.

IPv4 addresses

The address is a 32 bit integer which gives the IP address. This addresses down to a network interface card on a single device. The address is usually written as four bytes in decimal with a dot '.' between them, as in "127.0.0.1" or "66.102.11.104".

The IP address of any device is generally composed of two parts: the address of the network in which the device resides, and the address of the device within that network. Once upon a time, the split between network address and internal address was simple and was based upon the bytes used in the IP address.

This scheme doesn't work well if you want, say, 400 computers on a network. 254 is too small, while 65,536 (-2) is too large. In binary arithmetic terms, you want about 512 (-2). This can be achieved by using a 23 bit network address and 9 bits for the device addresses. Similarly, if you want upto 1024 (-2) devices, you use a 22 bit network address and a 10 bit device address.

Given an IP address of a device, and knowing how many bits N are used for the network address gives a relatively straightforward process for extracting the network address and the device address within that network. Form a "network mask" which is a 32-bit binary number with all ones in the first N places and all zeroes in the remaining ones. For example, if 16 bits are used for the network address, the mask is 11111111111111110000000000000000. It's a little inconvenient using binary, so decimal bytes are usually used. The netmask for 16 bit network addresses is 255.255.0.0, for 24 bit network addresses it is 255.255.255.0, while for 23 bit addresses it would be 255.255.254.0 and for 22 bit addresses it would be 255.255.252.0.

Then to find the network of a device, bit-wise AND it's IP address with the network mask, while the device address within the subnet is found with bit-wise AND of the 1's complement of the mask with the IP address.

IPv6 addresses

The internet has grown vastly beyond original expectations. The initially generous 32-bit addressing scheme is on the verge of running out. There are unpleasant workarounds such as NAT addressing, but eventually we will have to switch to a wider address space. IPv6 uses 128-bit addresses. Even bytes becomes cumbersome to express such addresses, so hexadecimal digits are used, grouped into 4 digits and separated by a colon ':'. A typical address might be 2002:c0e8:82e7:0:0:0:c0e8:82e7.

These addresses are not easy to remember! DNS will become even more important. There are tricks to reducing some addresses, such as eliding zeroes and repeated digits. For example, "localhost" is 0:0:0:0:0:0:0:1, which can be shortened to ::1

Each address is divided into three components: the first is the network address used for internet routing. My ISP for example gives me a 56 bit network address for my home network. Within that, I have 16 bits in which to create subnets. Most homes for example will only have a single subnet. The last part is the device component, of 64 bits, often based on a hosts MAC address, but not necessarily.

IPv6 can be unicast or multicast. Unicast addresses are primarily of three types

A further type (site local) has been deprecated and shld no longer be used (see Deprecating Site Local Addresses ).

Domain name service

For users, working with IP addresses is too difficult. Consequently, most hosts are given a host name such as www.google.com. These names are much easier for users to work with. However, the names must be resolved to IP addresses for most network functions. The resolver may be a list of hard-coded name-address pairs, but much more common is to use the Domain Name Service (DNS). This is a highly distributed service that maps names to IP addresses, and sometimes IP addresses back to names.

We won't go into any of the details of DNS, but most of the rest of this chapter is concerned with how each language uses DNS services to get IP addresses from host names.

Internationalized domain names

The world no longer accepts ASCII as the 'only' text encoding. An increasing number of organisations prefer to work in their own language such as Greek, Arabic, Thai, etc. IDN (Internationalized Domain Names) allows host names to be in any language. But DNS won't accept most of them and they have to be encoded into ASCII for lookup services to find them.

The actual domain name registered is the ASCII name, and s/w has to convert the language-specific name to the ASCII version. This will be discussed in the chapter on Text as it is a complex issue.

Java

The Javadoc for the appropriate class is Class InetAddress

IP types

The class java.net.InetAddress has two subclasses

These cannot be instantiated directly. An InetAddress can be contructed from the bytes of the address: 4 bytes for an IPv4 address, and 16 bytes for an IPv6 address. The method getByName() can take a string representation ("127.0.0.1" or "::1" for example) and create an InetAddress.

You can tell which type of InetAddress you have using instanceof.

There are methods in each class to tell what type of address you have. For IPv4, these are isLinkLocalAddress(), isMulticastAddress() and others. For IPv6 these are isLinkLocalAddress(), isMulticastAddress() and others. There is no isUniqueLocal() but there is IsSiteLocalAddress() which shouldn't really be there.

Get one DNS address

Java has the class InetAddress in package java.net. This class has the static method getByName() which will create an InetAddress object from a host name. From there, other methods will return the host name and host address. A sample program is GetInetInfo.java:


import java.net.InetAddress;
import java.net.UnknownHostException;

public class GetInetInfo{
    public static void main(String[] args){
        if (args.length != 1) {
            System.err.println("Usage: GetInetInfo address");
            // System.exit(1);
            return;
        }

        InetAddress address = null;
        try {
            address = InetAddress.getByName(args[0]);
        } catch(UnknownHostException e) {
            e.printStackTrace();
            // System.exit(2);
            return;
        }
        System.out.println("Host name: " + address.getHostName());
        System.out.println("Host address: " + address.getHostAddress());
        // System.exit(0);
        return;
    }
} // GetInetInfo

This can be run by e.g. java jan.newmarch.name to give


	  Host name: jan.newmarch.name
	  Host address: 103.79.105.27
      

Get all DNS addresses

Note that it is only giving one address, with my setup (and probably yours) an IPv4 address. if you want to get all addresses registered with DNS, you need to use the method getAllByName() as in GetAllInetInfo.java:


import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.UnknownHostException;

public class GetAllInetInfo{
    public static void main(String[] args){
        if (args.length != 1) {
            System.err.println("Usage: GetInetInfo address");
            // System.exit(1);
            return;
        }

        InetAddress[] addresses = null;
        try {
            addresses = InetAddress.getAllByName(args[0]);
        } catch(UnknownHostException e) {
            e.printStackTrace();
            // System.exit(2);
            return;
        }
        if (addresses.length > 0)
	    System.out.println("Host name: " + addresses[0].getHostName());
	for (int n = 0; n < addresses.length; n++) {
	    InetAddress address = addresses[n];
	    if (address instanceof Inet4Address)
		System.out.print("IPv4 address is ");
	    else
		System.out.print("Ipv6 address is ");
	    System.out.println(address.getHostAddress());
	}
        // System.exit(0);
        return;
    }
} // GetAllInetInfo

Then a typical run gives


	  $java GetAllInetInfo jan.newmarch.name
	  Host name: jan.newmarch.name
	  IPv4 address is 103.79.105.27
	  Ipv6 address is 2400:3740:200:d900:0:0:0:260
      

Get all addresses for an interface

An interface (such as eth0) may have multiple IPv6 addresses. There will usually be a link local address, probably a site local address and there could be many global addresses. A program to list them all is ListNets.java:


// From https://docs.oracle.com/javase/tutorial/networking/nifs/listing.html

import java.io.*;
import java.net.*;
import java.util.*;
import static java.lang.System.out;

public class ListNets {

    public static void main(String args[]) throws SocketException {
        Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
        for (NetworkInterface netint : Collections.list(nets))
            displayInterfaceInformation(netint);
    }

    static void displayInterfaceInformation(NetworkInterface netint) throws SocketException {
        out.printf("Display name: %s\n", netint.getDisplayName());
        out.printf("Name: %s\n", netint.getName());
        Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();
        for (InetAddress inetAddress : Collections.list(inetAddresses)) {
            out.printf("InetAddress: %s\n", inetAddress);
        }
        out.printf("\n");
     }
}  

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)
}

Python

Python uses the package socket described at socket — Low-level networking interface

IP types

There is the type ipaddress.ip_address with subclasses IPv4Address and IPv6Address. Example construtors are


      ipaddress.IPAddress('192.168.0.1')
      ipaddress.IPAddress(3232235521)
      ipaddress.IPAddress(b'\xC0\xA8\x00\x01')
  
all of which are the same IPv4 address.

The method version will return 4 or 6 depending on the type, and there are methods such as is_global

Get one DNS address

The following program gets a single IPv4 only, not IPv6 IP.py illustrates these:


import socket
import sys

if len(sys.argv) < 2:
    print('Usage: comm hostname')
    exit(1)

try:
    # only gets IPv4 address
    addr = socket.gethostbyname(sys.argv[1])
except:
    print('No address found')
    exit(2)
    
print(addr)
exit(0)

Get all DNS addresses

The following program gets all addresses IPs.py illustrates these:


import socket
import sys

if len(sys.argv) < 2:
    print('Usage: comm hostname')
    exit(1)

try:
    allAddr = socket.getaddrinfo(sys.argv[1], None)
except:
    print('No address found')
    exit(2)
    
# format is array of (family, type, proto, canonname, sockaddr)
# IPv4 sockaddr is (address, port)
# IPv6 sockaddr is (address, port, flow info, scope id)
    
ips = set()
for tuple in allAddr:
    addr = tuple[4][0]
    ips.add(addr)

print(ips)
exit(0)

    

Get all addresses for an interface

There doesn't seem to be a generic solution but many O/S specific ones. See e.g. https://stackoverflow.com/questions/24196932/how-can-i-get-the-ip-address-from-nic-in-python. The method socket.if_nameindex() returns all iface names though, getting partially there.

Javascript

Node.js uses the package dns for DNS lookups. The documentation is at Node.js Documentation

IP types

There does not appear to be a specific type for Ip addresses.

Get one DNS address

The following program gets one address, here an IPv6 address IP.js illustrates this:



if (process.argv.length < 3) {
    concole.log('Usage: command hostname')
    process.exit(1)
}
hostname = process.argv[2]

const dns = require('dns');

const options = {
  family: 6,
};
dns.lookup(hostname, options, (err, address, family) =>
  console.log('address: %j family: IPv%s', address, family));

Get all DNS addresses

The following program gets all addresses. IP.js illustrates this:


if (process.argv.length < 3) {
    concole.log('Usage: command hostname')
    process.exit(1)
}
hostname = process.argv[2]

const dns = require('dns');

// When options.all is true, the result will be an Array.
options = {
    all: true
}

dns.lookup(hostname, options, (err, addresses) =>
  console.log('addresses: %j', addresses));
// addresses: [{"address":"2606:2800:220:1:248:1893:25c8:1946","family":6}]

Get all addresses for an interface

This is given using the os.networkInterfaces() function interfaces.js illustrates this:



const os = require('os')

ifaces = os.networkInterfaces()
console.dir(ifaces)

Rust

Rust has a very small standard library documented at Crate std . The std.net library has types for IP addresses and for some TCP and UDP calls. There are no DNS functions, unlike the other languages considered here.

The small standard library appears to be a deliberate choice by Rust designers. There is a huge library of user-contributed 'crates' at The Rust community’s crate registry and if you search for DNS there it will return over 200 matches. The philosophy appears to be that crates.io is the defacto standard library and that crates should be pulled from there rather than cluttering up the "real" standard library.

The philosophy is contentious: see Expansion of standard library for lengthy discussions either way. I'm in favour of a large standard library, with consistency of approach, guarantee of non-breaking updates, security, etc, but that isn't going to happen with Rust. So I'll pick what seem to be the most popular crates here and in the sequel. Of course, that may change over time, or even next month.

IP types

Rust has the types IpAddr, Ipv4Addr and Ipv6Addr.

The type Ipv4Addr has a new constructor that takes four 8 bit octets (of type u8) as in


	  let addr = Ipv4Addr::new(127, 0, 0, 1);
      

The type Ipv4Addr also implements fromStr. There may be parsing errors in this, so a Result type is created as in


	  let addr: Result<Ipv4Addr, AddrParseError> = "103.79.105.27".parse();
	  match addr {
	      Ok(a) =>  println!("address {}", a),
	      Err(e) =>  println!("error {}", e)
	  }
      

Functions include is_loopback(), is_private() as in


use std::net::Ipv4Addr;

assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true);
assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false);	  

assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true);
assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true);
assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true);
assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true);
assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false);
assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true);
assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false);
      

The type Ipv6Addr has a new constructor that takes eight 16-bit segments as in


use std::net::Ipv6Addr;	  

let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);	
let newmarch = Ipv6Addr::new(0x2400, 0x3740, 0x200, 0xd900, 0, 0, 0, 0x260);
      

The type Ipv6Addr also implements fromStr. There may be parsing errors in this, so a Result type is created as in


	  let addr: Result<Ipv6Addr, AddrParseError> = "2400:3740:200:d900::260".parse();
	  match addr {
	      Ok(a) =>  println!("address {}", a),
	      Err(e) =>  println!("error {}", e)
	  }
      

Functions include is_global(), is_unicast_link_local_strict().

Get one DNS address

There is no support for DNS in the standard library. The most popular crate for DNS (as at May, 2020) seems to be trust-dns-resolver .

Creating and managing projects using crates is more complex than simply interpreting or compiling a source file. First you need to create a project


	  cargo new Resolv
      
which creates the following directory structure

Resolv
Resolv/.git
Resolv/.git/...
Resolv/.gitignore
Resolv/src
Resolv/src/main.rs
Resolv/Cargo.toml
      

To install the trust-dns-resolver crate, keep it ip to date, build it,etc the following line needs to be added to Resolv/Cargo.toml in the Dependencies section:


	  trust-dns-resolver = "0.19.5"
      

Then in the Resolv direstory, after every change to main.rs, run


	  cargo build
      

I copied the example code into src/main.js and built it - with two main results:

Despite these initial hiccups, it does look like a very solid package

The example program will return a single IP address, and IPv4 address if it exists, otherwise an IPv6 address. Adapted to get a host name from standard input, it is IP.rs illustrates these:


use trust_dns_resolver::Resolver;
use trust_dns_resolver::config::*;
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
	println!("Usage {} hostname", args[0]);
	std::process::exit(1);
    }
    let hostname = &args[1];
    
    // Construct a new Resolver with default configuration options
    // Default tries IPv4 and then IPv6 (if IPv4 fails)
    let opts = ResolverOpts::default();
    match opts.ip_strategy {
	 LookupIpStrategy::Ipv4Only => println!("IPv4 only"),
	 LookupIpStrategy::Ipv6Only => println!("IPv6 only"),
	 LookupIpStrategy::Ipv4AndIpv6 => println!("IPv4 and v6"),
	 LookupIpStrategy::Ipv6thenIpv4 => println!("IPv6 then 4"),
	 LookupIpStrategy::Ipv4thenIpv6 => println!("IPv4 then 6")
    }

    let resolver = Resolver::new(ResolverConfig::default(), opts).unwrap();

    let response = resolver.lookup_ip(hostname);
    match response {
	Ok(resp) =>
	{
	    // only one value to return
	    for a in resp.iter() {
		println!("addr {}", a);
	    }
	},
	Err(err) =>
	{
	    println!("err {}", err);
	    std::process::exit(2)
	}
    }
    std::process::exit(0);
}

Get all DNS addresses

The changes to get both IPv4 and IPv6 addresses are basically just to the options: IPs.rs illustrates these:


use trust_dns_resolver::Resolver;
use trust_dns_resolver::config::*;
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
	println!("Usage {} hostname", args[0]);
	std::process::exit(1);
    }
    let hostname = &args[1];
    
    // Construct a new Resolver with default configuration options
    // Default gets IPv4 and then IPv6
    let mut opts = ResolverOpts::default();
    match opts.ip_strategy {
	 LookupIpStrategy::Ipv4Only => println!("IPv4 only"),
	 LookupIpStrategy::Ipv6Only => println!("IPv6 only"),
	 LookupIpStrategy::Ipv4AndIpv6 => println!("IPv4 and v6"),
	 LookupIpStrategy::Ipv6thenIpv4 => println!("IPv6 then 4"),
	 LookupIpStrategy::Ipv4thenIpv6 => println!("IPv4 then 6")
    }
    // then change to find both
    opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
    
    let resolver = Resolver::new(ResolverConfig::default(), opts).unwrap();

    let response = resolver.lookup_ip(hostname);
    match response {
	Ok(resp) =>
	{
	    for a in resp.iter() {
		println!("addr {}", a);
	    }
	},
	Err(err) =>
	{
	    println!("err {}", err);
	    std::process::exit(2)
	}
    }
    std::process::exit(0);
}

Get all addresses for an interface

Again, there isn't anything in the standard library. The most popular crate that runs under Posix and Windows seems to be get_if_addrs.

The dependency entry in Cargo.toml is


get_if_addrs = "0.5.3"	  
      

The program to list all IP addresses for each NIC is simplicity itself: iface.rs illustrates these:


use get_if_addrs;

fn main() {
    for iface in get_if_addrs::get_if_addrs().unwrap() {
	println!("{:#?}", iface);
    }
}

Partial output is


Interface {
    name: "wlp2s0",
    addr: V4(
        Ifv4Addr {
            ip: 192.168.2.190,
            netmask: 255.255.255.0,
            broadcast: Some(
                192.168.2.255,
            ),
        },
    ),
}
Interface {
    name: "lo",
    addr: V6(
        Ifv6Addr {
            ip: ::1,
            netmask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,
            broadcast: None,
        },
    ),
}
Interface {
    name: "wlp2s0",
    addr: V6(
        Ifv6Addr {
            ip: fd5a:1870:766c::2aa,
            netmask: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,
            broadcast: None,
        },
    ),
}
      

Julia

The Julia network functions are in package Socket and are formally documented at Sockets

IP types

Julia has the abstract type IPAddr with subtypes IPv4 and IPv6. There are constructors for each, using an apprpopriate integer for each e.g.


julia> IPv4(3223256218)
ip"192.30.252.154"

julia> IPv6(3223256218)
ip"::c01e:fc9a"
      
and also string constructors

julia> ip"127.0.0.1"
ip"127.0.0.1"

julia> @ip_str "2001:db8:0:0:0:0:2:1"
ip"2001:db8::2:1"
      

Get one DNS address

Julia uses the C function getaddrinfo() to get an IPv4 or IPv6 address using a DNS lookup. A sample program is IP.jl illustrates these:


using Sockets

if size(ARGS)[1] < 1
   println("Usage: IP hostname")
   exit(1)
end


try
   global ip
   ip = Sockets.getaddrinfo(ARGS[1], IPv6)
catch exc
   println("No address")
   exit(2)
end

println(ip)

Get all DNS addresses

To get all IP addresses for a host, use getalladdrinfo() as in IPs.jl illustrates these:


# See https://docs.julialang.org/en/v1/stdlib/Sockets/#Sockets.getaddrinfo

using Sockets

if size(ARGS)[1] < 1
   println("Usage: IP hostname")
   exit(1)
end


try
   global ips
   ips = Sockets.getalladdrinfo(ARGS[1])
catch exc
   println("No address")
   exit(2)
end

println(ips)

Get all addresses for an interface

There doesn't seem to be anything yet.


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