--- cvsroot/EV-ADNS/ADNS.xs 2007/12/01 13:53:11 1.1 +++ cvsroot/EV-ADNS/ADNS.xs 2007/12/20 07:13:36 1.16 @@ -7,53 +7,264 @@ #include "EVAPI.h" -static adns_state ads; +static struct pollfd *fds; +static int nfd, mfd; +static ev_io *iow; +static ev_timer tw; +static ev_idle iw; +static ev_prepare pw; +static struct timeval tv_now; +static int outstanding; + +static void +outstanding_inc (adns_state ads) +{ + if (!outstanding++) + ev_prepare_start (EV_DEFAULT, &pw); +} + +static void +outstanding_dec (adns_state ads) +{ + --outstanding; +} struct ctx { - SV *req; + SV *self; + adns_state ads; + adns_query query; SV *cb; }; +static SV * +ha2sv (adns_rr_hostaddr *rr) +{ + AV *av = newAV (); + av_push (av, newSVpv (rr->host, 0)); + // TODO: add addresses + + return newRV_noinc ((SV *)av); +} + static void -process () +process (adns_state ads) { + dSP; + for (;;) { - adns_query q; + int i; + adns_query q = 0; adns_answer *a; - void *c; - int r = adns_check (ads, &q, &a, &c); + void *ctx; + SV *cb; + struct ctx *c; + int r = adns_check (ads, &q, &a, &ctx); if (r) break; + + c = (struct ctx *)ctx; + cb = c->cb; + c->cb = 0; outstanding_dec (ads); + SvREFCNT_dec (c->self); + + assert (cb); + + PUSHMARK (SP); + + EXTEND (SP, a->nrrs + 2); + PUSHs (sv_2mortal (newSViv (a->status))); + PUSHs (sv_2mortal (newSViv (a->expires))); + + for (i = 0; i < a->nrrs; ++i) + { + SV *sv; + + switch (a->type & adns_r_unknown ? adns_r_unknown : a->type) + { + case adns_r_ns_raw: + case adns_r_cname: + case adns_r_ptr: + case adns_r_ptr_raw: + sv = newSVpv (a->rrs.str [i], 0); + break; + + case adns_r_txt: + { + AV *av = newAV (); + adns_rr_intstr *rr = a->rrs.manyistr [i]; + + while (rr->str) + { + av_push (av, newSVpvn (rr->str, rr->i)); + ++rr; + } + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_a: + sv = newSVpv (inet_ntoa (a->rrs.inaddr [i]), 0); + break; + + case adns_r_ns: + sv = ha2sv (a->rrs.hostaddr + i); + break; + + case adns_r_hinfo: + { + /* untested */ + AV *av = newAV (); + adns_rr_intstrpair *rr = a->rrs.intstrpair + i; + + av_push (av, newSVpvn (rr->array [0].str, rr->array [0].i)); + av_push (av, newSVpvn (rr->array [1].str, rr->array [1].i)); + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_rp: + case adns_r_rp_raw: + { + /* untested */ + AV *av = newAV (); + adns_rr_strpair *rr = a->rrs.strpair + i; + + av_push (av, newSVpv (rr->array [0], 0)); + av_push (av, newSVpv (rr->array [1], 0)); + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_mx: + { + AV *av = newAV (); + adns_rr_inthostaddr *rr = a->rrs.inthostaddr + i; + + av_push (av, newSViv (rr->i)); + av_push (av, ha2sv (&rr->ha)); + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_mx_raw: + { + AV *av = newAV (); + adns_rr_intstr *rr = a->rrs.intstr + i; + + av_push (av, newSViv (rr->i)); + av_push (av, newSVpv (rr->str, 0)); + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_soa: + case adns_r_soa_raw: + { + AV *av = newAV (); + adns_rr_soa *rr = a->rrs.soa + i; + + av_push (av, newSVpv (rr->mname, 0)); + av_push (av, newSVpv (rr->rname, 0)); + av_push (av, newSVuv (rr->serial)); + av_push (av, newSVuv (rr->refresh)); + av_push (av, newSVuv (rr->retry)); + av_push (av, newSVuv (rr->expire)); + av_push (av, newSVuv (rr->minimum)); + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_srv_raw: + { + AV *av = newAV (); + adns_rr_srvraw *rr = a->rrs.srvraw + i; + + av_push (av, newSViv (rr->priority)); + av_push (av, newSViv (rr->weight)); + av_push (av, newSViv (rr->port)); + av_push (av, newSVpv (rr->host, 0)); + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_srv: + { + AV *av = newAV (); + adns_rr_srvha *rr = a->rrs.srvha + i; + + av_push (av, newSViv (rr->priority)); + av_push (av, newSViv (rr->weight)); + av_push (av, newSViv (rr->port)); + av_push (av, ha2sv (&rr->ha)); + + sv = newRV_noinc ((SV *)av); + } + break; + + case adns_r_unknown: + sv = newSVpvn (a->rrs.byteblock [i].data, a->rrs.byteblock [i].len); + break; + + default: + case adns_r_addr: + sv = &PL_sv_undef; /* not supported */ + break; + } + + PUSHs (sv_2mortal (sv)); + } + + free (a); + + PUTBACK; + call_sv (cb, G_VOID | G_DISCARD | G_EVAL); + SPAGAIN; + + if (SvTRUE (ERRSV)) + warn ("%s", SvPV_nolen (ERRSV)); + + SvREFCNT_dec (cb); } } -static struct pollfd *fds; -static int nfd; -static ev_io *iow; -static ev_timer tw; -static ev_prepare prepare_ev; -static struct timeval *tv_now; - static void update_now (EV_P) { - ev_tstamp t = ev_now (EV_P); + ev_tstamp t = ev_now (EV_A); - tv.tv_sec = (long)t; - tv.tv_usec = (long)((t - (ev_tstamp)tv.tv_sec) * 1e-6); + tv_now.tv_sec = (long)t; + tv_now.tv_usec = (long)((t - (ev_tstamp)tv_now.tv_sec) * 1e6); +} + +static void +idle_cb (EV_P_ ev_idle *w, int revents) +{ + ev_idle_stop (EV_A, w); } static void timer_cb (EV_P_ ev_timer *w, int revents) { + adns_state ads = (adns_state)w->data; + update_now (EV_A); + + adns_processtimeouts (ads, &tv_now); } static void io_cb (EV_P_ ev_io *w, int revents) { + adns_state ads = (adns_state)w->data; update_now (EV_A); if (revents & EV_READ ) adns_processreadable (ads, w->fd, &tv_now); @@ -66,50 +277,70 @@ { int i; int timeout = 3600000; + adns_state ads = (adns_state)w->data; + + if (ev_is_active (&tw)) + ev_timer_stop (EV_A, &tw); - ev_timer_stop (EV_A_ &tw); + if (ev_is_active (&iw)) + ev_idle_stop (EV_A, &iw); for (i = 0; i < nfd; ++i) - ev_io_stop (EV_A_ iow + i); + ev_io_stop (EV_A, iow + i); - process (); + process (ads); + + if (!outstanding) + { + ev_prepare_stop (EV_A, w); + return; + } update_now (EV_A); + nfd = mfd; + while (adns_beforepoll (ads, fds, &nfd, &timeout, &tv_now)) { - free (iow); iow = malloc (nfd * sizeof (struct pollfd)); - free (fds); fds = malloc (nfd * sizeof (ev_io)); + mfd = nfd; + + free (iow); iow = malloc (mfd * sizeof (ev_io)); + free (fds); fds = malloc (mfd * sizeof (struct pollfd)); } ev_timer_set (&tw, timeout * 1e-3, 0.); - ev_timer_start (EV_A_ &tw); + ev_timer_start (EV_A, &tw); - // create on ev_io per pollfd + // create one ev_io per pollfd for (i = 0; i < nfd; ++i) { - ev_io_init (iow + i, io_cb, fds [i].fd, + ev_io *w = iow + i; + + ev_io_init (w, io_cb, fds [i].fd, ((fds [i].events & POLLIN ? EV_READ : 0) | (fds [i].events & POLLOUT ? EV_WRITE : 0))); - ev_io_start (EV_A_ iow + i); + w->data = (void *)ads; + ev_io_start (EV_A, w); } } +static HV *stash; +static adns_state ads; + MODULE = EV::ADNS PACKAGE = EV::ADNS PROTOTYPES: ENABLE BOOT: { -#if 0 - HV *stash = gv_stashpv ("EV::ADNS", 1); + stash = gv_stashpv ("EV::ADNS", 1); static const struct { const char *name; IV iv; } *civ, const_iv[] = { -# define const_iv(pfx, name) { # name, (IV) adns_ ## name }, +# define const_iv(name) { # name, (IV) adns_ ## name }, const_iv (if_none) const_iv (if_noenv) const_iv (if_noerrprint) @@ -190,31 +421,78 @@ for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); civ-- > const_iv; ) newCONSTSUB (stash, (char *)civ->name, newSViv (civ->iv)); -#endif I_EV_API ("EV::ADNS"); - ev_prepare_init (&prepare_ev, prepare_cb); ev_prepare_start (EV_DEFAULT_ &prepare_ev); - ev_unref (); + adns_init (&ads, adns_if_noenv | adns_if_noerrprint | adns_if_noserverwarn | adns_if_noautosys, 0); - ev_init (&tw, timer_cb); + ev_prepare_init (&pw, prepare_cb); + pw.data = (void *)ads; - adns_init (&ads, adns_if_noenv | adns_if_noerrprint | adns_if_noserverwarn | adns_if_noautosys, 0); + ev_init (&iw, idle_cb); ev_set_priority (&iw, EV_MINPRI); + iw.data = (void *)ads; + ev_init (&tw, timer_cb); + tw.data = (void *)ads; } -int adns_submit (char *owner, int type, int flags, SV *cb) - CODE: +void submit (char *owner, int type, int flags, SV *cb) + PPCODE: { - struct ctx *c = (struct ctx *)malloc (sizeof (ctx)); - adns_query q; - int r = adns_submit (owner, type, flags, (void *)c, &q); + SV *csv = NEWSV (0, sizeof (struct ctx)); + struct ctx *c = (struct ctx *)SvPVX (csv); + int r = adns_submit (ads, owner, type, flags, (void *)c, &c->query); + + outstanding_inc (ads); if (r) { - free (c); + SvREFCNT_dec (csv); + errno = r; XSRETURN_EMPTY; } + else + { + SvPOK_only (csv); + SvCUR_set (csv, sizeof (struct ctx)); + + c->self = csv; + c->cb = newSVsv (cb); + c->ads = ads; + + if (!ev_is_active (&iw)) + ev_idle_start (EV_DEFAULT, &iw); + + if (GIMME_V != G_VOID) + { + csv = sv_2mortal (newRV_inc (csv)); + sv_bless (csv, stash); + XPUSHs (csv); + } + } +} + +void DESTROY (SV *req) + ALIAS: + cancel = 1 + CODE: +{ + struct ctx *c; + + if (!(SvROK (req) && SvOBJECT (SvRV (req)) + && (SvSTASH (SvRV (req)) == stash))) + croak ("object is not of type EV::ADNS"); + + c = (struct ctx *)SvPVX (SvRV (req)); + + if (c->cb) + { + SvREFCNT_dec (c->cb); + c->cb = 0; outstanding_dec (c->ads); + adns_cancel (c->query); + SvREFCNT_dec (c->self); + } } +