1 /* Copyright (c) 2014, Vsevolod Stakhov
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *       * Redistributions of source code must retain the above copyright
7  *         notice, this list of conditions and the following disclaimer.
8  *       * Redistributions in binary form must reproduce the above copyright
9  *         notice, this list of conditions and the following disclaimer in the
10  *         documentation and/or other materials provided with the distribution.
11  *
12  * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15  * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22  */
23 
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <sys/un.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <netdb.h>
35 #include <fcntl.h>
36 #include <ctype.h>
37 
38 #include "ottery.h"
39 #include "util.h"
40 #include "logger.h"
41 #include "rdns.h"
42 
43 static int
rdns_make_socket_nonblocking(int fd)44 rdns_make_socket_nonblocking (int fd)
45 {
46 	int                            ofl;
47 
48 	ofl = fcntl (fd, F_GETFL, 0);
49 
50 	if (fcntl (fd, F_SETFL, ofl | O_NONBLOCK) == -1) {
51 		return -1;
52 	}
53 	return 0;
54 }
55 
56 static int
rdns_make_inet_socket(int type,struct addrinfo * addr,struct sockaddr ** psockaddr,socklen_t * psocklen)57 rdns_make_inet_socket (int type, struct addrinfo *addr, struct sockaddr **psockaddr,
58 		socklen_t *psocklen)
59 {
60 	int fd = -1;
61 	struct addrinfo *cur;
62 
63 	cur = addr;
64 	while (cur) {
65 		/* Create socket */
66 		fd = socket (cur->ai_family, type, 0);
67 		if (fd == -1) {
68 			goto out;
69 		}
70 
71 		if (rdns_make_socket_nonblocking (fd) < 0) {
72 			goto out;
73 		}
74 
75 		/* Set close on exec */
76 		if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
77 			goto out;
78 		}
79 
80 		if (psockaddr) {
81 			*psockaddr = cur->ai_addr;
82 			*psocklen = cur->ai_addrlen;
83 		}
84 		break;
85 out:
86 		if (fd != -1) {
87 			close (fd);
88 		}
89 		fd = -1;
90 		cur = cur->ai_next;
91 	}
92 
93 	return (fd);
94 }
95 
96 static int
rdns_make_unix_socket(const char * path,struct sockaddr_un * addr,int type)97 rdns_make_unix_socket (const char *path, struct sockaddr_un *addr, int type)
98 {
99 	int fd = -1, serrno;
100 
101 	if (path == NULL) {
102 		return -1;
103 	}
104 
105 	addr->sun_family = AF_UNIX;
106 
107 	memset (addr->sun_path, 0, sizeof (addr->sun_path));
108 	memccpy (addr->sun_path, path, 0, sizeof (addr->sun_path) - 1);
109 #ifdef FREEBSD
110 	addr->sun_len = SUN_LEN (addr);
111 #endif
112 
113 	fd = socket (PF_LOCAL, type, 0);
114 
115 	if (fd == -1) {
116 		return -1;
117 	}
118 
119 	if (rdns_make_socket_nonblocking (fd) < 0) {
120 		goto out;
121 	}
122 
123 	/* Set close on exec */
124 	if (fcntl (fd, F_SETFD, FD_CLOEXEC) == -1) {
125 		goto out;
126 	}
127 
128 	return (fd);
129 
130   out:
131 	serrno = errno;
132 	if (fd != -1) {
133 		close (fd);
134 	}
135 	errno = serrno;
136 	return (-1);
137 }
138 
139 /**
140  * Make a universal socket
141  * @param credits host, ip or path to unix socket
142  * @param port port (used for network sockets)
143  * @param async make this socket asynced
144  * @param is_server make this socket as server socket
145  * @param try_resolve try name resolution for a socket (BLOCKING)
146  */
147 int
rdns_make_client_socket(const char * credits,uint16_t port,int type,struct sockaddr ** psockaddr,socklen_t * psocklen)148 rdns_make_client_socket (const char *credits,
149 						 uint16_t port,
150 						 int type,
151 						 struct sockaddr **psockaddr,
152 						 socklen_t *psocklen)
153 {
154 	struct sockaddr_un              un;
155 	struct stat                     st;
156 	struct addrinfo                 hints, *res;
157 	int                             r;
158 	char                            portbuf[8];
159 
160 	if (*credits == '/') {
161 		r = stat (credits, &st);
162 		if (r == -1) {
163 			/* Unix socket doesn't exists it must be created first */
164 			errno = ENOENT;
165 			return -1;
166 		}
167 		else {
168 			if ((st.st_mode & S_IFSOCK) == 0) {
169 				/* Path is not valid socket */
170 				errno = EINVAL;
171 				return -1;
172 			}
173 			else {
174 				r = rdns_make_unix_socket (credits, &un, type);
175 
176 				if (r != -1 && psockaddr) {
177 					struct sockaddr *cpy;
178 
179 					cpy = calloc (1, sizeof (un));
180 					*psocklen = sizeof (un);
181 
182 					if (cpy == NULL) {
183 						close (r);
184 
185 						return -1;
186 					}
187 
188 					memcpy (cpy, &un, *psocklen);
189 					*psockaddr = cpy;
190 				}
191 
192 				return r;
193 			}
194 		}
195 	}
196 	else {
197 		/* TCP related part */
198 		memset (&hints, 0, sizeof (hints));
199 		hints.ai_family = AF_UNSPEC;     /* Allow IPv4 or IPv6 */
200 		hints.ai_socktype = type; /* Type of the socket */
201 		hints.ai_flags = 0;
202 		hints.ai_protocol = 0;           /* Any protocol */
203 		hints.ai_canonname = NULL;
204 		hints.ai_addr = NULL;
205 		hints.ai_next = NULL;
206 
207 		hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
208 
209 		snprintf (portbuf, sizeof (portbuf), "%d", (int)port);
210 		if (getaddrinfo (credits, portbuf, &hints, &res) == 0) {
211 			r = rdns_make_inet_socket (type, res, psockaddr, psocklen);
212 
213 			if (r != -1 && psockaddr) {
214 				struct sockaddr *cpy;
215 
216 				cpy = calloc (1, *psocklen);
217 
218 				if (cpy == NULL) {
219 					close (r);
220 					freeaddrinfo (res);
221 
222 					return -1;
223 				}
224 
225 				memcpy (cpy, *psockaddr, *psocklen);
226 				*psockaddr = cpy;
227 			}
228 
229 			freeaddrinfo (res);
230 			return r;
231 		}
232 		else {
233 			return -1;
234 		}
235 	}
236 
237 	/* Not reached */
238 	return -1;
239 }
240 
241 const char *
rdns_strerror(enum dns_rcode rcode)242 rdns_strerror (enum dns_rcode rcode)
243 {
244 	rcode &= 0xf;
245 	static char numbuf[16];
246 
247 	if ('\0' == dns_rcodes[rcode][0]) {
248 		snprintf (numbuf, sizeof (numbuf), "UNKNOWN: %d", (int)rcode);
249 		return numbuf;
250 	}
251 	return dns_rcodes[rcode];
252 }
253 
254 const char *
rdns_strtype(enum rdns_request_type type)255 rdns_strtype (enum rdns_request_type type)
256 {
257 	return dns_types[type];
258 }
259 
260 enum rdns_request_type
rdns_type_fromstr(const char * str)261 rdns_type_fromstr (const char *str)
262 {
263 	if (str) {
264 		if (strcmp (str, "a") == 0) {
265 			return RDNS_REQUEST_A;
266 		}
267 		else if (strcmp (str, "ns") == 0) {
268 			return RDNS_REQUEST_NS;
269 		}
270 		else if (strcmp (str, "soa") == 0) {
271 			return RDNS_REQUEST_SOA;
272 		}
273 		else if (strcmp (str, "ptr") == 0) {
274 			return RDNS_REQUEST_PTR;
275 		}
276 		else if (strcmp (str, "mx") == 0) {
277 			return RDNS_REQUEST_MX;
278 		}
279 		else if (strcmp (str, "srv") == 0) {
280 			return RDNS_REQUEST_SRV;
281 		}
282 		else if (strcmp (str, "txt") == 0) {
283 			return RDNS_REQUEST_TXT;
284 		}
285 		else if (strcmp (str, "spf") == 0) {
286 			return RDNS_REQUEST_SPF;
287 		}
288 		else if (strcmp (str, "aaaa") == 0) {
289 			return RDNS_REQUEST_AAAA;
290 		}
291 		else if (strcmp (str, "tlsa") == 0) {
292 			return RDNS_REQUEST_TLSA;
293 		}
294 		else if (strcmp (str, "any") == 0) {
295 			return RDNS_REQUEST_ANY;
296 		}
297 	}
298 
299 	return RDNS_REQUEST_INVALID;
300 }
301 
302 const char *
rdns_str_from_type(enum rdns_request_type rcode)303 rdns_str_from_type (enum rdns_request_type rcode)
304 {
305 	switch (rcode) {
306 		case RDNS_REQUEST_INVALID:
307 			return "(invalid)";
308 		case RDNS_REQUEST_A:
309 			return "a";
310 		case RDNS_REQUEST_NS:
311 			return "ns";
312 		case RDNS_REQUEST_SOA:
313 			return "soa";
314 		case RDNS_REQUEST_PTR:
315 			return "ptr";
316 		case RDNS_REQUEST_MX:
317 			return "mx";
318 		case RDNS_REQUEST_TXT:
319 			return "txt";
320 		case RDNS_REQUEST_SRV:
321 			return "srv";
322 		case RDNS_REQUEST_SPF:
323 			return "spf";
324 		case RDNS_REQUEST_AAAA:
325 			return "aaaa";
326 		case RDNS_REQUEST_TLSA:
327 			return "tlsa";
328 		case RDNS_REQUEST_ANY:
329 			return "any";
330 		default:
331 			return "(unknown)";
332 	}
333 
334 }
335 
336 enum dns_rcode
rdns_rcode_fromstr(const char * str)337 rdns_rcode_fromstr (const char *str)
338 {
339 	if (str) {
340 		if (strcmp (str, "noerror") == 0) {
341 			return RDNS_RC_NOERROR;
342 		}
343 		else if (strcmp (str, "formerr") == 0) {
344 			return RDNS_RC_FORMERR;
345 		}
346 		else if (strcmp (str, "servfail") == 0) {
347 			return RDNS_RC_SERVFAIL;
348 		}
349 		else if (strcmp (str, "nxdomain") == 0) {
350 			return RDNS_RC_NXDOMAIN;
351 		}
352 		else if (strcmp (str, "notimp") == 0) {
353 			return RDNS_RC_NOTIMP;
354 		}
355 		else if (strcmp (str, "yxdomain") == 0) {
356 			return RDNS_RC_YXDOMAIN;
357 		}
358 		else if (strcmp (str, "yxrrset") == 0) {
359 			return RDNS_RC_YXRRSET;
360 		}
361 		else if (strcmp (str, "nxrrset") == 0) {
362 			return RDNS_RC_NXRRSET;
363 		}
364 		else if (strcmp (str, "notauth") == 0) {
365 			return RDNS_RC_NOTAUTH;
366 		}
367 		else if (strcmp (str, "notzone") == 0) {
368 			return RDNS_RC_NOTZONE;
369 		}
370 		else if (strcmp (str, "timeout") == 0) {
371 			return RDNS_RC_TIMEOUT;
372 		}
373 		else if (strcmp (str, "neterr") == 0) {
374 			return RDNS_RC_NETERR;
375 		}
376 		else if (strcmp (str, "norec") == 0) {
377 			return RDNS_RC_NOREC;
378 		}
379 	}
380 
381 	return RDNS_RC_INVALID;
382 }
383 
384 uint16_t
rdns_permutor_generate_id(void)385 rdns_permutor_generate_id (void)
386 {
387 	uint16_t id;
388 
389 	id = ottery_rand_unsigned ();
390 
391 	return id;
392 }
393 
394 
395 void
rdns_reply_free(struct rdns_reply * rep)396 rdns_reply_free (struct rdns_reply *rep)
397 {
398 	struct rdns_reply_entry *entry, *tmp;
399 
400 	/* We don't need to free data for faked replies */
401 	if (!rep->request || rep->request->state != RDNS_REQUEST_FAKE) {
402 		LL_FOREACH_SAFE (rep->entries, entry, tmp) {
403 			switch (entry->type) {
404 			case RDNS_REQUEST_PTR:
405 				free (entry->content.ptr.name);
406 				break;
407 			case RDNS_REQUEST_NS:
408 				free (entry->content.ns.name);
409 				break;
410 			case RDNS_REQUEST_MX:
411 				free (entry->content.mx.name);
412 				break;
413 			case RDNS_REQUEST_TXT:
414 			case RDNS_REQUEST_SPF:
415 				free (entry->content.txt.data);
416 				break;
417 			case RDNS_REQUEST_SRV:
418 				free (entry->content.srv.target);
419 				break;
420 			case RDNS_REQUEST_TLSA:
421 				free (entry->content.tlsa.data);
422 				break;
423 			case RDNS_REQUEST_SOA:
424 				free (entry->content.soa.mname);
425 				free (entry->content.soa.admin);
426 				break;
427 			default:
428 				break;
429 			}
430 			free (entry);
431 		}
432 	}
433 
434 	free (rep);
435 }
436 
437 void
rdns_request_free(struct rdns_request * req)438 rdns_request_free (struct rdns_request *req)
439 {
440 	unsigned int i;
441 
442 	if (req != NULL) {
443 		if (req->packet != NULL) {
444 			free (req->packet);
445 		}
446 		for (i = 0; i < req->qcount; i ++) {
447 			free (req->requested_names[i].name);
448 		}
449 		if (req->requested_names != NULL) {
450 			free (req->requested_names);
451 		}
452 		if (req->reply != NULL) {
453 			rdns_reply_free (req->reply);
454 		}
455 		if (req->async_event) {
456 			if (req->state == RDNS_REQUEST_WAIT_REPLY) {
457 				/* Remove timer */
458 				req->async->del_timer (req->async->data,
459 						req->async_event);
460 				/* Remove from id hashes */
461 				HASH_DEL (req->io->requests, req);
462 				req->async_event = NULL;
463 			}
464 			else if (req->state == RDNS_REQUEST_WAIT_SEND) {
465 				/* Remove retransmit event */
466 				req->async->del_write (req->async->data,
467 						req->async_event);
468 				HASH_DEL (req->io->requests, req);
469 				req->async_event = NULL;
470 			}
471 			else if (req->state == RDNS_REQUEST_FAKE) {
472 				req->async->del_write (req->async->data,
473 						req->async_event);
474 				req->async_event = NULL;
475 			}
476 		}
477 #ifdef TWEETNACL
478 		if (req->curve_plugin_data != NULL) {
479 			req->resolver->curve_plugin->cb.curve_plugin.finish_cb (
480 					req, req->resolver->curve_plugin->data);
481 		}
482 #endif
483 		if (req->io != NULL && req->state > RDNS_REQUEST_NEW) {
484 			REF_RELEASE (req->io);
485 			REF_RELEASE (req->resolver);
486 		}
487 
488 		free (req);
489 	}
490 }
491 
492 void
rdns_ioc_free(struct rdns_io_channel * ioc)493 rdns_ioc_free (struct rdns_io_channel *ioc)
494 {
495 	struct rdns_request *req, *rtmp;
496 
497 	HASH_ITER (hh, ioc->requests, req, rtmp) {
498 		REF_RELEASE (req);
499 	}
500 
501 	ioc->resolver->async->del_read (ioc->resolver->async->data,
502 			ioc->async_io);
503 	close (ioc->sock);
504 	free (ioc->saddr);
505 	free (ioc);
506 }
507 
508 void
rdns_resolver_release(struct rdns_resolver * resolver)509 rdns_resolver_release (struct rdns_resolver *resolver)
510 {
511 	REF_RELEASE (resolver);
512 }
513 
514 struct rdns_request*
rdns_request_retain(struct rdns_request * req)515 rdns_request_retain (struct rdns_request *req)
516 {
517 	REF_RETAIN (req);
518 	return req;
519 }
520 
521 void
rdns_request_unschedule(struct rdns_request * req)522 rdns_request_unschedule (struct rdns_request *req)
523 {
524 	if (req->async_event) {
525 		if (req->state == RDNS_REQUEST_WAIT_REPLY) {
526 			req->async->del_timer (req->async->data,
527 					req->async_event);
528 			/* Remove from id hashes */
529 			HASH_DEL (req->io->requests, req);
530 			req->async_event = NULL;
531 		}
532 		else if (req->state == RDNS_REQUEST_WAIT_SEND) {
533 			req->async->del_write (req->async->data,
534 					req->async_event);
535 			/* Remove from id hashes */
536 			HASH_DEL (req->io->requests, req);
537 			req->async_event = NULL;
538 		}
539 	}
540 }
541 
542 void
rdns_request_release(struct rdns_request * req)543 rdns_request_release (struct rdns_request *req)
544 {
545 	rdns_request_unschedule (req);
546 	REF_RELEASE (req);
547 }
548 
549 static bool
rdns_resolver_conf_process_line(struct rdns_resolver * resolver,const char * line,rdns_resolv_conf_cb cb,void * ud)550 rdns_resolver_conf_process_line (struct rdns_resolver *resolver,
551 		const char *line, rdns_resolv_conf_cb cb, void *ud)
552 {
553 	const char *p, *c, *end;
554 	bool has_obrace = false, ret;
555 	unsigned int port = dns_port;
556 	char *cpy_buf;
557 
558 	end = line + strlen (line);
559 
560 	if (end - line > sizeof ("nameserver") - 1 &&
561 			strncmp (line, "nameserver", sizeof ("nameserver") - 1) == 0) {
562 		p = line + sizeof ("nameserver") - 1;
563 		/* Skip spaces */
564 		while (isspace (*p)) {
565 			p ++;
566 		}
567 
568 		if (*p == '[') {
569 			has_obrace = true;
570 			p ++;
571 		}
572 
573 		if (isxdigit (*p) || *p == ':') {
574 			c = p;
575 			while (isxdigit (*p) || *p == ':' || *p == '.') {
576 				p ++;
577 			}
578 			if (has_obrace && *p != ']') {
579 				return false;
580 			}
581 			else if (*p != '\0' && !isspace (*p) && *p != '#') {
582 				return false;
583 			}
584 
585 			if (has_obrace) {
586 				p ++;
587 				if (*p == ':') {
588 					/* Maybe we have a port definition */
589 					port = strtoul (p + 1, NULL, 10);
590 					if (port == 0 || port > UINT16_MAX) {
591 						return false;
592 					}
593 				}
594 			}
595 
596 			cpy_buf = malloc (p - c + 1);
597 			assert (cpy_buf != NULL);
598 			memcpy (cpy_buf, c, p - c);
599 			cpy_buf[p - c] = '\0';
600 
601 			if (cb == NULL) {
602 				ret = rdns_resolver_add_server (resolver, cpy_buf, port, 0,
603 						default_io_cnt) != NULL;
604 			}
605 			else {
606 				ret = cb (resolver, cpy_buf, port, 0,
607 						default_io_cnt, ud);
608 			}
609 
610 			free (cpy_buf);
611 
612 			return ret;
613 		}
614 		else {
615 			return false;
616 		}
617 	}
618 	/* XXX: skip unknown resolv.conf lines */
619 
620 	return false;
621 }
622 
623 bool
rdns_resolver_parse_resolv_conf_cb(struct rdns_resolver * resolver,const char * path,rdns_resolv_conf_cb cb,void * ud)624 rdns_resolver_parse_resolv_conf_cb (struct rdns_resolver *resolver,
625 		const char *path, rdns_resolv_conf_cb cb, void *ud)
626 {
627 	FILE *in;
628 	char buf[BUFSIZ];
629 	char *p;
630 	bool processed = false;
631 
632 	in = fopen (path, "r");
633 
634 	if (in == NULL) {
635 		return false;
636 	}
637 
638 	while (!feof (in)) {
639 		if (fgets (buf, sizeof (buf) - 1, in) == NULL) {
640 			break;
641 		}
642 
643 		/* Strip trailing spaces */
644 		p = buf + strlen (buf) - 1;
645 		while (p > buf &&
646 				(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
647 			*p-- = '\0';
648 		}
649 
650 		if (rdns_resolver_conf_process_line (resolver, buf, cb, ud)) {
651 			processed = true;
652 		}
653 	}
654 
655 	fclose (in);
656 
657 	return processed;
658 }
659 
660 bool
rdns_resolver_parse_resolv_conf(struct rdns_resolver * resolver,const char * path)661 rdns_resolver_parse_resolv_conf (struct rdns_resolver *resolver, const char *path)
662 {
663 	return rdns_resolver_parse_resolv_conf_cb (resolver, path, NULL, NULL);
664 }
665 
666 bool
rdns_request_has_type(struct rdns_request * req,enum rdns_request_type type)667 rdns_request_has_type (struct rdns_request *req, enum rdns_request_type type)
668 {
669 	unsigned int i;
670 
671 	for (i = 0; i < req->qcount; i ++) {
672 		if (req->requested_names[i].type == type) {
673 			return true;
674 		}
675 	}
676 
677 	return false;
678 }
679 
680 const struct rdns_request_name *
rdns_request_get_name(struct rdns_request * req,unsigned int * count)681 rdns_request_get_name (struct rdns_request *req, unsigned int *count)
682 {
683 
684 	if (count != NULL) {
685 		*count = req->qcount;
686 	}
687 	return req->requested_names;
688 }
689 
690 const char*
rdns_request_get_server(struct rdns_request * req)691 rdns_request_get_server (struct rdns_request *req)
692 {
693 	if (req && req->io) {
694 		return req->io->srv->name;
695 	}
696 
697 	return NULL;
698 }
699 
700 char *
rdns_generate_ptr_from_str(const char * str)701 rdns_generate_ptr_from_str (const char *str)
702 {
703 	union {
704 		struct in_addr v4;
705 		struct in6_addr v6;
706 	} addr;
707 	char *res = NULL;
708 	unsigned char *bytes;
709 	size_t len;
710 
711 	if (inet_pton (AF_INET, str, &addr.v4) == 1) {
712 		bytes = (unsigned char *)&addr.v4;
713 
714 		len = 4 * 4 + sizeof ("in-addr.arpa");
715 		res = malloc (len);
716 		if (res) {
717 			snprintf (res, len, "%u.%u.%u.%u.in-addr.arpa",
718 					(unsigned)bytes[3]&0xFF,
719 					(unsigned)bytes[2]&0xFF,
720 					(unsigned)bytes[1]&0xFF,
721 					(unsigned)bytes[0]&0xFF);
722 		}
723 	}
724 	else if (inet_pton (AF_INET6, str, &addr.v6) == 1) {
725 		bytes = (unsigned char *)&addr.v6;
726 
727 		len = 2*32 + sizeof ("ip6.arpa");
728 		res = malloc (len);
729 		if (res) {
730 			snprintf(res, len,
731 					"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
732 					"%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa",
733 					bytes[15]&0xF, bytes[15] >> 4, bytes[14]&0xF, bytes[14] >> 4,
734 					bytes[13]&0xF, bytes[13] >> 4, bytes[12]&0xF, bytes[12] >> 4,
735 					bytes[11]&0xF, bytes[11] >> 4, bytes[10]&0xF, bytes[10] >> 4,
736 					bytes[9]&0xF, bytes[9] >> 4, bytes[8]&0xF, bytes[8] >> 4,
737 					bytes[7]&0xF, bytes[7] >> 4, bytes[6]&0xF, bytes[6] >> 4,
738 					bytes[5]&0xF, bytes[5] >> 4, bytes[4]&0xF, bytes[4] >> 4,
739 					bytes[3]&0xF, bytes[3] >> 4, bytes[2]&0xF, bytes[2] >> 4,
740 					bytes[1]&0xF, bytes[1] >> 4, bytes[0]&0xF, bytes[0] >> 4);
741 		}
742 	}
743 
744 	return res;
745 }
746