root/lib/libcasper/services/cap_net/tests/net_test.c
/*-
 * Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>
#include <netdb.h>

#include <atf-c.h>

#include <libcasper.h>
#include <casper/cap_net.h>

#define TEST_DOMAIN_0   "example.com"
#define TEST_DOMAIN_1   "freebsd.org"
#define TEST_IPV4       "1.1.1.1"
#define TEST_IPV6       "2001:4860:4860::8888"
#define TEST_BIND_IPV4  "127.0.0.1"
#define TEST_PORT       80
#define TEST_PORT_STR   "80"

static cap_channel_t *
create_network_service(void)
{
        cap_channel_t *capcas, *capnet;

        capcas = cap_init();
        ATF_REQUIRE(capcas != NULL);

        capnet = cap_service_open(capcas, "system.net");
        ATF_REQUIRE(capnet != NULL);

        cap_close(capcas);
        return (capnet);
}

static int
test_getnameinfo_v4(cap_channel_t *chan, int family, const char *ip)
{
        struct sockaddr_in ipaddr;
        char capfn[MAXHOSTNAMELEN];
        char origfn[MAXHOSTNAMELEN];
        int capret, sysret;

        memset(&ipaddr, 0, sizeof(ipaddr));
        ipaddr.sin_family = family;
        inet_pton(family, ip, &ipaddr.sin_addr);

        capret = cap_getnameinfo(chan, (struct sockaddr *)&ipaddr, sizeof(ipaddr),
            capfn, sizeof(capfn), NULL, 0, NI_NAMEREQD);
        if (capret != 0 && capret == ENOTCAPABLE)
                return (ENOTCAPABLE);

        sysret = getnameinfo((struct sockaddr *)&ipaddr, sizeof(ipaddr), origfn,
            sizeof(origfn), NULL, 0, NI_NAMEREQD);
        if (sysret != 0) {
                atf_tc_skip("getnameinfo(%s) failed: %s",
                    ip, gai_strerror(sysret));
        }
        ATF_REQUIRE(capret == 0);
        ATF_REQUIRE(strcmp(origfn, capfn) == 0);

        return (0);
}

static int
test_getnameinfo_v6(cap_channel_t *chan, const char *ip)
{
        struct sockaddr_in6 ipaddr;
        char capfn[MAXHOSTNAMELEN];
        char origfn[MAXHOSTNAMELEN];
        int capret, sysret;

        memset(&ipaddr, 0, sizeof(ipaddr));
        ipaddr.sin6_family = AF_INET6;
        inet_pton(AF_INET6, ip, &ipaddr.sin6_addr);

        capret = cap_getnameinfo(chan, (struct sockaddr *)&ipaddr, sizeof(ipaddr),
            capfn, sizeof(capfn), NULL, 0, NI_NAMEREQD);
        if (capret != 0 && capret == ENOTCAPABLE)
                return (ENOTCAPABLE);

        sysret = getnameinfo((struct sockaddr *)&ipaddr, sizeof(ipaddr), origfn,
            sizeof(origfn), NULL, 0, NI_NAMEREQD);
        if (sysret != 0) {
                atf_tc_skip("getnameinfo(%s) failed: %s",
                    ip, gai_strerror(sysret));
        }
        ATF_REQUIRE(capret == 0);
        ATF_REQUIRE(strcmp(origfn, capfn) == 0);

        return (0);
}

static int
test_getnameinfo(cap_channel_t *chan, int family, const char *ip)
{

        if (family == AF_INET6) {
                return (test_getnameinfo_v6(chan, ip));
        }

        return (test_getnameinfo_v4(chan, family, ip));
}

static int
test_gethostbyaddr_v4(cap_channel_t *chan, int family, const char *ip)
{
        struct in_addr ipaddr;
        struct hostent *caphp, *orighp;

        memset(&ipaddr, 0, sizeof(ipaddr));
        inet_pton(AF_INET, ip, &ipaddr);

        caphp = cap_gethostbyaddr(chan, &ipaddr, sizeof(ipaddr), family);
        if (caphp == NULL && h_errno == ENOTCAPABLE)
                return (ENOTCAPABLE);

        orighp = gethostbyaddr(&ipaddr, sizeof(ipaddr), family);
        if (orighp == NULL)
                atf_tc_skip("gethostbyaddr(%s) failed", ip);
        ATF_REQUIRE(caphp != NULL);
        ATF_REQUIRE(strcmp(orighp->h_name, caphp->h_name) == 0);

        return (0);
}

static int
test_gethostbyaddr_v6(cap_channel_t *chan, const char *ip)
{
        struct in6_addr ipaddr;
        struct hostent *caphp, *orighp;

        memset(&ipaddr, 0, sizeof(ipaddr));
        inet_pton(AF_INET6, ip, &ipaddr);

        caphp = cap_gethostbyaddr(chan, &ipaddr, sizeof(ipaddr), AF_INET6);
        if (caphp == NULL && h_errno == ENOTCAPABLE)
                return (ENOTCAPABLE);

        orighp = gethostbyaddr(&ipaddr, sizeof(ipaddr), AF_INET6);
        if (orighp == NULL)
                atf_tc_skip("gethostbyaddr(%s) failed", ip);
        ATF_REQUIRE(caphp != NULL);
        ATF_REQUIRE(strcmp(orighp->h_name, caphp->h_name) == 0);

        return (0);
}

static int
test_gethostbyaddr(cap_channel_t *chan, int family, const char *ip)
{

        if (family == AF_INET6) {
                return (test_gethostbyaddr_v6(chan, ip));
        } else {
                return (test_gethostbyaddr_v4(chan, family, ip));
        }
}

static int
test_getaddrinfo(cap_channel_t *chan, int family, const char *domain,
    const char *servname)
{
        struct addrinfo hints, *capres, *origres, *res0, *res1;
        bool found;
        int capret, sysret;

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = family;
        hints.ai_socktype = SOCK_STREAM;

        capret = cap_getaddrinfo(chan, domain, servname, &hints, &capres);
        if (capret != 0 && capret == ENOTCAPABLE)
                return (capret);

        sysret = getaddrinfo(domain, servname, &hints, &origres);
        if (sysret != 0)
                atf_tc_skip("getaddrinfo(%s) failed: %s",
                    domain, gai_strerror(sysret));
        ATF_REQUIRE(capret == 0);

        for (res0 = capres; res0 != NULL; res0 = res0->ai_next) {
                found = false;
                for (res1 = origres; res1 != NULL; res1 = res1->ai_next) {
                        if (res1->ai_addrlen == res0->ai_addrlen &&
                            memcmp(res1->ai_addr, res0->ai_addr,
                            res0->ai_addrlen) == 0) {
                                found = true;
                                break;
                        }
                }
                ATF_REQUIRE(found);
        }

        freeaddrinfo(capres);
        freeaddrinfo(origres);
        return (0);
}

static int
test_gethostbyname(cap_channel_t *chan, int family, const char *domain)
{
        struct hostent *caphp, *orighp;

        caphp = cap_gethostbyname2(chan, domain, family);
        if (caphp == NULL && h_errno == ENOTCAPABLE)
                return (h_errno);

        orighp = gethostbyname2(domain, family);
        if (orighp == NULL)
                atf_tc_skip("gethostbyname2(%s) failed", domain);

        ATF_REQUIRE(caphp != NULL);
        ATF_REQUIRE(strcmp(caphp->h_name, orighp->h_name) == 0);
        return (0);
}

static int
test_bind(cap_channel_t *chan, const char *ip)
{
        struct sockaddr_in ipv4;
        int capfd, ret, serrno;

        capfd = socket(AF_INET, SOCK_STREAM, 0);
        ATF_REQUIRE(capfd > 0);

        memset(&ipv4, 0, sizeof(ipv4));
        ipv4.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &ipv4.sin_addr);

        ret = cap_bind(chan, capfd, (struct sockaddr *)&ipv4, sizeof(ipv4));
        serrno = errno;
        close(capfd);

        return (ret < 0 ? serrno : 0);
}

static int
test_connect(cap_channel_t *chan, const char *ip, unsigned short port)
{
        struct sockaddr_in ipv4;
        int capfd, ret, serrno;

        capfd = socket(AF_INET, SOCK_STREAM, 0);
        ATF_REQUIRE(capfd >= 0);

        memset(&ipv4, 0, sizeof(ipv4));
        ipv4.sin_family = AF_INET;
        ipv4.sin_port = htons(port);
        inet_pton(AF_INET, ip, &ipv4.sin_addr);

        ret = cap_connect(chan, capfd, (struct sockaddr *)&ipv4, sizeof(ipv4));
        serrno = errno;
        ATF_REQUIRE(close(capfd) == 0);

        if (ret < 0 && serrno != ENOTCAPABLE) {
                int sd;

                /*
                 * If the connection failed, it might be because we can't reach
                 * the destination host.  To check, try a plain connect() and
                 * see if it fails with the same error.
                 */
                sd = socket(AF_INET, SOCK_STREAM, 0);
                ATF_REQUIRE(sd >= 0);

                memset(&ipv4, 0, sizeof(ipv4));
                ipv4.sin_family = AF_INET;
                ipv4.sin_port = htons(port);
                inet_pton(AF_INET, ip, &ipv4.sin_addr);
                ret = connect(sd, (struct sockaddr *)&ipv4, sizeof(ipv4));
                ATF_REQUIRE(ret < 0);
                ATF_REQUIRE_MSG(errno == serrno, "errno %d != serrno %d",
                    errno, serrno);
                ATF_REQUIRE(close(sd) == 0);
                atf_tc_skip("connect(%s:%d) failed: %s",
                    ip, port, strerror(serrno));
        }

        return (ret < 0 ? serrno : 0);
}

