15185a700Sflorian /*
25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian *
45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian * copyright notice and this permission notice appear in all copies.
75185a700Sflorian *
85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian */
165185a700Sflorian
175185a700Sflorian /* Reviewed: Thu Mar 16 14:06:44 PST 2000 by gson */
185185a700Sflorian
195185a700Sflorian /* RFC2671 */
205185a700Sflorian
215185a700Sflorian #ifndef RDATA_GENERIC_OPT_41_C
225185a700Sflorian #define RDATA_GENERIC_OPT_41_C
235185a700Sflorian
245185a700Sflorian static inline isc_result_t
totext_opt(ARGS_TOTEXT)255185a700Sflorian totext_opt(ARGS_TOTEXT) {
265185a700Sflorian isc_region_t r;
275185a700Sflorian isc_region_t or;
285185a700Sflorian uint16_t option;
295185a700Sflorian uint16_t length;
305185a700Sflorian char buf[sizeof("64000 64000")];
315185a700Sflorian
325185a700Sflorian /*
335185a700Sflorian * OPT records do not have a text format.
345185a700Sflorian */
355185a700Sflorian
365185a700Sflorian REQUIRE(rdata->type == dns_rdatatype_opt);
375185a700Sflorian
385185a700Sflorian dns_rdata_toregion(rdata, &r);
395185a700Sflorian while (r.length > 0) {
405185a700Sflorian option = uint16_fromregion(&r);
415185a700Sflorian isc_region_consume(&r, 2);
425185a700Sflorian length = uint16_fromregion(&r);
435185a700Sflorian isc_region_consume(&r, 2);
445185a700Sflorian snprintf(buf, sizeof(buf), "%u %u", option, length);
45*873f12b9Sflorian RETERR(isc_str_tobuffer(buf, target));
465185a700Sflorian INSIST(r.length >= length);
475185a700Sflorian if (length > 0) {
485185a700Sflorian if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
49*873f12b9Sflorian RETERR(isc_str_tobuffer(" (", target));
50*873f12b9Sflorian RETERR(isc_str_tobuffer(tctx->linebreak, target));
515185a700Sflorian or = r;
525185a700Sflorian or.length = length;
535185a700Sflorian if (tctx->width == 0) /* No splitting */
545185a700Sflorian RETERR(isc_base64_totext(&or, 60, "", target));
555185a700Sflorian else
565185a700Sflorian RETERR(isc_base64_totext(&or, tctx->width - 2,
575185a700Sflorian tctx->linebreak,
585185a700Sflorian target));
595185a700Sflorian isc_region_consume(&r, length);
605185a700Sflorian if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
61*873f12b9Sflorian RETERR(isc_str_tobuffer(" )", target));
625185a700Sflorian }
635185a700Sflorian if (r.length > 0)
64*873f12b9Sflorian RETERR(isc_str_tobuffer(" ", target));
655185a700Sflorian }
665185a700Sflorian
675185a700Sflorian return (ISC_R_SUCCESS);
685185a700Sflorian }
695185a700Sflorian
705185a700Sflorian static inline isc_result_t
fromwire_opt(ARGS_FROMWIRE)715185a700Sflorian fromwire_opt(ARGS_FROMWIRE) {
725185a700Sflorian isc_region_t sregion;
735185a700Sflorian isc_region_t tregion;
745185a700Sflorian uint16_t opt;
755185a700Sflorian uint16_t length;
765185a700Sflorian unsigned int total;
775185a700Sflorian
785185a700Sflorian REQUIRE(type == dns_rdatatype_opt);
795185a700Sflorian
805185a700Sflorian UNUSED(type);
815185a700Sflorian UNUSED(rdclass);
825185a700Sflorian UNUSED(dctx);
835185a700Sflorian UNUSED(options);
845185a700Sflorian
855185a700Sflorian isc_buffer_activeregion(source, &sregion);
865185a700Sflorian if (sregion.length == 0)
875185a700Sflorian return (ISC_R_SUCCESS);
885185a700Sflorian total = 0;
895185a700Sflorian while (sregion.length != 0) {
905185a700Sflorian if (sregion.length < 4)
915185a700Sflorian return (ISC_R_UNEXPECTEDEND);
925185a700Sflorian opt = uint16_fromregion(&sregion);
935185a700Sflorian isc_region_consume(&sregion, 2);
945185a700Sflorian length = uint16_fromregion(&sregion);
955185a700Sflorian isc_region_consume(&sregion, 2);
965185a700Sflorian total += 4;
975185a700Sflorian if (sregion.length < length)
985185a700Sflorian return (ISC_R_UNEXPECTEDEND);
995185a700Sflorian switch (opt) {
1005185a700Sflorian case DNS_OPT_CLIENT_SUBNET: {
1015185a700Sflorian uint16_t family;
1025185a700Sflorian uint8_t addrlen;
1035185a700Sflorian uint8_t scope;
1045185a700Sflorian uint8_t addrbytes;
1055185a700Sflorian
1065185a700Sflorian if (length < 4)
1075185a700Sflorian return (DNS_R_OPTERR);
1085185a700Sflorian family = uint16_fromregion(&sregion);
1095185a700Sflorian isc_region_consume(&sregion, 2);
1105185a700Sflorian addrlen = uint8_fromregion(&sregion);
1115185a700Sflorian isc_region_consume(&sregion, 1);
1125185a700Sflorian scope = uint8_fromregion(&sregion);
1135185a700Sflorian isc_region_consume(&sregion, 1);
1145185a700Sflorian
1155185a700Sflorian switch (family) {
1165185a700Sflorian case 0:
1175185a700Sflorian /*
1185185a700Sflorian * XXXMUKS: In queries and replies, if
1195185a700Sflorian * FAMILY is set to 0, SOURCE
1205185a700Sflorian * PREFIX-LENGTH and SCOPE PREFIX-LENGTH
1215185a700Sflorian * must be 0 and ADDRESS should not be
1225185a700Sflorian * present as the address and prefix
1235185a700Sflorian * lengths don't make sense because the
1245185a700Sflorian * family is unknown.
1255185a700Sflorian */
1265185a700Sflorian if (addrlen != 0U || scope != 0U)
1275185a700Sflorian return (DNS_R_OPTERR);
1285185a700Sflorian break;
1295185a700Sflorian case 1:
1305185a700Sflorian if (addrlen > 32U || scope > 32U)
1315185a700Sflorian return (DNS_R_OPTERR);
1325185a700Sflorian break;
1335185a700Sflorian case 2:
1345185a700Sflorian if (addrlen > 128U || scope > 128U)
1355185a700Sflorian return (DNS_R_OPTERR);
1365185a700Sflorian break;
1375185a700Sflorian default:
1385185a700Sflorian return (DNS_R_OPTERR);
1395185a700Sflorian }
1405185a700Sflorian addrbytes = (addrlen + 7) / 8;
1415185a700Sflorian if (addrbytes + 4 != length)
1425185a700Sflorian return (DNS_R_OPTERR);
1435185a700Sflorian
1445185a700Sflorian if (addrbytes != 0U && (addrlen % 8) != 0) {
1455185a700Sflorian uint8_t bits = ~0U << (8 - (addrlen % 8));
1465185a700Sflorian bits &= sregion.base[addrbytes - 1];
1475185a700Sflorian if (bits != sregion.base[addrbytes - 1])
1485185a700Sflorian return (DNS_R_OPTERR);
1495185a700Sflorian }
1505185a700Sflorian isc_region_consume(&sregion, addrbytes);
1515185a700Sflorian break;
1525185a700Sflorian }
1535185a700Sflorian case DNS_OPT_EXPIRE:
1545185a700Sflorian /*
1555185a700Sflorian * Request has zero length. Response is 32 bits.
1565185a700Sflorian */
1575185a700Sflorian if (length != 0 && length != 4)
1585185a700Sflorian return (DNS_R_OPTERR);
1595185a700Sflorian isc_region_consume(&sregion, length);
1605185a700Sflorian break;
1615185a700Sflorian case DNS_OPT_COOKIE:
1625185a700Sflorian if (length != 8 && (length < 16 || length > 40))
1635185a700Sflorian return (DNS_R_OPTERR);
1645185a700Sflorian isc_region_consume(&sregion, length);
1655185a700Sflorian break;
1665185a700Sflorian case DNS_OPT_KEY_TAG:
1675185a700Sflorian if (length == 0 || (length % 2) != 0)
1685185a700Sflorian return (DNS_R_OPTERR);
1695185a700Sflorian isc_region_consume(&sregion, length);
1705185a700Sflorian break;
1715185a700Sflorian default:
1725185a700Sflorian isc_region_consume(&sregion, length);
1735185a700Sflorian break;
1745185a700Sflorian }
1755185a700Sflorian total += length;
1765185a700Sflorian }
1775185a700Sflorian
1785185a700Sflorian isc_buffer_activeregion(source, &sregion);
1795185a700Sflorian isc_buffer_availableregion(target, &tregion);
1805185a700Sflorian if (tregion.length < total)
1815185a700Sflorian return (ISC_R_NOSPACE);
1825185a700Sflorian memmove(tregion.base, sregion.base, total);
1835185a700Sflorian isc_buffer_forward(source, total);
1845185a700Sflorian isc_buffer_add(target, total);
1855185a700Sflorian
1865185a700Sflorian return (ISC_R_SUCCESS);
1875185a700Sflorian }
1885185a700Sflorian
1895185a700Sflorian static inline isc_result_t
towire_opt(ARGS_TOWIRE)1905185a700Sflorian towire_opt(ARGS_TOWIRE) {
1915185a700Sflorian
1925185a700Sflorian REQUIRE(rdata->type == dns_rdatatype_opt);
1935185a700Sflorian
1945185a700Sflorian UNUSED(cctx);
1955185a700Sflorian
196637d8eb6Sflorian return (isc_mem_tobuffer(target, rdata->data, rdata->length));
1975185a700Sflorian }
1985185a700Sflorian
1995185a700Sflorian #endif /* RDATA_GENERIC_OPT_41_C */
200