File Coverage

cares.xs
Criterion Covered Total %
statement 646 756 85.4
branch 421 730 57.6
condition n/a
subroutine n/a
pod n/a
total 1067 1486 71.8


line stmt bran cond sub pod time code
1             /*
2             * EV::cares - high-performance async DNS resolver using c-ares and EV
3             */
4              
5             #include "EXTERN.h"
6             #include "perl.h"
7             #include "XSUB.h"
8              
9             #include "EVAPI.h"
10              
11             #include
12              
13             #include
14             #include
15             #include
16             #include
17             #include
18             #include
19              
20             /* suppress c-ares deprecation warnings - these functions still work */
21             #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
22              
23             /* DNS type constants (removed from ares_dns.h in newer c-ares) */
24             #ifndef T_A
25             #define T_A 1
26             #endif
27             #ifndef T_NS
28             #define T_NS 2
29             #endif
30             #ifndef T_CNAME
31             #define T_CNAME 5
32             #endif
33             #ifndef T_SOA
34             #define T_SOA 6
35             #endif
36             #ifndef T_PTR
37             #define T_PTR 12
38             #endif
39             #ifndef T_MX
40             #define T_MX 15
41             #endif
42             #ifndef T_TXT
43             #define T_TXT 16
44             #endif
45             #ifndef T_AAAA
46             #define T_AAAA 28
47             #endif
48             #ifndef T_SRV
49             #define T_SRV 33
50             #endif
51             #ifndef T_NAPTR
52             #define T_NAPTR 35
53             #endif
54             #ifndef T_DS
55             #define T_DS 43
56             #endif
57             #ifndef T_RRSIG
58             #define T_RRSIG 46
59             #endif
60             #ifndef T_DNSKEY
61             #define T_DNSKEY 48
62             #endif
63             #ifndef T_TLSA
64             #define T_TLSA 52
65             #endif
66             #ifndef T_SVCB
67             #define T_SVCB 64
68             #endif
69             #ifndef T_HTTPS
70             #define T_HTTPS 65
71             #endif
72             #ifndef T_CAA
73             #define T_CAA 257
74             #endif
75             #ifndef T_ANY
76             #define T_ANY 255
77             #endif
78              
79             /* Modern ares_dns_record_t API (HTTPS/SVCB parsing) — c-ares >= 1.28 */
80             #ifndef HAVE_ARES_DNS_PARSE
81             # if defined(ARES_VERSION) && ARES_VERSION >= 0x011C00
82             # define HAVE_ARES_DNS_PARSE 1
83             # endif
84             #endif
85              
86             /* DNS class constants */
87             #ifndef C_IN
88             #define C_IN 1
89             #endif
90             #ifndef C_CHAOS
91             #define C_CHAOS 3
92             #endif
93             #ifndef C_HS
94             #define C_HS 4
95             #endif
96             #ifndef C_ANY
97             #define C_ANY 255
98             #endif
99              
100             /* helper macro for BOOT constant registration */
101             #define CONST_IV(stash, name) newCONSTSUB(stash, #name, newSViv(name))
102              
103             /* flags that may not exist in older c-ares */
104             #ifndef ARES_FLAG_NO_DFLT_SVR
105             #define ARES_FLAG_NO_DFLT_SVR 0
106             #endif
107             #ifndef ARES_FLAG_DNS0x20
108             #define ARES_FLAG_DNS0x20 0
109             #endif
110             #ifndef ARES_ESERVICE
111             #define ARES_ESERVICE 25
112             #endif
113             #ifndef ARES_ENOSERVER
114             #define ARES_ENOSERVER 26
115             #endif
116              
117             #define EV_CARES_MAGIC 0xCA7E5001
118             #define MAX_IO 16
119              
120             #define REQUIRE_LIVE(self) \
121             STMT_START { \
122             if ((self)->magic != EV_CARES_MAGIC) croak("EV::cares: invalid object"); \
123             if ((self)->destroyed) croak("EV::cares: resolver is destroyed"); \
124             } STMT_END
125              
126             #define REQUIRE_CB(cb) \
127             STMT_START { \
128             if (!(SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV)) \
129             croak("EV::cares: callback must be a CODE reference"); \
130             } STMT_END
131              
132             typedef struct ev_cares_s ev_cares_t;
133             typedef ev_cares_t *EV__cares;
134              
135             typedef struct {
136             ev_io watcher;
137             ares_socket_t fd;
138             } ev_cares_io_t;
139              
140             struct ev_cares_s {
141             U32 magic;
142             ares_channel channel;
143             struct ev_loop *loop;
144             SV *loop_sv; /* keeps a custom EV::Loop alive; NULL for default loop */
145             ev_timer timer;
146             ev_cares_io_t ios[MAX_IO];
147             int active_queries;
148             int destroyed;
149             int in_callback; /* prevent Safefree while callbacks run */
150             int free_pending; /* deferred Safefree after last callback */
151             int last_timeouts; /* timeouts count from the most recent callback */
152             };
153              
154             typedef struct {
155             ev_cares_t *resolver;
156             SV *cb;
157             int qtype;
158             int with_ttl; /* addrinfo_cb returns hashrefs when set */
159             int by_addr; /* host_cb returns h_name+h_aliases instead of h_addr_list */
160             } ev_cares_req_t;
161              
162             /* forward declarations */
163             static void update_timer(ev_cares_t *self);
164              
165             /* Bracket an ares_* call that may fire callbacks synchronously.
166             * Prevents UAF if user DESTROY's the resolver inside a callback. */
167             #define ARES_CALL_BEGIN(self) (self)->in_callback++
168             #define ARES_CALL_END(self) \
169             STMT_START { \
170             if (--(self)->in_callback == 0 && (self)->free_pending) \
171             Safefree(self); \
172             else \
173             update_timer(self); \
174             } STMT_END
175             static void io_cb(EV_P_ ev_io *w, int revents);
176             static void timer_cb(EV_P_ ev_timer *w, int revents);
177             static void sock_state_cb(void *data, ares_socket_t fd, int readable, int writable);
178             static void cleanup(ev_cares_t *self);
179              
180             /* qtype is set by callers that need it (only search()); Newxz zeroes it. */
181             static ev_cares_req_t *
182 127           new_req(pTHX_ ev_cares_t *self, SV *cb) {
183             ev_cares_req_t *req;
184 127           Newxz(req, 1, ev_cares_req_t);
185 127           req->resolver = self;
186 127           req->cb = SvREFCNT_inc_simple_NN(cb);
187 127           self->active_queries++;
188 127           return req;
189             }
190              
191             /* Build a comma-separated server list. Each AV element may be either
192             a plain string (e.g. "8.8.8.8" or "8.8.8.8:53") or a hashref
193             { host => ..., port => ... }. Sparse-array holes are skipped
194             without emitting a stray comma. */
195             static SV *
196 6           av_to_csv(pTHX_ AV *av) {
197 6           SSize_t i, len = av_len(av) + 1;
198 6           SV *csv = sv_2mortal(newSVpvs(""));
199 6           int written = 0;
200 35 100         for (i = 0; i < len; i++) {
201 29           SV **elem = av_fetch(av, i, 0);
202 29 50         if (!elem) continue;
203             /* Validate (and croak) before bumping `written` so an aborted
204             iteration can't leave a dangling separator. */
205 31 100         if (SvROK(*elem) && SvTYPE(SvRV(*elem)) == SVt_PVHV) {
    50          
206 2           HV *hv = (HV *)SvRV(*elem);
207 2           SV **host = hv_fetchs(hv, "host", 0);
208 2           SV **port = hv_fetchs(hv, "port", 0);
209 2 50         if (!host)
210 0           croak("EV::cares: server hashref missing 'host' key (index %d)",
211             (int)i);
212 2 100         if (written++) sv_catpvs(csv, ",");
213 2           sv_catsv(csv, *host);
214 2 100         if (port) sv_catpvf(csv, ":%d", (int)SvIV(*port));
215             } else {
216 27 100         if (written++) sv_catpvs(csv, ",");
217 27           sv_catsv(csv, *elem);
218             }
219             }
220 6           return csv;
221             }
222              
223             static void
224 127           free_req(pTHX_ ev_cares_req_t *req) {
225 127           req->resolver->active_queries--;
226 127           SvREFCNT_dec(req->cb);
227 127           Safefree(req);
228 127           }
229              
230             /* ---- EV integration ---- */
231              
232             static void
233 94           sock_state_cb(void *data, ares_socket_t fd, int readable, int writable) {
234             dTHX;
235 94           ev_cares_t *self = (ev_cares_t *)data;
236 94           int i, events = 0;
237              
238 94 100         if (self->destroyed) return;
239              
240 93 100         if (readable) events |= EV_READ;
241 93 50         if (writable) events |= EV_WRITE;
242              
243             /* find existing watcher for this fd */
244 881 100         for (i = 0; i < MAX_IO; i++)
245 834 100         if (self->ios[i].fd == fd) break;
246              
247 93 100         if (events) {
248 47 50         if (i == MAX_IO) {
249             /* find empty slot */
250 83 50         for (i = 0; i < MAX_IO; i++)
251 83 100         if (self->ios[i].fd == ARES_SOCKET_BAD) break;
252 47 50         if (i == MAX_IO) {
253 0           warn("EV::cares: too many concurrent sockets (>%d), "
254             "query on fd %d may hang until timeout",
255             MAX_IO, (int)fd);
256 0           return;
257             }
258 47           self->ios[i].fd = fd;
259 47           ev_io_init(&self->ios[i].watcher, io_cb, (int)fd, events);
260 47           self->ios[i].watcher.data = (void *)self;
261             } else {
262 0           ev_io_stop(self->loop, &self->ios[i].watcher);
263 0           ev_io_set(&self->ios[i].watcher, (int)fd, events);
264             }
265 47           ev_io_start(self->loop, &self->ios[i].watcher);
266             } else {
267             /* fd closing */
268 46 50         if (i < MAX_IO) {
269 46           ev_io_stop(self->loop, &self->ios[i].watcher);
270 46           self->ios[i].fd = ARES_SOCKET_BAD;
271             }
272             }
273             }
274              
275             /* io_cb / timer_cb: cleanup() sets destroyed=1 before nulling channel,
276             so the destroyed flag implies !channel; one check covers both. */
277             static void
278 42           io_cb(EV_P_ ev_io *w, int revents) {
279 42           ev_cares_t *self = (ev_cares_t *)w->data;
280 42           ares_socket_t rfd = ARES_SOCKET_BAD, wfd = ARES_SOCKET_BAD;
281             int i;
282              
283 42 50         if (self->destroyed) return;
284              
285 72 50         for (i = 0; i < MAX_IO; i++) {
286 72 100         if (&self->ios[i].watcher == w) {
287 42 50         if (revents & EV_READ) rfd = self->ios[i].fd;
288 42 50         if (revents & EV_WRITE) wfd = self->ios[i].fd;
289             /* Bare EV_ERROR (no READ/WRITE bits) signals an unrecoverable
290             watcher problem and libev has already stopped the watcher.
291             Hand the fd to c-ares anyway so its next I/O attempt fails
292             immediately instead of waiting for the per-query timeout. */
293 42 50         if ((revents & EV_ERROR) &&
    0          
294 0 0         rfd == ARES_SOCKET_BAD && wfd == ARES_SOCKET_BAD)
295 0           rfd = self->ios[i].fd;
296 42           break;
297             }
298             }
299              
300 42           ARES_CALL_BEGIN(self);
301 42           ares_process_fd(self->channel, rfd, wfd);
302 42 50         ARES_CALL_END(self);
    50          
303             }
304              
305             static void
306 0           timer_cb(EV_P_ ev_timer *w, int revents) {
307 0           ev_cares_t *self = (ev_cares_t *)w->data;
308              
309 0 0         if (self->destroyed) return;
310              
311 0           ARES_CALL_BEGIN(self);
312 0           ares_process_fd(self->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
313 0 0         ARES_CALL_END(self);
    0          
314             }
315              
316             static void
317 301           update_timer(ev_cares_t *self) {
318             struct timeval tv, *tvp;
319              
320 301 100         if (self->destroyed) return;
321              
322 297           tvp = ares_timeout(self->channel, NULL, &tv);
323 297           ev_timer_stop(self->loop, &self->timer);
324 297 100         if (tvp) {
325 62           double after = (double)tvp->tv_sec + (double)tvp->tv_usec / 1e6;
326 62 50         if (after < 0.001) after = 0.001;
327 62           ev_timer_set(&self->timer, after, 0.);
328 62           ev_timer_start(self->loop, &self->timer);
329             }
330             }
331              
332             static void
333 111           cleanup(ev_cares_t *self) {
334             int i;
335              
336 111 100         if (self->destroyed) return;
337 97           self->destroyed = 1;
338              
339 97           ev_timer_stop(self->loop, &self->timer);
340 1649 100         for (i = 0; i < MAX_IO; i++) {
341 1552 100         if (self->ios[i].fd != ARES_SOCKET_BAD) {
342 1           ev_io_stop(self->loop, &self->ios[i].watcher);
343 1           self->ios[i].fd = ARES_SOCKET_BAD;
344             }
345             }
346              
347 97           ares_destroy(self->channel);
348 97           self->channel = NULL;
349              
350             /* Release our hold on a user-supplied EV::Loop. Doing this only after
351             the watchers are stopped means the loop is still valid above. */
352 97 100         if (self->loop_sv) {
353             dTHX;
354 3           SvREFCNT_dec(self->loop_sv);
355 3           self->loop_sv = NULL;
356             }
357             }
358              
359             /* ---- callback prologue/epilogue (prevents UAF if resolver freed mid-callback) ---- */
360              
361             /* All c-ares callbacks share this prologue. The macro relies on each
362             caller having an `int timeouts` parameter (per the c-ares callback
363             ABI) so it can record the latest retry count on the resolver. */
364             #define CB_PROLOGUE(arg, status_val) \
365             ev_cares_req_t *req = (ev_cares_req_t *)(arg); \
366             ev_cares_t *self = req->resolver; \
367             dTHX; dSP; \
368             self->last_timeouts = timeouts; \
369             self->in_callback++; \
370             ENTER; SAVETMPS; \
371             PUSHMARK(SP); \
372             mXPUSHi(status_val)
373              
374             #define CB_EPILOGUE \
375             PUTBACK; \
376             { \
377             SV *_cb = SvREFCNT_inc_simple_NN(req->cb); \
378             call_sv(_cb, G_DISCARD | G_EVAL); \
379             if (SvTRUE(ERRSV)) { \
380             warn("EV::cares: callback error: %" SVf, SVfARG(ERRSV)); \
381             sv_setsv(ERRSV, &PL_sv_undef); \
382             } \
383             SvREFCNT_dec(_cb); \
384             } \
385             FREETMPS; LEAVE; \
386             free_req(aTHX_ req); \
387             /* update_timer is a no-op once destroyed=1, so the order with the */ \
388             /* free_pending check below is intentionally tolerant of either */ \
389             update_timer(self); \
390             if (--self->in_callback == 0 && self->free_pending) \
391             Safefree(self)
392              
393             /* ---- query callbacks ---- */
394              
395             static void
396 67           addrinfo_cb(void *arg, int status, int timeouts, struct ares_addrinfo *result) {
397 67 50         CB_PROLOGUE(arg, status);
    50          
398              
399 67 100         if (result) {
400 48 50         if (status == ARES_SUCCESS) {
401             struct ares_addrinfo_node *node;
402             /* result->cnames->name is always set when cnames is non-NULL */
403 48 50         const char *cname = result->cnames ? result->cnames->name : NULL;
404 146 100         for (node = result->nodes; node; node = node->ai_next) {
405             char ip[INET6_ADDRSTRLEN];
406 98 100         if (node->ai_family == AF_INET) {
407 59           struct sockaddr_in *sin = (struct sockaddr_in *)node->ai_addr;
408 59           inet_ntop(AF_INET, &sin->sin_addr, ip, sizeof(ip));
409 39 50         } else if (node->ai_family == AF_INET6) {
410 39           struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)node->ai_addr;
411 39           inet_ntop(AF_INET6, &sin6->sin6_addr, ip, sizeof(ip));
412             } else {
413 0           continue;
414             }
415 98 100         if (req->with_ttl) {
416 7           HV *hv = newHV();
417 7           hv_stores(hv, "addr", newSVpv(ip, 0));
418 7           hv_stores(hv, "family", newSViv(node->ai_family));
419 7           hv_stores(hv, "ttl", newSViv(node->ai_ttl));
420 7           hv_stores(hv, "timeouts", newSViv(timeouts));
421 7 50         if (cname)
422 0           hv_stores(hv, "canonname", newSVpv(cname, 0));
423 7 50         mXPUSHs(newRV_noinc((SV *)hv));
424             } else {
425 91 50         mXPUSHp(ip, strlen(ip));
426             }
427             }
428             }
429 48           ares_freeaddrinfo(result);
430             }
431              
432 67 50         CB_EPILOGUE;
    100          
    50          
    50          
    50          
    100          
    50          
433 67           }
434              
435             static void
436 4           host_cb(void *arg, int status, int timeouts, struct hostent *hostent) {
437 4 50         CB_PROLOGUE(arg, status);
    50          
438              
439 4 50         if (status == ARES_SUCCESS && hostent) {
    50          
440 4 100         if (req->by_addr) {
441             /* reverse lookup: return canonical name + any aliases */
442 3 50         if (hostent->h_name)
443 3 50         mXPUSHp(hostent->h_name, strlen(hostent->h_name));
444 3 50         if (hostent->h_aliases) {
445             char **p;
446 4 100         for (p = hostent->h_aliases; *p; p++)
447 1 50         mXPUSHp(*p, strlen(*p));
448             }
449             } else {
450             /* forward lookup: return the address list */
451             char **p;
452 2 100         for (p = hostent->h_addr_list; *p; p++) {
453             char ip[INET6_ADDRSTRLEN];
454 1 50         if (hostent->h_addrtype == AF_INET)
455 1           inet_ntop(AF_INET, *p, ip, sizeof(ip));
456             else
457 0           inet_ntop(AF_INET6, *p, ip, sizeof(ip));
458 1 50         mXPUSHp(ip, strlen(ip));
459             }
460             }
461             }
462              
463 4 50         CB_EPILOGUE;
    50          
    0          
    0          
    50          
    50          
    0          
464 4           }
465              
466             static void
467 35           raw_cb(void *arg, int status, int timeouts, unsigned char *abuf, int alen) {
468 35 50         CB_PROLOGUE(arg, status);
    50          
469              
470 35 100         if (status == ARES_SUCCESS && abuf && alen > 0)
    50          
    50          
471 3 50         mXPUSHp((char *)abuf, alen);
472              
473 35 50         CB_EPILOGUE;
    50          
    0          
    0          
    50          
    50          
    0          
474 35           }
475              
476             static void
477 20           search_cb(void *arg, int status, int timeouts, unsigned char *abuf, int alen) {
478 20 50         CB_PROLOGUE(arg, status);
    50          
479              
480 20 100         if (status == ARES_SUCCESS && abuf && alen > 0) {
    50          
    50          
481 19           switch (req->qtype) {
482              
483 7           case T_A: {
484 7           struct hostent *host = NULL;
485 7 50         if (ares_parse_a_reply(abuf, alen, &host, NULL, NULL) == ARES_SUCCESS && host) {
    50          
486             char **p;
487 25 100         for (p = host->h_addr_list; *p; p++) {
488             char ip[INET_ADDRSTRLEN];
489 18           inet_ntop(AF_INET, *p, ip, sizeof(ip));
490 18 50         mXPUSHp(ip, strlen(ip));
491             }
492 7           ares_free_hostent(host);
493             }
494 7           break;
495             }
496              
497 1           case T_AAAA: {
498 1           struct hostent *host = NULL;
499 1 50         if (ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL) == ARES_SUCCESS && host) {
    50          
500             char **p;
501 5 100         for (p = host->h_addr_list; *p; p++) {
502             char ip[INET6_ADDRSTRLEN];
503 4           inet_ntop(AF_INET6, *p, ip, sizeof(ip));
504 4 50         mXPUSHp(ip, strlen(ip));
505             }
506 1           ares_free_hostent(host);
507             }
508 1           break;
509             }
510              
511 1           case T_MX: {
512 1           struct ares_mx_reply *mx_out = NULL;
513 1 50         if (ares_parse_mx_reply(abuf, alen, &mx_out) == ARES_SUCCESS && mx_out) {
    50          
514             struct ares_mx_reply *mx;
515 2 100         for (mx = mx_out; mx; mx = mx->next) {
516 1           HV *hv = newHV();
517 1           hv_stores(hv, "priority", newSViv(mx->priority));
518 1           hv_stores(hv, "host", newSVpv(mx->host, 0));
519 1 50         mXPUSHs(newRV_noinc((SV *)hv));
520             }
521 1           ares_free_data(mx_out);
522             }
523 1           break;
524             }
525              
526 1           case T_SRV: {
527 1           struct ares_srv_reply *srv_out = NULL;
528 1 50         if (ares_parse_srv_reply(abuf, alen, &srv_out) == ARES_SUCCESS && srv_out) {
    50          
529             struct ares_srv_reply *srv;
530 2 100         for (srv = srv_out; srv; srv = srv->next) {
531 1           HV *hv = newHV();
532 1           hv_stores(hv, "priority", newSViv(srv->priority));
533 1           hv_stores(hv, "weight", newSViv(srv->weight));
534 1           hv_stores(hv, "port", newSViv(srv->port));
535 1           hv_stores(hv, "target", newSVpv(srv->host, 0));
536 1 50         mXPUSHs(newRV_noinc((SV *)hv));
537             }
538 1           ares_free_data(srv_out);
539             }
540 1           break;
541             }
542              
543 1           case T_TXT: {
544 1           struct ares_txt_ext *txt_out = NULL;
545 1 50         if (ares_parse_txt_reply_ext(abuf, alen, &txt_out) == ARES_SUCCESS && txt_out) {
    50          
546             struct ares_txt_ext *txt;
547 1           SV *current = NULL;
548 14 100         for (txt = txt_out; txt; txt = txt->next) {
549 13 50         if (txt->record_start || !current) {
    0          
550 13 100         if (current) mXPUSHs(current);
    50          
551 13           current = newSVpvn((char *)txt->txt, txt->length);
552             } else {
553 0           sv_catpvn(current, (char *)txt->txt, txt->length);
554             }
555             }
556 1 50         if (current) mXPUSHs(current);
    50          
557 1           ares_free_data(txt_out);
558             }
559 1           break;
560             }
561              
562 1           case T_NS: {
563 1           struct hostent *host = NULL;
564 1 50         if (ares_parse_ns_reply(abuf, alen, &host) == ARES_SUCCESS && host) {
    50          
565 1 50         if (host->h_aliases) {
566             char **p;
567 5 100         for (p = host->h_aliases; *p; p++)
568 4 50         mXPUSHp(*p, strlen(*p));
569             }
570 1           ares_free_hostent(host);
571             }
572 1           break;
573             }
574              
575 1           case T_SOA: {
576 1           struct ares_soa_reply *soa = NULL;
577 1 50         if (ares_parse_soa_reply(abuf, alen, &soa) == ARES_SUCCESS && soa) {
    50          
578 1           HV *hv = newHV();
579 1           hv_stores(hv, "mname", newSVpv(soa->nsname, 0));
580 1           hv_stores(hv, "rname", newSVpv(soa->hostmaster, 0));
581 1           hv_stores(hv, "serial", newSVuv(soa->serial));
582 1           hv_stores(hv, "refresh", newSVuv(soa->refresh));
583 1           hv_stores(hv, "retry", newSVuv(soa->retry));
584 1           hv_stores(hv, "expire", newSVuv(soa->expire));
585 1           hv_stores(hv, "minttl", newSVuv(soa->minttl));
586 1 50         mXPUSHs(newRV_noinc((SV *)hv));
587 1           ares_free_data(soa);
588             }
589 1           break;
590             }
591              
592 0           case T_PTR: {
593 0           struct hostent *host = NULL;
594             /* family only affects h_addrtype/h_length, not PTR name parsing;
595             we pass NULL addr so these fields are unused */
596 0 0         if (ares_parse_ptr_reply(abuf, alen, NULL, 0, AF_UNSPEC, &host) == ARES_SUCCESS && host) {
    0          
597 0 0         if (host->h_name)
598 0 0         mXPUSHp(host->h_name, strlen(host->h_name));
599 0 0         if (host->h_aliases) {
600             char **p;
601 0 0         for (p = host->h_aliases; *p; p++)
602 0 0         mXPUSHp(*p, strlen(*p));
603             }
604 0           ares_free_hostent(host);
605             }
606 0           break;
607             }
608              
609 1           case T_NAPTR: {
610 1           struct ares_naptr_reply *naptr_out = NULL;
611 1 50         if (ares_parse_naptr_reply(abuf, alen, &naptr_out) == ARES_SUCCESS && naptr_out) {
    50          
612             struct ares_naptr_reply *n;
613 4 100         for (n = naptr_out; n; n = n->next) {
614 3           HV *hv = newHV();
615 3           hv_stores(hv, "order", newSViv(n->order));
616 3           hv_stores(hv, "preference", newSViv(n->preference));
617 3           hv_stores(hv, "flags", newSVpv((char *)n->flags, 0));
618 3           hv_stores(hv, "service", newSVpv((char *)n->service, 0));
619 3           hv_stores(hv, "regexp", newSVpv((char *)n->regexp, 0));
620 3           hv_stores(hv, "replacement", newSVpv(n->replacement, 0));
621 3 50         mXPUSHs(newRV_noinc((SV *)hv));
622             }
623 1           ares_free_data(naptr_out);
624             }
625 1           break;
626             }
627              
628 1           case T_CAA: {
629 1           struct ares_caa_reply *caa_out = NULL;
630 1 50         if (ares_parse_caa_reply(abuf, alen, &caa_out) == ARES_SUCCESS && caa_out) {
    50          
631             struct ares_caa_reply *c;
632 12 100         for (c = caa_out; c; c = c->next) {
633 11           HV *hv = newHV();
634 11           hv_stores(hv, "critical", newSViv(c->critical));
635 11           hv_stores(hv, "property", newSVpvn((char *)c->property, c->plength));
636 11           hv_stores(hv, "value", newSVpvn((char *)c->value, c->length));
637 11 50         mXPUSHs(newRV_noinc((SV *)hv));
638             }
639 1           ares_free_data(caa_out);
640             }
641 1           break;
642             }
643              
644             #ifdef HAVE_ARES_DNS_PARSE
645 2           case T_DS:
646             case T_RRSIG:
647             case T_DNSKEY: {
648             /* DS/DNSKEY/RRSIG aren't in c-ares' parsed type set, so they
649             arrive as ARES_REC_TYPE_RAW_RR. Pull out the rdata and
650             parse the wire format ourselves (RFC 4034). */
651 2           ares_dns_record_t *dnsrec = NULL;
652 2 50         if (ares_dns_parse(abuf, alen, 0, &dnsrec) == ARES_SUCCESS && dnsrec) {
    50          
653 2           size_t cnt = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
654             size_t i;
655 5 100         for (i = 0; i < cnt; i++) {
656 3           const ares_dns_rr_t *rr = ares_dns_record_rr_get_const(
657             dnsrec, ARES_SECTION_ANSWER, i);
658             unsigned short raw_type;
659             const unsigned char *rd;
660             size_t rdlen;
661             HV *hv;
662              
663 3 50         if (ares_dns_rr_get_type(rr) != ARES_REC_TYPE_RAW_RR)
664 0           continue;
665 3           raw_type = ares_dns_rr_get_u16(rr, ARES_RR_RAW_RR_TYPE);
666 3 50         if (raw_type != req->qtype) continue;
667              
668 3           rd = ares_dns_rr_get_bin(rr, ARES_RR_RAW_RR_DATA, &rdlen);
669 3 50         if (!rd) continue;
670              
671 3           hv = newHV();
672 3 100         if (raw_type == T_DS && rdlen >= 4) {
    50          
673 1           hv_stores(hv, "key_tag", newSViv((rd[0] << 8) | rd[1]));
674 1           hv_stores(hv, "algorithm", newSViv(rd[2]));
675 1           hv_stores(hv, "digest_type", newSViv(rd[3]));
676 1           hv_stores(hv, "digest",
677             newSVpvn((const char *)rd + 4, rdlen - 4));
678 2 50         } else if (raw_type == T_DNSKEY && rdlen >= 4) {
    50          
679 2           hv_stores(hv, "flags", newSViv((rd[0] << 8) | rd[1]));
680 2           hv_stores(hv, "protocol", newSViv(rd[2]));
681 2           hv_stores(hv, "algorithm", newSViv(rd[3]));
682 2           hv_stores(hv, "public_key",
683             newSVpvn((const char *)rd + 4, rdlen - 4));
684 0 0         } else if (raw_type == T_RRSIG && rdlen >= 18) {
    0          
685 0           size_t pos = 18;
686             char signer[256];
687 0           size_t snlen = 0;
688 0           int signer_overflow = 0;
689 0           int saw_null_label = 0;
690             /* uncompressed DNS labels (RFC 4034 sec 3.1.7) */
691 0 0         while (pos < rdlen) {
692 0           unsigned char l = rd[pos++];
693             size_t need;
694 0 0         if (l == 0) { saw_null_label = 1; break; }
695 0 0         if ((l & 0xc0) || pos + l > rdlen) {
    0          
696 0           pos = rdlen + 1; /* malformed */
697 0           break;
698             }
699 0           need = (snlen ? 1 : 0) + l;
700 0 0         if (snlen + need >= sizeof signer) {
701 0           signer_overflow = 1;
702 0           break;
703             }
704 0 0         if (snlen) signer[snlen++] = '.';
705 0           memcpy(signer + snlen, rd + pos, l);
706 0           snlen += l;
707 0           pos += l;
708             }
709             /* RFC 4034 §3.1.7: signer's name is uncompressed wire
710             format with explicit root label. Reject pointer/
711             overrun, presentation-buffer overflow, or labels
712             that were consumed without a closing null. */
713 0 0         if (pos > rdlen || signer_overflow || !saw_null_label) {
    0          
    0          
714 0           SvREFCNT_dec((SV *)hv);
715 0           continue;
716             }
717              
718 0           hv_stores(hv, "type_covered",
719             newSViv((rd[0] << 8) | rd[1]));
720 0           hv_stores(hv, "algorithm", newSViv(rd[2]));
721 0           hv_stores(hv, "labels", newSViv(rd[3]));
722 0           hv_stores(hv, "original_ttl",
723             newSVuv(((U32)rd[4] << 24) | ((U32)rd[5] << 16) |
724             ((U32)rd[6] << 8) | (U32)rd[7]));
725 0           hv_stores(hv, "sig_expiration",
726             newSVuv(((U32)rd[ 8] << 24) | ((U32)rd[ 9] << 16) |
727             ((U32)rd[10] << 8) | (U32)rd[11]));
728 0           hv_stores(hv, "sig_inception",
729             newSVuv(((U32)rd[12] << 24) | ((U32)rd[13] << 16) |
730             ((U32)rd[14] << 8) | (U32)rd[15]));
731 0           hv_stores(hv, "key_tag",
732             newSViv((rd[16] << 8) | rd[17]));
733 0           hv_stores(hv, "signer_name", newSVpv(signer, snlen));
734 0           hv_stores(hv, "signature",
735             newSVpvn((const char *)rd + pos, rdlen - pos));
736             } else {
737 0           SvREFCNT_dec((SV *)hv);
738 0           continue;
739             }
740 3 50         mXPUSHs(newRV_noinc((SV *)hv));
741             }
742 2           ares_dns_record_destroy(dnsrec);
743             }
744 2           break;
745             }
746              
747 1           case T_TLSA: {
748 1           ares_dns_record_t *dnsrec = NULL;
749 1 50         if (ares_dns_parse(abuf, alen, 0, &dnsrec) == ARES_SUCCESS && dnsrec) {
    50          
750 1           size_t cnt = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
751             size_t i;
752 3 100         for (i = 0; i < cnt; i++) {
753 2           const ares_dns_rr_t *rr = ares_dns_record_rr_get_const(
754             dnsrec, ARES_SECTION_ANSWER, i);
755             HV *hv;
756             const unsigned char *data;
757             size_t data_len;
758              
759 2 50         if (ares_dns_rr_get_type(rr) != ARES_REC_TYPE_TLSA) continue;
760              
761 2           hv = newHV();
762 2           hv_stores(hv, "cert_usage",
763             newSViv(ares_dns_rr_get_u8(rr, ARES_RR_TLSA_CERT_USAGE)));
764 2           hv_stores(hv, "selector",
765             newSViv(ares_dns_rr_get_u8(rr, ARES_RR_TLSA_SELECTOR)));
766 2           hv_stores(hv, "matching_type",
767             newSViv(ares_dns_rr_get_u8(rr, ARES_RR_TLSA_MATCH)));
768 2           data = ares_dns_rr_get_bin(rr, ARES_RR_TLSA_DATA, &data_len);
769 2 50         hv_stores(hv, "data",
    50          
770             newSVpvn(data ? (const char *)data : "", data ? data_len : 0));
771 2 50         mXPUSHs(newRV_noinc((SV *)hv));
772             }
773 1           ares_dns_record_destroy(dnsrec);
774             }
775 1           break;
776             }
777              
778 1           case T_HTTPS:
779             case T_SVCB: {
780 1           ares_dns_record_t *dnsrec = NULL;
781 1 50         if (ares_dns_parse(abuf, alen, 0, &dnsrec) == ARES_SUCCESS && dnsrec) {
    50          
782 1           size_t cnt = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
783             size_t i, j;
784 2 100         for (i = 0; i < cnt; i++) {
785 1           const ares_dns_rr_t *rr = ares_dns_record_rr_get_const(
786             dnsrec, ARES_SECTION_ANSWER, i);
787 1           ares_dns_rec_type_t rtype = ares_dns_rr_get_type(rr);
788             ares_dns_rr_key_t prio_key, target_key, params_key;
789             unsigned short prio;
790             const char *target;
791             HV *hv, *params;
792             size_t opt_cnt;
793              
794 1 50         if (rtype == ARES_REC_TYPE_HTTPS) {
795 1           prio_key = ARES_RR_HTTPS_PRIORITY;
796 1           target_key = ARES_RR_HTTPS_TARGET;
797 1           params_key = ARES_RR_HTTPS_PARAMS;
798 0 0         } else if (rtype == ARES_REC_TYPE_SVCB) {
799 0           prio_key = ARES_RR_SVCB_PRIORITY;
800 0           target_key = ARES_RR_SVCB_TARGET;
801 0           params_key = ARES_RR_SVCB_PARAMS;
802             } else {
803 0           continue;
804             }
805              
806 1           prio = ares_dns_rr_get_u16(rr, prio_key);
807 1           target = ares_dns_rr_get_str(rr, target_key);
808              
809 1           hv = newHV();
810 1           hv_stores(hv, "priority", newSViv(prio));
811 1 50         hv_stores(hv, "target", newSVpv(target ? target : "", 0));
812              
813 1           params = newHV();
814 1           opt_cnt = ares_dns_rr_get_opt_cnt(rr, params_key);
815 4 100         for (j = 0; j < opt_cnt; j++) {
816             const unsigned char *val;
817             size_t vlen;
818 3           unsigned short opt_id = ares_dns_rr_get_opt(
819             rr, params_key, j, &val, &vlen);
820              
821 3           switch (opt_id) {
822 1           case 1: { /* alpn: list of length-prefixed strings */
823 1           AV *av = newAV();
824 1           size_t pos = 0;
825 3 100         while (pos < vlen) {
826 2           size_t plen = val[pos++];
827             /* zero-length entry would loop forever; bail */
828 2 50         if (plen == 0 || pos + plen > vlen) break;
    50          
829 2           av_push(av, newSVpvn((const char *)&val[pos], plen));
830 2           pos += plen;
831             }
832 1           hv_stores(params, "alpn", newRV_noinc((SV *)av));
833 1           break;
834             }
835 0           case 2: /* no-default-alpn */
836 0           hv_stores(params, "no_default_alpn", newSViv(1));
837 0           break;
838 0           case 3: /* port: u16 */
839 0 0         if (vlen >= 2)
840 0           hv_stores(params, "port",
841             newSViv((val[0] << 8) | val[1]));
842 0           break;
843 1           case 4: { /* ipv4hint: list of in_addr */
844 1           AV *av = newAV();
845             size_t pos;
846 3 100         for (pos = 0; pos + 4 <= vlen; pos += 4) {
847             char ipstr[INET_ADDRSTRLEN];
848             struct in_addr a; /* aligned local — val[pos]
849             is byte-aligned only */
850 2           memcpy(&a, &val[pos], sizeof a);
851 2           inet_ntop(AF_INET, &a, ipstr, sizeof ipstr);
852 2           av_push(av, newSVpv(ipstr, 0));
853             }
854 1           hv_stores(params, "ipv4hint", newRV_noinc((SV *)av));
855 1           break;
856             }
857 0           case 5: /* ech: opaque base64-able blob */
858 0           hv_stores(params, "ech",
859             newSVpvn((const char *)val, vlen));
860 0           break;
861 1           case 6: { /* ipv6hint: list of in6_addr */
862 1           AV *av = newAV();
863             size_t pos;
864 3 100         for (pos = 0; pos + 16 <= vlen; pos += 16) {
865             char ipstr[INET6_ADDRSTRLEN];
866             struct in6_addr a; /* aligned local */
867 2           memcpy(&a, &val[pos], sizeof a);
868 2           inet_ntop(AF_INET6, &a, ipstr, sizeof ipstr);
869 2           av_push(av, newSVpv(ipstr, 0));
870             }
871 1           hv_stores(params, "ipv6hint", newRV_noinc((SV *)av));
872 1           break;
873             }
874 0           case 7: /* dohpath */
875 0           hv_stores(params, "dohpath",
876             newSVpvn((const char *)val, vlen));
877 0           break;
878 0           default: {
879             char k[16];
880 0           int klen = snprintf(k, sizeof k, "key%u", opt_id);
881 0           hv_store(params, k, klen,
882             newSVpvn((const char *)val, vlen), 0);
883 0           break;
884             }
885             }
886             }
887 1           hv_stores(hv, "params", newRV_noinc((SV *)params));
888 1 50         mXPUSHs(newRV_noinc((SV *)hv));
889             }
890 1           ares_dns_record_destroy(dnsrec);
891             }
892 1           break;
893             }
894             #endif
895              
896 0           default:
897 0 0         mXPUSHp((char *)abuf, alen);
898 0           break;
899             }
900             }
901              
902 20 50         CB_EPILOGUE;
    50          
    0          
    0          
    50          
    50          
    0          