static void
test_extend_mode(cap_channel_t *capnet, int current)
{
        cap_net_limit_t *limit;
        const int rights[] = {
                CAPNET_ADDR2NAME,
                CAPNET_NAME2ADDR,
                CAPNET_DEPRECATED_ADDR2NAME,
                CAPNET_DEPRECATED_NAME2ADDR,
                CAPNET_CONNECT,
                CAPNET_BIND,
                CAPNET_CONNECTDNS
        };
        size_t i;

        for (i = 0; i < nitems(rights); i++) {
                if (current == rights[i])
                        continue;

                limit = cap_net_limit_init(capnet, current | rights[i]);
                ATF_REQUIRE(limit != NULL);
                ATF_REQUIRE(cap_net_limit(limit) != 0);
        }
}

ATF_TC(capnet__getnameinfo);
ATF_TC_HEAD(capnet__getnameinfo, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__getnameinfo, tc)
{
        cap_channel_t *capnet;

        capnet = create_network_service();

        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0);

        cap_close(capnet);
}

ATF_TC(capnet__connect);
ATF_TC_HEAD(capnet__connect, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__connect, tc)
{
        cap_channel_t *capnet;

        capnet = create_network_service();

        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0);

        cap_close(capnet);
}

ATF_TC(capnet__bind);
ATF_TC_HEAD(capnet__bind, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__bind, tc)
{
        cap_channel_t *capnet;

        capnet = create_network_service();

        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == 0);

        cap_close(capnet);
}

