diff -Nru libaddr-1.0.17.0~xenial/cmake/CMakeLists.txt libaddr-1.0.18.0~xenial/cmake/CMakeLists.txt --- libaddr-1.0.17.0~xenial/cmake/CMakeLists.txt 2019-03-17 06:58:39.000000000 +0000 +++ libaddr-1.0.18.0~xenial/cmake/CMakeLists.txt 2019-09-02 19:47:43.000000000 +0000 @@ -1,7 +1,4 @@ -# File: cmake/CMakeLists.txt -# Object: Install necessary cmake modules -# -# Copyright: Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved +# Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved # # https://snapwebsites.org/project/libaddr # contact@m2osw.com @@ -23,13 +20,13 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# project(libaddr_cmake) install( FILES LibAddrConfig.cmake + DESTINATION share/cmake/LibAddr ) diff -Nru libaddr-1.0.17.0~xenial/CMakeLists.txt libaddr-1.0.18.0~xenial/CMakeLists.txt --- libaddr-1.0.17.0~xenial/CMakeLists.txt 2019-03-17 06:58:39.000000000 +0000 +++ libaddr-1.0.18.0~xenial/CMakeLists.txt 2019-09-02 20:50:57.000000000 +0000 @@ -1,8 +1,4 @@ -# -# File: CMakeLists.txt -# Object: Definitions to create the build environment with cmake -# -# Copyright: Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved +# Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved # # https://snapwebsites.org/project/libaddr # contact@m2osw.com @@ -24,11 +20,10 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# cmake_minimum_required(VERSION 3.0.0) -project(addr_library) +project(addr_project) enable_language(C) enable_language(CXX) @@ -38,14 +33,23 @@ # (must be included in a project even though it is not made # specific to that project) # -find_package( SnapCMakeModules REQUIRED ) -find_package( LibExcept REQUIRED ) -find_package( SnapDoxygen ) +find_package(SnapCMakeModules REQUIRED) +find_package(AdvGetOpt REQUIRED) +find_package(LibExcept REQUIRED) +find_package(LibUtf8 REQUIRED) +find_package(SnapDev REQUIRED) +find_package(SnapDoxygen ) SnapGetVersion( LIBADDR ${CMAKE_CURRENT_SOURCE_DIR} ) +include_directories( + ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR} +) + add_subdirectory( cmake ) -add_subdirectory( src ) +add_subdirectory( libaddr ) +add_subdirectory( tools ) add_subdirectory( doc ) add_subdirectory( tests ) diff -Nru libaddr-1.0.17.0~xenial/debian/changelog libaddr-1.0.18.0~xenial/debian/changelog --- libaddr-1.0.17.0~xenial/debian/changelog 2019-07-21 04:47:31.000000000 +0000 +++ libaddr-1.0.18.0~xenial/debian/changelog 2019-09-02 22:17:16.000000000 +0000 @@ -1,3 +1,20 @@ +libaddr (1.0.18.0~xenial) xenial; urgency=high + + * Fix: Default address and port are now used after testing for emptiness. + * Cleaned up the exceptions (name, use macros). + * Modernize the `mk` script. + * Renamed the test `unittest` (planned unification). + * Added the #include poison.h to all .cpp files. + * Started moving things around so we have the library and tools separated. + * Moved header files out of the sub-libaddr directory. + * Renamed the "src" directory as "libaddr". + * Actually implemented the validate_ip command line tool. + * Added new dependencies (AdvGetOpt, LibUtf8, SnapDev). + * Updated the dev/coverage script to work with the new folders. + * Avoid showing errno in one emit_error() when errno == 0. + + -- Alexis Wilke Mon, 2 Sep 2019 03:17:02 -0800 + libaddr (1.0.17.0~xenial) xenial; urgency=high * Upgraded tests to compile against snapcatch2 instead of catch1.x diff -Nru libaddr-1.0.17.0~xenial/debian/control libaddr-1.0.18.0~xenial/debian/control --- libaddr-1.0.17.0~xenial/debian/control 2019-07-21 03:57:02.000000000 +0000 +++ libaddr-1.0.18.0~xenial/debian/control 2019-09-02 22:14:52.000000000 +0000 @@ -1,13 +1,17 @@ Source: libaddr -Priority: optional +Priority: extra Maintainer: Alexis Wilke Build-Depends: cmake, debhelper, doxygen, graphviz, - libexcept-dev (>= 1.0.2.250~xenial), + libadvgetopt-dev (>= 2.0.1.0~xenial), + libboost-dev, + libexcept-dev (>= 1.1.4.0~xenial), + libutf8-dev (>= 1.0.6.0~xenial), snapcatch2 (>= 2.9.1.0~xenial), - snapcmakemodules (>= 1.0.35.3~xenial) + snapcmakemodules (>= 1.0.35.3~xenial), + snapdev (>= 1.1.3.0~xenial) Standards-Version: 3.9.4 Section: libs Homepage: https://snapwebsites.org/project/libaddr diff -Nru libaddr-1.0.17.0~xenial/dev/coverage libaddr-1.0.18.0~xenial/dev/coverage --- libaddr-1.0.17.0~xenial/dev/coverage 2019-05-19 07:07:43.000000000 +0000 +++ libaddr-1.0.18.0~xenial/dev/coverage 2019-09-02 20:40:23.000000000 +0000 @@ -2,7 +2,7 @@ # # Run all the libaddr tests with coverage # -# Copyright: Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved +# Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved # # https://snapwebsites.org/project/libaddr # contact@m2osw.com @@ -72,16 +72,16 @@ echo "***" echo "*** run (`date`)" echo "***" -if test `BUILD/tests/addr_test --version` != "$VERSION" +if test `BUILD/tests/unittest --version` != "$VERSION" then - echo "the version of addr_test (`BUILD/tests/addr_test --version`) is not equal to the project version ($VERSION)" + echo "the version of unittest (`BUILD/tests/unittest --version`) is not equal to the project version ($VERSION)" exit 1; fi # We test the pipe status on exit to detect whether the test failed echo "Start running the tests on `date`" >test_log.txt echo >>test_log.txt -BUILD/tests/addr_test --tcp-port 80 $TEST_NAME 2>&1 | tee -a test_log.txt; test ${PIPESTATUS[0]} -eq 0 +BUILD/tests/unittest --tcp-port 80 $TEST_NAME 2>&1 | tee -a test_log.txt; test ${PIPESTATUS[0]} -eq 0 echo >>test_log.txt echo "Finished running the tests on `date`" >>test_log.txt echo @@ -92,7 +92,7 @@ # Choose one of the following gcov commands mkdir -p gcov cd gcov -gcov -o ../BUILD/src/CMakeFiles/libaddr.dir/addr.cpp.gcno ../../../src/addr.cpp +gcov -o ../BUILD/libaddr/CMakeFiles/addr.cpp.gcno ../../../libaddr/addr.cpp cd .. @@ -111,7 +111,7 @@ # Statistics echo "libaddr $VERSION statistics" >html/statistics.html echo "

Statistics of the libaddr $VERSION code

" >>html/statistics.html
-cloc $SOURCE/src/ >>html/statistics.html
+cloc $SOURCE/libaddr/ >>html/statistics.html
 echo "

Statistics of the libaddr $VERSION tests

" >>html/statistics.html
 cloc $SOURCE/tests/ >>html/statistics.html
 #echo "

Statistics of libaddr scripts $VERSION

" >>html/statistics.html
diff -Nru libaddr-1.0.17.0~xenial/libaddr/addr.cpp libaddr-1.0.18.0~xenial/libaddr/addr.cpp
--- libaddr-1.0.17.0~xenial/libaddr/addr.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/addr.cpp	2019-09-02 20:19:25.000000000 +0000
@@ -0,0 +1,1400 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+/** \file
+ * \brief The implementation of the addr class.
+ *
+ * This file includes the implementation of the addr class. The one that
+ * deals with low level classes.
+ */
+
+// self
+//
+#include    "libaddr/addr.h"
+#include    "libaddr/addr_exception.h"
+
+
+// C++ library
+//
+#include    
+#include    
+
+
+// C library
+//
+#include    
+
+
+// last include
+//
+#include    
+
+
+
+/** \mainpage
+ * \brief libaddr, a C++ library to handle network IP addresses in IPv4 and IPv6.
+ *
+ * ### Introduction
+ *
+ * This library is used to parse strings of IP addresses to lists of
+ * binary IP addresses ready to be used by functions such as bind(),
+ * send(), recv(), etc.
+ *
+ * The library supports multiple addresses separated by commas and/or
+ * spaces, ports, and CIDR masks. It can check whether an address matches
+ * another taking the mask in account. It can sort IPs numerically. It
+ * can determine the type of an IP address (i.e. is it a local address,
+ * a private address, a public address?)
+ *
+ * The library also has a function to read IP addresses from your
+ * computer interfaces and return that list. Very practical to know
+ * whether an IP address represents your computer or not.
+ *
+ * ### Usage
+ *
+ * The library is composed of three main classes:
+ *
+ * \li addr
+ *
+ * The address class holds one address, a port, a protocol and a few
+ * other parts. This is what one uses to connect or listen with an
+ * address.
+ *
+ * The address is kept by addr in an IPv6 address structure.
+ *
+ * By default the CIDR of the address is all 1s (i.e. no masking, all
+ * bits considered important.) The mask is always 128 bits. If you are
+ * dealing with IPv4, make sure that the first 12 bytes are set to 255.
+ *
+ * The class also offers a set of functions to transform the binary
+ * address it is holding to a string.
+ *
+ * \li addr_range
+ *
+ * It is possible to define a range of addresses and ports. This class
+ * holds a 'from' address and a 'to' address. By default neither is
+ * defined. You have to call the set_from() and set_to() functions.
+ *
+ * The addr_range::vector_t is what the addr_parser returns after
+ * parsing a string representing one of more addresses.
+ *
+ * \note
+ * The range is functional, however, the parser does not yet support
+ * parsing range of addresses and ports.
+ *
+ * \li addr_parser
+ *
+ * The parser is used to transform a string to an address.
+ *
+ * It supports many variations of its input, which are handled by
+ * the 'allow' flags. The set_allow() and get_allow() functions can
+ * be used to tweak the parser in supporting such and such feature.
+ *
+ * By default, the input is expected to be an address and a port
+ * separated by a colon (i.e. `"1.2.3.4:1234"` in IPv4 and `"[::1]:1234"`
+ * in IPv6.)
+ *
+ * ### Parser
+ *
+ * The parser supports the following syntax (ranges are not yet supported
+ * and they do not appear in the following list):
+ *
+ * \code
+ *    start: address_list
+ *
+ *    address_list: address_cidr
+ *                | address_list address_list_separators address_cidr
+ *
+ *    address_list_separators: ' '
+ *                           | ','
+ *                           | address_list_separators address_list_separators
+ *
+ *    address_cidr: address_port
+ *                | address_port '/' cidr
+ *
+ *    address_port: address
+ *                | address ':' port
+ *
+ *    address: ipv4
+ *           | ipv6
+ *
+ *    cidr: decimal_number
+ *        | ipv4
+ *        | ipv6
+ *
+ *    ipv4: decimal_number '.' decimal_number '.' decimal_number '.' decimal_number
+ *
+ *    ipv6: '[' hexadecimal_number_list ']'
+ *
+ *    port: decimal_number
+ *
+ *    hexadecimal_number_list: hexadecimal_number
+ *                           | hexadecimal_number_list ':' hexadecimal_number
+ *
+ *    decimal_number: [0-9]+
+ *
+ *    hexadecimal_number: [0-9a-fA-F]+
+ * \endcode
+ *
+ * When accepting multiple addresses separated by commas or spaces, the
+ * number of commas and spaces separating two address is not significant.
+ * The input string can also start or end with commas and spaces. The
+ * following variable defines exactly two IP address:
+ *
+ * \code
+ *       addresses=  ,1.2.3.4,   ,5.6.7.8,,
+ * \endcode
+ *
+ * (note that the parser should not be passed the "addresses=" part.)
+ */
+
+
+/** \brief The libaddr classes are all defined in this namespace.
+ *
+ * The addr namespace includes all the addr classes.
+ */
+namespace addr
+{
+
+/*
+ * Various sytem address structures
+
+// Any address is 16 bytes or less
+struct sockaddr {
+   unsigned short    sa_family;    // address family, AF_xxx
+   char              sa_data[14];  // 14 bytes of protocol address
+};
+
+struct sockaddr_storage {
+    sa_family_t  ss_family;     // address family
+
+    // all this is padding, implementation specific, ignore it:
+    char      __ss_pad1[_SS_PAD1SIZE];
+    int64_t   __ss_align;
+    char      __ss_pad2[_SS_PAD2SIZE];
+};
+
+
+typedef uint32_t in_addr_t;   // or `__be32`
+struct in_addr {
+    in_addr_t        s_addr;
+};
+
+
+// IPv4
+struct sockaddr_in {
+    short            sin_family;   // e.g. AF_INET, AF_INET6
+    unsigned short   sin_port;     // e.g. htons(3490)
+    struct in_addr   sin_addr;     // see struct in_addr, below
+    char             sin_zero[8];  // zero this if you want to
+};
+
+
+// IPv6
+struct sockaddr_in6 {
+    u_int16_t       sin6_family;   // address family, AF_INET6
+    u_int16_t       sin6_port;     // port number, Network Byte Order
+    u_int32_t       sin6_flowinfo; // IPv6 flow information
+    struct in6_addr sin6_addr;     // IPv6 address
+    u_int32_t       sin6_scope_id; // Scope ID
+};
+
+struct in6_addr
+  {
+    union
+      {
+	uint8_t	__u6_addr8[16];
+#ifdef __USE_MISC
+	uint16_t __u6_addr16[8];
+	uint32_t __u6_addr32[4];
+#endif
+      } __in6_u;
+#define s6_addr			__in6_u.__u6_addr8
+#ifdef __USE_MISC
+# define s6_addr16		__in6_u.__u6_addr16
+# define s6_addr32		__in6_u.__u6_addr32
+#endif
+  };
+
+
+*/
+
+
+
+
+
+/** \brief Create an addr object that represents an ANY address.
+ *
+ * This function initializes the addr object with the ANY address.
+ * The port is set to 0 and the protocol to TCP.
+ *
+ * It is strongly suggested that you change those parameters
+ * before really using this address since a port of zero and
+ * the protocol may be wrong.
+ */
+addr::addr()
+{
+    // keep default protocol (TCP)
+}
+
+
+/** \brief Create an addr object from a binary IPv4 address.
+ *
+ * This function initializes this addr object with the specified IPv4
+ * address. The is_ipv4() function will return true.
+ *
+ * \param[in] in  The binary IPv4 address.
+ */
+addr::addr(struct sockaddr_in const & in)
+{
+    set_ipv4(in);
+    // keep default protocol (TCP)
+}
+
+
+/** \brief Create an addr object from a binary IPv6 address.
+ *
+ * This function initializes this addr object with the specified IPv6
+ * address. The is_ipv4() function will return false.
+ *
+ * \param[in] in6  The binary IPv6 address.
+ */
+addr::addr(struct sockaddr_in6 const & in6)
+{
+    set_ipv6(in6);
+    // keep default protocol (TCP)
+}
+
+
+/** \brief Save an IPv4 in this addr object.
+ *
+ * This function saves the specified IPv4 in this addr object.
+ *
+ * Since we save the data in an IPv6 structure, it is kept in
+ * the addr as an IPv4 mapped in an IPv6 address. It can still
+ * be retrieved right back as an IPv4 with the get_ipv4() function.
+ *
+ * \param[in] in  The IPv4 address to save in this addr object.
+ */
+void addr::set_ipv4(struct sockaddr_in const & in)
+{
+    if(in.sin_family != AF_INET)
+    {
+        // although we convert the IPv4 to an IPv6 below, we really only
+        // support AF_INET on entry
+        //
+        throw addr_invalid_argument("addr::set_ipv4(): the input address does not represent an IPv4 address (family is not AF_INET).");
+    }
+
+    // reset the address first
+    memset(&f_address, 0, sizeof(f_address));
+
+    // then transform the IPv4 to an IPv6
+    //
+    // Note: this is not an IPv6 per se, it is an IPv4 mapped within an
+    //       IPv6 and your network stack needs to support IPv4 anyway
+    //       in order to use that IP...
+    //
+    f_address.sin6_family = AF_INET6;
+    f_address.sin6_port = in.sin_port;
+    f_address.sin6_addr.s6_addr16[5] = 0xFFFF;
+    f_address.sin6_addr.s6_addr32[3] = in.sin_addr.s_addr;
+
+    address_changed();
+}
+
+
+/** \brief Set the port of this address.
+ *
+ * This function changes the port of this address to \p port.
+ *
+ * \exception addr_invalid_argument_exception
+ * This exception is raised whenever the \p port parameter is set to
+ * an invalid number (negative or larger than 65535.)
+ *
+ * \param[in] port  The new port to save in this address.
+ */
+void addr::set_port(int port)
+{
+    if(port > 65535 
+    || port < 0)
+    {
+        throw addr_invalid_argument("port to set_port() cannot be out of the allowed range [0..65535].");
+    }
+    f_address.sin6_port = htons(port);
+}
+
+
+/** \brief Change the protocol using a string.
+ *
+ * This function is used to change the current protocol defined in
+ * this addr object.
+ *
+ * \exception addr_invalid_argument_exception
+ * We currently support "tcp", "udp", and "ip". Any other protocol
+ * name generates this exception.
+ *
+ * \param[in] protocol  The name of the protocol ("tcp", "udp", or "ip")
+ */
+void addr::set_protocol(char const * protocol)
+{
+    if(protocol == nullptr)
+    {
+        throw addr_invalid_argument("protocol pointer to set_protocol() cannot be a nullptr.");
+    }
+
+    if(strcmp(protocol, "ip") == 0)
+    {
+        f_protocol = IPPROTO_IP;
+    }
+    else if(strcmp(protocol, "tcp") == 0)
+    {
+        f_protocol = IPPROTO_TCP;
+    }
+    else if(strcmp(protocol, "udp") == 0)
+    {
+        f_protocol = IPPROTO_UDP;
+    }
+    else
+    {
+        throw addr_invalid_argument(
+                          std::string("unknown protocol \"")
+                        + protocol
+                        + "\", expected \"tcp\" or \"udp\" (string).");
+    }
+
+    address_changed();
+}
+
+
+/** \brief Set the protocol numerically.
+ *
+ * This function sets the protocol from a number instead of a name.
+ *
+ * Note that we only support IPPROTO_TCP and IPPROTO_UDP for now.
+ * Any other protocol will make this function raise an exception.
+ *
+ * \todo
+ * We may want to support any protocol number at this level. If your
+ * application is limited then it should verify the protocol and
+ * make sure it supports it before using this address. At the same
+ * time, the IP protocol is pretty much locked up with just TCP
+ * and UDP these days (there is the IP protocol, but that's not
+ * useful at our level.)
+ *
+ * \exception addr_invalid_argument_exception
+ * This exception is raised if the specified protocol is not currently
+ * supported by the addr implementation.
+ *
+ * \param[in] protocol  The new numeric protocol.
+ */
+void addr::set_protocol(int protocol)
+{
+    switch(protocol)
+    {
+    case IPPROTO_IP:
+    case IPPROTO_TCP:
+    case IPPROTO_UDP:
+        f_protocol = protocol;
+        break;
+
+    default:
+        throw addr_invalid_argument(
+                          "unknown protocol number "
+                        + std::to_string(protocol)
+                        + ", expected \"tcp\" ("
+                        + std::to_string(static_cast(IPPROTO_TCP))
+                        + ") or \"udp\" ("
+                        + std::to_string(static_cast(IPPROTO_UDP))
+                        + ") (numeric).");
+
+    }
+}
+
+
+/** \brief Set the mask.
+ *
+ * The input mask must be exactly 16 bytes. If you are dealing with an
+ * IPv4, make sure the first 12 bytes are 255.
+ *
+ * \param[in] mask  The mask to save in this address.
+ */
+void addr::set_mask(uint8_t const * mask)
+{
+    memcpy(f_mask, mask, sizeof(f_mask));
+}
+
+
+/** \brief Apply the mask to the IP address.
+ *
+ * This function applies the mask to this address IP address. This means
+ * the bits that are 0 in the mask will also be 0 in the address once
+ * the function returns.
+ *
+ * This should be called if you are trying to canonicalize an IP/mask.
+ */
+void addr::apply_mask()
+{
+    for(int idx(0); idx < 16; ++idx)
+    {
+        f_address.sin6_addr.s6_addr[idx] &= f_mask[idx];
+    }
+}
+
+
+/** \brief Get the mask.
+ *
+ * The output buffer for the mask must be at least 16 bytes. If you are
+ * dealing with an IPv4, all the bytes are expected to be 255 except
+ * the bottom 4 bytes (offset 12, 13, 14, 15).
+ *
+ * \param[out] mask  The buffer where the mask gets copied.
+ */
+void addr::get_mask(uint8_t * mask) const
+{
+    memcpy(mask, f_mask, sizeof(f_mask));
+}
+
+
+/** \brief Check whether this address represents the ANY address.
+ *
+ * The IPv4 and IPv6 have an ANY address also called the default address.
+ * This function returns true if this address represents the ANY address.
+ *
+ * The any address is represented by `"0.0.0.0"` in IPv4 and `"::"` in
+ * IPv6. (i.e. all zeroes)
+ *
+ * \note
+ * You can also determine this by calling the get_network_type() function
+ * and compare the result against `network_type_t::NETWORK_TYPE_ANY`.
+ *
+ * \return true if this addr represents the any address.
+ */
+bool addr::is_default() const
+{
+    // this is for IPv4 or IPv6
+    //
+    return f_address.sin6_addr.s6_addr32[0] == 0
+        && f_address.sin6_addr.s6_addr32[1] == 0
+        && f_address.sin6_addr.s6_addr16[4] == 0
+        && (f_address.sin6_addr.s6_addr16[5] == 0 || f_address.sin6_addr.s6_addr16[5] == 0xFFFF)
+        && f_address.sin6_addr.s6_addr32[3] == 0;
+}
+
+
+/** \brief Check whether this address represents an IPv4 address.
+ *
+ * The IPv6 format supports embedding IPv4 addresses. This function
+ * returns true if the embedded address is an IPv4. When this function
+ * returns true, the get_ipv4() can be called. Otherwise, the get_ipv4()
+ * function throws an exception.
+ *
+ * \return true if this address represents an IPv4 address.
+ */
+bool addr::is_ipv4() const
+{
+    return f_address.sin6_addr.s6_addr32[0] == 0
+        && f_address.sin6_addr.s6_addr32[1] == 0
+        && f_address.sin6_addr.s6_addr16[4] == 0
+        && f_address.sin6_addr.s6_addr16[5] == 0xFFFF;
+}
+
+
+/** \brief Retreive the IPv4 address.
+ *
+ * This function can be used to retrieve the IPv4 address of this addr
+ * object. If the address is not an IPv4, then the function throws.
+ *
+ * \exception addr_invalid_structure_exception
+ * This exception is raised if the address is not an IPv4 address.
+ *
+ * \param[out] in  The structure where the IPv4 Internet address gets saved.
+ */
+void addr::get_ipv4(struct sockaddr_in & in) const
+{
+    if(is_ipv4())
+    {
+        // this is an IPv4 mapped in an IPv6, "unmap" that IP
+        //
+        memset(&in, 0, sizeof(in));
+        in.sin_family = AF_INET;
+        in.sin_port = f_address.sin6_port;
+        in.sin_addr.s_addr = f_address.sin6_addr.s6_addr32[3];
+        return;
+    }
+
+    throw addr_invalid_state("Not an IPv4 compatible address.");
+}
+
+
+/** \brief Save the specified IPv6 address in this addr object.
+ *
+ * This function saves the specified IPv6 address in this addr object.
+ * The function does not check the validity of the address. It is
+ * expected to be valid.
+ *
+ * The address may be an embedded IPv4 address.
+ *
+ * \param[in] in6  The source IPv6 to save in the addr object.
+ */
+void addr::set_ipv6(struct sockaddr_in6 const & in6)
+{
+    if(in6.sin6_family != AF_INET6)
+    {
+        throw addr_invalid_argument("addr::set_ipv6(): the input address does not represent an IPv6 address (family is not AF_INET6).");
+    }
+    memcpy(&f_address, &in6, sizeof(in6));
+
+    address_changed();
+}
+
+
+/** \brief Retrieve a copy of this addr IP address.
+ *
+ * This function returns the current IP address saved in this
+ * addr object. The IP may represent an IPv4 address in which
+ * case the is_ipv4() returns true.
+ *
+ * \param[out] in6  The structure where the address gets saved.
+ */
+void addr::get_ipv6(struct sockaddr_in6 & in6) const
+{
+    memcpy(&in6, &f_address, sizeof(in6));
+}
+
+
+/** \brief Retrive the IPv4 as a string.
+ *
+ * This function returns a string representing the IP address
+ * defined in this addr object.
+ *
+ * The \p mode parameter defines what gets output.
+ *
+ * \li ip_string_t::IP_STRING_ONLY -- only the IP address
+ * \li ip_string_t::IP_STRING_PORT -- the IP and port
+ * \li ip_string_t::IP_STRING_MASK -- the IP and mask
+ * \li ip_string_t::IP_STRING_ALL -- the IP, port, and mask
+ *
+ * The ip_string_t::IP_STRING_BRACKET is viewed as
+ * ip_string_t::IP_STRING_ONLY.
+ *
+ * The ip_string_t::IP_STRING_BRACKET_MASK is viewed as
+ * ip_string_t::IP_STRING_MASK.
+ *
+ * \exception addr_invalid_state_exception
+ * If the addr object does not currently represent an IPv4 then
+ * this exception is raised.
+ *
+ * \param[in] mode  How the output string is to be built.
+ */
+std::string addr::to_ipv4_string(string_ip_t mode) const
+{
+    if(is_ipv4())
+    {
+        // this is an IPv4 mapped in an IPv6, "unmap" that IP
+        // so the inet_ntop() can correctly generate an output IP
+        //
+        struct in_addr in;
+        memset(&in, 0, sizeof(in));
+        in.s_addr = f_address.sin6_addr.s6_addr32[3];
+        char buf[INET_ADDRSTRLEN + 1];
+        if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
+        {
+            if(mode != string_ip_t::STRING_IP_ONLY)
+            {
+                std::stringstream result;
+                result << buf;
+                if(mode == string_ip_t::STRING_IP_PORT
+                || mode == string_ip_t::STRING_IP_ALL)
+                {
+                    result << ":";
+                    result << ntohs(f_address.sin6_port);
+                }
+                if(mode == string_ip_t::STRING_IP_MASK
+                || mode == string_ip_t::STRING_IP_BRACKETS_MASK
+                || mode == string_ip_t::STRING_IP_ALL)
+                {
+                    memset(&in, 0, sizeof(in));
+                    in.s_addr = htonl((f_mask[12] << 24) | (f_mask[13] << 16) | (f_mask[14] << 8) | f_mask[15]);
+                    if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
+                    {
+                        result << "/";
+                        result << buf; // TODO: convert to simple number if possible
+                    }
+                }
+                return result.str();
+            }
+            return std::string(buf);
+        }
+        // IPv4 should never fail converting the address unless the
+        // buffer was too small...
+    }
+
+    throw addr_invalid_state("Not an IPv4 compatible address.");
+}
+
+
+/** \brief Convert the addr object to a string.
+ *
+ * This function converts the addr object to a canonicalized string.
+ * This can be used to compare two IPv6 together as strings, although
+ * it is probably better to compare them using the < and == operators.
+ *
+ * By default the function returns with the IPv6 address defined
+ * between square bracket, so the output of this function can be
+ * used as the input of the set_addr_port() function. You may
+ * also request the address without the brackets.
+ *
+ * \exception addr_invalid_state_exception
+ * If the binary IP address cannot be converted to ASCII, this exception
+ * is raised.
+ *
+ * \param[in] mode  How the output string is to be built.
+ *
+ * \return The addr object converted to an IPv6 address.
+ */
+std::string addr::to_ipv6_string(string_ip_t mode) const
+{
+    char buf[INET6_ADDRSTRLEN + 1];
+    if(inet_ntop(AF_INET6, &f_address.sin6_addr, buf, sizeof(buf)) != nullptr)
+    {
+        bool const include_brackets(mode == string_ip_t::STRING_IP_BRACKETS
+                                 || mode == string_ip_t::STRING_IP_BRACKETS_MASK
+                                 || mode == string_ip_t::STRING_IP_PORT // port requires us to add brackets
+                                 || mode == string_ip_t::STRING_IP_ALL);
+
+        std::stringstream result;
+
+        // always insert the IP, even if ANY or "BROADCAST"
+        //
+        if(include_brackets)
+        {
+            result << "[";
+        }
+        result << buf;
+        if(include_brackets)
+        {
+            result << "]";
+        }
+
+        // got a port?
+        //
+        if(mode == string_ip_t::STRING_IP_PORT
+        || mode == string_ip_t::STRING_IP_ALL)
+        {
+            result << ":";
+            result << ntohs(f_address.sin6_port);
+        }
+
+        // got a mask?
+        //
+        if(mode == string_ip_t::STRING_IP_MASK
+        || mode == string_ip_t::STRING_IP_BRACKETS_MASK
+        || mode == string_ip_t::STRING_IP_ALL)
+        {
+            if(inet_ntop(AF_INET6, f_mask, buf, sizeof(buf)) != nullptr)
+            {
+                result << "/";
+                if(include_brackets)
+                {
+                    result << "[";
+                }
+                result << buf; // TODO: convert to simple number if possible
+                if(include_brackets)
+                {
+                    result << "]";
+                }
+            }
+        }
+
+        return result.str();
+    }
+
+    throw addr_invalid_state("The address from this addr could not be converted to a valid canonicalized IPv6 address.");  // LCOV_EXCL_LINE
+}
+
+
+/** \brief Return the address as IPv4 or IPv6.
+ *
+ * Depending on whether the address represents an IPv4 or an IPv6,
+ * this function returns the corresponding address. Since the format
+ * of both types of addresses can always be distinguished, it poses
+ * no concerns.
+ *
+ * \exception 
+ * If include_brackets is false and include_port is true, this
+ * exception is raised because we cannot furfill the request.
+ *
+ * \param[in] mode  How the output string is to be built.
+ *
+ * \return The addr object converted to an IPv4 or an IPv6 address.
+ */
+std::string addr::to_ipv4or6_string(string_ip_t mode) const
+{
+    return is_ipv4() ? to_ipv4_string(mode)
+                     : to_ipv6_string(mode);
+}
+
+
+/** \brief Determine the type of network this IP represents.
+ *
+ * The IP address may represent various type of networks. This
+ * function returns that type.
+ *
+ * The function checks the address either as IPv4 when is_ipv4()
+ * returns true, otherwise as IPv6.
+ *
+ * See:
+ *
+ * \li https://en.wikipedia.org/wiki/Reserved_IP_addresses
+ * \li https://tools.ietf.org/html/rfc3330
+ * \li https://tools.ietf.org/html/rfc5735 (IPv4)
+ * \li https://tools.ietf.org/html/rfc5156 (IPv6)
+ *
+ * \return One of the possible network types as defined in the
+ *         network_type_t enumeration.
+ */
+addr::network_type_t addr::get_network_type() const
+{
+    if(f_private_network_defined == network_type_t::NETWORK_TYPE_UNDEFINED)
+    {
+        f_private_network_defined = network_type_t::NETWORK_TYPE_UNKNOWN;
+
+        if(is_ipv4())
+        {
+            // get the address in host order
+            //
+            // we can use a simple mask + compare to know whether it is
+            // this or that once in host order
+            //
+            uint32_t const host_ip(ntohl(f_address.sin6_addr.s6_addr32[3]));
+
+            if((host_ip & 0xFF000000) == 0x0A000000         // 10.0.0.0/8
+            || (host_ip & 0xFFF00000) == 0xAC100000         // 172.16.0.0/12
+            || (host_ip & 0xFFFF0000) == 0xC0A80000)        // 192.168.0.0/16
+            {
+                f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
+            }
+            else if((host_ip & 0xFFC00000) == 0x64400000)   // 100.64.0.0/10
+            {
+                f_private_network_defined = network_type_t::NETWORK_TYPE_CARRIER;
+            }
+            else if((host_ip & 0xFFFF0000) == 0xA9FE0000)   // 169.254.0.0/16
+            {
+                f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
+            }
+            else if((host_ip & 0xF0000000) == 0xE0000000)   // 224.0.0.0/4
+            {
+                // there are many sub-groups on this one which are probably
+                // still in use...
+                //
+                f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
+            }
+            else if((host_ip & 0xFF000000) == 0x7F000000)   // 127.0.0.0/8
+            {
+                f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK; // i.e. localhost
+            }
+            else if(host_ip == 0x00000000)
+            {
+                f_private_network_defined = network_type_t::NETWORK_TYPE_ANY; // i.e. 0.0.0.0
+            }
+        }
+        else //if(is_ipv6()) -- if not IPv4, we have an IPv6
+        {
+            // for IPv6 it was simplified by using a prefix for
+            // all types; really way easier than IPv4
+            //
+            if(f_address.sin6_addr.s6_addr32[0] == 0      // ::
+            && f_address.sin6_addr.s6_addr32[1] == 0
+            && f_address.sin6_addr.s6_addr32[2] == 0
+            && f_address.sin6_addr.s6_addr32[3] == 0)
+            {
+                // this is the "any" IP address
+                f_private_network_defined = network_type_t::NETWORK_TYPE_ANY;
+            }
+            else
+            {
+                uint16_t const prefix(ntohs(f_address.sin6_addr.s6_addr16[0]));
+
+                if((prefix & 0xFF00) == 0xFD00)                 // fd00::/8
+                {
+                    f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
+                }
+                else if((prefix & 0xFFC0) == 0xFE80    // fe80::/10
+                     || (prefix & 0xFF0F) == 0xFF02)   // ffx2::/16
+                {
+                    f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
+                }
+                else if((prefix & 0xFF0F) == 0xFF01    // ffx1::/16
+                     || (f_address.sin6_addr.s6_addr32[0] == 0      // ::1
+                      && f_address.sin6_addr.s6_addr32[1] == 0
+                      && f_address.sin6_addr.s6_addr32[2] == 0
+                      && f_address.sin6_addr.s6_addr16[6] == 0
+                      && f_address.sin6_addr.s6_addr16[7] == htons(1)))
+                {
+                    f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK;
+                }
+                else if((prefix & 0xFF00) == 0xFF00)   // ff00::/8
+                {
+                    // this one must be after the link-local and loopback networks
+                    f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
+                }
+            }
+        }
+    }
+
+    return f_private_network_defined;
+}
+
+
+/** \brief Get the network type string
+ *
+ * Translate the network type into a string, which can be really useful
+ * to log that information.
+ *
+ * Note that PUBLIC is the same as UNKNOWN, this function returns
+ * "Unknown" in that case, though.
+ *
+ * \return The string representing the type of network.
+ */
+std::string addr::get_network_type_string() const
+{
+    std::string name;
+    switch( get_network_type() )
+    {
+    case addr::network_type_t::NETWORK_TYPE_UNDEFINED  : name = "Undefined";  break; // LCOV_EXCL_LINE -- get_network_type() defines it...
+    case addr::network_type_t::NETWORK_TYPE_PRIVATE    : name = "Private";    break;
+    case addr::network_type_t::NETWORK_TYPE_CARRIER    : name = "Carrier";    break;
+    case addr::network_type_t::NETWORK_TYPE_LINK_LOCAL : name = "Local Link"; break;
+    case addr::network_type_t::NETWORK_TYPE_MULTICAST  : name = "Multicast";  break;
+    case addr::network_type_t::NETWORK_TYPE_LOOPBACK   : name = "Loopback";   break;
+    case addr::network_type_t::NETWORK_TYPE_ANY        : name = "Any";        break;
+    case addr::network_type_t::NETWORK_TYPE_UNKNOWN    : name = "Unknown";    break; // == NETWORK_TYPE_PUBLIC
+    }
+    return name;
+}
+
+
+/** \brief Create a socket from the IP address held by this addr object.
+ *
+ * This function creates a socket that corresponds to the addr object
+ * definitions, it takes the protocol and family information in account.
+ *
+ * The flags can be used to add one or more of the following flags:
+ *
+ * \li SOCKET_FLAG_NONBLOCK -- create socket as non-block
+ * \li SOCKET_FLAG_CLOEXEC -- close socket on an execv()
+ * \li SOCKET_FLAG_REUSE -- for TCP socket, mark the address as immediately
+ * reusable, ignored for UDP; only useful for server (bind + listen after
+ * this call)
+ *
+ * \note
+ * The IP protocol is viewed as TCP in this function.
+ *
+ * \warning
+ * This class does not hold the socket created by this function.
+ *
+ * \todo
+ * Move this to our eventdispatcher once we create that separate library.
+ * Probably within a form of low level socket class.
+ *
+ * \param[in] flags  A set of socket flags to use when creating the socket.
+ * \param[in] reuse_address  Set the reuse address flag.
+ *
+ * \return The socket file descriptor or -1 on errors.
+ */
+int addr::create_socket(socket_flag_t flags) const
+{
+    int const sock_flags(
+              ((flags & SOCKET_FLAG_CLOEXEC)  != 0 ? SOCK_CLOEXEC  : 0)
+            | ((flags & SOCKET_FLAG_NONBLOCK) != 0 ? SOCK_NONBLOCK : 0));
+    int const family(is_ipv4() ? AF_INET : AF_INET6);
+
+    switch(f_protocol)
+    {
+    case IPPROTO_IP: // interpret as TCP...
+    case IPPROTO_TCP:
+        {
+            int s(socket(family, SOCK_STREAM | sock_flags, IPPROTO_TCP));
+
+            if(s >= 0
+            && (flags & SOCKET_FLAG_REUSE) != 0)
+            {
+                // set the "reuse that address immediately" flag, we totally
+                // ignore errors on that one
+                //
+                int optval(1);
+                socklen_t const optlen(sizeof(optval));
+                static_cast(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, optlen));
+            }
+            return s;
+        }
+
+    case IPPROTO_UDP:
+        return socket(family, SOCK_DGRAM | sock_flags, IPPROTO_UDP);
+
+    default:
+        // this should never happen since we control the f_protocol field
+        //
+        return -1;      // LCOV_EXCL_LINE
+
+    }
+}
+
+
+/** \brief Connect the specified socket to this IP address.
+ *
+ * When you create a TCP client, you can connect to a server. This
+ * is done by using the connect() function which makes use of the
+ * address to connect to the server.
+ *
+ * This function makes sure to select the correct connect() function
+ * depending on whether this IP address is an IPv4 or an IPv6 address
+ * (although we could always try with the IPv6 structure, it may or
+ * may not work properly on all systems, so for now we use the
+ * distinction.)
+ *
+ * \todo
+ * Move this to our eventdispatcher once we create that separate library.
+ * Probably within a form of low level socket class.
+ *
+ * \param[in] s  The socket to connect to the address.
+ *
+ * \return 0 if the bind() succeeded, -1 on errors
+ */
+int addr::connect(int s) const
+{
+    // only TCP can connect, UDP binds and sends only
+    //
+    switch(f_protocol)
+    {
+    case IPPROTO_IP: // interpret as TCP...
+    case IPPROTO_TCP:
+        if(is_ipv4())
+        {
+            // this would most certainly work using the IPv6 address
+            // as in the else part, but to be sure, we use the IPv4
+            // as specified in the address (there could be other reasons
+            // than just your OS for this to fail if using IPv6.)
+            //
+            // IMPORTANT NOTE: also the family is used in the socket()
+            //                 call above and must match the address here...
+            //
+            sockaddr_in ipv4;
+            get_ipv4(ipv4);
+            return ::connect(s, reinterpret_cast(&ipv4), sizeof(ipv4));
+        }
+        else
+        {
+            return ::connect(s, reinterpret_cast(&f_address), sizeof(struct sockaddr_in6));
+        }
+        break;
+
+    }
+
+    return -1;
+}
+
+
+/** \brief Create a server with this socket listening on this IP address.
+ *
+ * This function will bind the socket \p s to the address defined in
+ * this addr object. This creates a server listening on that IP address.
+ *
+ * If the IP address is 127.0.0.1, then only local processes can connect
+ * to that server. If the IP address is 0.0.0.0, then anyone can connect
+ * to the server.
+ *
+ * This function works for TCP and UDP servers.
+ *
+ * If the IP address represents an IPv4 addressm then the bind() is done
+ * with an IPv4 address and not the IPv6 as it is stored.
+ *
+ * \todo
+ * Move this to our eventdispatcher once we create that separate library.
+ * Probably within a form of low level socket class.
+ *
+ * \param[in] s  The socket to bind to this address.
+ *
+ * \return 0 if the bind() succeeded, -1 on errors
+ */
+int addr::bind(int s) const
+{
+    if(is_ipv4())
+    {
+        sockaddr_in ipv4;
+        get_ipv4(ipv4);
+        return ::bind(s, reinterpret_cast(&ipv4), sizeof(ipv4));
+    }
+    else
+    {
+        return ::bind(s, reinterpret_cast(&f_address), sizeof(struct sockaddr_in6));
+    }
+}
+
+
+/** \brief Initializes this addr object from a socket information.
+ *
+ * When you connect to a server or a clients connect to your server, the
+ * socket defines two IP addresses and ports: one on your side and one on
+ * the other side.
+ *
+ * The other side is called the _peer name_.
+ *
+ * You side is called the _socket name_ (i.e. the IP address of your computer,
+ * representing the interface used to perform that connection.)
+ *
+ * If you call this function with \p peer set to false then you get the
+ * address and port from your side. If you set \p peer to true,
+ * you get the other side address and port details.
+ *
+ * \todo
+ * Move this to our eventdispatcher once we create that separate library.
+ * Probably within a form of low level socket class.
+ *
+ * \param[in] s  The socket from which you want to retrieve peer information.
+ * \param[in] peer  Whether to retrieve the peer or socket name.
+ */
+void addr::set_from_socket(int s, bool peer)
+{
+    // make sure the socket is defined and well
+    //
+    if(s < 0)
+    {
+        throw addr_invalid_argument("addr::set_from_socket(): the socket cannot be a negative number.");
+    }
+
+    struct sockaddr_storage address = sockaddr_storage();
+    socklen_t length(sizeof(address));
+    int r;
+    if(peer)
+    {
+        // this retrieves the information from the other side
+        //
+        r = getpeername(s, reinterpret_cast(&address), &length);
+    }
+    else
+    {
+        // retrieve the local socket information
+        //
+        r = getsockname(s, reinterpret_cast(&address), &length);
+    }
+    if(r != 0)
+    {
+        int const e(errno);
+        throw addr_io_error(
+                  std::string("addr::set_from_socket(): ")
+                + (peer ? "getpeername()" : "getsockname()")
+                + " failed to retrieve IP address details (errno: "
+                + std::to_string(e)
+                + ", "
+                + strerror(e)
+                + ").");
+    }
+
+    switch(address.ss_family)
+    {
+    case AF_INET:
+        set_ipv4(reinterpret_cast(address));
+        break;
+
+    case AF_INET6:
+        set_ipv6(reinterpret_cast(address));
+        break;
+
+    default:
+        throw addr_invalid_state(
+                  std::string("addr::set_from_socket(): ")
+                + (peer ? "getpeername()" : "getsockname()")
+                + " returned a type of address, which is not understood, i.e. not AF_INET or AF_INET6.");
+
+    }
+}
+
+
+/** \brief Transform the IP into a domain name.
+ *
+ * This function transforms the IP address in this `addr` object in a
+ * name such as "snap.website".
+ *
+ * \note
+ * The function does not cache the result because it is rarely used (at least
+ * at this time). So you should cache the result and avoid calling this
+ * function more than once as the process can be very slow.
+ *
+ * \todo
+ * Speed enhancement can be achieved by using getaddrinfo_a(). That would
+ * work with a vector of addr objects.
+ *
+ * \return The domain name. If not available, an empty string.
+ */
+std::string addr::get_name() const
+{
+    char host[NI_MAXHOST];
+
+    int flags(NI_NAMEREQD);
+    if(f_protocol == IPPROTO_UDP)
+    {
+        flags |= NI_DGRAM;
+    }
+
+    // TODO: test with the NI_IDN* flags and make sure we know what we get
+    //       (i.e. we want UTF-8 as a result)
+    //
+    int const r(getnameinfo(reinterpret_cast(&f_address), sizeof(f_address), host, sizeof(host), nullptr, 0, flags));
+
+    // return value is 0, then it worked
+    //
+    return r == 0 ? host : std::string();
+}
+
+
+/** \brief Transform the port into a service name.
+ *
+ * This function transforms the port in this `addr` object in a
+ * name such as "http".
+ *
+ * \note
+ * The function does not cache the result because it is rarely used (at least
+ * at this time). So you should cache the result and avoid calling this
+ * function more than once as the process is somewhat slow.
+ *
+ * \warning
+ * The getnameinfo() will return a string with a number if it does not
+ * know the server (i.e. this is the equivalent to std::to_string() of
+ * the port.) For port 0, the function always returns an empty string.
+ *
+ * \return The service name. If not available, an empty string.
+ */
+std::string addr::get_service() const
+{
+    if(f_address.sin6_port == 0)
+    {
+        return std::string();
+    }
+
+    char service[NI_MAXSERV];
+
+    int flags(NI_NAMEREQD);
+    if(f_protocol == IPPROTO_UDP)
+    {
+        flags |= NI_DGRAM;
+    }
+    int const r(getnameinfo(reinterpret_cast(&f_address), sizeof(f_address), nullptr, 0, service, sizeof(service), flags));
+
+    // return value is 0, then it worked
+    //
+    return r == 0 ? service
+                  : std::string();
+}
+
+
+/** \brief Retrieve the port.
+ *
+ * This function retrieves the port of the IP address in host order.
+ *
+ * \return The port defined along this address.
+ */
+int addr::get_port() const
+{
+    return ntohs(f_address.sin6_port);
+}
+
+
+/** \brief Retrieve the protocol.
+ *
+ * This function retrieves the protocol as specified on the
+ * set_addr_port() function or corresponding constructor.
+ *
+ * You may change the protocol with the set_protocol() function.
+ *
+ * \return protocol such as IPPROTO_TCP or IPPROTO_UDP.
+ */
+int addr::get_protocol() const
+{
+    return f_protocol;
+}
+
+
+/** \brief Check whether an IP matches a CIDR.
+ *
+ * When an IP address is defined along a mask, it can match a set of
+ * other IP addresses. This function can be used to see whether
+ * \p ip matches \p this IP address and mask.
+ *
+ * So in other words, the mask of `this` addr object is used to mask
+ * both, `this` and `p` before comparing the masked result.
+ *
+ * \warning
+ * This function only checks the IP address. It totally ignores the
+ * port, family, protocol and other peripheral details.
+ *
+ * \param[in] ip  The address to match against this IP/mask CIDR.
+ *
+ * \return true if \p ip is a match.
+ */
+bool addr::match(addr const & ip) const
+{
+    for(int idx(0); idx < 16; ++idx)
+    {
+        if((f_address.sin6_addr.s6_addr[idx] & f_mask[idx]) != (ip.f_address.sin6_addr.s6_addr[idx] & f_mask[idx]))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/** \brief Check whether two addresses are equal.
+ *
+ * This function compares the left hand side (this) and the right
+ * hand side (rhs) for equality. If both represent the same IP
+ * address, then the function returns true.
+ *
+ * \warning
+ * The function only compares the address itself. The family, port,
+ * flow info, scope identifier, protocol are all ignored.
+ *
+ * \return true if \p this is equal to \p rhs.
+ */
+bool addr::operator == (addr const & rhs) const
+{
+    return f_address.sin6_addr == rhs.f_address.sin6_addr;
+}
+
+
+/** \brief Check whether two addresses are not equal.
+ *
+ * This function compares the left hand side (this) and the right
+ * hand side (rhs) for inequality. If both represent the same IP
+ * address, then the function returns false.
+ *
+ * \warning
+ * The function only compares the address itself. The family, port,
+ * flow info, scope identifier, protocol are all ignored.
+ *
+ * \return true if \p this is not equal to \p rhs.
+ */
+bool addr::operator != (addr const & rhs) const
+{
+    return f_address.sin6_addr != rhs.f_address.sin6_addr;
+}
+
+
+/** \brief Compare two addresses to know which one is smaller.
+ *
+ * This function compares the left hand side (this) and the right
+ * hand side (rhs) to know which one is the smallest. If both
+ * are equal or the left hand side is larger than the right hand
+ * side, then it returns false, otherwise it returns true.
+ *
+ * \warning
+ * The function only compares the address itself. The family, port,
+ * flow info, scope identifier, protocol are all ignored.
+ *
+ * \return true if \p this is smaller than \p rhs.
+ */
+bool addr::operator < (addr const & rhs) const
+{
+    return f_address.sin6_addr < rhs.f_address.sin6_addr;
+}
+
+
+/** \brief Compare two addresses to know which one is smaller or equal.
+ *
+ * This function compares the left hand side (this) and the right
+ * hand side (rhs) to know whether the left hand side is smaller or
+ * equal to thr right handside.
+ *
+ * \warning
+ * The function only compares the address itself. The family, port,
+ * flow info, scope identifier, protocol are all ignored.
+ *
+ * \return true if \p this is smaller than \p rhs.
+ */
+bool addr::operator <= (addr const & rhs) const
+{
+    return f_address.sin6_addr <= rhs.f_address.sin6_addr;
+}
+
+
+/** \brief Compare two addresses to know which one is smaller.
+ *
+ * This function compares the left hand side (this) and the right
+ * hand side (rhs) to know which one is the smallest. If both
+ * are equal or the left hand side is larger than the right hand
+ * side, then it returns false, otherwise it returns true.
+ *
+ * \warning
+ * The function only compares the address itself. The family, port,
+ * flow info, scope identifier, protocol are all ignored.
+ *
+ * \return true if \p this is smaller than \p rhs.
+ */
+bool addr::operator > (addr const & rhs) const
+{
+    return f_address.sin6_addr > rhs.f_address.sin6_addr;
+}
+
+
+/** \brief Compare two addresses to know which one is smaller.
+ *
+ * This function compares the left hand side (this) and the right
+ * hand side (rhs) to know which one is the smallest. If both
+ * are equal or the left hand side is larger than the right hand
+ * side, then it returns false, otherwise it returns true.
+ *
+ * \warning
+ * The function only compares the address itself. The family, port,
+ * flow info, scope identifier, protocol are all ignored.
+ *
+ * \return true if \p this is smaller than \p rhs.
+ */
+bool addr::operator >= (addr const & rhs) const
+{
+    return f_address.sin6_addr >= rhs.f_address.sin6_addr;
+}
+
+
+/** \brief Mark that the address changed.
+ *
+ * This functions makes sure that some of the parameters being cached
+ * get reset in such a way that checking the cache will again return
+ * the correct answer.
+ *
+ * \sa get_network_type()
+ */
+void addr::address_changed()
+{
+    f_private_network_defined = network_type_t::NETWORK_TYPE_UNDEFINED;
+}
+
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/addr_exception.h libaddr-1.0.18.0~xenial/libaddr/addr_exception.h
--- libaddr-1.0.17.0~xenial/libaddr/addr_exception.h	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/addr_exception.h	2019-09-02 20:10:36.000000000 +0000
@@ -0,0 +1,52 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#pragma once
+
+/** \file
+ * \brief The list of libaddr exceptions.
+ *
+ * This header defines various exceptions used throughout the addr library.
+ */
+
+// libexcept library
+//
+#include    
+
+
+namespace addr
+{
+
+
+DECLARE_MAIN_EXCEPTION(addr_error);
+
+DECLARE_EXCEPTION(addr_error, addr_invalid_argument);
+DECLARE_EXCEPTION(addr_error, addr_invalid_parameter);
+DECLARE_EXCEPTION(addr_error, addr_invalid_state);
+DECLARE_EXCEPTION(addr_error, addr_invalid_structure);
+DECLARE_EXCEPTION(addr_error, addr_io_error);
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/addr.h libaddr-1.0.18.0~xenial/libaddr/addr.h
--- libaddr-1.0.17.0~xenial/libaddr/addr.h	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/addr.h	2019-09-02 20:11:07.000000000 +0000
@@ -0,0 +1,232 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#pragma once
+
+/** \file
+ * \brief The various libaddr classes.
+ *
+ * This header includes the base addr class used to handle one binary
+ * address.
+ */
+
+// C++ library
+//
+#include    
+#include    
+#include    
+
+
+// C library
+//
+#include    
+
+
+
+namespace addr
+{
+
+
+
+
+/** \brief Initialize an IPv6 address as such.
+ *
+ * This function initializes a sockaddr_in6 with all zeroes except
+ * for the sin6_family which is set to AF_INET6.
+ *
+ * return The initialized IPv6 address.
+ */
+constexpr struct sockaddr_in6 init_in6()
+{
+    struct sockaddr_in6 in6 = sockaddr_in6();
+    in6.sin6_family = AF_INET6;
+    return in6;
+}
+
+
+class addr
+{
+public:
+    enum class network_type_t
+    {
+        NETWORK_TYPE_UNDEFINED,
+        NETWORK_TYPE_PRIVATE,
+        NETWORK_TYPE_CARRIER,
+        NETWORK_TYPE_LINK_LOCAL,
+        NETWORK_TYPE_MULTICAST,
+        NETWORK_TYPE_LOOPBACK,
+        NETWORK_TYPE_ANY,
+        NETWORK_TYPE_UNKNOWN,
+        NETWORK_TYPE_PUBLIC = NETWORK_TYPE_UNKNOWN  // we currently do not distinguish public and unknown
+    };
+
+    enum class string_ip_t
+    {
+        STRING_IP_ONLY,
+        STRING_IP_BRACKETS,         // IPv6 only
+        STRING_IP_PORT,             // in IPv6, includes brackets
+        STRING_IP_MASK,
+        STRING_IP_BRACKETS_MASK,    // IPv6 only
+        STRING_IP_ALL
+    };
+
+    typedef std::shared_ptr   pointer_t;
+    typedef std::vector       vector_t;
+    typedef int                     socket_flag_t;
+
+    static socket_flag_t const      SOCKET_FLAG_CLOEXEC  = 0x01;
+    static socket_flag_t const      SOCKET_FLAG_NONBLOCK = 0x02;
+    static socket_flag_t const      SOCKET_FLAG_REUSE    = 0x04;
+
+                                    addr();
+                                    addr(struct sockaddr_in const & in);
+                                    addr(struct sockaddr_in6 const & in6);
+
+    void                            set_from_socket(int s, bool peer);
+    void                            set_ipv4(struct sockaddr_in const & in);
+    void                            set_ipv6(struct sockaddr_in6 const & in6);
+    void                            set_port(int port);
+    void                            set_protocol(char const * protocol);
+    void                            set_protocol(int protocol);
+    void                            set_mask(uint8_t const * mask);
+    void                            apply_mask();
+
+    bool                            is_default() const;
+    bool                            is_ipv4() const;
+    void                            get_ipv4(struct sockaddr_in & in) const;
+    void                            get_ipv6(struct sockaddr_in6 & in6) const;
+    std::string                     to_ipv4_string(string_ip_t mode) const;
+    std::string                     to_ipv6_string(string_ip_t mode) const;
+    std::string                     to_ipv4or6_string(string_ip_t mode) const;
+
+    network_type_t                  get_network_type() const;
+    std::string                     get_network_type_string() const;
+
+    int                             create_socket(socket_flag_t flags) const;
+    int                             connect(int s) const;
+    int                             bind(int s) const;
+    std::string                     get_name() const;
+    std::string                     get_service() const;
+    int                             get_port() const;
+    int                             get_protocol() const;
+    void                            get_mask(uint8_t * mask) const;
+
+    bool                            match(addr const & ip) const;
+    bool                            operator == (addr const & rhs) const;
+    bool                            operator != (addr const & rhs) const;
+    bool                            operator <  (addr const & rhs) const;
+    bool                            operator <= (addr const & rhs) const;
+    bool                            operator >  (addr const & rhs) const;
+    bool                            operator >= (addr const & rhs) const;
+
+private:
+    void                            address_changed();
+
+    // always keep address in an IPv6 structure
+    //
+    struct sockaddr_in6             f_address = init_in6();
+    uint8_t                         f_mask[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
+    int                             f_protocol = IPPROTO_TCP;
+    mutable network_type_t          f_private_network_defined = network_type_t::NETWORK_TYPE_UNDEFINED;
+};
+
+
+
+
+
+}
+// namespace addr
+
+
+inline bool operator == (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
+{
+    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) == 0;
+}
+
+
+inline bool operator != (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
+{
+    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) != 0;
+}
+
+
+inline bool operator < (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
+{
+    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) < 0;
+}
+
+
+inline bool operator <= (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
+{
+    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) <= 0;
+}
+
+
+inline bool operator > (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
+{
+    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) > 0;
+}
+
+
+inline bool operator >= (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
+{
+    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) >= 0;
+}
+
+
+inline bool operator == (in6_addr const & a, in6_addr const & b)
+{
+    return memcmp(&a, &b, sizeof(in6_addr)) == 0;
+}
+
+
+inline bool operator != (in6_addr const & a, in6_addr const & b)
+{
+    return memcmp(&a, &b, sizeof(in6_addr)) != 0;
+}
+
+
+inline bool operator < (in6_addr const & a, in6_addr const & b)
+{
+    return memcmp(&a, &b, sizeof(in6_addr)) < 0;
+}
+
+
+inline bool operator <= (in6_addr const & a, in6_addr const & b)
+{
+    return memcmp(&a, &b, sizeof(in6_addr)) <= 0;
+}
+
+
+inline bool operator > (in6_addr const & a, in6_addr const & b)
+{
+    return memcmp(&a, &b, sizeof(in6_addr)) > 0;
+}
+
+
+inline bool operator >= (in6_addr const & a, in6_addr const & b)
+{
+    return memcmp(&a, &b, sizeof(in6_addr)) >= 0;
+}
+
+
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/addr_parser.cpp libaddr-1.0.18.0~xenial/libaddr/addr_parser.cpp
--- libaddr-1.0.17.0~xenial/libaddr/addr_parser.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/addr_parser.cpp	2019-09-02 21:54:28.000000000 +0000
@@ -0,0 +1,1728 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+/** \file
+ * \brief The implementation of the IP address parser.
+ *
+ * This function is used to parse IP addresses from a string to a
+ * vector of ranges.
+ */
+
+// self
+//
+#include    "libaddr/addr_parser.h"
+#include    "libaddr/addr_exception.h"
+
+
+// C++ library
+//
+#include    
+#include    
+
+
+// C library
+//
+#include    
+#include    
+
+
+// last include
+//
+#include    
+
+
+
+namespace addr
+{
+
+
+namespace
+{
+
+
+/** \brief Delete an addrinfo structure.
+ *
+ * This deleter is used to make sure all the addinfo get released when
+ * an exception occurs or the function using such exists.
+ *
+ * \param[in] ai  The addrinfo structure to free.
+ */
+void addrinfo_deleter(struct addrinfo * ai)
+{
+    freeaddrinfo(ai);
+}
+
+
+}
+
+
+
+
+
+/** \brief Set the default IP addresses.
+ *
+ * This function sets the default IP addresses to be used by the parser
+ * when the input string of the parse() function does not include an IP
+ * address.
+ *
+ * The \p address parameter cannot include a port. See
+ * set_default_port() as a way to change the default port.
+ *
+ * The function expects either an IPv4 or an IPv6 address. It can be
+ * called twice if you need to define both types of addresses (which
+ * is often a good idea.)
+ *
+ * For example, the following input is considered valid when a default
+ * address is defined:
+ *
+ * \code
+ *      parser.parse(":123");
+ * \endcode
+ *
+ * It returns the default address and port 123. Note that by default
+ * an address is mandatory unless a default address is defined.
+ *
+ * To prevent the parser from working when no default and no address
+ * are specified, then make sure to set the REQUIRED_ADDRESS allow
+ * flag to true:
+ *
+ * \code
+ *      parser.set_allow(parser.flag_t::REQUIRED_ADDRESS, true);
+ *      // now address is mandatory
+ * \endcode
+ *
+ * To completely prevent the use of an address in an input string, set
+ * the `ADDRESS` and `REQUIRED_ADDRESS` values to false:
+ *
+ * \code
+ *      parser.set_allow(parser.flag_t::ADDRESS,          false);
+ *      parser.set_allow(parser.flag_t::REQUIRED_ADDRESS, false);
+ * \endcode
+ *
+ * To remove both default IP addresses, call this function with an empty
+ * string:
+ *
+ * \code
+ *      parser.set_default_address(std::string());
+ * \endcode
+ *
+ * \todo
+ * Consider saving the default IPs as addr structures and allow such
+ * as input.
+ *
+ * \param[in] addr  The new address.
+ */
+void addr_parser::set_default_address(std::string const & address)
+{
+    if(address.empty())
+    {
+        f_default_address4.clear();
+        f_default_address6.clear();
+    }
+    else if(address[0] == '[')
+    {
+        // remove the '[' and ']'
+        //
+        if(address.back() != ']')
+        {
+            throw addr_invalid_argument("an IPv6 address starting with '[' must end with ']'.");
+        }
+        f_default_address6 = address.substr(1, address.length() - 2);
+    }
+    else if(address.find(':') != std::string::npos)
+    {
+        f_default_address6 = address;
+    }
+    else
+    {
+        f_default_address4 = address;
+    }
+}
+
+
+/** \brief Retrieve the default IP address for IPv4 parsing.
+ *
+ * This function returns a copy of the default IP address used by
+ * the parser when the input string does not include an IP address.
+ *
+ * If the function returns an empty string, then no default address
+ * is defined.
+ *
+ * \return The default IPv4 address.
+ *
+ * \sa get_default_address6()
+ * \sa set_default_address()
+ */
+std::string const & addr_parser::get_default_address4() const
+{
+    return f_default_address4;
+}
+
+
+/** \brief Retrieve the default IP address for IPv4 parsing.
+ *
+ * This function returns a copy of the default IP address used by
+ * the parser when the input string does not include an IP address.
+ *
+ * If the function returns an empty string, then no default address
+ * is defined.
+ *
+ * \return The default IPv6 address, without square brackets.
+ *
+ * \sa get_default_address4()
+ * \sa set_default_address()
+ */
+std::string const & addr_parser::get_default_address6() const
+{
+    return f_default_address6;
+}
+
+
+/** \brief Define the default port.
+ *
+ * This function is used to define the default port to use in the address
+ * parser object. By default this is set to -1 meaning: no default port.
+ *
+ * This function accepts any port number from 0 to 65535. It also accepts
+ * -1 to reset the port back to "no default".
+ *
+ * To prevent the parser from working when no default and no port
+ * are specified, then make sure to set the REQUIRED_PORT allow
+ * flag to true:
+ *
+ * \code
+ *      parser.set_allow(parser.flag_t::REQUIRED_PORT, true);
+ *      // now port is mandatory
+ * \endcode
+ *
+ * To completely prevent the use of a port in an input string, set
+ * the `PORT` and `REQUIRED_PORT` values to false:
+ *
+ * \code
+ *      parser.set_allow(parser.flag_t::PORT,          false);
+ *      parser.set_allow(parser.flag_t::REQUIRED_PORT, false);
+ * \endcode
+ *
+ * \exception addr_invalid_argument_exception
+ * If the port number is out of range, then this expcetion is raised.
+ * The allowed range for a port is 0 to 65535. This function also
+ * accepts -1 meaning that no default port is specified.
+ *
+ * \param[in] port  The new default port.
+ */
+void addr_parser::set_default_port(int const port)
+{
+    if(port < -1
+    || port > 65535)
+    {
+        throw addr_invalid_argument("addr_parser::set_default_port(): port must be in range [-1..65535].");
+    }
+
+    f_default_port = port;
+}
+
+
+/** \brief Retrieve the default port.
+ *
+ * This function retrieves the default port as defined by the
+ * set_default_port() function.
+ */
+int addr_parser::get_default_port() const
+{
+    return f_default_port;
+}
+
+
+/** \brief Define the default mask.
+ *
+ * This function is used to define the default mask. Note that the
+ * default mask will not be used at all if the flag_t::MASK allow
+ * flag is not set to true:
+ *
+ * \code
+ *      parser.set_allow(parser.flag_t::MASK, true);
+ *      parser.set_default_mask("255.255.0.0");
+ *      parser.set_default_mask("[ffff:ffff:ffff::]");
+ * \endcode
+ *
+ * The IPv6 mask does not require the square brackets (`'['` and `']'`).
+ *
+ * To remove the default mask, call this function with an empty
+ * string:
+ *
+ * \code
+ *      parser.set_default_mask(std::string());
+ * \endcode
+ *
+ * \note
+ * As you can see, here we expect the mask to be a string. This is because
+ * it gets parsed as if it came from the input string of the parser. This
+ * also means that if the mask is invalid, it will not be detected until
+ * you attempt to parse an input string that does not include a mask and
+ * the default gets used.
+ *
+ * \todo
+ * Add a check of the default mask when it gets set so we can throw on
+ * errors and that way it is much more likely that programmers can fix
+ * their errors early. (Actually by pre-parsing we could save it as
+ * an addr and allow a `set_default_mask(addr ...)`!)
+ *
+ * \param[in] mask  The mask to use by default.
+ */
+void addr_parser::set_default_mask(std::string const & mask)
+{
+    if(mask.empty())
+    {
+        f_default_mask4.clear();
+        f_default_mask6.clear();
+    }
+    else if(mask[0] == '[')
+    {
+        // remove the '[' and ']'
+        //
+        if(mask.back() != ']')
+        {
+            throw addr_invalid_argument("an IPv6 mask starting with '[' must end with ']'.");
+        }
+        f_default_mask6 = mask.substr(1, mask.length() - 2);
+    }
+    else if(mask.find(':') != std::string::npos)
+    {
+        f_default_mask6 = mask;
+    }
+    else
+    {
+        f_default_mask4 = mask;
+    }
+}
+
+
+/** \brief Retrieve the default mask.
+ *
+ * This function returns a reference to the mask as set by the
+ * set_default_mask() function. The value is an empty string by
+ * default.
+ *
+ * The default mask will be used if no mask is specified in the
+ * input string to the parse() function. When no default mask
+ * is defined, the mask is set to all 1s.
+ *
+ * \note
+ * The default mask is a string, not a binary mask. It gets
+ * converted by the parser at the time it is required.
+ *
+ * \return The default mask.
+ *
+ * \sa get_default_mask6()
+ * \sa set_default_mask()
+ */
+std::string const & addr_parser::get_default_mask4() const
+{
+    return f_default_mask4;
+}
+
+
+/** \brief Retrieve the default mask.
+ *
+ * This function returns a reference to the mask as set by the
+ * set_default_mask() function. The value is an empty string by
+ * default.
+ *
+ * The default mask will be used if no mask is specified in the
+ * input string to the parse() function. When no default mask
+ * is defined, the mask is set to all 1s.
+ *
+ * \note
+ * The default mask is a string, not a binary mask. It gets
+ * converted by the parser at the time it is required.
+ *
+ * \return The default mask.
+ *
+ * \sa get_default_mask4()
+ * \sa set_default_mask()
+ */
+std::string const & addr_parser::get_default_mask6() const
+{
+    return f_default_mask6;
+}
+
+
+/** \brief Set the protocol to use to filter addresses.
+ *
+ * This function sets the protocol as one of the following:
+ *
+ * \li "ip" -- only return IP address supporting the IP protocol
+ * (this is offered because getaddrinfo() may return such IP addresses.)
+ * \li "tcp" -- only return IP address supporting TCP
+ * \li "udp" -- only return IP address supporting UDP
+ *
+ * Any other value is refused. To reset the protocol to the default,
+ * which is "do not filter by protocol", call the clear_protocol().
+ *
+ * \exception addr_invalid_argument_exception
+ * If the string passed to this function is not one of the acceptable
+ * protocols (ip, tcp, udp), then this exception is raised.
+ *
+ * \param[in] protocol  The default protocol for this parser.
+ *
+ * \sa clear_protocol()
+ * \sa get_protocol()
+ */
+void addr_parser::set_protocol(std::string const & protocol)
+{
+    if(protocol == "ip")
+    {
+        f_protocol = IPPROTO_IP;
+    }
+    else if(protocol == "tcp")
+    {
+        f_protocol = IPPROTO_TCP;
+    }
+    else if(protocol == "udp")
+    {
+        f_protocol = IPPROTO_UDP;
+    }
+    else
+    {
+        // not a protocol we support
+        //
+        throw addr_invalid_argument(
+                  std::string("unknown protocol \"")
+                + protocol
+                + "\", expected \"tcp\" or \"udp\".");
+    }
+}
+
+
+/** \brief Set the protocol to use to filter addresses.
+ *
+ * This function sets the protocol as one of the following:
+ *
+ * \li IPPROTO_IP -- only return IP address supporting the IP protocol
+ * (this is offered because getaddrinfo() may return such IP addresses.)
+ * \li IPPROTO_TCP -- only return IP address supporting TCP
+ * \li IPPROTO_UDP -- only return IP address supporting UDP
+ *
+ * Any other value is refused. To reset the protocol to the default,
+ * which is "do not filter by protocol", call the clear_protocol().
+ *
+ * \exception addr_invalid_argument_exception
+ * If the string passed to this function is not one of the acceptable
+ * protocols (ip, tcp, udp), then this exception is raised.
+ *
+ * \param[in] protocol  The default protocol for this parser.
+ *
+ * \sa clear_protocol()
+ * \sa get_protocol()
+ */
+void addr_parser::set_protocol(int const protocol)
+{
+    // make sure that's a protocol we support
+    //
+    switch(protocol)
+    {
+    case IPPROTO_IP:
+    case IPPROTO_TCP:
+    case IPPROTO_UDP:
+        break;
+
+    default:
+        throw addr_invalid_argument(
+                  std::string("unknown protocol \"")
+                + std::to_string(protocol)
+                + "\", expected \"tcp\" or \"udp\".");
+
+    }
+
+    f_protocol = protocol;
+}
+
+
+/** \brief Use this function to reset the protocol back to "no default."
+ *
+ * This function sets the protocol to -1 (which is something you cannot
+ * do by calling the set_protocol() functions above.)
+ *
+ * The -1 special value means that the protocol is not defined, that
+ * there is no default. In most cases this means all the addresses
+ * that match, ignoring the protocol, will be returned by the parse()
+ * function.
+ *
+ * \sa set_protocol()
+ * \sa get_protocol()
+ */
+void addr_parser::clear_protocol()
+{
+    f_protocol = -1;
+}
+
+
+/** \brief Retrieve the protocol as defined by the set_protocol().
+ *
+ * This function returns the protocol number as defined by the
+ * set_protocol.
+ *
+ * When defined, the protocol is used whenever we call the
+ * getaddrinfo() function. In general, this means the IP addresses
+ * returned will have  to match that protocol.
+ *
+ * This function may return -1. The value -1 is used as "do not
+ * filter by protocol". The protocol can be set to -1 by calling
+ * the clear_protocol() function.
+ *
+ * \return The parser default protocol.
+ *
+ * \sa set_protocol()
+ * \sa clear_protocol()
+ */
+int addr_parser::get_protocol() const
+{
+    return f_protocol;
+}
+
+
+/** \brief Set or clear allow flags in the parser.
+ *
+ * This parser has a set of flags it uses to know whether the input
+ * string can include certain things such as a port or a mask.
+ *
+ * This function is used to allow or require certain parameters and
+ * to disallow others.
+ *
+ * By default, the ADDRESS and PORT flags are set, meaning that an
+ * address and a port can appear, but either or both are optinal.
+ * If unspecified, then the default will be used. If not default
+ * is defined, then the parser may fail in this situation.
+ *
+ * One problem is that we include contradictory syntatical features.
+ * The parser supports lists of addresses separated by commas and
+ * lists of ports separated by commas. Both are not supported
+ * simultaneously. This means you want to allow multiple addresses
+ * separated by commas, the function makes sure that the multiple
+ * port separated by commas support is turned of.
+ *
+ * \li ADDRESS -- the IP address is allowed, but optional
+ * \li REQUIRED_ADDRESS -- the IP address is mandatory
+ * \li PORT -- the port is allowed, but optional
+ * \li REQUIRED_PORT -- the port is mandatory
+ * \li MASK -- the mask is allowed, but optional
+ * \li MULTI_ADDRESSES_COMMAS -- the input can have multiple addresses
+ * separated by commas, spaces are not allowed (prevents MULTI_PORTS_COMMAS)
+ * \li MULTI_ADDRESSES_SPACES -- the input can have multiple addresses
+ * separated by spaces
+ * \li MULTI_ADDRESSES_COMMAS_AND_SPACES -- the input can have multiple
+ * addresses separated by spaces and commas (prevents MULTI_PORTS_COMMAS)
+ * \li MULTI_PORTS_SEMICOLONS -- the input can  have multiple ports
+ * separated by semicolons _NOT IMPLEMENTED YET_
+ * \li MULTI_PORTS_COMMAS -- the input can have multiple ports separated
+ * by commas (prevents MULTI_ADDRESSES_COMMAS and
+ * MULTI_ADDRESSES_COMMAS_AND_SPACES) _NOT IMPLEMENTED YET_
+ * \li PORT_RANGE -- the input supports port ranges (p1-p2) _NOT
+ * IMPLEMENTED YET_
+ * \li ADDRESS_RANGE -- the input supports address ranges (addr-addr) _NOT
+ * IMPLEMENTED YET_
+ *
+ * \param[in] flag  The flag to set or clear.
+ * \param[in] allow  Whether to allow (true) or disallow (false).
+ *
+ * \sa get_allow()
+ */
+void addr_parser::set_allow(flag_t const flag, bool const allow)
+{
+    if(flag < static_cast(0)
+    || flag >= flag_t::FLAG_max)
+    {
+        throw addr_invalid_argument("addr_parser::set_allow(): flag has to be one of the valid flags.");
+    }
+
+    f_flags[static_cast(flag)] = allow;
+
+    // if we just set a certain flag, others may need to go to false
+    //
+    if(allow)
+    {
+        // we can only support one type of commas
+        //
+        switch(flag)
+        {
+        case flag_t::MULTI_ADDRESSES_COMMAS:
+        case flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES:
+            f_flags[static_cast(flag_t::MULTI_PORTS_COMMAS)] = false;
+            break;
+
+        case flag_t::MULTI_PORTS_COMMAS:
+            f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS)] = false;
+            f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)] = false;
+            break;
+
+        default:
+            break;
+
+        }
+    }
+}
+
+
+/** \brief Retrieve the current statius of an allow flag.
+ *
+ * This function returns the current status of the allow flags.
+ *
+ * By default, the `ADDRESS` and `PORT` flags are set to true.
+ * All the other flags are set to false.
+ *
+ * You may change the value of an allow flag by calling the
+ * set_allow() function.
+ *
+ * \param[in] flag  Which flag is to be checked.
+ *
+ * \return The value of the flag: true or false.
+ *
+ * \sa set_allow()
+ */
+bool addr_parser::get_allow(flag_t const flag) const
+{
+    if(flag < static_cast(0)
+    || flag >= flag_t::FLAG_max)
+    {
+        throw addr_invalid_argument("addr_parser::get_allow(): flag has to be one of the valid flags.");
+    }
+
+    return f_flags[static_cast(flag)];
+}
+
+
+/** \brief Check whether errors were registered so far.
+ *
+ * This function returns true if the system detected errors in one
+ * of the previous calls to parse(). The flag can be cleared using
+ * the clear_errors() function.
+ *
+ * On construction and after a call to clear_error(), this flag is
+ * always false. If you are to call parser() multiple times with
+ * the same addr_parser object, then you want to make sure to call
+ * the clear_errors() function before calling the parse() function.
+ * Otherwise you won't know whether errors occurred in a earlier
+ * or later call.
+ *
+ * \code
+ *      // first time, not required
+ *      parser.parse(...);
+ *      ...
+ *
+ *      // next time, required
+ *      parser.clear_errors();
+ *      parser.parse(...);
+ *      ...
+ * \endcode
+ *
+ * \return true if errors were generated.
+ */
+bool addr_parser::has_errors() const
+{
+    return !f_error.empty();
+}
+
+
+/** \brief Emit an error and save it in this class.
+ *
+ * This function adds the message to the error string part of this
+ * object. A newline is also added at the end of the message.
+ *
+ * Next the function increments the error counter.
+ *
+ * \note
+ * You are expected to emit one error at a time. If you want to
+ * emit several messages in a row, that will work and properly
+ * count each message.
+ *
+ * \param[in] msg  The message to add to the parser error messages.
+ *
+ * \sa error_messages()
+ */
+void addr_parser::emit_error(std::string const & msg)
+{
+    f_error += msg;
+    f_error += "\n";
+    ++f_error_count;
+}
+
+
+/** \brief Return the current error messages.
+ *
+ * The error messages are added to the addr_parser using the
+ * emit_error() function.
+ *
+ * This function does not clear the list of error messages.
+ * To do that, call the clear_errors() function.
+ *
+ * The number of messages can be determined by counting the
+ * number of "\n" characters in the string. The error_count()
+ * will return that same number (assuming no message included
+ * a '\n' character when emit_error() was called.)
+ *
+ * \return A string with the list of messages.
+ *
+ * \sa emit_error()
+ * \sa clear_errors()
+ */
+std::string const & addr_parser::error_messages() const
+{
+    return f_error;
+}
+
+
+/** \brief Return the number of error messages that were emitted.
+ *
+ * Each time the emit_error() function is called, the error
+ * counter is incremented by 1. This function returns that
+ * error counter.
+ *
+ * The clear_errors() function can be used to clear the
+ * counter back to zero.
+ *
+ * \return The number of errors that were emitted so far.
+ *
+ * \sa emit_error()
+ */
+int addr_parser::error_count() const
+{
+    return f_error_count;
+}
+
+
+/** \brief Clear the error message and error counter.
+ *
+ * This function clears all the error messages and reset the
+ * counter back to zero. In order words, it will be possible
+ * to tell how many times the emit_error() was called since
+ * the start or the last clear_errors() call.
+ *
+ * To retrieve a copy of the error counter, use the error_count()
+ * function.
+ *
+ * \sa error_count()
+ */
+void addr_parser::clear_errors()
+{
+    f_error.clear();
+    f_error_count = 0;
+}
+
+
+/** \brief Parse a string of addresses, ports, and masks.
+ *
+ * This function is used to parse the list of addresses defined
+ * in the \p in parameter.
+ *
+ * One address is composed of one to three elements:
+ *
+ * \code
+ *          [ address ] [ ':' port ] [ '/' mask ]
+ * \endcode
+ *
+ * Although all three elements are optional (at least by default),
+ * a valid address is expected to include at least one of the
+ * three elements. (i.e. an empty string is just skipped silently.)
+ *
+ * ### Multiple Addresses
+ *
+ * Multiple addresses can be defined if at least one of the
+ * `MULTI_ADDRESSES_COMMAS`, `MULTI_ADDRESSES_SPACES`, or
+ * `MULTI_ADDRESSES_COMMAS_AND_SPACES` allow flags is set to true.
+ *
+ * Note that the `MULTI_ADDRESSES_COMMAS_AND_SPACES` has priotity. If
+ * set to true, then both, commas and spaces are allowed between
+ * addresses.
+ *
+ * Next comes `MULTI_ADDRESSES_COMMAS`: if set to true, addresses
+ * must be separated by commas and spaces are not allowed.
+ *
+ * Finally we have `MULTI_ADDRESSES_SPACES`. If that one is true, then
+ * addresses must be separated by spaces and commas are not allowed.
+ *
+ * ### Make Address Field Required
+ *
+ * To make the address field a required field, set the
+ * `REQUIRED_ADDRESS` flag (see set_allow()) to true and do not define a
+ * default address (see set_default_address()).
+ *
+ * ### Make Port Field Required
+ *
+ * To make the port field a required fiel, set the `REQUIRED_PORT`
+ * flag (see set_allow()) to true and do not define a default port
+ * (see set_default_port()).
+ *
+ * ### Allow Mask
+ *
+ * The mask cannot be made mandatory. However, you have to set
+ * the `MASK` flag to true to allow it. By default it is not
+ * allowed.
+ *
+ * ### Ranges
+ *
+ * At this time we do not need support for ranges so it did not yet
+ * get implemented.
+ *
+ * \param[in] in  The input string to be parsed.
+ */
+addr_range::vector_t addr_parser::parse(std::string const & in)
+{
+    addr_range::vector_t result;
+
+    if(f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)])
+    {
+        std::string comma_space(", ");
+        std::string::size_type s(0);
+        while(s < in.length())
+        {
+            // since C++11 we have a way to search for a set of character
+            // in a string with an algorithm!
+            //
+            auto const it(std::find_first_of(in.begin() + s, in.end(), comma_space.begin(), comma_space.end()));
+            std::string::size_type const e(it == in.end() ? in.length() : it - in.begin());
+            if(e > s)
+            {
+                parse_cidr(in.substr(s, e - s), result);
+            }
+            s = e + 1;
+        }
+    }
+    else if(f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS)]
+         || f_flags[static_cast(flag_t::MULTI_ADDRESSES_SPACES)])
+    {
+        char sep(f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS)] ? ',' : ' ');
+        std::string::size_type s(0);
+        while(s < in.length())
+        {
+            std::string::size_type e(in.find(sep, s));
+            if(e == std::string::npos)
+            {
+                e = in.length();
+            }
+            if(e > s)
+            {
+                parse_cidr(in.substr(s, e - s), result);
+            }
+            s = e + 1;
+        }
+    }
+    else
+    {
+        parse_cidr(in, result);
+    }
+
+    return result;
+}
+
+
+/** \brief Check one address.
+ *
+ * This function checks one address, although if it is a name, it could
+ * represent multiple IP addresses.
+ *
+ * This function separate the address:port from the mask if the mask is
+ * allowed. Then it parses the address:port part and the mask separately.
+ *
+ * \param[in] in  The address to parse.
+ * \param[in,out] result  The list of resulting addresses.
+ */
+void addr_parser::parse_cidr(std::string const & in, addr_range::vector_t & result)
+{
+    if(f_flags[static_cast(flag_t::MASK)])
+    {
+        // check whether there is a mask
+        //
+        std::string mask;
+
+        std::string address;
+        std::string::size_type const p(in.find('/'));
+        if(p != std::string::npos)
+        {
+            address = in.substr(0, p);
+            mask = in.substr(p + 1);
+        }
+        else
+        {
+            address = in;
+        }
+
+        int const errcnt(f_error_count);
+
+        // handle the address first
+        //
+        addr_range::vector_t addr_mask;
+        parse_address(address, mask, addr_mask);
+
+        // now check for the mask
+        //
+        for(auto & am : addr_mask)
+        {
+            std::string m(mask);
+            if(m.empty())
+            {
+                // the mask was not defined in the input, then adapt it to
+                // the type of address we got in 'am'
+                //
+                if(am.get_from().is_ipv4())
+                {
+                    m = f_default_mask4;
+                }
+                else
+                {
+                    // parse_mask() expects '[...]' around IPv6 addresses
+                    //
+                    m = "[" + f_default_mask6 + "]";
+                }
+            }
+
+            parse_mask(m, am.get_from());
+        }
+
+        // now append the list to the result if no errors occurred
+        //
+        if(errcnt == f_error_count)
+        {
+            result.insert(result.end(), addr_mask.begin(), addr_mask.end());
+        }
+    }
+    else
+    {
+        // no mask allowed, if there is one, this call will fail
+        //
+        parse_address(in, std::string(), result);
+    }
+}
+
+
+/** \brief Parse one address.
+ *
+ * This function is called with one address. It determines whether we
+ * are dealing with an IPv4 or an IPv6 address and call the
+ * corresponding sub-function.
+ *
+ * An address is considered an IPv6 address if it starts with a '['
+ * character.
+ *
+ * \note
+ * The input cannot include a mask. It has to already have been
+ * removed.
+ *
+ * \note
+ * The mask parameter is only used to determine whether this function
+ * is being called with an IPv6 or not. It is otherwise ignored.
+ *
+ * \param[in] in  The input address eventually including a port.
+ * \param[in] mask  The mask used to determine whether we are dealing with
+ *                  an IPv6 or not.
+ * \param[in,out] result  The list of resulting addresses.
+ */
+void addr_parser::parse_address(std::string const & in, std::string const & mask, addr_range::vector_t & result)
+{
+    // With our only supported format, ipv6 addresses must be between square
+    // brackets. The address may just be a mask in which case the '[' may
+    // not be at the very start (i.e. "/[ffff:ffff::]")
+    //
+    if(in.empty()
+    || in[0] == ':')    // if it start with ':' then there is no address
+    {
+        // if the address is empty, then use the mask to determine the
+        // type of IP address (note: if the address starts with ':'
+        // it is considered empty since an IPv6 would have a '[' at
+        // the start)
+        //
+        if(!mask.empty())
+        {
+            if(mask[0] == '[')
+            {
+                // IPv6 parsing
+                //
+                parse_address6(in, result);
+            }
+            else
+            {
+                // if the number is 33 or more, it has to be IPv6, otherwise
+                // we cannot know...
+                //
+                int mask_count(0);
+                for(char const * s(mask.c_str()); *s != '\0'; ++s)
+                {
+                    if(*s >= '0' && *s <= '9')
+                    {
+                        mask_count = mask_count * 10 + *s - '0';
+                        if(mask_count > 1000)
+                        {
+                            // not valid
+                            //
+                            mask_count = -1;
+                            break;;
+                        }
+                    }
+                    else
+                    {
+                        // not a valid decimal number
+                        //
+                        mask_count = -1;
+                        break;
+                    }
+                }
+                if(mask_count > 32)
+                {
+                    parse_address6(in, result);
+                }
+                else
+                {
+                    parse_address4(in, result);
+                }
+            }
+        }
+        else
+        {
+            if(f_default_address4.empty()
+            && !f_default_address6.empty())
+            {
+                parse_address6(in, result);
+            }
+            else
+            {
+                parse_address4(in, result);
+            }
+        }
+    }
+    else
+    {
+        // if an address has a ']' then it is IPv6 even if the '['
+        // is missing, that being said, it is still considered
+        // invalid as per our processes
+        //
+        if(in[0] == '['
+        || in.find(']') != std::string::npos)
+        {
+            parse_address6(in, result);
+        }
+        else
+        {
+            // if there is no port, then a ':' can be viewed as an IPv6
+            // address because there is no other ':', but if there are
+            // '.' before the ':' then we assume that it is IPv4 still
+            //
+            if(!f_flags[static_cast(flag_t::PORT)]
+            && !f_flags[static_cast(flag_t::REQUIRED_PORT)])
+            {
+                std::string::size_type const p(in.find(':'));
+                if(p != std::string::npos
+                && in.find('.') > p)
+                {
+                    parse_address6(in, result);
+                }
+                else
+                {
+                    parse_address4(in, result);
+                }
+            }
+            else
+            {
+                parse_address4(in, result);
+            }
+        }
+    }
+}
+
+
+/** \brief Parse one IPv4 address.
+ *
+ * This function checks the input parameter \p in and extracts the
+ * address and port. There is a port if the input strings includes
+ * a `':'` character.
+ *
+ * If this function detects that a port is not allowed and yet
+ * a `':'` character is found, then it generates an error and
+ * returns without adding anything to `result`.
+ *
+ * \param[in] in  The input string with the address and optional port.
+ * \param[in,out] result  The list of resulting addresses.
+ */
+void addr_parser::parse_address4(std::string const & in, addr_range::vector_t & result)
+{
+    std::string address;
+    std::string port_str;
+
+    std::string::size_type const p(in.find(':'));
+
+    if(f_flags[static_cast(flag_t::PORT)]
+    || f_flags[static_cast(flag_t::REQUIRED_PORT)])
+    {
+        // the address can include a port
+        //
+        if(p != std::string::npos)
+        {
+            // get the address only if not empty (otherwise we want to
+            // keep the default)
+            //
+            if(p > 0)
+            {
+                address = in.substr(0, p);
+            }
+
+            // get the port only if not empty (otherwise we want to
+            // keep the default)
+            //
+            if(p + 1 < in.length())
+            {
+                port_str = in.substr(p + 1);
+            }
+        }
+        else if(!in.empty())
+        {
+            address = in;
+        }
+    }
+    else
+    {
+        if(p != std::string::npos
+        && !f_flags[static_cast(flag_t::PORT)]
+        && !f_flags[static_cast(flag_t::REQUIRED_PORT)])
+        {
+            emit_error("Port not allowed (" + in + ").");
+            return;
+        }
+
+        if(!in.empty())
+        {
+            address = in;
+        }
+    }
+
+    parse_address_port(address, port_str, result, false);
+}
+
+
+/** \brief Parse one IPv6 address.
+ *
+ * This function checks the input parameter \p in and extracts the
+ * address and port. There is a port if the input strings includes
+ * a `':'` character after the closing square bracket (`']'`).
+ *
+ * If this function detects that a port is not allowed and yet
+ * a `':'` character is found, then it generates an error and
+ * returns without adding anything to `result`.
+ *
+ * \note
+ * This function can be called with an IPv6
+ *
+ * \param[in] in  The input string with the address and optional port.
+ * \param[in,out] result  The list of resulting addresses.
+ */
+void addr_parser::parse_address6(std::string const & in, addr_range::vector_t & result)
+{
+    std::string::size_type p(0);
+
+    std::string address;
+    std::string port_str;
+
+    // if there is an address extract it otherwise put the default
+    //
+    if(!in.empty()
+    && in[0] == '[')
+    {
+        p = in.find(']');
+
+        if(p == std::string::npos)
+        {
+            emit_error("IPv6 is missing the ']' (" + in + ").");
+            return;
+        }
+
+        if(p != 1)
+        {
+            // get the address only if not empty (otherwise we want to
+            // keep the default) -- so we actually support "[]" to
+            // represent "use the default address if defined".
+            //
+            address = in.substr(1, p - 1);
+        }
+    }
+
+    // on entry 'p' is either 0 or the position of the ']' character
+    //
+    p = in.find(':', p);
+
+    if(p != std::string::npos)
+    {
+        if(f_flags[static_cast(flag_t::PORT)]
+        || f_flags[static_cast(flag_t::REQUIRED_PORT)])
+        {
+            // there is also a port, extract it
+            //
+            port_str = in.substr(p + 1);
+        }
+        else if(!f_flags[static_cast(flag_t::PORT)]
+             && !f_flags[static_cast(flag_t::REQUIRED_PORT)])
+        {
+            emit_error("Port not allowed (" + in + ").");
+            return;
+        }
+    }
+
+    parse_address_port(address, port_str, result, true);
+}
+
+
+/** \brief Parse the address and port.
+ *
+ * This function receives an address and a port string and
+ * convert them in an addr object which gets saved in
+ * the specified result range vector.
+ *
+ * The address can be an IPv4 or an IPv6 address.
+ *
+ * The port may be numeric or a name such as `"http"`.
+ *
+ * \note
+ * This function is not responsible for handling the default address
+ * and default port. This is expected to be dealt with by the caller
+ * if required.
+ *
+ * \param[in] address  The address to convert to binary.
+ * \param[in] port_str  The port as a string.
+ * \param[out] result  The range where we save the results.
+ * \param[in] ipv6  Use the default IPv6 address if the address is empty.
+ */
+void addr_parser::parse_address_port(std::string address, std::string port_str, addr_range::vector_t & result, bool ipv6)
+{
+    // make sure the port is good
+    //
+    if(port_str.empty())
+    {
+        if(f_flags[static_cast(flag_t::REQUIRED_PORT)])
+        {
+            emit_error("Required port is missing.");
+            return;
+        }
+        if(f_default_port != -1)
+        {
+            port_str = std::to_string(f_default_port);
+        }
+    }
+
+    // make sure the address is good
+    //
+    if(address.empty())
+    {
+        if(f_flags[static_cast(flag_t::REQUIRED_ADDRESS)])
+        {
+            emit_error("Required address is missing.");
+            return;
+        }
+        // internal default if no address was defined
+        // (TBD: should it be an IPv6 instead?)
+        //
+        if(ipv6)
+        {
+            if(f_default_address6.empty())
+            {
+                address = "::";
+            }
+            else
+            {
+                address = f_default_address6;
+            }
+        }
+        else
+        {
+            if(f_default_address4.empty())
+            {
+                address = "0.0.0.0";
+            }
+            else
+            {
+                address = f_default_address4;
+            }
+        }
+    }
+
+    // prepare hints for the the getaddrinfo() function
+    //
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
+    hints.ai_family = AF_UNSPEC;
+
+    switch(f_protocol)
+    {
+    case IPPROTO_TCP:
+        hints.ai_socktype = SOCK_STREAM;
+        hints.ai_protocol = IPPROTO_TCP;
+        break;
+
+    case IPPROTO_UDP:
+        hints.ai_socktype = SOCK_DGRAM;
+        hints.ai_protocol = IPPROTO_UDP;
+        break;
+
+    }
+
+    // convert address to binary
+    //
+    struct addrinfo * addrlist(nullptr);
+    {
+        errno = 0;
+        int const r(getaddrinfo(address.c_str(), port_str.c_str(), &hints, &addrlist));
+        if(r != 0)
+        {
+            // break on invalid addresses
+            //
+            int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
+            emit_error(
+                      "Invalid address in \""
+                    + address
+                    + (port_str.empty() ? "" : ":")
+                    + port_str
+                    + "\" error "
+                    + std::to_string(r)
+                    + " -- "
+                    + gai_strerror(r)
+                    + (e == 0
+                        ? ""
+                        : " (errno: "
+                        + std::to_string(e)
+                        + " -- "
+                        + strerror(e)
+                        + ")."));
+            return;
+        }
+    }
+    std::shared_ptr ai(addrlist, addrinfo_deleter);
+
+    bool first(true);
+    while(addrlist != nullptr)
+    {
+        // go through the addresses and create ranges and save that in the result
+        //
+        if(addrlist->ai_family == AF_INET)
+        {
+            if(addrlist->ai_addrlen != sizeof(struct sockaddr_in))
+            {
+                emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
+                         + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
+                         + ", expected"                                  // LCOV_EXCL_LINE
+                         + std::to_string(sizeof(struct sockaddr_in))    // LCOV_EXCL_LINE
+                         + ").");                                        // LCOV_EXCL_LINE
+            }
+            else
+            {
+                addr a(*reinterpret_cast(addrlist->ai_addr));
+                // in most cases we do not get a protocol from
+                // the getaddrinfo() function...
+                a.set_protocol(addrlist->ai_protocol);
+                addr_range r;
+                r.set_from(a);
+                result.push_back(r);
+            }
+        }
+        else if(addrlist->ai_family == AF_INET6)
+        {
+            if(addrlist->ai_addrlen != sizeof(struct sockaddr_in6))
+            {
+                emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
+                         + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
+                         + ", expected "                                 // LCOV_EXCL_LINE
+                         + std::to_string(sizeof(struct sockaddr_in6))   // LCOV_EXCL_LINE
+                         + ").");                                        // LCOV_EXCL_LINE
+            }
+            else
+            {
+                addr a(*reinterpret_cast(addrlist->ai_addr));
+                a.set_protocol(addrlist->ai_protocol);
+                addr_range r;
+                r.set_from(a);
+                result.push_back(r);
+            }
+        }
+        else if(first)                                                  // LCOV_EXCL_LINE
+        {
+            // ignore errors from further addresses
+            //
+            emit_error("Unsupported address family "                     // LCOV_EXCL_LINE
+                     + std::to_string(addrlist->ai_family)               // LCOV_EXCL_LINE
+                     + ".");                                             // LCOV_EXCL_LINE
+        }
+
+        first = false;
+
+        addrlist = addrlist->ai_next;
+    }
+}
+
+
+/** \brief Parse a mask.
+ *
+ * If the input string is a decimal number, then use that as the
+ * number of bits to clear.
+ *
+ * If the mask is not just one decimal number, try to convert it
+ * as an address.
+ *
+ * If the string is neither a decimal number nor a valid IP address
+ * then the parser adds an error string to the f_error variable.
+ *
+ * \param[in] mask  The mask to transform to binary.
+ * \param[in,out] cidr  The address to which the mask will be added.
+ */
+void addr_parser::parse_mask(std::string const & mask, addr & cidr)
+{
+    // no mask?
+    //
+    if(mask.empty())
+    {
+        // in the current implementation this cannot happen since we
+        // do not call this function when mask is empty
+        //
+        // hwoever, the algorithm below expect that 'mask' is not
+        // empty (otherwise we get the case of 0 even though it
+        // may not be correct.)
+        //
+        return;  // LCOV_EXCL_LINE
+    }
+
+    // the mask may be a decimal number or an address, if just one number
+    // then it's not an address, so test that first
+    //
+    uint8_t mask_bits[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
+
+    // convert the mask to an integer, if possible
+    //
+    int mask_count(0);
+    {
+        for(char const * s(mask.c_str()); *s != '\0'; ++s)
+        {
+            if(*s >= '0' && *s <= '9')
+            {
+                mask_count = mask_count * 10 + *s - '0';
+                if(mask_count > 1000)
+                {
+                    emit_error("Mask number too large ("
+                             + mask
+                             + ", expected a maximum of 128).");
+                    return;
+                }
+            }
+            else
+            {
+                mask_count = -1;
+                break;
+            }
+        }
+    }
+
+    // the conversion to an integer worked if mask_count != -1
+    //
+    if(mask_count != -1)
+    {
+        if(cidr.is_ipv4())
+        {
+            if(mask_count > 32)
+            {
+                emit_error("Unsupported mask size ("
+                         + std::to_string(mask_count)
+                         + ", expected 32 at the most for an IPv4).");
+                return;
+            }
+            mask_count = 32 - mask_count;
+        }
+        else
+        {
+            if(mask_count > 128)
+            {
+                emit_error("Unsupported mask size ("
+                         + std::to_string(mask_count)
+                         + ", expected 128 at the most for an IPv6).");
+                return;
+            }
+            mask_count = 128 - mask_count;
+        }
+
+        // clear a few bits at the bottom of mask_bits
+        //
+        int idx(15);
+        for(; mask_count > 8; mask_count -= 8, --idx)
+        {
+            mask_bits[idx] = 0;
+        }
+        mask_bits[idx] = 255 << mask_count;
+    }
+    else //if(mask_count < 0)
+    {
+        // prepare hints for the the getaddrinfo() function
+        //
+        struct addrinfo hints;
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
+        hints.ai_family = AF_UNSPEC;
+
+        switch(cidr.get_protocol())
+        {
+        case IPPROTO_TCP:
+            hints.ai_socktype = SOCK_STREAM;
+            hints.ai_protocol = IPPROTO_TCP;
+            break;
+
+        case IPPROTO_UDP:
+            hints.ai_socktype = SOCK_DGRAM;
+            hints.ai_protocol = IPPROTO_UDP;
+            break;
+
+        }
+
+        std::string const port_str(std::to_string(cidr.get_port()));
+
+        // if the mask is an IPv6, then it has to have the '[...]'
+        std::string m(mask);
+        if(cidr.is_ipv4())
+        {
+            if(mask[0] == '[')
+            {
+                emit_error("The address uses the IPv4 syntax, the mask cannot use IPv6.");
+                return;
+            }
+        }
+        else //if(!cidr.is_ipv4())
+        {
+            if(mask[0] != '[')
+            {
+                emit_error("The address uses the IPv6 syntax, the mask cannot use IPv4.");
+                return;
+            }
+            if(mask.back() != ']')
+            {
+                emit_error("The IPv6 mask is missing the ']' (" + mask + ").");
+                return;
+            }
+
+            // note that we know that mask.length() >= 2 here since
+            // we at least have a '[' and ']'
+            //
+            m = mask.substr(1, mask.length() - 2);
+            if(m.empty())
+            {
+                // an empty mask is valid, it just means keep the default
+                // (getaddrinfo() fails on an empty string)
+                //
+                return;
+            }
+        }
+
+        // if negative, we may have a full address here, so call the
+        // getaddrinfo() on this other string
+        //
+        struct addrinfo * masklist(nullptr);
+        errno = 0;
+        int const r(getaddrinfo(m.c_str(), port_str.c_str(), &hints, &masklist));
+        if(r != 0)
+        {
+            // break on invalid addresses
+            //
+            int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
+            emit_error("Invalid mask in \"/"
+                     + mask
+                     + "\", error "
+                     + std::to_string(r)
+                     + " -- "
+                     + gai_strerror(r)
+                     + " (errno: "
+                     + std::to_string(e)
+                     + " -- "
+                     + strerror(e)
+                     + ").");
+            return;
+        }
+        std::shared_ptr mask_ai(masklist, addrinfo_deleter);
+
+        if(cidr.is_ipv4())
+        {
+            if(masklist->ai_family != AF_INET)
+            {
+                // this one happens when the user does not put the '[...]'
+                // around an IPv6 address
+                //
+                emit_error("Incompatible address between the address and"
+                          " mask address (first was an IPv4 second an IPv6).");
+                return;
+            }
+            if(masklist->ai_addrlen != sizeof(struct sockaddr_in))
+            {
+                emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
+                        + std::to_string(masklist->ai_addrlen)          // LCOV_EXCL_LINE
+                        + ", expected"                                  // LCOV_EXCL_LINE
+                        + std::to_string(sizeof(struct sockaddr_in))    // LCOV_EXCL_LINE
+                        + ").");                                        // LCOV_EXCL_LINE
+                return;                                                 // LCOV_EXCL_LINE
+            }
+            memcpy(mask_bits + 12, &reinterpret_cast(masklist->ai_addr)->sin_addr.s_addr, 4); // last 4 bytes are the IPv4 address, keep the rest as 1s
+        }
+        else //if(!cidr.is_ipv4())
+        {
+            if(masklist->ai_family != AF_INET6)
+            {
+                // this one happens if the user puts the '[...]'
+                // around an IPv4 address
+                //
+                emit_error("Incompatible address between the address"
+                          " and mask address (first was an IPv6 second an IPv4).");
+                return;
+            }
+            if(masklist->ai_addrlen != sizeof(struct sockaddr_in6))
+            {
+                emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
+                         + std::to_string(masklist->ai_addrlen)         // LCOV_EXCL_LINE
+                         + ", expected "                                // LCOV_EXCL_LINE
+                         + std::to_string(sizeof(struct sockaddr_in6))  // LCOV_EXCL_LINE
+                         + ").");                                       // LCOV_EXCL_LINE
+                return;                                                 // LCOV_EXCL_LINE
+            }
+            memcpy(mask_bits, &reinterpret_cast(masklist->ai_addr)->sin6_addr.s6_addr, 16);
+        }
+    }
+
+    cidr.set_mask(mask_bits);
+}
+
+
+/** \brief Transform a string into an `addr` object.
+ *
+ * This function converts the string \p a in an IP address saved in
+ * the returned addr object or throws an error if the conversion
+ * fails.
+ *
+ * The \p default_address parameter string can be set to an address
+ * which is returned if the input in \p a does not include an
+ * address such as in ":123".
+ *
+ * The \p port parameter can be specified or set to -1. If -1, then
+ * there is no default port. Either way, the port can be defined in
+ * \p a.
+ *
+ * The protocol can be specified, as a string. For example, you can
+ * use "tcp". The default is no specific protocol which means any
+ * type of IP address can be returned. Note that if more than one
+ * result is returned when the protocol was not specified, the
+ * results will be filtered to only keep the address that uses the
+ * TCP protocol. If as a result we have a single address, then that
+ * result gets returned.
+ *
+ * \note
+ * This function does not allow for address or port ranges. It is
+ * expected to return exactly one address. You can allow a \p mask
+ * by setting that parameter to true.
+ *
+ * \param[in] a  The address string to be converted.
+ * \param[in] default_addrress  The default address or an empty string.
+ * \param[in] default_port  The default port or -1
+ * \param[in] protocol  The protocol the address has to be of, or the
+ *                      empty string to allow any protocol.
+ * \param[in] m  Whether to allow a mask (true) or not (false).
+ *
+ * \return The address converted in an `addr` object.
+ */
+addr string_to_addr(
+          std::string const & a
+        , std::string const & default_address
+        , int default_port
+        , std::string const & protocol
+        , bool mask)
+{
+    addr_parser p;
+
+    if(!default_address.empty())
+    {
+        p.set_default_address(default_address);
+    }
+
+    if(default_port != -1)
+    {
+        p.set_default_port(default_port);
+    }
+
+    if(!protocol.empty())
+    {
+        p.set_protocol(protocol);
+    }
+
+    p.set_allow(addr_parser::flag_t::MASK, mask);
+
+    addr_range::vector_t result(p.parse(a));
+
+    if(result.size() != 1)
+    {
+        // when the protocol is not specified, this happens like all the
+        // time, we search for an entry with protocol TCP by default
+        // because in most cases that's what people want
+        //
+        if(protocol.empty())
+        {
+            result.erase(
+                      std::remove_if(
+                          result.begin()
+                        , result.end()
+                        , [](auto const it)
+                        {
+                            return it.has_from() && it.get_from().get_protocol() != IPPROTO_TCP;
+                        })
+                    , result.end());
+        }
+        if(result.size() != 1)
+        {
+            // an invalid protocol is caught by the set_protocol()
+            // function so we should never be able to reach here
+            //
+            throw addr_invalid_argument(                                                            // LCOV_EXCL_LINE
+                      "the address \""                                                              // LCOV_EXCL_LINE
+                    + a                                                                             // LCOV_EXCL_LINE
+                    + "\" could not be converted to a single address in string_to_addr(), found "   // LCOV_EXCL_LINE
+                    + std::to_string(result.size())                                                 // LCOV_EXCL_LINE
+                    + " entries instead.");                                                         // LCOV_EXCL_LINE
+        }
+    }
+
+    // at the movement we only can get a "to" so the following exceptions
+    // can't happen which is why we have an LCOV_EXCL_LINE
+    //
+    if(result[0].has_to()
+    || result[0].is_range())
+    {
+        throw addr_invalid_argument("string_to_addr() does not support ranges.");     // LCOV_EXCL_LINE
+    }
+
+    if(!result[0].has_from())
+    {
+        throw addr_invalid_argument("string_to_addr() has no 'from' address.");       // LCOV_EXCL_LINE
+    }
+
+    return result[0].get_from();
+}
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/addr_parser.h libaddr-1.0.18.0~xenial/libaddr/addr_parser.h
--- libaddr-1.0.17.0~xenial/libaddr/addr_parser.h	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/addr_parser.h	2019-09-02 21:51:26.000000000 +0000
@@ -0,0 +1,122 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#pragma once
+
+/** \file
+ * \brief The declaration of the parser of the libaddr classes.
+ *
+ * This header includes the addr_parser class used to parse user input
+ * string and convert them to binary IP addresses.
+ */
+
+// self
+//
+#include    "libaddr/addr_range.h"
+
+
+
+namespace addr
+{
+
+
+class addr_parser
+{
+public:
+    enum class flag_t
+    {
+        ADDRESS,                            // address (IP)
+        REQUIRED_ADDRESS,                   // address cannot be empty
+        PORT,                               // port
+        REQUIRED_PORT,                      // port cannot be empty
+        MASK,                               // mask
+        MULTI_ADDRESSES_COMMAS,             // IP:port/mask,IP:port/mask,...
+        MULTI_ADDRESSES_SPACES,             // IP:port/mask IP:port/mask ...
+        MULTI_ADDRESSES_COMMAS_AND_SPACES,  // IP:port/mask, IP:port/mask, ...
+
+        // the following are not yet implemented
+        MULTI_PORTS_SEMICOLONS,             // port1;port2;...
+        MULTI_PORTS_COMMAS,                 // port1,port2,...
+        PORT_RANGE,                         // port1-port2
+        ADDRESS_RANGE,                      // IP-IP
+
+        FLAG_max
+    };
+
+    void                    set_default_address(std::string const & address);
+    std::string const &     get_default_address4() const;
+    std::string const &     get_default_address6() const;
+
+    void                    set_default_port(int const port);
+    int                     get_default_port() const;
+
+    void                    set_default_mask(std::string const & mask);
+    std::string const &     get_default_mask4() const;
+    std::string const &     get_default_mask6() const;
+
+    void                    set_protocol(std::string const & protocol);
+    void                    set_protocol(int const protocol);
+    void                    clear_protocol();
+    int                     get_protocol() const;
+
+    void                    set_allow(flag_t const flag, bool const allow);
+    bool                    get_allow(flag_t const flag) const;
+
+    bool                    has_errors() const;
+    void                    emit_error(std::string const & msg);
+    std::string const &     error_messages() const;
+    int                     error_count() const;
+    void                    clear_errors();
+
+    addr_range::vector_t    parse(std::string const & in);
+
+private:
+    void                    parse_cidr(std::string const & in, addr_range::vector_t & result);
+    void                    parse_address(std::string const & in, std::string const & mask, addr_range::vector_t & result);
+    void                    parse_address4(std::string const & in, addr_range::vector_t & result);
+    void                    parse_address6(std::string const & in, addr_range::vector_t & result);
+    void                    parse_address_port(std::string address, std::string port_str, addr_range::vector_t & result, bool ipv6);
+    void                    parse_mask(std::string const & mask, addr & cidr);
+
+    bool                    f_flags[static_cast(flag_t::FLAG_max)] = { true, false, true, false, false, false, false, false, false, false, false, false };
+    std::string             f_default_address4 = std::string();
+    std::string             f_default_address6 = std::string();
+    std::string             f_default_mask4 = std::string();
+    std::string             f_default_mask6 = std::string();
+    int                     f_protocol = -1;
+    int                     f_default_port = -1;
+    std::string             f_error = std::string();
+    int                     f_error_count = 0;
+};
+
+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);
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/addr_range.cpp libaddr-1.0.18.0~xenial/libaddr/addr_range.cpp
--- libaddr-1.0.17.0~xenial/libaddr/addr_range.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/addr_range.cpp	2019-09-02 20:15:53.000000000 +0000
@@ -0,0 +1,359 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+/** \file
+ * \brief The implementation of the addr_range class.
+ *
+ * This file includes the implementation of the addr_range class
+ * and the address_match_ranges() global function.
+ */
+
+// self
+//
+#include    "libaddr/addr_range.h"
+#include    "libaddr/addr_exception.h"
+
+
+// C++ library
+//
+#include    
+
+
+// last include
+//
+#include    
+
+
+
+namespace addr
+{
+
+
+/** \brief Return true if the range has a 'from' address defined.
+ *
+ * By default the 'from' and 'to' addresses of an addr_range are legal
+ * but considered undefined. After you called the set_from() function
+ * once, this function will always return true.
+ *
+ * \return false until 'set_from()' is called at least once.
+ */
+bool addr_range::has_from() const
+{
+    return f_has_from;
+}
+
+
+/** \brief Return true if the range has a 'to' address defined.
+ *
+ * By default the 'from' and 'to' addresses of an addr_range are legal
+ * but considered undefined. After you called the set_to() function
+ * once, this function will always return true.
+ *
+ * \return false until 'set_to()' is called at least once.
+ */
+bool addr_range::has_to() const
+{
+    return f_has_to;
+}
+
+
+/** \brief Determine whether an addr_range object is considered a range.
+ *
+ * This function returns false until both, set_from() and set_to(),
+ * were called.
+ *
+ * Note that the order in which the two functions get called is not
+ * important, although we generally expect set_from() to be called
+ * first, it does not matter.
+ *
+ * \return true if both, 'from' and 'to', were set.
+ */
+bool addr_range::is_range() const
+{
+    return f_has_from && f_has_to;
+}
+
+
+/** \brief Check whether this range is empty.
+ *
+ * If you defined the 'from' and 'to' addresses of the range, then you
+ * can check whether the range is empty or not.
+ *
+ * A range is considered empty if 'from' is larger than 'to' because
+ * in that case nothing can appear in between (no IP can at the same
+ * time be larger than 'from' and smaller than 'to' if 'from > to'
+ * is true.)
+ *
+ * \return true if 'from > to' and is_range() returns true.
+ *
+ * \sa is_range()
+ * \sa has_from()
+ * \sa has_to()
+ */
+bool addr_range::is_empty() const
+{
+    if(!is_range())
+    {
+        return false;
+    }
+    return f_from > f_to;
+}
+
+
+/** \brief Check whether \p rhs is part of this range.
+ *
+ * If the address specified in rhs is part of this range, then the function
+ * returns true. The 'from' and 'to' addresses are considered inclusive,
+ * so if rhs is equal to 'from' or 'to', then the function returns true.
+ *
+ * If 'from' is larger than 'to' then the function already returns false
+ * since the range represents an empty range.
+ *
+ * \exception addr_invalid_state_exception
+ * The addr_range object must be a range or this function throws this
+ * exception. To test whether you can call this function, first call
+ * the is_range() function. If it returns true, then is_in() is available.
+ *
+ * \param[in] rhs  The address to check for inclusion.
+ *
+ * \return true if rhs is considered part of this range.
+ */
+bool addr_range::is_in(addr const & rhs) const
+{
+    if(!is_range())
+    {
+        throw addr_invalid_state("addr_range::is_in(): range is not complete (from or to missing.)");
+    }
+
+    if(f_from <= f_to)
+    {
+        //
+        return rhs >= f_from && rhs <= f_to;
+    }
+    //else -- from/to are swapped... this represents an empty range
+
+    return false;
+}
+
+
+/** \brief Set 'from' address.
+ *
+ * This function saves the 'from' address in this range object.
+ *
+ * Once this function was called at least once, the has_from() returns true.
+ *
+ * \param[in] from  The address to save as the 'from' address.
+ */
+void addr_range::set_from(addr const & from)
+{
+    f_has_from = true;
+    f_from = from;
+}
+
+
+/** \brief Get 'from' address.
+ *
+ * This function return the 'from' address as set by the set_from()
+ * functions.
+ *
+ * The get_from() function can be called even if the has_from()
+ * function returns false. It will return a default address
+ * (a new 'addr' object.)
+ *
+ * \return The address saved as the 'from' address.
+ */
+addr & addr_range::get_from()
+{
+    return f_from;
+}
+
+
+/** \brief Get the 'from' address when addr_range is constant.
+ *
+ * This function return the 'from' address as set by the set_from()
+ * functions.
+ *
+ * The get_from() function can be called even if the has_from()
+ * function returns false. It will return a default address
+ * (a new 'addr' object.)
+ *
+ * \return The address saved as the 'from' address.
+ */
+addr const & addr_range::get_from() const
+{
+    return f_from;
+}
+
+
+/** \brief Set 'to' address.
+ *
+ * This function saves the 'to' address in this range object.
+ *
+ * Once this function was called at least once, the has_to() returns true.
+ *
+ * \param[in] to  The address to save as the 'to' address.
+ */
+void addr_range::set_to(addr const & to)
+{
+    f_has_to = true;
+    f_to = to;
+}
+
+
+/** \brief Get the 'to' address.
+ *
+ * This function return the 'to' address as set by the set_to()
+ * function.
+ *
+ * The get_from() function can be called even if the has_from()
+ * function returns false. It will return a default address
+ * (a new 'addr' object.)
+ *
+ * \return The address saved as the 'to' address.
+ */
+addr & addr_range::get_to()
+{
+    return f_to;
+}
+
+
+/** \brief Get the 'to' address when addr_range is constant.
+ *
+ * This function return the 'to' address as set by the set_to()
+ * function.
+ *
+ * The get_to() function can be called even if the has_to()
+ * function returns false. It will return a default address
+ * (a new 'addr' object.)
+ *
+ * \return The address saved as the 'to' address.
+ */
+addr const & addr_range::get_to() const
+{
+    return f_to;
+}
+
+
+/** \brief Compute a new range with the part that is shared between both inputs.
+ *
+ * This function computers a range which encompasses all the addresses found
+ * in \p this range and \p rhs range.
+ *
+ * If the two range do not intersect, then the resulting range will be an
+ * empty range (see is_empty() for details).
+ *
+ * The new range receives the largest 'from' address from both inputs and
+ * the smallest 'to' address from both inputs.
+ *
+ * \param[in] rhs  The other range to compute the intersection with.
+ *
+ * \return The resulting intersection range.
+ */
+addr_range addr_range::intersection(addr_range const & rhs) const
+{
+    addr_range result;
+
+    result.set_from(f_from > rhs.f_from ? f_from : rhs.f_from);
+    result.set_to  (f_to   < rhs.f_to   ? f_to   : rhs.f_to);
+
+    return result;
+}
+
+
+/** \brief Check whether an address matches a range.
+ *
+ * This function checks whether an address matches a range of addresses.
+ *
+ * The range may be empty, in which case the result is always false.
+ *
+ * If the range is a range (i.e. 'from' and 'to' are both defined,)
+ * then the is_in() function is used to determine whether the address
+ * is a match.
+ *
+ * If only one of the 'from' or 'to' addresses is defined, then that
+ * one address addr::match() function is used to determine whether the
+ * input \p address is a match.
+ *
+ * \param[in] address  The address to match against a range of addresses.
+ *
+ * \return true if address matches this range.
+ */
+bool addr_range::match(addr const & address) const
+{
+    // if neith 'from' nor 'to' were defined, return
+    //
+    if(is_empty())
+    {
+        return false;
+    }
+
+    if(is_range())
+    {
+        return is_in(address);
+    }
+
+    if(has_from())
+    {
+        return f_from.match(address);
+    }
+    else
+    {
+        // if not empty and it does not have 'from', it has to be 'to'
+        //
+        return f_to.match(address);
+    }
+}
+
+
+/** \brief Check whether an address matches a range.
+ *
+ * When you call the addr_parser::parse() function, you get a vector of
+ * ranges as a result. This function allows you to check whether an
+ * address matches any one of those ranges.
+ *
+ * \param[in] ranges  The vector of ranges to search for \p address.
+ * \param[in] address  The address to search in \p ranges.
+ *
+ * \return true if \p address matches any one of the \p ranges.
+ */
+bool address_match_ranges(addr_range::vector_t ranges, addr const & address)
+{
+    auto const it(std::find_if
+            ( ranges.begin()
+            , ranges.end()
+            , [&address](auto const & range)
+                {
+                    return range.match(address);
+                }
+            ));
+
+    return it != ranges.end();
+}
+
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/addr_range.h libaddr-1.0.18.0~xenial/libaddr/addr_range.h
--- libaddr-1.0.17.0~xenial/libaddr/addr_range.h	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/addr_range.h	2019-09-02 20:15:57.000000000 +0000
@@ -0,0 +1,85 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#pragma once
+
+/** \file
+ * \brief The addr_range class.
+ *
+ * This header defines the addr_range class used to handle one range of
+ * addresses (with a 'from' and a 'to' pair of addresses.)
+ *
+ * Also we support CIDR, a CIDR is not a range. A range can define anything
+ * that is not a perfect CIDR match. For example, you could have a start
+ * address of 192.168.10.5 and an end address of 192.168.10.10.
+ */
+
+// self
+//
+#include    "libaddr/addr.h"
+
+
+
+namespace addr
+{
+
+
+
+
+class addr_range
+{
+public:
+    typedef std::shared_ptr     pointer_t;
+    typedef std::vector         vector_t;
+
+    bool                            has_from() const;
+    bool                            has_to() const;
+    bool                            is_range() const;
+    bool                            is_empty() const;
+    bool                            is_in(addr const & rhs) const;
+
+    void                            set_from(addr const & from);
+    addr &                          get_from();
+    addr const &                    get_from() const;
+    void                            set_to(addr const & to);
+    addr &                          get_to();
+    addr const &                    get_to() const;
+
+    addr_range                      intersection(addr_range const & rhs) const;
+    bool                            match(addr const & address) const;
+
+private:
+    bool                            f_has_from = false;
+    bool                            f_has_to = false;
+    addr                            f_from = addr();
+    addr                            f_to = addr();
+};
+
+
+bool address_match_ranges(addr_range::vector_t ranges, addr const & address);
+
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/CMakeLists.txt libaddr-1.0.18.0~xenial/libaddr/CMakeLists.txt
--- libaddr-1.0.17.0~xenial/libaddr/CMakeLists.txt	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/CMakeLists.txt	2019-09-02 20:33:12.000000000 +0000
@@ -0,0 +1,84 @@
+# Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+#
+# https://snapwebsites.org/project/libaddr
+# contact@m2osw.com
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+##
+## addr library
+##
+project(addr)
+
+configure_file(
+    version.h.in
+    ${PROJECT_BINARY_DIR}/version.h
+)
+
+include_directories(
+    ${LIBEXCEPT_INCLUDE_DIRS}
+)
+
+add_library(${PROJECT_NAME} SHARED
+    addr.cpp
+    addr_parser.cpp
+    addr_range.cpp
+    iface.cpp
+    route.cpp
+    version.cpp
+)
+
+set_target_properties(${PROJECT_NAME} PROPERTIES
+    VERSION
+        ${LIBADDR_VERSION_MAJOR}.${LIBADDR_VERSION_MINOR}
+
+    SOVERSION
+        ${LIBADDR_VERSION_MAJOR}
+)
+
+target_link_libraries(${PROJECT_NAME}
+    ${LIBEXCEPT_LIBRARIES}
+)
+
+install(
+    TARGETS
+        ${PROJECT_NAME}
+
+    LIBRARY DESTINATION
+        lib
+)
+
+install(
+    FILES
+        addr.h
+        addr_exception.h
+        addr_parser.h
+        addr_range.h
+        iface.h
+        route.h
+        ${CMAKE_CURRENT_BINARY_DIR}/version.h
+
+    DESTINATION
+        include/libaddr
+)
+
+
+# vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/iface.cpp libaddr-1.0.18.0~xenial/libaddr/iface.cpp
--- libaddr-1.0.17.0~xenial/libaddr/iface.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/iface.cpp	2019-09-02 20:16:06.000000000 +0000
@@ -0,0 +1,620 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+/** \file
+ * \brief The implementation of the addr class.
+ *
+ * This file includes the implementation of the addr class. The one that
+ * deals with low level classes.
+ */
+
+
+// addr library
+//
+#include    "libaddr/iface.h"
+#include    "libaddr/route.h"
+
+
+// C++ library
+//
+#include    
+#include    
+
+
+// C library
+//
+#include    
+#include    
+
+
+// last include
+//
+#include    
+
+
+
+namespace addr
+{
+
+
+/** \brief Details used by the addr class implementation.
+ *
+ * We have a function to check whether an address is part of
+ * the interfaces of your computer. This check requires the
+ * use of a `struct ifaddrs` and as such it requires to
+ * delete that structure. We define a deleter for that
+ * strucure here.
+ */
+namespace
+{
+
+/** \brief Delete an ifaddrs structure.
+ *
+ * This deleter is used to make sure all the ifaddrs get released when
+ * an exception occurs or the function using such exists.
+ *
+ * \param[in] ia  The ifaddrs structure to free.
+ */
+void ifaddrs_deleter(struct ifaddrs * ia)
+{
+    freeifaddrs(ia);
+}
+
+
+}
+// no name namespace
+
+
+
+
+
+/** \brief Initializes an interface name/index pair.
+ *
+ * This function creates an interface name/index object.
+ *
+ * In some circumstances (NETLINK) you need to specify the index of an
+ * interface. The kernel keeps a list of interface by index starting at 1
+ * and each have a name such as "eth0". This function initializes
+ * one of those pairs.
+ *
+ * \param[in] index  The index of the interface.
+ * \param[in] name  The name of the interface.
+ */
+iface_index_name::iface_index_name(int index, std::string const & name)
+    : f_index(index)
+    , f_name(name)
+{
+}
+
+
+/** \brief Get the index of this interface.
+ *
+ * This function is used to retrieve the index of a name/index pair.
+ *
+ * \return The index of the name/index pair.
+ */
+int iface_index_name::get_index() const
+{
+    return f_index;
+}
+
+
+/** \brief Get the name of this interface.
+ *
+ * This function is used to retrieve the name of a name/index pair.
+ *
+ * \return The name of the name/index pair.
+ */
+std::string const & iface_index_name::get_name() const
+{
+    return f_name;
+}
+
+
+
+/** \brief Get the list of existing interfaces.
+ *
+ * This function gathers the complete list of interfaces by index and
+ * name pairs. The result is a vector of iface_index_name objects.
+ *
+ * Note that over time the index of an interface can change since interfaces
+ * can be added and removed from your network configuration. It is a good
+ * idea to not cache that information.
+ *
+ * \return A vector of index/name pair objects.
+ */
+iface_index_name::vector_t get_interface_name_index()
+{
+    iface_index_name::vector_t result;
+
+    // the index starts at 1
+    //
+    for(int index(1);; ++index)
+    {
+        // get the next interface name by index
+        //
+        char name[IF_NAMESIZE + 1];
+        if(if_indextoname(index, name) == nullptr)
+        {
+            return result;
+        }
+
+        // make sure the name is null terminated
+        //
+        name[IF_NAMESIZE] = '\0';
+
+        iface_index_name const in(index, name);
+        result.push_back(in);
+    }
+    // NOT REACHED, the loop is broken by a "return"
+}
+
+
+/** \brief Get the index of an interface from its name.
+ *
+ * If you are given the name of an interface, you can retrieve its index
+ * by calling this function. The resulting value is the index from 1 to n.
+ *
+ * If the named interface is not found, then the function returns 0.
+ *
+ * \return The interface index or 0 on error.
+ */
+unsigned int get_interface_index_by_name(std::string const & name)
+{
+    return if_nametoindex(name.c_str());
+}
+
+
+
+
+
+
+/** \brief Return a list of local addresses on this machine.
+ *
+ * Peruse the list of available interfaces, and return any detected
+ * ip addresses in a vector.
+ *
+ * These addresses include:
+ *
+ * \li A mask whenever available (very likely if the interface is up).
+ * \li A name you can retrieve with get_iface_name()
+ * \li A set of flags defining the current status of the network interface
+ *     (i.e. IFF_UP, IFF_BROADCAST, IFF_NOARP, etc.)
+ *
+ * \return A vector of all the local interface IP addresses.
+ */
+iface::vector_t iface::get_local_addresses()
+{
+    // get the list of interface addresses
+    //
+    struct ifaddrs * ifa_start(nullptr);
+    if(getifaddrs(&ifa_start) != 0)
+    {
+        // TODO: Should this throw, or just return an empty list quietly?
+        //
+        return iface::vector_t(); // LCOV_EXCL_LINE
+    }
+
+    std::shared_ptr auto_free(ifa_start, ifaddrs_deleter);
+
+    uint8_t mask[16];
+    iface::vector_t iface_list;
+    for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
+    {
+        // the documentation says there may be no address at all
+        // skip such entries at the moment
+        //
+        if(ifa->ifa_addr == nullptr)
+        {
+            continue;
+        }
+
+        // initialize an interface
+        iface the_interface;
+
+        // copy the name and flags as is
+        //
+        // TBD: can the ifa_name even be a null pointer?
+        //
+        the_interface.f_name = ifa->ifa_name;
+        the_interface.f_flags = ifa->ifa_flags; // IFF_... flags (see `man 7 netdevice` search for SIOCGIFFLAGS)
+
+        // get the family to know how to treat the address
+        //
+        // when an interface has an IPv4 and an IPv6, there are two entries in
+        // the list, both with the same name
+        //
+        uint16_t const family(ifa->ifa_addr->sa_family);
+
+        switch(family)
+        {
+        case AF_INET:
+            the_interface.f_address.set_ipv4(*(reinterpret_cast(ifa->ifa_addr)));
+
+            if((ifa->ifa_flags & IFF_BROADCAST) != 0
+            && ifa->ifa_broadaddr != nullptr)
+            {
+                the_interface.f_broadcast_address.set_ipv4(*(reinterpret_cast(ifa->ifa_broadaddr)));
+            }
+            if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
+            && ifa->ifa_dstaddr != nullptr)  // LCOV_EXCL_LINE
+            {
+                the_interface.f_destination_address.set_ipv4(*(reinterpret_cast(ifa->ifa_dstaddr)));  // LCOV_EXCL_LINE
+            }
+
+            // if present, add the mask as well
+            //
+            if(ifa->ifa_netmask != nullptr)
+            {
+                // for the IPv4 mask, we have to break it down in such a
+                // way as to make it IPv6 compatible
+                //
+                memset(mask, 255, 12);
+                mask[12] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >>  0;
+                mask[13] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >>  8;
+                mask[14] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >> 16;
+                mask[15] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >> 24;
+                the_interface.f_address.set_mask(mask);
+            }
+            break;
+
+        case AF_INET6:
+            the_interface.f_address.set_ipv6(*(reinterpret_cast(ifa->ifa_addr)));
+
+            if((ifa->ifa_flags & IFF_BROADCAST) != 0
+            && ifa->ifa_broadaddr != nullptr)
+            {
+                the_interface.f_broadcast_address.set_ipv6(*(reinterpret_cast(ifa->ifa_broadaddr)));  // LCOV_EXCL_LINE
+            }
+            if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
+            && ifa->ifa_dstaddr != nullptr)  // LCOV_EXCL_LINE
+            {
+                the_interface.f_destination_address.set_ipv6(*(reinterpret_cast(ifa->ifa_dstaddr)));  // LCOV_EXCL_LINE
+            }
+
+            // if present, add the mask as well
+            //
+            if(ifa->ifa_netmask != nullptr)
+            {
+                the_interface.f_address.set_mask(reinterpret_cast(&reinterpret_cast(ifa->ifa_netmask)->sin6_addr));
+            }
+            break;
+
+        default:
+            // TODO: can we just ignore unexpected address families?
+            //throw addr_invalid_structure("Unknown address family!");
+            continue;
+
+        }
+
+        iface_list.push_back(the_interface);
+    }
+
+    return iface_list;
+}
+
+
+/** \brief Get the interface name.
+ *
+ * This function returns the name of the interface such as 'eth0' or 'p4p1'.
+ *
+ * The name is used in a few places such as the ioctl() function with the
+ * SIOCGIFMTU command. Otherwise, it's mainly for display and easing use
+ * (i.e. to let users select which interface to connect to.)
+ *
+ * \return The interface name.
+ */
+std::string iface::get_name() const
+{
+    return f_name;
+}
+
+
+/** \brief Get the interface setup flags.
+ *
+ * This function returns a set of flags defined on that interface. The flags
+ * are defined in the `man 7 netdevice` as the IFF_... flags. The flags are
+ * defined under the SIOCGIFFLAGS and SIOCSIFFLAGS entries.
+ *
+ * One flag of interest is the IFF_UP flag. This means the interface is
+ * active (even if not actually in use.)
+ *
+ * \return The interface flags.
+ */
+unsigned int iface::get_flags() const
+{   
+    return f_flags;
+}
+
+
+/** \brief Get this interface address.
+ *
+ * This function returns the address of the interface. This address is very
+ * likely to have a mask (i.e. 192.168.0.0/255.255.0.0).
+ *
+ * The address may be an IPv4 or an IPv6 address.
+ *
+ * \return The address of the interface.
+ */
+addr const & iface::get_address() const
+{
+    return f_address;
+}
+
+
+/** \brief Get the broadcast address.
+ *
+ * This function returns a constant reference to the broadcast address
+ * of this interface. The address is always available in this class. It
+ * will be set to the ANY address if it was not defined. Note, however,
+ * that even though the ANY address is not a valid broadcast address,
+ * you should call the has_broadcast_address() function to know whether
+ * this address is indeed defined.
+ *
+ * \return The broadcast address of this interface.
+ */
+addr const & iface::get_broadcast_address() const
+{
+    return f_broadcast_address;
+}
+
+
+/** \brief Get the destination address.
+ *
+ * This function returns a constant reference to the destination address
+ * of this interface. The address is always available in this class. It
+ * will be set to the ANY address if it was not defined. Note, however,
+ * that the ANY address is a valid destination address (i.e. default
+ * route).
+ *
+ * To know whether the destination address is defined in that interface,
+ * make sure to call the has_destination_address() function first.
+ *
+ * \return The destination address of this interface.
+ */
+addr const & iface::get_destination_address() const
+{
+    return f_destination_address;
+}
+
+
+/** \brief Check whether a broadcast address.
+ *
+ * The broadcast address is not present on all interfaces. When it is, this
+ * function returns true.
+ *
+ * Note that you can always call the get_broadcast_address(), but if
+ * undefined it will appear as a default address (NETWORK_TYPE_ANY)
+ * which you can't distinguish from a valid address although a
+ * the NETWORK_TYPE_ANY is not a valid address for a broacast
+ * address.
+ *
+ * \note
+ * When a broadcast address is defined on an interface, then there can't
+ * be a destination address.
+ *
+ * \return true if a broadcast address is defined.
+ */
+bool iface::has_broadcast_address() const
+{
+    return (f_flags & IFF_BROADCAST) != 0;
+}
+
+
+/** \brief Check whether this interface defines a destination address.
+ *
+ * This function returns true if this interface defined a destination
+ * address. Either way you can call the get_destination_address()
+ * function, however, the address will be of type NETWORK_TYPE_ANY
+ * which does not tell you whether it was defined or not.
+ *
+ * Ethernet and the local interfaces all define a destination address.
+ *
+ * \note
+ * The destination address is not assigned any specific mask (all
+ * are ff or 255).
+ *
+ * \note
+ * When there is a destination address defined on an interface, then
+ * there can't be a broadcast address.
+ *
+ * \return true when that interface defined a destination address.
+ */
+bool iface::has_destination_address() const
+{
+    return (f_flags & IFF_POINTOPOINT) != 0;
+}
+
+
+/** \brief Search for the interface corresponding to this address.
+ *
+ * Peruse the list of available interfaces and return the one that matches
+ * this address if any, otherwise return a null pointer.
+ *
+ * Say you create an addr object with the IP address "127.0.0.1" and then
+ * call this function. You will get a pointer to the "lo" interface and
+ * can check the validity of the flags (i.e. is the interface UP, can it
+ * BROADCAST or MULTICAST your UDP packets, etc.)
+ *
+ * If the address is a remote address, then this function returns a null
+ * pointer.
+ *
+ * \note
+ * This function replaces the addr::is_computer_interface_address() function.
+ * If this function returns a non-null pointer when allow_default_destination
+ * set to false, then you've got the same result plus you have access to all
+ * the available information from that interface.
+ *
+ * \warning
+ * If you allow for the default destination, this function calls the
+ * route::get_ipv4_routes() function which can be costly. Try to avoid
+ * doing that in a loop.
+ *
+ * \param[in] a  The address used to search for an interface.
+ * \param[in] allow_default_destination  If true and \p a doesn't match
+ *            any of the interfaces, use the one interface with its
+ *            destination set to 0.0.0.0 or equivalent.
+ *
+ * \return A pointer to an interface IP address.
+ */
+iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
+{
+    iface::vector_t interfaces(iface::get_local_addresses());
+
+    for(auto i : interfaces)
+    {
+        if(i.get_address().match(a))
+        {
+            return iface::pointer_t(new iface(i));
+        }
+    }
+
+    // if there is a default, keep a copy in case we do not find a
+    // local address while looking (and only if the user requested
+    // such, which is the default)
+    //
+    if(!allow_default_destination)
+    {
+        return iface::pointer_t();
+    }
+
+    // to determine the default interface, we need the list of routes
+    // so we first gather that information and then search for the
+    // interface that has that name
+    //
+    route::vector_t routes(route::get_ipv4_routes());
+    route::pointer_t default_route(find_default_route(routes));
+    if(default_route == nullptr)
+    {
+        return iface::pointer_t(); // LCOV_EXCL_LINE
+    }
+
+    std::string const & default_iface(default_route->get_interface_name());
+    auto it(std::find_if(
+              interfaces.cbegin()
+            , interfaces.cend()
+            , [default_iface](auto & i)
+            {
+                return i.get_name() == default_iface;
+            }));
+    if(it == interfaces.cend())
+    {
+        return iface::pointer_t(); // LCOV_EXCL_LINE
+    }
+
+    return iface::pointer_t(new iface(*it));
+}
+
+
+#if 0
+/** \brief Check whether this address represents this computer.
+ *
+ * This function reads the addresses as given to us by the getifaddrs()
+ * function. This is a system function that returns a complete list of
+ * all the addresses this computer is managing / represents. In other
+ * words, a list of address that other computers can use to connect
+ * to this computer (assuming proper firewall, of course.)
+ *
+ * \warning
+ * The list of addresses from getifaddrs() is not being cached. So you
+ * probably do not want to call this function in a loop. That being
+ * said, I still would imagine that retrieving that list is fast.
+ *
+ * \todo
+ * We need to apply the mask to make this work properly. This is why
+ * the current implementation fails big time (used by snapcommunicator.cpp).
+ *
+ * \return a computer_interface_address_t enumeration: error, true, or
+ *         false at this time; on error errno should be set to represent
+ *         what the error was.
+ */
+
+// replaced by with a pointer_t == nullptr if there was no match,
+// although make sure to set allow_default_destination to false
+//
+iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
+
+bool is_computer_interface_address(addr const & a)
+{
+    iface::vector_t interfaces(iface::get_local_addresses());
+
+    for(auto i : interfaces)
+    {
+    }
+
+
+    // TBD: maybe we could cache the ifaddrs for a small amount of time
+    //      (i.e. 1 minute) so additional calls within that time
+    //      can go even faster?
+    //
+
+    // get the list of interface addresses
+    //
+    struct ifaddrs * ifa_start(nullptr);
+    if(getifaddrs(&ifa_start) != 0)
+    {
+        return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
+    }
+    std::shared_ptr auto_free(ifa_start, ifaddrs_deleter);
+
+    bool const ipv4(a.is_ipv4());
+    uint16_t const family(ipv4 ? AF_INET : AF_INET6);
+    for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
+    {
+        if(ifa->ifa_addr != nullptr
+        && ifa->ifa_addr->sa_family == family)
+        {
+            if(ipv4)
+            {
+                // the interface address structure is a 'struct sockaddr_in'
+                //
+                if(memcmp(&reinterpret_cast(ifa->ifa_addr)->sin_addr,
+                            f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast(&f_address)->sin_addr,
+                            sizeof(struct in_addr)) == 0)
+                {
+                    return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
+                }
+            }
+            else
+            {
+                // the interface address structure is a 'struct sockaddr_in6'
+                //
+                if(memcmp(&reinterpret_cast(ifa->ifa_addr)->sin6_addr,
+                            &f_address.sin6_addr,
+                            sizeof(f_address.sin6_addr)) == 0)
+                {
+                    return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
+                }
+            }
+        }
+    }
+
+    return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
+}
+#endif
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/iface.h libaddr-1.0.18.0~xenial/libaddr/iface.h
--- libaddr-1.0.17.0~xenial/libaddr/iface.h	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/iface.h	2019-09-02 20:13:33.000000000 +0000
@@ -0,0 +1,93 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#pragma once
+
+/** \file
+ * \brief The various libaddr classes.
+ *
+ * This header includes the base addr class used to handle one binary
+ * address.
+ */
+
+// addr library
+//
+#include    "libaddr/addr.h"
+
+
+
+namespace addr
+{
+
+
+class iface_index_name
+{
+public:
+    typedef std::vector   vector_t;
+
+                                    iface_index_name(int index, std::string const & name);
+
+    int                             get_index() const;
+    std::string const &             get_name() const;
+
+private:
+    int                             f_index;
+    std::string                     f_name;
+};
+
+iface_index_name::vector_t          get_interface_name_index();
+
+
+class iface
+{
+public:
+    typedef std::shared_ptr  pointer_t;
+    typedef std::vector      vector_t;
+
+    static iface::vector_t          get_local_addresses();
+
+    std::string                     get_name() const;
+    unsigned int                    get_flags() const;
+    addr const &                    get_address() const;
+    addr const &                    get_broadcast_address() const;
+    addr const &                    get_destination_address() const;
+
+    bool                            has_broadcast_address() const;
+    bool                            has_destination_address() const;
+
+private:
+    std::string                     f_name = std::string();
+    unsigned int                    f_flags = 0;
+    addr                            f_address = addr();
+    addr                            f_broadcast_address = addr();
+    addr                            f_destination_address = addr();
+};
+
+
+
+iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination = true);
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/route.cpp libaddr-1.0.18.0~xenial/libaddr/route.cpp
--- libaddr-1.0.17.0~xenial/libaddr/route.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/route.cpp	2019-09-02 20:13:56.000000000 +0000
@@ -0,0 +1,444 @@
+// Copyright (c) 2018-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+/** \file
+ * \brief The implementation of the route class.
+ *
+ * This file includes the implementation of the route class.
+ *
+ * The route class registers information about a route. It is created
+ * by using the get_routes() function.
+ */
+
+// self
+//
+#include    "libaddr/addr_exception.h"
+#include    "libaddr/route.h"
+
+
+// C++ library
+//
+#include    
+#include    
+#include    
+
+
+// C library
+//
+#include    
+
+
+// last include
+//
+#include    
+
+
+
+namespace addr
+{
+
+
+typedef std::vector words_t;
+
+
+/** \brief Details used by the route class implementation.
+ *
+ * The following handles the route class and gathering of the routes.
+ */
+namespace
+{
+
+
+/** \brief Read one line of content from a file.
+ *
+ * This function reads one line of content up to the next '\n'.
+ *
+ * \param[in,out] in  The input stream.
+ * \param[out] line  The line just read.
+ *
+ * \return 0 on success, -1 on error (generally EOF)
+ */
+int readwords(std::ifstream & in, words_t & words)
+{
+    words.clear();
+    std::string w;
+    for(;;)
+    {
+        int const c(in.get());
+        if(c < 0)
+        {
+            return -1;
+        }
+        if(c == '\n')
+        {
+            return 0;
+        }
+        if(c == '\t' || c == ' ')
+        {
+            // there should always be a first word, however, there can
+            // be multiple '\t' or ' ' after one another
+            if(!w.empty())
+            {
+                words.push_back(w);
+                w.clear();
+            }
+        }
+        else
+        {
+            w += c;
+        }
+    }
+}
+
+
+int get_position(words_t const & headers, std::string const & column_name)
+{
+    auto it(std::find(
+          headers.cbegin()
+        , headers.cend()
+        , column_name));
+
+    if(it == headers.end())
+    {
+        return -1; // LCOV_EXCL_LINE
+    }
+
+    return it - headers.begin();
+}
+
+
+std::string const & get_value(words_t const & entries, int pos)
+{
+    static std::string const    not_found;
+
+    if(pos < static_cast(entries.size()))
+    {
+        return entries[pos];
+    }
+
+    return not_found; // LCOV_EXCL_LINE
+}
+
+
+int hex_to_number(char c)
+{
+    if(c >= '0' && c <= '9')
+    {
+        return c - '0';
+    }
+    if(c >= 'a' && c <= 'f')
+    {
+        return c - 'a' + 10; // LCOV_EXCL_LINE
+    }
+    if(c >= 'A' && c <= 'F')
+    {
+        return c - 'A' + 10;
+    }
+    throw addr_invalid_argument("invalid hexadecimal digit"); // LCOV_EXCL_LINE
+}
+
+
+addr hex_to_addr(std::string const & address)
+{
+    if(address.length() != 8)
+    {
+        throw addr_invalid_argument("invalid length for an hex address"); // LCOV_EXCL_LINE
+    }
+
+    struct sockaddr_in in = sockaddr_in();
+    in.sin_family = AF_INET;
+    in.sin_port = 0;
+    in.sin_addr.s_addr =
+                  (hex_to_number(address[7]) <<  0)
+                | (hex_to_number(address[6]) <<  4)
+                | (hex_to_number(address[5]) <<  8)
+                | (hex_to_number(address[4]) << 12)
+                | (hex_to_number(address[3]) << 16)
+                | (hex_to_number(address[2]) << 20)
+                | (hex_to_number(address[1]) << 24)
+                | (hex_to_number(address[0]) << 28)
+            ;
+
+    return addr(in);
+}
+
+
+struct flag_name_t
+{
+    uint32_t const  f_flag;
+    char const      f_name;
+};
+
+/** \brief Flags used by route tables.
+ *
+ * \note
+ * The list of flags presented here includes IPv4 and IPv6 flags.
+ *
+ * \note
+ * Some of the flags are not defined in Ubuntu 16.04.
+ */
+flag_name_t const g_rtf_flag_names[] =
+{
+    { RTF_UP,        'U' },
+    { RTF_GATEWAY,   'G' },
+    { RTF_REJECT,    '!' },        // may not be defined
+    { RTF_HOST,      'H' },
+    { RTF_REINSTATE, 'R' },
+    { RTF_DYNAMIC,   'D' },
+    { RTF_MODIFIED,  'M' },
+    { RTF_DEFAULT,   'd' },
+    { RTF_ALLONLINK, 'a' },
+    { RTF_ADDRCONF,  'c' },
+    { RTF_NONEXTHOP, 'o' },
+    //{ RTF_EXPIRES,   'e' },
+    { RTF_CACHE,     'C' },
+    { RTF_FLOW,      'f' },
+    { RTF_POLICY,    'p' },
+    { RTF_LOCAL,     'l' },
+    { RTF_MTU,       'u' },
+    { RTF_WINDOW,    'w' },
+    { RTF_IRTT,      'i' },
+    //{ RTF_NOTCACHED, 'n' },
+};
+
+
+
+}
+// no name namespace
+
+
+
+/** \brief Read the list of routes.
+ *
+ * This function reads the list of routes using the /proc/net/routes
+ * file. It returns a vector of easy to use route objects.
+ *
+ * The content of the route table is scanned using the column names
+ * so it makes sure that it does not use the wrong column (i.e.
+ * expect that the columns never change over time.)
+ *
+ * \note
+ * If an error occurs, the reurned vector is empty and errno is
+ * set to the error that happened. The ENODATA error is used
+ * if some mandatory columns are missing and thus this function
+ * cannot properly load the columns.
+ *
+ * \todo
+ * Write the IPv6 function. It's similar only there are no headers
+ * and (obviously?!) the IPs are IPv6 instead of IPv4.
+ *
+ * \return A vector of the routes found in the file.
+ */
+route::vector_t route::get_ipv4_routes()
+{
+    // the 'route' tool uses '/proc/net/route' so we do that too here
+    //
+    route::vector_t routes;
+
+    std::ifstream in("/proc/net/route");
+
+    // the first line is a set of headers, we use that to make sure that
+    // we know what each column is
+    //
+    words_t headers;
+    int e(readwords(in, headers));
+    if(e < 0)
+    {
+        return routes; // LCOV_EXCL_LINE
+    }
+
+    // TODO: we may want to remove case although I don't think it will
+    //       change over time, it could be one more thing that could...
+
+    int const pos_iface      (get_position(headers, "Iface"));
+    int const pos_destination(get_position(headers, "Destination"));
+    int const pos_gateway    (get_position(headers, "Gateway"));
+    int const pos_flags      (get_position(headers, "Flags"));
+    int const pos_refcnt     (get_position(headers, "RefCnt"));
+    int const pos_use        (get_position(headers, "Use"));
+    int const pos_metric     (get_position(headers, "Metric"));
+    int const pos_mask       (get_position(headers, "Mask"));
+    int const pos_mtu        (get_position(headers, "MTU"));
+    int const pos_window     (get_position(headers, "Window"));
+    int const pos_irtt       (get_position(headers, "IRTT"));
+
+    if(pos_iface == -1
+    || pos_destination == -1
+    || pos_gateway == -1)
+    {
+        errno = ENODATA; // LCOV_EXCL_LINE
+        return routes;   // LCOV_EXCL_LINE
+    }
+
+    for(;;)
+    {
+        // read one entry
+        //
+        words_t entries;
+        e = readwords(in, entries);
+        if(e < 0)
+        {
+            break;
+        }
+
+        // convert each column to data in a 'route' object
+        //
+        route r;
+
+        r.f_interface_name      = get_value(entries, pos_iface);
+        r.f_destination_address = hex_to_addr(get_value(entries, pos_destination));
+        r.f_gateway_address     = hex_to_addr(get_value(entries, pos_gateway));
+        r.f_flags               = std::stol(get_value(entries, pos_flags));
+        r.f_reference_count     = std::stol(get_value(entries, pos_refcnt));
+        r.f_use                 = std::stol(get_value(entries, pos_use));
+        r.f_metric              = std::stol(get_value(entries, pos_metric));
+        r.f_mtu                 = std::stol(get_value(entries, pos_mtu));
+        r.f_window              = std::stol(get_value(entries, pos_window));
+        r.f_irtt                = std::stol(get_value(entries, pos_irtt));
+
+        // the mask is handled specially
+        //
+        std::string mask_str(get_value(entries, pos_mask));
+        if(!mask_str.empty())
+        {
+            addr mask = hex_to_addr(mask_str);
+            struct sockaddr_in ipv4 = sockaddr_in();
+            mask.get_ipv4(ipv4);
+            uint8_t m[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
+            m[12] = ipv4.sin_addr.s_addr >>  0;
+            m[13] = ipv4.sin_addr.s_addr >>  8;
+            m[14] = ipv4.sin_addr.s_addr >> 16;
+            m[15] = ipv4.sin_addr.s_addr >> 24;
+            r.f_destination_address.set_mask(m);
+        }
+
+        routes.push_back(pointer_t(new route(r)));
+    }
+
+    return routes;
+}
+
+
+std::string const & route::get_interface_name() const
+{
+    return f_interface_name;
+}
+
+
+addr const & route::get_destination_address() const
+{
+    return f_destination_address;
+}
+
+
+addr const & route::get_gateway_address() const
+{
+    return f_gateway_address;
+}
+
+
+int route::get_flags() const
+{
+    return f_flags;
+}
+
+
+std::string route::flags_to_string() const
+{
+    std::string result;
+
+    std::for_each(
+          g_rtf_flag_names
+        , g_rtf_flag_names + sizeof(g_rtf_flag_names) / sizeof(g_rtf_flag_names[0])
+        , [&result, this](auto const & fn)
+        {
+            if((f_flags & fn.f_flag) != 0)
+            {
+                result += fn.f_name;
+            }
+        });
+
+    return result;
+}
+
+
+int route::get_reference_count() const
+{
+    return f_reference_count;
+}
+
+
+int route::get_use() const
+{
+    return f_use;
+}
+
+
+int route::get_metric() const
+{
+    return f_metric;
+}
+
+
+int route::get_mtu() const
+{
+    return f_mtu;
+}
+
+
+int route::get_window() const
+{
+    return f_window;
+}
+
+
+int route::get_irtt() const
+{
+    return f_irtt;
+}
+
+
+route::pointer_t find_default_route(route::vector_t const & routes)
+{
+    auto it(std::find_if(
+          routes.cbegin()
+        , routes.cend()
+        , [](auto const & r)
+        {
+            return r->get_destination_address().is_default();
+        }));
+
+    if(it == routes.cend())
+    {
+        return nullptr;
+    }
+
+    return *it;
+}
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/route.h libaddr-1.0.18.0~xenial/libaddr/route.h
--- libaddr-1.0.17.0~xenial/libaddr/route.h	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/route.h	2019-09-02 20:14:08.000000000 +0000
@@ -0,0 +1,82 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#pragma once
+
+/** \file
+ * \brief The various route class.
+ *
+ * This header includes the route class used to handle routes as found
+ * in the routing tables of the kernel.
+ */
+
+// addr lib
+//
+#include    "libaddr/addr.h"
+
+
+
+namespace addr
+{
+
+
+
+class route
+{
+public:
+    typedef std::shared_ptr  pointer_t;
+    typedef std::vector  vector_t;
+
+    static vector_t                 get_ipv4_routes();
+
+    std::string const &             get_interface_name() const;
+    addr const &                    get_destination_address() const;
+    addr const &                    get_gateway_address() const;
+    int                             get_flags() const;
+    std::string                     flags_to_string() const;
+    int                             get_reference_count() const;
+    int                             get_use() const;
+    int                             get_metric() const;
+    int                             get_mtu() const;
+    int                             get_window() const;
+    int                             get_irtt() const;
+
+private:
+    std::string                     f_interface_name = std::string();
+    addr                            f_destination_address = addr();  // Destination + Mask
+    addr                            f_gateway_address = addr();
+    int                             f_flags = 0;
+    int                             f_reference_count = 0;
+    int                             f_use = 0;
+    int                             f_metric = 0;
+    int                             f_mtu = 0;
+    int                             f_window = 0;
+    int                             f_irtt = 0;
+};
+
+
+route::pointer_t find_default_route(route::vector_t const & routes);
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/version.cpp libaddr-1.0.18.0~xenial/libaddr/version.cpp
--- libaddr-1.0.17.0~xenial/libaddr/version.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/version.cpp	2019-09-02 20:14:30.000000000 +0000
@@ -0,0 +1,122 @@
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+/** \file
+ * \brief Implementation of the few global functions used to define the version.
+ *
+ * This file includes the few functions one can use to dynamically check
+ * the version of the libaddr library. If you compiled with a different
+ * version, then you very certainly could have an incompatible version
+ * of the interface (i.e. C++ classes cannot always work right when
+ * created through modified versions of such.)
+ */
+
+// self
+//
+#include    "libaddr/addr.h"
+#include    "libaddr/version.h"
+
+
+// last include
+//
+#include    
+
+
+
+namespace addr
+{
+
+/** \brief Major version of the libaddr library.
+ *
+ * This function returns the major version of the library at the
+ * time it was compiled.
+ *
+ * The C++ classes are very likely different when compiled against
+ * a different major version of the library. This means it is likely
+ * to crash if used.
+ *
+ * \return The major version of the libaddr library.
+ */
+int get_version_major()
+{
+    return LIBADDR_VERSION_MAJOR;
+}
+
+
+/** \brief Minor version of the libaddr library.
+ *
+ * This function returns the minor version of the library at the
+ * time it was compiled.
+ *
+ * The C++ classes are likely different when compiled against
+ * a different minor version of the library. This means it is likely
+ * to crash if used.
+ *
+ * \return The minor version of the libaddr library.
+ */
+int get_version_minor()
+{
+    return LIBADDR_VERSION_MINOR;
+}
+
+
+/** \brief Patch version of the libaddr library.
+ *
+ * This function returns the patch version of the library at the
+ * time it was compiled.
+ *
+ * The C++ classes should not have changed in such a way that it
+ * will crash your application when compiled against a version
+ * that has a different patching version.
+ *
+ * \return The patch version of the libaddr library.
+ */
+int get_version_patch()
+{
+    return LIBADDR_VERSION_PATCH;
+}
+
+
+/** \brief The full version of the libaddr library as a string.
+ *
+ * This function returns a string with the major, minor, and
+ * path versions of the library. The build number is not included.
+ *
+ * If you want to compare the version, we suggest that you use
+ * the other functions: get_version_major(), get_version_minor(),
+ * and get_version_patch(). This function should be used for
+ * display only.
+ *
+ * \return The full version of the libaddr library as a string.
+ */
+char const * get_version_string()
+{
+    return LIBADDR_VERSION_STRING;
+}
+
+
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/libaddr/version.h.in libaddr-1.0.18.0~xenial/libaddr/version.h.in
--- libaddr-1.0.17.0~xenial/libaddr/version.h.in	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/libaddr/version.h.in	2019-09-02 20:15:11.000000000 +0000
@@ -0,0 +1,46 @@
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#pragma once
+
+/** \file
+ * \brief Version header.
+ *
+ * This header file defines the libaddr library version information.
+ */
+
+#define    LIBADDR_VERSION_MAJOR   @LIBADDR_VERSION_MAJOR@
+#define    LIBADDR_VERSION_MINOR   @LIBADDR_VERSION_MINOR@
+#define    LIBADDR_VERSION_PATCH   @LIBADDR_VERSION_PATCH@
+#define    LIBADDR_VERSION_STRING  "@LIBADDR_VERSION_MAJOR@.@LIBADDR_VERSION_MINOR@.@LIBADDR_VERSION_PATCH@"
+
+namespace addr
+{
+
+int             get_version_major();
+int             get_version_minor();
+int             get_version_patch();
+char const *    get_version_string();
+
+}
+// namespace addr
+// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/mk libaddr-1.0.18.0~xenial/mk
--- libaddr-1.0.17.0~xenial/mk	2018-06-10 21:21:22.000000000 +0000
+++ libaddr-1.0.18.0~xenial/mk	2019-09-02 10:12:57.000000000 +0000
@@ -1,7 +1,43 @@
 #!/bin/sh
 #
-# Run make against libaddr
+# Sample script to run make without having to retype the long path each time
+# This will work if you built the environment using our ~/bin/build-snap script
 
-make -C ../../../BUILD/contrib/libaddr/ install
+PROJECT_NAME=libaddr
+PROCESSORS=4
+
+case $1 in
+"-l")
+    make -C ../../../BUILD/contrib/${PROJECT_NAME} 2>&1 | less -SR
+    ;;
+
+"-d")
+    rm -rf ../../../BUILD/contrib//${PROJECT_NAME}/doc//${PROJECT_NAME}-doc-1.0.tar.gz
+    make -C ../../../BUILD/contrib//${PROJECT_NAME}
+    ;;
+
+"-i")
+    make -j${PROCESSORS} -C ../../../BUILD/contrib//${PROJECT_NAME} install
+    ;;
+
+"-t")
+    (
+        if make -j${PROCESSORS} -C ../../../BUILD/contrib//${PROJECT_NAME}
+        then
+            shift
+            ../../../BUILD/contrib//${PROJECT_NAME}/tests/unittest --progress $*
+        fi
+    ) 2>&1 | less -SR
+    ;;
+
+"")
+    make -j${PROCESSORS} -C ../../../BUILD/contrib//${PROJECT_NAME}
+    ;;
+
+*)
+    echo "error: unknown command line option \"$1\""
+    ;;
+
+esac
 
 # vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/addr.cpp libaddr-1.0.18.0~xenial/src/addr.cpp
--- libaddr-1.0.17.0~xenial/src/addr.cpp	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/addr.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,1394 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief The implementation of the addr class.
- *
- * This file includes the implementation of the addr class. The one that
- * deals with low level classes.
- */
-
-// self
-//
-#include "libaddr/addr.h"
-#include "libaddr/addr_exceptions.h"
-
-// C++ library
-//
-#include 
-#include 
-
-// C library
-//
-#include 
-
-
-
-/** \mainpage
- * \brief libaddr, a C++ library to handle network IP addresses in IPv4 and IPv6.
- *
- * ### Introduction
- *
- * This library is used to parse strings of IP addresses to lists of
- * binary IP addresses ready to be used by functions such as bind(),
- * send(), recv(), etc.
- *
- * The library supports multiple addresses separated by commas and/or
- * spaces, ports, and CIDR masks. It can check whether an address matches
- * another taking the mask in account. It can sort IPs numerically. It
- * can determine the type of an IP address (i.e. is it a local address,
- * a private address, a public address?)
- *
- * The library also has a function to read IP addresses from your
- * computer interfaces and return that list. Very practical to know
- * whether an IP address represents your computer or not.
- *
- * ### Usage
- *
- * The library is composed of three main classes:
- *
- * \li addr
- *
- * The address class holds one address, a port, a protocol and a few
- * other parts. This is what one uses to connect or listen with an
- * address.
- *
- * The address is kept by addr in an IPv6 address structure.
- *
- * By default the CIDR of the address is all 1s (i.e. no masking, all
- * bits considered important.) The mask is always 128 bits. If you are
- * dealing with IPv4, make sure that the first 12 bytes are set to 255.
- *
- * The class also offers a set of functions to transform the binary
- * address it is holding to a string.
- *
- * \li addr_range
- *
- * It is possible to define a range of addresses and ports. This class
- * holds a 'from' address and a 'to' address. By default neither is
- * defined. You have to call the set_from() and set_to() functions.
- *
- * The addr_range::vector_t is what the addr_parser returns after
- * parsing a string representing one of more addresses.
- *
- * \note
- * The range is functional, however, the parser does not yet support
- * parsing range of addresses and ports.
- *
- * \li addr_parser
- *
- * The parser is used to transform a string to an address.
- *
- * It supports many variations of its input, which are handled by
- * the 'allow' flags. The set_allow() and get_allow() functions can
- * be used to tweak the parser in supporting such and such feature.
- *
- * By default, the input is expected to be an address and a port
- * separated by a colon (i.e. `"1.2.3.4:1234"` in IPv4 and `"[::1]:1234"`
- * in IPv6.)
- *
- * ### Parser
- *
- * The parser supports the following syntax (ranges are not yet supported
- * and they do not appear in the following list):
- *
- * \code
- *    start: address_list
- *
- *    address_list: address_cidr
- *                | address_list address_list_separators address_cidr
- *
- *    address_list_separators: ' '
- *                           | ','
- *                           | address_list_separators address_list_separators
- *
- *    address_cidr: address_port
- *                | address_port '/' cidr
- *
- *    address_port: address
- *                | address ':' port
- *
- *    address: ipv4
- *           | ipv6
- *
- *    cidr: decimal_number
- *        | ipv4
- *        | ipv6
- *
- *    ipv4: decimal_number '.' decimal_number '.' decimal_number '.' decimal_number
- *
- *    ipv6: '[' hexadecimal_number_list ']'
- *
- *    port: decimal_number
- *
- *    hexadecimal_number_list: hexadecimal_number
- *                           | hexadecimal_number_list ':' hexadecimal_number
- *
- *    decimal_number: [0-9]+
- *
- *    hexadecimal_number: [0-9a-fA-F]+
- * \endcode
- *
- * When accepting multiple addresses separated by commas or spaces, the
- * number of commas and spaces separating two address is not significant.
- * The input string can also start or end with commas and spaces. The
- * following variable defines exactly two IP address:
- *
- * \code
- *       addresses=  ,1.2.3.4,   ,5.6.7.8,,
- * \endcode
- *
- * (note that the parser should not be passed the "addresses=" part.)
- */
-
-
-/** \brief The libaddr classes are all defined in this namespace.
- *
- * The addr namespace includes all the addr classes.
- */
-namespace addr
-{
-
-/*
- * Various sytem address structures
-
-// Any address is 16 bytes or less
-struct sockaddr {
-   unsigned short    sa_family;    // address family, AF_xxx
-   char              sa_data[14];  // 14 bytes of protocol address
-};
-
-struct sockaddr_storage {
-    sa_family_t  ss_family;     // address family
-
-    // all this is padding, implementation specific, ignore it:
-    char      __ss_pad1[_SS_PAD1SIZE];
-    int64_t   __ss_align;
-    char      __ss_pad2[_SS_PAD2SIZE];
-};
-
-
-typedef uint32_t in_addr_t;   // or `__be32`
-struct in_addr {
-    in_addr_t        s_addr;
-};
-
-
-// IPv4
-struct sockaddr_in {
-    short            sin_family;   // e.g. AF_INET, AF_INET6
-    unsigned short   sin_port;     // e.g. htons(3490)
-    struct in_addr   sin_addr;     // see struct in_addr, below
-    char             sin_zero[8];  // zero this if you want to
-};
-
-
-// IPv6
-struct sockaddr_in6 {
-    u_int16_t       sin6_family;   // address family, AF_INET6
-    u_int16_t       sin6_port;     // port number, Network Byte Order
-    u_int32_t       sin6_flowinfo; // IPv6 flow information
-    struct in6_addr sin6_addr;     // IPv6 address
-    u_int32_t       sin6_scope_id; // Scope ID
-};
-
-struct in6_addr
-  {
-    union
-      {
-	uint8_t	__u6_addr8[16];
-#ifdef __USE_MISC
-	uint16_t __u6_addr16[8];
-	uint32_t __u6_addr32[4];
-#endif
-      } __in6_u;
-#define s6_addr			__in6_u.__u6_addr8
-#ifdef __USE_MISC
-# define s6_addr16		__in6_u.__u6_addr16
-# define s6_addr32		__in6_u.__u6_addr32
-#endif
-  };
-
-
-*/
-
-
-
-
-
-/** \brief Create an addr object that represents an ANY address.
- *
- * This function initializes the addr object with the ANY address.
- * The port is set to 0 and the protocol to TCP.
- *
- * It is strongly suggested that you change those parameters
- * before really using this address since a port of zero and
- * the protocol may be wrong.
- */
-addr::addr()
-{
-    // keep default protocol (TCP)
-}
-
-
-/** \brief Create an addr object from a binary IPv4 address.
- *
- * This function initializes this addr object with the specified IPv4
- * address. The is_ipv4() function will return true.
- *
- * \param[in] in  The binary IPv4 address.
- */
-addr::addr(struct sockaddr_in const & in)
-{
-    set_ipv4(in);
-    // keep default protocol (TCP)
-}
-
-
-/** \brief Create an addr object from a binary IPv6 address.
- *
- * This function initializes this addr object with the specified IPv6
- * address. The is_ipv4() function will return false.
- *
- * \param[in] in6  The binary IPv6 address.
- */
-addr::addr(struct sockaddr_in6 const & in6)
-{
-    set_ipv6(in6);
-    // keep default protocol (TCP)
-}
-
-
-/** \brief Save an IPv4 in this addr object.
- *
- * This function saves the specified IPv4 in this addr object.
- *
- * Since we save the data in an IPv6 structure, it is kept in
- * the addr as an IPv4 mapped in an IPv6 address. It can still
- * be retrieved right back as an IPv4 with the get_ipv4() function.
- *
- * \param[in] in  The IPv4 address to save in this addr object.
- */
-void addr::set_ipv4(struct sockaddr_in const & in)
-{
-    if(in.sin_family != AF_INET)
-    {
-        // although we convert the IPv4 to an IPv6 below, we really only
-        // support AF_INET on entry
-        //
-        throw addr_invalid_argument_exception("addr::set_ipv4(): the input address does not represent an IPv4 address (family is not AF_INET).");
-    }
-
-    // reset the address first
-    memset(&f_address, 0, sizeof(f_address));
-
-    // then transform the IPv4 to an IPv6
-    //
-    // Note: this is not an IPv6 per se, it is an IPv4 mapped within an
-    //       IPv6 and your network stack needs to support IPv4 anyway
-    //       in order to use that IP...
-    //
-    f_address.sin6_family = AF_INET6;
-    f_address.sin6_port = in.sin_port;
-    f_address.sin6_addr.s6_addr16[5] = 0xFFFF;
-    f_address.sin6_addr.s6_addr32[3] = in.sin_addr.s_addr;
-
-    address_changed();
-}
-
-
-/** \brief Set the port of this address.
- *
- * This function changes the port of this address to \p port.
- *
- * \exception addr_invalid_argument_exception
- * This exception is raised whenever the \p port parameter is set to
- * an invalid number (negative or larger than 65535.)
- *
- * \param[in] port  The new port to save in this address.
- */
-void addr::set_port(int port)
-{
-    if(port > 65535 
-    || port < 0)
-    {
-        throw addr_invalid_argument_exception("port to set_port() cannot be out of the allowed range [0..65535].");
-    }
-    f_address.sin6_port = htons(port);
-}
-
-
-/** \brief Change the protocol using a string.
- *
- * This function is used to change the current protocol defined in
- * this addr object.
- *
- * \exception addr_invalid_argument_exception
- * We currently support "tcp", "udp", and "ip". Any other protocol
- * name generates this exception.
- *
- * \param[in] protocol  The name of the protocol ("tcp", "udp", or "ip")
- */
-void addr::set_protocol(char const * protocol)
-{
-    if(protocol == nullptr)
-    {
-        throw addr_invalid_argument_exception("protocol pointer to set_protocol() cannot be a nullptr.");
-    }
-
-    if(strcmp(protocol, "ip") == 0)
-    {
-        f_protocol = IPPROTO_IP;
-    }
-    else if(strcmp(protocol, "tcp") == 0)
-    {
-        f_protocol = IPPROTO_TCP;
-    }
-    else if(strcmp(protocol, "udp") == 0)
-    {
-        f_protocol = IPPROTO_UDP;
-    }
-    else
-    {
-        throw addr_invalid_argument_exception(
-                          std::string("unknown protocol \"")
-                        + protocol
-                        + "\", expected \"tcp\" or \"udp\" (string).");
-    }
-
-    address_changed();
-}
-
-
-/** \brief Set the protocol numerically.
- *
- * This function sets the protocol from a number instead of a name.
- *
- * Note that we only support IPPROTO_TCP and IPPROTO_UDP for now.
- * Any other protocol will make this function raise an exception.
- *
- * \todo
- * We may want to support any protocol number at this level. If your
- * application is limited then it should verify the protocol and
- * make sure it supports it before using this address. At the same
- * time, the IP protocol is pretty much locked up with just TCP
- * and UDP these days (there is the IP protocol, but that's not
- * useful at our level.)
- *
- * \exception addr_invalid_argument_exception
- * This exception is raised if the specified protocol is not currently
- * supported by the addr implementation.
- *
- * \param[in] protocol  The new numeric protocol.
- */
-void addr::set_protocol(int protocol)
-{
-    switch(protocol)
-    {
-    case IPPROTO_IP:
-    case IPPROTO_TCP:
-    case IPPROTO_UDP:
-        f_protocol = protocol;
-        break;
-
-    default:
-        throw addr_invalid_argument_exception(
-                          "unknown protocol number "
-                        + std::to_string(protocol)
-                        + ", expected \"tcp\" ("
-                        + std::to_string(static_cast(IPPROTO_TCP))
-                        + ") or \"udp\" ("
-                        + std::to_string(static_cast(IPPROTO_UDP))
-                        + ") (numeric).");
-
-    }
-}
-
-
-/** \brief Set the mask.
- *
- * The input mask must be exactly 16 bytes. If you are dealing with an
- * IPv4, make sure the first 12 bytes are 255.
- *
- * \param[in] mask  The mask to save in this address.
- */
-void addr::set_mask(uint8_t const * mask)
-{
-    memcpy(f_mask, mask, sizeof(f_mask));
-}
-
-
-/** \brief Apply the mask to the IP address.
- *
- * This function applies the mask to this address IP address. This means
- * the bits that are 0 in the mask will also be 0 in the address once
- * the function returns.
- *
- * This should be called if you are trying to canonicalize an IP/mask.
- */
-void addr::apply_mask()
-{
-    for(int idx(0); idx < 16; ++idx)
-    {
-        f_address.sin6_addr.s6_addr[idx] &= f_mask[idx];
-    }
-}
-
-
-/** \brief Get the mask.
- *
- * The output buffer for the mask must be at least 16 bytes. If you are
- * dealing with an IPv4, all the bytes are expected to be 255 except
- * the bottom 4 bytes (offset 12, 13, 14, 15).
- *
- * \param[out] mask  The buffer where the mask gets copied.
- */
-void addr::get_mask(uint8_t * mask) const
-{
-    memcpy(mask, f_mask, sizeof(f_mask));
-}
-
-
-/** \brief Check whether this address represents the ANY address.
- *
- * The IPv4 and IPv6 have an ANY address also called the default address.
- * This function returns true if this address represents the ANY address.
- *
- * The any address is represented by `"0.0.0.0"` in IPv4 and `"::"` in
- * IPv6. (i.e. all zeroes)
- *
- * \note
- * You can also determine this by calling the get_network_type() function
- * and compare the result against `network_type_t::NETWORK_TYPE_ANY`.
- *
- * \return true if this addr represents the any address.
- */
-bool addr::is_default() const
-{
-    // this is for IPv4 or IPv6
-    //
-    return f_address.sin6_addr.s6_addr32[0] == 0
-        && f_address.sin6_addr.s6_addr32[1] == 0
-        && f_address.sin6_addr.s6_addr16[4] == 0
-        && (f_address.sin6_addr.s6_addr16[5] == 0 || f_address.sin6_addr.s6_addr16[5] == 0xFFFF)
-        && f_address.sin6_addr.s6_addr32[3] == 0;
-}
-
-
-/** \brief Check whether this address represents an IPv4 address.
- *
- * The IPv6 format supports embedding IPv4 addresses. This function
- * returns true if the embedded address is an IPv4. When this function
- * returns true, the get_ipv4() can be called. Otherwise, the get_ipv4()
- * function throws an exception.
- *
- * \return true if this address represents an IPv4 address.
- */
-bool addr::is_ipv4() const
-{
-    return f_address.sin6_addr.s6_addr32[0] == 0
-        && f_address.sin6_addr.s6_addr32[1] == 0
-        && f_address.sin6_addr.s6_addr16[4] == 0
-        && f_address.sin6_addr.s6_addr16[5] == 0xFFFF;
-}
-
-
-/** \brief Retreive the IPv4 address.
- *
- * This function can be used to retrieve the IPv4 address of this addr
- * object. If the address is not an IPv4, then the function throws.
- *
- * \exception addr_invalid_structure_exception
- * This exception is raised if the address is not an IPv4 address.
- *
- * \param[out] in  The structure where the IPv4 Internet address gets saved.
- */
-void addr::get_ipv4(struct sockaddr_in & in) const
-{
-    if(is_ipv4())
-    {
-        // this is an IPv4 mapped in an IPv6, "unmap" that IP
-        //
-        memset(&in, 0, sizeof(in));
-        in.sin_family = AF_INET;
-        in.sin_port = f_address.sin6_port;
-        in.sin_addr.s_addr = f_address.sin6_addr.s6_addr32[3];
-        return;
-    }
-
-    throw addr_invalid_state_exception("Not an IPv4 compatible address.");
-}
-
-
-/** \brief Save the specified IPv6 address in this addr object.
- *
- * This function saves the specified IPv6 address in this addr object.
- * The function does not check the validity of the address. It is
- * expected to be valid.
- *
- * The address may be an embedded IPv4 address.
- *
- * \param[in] in6  The source IPv6 to save in the addr object.
- */
-void addr::set_ipv6(struct sockaddr_in6 const & in6)
-{
-    if(in6.sin6_family != AF_INET6)
-    {
-        throw addr_invalid_argument_exception("addr::set_ipv6(): the input address does not represent an IPv6 address (family is not AF_INET6).");
-    }
-    memcpy(&f_address, &in6, sizeof(in6));
-
-    address_changed();
-}
-
-
-/** \brief Retrieve a copy of this addr IP address.
- *
- * This function returns the current IP address saved in this
- * addr object. The IP may represent an IPv4 address in which
- * case the is_ipv4() returns true.
- *
- * \param[out] in6  The structure where the address gets saved.
- */
-void addr::get_ipv6(struct sockaddr_in6 & in6) const
-{
-    memcpy(&in6, &f_address, sizeof(in6));
-}
-
-
-/** \brief Retrive the IPv4 as a string.
- *
- * This function returns a string representing the IP address
- * defined in this addr object.
- *
- * The \p mode parameter defines what gets output.
- *
- * \li ip_string_t::IP_STRING_ONLY -- only the IP address
- * \li ip_string_t::IP_STRING_PORT -- the IP and port
- * \li ip_string_t::IP_STRING_MASK -- the IP and mask
- * \li ip_string_t::IP_STRING_ALL -- the IP, port, and mask
- *
- * The ip_string_t::IP_STRING_BRACKET is viewed as
- * ip_string_t::IP_STRING_ONLY.
- *
- * The ip_string_t::IP_STRING_BRACKET_MASK is viewed as
- * ip_string_t::IP_STRING_MASK.
- *
- * \exception addr_invalid_state_exception
- * If the addr object does not currently represent an IPv4 then
- * this exception is raised.
- *
- * \param[in] mode  How the output string is to be built.
- */
-std::string addr::to_ipv4_string(string_ip_t mode) const
-{
-    if(is_ipv4())
-    {
-        // this is an IPv4 mapped in an IPv6, "unmap" that IP
-        // so the inet_ntop() can correctly generate an output IP
-        //
-        struct in_addr in;
-        memset(&in, 0, sizeof(in));
-        in.s_addr = f_address.sin6_addr.s6_addr32[3];
-        char buf[INET_ADDRSTRLEN + 1];
-        if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
-        {
-            if(mode != string_ip_t::STRING_IP_ONLY)
-            {
-                std::stringstream result;
-                result << buf;
-                if(mode == string_ip_t::STRING_IP_PORT
-                || mode == string_ip_t::STRING_IP_ALL)
-                {
-                    result << ":";
-                    result << ntohs(f_address.sin6_port);
-                }
-                if(mode == string_ip_t::STRING_IP_MASK
-                || mode == string_ip_t::STRING_IP_BRACKETS_MASK
-                || mode == string_ip_t::STRING_IP_ALL)
-                {
-                    memset(&in, 0, sizeof(in));
-                    in.s_addr = htonl((f_mask[12] << 24) | (f_mask[13] << 16) | (f_mask[14] << 8) | f_mask[15]);
-                    if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
-                    {
-                        result << "/";
-                        result << buf; // TODO: convert to simple number if possible
-                    }
-                }
-                return result.str();
-            }
-            return std::string(buf);
-        }
-        // IPv4 should never fail converting the address unless the
-        // buffer was too small...
-    }
-
-    throw addr_invalid_state_exception("Not an IPv4 compatible address.");
-}
-
-
-/** \brief Convert the addr object to a string.
- *
- * This function converts the addr object to a canonicalized string.
- * This can be used to compare two IPv6 together as strings, although
- * it is probably better to compare them using the < and == operators.
- *
- * By default the function returns with the IPv6 address defined
- * between square bracket, so the output of this function can be
- * used as the input of the set_addr_port() function. You may
- * also request the address without the brackets.
- *
- * \exception addr_invalid_state_exception
- * If the binary IP address cannot be converted to ASCII, this exception
- * is raised.
- *
- * \param[in] mode  How the output string is to be built.
- *
- * \return The addr object converted to an IPv6 address.
- */
-std::string addr::to_ipv6_string(string_ip_t mode) const
-{
-    char buf[INET6_ADDRSTRLEN + 1];
-    if(inet_ntop(AF_INET6, &f_address.sin6_addr, buf, sizeof(buf)) != nullptr)
-    {
-        bool const include_brackets(mode == string_ip_t::STRING_IP_BRACKETS
-                                 || mode == string_ip_t::STRING_IP_BRACKETS_MASK
-                                 || mode == string_ip_t::STRING_IP_PORT // port requires us to add brackets
-                                 || mode == string_ip_t::STRING_IP_ALL);
-
-        std::stringstream result;
-
-        // always insert the IP, even if ANY or "BROADCAST"
-        //
-        if(include_brackets)
-        {
-            result << "[";
-        }
-        result << buf;
-        if(include_brackets)
-        {
-            result << "]";
-        }
-
-        // got a port?
-        //
-        if(mode == string_ip_t::STRING_IP_PORT
-        || mode == string_ip_t::STRING_IP_ALL)
-        {
-            result << ":";
-            result << ntohs(f_address.sin6_port);
-        }
-
-        // got a mask?
-        //
-        if(mode == string_ip_t::STRING_IP_MASK
-        || mode == string_ip_t::STRING_IP_BRACKETS_MASK
-        || mode == string_ip_t::STRING_IP_ALL)
-        {
-            if(inet_ntop(AF_INET6, f_mask, buf, sizeof(buf)) != nullptr)
-            {
-                result << "/";
-                if(include_brackets)
-                {
-                    result << "[";
-                }
-                result << buf; // TODO: convert to simple number if possible
-                if(include_brackets)
-                {
-                    result << "]";
-                }
-            }
-        }
-
-        return result.str();
-    }
-
-    throw addr_invalid_state_exception("The address from this addr could not be converted to a valid canonicalized IPv6 address.");  // LCOV_EXCL_LINE
-}
-
-
-/** \brief Return the address as IPv4 or IPv6.
- *
- * Depending on whether the address represents an IPv4 or an IPv6,
- * this function returns the corresponding address. Since the format
- * of both types of addresses can always be distinguished, it poses
- * no concerns.
- *
- * \exception 
- * If include_brackets is false and include_port is true, this
- * exception is raised because we cannot furfill the request.
- *
- * \param[in] mode  How the output string is to be built.
- *
- * \return The addr object converted to an IPv4 or an IPv6 address.
- */
-std::string addr::to_ipv4or6_string(string_ip_t mode) const
-{
-    return is_ipv4() ? to_ipv4_string(mode)
-                     : to_ipv6_string(mode);
-}
-
-
-/** \brief Determine the type of network this IP represents.
- *
- * The IP address may represent various type of networks. This
- * function returns that type.
- *
- * The function checks the address either as IPv4 when is_ipv4()
- * returns true, otherwise as IPv6.
- *
- * See:
- *
- * \li https://en.wikipedia.org/wiki/Reserved_IP_addresses
- * \li https://tools.ietf.org/html/rfc3330
- * \li https://tools.ietf.org/html/rfc5735 (IPv4)
- * \li https://tools.ietf.org/html/rfc5156 (IPv6)
- *
- * \return One of the possible network types as defined in the
- *         network_type_t enumeration.
- */
-addr::network_type_t addr::get_network_type() const
-{
-    if(f_private_network_defined == network_type_t::NETWORK_TYPE_UNDEFINED)
-    {
-        f_private_network_defined = network_type_t::NETWORK_TYPE_UNKNOWN;
-
-        if(is_ipv4())
-        {
-            // get the address in host order
-            //
-            // we can use a simple mask + compare to know whether it is
-            // this or that once in host order
-            //
-            uint32_t const host_ip(ntohl(f_address.sin6_addr.s6_addr32[3]));
-
-            if((host_ip & 0xFF000000) == 0x0A000000         // 10.0.0.0/8
-            || (host_ip & 0xFFF00000) == 0xAC100000         // 172.16.0.0/12
-            || (host_ip & 0xFFFF0000) == 0xC0A80000)        // 192.168.0.0/16
-            {
-                f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
-            }
-            else if((host_ip & 0xFFC00000) == 0x64400000)   // 100.64.0.0/10
-            {
-                f_private_network_defined = network_type_t::NETWORK_TYPE_CARRIER;
-            }
-            else if((host_ip & 0xFFFF0000) == 0xA9FE0000)   // 169.254.0.0/16
-            {
-                f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
-            }
-            else if((host_ip & 0xF0000000) == 0xE0000000)   // 224.0.0.0/4
-            {
-                // there are many sub-groups on this one which are probably
-                // still in use...
-                //
-                f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
-            }
-            else if((host_ip & 0xFF000000) == 0x7F000000)   // 127.0.0.0/8
-            {
-                f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK; // i.e. localhost
-            }
-            else if(host_ip == 0x00000000)
-            {
-                f_private_network_defined = network_type_t::NETWORK_TYPE_ANY; // i.e. 0.0.0.0
-            }
-        }
-        else //if(is_ipv6()) -- if not IPv4, we have an IPv6
-        {
-            // for IPv6 it was simplified by using a prefix for
-            // all types; really way easier than IPv4
-            //
-            if(f_address.sin6_addr.s6_addr32[0] == 0      // ::
-            && f_address.sin6_addr.s6_addr32[1] == 0
-            && f_address.sin6_addr.s6_addr32[2] == 0
-            && f_address.sin6_addr.s6_addr32[3] == 0)
-            {
-                // this is the "any" IP address
-                f_private_network_defined = network_type_t::NETWORK_TYPE_ANY;
-            }
-            else
-            {
-                uint16_t const prefix(ntohs(f_address.sin6_addr.s6_addr16[0]));
-
-                if((prefix & 0xFF00) == 0xFD00)                 // fd00::/8
-                {
-                    f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
-                }
-                else if((prefix & 0xFFC0) == 0xFE80    // fe80::/10
-                     || (prefix & 0xFF0F) == 0xFF02)   // ffx2::/16
-                {
-                    f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
-                }
-                else if((prefix & 0xFF0F) == 0xFF01    // ffx1::/16
-                     || (f_address.sin6_addr.s6_addr32[0] == 0      // ::1
-                      && f_address.sin6_addr.s6_addr32[1] == 0
-                      && f_address.sin6_addr.s6_addr32[2] == 0
-                      && f_address.sin6_addr.s6_addr16[6] == 0
-                      && f_address.sin6_addr.s6_addr16[7] == htons(1)))
-                {
-                    f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK;
-                }
-                else if((prefix & 0xFF00) == 0xFF00)   // ff00::/8
-                {
-                    // this one must be after the link-local and loopback networks
-                    f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
-                }
-            }
-        }
-    }
-
-    return f_private_network_defined;
-}
-
-
-/** \brief Get the network type string
- *
- * Translate the network type into a string, which can be really useful
- * to log that information.
- *
- * Note that PUBLIC is the same as UNKNOWN, this function returns
- * "Unknown" in that case, though.
- *
- * \return The string representing the type of network.
- */
-std::string addr::get_network_type_string() const
-{
-    std::string name;
-    switch( get_network_type() )
-    {
-    case addr::network_type_t::NETWORK_TYPE_UNDEFINED  : name = "Undefined";  break; // LCOV_EXCL_LINE -- get_network_type() defines it...
-    case addr::network_type_t::NETWORK_TYPE_PRIVATE    : name = "Private";    break;
-    case addr::network_type_t::NETWORK_TYPE_CARRIER    : name = "Carrier";    break;
-    case addr::network_type_t::NETWORK_TYPE_LINK_LOCAL : name = "Local Link"; break;
-    case addr::network_type_t::NETWORK_TYPE_MULTICAST  : name = "Multicast";  break;
-    case addr::network_type_t::NETWORK_TYPE_LOOPBACK   : name = "Loopback";   break;
-    case addr::network_type_t::NETWORK_TYPE_ANY        : name = "Any";        break;
-    case addr::network_type_t::NETWORK_TYPE_UNKNOWN    : name = "Unknown";    break; // == NETWORK_TYPE_PUBLIC
-    }
-    return name;
-}
-
-
-/** \brief Create a socket from the IP address held by this addr object.
- *
- * This function creates a socket that corresponds to the addr object
- * definitions, it takes the protocol and family information in account.
- *
- * The flags can be used to add one or more of the following flags:
- *
- * \li SOCKET_FLAG_NONBLOCK -- create socket as non-block
- * \li SOCKET_FLAG_CLOEXEC -- close socket on an execv()
- * \li SOCKET_FLAG_REUSE -- for TCP socket, mark the address as immediately
- * reusable, ignored for UDP; only useful for server (bind + listen after
- * this call)
- *
- * \note
- * The IP protocol is viewed as TCP in this function.
- *
- * \warning
- * This class does not hold the socket created by this function.
- *
- * \todo
- * Move this to our libsnapnetwork once we create that separate library.
- * Probably within a form of low level socket class.
- *
- * \param[in] flags  A set of socket flags to use when creating the socket.
- * \param[in] reuse_address  Set the reuse address flag.
- *
- * \return The socket file descriptor or -1 on errors.
- */
-int addr::create_socket(socket_flag_t flags) const
-{
-    int const sock_flags(
-              ((flags & SOCKET_FLAG_CLOEXEC)  != 0 ? SOCK_CLOEXEC  : 0)
-            | ((flags & SOCKET_FLAG_NONBLOCK) != 0 ? SOCK_NONBLOCK : 0));
-    int const family(is_ipv4() ? AF_INET : AF_INET6);
-
-    switch(f_protocol)
-    {
-    case IPPROTO_IP: // interpret as TCP...
-    case IPPROTO_TCP:
-        {
-            int s(socket(family, SOCK_STREAM | sock_flags, IPPROTO_TCP));
-
-            if(s >= 0
-            && (flags & SOCKET_FLAG_REUSE) != 0)
-            {
-                // set the "reuse that address immediately" flag, we totally
-                // ignore errors on that one
-                //
-                int optval(1);
-                socklen_t const optlen(sizeof(optval));
-                static_cast(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, optlen));
-            }
-            return s;
-        }
-
-    case IPPROTO_UDP:
-        return socket(family, SOCK_DGRAM | sock_flags, IPPROTO_UDP);
-
-    default:
-        // this should never happen since we control the f_protocol field
-        //
-        return -1;      // LCOV_EXCL_LINE
-
-    }
-}
-
-
-/** \brief Connect the specified socket to this IP address.
- *
- * When you create a TCP client, you can connect to a server. This
- * is done by using the connect() function which makes use of the
- * address to connect to the server.
- *
- * This function makes sure to select the correct connect() function
- * depending on whether this IP address is an IPv4 or an IPv6 address
- * (although we could always try with the IPv6 structure, it may or
- * may not work properly on all systems, so for now we use the
- * distinction.)
- *
- * \todo
- * Move this to our libsnapnetwork once we create that separate library.
- * Probably within a form of low level socket class.
- *
- * \param[in] s  The socket to connect to the address.
- *
- * \return 0 if the bind() succeeded, -1 on errors
- */
-int addr::connect(int s) const
-{
-    // only TCP can connect, UDP binds and sends only
-    //
-    switch(f_protocol)
-    {
-    case IPPROTO_IP: // interpret as TCP...
-    case IPPROTO_TCP:
-        if(is_ipv4())
-        {
-            // this would most certainly work using the IPv6 address
-            // as in the else part, but to be sure, we use the IPv4
-            // as specified in the address (there could be other reasons
-            // than just your OS for this to fail if using IPv6.)
-            //
-            // IMPORTANT NOTE: also the family is used in the socket()
-            //                 call above and must match the address here...
-            //
-            sockaddr_in ipv4;
-            get_ipv4(ipv4);
-            return ::connect(s, reinterpret_cast(&ipv4), sizeof(ipv4));
-        }
-        else
-        {
-            return ::connect(s, reinterpret_cast(&f_address), sizeof(struct sockaddr_in6));
-        }
-        break;
-
-    }
-
-    return -1;
-}
-
-
-/** \brief Create a server with this socket listening on this IP address.
- *
- * This function will bind the socket \p s to the address defined in
- * this addr object. This creates a server listening on that IP address.
- *
- * If the IP address is 127.0.0.1, then only local processes can connect
- * to that server. If the IP address is 0.0.0.0, then anyone can connect
- * to the server.
- *
- * This function works for TCP and UDP servers.
- *
- * If the IP address represents an IPv4 addressm then the bind() is done
- * with an IPv4 address and not the IPv6 as it is stored.
- *
- * \todo
- * Move this to our libsnapnetwork once we create that separate library.
- * Probably within a form of low level socket class.
- *
- * \param[in] s  The socket to bind to this address.
- *
- * \return 0 if the bind() succeeded, -1 on errors
- */
-int addr::bind(int s) const
-{
-    if(is_ipv4())
-    {
-        sockaddr_in ipv4;
-        get_ipv4(ipv4);
-        return ::bind(s, reinterpret_cast(&ipv4), sizeof(ipv4));
-    }
-    else
-    {
-        return ::bind(s, reinterpret_cast(&f_address), sizeof(struct sockaddr_in6));
-    }
-}
-
-
-/** \brief Initializes this addr object from a socket information.
- *
- * When you connect to a server or a clients connect to your server, the
- * socket defines two IP addresses and ports: one on your side and one on
- * the other side.
- *
- * The other side is called the _peer name_.
- *
- * You side is called the _socket name_ (i.e. the IP address of your computer,
- * representing the interface used to perform that connection.)
- *
- * If you call this function with \p peer set to false then you get the
- * address and port from your side. If you set \p peer to true,
- * you get the other side address and port details.
- *
- * \todo
- * Move this to our libsnapnetwork once we create that separate library.
- * Probably within a form of low level socket class.
- *
- * \param[in] s  The socket from which you want to retrieve peer information.
- * \param[in] peer  Whether to retrieve the peer or socket name.
- */
-void addr::set_from_socket(int s, bool peer)
-{
-    // make sure the socket is defined and well
-    //
-    if(s < 0)
-    {
-        throw addr_invalid_argument_exception("addr::set_from_socket(): the socket cannot be a negative number.");
-    }
-
-    struct sockaddr_storage address = sockaddr_storage();
-    socklen_t length(sizeof(address));
-    int r;
-    if(peer)
-    {
-        // this retrieves the information from the other side
-        //
-        r = getpeername(s, reinterpret_cast(&address), &length);
-    }
-    else
-    {
-        // retrieve the local socket information
-        //
-        r = getsockname(s, reinterpret_cast(&address), &length);
-    }
-    if(r != 0)
-    {
-        int const e(errno);
-        throw addr_io_exception(
-                  std::string("addr::set_from_socket(): ")
-                + (peer ? "getpeername()" : "getsockname()")
-                + " failed to retrieve IP address details (errno: "
-                + std::to_string(e)
-                + ", "
-                + strerror(e)
-                + ").");
-    }
-
-    switch(address.ss_family)
-    {
-    case AF_INET:
-        set_ipv4(reinterpret_cast(address));
-        break;
-
-    case AF_INET6:
-        set_ipv6(reinterpret_cast(address));
-        break;
-
-    default:
-        throw addr_invalid_state_exception(
-                  std::string("addr::set_from_socket(): ")
-                + (peer ? "getpeername()" : "getsockname()")
-                + " returned a type of address, which is not understood, i.e. not AF_INET or AF_INET6.");
-
-    }
-}
-
-
-/** \brief Transform the IP into a domain name.
- *
- * This function transforms the IP address in this `addr` object in a
- * name such as "snap.website".
- *
- * \note
- * The function does not cache the result because it is rarely used (at least
- * at this time). So you should cache the result and avoid calling this
- * function more than once as the process can be very slow.
- *
- * \todo
- * Speed enhancement can be achieved by using getaddrinfo_a(). That would
- * work with a vector of addr objects.
- *
- * \return The domain name. If not available, an empty string.
- */
-std::string addr::get_name() const
-{
-    char host[NI_MAXHOST];
-
-    int flags(NI_NAMEREQD);
-    if(f_protocol == IPPROTO_UDP)
-    {
-        flags |= NI_DGRAM;
-    }
-
-    // TODO: test with the NI_IDN* flags and make sure we know what we get
-    //       (i.e. we want UTF-8 as a result)
-    //
-    int const r(getnameinfo(reinterpret_cast(&f_address), sizeof(f_address), host, sizeof(host), nullptr, 0, flags));
-
-    // return value is 0, then it worked
-    //
-    return r == 0 ? host : std::string();
-}
-
-
-/** \brief Transform the port into a service name.
- *
- * This function transforms the port in this `addr` object in a
- * name such as "http".
- *
- * \note
- * The function does not cache the result because it is rarely used (at least
- * at this time). So you should cache the result and avoid calling this
- * function more than once as the process is somewhat slow.
- *
- * \warning
- * The getnameinfo() will return a string with a number if it does not
- * know the server (i.e. this is the equivalent to std::to_string() of
- * the port.) For port 0, the function always returns an empty string.
- *
- * \return The service name. If not available, an empty string.
- */
-std::string addr::get_service() const
-{
-    if(f_address.sin6_port == 0)
-    {
-        return std::string();
-    }
-
-    char service[NI_MAXSERV];
-
-    int flags(NI_NAMEREQD);
-    if(f_protocol == IPPROTO_UDP)
-    {
-        flags |= NI_DGRAM;
-    }
-    int const r(getnameinfo(reinterpret_cast(&f_address), sizeof(f_address), nullptr, 0, service, sizeof(service), flags));
-
-    // return value is 0, then it worked
-    //
-    return r == 0 ? service
-                  : std::string();
-}
-
-
-/** \brief Retrieve the port.
- *
- * This function retrieves the port of the IP address in host order.
- *
- * \return The port defined along this address.
- */
-int addr::get_port() const
-{
-    return ntohs(f_address.sin6_port);
-}
-
-
-/** \brief Retrieve the protocol.
- *
- * This function retrieves the protocol as specified on the
- * set_addr_port() function or corresponding constructor.
- *
- * You may change the protocol with the set_protocol() function.
- *
- * \return protocol such as IPPROTO_TCP or IPPROTO_UDP.
- */
-int addr::get_protocol() const
-{
-    return f_protocol;
-}
-
-
-/** \brief Check whether an IP matches a CIDR.
- *
- * When an IP address is defined along a mask, it can match a set of
- * other IP addresses. This function can be used to see whether
- * \p ip matches \p this IP address and mask.
- *
- * So in other words, the mask of `this` addr object is used to mask
- * both, `this` and `p` before comparing the masked result.
- *
- * \warning
- * This function only checks the IP address. It totally ignores the
- * port, family, protocol and other peripheral details.
- *
- * \param[in] ip  The address to match against this IP/mask CIDR.
- *
- * \return true if \p ip is a match.
- */
-bool addr::match(addr const & ip) const
-{
-    for(int idx(0); idx < 16; ++idx)
-    {
-        if((f_address.sin6_addr.s6_addr[idx] & f_mask[idx]) != (ip.f_address.sin6_addr.s6_addr[idx] & f_mask[idx]))
-        {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-
-/** \brief Check whether two addresses are equal.
- *
- * This function compares the left hand side (this) and the right
- * hand side (rhs) for equality. If both represent the same IP
- * address, then the function returns true.
- *
- * \warning
- * The function only compares the address itself. The family, port,
- * flow info, scope identifier, protocol are all ignored.
- *
- * \return true if \p this is equal to \p rhs.
- */
-bool addr::operator == (addr const & rhs) const
-{
-    return f_address.sin6_addr == rhs.f_address.sin6_addr;
-}
-
-
-/** \brief Check whether two addresses are not equal.
- *
- * This function compares the left hand side (this) and the right
- * hand side (rhs) for inequality. If both represent the same IP
- * address, then the function returns false.
- *
- * \warning
- * The function only compares the address itself. The family, port,
- * flow info, scope identifier, protocol are all ignored.
- *
- * \return true if \p this is not equal to \p rhs.
- */
-bool addr::operator != (addr const & rhs) const
-{
-    return f_address.sin6_addr != rhs.f_address.sin6_addr;
-}
-
-
-/** \brief Compare two addresses to know which one is smaller.
- *
- * This function compares the left hand side (this) and the right
- * hand side (rhs) to know which one is the smallest. If both
- * are equal or the left hand side is larger than the right hand
- * side, then it returns false, otherwise it returns true.
- *
- * \warning
- * The function only compares the address itself. The family, port,
- * flow info, scope identifier, protocol are all ignored.
- *
- * \return true if \p this is smaller than \p rhs.
- */
-bool addr::operator < (addr const & rhs) const
-{
-    return f_address.sin6_addr < rhs.f_address.sin6_addr;
-}
-
-
-/** \brief Compare two addresses to know which one is smaller or equal.
- *
- * This function compares the left hand side (this) and the right
- * hand side (rhs) to know whether the left hand side is smaller or
- * equal to thr right handside.
- *
- * \warning
- * The function only compares the address itself. The family, port,
- * flow info, scope identifier, protocol are all ignored.
- *
- * \return true if \p this is smaller than \p rhs.
- */
-bool addr::operator <= (addr const & rhs) const
-{
-    return f_address.sin6_addr <= rhs.f_address.sin6_addr;
-}
-
-
-/** \brief Compare two addresses to know which one is smaller.
- *
- * This function compares the left hand side (this) and the right
- * hand side (rhs) to know which one is the smallest. If both
- * are equal or the left hand side is larger than the right hand
- * side, then it returns false, otherwise it returns true.
- *
- * \warning
- * The function only compares the address itself. The family, port,
- * flow info, scope identifier, protocol are all ignored.
- *
- * \return true if \p this is smaller than \p rhs.
- */
-bool addr::operator > (addr const & rhs) const
-{
-    return f_address.sin6_addr > rhs.f_address.sin6_addr;
-}
-
-
-/** \brief Compare two addresses to know which one is smaller.
- *
- * This function compares the left hand side (this) and the right
- * hand side (rhs) to know which one is the smallest. If both
- * are equal or the left hand side is larger than the right hand
- * side, then it returns false, otherwise it returns true.
- *
- * \warning
- * The function only compares the address itself. The family, port,
- * flow info, scope identifier, protocol are all ignored.
- *
- * \return true if \p this is smaller than \p rhs.
- */
-bool addr::operator >= (addr const & rhs) const
-{
-    return f_address.sin6_addr >= rhs.f_address.sin6_addr;
-}
-
-
-/** \brief Mark that the address changed.
- *
- * This functions makes sure that some of the parameters being cached
- * get reset in such a way that checking the cache will again return
- * the correct answer.
- *
- * \sa get_network_type()
- */
-void addr::address_changed()
-{
-    f_private_network_defined = network_type_t::NETWORK_TYPE_UNDEFINED;
-}
-
-
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/addr_parser.cpp libaddr-1.0.18.0~xenial/src/addr_parser.cpp
--- libaddr-1.0.17.0~xenial/src/addr_parser.cpp	2019-03-17 06:58:37.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/addr_parser.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,1664 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief The implementation of the IP address parser.
- *
- * This function is used to parse IP addresses from a string to a
- * vector of ranges.
- */
-
-// self
-//
-#include "libaddr/addr_parser.h"
-#include "libaddr/addr_exceptions.h"
-
-// C++ library
-//
-#include 
-#include 
-
-// C library
-//
-#include 
-#include 
-
-
-
-namespace addr
-{
-
-
-namespace
-{
-
-
-/** \brief Delete an addrinfo structure.
- *
- * This deleter is used to make sure all the addinfo get released when
- * an exception occurs or the function using such exists.
- *
- * \param[in] ai  The addrinfo structure to free.
- */
-void addrinfo_deleter(struct addrinfo * ai)
-{
-    freeaddrinfo(ai);
-}
-
-
-}
-
-
-
-
-
-/** \brief Set the default IP addresses.
- *
- * This function sets the default IP addresses to be used by the parser
- * when the input string of the parse() function does not include an IP
- * address.
- *
- * The function expects either an IPv4 or an IPv6 address. It can be
- * called twice if you need to define both types of addresses (which
- * is often a good idea.)
- *
- * For example, the following input is considered valid when a default
- * address is defined:
- *
- * \code
- *      parser.parse(":123");
- * \endcode
- *
- * It returns the default address and port 123. Note that by default
- * an address is mandatory unless a default address is defined.
- *
- * To prevent the parser from working when no default and no address
- * are specified, then make sure to set the REQUIRED_ADDRESS allow
- * flag to true:
- *
- * \code
- *      parser.set_allow(parser.flag_t::REQUIRED_ADDRESS, true);
- *      // now address is mandatory
- * \endcode
- *
- * To completely prevent the use of an address in an input string, set
- * the `ADDRESS` and `REQUIRED_ADDRESS` values to false:
- *
- * \code
- *      parser.set_allow(parser.flag_t::ADDRESS,          false);
- *      parser.set_allow(parser.flag_t::REQUIRED_ADDRESS, false);
- * \endcode
- *
- * To remove both default IP addresses, call this function with an empty
- * string:
- *
- * \code
- *      parser.set_default_address(std::string());
- * \endcode
- *
- * \param[in] addr  The new address.
- */
-void addr_parser::set_default_address(std::string const & address)
-{
-    if(address.empty())
-    {
-        f_default_address4.clear();
-        f_default_address6.clear();
-    }
-    else if(address[0] == '[')
-    {
-        // remove the '[' and ']'
-        //
-        if(address.back() != ']')
-        {
-            throw addr_invalid_argument_exception("an IPv6 address starting with '[' must end with ']'.");
-        }
-        f_default_address6 = address.substr(1, address.length() - 2);
-    }
-    else if(address.find(':') != std::string::npos)
-    {
-        f_default_address6 = address;
-    }
-    else
-    {
-        f_default_address4 = address;
-    }
-}
-
-
-/** \brief Retrieve the default IP address for IPv4 parsing.
- *
- * This function returns a copy of the default IP address used by
- * the parser when the input string does not include an IP address.
- *
- * If the function returns an empty string, then no default address
- * is defined.
- *
- * \return The default IPv4 address.
- *
- * \sa get_default_address6()
- * \sa set_default_address()
- */
-std::string const & addr_parser::get_default_address4() const
-{
-    return f_default_address4;
-}
-
-
-/** \brief Retrieve the default IP address for IPv4 parsing.
- *
- * This function returns a copy of the default IP address used by
- * the parser when the input string does not include an IP address.
- *
- * If the function returns an empty string, then no default address
- * is defined.
- *
- * \return The default IPv6 address, without square brackets.
- *
- * \sa get_default_address4()
- * \sa set_default_address()
- */
-std::string const & addr_parser::get_default_address6() const
-{
-    return f_default_address6;
-}
-
-
-/** \brief Define the default port.
- *
- * This function is used to define the default port to use in the address
- * parser object. By default this is set to -1 meaning: no default port.
- *
- * This function accepts any port number from 0 to 65535. It also accepts
- * -1 to reset the port back to "no default".
- *
- * To prevent the parser from working when no default and no port
- * are specified, then make sure to set the REQUIRED_PORT allow
- * flag to true:
- *
- * \code
- *      parser.set_allow(parser.flag_t::REQUIRED_PORT, true);
- *      // now port is mandatory
- * \endcode
- *
- * To completely prevent the use of a port in an input string, set
- * the `PORT` and `REQUIRED_PORT` values to false:
- *
- * \code
- *      parser.set_allow(parser.flag_t::PORT,          false);
- *      parser.set_allow(parser.flag_t::REQUIRED_PORT, false);
- * \endcode
- *
- * \exception addr_invalid_argument_exception
- * If the port number is out of range, then this expcetion is raised.
- * The allowed range for a port is 0 to 65535. This function also
- * accepts -1 meaning that no default port is specified.
- *
- * \param[in] port  The new default port.
- */
-void addr_parser::set_default_port(int const port)
-{
-    if(port < -1
-    || port > 65535)
-    {
-        throw addr_invalid_argument_exception("addr_parser::set_default_port(): port must be in range [-1..65535].");
-    }
-
-    f_default_port = port;
-}
-
-
-/** \brief Retrieve the default port.
- *
- * This function retrieves the default port as defined by the
- * set_default_port() function.
- */
-int addr_parser::get_default_port() const
-{
-    return f_default_port;
-}
-
-
-/** \brief Define the default mask.
- *
- * This function is used to define the default mask. Note that the
- * default mask will not be used at all if the flag_t::MASK allow
- * flag is not set to true:
- *
- * \code
- *      parser.set_allow(parser.flag_t::MASK, true);
- *      parser.set_default_mask("255.255.0.0");
- *      parser.set_default_mask("[ffff:ffff:ffff::]");
- * \endcode
- *
- * The IPv6 mask does not require the square brackets (`'['` and `']'`).
- *
- * To remove the default mask, call this function with an empty
- * string:
- *
- * \code
- *      parser.set_default_mask(std::string());
- * \endcode
- *
- * \note
- * As you can see, here we expect the mask to be a string. This is because
- * it gets parsed as if it came from the input string of the parser. This
- * also means that if the mask is invalid, it will not be detected until
- * you attempt to parse an input string that does not include a mask and
- * the default gets used.
- *
- * \todo
- * Add a check of the default mask when it gets set so we can throw on
- * errors and that way it is much more likely that programmers can fix
- * their errors early.
- *
- * \param[in] mask  The mask to use by default.
- */
-void addr_parser::set_default_mask(std::string const & mask)
-{
-    if(mask.empty())
-    {
-        f_default_mask4.clear();
-        f_default_mask6.clear();
-    }
-    else if(mask[0] == '[')
-    {
-        // remove the '[' and ']'
-        //
-        if(mask.back() != ']')
-        {
-            throw addr_invalid_argument_exception("an IPv6 mask starting with '[' must end with ']'.");
-        }
-        f_default_mask6 = mask.substr(1, mask.length() - 2);
-    }
-    else if(mask.find(':') != std::string::npos)
-    {
-        f_default_mask6 = mask;
-    }
-    else
-    {
-        f_default_mask4 = mask;
-    }
-}
-
-
-/** \brief Retrieve the default mask.
- *
- * This function returns a reference to the mask as set by the
- * set_default_mask() function. The value is an empty string by
- * default.
- *
- * The default mask will be used if no mask is specified in the
- * input string to the parse() function. When no default mask
- * is defined, the mask is set to all 1s.
- *
- * \note
- * The default mask is a string, not a binary mask. It gets
- * converted by the parser at the time it is required.
- *
- * \return The default mask.
- *
- * \sa get_default_mask6()
- * \sa set_default_mask()
- */
-std::string const & addr_parser::get_default_mask4() const
-{
-    return f_default_mask4;
-}
-
-
-/** \brief Retrieve the default mask.
- *
- * This function returns a reference to the mask as set by the
- * set_default_mask() function. The value is an empty string by
- * default.
- *
- * The default mask will be used if no mask is specified in the
- * input string to the parse() function. When no default mask
- * is defined, the mask is set to all 1s.
- *
- * \note
- * The default mask is a string, not a binary mask. It gets
- * converted by the parser at the time it is required.
- *
- * \return The default mask.
- *
- * \sa get_default_mask4()
- * \sa set_default_mask()
- */
-std::string const & addr_parser::get_default_mask6() const
-{
-    return f_default_mask6;
-}
-
-
-/** \brief Set the protocol to use to filter addresses.
- *
- * This function sets the protocol as one of the following:
- *
- * \li "ip" -- only return IP address supporting the IP protocol
- * (this is offered because getaddrinfo() may return such IP addresses.)
- * \li "tcp" -- only return IP address supporting TCP
- * \li "udp" -- only return IP address supporting UDP
- *
- * Any other value is refused. To reset the protocol to the default,
- * which is "do not filter by protocol", call the clear_protocol().
- *
- * \exception addr_invalid_argument_exception
- * If the string passed to this function is not one of the acceptable
- * protocols (ip, tcp, udp), then this exception is raised.
- *
- * \param[in] protocol  The default protocol for this parser.
- *
- * \sa clear_protocol()
- * \sa get_protocol()
- */
-void addr_parser::set_protocol(std::string const & protocol)
-{
-    if(protocol == "ip")
-    {
-        f_protocol = IPPROTO_IP;
-    }
-    else if(protocol == "tcp")
-    {
-        f_protocol = IPPROTO_TCP;
-    }
-    else if(protocol == "udp")
-    {
-        f_protocol = IPPROTO_UDP;
-    }
-    else
-    {
-        // not a protocol we support
-        //
-        throw addr_invalid_argument_exception(
-                  std::string("unknown protocol \"")
-                + protocol
-                + "\", expected \"tcp\" or \"udp\".");
-    }
-}
-
-
-/** \brief Set the protocol to use to filter addresses.
- *
- * This function sets the protocol as one of the following:
- *
- * \li IPPROTO_IP -- only return IP address supporting the IP protocol
- * (this is offered because getaddrinfo() may return such IP addresses.)
- * \li IPPROTO_TCP -- only return IP address supporting TCP
- * \li IPPROTO_UDP -- only return IP address supporting UDP
- *
- * Any other value is refused. To reset the protocol to the default,
- * which is "do not filter by protocol", call the clear_protocol().
- *
- * \exception addr_invalid_argument_exception
- * If the string passed to this function is not one of the acceptable
- * protocols (ip, tcp, udp), then this exception is raised.
- *
- * \param[in] protocol  The default protocol for this parser.
- *
- * \sa clear_protocol()
- * \sa get_protocol()
- */
-void addr_parser::set_protocol(int const protocol)
-{
-    // make sure that's a protocol we support
-    //
-    switch(protocol)
-    {
-    case IPPROTO_IP:
-    case IPPROTO_TCP:
-    case IPPROTO_UDP:
-        break;
-
-    default:
-        throw addr_invalid_argument_exception(
-                  std::string("unknown protocol \"")
-                + std::to_string(protocol)
-                + "\", expected \"tcp\" or \"udp\".");
-
-    }
-
-    f_protocol = protocol;
-}
-
-
-/** \brief Use this function to reset the protocol back to "no default."
- *
- * This function sets the protocol to -1 (which is something you cannot
- * do by calling the set_protocol() functions above.)
- *
- * The -1 special value means that the protocol is not defined, that
- * there is no default. In most cases this means all the addresses
- * that match, ignoring the protocol, will be returned by the parse()
- * function.
- *
- * \sa set_protocol()
- * \sa get_protocol()
- */
-void addr_parser::clear_protocol()
-{
-    f_protocol = -1;
-}
-
-
-/** \brief Retrieve the protocol as defined by the set_protocol().
- *
- * This function returns the protocol number as defined by the
- * set_protocol.
- *
- * When defined, the protocol is used whenever we call the
- * getaddrinfo() function. In general, this means the IP addresses
- * returned will have  to match that protocol.
- *
- * This function may return -1. The value -1 is used as "do not
- * filter by protocol". The protocol can be set to -1 by calling
- * the clear_protocol() function.
- *
- * \return The parser default protocol.
- *
- * \sa set_protocol()
- * \sa clear_protocol()
- */
-int addr_parser::get_protocol() const
-{
-    return f_protocol;
-}
-
-
-/** \brief Set or clear allow flags in the parser.
- *
- * This parser has a set of flags it uses to know whether the input
- * string can include certain things such as a port or a mask.
- *
- * This function is used to allow or require certain parameters and
- * to disallow others.
- *
- * By default, the ADDRESS and PORT flags are set, meaning that an
- * address and a port can appear, but either or both are optinal.
- * If unspecified, then the default will be used. If not default
- * is defined, then the parser may fail in this situation.
- *
- * One problem is that we include contradictory syntatical features.
- * The parser supports lists of addresses separated by commas and
- * lists of ports separated by commas. Both are not supported
- * simultaneously. This means you want to allow multiple addresses
- * separated by commas, the function makes sure that the multiple
- * port separated by commas support is turned of.
- *
- * \li ADDRESS -- the IP address is allowed, but optional
- * \li REQUIRED_ADDRESS -- the IP address is mandatory
- * \li PORT -- the port is allowed, but optional
- * \li REQUIRED_PORT -- the port is mandatory
- * \li MASK -- the mask is allowed, but optional
- * \li MULTI_ADDRESSES_COMMAS -- the input can have multiple addresses
- * separated by commas, spaces are not allowed (prevents MULTI_PORTS_COMMAS)
- * \li MULTI_ADDRESSES_SPACES -- the input can have multiple addresses
- * separated by spaces
- * \li MULTI_ADDRESSES_COMMAS_AND_SPACES -- the input can have multiple
- * addresses separated by spaces and commas (prevents MULTI_PORTS_COMMAS)
- * \li MULTI_PORTS_SEMICOLONS -- the input can  have multiple ports
- * separated by semicolons _NOT IMPLEMENTED YET_
- * \li MULTI_PORTS_COMMAS -- the input can have multiple ports separated
- * by commas (prevents MULTI_ADDRESSES_COMMAS and
- * MULTI_ADDRESSES_COMMAS_AND_SPACES) _NOT IMPLEMENTED YET_
- * \li PORT_RANGE -- the input supports port ranges (p1-p2) _NOT
- * IMPLEMENTED YET_
- * \li ADDRESS_RANGE -- the input supports address ranges (addr-addr) _NOT
- * IMPLEMENTED YET_
- *
- * \param[in] flag  The flag to set or clear.
- * \param[in] allow  Whether to allow (true) or disallow (false).
- *
- * \sa get_allow()
- */
-void addr_parser::set_allow(flag_t const flag, bool const allow)
-{
-    if(flag < static_cast(0)
-    || flag >= flag_t::FLAG_max)
-    {
-        throw addr_invalid_argument_exception("addr_parser::set_allow(): flag has to be one of the valid flags.");
-    }
-
-    f_flags[static_cast(flag)] = allow;
-
-    // if we just set a certain flag, others may need to go to false
-    //
-    if(allow)
-    {
-        // we can only support one type of commas
-        //
-        switch(flag)
-        {
-        case flag_t::MULTI_ADDRESSES_COMMAS:
-        case flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES:
-            f_flags[static_cast(flag_t::MULTI_PORTS_COMMAS)] = false;
-            break;
-
-        case flag_t::MULTI_PORTS_COMMAS:
-            f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS)] = false;
-            f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)] = false;
-            break;
-
-        default:
-            break;
-
-        }
-    }
-}
-
-
-/** \brief Retrieve the current statius of an allow flag.
- *
- * This function returns the current status of the allow flags.
- *
- * By default, the `ADDRESS` and `PORT` flags are set to true.
- * All the other flags are set to false.
- *
- * You may change the value of an allow flag by calling the
- * set_allow() function.
- *
- * \param[in] flag  Which flag is to be checked.
- *
- * \return The value of the flag: true or false.
- *
- * \sa set_allow()
- */
-bool addr_parser::get_allow(flag_t const flag) const
-{
-    if(flag < static_cast(0)
-    || flag >= flag_t::FLAG_max)
-    {
-        throw addr_invalid_argument_exception("addr_parser::get_allow(): flag has to be one of the valid flags.");
-    }
-
-    return f_flags[static_cast(flag)];
-}
-
-
-/** \brief Check whether errors were registered so far.
- *
- * This function returns true if the system detected errors in one
- * of the previous calls to parse(). The flag can be cleared using
- * the clear_errors() function.
- *
- * On construction and after a call to clear_error(), this flag is
- * always false. If you are to call parser() multiple times with
- * the same addr_parser object, then you want to make sure to call
- * the clear_errors() function before calling the parse() function.
- * Otherwise you won't know whether errors occurred in a earlier
- * or later call.
- *
- * \code
- *      // first time, not required
- *      parser.parse(...);
- *      ...
- *
- *      // next time, required
- *      parser.clear_errors();
- *      parser.parse(...);
- *      ...
- * \endcode
- *
- * \return true if errors were generated.
- */
-bool addr_parser::has_errors() const
-{
-    return !f_error.empty();
-}
-
-
-/** \brief Emit an error and save it in this class.
- *
- * This function adds the message to the error string part of this
- * object. A newline is also added at the end of the message.
- *
- * Next the function increments the error counter.
- *
- * \note
- * You are expected to emit one error at a time. If you want to
- * emit several messages in a row, that will work and properly
- * count each message.
- *
- * \param[in] msg  The message to add to the parser error messages.
- *
- * \sa error_messages()
- */
-void addr_parser::emit_error(std::string const & msg)
-{
-    f_error += msg;
-    f_error += "\n";
-    ++f_error_count;
-}
-
-
-/** \brief Return the current error messages.
- *
- * The error messages are added to the addr_parser using the
- * emit_error() function.
- *
- * This function does not clear the list of error messages.
- * To do that, call the clear_errors() function.
- *
- * The number of messages can be determined by counting the
- * number of "\n" characters in the string. The error_count()
- * will return that same number (assuming no message included
- * a '\n' character when emit_error() was called.)
- *
- * \return A string with the list of messages.
- *
- * \sa emit_error()
- * \sa clear_errors()
- */
-std::string const & addr_parser::error_messages() const
-{
-    return f_error;
-}
-
-
-/** \brief Return the number of error messages that were emitted.
- *
- * Each time the emit_error() function is called, the error
- * counter is incremented by 1. This function returns that
- * error counter.
- *
- * The clear_errors() function can be used to clear the
- * counter back to zero.
- *
- * \return The number of errors that were emitted so far.
- *
- * \sa emit_error()
- */
-int addr_parser::error_count() const
-{
-    return f_error_count;
-}
-
-
-/** \brief Clear the error message and error counter.
- *
- * This function clears all the error messages and reset the
- * counter back to zero. In order words, it will be possible
- * to tell how many times the emit_error() was called since
- * the start or the last clear_errors() call.
- *
- * To retrieve a copy of the error counter, use the error_count()
- * function.
- *
- * \sa error_count()
- */
-void addr_parser::clear_errors()
-{
-    f_error.clear();
-    f_error_count = 0;
-}
-
-
-/** \brief Parse a string of addresses, ports, and masks.
- *
- * This function is used to parse the list of addresses defined
- * in the \p in parameter.
- *
- * One address is composed of one to three elements:
- *
- * \code
- *          [ address ] [ ':' port ] [ '/' mask ]
- * \endcode
- *
- * Although all three elements are optional (at least by default),
- * a valid address is expected to include at least one of the
- * three elements. (i.e. an empty string is just skipped silently.)
- *
- * ### Multiple Addresses
- *
- * Multiple addresses can be defined if at least one of the
- * `MULTI_ADDRESSES_COMMAS`, `MULTI_ADDRESSES_SPACES`, or
- * `MULTI_ADDRESSES_COMMAS_AND_SPACES` allow flags is set to true.
- *
- * Note that the `MULTI_ADDRESSES_COMMAS_AND_SPACES` has priotity. If
- * set to true, then both, commas and spaces are allowed between
- * addresses.
- *
- * Next comes `MULTI_ADDRESSES_COMMAS`: if set to true, addresses
- * must be separated by commas and spaces are not allowed.
- *
- * Finally we have `MULTI_ADDRESSES_SPACES`. If that one is true, then
- * addresses must be separated by spaces and commas are not allowed.
- *
- * ### Make Address Field Required
- *
- * To make the address field a required field, set the
- * `REQUIRED_ADDRESS` flag (see set_allow()) to true and do not define a
- * default address (see set_default_address()).
- *
- * ### Make Port Field Required
- *
- * To make the port field a required fiel, set the `REQUIRED_PORT`
- * flag (see set_allow()) to true and do not define a default port
- * (see set_default_port()).
- *
- * ### Allow Mask
- *
- * The mask cannot be made mandatory. However, you have to set
- * the `MASK` flag to true to allow it. By default it is not
- * allowed.
- *
- * ### Ranges
- *
- * At this time we do not need support for ranges so it did not yet
- * get implemented.
- *
- * \param[in] in  The input string to be parsed.
- */
-addr_range::vector_t addr_parser::parse(std::string const & in)
-{
-    addr_range::vector_t result;
-
-    if(f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)])
-    {
-        std::string comma_space(", ");
-        std::string::size_type s(0);
-        while(s < in.length())
-        {
-            // since C++11 we have a way to search for a set of character
-            // in a string with an algorithm!
-            //
-            auto const it(std::find_first_of(in.begin() + s, in.end(), comma_space.begin(), comma_space.end()));
-            std::string::size_type const e(it == in.end() ? in.length() : it - in.begin());
-            if(e > s)
-            {
-                parse_cidr(in.substr(s, e - s), result);
-            }
-            s = e + 1;
-        }
-    }
-    else if(f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS)]
-         || f_flags[static_cast(flag_t::MULTI_ADDRESSES_SPACES)])
-    {
-        char sep(f_flags[static_cast(flag_t::MULTI_ADDRESSES_COMMAS)] ? ',' : ' ');
-        std::string::size_type s(0);
-        while(s < in.length())
-        {
-            std::string::size_type e(in.find(sep, s));
-            if(e == std::string::npos)
-            {
-                e = in.length();
-            }
-            if(e > s)
-            {
-                parse_cidr(in.substr(s, e - s), result);
-            }
-            s = e + 1;
-        }
-    }
-    else
-    {
-        parse_cidr(in, result);
-    }
-
-    return result;
-}
-
-
-/** \brief Check one address.
- *
- * This function checks one address, although if it is a name, it could
- * represent multiple IP addresses.
- *
- * This function separate the address:port from the mask if the mask is
- * allowed. Then it parses the address:port part and the mask separately.
- *
- * \param[in] in  The address to parse.
- * \param[in,out] result  The list of resulting addresses.
- */
-void addr_parser::parse_cidr(std::string const & in, addr_range::vector_t & result)
-{
-    if(f_flags[static_cast(flag_t::MASK)])
-    {
-        // check whether there is a mask
-        //
-        std::string mask;
-
-        std::string address;
-        std::string::size_type const p(in.find('/'));
-        if(p != std::string::npos)
-        {
-            address = in.substr(0, p);
-            mask = in.substr(p + 1);
-        }
-        else
-        {
-            address = in;
-        }
-
-        int const errcnt(f_error_count);
-
-        // handle the address first
-        //
-        addr_range::vector_t addr_mask;
-        parse_address(address, mask, addr_mask);
-
-        // now check for the mask
-        //
-        for(auto & am : addr_mask)
-        {
-            std::string m(mask);
-            if(m.empty())
-            {
-                // the mask was not defined in the input, then adapt it to
-                // the type of address we got in 'am'
-                //
-                if(am.get_from().is_ipv4())
-                {
-                    m = f_default_mask4;
-                }
-                else
-                {
-                    // parse_mask() expects '[...]' around IPv6 addresses
-                    //
-                    m = "[" + f_default_mask6 + "]";
-                }
-            }
-
-            parse_mask(m, am.get_from());
-        }
-
-        // now append the list to the result if no errors occurred
-        //
-        if(errcnt == f_error_count)
-        {
-            result.insert(result.end(), addr_mask.begin(), addr_mask.end());
-        }
-    }
-    else
-    {
-        // no mask allowed, if there is one, this call will fail
-        //
-        parse_address(in, std::string(), result);
-    }
-}
-
-
-/** \brief Parse one address.
- *
- * This function is called with one address. It determines whether we
- * are dealing with an IPv4 or an IPv6 address and call the
- * corresponding sub-function.
- *
- * An address is considered an IPv6 address if it starts with a '['
- * character.
- *
- * \note
- * The input cannot include a mask. It has to already have been
- * removed.
- *
- * \note
- * The mask parameter is only used to determine whether this function
- * is being called with an IPv6 or not. It is otherwise ignored.
- *
- * \param[in] in  The input address eventually including a port.
- * \param[in] mask  The mask used to determine whether we are dealing with
- *                  an IPv6 or not.
- * \param[in,out] result  The list of resulting addresses.
- */
-void addr_parser::parse_address(std::string const & in, std::string const & mask, addr_range::vector_t & result)
-{
-    // With our only supported format, ipv6 addresses must be between square
-    // brackets. The address may just be a mask in which case the '[' may
-    // not be at the very start (i.e. "/[ffff:ffff::]")
-    //
-    if(in.empty()
-    || in[0] == ':')    // if it start with ':' then there is no address
-    {
-        // if the address is empty, then use the mask to determine the
-        // type of IP address (note: if the address starts with ':'
-        // it is considered empty since an IPv6 would have a '[' at
-        // the start)
-        //
-        if(!mask.empty())
-        {
-            if(mask[0] == '[')
-            {
-                // IPv6 parsing
-                //
-                parse_address6(in, result);
-            }
-            else
-            {
-                // if the number is 33 or more, it has to be IPv6, otherwise
-                // we cannot know...
-                //
-                int mask_count(0);
-                for(char const * s(mask.c_str()); *s != '\0'; ++s)
-                {
-                    if(*s >= '0' && *s <= '9')
-                    {
-                        mask_count = mask_count * 10 + *s - '0';
-                        if(mask_count > 1000)
-                        {
-                            // not valid
-                            //
-                            mask_count = -1;
-                            break;;
-                        }
-                    }
-                    else
-                    {
-                        // not a valid decimal number
-                        //
-                        mask_count = -1;
-                        break;
-                    }
-                }
-                if(mask_count > 32)
-                {
-                    parse_address6(in, result);
-                }
-                else
-                {
-                    parse_address4(in, result);
-                }
-            }
-        }
-        else
-        {
-            if(f_default_address4.empty()
-            && !f_default_address6.empty())
-            {
-                parse_address6(in, result);
-            }
-            else
-            {
-                parse_address4(in, result);
-            }
-        }
-    }
-    else
-    {
-        // if an address has a ']' then it is IPv6 even if the '['
-        // is missing, that being said, it is still considered
-        // invalid as per our processes
-        //
-        if(in[0] == '['
-        || in.find(']') != std::string::npos)
-        {
-            parse_address6(in, result);
-        }
-        else
-        {
-            // if there is no port, then a ':' can be viewed as an IPv6
-            // address because there is no other ':', but if there are
-            // '.' before the ':' then we assume that it is IPv4 still
-            //
-            if(!f_flags[static_cast(flag_t::PORT)]
-            && !f_flags[static_cast(flag_t::REQUIRED_PORT)])
-            {
-                std::string::size_type const p(in.find(':'));
-                if(p != std::string::npos
-                && in.find('.') > p)
-                {
-                    parse_address6(in, result);
-                }
-                else
-                {
-                    parse_address4(in, result);
-                }
-            }
-            else
-            {
-                parse_address4(in, result);
-            }
-        }
-    }
-}
-
-
-/** \brief Parse one IPv4 address.
- *
- * This function checks the input parameter \p in and extracts the
- * address and port. There is a port if the input strings includes
- * a `':'` character.
- *
- * If this function detects that a port is not allowed and yet
- * a `':'` character is found, then it generates an error and
- * returns without adding anything to `result`.
- *
- * \param[in] in  The input string with the address and optional port.
- * \param[in,out] result  The list of resulting addresses.
- */
-void addr_parser::parse_address4(std::string const & in, addr_range::vector_t & result)
-{
-    std::string address(f_default_address4);
-    std::string port_str(f_default_port == -1 ? std::string() : std::to_string(f_default_port));
-
-    std::string::size_type const p(in.find(':'));
-
-    if(f_flags[static_cast(flag_t::PORT)]
-    || f_flags[static_cast(flag_t::REQUIRED_PORT)])
-    {
-        // the address can include a port
-        //
-        if(p != std::string::npos)
-        {
-            // get the address only if not empty (otherwise we want to
-            // keep the default)
-            //
-            if(p > 0)
-            {
-                address = in.substr(0, p);
-            }
-
-            // get the port only if not empty (otherwise we want to
-            // keep the default)
-            //
-            if(p + 1 < in.length())
-            {
-                port_str = in.substr(p + 1);
-            }
-        }
-        else if(!in.empty())
-        {
-            address = in;
-        }
-    }
-    else
-    {
-        if(p != std::string::npos
-        && !f_flags[static_cast(flag_t::PORT)]
-        && !f_flags[static_cast(flag_t::REQUIRED_PORT)])
-        {
-            emit_error("Port not allowed (" + in + ").");
-            return;
-        }
-
-        if(!in.empty())
-        {
-            address = in;
-        }
-    }
-
-    parse_address_port(address, port_str, result, "0.0.0.0");
-}
-
-
-/** \brief Parse one IPv6 address.
- *
- * This function checks the input parameter \p in and extracts the
- * address and port. There is a port if the input strings includes
- * a `':'` character after the closing square bracket (`']'`).
- *
- * If this function detects that a port is not allowed and yet
- * a `':'` character is found, then it generates an error and
- * returns without adding anything to `result`.
- *
- * \note
- * This function can be called with an IPv6
- *
- * \param[in] in  The input string with the address and optional port.
- * \param[in,out] result  The list of resulting addresses.
- */
-void addr_parser::parse_address6(std::string const & in, addr_range::vector_t & result)
-{
-    std::string::size_type p(0);
-
-    std::string address(f_default_address6);
-    std::string port_str(f_default_port == -1 ? std::string() : std::to_string(f_default_port));
-
-    // if there is an address extract it otherwise put the default
-    //
-    if(!in.empty()
-    && in[0] == '[')
-    {
-        p = in.find(']');
-
-        if(p == std::string::npos)
-        {
-            emit_error("IPv6 is missing the ']' (" + in + ").");
-            return;
-        }
-
-        if(p != 1)
-        {
-            // get the address only if not empty (otherwise we want to
-            // keep the default) -- so we actually support "[]" to
-            // represent "use the default address if defined".
-            //
-            address = in.substr(1, p - 1);
-        }
-    }
-
-    // on entry 'p' is either 0 or the position of the ']' character
-    //
-    p = in.find(':', p);
-
-    if(p != std::string::npos)
-    {
-        if(f_flags[static_cast(flag_t::PORT)]
-        || f_flags[static_cast(flag_t::REQUIRED_PORT)])
-        {
-            // there is also a port, extract it
-            //
-            port_str = in.substr(p + 1);
-        }
-        else if(!f_flags[static_cast(flag_t::PORT)]
-             && !f_flags[static_cast(flag_t::REQUIRED_PORT)])
-        {
-            emit_error("Port not allowed (" + in + ").");
-            return;
-        }
-    }
-
-    parse_address_port(address, port_str, result, "::");
-}
-
-
-/** \brief Parse the address and port.
- *
- * This function receives an address and a port string and
- * convert them in an addr object which gets saved in
- * the specified result range vector.
- *
- * The address can be an IPv4 or an IPv6 address.
- *
- * The port may be numeric or a name such as `"http"`.
- *
- * \note
- * This function is not responsible for handling the default address
- * and default port. This is expected to be dealt with by the caller
- * if required.
- *
- * \param[in] address  The address to convert to binary.
- */
-void addr_parser::parse_address_port(std::string address, std::string const & port_str, addr_range::vector_t & result, std::string const & default_address)
-{
-    // make sure the port is good
-    //
-    if(port_str.empty()
-    && f_flags[static_cast(flag_t::REQUIRED_PORT)])
-    {
-        emit_error("Required port is missing.");
-        return;
-    }
-
-    // make sure the address is good
-    //
-    if(address.empty())
-    {
-        if(f_flags[static_cast(flag_t::REQUIRED_ADDRESS)])
-        {
-            emit_error("Required address is missing.");
-            return;
-        }
-        // internal default if no address was defined
-        // (TBD: should it be an IPv6 instead?)
-        //
-        address = default_address;
-    }
-
-    // prepare hints for the the getaddrinfo() function
-    //
-    struct addrinfo hints;
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
-    hints.ai_family = AF_UNSPEC;
-
-    switch(f_protocol)
-    {
-    case IPPROTO_TCP:
-        hints.ai_socktype = SOCK_STREAM;
-        hints.ai_protocol = IPPROTO_TCP;
-        break;
-
-    case IPPROTO_UDP:
-        hints.ai_socktype = SOCK_DGRAM;
-        hints.ai_protocol = IPPROTO_UDP;
-        break;
-
-    }
-
-    // convert address to binary
-    //
-    struct addrinfo * addrlist(nullptr);
-    {
-        errno = 0;
-        int const r(getaddrinfo(address.c_str(), port_str.c_str(), &hints, &addrlist));
-        if(r != 0)
-        {
-            // break on invalid addresses
-            //
-            int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
-            emit_error("Invalid address in \""
-                     + address
-                     + (port_str.empty() ? "" : ":")
-                     + port_str
-                     + "\" error "
-                     + std::to_string(r)
-                     + " -- "
-                     + gai_strerror(r)
-                     + " (errno: "
-                     + std::to_string(e)
-                     + " -- "
-                     + strerror(e)
-                     + ").");
-            return;
-        }
-    }
-    std::shared_ptr ai(addrlist, addrinfo_deleter);
-
-    bool first(true);
-    while(addrlist != nullptr)
-    {
-        // go through the addresses and create ranges and save that in the result
-        //
-        if(addrlist->ai_family == AF_INET)
-        {
-            if(addrlist->ai_addrlen != sizeof(struct sockaddr_in))
-            {
-                emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
-                         + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
-                         + ", expected"                                  // LCOV_EXCL_LINE
-                         + std::to_string(sizeof(struct sockaddr_in))    // LCOV_EXCL_LINE
-                         + ").");                                        // LCOV_EXCL_LINE
-            }
-            else
-            {
-                addr a(*reinterpret_cast(addrlist->ai_addr));
-                // in most cases we do not get a protocol from
-                // the getaddrinfo() function...
-                a.set_protocol(addrlist->ai_protocol);
-                addr_range r;
-                r.set_from(a);
-                result.push_back(r);
-            }
-        }
-        else if(addrlist->ai_family == AF_INET6)
-        {
-            if(addrlist->ai_addrlen != sizeof(struct sockaddr_in6))
-            {
-                emit_error("Unsupported address size ("                  // LCOV_EXCL_LINE
-                         + std::to_string(addrlist->ai_addrlen)          // LCOV_EXCL_LINE
-                         + ", expected "                                 // LCOV_EXCL_LINE
-                         + std::to_string(sizeof(struct sockaddr_in6))   // LCOV_EXCL_LINE
-                         + ").");                                        // LCOV_EXCL_LINE
-            }
-            else
-            {
-                addr a(*reinterpret_cast(addrlist->ai_addr));
-                a.set_protocol(addrlist->ai_protocol);
-                addr_range r;
-                r.set_from(a);
-                result.push_back(r);
-            }
-        }
-        else if(first)                                                  // LCOV_EXCL_LINE
-        {
-            // ignore errors from further addresses
-            //
-            emit_error("Unsupported address family "                     // LCOV_EXCL_LINE
-                     + std::to_string(addrlist->ai_family)               // LCOV_EXCL_LINE
-                     + ".");                                             // LCOV_EXCL_LINE
-        }
-
-        first = false;
-
-        addrlist = addrlist->ai_next;
-    }
-}
-
-
-/** \brief Parse a mask.
- *
- * If the input string is a decimal number, then use that as the
- * number of bits to clear.
- *
- * If the mask is not just one decimal number, try to convert it
- * as an address.
- *
- * If the string is neither a decimal number nor a valid IP address
- * then the parser adds an error string to the f_error variable.
- *
- * \param[in] mask  The mask to transform to binary.
- * \param[in,out] cidr  The address to which the mask will be added.
- */
-void addr_parser::parse_mask(std::string const & mask, addr & cidr)
-{
-    // no mask?
-    //
-    if(mask.empty())
-    {
-        // in the current implementation this cannot happen since we
-        // do not call this function when mask is empty
-        //
-        // hwoever, the algorithm below expect that 'mask' is not
-        // empty (otherwise we get the case of 0 even though it
-        // may not be correct.)
-        //
-        return;  // LCOV_EXCL_LINE
-    }
-
-    // the mask may be a decimal number or an address, if just one number
-    // then it's not an address, so test that first
-    //
-    uint8_t mask_bits[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
-
-    // convert the mask to an integer, if possible
-    //
-    int mask_count(0);
-    {
-        for(char const * s(mask.c_str()); *s != '\0'; ++s)
-        {
-            if(*s >= '0' && *s <= '9')
-            {
-                mask_count = mask_count * 10 + *s - '0';
-                if(mask_count > 1000)
-                {
-                    emit_error("Mask number too large ("
-                             + mask
-                             + ", expected a maximum of 128).");
-                    return;
-                }
-            }
-            else
-            {
-                mask_count = -1;
-                break;
-            }
-        }
-    }
-
-    // the conversion to an integer worked if mask_count != -1
-    //
-    if(mask_count != -1)
-    {
-        if(cidr.is_ipv4())
-        {
-            if(mask_count > 32)
-            {
-                emit_error("Unsupported mask size ("
-                         + std::to_string(mask_count)
-                         + ", expected 32 at the most for an IPv4).");
-                return;
-            }
-            mask_count = 32 - mask_count;
-        }
-        else
-        {
-            if(mask_count > 128)
-            {
-                emit_error("Unsupported mask size ("
-                         + std::to_string(mask_count)
-                         + ", expected 128 at the most for an IPv6).");
-                return;
-            }
-            mask_count = 128 - mask_count;
-        }
-
-        // clear a few bits at the bottom of mask_bits
-        //
-        int idx(15);
-        for(; mask_count > 8; mask_count -= 8, --idx)
-        {
-            mask_bits[idx] = 0;
-        }
-        mask_bits[idx] = 255 << mask_count;
-    }
-    else //if(mask_count < 0)
-    {
-        // prepare hints for the the getaddrinfo() function
-        //
-        struct addrinfo hints;
-        memset(&hints, 0, sizeof(hints));
-        hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
-        hints.ai_family = AF_UNSPEC;
-
-        switch(cidr.get_protocol())
-        {
-        case IPPROTO_TCP:
-            hints.ai_socktype = SOCK_STREAM;
-            hints.ai_protocol = IPPROTO_TCP;
-            break;
-
-        case IPPROTO_UDP:
-            hints.ai_socktype = SOCK_DGRAM;
-            hints.ai_protocol = IPPROTO_UDP;
-            break;
-
-        }
-
-        std::string const port_str(std::to_string(cidr.get_port()));
-
-        // if the mask is an IPv6, then it has to have the '[...]'
-        std::string m(mask);
-        if(cidr.is_ipv4())
-        {
-            if(mask[0] == '[')
-            {
-                emit_error("The address uses the IPv4 syntax, the mask cannot use IPv6.");
-                return;
-            }
-        }
-        else //if(!cidr.is_ipv4())
-        {
-            if(mask[0] != '[')
-            {
-                emit_error("The address uses the IPv6 syntax, the mask cannot use IPv4.");
-                return;
-            }
-            if(mask.back() != ']')
-            {
-                emit_error("The IPv6 mask is missing the ']' (" + mask + ").");
-                return;
-            }
-
-            // note that we know that mask.length() >= 2 here since
-            // we at least have a '[' and ']'
-            //
-            m = mask.substr(1, mask.length() - 2);
-            if(m.empty())
-            {
-                // an empty mask is valid, it just means keep the default
-                // (getaddrinfo() fails on an empty string)
-                //
-                return;
-            }
-        }
-
-        // if negative, we may have a full address here, so call the
-        // getaddrinfo() on this other string
-        //
-        struct addrinfo * masklist(nullptr);
-        errno = 0;
-        int const r(getaddrinfo(m.c_str(), port_str.c_str(), &hints, &masklist));
-        if(r != 0)
-        {
-            // break on invalid addresses
-            //
-            int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
-            emit_error("Invalid mask in \"/"
-                     + mask
-                     + "\", error "
-                     + std::to_string(r)
-                     + " -- "
-                     + gai_strerror(r)
-                     + " (errno: "
-                     + std::to_string(e)
-                     + " -- "
-                     + strerror(e)
-                     + ").");
-            return;
-        }
-        std::shared_ptr mask_ai(masklist, addrinfo_deleter);
-
-        if(cidr.is_ipv4())
-        {
-            if(masklist->ai_family != AF_INET)
-            {
-                // this one happens when the user does not put the '[...]'
-                // around an IPv6 address
-                //
-                emit_error("Incompatible address between the address and"
-                          " mask address (first was an IPv4 second an IPv6).");
-                return;
-            }
-            if(masklist->ai_addrlen != sizeof(struct sockaddr_in))
-            {
-                emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
-                        + std::to_string(masklist->ai_addrlen)          // LCOV_EXCL_LINE
-                        + ", expected"                                  // LCOV_EXCL_LINE
-                        + std::to_string(sizeof(struct sockaddr_in))    // LCOV_EXCL_LINE
-                        + ").");                                        // LCOV_EXCL_LINE
-                return;                                                 // LCOV_EXCL_LINE
-            }
-            memcpy(mask_bits + 12, &reinterpret_cast(masklist->ai_addr)->sin_addr.s_addr, 4); // last 4 bytes are the IPv4 address, keep the rest as 1s
-        }
-        else //if(!cidr.is_ipv4())
-        {
-            if(masklist->ai_family != AF_INET6)
-            {
-                // this one happens if the user puts the '[...]'
-                // around an IPv4 address
-                //
-                emit_error("Incompatible address between the address"
-                          " and mask address (first was an IPv6 second an IPv4).");
-                return;
-            }
-            if(masklist->ai_addrlen != sizeof(struct sockaddr_in6))
-            {
-                emit_error("Unsupported address size ("                 // LCOV_EXCL_LINE
-                         + std::to_string(masklist->ai_addrlen)         // LCOV_EXCL_LINE
-                         + ", expected "                                // LCOV_EXCL_LINE
-                         + std::to_string(sizeof(struct sockaddr_in6))  // LCOV_EXCL_LINE
-                         + ").");                                       // LCOV_EXCL_LINE
-                return;                                                 // LCOV_EXCL_LINE
-            }
-            memcpy(mask_bits, &reinterpret_cast(masklist->ai_addr)->sin6_addr.s6_addr, 16);
-        }
-    }
-
-    cidr.set_mask(mask_bits);
-}
-
-
-/** \brief Transform a string into an `addr` object.
- *
- * This function converts the string \p a in an IP address saved in
- * the returned addr object or throws an error if the conversion
- * fails.
- *
- * The port can be specified or set to -1. If -1, then there is no
- * default port. Either way, the port can be defined in `a`.
- *
- * The protocol can be specified, as a string. For example, you can
- * use "tcp". The default is no specific protocol which means any
- * type of IP address can be returned.
- *
- * \note
- * This function does not allow for address or port ranges. It is
- * expected to return exactly one addresss. A mask can be allowed,
- * though.
- *
- * \param[in] a  The address string to be converted.
- * \param[in] default_addrress  The default address or an empty string.
- * \param[in] default_port  The default port or -1
- * \param[in] protocol  The protocol the address has to be of, or the
- *                      empty string to allow any protocol.
- * \param[in] m  Whether to allow a mask (true) or not (false).
- *
- * \return The address converted in an `addr` object.
- */
-addr string_to_addr(
-          std::string const & a
-        , std::string const & default_address
-        , int default_port
-        , std::string const & protocol
-        , bool mask)
-{
-    addr_parser p;
-
-    if(!default_address.empty())
-    {
-        p.set_default_address(default_address);
-    }
-
-    p.set_default_port(default_port);
-
-    if(!protocol.empty())
-    {
-        p.set_protocol(protocol);
-    }
-
-    p.set_allow(addr_parser::flag_t::MASK, mask);
-
-    addr_range::vector_t result(p.parse(a));
-
-    if(result.size() != 1)
-    {
-        // when the protocol is not specified, this happens like all the
-        // time, we search for an entry with protocol TCP by default
-        // because in most cases that's what people want
-        //
-        result.erase(
-                  std::remove_if(
-                      result.begin()
-                    , result.end()
-                    , [](auto const it)
-                    {
-                        return it.has_from() && it.get_from().get_protocol() != IPPROTO_TCP;
-                    })
-                , result.end());
-        if(result.size() != 1)
-        {
-            // an invalid protocol is caught by the set_protocol()
-            // function so we should never be able to reach here
-            //
-            throw addr_invalid_argument_exception(                                                          // LCOV_EXCL_LINE
-                    "the address could not be converted in a single address in string_to_addr(), found "    // LCOV_EXCL_LINE
-                            + std::to_string(result.size())                                                 // LCOV_EXCL_LINE
-                            + " instead.");                                                                 // LCOV_EXCL_LINE
-        }
-    }
-
-    // at the movement we only can get a "to" so the following exceptions
-    // can't happen which is why we have an LCOV_EXCL_LINE
-    //
-    if(result[0].has_to()
-    || result[0].is_range())
-    {
-        throw addr_invalid_argument_exception("string_to_addr() does not support ranges.");     // LCOV_EXCL_LINE
-    }
-
-    if(!result[0].has_from())
-    {
-        throw addr_invalid_argument_exception("string_to_addr() has no 'from' address.");       // LCOV_EXCL_LINE
-    }
-
-    return result[0].get_from();
-}
-
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/addr_range.cpp libaddr-1.0.18.0~xenial/src/addr_range.cpp
--- libaddr-1.0.17.0~xenial/src/addr_range.cpp	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/addr_range.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,354 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief The implementation of the addr_range class.
- *
- * This file includes the implementation of the addr_range class
- * and the address_match_ranges() global function.
- */
-
-// self
-//
-#include "libaddr/addr_range.h"
-#include "libaddr/addr_exceptions.h"
-
-// C++ library
-//
-#include 
-
-
-
-namespace addr
-{
-
-
-/** \brief Return true if the range has a 'from' address defined.
- *
- * By default the 'from' and 'to' addresses of an addr_range are legal
- * but considered undefined. After you called the set_from() function
- * once, this function will always return true.
- *
- * \return false until 'set_from()' is called at least once.
- */
-bool addr_range::has_from() const
-{
-    return f_has_from;
-}
-
-
-/** \brief Return true if the range has a 'to' address defined.
- *
- * By default the 'from' and 'to' addresses of an addr_range are legal
- * but considered undefined. After you called the set_to() function
- * once, this function will always return true.
- *
- * \return false until 'set_to()' is called at least once.
- */
-bool addr_range::has_to() const
-{
-    return f_has_to;
-}
-
-
-/** \brief Determine whether an addr_range object is considered a range.
- *
- * This function returns false until both, set_from() and set_to(),
- * were called.
- *
- * Note that the order in which the two functions get called is not
- * important, although we generally expect set_from() to be called
- * first, it does not matter.
- *
- * \return true if both, 'from' and 'to', were set.
- */
-bool addr_range::is_range() const
-{
-    return f_has_from && f_has_to;
-}
-
-
-/** \brief Check whether this range is empty.
- *
- * If you defined the 'from' and 'to' addresses of the range, then you
- * can check whether the range is empty or not.
- *
- * A range is considered empty if 'from' is larger than 'to' because
- * in that case nothing can appear in between (no IP can at the same
- * time be larger than 'from' and smaller than 'to' if 'from > to'
- * is true.)
- *
- * \return true if 'from > to' and is_range() returns true.
- *
- * \sa is_range()
- * \sa has_from()
- * \sa has_to()
- */
-bool addr_range::is_empty() const
-{
-    if(!is_range())
-    {
-        return false;
-    }
-    return f_from > f_to;
-}
-
-
-/** \brief Check whether \p rhs is part of this range.
- *
- * If the address specified in rhs is part of this range, then the function
- * returns true. The 'from' and 'to' addresses are considered inclusive,
- * so if rhs is equal to 'from' or 'to', then the function returns true.
- *
- * If 'from' is larger than 'to' then the function already returns false
- * since the range represents an empty range.
- *
- * \exception addr_invalid_state_exception
- * The addr_range object must be a range or this function throws this
- * exception. To test whether you can call this function, first call
- * the is_range() function. If it returns true, then is_in() is available.
- *
- * \param[in] rhs  The address to check for inclusion.
- *
- * \return true if rhs is considered part of this range.
- */
-bool addr_range::is_in(addr const & rhs) const
-{
-    if(!is_range())
-    {
-        throw addr_invalid_state_exception("addr_range::is_in(): range is not complete (from or to missing.)");
-    }
-
-    if(f_from <= f_to)
-    {
-        //
-        return rhs >= f_from && rhs <= f_to;
-    }
-    //else -- from/to are swapped... this represents an empty range
-
-    return false;
-}
-
-
-/** \brief Set 'from' address.
- *
- * This function saves the 'from' address in this range object.
- *
- * Once this function was called at least once, the has_from() returns true.
- *
- * \param[in] from  The address to save as the 'from' address.
- */
-void addr_range::set_from(addr const & from)
-{
-    f_has_from = true;
-    f_from = from;
-}
-
-
-/** \brief Get 'from' address.
- *
- * This function return the 'from' address as set by the set_from()
- * functions.
- *
- * The get_from() function can be called even if the has_from()
- * function returns false. It will return a default address
- * (a new 'addr' object.)
- *
- * \return The address saved as the 'from' address.
- */
-addr & addr_range::get_from()
-{
-    return f_from;
-}
-
-
-/** \brief Get the 'from' address when addr_range is constant.
- *
- * This function return the 'from' address as set by the set_from()
- * functions.
- *
- * The get_from() function can be called even if the has_from()
- * function returns false. It will return a default address
- * (a new 'addr' object.)
- *
- * \return The address saved as the 'from' address.
- */
-addr const & addr_range::get_from() const
-{
-    return f_from;
-}
-
-
-/** \brief Set 'to' address.
- *
- * This function saves the 'to' address in this range object.
- *
- * Once this function was called at least once, the has_to() returns true.
- *
- * \param[in] to  The address to save as the 'to' address.
- */
-void addr_range::set_to(addr const & to)
-{
-    f_has_to = true;
-    f_to = to;
-}
-
-
-/** \brief Get the 'to' address.
- *
- * This function return the 'to' address as set by the set_to()
- * function.
- *
- * The get_from() function can be called even if the has_from()
- * function returns false. It will return a default address
- * (a new 'addr' object.)
- *
- * \return The address saved as the 'to' address.
- */
-addr & addr_range::get_to()
-{
-    return f_to;
-}
-
-
-/** \brief Get the 'to' address when addr_range is constant.
- *
- * This function return the 'to' address as set by the set_to()
- * function.
- *
- * The get_to() function can be called even if the has_to()
- * function returns false. It will return a default address
- * (a new 'addr' object.)
- *
- * \return The address saved as the 'to' address.
- */
-addr const & addr_range::get_to() const
-{
-    return f_to;
-}
-
-
-/** \brief Compute a new range with the part that is shared between both inputs.
- *
- * This function computers a range which encompasses all the addresses found
- * in \p this range and \p rhs range.
- *
- * If the two range do not intersect, then the resulting range will be an
- * empty range (see is_empty() for details).
- *
- * The new range receives the largest 'from' address from both inputs and
- * the smallest 'to' address from both inputs.
- *
- * \param[in] rhs  The other range to compute the intersection with.
- *
- * \return The resulting intersection range.
- */
-addr_range addr_range::intersection(addr_range const & rhs) const
-{
-    addr_range result;
-
-    result.set_from(f_from > rhs.f_from ? f_from : rhs.f_from);
-    result.set_to  (f_to   < rhs.f_to   ? f_to   : rhs.f_to);
-
-    return result;
-}
-
-
-/** \brief Check whether an address matches a range.
- *
- * This function checks whether an address matches a range of addresses.
- *
- * The range may be empty, in which case the result is always false.
- *
- * If the range is a range (i.e. 'from' and 'to' are both defined,)
- * then the is_in() function is used to determine whether the address
- * is a match.
- *
- * If only one of the 'from' or 'to' addresses is defined, then that
- * one address addr::match() function is used to determine whether the
- * input \p address is a match.
- *
- * \param[in] address  The address to match against a range of addresses.
- *
- * \return true if address matches this range.
- */
-bool addr_range::match(addr const & address) const
-{
-    // if neith 'from' nor 'to' were defined, return
-    //
-    if(is_empty())
-    {
-        return false;
-    }
-
-    if(is_range())
-    {
-        return is_in(address);
-    }
-
-    if(has_from())
-    {
-        return f_from.match(address);
-    }
-    else
-    {
-        // if not empty and it does not have 'from', it has to be 'to'
-        //
-        return f_to.match(address);
-    }
-}
-
-
-/** \brief Check whether an address matches a range.
- *
- * When you call the addr_parser::parse() function, you get a vector of
- * ranges as a result. This function allows you to check whether an
- * address matches any one of those ranges.
- *
- * \param[in] ranges  The vector of ranges to search for \p address.
- * \param[in] address  The address to search in \p ranges.
- *
- * \return true if \p address matches any one of the \p ranges.
- */
-bool address_match_ranges(addr_range::vector_t ranges, addr const & address)
-{
-    auto const it(std::find_if
-            ( ranges.begin()
-            , ranges.end()
-            , [&address](auto const & range)
-                {
-                    return range.match(address);
-                }
-            ));
-
-    return it != ranges.end();
-}
-
-
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/CMakeLists.txt libaddr-1.0.18.0~xenial/src/CMakeLists.txt
--- libaddr-1.0.17.0~xenial/src/CMakeLists.txt	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/CMakeLists.txt	1970-01-01 00:00:00.000000000 +0000
@@ -1,141 +0,0 @@
-#
-# File:         src/CMakeLists.txt
-# Object:       Definitions to create the build environment with cmake
-#
-# Copyright:    Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
-#
-# https://snapwebsites.org/project/libaddr
-# contact@m2osw.com
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-##
-## TLD parser
-##
-project(libaddr)
-
-configure_file(
-    libaddr/version.h.in
-    ${PROJECT_BINARY_DIR}/libaddr/version.h
-)
-
-include_directories(
-    ${addr_library_BINARY_DIR}/src
-    ${addr_library_SOURCE_DIR}/src
-    ${LIBEXCEPT_INCLUDE_DIRS}
-)
-
-add_library(addr SHARED
-    addr.cpp
-    addr_parser.cpp
-    addr_range.cpp
-    iface.cpp
-    route.cpp
-    version.cpp
-)
-
-set_target_properties(addr PROPERTIES
-    VERSION
-        ${LIBADDR_VERSION_MAJOR}.${LIBADDR_VERSION_MINOR}
-    SOVERSION
-        ${LIBADDR_VERSION_MAJOR}
-)
-
-target_link_libraries(addr
-    ${LIBEXCEPT_LIBRARIES}
-)
-
-install(
-    TARGETS
-        addr
-    LIBRARY DESTINATION
-        lib
-)
-
-install(
-    DIRECTORY
-        ${CMAKE_CURRENT_SOURCE_DIR}/libaddr
-    DESTINATION
-        include
-    FILES_MATCHING PATTERN
-        "libaddr/*.h"
-)
-
-install(
-    FILES
-        ${CMAKE_CURRENT_BINARY_DIR}/libaddr/version.h
-    DESTINATION
-        include/libaddr
-)
-
-##
-## Addr command line tool
-##
-project(validate_ip)
-
-include_directories(
-    ${addr_library_BINARY_DIR}/src
-    ${addr_library_SOURCE_DIR}/src
-    ${LIBEXCEPT_INCLUDE_DIRS}
-)
-
-add_executable(${PROJECT_NAME}
-    validate_ip.cpp
-)
-
-target_link_libraries(${PROJECT_NAME}
-    addr
-)
-
-install(
-    TARGETS
-        ${PROJECT_NAME}
-    RUNTIME DESTINATION
-        bin
-)
-
-##
-## Route command line tool
-##
-project(ipv4_routes)
-
-include_directories(
-    ${addr_library_BINARY_DIR}/src
-    ${addr_library_SOURCE_DIR}/src
-    ${LIBEXCEPT_INCLUDE_DIRS}
-)
-
-add_executable(${PROJECT_NAME}
-    ipv4_routes.cpp
-)
-
-target_link_libraries(${PROJECT_NAME}
-    addr
-)
-
-install(
-    TARGETS
-        ${PROJECT_NAME}
-    RUNTIME DESTINATION
-        bin
-)
-
-# vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/iface.cpp libaddr-1.0.18.0~xenial/src/iface.cpp
--- libaddr-1.0.17.0~xenial/src/iface.cpp	2019-03-17 06:58:37.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/iface.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,616 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief The implementation of the addr class.
- *
- * This file includes the implementation of the addr class. The one that
- * deals with low level classes.
- */
-
-// self
-//
-#include "libaddr/iface.h"
-
-// addr library
-//
-#include "libaddr/route.h"
-
-// C++ library
-//
-#include 
-#include 
-
-// C library
-//
-#include 
-#include 
-
-
-
-namespace addr
-{
-
-
-/** \brief Details used by the addr class implementation.
- *
- * We have a function to check whether an address is part of
- * the interfaces of your computer. This check requires the
- * use of a `struct ifaddrs` and as such it requires to
- * delete that structure. We define a deleter for that
- * strucure here.
- */
-namespace
-{
-
-/** \brief Delete an ifaddrs structure.
- *
- * This deleter is used to make sure all the ifaddrs get released when
- * an exception occurs or the function using such exists.
- *
- * \param[in] ia  The ifaddrs structure to free.
- */
-void ifaddrs_deleter(struct ifaddrs * ia)
-{
-    freeifaddrs(ia);
-}
-
-
-}
-// no name namespace
-
-
-
-
-
-/** \brief Initializes an interface name/index pair.
- *
- * This function creates an interface name/index object.
- *
- * In some circumstances (NETLINK) you need to specify the index of an
- * interface. The kernel keeps a list of interface by index starting at 1
- * and each have a name such as "eth0". This function initializes
- * one of those pairs.
- *
- * \param[in] index  The index of the interface.
- * \param[in] name  The name of the interface.
- */
-iface_index_name::iface_index_name(int index, std::string const & name)
-    : f_index(index)
-    , f_name(name)
-{
-}
-
-
-/** \brief Get the index of this interface.
- *
- * This function is used to retrieve the index of a name/index pair.
- *
- * \return The index of the name/index pair.
- */
-int iface_index_name::get_index() const
-{
-    return f_index;
-}
-
-
-/** \brief Get the name of this interface.
- *
- * This function is used to retrieve the name of a name/index pair.
- *
- * \return The name of the name/index pair.
- */
-std::string const & iface_index_name::get_name() const
-{
-    return f_name;
-}
-
-
-
-/** \brief Get the list of existing interfaces.
- *
- * This function gathers the complete list of interfaces by index and
- * name pairs. The result is a vector of iface_index_name objects.
- *
- * Note that over time the index of an interface can change since interfaces
- * can be added and removed from your network configuration. It is a good
- * idea to not cache that information.
- *
- * \return A vector of index/name pair objects.
- */
-iface_index_name::vector_t get_interface_name_index()
-{
-    iface_index_name::vector_t result;
-
-    // the index starts at 1
-    //
-    for(int index(1);; ++index)
-    {
-        // get the next interface name by index
-        //
-        char name[IF_NAMESIZE + 1];
-        if(if_indextoname(index, name) == nullptr)
-        {
-            return result;
-        }
-
-        // make sure the name is null terminated
-        //
-        name[IF_NAMESIZE] = '\0';
-
-        iface_index_name const in(index, name);
-        result.push_back(in);
-    }
-    // NOT REACHED, the loop is broken by a "return"
-}
-
-
-/** \brief Get the index of an interface from its name.
- *
- * If you are given the name of an interface, you can retrieve its index
- * by calling this function. The resulting value is the index from 1 to n.
- *
- * If the named interface is not found, then the function returns 0.
- *
- * \return The interface index or 0 on error.
- */
-unsigned int get_interface_index_by_name(std::string const & name)
-{
-    return if_nametoindex(name.c_str());
-}
-
-
-
-
-
-
-/** \brief Return a list of local addresses on this machine.
- *
- * Peruse the list of available interfaces, and return any detected
- * ip addresses in a vector.
- *
- * These addresses include:
- *
- * \li A mask whenever available (very likely if the interface is up).
- * \li A name you can retrieve with get_iface_name()
- * \li A set of flags defining the current status of the network interface
- *     (i.e. IFF_UP, IFF_BROADCAST, IFF_NOARP, etc.)
- *
- * \return A vector of all the local interface IP addresses.
- */
-iface::vector_t iface::get_local_addresses()
-{
-    // get the list of interface addresses
-    //
-    struct ifaddrs * ifa_start(nullptr);
-    if(getifaddrs(&ifa_start) != 0)
-    {
-        // TODO: Should this throw, or just return an empty list quietly?
-        //
-        return iface::vector_t(); // LCOV_EXCL_LINE
-    }
-
-    std::shared_ptr auto_free(ifa_start, ifaddrs_deleter);
-
-    uint8_t mask[16];
-    iface::vector_t iface_list;
-    for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
-    {
-        // the documentation says there may be no address at all
-        // skip such entries at the moment
-        //
-        if(ifa->ifa_addr == nullptr)
-        {
-            continue;
-        }
-
-        // initialize an interface
-        iface the_interface;
-
-        // copy the name and flags as is
-        //
-        // TBD: can the ifa_name even be a null pointer?
-        //
-        the_interface.f_name = ifa->ifa_name;
-        the_interface.f_flags = ifa->ifa_flags; // IFF_... flags (see `man 7 netdevice` search for SIOCGIFFLAGS)
-
-        // get the family to know how to treat the address
-        //
-        // when an interface has an IPv4 and an IPv6, there are two entries in
-        // the list, both with the same name
-        //
-        uint16_t const family(ifa->ifa_addr->sa_family);
-
-        switch(family)
-        {
-        case AF_INET:
-            the_interface.f_address.set_ipv4(*(reinterpret_cast(ifa->ifa_addr)));
-
-            if((ifa->ifa_flags & IFF_BROADCAST) != 0
-            && ifa->ifa_broadaddr != nullptr)
-            {
-                the_interface.f_broadcast_address.set_ipv4(*(reinterpret_cast(ifa->ifa_broadaddr)));
-            }
-            if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
-            && ifa->ifa_dstaddr != nullptr)  // LCOV_EXCL_LINE
-            {
-                the_interface.f_destination_address.set_ipv4(*(reinterpret_cast(ifa->ifa_dstaddr)));  // LCOV_EXCL_LINE
-            }
-
-            // if present, add the mask as well
-            //
-            if(ifa->ifa_netmask != nullptr)
-            {
-                // for the IPv4 mask, we have to break it down in such a
-                // way as to make it IPv6 compatible
-                //
-                memset(mask, 255, 12);
-                mask[12] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >>  0;
-                mask[13] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >>  8;
-                mask[14] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >> 16;
-                mask[15] = reinterpret_cast(ifa->ifa_netmask)->sin_addr.s_addr >> 24;
-                the_interface.f_address.set_mask(mask);
-            }
-            break;
-
-        case AF_INET6:
-            the_interface.f_address.set_ipv6(*(reinterpret_cast(ifa->ifa_addr)));
-
-            if((ifa->ifa_flags & IFF_BROADCAST) != 0
-            && ifa->ifa_broadaddr != nullptr)
-            {
-                the_interface.f_broadcast_address.set_ipv6(*(reinterpret_cast(ifa->ifa_broadaddr)));  // LCOV_EXCL_LINE
-            }
-            if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
-            && ifa->ifa_dstaddr != nullptr)  // LCOV_EXCL_LINE
-            {
-                the_interface.f_destination_address.set_ipv6(*(reinterpret_cast(ifa->ifa_dstaddr)));  // LCOV_EXCL_LINE
-            }
-
-            // if present, add the mask as well
-            //
-            if(ifa->ifa_netmask != nullptr)
-            {
-                the_interface.f_address.set_mask(reinterpret_cast(&reinterpret_cast(ifa->ifa_netmask)->sin6_addr));
-            }
-            break;
-
-        default:
-            // TODO: can we just ignore unexpected address families?
-            //throw addr_invalid_structure_exception( "Unknown address family!" );
-            continue;
-
-        }
-
-        iface_list.push_back(the_interface);
-    }
-
-    return iface_list;
-}
-
-
-/** \brief Get the interface name.
- *
- * This function returns the name of the interface such as 'eth0' or 'p4p1'.
- *
- * The name is used in a few places such as the ioctl() function with the
- * SIOCGIFMTU command. Otherwise, it's mainly for display and easing use
- * (i.e. to let users select which interface to connect to.)
- *
- * \return The interface name.
- */
-std::string iface::get_name() const
-{
-    return f_name;
-}
-
-
-/** \brief Get the interface setup flags.
- *
- * This function returns a set of flags defined on that interface. The flags
- * are defined in the `man 7 netdevice` as the IFF_... flags. The flags are
- * defined under the SIOCGIFFLAGS and SIOCSIFFLAGS entries.
- *
- * One flag of interest is the IFF_UP flag. This means the interface is
- * active (even if not actually in use.)
- *
- * \return The interface flags.
- */
-unsigned int iface::get_flags() const
-{   
-    return f_flags;
-}
-
-
-/** \brief Get this interface address.
- *
- * This function returns the address of the interface. This address is very
- * likely to have a mask (i.e. 192.168.0.0/255.255.0.0).
- *
- * The address may be an IPv4 or an IPv6 address.
- *
- * \return The address of the interface.
- */
-addr const & iface::get_address() const
-{
-    return f_address;
-}
-
-
-/** \brief Get the broadcast address.
- *
- * This function returns a constant reference to the broadcast address
- * of this interface. The address is always available in this class. It
- * will be set to the ANY address if it was not defined. Note, however,
- * that even though the ANY address is not a valid broadcast address,
- * you should call the has_broadcast_address() function to know whether
- * this address is indeed defined.
- *
- * \return The broadcast address of this interface.
- */
-addr const & iface::get_broadcast_address() const
-{
-    return f_broadcast_address;
-}
-
-
-/** \brief Get the destination address.
- *
- * This function returns a constant reference to the destination address
- * of this interface. The address is always available in this class. It
- * will be set to the ANY address if it was not defined. Note, however,
- * that the ANY address is a valid destination address (i.e. default
- * route).
- *
- * To know whether the destination address is defined in that interface,
- * make sure to call the has_destination_address() function first.
- *
- * \return The destination address of this interface.
- */
-addr const & iface::get_destination_address() const
-{
-    return f_destination_address;
-}
-
-
-/** \brief Check whether a broadcast address.
- *
- * The broadcast address is not present on all interfaces. When it is, this
- * function returns true.
- *
- * Note that you can always call the get_broadcast_address(), but if
- * undefined it will appear as a default address (NETWORK_TYPE_ANY)
- * which you can't distinguish from a valid address although a
- * the NETWORK_TYPE_ANY is not a valid address for a broacast
- * address.
- *
- * \note
- * When a broadcast address is defined on an interface, then there can't
- * be a destination address.
- *
- * \return true if a broadcast address is defined.
- */
-bool iface::has_broadcast_address() const
-{
-    return (f_flags & IFF_BROADCAST) != 0;
-}
-
-
-/** \brief Check whether this interface defines a destination address.
- *
- * This function returns true if this interface defined a destination
- * address. Either way you can call the get_destination_address()
- * function, however, the address will be of type NETWORK_TYPE_ANY
- * which does not tell you whether it was defined or not.
- *
- * Ethernet and the local interfaces all define a destination address.
- *
- * \note
- * The destination address is not assigned any specific mask (all
- * are ff or 255).
- *
- * \note
- * When there is a destination address defined on an interface, then
- * there can't be a broadcast address.
- *
- * \return true when that interface defined a destination address.
- */
-bool iface::has_destination_address() const
-{
-    return (f_flags & IFF_POINTOPOINT) != 0;
-}
-
-
-/** \brief Search for the interface corresponding to this address.
- *
- * Peruse the list of available interfaces and return the one that matches
- * this address if any, otherwise return a null pointer.
- *
- * Say you create an addr object with the IP address "127.0.0.1" and then
- * call this function. You will get a pointer to the "lo" interface and
- * can check the validity of the flags (i.e. is the interface UP, can it
- * BROADCAST or MULTICAST your UDP packets, etc.)
- *
- * If the address is a remote address, then this function returns a null
- * pointer.
- *
- * \note
- * This function replaces the addr::is_computer_interface_address() function.
- * If this function returns a non-null pointer when allow_default_destination
- * set to false, then you've got the same result plus you have access to all
- * the available information from that interface.
- *
- * \warning
- * If you allow for the default destination, this function calls the
- * route::get_ipv4_routes() function which can be costly. Try to avoid
- * doing that in a loop.
- *
- * \param[in] a  The address used to search for an interface.
- * \param[in] allow_default_destination  If true and \p a doesn't match
- *            any of the interfaces, use the one interface with its
- *            destination set to 0.0.0.0 or equivalent.
- *
- * \return A pointer to an interface IP address.
- */
-iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
-{
-    iface::vector_t interfaces(iface::get_local_addresses());
-
-    for(auto i : interfaces)
-    {
-        if(i.get_address().match(a))
-        {
-            return iface::pointer_t(new iface(i));
-        }
-    }
-
-    // if there is a default, keep a copy in case we do not find a
-    // local address while looking (and only if the user requested
-    // such, which is the default)
-    //
-    if(!allow_default_destination)
-    {
-        return iface::pointer_t();
-    }
-
-    // to determine the default interface, we need the list of routes
-    // so we first gather that information and then search for the
-    // interface that has that name
-    //
-    route::vector_t routes(route::get_ipv4_routes());
-    route::pointer_t default_route(find_default_route(routes));
-    if(default_route == nullptr)
-    {
-        return iface::pointer_t(); // LCOV_EXCL_LINE
-    }
-
-    std::string const & default_iface(default_route->get_interface_name());
-    auto it(std::find_if(
-              interfaces.cbegin()
-            , interfaces.cend()
-            , [default_iface](auto & i)
-            {
-                return i.get_name() == default_iface;
-            }));
-    if(it == interfaces.cend())
-    {
-        return iface::pointer_t(); // LCOV_EXCL_LINE
-    }
-
-    return iface::pointer_t(new iface(*it));
-}
-
-
-#if 0
-/** \brief Check whether this address represents this computer.
- *
- * This function reads the addresses as given to us by the getifaddrs()
- * function. This is a system function that returns a complete list of
- * all the addresses this computer is managing / represents. In other
- * words, a list of address that other computers can use to connect
- * to this computer (assuming proper firewall, of course.)
- *
- * \warning
- * The list of addresses from getifaddrs() is not being cached. So you
- * probably do not want to call this function in a loop. That being
- * said, I still would imagine that retrieving that list is fast.
- *
- * \todo
- * We need to apply the mask to make this work properly. This is why
- * the current implementation fails big time (used by snapcommunicator.cpp).
- *
- * \return a computer_interface_address_t enumeration: error, true, or
- *         false at this time; on error errno should be set to represent
- *         what the error was.
- */
-
-// replaced by with a pointer_t == nullptr if there was no match,
-// although make sure to set allow_default_destination to false
-//
-iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
-
-bool is_computer_interface_address(addr const & a)
-{
-    iface::vector_t interfaces(iface::get_local_addresses());
-
-    for(auto i : interfaces)
-    {
-    }
-
-
-    // TBD: maybe we could cache the ifaddrs for a small amount of time
-    //      (i.e. 1 minute) so additional calls within that time
-    //      can go even faster?
-    //
-
-    // get the list of interface addresses
-    //
-    struct ifaddrs * ifa_start(nullptr);
-    if(getifaddrs(&ifa_start) != 0)
-    {
-        return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
-    }
-    std::shared_ptr auto_free(ifa_start, ifaddrs_deleter);
-
-    bool const ipv4(a.is_ipv4());
-    uint16_t const family(ipv4 ? AF_INET : AF_INET6);
-    for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
-    {
-        if(ifa->ifa_addr != nullptr
-        && ifa->ifa_addr->sa_family == family)
-        {
-            if(ipv4)
-            {
-                // the interface address structure is a 'struct sockaddr_in'
-                //
-                if(memcmp(&reinterpret_cast(ifa->ifa_addr)->sin_addr,
-                            f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast(&f_address)->sin_addr,
-                            sizeof(struct in_addr)) == 0)
-                {
-                    return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
-                }
-            }
-            else
-            {
-                // the interface address structure is a 'struct sockaddr_in6'
-                //
-                if(memcmp(&reinterpret_cast(ifa->ifa_addr)->sin6_addr,
-                            &f_address.sin6_addr,
-                            sizeof(f_address.sin6_addr)) == 0)
-                {
-                    return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
-                }
-            }
-        }
-    }
-
-    return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
-}
-#endif
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/ipv4_routes.cpp libaddr-1.0.18.0~xenial/src/ipv4_routes.cpp
--- libaddr-1.0.17.0~xenial/src/ipv4_routes.cpp	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/ipv4_routes.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,138 +0,0 @@
-// Network Address -- get routes and print them, similar to system `route`
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief A tool to check the system routes.
- *
- * This tool is used to verify that our route class works as expected.
- */
-
-// addr library
-//
-#include "libaddr/route.h"
-
-// C++ library
-//
-#include 
-#include 
-#include 
-
-
-namespace
-{
-
-bool g_show_default = false;
-bool g_hide_headers = false;
-
-}
-
-int main(int argc, char * argv[])
-{
-    addr::route::vector_t routes(addr::route::get_ipv4_routes());
-
-    for(int idx(1); idx < argc; ++idx)
-    {
-        if(strcmp(argv[idx], "-h") == 0
-        || strcmp(argv[idx], "--help") == 0)
-        {
-            std::cout << "Usage: %s [-opts]" << std::endl;
-            std::cout << "where -opts is one or more of:" << std::endl;
-            std::cout << "  --help | -h        print out this help screen." << std::endl;
-            std::cout << "  --default | -d     only print the default route." << std::endl;
-            std::cout << "  --hide-headers     do not print the headers." << std::endl;
-            exit(1);
-        }
-        else if(strcmp(argv[idx], "-d") == 0
-             || strcmp(argv[idx], "--default") == 0)
-        {
-            g_show_default = true;
-        }
-        else if(strcmp(argv[idx], "--hide-headers") == 0)
-        {
-            g_hide_headers = true;
-        }
-        else
-        {
-            std::cerr << "error: unknown command line option \"" << argv[idx] << "\". Try --help for additional info." << std::endl;
-            exit(1);
-        }
-    }
-
-    if(routes.empty())
-    {
-        std::cerr << "error: no routes found, is your network up?" << std::endl;
-        return 1;
-    }
-
-    // headers
-    //
-    if(!g_hide_headers)
-    {
-        std::cout << "Iface   "
-                     "Destination     "
-                     "Gateway         "
-                     "Flags   "
-                     "RefCnt  "
-                     "Use     "
-                     "Metric  "
-                     "Mask            "
-                     "MTU     "
-                     "Window  "
-                     "IRTT    "
-                  << std::endl;
-    }
-
-    for(auto r : routes)
-    {
-        if(!g_show_default
-        || r->get_destination_address().is_default())
-        {
-            uint8_t mask[16];
-            r->get_destination_address().get_mask(mask);
-            std::stringstream m;
-            m << static_cast(mask[12]) << "."
-              << static_cast(mask[13]) << "."
-              << static_cast(mask[14]) << "."
-              << static_cast(mask[15]);
-            std::cout << std::left << std::setw( 8) << r->get_interface_name()
-                      << std::left << std::setw(16) << r->get_destination_address().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY)
-                      << std::left << std::setw(16) << r->get_gateway_address().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY)
-                      << std::left << std::setw( 8) << r->flags_to_string()
-                      << std::left << std::setw( 8) << r->get_reference_count()
-                      << std::left << std::setw( 8) << r->get_use()
-                      << std::left << std::setw( 8) << r->get_metric()
-                      << std::left << std::setw(16) << m.str()
-                      << std::left << std::setw( 8) << r->get_mtu()
-                      << std::left << std::setw( 8) << r->get_window()
-                      << std::left << std::setw( 8) << r->get_irtt()
-                      << std::endl;
-        }
-    }
-
-    return 0;
-}
-
-// vim: ts=4 sw=4 et
-
diff -Nru libaddr-1.0.17.0~xenial/src/libaddr/addr_exceptions.h libaddr-1.0.18.0~xenial/src/libaddr/addr_exceptions.h
--- libaddr-1.0.17.0~xenial/src/libaddr/addr_exceptions.h	2019-07-21 04:20:09.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/libaddr/addr_exceptions.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,87 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-#pragma once
-
-/** \file
- * \brief The libaddr exceptions.
- *
- * This header defines various exceptions used throughout the addr library.
- */
-
-// libexcept library
-//
-#include "libexcept/exception.h"
-
-
-namespace addr
-{
-
-
-class addr_exception : public libexcept::exception_t
-{
-public:
-    addr_exception(char const *        what_msg) : exception_t(std::string("addr: ") + what_msg) {}
-    addr_exception(std::string const & what_msg) : exception_t("addr: " + what_msg) {}
-};
-
-class addr_invalid_argument_exception : public addr_exception
-{
-public:
-    addr_invalid_argument_exception(char const *        what_msg) : addr_exception(what_msg) {}
-    addr_invalid_argument_exception(std::string const & what_msg) : addr_exception(what_msg) {}
-};
-
-class addr_invalid_state_exception : public addr_exception
-{
-public:
-    addr_invalid_state_exception(char const *        what_msg) : addr_exception(what_msg) {}
-    addr_invalid_state_exception(std::string const & what_msg) : addr_exception(what_msg) {}
-};
-
-class addr_io_exception : public addr_exception
-{
-public:
-    addr_io_exception(char const *        what_msg) : addr_exception(what_msg) {}
-    addr_io_exception(std::string const & what_msg) : addr_exception(what_msg) {}
-};
-
-class addr_invalid_structure_exception : public libexcept::logic_exception_t
-{
-public:
-    addr_invalid_structure_exception(char const *        what_msg) : logic_exception_t(what_msg) {}
-    addr_invalid_structure_exception(std::string const & what_msg) : logic_exception_t(what_msg) {}
-};
-
-class addr_invalid_parameter_exception : public libexcept::logic_exception_t
-{
-public:
-    addr_invalid_parameter_exception(char const *        what_msg) : logic_exception_t(what_msg) {}
-    addr_invalid_parameter_exception(std::string const & what_msg) : logic_exception_t(what_msg) {}
-};
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/libaddr/addr.h libaddr-1.0.18.0~xenial/src/libaddr/addr.h
--- libaddr-1.0.17.0~xenial/src/libaddr/addr.h	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/libaddr/addr.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,233 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-#pragma once
-
-/** \file
- * \brief The various libaddr classes.
- *
- * This header includes the base addr class used to handle one binary
- * address.
- */
-
-// C++ library
-//
-#include 
-#include 
-#include 
-
-// C library
-//
-#include 
-
-
-
-namespace addr
-{
-
-
-
-
-/** \brief Initialize an IPv6 address as such.
- *
- * This function initializes a sockaddr_in6 with all zeroes except
- * for the sin6_family which is set to AF_INET6.
- *
- * return The initialized IPv6 address.
- */
-constexpr struct sockaddr_in6 init_in6()
-{
-    struct sockaddr_in6 in6 = sockaddr_in6();
-    in6.sin6_family = AF_INET6;
-    return in6;
-}
-
-
-class addr
-{
-public:
-    enum class network_type_t
-    {
-        NETWORK_TYPE_UNDEFINED,
-        NETWORK_TYPE_PRIVATE,
-        NETWORK_TYPE_CARRIER,
-        NETWORK_TYPE_LINK_LOCAL,
-        NETWORK_TYPE_MULTICAST,
-        NETWORK_TYPE_LOOPBACK,
-        NETWORK_TYPE_ANY,
-        NETWORK_TYPE_UNKNOWN,
-        NETWORK_TYPE_PUBLIC = NETWORK_TYPE_UNKNOWN  // we currently do not distinguish public and unknown
-    };
-
-    enum class string_ip_t
-    {
-        STRING_IP_ONLY,
-        STRING_IP_BRACKETS,         // IPv6 only
-        STRING_IP_PORT,             // in IPv6, includes brackets
-        STRING_IP_MASK,
-        STRING_IP_BRACKETS_MASK,    // IPv6 only
-        STRING_IP_ALL
-    };
-
-    typedef std::shared_ptr   pointer_t;
-    typedef std::vector       vector_t;
-    typedef int                     socket_flag_t;
-
-    static socket_flag_t const      SOCKET_FLAG_CLOEXEC  = 0x01;
-    static socket_flag_t const      SOCKET_FLAG_NONBLOCK = 0x02;
-    static socket_flag_t const      SOCKET_FLAG_REUSE    = 0x04;
-
-                                    addr();
-                                    addr(struct sockaddr_in const & in);
-                                    addr(struct sockaddr_in6 const & in6);
-
-    void                            set_from_socket(int s, bool peer);
-    void                            set_ipv4(struct sockaddr_in const & in);
-    void                            set_ipv6(struct sockaddr_in6 const & in6);
-    void                            set_port(int port);
-    void                            set_protocol(char const * protocol);
-    void                            set_protocol(int protocol);
-    void                            set_mask(uint8_t const * mask);
-    void                            apply_mask();
-
-    bool                            is_default() const;
-    bool                            is_ipv4() const;
-    void                            get_ipv4(struct sockaddr_in & in) const;
-    void                            get_ipv6(struct sockaddr_in6 & in6) const;
-    std::string                     to_ipv4_string(string_ip_t mode) const;
-    std::string                     to_ipv6_string(string_ip_t mode) const;
-    std::string                     to_ipv4or6_string(string_ip_t mode) const;
-
-    network_type_t                  get_network_type() const;
-    std::string                     get_network_type_string() const;
-
-    int                             create_socket(socket_flag_t flags) const;
-    int                             connect(int s) const;
-    int                             bind(int s) const;
-    std::string                     get_name() const;
-    std::string                     get_service() const;
-    int                             get_port() const;
-    int                             get_protocol() const;
-    void                            get_mask(uint8_t * mask) const;
-
-    bool                            match(addr const & ip) const;
-    bool                            operator == (addr const & rhs) const;
-    bool                            operator != (addr const & rhs) const;
-    bool                            operator <  (addr const & rhs) const;
-    bool                            operator <= (addr const & rhs) const;
-    bool                            operator >  (addr const & rhs) const;
-    bool                            operator >= (addr const & rhs) const;
-
-private:
-    void                            address_changed();
-
-    // always keep address in an IPv6 structure
-    //
-    struct sockaddr_in6             f_address = init_in6();
-    uint8_t                         f_mask[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
-    int                             f_protocol = IPPROTO_TCP;
-    mutable network_type_t          f_private_network_defined = network_type_t::NETWORK_TYPE_UNDEFINED;
-};
-
-
-
-
-
-}
-// addr namespace
-
-
-inline bool operator == (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
-{
-    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) == 0;
-}
-
-
-inline bool operator != (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
-{
-    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) != 0;
-}
-
-
-inline bool operator < (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
-{
-    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) < 0;
-}
-
-
-inline bool operator <= (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
-{
-    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) <= 0;
-}
-
-
-inline bool operator > (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
-{
-    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) > 0;
-}
-
-
-inline bool operator >= (struct sockaddr_in6 const & a, struct sockaddr_in6 const & b)
-{
-    return memcmp(&a, &b, sizeof(struct sockaddr_in6)) >= 0;
-}
-
-
-inline bool operator == (in6_addr const & a, in6_addr const & b)
-{
-    return memcmp(&a, &b, sizeof(in6_addr)) == 0;
-}
-
-
-inline bool operator != (in6_addr const & a, in6_addr const & b)
-{
-    return memcmp(&a, &b, sizeof(in6_addr)) != 0;
-}
-
-
-inline bool operator < (in6_addr const & a, in6_addr const & b)
-{
-    return memcmp(&a, &b, sizeof(in6_addr)) < 0;
-}
-
-
-inline bool operator <= (in6_addr const & a, in6_addr const & b)
-{
-    return memcmp(&a, &b, sizeof(in6_addr)) <= 0;
-}
-
-
-inline bool operator > (in6_addr const & a, in6_addr const & b)
-{
-    return memcmp(&a, &b, sizeof(in6_addr)) > 0;
-}
-
-
-inline bool operator >= (in6_addr const & a, in6_addr const & b)
-{
-    return memcmp(&a, &b, sizeof(in6_addr)) >= 0;
-}
-
-
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/libaddr/addr_parser.h libaddr-1.0.18.0~xenial/src/libaddr/addr_parser.h
--- libaddr-1.0.17.0~xenial/src/libaddr/addr_parser.h	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/libaddr/addr_parser.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,124 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-#pragma once
-
-/** \file
- * \brief The declaration of the parser of the libaddr classes.
- *
- * This header includes the addr_parser class used to parse user input
- * string and convert them to binary IP addresses.
- */
-
-// self
-//
-#include "libaddr/addr_range.h"
-
-
-
-namespace addr
-{
-
-
-class addr_parser
-{
-public:
-    enum class flag_t
-    {
-        ADDRESS,                            // address (IP)
-        REQUIRED_ADDRESS,                   // address cannot be empty
-        PORT,                               // port
-        REQUIRED_PORT,                      // port cannot be empty
-        MASK,                               // mask
-        MULTI_ADDRESSES_COMMAS,             // IP:port/mask,IP:port/mask,...
-        MULTI_ADDRESSES_SPACES,             // IP:port/mask IP:port/mask ...
-        MULTI_ADDRESSES_COMMAS_AND_SPACES,  // IP:port/mask, IP:port/mask, ...
-
-        // the following are not yet implemented
-        MULTI_PORTS_SEMICOLONS,             // port1;port2;...
-        MULTI_PORTS_COMMAS,                 // port1,port2,...
-        PORT_RANGE,                         // port1-port2
-        ADDRESS_RANGE,                      // IP-IP
-
-        FLAG_max
-    };
-
-    void                    set_default_address(std::string const & address);
-    std::string const &     get_default_address4() const;
-    std::string const &     get_default_address6() const;
-
-    void                    set_default_port(int const port);
-    int                     get_default_port() const;
-
-    void                    set_default_mask(std::string const & mask);
-    std::string const &     get_default_mask4() const;
-    std::string const &     get_default_mask6() const;
-
-    void                    set_protocol(std::string const & protocol);
-    void                    set_protocol(int const protocol);
-    void                    clear_protocol();
-    int                     get_protocol() const;
-
-    void                    set_allow(flag_t const flag, bool const allow);
-    bool                    get_allow(flag_t const flag) const;
-
-    bool                    has_errors() const;
-    void                    emit_error(std::string const & msg);
-    std::string const &     error_messages() const;
-    int                     error_count() const;
-    void                    clear_errors();
-
-    addr_range::vector_t    parse(std::string const & in);
-
-private:
-    void                    parse_cidr(std::string const & in, addr_range::vector_t & result);
-    void                    parse_address(std::string const & in, std::string const & mask, addr_range::vector_t & result);
-    void                    parse_address4(std::string const & in, addr_range::vector_t & result);
-    void                    parse_address6(std::string const & in, addr_range::vector_t & result);
-    void                    parse_address_port(std::string address, std::string const & port_str, addr_range::vector_t & result, std::string const & default_address);
-    void                    parse_mask(std::string const & mask, addr & cidr);
-
-    bool                    f_flags[static_cast(flag_t::FLAG_max)] = { true, false, true, false, false, false, false, false, false, false, false, false };
-    std::string             f_default_address4 = std::string();
-    std::string             f_default_address6 = std::string();
-    std::string             f_default_mask4 = std::string();
-    std::string             f_default_mask6 = std::string();
-    int                     f_protocol = -1;
-    int                     f_default_port = -1;
-    std::string             f_error = std::string();
-    int                     f_error_count = 0;
-};
-
-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);
-
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/libaddr/addr_range.h libaddr-1.0.18.0~xenial/src/libaddr/addr_range.h
--- libaddr-1.0.17.0~xenial/src/libaddr/addr_range.h	2019-03-28 03:37:04.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/libaddr/addr_range.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,87 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-#pragma once
-
-/** \file
- * \brief The addr_range class.
- *
- * This header defines the addr_range class used to handle one range of
- * addresses (with a 'from' and a 'to' pair of addresses.)
- *
- * Also we support CIDR, a CIDR is not a range. A range can define anything
- * that is not a perfect CIDR match. For example, you could have a start
- * address of 192.168.10.5 and an end address of 192.168.10.10.
- */
-
-// self
-//
-#include "libaddr/addr.h"
-
-
-
-namespace addr
-{
-
-
-
-
-class addr_range
-{
-public:
-    typedef std::shared_ptr     pointer_t;
-    typedef std::vector         vector_t;
-
-    bool                            has_from() const;
-    bool                            has_to() const;
-    bool                            is_range() const;
-    bool                            is_empty() const;
-    bool                            is_in(addr const & rhs) const;
-
-    void                            set_from(addr const & from);
-    addr &                          get_from();
-    addr const &                    get_from() const;
-    void                            set_to(addr const & to);
-    addr &                          get_to();
-    addr const &                    get_to() const;
-
-    addr_range                      intersection(addr_range const & rhs) const;
-    bool                            match(addr const & address) const;
-
-private:
-    bool                            f_has_from = false;
-    bool                            f_has_to = false;
-    addr                            f_from = addr();
-    addr                            f_to = addr();
-};
-
-
-bool address_match_ranges(addr_range::vector_t ranges, addr const & address);
-
-
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/libaddr/iface.h libaddr-1.0.18.0~xenial/src/libaddr/iface.h
--- libaddr-1.0.17.0~xenial/src/libaddr/iface.h	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/libaddr/iface.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,95 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-#pragma once
-
-/** \file
- * \brief The various libaddr classes.
- *
- * This header includes the base addr class used to handle one binary
- * address.
- */
-
-// addr library
-//
-#include 
-
-
-
-namespace addr
-{
-
-
-class iface_index_name
-{
-public:
-    typedef std::vector   vector_t;
-
-                                    iface_index_name(int index, std::string const & name);
-
-    int                             get_index() const;
-    std::string const &             get_name() const;
-
-private:
-    int                             f_index;
-    std::string                     f_name;
-};
-
-iface_index_name::vector_t          get_interface_name_index();
-
-
-class iface
-{
-public:
-    typedef std::shared_ptr  pointer_t;
-    typedef std::vector      vector_t;
-
-    static iface::vector_t          get_local_addresses();
-
-    std::string                     get_name() const;
-    unsigned int                    get_flags() const;
-    addr const &                    get_address() const;
-    addr const &                    get_broadcast_address() const;
-    addr const &                    get_destination_address() const;
-
-    bool                            has_broadcast_address() const;
-    bool                            has_destination_address() const;
-
-private:
-    std::string                     f_name = std::string();
-    unsigned int                    f_flags = 0;
-    addr                            f_address = addr();
-    addr                            f_broadcast_address = addr();
-    addr                            f_destination_address = addr();
-};
-
-
-
-iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination = true);
-
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/libaddr/route.h libaddr-1.0.18.0~xenial/src/libaddr/route.h
--- libaddr-1.0.17.0~xenial/src/libaddr/route.h	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/libaddr/route.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,84 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-#pragma once
-
-/** \file
- * \brief The various route class.
- *
- * This header includes the route class used to handle routes as found
- * in the routing tables of the kernel.
- */
-
-// addr lib
-//
-#include "libaddr/addr.h"
-
-
-
-namespace addr
-{
-
-
-
-class route
-{
-public:
-    typedef std::shared_ptr  pointer_t;
-    typedef std::vector  vector_t;
-
-    static vector_t                 get_ipv4_routes();
-
-    std::string const &             get_interface_name() const;
-    addr const &                    get_destination_address() const;
-    addr const &                    get_gateway_address() const;
-    int                             get_flags() const;
-    std::string                     flags_to_string() const;
-    int                             get_reference_count() const;
-    int                             get_use() const;
-    int                             get_metric() const;
-    int                             get_mtu() const;
-    int                             get_window() const;
-    int                             get_irtt() const;
-
-private:
-    std::string                     f_interface_name = std::string();
-    addr                            f_destination_address = addr();  // Destination + Mask
-    addr                            f_gateway_address = addr();
-    int                             f_flags = 0;
-    int                             f_reference_count = 0;
-    int                             f_use = 0;
-    int                             f_metric = 0;
-    int                             f_mtu = 0;
-    int                             f_window = 0;
-    int                             f_irtt = 0;
-};
-
-
-route::pointer_t find_default_route(route::vector_t const & routes);
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/libaddr/version.h.in libaddr-1.0.18.0~xenial/src/libaddr/version.h.in
--- libaddr-1.0.17.0~xenial/src/libaddr/version.h.in	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/libaddr/version.h.in	1970-01-01 00:00:00.000000000 +0000
@@ -1,50 +0,0 @@
-// Snap Websites Server -- version of libaddr
-// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-#pragma once
-
-/** \file
- * \brief Version header.
- *
- * This header file defines the snapwebsites library version information.
- *
- * See the addr.cpp/.h for other details.
- */
-
-#define    LIBADDR_VERSION_MAJOR   @LIBADDR_VERSION_MAJOR@
-#define    LIBADDR_VERSION_MINOR   @LIBADDR_VERSION_MINOR@
-#define    LIBADDR_VERSION_PATCH   @LIBADDR_VERSION_PATCH@
-#define    LIBADDR_VERSION_STRING  "@LIBADDR_VERSION_MAJOR@.@LIBADDR_VERSION_MINOR@.@LIBADDR_VERSION_PATCH@"
-
-namespace addr
-{
-
-int             get_version_major();
-int             get_version_minor();
-int             get_version_patch();
-char const *    get_version_string();
-
-}
-
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/route.cpp libaddr-1.0.18.0~xenial/src/route.cpp
--- libaddr-1.0.17.0~xenial/src/route.cpp	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/route.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,437 +0,0 @@
-// Network Address -- route class functions
-// Copyright (c) 2018-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief The implementation of the route class.
- *
- * This file includes the implementation of the route class.
- *
- * The route class registers information about a route. It is created
- * by using the get_routes() function.
- */
-
-// self
-//
-#include "libaddr/route.h"
-
-// C++ library
-//
-#include 
-#include 
-#include 
-
-// C library
-//
-#include 
-
-
-
-namespace addr
-{
-
-
-typedef std::vector words_t;
-
-
-/** \brief Details used by the route class implementation.
- *
- * The following handles the route class and gathering of the routes.
- */
-namespace
-{
-
-
-/** \brief Read one line of content from a file.
- *
- * This function reads one line of content up to the next '\n'.
- *
- * \param[in,out] in  The input stream.
- * \param[out] line  The line just read.
- *
- * \return 0 on success, -1 on error (generally EOF)
- */
-int readwords(std::ifstream & in, words_t & words)
-{
-    words.clear();
-    std::string w;
-    for(;;)
-    {
-        int const c(in.get());
-        if(c < 0)
-        {
-            return -1;
-        }
-        if(c == '\n')
-        {
-            return 0;
-        }
-        if(c == '\t' || c == ' ')
-        {
-            // there should always be a first word, however, there can
-            // be multiple '\t' or ' ' after one another
-            if(!w.empty())
-            {
-                words.push_back(w);
-                w.clear();
-            }
-        }
-        else
-        {
-            w += c;
-        }
-    }
-}
-
-
-int get_position(words_t const & headers, std::string const & column_name)
-{
-    auto it(std::find(
-          headers.cbegin()
-        , headers.cend()
-        , column_name));
-
-    if(it == headers.end())
-    {
-        return -1; // LCOV_EXCL_LINE
-    }
-
-    return it - headers.begin();
-}
-
-
-std::string const & get_value(words_t const & entries, int pos)
-{
-    static std::string const    not_found;
-
-    if(pos < static_cast(entries.size()))
-    {
-        return entries[pos];
-    }
-
-    return not_found; // LCOV_EXCL_LINE
-}
-
-
-int hex_to_number(char c)
-{
-    if(c >= '0' && c <= '9')
-    {
-        return c - '0';
-    }
-    if(c >= 'a' && c <= 'f')
-    {
-        return c - 'a' + 10; // LCOV_EXCL_LINE
-    }
-    if(c >= 'A' && c <= 'F')
-    {
-        return c - 'A' + 10;
-    }
-    throw std::runtime_error("invalid hexadecimal digit"); // LCOV_EXCL_LINE
-}
-
-
-addr hex_to_addr(std::string const & address)
-{
-    if(address.length() != 8)
-    {
-        throw std::runtime_error("invalid length for an hex address"); // LCOV_EXCL_LINE
-    }
-
-    struct sockaddr_in in = sockaddr_in();
-    in.sin_family = AF_INET;
-    in.sin_port = 0;
-    in.sin_addr.s_addr =
-                  (hex_to_number(address[7]) <<  0)
-                | (hex_to_number(address[6]) <<  4)
-                | (hex_to_number(address[5]) <<  8)
-                | (hex_to_number(address[4]) << 12)
-                | (hex_to_number(address[3]) << 16)
-                | (hex_to_number(address[2]) << 20)
-                | (hex_to_number(address[1]) << 24)
-                | (hex_to_number(address[0]) << 28)
-            ;
-
-    return addr(in);
-}
-
-
-struct flag_name_t
-{
-    uint32_t const  f_flag;
-    char const      f_name;
-};
-
-/** \brief Flags used by route tables.
- *
- * \note
- * The list of flags presented here includes IPv4 and IPv6 flags.
- *
- * \note
- * Some of the flags are not defined in Ubuntu 16.04.
- */
-flag_name_t const g_rtf_flag_names[] =
-{
-    { RTF_UP,        'U' },
-    { RTF_GATEWAY,   'G' },
-    { RTF_REJECT,    '!' },        // may not be defined
-    { RTF_HOST,      'H' },
-    { RTF_REINSTATE, 'R' },
-    { RTF_DYNAMIC,   'D' },
-    { RTF_MODIFIED,  'M' },
-    { RTF_DEFAULT,   'd' },
-    { RTF_ALLONLINK, 'a' },
-    { RTF_ADDRCONF,  'c' },
-    { RTF_NONEXTHOP, 'o' },
-    //{ RTF_EXPIRES,   'e' },
-    { RTF_CACHE,     'C' },
-    { RTF_FLOW,      'f' },
-    { RTF_POLICY,    'p' },
-    { RTF_LOCAL,     'l' },
-    { RTF_MTU,       'u' },
-    { RTF_WINDOW,    'w' },
-    { RTF_IRTT,      'i' },
-    //{ RTF_NOTCACHED, 'n' },
-};
-
-
-
-}
-// no name namespace
-
-
-
-/** \brief Read the list of routes.
- *
- * This function reads the list of routes using the /proc/net/routes
- * file. It returns a vector of easy to use route objects.
- *
- * The content of the route table is scanned using the column names
- * so it makes sure that it does not use the wrong column (i.e.
- * expect that the columns never change over time.)
- *
- * \note
- * If an error occurs, the reurned vector is empty and errno is
- * set to the error that happened. The ENODATA error is used
- * if some mandatory columns are missing and thus this function
- * cannot properly load the columns.
- *
- * \todo
- * Write the IPv6 function. It's similar only there are no headers
- * and (obviously?!) the IPs are IPv6 instead of IPv4.
- *
- * \return A vector of the routes found in the file.
- */
-route::vector_t route::get_ipv4_routes()
-{
-    // the 'route' tool uses '/proc/net/route' so we do that too here
-    //
-    route::vector_t routes;
-
-    std::ifstream in("/proc/net/route");
-
-    // the first line is a set of headers, we use that to make sure that
-    // we know what each column is
-    //
-    words_t headers;
-    int e(readwords(in, headers));
-    if(e < 0)
-    {
-        return routes; // LCOV_EXCL_LINE
-    }
-
-    // TODO: we may want to remove case although I don't think it will
-    //       change over time, it could be one more thing that could...
-
-    int const pos_iface      (get_position(headers, "Iface"));
-    int const pos_destination(get_position(headers, "Destination"));
-    int const pos_gateway    (get_position(headers, "Gateway"));
-    int const pos_flags      (get_position(headers, "Flags"));
-    int const pos_refcnt     (get_position(headers, "RefCnt"));
-    int const pos_use        (get_position(headers, "Use"));
-    int const pos_metric     (get_position(headers, "Metric"));
-    int const pos_mask       (get_position(headers, "Mask"));
-    int const pos_mtu        (get_position(headers, "MTU"));
-    int const pos_window     (get_position(headers, "Window"));
-    int const pos_irtt       (get_position(headers, "IRTT"));
-
-    if(pos_iface == -1
-    || pos_destination == -1
-    || pos_gateway == -1)
-    {
-        errno = ENODATA; // LCOV_EXCL_LINE
-        return routes;   // LCOV_EXCL_LINE
-    }
-
-    for(;;)
-    {
-        // read one entry
-        //
-        words_t entries;
-        e = readwords(in, entries);
-        if(e < 0)
-        {
-            break;
-        }
-
-        // convert each column to data in a 'route' object
-        //
-        route r;
-
-        r.f_interface_name      = get_value(entries, pos_iface);
-        r.f_destination_address = hex_to_addr(get_value(entries, pos_destination));
-        r.f_gateway_address     = hex_to_addr(get_value(entries, pos_gateway));
-        r.f_flags               = std::stol(get_value(entries, pos_flags));
-        r.f_reference_count     = std::stol(get_value(entries, pos_refcnt));
-        r.f_use                 = std::stol(get_value(entries, pos_use));
-        r.f_metric              = std::stol(get_value(entries, pos_metric));
-        r.f_mtu                 = std::stol(get_value(entries, pos_mtu));
-        r.f_window              = std::stol(get_value(entries, pos_window));
-        r.f_irtt                = std::stol(get_value(entries, pos_irtt));
-
-        // the mask is handled specially
-        //
-        std::string mask_str(get_value(entries, pos_mask));
-        if(!mask_str.empty())
-        {
-            addr mask = hex_to_addr(mask_str);
-            struct sockaddr_in ipv4 = sockaddr_in();
-            mask.get_ipv4(ipv4);
-            uint8_t m[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
-            m[12] = ipv4.sin_addr.s_addr >>  0;
-            m[13] = ipv4.sin_addr.s_addr >>  8;
-            m[14] = ipv4.sin_addr.s_addr >> 16;
-            m[15] = ipv4.sin_addr.s_addr >> 24;
-            r.f_destination_address.set_mask(m);
-        }
-
-        routes.push_back(pointer_t(new route(r)));
-    }
-
-    return routes;
-}
-
-
-std::string const & route::get_interface_name() const
-{
-    return f_interface_name;
-}
-
-
-addr const & route::get_destination_address() const
-{
-    return f_destination_address;
-}
-
-
-addr const & route::get_gateway_address() const
-{
-    return f_gateway_address;
-}
-
-
-int route::get_flags() const
-{
-    return f_flags;
-}
-
-
-std::string route::flags_to_string() const
-{
-    std::string result;
-
-    std::for_each(
-          g_rtf_flag_names
-        , g_rtf_flag_names + sizeof(g_rtf_flag_names) / sizeof(g_rtf_flag_names[0])
-        , [&result, this](auto const & fn)
-        {
-            if((f_flags & fn.f_flag) != 0)
-            {
-                result += fn.f_name;
-            }
-        });
-
-    return result;
-}
-
-
-int route::get_reference_count() const
-{
-    return f_reference_count;
-}
-
-
-int route::get_use() const
-{
-    return f_use;
-}
-
-
-int route::get_metric() const
-{
-    return f_metric;
-}
-
-
-int route::get_mtu() const
-{
-    return f_mtu;
-}
-
-
-int route::get_window() const
-{
-    return f_window;
-}
-
-
-int route::get_irtt() const
-{
-    return f_irtt;
-}
-
-
-route::pointer_t find_default_route(route::vector_t const & routes)
-{
-    auto it(std::find_if(
-          routes.cbegin()
-        , routes.cend()
-        , [](auto const & r)
-        {
-            return r->get_destination_address().is_default();
-        }));
-
-    if(it == routes.cend())
-    {
-        return nullptr;
-    }
-
-    return *it;
-}
-
-
-
-}
-// addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/validate_ip.cpp libaddr-1.0.18.0~xenial/src/validate_ip.cpp
--- libaddr-1.0.17.0~xenial/src/validate_ip.cpp	2019-03-17 06:58:36.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/validate_ip.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,43 +0,0 @@
-// Network Address -- get addresses on the command line and validate them
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief A tool to check the validity of an IP address.
- *
- * This tool can be used to check that an IP address or a list of
- * IP addresses are all valid.
- */
-
-#include "libaddr/addr.h"
-
-#include 
-
-int main(int argc, char * argv[])
-{
-    std::cerr << "error: " << argc << " not implemented " << argv << "...\n";
-    return 0;
-}
-
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/src/version.cpp libaddr-1.0.18.0~xenial/src/version.cpp
--- libaddr-1.0.17.0~xenial/src/version.cpp	2019-03-17 06:58:37.000000000 +0000
+++ libaddr-1.0.18.0~xenial/src/version.cpp	1970-01-01 00:00:00.000000000 +0000
@@ -1,115 +0,0 @@
-// Network Address -- classes functions to ease handling IP addresses
-// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
-//
-// https://snapwebsites.org/project/libaddr
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-/** \file
- * \brief Implementation of the few global functions used to define the version.
- *
- * This file includes the few functions one can use to dynamically check
- * the version of the libaddr library. If you compiled with a different
- * version, then you very certainly could have an incompatible version
- * of the interface (i.e. C++ classes cannot always work right when
- * created through modified versions of such.)
- */
-
-// self
-//
-#include "libaddr/addr.h"
-#include "libaddr/version.h"
-
-
-namespace addr
-{
-
-/** \brief Major version of the libaddr library.
- *
- * This function returns the major version of the library at the
- * time it was compiled.
- *
- * The C++ classes are very likely different when compiled against
- * a different major version of the library. This means it is likely
- * to crash if used.
- *
- * \return The major version of the libaddr library.
- */
-int get_version_major()
-{
-    return LIBADDR_VERSION_MAJOR;
-}
-
-
-/** \brief Minor version of the libaddr library.
- *
- * This function returns the minor version of the library at the
- * time it was compiled.
- *
- * The C++ classes are likely different when compiled against
- * a different minor version of the library. This means it is likely
- * to crash if used.
- *
- * \return The minor version of the libaddr library.
- */
-int get_version_minor()
-{
-    return LIBADDR_VERSION_MINOR;
-}
-
-
-/** \brief Patch version of the libaddr library.
- *
- * This function returns the patch version of the library at the
- * time it was compiled.
- *
- * The C++ classes should not have changed in such a way that it
- * will crash your application when compiled against a version
- * that has a different patching version.
- *
- * \return The patch version of the libaddr library.
- */
-int get_version_patch()
-{
-    return LIBADDR_VERSION_PATCH;
-}
-
-
-/** \brief The full version of the libaddr library as a string.
- *
- * This function returns a string with the major, minor, and
- * path versions of the library. The build number is not included.
- *
- * If you want to compare the version, we suggest that you use
- * the other functions: get_version_major(), get_version_minor(),
- * and get_version_patch(). This function should be used for
- * display only.
- *
- * \return The full version of the libaddr library as a string.
- */
-char const * get_version_string()
-{
-    return LIBADDR_VERSION_STRING;
-}
-
-}
-// snap_addr namespace
-// vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/tests/CMakeLists.txt libaddr-1.0.18.0~xenial/tests/CMakeLists.txt
--- libaddr-1.0.17.0~xenial/tests/CMakeLists.txt	2019-07-21 04:00:24.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/CMakeLists.txt	2019-09-02 20:07:49.000000000 +0000
@@ -1,8 +1,4 @@
-#
-# File:         tests/CMakeLists.txt
-# Object:       Definitions to create the build environment with cmake
-#
-# Copyright:    Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+# Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
 #
 # https://snapwebsites.org/project/libaddr
 # contact@m2osw.com
@@ -24,12 +20,12 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
-#
+
 
 ##
 ## libaddr tests
 ##
-project(addr_test)
+project(unittest)
 
 find_package(Catch)
 if(CATCH_FOUND)
@@ -40,8 +36,6 @@
 
     # The reference to the src folder is required by the internal test
     include_directories(
-        ${addr_library_BINARY_DIR}/src
-        ${addr_library_SOURCE_DIR}/src
         ${CATCH_INCLUDE_DIR}
         ${LIBEXCEPT_INCLUDE_DIRS}
     )
diff -Nru libaddr-1.0.17.0~xenial/tests/test_addr_global.cpp libaddr-1.0.18.0~xenial/tests/test_addr_global.cpp
--- libaddr-1.0.17.0~xenial/tests/test_addr_global.cpp	2019-07-21 04:04:02.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/test_addr_global.cpp	2019-09-02 20:08:27.000000000 +0000
@@ -1,33 +1,32 @@
-/* test_addr_global.cpp
- * Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
- *
- * Project: https://snapwebsites.org/project/libaddr
- *
- * Permission is hereby granted, free of charge, to any
- * person obtaining a copy of this software and
- * associated documentation files (the "Software"), to
- * deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice
- * shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// Project: https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and
+// associated documentation files (the "Software"), to
+// deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
 
 /** \file
  * \brief Check the global functions.
@@ -43,12 +42,19 @@
 
 // self
 //
-#include "test_addr_main.h"
+#include    "test_addr_main.h"
+
 
 // addr lib
 //
-#include "libaddr/addr.h"
-#include "libaddr/version.h"
+#include    
+#include    
+
+
+// last include
+//
+#include    
+
 
 
 
diff -Nru libaddr-1.0.17.0~xenial/tests/test_addr_interfaces.cpp libaddr-1.0.18.0~xenial/tests/test_addr_interfaces.cpp
--- libaddr-1.0.17.0~xenial/tests/test_addr_interfaces.cpp	2019-07-21 04:14:01.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/test_addr_interfaces.cpp	2019-09-02 20:08:34.000000000 +0000
@@ -1,33 +1,32 @@
-/* test_addr_interfaces.cpp
- * Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
- *
- * Project: https://snapwebsites.org/project/libaddr
- *
- * Permission is hereby granted, free of charge, to any
- * person obtaining a copy of this software and
- * associated documentation files (the "Software"), to
- * deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice
- * shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// Project: https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and
+// associated documentation files (the "Software"), to
+// deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
 
 /** \file
  * \brief Verify the interfaces function.
@@ -39,15 +38,23 @@
 
 // self
 //
-#include "test_addr_main.h"
+#include    "test_addr_main.h"
+
 
 // addr lib
 //
-#include "libaddr/iface.h"
+#include    
+
 
 // C lib
 //
-#include 
+#include    
+
+
+// last include
+//
+#include    
+
 
 
 CATCH_TEST_CASE( "ipv4::interfaces", "[ipv4]" )
diff -Nru libaddr-1.0.17.0~xenial/tests/test_addr_ipv4.cpp libaddr-1.0.18.0~xenial/tests/test_addr_ipv4.cpp
--- libaddr-1.0.17.0~xenial/tests/test_addr_ipv4.cpp	2019-07-21 04:28:01.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/test_addr_ipv4.cpp	2019-09-02 20:08:19.000000000 +0000
@@ -1,33 +1,32 @@
-/* test_addr_ipv4.cpp
- * Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
- *
- * Project: https://snapwebsites.org/project/libaddr
- *
- * Permission is hereby granted, free of charge, to any
- * person obtaining a copy of this software and
- * associated documentation files (the "Software"), to
- * deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice
- * shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// Project: https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and
+// associated documentation files (the "Software"), to
+// deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
 
 /** \file
  * \brief Test the IPv6 interface.
@@ -43,11 +42,18 @@
 
 // self
 //
-#include "test_addr_main.h"
+#include    "test_addr_main.h"
+
 
 // addr lib
 //
-#include "libaddr/iface.h"
+#include    
+
+
+// last include
+//
+#include    
+
 
 
 
@@ -98,8 +104,8 @@
                 while(in.sin_family == AF_INET);
                 in.sin_port = htons(rand());
                 in.sin_addr.s_addr = htonl(rand() ^ (rand() << 16));
-                CATCH_REQUIRE_THROWS_AS(a.set_ipv4(in), addr::addr_invalid_argument_exception);
-                CATCH_REQUIRE_THROWS_AS(addr::addr(in), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(a.set_ipv4(in), addr::addr_invalid_argument);
+                CATCH_REQUIRE_THROWS_AS(addr::addr(in), addr::addr_invalid_argument);
             }
         }
     }
@@ -129,9 +135,9 @@
                 }
                 addr::addr_parser::flag_t const flag(static_cast(n));
 
-                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, true), addr::addr_invalid_argument_exception);
-                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, false), addr::addr_invalid_argument_exception);
-                CATCH_REQUIRE_THROWS_AS(a.get_allow(flag), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, true), addr::addr_invalid_argument);
+                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, false), addr::addr_invalid_argument);
+                CATCH_REQUIRE_THROWS_AS(a.get_allow(flag), addr::addr_invalid_argument);
             }
         }
 
@@ -153,9 +159,9 @@
                 while(n < static_cast(addr::addr_parser::flag_t::FLAG_max));
                 addr::addr_parser::flag_t const flag(static_cast(n));
 
-                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, true), addr::addr_invalid_argument_exception);
-                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, false), addr::addr_invalid_argument_exception);
-                CATCH_REQUIRE_THROWS_AS(a.get_allow(flag), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, true), addr::addr_invalid_argument);
+                CATCH_REQUIRE_THROWS_AS(a.set_allow(flag, false), addr::addr_invalid_argument);
+                CATCH_REQUIRE_THROWS_AS(a.get_allow(flag), addr::addr_invalid_argument);
             }
         }
     }
@@ -260,7 +266,7 @@
                     port = rand() ^ (rand() << 16);
                 }
                 while(port >= -1 && port <= 65535); // -1 is valid here, it represents "no default port defined"
-                CATCH_REQUIRE_THROWS_AS(p.set_default_port(port), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(p.set_default_port(port), addr::addr_invalid_argument);
 
                 // verify port unchanged
                 //
@@ -362,13 +368,13 @@
             // not changing default protocol
             //
             CATCH_REQUIRE(p.get_protocol() == -1);
-            CATCH_REQUIRE_THROWS_AS(p.set_protocol("igmp"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_protocol("igmp"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_protocol() == -1);
 
             // change protocol to another valid value first
             //
             p.set_protocol("tcp");
-            CATCH_REQUIRE_THROWS_AS(p.set_protocol("icmp"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_protocol("icmp"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_protocol() == IPPROTO_TCP);
         }
 
@@ -387,13 +393,13 @@
 
                 addr::addr_parser p;
 
-                CATCH_REQUIRE_THROWS_AS(p.set_protocol(protocol), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(p.set_protocol(protocol), addr::addr_invalid_argument);
                 CATCH_REQUIRE(p.get_protocol() == -1);
 
                 // change protocol to another valid value first
                 //
                 p.set_protocol("tcp");
-                CATCH_REQUIRE_THROWS_AS(p.set_protocol(protocol), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(p.set_protocol(protocol), addr::addr_invalid_argument);
                 CATCH_REQUIRE(p.get_protocol() == IPPROTO_TCP);
             }
         }
@@ -412,13 +418,13 @@
             CATCH_REQUIRE_FALSE(a.is_ipv4());
 
             struct sockaddr_in in;
-            CATCH_REQUIRE_THROWS_AS(a.get_ipv4(in), addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_ONLY),          addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_BRACKETS),      addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_PORT),          addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_MASK),          addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_BRACKETS_MASK), addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_ALL),           addr::addr_invalid_state_exception);
+            CATCH_REQUIRE_THROWS_AS(a.get_ipv4(in), addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_ONLY),          addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_BRACKETS),      addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_PORT),          addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_MASK),          addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_BRACKETS_MASK), addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(a.to_ipv4_string(addr::addr::string_ip_t::STRING_IP_ALL),           addr::addr_invalid_state);
         }
 
         CATCH_SECTION("default network type (0.0.0.0)")
@@ -1585,7 +1591,7 @@
                 while(invalid_protocol == IPPROTO_IP
                    || invalid_protocol == IPPROTO_TCP
                    || invalid_protocol == IPPROTO_UDP);
-                CATCH_REQUIRE_THROWS_AS(a.set_protocol(invalid_protocol), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(a.set_protocol(invalid_protocol), addr::addr_invalid_argument);
 
                 // make sure the protocol does not change on errors
                 CATCH_REQUIRE(a.get_protocol() == start_protocol);
@@ -1593,13 +1599,13 @@
 
             // null string is not allowed
             //
-            CATCH_REQUIRE_THROWS_AS(a.set_protocol(nullptr), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(a.set_protocol(nullptr), addr::addr_invalid_argument);
 
             // other "invalid" (unsupported, really) string protocols
             //
-            CATCH_REQUIRE_THROWS_AS(a.set_protocol("icmp"), addr::addr_invalid_argument_exception);
-            CATCH_REQUIRE_THROWS_AS(a.set_protocol("raw"), addr::addr_invalid_argument_exception);
-            CATCH_REQUIRE_THROWS_AS(a.set_protocol("hmp"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(a.set_protocol("icmp"), addr::addr_invalid_argument);
+            CATCH_REQUIRE_THROWS_AS(a.set_protocol("raw"), addr::addr_invalid_argument);
+            CATCH_REQUIRE_THROWS_AS(a.set_protocol("hmp"), addr::addr_invalid_argument);
 
             // test all valid protocols (numeric)
             //
@@ -1893,8 +1899,8 @@
         CATCH_SECTION("invalid socket")
         {
             addr::addr a;
-            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(-1, true),  addr::addr_invalid_argument_exception);
-            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(-1, false), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(-1, true),  addr::addr_invalid_argument);
+            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(-1, false), addr::addr_invalid_argument);
         }
 
         CATCH_SECTION("non-opened file descriptor")
@@ -1904,8 +1910,8 @@
             // unless we have a bug, there should not be any file descriptor
             // currently open with an ID of 1,000
             //
-            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(1000, true),  addr::addr_io_exception);
-            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(1000, false), addr::addr_io_exception);
+            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(1000, true),  addr::addr_io_error);
+            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(1000, false), addr::addr_io_error);
         }
 
         CATCH_SECTION("unknown socket type")
@@ -1919,8 +1925,8 @@
             // unless we have a bug, there should not be any file descriptor
             // currently open with an ID of 1,000
             //
-            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(s, true),  addr::addr_io_exception);
-            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(s, false), addr::addr_invalid_state_exception);
+            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(s, true),  addr::addr_io_error);
+            CATCH_REQUIRE_THROWS_AS(a.set_from_socket(s, false), addr::addr_invalid_state);
         }
 
         CATCH_SECTION("create a server, but do not test it (yet)...")
@@ -2003,7 +2009,7 @@
             // get socket info from the other side (peer == true)
             //
             addr::addr b;
-            CATCH_REQUIRE_THROWS_AS(b.set_from_socket(s, true), addr::addr_io_exception);
+            CATCH_REQUIRE_THROWS_AS(b.set_from_socket(s, true), addr::addr_io_error);
             CATCH_REQUIRE_FALSE(b.is_ipv4());
             CATCH_REQUIRE(b.to_ipv6_string(addr::addr::string_ip_t::STRING_IP_ONLY)    == "::");
             CATCH_REQUIRE(b.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY) == "::");
@@ -2471,7 +2477,7 @@
         CATCH_SECTION("addr with port and invalid protocol so we get an exception")
         {
             CATCH_REQUIRE_THROWS_AS(addr::string_to_addr("169.60.33.0:9322/24", std::string(), -1, "icmp", true),
-                                                                        addr::addr_invalid_argument_exception);
+                                                                        addr::addr_invalid_argument);
         }
     }
     // TODO: add ipv6 tests, although at this point it's not too
diff -Nru libaddr-1.0.17.0~xenial/tests/test_addr_ipv6.cpp libaddr-1.0.18.0~xenial/tests/test_addr_ipv6.cpp
--- libaddr-1.0.17.0~xenial/tests/test_addr_ipv6.cpp	2019-07-21 04:43:51.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/test_addr_ipv6.cpp	2019-09-02 20:08:57.000000000 +0000
@@ -1,33 +1,32 @@
-/* test_addr_ipv6.cpp
- * Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
- *
- * Project: https://snapwebsites.org/project/libaddr
- *
- * Permission is hereby granted, free of charge, to any
- * person obtaining a copy of this software and
- * associated documentation files (the "Software"), to
- * deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice
- * shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// Project: https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and
+// associated documentation files (the "Software"), to
+// deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
 
 /** \file
  * \brief Test the IPv6 interface.
@@ -43,11 +42,17 @@
 
 // self
 //
-#include "test_addr_main.h"
+#include    "test_addr_main.h"
+
 
 // addr lib
 //
-#include "libaddr/iface.h"
+#include    
+
+
+// last include
+//
+#include    
 
 
 
@@ -100,8 +105,8 @@
             {
                 in6.sin6_addr.s6_addr16[idx] = rand();
             }
-            CATCH_REQUIRE_THROWS_AS(a.set_ipv6(in6), addr::addr_invalid_argument_exception);
-            CATCH_REQUIRE_THROWS_AS(addr::addr(in6), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(a.set_ipv6(in6), addr::addr_invalid_argument);
+            CATCH_REQUIRE_THROWS_AS(addr::addr(in6), addr::addr_invalid_argument);
         }
     }
 
@@ -351,7 +356,7 @@
         {
             addr::addr_parser p;
 
-            CATCH_REQUIRE_THROWS_AS(p.set_default_address("[4:5:4:5:7:8:7:8"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_default_address("[4:5:4:5:7:8:7:8"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_default_address4() == "");
             CATCH_REQUIRE(p.get_default_address6() == "");
 
@@ -359,7 +364,7 @@
             CATCH_REQUIRE(p.get_default_address4() == "");
             CATCH_REQUIRE(p.get_default_address6() == "1:7:1:7:1:7:1:7");
 
-            CATCH_REQUIRE_THROWS_AS(p.set_default_address("[9:5:9:5:9:8:9:8"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_default_address("[9:5:9:5:9:8:9:8"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_default_address4() == "");
             CATCH_REQUIRE(p.get_default_address6() == "1:7:1:7:1:7:1:7");
 
@@ -367,7 +372,7 @@
             CATCH_REQUIRE(p.get_default_address4() == "12.55.1.9");
             CATCH_REQUIRE(p.get_default_address6() == "1:7:1:7:1:7:1:7");
 
-            CATCH_REQUIRE_THROWS_AS(p.set_default_address("[9:f00f:9:e00e:9:d00d:9:c00c"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_default_address("[9:f00f:9:e00e:9:d00d:9:c00c"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_default_address4() == "12.55.1.9");
             CATCH_REQUIRE(p.get_default_address6() == "1:7:1:7:1:7:1:7");
 
@@ -380,7 +385,7 @@
         {
             addr::addr_parser p;
 
-            CATCH_REQUIRE_THROWS_AS(p.set_default_mask("[4:5:4:5:7:8:7:8"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_default_mask("[4:5:4:5:7:8:7:8"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_default_mask4() == "");
             CATCH_REQUIRE(p.get_default_mask6() == "");
 
@@ -388,7 +393,7 @@
             CATCH_REQUIRE(p.get_default_mask4() == "");
             CATCH_REQUIRE(p.get_default_mask6() == "1:7:1:7:1:7:1:7");
 
-            CATCH_REQUIRE_THROWS_AS(p.set_default_mask("[9:5:9:5:9:8:9:8"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_default_mask("[9:5:9:5:9:8:9:8"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_default_mask4() == "");
             CATCH_REQUIRE(p.get_default_mask6() == "1:7:1:7:1:7:1:7");
 
@@ -396,7 +401,7 @@
             CATCH_REQUIRE(p.get_default_mask4() == "12.55.1.9");
             CATCH_REQUIRE(p.get_default_mask6() == "1:7:1:7:1:7:1:7");
 
-            CATCH_REQUIRE_THROWS_AS(p.set_default_mask("[9:f00f:9:e00e:9:d00d:9:c00c"), addr::addr_invalid_argument_exception);
+            CATCH_REQUIRE_THROWS_AS(p.set_default_mask("[9:f00f:9:e00e:9:d00d:9:c00c"), addr::addr_invalid_argument);
             CATCH_REQUIRE(p.get_default_mask4() == "12.55.1.9");
             CATCH_REQUIRE(p.get_default_mask6() == "1:7:1:7:1:7:1:7");
 
@@ -670,11 +675,11 @@
                     port_too_small = -(rand() & 0xFFFF);
                 }
                 while(port_too_small == 0);
-                CATCH_REQUIRE_THROWS_AS(a.set_port(port_too_small), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(a.set_port(port_too_small), addr::addr_invalid_argument);
 
                 // first try a negative port
                 int const port_too_large = (rand() & 0xFFFF) + 65536;
-                CATCH_REQUIRE_THROWS_AS(a.set_port(port_too_large), addr::addr_invalid_argument_exception);
+                CATCH_REQUIRE_THROWS_AS(a.set_port(port_too_large), addr::addr_invalid_argument);
 
                 // make sure port does not get modified on errors
                 CATCH_REQUIRE(a.get_port() == start_port);
@@ -1553,7 +1558,7 @@
             // get socket info from the other side (peer == true)
             //
             addr::addr b;
-            CATCH_REQUIRE_THROWS_AS(b.set_from_socket(s, true), addr::addr_io_exception);
+            CATCH_REQUIRE_THROWS_AS(b.set_from_socket(s, true), addr::addr_io_error);
             CATCH_REQUIRE_FALSE(b.is_ipv4());
             CATCH_REQUIRE(b.to_ipv6_string(addr::addr::string_ip_t::STRING_IP_ONLY)    == "::");
             CATCH_REQUIRE(b.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY) == "::");
diff -Nru libaddr-1.0.17.0~xenial/tests/test_addr_main.cpp libaddr-1.0.18.0~xenial/tests/test_addr_main.cpp
--- libaddr-1.0.17.0~xenial/tests/test_addr_main.cpp	2019-07-21 03:49:31.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/test_addr_main.cpp	2019-09-02 20:03:40.000000000 +0000
@@ -1,33 +1,32 @@
-/* test_addr_main.cpp
- * Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
- *
- * Project: https://snapwebsites.org/project/libaddr
- *
- * Permission is hereby granted, free of charge, to any
- * person obtaining a copy of this software and
- * associated documentation files (the "Software"), to
- * deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice
- * shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// Project: https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and
+// associated documentation files (the "Software"), to
+// deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
 
 /** \file
  * \brief The main() function of the addr library tests.
@@ -44,8 +43,12 @@
 
 // self
 //
-#include "test_addr_main.h"
+#include    "test_addr_main.h"
+
 
+// last include
+//
+#include    
 
 
 
diff -Nru libaddr-1.0.17.0~xenial/tests/test_addr_main.h libaddr-1.0.18.0~xenial/tests/test_addr_main.h
--- libaddr-1.0.17.0~xenial/tests/test_addr_main.h	2019-07-21 03:58:58.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/test_addr_main.h	2019-09-02 20:04:35.000000000 +0000
@@ -1,35 +1,34 @@
-/* test_addr_main.h
- * Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
- *
- * Project: https://snapwebsites.org/project/libaddr
- *
- * Permission is hereby granted, free of charge, to any
- * person obtaining a copy of this software and
- * associated documentation files (the "Software"), to
- * deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice
- * shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// Project: https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and
+// associated documentation files (the "Software"), to
+// deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
 #pragma once
 
+
 /** \file
  * \brief Basic definitions for all of our libaddr unit tests.
  *
@@ -37,30 +36,38 @@
  * by all our unit tests.
  */
 
+
 // libaddr library
 //
-#include "libaddr/addr_parser.h"
-#include "libaddr/addr_exceptions.h"
-#include "libaddr/version.h"
+#include    
+#include    
+#include    
+
 
 // catch
 //
-#include 
+#include    
+
 
 // C++ library
 //
-#include 
-#include 
-#include 
-#include 
-#include 
+#include    
+#include    
+#include    
+#include    
+#include    
+
 
 // C library
-#include 
-#include 
-#include 
+//
+#include    
+#include    
+#include    
 
 
+// last include
+//
+#include    
 
 
 
diff -Nru libaddr-1.0.17.0~xenial/tests/test_addr_range.cpp libaddr-1.0.18.0~xenial/tests/test_addr_range.cpp
--- libaddr-1.0.17.0~xenial/tests/test_addr_range.cpp	2019-07-21 04:45:50.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tests/test_addr_range.cpp	2019-09-02 20:19:06.000000000 +0000
@@ -1,33 +1,32 @@
-/* test_addr_range.cpp
- * Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
- *
- * Project: https://snapwebsites.org/project/libaddr
- *
- * Permission is hereby granted, free of charge, to any
- * person obtaining a copy of this software and
- * associated documentation files (the "Software"), to
- * deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice
- * shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
- * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
+// Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// Project: https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and
+// associated documentation files (the "Software"), to
+// deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom
+// the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
 
 /** \file
  * \brief Check the addr_range class basics.
@@ -38,7 +37,16 @@
  * addr_parser class.
  */
 
-#include "test_addr_main.h"
+
+// self
+//
+#include    "test_addr_main.h"
+
+
+// last include
+//
+#include    
+
 
 
 
@@ -84,8 +92,8 @@
             CATCH_REQUIRE(r.get_to() == a);
 
             addr::addr other;
-            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(r.is_in(other), addr::addr_invalid_state_exception);
+            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(r.is_in(other), addr::addr_invalid_state);
         }
 
         CATCH_SECTION("test normal range (from <= to)")
@@ -128,8 +136,8 @@
             auto const & r1(range);
             CATCH_REQUIRE(r1.get_from() == a);
             CATCH_REQUIRE(r1.get_to() == a);
-            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(r1.is_in(other), addr::addr_invalid_state_exception);
+            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(r1.is_in(other), addr::addr_invalid_state);
             CATCH_REQUIRE(range.match(a));
             CATCH_REQUIRE_FALSE(range.match(f));
             CATCH_REQUIRE_FALSE(range.match(t));
@@ -147,8 +155,8 @@
             auto const & r2(range);
             CATCH_REQUIRE(r2.get_from() == f);
             CATCH_REQUIRE(r2.get_to() == a);
-            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(r2.is_in(other), addr::addr_invalid_state_exception);
+            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(r2.is_in(other), addr::addr_invalid_state);
             CATCH_REQUIRE_FALSE(range.match(a));
             CATCH_REQUIRE(range.match(f));
             CATCH_REQUIRE_FALSE(range.match(t));
@@ -293,8 +301,8 @@
             auto const & r1(range);
             CATCH_REQUIRE(r1.get_from() == a);
             CATCH_REQUIRE(r1.get_to() == a);
-            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(r1.is_in(other), addr::addr_invalid_state_exception);
+            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(r1.is_in(other), addr::addr_invalid_state);
             CATCH_REQUIRE(range.match(a));
             CATCH_REQUIRE_FALSE(range.match(f));
             CATCH_REQUIRE_FALSE(range.match(t));
@@ -312,8 +320,8 @@
             auto const & r2(range);
             CATCH_REQUIRE(r2.get_from() == f);
             CATCH_REQUIRE(r2.get_to() == a);
-            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state_exception);
-            CATCH_REQUIRE_THROWS_AS(r2.is_in(other), addr::addr_invalid_state_exception);
+            CATCH_REQUIRE_THROWS_AS(range.is_in(other), addr::addr_invalid_state);
+            CATCH_REQUIRE_THROWS_AS(r2.is_in(other), addr::addr_invalid_state);
             CATCH_REQUIRE_FALSE(range.match(a));
             CATCH_REQUIRE(range.match(f));
             CATCH_REQUIRE_FALSE(range.match(t));
diff -Nru libaddr-1.0.17.0~xenial/tools/CMakeLists.txt libaddr-1.0.18.0~xenial/tools/CMakeLists.txt
--- libaddr-1.0.17.0~xenial/tools/CMakeLists.txt	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tools/CMakeLists.txt	2019-09-02 22:48:02.000000000 +0000
@@ -0,0 +1,79 @@
+# Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
+#
+# https://snapwebsites.org/project/libaddr
+# contact@m2osw.com
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+##
+## Addr command line tool
+##
+project(validate_ip)
+
+include_directories(
+    ${LIBEXCEPT_INCLUDE_DIRS}
+)
+
+add_executable(${PROJECT_NAME}
+    validate_ip.cpp
+)
+
+target_link_libraries(${PROJECT_NAME}
+    addr
+    ${ADVGETOPT_LIBRARIES}
+    ${LIBUTF8_LIBRARIES}
+)
+
+install(
+    TARGETS
+        ${PROJECT_NAME}
+
+    RUNTIME DESTINATION
+        bin
+)
+
+
+##
+## Route command line tool
+##
+project(ipv4_routes)
+
+include_directories(
+    ${LIBEXCEPT_INCLUDE_DIRS}
+)
+
+add_executable(${PROJECT_NAME}
+    ipv4_routes.cpp
+)
+
+target_link_libraries(${PROJECT_NAME}
+    addr
+)
+
+install(
+    TARGETS
+        ${PROJECT_NAME}
+
+    RUNTIME DESTINATION
+        bin
+)
+
+# vim: ts=4 sw=4 et
diff -Nru libaddr-1.0.17.0~xenial/tools/ipv4_routes.cpp libaddr-1.0.18.0~xenial/tools/ipv4_routes.cpp
--- libaddr-1.0.17.0~xenial/tools/ipv4_routes.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tools/ipv4_routes.cpp	2019-09-02 19:22:02.000000000 +0000
@@ -0,0 +1,144 @@
+// Network Address -- get routes and print them, similar to system `route`
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+/** \file
+ * \brief A tool to check the system routes.
+ *
+ * This tool is used to verify that our route class works as expected.
+ */
+
+// libaddr library
+//
+#include    "libaddr/route.h"
+
+
+// C++ library
+//
+#include    
+#include    
+#include    
+
+
+// last include
+//
+#include    
+
+
+namespace
+{
+
+bool g_show_default = false;
+bool g_hide_headers = false;
+
+}
+
+int main(int argc, char * argv[])
+{
+    addr::route::vector_t routes(addr::route::get_ipv4_routes());
+
+    for(int idx(1); idx < argc; ++idx)
+    {
+        if(strcmp(argv[idx], "-h") == 0
+        || strcmp(argv[idx], "--help") == 0)
+        {
+            std::cout << "Usage: %s [-opts]" << std::endl;
+            std::cout << "where -opts is one or more of:" << std::endl;
+            std::cout << "  --help | -h        print out this help screen." << std::endl;
+            std::cout << "  --default | -d     only print the default route." << std::endl;
+            std::cout << "  --hide-headers     do not print the headers." << std::endl;
+            exit(1);
+        }
+        else if(strcmp(argv[idx], "-d") == 0
+             || strcmp(argv[idx], "--default") == 0)
+        {
+            g_show_default = true;
+        }
+        else if(strcmp(argv[idx], "--hide-headers") == 0)
+        {
+            g_hide_headers = true;
+        }
+        else
+        {
+            std::cerr << "error: unknown command line option \"" << argv[idx] << "\". Try --help for additional info." << std::endl;
+            exit(1);
+        }
+    }
+
+    if(routes.empty())
+    {
+        std::cerr << "error: no routes found, is your network up?" << std::endl;
+        return 1;
+    }
+
+    // headers
+    //
+    if(!g_hide_headers)
+    {
+        std::cout << "Iface   "
+                     "Destination     "
+                     "Gateway         "
+                     "Flags   "
+                     "RefCnt  "
+                     "Use     "
+                     "Metric  "
+                     "Mask            "
+                     "MTU     "
+                     "Window  "
+                     "IRTT    "
+                  << std::endl;
+    }
+
+    for(auto r : routes)
+    {
+        if(!g_show_default
+        || r->get_destination_address().is_default())
+        {
+            uint8_t mask[16];
+            r->get_destination_address().get_mask(mask);
+            std::stringstream m;
+            m << static_cast(mask[12]) << "."
+              << static_cast(mask[13]) << "."
+              << static_cast(mask[14]) << "."
+              << static_cast(mask[15]);
+            std::cout << std::left << std::setw( 8) << r->get_interface_name()
+                      << std::left << std::setw(16) << r->get_destination_address().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY)
+                      << std::left << std::setw(16) << r->get_gateway_address().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY)
+                      << std::left << std::setw( 8) << r->flags_to_string()
+                      << std::left << std::setw( 8) << r->get_reference_count()
+                      << std::left << std::setw( 8) << r->get_use()
+                      << std::left << std::setw( 8) << r->get_metric()
+                      << std::left << std::setw(16) << m.str()
+                      << std::left << std::setw( 8) << r->get_mtu()
+                      << std::left << std::setw( 8) << r->get_window()
+                      << std::left << std::setw( 8) << r->get_irtt()
+                      << std::endl;
+        }
+    }
+
+    return 0;
+}
+
+// vim: ts=4 sw=4 et
+
diff -Nru libaddr-1.0.17.0~xenial/tools/validate_ip.cpp libaddr-1.0.18.0~xenial/tools/validate_ip.cpp
--- libaddr-1.0.17.0~xenial/tools/validate_ip.cpp	1970-01-01 00:00:00.000000000 +0000
+++ libaddr-1.0.18.0~xenial/tools/validate_ip.cpp	2019-09-02 21:59:04.000000000 +0000
@@ -0,0 +1,395 @@
+// Network Address -- get addresses on the command line and validate them
+// Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
+//
+// https://snapwebsites.org/project/libaddr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+/** \file
+ * \brief A tool to check the validity of an IP address.
+ *
+ * This tool is used to verify or canonicalize an IP address or a list of
+ * IP addresses.
+ *
+ * The default is to verify (`--verify`). In this case, the tool generates
+ * no output. It exits with 0 when no error were found. It prints out
+ * errors to stderr and exit with 1 when errors there are.
+ *
+ * The `--canonicalize` command line option requires the tool to verify
+ * each address and then print them, canonicalized, to stdout. At this
+ * time, the CIDR are printed in full (i.e. `255.255.255.255`).
+ *
+ * The tool supports a few options as follow:
+ *
+ * \li `--address` -- require that an address be specified otherwise we
+ * generate an error.
+ * \li `--port` -- require that a port be specified otherwise we generate
+ * an error.
+ * \li `--list` -- allow list of IP address (command & space separated).
+ * \li `--mask` -- allow a mask.
+ * \li `--protocol \` -- specify the protocol (default "tcp").
+ * \li `--default-address` -- define the default address, used when not
+ * specified in an input string (i.e. `:123`).
+ * \li `--default-port` -- define the default port, used when not
+ * specified in an input string (i.e. `[::]`).
+ *
+ * To avoid receiving the error messages in stderr, use the `--quiet` flag.
+ */
+
+
+// libaddr lib
+//
+#include    "libaddr/addr.h"
+#include    "libaddr/addr_exception.h"
+#include    "libaddr/addr_parser.h"
+#include    "libaddr/version.h"
+
+
+// advgetopt lib
+//
+#include    
+#include    
+#include    
+#include    
+#include    
+
+
+// snapdev lib
+//
+#include    
+
+
+// boost lib
+//
+#include    
+#include    
+
+
+// C++ lib
+//
+#include    
+
+
+// last include
+//
+#include    
+
+
+
+namespace
+{
+
+
+advgetopt::option const g_options[] =
+{
+    // COMMANDS
+    //
+    advgetopt::define_option(
+          advgetopt::Name("canonicalize")
+        , advgetopt::ShortName('c')
+        , advgetopt::Flags(advgetopt::standalone_command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_COMMANDS>())
+        , advgetopt::Help("canonicalize the addresses and print the result in stdout.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("verify")
+        , advgetopt::Flags(advgetopt::standalone_command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_COMMANDS>())
+        , advgetopt::Help("verify the specify addresses.")
+    ),
+
+    // OPTIONS
+    //
+    advgetopt::define_option(
+          advgetopt::Name("address")
+        , advgetopt::ShortName('a')
+        , advgetopt::Flags(advgetopt::standalone_command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
+        , advgetopt::Help("address is required.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("default-address")
+        , advgetopt::Flags(advgetopt::command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS
+                    , advgetopt::GETOPT_FLAG_REQUIRED>())
+        , advgetopt::DefaultValue("127.0.0.1,::")
+        , advgetopt::Help("default IPv4 and IPv6 addresses.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("default-port")
+        , advgetopt::Flags(advgetopt::command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS
+                    , advgetopt::GETOPT_FLAG_REQUIRED>())
+        , advgetopt::DefaultValue("60000")
+        , advgetopt::Help("default port.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("list")
+        , advgetopt::ShortName('l')
+        , advgetopt::Flags(advgetopt::standalone_command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
+        , advgetopt::Help("specify protocol.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("mask")
+        , advgetopt::ShortName('m')
+        , advgetopt::Flags(advgetopt::standalone_command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
+        , advgetopt::Help("mask is allowed.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("port")
+        , advgetopt::ShortName('p')
+        , advgetopt::Flags(advgetopt::standalone_command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
+        , advgetopt::Help("port is required.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("protocol")
+        , advgetopt::Flags(advgetopt::command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS
+                    , advgetopt::GETOPT_FLAG_REQUIRED>())
+        , advgetopt::DefaultValue("tcp")
+        , advgetopt::Help("specify protocol.")
+    ),
+    advgetopt::define_option(
+          advgetopt::Name("quiet")
+        , advgetopt::ShortName('q')
+        , advgetopt::Flags(advgetopt::standalone_command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_OPTIONS>())
+        , advgetopt::Help("do not show error messages.")
+    ),
+
+    // FILENAMES/PATHS
+    //
+    advgetopt::define_option(
+          advgetopt::Name("--")
+        , advgetopt::Flags(advgetopt::command_flags<
+                      advgetopt::GETOPT_FLAG_GROUP_NONE
+                    , advgetopt::GETOPT_FLAG_MULTIPLE
+                    , advgetopt::GETOPT_FLAG_DEFAULT_OPTION>())
+    ),
+
+    // END
+    //
+    advgetopt::end_options()
+};
+
+
+advgetopt::group_description const g_group_descriptions[] =
+{
+    advgetopt::define_group(
+          advgetopt::GroupNumber(advgetopt::GETOPT_FLAG_GROUP_COMMANDS)
+        , advgetopt::GroupName("command")
+        , advgetopt::GroupDescription("Commands:")
+    ),
+    advgetopt::define_group(
+          advgetopt::GroupNumber(advgetopt::GETOPT_FLAG_GROUP_OPTIONS)
+        , advgetopt::GroupName("option")
+        , advgetopt::GroupDescription("Options:")
+    ),
+    advgetopt::end_groups()
+};
+
+
+// until we have C++20, remove warnings this way
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+advgetopt::options_environment const g_options_environment =
+{
+    .f_project_name = "libaddr",
+    .f_options = g_options,
+    .f_options_files_directory = nullptr,
+    .f_environment_variable_name = nullptr,
+    .f_configuration_files = nullptr,
+    .f_configuration_filename = nullptr,
+    .f_configuration_directories = nullptr,
+    .f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_PROCESS_SYSTEM_PARAMETERS,
+    .f_help_header = "Usage: %p [--]  ...\n"
+                     "where -- is one or more of:",
+    .f_help_footer = "%c",
+    .f_version = LIBADDR_VERSION_STRING,
+    .f_license = "GNU GPL v2",
+    .f_copyright = "Copyright (c) 2013-"
+                   BOOST_PP_STRINGIZE(UTC_BUILD_YEAR)
+                   " by Made to Order Software Corporation -- All Rights Reserved",
+    .f_build_date = UTC_BUILD_DATE,
+    .f_build_time = UTC_BUILD_TIME,
+    .f_groups = g_group_descriptions
+};
+#pragma GCC diagnostic pop
+
+
+
+class validate_ip
+{
+public:
+                            validate_ip(int argc, char * argv[]);
+
+    void                    run();
+    int                     error_count() const;
+
+private:
+    advgetopt::getopt       f_opts;
+    int                     f_error_count = 0;
+};
+
+
+
+validate_ip::validate_ip(int argc, char * argv[])
+    : f_opts(g_options_environment, argc, argv)
+{
+}
+
+
+void validate_ip::run()
+{
+    size_t const max(f_opts.size("--"));
+    for(size_t idx(0); idx < max; ++idx)
+    {
+        std::string addr(f_opts.get_string("--", idx));
+
+        addr::addr_parser p;
+
+        p.set_default_address("127.0.0.1");
+        p.set_default_address("::");
+        std::string const default_address(f_opts.get_string("default-address"));
+        std::string::size_type const pos(default_address.find(','));
+        if(pos == std::string::npos)
+        {
+            p.set_default_address(default_address);
+        }
+        else
+        {
+            p.set_default_address(default_address.substr(0, pos));
+            p.set_default_address(default_address.substr(pos + 1));
+        }
+
+        p.set_default_port(f_opts.get_long("default-port"));
+
+        p.set_default_mask("255.255.255.255");
+        p.set_default_mask("FF:FF:FF:FF:FF:FF:FF:FF");
+
+        p.set_protocol(f_opts.get_string("protocol"));
+
+        p.set_allow(addr::addr_parser::flag_t::ADDRESS, true);
+        p.set_allow(addr::addr_parser::flag_t::PORT, true);
+
+        if(f_opts.is_defined("address"))
+        {
+            p.set_allow(addr::addr_parser::flag_t::REQUIRED_ADDRESS, true);
+        }
+        if(f_opts.is_defined("port"))
+        {
+            p.set_allow(addr::addr_parser::flag_t::REQUIRED_PORT, true);
+        }
+        if(f_opts.is_defined("mask"))
+        {
+            p.set_allow(addr::addr_parser::flag_t::MASK, true);
+        }
+        if(f_opts.is_defined("list"))
+        {
+            p.set_allow(addr::addr_parser::flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES, true);
+        }
+
+        addr::addr_range::vector_t const range(p.parse(addr));
+
+        if(p.has_errors())
+        {
+            f_error_count += p.error_count();
+            if(!f_opts.is_defined("quiet"))
+            {
+                std::cerr << "address \""
+                          << addr
+                          << "\" generated "
+                          << p.error_count()
+                          << " error"
+                          << (p.error_count() == 1 ? "" : "s")
+                          << ": "
+                          << p.error_messages()
+                          << std::endl;
+            }
+        }
+        else if(f_opts.is_defined("canonicalize"))
+        {
+            for(auto r : range)
+            {
+                if(r.is_range())
+                {
+                    std::cout << r.get_from().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ALL)
+                              << " .. "
+                              << r.get_to().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ALL)
+                              << std::endl;
+                }
+                else if(r.has_from())
+                {
+                    std::cout << r.get_from().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ALL)
+                              << std::endl;
+                }
+                else if(r.has_to())
+                {
+                    std::cout << r.get_to().to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ALL)
+                              << std::endl;
+                }
+                else
+                {
+                    throw addr::addr_invalid_structure("unexpected range, no from and no to defined.");
+                }
+            }
+        }
+    }
+}
+
+
+
+int validate_ip::error_count() const
+{
+    return f_error_count;
+}
+
+
+
+
+}
+// no name namespace
+
+
+int main(int argc, char * argv[])
+{
+    try
+    {
+        validate_ip v(argc, argv);
+        v.run();
+        return v.error_count() != 0 ? 1 : 0;
+    }
+    catch(advgetopt::getopt_exception_exit const & e)
+    {
+        snap::NOTUSED(e);
+        return 0;
+    }
+    catch(std::exception const & e)
+    {
+        std::cerr << "error: an error occurred: " << e.what() << std::endl;
+        return 1;
+    }
+}
+
+// vim: ts=4 sw=4 et