root/lib/libcasper/services/cap_dns/tests/dns_test.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2013 The FreeBSD Foundation
 *
 * This software was developed by Pawel Jakub Dawidek under sponsorship from
 * the FreeBSD Foundation.
 *
 * 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 AUTHORS 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 AUTHORS 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/capsicum.h>
#include <sys/nv.h>

#include <arpa/inet.h>
#include <netinet/in.h>

#include <assert.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

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

#include <atf-c.h>

#define GETHOSTBYNAME                   0x01
#define GETHOSTBYNAME2_AF_INET          0x02
#define GETHOSTBYNAME2_AF_INET6         0x04
#define GETHOSTBYADDR_AF_INET           0x08
#define GETHOSTBYADDR_AF_INET6          0x10
#define GETADDRINFO_AF_UNSPEC           0x20
#define GETADDRINFO_AF_INET             0x40
#define GETADDRINFO_AF_INET6            0x80

static bool
addrinfo_compare(struct addrinfo *ai0, struct addrinfo *ai1)
{
        struct addrinfo *at0, *at1;

        if (ai0 == NULL && ai1 == NULL)
                return (true);
        if (ai0 == NULL || ai1 == NULL)
                return (false);

        at0 = ai0;
        at1 = ai1;
        while (true) {
                if ((at0->ai_flags == at1->ai_flags) &&
                    (at0->ai_family == at1->ai_family) &&
                    (at0->ai_socktype == at1->ai_socktype) &&
                    (at0->ai_protocol == at1->ai_protocol) &&
                    (at0->ai_addrlen == at1->ai_addrlen) &&
                    (memcmp(at0->ai_addr, at1->ai_addr,
                        at0->ai_addrlen) == 0)) {
                        if (at0->ai_canonname != NULL &&
                            at1->ai_canonname != NULL) {
                                if (strcmp(at0->ai_canonname,
                                    at1->ai_canonname) != 0) {
                                        return (false);
                                }
                        }

                        if (at0->ai_canonname == NULL &&
                            at1->ai_canonname != NULL) {
                                return (false);
                        }
                        if (at0->ai_canonname != NULL &&
                            at1->ai_canonname == NULL) {
                                return (false);
                        }

                        if (at0->ai_next == NULL && at1->ai_next == NULL)
                                return (true);
                        if (at0->ai_next == NULL || at1->ai_next == NULL)
                                return (false);

                        at0 = at0->ai_next;
                        at1 = at1->ai_next;
                } else {
                        return (false);
                }
        }

        /* NOTREACHED */
        fprintf(stderr, "Dead code reached in addrinfo_compare()\n");
        exit(1);
}

static bool
hostent_aliases_compare(char **aliases0, char **aliases1)
{
        int i0, i1;

        if (aliases0 == NULL && aliases1 == NULL)
                return (true);
        if (aliases0 == NULL || aliases1 == NULL)
                return (false);

        for (i0 = 0; aliases0[i0] != NULL; i0++) {
                for (i1 = 0; aliases1[i1] != NULL; i1++) {
                        if (strcmp(aliases0[i0], aliases1[i1]) == 0)
                                break;
                }
                if (aliases1[i1] == NULL)
                        return (false);
        }

        return (true);
}

static bool
hostent_addr_list_compare(char **addr_list0, char **addr_list1, int length)
{
        int i0, i1;

        if (addr_list0 == NULL && addr_list1 == NULL)
                return (true);
        if (addr_list0 == NULL || addr_list1 == NULL)
                return (false);

        for (i0 = 0; addr_list0[i0] != NULL; i0++) {
                for (i1 = 0; addr_list1[i1] != NULL; i1++) {
                        if (memcmp(addr_list0[i0], addr_list1[i1], length) == 0)
                                break;
                }
                if (addr_list1[i1] == NULL)
                        return (false);
        }

        return (true);
}

