xref: /PHP-7.0/ext/standard/dns.c (revision 61538eba)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2017 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 (type_to_fetch != T_ANY && type != type_to_fetch) {
463 		cp += dlen;
464 		return cp;
465 	}
466 
467 	if (!store) {
468 		cp += dlen;
469 		return cp;
470 	}
471 
472 	array_init(subarray);
473 
474 	add_assoc_string(subarray, "host", name);
475 	add_assoc_string(subarray, "class", "IN");
476 	add_assoc_long(subarray, "ttl", ttl);
477 	(void) class;
478 
479 	if (raw) {
480 		add_assoc_long(subarray, "type", type);
481 		add_assoc_stringl(subarray, "data", (char*) cp, (uint) dlen);
482 		cp += dlen;
483 		return cp;
484 	}
485 
486 	switch (type) {
487 		case DNS_T_A:
488 			CHECKCP(4);
489 			add_assoc_string(subarray, "type", "A");
490 			snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
491 			add_assoc_string(subarray, "ip", name);
492 			cp += dlen;
493 			break;
494 		case DNS_T_MX:
495 			CHECKCP(2);
496 			add_assoc_string(subarray, "type", "MX");
497 			GETSHORT(n, cp);
498 			add_assoc_long(subarray, "pri", n);
499 			/* no break; */
500 		case DNS_T_CNAME:
501 			if (type == DNS_T_CNAME) {
502 				add_assoc_string(subarray, "type", "CNAME");
503 			}
504 			/* no break; */
505 		case DNS_T_NS:
506 			if (type == DNS_T_NS) {
507 				add_assoc_string(subarray, "type", "NS");
508 			}
509 			/* no break; */
510 		case DNS_T_PTR:
511 			if (type == DNS_T_PTR) {
512 				add_assoc_string(subarray, "type", "PTR");
513 			}
514 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
515 			if (n < 0) {
516 				return NULL;
517 			}
518 			cp += n;
519 			add_assoc_string(subarray, "target", name);
520 			break;
521 		case DNS_T_HINFO:
522 			/* See RFC 1010 for values */
523 			add_assoc_string(subarray, "type", "HINFO");
524 			CHECKCP(1);
525 			n = *cp & 0xFF;
526 			cp++;
527 			CHECKCP(n);
528 			add_assoc_stringl(subarray, "cpu", (char*)cp, n);
529 			cp += n;
530 			CHECKCP(1);
531 			n = *cp & 0xFF;
532 			cp++;
533 			CHECKCP(n);
534 			add_assoc_stringl(subarray, "os", (char*)cp, n);
535 			cp += n;
536 			break;
537 		case DNS_T_CAA:
538 			/* See RFC 6844 for values https://tools.ietf.org/html/rfc6844 */
539 			add_assoc_string(subarray, "type", "CAA");
540 			// 1 flag byte
541 			CHECKCP(1);
542 			n = *cp & 0xFF;
543 			add_assoc_long(subarray, "flags", n);
544 			cp++;
545 			// Tag length (1 byte)
546 			CHECKCP(1);
547 			n = *cp & 0xFF;
548 			cp++;
549 			CHECKCP(n);
550 			add_assoc_stringl(subarray, "tag", (char*)cp, n);
551 			cp += n;
552 			add_assoc_string(subarray, "value", (char*)cp);
553 			break;
554 		case DNS_T_TXT:
555 			{
556 				int l1 = 0, l2 = 0;
557 				zval entries;
558 				zend_string *tp;
559 
560 				add_assoc_string(subarray, "type", "TXT");
561 				tp = zend_string_alloc(dlen, 0);
562 
563 				array_init(&entries);
564 
565 				while (l1 < dlen) {
566 					n = cp[l1];
567 					if ((l1 + n) >= dlen) {
568 						// Invalid chunk length, truncate
569 						n = dlen - (l1 + 1);
570 					}
571 					if (n) {
572 						memcpy(ZSTR_VAL(tp) + l2 , cp + l1 + 1, n);
573 						add_next_index_stringl(&entries, (char *) cp + l1 + 1, n);
574 					}
575 					l1 = l1 + n + 1;
576 					l2 = l2 + n;
577 				}
578 				ZSTR_VAL(tp)[l2] = '\0';
579 				ZSTR_LEN(tp) = l2;
580 				cp += dlen;
581 
582 				add_assoc_str(subarray, "txt", tp);
583 				add_assoc_zval(subarray, "entries", &entries);
584 			}
585 			break;
586 		case DNS_T_SOA:
587 			add_assoc_string(subarray, "type", "SOA");
588 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
589 			if (n < 0) {
590 				return NULL;
591 			}
592 			cp += n;
593 			add_assoc_string(subarray, "mname", name);
594 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
595 			if (n < 0) {
596 				return NULL;
597 			}
598 			cp += n;
599 			add_assoc_string(subarray, "rname", name);
600 			CHECKCP(5*4);
601 			GETLONG(n, cp);
602 			add_assoc_long(subarray, "serial", n);
603 			GETLONG(n, cp);
604 			add_assoc_long(subarray, "refresh", n);
605 			GETLONG(n, cp);
606 			add_assoc_long(subarray, "retry", n);
607 			GETLONG(n, cp);
608 			add_assoc_long(subarray, "expire", n);
609 			GETLONG(n, cp);
610 			add_assoc_long(subarray, "minimum-ttl", n);
611 			break;
612 		case DNS_T_AAAA:
613 			tp = (u_char*)name;
614 			CHECKCP(8*2);
615 			for(i=0; i < 8; i++) {
616 				GETSHORT(s, cp);
617 				if (s != 0) {
618 					if (tp > (u_char *)name) {
619 						in_v6_break = 0;
620 						tp[0] = ':';
621 						tp++;
622 					}
623 					tp += sprintf((char*)tp,"%x",s);
624 				} else {
625 					if (!have_v6_break) {
626 						have_v6_break = 1;
627 						in_v6_break = 1;
628 						tp[0] = ':';
629 						tp++;
630 					} else if (!in_v6_break) {
631 						tp[0] = ':';
632 						tp++;
633 						tp[0] = '0';
634 						tp++;
635 					}
636 				}
637 			}
638 			if (have_v6_break && in_v6_break) {
639 				tp[0] = ':';
640 				tp++;
641 			}
642 			tp[0] = '\0';
643 			add_assoc_string(subarray, "type", "AAAA");
644 			add_assoc_string(subarray, "ipv6", name);
645 			break;
646 		case DNS_T_A6:
647 			p = cp;
648 			add_assoc_string(subarray, "type", "A6");
649 			CHECKCP(1);
650 			n = ((int)cp[0]) & 0xFF;
651 			cp++;
652 			add_assoc_long(subarray, "masklen", n);
653 			tp = (u_char*)name;
654 			if (n > 15) {
655 				have_v6_break = 1;
656 				in_v6_break = 1;
657 				tp[0] = ':';
658 				tp++;
659 			}
660 			if (n % 16 > 8) {
661 				/* Partial short */
662 				if (cp[0] != 0) {
663 					if (tp > (u_char *)name) {
664 						in_v6_break = 0;
665 						tp[0] = ':';
666 						tp++;
667 					}
668 					sprintf((char*)tp, "%x", cp[0] & 0xFF);
669 				} else {
670 					if (!have_v6_break) {
671 						have_v6_break = 1;
672 						in_v6_break = 1;
673 						tp[0] = ':';
674 						tp++;
675 					} else if (!in_v6_break) {
676 						tp[0] = ':';
677 						tp++;
678 						tp[0] = '0';
679 						tp++;
680 					}
681 				}
682 				cp++;
683 			}
684 			for (i = (n + 8) / 16; i < 8; i++) {
685 				CHECKCP(2);
686 				GETSHORT(s, cp);
687 				if (s != 0) {
688 					if (tp > (u_char *)name) {
689 						in_v6_break = 0;
690 						tp[0] = ':';
691 						tp++;
692 					}
693 					tp += sprintf((char*)tp,"%x",s);
694 				} else {
695 					if (!have_v6_break) {
696 						have_v6_break = 1;
697 						in_v6_break = 1;
698 						tp[0] = ':';
699 						tp++;
700 					} else if (!in_v6_break) {
701 						tp[0] = ':';
702 						tp++;
703 						tp[0] = '0';
704 						tp++;
705 					}
706 				}
707 			}
708 			if (have_v6_break && in_v6_break) {
709 				tp[0] = ':';
710 				tp++;
711 			}
712 			tp[0] = '\0';
713 			add_assoc_string(subarray, "ipv6", name);
714 			if (cp < p + dlen) {
715 				n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
716 				if (n < 0) {
717 					return NULL;
718 				}
719 				cp += n;
720 				add_assoc_string(subarray, "chain", name);
721 			}
722 			break;
723 		case DNS_T_SRV:
724 			CHECKCP(3*2);
725 			add_assoc_string(subarray, "type", "SRV");
726 			GETSHORT(n, cp);
727 			add_assoc_long(subarray, "pri", n);
728 			GETSHORT(n, cp);
729 			add_assoc_long(subarray, "weight", n);
730 			GETSHORT(n, cp);
731 			add_assoc_long(subarray, "port", n);
732 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
733 			if (n < 0) {
734 				return NULL;
735 			}
736 			cp += n;
737 			add_assoc_string(subarray, "target", name);
738 			break;
739 		case DNS_T_NAPTR:
740 			CHECKCP(2*2);
741 			add_assoc_string(subarray, "type", "NAPTR");
742 			GETSHORT(n, cp);
743 			add_assoc_long(subarray, "order", n);
744 			GETSHORT(n, cp);
745 			add_assoc_long(subarray, "pref", n);
746 
747 			CHECKCP(1);
748 			n = (cp[0] & 0xFF);
749 			cp++;
750 			CHECKCP(n);
751 			add_assoc_stringl(subarray, "flags", (char*)cp, n);
752 			cp += n;
753 
754 			CHECKCP(1);
755 			n = (cp[0] & 0xFF);
756 			cp++;
757 			CHECKCP(n);
758 			add_assoc_stringl(subarray, "services", (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, "regex", (char*)cp, n);
766 			cp += n;
767 
768 			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
769 			if (n < 0) {
770 				return NULL;
771 			}
772 			cp += n;
773 			add_assoc_string(subarray, "replacement", name);
774 			break;
775 		default:
776 			zval_ptr_dtor(subarray);
777 			ZVAL_UNDEF(subarray);
778 			cp += dlen;
779 			break;
780 	}
781 
782 	return cp;
783 }
784 /* }}} */
785 
786 /* {{{ proto array|false dns_get_record(string hostname [, int type[, array &authns[, array &addtl[, bool raw]]]])
787    Get any Resource Record corresponding to a given Internet host name */
PHP_FUNCTION(dns_get_record)788 PHP_FUNCTION(dns_get_record)
789 {
790 	char *hostname;
791 	size_t hostname_len;
792 	long type_param = PHP_DNS_ANY;
793 	zval *authns = NULL, *addtl = NULL;
794 	int type_to_fetch;
795 #if defined(HAVE_DNS_SEARCH)
796 	struct sockaddr_storage from;
797 	uint32_t fromsize = sizeof(from);
798 	dns_handle_t handle;
799 #elif defined(HAVE_RES_NSEARCH)
800 	struct __res_state state;
801 	struct __res_state *handle = &state;
802 #endif
803 	HEADER *hp;
804 	querybuf answer;
805 	u_char *cp = NULL, *end = NULL;
806 	int n, qd, an, ns = 0, ar = 0;
807 	int type, first_query = 1, store_results = 1;
808 	zend_bool raw = 0;
809 
810 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lz/!z/!b",
811 			&hostname, &hostname_len, &type_param, &authns, &addtl, &raw) == FAILURE) {
812 		return;
813 	}
814 
815 	if (authns) {
816 		zval_dtor(authns);
817 		array_init(authns);
818 	}
819 	if (addtl) {
820 		zval_dtor(addtl);
821 		array_init(addtl);
822 	}
823 
824 	if (!raw) {
825 		if ((type_param & ~PHP_DNS_ALL) && (type_param != PHP_DNS_ANY)) {
826 			php_error_docref(NULL, E_WARNING, "Type '%ld' not supported", type_param);
827 			RETURN_FALSE;
828 		}
829 	} else {
830 		if ((type_param < 1) || (type_param > 0xFFFF)) {
831 			php_error_docref(NULL, E_WARNING,
832 				"Numeric DNS record type must be between 1 and 65535, '%ld' given", type_param);
833 			RETURN_FALSE;
834 		}
835 	}
836 
837 	/* Initialize the return array */
838 	array_init(return_value);
839 
840 	/* - We emulate an or'ed type mask by querying type by type. (Steps 0 - NUMTYPES-1 )
841 	 *   If additional info is wanted we check again with DNS_T_ANY (step NUMTYPES / NUMTYPES+1 )
842 	 *   store_results is used to skip storing the results retrieved in step
843 	 *   NUMTYPES+1 when results were already fetched.
844 	 * - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY. (step NUMTYPES+1 )
845 	 * - In case of raw mode, we query only the requestd type instead of looping type by type
846 	 *   before going with the additional info stuff.
847 	 */
848 
849 	if (raw) {
850 		type = -1;
851 	} else if (type_param == PHP_DNS_ANY) {
852 		type = PHP_DNS_NUM_TYPES + 1;
853 	} else {
854 		type = 0;
855 	}
856 
857 	for ( ;
858 		type < (addtl ? (PHP_DNS_NUM_TYPES + 2) : PHP_DNS_NUM_TYPES) || first_query;
859 		type++
860 	) {
861 		first_query = 0;
862 		switch (type) {
863 			case -1: /* raw */
864 				type_to_fetch = type_param;
865 				/* skip over the rest and go directly to additional records */
866 				type = PHP_DNS_NUM_TYPES - 1;
867 				break;
868 			case 0:
869 				type_to_fetch = type_param&PHP_DNS_A     ? DNS_T_A     : 0;
870 				break;
871 			case 1:
872 				type_to_fetch = type_param&PHP_DNS_NS    ? DNS_T_NS    : 0;
873 				break;
874 			case 2:
875 				type_to_fetch = type_param&PHP_DNS_CNAME ? DNS_T_CNAME : 0;
876 				break;
877 			case 3:
878 				type_to_fetch = type_param&PHP_DNS_SOA   ? DNS_T_SOA   : 0;
879 				break;
880 			case 4:
881 				type_to_fetch = type_param&PHP_DNS_PTR   ? DNS_T_PTR   : 0;
882 				break;
883 			case 5:
884 				type_to_fetch = type_param&PHP_DNS_HINFO ? DNS_T_HINFO : 0;
885 				break;
886 			case 6:
887 				type_to_fetch = type_param&PHP_DNS_MX    ? DNS_T_MX    : 0;
888 				break;
889 			case 7:
890 				type_to_fetch = type_param&PHP_DNS_TXT   ? DNS_T_TXT   : 0;
891 				break;
892 			case 8:
893 				type_to_fetch = type_param&PHP_DNS_AAAA	 ? DNS_T_AAAA  : 0;
894 				break;
895 			case 9:
896 				type_to_fetch = type_param&PHP_DNS_SRV   ? DNS_T_SRV   : 0;
897 				break;
898 			case 10:
899 				type_to_fetch = type_param&PHP_DNS_NAPTR ? DNS_T_NAPTR : 0;
900 				break;
901 			case 11:
902 				type_to_fetch = type_param&PHP_DNS_A6	 ? DNS_T_A6 : 0;
903 				break;
904 			case 12:
905 				type_to_fetch = type_param&PHP_DNS_CAA ? DNS_T_CAA : 0;
906 				break;
907 			case PHP_DNS_NUM_TYPES:
908 				store_results = 0;
909 				continue;
910 			default:
911 			case (PHP_DNS_NUM_TYPES + 1):
912 				type_to_fetch = DNS_T_ANY;
913 				break;
914 		}
915 
916 		if (type_to_fetch) {
917 #if defined(HAVE_DNS_SEARCH)
918 			handle = dns_open(NULL);
919 			if (handle == NULL) {
920 				zval_dtor(return_value);
921 				RETURN_FALSE;
922 			}
923 #elif defined(HAVE_RES_NSEARCH)
924 		    memset(&state, 0, sizeof(state));
925 		    if (res_ninit(handle)) {
926 		    	zval_dtor(return_value);
927 				RETURN_FALSE;
928 			}
929 #else
930 			res_init();
931 #endif
932 
933 			n = php_dns_search(handle, hostname, C_IN, type_to_fetch, answer.qb2, sizeof answer);
934 
935 			if (n < 0) {
936 				php_dns_free_handle(handle);
937 				switch (h_errno) {
938 					case NO_DATA:
939 					case HOST_NOT_FOUND:
940 						continue;
941 
942 					case NO_RECOVERY:
943 						php_error_docref(NULL, E_WARNING, "An unexpected server failure occurred.");
944 						break;
945 
946 					case TRY_AGAIN:
947 						php_error_docref(NULL, E_WARNING, "A temporary server error occurred.");
948 						break;
949 
950 					default:
951 						php_error_docref(NULL, E_WARNING, "DNS Query failed");
952 				}
953 				zval_dtor(return_value);
954 				RETURN_FALSE;
955 			}
956 
957 			cp = answer.qb2 + HFIXEDSZ;
958 			end = answer.qb2 + n;
959 			hp = (HEADER *)&answer;
960 			qd = ntohs(hp->qdcount);
961 			an = ntohs(hp->ancount);
962 			ns = ntohs(hp->nscount);
963 			ar = ntohs(hp->arcount);
964 
965 			/* Skip QD entries, they're only used by dn_expand later on */
966 			while (qd-- > 0) {
967 				n = dn_skipname(cp, end);
968 				if (n < 0) {
969 					php_error_docref(NULL, E_WARNING, "Unable to parse DNS data received");
970 					zval_dtor(return_value);
971 					php_dns_free_handle(handle);
972 					RETURN_FALSE;
973 				}
974 				cp += n + QFIXEDSZ;
975 			}
976 
977 			/* YAY! Our real answers! */
978 			while (an-- && cp && cp < end) {
979 				zval retval;
980 
981 				cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval);
982 				if (Z_TYPE(retval) != IS_UNDEF && store_results) {
983 					add_next_index_zval(return_value, &retval);
984 				}
985 			}
986 
987 			if (authns || addtl) {
988 				/* List of Authoritative Name Servers
989 				 * Process when only requesting addtl so that we can skip through the section
990 				 */
991 				while (ns-- > 0 && cp && cp < end) {
992 					zval retval;
993 
994 					cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval);
995 					if (Z_TYPE(retval) != IS_UNDEF) {
996 						add_next_index_zval(authns, &retval);
997 					}
998 				}
999 			}
1000 
1001 			if (addtl) {
1002 				/* Additional records associated with authoritative name servers */
1003 				while (ar-- > 0 && cp && cp < end) {
1004 					zval retval;
1005 
1006 					cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval);
1007 					if (Z_TYPE(retval) != IS_UNDEF) {
1008 						add_next_index_zval(addtl, &retval);
1009 					}
1010 				}
1011 			}
1012 			php_dns_free_handle(handle);
1013 		}
1014 	}
1015 }
1016 /* }}} */
1017 
1018 /* {{{ proto bool dns_get_mx(string hostname, array mxhosts [, array weight])
1019    Get MX records corresponding to a given Internet host name */
PHP_FUNCTION(dns_get_mx)1020 PHP_FUNCTION(dns_get_mx)
1021 {
1022 	char *hostname;
1023 	size_t hostname_len;
1024 	zval *mx_list, *weight_list = NULL;
1025 	int count, qdc;
1026 	u_short type, weight;
1027 	u_char ans[MAXPACKET];
1028 	char buf[MAXHOSTNAMELEN];
1029 	HEADER *hp;
1030 	u_char *cp, *end;
1031 	int i;
1032 #if defined(HAVE_DNS_SEARCH)
1033 	struct sockaddr_storage from;
1034 	uint32_t fromsize = sizeof(from);
1035 	dns_handle_t handle;
1036 #elif defined(HAVE_RES_NSEARCH)
1037 	struct __res_state state;
1038 	struct __res_state *handle = &state;
1039 #endif
1040 
1041 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|z/", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) {
1042 		return;
1043 	}
1044 
1045 	zval_dtor(mx_list);
1046 	array_init(mx_list);
1047 
1048 	if (weight_list) {
1049 		zval_dtor(weight_list);
1050 		array_init(weight_list);
1051 	}
1052 
1053 #if defined(HAVE_DNS_SEARCH)
1054 	handle = dns_open(NULL);
1055 	if (handle == NULL) {
1056 		RETURN_FALSE;
1057 	}
1058 #elif defined(HAVE_RES_NSEARCH)
1059     memset(&state, 0, sizeof(state));
1060     if (res_ninit(handle)) {
1061 			RETURN_FALSE;
1062 	}
1063 #else
1064 	res_init();
1065 #endif
1066 
1067 	i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, (u_char *)&ans, sizeof(ans));
1068 	if (i < 0) {
1069 		RETURN_FALSE;
1070 	}
1071 	if (i > (int)sizeof(ans)) {
1072 		i = sizeof(ans);
1073 	}
1074 	hp = (HEADER *)&ans;
1075 	cp = (u_char *)&ans + HFIXEDSZ;
1076 	end = (u_char *)&ans +i;
1077 	for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) {
1078 		if ((i = dn_skipname(cp, end)) < 0 ) {
1079 			php_dns_free_handle(handle);
1080 			RETURN_FALSE;
1081 		}
1082 	}
1083 	count = ntohs((unsigned short)hp->ancount);
1084 	while (--count >= 0 && cp < end) {
1085 		if ((i = dn_skipname(cp, end)) < 0 ) {
1086 			php_dns_free_handle(handle);
1087 			RETURN_FALSE;
1088 		}
1089 		cp += i;
1090 		GETSHORT(type, cp);
1091 		cp += INT16SZ + INT32SZ;
1092 		GETSHORT(i, cp);
1093 		if (type != DNS_T_MX) {
1094 			cp += i;
1095 			continue;
1096 		}
1097 		GETSHORT(weight, cp);
1098 		if ((i = dn_expand(ans, end, cp, buf, sizeof(buf)-1)) < 0) {
1099 			php_dns_free_handle(handle);
1100 			RETURN_FALSE;
1101 		}
1102 		cp += i;
1103 		add_next_index_string(mx_list, buf);
1104 		if (weight_list) {
1105 			add_next_index_long(weight_list, weight);
1106 		}
1107 	}
1108 	php_dns_free_handle(handle);
1109 	RETURN_TRUE;
1110 }
1111 /* }}} */
1112 #endif /* HAVE_FULL_DNS_FUNCS */
1113 #endif /* !defined(PHP_WIN32) && (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE))) */
1114 
1115 #if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32)
PHP_MINIT_FUNCTION(dns)1116 PHP_MINIT_FUNCTION(dns) {
1117 	REGISTER_LONG_CONSTANT("DNS_A",     PHP_DNS_A,     CONST_CS | CONST_PERSISTENT);
1118 	REGISTER_LONG_CONSTANT("DNS_NS",    PHP_DNS_NS,    CONST_CS | CONST_PERSISTENT);
1119 	REGISTER_LONG_CONSTANT("DNS_CNAME", PHP_DNS_CNAME, CONST_CS | CONST_PERSISTENT);
1120 	REGISTER_LONG_CONSTANT("DNS_SOA",   PHP_DNS_SOA,   CONST_CS | CONST_PERSISTENT);
1121 	REGISTER_LONG_CONSTANT("DNS_PTR",   PHP_DNS_PTR,   CONST_CS | CONST_PERSISTENT);
1122 	REGISTER_LONG_CONSTANT("DNS_HINFO", PHP_DNS_HINFO, CONST_CS | CONST_PERSISTENT);
1123 	REGISTER_LONG_CONSTANT("DNS_CAA",   PHP_DNS_CAA,   CONST_CS | CONST_PERSISTENT);
1124 	REGISTER_LONG_CONSTANT("DNS_MX",    PHP_DNS_MX,    CONST_CS | CONST_PERSISTENT);
1125 	REGISTER_LONG_CONSTANT("DNS_TXT",   PHP_DNS_TXT,   CONST_CS | CONST_PERSISTENT);
1126 	REGISTER_LONG_CONSTANT("DNS_SRV",   PHP_DNS_SRV,   CONST_CS | CONST_PERSISTENT);
1127 	REGISTER_LONG_CONSTANT("DNS_NAPTR", PHP_DNS_NAPTR, CONST_CS | CONST_PERSISTENT);
1128 	REGISTER_LONG_CONSTANT("DNS_AAAA",  PHP_DNS_AAAA,  CONST_CS | CONST_PERSISTENT);
1129 	REGISTER_LONG_CONSTANT("DNS_A6",    PHP_DNS_A6,    CONST_CS | CONST_PERSISTENT);
1130 	REGISTER_LONG_CONSTANT("DNS_ANY",   PHP_DNS_ANY,   CONST_CS | CONST_PERSISTENT);
1131 	REGISTER_LONG_CONSTANT("DNS_ALL",   PHP_DNS_ALL,   CONST_CS | CONST_PERSISTENT);
1132 	return SUCCESS;
1133 }
1134 #endif /* HAVE_FULL_DNS_FUNCS */
1135 
1136 /*
1137  * Local variables:
1138  * tab-width: 4
1139  * c-basic-offset: 4
1140  * End:
1141  * vim600: sw=4 ts=4 fdm=marker
1142  * vim<600: sw=4 ts=4
1143  */
1144