903 20           }
904              
905             static void
906 1           nameinfo_cb(void *arg, int status, int timeouts, char *node, char *service) {
907 1 50         CB_PROLOGUE(arg, status);
    50          
908              
909 1 50         if (status == ARES_SUCCESS) {
910 1 50         if (node)
911 1 50         mXPUSHp(node, strlen(node));
912             else
913 0 0         XPUSHs(&PL_sv_undef);
914              
915 1 50         if (service)
916 0 0         mXPUSHp(service, strlen(service));
917             else
918 1 50         XPUSHs(&PL_sv_undef);
919             }
920              
921 1 50         CB_EPILOGUE;
    50          
    0          
    0          
    50          
    50          
    0          
922 1           }
923              
924              
925             MODULE = EV::cares PACKAGE = EV::cares PREFIX = ev_cares_
926              
927             BOOT:
928             {
929 19 50         I_EV_API("EV::cares");
    50          
    50          
930             {
931 19           int rc = ares_library_init(ARES_LIB_INIT_ALL);
932 19 50         if (rc != ARES_SUCCESS)
933 0           croak("EV::cares: ares_library_init: %s", ares_strerror(rc));
934             }
935             {
936 19           HV *stash = gv_stashpvn("EV::cares", 9, GV_ADD);
937              
938             /* status codes */
939 19           CONST_IV(stash, ARES_SUCCESS);
940 19           CONST_IV(stash, ARES_ENODATA);
941 19           CONST_IV(stash, ARES_EFORMERR);
942 19           CONST_IV(stash, ARES_ESERVFAIL);
943 19           CONST_IV(stash, ARES_ENOTFOUND);
944 19           CONST_IV(stash, ARES_ENOTIMP);
945 19           CONST_IV(stash, ARES_EREFUSED);
946 19           CONST_IV(stash, ARES_EBADQUERY);
947 19           CONST_IV(stash, ARES_EBADNAME);
948 19           CONST_IV(stash, ARES_EBADFAMILY);
949 19           CONST_IV(stash, ARES_EBADRESP);
950 19           CONST_IV(stash, ARES_ECONNREFUSED);
951 19           CONST_IV(stash, ARES_ETIMEOUT);
952 19           CONST_IV(stash, ARES_EOF);
953 19           CONST_IV(stash, ARES_EFILE);
954 19           CONST_IV(stash, ARES_ENOMEM);
955 19           CONST_IV(stash, ARES_EDESTRUCTION);
956 19           CONST_IV(stash, ARES_EBADSTR);
957 19           CONST_IV(stash, ARES_EBADFLAGS);
958 19           CONST_IV(stash, ARES_ENONAME);
959 19           CONST_IV(stash, ARES_EBADHINTS);
960 19           CONST_IV(stash, ARES_ENOTINITIALIZED);
961 19           CONST_IV(stash, ARES_ECANCELLED);
962 19           CONST_IV(stash, ARES_ESERVICE);
963 19           CONST_IV(stash, ARES_ENOSERVER);
964              
965             /* DNS types */
966 19           CONST_IV(stash, T_A);
967 19           CONST_IV(stash, T_NS);
968 19           CONST_IV(stash, T_CNAME);
969 19           CONST_IV(stash, T_SOA);
970 19           CONST_IV(stash, T_PTR);
971 19           CONST_IV(stash, T_MX);
972 19           CONST_IV(stash, T_TXT);
973 19           CONST_IV(stash, T_AAAA);
974 19           CONST_IV(stash, T_SRV);
975 19           CONST_IV(stash, T_NAPTR);
976 19           CONST_IV(stash, T_DS);
977 19           CONST_IV(stash, T_RRSIG);
978 19           CONST_IV(stash, T_DNSKEY);
979 19           CONST_IV(stash, T_TLSA);
980 19           CONST_IV(stash, T_SVCB);
981 19           CONST_IV(stash, T_HTTPS);
982 19           CONST_IV(stash, T_CAA);
983 19           CONST_IV(stash, T_ANY);
984              
985             /* DNS classes */
986 19           CONST_IV(stash, C_IN);
987 19           CONST_IV(stash, C_CHAOS);
988 19           CONST_IV(stash, C_HS);
989 19           CONST_IV(stash, C_ANY);
990              
991             /* channel flags */
992 19           CONST_IV(stash, ARES_FLAG_USEVC);
993 19           CONST_IV(stash, ARES_FLAG_PRIMARY);
994 19           CONST_IV(stash, ARES_FLAG_IGNTC);
995 19           CONST_IV(stash, ARES_FLAG_NORECURSE);
996 19           CONST_IV(stash, ARES_FLAG_STAYOPEN);
997 19           CONST_IV(stash, ARES_FLAG_NOSEARCH);
998 19           CONST_IV(stash, ARES_FLAG_NOALIASES);
999 19           CONST_IV(stash, ARES_FLAG_NOCHECKRESP);
1000 19           CONST_IV(stash, ARES_FLAG_EDNS);
1001 19           CONST_IV(stash, ARES_FLAG_NO_DFLT_SVR);
1002 19           CONST_IV(stash, ARES_FLAG_DNS0x20);
1003              
1004             /* addrinfo hint flags */
1005 19           CONST_IV(stash, ARES_AI_CANONNAME);
1006 19           CONST_IV(stash, ARES_AI_NUMERICHOST);
1007 19           CONST_IV(stash, ARES_AI_PASSIVE);
1008 19           CONST_IV(stash, ARES_AI_NUMERICSERV);
1009 19           CONST_IV(stash, ARES_AI_V4MAPPED);
1010 19           CONST_IV(stash, ARES_AI_ALL);
1011 19           CONST_IV(stash, ARES_AI_ADDRCONFIG);
1012 19           CONST_IV(stash, ARES_AI_NOSORT);
1013              
1014             /* nameinfo flags */
1015 19           CONST_IV(stash, ARES_NI_NOFQDN);
1016 19           CONST_IV(stash, ARES_NI_NUMERICHOST);
1017 19           CONST_IV(stash, ARES_NI_NAMEREQD);
1018 19           CONST_IV(stash, ARES_NI_NUMERICSERV);
1019 19           CONST_IV(stash, ARES_NI_DGRAM);
1020 19           CONST_IV(stash, ARES_NI_TCP);
1021 19           CONST_IV(stash, ARES_NI_UDP);
1022              
1023             /* address families */
1024 19           CONST_IV(stash, AF_INET);
1025 19           CONST_IV(stash, AF_INET6);
1026 19           CONST_IV(stash, AF_UNSPEC);
1027             }
1028             }
1029              
1030             void
1031             new(class, ...)
1032             PPCODE:
1033             {
1034 100           const char *class_name = SvPV_nolen(ST(0));
1035             ev_cares_t *self;
1036             struct ares_options opts;
1037 100           int optmask = 0;
1038             int status, i;
1039 100           const char *servers = NULL;
1040 100           struct ev_loop *loop_ptr = EV_DEFAULT;
1041 100           SV *loop_sv = NULL;
1042              
1043 100 50         if ((items - 1) % 2 != 0)
1044 0           croak("EV::cares::new: odd number of arguments");
1045              
1046 100           memset(&opts, 0, sizeof(opts));
1047              
1048 215 100         for (i = 1; i < items; i += 2) {
1049 118           const char *key = SvPV_nolen(ST(i));
1050 118           SV *val = ST(i + 1);
1051              
1052 118 100         if (strEQ(key, "loop")) {
1053 6 100         if (SvOK(val)) {
1054 5 100         if (!SvROK(val) || !sv_derived_from(val, "EV::Loop"))
    100          
1055 2           croak("EV::cares::new: 'loop' must be an EV::Loop instance");
1056             /* SvIV (not SvIVX) so any get-magic on the inner SV is
1057             honoured before extracting the loop pointer. */
1058 3           loop_ptr = INT2PTR(struct ev_loop *, SvIV(SvRV(val)));
1059 3           loop_sv = val; /* refcount bumped after struct alloc */
1060             }
1061             }
1062 112 100         else if (strEQ(key, "timeout")) {
1063 18           opts.timeout = (int)(SvNV(val) * 1000);
1064 18           optmask |= ARES_OPT_TIMEOUTMS;
1065             }
1066 94 100         else if (strEQ(key, "tries")) {
1067 17           opts.tries = SvIV(val);
1068 17           optmask |= ARES_OPT_TRIES;
1069             }
1070 77 100         else if (strEQ(key, "ndots")) {
1071 1           opts.ndots = SvIV(val);
1072 1           optmask |= ARES_OPT_NDOTS;
1073             }
1074 76 100         else if (strEQ(key, "flags")) {
1075 7           opts.flags = SvIV(val);
1076 7           optmask |= ARES_OPT_FLAGS;
1077             }
1078 69 100         else if (strEQ(key, "lookups")) {
1079 48           opts.lookups = SvPV_nolen(val); /* c-ares copies internally */
1080 48           optmask |= ARES_OPT_LOOKUPS;
1081             }
1082 21 50         else if (strEQ(key, "tcp_port")) {
1083 0           opts.tcp_port = (unsigned short)SvUV(val);
1084 0           optmask |= ARES_OPT_TCP_PORT;
1085             }
1086 21 50         else if (strEQ(key, "udp_port")) {
1087 0           opts.udp_port = (unsigned short)SvUV(val);
1088 0           optmask |= ARES_OPT_UDP_PORT;
1089             }
1090 21 100         else if (strEQ(key, "servers")) {
1091 9 50         if (SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVAV) {
    50          
1092 5           AV *av = (AV *)SvRV(val);
1093 5 100         if (av_len(av) < 0)
1094 1           croak("EV::cares::new: 'servers' arrayref is empty");
1095 4           servers = SvPV_nolen(av_to_csv(aTHX_ av));
1096             } else {
1097 0           servers = SvPV_nolen(val);
1098             }
1099             }
1100 16 100         else if (strEQ(key, "rotate")) {
1101 2 50         if (SvTRUE(val)) {
1102 2           #ifdef ARES_OPT_ROTATE
1103             optmask |= ARES_OPT_ROTATE;
1104             #endif
1105 14 100         }
1106 1           }
1107 1           #ifdef ARES_OPT_EDNSPSZ
1108             else if (strEQ(key, "ednspsz")) {
1109 13 100         opts.ednspsz = SvIV(val);
1110 1           optmask |= ARES_OPT_EDNSPSZ;
1111 1           }
1112             #endif
1113 12 100         #ifdef ARES_OPT_RESOLVCONF
1114 9           else if (strEQ(key, "resolvconf")) {
1115 9           opts.resolvconf_path = SvPV_nolen(val); /* c-ares copies internally */
1116             optmask |= ARES_OPT_RESOLVCONF;
1117 3 100         }
1118 1           #endif
1119 1           #ifdef ARES_OPT_HOSTS_FILE
1120             else if (strEQ(key, "hosts_file")) {
1121 2 100         opts.hosts_path = SvPV_nolen(val); /* c-ares copies internally */
1122 1           optmask |= ARES_OPT_HOSTS_FILE;
1123 1           }
1124             #endif
1125 1 50         #ifdef ARES_OPT_UDP_MAX_QUERIES
1126 1           else if (strEQ(key, "udp_max_queries")) {
1127 1           opts.udp_max_queries = SvIV(val);
1128             optmask |= ARES_OPT_UDP_MAX_QUERIES;
1129             }
1130 0           #endif
1131             #ifdef ARES_OPT_MAXTIMEOUTMS
1132             else if (strEQ(key, "maxtimeout")) {
1133             opts.maxtimeout = (int)(SvNV(val) * 1000);
1134 97           optmask |= ARES_OPT_MAXTIMEOUTMS;
1135 97           }
1136 97           #endif
1137             #ifdef ARES_OPT_QUERY_CACHE
1138             else if (strEQ(key, "qcache")) {
1139             opts.qcache_max_ttl = (unsigned int)SvUV(val);
1140 97 100         optmask |= ARES_OPT_QUERY_CACHE;
1141 1649 100         }
1142 1552           #endif
1143             else {
1144 97           warn("EV::cares::new: unknown option '%s'", key);
1145 97           }
1146 97           }
1147              
1148 97           Newxz(self, 1, ev_cares_t);
1149 97 50         self->magic = EV_CARES_MAGIC;
1150 0 0         self->loop = loop_ptr;
1151 0           /* Keep the EV::Loop's blessed object alive — its DESTROY calls
1152 0           ev_loop_destroy(). Holding the outer RV is not enough; inc the
1153             underlying SV (the IV that stores the loop pointer). */
1154             if (loop_sv) self->loop_sv = SvREFCNT_inc_simple_NN(SvRV(loop_sv));
1155 97 100         for (i = 0; i < MAX_IO; i++)
1156 4           self->ios[i].fd = ARES_SOCKET_BAD;
1157 4 50          
1158 0           opts.sock_state_cb = sock_state_cb;
1159 0 0         opts.sock_state_cb_data = self;
1160 0           optmask |= ARES_OPT_SOCK_STATE_CB;
1161 0            
1162             status = ares_init_options(&self->channel, &opts, optmask);
1163             if (status != ARES_SUCCESS) {
1164             if (self->loop_sv) SvREFCNT_dec(self->loop_sv);
1165 97           Safefree(self);
1166 97           croak("EV::cares::new: ares_init_options: %s", ares_strerror(status));
1167             }
1168              
1169 97           if (servers) {
1170 97           status = ares_set_servers_csv(self->channel, servers);
1171 97 50         if (status != ARES_SUCCESS) {
1172             ares_destroy(self->channel);
1173             if (self->loop_sv) SvREFCNT_dec(self->loop_sv);
1174             Safefree(self);
1175             croak("EV::cares::new: set_servers: %s", ares_strerror(status));
1176             }
1177             }
1178              
1179             ev_timer_init(&self->timer, timer_cb, 0., 0.);
1180             self->timer.data = (void *)self;
1181              
1182             {
1183             SV *sv = newSV(0);
1184             sv_setref_pv(sv, class_name, (void *)self);
1185             XPUSHs(sv_2mortal(sv));
1186             }
1187             }
1188              
1189             void
1190             resolve(self, name, cb)
1191             EV::cares self
1192             const char *name
1193             SV *cb
1194             CODE:
1195             {
1196             struct ares_addrinfo_hints hints;
1197             ev_cares_req_t *req;
1198              
1199 60 50         REQUIRE_LIVE(self);
    100          
1200 58 100         REQUIRE_CB(cb);
    50          
1201              
1202 57           memset(&hints, 0, sizeof(hints));
1203 57           hints.ai_family = AF_UNSPEC;
1204 57           hints.ai_socktype = SOCK_STREAM;
1205              
1206 57           req = new_req(aTHX_ self, cb);
1207 57           ARES_CALL_BEGIN(self);
1208 57           ares_getaddrinfo(self->channel, name, NULL, &hints, addrinfo_cb, req);
1209 57 100         ARES_CALL_END(self);
    50          
1210             }
1211              
1212             void
1213             resolve_ttl(self, name, cb)
1214             EV::cares self
1215             const char *name
1216             SV *cb
1217             CODE:
1218             {
1219             struct ares_addrinfo_hints hints;
1220             ev_cares_req_t *req;
1221              
1222 4 50         REQUIRE_LIVE(self);
    50          
1223 4 50         REQUIRE_CB(cb);
    50          
1224              
1225 4           memset(&hints, 0, sizeof(hints));
1226 4           hints.ai_family = AF_UNSPEC;
1227 4           hints.ai_socktype = SOCK_STREAM;
1228              
1229 4           req = new_req(aTHX_ self, cb);
1230 4           req->with_ttl = 1;
1231 4           ARES_CALL_BEGIN(self);
1232 4           ares_getaddrinfo(self->channel, name, NULL, &hints, addrinfo_cb, req);
1233 4 50         ARES_CALL_END(self);
    50          
1234             }
1235              
1236             void
1237             getaddrinfo(self, node, service, hints_hv, cb)
1238             EV::cares self
1239             SV *node
1240             SV *service
1241             SV *hints_hv
1242             SV *cb
1243             CODE:
1244             {
1245 6 50         const char *c_node = SvOK(node) ? SvPV_nolen(node) : NULL;
1246 6 100         const char *c_service = SvOK(service) ? SvPV_nolen(service) : NULL;
1247             struct ares_addrinfo_hints hints;
1248             ev_cares_req_t *req;
1249              
1250 6 50         REQUIRE_LIVE(self);
    50          
1251 6 50         REQUIRE_CB(cb);
    50          
1252              
1253 6           memset(&hints, 0, sizeof(hints));
1254 6           hints.ai_family = AF_UNSPEC;
1255 6           hints.ai_socktype = SOCK_STREAM; /* avoid duplicate entries per socktype */
1256              
1257 6           req = new_req(aTHX_ self, cb);
1258              
1259 6 50         if (SvROK(hints_hv) && SvTYPE(SvRV(hints_hv)) == SVt_PVHV) {
    50          
1260 6           HV *hv = (HV *)SvRV(hints_hv);
1261             SV **sv;
1262 6 50         if ((sv = hv_fetchs(hv, "family", 0))) hints.ai_family = SvIV(*sv);
1263 6 50         if ((sv = hv_fetchs(hv, "socktype", 0))) hints.ai_socktype = SvIV(*sv);
1264 6 50         if ((sv = hv_fetchs(hv, "protocol", 0))) hints.ai_protocol = SvIV(*sv);
1265 6 50         if ((sv = hv_fetchs(hv, "flags", 0))) hints.ai_flags = SvIV(*sv);
1266 6 100         if ((sv = hv_fetchs(hv, "ttl", 0)) && SvTRUE(*sv))
    50          
1267 1           req->with_ttl = 1;
1268             }
1269              
1270 6           ARES_CALL_BEGIN(self);
1271 6           ares_getaddrinfo(self->channel, c_node, c_service, &hints, addrinfo_cb, req);
1272 6 50         ARES_CALL_END(self);
    50          
1273             }
1274              
1275             void
1276             gethostbyname(self, name, family, cb)
1277             EV::cares self
1278             const char *name
1279             int family
1280             SV *cb
1281             CODE:
1282             {
1283             ev_cares_req_t *req;
1284 1 50         REQUIRE_LIVE(self);
    50          
1285 1 50         REQUIRE_CB(cb);
    50          
1286 1           req = new_req(aTHX_ self, cb);
1287 1           ARES_CALL_BEGIN(self);
1288 1           ares_gethostbyname(self->channel, name, family, host_cb, req);
1289 1 50         ARES_CALL_END(self);
    50          
1290             }
1291              
1292             void
1293             search(self, name, type, ...)
1294             EV::cares self
1295             const char *name
1296             int type
1297             PREINIT:
1298             SV *cb;
1299 21 50         int dnsclass = C_IN;
1300             ev_cares_req_t *req;
1301             CODE:
1302             {
1303 21 50         REQUIRE_LIVE(self);
    50          
1304             /* search($name, $type, $cb) -> items == 4
1305             search($name, $type, $class, $cb) -> items == 5 */
1306 21 100         if (items == 4) {
1307 18           cb = ST(3);
1308 3 100         } else if (items == 5) {
1309 2           dnsclass = SvIV(ST(3));
1310 2           cb = ST(4);
1311             } else {
1312 1           croak("Usage: $r->search($name, $type, [$class,] $cb)");
1313             }
1314 20 50         REQUIRE_CB(cb);
    50          
1315              
1316 20           req = new_req(aTHX_ self, cb);
1317 20           req->qtype = type;
1318 20           ARES_CALL_BEGIN(self);
1319 20           ares_search(self->channel, name, dnsclass, type, search_cb, req);
1320 20 50         ARES_CALL_END(self);
    50          
1321             }
1322              
1323             void
1324             query(self, name, dnsclass, type, cb)
1325             EV::cares self
1326             const char *name
1327             int dnsclass
1328             int type
1329             SV *cb
1330             CODE:
1331             {
1332             ev_cares_req_t *req;
1333 35 50         REQUIRE_LIVE(self);
    50          
1334 35 50         REQUIRE_CB(cb);
    50          
1335 35           req = new_req(aTHX_ self, cb);
1336 35           ARES_CALL_BEGIN(self);
1337 35           ares_query(self->channel, name, dnsclass, type, raw_cb, req);
1338 35 50         ARES_CALL_END(self);
    50          
1339             }
1340              
1341             void
1342             reverse(self, ip, cb)
1343             EV::cares self
1344             const char *ip
1345             SV *cb
1346             CODE:
1347             {
1348             struct in_addr addr4;
1349             struct in6_addr addr6;
1350             ev_cares_req_t *req;
1351             const void *ap;
1352             size_t alen;
1353             int family;
1354              
1355 4 50         REQUIRE_LIVE(self);
    50          
1356 4 50         REQUIRE_CB(cb);
    50          
1357              
1358             /* validate before entering the ARES_CALL bracket so croak doesn't
1359             leave in_callback unbalanced */
1360 4 100         if (inet_pton(AF_INET, ip, &addr4) == 1) {
1361 3           family = AF_INET; ap = &addr4; alen = sizeof addr4;
1362 1 50         } else if (inet_pton(AF_INET6, ip, &addr6) == 1) {
1363 0           family = AF_INET6; ap = &addr6; alen = sizeof addr6;
1364             } else {
1365 1           croak("EV::cares::reverse: invalid IP address: %s", ip);
1366             }
1367              
1368 3           req = new_req(aTHX_ self, cb);
1369 3           req->by_addr = 1;
1370 3           ARES_CALL_BEGIN(self);
1371 3           ares_gethostbyaddr(self->channel, ap, alen, family, host_cb, req);
1372 3 50         ARES_CALL_END(self);
    50          
1373             }
1374              
1375             void
1376             getnameinfo(self, sa, flags, cb)
1377             EV::cares self
1378             SV *sa
1379             int flags
1380             SV *cb
1381             CODE:
1382             {
1383             STRLEN len;
1384 4           const char *addr = SvPV(sa, len);
1385             ev_cares_req_t *req;
1386             struct sockaddr_storage ss;
1387             sa_family_t family;
1388             STRLEN min_len;
1389              
1390 4 50         REQUIRE_LIVE(self);
    50          
1391 4 50         REQUIRE_CB(cb);
    50          
1392              
1393             /* Require enough bytes to safely read sa_family (sa_family_t lives at
1394             different offsets across platforms — Linux puts it at byte 0, BSD
1395             has sa_len at byte 0 and sa_family at byte 1). sizeof(struct
1396             sockaddr) is the universal lower bound for either layout. */
1397 4 100         if (len < sizeof(struct sockaddr))
1398 1           croak("EV::cares::getnameinfo: sockaddr too short (%d bytes)", (int)len);
1399              
1400             /* Copy into a local sockaddr_storage so both the family read and
1401             the c-ares call see properly-aligned bytes (matters on strict-
1402             alignment architectures like SPARC / classic MIPS). */
1403 3 50         if (len > sizeof ss) len = sizeof ss;
1404 3           memcpy(&ss, addr, len);
1405 3           family = ((struct sockaddr *)&ss)->sa_family;
1406 3 100         if (family == AF_INET) min_len = sizeof(struct sockaddr_in);
1407 2 100         else if (family == AF_INET6) min_len = sizeof(struct sockaddr_in6);
1408 1           else croak("EV::cares::getnameinfo: unsupported sockaddr family %d "
1409             "(need AF_INET or AF_INET6)", (int)family);
1410 2 100         if (len < min_len)
1411 1 50         croak("EV::cares::getnameinfo: sockaddr too short for %s (%d bytes)",
1412             family == AF_INET6 ? "AF_INET6" : "AF_INET", (int)len);
1413              
1414 1           req = new_req(aTHX_ self, cb);
1415 1           ARES_CALL_BEGIN(self);
1416 1           ares_getnameinfo(self->channel, (const struct sockaddr *)&ss,
1417             (ares_socklen_t)len, flags, nameinfo_cb, req);
1418 1 50         ARES_CALL_END(self);
    50          
1419             }
1420              
1421             void
1422             cancel(self)
1423             EV::cares self
1424             CODE:
1425 6 50         REQUIRE_LIVE(self);
    100          