static bool
hostent_compare(const struct hostent *hp0, const struct hostent *hp1)
{

        if (hp0 == NULL && hp1 != NULL)
                return (true);

        if (hp0 == NULL || hp1 == NULL)
                return (false);

        if (hp0->h_name != NULL || hp1->h_name != NULL) {
                if (hp0->h_name == NULL || hp1->h_name == NULL)
                        return (false);
                if (strcmp(hp0->h_name, hp1->h_name) != 0)
                        return (false);
        }

        if (!hostent_aliases_compare(hp0->h_aliases, hp1->h_aliases))
                return (false);
        if (!hostent_aliases_compare(hp1->h_aliases, hp0->h_aliases))
                return (false);

        if (hp0->h_addrtype != hp1->h_addrtype)
                return (false);

        if (hp0->h_length != hp1->h_length)
                return (false);

        if (!hostent_addr_list_compare(hp0->h_addr_list, hp1->h_addr_list,
            hp0->h_length)) {
                return (false);
        }
        if (!hostent_addr_list_compare(hp1->h_addr_list, hp0->h_addr_list,
            hp0->h_length)) {
                return (false);
        }

        return (true);
}

static void
runtest(cap_channel_t *capdns, unsigned int expected)
{
        unsigned int result;
        struct addrinfo *ais, *aic, hints, *hintsp;
        struct hostent *hps, *hpc;
        struct in_addr ip4;
        struct in6_addr ip6;
        int caperr, syserr;

        result = 0;

        hps = gethostbyname("example.com");
        if (hps == NULL) {
                fprintf(stderr, "Unable to resolve %s IPv4.\n", "example.com");
        } else {
                hpc = cap_gethostbyname(capdns, "example.com");
                if (hostent_compare(hps, hpc))
                        result |= GETHOSTBYNAME;
        }

        hps = gethostbyname2("example.com", AF_INET);
        if (hps == NULL) {
                fprintf(stderr, "Unable to resolve %s IPv4.\n", "example.com");
        } else {
                hpc = cap_gethostbyname2(capdns, "example.com", AF_INET);
                if (hostent_compare(hps, hpc))
                        result |= GETHOSTBYNAME2_AF_INET;
        }

        hps = gethostbyname2("example.com", AF_INET6);
        if (hps == NULL) {
                fprintf(stderr, "Unable to resolve %s IPv6.\n", "example.com");
        } else {
                hpc = cap_gethostbyname2(capdns, "example.com", AF_INET6);
                if (hostent_compare(hps, hpc))
                        result |= GETHOSTBYNAME2_AF_INET6;
        }

        hints.ai_flags = 0;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = 0;
        hints.ai_protocol = 0;
        hints.ai_addrlen = 0;
        hints.ai_addr = NULL;
        hints.ai_canonname = NULL;
        hints.ai_next = NULL;

        hintsp = &hints;

        syserr = getaddrinfo("freebsd.org", "25", hintsp, &ais);
        if (syserr != 0) {
                fprintf(stderr,
                    "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n",
                    gai_strerror(syserr));
        } else {
                caperr = cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp,
                    &aic);
                if (caperr == 0) {
                        if (addrinfo_compare(ais, aic))
                                result |= GETADDRINFO_AF_UNSPEC;
                        freeaddrinfo(ais);
                        freeaddrinfo(aic);
                }
        }

        hints.ai_family = AF_INET;
        syserr = getaddrinfo("freebsd.org", "25", hintsp, &ais);
        if (syserr != 0) {
                fprintf(stderr,
                    "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n",
                    gai_strerror(syserr));
        } else {
                caperr = cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp,
                    &aic);
                if (caperr == 0) {
                        if (addrinfo_compare(ais, aic))
                                result |= GETADDRINFO_AF_INET;
                        freeaddrinfo(ais);
                        freeaddrinfo(aic);
                }
        }

        hints.ai_family = AF_INET6;
        syserr = getaddrinfo("freebsd.org", "25", hintsp, &ais);
        if (syserr != 0) {
                fprintf(stderr,
                    "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n",
                    gai_strerror(syserr));
        } else {
                caperr = cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp,
                    &aic);
                if (caperr == 0) {
                        if (addrinfo_compare(ais, aic))
                                result |= GETADDRINFO_AF_INET6;
                        freeaddrinfo(ais);
                        freeaddrinfo(aic);
                }
        }

        /* XXX: hardcoded addresses for "google-public-dns-a.google.com". */
