1 /*	$NetBSD: loc_29.c,v 1.9 2023/01/25 21:43:30 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /* RFC1876 */
17 
18 #ifndef RDATA_GENERIC_LOC_29_C
19 #define RDATA_GENERIC_LOC_29_C
20 
21 #define RRTYPE_LOC_ATTRIBUTES (0)
22 
23 static isc_result_t
loc_getdecimal(const char * str,unsigned long max,size_t precision,char units,unsigned long * valuep)24 loc_getdecimal(const char *str, unsigned long max, size_t precision, char units,
25 	       unsigned long *valuep) {
26 	bool ok;
27 	char *e;
28 	size_t i;
29 	long tmp;
30 	unsigned long value;
31 
32 	value = strtoul(str, &e, 10);
33 	if (*e != 0 && *e != '.' && *e != units) {
34 		return (DNS_R_SYNTAX);
35 	}
36 	if (value > max) {
37 		return (ISC_R_RANGE);
38 	}
39 	ok = e != str;
40 	if (*e == '.') {
41 		e++;
42 		for (i = 0; i < precision; i++) {
43 			if (*e == 0 || *e == units) {
44 				break;
45 			}
46 			if ((tmp = decvalue(*e++)) < 0) {
47 				return (DNS_R_SYNTAX);
48 			}
49 			ok = true;
50 			value *= 10;
51 			value += tmp;
52 		}
53 		for (; i < precision; i++) {
54 			value *= 10;
55 		}
56 	} else {
57 		for (i = 0; i < precision; i++) {
58 			value *= 10;
59 		}
60 	}
61 	if (*e != 0 && *e == units) {
62 		e++;
63 	}
64 	if (!ok || *e != 0) {
65 		return (DNS_R_SYNTAX);
66 	}
67 	*valuep = value;
68 	return (ISC_R_SUCCESS);
69 }
70 
71 static isc_result_t
loc_getprecision(const char * str,unsigned char * valuep)72 loc_getprecision(const char *str, unsigned char *valuep) {
73 	unsigned long poweroften[8] = { 1,     10,     100,	1000,
74 					10000, 100000, 1000000, 10000000 };
75 	unsigned long m, cm;
76 	bool ok;
77 	char *e;
78 	size_t i;
79 	long tmp;
80 	int man;
81 	int exp;
82 
83 	m = strtoul(str, &e, 10);
84 	if (*e != 0 && *e != '.' && *e != 'm') {
85 		return (DNS_R_SYNTAX);
86 	}
87 	if (m > 90000000) {
88 		return (ISC_R_RANGE);
89 	}
90 	cm = 0;
91 	ok = e != str;
92 	if (*e == '.') {
93 		e++;
94 		for (i = 0; i < 2; i++) {
95 			if (*e == 0 || *e == 'm') {
96 				break;
97 			}
98 			if ((tmp = decvalue(*e++)) < 0) {
99 				return (DNS_R_SYNTAX);
100 			}
101 			ok = true;
102 			cm *= 10;
103 			cm += tmp;
104 		}
105 		for (; i < 2; i++) {
106 			cm *= 10;
107 		}
108 	}
109 	if (*e == 'm') {
110 		e++;
111 	}
112 	if (!ok || *e != 0) {
113 		return (DNS_R_SYNTAX);
114 	}
115 
116 	/*
117 	 * We don't just multiply out as we will overflow.
118 	 */
119 	if (m > 0) {
120 		for (exp = 0; exp < 7; exp++) {
121 			if (m < poweroften[exp + 1]) {
122 				break;
123 			}
124 		}
125 		man = m / poweroften[exp];
126 		exp += 2;
127 	} else if (cm >= 10) {
128 		man = cm / 10;
129 		exp = 1;
130 	} else {
131 		man = cm;
132 		exp = 0;
133 	}
134 	*valuep = (man << 4) + exp;
135 	return (ISC_R_SUCCESS);
136 }
137 
138 static isc_result_t
get_degrees(isc_lex_t * lexer,isc_token_t * token,unsigned long * d)139 get_degrees(isc_lex_t *lexer, isc_token_t *token, unsigned long *d) {
140 	RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_number,
141 				      false));
142 	*d = token->value.as_ulong;
143 
144 	return (ISC_R_SUCCESS);
145 }
146 
147 static isc_result_t
check_coordinate(unsigned long d,unsigned long m,unsigned long s,unsigned long maxd)148 check_coordinate(unsigned long d, unsigned long m, unsigned long s,
149 		 unsigned long maxd) {
150 	if (d > maxd || m > 59U) {
151 		return (ISC_R_RANGE);
152 	}
153 	if (d == maxd && (m != 0 || s != 0)) {
154 		return (ISC_R_RANGE);
155 	}
156 
157 	return (ISC_R_SUCCESS);
158 }
159 
160 static isc_result_t
get_minutes(isc_lex_t * lexer,isc_token_t * token,unsigned long * m)161 get_minutes(isc_lex_t *lexer, isc_token_t *token, unsigned long *m) {
162 	RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_number,
163 				      false));
164 
165 	*m = token->value.as_ulong;
166 
167 	return (ISC_R_SUCCESS);
168 }
169 
170 static isc_result_t
get_seconds(isc_lex_t * lexer,isc_token_t * token,unsigned long * s)171 get_seconds(isc_lex_t *lexer, isc_token_t *token, unsigned long *s) {
172 	RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_string,
173 				      false));
174 	RETERR(loc_getdecimal(DNS_AS_STR(*token), 59, 3, '\0', s));
175 
176 	return (ISC_R_SUCCESS);
177 }
178 
179 static isc_result_t
get_direction(isc_lex_t * lexer,isc_token_t * token,const char * directions,int * direction)180 get_direction(isc_lex_t *lexer, isc_token_t *token, const char *directions,
181 	      int *direction) {
182 	RETERR(isc_lex_getmastertoken(lexer, token, isc_tokentype_string,
183 				      false));
184 	if (DNS_AS_STR(*token)[0] == directions[1] &&
185 	    DNS_AS_STR(*token)[1] == 0)
186 	{
187 		*direction = DNS_AS_STR(*token)[0];
188 		return (ISC_R_SUCCESS);
189 	}
190 
191 	if (DNS_AS_STR(*token)[0] == directions[0] &&
192 	    DNS_AS_STR(*token)[1] == 0)
193 	{
194 		*direction = DNS_AS_STR(*token)[0];
195 		return (ISC_R_SUCCESS);
196 	}
197 
198 	*direction = 0;
199 	isc_lex_ungettoken(lexer, token);
200 	return (ISC_R_SUCCESS);
201 }
202 
203 static isc_result_t
loc_getcoordinate(isc_lex_t * lexer,unsigned long * dp,unsigned long * mp,unsigned long * sp,const char * directions,int * directionp,unsigned long maxd)204 loc_getcoordinate(isc_lex_t *lexer, unsigned long *dp, unsigned long *mp,
205 		  unsigned long *sp, const char *directions, int *directionp,
206 		  unsigned long maxd) {
207 	isc_result_t result = ISC_R_SUCCESS;
208 	isc_token_t token;
209 	unsigned long d, m, s;
210 	int direction = 0;
211 
212 	m = 0;
213 	s = 0;
214 
215 	/*
216 	 * Degrees.
217 	 */
218 	RETERR(get_degrees(lexer, &token, &d));
219 	RETTOK(check_coordinate(d, m, s, maxd));
220 
221 	/*
222 	 * Minutes.
223 	 */
224 	RETERR(get_direction(lexer, &token, directions, &direction));
225 	if (direction > 0) {
226 		goto done;
227 	}
228 
229 	RETERR(get_minutes(lexer, &token, &m));
230 	RETTOK(check_coordinate(d, m, s, maxd));
231 
232 	/*
233 	 * Seconds.
234 	 */
235 	RETERR(get_direction(lexer, &token, directions, &direction));
236 	if (direction > 0) {
237 		goto done;
238 	}
239 
240 	result = get_seconds(lexer, &token, &s);
241 	if (result == ISC_R_RANGE || result == DNS_R_SYNTAX) {
242 		RETTOK(result);
243 	}
244 	RETERR(result);
245 	RETTOK(check_coordinate(d, m, s, maxd));
246 
247 	/*
248 	 * Direction.
249 	 */
250 	RETERR(get_direction(lexer, &token, directions, &direction));
251 	if (direction == 0) {
252 		RETERR(DNS_R_SYNTAX);
253 	}
254 done:
255 
256 	*directionp = direction;
257 	*dp = d;
258 	*mp = m;
259 	*sp = s;
260 
261 	return (ISC_R_SUCCESS);
262 }
263 
264 static isc_result_t
loc_getlatitude(isc_lex_t * lexer,unsigned long * latitude)265 loc_getlatitude(isc_lex_t *lexer, unsigned long *latitude) {
266 	unsigned long d1 = 0, m1 = 0, s1 = 0;
267 	int direction = 0;
268 
269 	RETERR(loc_getcoordinate(lexer, &d1, &m1, &s1, "SN", &direction, 90U));
270 
271 	switch (direction) {
272 	case 'N':
273 		*latitude = 0x80000000 + (d1 * 3600 + m1 * 60) * 1000 + s1;
274 		break;
275 	case 'S':
276 		*latitude = 0x80000000 - (d1 * 3600 + m1 * 60) * 1000 - s1;
277 		break;
278 	default:
279 		UNREACHABLE();
280 	}
281 
282 	return (ISC_R_SUCCESS);
283 }
284 
285 static isc_result_t
loc_getlongitude(isc_lex_t * lexer,unsigned long * longitude)286 loc_getlongitude(isc_lex_t *lexer, unsigned long *longitude) {
287 	unsigned long d2 = 0, m2 = 0, s2 = 0;
288 	int direction = 0;
289 
290 	RETERR(loc_getcoordinate(lexer, &d2, &m2, &s2, "WE", &direction, 180U));
291 
292 	switch (direction) {
293 	case 'E':
294 		*longitude = 0x80000000 + (d2 * 3600 + m2 * 60) * 1000 + s2;
295 		break;
296 	case 'W':
297 		*longitude = 0x80000000 - (d2 * 3600 + m2 * 60) * 1000 - s2;
298 		break;
299 	default:
300 		UNREACHABLE();
301 	}
302 
303 	return (ISC_R_SUCCESS);
304 }
305 
306 static isc_result_t
loc_getaltitude(isc_lex_t * lexer,unsigned long * altitude)307 loc_getaltitude(isc_lex_t *lexer, unsigned long *altitude) {
308 	isc_token_t token;
309 	unsigned long cm;
310 	const char *str;
311 
312 	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
313 				      false));
314 	str = DNS_AS_STR(token);
315 	if (DNS_AS_STR(token)[0] == '-') {
316 		RETTOK(loc_getdecimal(str + 1, 100000, 2, 'm', &cm));
317 		if (cm > 10000000UL) {
318 			RETTOK(ISC_R_RANGE);
319 		}
320 		*altitude = 10000000 - cm;
321 	} else {
322 		RETTOK(loc_getdecimal(str, 42849672, 2, 'm', &cm));
323 		if (cm > 4284967295UL) {
324 			RETTOK(ISC_R_RANGE);
325 		}
326 		*altitude = 10000000 + cm;
327 	}
328 
329 	return (ISC_R_SUCCESS);
330 }
331 
332 static isc_result_t
loc_getoptionalprecision(isc_lex_t * lexer,unsigned char * valuep)333 loc_getoptionalprecision(isc_lex_t *lexer, unsigned char *valuep) {
334 	isc_token_t token;
335 
336 	RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
337 				      true));
338 	if (token.type == isc_tokentype_eol || token.type == isc_tokentype_eof)
339 	{
340 		isc_lex_ungettoken(lexer, &token);
341 		return (ISC_R_NOMORE);
342 	}
343 	RETTOK(loc_getprecision(DNS_AS_STR(token), valuep));
344 
345 	return (ISC_R_SUCCESS);
346 }
347 
348 static isc_result_t
loc_getsize(isc_lex_t * lexer,unsigned char * sizep)349 loc_getsize(isc_lex_t *lexer, unsigned char *sizep) {
350 	return (loc_getoptionalprecision(lexer, sizep));
351 }
352 
353 static isc_result_t
loc_gethorizontalprecision(isc_lex_t * lexer,unsigned char * hpp)354 loc_gethorizontalprecision(isc_lex_t *lexer, unsigned char *hpp) {
355 	return (loc_getoptionalprecision(lexer, hpp));
356 }
357 
358 static isc_result_t
loc_getverticalprecision(isc_lex_t * lexer,unsigned char * vpp)359 loc_getverticalprecision(isc_lex_t *lexer, unsigned char *vpp) {
360 	return (loc_getoptionalprecision(lexer, vpp));
361 }
362 
363 /* The LOC record is expressed in a master file in the following format:
364  *
365  * <owner> <TTL> <class> LOC ( d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]]
366  *                             {"E"|"W"} alt["m"] [siz["m"] [hp["m"]
367  *                             [vp["m"]]]] )
368  *
369  * (The parentheses are used for multi-line data as specified in [RFC
370  * 1035] section 5.1.)
371  *
372  * where:
373  *
374  *     d1:     [0 .. 90]            (degrees latitude)
375  *     d2:     [0 .. 180]           (degrees longitude)
376  *     m1, m2: [0 .. 59]            (minutes latitude/longitude)
377  *     s1, s2: [0 .. 59.999]        (seconds latitude/longitude)
378  *     alt:    [-100000.00 .. 42849672.95] BY .01 (altitude in meters)
379  *     siz, hp, vp: [0 .. 90000000.00] (size/precision in meters)
380  *
381  * If omitted, minutes and seconds default to zero, size defaults to 1m,
382  * horizontal precision defaults to 10000m, and vertical precision
383  * defaults to 10m.  These defaults are chosen to represent typical
384  * ZIP/postal code area sizes, since it is often easy to find
385  * approximate geographical location by ZIP/postal code.
386  */
387 static isc_result_t
fromtext_loc(ARGS_FROMTEXT)388 fromtext_loc(ARGS_FROMTEXT) {
389 	isc_result_t result = ISC_R_SUCCESS;
390 	unsigned long latitude = 0;
391 	unsigned long longitude = 0;
392 	unsigned long altitude = 0;
393 	unsigned char size = 0x12; /* Default: 1.00m */
394 	unsigned char hp = 0x16;   /* Default: 10000.00 m */
395 	unsigned char vp = 0x13;   /* Default: 10.00 m */
396 	unsigned char version = 0;
397 
398 	REQUIRE(type == dns_rdatatype_loc);
399 
400 	UNUSED(type);
401 	UNUSED(rdclass);
402 	UNUSED(origin);
403 	UNUSED(options);
404 	UNUSED(callbacks);
405 
406 	RETERR(loc_getlatitude(lexer, &latitude));
407 	RETERR(loc_getlongitude(lexer, &longitude));
408 	RETERR(loc_getaltitude(lexer, &altitude));
409 	result = loc_getsize(lexer, &size);
410 	if (result == ISC_R_NOMORE) {
411 		result = ISC_R_SUCCESS;
412 		goto encode;
413 	}
414 	RETERR(result);
415 	result = loc_gethorizontalprecision(lexer, &hp);
416 	if (result == ISC_R_NOMORE) {
417 		result = ISC_R_SUCCESS;
418 		goto encode;
419 	}
420 	RETERR(result);
421 	result = loc_getverticalprecision(lexer, &vp);
422 	if (result == ISC_R_NOMORE) {
423 		result = ISC_R_SUCCESS;
424 		goto encode;
425 	}
426 	RETERR(result);
427 encode:
428 	RETERR(mem_tobuffer(target, &version, 1));
429 	RETERR(mem_tobuffer(target, &size, 1));
430 	RETERR(mem_tobuffer(target, &hp, 1));
431 	RETERR(mem_tobuffer(target, &vp, 1));
432 
433 	RETERR(uint32_tobuffer(latitude, target));
434 	RETERR(uint32_tobuffer(longitude, target));
435 	RETERR(uint32_tobuffer(altitude, target));
436 
437 	return (result);
438 }
439 
440 static isc_result_t
totext_loc(ARGS_TOTEXT)441 totext_loc(ARGS_TOTEXT) {
442 	int d1, m1, s1, fs1;
443 	int d2, m2, s2, fs2;
444 	unsigned long latitude;
445 	unsigned long longitude;
446 	unsigned long altitude;
447 	bool north;
448 	bool east;
449 	bool below;
450 	isc_region_t sr;
451 	char sbuf[sizeof("90000000m")];
452 	char hbuf[sizeof("90000000m")];
453 	char vbuf[sizeof("90000000m")];
454 	/* "89 59 59.999 N 179 59 59.999 E " */
455 	/* "-42849672.95m 90000000m 90000000m 90000000m"; */
456 	char buf[8 * 6 + 12 * 1 + 2 * 10 + sizeof(sbuf) + sizeof(hbuf) +
457 		 sizeof(vbuf)];
458 	unsigned char size, hp, vp;
459 	unsigned long poweroften[8] = { 1,     10,     100,	1000,
460 					10000, 100000, 1000000, 10000000 };
461 
462 	UNUSED(tctx);
463 
464 	REQUIRE(rdata->type == dns_rdatatype_loc);
465 	REQUIRE(rdata->length != 0);
466 
467 	dns_rdata_toregion(rdata, &sr);
468 
469 	if (sr.base[0] != 0) {
470 		return (ISC_R_NOTIMPLEMENTED);
471 	}
472 
473 	REQUIRE(rdata->length == 16);
474 
475 	size = sr.base[1];
476 	INSIST((size & 0x0f) < 10 && (size >> 4) < 10);
477 	if ((size & 0x0f) > 1) {
478 		snprintf(sbuf, sizeof(sbuf), "%lum",
479 			 (size >> 4) * poweroften[(size & 0x0f) - 2]);
480 	} else {
481 		snprintf(sbuf, sizeof(sbuf), "0.%02lum",
482 			 (size >> 4) * poweroften[(size & 0x0f)]);
483 	}
484 	hp = sr.base[2];
485 	INSIST((hp & 0x0f) < 10 && (hp >> 4) < 10);
486 	if ((hp & 0x0f) > 1) {
487 		snprintf(hbuf, sizeof(hbuf), "%lum",
488 			 (hp >> 4) * poweroften[(hp & 0x0f) - 2]);
489 	} else {
490 		snprintf(hbuf, sizeof(hbuf), "0.%02lum",
491 			 (hp >> 4) * poweroften[(hp & 0x0f)]);
492 	}
493 	vp = sr.base[3];
494 	INSIST((vp & 0x0f) < 10 && (vp >> 4) < 10);
495 	if ((vp & 0x0f) > 1) {
496 		snprintf(vbuf, sizeof(vbuf), "%lum",
497 			 (vp >> 4) * poweroften[(vp & 0x0f) - 2]);
498 	} else {
499 		snprintf(vbuf, sizeof(vbuf), "0.%02lum",
500 			 (vp >> 4) * poweroften[(vp & 0x0f)]);
501 	}
502 	isc_region_consume(&sr, 4);
503 
504 	latitude = uint32_fromregion(&sr);
505 	isc_region_consume(&sr, 4);
506 	if (latitude >= 0x80000000) {
507 		north = true;
508 		latitude -= 0x80000000;
509 	} else {
510 		north = false;
511 		latitude = 0x80000000 - latitude;
512 	}
513 	fs1 = (int)(latitude % 1000);
514 	latitude /= 1000;
515 	s1 = (int)(latitude % 60);
516 	latitude /= 60;
517 	m1 = (int)(latitude % 60);
518 	latitude /= 60;
519 	d1 = (int)latitude;
520 	INSIST(latitude <= 90U);
521 
522 	longitude = uint32_fromregion(&sr);
523 	isc_region_consume(&sr, 4);
524 	if (longitude >= 0x80000000) {
525 		east = true;
526 		longitude -= 0x80000000;
527 	} else {
528 		east = false;
529 		longitude = 0x80000000 - longitude;
530 	}
531 	fs2 = (int)(longitude % 1000);
532 	longitude /= 1000;
533 	s2 = (int)(longitude % 60);
534 	longitude /= 60;
535 	m2 = (int)(longitude % 60);
536 	longitude /= 60;
537 	d2 = (int)longitude;
538 	INSIST(longitude <= 180U);
539 
540 	altitude = uint32_fromregion(&sr);
541 	isc_region_consume(&sr, 4);
542 	if (altitude < 10000000U) {
543 		below = true;
544 		altitude = 10000000 - altitude;
545 	} else {
546 		below = false;
547 		altitude -= 10000000;
548 	}
549 
550 	snprintf(buf, sizeof(buf),
551 		 "%d %d %d.%03d %s %d %d %d.%03d %s %s%lu.%02lum %s %s %s", d1,
552 		 m1, s1, fs1, north ? "N" : "S", d2, m2, s2, fs2,
553 		 east ? "E" : "W", below ? "-" : "", altitude / 100,
554 		 altitude % 100, sbuf, hbuf, vbuf);
555 
556 	return (str_totext(buf, target));
557 }
558 
559 static isc_result_t
fromwire_loc(ARGS_FROMWIRE)560 fromwire_loc(ARGS_FROMWIRE) {
561 	isc_region_t sr;
562 	unsigned char c;
563 	unsigned long latitude;
564 	unsigned long longitude;
565 
566 	REQUIRE(type == dns_rdatatype_loc);
567 
568 	UNUSED(type);
569 	UNUSED(rdclass);
570 	UNUSED(dctx);
571 	UNUSED(options);
572 
573 	isc_buffer_activeregion(source, &sr);
574 	if (sr.length < 1) {
575 		return (ISC_R_UNEXPECTEDEND);
576 	}
577 	if (sr.base[0] != 0) {
578 		/* Treat as unknown. */
579 		isc_buffer_forward(source, sr.length);
580 		return (mem_tobuffer(target, sr.base, sr.length));
581 	}
582 	if (sr.length < 16) {
583 		return (ISC_R_UNEXPECTEDEND);
584 	}
585 
586 	/*
587 	 * Size.
588 	 */
589 	c = sr.base[1];
590 	if (c != 0) {
591 		if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 ||
592 		    ((c >> 4) & 0xf) == 0)
593 		{
594 			return (ISC_R_RANGE);
595 
596 			/*
597 			 * Horizontal precision.
598 			 */
599 		}
600 	}
601 
602 	/*
603 	 * Horizontal precision.
604 	 */
605 	c = sr.base[2];
606 	if (c != 0) {
607 		if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 ||
608 		    ((c >> 4) & 0xf) == 0)
609 		{
610 			return (ISC_R_RANGE);
611 
612 			/*
613 			 * Vertical precision.
614 			 */
615 		}
616 	}
617 
618 	/*
619 	 * Vertical precision.
620 	 */
621 	c = sr.base[3];
622 	if (c != 0) {
623 		if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 ||
624 		    ((c >> 4) & 0xf) == 0)
625 		{
626 			return (ISC_R_RANGE);
627 		}
628 	}
629 	isc_region_consume(&sr, 4);
630 
631 	/*
632 	 * Latitude.
633 	 */
634 	latitude = uint32_fromregion(&sr);
635 	if (latitude < (0x80000000UL - 90 * 3600000) ||
636 	    latitude > (0x80000000UL + 90 * 3600000))
637 	{
638 		return (ISC_R_RANGE);
639 	}
640 	isc_region_consume(&sr, 4);
641 
642 	/*
643 	 * Longitude.
644 	 */
645 	longitude = uint32_fromregion(&sr);
646 	if (longitude < (0x80000000UL - 180 * 3600000) ||
647 	    longitude > (0x80000000UL + 180 * 3600000))
648 	{
649 		return (ISC_R_RANGE);
650 	}
651 
652 	/*
653 	 * Altitude.
654 	 * All values possible.
655 	 */
656 
657 	isc_buffer_activeregion(source, &sr);
658 	isc_buffer_forward(source, 16);
659 	return (mem_tobuffer(target, sr.base, 16));
660 }
661 
662 static isc_result_t
towire_loc(ARGS_TOWIRE)663 towire_loc(ARGS_TOWIRE) {
664 	UNUSED(cctx);
665 
666 	REQUIRE(rdata->type == dns_rdatatype_loc);
667 	REQUIRE(rdata->length != 0);
668 
669 	return (mem_tobuffer(target, rdata->data, rdata->length));
670 }
671 
672 static int
compare_loc(ARGS_COMPARE)673 compare_loc(ARGS_COMPARE) {
674 	isc_region_t r1;
675 	isc_region_t r2;
676 
677 	REQUIRE(rdata1->type == rdata2->type);
678 	REQUIRE(rdata1->rdclass == rdata2->rdclass);
679 	REQUIRE(rdata1->type == dns_rdatatype_loc);
680 	REQUIRE(rdata1->length != 0);
681 	REQUIRE(rdata2->length != 0);
682 
683 	dns_rdata_toregion(rdata1, &r1);
684 	dns_rdata_toregion(rdata2, &r2);
685 	return (isc_region_compare(&r1, &r2));
686 }
687 
688 static isc_result_t
fromstruct_loc(ARGS_FROMSTRUCT)689 fromstruct_loc(ARGS_FROMSTRUCT) {
690 	dns_rdata_loc_t *loc = source;
691 	uint8_t c;
692 
693 	REQUIRE(type == dns_rdatatype_loc);
694 	REQUIRE(loc != NULL);
695 	REQUIRE(loc->common.rdtype == type);
696 	REQUIRE(loc->common.rdclass == rdclass);
697 
698 	UNUSED(type);
699 	UNUSED(rdclass);
700 
701 	if (loc->v.v0.version != 0) {
702 		return (ISC_R_NOTIMPLEMENTED);
703 	}
704 	RETERR(uint8_tobuffer(loc->v.v0.version, target));
705 
706 	c = loc->v.v0.size;
707 	if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 || ((c >> 4) & 0xf) == 0) {
708 		return (ISC_R_RANGE);
709 	}
710 	RETERR(uint8_tobuffer(loc->v.v0.size, target));
711 
712 	c = loc->v.v0.horizontal;
713 	if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 || ((c >> 4) & 0xf) == 0) {
714 		return (ISC_R_RANGE);
715 	}
716 	RETERR(uint8_tobuffer(loc->v.v0.horizontal, target));
717 
718 	c = loc->v.v0.vertical;
719 	if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 || ((c >> 4) & 0xf) == 0) {
720 		return (ISC_R_RANGE);
721 	}
722 	RETERR(uint8_tobuffer(loc->v.v0.vertical, target));
723 
724 	if (loc->v.v0.latitude < (0x80000000UL - 90 * 3600000) ||
725 	    loc->v.v0.latitude > (0x80000000UL + 90 * 3600000))
726 	{
727 		return (ISC_R_RANGE);
728 	}
729 	RETERR(uint32_tobuffer(loc->v.v0.latitude, target));
730 
731 	if (loc->v.v0.longitude < (0x80000000UL - 180 * 3600000) ||
732 	    loc->v.v0.longitude > (0x80000000UL + 180 * 3600000))
733 	{
734 		return (ISC_R_RANGE);
735 	}
736 	RETERR(uint32_tobuffer(loc->v.v0.longitude, target));
737 	return (uint32_tobuffer(loc->v.v0.altitude, target));
738 }
739 
740 static isc_result_t
tostruct_loc(ARGS_TOSTRUCT)741 tostruct_loc(ARGS_TOSTRUCT) {
742 	dns_rdata_loc_t *loc = target;
743 	isc_region_t r;
744 	uint8_t version;
745 
746 	REQUIRE(rdata->type == dns_rdatatype_loc);
747 	REQUIRE(loc != NULL);
748 	REQUIRE(rdata->length != 0);
749 
750 	UNUSED(mctx);
751 
752 	dns_rdata_toregion(rdata, &r);
753 	version = uint8_fromregion(&r);
754 	if (version != 0) {
755 		return (ISC_R_NOTIMPLEMENTED);
756 	}
757 
758 	loc->common.rdclass = rdata->rdclass;
759 	loc->common.rdtype = rdata->type;
760 	ISC_LINK_INIT(&loc->common, link);
761 
762 	loc->v.v0.version = version;
763 	isc_region_consume(&r, 1);
764 	loc->v.v0.size = uint8_fromregion(&r);
765 	isc_region_consume(&r, 1);
766 	loc->v.v0.horizontal = uint8_fromregion(&r);
767 	isc_region_consume(&r, 1);
768 	loc->v.v0.vertical = uint8_fromregion(&r);
769 	isc_region_consume(&r, 1);
770 	loc->v.v0.latitude = uint32_fromregion(&r);
771 	isc_region_consume(&r, 4);
772 	loc->v.v0.longitude = uint32_fromregion(&r);
773 	isc_region_consume(&r, 4);
774 	loc->v.v0.altitude = uint32_fromregion(&r);
775 	isc_region_consume(&r, 4);
776 	return (ISC_R_SUCCESS);
777 }
778 
779 static void
freestruct_loc(ARGS_FREESTRUCT)780 freestruct_loc(ARGS_FREESTRUCT) {
781 	dns_rdata_loc_t *loc = source;
782 
783 	REQUIRE(loc != NULL);
784 	REQUIRE(loc->common.rdtype == dns_rdatatype_loc);
785 
786 	UNUSED(source);
787 	UNUSED(loc);
788 }
789 
790 static isc_result_t
additionaldata_loc(ARGS_ADDLDATA)791 additionaldata_loc(ARGS_ADDLDATA) {
792 	REQUIRE(rdata->type == dns_rdatatype_loc);
793 
794 	UNUSED(rdata);
795 	UNUSED(add);
796 	UNUSED(arg);
797 
798 	return (ISC_R_SUCCESS);
799 }
800 
801 static isc_result_t
digest_loc(ARGS_DIGEST)802 digest_loc(ARGS_DIGEST) {
803 	isc_region_t r;
804 
805 	REQUIRE(rdata->type == dns_rdatatype_loc);
806 
807 	dns_rdata_toregion(rdata, &r);
808 
809 	return ((digest)(arg, &r));
810 }
811 
812 static bool
checkowner_loc(ARGS_CHECKOWNER)813 checkowner_loc(ARGS_CHECKOWNER) {
814 	REQUIRE(type == dns_rdatatype_loc);
815 
816 	UNUSED(name);
817 	UNUSED(type);
818 	UNUSED(rdclass);
819 	UNUSED(wildcard);
820 
821 	return (true);
822 }
823 
824 static bool
checknames_loc(ARGS_CHECKNAMES)825 checknames_loc(ARGS_CHECKNAMES) {
826 	REQUIRE(rdata->type == dns_rdatatype_loc);
827 
828 	UNUSED(rdata);
829 	UNUSED(owner);
830 	UNUSED(bad);
831 
832 	return (true);
833 }
834 
835 static int
casecompare_loc(ARGS_COMPARE)836 casecompare_loc(ARGS_COMPARE) {
837 	return (compare_loc(rdata1, rdata2));
838 }
839 
840 #endif /* RDATA_GENERIC_LOC_29_C */
841