ATF_TC(capnet__getaddrinfo);
ATF_TC_HEAD(capnet__getaddrinfo, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__getaddrinfo, tc)
{
        cap_channel_t *capnet;
        struct addrinfo hints, *capres;

        capnet = create_network_service();

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;

        ATF_REQUIRE(cap_getaddrinfo(capnet, TEST_IPV4, "80", &hints, &capres) ==
            0);

        cap_close(capnet);
}

ATF_TC(capnet__gethostbyname);
ATF_TC_HEAD(capnet__gethostbyname, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__gethostbyname, tc)
{
        cap_channel_t *capnet;

        capnet = create_network_service();

        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0);

        cap_close(capnet);
}

ATF_TC(capnet__gethostbyaddr);
ATF_TC_HEAD(capnet__gethostbyaddr, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__gethostbyaddr, tc)
{
        cap_channel_t *capnet;

        capnet = create_network_service();

        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0);

        cap_close(capnet);
}

ATF_TC(capnet__getnameinfo_buffer);
ATF_TC_HEAD(capnet__getnameinfo_buffer, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__getnameinfo_buffer, tc)
{
        cap_channel_t *chan;
        struct sockaddr_in sin;
        int ret;
        struct {
                char host[sizeof(TEST_IPV4)];
                char host_canary;
                char serv[sizeof(TEST_PORT_STR)];
                char serv_canary;
        } buffers;

        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(TEST_PORT);
        ret = inet_pton(AF_INET, TEST_IPV4, &sin.sin_addr);
        ATF_REQUIRE_EQ(1, ret);

        memset(&buffers, '!', sizeof(buffers));

        chan = create_network_service();
        ret = cap_getnameinfo(chan, (struct sockaddr *)&sin, sizeof(sin),
            buffers.host, sizeof(buffers.host),
            buffers.serv, sizeof(buffers.serv),
            NI_NUMERICHOST | NI_NUMERICSERV);
        ATF_REQUIRE_EQ_MSG(0, ret, "%d", ret);

        // Verify that cap_getnameinfo worked with minimally sized buffers.
        ATF_CHECK_EQ(0, strcmp(TEST_IPV4, buffers.host));
        ATF_CHECK_EQ(0, strcmp(TEST_PORT_STR, buffers.serv));

        // Verify that cap_getnameinfo did not overflow the buffers.
        ATF_CHECK_EQ('!', buffers.host_canary);
        ATF_CHECK_EQ('!', buffers.serv_canary);

        cap_close(chan);
}

