1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 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
123 static char *php_gethostbyaddr(char *ip);
124 static char *php_gethostbyname(char *name);
125
126 #ifdef HAVE_GETHOSTNAME
127 /* {{{ proto string gethostname()
128 Get the host name of the current machine */
PHP_FUNCTION(gethostname)129 PHP_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 */
PHP_FUNCTION(gethostbyaddr)153 PHP_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 */
php_gethostbyaddr(char * ip)179 static 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 */
PHP_FUNCTION(gethostbyname)215 PHP_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. */
PHP_FUNCTION(gethostbynamel)238 PHP_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 */
php_gethostbyname(char * name)271 static 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
325 typedef 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__)
_php_dns_free_res(struct __res_state res)338 static 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 */
PHP_FUNCTION(dns_check_record)353 PHP_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 */
php_parserr(u_char * cp,u_char * end,querybuf * answer,int type_to_fetch,int store,int raw,zval ** subarray)433 static 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 */
PHP_FUNCTION(dns_get_record)765 PHP_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 continue;
912 }
913
914 cp = answer.qb2 + HFIXEDSZ;
915 end = answer.qb2 + n;
916 hp = (HEADER *)&answer;
917 qd = ntohs(hp->qdcount);
918 an = ntohs(hp->ancount);
919 ns = ntohs(hp->nscount);
920 ar = ntohs(hp->arcount);
921
922 /* Skip QD entries, they're only used by dn_expand later on */
923 while (qd-- > 0) {
924 n = dn_skipname(cp, end);
925 if (n < 0) {
926 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse DNS data received");
927 zval_dtor(return_value);
928 php_dns_free_handle(handle);
929 RETURN_FALSE;
930 }
931 cp += n + QFIXEDSZ;
932 }
933
934 /* YAY! Our real answers! */
935 while (an-- && cp && cp < end) {
936 zval *retval;
937
938 cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval);
939 if (retval != NULL && store_results) {
940 add_next_index_zval(return_value, retval);
941 }
942 }
943
944 if (authns || addtl) {
945 /* List of Authoritative Name Servers
946 * Process when only requesting addtl so that we can skip through the section
947 */
948 while (ns-- > 0 && cp && cp < end) {
949 zval *retval = NULL;
950
951 cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval);
952 if (retval != NULL) {
953 add_next_index_zval(authns, retval);
954 }
955 }
956 }
957
958 if (addtl) {
959 /* Additional records associated with authoritative name servers */
960 while (ar-- > 0 && cp && cp < end) {
961 zval *retval = NULL;
962
963 cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval);
964 if (retval != NULL) {
965 add_next_index_zval(addtl, retval);
966 }
967 }
968 }
969 php_dns_free_handle(handle);
970 }
971 }
972 }
973 /* }}} */
974
975 /* {{{ proto bool dns_get_mx(string hostname, array mxhosts [, array weight])
976 Get MX records corresponding to a given Internet host name */
PHP_FUNCTION(dns_get_mx)977 PHP_FUNCTION(dns_get_mx)
978 {
979 char *hostname;
980 int hostname_len;
981 zval *mx_list, *weight_list = NULL;
982 int count, qdc;
983 u_short type, weight;
984 u_char ans[MAXPACKET];
985 char buf[MAXHOSTNAMELEN];
986 HEADER *hp;
987 u_char *cp, *end;
988 int i;
989 #if defined(HAVE_DNS_SEARCH)
990 struct sockaddr_storage from;
991 uint32_t fromsize = sizeof(from);
992 dns_handle_t handle;
993 #elif defined(HAVE_RES_NSEARCH)
994 struct __res_state state;
995 struct __res_state *handle = &state;
996 #endif
997
998 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z", &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) {
999 return;
1000 }
1001
1002 zval_dtor(mx_list);
1003 array_init(mx_list);
1004
1005 if (weight_list) {
1006 zval_dtor(weight_list);
1007 array_init(weight_list);
1008 }
1009
1010 #if defined(HAVE_DNS_SEARCH)
1011 handle = dns_open(NULL);
1012 if (handle == NULL) {
1013 RETURN_FALSE;
1014 }
1015 #elif defined(HAVE_RES_NSEARCH)
1016 memset(&state, 0, sizeof(state));
1017 if (res_ninit(handle)) {
1018 RETURN_FALSE;
1019 }
1020 #else
1021 res_init();
1022 #endif
1023
1024 i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, (u_char *)&ans, sizeof(ans));
1025 if (i < 0) {
1026 RETURN_FALSE;
1027 }
1028 if (i > (int)sizeof(ans)) {
1029 i = sizeof(ans);
1030 }
1031 hp = (HEADER *)&ans;
1032 cp = (u_char *)&ans + HFIXEDSZ;
1033 end = (u_char *)&ans +i;
1034 for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) {
1035 if ((i = dn_skipname(cp, end)) < 0 ) {
1036 php_dns_free_handle(handle);
1037 RETURN_FALSE;
1038 }
1039 }
1040 count = ntohs((unsigned short)hp->ancount);
1041 while (--count >= 0 && cp < end) {
1042 if ((i = dn_skipname(cp, end)) < 0 ) {
1043 php_dns_free_handle(handle);
1044 RETURN_FALSE;
1045 }
1046 cp += i;
1047 GETSHORT(type, cp);
1048 cp += INT16SZ + INT32SZ;
1049 GETSHORT(i, cp);
1050 if (type != DNS_T_MX) {
1051 cp += i;
1052 continue;
1053 }
1054 GETSHORT(weight, cp);
1055 if ((i = dn_expand(ans, end, cp, buf, sizeof(buf)-1)) < 0) {
1056 php_dns_free_handle(handle);
1057 RETURN_FALSE;
1058 }
1059 cp += i;
1060 add_next_index_string(mx_list, buf, 1);
1061 if (weight_list) {
1062 add_next_index_long(weight_list, weight);
1063 }
1064 }
1065 php_dns_free_handle(handle);
1066 RETURN_TRUE;
1067 }
1068 /* }}} */
1069 #endif /* HAVE_FULL_DNS_FUNCS */
1070 #endif /* !defined(PHP_WIN32) && (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE))) */
1071
1072 #if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32)
PHP_MINIT_FUNCTION(dns)1073 PHP_MINIT_FUNCTION(dns) {
1074 REGISTER_LONG_CONSTANT("DNS_A", PHP_DNS_A, CONST_CS | CONST_PERSISTENT);
1075 REGISTER_LONG_CONSTANT("DNS_NS", PHP_DNS_NS, CONST_CS | CONST_PERSISTENT);
1076 REGISTER_LONG_CONSTANT("DNS_CNAME", PHP_DNS_CNAME, CONST_CS | CONST_PERSISTENT);
1077 REGISTER_LONG_CONSTANT("DNS_SOA", PHP_DNS_SOA, CONST_CS | CONST_PERSISTENT);
1078 REGISTER_LONG_CONSTANT("DNS_PTR", PHP_DNS_PTR, CONST_CS | CONST_PERSISTENT);
1079 REGISTER_LONG_CONSTANT("DNS_HINFO", PHP_DNS_HINFO, CONST_CS | CONST_PERSISTENT);
1080 REGISTER_LONG_CONSTANT("DNS_MX", PHP_DNS_MX, CONST_CS | CONST_PERSISTENT);
1081 REGISTER_LONG_CONSTANT("DNS_TXT", PHP_DNS_TXT, CONST_CS | CONST_PERSISTENT);
1082 REGISTER_LONG_CONSTANT("DNS_SRV", PHP_DNS_SRV, CONST_CS | CONST_PERSISTENT);
1083 REGISTER_LONG_CONSTANT("DNS_NAPTR", PHP_DNS_NAPTR, CONST_CS | CONST_PERSISTENT);
1084 REGISTER_LONG_CONSTANT("DNS_AAAA", PHP_DNS_AAAA, CONST_CS | CONST_PERSISTENT);
1085 REGISTER_LONG_CONSTANT("DNS_A6", PHP_DNS_A6, CONST_CS | CONST_PERSISTENT);
1086 REGISTER_LONG_CONSTANT("DNS_ANY", PHP_DNS_ANY, CONST_CS | CONST_PERSISTENT);
1087 REGISTER_LONG_CONSTANT("DNS_ALL", PHP_DNS_ALL, CONST_CS | CONST_PERSISTENT);
1088 return SUCCESS;
1089 }
1090 #endif /* HAVE_FULL_DNS_FUNCS */
1091
1092 /*
1093 * Local variables:
1094 * tab-width: 4
1095 * c-basic-offset: 4
1096 * End:
1097 * vim600: sw=4 ts=4 fdm=marker
1098 * vim<600: sw=4 ts=4
1099 */
1100