#define GOOGLE_DNS_IPV4 "8.8.8.8"
#define GOOGLE_DNS_IPV6 "2001:4860:4860::8888"

        inet_pton(AF_INET, GOOGLE_DNS_IPV4, &ip4);
        hps = gethostbyaddr(&ip4, sizeof(ip4), AF_INET);
        if (hps == NULL) {
                fprintf(stderr, "Unable to resolve %s.\n", GOOGLE_DNS_IPV4);
        } else {
                hpc = cap_gethostbyaddr(capdns, &ip4, sizeof(ip4), AF_INET);
                if (hostent_compare(hps, hpc))
                        result |= GETHOSTBYADDR_AF_INET;
        }

        inet_pton(AF_INET6, GOOGLE_DNS_IPV6, &ip6);
        hps = gethostbyaddr(&ip6, sizeof(ip6), AF_INET6);
        if (hps == NULL) {
                fprintf(stderr, "Unable to resolve %s.\n", GOOGLE_DNS_IPV6);
        } else {
                hpc = cap_gethostbyaddr(capdns, &ip6, sizeof(ip6), AF_INET6);
                if (hostent_compare(hps, hpc)) {
                        caperr = h_errno;
                        result |= GETHOSTBYADDR_AF_INET6;
                }
        }

        ATF_REQUIRE_MSG(result == expected,
            "expected 0x%x, got 0x%x", expected, result);
}

static cap_channel_t *
cap_dns_init(void)
{
        cap_channel_t *capcas, *capdns;

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

        capdns = cap_service_open(capcas, "system.dns");
        ATF_REQUIRE(capdns != NULL);

        cap_close(capcas);

        return (capdns);
}

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

        capdns = cap_dns_init();

        runtest(capdns,
            (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 |
             GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6 |
             GETADDRINFO_AF_UNSPEC | GETADDRINFO_AF_INET |
             GETADDRINFO_AF_INET6));

        cap_close(capdns);
}

ATF_TC(dns_all_limits);
ATF_TC_HEAD(dns_all_limits, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_all_limits, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        types[1] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0);
        families[0] = AF_INET;
        families[1] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0);
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, NULL, 0) == -1);
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, NULL, 0) == -1);

        runtest(capdns,
            (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 |
             GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6 |
             GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6));

        cap_close(capdns);
}

ATF_TC(dns_name_limit);
ATF_TC_HEAD(dns_name_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_name_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0);
        types[1] = "ADDR2NAME";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 2) == -1);
        types[0] = "ADDR2NAME";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 1) == -1);
        families[0] = AF_INET;
        families[1] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0);

        runtest(capdns,
            (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 |
            GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6));

        cap_close(capdns);
}

ATF_TC(dns_addr_limit);
ATF_TC_HEAD(dns_addr_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_addr_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0);
        types[1] = "NAME2ADDR";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 2) == -1);
        types[0] = "NAME2ADDR";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 1) == -1);
        families[0] = AF_INET;
        families[1] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0);

        runtest(capdns,
            (GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6));

        cap_close(capdns);
}

ATF_TC(dns_inet_limit);
ATF_TC_HEAD(dns_inet_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_inet_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        types[1] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0);
        families[0] = AF_INET;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0);
        families[1] = AF_INET6;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 2) == -1);
        families[0] = AF_INET6;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 1) == -1);

        runtest(capdns,
            (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYADDR_AF_INET |
            GETADDRINFO_AF_INET));

        cap_close(capdns);
}

ATF_TC(dns_inet6_limit);
ATF_TC_HEAD(dns_inet6_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_inet6_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        types[1] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0);
        families[0] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0);
        families[1] = AF_INET;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 2) == -1);
        families[0] = AF_INET;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 1) == -1);

        runtest(capdns,
            (GETHOSTBYNAME2_AF_INET6 | GETHOSTBYADDR_AF_INET6 |
            GETADDRINFO_AF_INET6));

        cap_close(capdns);
}