ATF_TC(capnet__limits_addr2name_mode);
ATF_TC_HEAD(capnet__limits_addr2name_mode, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_addr2name_mode, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* LIMIT */
        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        /* ALLOWED */
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0);

        /* DISALLOWED */
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE);

        test_extend_mode(capnet, CAPNET_ADDR2NAME);

        cap_close(capnet);
}

ATF_TC(capnet__limits_addr2name_family);
ATF_TC_HEAD(capnet__limits_addr2name_family, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_addr2name_family, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        int family[] = { AF_INET6, AF_INET };

        capnet = create_network_service();

        /* Limit to AF_INET6 and AF_INET. */
        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name_family(limit, family, nitems(family));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0);

        /* Limit to AF_INET6 and AF_INET. */
        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name_family(limit, &family[0], 1);
        cap_net_limit_addr2name_family(limit, &family[1], 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0);

        /* Limit to AF_INET6. */
        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name_family(limit, family, 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_addr2name);
ATF_TC_HEAD(capnet__limits_addr2name, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_addr2name, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        struct sockaddr_in ipaddrv4;
        struct sockaddr_in6 ipaddrv6;

        capnet = create_network_service();

        /* Limit to TEST_IPV4 and TEST_IPV6. */
        memset(&ipaddrv4, 0, sizeof(ipaddrv4));
        memset(&ipaddrv6, 0, sizeof(ipaddrv6));

        ipaddrv4.sin_family = AF_INET;
        inet_pton(AF_INET, TEST_IPV4, &ipaddrv4.sin_addr);

        ipaddrv6.sin6_family = AF_INET6;
        inet_pton(AF_INET6, TEST_IPV6, &ipaddrv6.sin6_addr);

        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);

        cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4,
            sizeof(ipaddrv4));
        cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv6,
            sizeof(ipaddrv6));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, "127.0.0.1") ==
            ENOTCAPABLE);

        /* Limit to AF_INET. */
        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4,
            sizeof(ipaddrv4));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, "127.0.0.1") ==
            ENOTCAPABLE);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_deprecated_addr2name_mode);
ATF_TC_HEAD(capnet__limits_deprecated_addr2name_mode, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_deprecated_addr2name_mode, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* LIMIT */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        /* ALLOWED */
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0);

        /* DISALLOWED */
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE);

        cap_close(capnet);
}

