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