You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
5.9 KiB
248 lines
5.9 KiB
9 years ago
|
/* Async dns helper. */
|
||
|
#include "dns.h"
|
||
|
#include "lightningd.h"
|
||
|
#include "log.h"
|
||
7 years ago
|
#include "netaddr.h"
|
||
9 years ago
|
#include <assert.h>
|
||
|
#include <ccan/err/err.h>
|
||
|
#include <ccan/read_write_all/read_write_all.h>
|
||
|
#include <ccan/tal/str/str.h>
|
||
|
#include <ccan/tal/tal.h>
|
||
|
#include <errno.h>
|
||
|
#include <netdb.h>
|
||
|
#include <stdio.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
|
||
|
struct dns_async {
|
||
9 years ago
|
struct lightningd_state *dstate;
|
||
9 years ago
|
struct io_plan *(*init)(struct io_conn *, struct lightningd_state *,
|
||
8 years ago
|
const struct netaddr *,
|
||
9 years ago
|
void *);
|
||
|
void (*fail)(struct lightningd_state *, void *arg);
|
||
|
const char *name;
|
||
|
void *arg;
|
||
9 years ago
|
int pid;
|
||
|
size_t num_addresses;
|
||
|
struct netaddr *addresses;
|
||
|
};
|
||
|
|
||
|
/* This runs in the child */
|
||
|
static void lookup_and_write(int fd, const char *name, const char *port)
|
||
|
{
|
||
|
struct addrinfo *addr, *i;
|
||
|
struct netaddr *addresses;
|
||
|
size_t num;
|
||
9 years ago
|
struct addrinfo hints;
|
||
9 years ago
|
|
||
9 years ago
|
/* We don't want UDP sockets (yet?) */
|
||
|
memset(&hints, 0, sizeof(hints));
|
||
|
hints.ai_family = AF_UNSPEC;
|
||
|
hints.ai_socktype = SOCK_STREAM;
|
||
|
if (getaddrinfo(name, port, &hints, &addr) != 0)
|
||
9 years ago
|
return;
|
||
|
|
||
|
num = 0;
|
||
|
for (i = addr; i; i = i->ai_next)
|
||
|
num++;
|
||
|
|
||
|
addresses = tal_arr(NULL, struct netaddr, num);
|
||
|
num = 0;
|
||
|
for (i = addr; i; i = i->ai_next) {
|
||
|
addresses[num].type = i->ai_socktype;
|
||
|
addresses[num].protocol = i->ai_protocol;
|
||
|
addresses[num].addrlen = i->ai_addrlen;
|
||
|
memset(&addresses[num].saddr, 0, sizeof(addresses[num].saddr));
|
||
|
/* Let parent report this error. */
|
||
|
if (i->ai_addrlen <= sizeof(addresses[num].saddr))
|
||
|
memcpy(&addresses[num].saddr, i->ai_addr, i->ai_addrlen);
|
||
|
num++;
|
||
|
}
|
||
|
|
||
|
if (!num) {
|
||
|
tal_free(addresses);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (write_all(fd, &num, sizeof(num)))
|
||
|
write_all(fd, addresses, num * sizeof(addresses[0]));
|
||
|
tal_free(addresses);
|
||
|
}
|
||
|
|
||
|
static struct io_plan *connected(struct io_conn *conn, struct dns_async *d)
|
||
|
{
|
||
8 years ago
|
struct io_plan *plan;
|
||
|
|
||
|
/* No longer need to try more connections via connect_failed. */
|
||
9 years ago
|
io_set_finish(conn, NULL, NULL);
|
||
9 years ago
|
|
||
8 years ago
|
plan = d->init(conn, d->dstate, &d->addresses[-1], d->arg);
|
||
8 years ago
|
tal_free(d);
|
||
|
|
||
|
return plan;
|
||
9 years ago
|
}
|
||
|
|
||
|
static void try_connect_one(struct dns_async *d);
|
||
|
|
||
|
/* If this connection failed, try connecting to another address. */
|
||
|
static void connect_failed(struct io_conn *conn, struct dns_async *d)
|
||
|
{
|
||
|
try_connect_one(d);
|
||
|
}
|
||
|
|
||
|
static struct io_plan *init_conn(struct io_conn *conn, struct dns_async *d)
|
||
|
{
|
||
|
struct addrinfo a;
|
||
|
|
||
|
netaddr_to_addrinfo(&a, &d->addresses[0]);
|
||
9 years ago
|
|
||
|
/* Consume that address. */
|
||
|
d->addresses++;
|
||
|
d->num_addresses--;
|
||
|
|
||
9 years ago
|
io_set_finish(conn, connect_failed, d);
|
||
|
|
||
|
/* That new connection owns d */
|
||
|
return io_connect(conn, &a, connected, d);
|
||
|
}
|
||
|
|
||
|
static void try_connect_one(struct dns_async *d)
|
||
|
{
|
||
|
int fd;
|
||
|
|
||
|
while (d->num_addresses) {
|
||
|
const struct netaddr *a = &d->addresses[0];
|
||
|
|
||
|
/* Now we can warn if it's overlength */
|
||
|
if (a->addrlen > sizeof(a->saddr)) {
|
||
9 years ago
|
log_broken(d->dstate->base_log,
|
||
9 years ago
|
"DNS lookup gave overlength address for %s"
|
||
9 years ago
|
" for family %u, len=%u",
|
||
9 years ago
|
d->name, a->saddr.s.sa_family, a->addrlen);
|
||
9 years ago
|
} else {
|
||
|
/* Might not even be able to create eg. IPv6 sockets */
|
||
|
fd = socket(a->saddr.s.sa_family, a->type, a->protocol);
|
||
|
if (fd >= 0) {
|
||
9 years ago
|
io_new_conn(d->dstate, fd, init_conn, d);
|
||
9 years ago
|
return;
|
||
9 years ago
|
}
|
||
|
}
|
||
9 years ago
|
|
||
|
/* Consume that address. */
|
||
|
d->addresses++;
|
||
|
d->num_addresses--;
|
||
9 years ago
|
}
|
||
9 years ago
|
|
||
|
/* We're out of things to try. Fail. */
|
||
8 years ago
|
d->fail(d->dstate, d->arg);
|
||
|
tal_free(d);
|
||
9 years ago
|
}
|
||
|
|
||
|
static struct io_plan *start_connecting(struct io_conn *conn,
|
||
|
struct dns_async *d)
|
||
|
{
|
||
|
assert(d->num_addresses);
|
||
9 years ago
|
|
||
8 years ago
|
/* OK, we've read all we want, child should exit. */
|
||
|
waitpid(d->pid, NULL, 0);
|
||
|
|
||
|
/* No need to call dns_lookup_failed now. */
|
||
|
io_set_finish(conn, NULL, NULL);
|
||
|
|
||
9 years ago
|
try_connect_one(d);
|
||
|
return io_close(conn);
|
||
|
}
|
||
|
|
||
8 years ago
|
struct dns_async *multiaddress_connect_(struct lightningd_state *dstate,
|
||
|
const struct netaddr *addresses,
|
||
|
struct io_plan *(*init)(struct io_conn *,
|
||
|
struct lightningd_state *,
|
||
|
const struct netaddr *,
|
||
|
void *arg),
|
||
|
void (*fail)(struct lightningd_state *, void *arg),
|
||
|
void *arg)
|
||
|
{
|
||
|
struct dns_async *d = tal(dstate, struct dns_async);
|
||
|
|
||
|
d->dstate = dstate;
|
||
|
d->init = init;
|
||
|
d->fail = fail;
|
||
|
d->arg = arg;
|
||
|
d->name = "names from address list";
|
||
|
d->num_addresses = tal_count(addresses);
|
||
|
d->addresses = tal_dup_arr(d, struct netaddr, addresses,
|
||
|
d->num_addresses, 0);
|
||
|
try_connect_one(d);
|
||
|
return d;
|
||
|
}
|
||
|
|
||
9 years ago
|
static struct io_plan *read_addresses(struct io_conn *conn, struct dns_async *d)
|
||
|
{
|
||
|
d->addresses = tal_arr(d, struct netaddr, d->num_addresses);
|
||
|
return io_read(conn, d->addresses,
|
||
|
d->num_addresses * sizeof(d->addresses[0]),
|
||
|
start_connecting, d);
|
||
|
}
|
||
|
|
||
|
static struct io_plan *init_dns_conn(struct io_conn *conn, struct dns_async *d)
|
||
|
{
|
||
|
return io_read(conn, &d->num_addresses, sizeof(d->num_addresses),
|
||
|
read_addresses, d);
|
||
|
}
|
||
|
|
||
8 years ago
|
static void dns_lookup_failed(struct io_conn *conn, struct dns_async *d)
|
||
9 years ago
|
{
|
||
|
waitpid(d->pid, NULL, 0);
|
||
8 years ago
|
d->fail(d->dstate, d->arg);
|
||
|
tal_free(d);
|
||
9 years ago
|
}
|
||
|
|
||
9 years ago
|
struct dns_async *dns_resolve_and_connect_(struct lightningd_state *dstate,
|
||
9 years ago
|
const char *name, const char *port,
|
||
|
struct io_plan *(*init)(struct io_conn *,
|
||
|
struct lightningd_state *,
|
||
8 years ago
|
const struct netaddr *,
|
||
9 years ago
|
void *arg),
|
||
|
void (*fail)(struct lightningd_state *, void *arg),
|
||
|
void *arg)
|
||
9 years ago
|
{
|
||
|
int pfds[2];
|
||
8 years ago
|
struct dns_async *d = tal(dstate, struct dns_async);
|
||
9 years ago
|
struct io_conn *conn;
|
||
|
|
||
9 years ago
|
d->dstate = dstate;
|
||
9 years ago
|
d->init = init;
|
||
9 years ago
|
d->fail = fail;
|
||
|
d->arg = arg;
|
||
|
d->name = tal_fmt(d, "%s:%s", name, port);
|
||
9 years ago
|
|
||
|
/* First fork child to get addresses. */
|
||
|
if (pipe(pfds) != 0) {
|
||
9 years ago
|
log_unusual(dstate->base_log,
|
||
|
"Creating pipes for dns lookup: %s",
|
||
9 years ago
|
strerror(errno));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
fflush(stdout);
|
||
|
d->pid = fork();
|
||
|
switch (d->pid) {
|
||
|
case -1:
|
||
9 years ago
|
log_unusual(dstate->base_log, "forking for dns lookup: %s",
|
||
9 years ago
|
strerror(errno));
|
||
|
close(pfds[0]);
|
||
|
close(pfds[1]);
|
||
|
return NULL;
|
||
|
case 0:
|
||
|
close(pfds[0]);
|
||
|
lookup_and_write(pfds[1], name, port);
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
close(pfds[1]);
|
||
9 years ago
|
conn = io_new_conn(dstate, pfds[0], init_dns_conn, d);
|
||
8 years ago
|
io_set_finish(conn, dns_lookup_failed, d);
|
||
9 years ago
|
return d;
|
||
|
}
|