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