ATF_TC(capnet__limits_deprecated_addr2name_family);
ATF_TC_HEAD(capnet__limits_deprecated_addr2name_family, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_deprecated_addr2name_family, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        int family[] = { AF_INET6, AF_INET };

        capnet = create_network_service();

        /* Limit to AF_INET6 and AF_INET. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name_family(limit, family, nitems(family));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, PF_LINK, TEST_IPV4) ==
            ENOTCAPABLE);

        /* Limit to AF_INET6 and AF_INET. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name_family(limit, &family[0], 1);
        cap_net_limit_addr2name_family(limit, &family[1], 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, PF_LINK, TEST_IPV4) ==
            ENOTCAPABLE);

        /* Limit to AF_INET6. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name_family(limit, family, 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, PF_LINK, TEST_IPV4) ==
            ENOTCAPABLE);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_deprecated_addr2name);
ATF_TC_HEAD(capnet__limits_deprecated_addr2name, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_deprecated_addr2name, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        struct in_addr ipaddrv4;
        struct in6_addr ipaddrv6;

        capnet = create_network_service();

        /* Limit to TEST_IPV4 and TEST_IPV6. */
        memset(&ipaddrv4, 0, sizeof(ipaddrv4));
        memset(&ipaddrv6, 0, sizeof(ipaddrv6));

        inet_pton(AF_INET, TEST_IPV4, &ipaddrv4);
        inet_pton(AF_INET6, TEST_IPV6, &ipaddrv6);

        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);

        cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4,
            sizeof(ipaddrv4));
        cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv6,
            sizeof(ipaddrv6));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, "127.0.0.1") ==
            ENOTCAPABLE);

        /* Limit to AF_INET. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4,
            sizeof(ipaddrv4));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, "127.0.0.1") ==
            ENOTCAPABLE);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}


ATF_TC(capnet__limits_name2addr_mode);
ATF_TC_HEAD(capnet__limits_name2addr_mode, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_name2addr_mode, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* LIMIT */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        /* ALLOWED */
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            0);

        /* DISALLOWED */
        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE);

        test_extend_mode(capnet, CAPNET_ADDR2NAME);

        cap_close(capnet);
}

ATF_TC(capnet__limits_name2addr_hosts);
ATF_TC_HEAD(capnet__limits_name2addr_hosts, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_name2addr_hosts, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* Limit to TEST_DOMAIN_0 and localhost only. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr(limit, "localhost", NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, "localhost", NULL) == 0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, NULL) ==
            ENOTCAPABLE);

        /* Limit to TEST_DOMAIN_0 only. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, "localhost", NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            0);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        /* Try to extend the limit. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_1, NULL);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_1, NULL);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_name2addr_hosts_servnames_strict);
ATF_TC_HEAD(capnet__limits_name2addr_hosts_servnames_strict, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_name2addr_hosts_servnames_strict, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /*
         * Limit to TEST_DOMAIN_0 and HTTP service.
         */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, "http");
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "http") ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "snmp") ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "http") ==
            ENOTCAPABLE);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_name2addr_hosts_servnames_mix);
ATF_TC_HEAD(capnet__limits_name2addr_hosts_servnames_mix, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_name2addr_hosts_servnames_mix, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /*
         * Limit to TEST_DOMAIN_0 and any servnamex, and any domain with
         * servname HTTP.
         */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr(limit, NULL, "http");
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "http") ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "http") ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "snmp") ==
            ENOTCAPABLE);

        /* Limit to HTTP servname only. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, NULL, "http");
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "http") ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "http") ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "snmp") ==
            ENOTCAPABLE);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_name2addr_family);
ATF_TC_HEAD(capnet__limits_name2addr_family, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_name2addr_family, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        int family[] = { AF_INET6, AF_INET };

        capnet = create_network_service();

        /* Limit to AF_INET and AF_INET6. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr_family(limit, family, nitems(family));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET6, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, PF_LINK, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);

        /* Limit to AF_INET and AF_INET6. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr_family(limit, &family[0], 1);
        cap_net_limit_name2addr_family(limit, &family[1], 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET6, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, PF_LINK, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);

        /* Limit to AF_INET6 only. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr_family(limit, family, 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET6, TEST_DOMAIN_0, NULL) ==
            0);
        ATF_REQUIRE(test_getaddrinfo(capnet, PF_LINK, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_deprecated_name2addr_mode);
ATF_TC_HEAD(capnet__limits_deprecated_name2addr_mode, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_deprecated_name2addr_mode, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* LIMIT */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        /* ALLOWED */
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0);

        /* DISALLOWED */
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE);

        test_extend_mode(capnet, CAPNET_ADDR2NAME);

        cap_close(capnet);
}

