xref: /netbsd-src/external/mpl/bind/dist/lib/dns/rdata/generic/loc_29.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: loc_29.c,v 1.11 2025/01/26 16:25:31 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
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
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
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
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
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
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
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
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
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
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
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
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
349 loc_getsize(isc_lex_t *lexer, unsigned char *sizep) {
350 	return loc_getoptionalprecision(lexer, sizep);
351 }
352 
353 static isc_result_t
354 loc_gethorizontalprecision(isc_lex_t *lexer, unsigned char *hpp) {
355 	return loc_getoptionalprecision(lexer, hpp);
356 }
357 
358 static isc_result_t
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
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
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
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 
572 	isc_buffer_activeregion(source, &sr);
573 	if (sr.length < 1) {
574 		return ISC_R_UNEXPECTEDEND;
575 	}
576 	if (sr.base[0] != 0) {
577 		/* Treat as unknown. */
578 		isc_buffer_forward(source, sr.length);
579 		return mem_tobuffer(target, sr.base, sr.length);
580 	}
581 	if (sr.length < 16) {
582 		return ISC_R_UNEXPECTEDEND;
583 	}
584 
585 	/*
586 	 * Size.
587 	 */
588 	c = sr.base[1];
589 	if (c != 0) {
590 		if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 ||
591 		    ((c >> 4) & 0xf) == 0)
592 		{
593 			return ISC_R_RANGE;
594 
595 			/*
596 			 * Horizontal precision.
597 			 */
598 		}
599 	}
600 
601 	/*
602 	 * Horizontal precision.
603 	 */
604 	c = sr.base[2];
605 	if (c != 0) {
606 		if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 ||
607 		    ((c >> 4) & 0xf) == 0)
608 		{
609 			return ISC_R_RANGE;
610 
611 			/*
612 			 * Vertical precision.
613 			 */
614 		}
615 	}
616 
617 	/*
618 	 * Vertical precision.
619 	 */
620 	c = sr.base[3];
621 	if (c != 0) {
622 		if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 ||
623 		    ((c >> 4) & 0xf) == 0)
624 		{
625 			return ISC_R_RANGE;
626 		}
627 	}
628 	isc_region_consume(&sr, 4);
629 
630 	/*
631 	 * Latitude.
632 	 */
633 	latitude = uint32_fromregion(&sr);
634 	if (latitude < (0x80000000UL - 90 * 3600000) ||
635 	    latitude > (0x80000000UL + 90 * 3600000))
636 	{
637 		return ISC_R_RANGE;
638 	}
639 	isc_region_consume(&sr, 4);
640 
641 	/*
642 	 * Longitude.
643 	 */
644 	longitude = uint32_fromregion(&sr);
645 	if (longitude < (0x80000000UL - 180 * 3600000) ||
646 	    longitude > (0x80000000UL + 180 * 3600000))
647 	{
648 		return ISC_R_RANGE;
649 	}
650 
651 	/*
652 	 * Altitude.
653 	 * All values possible.
654 	 */
655 
656 	isc_buffer_activeregion(source, &sr);
657 	isc_buffer_forward(source, 16);
658 	return mem_tobuffer(target, sr.base, 16);
659 }
660 
661 static isc_result_t
662 towire_loc(ARGS_TOWIRE) {
663 	UNUSED(cctx);
664 
665 	REQUIRE(rdata->type == dns_rdatatype_loc);
666 	REQUIRE(rdata->length != 0);
667 
668 	return mem_tobuffer(target, rdata->data, rdata->length);
669 }
670 
671 static int
672 compare_loc(ARGS_COMPARE) {
673 	isc_region_t r1;
674 	isc_region_t r2;
675 
676 	REQUIRE(rdata1->type == rdata2->type);
677 	REQUIRE(rdata1->rdclass == rdata2->rdclass);
678 	REQUIRE(rdata1->type == dns_rdatatype_loc);
679 	REQUIRE(rdata1->length != 0);
680 	REQUIRE(rdata2->length != 0);
681 
682 	dns_rdata_toregion(rdata1, &r1);
683 	dns_rdata_toregion(rdata2, &r2);
684 	return isc_region_compare(&r1, &r2);
685 }
686 
687 static isc_result_t
688 fromstruct_loc(ARGS_FROMSTRUCT) {
689 	dns_rdata_loc_t *loc = source;
690 	uint8_t c;
691 
692 	REQUIRE(type == dns_rdatatype_loc);
693 	REQUIRE(loc != NULL);
694 	REQUIRE(loc->common.rdtype == type);
695 	REQUIRE(loc->common.rdclass == rdclass);
696 
697 	UNUSED(type);
698 	UNUSED(rdclass);
699 
700 	if (loc->v.v0.version != 0) {
701 		return ISC_R_NOTIMPLEMENTED;
702 	}
703 	RETERR(uint8_tobuffer(loc->v.v0.version, target));
704 
705 	c = loc->v.v0.size;
706 	if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 || ((c >> 4) & 0xf) == 0) {
707 		return ISC_R_RANGE;
708 	}
709 	RETERR(uint8_tobuffer(loc->v.v0.size, target));
710 
711 	c = loc->v.v0.horizontal;
712 	if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 || ((c >> 4) & 0xf) == 0) {
713 		return ISC_R_RANGE;
714 	}
715 	RETERR(uint8_tobuffer(loc->v.v0.horizontal, target));
716 
717 	c = loc->v.v0.vertical;
718 	if ((c & 0xf) > 9 || ((c >> 4) & 0xf) > 9 || ((c >> 4) & 0xf) == 0) {
719 		return ISC_R_RANGE;
720 	}
721 	RETERR(uint8_tobuffer(loc->v.v0.vertical, target));
722 
723 	if (loc->v.v0.latitude < (0x80000000UL - 90 * 3600000) ||
724 	    loc->v.v0.latitude > (0x80000000UL + 90 * 3600000))
725 	{
726 		return ISC_R_RANGE;
727 	}
728 	RETERR(uint32_tobuffer(loc->v.v0.latitude, target));
729 
730 	if (loc->v.v0.longitude < (0x80000000UL - 180 * 3600000) ||
731 	    loc->v.v0.longitude > (0x80000000UL + 180 * 3600000))
732 	{
733 		return ISC_R_RANGE;
734 	}
735 	RETERR(uint32_tobuffer(loc->v.v0.longitude, target));
736 	return uint32_tobuffer(loc->v.v0.altitude, target);
737 }
738 
739 static isc_result_t
740 tostruct_loc(ARGS_TOSTRUCT) {
741 	dns_rdata_loc_t *loc = target;
742 	isc_region_t r;
743 	uint8_t version;
744 
745 	REQUIRE(rdata->type == dns_rdatatype_loc);
746 	REQUIRE(loc != NULL);
747 	REQUIRE(rdata->length != 0);
748 
749 	UNUSED(mctx);
750 
751 	dns_rdata_toregion(rdata, &r);
752 	version = uint8_fromregion(&r);
753 	if (version != 0) {
754 		return ISC_R_NOTIMPLEMENTED;
755 	}
756 
757 	loc->common.rdclass = rdata->rdclass;
758 	loc->common.rdtype = rdata->type;
759 	ISC_LINK_INIT(&loc->common, link);
760 
761 	loc->v.v0.version = version;
762 	isc_region_consume(&r, 1);
763 	loc->v.v0.size = uint8_fromregion(&r);
764 	isc_region_consume(&r, 1);
765 	loc->v.v0.horizontal = uint8_fromregion(&r);
766 	isc_region_consume(&r, 1);
767 	loc->v.v0.vertical = uint8_fromregion(&r);
768 	isc_region_consume(&r, 1);
769 	loc->v.v0.latitude = uint32_fromregion(&r);
770 	isc_region_consume(&r, 4);
771 	loc->v.v0.longitude = uint32_fromregion(&r);
772 	isc_region_consume(&r, 4);
773 	loc->v.v0.altitude = uint32_fromregion(&r);
774 	isc_region_consume(&r, 4);
775 	return ISC_R_SUCCESS;
776 }
777 
778 static void
779 freestruct_loc(ARGS_FREESTRUCT) {
780 	dns_rdata_loc_t *loc = source;
781 
782 	REQUIRE(loc != NULL);
783 	REQUIRE(loc->common.rdtype == dns_rdatatype_loc);
784 
785 	UNUSED(source);
786 	UNUSED(loc);
787 }
788 
789 static isc_result_t
790 additionaldata_loc(ARGS_ADDLDATA) {
791 	REQUIRE(rdata->type == dns_rdatatype_loc);
792 
793 	UNUSED(rdata);
794 	UNUSED(owner);
795 	UNUSED(add);
796 	UNUSED(arg);
797 
798 	return ISC_R_SUCCESS;
799 }
800 
801 static isc_result_t
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
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
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
836 casecompare_loc(ARGS_COMPARE) {
837 	return compare_loc(rdata1, rdata2);
838 }
839 
840 #endif /* RDATA_GENERIC_LOC_29_C */
841