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