xref: /PHP-8.1/ext/standard/dns.c (revision 7daf0125)
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 #if 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 #if 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 #if HAVE_ARPA_NAMESER_H
43 #ifdef DARWIN
44 # define BIND_8_COMPAT 1
45 #endif
46 #include <arpa/nameser.h>
47 #endif
48 #if 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 HAVE_IPV6 && 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 HAVE_IPV6 && 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 #if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32)
320 # define PHP_DNS_NUM_TYPES	13	/* Number of DNS Types Supported by PHP currently */
321 
322 # define PHP_DNS_A      0x00000001
323 # define PHP_DNS_NS     0x00000002
324 # define PHP_DNS_CNAME  0x00000010
325 # define PHP_DNS_SOA    0x00000020
326 # define PHP_DNS_PTR    0x00000800
327 # define PHP_DNS_HINFO  0x00001000
328 # define PHP_DNS_CAA    0x00002000
329 # define PHP_DNS_MX     0x00004000
330 # define PHP_DNS_TXT    0x00008000
331 # define PHP_DNS_A6     0x01000000
332 # define PHP_DNS_SRV    0x02000000
333 # define PHP_DNS_NAPTR  0x04000000
334 # define PHP_DNS_AAAA   0x08000000
335 # define PHP_DNS_ANY    0x10000000
336 # define PHP_DNS_ALL    (PHP_DNS_A|PHP_DNS_NS|PHP_DNS_CNAME|PHP_DNS_SOA|PHP_DNS_PTR|PHP_DNS_HINFO|PHP_DNS_CAA|PHP_DNS_MX|PHP_DNS_TXT|PHP_DNS_A6|PHP_DNS_SRV|PHP_DNS_NAPTR|PHP_DNS_AAAA)
337 #endif /* HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) */
338 
339 /* Note: These functions are defined in ext/standard/dns_win32.c for Windows! */
340 #if !defined(PHP_WIN32) && HAVE_DNS_SEARCH_FUNC
341 
342 #ifndef HFIXEDSZ
343 #define HFIXEDSZ        12      /* fixed data in header <arpa/nameser.h> */
344 #endif /* HFIXEDSZ */
345 
346 #ifndef QFIXEDSZ
347 #define QFIXEDSZ        4       /* fixed data in query <arpa/nameser.h> */
348 #endif /* QFIXEDSZ */
349 
350 #undef MAXHOSTNAMELEN
351 #define MAXHOSTNAMELEN  1024
352 
353 #ifndef MAXRESOURCERECORDS
354 #define MAXRESOURCERECORDS	64
355 #endif /* MAXRESOURCERECORDS */
356 
357 typedef union {
358 	HEADER qb1;
359 	u_char qb2[65536];
360 } querybuf;
361 
362 /* just a hack to free resources allocated by glibc in __res_nsend()
363  * See also:
364  *   res_thread_freeres() in glibc/resolv/res_init.c
365  *   __libc_res_nsend()   in resolv/res_send.c
366  * */
367 
368 #if defined(__GLIBC__) && !defined(HAVE_DEPRECATED_DNS_FUNCS)
369 #define php_dns_free_res(__res__) _php_dns_free_res(__res__)
_php_dns_free_res(struct __res_state * res)370 static void _php_dns_free_res(struct __res_state *res) { /* {{{ */
371 	int ns;
372 	for (ns = 0; ns < MAXNS; ns++) {
373 		if (res->_u._ext.nsaddrs[ns] != NULL) {
374 			free (res->_u._ext.nsaddrs[ns]);
375 			res->_u._ext.nsaddrs[ns] = NULL;
376 		}
377 	}
378 } /* }}} */
379 #else
380 #define php_dns_free_res(__res__)
381 #endif
382 
383 /* {{{ Check DNS records corresponding to a given Internet host name or IP address */
PHP_FUNCTION(dns_check_record)384 PHP_FUNCTION(dns_check_record)
385 {
386 	HEADER *hp;
387 	querybuf answer;
388 	char *hostname;
389 	size_t hostname_len;
390 	zend_string *rectype = NULL;
391 	int type = DNS_T_MX, i;
392 #if defined(HAVE_DNS_SEARCH)
393 	struct sockaddr_storage from;
394 	uint32_t fromsize = sizeof(from);
395 	dns_handle_t handle;
396 #elif defined(HAVE_RES_NSEARCH)
397 	struct __res_state state;
398 	struct __res_state *handle = &state;
399 #endif
400 
401 	ZEND_PARSE_PARAMETERS_START(1, 2)
402 		Z_PARAM_STRING(hostname, hostname_len)
403 		Z_PARAM_OPTIONAL
404 		Z_PARAM_STR(rectype)
405 	ZEND_PARSE_PARAMETERS_END();
406 
407 	if (hostname_len == 0) {
408 		zend_argument_value_error(1, "cannot be empty");
409 		RETURN_THROWS();
410 	}
411 
412 	if (rectype) {
413 		if (zend_string_equals_literal_ci(rectype, "A")) type = DNS_T_A;
414 		else if (zend_string_equals_literal_ci(rectype, "NS")) type = DNS_T_NS;
415 		else if (zend_string_equals_literal_ci(rectype, "MX")) type = DNS_T_MX;
416 		else if (zend_string_equals_literal_ci(rectype, "PTR")) type = DNS_T_PTR;
417 		else if (zend_string_equals_literal_ci(rectype, "ANY")) type = DNS_T_ANY;
418 		else if (zend_string_equals_literal_ci(rectype, "SOA")) type = DNS_T_SOA;
419 		else if (zend_string_equals_literal_ci(rectype, "CAA")) type = DNS_T_CAA;
420 		else if (zend_string_equals_literal_ci(rectype, "TXT")) type = DNS_T_TXT;
421 		else if (zend_string_equals_literal_ci(rectype, "CNAME")) type = DNS_T_CNAME;
422 		else if (zend_string_equals_literal_ci(rectype, "AAAA")) type = DNS_T_AAAA;
423 		else if (zend_string_equals_literal_ci(rectype, "SRV")) type = DNS_T_SRV;
424 		else if (zend_string_equals_literal_ci(rectype, "NAPTR")) type = DNS_T_NAPTR;
425 		else if (zend_string_equals_literal_ci(rectype, "A6")) type = DNS_T_A6;
426 		else {
427 			zend_argument_value_error(2, "must be a valid DNS record type");
428 			RETURN_THROWS();
429 		}
430 	}
431 
432 #if defined(HAVE_DNS_SEARCH)
433 	handle = dns_open(NULL);
434 	if (handle == NULL) {
435 		RETURN_FALSE;
436 	}
437 #elif defined(HAVE_RES_NSEARCH)
438 	memset(&state, 0, sizeof(state));
439 	if (res_ninit(handle)) {
440 			RETURN_FALSE;
441 	}
442 #else
443 	res_init();
444 #endif
445 
446 	i = php_dns_search(handle, hostname, C_IN, type, answer.qb2, sizeof answer);
447 	php_dns_free_handle(handle);
448 
449 	if (i < 0) {
450 		RETURN_FALSE;
451 	}
452 	hp = (HEADER *)&answer;
453 	RETURN_BOOL(ntohs(hp->ancount) != 0);
454 }
455 /* }}} */
456 
457 #if HAVE_FULL_DNS_FUNCS
458 
459 #define CHECKCP(n) do { \
460 	if (cp + n > end) { \
461 		return NULL; \
462 	} \
463 } while (0)
464 
465 /* {{{ php_parserr */
php_parserr(u_char * cp,u_char * end,querybuf * answer,int type_to_fetch,int store,int raw,zval * subarray)466 static u_char *php_parserr(u_char *cp, u_char *end, querybuf *answer, int type_to_fetch, int store, int raw, zval *subarray)
467 {
468 	u_short type, class, dlen;
469 	u_long ttl;
470 	long n, i;
471 	u_short s;
472 	u_char *tp, *p;
473 	char name[MAXHOSTNAMELEN];
474 	int have_v6_break = 0, in_v6_break = 0;
475 
476 	ZVAL_UNDEF(subarray);
477 
478 	n = dn_expand(answer->qb2, end, cp, name, sizeof(name) - 2);
479 	if (n < 0) {
480 		return NULL;
481 	}
482 	cp += n;
483 
484 	CHECKCP(10);
485 	GETSHORT(type, cp);
486 	GETSHORT(class, cp);
487 	GETLONG(ttl, cp);
488 	GETSHORT(dlen, cp);
489 	CHECKCP(dlen);
490 	if (dlen == 0) {
491 		/* No data in the response - nothing to do */
492 		return NULL;
493 	}
494 	if (type_to_fetch != DNS_T_ANY && type != type_to_fetch) {
495 		cp += dlen;
496 		return cp;
497 	}
498 
499 	if (!store) {
500 		cp += dlen;
501 		return cp;
502 	}
503 
504 	array_init(subarray);
505 
506 	add_assoc_string(subarray, "host", name);
507 	add_assoc_string(subarray, "class", "IN");
508 	add_assoc_long(subarray, "ttl", ttl);
509 	(void) class;
510 
511 	if (raw) {
512 		add_assoc_long(subarray, "type", type);
513 		add_assoc_stringl(subarray, "data", (char*) cp, (uint32_t) dlen);
514 		cp += dlen;
515 		return cp;
516 	}
517 
518 	switch (type) {
519 		case DNS_T_A:
520 			CHECKCP(4);
521 			add_assoc_string(subarray, "type", "A");
522 			snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
523 			add_assoc_string(subarray, "ip", name);
524 			cp += dlen;
525 			break;
526 		case DNS_T_MX:
527 			CHECKCP(2);
528 			add_assoc_string(subarray, "type", "MX");
529 			GETSHORT(n, cp);
530 			add_assoc_long(subarray, "pri", n);
531 			ZEND_FALLTHROUGH;
532 		case DNS_T_CNAME:
533 			if (type == DNS_T_CNAME) {
534 				add_assoc_string(subarray, "type", "CNAME");
535 			}
536 			ZEND_FALLTHROUGH;
537 		case DNS_T_NS:
538 			if (type == DNS_T_NS) {
539 				add_assoc_string(subarray, "type", "NS");
540 			}
541 			ZEND_FALLTHROUGH;
542 		case DNS_T_PTR:
543 			if (type == DNS_T_PTR) {
544 				add_assoc_string(subarray, "type", "PTR");
545 			}
546 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
547 			if (n < 0) {
548 				return NULL;
549 			}
550 			cp += n;
551 			add_assoc_string(subarray, "target", name);
552 			break;
553 		case DNS_T_HINFO:
554 			/* See RFC 1010 for values */
555 			add_assoc_string(subarray, "type", "HINFO");
556 			CHECKCP(1);
557 			n = *cp & 0xFF;
558 			cp++;
559 			CHECKCP(n);
560 			add_assoc_stringl(subarray, "cpu", (char*)cp, n);
561 			cp += n;
562 			CHECKCP(1);
563 			n = *cp & 0xFF;
564 			cp++;
565 			CHECKCP(n);
566 			add_assoc_stringl(subarray, "os", (char*)cp, n);
567 			cp += n;
568 			break;
569 		case DNS_T_CAA:
570 			/* See RFC 6844 for values https://tools.ietf.org/html/rfc6844 */
571 			add_assoc_string(subarray, "type", "CAA");
572 			// 1 flag byte
573 			CHECKCP(1);
574 			n = *cp & 0xFF;
575 			add_assoc_long(subarray, "flags", n);
576 			cp++;
577 			// Tag length (1 byte)
578 			CHECKCP(1);
579 			n = *cp & 0xFF;
580 			cp++;
581 			CHECKCP(n);
582 			add_assoc_stringl(subarray, "tag", (char*)cp, n);
583 			cp += n;
584 			if ( (size_t) dlen < ((size_t)n) + 2 ) {
585 				return NULL;
586 			}
587 			n = dlen - n - 2;
588 			CHECKCP(n);
589 			add_assoc_stringl(subarray, "value", (char*)cp, n);
590 			cp += n;
591 			break;
592 		case DNS_T_TXT:
593 			{
594 				int l1 = 0, l2 = 0;
595 				zval entries;
596 				zend_string *tp;
597 
598 				add_assoc_string(subarray, "type", "TXT");
599 				tp = zend_string_alloc(dlen, 0);
600 
601 				array_init(&entries);
602 
603 				while (l1 < dlen) {
604 					n = cp[l1];
605 					if ((l1 + n) >= dlen) {
606 						// Invalid chunk length, truncate
607 						n = dlen - (l1 + 1);
608 					}
609 					if (n) {
610 						memcpy(ZSTR_VAL(tp) + l2 , cp + l1 + 1, n);
611 						add_next_index_stringl(&entries, (char *) cp + l1 + 1, n);
612 					}
613 					l1 = l1 + n + 1;
614 					l2 = l2 + n;
615 				}
616 				ZSTR_VAL(tp)[l2] = '\0';
617 				ZSTR_LEN(tp) = l2;
618 				cp += dlen;
619 
620 				add_assoc_str(subarray, "txt", tp);
621 				add_assoc_zval(subarray, "entries", &entries);
622 			}
623 			break;
624 		case DNS_T_SOA:
625 			add_assoc_string(subarray, "type", "SOA");
626 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
627 			if (n < 0) {
628 				return NULL;
629 			}
630 			cp += n;
631 			add_assoc_string(subarray, "mname", name);
632 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
633 			if (n < 0) {
634 				return NULL;
635 			}
636 			cp += n;
637 			add_assoc_string(subarray, "rname", name);
638 			CHECKCP(5*4);
639 			GETLONG(n, cp);
640 			add_assoc_long(subarray, "serial", n);
641 			GETLONG(n, cp);
642 			add_assoc_long(subarray, "refresh", n);
643 			GETLONG(n, cp);
644 			add_assoc_long(subarray, "retry", n);
645 			GETLONG(n, cp);
646 			add_assoc_long(subarray, "expire", n);
647 			GETLONG(n, cp);
648 			add_assoc_long(subarray, "minimum-ttl", n);
649 			break;
650 		case DNS_T_AAAA:
651 			tp = (u_char*)name;
652 			CHECKCP(8*2);
653 			for(i=0; i < 8; i++) {
654 				GETSHORT(s, cp);
655 				if (s != 0) {
656 					if (tp > (u_char *)name) {
657 						in_v6_break = 0;
658 						tp[0] = ':';
659 						tp++;
660 					}
661 					tp += sprintf((char*)tp,"%x",s);
662 				} else {
663 					if (!have_v6_break) {
664 						have_v6_break = 1;
665 						in_v6_break = 1;
666 						tp[0] = ':';
667 						tp++;
668 					} else if (!in_v6_break) {
669 						tp[0] = ':';
670 						tp++;
671 						tp[0] = '0';
672 						tp++;
673 					}
674 				}
675 			}
676 			if (have_v6_break && in_v6_break) {
677 				tp[0] = ':';
678 				tp++;
679 			}
680 			tp[0] = '\0';
681 			add_assoc_string(subarray, "type", "AAAA");
682 			add_assoc_string(subarray, "ipv6", name);
683 			break;
684 		case DNS_T_A6:
685 			p = cp;
686 			add_assoc_string(subarray, "type", "A6");
687 			CHECKCP(1);
688 			n = ((int)cp[0]) & 0xFF;
689 			cp++;
690 			add_assoc_long(subarray, "masklen", n);
691 			tp = (u_char*)name;
692 			if (n > 15) {
693 				have_v6_break = 1;
694 				in_v6_break = 1;
695 				tp[0] = ':';
696 				tp++;
697 			}
698 			if (n % 16 > 8) {
699 				/* Partial short */
700 				if (cp[0] != 0) {
701 					if (tp > (u_char *)name) {
702 						in_v6_break = 0;
703 						tp[0] = ':';
704 						tp++;
705 					}
706 					sprintf((char*)tp, "%x", cp[0] & 0xFF);
707 				} else {
708 					if (!have_v6_break) {
709 						have_v6_break = 1;
710 						in_v6_break = 1;
711 						tp[0] = ':';
712 						tp++;
713 					} else if (!in_v6_break) {
714 						tp[0] = ':';
715 						tp++;
716 						tp[0] = '0';
717 						tp++;
718 					}
719 				}
720 				cp++;
721 			}
722 			for (i = (n + 8) / 16; i < 8; i++) {
723 				CHECKCP(2);
724 				GETSHORT(s, cp);
725 				if (s != 0) {
726 					if (tp > (u_char *)name) {
727 						in_v6_break = 0;
728 						tp[0] = ':';
729 						tp++;
730 					}
731 					tp += sprintf((char*)tp,"%x",s);
732 				} else {
733 					if (!have_v6_break) {
734 						have_v6_break = 1;
735 						in_v6_break = 1;
736 						tp[0] = ':';
737 						tp++;
738 					} else if (!in_v6_break) {
739 						tp[0] = ':';
740 						tp++;
741 						tp[0] = '0';
742 						tp++;
743 					}
744 				}
745 			}
746 			if (have_v6_break && in_v6_break) {
747 				tp[0] = ':';
748 				tp++;
749 			}
750 			tp[0] = '\0';
751 			add_assoc_string(subarray, "ipv6", name);
752 			if (cp < p + dlen) {
753 				n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
754 				if (n < 0) {
755 					return NULL;
756 				}
757 				cp += n;
758 				add_assoc_string(subarray, "chain", name);
759 			}
760 			break;
761 		case DNS_T_SRV:
762 			CHECKCP(3*2);
763 			add_assoc_string(subarray, "type", "SRV");
764 			GETSHORT(n, cp);
765 			add_assoc_long(subarray, "pri", n);
766 			GETSHORT(n, cp);
767 			add_assoc_long(subarray, "weight", n);
768 			GETSHORT(n, cp);
769 			add_assoc_long(subarray, "port", n);
770 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
771 			if (n < 0) {
772 				return NULL;
773 			}
774 			cp += n;
775 			add_assoc_string(subarray, "target", name);
776 			break;
777 		case DNS_T_NAPTR:
778 			CHECKCP(2*2);
779 			add_assoc_string(subarray, "type", "NAPTR");
780 			GETSHORT(n, cp);
781 			add_assoc_long(subarray, "order", n);
782 			GETSHORT(n, cp);
783 			add_assoc_long(subarray, "pref", n);
784 
785 			CHECKCP(1);
786 			n = (cp[0] & 0xFF);
787 			cp++;
788 			CHECKCP(n);
789 			add_assoc_stringl(subarray, "flags", (char*)cp, n);
790 			cp += n;
791 
792 			CHECKCP(1);
793 			n = (cp[0] & 0xFF);
794 			cp++;
795 			CHECKCP(n);
796 			add_assoc_stringl(subarray, "services", (char*)cp, n);
797 			cp += n;
798 
799 			CHECKCP(1);
800 			n = (cp[0] & 0xFF);
801 			cp++;
802 			CHECKCP(n);
803 			add_assoc_stringl(subarray, "regex", (char*)cp, n);
804 			cp += n;
805 
806 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
807 			if (n < 0) {
808 				return NULL;
809 			}
810 			cp += n;
811 			add_assoc_string(subarray, "replacement", name);
812 			break;
813 		default:
814 			zval_ptr_dtor(subarray);
815 			ZVAL_UNDEF(subarray);
816 			cp += dlen;
817 			break;
818 	}
819 
820 	return cp;
821 }
822 /* }}} */
823 
824 /* {{{ Get any Resource Record corresponding to a given Internet host name */
PHP_FUNCTION(dns_get_record)825 PHP_FUNCTION(dns_get_record)
826 {
827 	char *hostname;
828 	size_t hostname_len;
829 	zend_long type_param = PHP_DNS_ANY;
830 	zval *authns = NULL, *addtl = NULL;
831 	int type_to_fetch;
832 	int dns_errno;
833 #if defined(HAVE_DNS_SEARCH)
834 	struct sockaddr_storage from;
835 	uint32_t fromsize = sizeof(from);
836 	dns_handle_t handle;
837 #elif defined(HAVE_RES_NSEARCH)
838 	struct __res_state state;
839 	struct __res_state *handle = &state;
840 #endif
841 	HEADER *hp;
842 	querybuf answer;
843 	u_char *cp = NULL, *end = NULL;
844 	int n, qd, an, ns = 0, ar = 0;
845 	int type, first_query = 1, store_results = 1;
846 	bool raw = 0;
847 
848 	ZEND_PARSE_PARAMETERS_START(1, 5)
849 		Z_PARAM_STRING(hostname, hostname_len)
850 		Z_PARAM_OPTIONAL
851 		Z_PARAM_LONG(type_param)
852 		Z_PARAM_ZVAL(authns)
853 		Z_PARAM_ZVAL(addtl)
854 		Z_PARAM_BOOL(raw)
855 	ZEND_PARSE_PARAMETERS_END();
856 
857 	if (authns) {
858 		authns = zend_try_array_init(authns);
859 		if (!authns) {
860 			RETURN_THROWS();
861 		}
862 	}
863 	if (addtl) {
864 		addtl = zend_try_array_init(addtl);
865 		if (!addtl) {
866 			RETURN_THROWS();
867 		}
868 	}
869 
870 	if (!raw) {
871 		if ((type_param & ~PHP_DNS_ALL) && (type_param != PHP_DNS_ANY)) {
872 			zend_argument_value_error(2, "must be a DNS_* constant");
873 			RETURN_THROWS();
874 		}
875 	} else {
876 		if ((type_param < 1) || (type_param > 0xFFFF)) {
877 			zend_argument_value_error(2, "must be between 1 and 65535 when argument #5 ($raw) is true");
878 			RETURN_THROWS();
879 		}
880 	}
881 
882 	/* Initialize the return array */
883 	array_init(return_value);
884 
885 	/* - We emulate an or'ed type mask by querying type by type. (Steps 0 - NUMTYPES-1 )
886 	 *   If additional info is wanted we check again with DNS_T_ANY (step NUMTYPES / NUMTYPES+1 )
887 	 *   store_results is used to skip storing the results retrieved in step
888 	 *   NUMTYPES+1 when results were already fetched.
889 	 * - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY. (step NUMTYPES+1 )
890 	 * - In case of raw mode, we query only the requested type instead of looping type by type
891 	 *   before going with the additional info stuff.
892 	 */
893 
894 	if (raw) {
895 		type = -1;
896 	} else if (type_param == PHP_DNS_ANY) {
897 		type = PHP_DNS_NUM_TYPES + 1;
898 	} else {
899 		type = 0;
900 	}
901 
902 	for ( ;
903 		type < (addtl ? (PHP_DNS_NUM_TYPES + 2) : PHP_DNS_NUM_TYPES) || first_query;
904 		type++
905 	) {
906 		first_query = 0;
907 		switch (type) {
908 			case -1: /* raw */
909 				type_to_fetch = type_param;
910 				/* skip over the rest and go directly to additional records */
911 				type = PHP_DNS_NUM_TYPES - 1;
912 				break;
913 			case 0:
914 				type_to_fetch = type_param&PHP_DNS_A     ? DNS_T_A     : 0;
915 				break;
916 			case 1:
917 				type_to_fetch = type_param&PHP_DNS_NS    ? DNS_T_NS    : 0;
918 				break;
919 			case 2:
920 				type_to_fetch = type_param&PHP_DNS_CNAME ? DNS_T_CNAME : 0;
921 				break;
922 			case 3:
923 				type_to_fetch = type_param&PHP_DNS_SOA   ? DNS_T_SOA   : 0;
924 				break;
925 			case 4:
926 				type_to_fetch = type_param&PHP_DNS_PTR   ? DNS_T_PTR   : 0;
927 				break;
928 			case 5:
929 				type_to_fetch = type_param&PHP_DNS_HINFO ? DNS_T_HINFO : 0;
930 				break;
931 			case 6:
932 				type_to_fetch = type_param&PHP_DNS_MX    ? DNS_T_MX    : 0;
933 				break;
934 			case 7:
935 				type_to_fetch = type_param&PHP_DNS_TXT   ? DNS_T_TXT   : 0;
936 				break;
937 			case 8:
938 				type_to_fetch = type_param&PHP_DNS_AAAA	 ? DNS_T_AAAA  : 0;
939 				break;
940 			case 9:
941 				type_to_fetch = type_param&PHP_DNS_SRV   ? DNS_T_SRV   : 0;
942 				break;
943 			case 10:
944 				type_to_fetch = type_param&PHP_DNS_NAPTR ? DNS_T_NAPTR : 0;
945 				break;
946 			case 11:
947 				type_to_fetch = type_param&PHP_DNS_A6	 ? DNS_T_A6 : 0;
948 				break;
949 			case 12:
950 				type_to_fetch = type_param&PHP_DNS_CAA ? DNS_T_CAA : 0;
951 				break;
952 			case PHP_DNS_NUM_TYPES:
953 				store_results = 0;
954 				continue;
955 			default:
956 			case (PHP_DNS_NUM_TYPES + 1):
957 				type_to_fetch = DNS_T_ANY;
958 				break;
959 		}
960 
961 		if (type_to_fetch) {
962 #if defined(HAVE_DNS_SEARCH)
963 			handle = dns_open(NULL);
964 			if (handle == NULL) {
965 				zend_array_destroy(Z_ARR_P(return_value));
966 				RETURN_FALSE;
967 			}
968 #elif defined(HAVE_RES_NSEARCH)
969 		    memset(&state, 0, sizeof(state));
970 		    if (res_ninit(handle)) {
971 		    	zend_array_destroy(Z_ARR_P(return_value));
972 				RETURN_FALSE;
973 			}
974 #else
975 			res_init();
976 #endif
977 
978 			n = php_dns_search(handle, hostname, C_IN, type_to_fetch, answer.qb2, sizeof answer);
979 
980 			if (n < 0) {
981 				dns_errno = php_dns_errno(handle);
982 				php_dns_free_handle(handle);
983 				switch (dns_errno) {
984 					case NO_DATA:
985 					case HOST_NOT_FOUND:
986 						continue;
987 
988 					case NO_RECOVERY:
989 						php_error_docref(NULL, E_WARNING, "An unexpected server failure occurred.");
990 						break;
991 
992 					case TRY_AGAIN:
993 						php_error_docref(NULL, E_WARNING, "A temporary server error occurred.");
994 						break;
995 
996 					default:
997 						php_error_docref(NULL, E_WARNING, "DNS Query failed");
998 				}
999 				zend_array_destroy(Z_ARR_P(return_value));
1000 				RETURN_FALSE;
1001 			}
1002 
1003 			cp = answer.qb2 + HFIXEDSZ;
1004 			end = answer.qb2 + n;
1005 			hp = (HEADER *)&answer;
1006 			qd = ntohs(hp->qdcount);
1007 			an = ntohs(hp->ancount);
1008 			ns = ntohs(hp->nscount);
1009 			ar = ntohs(hp->arcount);
1010 
1011 			/* Skip QD entries, they're only used by dn_expand later on */
1012 			while (qd-- > 0) {
1013 				n = dn_skipname(cp, end);
1014 				if (n < 0) {
1015 					php_error_docref(NULL, E_WARNING, "Unable to parse DNS data received");
1016 					zend_array_destroy(Z_ARR_P(return_value));
1017 					php_dns_free_handle(handle);
1018 					RETURN_FALSE;
1019 				}
1020 				cp += n + QFIXEDSZ;
1021 			}
1022 
1023 			/* YAY! Our real answers! */
1024 			while (an-- && cp && cp < end) {
1025 				zval retval;
1026 
1027 				cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval);
1028 				if (Z_TYPE(retval) != IS_UNDEF && store_results) {
1029 					add_next_index_zval(return_value, &retval);
1030 				}
1031 			}
1032 
1033 			if (authns || addtl) {
1034 				/* List of Authoritative Name Servers
1035 				 * Process when only requesting addtl so that we can skip through the section
1036 				 */
1037 				while (ns-- > 0 && cp && cp < end) {
1038 					zval retval;
1039 
1040 					cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval);
1041 					if (Z_TYPE(retval) != IS_UNDEF) {
1042 						add_next_index_zval(authns, &retval);
1043 					}
1044 				}
1045 			}
1046 
1047 			if (addtl) {
1048 				/* Additional records associated with authoritative name servers */
1049 				while (ar-- > 0 && cp && cp < end) {
1050 					zval retval;
1051 
1052 					cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval);
1053 					if (Z_TYPE(retval) != IS_UNDEF) {
1054 						add_next_index_zval(addtl, &retval);
1055 					}
1056 				}
1057 			}
1058 			php_dns_free_handle(handle);
1059 		}
1060 	}
1061 }
1062 /* }}} */
1063 
1064 /* {{{ Get MX records corresponding to a given Internet host name */
PHP_FUNCTION(dns_get_mx)1065 PHP_FUNCTION(dns_get_mx)
1066 {
1067 	char *hostname;
1068 	size_t hostname_len;
1069 	zval *mx_list, *weight_list = NULL;
1070 	int count, qdc;
1071 	u_short type, weight;
1072 	querybuf answer;
1073 	char buf[MAXHOSTNAMELEN];
1074 	HEADER *hp;
1075 	u_char *cp, *end;
1076 	int i;
1077 #if defined(HAVE_DNS_SEARCH)
1078 	struct sockaddr_storage from;
1079 	uint32_t fromsize = sizeof(from);
1080 	dns_handle_t handle;
1081 #elif defined(HAVE_RES_NSEARCH)
1082 	struct __res_state state;
1083 	struct __res_state *handle = &state;
1084 #endif
1085 
1086 	ZEND_PARSE_PARAMETERS_START(2, 3)
1087 		Z_PARAM_STRING(hostname, hostname_len)
1088 		Z_PARAM_ZVAL(mx_list)
1089 		Z_PARAM_OPTIONAL
1090 		Z_PARAM_ZVAL(weight_list)
1091 	ZEND_PARSE_PARAMETERS_END();
1092 
1093 	mx_list = zend_try_array_init(mx_list);
1094 	if (!mx_list) {
1095 		RETURN_THROWS();
1096 	}
1097 
1098 	if (weight_list) {
1099 		weight_list = zend_try_array_init(weight_list);
1100 		if (!weight_list) {
1101 			RETURN_THROWS();
1102 		}
1103 	}
1104 
1105 #if defined(HAVE_DNS_SEARCH)
1106 	handle = dns_open(NULL);
1107 	if (handle == NULL) {
1108 		RETURN_FALSE;
1109 	}
1110 #elif defined(HAVE_RES_NSEARCH)
1111 	memset(&state, 0, sizeof(state));
1112 	if (res_ninit(handle)) {
1113 			RETURN_FALSE;
1114 	}
1115 #else
1116 	res_init();
1117 #endif
1118 
1119 	i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, answer.qb2, sizeof answer);
1120 	if (i < 0) {
1121 		php_dns_free_handle(handle);
1122 		RETURN_FALSE;
1123 	}
1124 	hp = (HEADER *)&answer;
1125 	cp = answer.qb2 + HFIXEDSZ;
1126 	end = answer.qb2 + i;
1127 	for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) {
1128 		if ((i = dn_skipname(cp, end)) < 0 ) {
1129 			php_dns_free_handle(handle);
1130 			RETURN_FALSE;
1131 		}
1132 	}
1133 	count = ntohs((unsigned short)hp->ancount);
1134 	while (--count >= 0 && cp < end) {
1135 		if ((i = dn_skipname(cp, end)) < 0 ) {
1136 			php_dns_free_handle(handle);
1137 			RETURN_FALSE;
1138 		}
1139 		cp += i;
1140 		GETSHORT(type, cp);
1141 		cp += INT16SZ + INT32SZ;
1142 		GETSHORT(i, cp);
1143 		if (type != DNS_T_MX) {
1144 			cp += i;
1145 			continue;
1146 		}
1147 		GETSHORT(weight, cp);
1148 		if ((i = dn_expand(answer.qb2, end, cp, buf, sizeof(buf)-1)) < 0) {
1149 			php_dns_free_handle(handle);
1150 			RETURN_FALSE;
1151 		}
1152 		cp += i;
1153 		add_next_index_string(mx_list, buf);
1154 		if (weight_list) {
1155 			add_next_index_long(weight_list, weight);
1156 		}
1157 	}
1158 	php_dns_free_handle(handle);
1159 	RETURN_BOOL(zend_hash_num_elements(Z_ARRVAL_P(mx_list)) != 0);
1160 }
1161 /* }}} */
1162 #endif /* HAVE_FULL_DNS_FUNCS */
1163 #endif /* !defined(PHP_WIN32) && HAVE_DNS_SEARCH_FUNC */
1164 
1165 #if HAVE_FULL_DNS_FUNCS && !defined(PHP_WIN32)
PHP_MINIT_FUNCTION(dns)1166 PHP_MINIT_FUNCTION(dns) {
1167 	REGISTER_LONG_CONSTANT("DNS_A",     PHP_DNS_A,     CONST_CS | CONST_PERSISTENT);
1168 	REGISTER_LONG_CONSTANT("DNS_NS",    PHP_DNS_NS,    CONST_CS | CONST_PERSISTENT);
1169 	REGISTER_LONG_CONSTANT("DNS_CNAME", PHP_DNS_CNAME, CONST_CS | CONST_PERSISTENT);
1170 	REGISTER_LONG_CONSTANT("DNS_SOA",   PHP_DNS_SOA,   CONST_CS | CONST_PERSISTENT);
1171 	REGISTER_LONG_CONSTANT("DNS_PTR",   PHP_DNS_PTR,   CONST_CS | CONST_PERSISTENT);
1172 	REGISTER_LONG_CONSTANT("DNS_HINFO", PHP_DNS_HINFO, CONST_CS | CONST_PERSISTENT);
1173 	REGISTER_LONG_CONSTANT("DNS_CAA",   PHP_DNS_CAA,   CONST_CS | CONST_PERSISTENT);
1174 	REGISTER_LONG_CONSTANT("DNS_MX",    PHP_DNS_MX,    CONST_CS | CONST_PERSISTENT);
1175 	REGISTER_LONG_CONSTANT("DNS_TXT",   PHP_DNS_TXT,   CONST_CS | CONST_PERSISTENT);
1176 	REGISTER_LONG_CONSTANT("DNS_SRV",   PHP_DNS_SRV,   CONST_CS | CONST_PERSISTENT);
1177 	REGISTER_LONG_CONSTANT("DNS_NAPTR", PHP_DNS_NAPTR, CONST_CS | CONST_PERSISTENT);
1178 	REGISTER_LONG_CONSTANT("DNS_AAAA",  PHP_DNS_AAAA,  CONST_CS | CONST_PERSISTENT);
1179 	REGISTER_LONG_CONSTANT("DNS_A6",    PHP_DNS_A6,    CONST_CS | CONST_PERSISTENT);
1180 	REGISTER_LONG_CONSTANT("DNS_ANY",   PHP_DNS_ANY,   CONST_CS | CONST_PERSISTENT);
1181 	REGISTER_LONG_CONSTANT("DNS_ALL",   PHP_DNS_ALL,   CONST_CS | CONST_PERSISTENT);
1182 	return SUCCESS;
1183 }
1184 #endif /* HAVE_FULL_DNS_FUNCS */
1185