ATF_TC(capnet__limits_deprecated_name2addr_hosts);
ATF_TC_HEAD(capnet__limits_deprecated_name2addr_hosts, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_deprecated_name2addr_hosts, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* Limit to TEST_DOMAIN_0 and localhost only. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr(limit, "localhost", NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0);
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, "localhost") == 0);
        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_1) == ENOTCAPABLE);

        /* Limit to TEST_DOMAIN_0 only. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, "localhost") == ENOTCAPABLE);
        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_1) == ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_deprecated_name2addr_family);
ATF_TC_HEAD(capnet__limits_deprecated_name2addr_family, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_deprecated_name2addr_family, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        int family[] = { AF_INET6, AF_INET };

        capnet = create_network_service();

        /* Limit to AF_INET and AF_INET6. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr_family(limit, family, nitems(family));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0);
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET6, TEST_DOMAIN_0) == 0);
        ATF_REQUIRE(
            test_gethostbyname(capnet, PF_LINK, TEST_DOMAIN_0) == ENOTCAPABLE);

        /* Limit to AF_INET and AF_INET6. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr_family(limit, &family[0], 1);
        cap_net_limit_name2addr_family(limit, &family[1], 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0);
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET6, TEST_DOMAIN_0) == 0);
        ATF_REQUIRE(
            test_gethostbyname(capnet, PF_LINK, TEST_DOMAIN_0) == ENOTCAPABLE);

        /* Limit to AF_INET6 only. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL);
        cap_net_limit_name2addr_family(limit, family, 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyname(capnet, AF_INET6, TEST_DOMAIN_0) == 0);
        ATF_REQUIRE(
            test_gethostbyname(capnet, PF_LINK, TEST_DOMAIN_0) == ENOTCAPABLE);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_bind_mode);
ATF_TC_HEAD(capnet__limits_bind_mode, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_bind_mode, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* LIMIT */
        limit = cap_net_limit_init(capnet, CAPNET_BIND);
        ATF_REQUIRE(limit != NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        /* ALLOWED */
        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == 0);

        /* DISALLOWED */
        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE);

        test_extend_mode(capnet, CAPNET_ADDR2NAME);

        cap_close(capnet);
}

ATF_TC(capnet__limits_bind);
ATF_TC_HEAD(capnet__limits_bind, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_bind, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        struct sockaddr_in ipv4;

        capnet = create_network_service();

        limit = cap_net_limit_init(capnet, CAPNET_BIND);
        ATF_REQUIRE(limit != NULL);

        memset(&ipv4, 0, sizeof(ipv4));
        ipv4.sin_family = AF_INET;
        inet_pton(AF_INET, TEST_BIND_IPV4, &ipv4.sin_addr);

        cap_net_limit_bind(limit, (struct sockaddr *)&ipv4, sizeof(ipv4));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == 0);
        ATF_REQUIRE(test_bind(capnet, "127.0.0.2") == ENOTCAPABLE);

        cap_close(capnet);
}

ATF_TC(capnet__limits_connect_mode);
ATF_TC_HEAD(capnet__limits_connect_mode, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_connect_mode, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* LIMIT */
        limit = cap_net_limit_init(capnet, CAPNET_CONNECT);
        ATF_REQUIRE(limit != NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        /* ALLOWED */
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0);

        /* DISALLOWED */
        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE);

        test_extend_mode(capnet, CAPNET_ADDR2NAME);

        cap_close(capnet);
}

