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