/*----- Static variables --------------------------------------------------*/
+#ifdef HAVE_LIBADNS
+ static adns_state ads;
+ sel_hook hook;
+#endif
static admin *admins;
static admin *a_dead;
static sel_file sock;
} else if (*fmt == '?') {
if (strcmp(fmt, "?ADDR") == 0) {
const addr *a = va_arg(*ap, const addr *);
- switch (a->sa.sa_family) {
- case AF_INET:
- u_quotify(d, "INET");
- u_quotify(d, inet_ntoa(a->sin.sin_addr));
- dstr_putf(d, " %u", (unsigned)ntohs(a->sin.sin_port));
- break;
- default:
- abort();
+ char name[NI_MAXHOST], serv[NI_MAXSERV];
+ int ix, err;
+ if ((err = getnameinfo(&a->sa, addrsz(a),
+ name, sizeof(name), serv, sizeof(serv),
+ (NI_NUMERICHOST | NI_NUMERICSERV |
+ NI_DGRAM)))) {
+ dstr_putf(d, " E%d", err);
+ u_quotify(d, gai_strerror(err));
+ } else {
+ ix = afix(a->sa.sa_family); assert(ix >= 0);
+ u_quotify(d, aftab[ix].name);
+ u_quotify(d, name);
+ u_quotify(d, serv);
}
} else if (strcmp(fmt, "?B64") == 0) {
const octet *p = va_arg(*ap, const octet *);
/*----- Name resolution operations ----------------------------------------*/
+#ifdef HAVE_LIBADNS
+
+/* --- @before_select@ --- *
+ *
+ * Arguments: @sel_state *s@ = the @sel@ multiplexor (unused)
+ * @sel_args *a@ = input to @select@, to be updated
+ * @void *p@ = a context pointer (unused)
+ *
+ * Returns: ---
+ *
+ * Use: An I/O multiplexor hook, called just before waiting for I/O
+ * events.
+ *
+ * Currently its main purpose is to wire ADNS into the event
+ * loop.
+ */
+
+static void before_select(sel_state *s, sel_args *a, void *p)
+{
+ struct timeval now;
+ adns_query q;
+ adns_answer *n;
+ admin_resop *r;
+ int any = 0;
+
+ /* --- Check for name-resolution progress --- *
+ *
+ * If there is any, then clobber the timeout: one of the resolver
+ * callbacks might have renewed its interest in a file descriptor, but too
+ * late to affect this @select@ call.
+ *
+ * I think, strictly, this is an mLib bug, but it's cheap enough to hack
+ * around here. Fixing it will wait for mLib 3.
+ */
+
+ for (;;) {
+ q = 0;
+ if (adns_check(ads, &q, &n, &p)) break;
+ r = p;
+ any = 1;
+ if (n->status != adns_s_ok) {
+ T( trace(T_ADMIN, "admin: resop %s failed: %s",
+ BGTAG(r), adns_strerror(n->status)); )
+ a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END);
+ r->func(r, ARES_FAIL);
+ } else {
+ T( trace(T_ADMIN, "admin: resop %s ok", BGTAG(r)); )
+ assert(n->type == adns_r_addr);
+ assert(n->nrrs > 0);
+ assert(n->rrs.addr[0].len <= sizeof(r->sa));
+ memcpy(&r->sa, &n->rrs.addr[0].addr, n->rrs.addr[0].len);
+ setport(&r->sa, r->port);
+ r->func(r, ARES_OK);
+ }
+ free(n);
+ sel_rmtimer(&r->t);
+ xfree(r->addr);
+ a_bgrelease(&r->bg);
+ }
+
+ if (any) { a->tvp = &a->tv; a->tv.tv_sec = 0; a->tv.tv_usec = 0; }
+
+ gettimeofday(&now, 0);
+ adns_beforeselect(ads, &a->maxfd,
+ &a->fd[SEL_READ], &a->fd[SEL_WRITE], &a->fd[SEL_EXC],
+ &a->tvp, &a->tv, &now);
+}
+
+/* --- @after_select@ --- *
+ *
+ * Arguments: @sel_state *s@ = the @sel@ multiplexor (unused)
+ * @sel_args *a@ = input to @select@, to be updated
+ * @void *p@ = a context pointer (unused)
+ *
+ * Returns: ---
+ *
+ * Use: An I/O multiplexor hook, called just after waiting for I/O
+ * events.
+ *
+ * Currently its main purpose is to wire ADNS into the event
+ * loop.
+ */
+
+static void after_select(sel_state *s, sel_args *a, void *p)
+{
+ struct timeval now;
+
+ gettimeofday(&now, 0);
+ adns_afterselect(ads, a->maxfd,
+ &a->fd[SEL_READ], &a->fd[SEL_WRITE], &a->fd[SEL_EXC],
+ &now);
+}
+
+#else
+
/* --- @a_resolved@ --- *
*
* Arguments: @struct hostent *h@ = pointer to resolved hostname
{
admin_resop *r = v;
- T( trace(T_ADMIN, "admin: resop %s resolved", BGTAG(r)); )
QUICKRAND;
if (!h) {
+ T( trace(T_ADMIN, "admin: resop %s failed: %s",
+ BGTAG(r), hstrerror(h_errno)); )
a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END);
r->func(r, ARES_FAIL);
} else {
+ T( trace(T_ADMIN, "admin: resop %s ok", BGTAG(r)); )
+ r->sa.sin.sin_family = AF_INET;
memcpy(&r->sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
+ setport(&r->sa, r->port);
r->func(r, ARES_OK);
}
sel_rmtimer(&r->t);
a_bgrelease(&r->bg);
}
+#endif
+
/* --- @a_restimer@ --- *
*
* Arguments: @struct timeval *tv@ = timer
T( trace(T_ADMIN, "admin: resop %s timeout", BGTAG(r)); )
a_bgfail(&r->bg, "resolver-timeout", "%s", r->addr, A_END);
r->func(r, ARES_FAIL);
+#ifdef HAVE_LIBADNS
+ adns_cancel(r->q);
+#else
bres_abort(&r->r);
+#endif
xfree(r->addr);
a_bgrelease(&r->bg);
}
r->func(r, ARES_FAIL);
sel_rmtimer(&r->t);
xfree(r->addr);
+#ifdef HAVE_LIBADNS
+ adns_cancel(r->q);
+#else
bres_abort(&r->r);
+#endif
}
/* --- @a_resolve@ --- *
{
struct timeval tv;
unsigned long pt;
+ int af = AF_UNSPEC;
+ const char *fam = "ANY";
char *p;
- int i = 0;
+ int i = 0, j;
+ struct addrinfo *ai, *ailist, aihint = { 0 };
+#ifdef HAVE_LIBADNS
+ int err;
+ adns_queryflags qf;
+#endif
/* --- Fill in the easy bits of address --- */
r->bg.tag = "<starting>";
r->addr = 0;
r->func = func;
- if (mystrieq(av[i], "inet")) i++;
+ if (mystrieq(av[i], "any"))
+ { fam = "ANY"; af = AF_UNSPEC; i++; }
+ else for (j = 0; j < NADDRFAM; j++) {
+ if (mystrieq(av[i], aftab[j].name)) {
+ assert(udpsock[j].fd >= 0);
+ fam = aftab[j].name;
+ af = aftab[j].af;
+ i++;
+ break;
+ }
+ }
if (ac - i != 1 && ac - i != 2) {
- a_fail(a, "bad-addr-syntax", "[inet] ADDRESS [PORT]", A_END);
+ a_fail(a, "bad-addr-syntax", "[FAMILY] ADDRESS [PORT]", A_END);
goto fail;
}
- r->sa.sin.sin_family = AF_INET;
- r->sasz = sizeof(r->sa.sin);
r->addr = xstrdup(av[i]);
if (!av[i + 1])
pt = TRIPE_PORT;
a_fail(a, "invalid-port", "%lu", pt, A_END);
goto fail;
}
- r->sa.sin.sin_port = htons(pt);
+ r->port = pt;
/* --- Report backgrounding --- *
*
if (a_bgadd(a, &r->bg, tag, a_rescancel))
goto fail;
- T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'",
- a->seq, BGTAG(r), r->addr); )
+ T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s', family `%s'",
+ a->seq, BGTAG(r), r->addr, fam); )
+
+ /* --- Make sure the address family is something we can implement --- */
+
+ if (af != AF_UNSPEC && af != AF_INET) {
+ T( trace(T_ADMIN, "admin: resop %s failed: unsupported address family",
+ BGTAG(r)); )
+ a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END);
+ goto fail_release;
+ }
+ assert(udpsock[AFIX_INET].fd >= 0);
/* --- If the name is numeric, do it the easy way --- */
- if (inet_aton(av[i], &r->sa.sin.sin_addr)) {
- T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); )
- func(r, ARES_OK);
+ aihint.ai_family = AF_INET;
+ aihint.ai_socktype = SOCK_DGRAM;
+ aihint.ai_protocol = IPPROTO_UDP;
+ aihint.ai_flags = AI_NUMERICHOST;
+ if (!getaddrinfo(av[i], 0, &aihint, &ailist)) {
+ for (ai = ailist; ai; ai = ai->ai_next) {
+ if ((j = afix(ai->ai_family)) >= 0 && udpsock[j].fd >= 0)
+ break;
+ }
+ if (!ai) {
+ T( trace(T_ADMIN, "admin: resop %s failed: "
+ "no suitable addresses returned", BGTAG(r)); )
+ a_bgfail(&r->bg, "resolve-error", "%s" , r->addr, A_END);
+ func(r, ARES_FAIL);
+ } else {
+ T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); )
+ assert(ai->ai_addrlen <= sizeof(r->sa));
+ memcpy(&r->sa, ai->ai_addr, ai->ai_addrlen);
+ setport(&r->sa, r->port);
+ func(r, ARES_OK);
+ }
+ freeaddrinfo(ailist);
xfree(r->addr);
a_bgrelease(&r->bg);
return;
gettimeofday(&tv, 0);
tv.tv_sec += T_RESOLVE;
sel_addtimer(&sel, &r->t, &tv, a_restimer, r);
+#ifdef HAVE_LIBADNS
+ qf = adns_qf_search | adns_qf_want_ipv4;
+ if ((err = adns_submit(ads, r->addr, adns_r_addr, qf, r, &r->q)) != 0) {
+ T( trace(T_ADMIN, "admin: resop %s adns_submit failed: %s",
+ BGTAG(r), strerror(err)); )
+ a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END);
+ goto fail_release;
+ }
+#else
bres_byname(&r->r, r->addr, a_resolved, r);
+#endif
return;
fail:
func(r, ARES_FAIL);
if (r->addr) xfree(r->addr);
xfree(r);
+ return;
+
+fail_release:
+ func(r, ARES_FAIL);
+ xfree(r->addr);
+ a_bgrelease(&r->bg);
}
/*----- Option parsing ----------------------------------------------------*/
T( trace(T_ADMIN, "admin: done add op %s", BGTAG(add)); )
if (rc == ARES_OK) {
- add->peer.sasz = add->r.sasz;
add->peer.sa = add->r.sa;
if (p_findbyaddr(&add->r.sa))
a_bgfail(&add->r.bg, "peer-addr-exists", "?ADDR", &add->r.sa, A_END);
{ alertcmd(a, AF_WARN, AF_WARN, "WARN", av); }
static void acmd_port(admin *a, unsigned ac, char *av[])
- { a_info(a, "%u", p_port(), A_END); a_ok(a); }
+{
+ int i;
+
+ if (ac) {
+ for (i = 0; i < NADDRFAM; i++)
+ if (mystrieq(av[0], aftab[i].name)) goto found;
+ a_fail(a, "unknown-address-family", "%s", av[0], A_END);
+ return;
+ found:
+ if (udpsock[i].fd < 0) {
+ a_fail(a, "disabled-address-family", "%s", aftab[i].name, A_END);
+ return;
+ }
+ } else {
+ for (i = 0; i < NADDRFAM; i++)
+ if (udpsock[i].fd >= 0) goto found;
+ abort();
+ }
+ a_info(a, "%u", p_port(i), A_END);
+ a_ok(a);
+}
static void acmd_daemon(admin *a, unsigned ac, char *av[])
{
if ((p = a_findpeer(a, av[0])) != 0) {
ad = p_addr(p);
- assert(ad->sa.sa_family == AF_INET);
a_info(a, "?ADDR", ad, A_END);
a_ok(a);
}
a_info(a, "private-key=%s", ptag,
"current-private-key=%s", p->kx.kpriv->tag, A_END);
a_info(a, "keepalive=%lu", ps->t_ka, A_END);
+ a_info(a, "corked=%s", BOOL(p->kx.f&KXF_CORK),
+ "mobile=%s", BOOL(ps->f&PSF_MOBILE),
+ A_END);
a_ok(a);
}
}
{ "notify", "MESSAGE ...", 1, 0xffff, acmd_notify },
{ "peerinfo", "PEER", 1, 1, acmd_peerinfo },
{ "ping", "[OPTIONS] PEER", 1, 0xffff, acmd_ping },
- { "port", 0, 0, 0, acmd_port },
+ { "port", "[FAMILY]", 0, 1, acmd_port },
{ "quit", 0, 0, 0, acmd_quit },
{ "reload", 0, 0, 0, acmd_reload },
{ "servinfo", 0, 0, 0, acmd_servinfo },
{ "setifname", "PEER NEW-NAME", 2, 2, acmd_setifname },
+ { "stats", "PEER", 1, 1, acmd_stats },
{ "svcclaim", "SERVICE VERSION", 2, 2, acmd_svcclaim },
{ "svcensure", "SERVICE [VERSION]", 1, 2, acmd_svcensure },
{ "svcfail", "JOBID TOKENS...", 1, 0xffff, acmd_svcfail },
{ "svcrelease", "SERVICE", 1, 1, acmd_svcrelease },
{ "svcsubmit", "[OPTIONS] SERVICE TOKENS...",
2, 0xffff, acmd_svcsubmit },
- { "stats", "PEER", 1, 1, acmd_stats },
#ifndef NTRACE
{ "trace", "[OPTIONS]", 0, 1, acmd_trace },
#endif
struct sigaction sa;
size_t sz;
mode_t omask;
+#ifdef HAVE_LIBADNS
+ int err;
+#endif
/* --- Create services table --- */
sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
sel_addfile(&sock);
sockname = name;
+#ifdef HAVE_LIBADNS
+ if ((err = adns_init(&ads,
+ (adns_if_permit_ipv4 | adns_if_permit_ipv6 |
+ adns_if_noserverwarn | adns_if_nosigpipe |
+ adns_if_noautosys),
+ 0)) != 0)
+ die(EXIT_FAILURE, "failed to initialize ADNS: %s", strerror(errno));
+ sel_addhook(&sel, &hook, before_select, after_select, 0);
+#else
bres_init(&sel);
+#endif
T( trace_custom(a_trace, 0);
trace(T_ADMIN, "admin: enabled custom tracing"); )
flags |= F_INIT;
sig_add(&s_term, SIGTERM, a_sigdie, 0);
sig_add(&s_hup, SIGHUP, a_sighup, 0);
- signal(SIGPIPE, SIG_IGN);
sigaction(SIGINT, 0, &sa);
if (sa.sa_handler != SIG_IGN)
sig_add(&s_int, SIGINT, a_sigdie, 0);