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