ATF_TC(capnet__limits_connect_dns_mode);
ATF_TC_HEAD(capnet__limits_connect_dns_mode, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_connect_dns_mode, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;

        capnet = create_network_service();

        /* LIMIT */
        limit = cap_net_limit_init(capnet, CAPNET_CONNECT | CAPNET_CONNECTDNS);
        ATF_REQUIRE(limit != NULL);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        /* ALLOWED */
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0);

        /* DISALLOWED */
        ATF_REQUIRE(
            test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE);
        ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) ==
            ENOTCAPABLE);
        ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE);

        test_extend_mode(capnet, CAPNET_ADDR2NAME);

        cap_close(capnet);
}

ATF_TC(capnet__limits_connect);
ATF_TC_HEAD(capnet__limits_connect, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_connect, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        struct sockaddr_in ipv4;

        capnet = create_network_service();

        /* Limit only to TEST_IPV4 on port 80 and 443. */
        limit = cap_net_limit_init(capnet, CAPNET_CONNECT);
        ATF_REQUIRE(limit != NULL);
        memset(&ipv4, 0, sizeof(ipv4));
        ipv4.sin_family = AF_INET;
        ipv4.sin_port = htons(80);
        inet_pton(AF_INET, TEST_IPV4, &ipv4.sin_addr);
        cap_net_limit_connect(limit, (struct sockaddr *)&ipv4, sizeof(ipv4));

        ipv4.sin_port = htons(443);
        cap_net_limit_connect(limit, (struct sockaddr *)&ipv4, sizeof(ipv4));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0);
        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 80) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 443) == 0);

        /* Limit only to TEST_IPV4 on port 443. */
        limit = cap_net_limit_init(capnet, CAPNET_CONNECT);
        cap_net_limit_connect(limit, (struct sockaddr *)&ipv4, sizeof(ipv4));
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 433) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 80) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE);
        ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 443) == 0);

        /* Unable to set empty limits. Empty limits means full access. */
        limit = cap_net_limit_init(capnet, CAPNET_CONNECT);
        ATF_REQUIRE(cap_net_limit(limit) != 0);

        cap_close(capnet);
}

ATF_TC(capnet__limits_connecttodns);
ATF_TC_HEAD(capnet__limits_connecttodns, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_connecttodns, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        struct addrinfo hints, *capres, *res;
        int family[] = { AF_INET };
        int error;

        capnet = create_network_service();

        limit = cap_net_limit_init(capnet, CAPNET_CONNECTDNS |
            CAPNET_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_IPV4, "80");
        cap_net_limit_name2addr_family(limit, family, 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;

        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE);
        ATF_REQUIRE(cap_getaddrinfo(capnet, TEST_IPV4, "80", &hints, &capres) ==
            0);
        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE);

        for (res = capres; res != NULL; res = res->ai_next) {
                int s;

                ATF_REQUIRE(res->ai_family == AF_INET);
                ATF_REQUIRE(res->ai_socktype == SOCK_STREAM);

                s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
                ATF_REQUIRE(s >= 0);

                error = cap_connect(capnet, s, res->ai_addr,
                    res->ai_addrlen);
                if (error != 0 && errno != ENOTCAPABLE)
                        atf_tc_skip("unable to connect: %s", strerror(errno));
                ATF_REQUIRE(error == 0);
                ATF_REQUIRE(close(s) == 0);
        }

        freeaddrinfo(capres);
        cap_close(capnet);
}


