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