1426 5           ARES_CALL_BEGIN(self);
1427 5           ares_cancel(self->channel);
1428 5 50         ARES_CALL_END(self);
    50          
1429              
1430             void
1431             set_servers(self, ...)
1432             EV::cares self
1433             CODE:
1434             {
1435             SV *csv;
1436             int rc, i;
1437              
1438 10 50         REQUIRE_LIVE(self);
    50          
1439 10 100         if (items <= 1)
1440 1           croak("EV::cares::set_servers: requires at least one server address");
1441              
1442             /* accept either a single arrayref or a flat list, mirroring `new(servers => ...)` */
1443 11 100         if (items == 2 && SvROK(ST(1)) && SvTYPE(SvRV(ST(1))) == SVt_PVAV) {
    100          
    100          
1444 3           AV *av = (AV *)SvRV(ST(1));
1445 3 100         if (av_len(av) < 0)
1446 1           croak("EV::cares::set_servers: empty arrayref");
1447 2           csv = av_to_csv(aTHX_ av);
1448             } else {
1449 6           csv = sv_2mortal(newSVpvs(""));
1450 12 100         for (i = 1; i < items; i++) {
1451 7 100         if (SvROK(ST(i)))
1452 1           croak("EV::cares::set_servers: arg %d is a reference; "
1453             "use the arrayref form for {host,port} hashrefs", i);
1454 6 100         if (i > 1) sv_catpvs(csv, ",");
1455 6           sv_catsv(csv, ST(i));
1456             }
1457             }
1458              
1459 7           rc = ares_set_servers_csv(self->channel, SvPV_nolen(csv));
1460 7 50         if (rc != ARES_SUCCESS)
1461 0           croak("EV::cares::set_servers: %s", ares_strerror(rc));
1462             }
1463              
1464             void
1465             servers(self)
1466             EV::cares self
1467             PPCODE:
1468             {
1469             char *csv;
1470 8 50         REQUIRE_LIVE(self);
    50          
1471 8           csv = ares_get_servers_csv(self->channel);
1472 8 50         if (!csv) croak("EV::cares::servers: ares_get_servers_csv failed");
1473 8 50         mXPUSHp(csv, strlen(csv));
1474 8           ares_free_string(csv);
1475             }
1476              
1477             void
1478             set_local_dev(self, dev)
1479             EV::cares self
1480             const char *dev
1481             CODE:
1482 1 50         REQUIRE_LIVE(self);
    50          