ATF_TC(dns_name_inet_limit);
ATF_TC_HEAD(dns_name_inet_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_name_inet_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        types[1] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0);
        families[0] = AF_INET;
        families[1] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0);
        types[0] = "NAME2ADDR";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0);
        types[1] = "ADDR2NAME";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 2) == -1);
        types[0] = "ADDR2NAME";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 1) == -1);
        families[0] = AF_INET;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0);
        families[1] = AF_INET6;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 2) == -1);
        families[0] = AF_INET6;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 1) == -1);

        runtest(capdns,
            (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETADDRINFO_AF_INET));

        cap_close(capdns);
}

ATF_TC(dns_name_inet6_limit);
ATF_TC_HEAD(dns_name_inet6_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_name_inet6_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        types[1] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0);
        families[0] = AF_INET6;
        families[1] = AF_INET;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0);
        types[0] = "NAME2ADDR";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0);
        types[1] = "ADDR2NAME";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 2) == -1);
        types[0] = "ADDR2NAME";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 1) == -1);
        families[0] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0);
        families[1] = AF_INET;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 2) == -1);
        families[0] = AF_INET;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 1) == -1);

        runtest(capdns,
            (GETHOSTBYNAME2_AF_INET6 | GETADDRINFO_AF_INET6));

        cap_close(capdns);
}

ATF_TC(dns_addr_inet_limit);
ATF_TC_HEAD(dns_addr_inet_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_addr_inet_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        types[1] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0);
        families[0] = AF_INET;
        families[1] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0);
        types[0] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0);
        types[1] = "NAME2ADDR";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 2) == -1);
        types[0] = "NAME2ADDR";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 1) == -1);
        families[0] = AF_INET;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0);
        families[1] = AF_INET6;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 2) == -1);
        families[0] = AF_INET6;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 1) == -1);

        runtest(capdns, GETHOSTBYADDR_AF_INET);

        cap_close(capdns);
}

ATF_TC(dns_addr_inet6_limit);
ATF_TC_HEAD(dns_addr_inet6_limit, tc)
{
        atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(dns_addr_inet6_limit, tc)
{
        cap_channel_t *capdns;
        const char *types[2];
        int families[2];

        capdns = cap_dns_init();

        types[0] = "NAME2ADDR";
        types[1] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0);
        families[0] = AF_INET6;
        families[1] = AF_INET;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0);
        types[0] = "ADDR2NAME";
        ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0);
        types[1] = "NAME2ADDR";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 2) == -1);
        types[0] = "NAME2ADDR";
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_type_limit(capdns, types, 1) == -1);
        families[0] = AF_INET6;
        ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0);
        families[1] = AF_INET;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 2) == -1);
        families[0] = AF_INET;
        ATF_REQUIRE_ERRNO(ENOTCAPABLE,
            cap_dns_family_limit(capdns, families, 1) == -1);

        runtest(capdns, GETHOSTBYADDR_AF_INET6);

        cap_close(capdns);
}

ATF_TP_ADD_TCS(tp)
{
        ATF_TP_ADD_TC(tp, dns_no_limits);
        ATF_TP_ADD_TC(tp, dns_all_limits);
        ATF_TP_ADD_TC(tp, dns_name_limit);
        ATF_TP_ADD_TC(tp, dns_addr_limit);
        ATF_TP_ADD_TC(tp, dns_inet_limit);
        ATF_TP_ADD_TC(tp, dns_inet6_limit);
        ATF_TP_ADD_TC(tp, dns_name_inet_limit);
        ATF_TP_ADD_TC(tp, dns_name_inet6_limit);
        ATF_TP_ADD_TC(tp, dns_addr_inet_limit);
        ATF_TP_ADD_TC(tp, dns_addr_inet6_limit);

        return atf_no_error();
}