C++ IP & Unix Address Library (libaddr)

Introduction

libaddr logo, with arrows going in many directionslibaddr is an easy to use C++ library that parses IP & Unix addresses to C++ objects. This includes parsing one or more IPv4 and IPv6 addresses, with a port or a CIDR mask and the latest also supports real or abstract paths to Unix addresses.

The library also includes functions to list interfaces (i.e. "eth0", "eth1", etc.) and your route tables (see route(8).) The library actually includes a tool, ipv4_routes, one can use to list routes as the libaddr sees them.

The existing parser has limits since it is streaming the input characters instead of converting the input to tokens that can be managed. However, it is still much more powerful than what the default C library functions offer.

IP Addresses Support

The parser accepts IPv4 and IPv6 addresses. Because we also support addresses followed by a port number, the library accepts square brackets for IPv6 (For example: "[::]:443").

The parser accepts a default address, if the input string does not include an address, then the parser makes use of the default one instead.

Addresses can be marked as required. In that case a missing address is an error.

The parser supports lists of addresses separated by commas, spaces, or both. The result of the parser is always a list of address ranges. When multiple addresses are defined, expect the result to include multiple addresses.

Two addresses can be separated by a dash to define a range of addresses. This is similar to using a CIDR, only you may start and end your range on any address instead of a specific number of bits. (Not yet implemented)

IPv4 support

The IPv4 support is limited to the 4 number dot notation (A.B.C.D).

IPv6 support

IPv6 requires square brackets when a port is required as we otherwise view the colon (:) as the port separator. So something like [::1]:123 will work. Just ::1:123 would not be viewed as the address ::1 and port 123.

Port support

We support basic port notation by separating the address and port with a colon.

You may define a port range by separating two decimal numbers by a dash (-). For example 1-1023 would define all reserved ports. (Not yet implemented)

Multiports is also available. You can write multiple ports separated by commas. This is useful if you want to handle a certain number of specific ports that are not clearly defined in a range. For example 1.2.3.4:80,443 references IPv4 address 1.2.3.4 and ports 80 and 443. (Not yet implemented)

Mask support (CIDR)

Masks are written after a slash (/).

A mask can be one decimal number, in which case it represents the number of bits from left to right that are set to 1. The special case 0 is supported (i.e. match all.)

The mask can also be an address. In case of an address, it has to use the same format as the first part (IPv4/IPv4 or IPv6/IPv6). Such a mask can be absolutely anything. (i.e. 85.85.85.85 would clear all even bits of an IPv4 address.)

Range of Addresses and Ports

The library includes an addr_range class used to support a range of addresses and/or port.

The range class supports intersection and matching (i.e. a matching address or port is within the minimum and maximum address and port.)

There is also a function to support matching against a uniion. A union is created by using a vector of ranges.

Unix Addresses

The library offers a class to handle address used by Unix sockets.

This include a full path to a SOCK type of file.

We also support abstract Unix addresses, those are in memory only and can be used as a FIFO between a parent and child process or between threads (although between threads you should use the thread fifo class instead, which will be a lot faster).

Interfaces

The library offers a function to list all the interfaces available on your system.

This is a separate class since one interface may include multiple addresses (the main address, the broadcast address, and the destination address.)

Note that the function returns a vector. The same interface supports multiple IP addresses and thus may appear multiple times in the vector. Not only that, it may include an IPv4 and an IPv6 address.

Routes

The library includes the route class used to save the information about one route.

The routes gathered by the libaddr library are the same as what you get with the standard `route` tool in your command line. Only this is an easy to use C++ object.

The find_default_route() function can be used to specifically find the route that is used whenever non of the others match an IP address. Note that a properly setup network should always have a valid default route, however, it is not guaranteed. The function returns a null pointer in case no default can be determined.

Source

The source code is part of the snapcpp environment. You can find libaddr under the contrib directory.

There are two dependencies found in the snapcpp environment: the libexcept library and the top cmake directory to run cmake. The easiest is still to get the entire environment and run snap-ubuntu-packages and build-snap scripts (under snapcpp/bin/). This compiles everything, including the libaddr library.

Documentation

The full technical documentation is available on the References page.

In most cases, you use the addr::parser class (libaddr/addr_parser.h) in order to transform a user string in an addr::addr or addr::range object.

The concerned function is the string_to_addr() function:

addr string_to_addr(
          std::string const & a
        , std::string const & default_address = std::string()
        , int default_port = -1
        , std::string const & protocol = std::string()
        , bool mask = false);

The usuage is pretty simple. You first have the input address to convert and then four additional parameter to define a set of defaults and whether a mask is acceptable in the input.

The default_address parameter is expected to be a direct IP address such as "127.0.0.1".

The default_port parameter is the port expected to be used by that specific tool. For example, were you to write an HTTP server, you would use 80. The default is -1 which is not a valid port number. It gives you a chance to know whether a port was defined or not.

The protocol parameter forces the protocol to something such as "tcp" or "udp". In most cases, it has no real effect except (in a few cases) for the default port.

The mask parameter determines whether a CIDR mask is allowed or not. We support any type of mask, although remember that official network specification now only support masks with ones on the left and zeroes on the right. Any other combinaison is not allowed. So we allow some backward compatibility. At some point we may add a function you can use to verify the mask validity.

There is an example where I convert a user supplied address but use defaults if the user did not supply valid parameters:

#include <libaddr/addr_parser.h>
...
addr::addr a(string_to_addr(argv[1], "0.0.0.0", 80, "tcp", false));
struct sockaddr_in6 in;
a.get_ipv6(&in);
bind(f_socket, &in, sizeof(in));

Here I show how to bind using the IPv6 address. The fact is that if the address represents an IPv4, it will bind() just as well. This is true for most functions that accept the AF_INET or AF_INET6 type of addresses.

Coverage Test Results

Access full page here.

The library has a test suite that covers 100% of the code, making it a little more certain that it does not include too many bugs. We try to run the tests each time we create a new version to ensure that it works as expected.

 

Snap! Websites
An Open Source CMS System in C++

Contact Us Directly