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