2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/std/ext_std_network.h"
19 #include "hphp/runtime/ext/std/ext_std_network-internal.h"
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
24 #include <netinet/in.h>
26 #include <sys/socket.h>
28 #include <folly/IPAddress.h>
29 #include <folly/ScopeGuard.h>
31 #include "hphp/runtime/base/builtin-functions.h"
32 #include "hphp/runtime/base/file.h"
33 #include "hphp/runtime/base/runtime-option.h"
34 #include "hphp/runtime/ext/sockets/ext_sockets.h"
35 #include "hphp/runtime/ext/std/ext_std_function.h"
36 #include "hphp/runtime/ext/string/ext_string.h"
37 #include "hphp/runtime/server/server-stats.h"
38 #include "hphp/util/lock.h"
39 #include "hphp/util/network.h"
41 #if defined(__APPLE__)
42 # include <arpa/nameser_compat.h>
48 ///////////////////////////////////////////////////////////////////////////////
52 ResolverInit() : m_res(nullptr) {
53 m_res
= (struct __res_state
*)calloc(1, sizeof(*m_res
));
62 struct __res_state
*getResolver(void) {
67 static THREAD_LOCAL(ResolverInit
, s_res
);
71 memset(m_res
, 0, sizeof(*m_res
));
72 if (res_ninit(m_res
)) {
79 struct __res_state
*m_res
;
81 THREAD_LOCAL(ResolverInit
, ResolverInit::s_res
);
83 /* just a hack to free resources allocated by glibc in __res_nsend()
85 * res_thread_freeres() in glibc/resolv/res_init.c
86 * __libc_res_nsend() in resolv/res_send.c
89 static void php_dns_free_res(struct __res_state
*res
) {
90 #if defined(__GLIBC__)
92 for (ns
= 0; ns
< MAXNS
; ns
++) {
93 if (res
->_u
._ext
.nsaddrs
[ns
] != NULL
) {
94 free(res
->_u
._ext
.nsaddrs
[ns
]);
95 res
->_u
._ext
.nsaddrs
[ns
] = NULL
;
101 bool HHVM_FUNCTION(checkdnsrr
, const String
& host
,
102 const String
& type
/* = null_string */) {
104 if (!validate_dns_arguments(host
, type
, ntype
)) {
108 unsigned char ans
[MAXPACKET
];
109 struct __res_state
*res
;
110 res
= ResolverInit::s_res
.get()->getResolver();
115 int i
= res_nsearch(res
, host
.data(), C_IN
, ntype
, ans
, sizeof(ans
));
117 php_dns_free_res(res
);
142 s_refresh("refresh"),
145 s_minimum_ttl("minimum-ttl"),
147 s_masklen("masklen"),
150 s_services("services"),
152 s_replacement("replacement"),
169 #define CHECKCP(n) do { \
170 if ((cp + (n)) > end) { \
175 static unsigned char *php_parserr(unsigned char *cp
, unsigned char* end
,
177 int type_to_fetch
, bool store
,
179 unsigned short type
, cls ATTRIBUTE_UNUSED
, dlen
;
183 unsigned char *tp
, *p
;
184 char name
[255 + 2]; // IETF STD 13 section 3.1; 255 bytes
185 int have_v6_break
= 0, in_v6_break
= 0;
187 n
= dn_expand(answer
->qb2
, answer
->qb2
+65536, cp
, name
, sizeof(name
) - 2);
199 if (type_to_fetch
!= T_ANY
&& type
!= type_to_fetch
) {
209 subarray
.set(s_host
, String(name
, CopyString
));
213 subarray
.set(s_type
, s_A
);
214 snprintf(name
, sizeof(name
), "%d.%d.%d.%d", cp
[0], cp
[1], cp
[2], cp
[3]);
215 subarray
.set(s_ip
, String(name
, CopyString
));
220 subarray
.set(s_type
, s_MX
);
222 subarray
.set(s_pri
, n
);
225 if (type
== DNS_T_CNAME
) {
226 subarray
.set(s_type
, s_CNAME
);
230 if (type
== DNS_T_NS
) {
231 subarray
.set(s_type
, s_NS
);
235 if (type
== DNS_T_PTR
) {
236 subarray
.set(s_type
, s_PTR
);
238 n
= dn_expand(answer
->qb2
, answer
->qb2
+65536, cp
, name
, (sizeof name
) - 2);
243 subarray
.set(s_target
, String(name
, CopyString
));
246 /* See RFC 1010 for values */
247 subarray
.set(s_type
, s_HINFO
);
252 subarray
.set(s_cpu
, String((const char *)cp
, n
, CopyString
));
258 subarray
.set(s_os
, String((const char *)cp
, n
, CopyString
));
264 String s
= String(dlen
, ReserveString
);
265 tp
= (unsigned char *)s
.mutableData();
269 if ((n
+ l1
) > dlen
) {
270 // bad record, don't set anything
273 memcpy(tp
+ l1
, cp
+ l1
+ 1, n
);
280 subarray
.set(s_type
, s_TXT
);
281 subarray
.set(s_txt
, s
);
285 subarray
.set(s_type
, s_SOA
);
286 n
= dn_expand(answer
->qb2
, end
, cp
, name
, (sizeof name
) -2);
291 subarray
.set(s_mname
, String(name
, CopyString
));
292 n
= dn_expand(answer
->qb2
, end
, cp
, name
, (sizeof name
) -2);
297 subarray
.set(s_rname
, String(name
, CopyString
));
300 subarray
.set(s_serial
, n
);
302 subarray
.set(s_refresh
, n
);
304 subarray
.set(s_retry
, n
);
306 subarray
.set(s_expire
, n
);
308 subarray
.set(s_minimum_ttl
, n
);
311 tp
= (unsigned char *)name
;
313 for (i
= 0; i
< 8; i
++) {
316 if (tp
> (u_char
*)name
) {
321 tp
+= sprintf((char *)tp
, "%x", s
);
323 if (!have_v6_break
) {
328 } else if (!in_v6_break
) {
336 if (have_v6_break
&& in_v6_break
) {
341 subarray
.set(s_type
, s_AAAA
);
342 subarray
.set(s_ipv6
, String(name
, CopyString
));
346 subarray
.set(s_type
, s_A6
);
348 n
= ((int)cp
[0]) & 0xFF;
350 subarray
.set(s_masklen
, n
);
351 tp
= (unsigned char *)name
;
361 if (tp
> (u_char
*)name
) {
366 sprintf((char *)tp
, "%x", cp
[0] & 0xFF);
368 if (!have_v6_break
) {
373 } else if (!in_v6_break
) {
382 for (i
= (n
+ 8)/16; i
< 8; i
++) {
386 if (tp
> (u_char
*)name
) {
391 tp
+= sprintf((char*)tp
,"%x",s
);
393 if (!have_v6_break
) {
398 } else if (!in_v6_break
) {
406 if (have_v6_break
&& in_v6_break
) {
411 subarray
.set(s_ipv6
, String(name
, CopyString
));
413 n
= dn_expand(answer
->qb2
, end
, cp
, name
,
419 subarray
.set(s_chain
, String(name
, CopyString
));
424 subarray
.set(s_type
, s_SRV
);
426 subarray
.set(s_pri
, n
);
428 subarray
.set(s_weight
, n
);
430 subarray
.set(s_port
, n
);
431 n
= dn_expand(answer
->qb2
, end
, cp
, name
, (sizeof name
) - 2);
436 subarray
.set(s_target
, String(name
, CopyString
));
440 subarray
.set(s_type
, s_NAPTR
);
442 subarray
.set(s_order
, n
);
444 subarray
.set(s_pref
, n
);
450 subarray
.set(s_flags
, String((const char *)cp
, n
, CopyString
));
457 subarray
.set(s_services
, String((const char *)cp
, n
, CopyString
));
464 subarray
.set(s_regex
, String((const char *)cp
, n
, CopyString
));
467 n
= dn_expand(answer
->qb2
, end
, cp
, name
, (sizeof name
) - 2);
472 subarray
.set(s_replacement
, String(name
, CopyString
));
478 subarray
.set(s_class
, s_IN
);
479 subarray
.set(s_ttl
, (int)ttl
);
483 Variant
HHVM_FUNCTION(dns_get_record
, const String
& hostname
, int type
/*= -1*/,
484 VRefParam authnsRef
/* = null */,
485 VRefParam addtlRef
/* = null */) {
486 IOStatusHelper
io("dns_get_record", hostname
.data(), type
);
487 if (type
< 0) type
= PHP_DNS_ALL
;
488 if (type
& ~PHP_DNS_ALL
&& type
!= PHP_DNS_ANY
) {
489 raise_warning("Type '%d' not supported", type
);
493 unsigned char *cp
= NULL
, *end
= NULL
;
494 int qd
, an
, ns
= 0, ar
= 0;
497 /* - We emulate an or'ed type mask by querying type by type.
498 * (Steps 0 - NUMTYPES-1 )
499 * If additional info is wanted we check again with DNS_T_ANY
500 * (step NUMTYPES / NUMTYPES+1 )
501 * store_results is used to skip storing the results retrieved in step
502 * NUMTYPES+1 when results were already fetched.
503 * - In case of PHP_DNS_ANY we use the directly fetch DNS_T_ANY.
506 Array ret
= Array::Create();
507 bool first_query
= true;
508 bool store_results
= true;
509 for (int t
= (type
== PHP_DNS_ANY
? (PHP_DNS_NUM_TYPES
+ 1) : 0);
510 t
< PHP_DNS_NUM_TYPES
+ 2 || first_query
; t
++) {
514 case 0: type_to_fetch
= type
& PHP_DNS_A
? DNS_T_A
: 0; break;
515 case 1: type_to_fetch
= type
& PHP_DNS_NS
? DNS_T_NS
: 0; break;
516 case 2: type_to_fetch
= type
& PHP_DNS_CNAME
? DNS_T_CNAME
: 0; break;
517 case 3: type_to_fetch
= type
& PHP_DNS_SOA
? DNS_T_SOA
: 0; break;
518 case 4: type_to_fetch
= type
& PHP_DNS_PTR
? DNS_T_PTR
: 0; break;
519 case 5: type_to_fetch
= type
& PHP_DNS_HINFO
? DNS_T_HINFO
: 0; break;
520 case 6: type_to_fetch
= type
& PHP_DNS_MX
? DNS_T_MX
: 0; break;
521 case 7: type_to_fetch
= type
& PHP_DNS_TXT
? DNS_T_TXT
: 0; break;
522 case 8: type_to_fetch
= type
& PHP_DNS_AAAA
? DNS_T_AAAA
: 0; break;
523 case 9: type_to_fetch
= type
& PHP_DNS_SRV
? DNS_T_SRV
: 0; break;
524 case 10: type_to_fetch
= type
& PHP_DNS_NAPTR
? DNS_T_NAPTR
: 0; break;
525 case 11: type_to_fetch
= type
& PHP_DNS_A6
? DNS_T_A6
: 0; break;
526 case PHP_DNS_NUM_TYPES
:
527 store_results
= false;
530 case (PHP_DNS_NUM_TYPES
+ 1):
531 type_to_fetch
= DNS_T_ANY
;
534 if (!type_to_fetch
) continue;
536 struct __res_state
*res
;
537 res
= ResolverInit::s_res
.get()->getResolver();
542 int n
= res_nsearch(res
, hostname
.data(), C_IN
, type_to_fetch
,
543 answer
.qb2
, sizeof answer
);
546 php_dns_free_res(res
);
551 cp
= answer
.qb2
+ HFIXEDSZ
;
552 end
= answer
.qb2
+ n
;
553 hp
= (HEADER
*)&answer
;
554 qd
= ntohs(hp
->qdcount
);
555 an
= ntohs(hp
->ancount
);
556 ns
= ntohs(hp
->nscount
);
557 ar
= ntohs(hp
->arcount
);
559 /* Skip QD entries, they're only used by dn_expand later on */
561 n
= dn_skipname(cp
, end
);
563 raise_warning("Unable to parse DNS data received");
565 php_dns_free_res(res
);
571 /* YAY! Our real answers! */
572 while (an
-- && cp
&& cp
< end
) {
574 cp
= php_parserr(cp
, end
, &answer
, type_to_fetch
, store_results
, retval
);
575 if (!retval
.empty() && store_results
) {
580 php_dns_free_res(res
);
586 /* List of Authoritative Name Servers */
587 while (ns
-- > 0 && cp
&& cp
< end
) {
589 cp
= php_parserr(cp
, end
, &answer
, DNS_T_ANY
, true, retval
);
590 if (!retval
.empty()) {
591 authns
.append(retval
);
595 /* Additional records associated with authoritative name servers */
596 while (ar
-- > 0 && cp
&& cp
< end
) {
598 cp
= php_parserr(cp
, end
, &answer
, DNS_T_ANY
, true, retval
);
599 if (!retval
.empty()) {
600 addtl
.append(retval
);
604 authnsRef
.assignIfRef(authns
);
605 addtlRef
.assignIfRef(addtl
);
609 bool HHVM_FUNCTION(getmxrr
, const String
& hostname
,
610 VRefParam mxhostsRef
,
611 VRefParam weightsRef
/* = null */) {
612 IOStatusHelper
io("dns_get_mx", hostname
.data());
614 unsigned short type
, weight
;
615 unsigned char ans
[MAXPACKET
];
616 char buf
[255 + 1]; // IETF STD 13 section 3.1; 255 bytes
617 unsigned char *cp
, *end
;
622 mxhostsRef
.assignIfRef(mxhosts
);
623 weightsRef
.assignIfRef(weights
);
627 struct __res_state
*res
;
628 res
= ResolverInit::s_res
.get()->getResolver();
633 int i
= res_nsearch(res
, hostname
.data(), C_IN
, DNS_T_MX
,
634 (unsigned char*)&ans
, sizeof(ans
));
637 php_dns_free_res(res
);
640 if (i
> (int)sizeof(ans
)) {
643 HEADER
*hp
= (HEADER
*)&ans
;
644 cp
= (unsigned char *)&ans
+ HFIXEDSZ
;
645 end
= (unsigned char *)&ans
+i
;
646 for (qdc
= ntohs((unsigned short)hp
->qdcount
); qdc
--; cp
+= i
+ QFIXEDSZ
) {
647 if ((i
= dn_skipname(cp
, end
)) < 0 ) {
649 php_dns_free_res(res
);
653 count
= ntohs((unsigned short)hp
->ancount
);
654 while (--count
>= 0 && cp
< end
) {
655 if ((i
= dn_skipname(cp
, end
)) < 0 ) {
657 php_dns_free_res(res
);
662 cp
+= INT16SZ
+ INT32SZ
;
664 if (type
!= DNS_T_MX
) {
668 GETSHORT(weight
, cp
);
669 if ((i
= dn_expand(ans
, end
, cp
, buf
, sizeof(buf
)-1)) < 0) {
671 php_dns_free_res(res
);
675 mxhosts
.append(String(buf
, CopyString
));
676 weights
.append(weight
);
679 php_dns_free_res(res
);
683 ///////////////////////////////////////////////////////////////////////////////