xref: /PHP-8.3/ext/standard/dns.c (revision c8955c07)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: The typical suspects                                        |
14    |          Pollita <pollita@php.net>                                   |
15    |          Marcus Boerger <helly@php.net>                              |
16    +----------------------------------------------------------------------+
17  */
18 
19 /* {{{ includes */
20 #include "php.h"
21 #include "php_network.h"
22 
23 #ifdef HAVE_SYS_SOCKET_H
24 #include <sys/socket.h>
25 #endif
26 
27 #ifdef PHP_WIN32
28 # include "win32/inet.h"
29 # include <winsock2.h>
30 # include <windows.h>
31 # include <Ws2tcpip.h>
32 #else
33 #include <netinet/in.h>
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
36 #endif
37 #include <netdb.h>
38 #ifdef _OSD_POSIX
39 #undef STATUS
40 #undef T_UNSPEC
41 #endif
42 #ifdef HAVE_ARPA_NAMESER_H
43 #ifdef DARWIN
44 # define BIND_8_COMPAT 1
45 #endif
46 #include <arpa/nameser.h>
47 #endif
48 #ifdef HAVE_RESOLV_H
49 #include <resolv.h>
50 #if defined(__HAIKU__)
51 extern void __res_ndestroy(res_state statp);
52 #define res_ndestroy __res_ndestroy
53 #endif
54 #endif
55 #ifdef HAVE_DNS_H
56 #include <dns.h>
57 #endif
58 #endif
59 
60 #ifndef MAXHOSTNAMELEN
61 #define MAXHOSTNAMELEN 255
62 #endif
63 
64 /* For the local hostname obtained via gethostname which is different from the
65    dns-related MAXHOSTNAMELEN constant above */
66 #ifndef HOST_NAME_MAX
67 #define HOST_NAME_MAX 255
68 #endif
69 
70 #include "php_dns.h"
71 
72 /* type compat */
73 #ifndef DNS_T_A
74 #define DNS_T_A		1
75 #endif
76 #ifndef DNS_T_NS
77 #define DNS_T_NS	2
78 #endif
79 #ifndef DNS_T_CNAME
80 #define DNS_T_CNAME	5
81 #endif
82 #ifndef DNS_T_SOA
83 #define DNS_T_SOA	6
84 #endif
85 #ifndef DNS_T_PTR
86 #define DNS_T_PTR	12
87 #endif
88 #ifndef DNS_T_HINFO
89 #define DNS_T_HINFO	13
90 #endif
91 #ifndef DNS_T_MINFO
92 #define DNS_T_MINFO	14
93 #endif
94 #ifndef DNS_T_MX
95 #define DNS_T_MX	15
96 #endif
97 #ifndef DNS_T_TXT
98 #define DNS_T_TXT	16
99 #endif
100 #ifndef DNS_T_AAAA
101 #define DNS_T_AAAA	28
102 #endif
103 #ifndef DNS_T_SRV
104 #define DNS_T_SRV	33
105 #endif
106 #ifndef DNS_T_NAPTR
107 #define DNS_T_NAPTR	35
108 #endif
109 #ifndef DNS_T_A6
110 #define DNS_T_A6	38
111 #endif
112 #ifndef DNS_T_CAA
113 #define DNS_T_CAA	257
114 #endif
115 
116 #ifndef DNS_T_ANY
117 #define DNS_T_ANY	255
118 #endif
119 /* }}} */
120 
121 static zend_string *php_gethostbyaddr(char *ip);
122 static zend_string *php_gethostbyname(char *name);
123 
124 #ifdef HAVE_GETHOSTNAME
125 /* {{{ Get the host name of the current machine */
PHP_FUNCTION(gethostname)126 PHP_FUNCTION(gethostname)
127 {
128 	char buf[HOST_NAME_MAX + 1];
129 
130 	ZEND_PARSE_PARAMETERS_NONE();
131 
132 	if (gethostname(buf, sizeof(buf))) {
133 		php_error_docref(NULL, E_WARNING, "Unable to fetch host [%d]: %s", errno, strerror(errno));
134 		RETURN_FALSE;
135 	}
136 
137 	RETURN_STRING(buf);
138 }
139 /* }}} */
140 #endif
141 
142 /* TODO: Reimplement the gethostby* functions using the new winxp+ API, in dns_win32.c, then
143  we can have a dns.c, dns_unix.c and dns_win32.c instead of a messy dns.c full of #ifdef
144 */
145 
146 /* {{{ Get the Internet host name corresponding to a given IP address */
PHP_FUNCTION(gethostbyaddr)147 PHP_FUNCTION(gethostbyaddr)
148 {
149 	char *addr;
150 	size_t addr_len;
151 	zend_string *hostname;
152 
153 	ZEND_PARSE_PARAMETERS_START(1, 1)
154 		Z_PARAM_PATH(addr, addr_len)
155 	ZEND_PARSE_PARAMETERS_END();
156 
157 	hostname = php_gethostbyaddr(addr);
158 
159 	if (hostname == NULL) {
160 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
161 		php_error_docref(NULL, E_WARNING, "Address is not a valid IPv4 or IPv6 address");
162 #else
163 		php_error_docref(NULL, E_WARNING, "Address is not in a.b.c.d form");
164 #endif
165 		RETVAL_FALSE;
166 	} else {
167 		RETVAL_STR(hostname);
168 	}
169 }
170 /* }}} */
171 
172 /* {{{ php_gethostbyaddr */
php_gethostbyaddr(char * ip)173 static zend_string *php_gethostbyaddr(char *ip)
174 {
175 #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON)
176 	struct sockaddr_in sa4;
177 	struct sockaddr_in6 sa6;
178 	char out[NI_MAXHOST];
179 	memset(&sa4, 0, sizeof(struct sockaddr_in));
180 	memset(&sa6, 0, sizeof(struct sockaddr_in6));
181 
182 	if (inet_pton(AF_INET6, ip, &sa6.sin6_addr)) {
183 		sa6.sin6_family = AF_INET6;
184 
185 		if (getnameinfo((struct sockaddr *)&sa6, sizeof(sa6), out, sizeof(out), NULL, 0, NI_NAMEREQD) != 0) {
186 			return zend_string_init(ip, strlen(ip), 0);
187 		}
188 		return zend_string_init(out, strlen(out), 0);
189 	} else if (inet_pton(AF_INET, ip, &sa4.sin_addr)) {
190 		sa4.sin_family = AF_INET;
191 
192 		if (getnameinfo((struct sockaddr *)&sa4, sizeof(sa4), out, sizeof(out), NULL, 0, NI_NAMEREQD) != 0) {
193 			return zend_string_init(ip, strlen(ip), 0);
194 		}
195 		return zend_string_init(out, strlen(out), 0);
196 	}
197 	return NULL; /* not a valid IP */
198 #else
199 	struct in_addr addr;
200 	struct hostent *hp;
201 
202 	addr.s_addr = inet_addr(ip);
203 
204 	if (addr.s_addr == -1) {
205 		return NULL;
206 	}
207 
208 	hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
209 
210 	if (!hp || hp->h_name == NULL || hp->h_name[0] == '\0') {
211 		return zend_string_init(ip, strlen(ip), 0);
212 	}
213 
214 	return zend_string_init(hp->h_name, strlen(hp->h_name), 0);
215 #endif
216 }
217 /* }}} */
218 
219 /* {{{ Get the IP address corresponding to a given Internet host name */
PHP_FUNCTION(gethostbyname)220 PHP_FUNCTION(gethostbyname)
221 {
222 	char *hostname;
223 	size_t hostname_len;
224 
225 	ZEND_PARSE_PARAMETERS_START(1, 1)
226 		Z_PARAM_PATH(hostname, hostname_len)
227 	ZEND_PARSE_PARAMETERS_END();
228 
229 	if (hostname_len > MAXFQDNLEN) {
230 		/* name too long, protect from CVE-2015-0235 */
231 		php_error_docref(NULL, E_WARNING, "Host name cannot be longer than %d characters", MAXFQDNLEN);
232 		RETURN_STRINGL(hostname, hostname_len);
233 	}
234 
235 	RETURN_STR(php_gethostbyname(hostname));
236 }
237 /* }}} */
238 
239 /* {{{ Return a list of IP addresses that a given hostname resolves to. */
PHP_FUNCTION(gethostbynamel)240 PHP_FUNCTION(gethostbynamel)
241 {
242 	char *hostname;
243 	size_t hostname_len;
244 	struct hostent *hp;
245 	struct in_addr in;
246 	int i;
247 #ifdef HAVE_INET_NTOP
248 	char addr4[INET_ADDRSTRLEN];
249 #endif
250 
251 	ZEND_PARSE_PARAMETERS_START(1, 1)
252 		Z_PARAM_PATH(hostname, hostname_len)
253 	ZEND_PARSE_PARAMETERS_END();
254 
255 	if (hostname_len > MAXFQDNLEN) {
256 		/* name too long, protect from CVE-2015-0235 */
257 		php_error_docref(NULL, E_WARNING, "Host name cannot be longer than %d characters", MAXFQDNLEN);
258 		RETURN_FALSE;
259 	}
260 
261 	hp = php_network_gethostbyname(hostname);
262 	if (!hp) {
263 		RETURN_FALSE;
264 	}
265 
266 	array_init(return_value);
267 
268 	for (i = 0;; i++) {
269 		/* On macos h_addr_list entries may be misaligned. */
270 		struct in_addr *h_addr_entry; /* Don't call this h_addr, it's a macro! */
271 		memcpy(&h_addr_entry, &hp->h_addr_list[i], sizeof(struct in_addr *));
272 		if (!h_addr_entry) {
273 			return;
274 		}
275 
276 		in = *h_addr_entry;
277 #ifdef HAVE_INET_NTOP
278 		add_next_index_string(return_value, inet_ntop(AF_INET, &in, addr4, INET_ADDRSTRLEN));
279 #else
280 		add_next_index_string(return_value, inet_ntoa(in));
281 #endif
282 	}
283 }
284 /* }}} */
285 
286 /* {{{ php_gethostbyname */
php_gethostbyname(char * name)287 static zend_string *php_gethostbyname(char *name)
288 {
289 	struct hostent *hp;
290 	struct in_addr *h_addr_0; /* Don't call this h_addr, it's a macro! */
291 	struct in_addr in;
292 #ifdef HAVE_INET_NTOP
293 	char addr4[INET_ADDRSTRLEN];
294 #endif
295 	const char *address;
296 
297 	hp = php_network_gethostbyname(name);
298 	if (!hp) {
299 		return zend_string_init(name, strlen(name), 0);
300 	}
301 
302 	/* On macos h_addr_list entries may be misaligned. */
303 	memcpy(&h_addr_0, &hp->h_addr_list[0], sizeof(struct in_addr *));
304 	if (!h_addr_0) {
305 		return zend_string_init(name, strlen(name), 0);
306 	}
307 
308 	memcpy(&in.s_addr, h_addr_0, sizeof(in.s_addr));
309 
310 #ifdef HAVE_INET_NTOP
311 	address = inet_ntop(AF_INET, &in, addr4, INET_ADDRSTRLEN);
312 #else
313 	address = inet_ntoa(in);
314 #endif
315 	return zend_string_init(address, strlen(address), 0);
316 }
317 /* }}} */
318 
319 /* Note: These functions are defined in ext/standard/dns_win32.c for Windows! */
320 #if !defined(PHP_WIN32) && defined(HAVE_DNS_SEARCH_FUNC)
321 
322 #ifndef HFIXEDSZ
323 #define HFIXEDSZ        12      /* fixed data in header <arpa/nameser.h> */
324 #endif /* HFIXEDSZ */
325 
326 #ifndef QFIXEDSZ
327 #define QFIXEDSZ        4       /* fixed data in query <arpa/nameser.h> */
328 #endif /* QFIXEDSZ */
329 
330 #undef MAXHOSTNAMELEN
331 #define MAXHOSTNAMELEN  1024
332 
333 #ifndef MAXRESOURCERECORDS
334 #define MAXRESOURCERECORDS	64
335 #endif /* MAXRESOURCERECORDS */
336 
337 typedef union {
338 	HEADER qb1;
339 	uint8_t qb2[65536];
340 } querybuf;
341 
342 /* just a hack to free resources allocated by glibc in __res_nsend()
343  * See also:
344  *   res_thread_freeres() in glibc/resolv/res_init.c
345  *   __libc_res_nsend()   in resolv/res_send.c
346  * */
347 
348 #if defined(__GLIBC__) && !defined(HAVE_DEPRECATED_DNS_FUNCS)
349 #define php_dns_free_res(__res__) _php_dns_free_res(__res__)
_php_dns_free_res(struct __res_state * res)350 static void _php_dns_free_res(struct __res_state *res) { /* {{{ */
351 	int ns;
352 	for (ns = 0; ns < MAXNS; ns++) {
353 		if (res->_u._ext.nsaddrs[ns] != NULL) {
354 			free (res->_u._ext.nsaddrs[ns]);
355 			res->_u._ext.nsaddrs[ns] = NULL;
356 		}
357 	}
358 } /* }}} */
359 #else
360 #define php_dns_free_res(__res__)
361 #endif
362 
363 /* {{{ Check DNS records corresponding to a given Internet host name or IP address */
PHP_FUNCTION(dns_check_record)364 PHP_FUNCTION(dns_check_record)
365 {
366 	HEADER *hp;
367 	querybuf answer = {0};
368 	char *hostname;
369 	size_t hostname_len;
370 	zend_string *rectype = NULL;
371 	int type = DNS_T_MX, i;
372 #if defined(HAVE_DNS_SEARCH)
373 	struct sockaddr_storage from;
374 	uint32_t fromsize = sizeof(from);
375 	dns_handle_t handle;
376 #elif defined(HAVE_RES_NSEARCH)
377 	struct __res_state state;
378 	struct __res_state *handle = &state;
379 #endif
380 
381 	ZEND_PARSE_PARAMETERS_START(1, 2)
382 		Z_PARAM_STRING(hostname, hostname_len)
383 		Z_PARAM_OPTIONAL
384 		Z_PARAM_STR(rectype)
385 	ZEND_PARSE_PARAMETERS_END();
386 
387 	if (hostname_len == 0) {
388 		zend_argument_value_error(1, "cannot be empty");
389 		RETURN_THROWS();
390 	}
391 
392 	if (rectype) {
393 		if (zend_string_equals_literal_ci(rectype, "A")) type = DNS_T_A;
394 		else if (zend_string_equals_literal_ci(rectype, "NS")) type = DNS_T_NS;
395 		else if (zend_string_equals_literal_ci(rectype, "MX")) type = DNS_T_MX;
396 		else if (zend_string_equals_literal_ci(rectype, "PTR")) type = DNS_T_PTR;
397 		else if (zend_string_equals_literal_ci(rectype, "ANY")) type = DNS_T_ANY;
398 		else if (zend_string_equals_literal_ci(rectype, "SOA")) type = DNS_T_SOA;
399 		else if (zend_string_equals_literal_ci(rectype, "CAA")) type = DNS_T_CAA;
400 		else if (zend_string_equals_literal_ci(rectype, "TXT")) type = DNS_T_TXT;
401 		else if (zend_string_equals_literal_ci(rectype, "CNAME")) type = DNS_T_CNAME;
402 		else if (zend_string_equals_literal_ci(rectype, "AAAA")) type = DNS_T_AAAA;
403 		else if (zend_string_equals_literal_ci(rectype, "SRV")) type = DNS_T_SRV;
404 		else if (zend_string_equals_literal_ci(rectype, "NAPTR")) type = DNS_T_NAPTR;
405 		else if (zend_string_equals_literal_ci(rectype, "A6")) type = DNS_T_A6;
406 		else {
407 			zend_argument_value_error(2, "must be a valid DNS record type");
408 			RETURN_THROWS();
409 		}
410 	}
411 
412 #if defined(HAVE_DNS_SEARCH)
413 	handle = dns_open(NULL);
414 	if (handle == NULL) {
415 		RETURN_FALSE;
416 	}
417 #elif defined(HAVE_RES_NSEARCH)
418 	memset(&state, 0, sizeof(state));
419 	if (res_ninit(handle)) {
420 			RETURN_FALSE;
421 	}
422 #else
423 	res_init();
424 #endif
425 
426 	i = php_dns_search(handle, hostname, C_IN, type, answer.qb2, sizeof answer);
427 	php_dns_free_handle(handle);
428 
429 	if (i < 0) {
430 		RETURN_FALSE;
431 	}
432 	hp = (HEADER *)&answer;
433 	RETURN_BOOL(ntohs(hp->ancount) != 0);
434 }
435 /* }}} */
436 
437 #ifdef HAVE_FULL_DNS_FUNCS
438 
439 #define CHECKCP(n) do { \
440 	if (cp + n > end) { \
441 		return NULL; \
442 	} \
443 } while (0)
444 
445 /* {{{ php_parserr */
php_parserr(uint8_t * cp,uint8_t * end,querybuf * answer,int type_to_fetch,int store,bool raw,zval * subarray)446 static uint8_t *php_parserr(uint8_t *cp, uint8_t *end, querybuf *answer, int type_to_fetch, int store, bool raw, zval *subarray)
447 {
448 	u_short type, class, dlen;
449 	u_long ttl;
450 	long n, i;
451 	u_short s;
452 	uint8_t *tp, *p;
453 	char name[MAXHOSTNAMELEN] = {0};
454 	int have_v6_break = 0, in_v6_break = 0;
455 
456 	ZVAL_UNDEF(subarray);
457 
458 	n = dn_expand(answer->qb2, end, cp, name, sizeof(name) - 2);
459 	if (n < 0) {
460 		return NULL;
461 	}
462 	cp += n;
463 
464 	CHECKCP(10);
465 	GETSHORT(type, cp);
466 	GETSHORT(class, cp);
467 	GETLONG(ttl, cp);
468 	GETSHORT(dlen, cp);
469 	CHECKCP(dlen);
470 	if (dlen == 0) {
471 		/* No data in the response - nothing to do */
472 		return NULL;
473 	}
474 	if (type_to_fetch != DNS_T_ANY && type != type_to_fetch) {
475 		cp += dlen;
476 		return cp;
477 	}
478 
479 	if (!store) {
480 		cp += dlen;
481 		return cp;
482 	}
483 
484 	array_init(subarray);
485 
486 	add_assoc_string(subarray, "host", name);
487 	add_assoc_string(subarray, "class", "IN");
488 	add_assoc_long(subarray, "ttl", ttl);
489 	(void) class;
490 
491 	if (raw) {
492 		add_assoc_long(subarray, "type", type);
493 		add_assoc_stringl(subarray, "data", (char*) cp, (uint32_t) dlen);
494 		cp += dlen;
495 		return cp;
496 	}
497 
498 	switch (type) {
499 		case DNS_T_A:
500 			CHECKCP(4);
501 			add_assoc_string(subarray, "type", "A");
502 			snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
503 			add_assoc_string(subarray, "ip", name);
504 			cp += dlen;
505 			break;
506 		case DNS_T_MX:
507 			CHECKCP(2);
508 			add_assoc_string(subarray, "type", "MX");
509 			GETSHORT(n, cp);
510 			add_assoc_long(subarray, "pri", n);
511 			ZEND_FALLTHROUGH;
512 		case DNS_T_CNAME:
513 			if (type == DNS_T_CNAME) {
514 				add_assoc_string(subarray, "type", "CNAME");
515 			}
516 			ZEND_FALLTHROUGH;
517 		case DNS_T_NS:
518 			if (type == DNS_T_NS) {
519 				add_assoc_string(subarray, "type", "NS");
520 			}
521 			ZEND_FALLTHROUGH;
522 		case DNS_T_PTR:
523 			if (type == DNS_T_PTR) {
524 				add_assoc_string(subarray, "type", "PTR");
525 			}
526 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
527 			if (n < 0) {
528 				return NULL;
529 			}
530 			cp += n;
531 			add_assoc_string(subarray, "target", name);
532 			break;
533 		case DNS_T_HINFO:
534 			/* See RFC 1010 for values */
535 			add_assoc_string(subarray, "type", "HINFO");
536 			CHECKCP(1);
537 			n = *cp & 0xFF;
538 			cp++;
539 			CHECKCP(n);
540 			add_assoc_stringl(subarray, "cpu", (char*)cp, n);
541 			cp += n;
542 			CHECKCP(1);
543 			n = *cp & 0xFF;
544 			cp++;
545 			CHECKCP(n);
546 			add_assoc_stringl(subarray, "os", (char*)cp, n);
547 			cp += n;
548 			break;
549 		case DNS_T_CAA:
550 			/* See RFC 6844 for values https://tools.ietf.org/html/rfc6844 */
551 			add_assoc_string(subarray, "type", "CAA");
552 			// 1 flag byte
553 			CHECKCP(1);
554 			n = *cp & 0xFF;
555 			add_assoc_long(subarray, "flags", n);
556 			cp++;
557 			// Tag length (1 byte)
558 			CHECKCP(1);
559 			n = *cp & 0xFF;
560 			cp++;
561 			CHECKCP(n);
562 			add_assoc_stringl(subarray, "tag", (char*)cp, n);
563 			cp += n;
564 			if ( (size_t) dlen < ((size_t)n) + 2 ) {
565 				return NULL;
566 			}
567 			n = dlen - n - 2;
568 			CHECKCP(n);
569 			add_assoc_stringl(subarray, "value", (char*)cp, n);
570 			cp += n;
571 			break;
572 		case DNS_T_TXT:
573 			{
574 				int l1 = 0, l2 = 0;
575 				zval entries;
576 				zend_string *tp;
577 
578 				add_assoc_string(subarray, "type", "TXT");
579 				tp = zend_string_alloc(dlen, 0);
580 
581 				array_init(&entries);
582 
583 				while (l1 < dlen) {
584 					n = cp[l1];
585 					if ((l1 + n) >= dlen) {
586 						// Invalid chunk length, truncate
587 						n = dlen - (l1 + 1);
588 					}
589 					if (n) {
590 						memcpy(ZSTR_VAL(tp) + l2 , cp + l1 + 1, n);
591 						add_next_index_stringl(&entries, (char *) cp + l1 + 1, n);
592 					}
593 					l1 = l1 + n + 1;
594 					l2 = l2 + n;
595 				}
596 				ZSTR_VAL(tp)[l2] = '\0';
597 				ZSTR_LEN(tp) = l2;
598 				cp += dlen;
599 
600 				add_assoc_str(subarray, "txt", tp);
601 				add_assoc_zval(subarray, "entries", &entries);
602 			}
603 			break;
604 		case DNS_T_SOA:
605 			add_assoc_string(subarray, "type", "SOA");
606 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
607 			if (n < 0) {
608 				return NULL;
609 			}
610 			cp += n;
611 			add_assoc_string(subarray, "mname", name);
612 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
613 			if (n < 0) {
614 				return NULL;
615 			}
616 			cp += n;
617 			add_assoc_string(subarray, "rname", name);
618 			CHECKCP(5*4);
619 			GETLONG(n, cp);
620 			add_assoc_long(subarray, "serial", n);
621 			GETLONG(n, cp);
622 			add_assoc_long(subarray, "refresh", n);
623 			GETLONG(n, cp);
624 			add_assoc_long(subarray, "retry", n);
625 			GETLONG(n, cp);
626 			add_assoc_long(subarray, "expire", n);
627 			GETLONG(n, cp);
628 			add_assoc_long(subarray, "minimum-ttl", n);
629 			break;
630 		case DNS_T_AAAA:
631 			tp = (uint8_t*)name;
632 			CHECKCP(8*2);
633 			for(i=0; i < 8; i++) {
634 				GETSHORT(s, cp);
635 				if (s != 0) {
636 					if (tp > (uint8_t *)name) {
637 						in_v6_break = 0;
638 						tp[0] = ':';
639 						tp++;
640 					}
641 					tp += sprintf((char*)tp,"%x",s);
642 				} else {
643 					if (!have_v6_break) {
644 						have_v6_break = 1;
645 						in_v6_break = 1;
646 						tp[0] = ':';
647 						tp++;
648 					} else if (!in_v6_break) {
649 						tp[0] = ':';
650 						tp++;
651 						tp[0] = '0';
652 						tp++;
653 					}
654 				}
655 			}
656 			if (have_v6_break && in_v6_break) {
657 				tp[0] = ':';
658 				tp++;
659 			}
660 			tp[0] = '\0';
661 			add_assoc_string(subarray, "type", "AAAA");
662 			add_assoc_string(subarray, "ipv6", name);
663 			break;
664 		case DNS_T_A6:
665 			p = cp;
666 			add_assoc_string(subarray, "type", "A6");
667 			CHECKCP(1);
668 			n = ((int)cp[0]) & 0xFF;
669 			cp++;
670 			add_assoc_long(subarray, "masklen", n);
671 			tp = (uint8_t*)name;
672 			if (n > 15) {
673 				have_v6_break = 1;
674 				in_v6_break = 1;
675 				tp[0] = ':';
676 				tp++;
677 			}
678 			if (n % 16 > 8) {
679 				/* Partial short */
680 				if (cp[0] != 0) {
681 					if (tp > (uint8_t *)name) {
682 						in_v6_break = 0;
683 						tp[0] = ':';
684 						tp++;
685 					}
686 					sprintf((char*)tp, "%x", cp[0] & 0xFF);
687 				} else {
688 					if (!have_v6_break) {
689 						have_v6_break = 1;
690 						in_v6_break = 1;
691 						tp[0] = ':';
692 						tp++;
693 					} else if (!in_v6_break) {
694 						tp[0] = ':';
695 						tp++;
696 						tp[0] = '0';
697 						tp++;
698 					}
699 				}
700 				cp++;
701 			}
702 			for (i = (n + 8) / 16; i < 8; i++) {
703 				CHECKCP(2);
704 				GETSHORT(s, cp);
705 				if (s != 0) {
706 					if (tp > (uint8_t *)name) {
707 						in_v6_break = 0;
708 						tp[0] = ':';
709 						tp++;
710 					}
711 					tp += sprintf((char*)tp,"%x",s);
712 				} else {
713 					if (!have_v6_break) {
714 						have_v6_break = 1;
715 						in_v6_break = 1;
716 						tp[0] = ':';
717 						tp++;
718 					} else if (!in_v6_break) {
719 						tp[0] = ':';
720 						tp++;
721 						tp[0] = '0';
722 						tp++;
723 					}
724 				}
725 			}
726 			if (have_v6_break && in_v6_break) {
727 				tp[0] = ':';
728 				tp++;
729 			}
730 			tp[0] = '\0';
731 			add_assoc_string(subarray, "ipv6", name);
732 			if (cp < p + dlen) {
733 				n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
734 				if (n < 0) {
735 					return NULL;
736 				}
737 				cp += n;
738 				add_assoc_string(subarray, "chain", name);
739 			}
740 			break;
741 		case DNS_T_SRV:
742 			CHECKCP(3*2);
743 			add_assoc_string(subarray, "type", "SRV");
744 			GETSHORT(n, cp);
745 			add_assoc_long(subarray, "pri", n);
746 			GETSHORT(n, cp);
747 			add_assoc_long(subarray, "weight", n);
748 			GETSHORT(n, cp);
749 			add_assoc_long(subarray, "port", n);
750 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
751 			if (n < 0) {
752 				return NULL;
753 			}
754 			cp += n;
755 			add_assoc_string(subarray, "target", name);
756 			break;
757 		case DNS_T_NAPTR:
758 			CHECKCP(2*2);
759 			add_assoc_string(subarray, "type", "NAPTR");
760 			GETSHORT(n, cp);
761 			add_assoc_long(subarray, "order", n);
762 			GETSHORT(n, cp);
763 			add_assoc_long(subarray, "pref", n);
764 
765 			CHECKCP(1);
766 			n = (cp[0] & 0xFF);
767 			cp++;
768 			CHECKCP(n);
769 			add_assoc_stringl(subarray, "flags", (char*)cp, n);
770 			cp += n;
771 
772 			CHECKCP(1);
773 			n = (cp[0] & 0xFF);
774 			cp++;
775 			CHECKCP(n);
776 			add_assoc_stringl(subarray, "services", (char*)cp, n);
777 			cp += n;
778 
779 			CHECKCP(1);
780 			n = (cp[0] & 0xFF);
781 			cp++;
782 			CHECKCP(n);
783 			add_assoc_stringl(subarray, "regex", (char*)cp, n);
784 			cp += n;
785 
786 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
787 			if (n < 0) {
788 				return NULL;
789 			}
790 			cp += n;
791 			add_assoc_string(subarray, "replacement", name);
792 			break;
793 		default:
794 			zval_ptr_dtor(subarray);
795 			ZVAL_UNDEF(subarray);
796 			cp += dlen;
797 			break;
798 	}
799 
800 	return cp;
801 }
802 /* }}} */
803 
804 /* {{{ Get any Resource Record corresponding to a given Internet host name */
PHP_FUNCTION(dns_get_record)805 PHP_FUNCTION(dns_get_record)
806 {
807 	char *hostname;
808 	size_t hostname_len;
809 	zend_long type_param = PHP_DNS_ANY;
810 	zval *authns = NULL, *addtl = NULL;
811 	int type_to_fetch;
812 	int dns_errno;
813 #if defined(HAVE_DNS_SEARCH)
814 	struct sockaddr_storage from;
815 	uint32_t fromsize = sizeof(from);
816 	dns_handle_t handle;
817 #elif defined(HAVE_RES_NSEARCH)
818 	struct __res_state state;
819 	struct __res_state *handle = &state;
820 #endif
821 	HEADER *hp;
822 	querybuf answer = {0};
823 	uint8_t *cp = NULL, *end = NULL;
824 	int n, qd, an, ns = 0, ar = 0;
825 	int type, first_query = 1, store_results = 1;
826 	bool raw = 0;
827 
828 	ZEND_PARSE_PARAMETERS_START(1, 5)
829 		Z_PARAM_STRING(hostname, hostname_len)
830 		Z_PARAM_OPTIONAL
831 		Z_PARAM_LONG(type_param)
832 		Z_PARAM_ZVAL(authns)
833 		Z_PARAM_ZVAL(addtl)
834 		Z_PARAM_BOOL(raw)
835 	ZEND_PARSE_PARAMETERS_END();
836 
837 	if (authns) {
838 		authns = zend_try_array_init(authns);
839 		if (!authns) {
840 			RETURN_THROWS();
841 		}
842 	}
843 	if (addtl) {
844 		addtl = zend_try_array_init(addtl);
845 		if (!addtl) {
846 			RETURN_THROWS();
847 		}
848 	}
849 
850 	if (!raw) {
851 		if ((type_param & ~PHP_DNS_ALL) && (type_param != PHP_DNS_ANY)) {
852 			zend_argument_value_error(2, "must be a DNS_* constant");
853 			RETURN_THROWS();
854 		}
855 	} else {
856 		if ((type_param < 1) || (type_param > 0xFFFF)) {
857 			zend_argument_value_error(2, "must be between 1 and 65535 when argument #5 ($raw) is true");
858 			RETURN_THROWS();
859 		}
860 	}
861 
862 	/* Initialize the return array */
863 	array_init(return_value);
864 
865 	/* - We emulate an or'ed type mask by querying type by type. (Steps 0 - NUMTYPES-1 )
866 	 *   If additional info is wanted we check again with DNS_T_ANY (step NUMTYPES / NUMTYPES+1 )
867 	 *   store_results is used to skip storing the results retrieved in step
868 	 *   NUMTYPES+1 when results were already fetched.
869 	 * - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY. (step NUMTYPES+1 )
870 	 * - In case of raw mode, we query only the requested type instead of looping type by type
871 	 *   before going with the additional info stuff.
872 	 */
873 
874 	if (raw) {
875 		type = -1;
876 	} else if (type_param == PHP_DNS_ANY) {
877 		type = PHP_DNS_NUM_TYPES + 1;
878 	} else {
879 		type = 0;
880 	}
881 
882 	for ( ;
883 		type < (addtl ? (PHP_DNS_NUM_TYPES + 2) : PHP_DNS_NUM_TYPES) || first_query;
884 		type++
885 	) {
886 		first_query = 0;
887 		switch (type) {
888 			case -1: /* raw */
889 				type_to_fetch = type_param;
890 				/* skip over the rest and go directly to additional records */
891 				type = PHP_DNS_NUM_TYPES - 1;
892 				break;
893 			case 0:
894 				type_to_fetch = type_param&PHP_DNS_A     ? DNS_T_A     : 0;
895 				break;
896 			case 1:
897 				type_to_fetch = type_param&PHP_DNS_NS    ? DNS_T_NS    : 0;
898 				break;
899 			case 2:
900 				type_to_fetch = type_param&PHP_DNS_CNAME ? DNS_T_CNAME : 0;
901 				break;
902 			case 3:
903 				type_to_fetch = type_param&PHP_DNS_SOA   ? DNS_T_SOA   : 0;
904 				break;
905 			case 4:
906 				type_to_fetch = type_param&PHP_DNS_PTR   ? DNS_T_PTR   : 0;
907 				break;
908 			case 5:
909 				type_to_fetch = type_param&PHP_DNS_HINFO ? DNS_T_HINFO : 0;
910 				break;
911 			case 6:
912 				type_to_fetch = type_param&PHP_DNS_MX    ? DNS_T_MX    : 0;
913 				break;
914 			case 7:
915 				type_to_fetch = type_param&PHP_DNS_TXT   ? DNS_T_TXT   : 0;
916 				break;
917 			case 8:
918 				type_to_fetch = type_param&PHP_DNS_AAAA	 ? DNS_T_AAAA  : 0;
919 				break;
920 			case 9:
921 				type_to_fetch = type_param&PHP_DNS_SRV   ? DNS_T_SRV   : 0;
922 				break;
923 			case 10:
924 				type_to_fetch = type_param&PHP_DNS_NAPTR ? DNS_T_NAPTR : 0;
925 				break;
926 			case 11:
927 				type_to_fetch = type_param&PHP_DNS_A6	 ? DNS_T_A6 : 0;
928 				break;
929 			case 12:
930 				type_to_fetch = type_param&PHP_DNS_CAA ? DNS_T_CAA : 0;
931 				break;
932 			case PHP_DNS_NUM_TYPES:
933 				store_results = 0;
934 				continue;
935 			default:
936 			case (PHP_DNS_NUM_TYPES + 1):
937 				type_to_fetch = DNS_T_ANY;
938 				break;
939 		}
940 
941 		if (type_to_fetch) {
942 #if defined(HAVE_DNS_SEARCH)
943 			handle = dns_open(NULL);
944 			if (handle == NULL) {
945 				zend_array_destroy(Z_ARR_P(return_value));
946 				RETURN_FALSE;
947 			}
948 #elif defined(HAVE_RES_NSEARCH)
949 		    memset(&state, 0, sizeof(state));
950 		    if (res_ninit(handle)) {
951 		    	zend_array_destroy(Z_ARR_P(return_value));
952 				RETURN_FALSE;
953 			}
954 #else
955 			res_init();
956 #endif
957 
958 			n = php_dns_search(handle, hostname, C_IN, type_to_fetch, answer.qb2, sizeof answer);
959 
960 			if (n < 0) {
961 				dns_errno = php_dns_errno(handle);
962 				php_dns_free_handle(handle);
963 				switch (dns_errno) {
964 					case NO_DATA:
965 					case HOST_NOT_FOUND:
966 						continue;
967 
968 					case NO_RECOVERY:
969 						php_error_docref(NULL, E_WARNING, "An unexpected server failure occurred.");
970 						break;
971 
972 					case TRY_AGAIN:
973 						php_error_docref(NULL, E_WARNING, "A temporary server error occurred.");
974 						break;
975 
976 					default:
977 						php_error_docref(NULL, E_WARNING, "DNS Query failed");
978 				}
979 				zend_array_destroy(Z_ARR_P(return_value));
980 				RETURN_FALSE;
981 			}
982 
983 			cp = answer.qb2 + HFIXEDSZ;
984 			end = answer.qb2 + n;
985 			hp = (HEADER *)&answer;
986 			qd = ntohs(hp->qdcount);
987 			an = ntohs(hp->ancount);
988 			ns = ntohs(hp->nscount);
989 			ar = ntohs(hp->arcount);
990 
991 			/* Skip QD entries, they're only used by dn_expand later on */
992 			while (qd-- > 0) {
993 				n = dn_skipname(cp, end);
994 				if (n < 0) {
995 					php_error_docref(NULL, E_WARNING, "Unable to parse DNS data received");
996 					zend_array_destroy(Z_ARR_P(return_value));
997 					php_dns_free_handle(handle);
998 					RETURN_FALSE;
999 				}
1000 				cp += n + QFIXEDSZ;
1001 			}
1002 
1003 			/* YAY! Our real answers! */
1004 			while (an-- && cp && cp < end) {
1005 				zval retval;
1006 
1007 				cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval);
1008 				if (Z_TYPE(retval) != IS_UNDEF && store_results) {
1009 					add_next_index_zval(return_value, &retval);
1010 				}
1011 			}
1012 
1013 			if (authns || addtl) {
1014 				/* List of Authoritative Name Servers
1015 				 * Process when only requesting addtl so that we can skip through the section
1016 				 */
1017 				while (ns-- > 0 && cp && cp < end) {
1018 					zval retval;
1019 
1020 					cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval);
1021 					if (Z_TYPE(retval) != IS_UNDEF) {
1022 						add_next_index_zval(authns, &retval);
1023 					}
1024 				}
1025 			}
1026 
1027 			if (addtl) {
1028 				/* Additional records associated with authoritative name servers */
1029 				while (ar-- > 0 && cp && cp < end) {
1030 					zval retval;
1031 
1032 					cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval);
1033 					if (Z_TYPE(retval) != IS_UNDEF) {
1034 						add_next_index_zval(addtl, &retval);
1035 					}
1036 				}
1037 			}
1038 			php_dns_free_handle(handle);
1039 		}
1040 	}
1041 }
1042 /* }}} */
1043 
1044 /* {{{ Get MX records corresponding to a given Internet host name */
PHP_FUNCTION(dns_get_mx)1045 PHP_FUNCTION(dns_get_mx)
1046 {
1047 	char *hostname;
1048 	size_t hostname_len;
1049 	zval *mx_list, *weight_list = NULL;
1050 	int count, qdc;
1051 	u_short type, weight;
1052 	querybuf answer = {0};
1053 	char buf[MAXHOSTNAMELEN] = {0};
1054 	HEADER *hp;
1055 	uint8_t *cp, *end;
1056 	int i;
1057 #if defined(HAVE_DNS_SEARCH)
1058 	struct sockaddr_storage from;
1059 	uint32_t fromsize = sizeof(from);
1060 	dns_handle_t handle;
1061 #elif defined(HAVE_RES_NSEARCH)
1062 	struct __res_state state;
1063 	struct __res_state *handle = &state;
1064 #endif
1065 
1066 	ZEND_PARSE_PARAMETERS_START(2, 3)
1067 		Z_PARAM_STRING(hostname, hostname_len)
1068 		Z_PARAM_ZVAL(mx_list)
1069 		Z_PARAM_OPTIONAL
1070 		Z_PARAM_ZVAL(weight_list)
1071 	ZEND_PARSE_PARAMETERS_END();
1072 
1073 	mx_list = zend_try_array_init(mx_list);
1074 	if (!mx_list) {
1075 		RETURN_THROWS();
1076 	}
1077 
1078 	if (weight_list) {
1079 		weight_list = zend_try_array_init(weight_list);
1080 		if (!weight_list) {
1081 			RETURN_THROWS();
1082 		}
1083 	}
1084 
1085 #if defined(HAVE_DNS_SEARCH)
1086 	handle = dns_open(NULL);
1087 	if (handle == NULL) {
1088 		RETURN_FALSE;
1089 	}
1090 #elif defined(HAVE_RES_NSEARCH)
1091 	memset(&state, 0, sizeof(state));
1092 	if (res_ninit(handle)) {
1093 			RETURN_FALSE;
1094 	}
1095 #else
1096 	res_init();
1097 #endif
1098 
1099 	i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, answer.qb2, sizeof(answer));
1100 	if (i < 0) {
1101 		php_dns_free_handle(handle);
1102 		RETURN_FALSE;
1103 	}
1104 	hp = (HEADER *)&answer;
1105 	cp = answer.qb2 + HFIXEDSZ;
1106 	end = answer.qb2 + i;
1107 	for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) {
1108 		if ((i = dn_skipname(cp, end)) < 0 ) {
1109 			php_dns_free_handle(handle);
1110 			RETURN_FALSE;
1111 		}
1112 	}
1113 	count = ntohs((unsigned short)hp->ancount);
1114 	while (--count >= 0 && cp < end) {
1115 		if ((i = dn_skipname(cp, end)) < 0 ) {
1116 			php_dns_free_handle(handle);
1117 			RETURN_FALSE;
1118 		}
1119 		cp += i;
1120 		GETSHORT(type, cp);
1121 		cp += INT16SZ + INT32SZ;
1122 		GETSHORT(i, cp);
1123 		if (type != DNS_T_MX) {
1124 			cp += i;
1125 			continue;
1126 		}
1127 		GETSHORT(weight, cp);
1128 		if ((i = dn_expand(answer.qb2, end, cp, buf, sizeof(buf)-1)) < 0) {
1129 			php_dns_free_handle(handle);
1130 			RETURN_FALSE;
1131 		}
1132 		cp += i;
1133 		add_next_index_string(mx_list, buf);
1134 		if (weight_list) {
1135 			add_next_index_long(weight_list, weight);
1136 		}
1137 	}
1138 	php_dns_free_handle(handle);
1139 	RETURN_BOOL(zend_hash_num_elements(Z_ARRVAL_P(mx_list)) != 0);
1140 }
1141 /* }}} */
1142 #endif /* HAVE_FULL_DNS_FUNCS */
1143 #endif /* !defined(PHP_WIN32) && HAVE_DNS_SEARCH_FUNC */
1144