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