root/usr/src/lib/libresolv2/common/nameser/ns_rdata.c
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "port_before.h"

#if __OpenBSD__
#include <sys/types.h>
#endif
#include <netinet/in.h>
#include <arpa/nameser.h>

#include <errno.h>
#include <resolv.h>
#include <string.h>

#include "port_after.h"

#define CONSUME_SRC \
        do { \
                rdata += n, rdlen -= n; \
        } while (0)

#define CONSUME_DST \
        do { \
                nrdata += n, nrdsiz -= n, nrdlen += n; \
        } while (0)

#define UNPACK_DNAME \
        do { \
                size_t t; \
                \
                if ((n = ns_name_unpack2(msg,eom,rdata,nrdata,nrdsiz,&t))<0) {\
                        errno = EMSGSIZE; \
                        return (-1); \
                } \
                CONSUME_SRC; \
                n = t; \
                CONSUME_DST; \
        } while (0)

#define UNPACK_SOME(x) \
        do { \
                n = (x); \
                if ((size_t)n > rdlen || (size_t)n > nrdsiz) { \
                        errno = EMSGSIZE; \
                        return (-1); \
                } \
                memcpy(nrdata, rdata, n); \
                CONSUME_SRC; CONSUME_DST; \
        } while (0)

#define UNPACK_REST(x) \
        do { \
                n = (x); \
                if ((size_t)n != rdlen) { \
                        errno = EMSGSIZE; \
                        return (-1); \
                } \
                memcpy(nrdata, rdata, n); \
                CONSUME_SRC; CONSUME_DST; \
        } while (0)

ssize_t
ns_rdata_unpack(const u_char *msg, const u_char *eom,
                ns_type type, const u_char *rdata, size_t rdlen,
                u_char *nrdata, size_t nrdsiz)
{
        size_t nrdlen = 0;
        int n;

        switch (type) {
        case ns_t_a:
                UNPACK_REST(NS_INADDRSZ);
                break;
        case ns_t_aaaa:
                UNPACK_REST(NS_IN6ADDRSZ);
                break;
        case ns_t_cname:
        case ns_t_mb:
        case ns_t_mg:
        case ns_t_mr:
        case ns_t_ns:
        case ns_t_ptr:
        case ns_t_dname:
                UNPACK_DNAME;
                break;
        case ns_t_soa:
                UNPACK_DNAME;
                UNPACK_DNAME;
                UNPACK_SOME(NS_INT32SZ * 5);
                break;
        case ns_t_mx:
        case ns_t_afsdb:
        case ns_t_rt:
                UNPACK_SOME(NS_INT16SZ);
                UNPACK_DNAME;
                break;
        case ns_t_px:
                UNPACK_SOME(NS_INT16SZ);
                UNPACK_DNAME;
                UNPACK_DNAME;
                break;
        case ns_t_srv:
                UNPACK_SOME(NS_INT16SZ * 3);
                UNPACK_DNAME;
                break;
        case ns_t_minfo:
        case ns_t_rp:
                UNPACK_DNAME;
                UNPACK_DNAME;
                break;
        default:
                UNPACK_SOME(rdlen);
                break;
        }
        if (rdlen > 0) {
                errno = EMSGSIZE;
                return (-1);
        }
        return (nrdlen);
}

#define EQUAL_CONSUME \
        do { \
                rdata1 += n, rdlen1 -= n; \
                rdata2 += n, rdlen2 -= n; \
        } while (0)

#define EQUAL_DNAME \
        do { \
                ssize_t n; \
                \
                if (rdlen1 != rdlen2) \
                        return (0); \
                n = ns_name_eq(rdata1, rdlen1, rdata2, rdlen2); \
                if (n <= 0) \
                        return (n); \
                n = rdlen1; \
                EQUAL_CONSUME; \
        } while (0)

#define EQUAL_SOME(x) \
        do { \
                size_t n = (x); \
                \
                if (n > rdlen1 || n > rdlen2) { \
                        errno = EMSGSIZE; \
                        return (-1); \
                } \
                if (memcmp(rdata1, rdata2, n) != 0) \
                        return (0); \
                EQUAL_CONSUME; \
        } while (0)

int
ns_rdata_equal(ns_type type,
               const u_char *rdata1, size_t rdlen1,
               const u_char *rdata2, size_t rdlen2)
{
        switch (type) {
        case ns_t_cname:
        case ns_t_mb:
        case ns_t_mg:
        case ns_t_mr:
        case ns_t_ns:
        case ns_t_ptr:
        case ns_t_dname:
                EQUAL_DNAME;
                break;
        case ns_t_soa:
                /* "There can be only one." --Highlander */
                break;
        case ns_t_mx:
        case ns_t_afsdb:
        case ns_t_rt:
                EQUAL_SOME(NS_INT16SZ);
                EQUAL_DNAME;
                break;
        case ns_t_px:
                EQUAL_SOME(NS_INT16SZ);
                EQUAL_DNAME;
                EQUAL_DNAME;
                break;
        case ns_t_srv:
                EQUAL_SOME(NS_INT16SZ * 3);
                EQUAL_DNAME;
                break;
        case ns_t_minfo:
        case ns_t_rp:
                EQUAL_DNAME;
                EQUAL_DNAME;
                break;
        default:
                EQUAL_SOME(rdlen1);
                break;
        }
        if (rdlen1 != 0 || rdlen2 != 0)
                return (0);
        return (1);
}

#define REFERS_DNAME \
        do { \
                int n; \
                \
                n = ns_name_eq(rdata, rdlen, nname, NS_MAXNNAME); \
                if (n < 0) \
                        return (-1); \
                if (n > 0) \
                        return (1); \
                n = dn_skipname(rdata, rdata + rdlen); \
                if (n < 0) \
                        return (-1); \
                CONSUME_SRC; \
        } while (0)

#define REFERS_SOME(x) \
        do { \
                size_t n = (x); \
                \
                if (n > rdlen) { \
                        errno = EMSGSIZE; \
                        return (-1); \
                } \
                CONSUME_SRC; \
        } while (0)

int
ns_rdata_refers(ns_type type,
                const u_char *rdata, size_t rdlen,
                const u_char *nname)
{
        switch (type) {
        case ns_t_cname:
        case ns_t_mb:
        case ns_t_mg:
        case ns_t_mr:
        case ns_t_ns:
        case ns_t_ptr:
        case ns_t_dname:
                REFERS_DNAME;
                break;
        case ns_t_soa:
                REFERS_DNAME;
                REFERS_DNAME;
                REFERS_SOME(NS_INT32SZ * 5);
                break;
        case ns_t_mx:
        case ns_t_afsdb:
        case ns_t_rt:
                REFERS_SOME(NS_INT16SZ);
                REFERS_DNAME;
                break;
        case ns_t_px:
                REFERS_SOME(NS_INT16SZ);
                REFERS_DNAME;
                REFERS_DNAME;
                break;
        case ns_t_srv:
                REFERS_SOME(NS_INT16SZ * 3);
                REFERS_DNAME;
                break;
        case ns_t_minfo:
        case ns_t_rp:
                REFERS_DNAME;
                REFERS_DNAME;
                break;
        default:
                REFERS_SOME(rdlen);
                break;
        }
        if (rdlen != 0) {
                errno = EMSGSIZE;
                return (-1);
        }
        return (0);
}