ATF_TC(capnet__limits_deprecated_connecttodns);
ATF_TC_HEAD(capnet__limits_deprecated_connecttodns, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(capnet__limits_deprecated_connecttodns, tc)
{
        cap_channel_t *capnet;
        cap_net_limit_t *limit;
        struct hostent *caphp;
        struct in_addr ipaddr;
        struct sockaddr_in connaddr;
        int family[] = { AF_INET };
        int error, i;

        capnet = create_network_service();

        limit = cap_net_limit_init(capnet, CAPNET_CONNECTDNS |
            CAPNET_DEPRECATED_NAME2ADDR);
        ATF_REQUIRE(limit != NULL);
        cap_net_limit_name2addr(limit, TEST_IPV4, NULL);
        cap_net_limit_name2addr_family(limit, family, 1);
        ATF_REQUIRE(cap_net_limit(limit) == 0);

        memset(&ipaddr, 0, sizeof(ipaddr));
        inet_pton(AF_INET, TEST_IPV4, &ipaddr);

        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE);
        caphp = cap_gethostbyname2(capnet, TEST_IPV4, AF_INET);
        ATF_REQUIRE(caphp != NULL);
        ATF_REQUIRE(caphp->h_addrtype == AF_INET);
        ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE);

        for (i = 0; caphp->h_addr_list[i] != NULL; i++) {
                int s;

                s = socket(AF_INET, SOCK_STREAM, 0);
                ATF_REQUIRE(s >= 0);

                memset(&connaddr, 0, sizeof(connaddr));
                connaddr.sin_family = AF_INET;
                memcpy((char *)&connaddr.sin_addr.s_addr,
                    (char *)caphp->h_addr_list[i], caphp->h_length);
                connaddr.sin_port = htons(80);

                error = cap_connect(capnet, s, (struct sockaddr *)&connaddr,
                    sizeof(connaddr));
                if (error != 0 && errno != ENOTCAPABLE)
                        atf_tc_skip("unable to connect: %s", strerror(errno));
                ATF_REQUIRE(error == 0);
                ATF_REQUIRE(close(s) == 0);
        }

        cap_close(capnet);
}

ATF_TP_ADD_TCS(tp)
{

        ATF_TP_ADD_TC(tp, capnet__connect);
        ATF_TP_ADD_TC(tp, capnet__bind);
        ATF_TP_ADD_TC(tp, capnet__getnameinfo);
        ATF_TP_ADD_TC(tp, capnet__getaddrinfo);
        ATF_TP_ADD_TC(tp, capnet__gethostbyname);
        ATF_TP_ADD_TC(tp, capnet__gethostbyaddr);

        ATF_TP_ADD_TC(tp, capnet__getnameinfo_buffer);

        ATF_TP_ADD_TC(tp, capnet__limits_addr2name_mode);
        ATF_TP_ADD_TC(tp, capnet__limits_addr2name_family);
        ATF_TP_ADD_TC(tp, capnet__limits_addr2name);

        ATF_TP_ADD_TC(tp, capnet__limits_deprecated_addr2name_mode);
        ATF_TP_ADD_TC(tp, capnet__limits_deprecated_addr2name_family);
        ATF_TP_ADD_TC(tp, capnet__limits_deprecated_addr2name);

        ATF_TP_ADD_TC(tp, capnet__limits_name2addr_mode);
        ATF_TP_ADD_TC(tp, capnet__limits_name2addr_hosts);
        ATF_TP_ADD_TC(tp, capnet__limits_name2addr_hosts_servnames_strict);
        ATF_TP_ADD_TC(tp, capnet__limits_name2addr_hosts_servnames_mix);
        ATF_TP_ADD_TC(tp, capnet__limits_name2addr_family);

        ATF_TP_ADD_TC(tp, capnet__limits_deprecated_name2addr_mode);
        ATF_TP_ADD_TC(tp, capnet__limits_deprecated_name2addr_hosts);
        ATF_TP_ADD_TC(tp, capnet__limits_deprecated_name2addr_family);

        ATF_TP_ADD_TC(tp, capnet__limits_bind_mode);
        ATF_TP_ADD_TC(tp, capnet__limits_bind);

        ATF_TP_ADD_TC(tp, capnet__limits_connect_mode);
        ATF_TP_ADD_TC(tp, capnet__limits_connect_dns_mode);
        ATF_TP_ADD_TC(tp, capnet__limits_connect);

        ATF_TP_ADD_TC(tp, capnet__limits_connecttodns);
        ATF_TP_ADD_TC(tp, capnet__limits_deprecated_connecttodns);

        return (atf_no_error());
}