1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 The PHP Group | |
6 | +----------------------------------------------------------------------+ |
7 | | This source file is subject to version 3.01 of the PHP license, | |
8 | | that is bundled with this package in the file LICENSE, and is | |
9 | | available through the world-wide-web at the following url: | |
10 | | http://www.php.net/license/3_01.txt | |
11 | | If you did not receive a copy of the PHP license and are unable to | |
12 | | obtain it through the world-wide-web, please send a note to | |
13 | | license@php.net so we can mail you a copy immediately. | |
14 | +----------------------------------------------------------------------+ |
15 | | Authors: The typical suspects | |
16 | | Pollita <pollita@php.net> | |
17 | | Marcus Boerger <helly@php.net> | |
18 | +----------------------------------------------------------------------+ |
19 | */ |
20 | |
21 | /* $Id$ */ |
22 | |
23 | /* {{{ includes */ |
24 | #include "php.h" |
25 | #include "php_network.h" |
26 | |
27 | #if HAVE_SYS_SOCKET_H |
28 | #include <sys/socket.h> |
29 | #endif |
30 | |
31 | #ifdef PHP_WIN32 |
32 | # include "win32/inet.h" |
33 | # include <winsock2.h> |
34 | # include <windows.h> |
35 | # include <Ws2tcpip.h> |
36 | #else /* This holds good for NetWare too, both for Winsock and Berkeley sockets */ |
37 | #include <netinet/in.h> |
38 | #if HAVE_ARPA_INET_H |
39 | #include <arpa/inet.h> |
40 | #endif |
41 | #include <netdb.h> |
42 | #ifdef _OSD_POSIX |
43 | #undef STATUS |
44 | #undef T_UNSPEC |
45 | #endif |
46 | #if HAVE_ARPA_NAMESER_H |
47 | #ifdef DARWIN |
48 | # define BIND_8_COMPAT 1 |
49 | #endif |
50 | #include <arpa/nameser.h> |
51 | #endif |
52 | #if HAVE_RESOLV_H |
53 | #include <resolv.h> |
54 | #endif |
55 | #ifdef HAVE_DNS_H |
56 | #include <dns.h> |
57 | #endif |
58 | #endif |
59 | |
60 | /* Borrowed from SYS/SOCKET.H */ |
61 | #if defined(NETWARE) && defined(USE_WINSOCK) |
62 | #define AF_INET 2 /* internetwork: UDP, TCP, etc. */ |
63 | #endif |
64 | |
65 | #ifndef MAXHOSTNAMELEN |
66 | #define MAXHOSTNAMELEN 255 |
67 | #endif |
68 | |
69 | /* For the local hostname obtained via gethostname which is different from the |
70 | dns-related MAXHOSTNAMELEN constant above */ |
71 | #ifndef HOST_NAME_MAX |
72 | #define HOST_NAME_MAX 255 |
73 | #endif |
74 | |
75 | #include "php_dns.h" |
76 | |
77 | /* type compat */ |
78 | #ifndef DNS_T_A |
79 | #define DNS_T_A 1 |
80 | #endif |
81 | #ifndef DNS_T_NS |
82 | #define DNS_T_NS 2 |
83 | #endif |
84 | #ifndef DNS_T_CNAME |
85 | #define DNS_T_CNAME 5 |
86 | #endif |
87 | #ifndef DNS_T_SOA |
88 | #define DNS_T_SOA 6 |
89 | #endif |
90 | #ifndef DNS_T_PTR |
91 | #define DNS_T_PTR 12 |
92 | #endif |
93 | #ifndef DNS_T_HINFO |
94 | #define DNS_T_HINFO 13 |
95 | #endif |
96 | #ifndef DNS_T_MINFO |
97 | #define DNS_T_MINFO 14 |
98 | #endif |
99 | #ifndef DNS_T_MX |
100 | #define DNS_T_MX 15 |
101 | #endif |
102 | #ifndef DNS_T_TXT |
103 | #define DNS_T_TXT 16 |
104 | #endif |
105 | #ifndef DNS_T_AAAA |
106 | #define DNS_T_AAAA 28 |
107 | #endif |
108 | #ifndef DNS_T_SRV |
109 | #define DNS_T_SRV 33 |
110 | #endif |
111 | #ifndef DNS_T_NAPTR |
112 | #define DNS_T_NAPTR 35 |
113 | #endif |
114 | #ifndef DNS_T_A6 |
115 | #define DNS_T_A6 38 |
116 | #endif |
117 | |
118 | #ifndef DNS_T_ANY |
119 | #define DNS_T_ANY 255 |
120 | #endif |
121 | /* }}} */ |
122 | |
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 */ |
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 */ |
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 */ |
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 */ |
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. */ |
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 */ |
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__) |
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 */ |
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 */ |
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 */ |
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 | switch (h_errno) { |
912 | case NO_DATA: |
913 | case HOST_NOT_FOUND: |
914 | continue; |
915 | |
916 | case NO_RECOVERY: |
917 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "An unexpected server failure occurred." ); |
918 | break; |
919 | |
920 | case TRY_AGAIN: |
921 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "A temporary server error occurred." ); |
922 | break; |
923 | |
924 | default: |
925 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "DNS Query failed" ); |
926 | } |
927 | zval_dtor(return_value); |
928 | RETURN_FALSE; |
929 | } |
930 | |
931 | cp = answer.qb2 + HFIXEDSZ; |
932 | end = answer.qb2 + n; |
933 | hp = (HEADER *)&answer; |
934 | qd = ntohs(hp->qdcount); |
935 | an = ntohs(hp->ancount); |
936 | ns = ntohs(hp->nscount); |
937 | ar = ntohs(hp->arcount); |
938 | |
939 | /* Skip QD entries, they're only used by dn_expand later on */ |
940 | while (qd-- > 0) { |
941 | n = dn_skipname(cp, end); |
942 | if (n < 0) { |
943 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse DNS data received" ); |
944 | zval_dtor(return_value); |
945 | php_dns_free_handle(handle); |
946 | RETURN_FALSE; |
947 | } |
948 | cp += n + QFIXEDSZ; |
949 | } |
950 | |
951 | /* YAY! Our real answers! */ |
952 | while (an-- && cp && cp < end) { |
953 | zval *retval; |
954 | |
955 | cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval); |
956 | if (retval != NULL && store_results) { |
957 | add_next_index_zval(return_value, retval); |
958 | } |
959 | } |
960 | |
961 | if (authns || addtl) { |
962 | /* List of Authoritative Name Servers |
963 | * Process when only requesting addtl so that we can skip through the section |
964 | */ |
965 | while (ns-- > 0 && cp && cp < end) { |
966 | zval *retval = NULL; |
967 | |
968 | cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval); |
969 | if (retval != NULL) { |
970 | add_next_index_zval(authns, retval); |
971 | } |
972 | } |
973 | } |
974 | |
975 | if (addtl) { |
976 | /* Additional records associated with authoritative name servers */ |
977 | while (ar-- > 0 && cp && cp < end) { |
978 | zval *retval = NULL; |
979 | |
980 | cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval); |
981 | if (retval != NULL) { |
982 | add_next_index_zval(addtl, retval); |
983 | } |
984 | } |
985 | } |
986 | php_dns_free_handle(handle); |
987 | } |
988 | } |
989 | } |
990 | /* }}} */ |
991 | |
992 | /* {{{ proto bool dns_get_mx(string hostname, array mxhosts [, array weight]) |
993 | Get MX records corresponding to a given Internet host name */ |
994 | PHP_FUNCTION(dns_get_mx) |
995 | { |
996 | char *hostname; |
997 | int hostname_len; |
998 | zval *mx_list, *weight_list = NULL; |
999 | int count, qdc; |
1000 | u_short type, weight; |
1001 | u_char ans[MAXPACKET]; |
1002 | char buf[MAXHOSTNAMELEN]; |
1003 | HEADER *hp; |
1004 | u_char *cp, *end; |
1005 | int i; |
1006 | #if defined(HAVE_DNS_SEARCH) |
1007 | struct sockaddr_storage from; |
1008 | uint32_t fromsize = sizeof(from); |
1009 | dns_handle_t handle; |
1010 | #elif defined(HAVE_RES_NSEARCH) |
1011 | struct __res_state state; |
1012 | struct __res_state *handle = &state; |
1013 | #endif |
1014 | |
1015 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|z" , &hostname, &hostname_len, &mx_list, &weight_list) == FAILURE) { |
1016 | return; |
1017 | } |
1018 | |
1019 | zval_dtor(mx_list); |
1020 | array_init(mx_list); |
1021 | |
1022 | if (weight_list) { |
1023 | zval_dtor(weight_list); |
1024 | array_init(weight_list); |
1025 | } |
1026 | |
1027 | #if defined(HAVE_DNS_SEARCH) |
1028 | handle = dns_open(NULL); |
1029 | if (handle == NULL) { |
1030 | RETURN_FALSE; |
1031 | } |
1032 | #elif defined(HAVE_RES_NSEARCH) |
1033 | memset(&state, 0, sizeof(state)); |
1034 | if (res_ninit(handle)) { |
1035 | RETURN_FALSE; |
1036 | } |
1037 | #else |
1038 | res_init(); |
1039 | #endif |
1040 | |
1041 | i = php_dns_search(handle, hostname, C_IN, DNS_T_MX, (u_char *)&ans, sizeof(ans)); |
1042 | if (i < 0) { |
1043 | RETURN_FALSE; |
1044 | } |
1045 | if (i > (int)sizeof(ans)) { |
1046 | i = sizeof(ans); |
1047 | } |
1048 | hp = (HEADER *)&ans; |
1049 | cp = (u_char *)&ans + HFIXEDSZ; |
1050 | end = (u_char *)&ans +i; |
1051 | for (qdc = ntohs((unsigned short)hp->qdcount); qdc--; cp += i + QFIXEDSZ) { |
1052 | if ((i = dn_skipname(cp, end)) < 0 ) { |
1053 | php_dns_free_handle(handle); |
1054 | RETURN_FALSE; |
1055 | } |
1056 | } |
1057 | count = ntohs((unsigned short)hp->ancount); |
1058 | while (--count >= 0 && cp < end) { |
1059 | if ((i = dn_skipname(cp, end)) < 0 ) { |
1060 | php_dns_free_handle(handle); |
1061 | RETURN_FALSE; |
1062 | } |
1063 | cp += i; |
1064 | GETSHORT(type, cp); |
1065 | cp += INT16SZ + INT32SZ; |
1066 | GETSHORT(i, cp); |
1067 | if (type != DNS_T_MX) { |
1068 | cp += i; |
1069 | continue; |
1070 | } |
1071 | GETSHORT(weight, cp); |
1072 | if ((i = dn_expand(ans, end, cp, buf, sizeof(buf)-1)) < 0) { |
1073 | php_dns_free_handle(handle); |
1074 | RETURN_FALSE; |
1075 | } |
1076 | cp += i; |
1077 | add_next_index_string(mx_list, buf, 1); |
1078 | if (weight_list) { |
1079 | add_next_index_long(weight_list, weight); |
1080 | } |
1081 | } |
1082 | php_dns_free_handle(handle); |
1083 | RETURN_TRUE; |
1084 | } |
1085 | /* }}} */ |
1086 | #endif /* HAVE_FULL_DNS_FUNCS */ |
1087 | #endif /* !defined(PHP_WIN32) && (HAVE_DNS_SEARCH_FUNC && !(defined(__BEOS__) || defined(NETWARE))) */ |
1088 | |
1089 | #if HAVE_FULL_DNS_FUNCS || defined(PHP_WIN32) |
1090 | PHP_MINIT_FUNCTION(dns) { |
1091 | REGISTER_LONG_CONSTANT("DNS_A" , PHP_DNS_A, CONST_CS | CONST_PERSISTENT); |
1092 | REGISTER_LONG_CONSTANT("DNS_NS" , PHP_DNS_NS, CONST_CS | CONST_PERSISTENT); |
1093 | REGISTER_LONG_CONSTANT("DNS_CNAME" , PHP_DNS_CNAME, CONST_CS | CONST_PERSISTENT); |
1094 | REGISTER_LONG_CONSTANT("DNS_SOA" , PHP_DNS_SOA, CONST_CS | CONST_PERSISTENT); |
1095 | REGISTER_LONG_CONSTANT("DNS_PTR" , PHP_DNS_PTR, CONST_CS | CONST_PERSISTENT); |
1096 | REGISTER_LONG_CONSTANT("DNS_HINFO" , PHP_DNS_HINFO, CONST_CS | CONST_PERSISTENT); |
1097 | REGISTER_LONG_CONSTANT("DNS_MX" , PHP_DNS_MX, CONST_CS | CONST_PERSISTENT); |
1098 | REGISTER_LONG_CONSTANT("DNS_TXT" , PHP_DNS_TXT, CONST_CS | CONST_PERSISTENT); |
1099 | REGISTER_LONG_CONSTANT("DNS_SRV" , PHP_DNS_SRV, CONST_CS | CONST_PERSISTENT); |
1100 | REGISTER_LONG_CONSTANT("DNS_NAPTR" , PHP_DNS_NAPTR, CONST_CS | CONST_PERSISTENT); |
1101 | REGISTER_LONG_CONSTANT("DNS_AAAA" , PHP_DNS_AAAA, CONST_CS | CONST_PERSISTENT); |
1102 | REGISTER_LONG_CONSTANT("DNS_A6" , PHP_DNS_A6, CONST_CS | CONST_PERSISTENT); |
1103 | REGISTER_LONG_CONSTANT("DNS_ANY" , PHP_DNS_ANY, CONST_CS | CONST_PERSISTENT); |
1104 | REGISTER_LONG_CONSTANT("DNS_ALL" , PHP_DNS_ALL, CONST_CS | CONST_PERSISTENT); |
1105 | return SUCCESS; |
1106 | } |
1107 | #endif /* HAVE_FULL_DNS_FUNCS */ |
1108 | |
1109 | /* |
1110 | * Local variables: |
1111 | * tab-width: 4 |
1112 | * c-basic-offset: 4 |
1113 | * End: |
1114 | * vim600: sw=4 ts=4 fdm=marker |
1115 | * vim<600: sw=4 ts=4 |
1116 | */ |
1117 | |