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