xref: /openbsd/usr.bin/dig/lib/dns/rdata/generic/opt_41.c (revision 76d0caae)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /* Reviewed: Thu Mar 16 14:06:44 PST 2000 by gson */
18 
19 /* RFC2671 */
20 
21 #ifndef RDATA_GENERIC_OPT_41_C
22 #define RDATA_GENERIC_OPT_41_C
23 
24 static inline isc_result_t
25 totext_opt(ARGS_TOTEXT) {
26 	isc_region_t r;
27 	isc_region_t or;
28 	uint16_t option;
29 	uint16_t length;
30 	char buf[sizeof("64000 64000")];
31 
32 	/*
33 	 * OPT records do not have a text format.
34 	 */
35 
36 	REQUIRE(rdata->type == dns_rdatatype_opt);
37 
38 	dns_rdata_toregion(rdata, &r);
39 	while (r.length > 0) {
40 		option = uint16_fromregion(&r);
41 		isc_region_consume(&r, 2);
42 		length = uint16_fromregion(&r);
43 		isc_region_consume(&r, 2);
44 		snprintf(buf, sizeof(buf), "%u %u", option, length);
45 		RETERR(isc_str_tobuffer(buf, target));
46 		INSIST(r.length >= length);
47 		if (length > 0) {
48 			if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
49 				RETERR(isc_str_tobuffer(" (", target));
50 			RETERR(isc_str_tobuffer(tctx->linebreak, target));
51 			or = r;
52 			or.length = length;
53 			if (tctx->width == 0)   /* No splitting */
54 				RETERR(isc_base64_totext(&or, 60, "", target));
55 			else
56 				RETERR(isc_base64_totext(&or, tctx->width - 2,
57 							 tctx->linebreak,
58 							 target));
59 			isc_region_consume(&r, length);
60 			if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
61 				RETERR(isc_str_tobuffer(" )", target));
62 		}
63 		if (r.length > 0)
64 			RETERR(isc_str_tobuffer(" ", target));
65 	}
66 
67 	return (ISC_R_SUCCESS);
68 }
69 
70 static inline isc_result_t
71 fromwire_opt(ARGS_FROMWIRE) {
72 	isc_region_t sregion;
73 	isc_region_t tregion;
74 	uint16_t opt;
75 	uint16_t length;
76 	unsigned int total;
77 
78 	REQUIRE(type == dns_rdatatype_opt);
79 
80 	UNUSED(type);
81 	UNUSED(rdclass);
82 	UNUSED(dctx);
83 	UNUSED(options);
84 
85 	isc_buffer_activeregion(source, &sregion);
86 	if (sregion.length == 0)
87 		return (ISC_R_SUCCESS);
88 	total = 0;
89 	while (sregion.length != 0) {
90 		if (sregion.length < 4)
91 			return (ISC_R_UNEXPECTEDEND);
92 		opt = uint16_fromregion(&sregion);
93 		isc_region_consume(&sregion, 2);
94 		length = uint16_fromregion(&sregion);
95 		isc_region_consume(&sregion, 2);
96 		total += 4;
97 		if (sregion.length < length)
98 			return (ISC_R_UNEXPECTEDEND);
99 		switch (opt) {
100 		case DNS_OPT_CLIENT_SUBNET: {
101 			uint16_t family;
102 			uint8_t addrlen;
103 			uint8_t scope;
104 			uint8_t addrbytes;
105 
106 			if (length < 4)
107 				return (DNS_R_OPTERR);
108 			family = uint16_fromregion(&sregion);
109 			isc_region_consume(&sregion, 2);
110 			addrlen = uint8_fromregion(&sregion);
111 			isc_region_consume(&sregion, 1);
112 			scope = uint8_fromregion(&sregion);
113 			isc_region_consume(&sregion, 1);
114 
115 			switch (family) {
116 			case 0:
117 				/*
118 				 * XXXMUKS: In queries and replies, if
119 				 * FAMILY is set to 0, SOURCE
120 				 * PREFIX-LENGTH and SCOPE PREFIX-LENGTH
121 				 * must be 0 and ADDRESS should not be
122 				 * present as the address and prefix
123 				 * lengths don't make sense because the
124 				 * family is unknown.
125 				 */
126 				if (addrlen != 0U || scope != 0U)
127 					return (DNS_R_OPTERR);
128 				break;
129 			case 1:
130 				if (addrlen > 32U || scope > 32U)
131 					return (DNS_R_OPTERR);
132 				break;
133 			case 2:
134 				if (addrlen > 128U || scope > 128U)
135 					return (DNS_R_OPTERR);
136 				break;
137 			default:
138 				return (DNS_R_OPTERR);
139 			}
140 			addrbytes = (addrlen + 7) / 8;
141 			if (addrbytes + 4 != length)
142 				return (DNS_R_OPTERR);
143 
144 			if (addrbytes != 0U && (addrlen % 8) != 0) {
145 				uint8_t bits = ~0U << (8 - (addrlen % 8));
146 				bits &= sregion.base[addrbytes - 1];
147 				if (bits != sregion.base[addrbytes - 1])
148 					return (DNS_R_OPTERR);
149 			}
150 			isc_region_consume(&sregion, addrbytes);
151 			break;
152 		}
153 		case DNS_OPT_EXPIRE:
154 			/*
155 			 * Request has zero length.  Response is 32 bits.
156 			 */
157 			if (length != 0 && length != 4)
158 				return (DNS_R_OPTERR);
159 			isc_region_consume(&sregion, length);
160 			break;
161 		case DNS_OPT_COOKIE:
162 			if (length != 8 && (length < 16 || length > 40))
163 				return (DNS_R_OPTERR);
164 			isc_region_consume(&sregion, length);
165 			break;
166 		case DNS_OPT_KEY_TAG:
167 			if (length == 0 || (length % 2) != 0)
168 				return (DNS_R_OPTERR);
169 			isc_region_consume(&sregion, length);
170 			break;
171 		default:
172 			isc_region_consume(&sregion, length);
173 			break;
174 		}
175 		total += length;
176 	}
177 
178 	isc_buffer_activeregion(source, &sregion);
179 	isc_buffer_availableregion(target, &tregion);
180 	if (tregion.length < total)
181 		return (ISC_R_NOSPACE);
182 	memmove(tregion.base, sregion.base, total);
183 	isc_buffer_forward(source, total);
184 	isc_buffer_add(target, total);
185 
186 	return (ISC_R_SUCCESS);
187 }
188 
189 static inline isc_result_t
190 towire_opt(ARGS_TOWIRE) {
191 
192 	REQUIRE(rdata->type == dns_rdatatype_opt);
193 
194 	UNUSED(cctx);
195 
196 	return (isc_mem_tobuffer(target, rdata->data, rdata->length));
197 }
198 
199 #endif	/* RDATA_GENERIC_OPT_41_C */
200