1483 1           ares_set_local_dev(self->channel, dev);
1484              
1485             void
1486             set_local_ip4(self, ip)
1487             EV::cares self
1488             const char *ip
1489             CODE:
1490             {
1491             struct in_addr addr;
1492 2 50         REQUIRE_LIVE(self);
    50          
1493 2 100         if (inet_pton(AF_INET, ip, &addr) != 1)
1494 1           croak("EV::cares::set_local_ip4: invalid IPv4 address: %s", ip);
1495 1           ares_set_local_ip4(self->channel, ntohl(addr.s_addr));
1496             }
1497              
1498             void
1499             set_local_ip6(self, ip)
1500             EV::cares self
1501             const char *ip
1502             CODE:
1503             {
1504             struct in6_addr addr;
1505 2 50         REQUIRE_LIVE(self);
    50          
1506 2 100         if (inet_pton(AF_INET6, ip, &addr) != 1)
1507 1           croak("EV::cares::set_local_ip6: invalid IPv6 address: %s", ip);
1508 1           ares_set_local_ip6(self->channel, (const unsigned char *)&addr);
1509             }
1510              
1511             int
1512             active_queries(self)
1513             EV::cares self
1514             CODE:
1515 30 100         RETVAL = self->active_queries;
1516             OUTPUT:
1517             RETVAL
1518              
1519             int
1520             last_query_timeouts(self)
1521             EV::cares self
1522             CODE:
1523 3 50         RETVAL = self->last_timeouts;
1524             OUTPUT:
1525             RETVAL
1526              
1527             int
1528             is_destroyed(self)
1529             EV::cares self
1530             CODE:
1531 32 100         RETVAL = self->destroyed;
1532             OUTPUT:
1533             RETVAL
1534              
1535             SV *
1536             loop(self)
1537             EV::cares self
1538             CODE:
1539             /* Return a fresh RV to the user-supplied EV::Loop blessed object, or
1540             undef when this resolver runs on EV's default loop. Used by
1541             wait_idle() so it pumps the same loop the watchers are armed on. */
1542 3 100         RETVAL = self->loop_sv ? newRV_inc(self->loop_sv) : &PL_sv_undef;
1543             OUTPUT:
1544             RETVAL
1545              
1546             double
1547             next_timeout(self)
1548             EV::cares self
1549             CODE:
1550             {
1551             struct timeval tv, *tvp;
1552 2 50         REQUIRE_LIVE(self);
    100          
1553 1           tvp = ares_timeout(self->channel, NULL, &tv);
1554 1 50         RETVAL = tvp ? (double)tvp->tv_sec + (double)tvp->tv_usec / 1e6 : -1.0;
    50          
1555             }
1556             OUTPUT:
1557             RETVAL
1558              
1559             void
1560             set_sortlist(self, sortlist)
1561             EV::cares self
1562             const char *sortlist
1563             CODE:
1564             {
1565             int rc;
1566 2 50         REQUIRE_LIVE(self);
    50          
1567 2           rc = ares_set_sortlist(self->channel, sortlist);
1568 2 100         if (rc != ARES_SUCCESS)
1569 1           croak("EV::cares::set_sortlist: %s", ares_strerror(rc));
1570             }
1571              
1572             void
1573             destroy(self)
1574             EV::cares self
1575             CODE:
1576             /* Deliberately weaker than REQUIRE_LIVE — only checks magic, not
1577             destroyed. This makes double-destroy a silent no-op (cleanup()
1578             early-returns when destroyed=1), per the documented contract. */
1579 14 50         if (self->magic != EV_CARES_MAGIC) croak("EV::cares: invalid object");
1580 14           cleanup(self);
1581              
1582             void
1583             reinit(self)
1584             EV::cares self
1585             CODE:
1586             {
1587             int rc;
1588 4 50         REQUIRE_LIVE(self);
    100          
1589 3           rc = ares_reinit(self->channel);
1590 3 50         if (rc != ARES_SUCCESS)
1591 0           croak("EV::cares::reinit: %s", ares_strerror(rc));
1592             }
1593              
1594             void
1595             DESTROY(self)
1596             EV::cares self
1597             CODE:
1598 97 50         if (self->magic != EV_CARES_MAGIC) return;
1599              
1600 97 50         if (PL_dirty) {
1601             /* Global destruction: skip everything that would touch other
1602             SVs or libev (their teardown order is unspecified) and leak
1603             self. Stopping watchers during PL_dirty is unsafe, and
1604             freeing self while watchers still reference it has caused
1605             SEGVs on musl during process exit. OS reclaims at exit. */
1606 0           self->destroyed = 1;
1607 0           self->channel = NULL;
1608 0           return;
1609             }
1610              
1611 97           cleanup(self);
1612 97 50         if (self->in_callback) {
1613             /* defer Safefree until the outer ARES_CALL_END unwinds. Every
1614             callback path enters through an ARES_CALL_BEGIN bracket (io_cb,
1615             timer_cb, or an XS query method), so the matching ARES_CALL_END
1616             drops in_callback to 0 and frees self there. */
1617 0           self->free_pending = 1;
1618             } else {
1619 97           Safefree(self);
1620             }
1621              
1622             const char *
1623             strerror(...)
1624             CODE:
1625             {
1626             SV *arg;
1627             /* accept both EV::cares::strerror($n) and EV::cares->strerror($n) */
1628 35 100         if (items == 1) arg = ST(0);
1629 3 100         else if (items >= 2) arg = ST(1); /* skip class/instance arg */
1630 1           else croak("Usage: EV::cares::strerror(status)");
1631              
1632             /* looks_like_number returns false for refs, so SvROK precheck not needed */
1633 34 100         if (!looks_like_number(arg))
1634 3           croak("Usage: EV::cares::strerror(status)");
1635              
1636 31           RETVAL = ares_strerror(SvIV(arg));
1637             }
1638             OUTPUT:
1639             RETVAL
1640              
1641             const char *
1642             lib_version(...)
1643             CODE:
1644 4           RETVAL = ares_version(NULL);
1645             OUTPUT:
1646             RETVAL