Browse Source
Also based on pettycoin code. (With embarrassing bug fixed where it didn't increment the address used, thus using 100% CPU if that connect failed!) Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>ppa-0.6.1
3 changed files with 209 additions and 0 deletions
@ -0,0 +1,191 @@ |
|||
/* Async dns helper. */ |
|||
#include "dns.h" |
|||
#include "lightningd.h" |
|||
#include "log.h" |
|||
#include "peer.h" |
|||
#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 { |
|||
struct lightningd_state *state; |
|||
struct io_plan *(*init)(struct io_conn *, |
|||
struct lightningd_state *, |
|||
const char *name, const char *port); |
|||
const char *name, *port; |
|||
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; |
|||
|
|||
if (getaddrinfo(name, port, NULL, &addr) != 0) |
|||
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) |
|||
{ |
|||
/* No longer need to try more connections. */ |
|||
io_set_finish(conn, NULL, NULL); |
|||
return d->init(conn, d->state, d->name, d->port); |
|||
} |
|||
|
|||
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]); |
|||
io_set_finish(conn, connect_failed, d); |
|||
|
|||
/* That new connection owns d */ |
|||
tal_steal(conn, 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]; |
|||
|
|||
/* Consume that address. */ |
|||
d->addresses++; |
|||
d->num_addresses--; |
|||
|
|||
/* Now we can warn if it's overlength */ |
|||
if (a->addrlen > sizeof(a->saddr)) { |
|||
log_broken(d->state->base_log, |
|||
"DNS lookup gave overlength address for %s:%s" |
|||
" for family %u, len=%u", |
|||
d->name, d->port, |
|||
a->saddr.s.sa_family, a->addrlen); |
|||
} 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) { |
|||
io_new_conn(d->state, fd, init_conn, d); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
static struct io_plan *start_connecting(struct io_conn *conn, |
|||
struct dns_async *d) |
|||
{ |
|||
assert(d->num_addresses); |
|||
try_connect_one(d); |
|||
return io_close(conn); |
|||
} |
|||
|
|||
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); |
|||
} |
|||
|
|||
static void reap_child(struct io_conn *conn, struct dns_async *d) |
|||
{ |
|||
waitpid(d->pid, NULL, 0); |
|||
} |
|||
|
|||
struct dns_async *dns_resolve_and_connect(struct lightningd_state *state, |
|||
const char *name, const char *port, |
|||
struct io_plan *(*init)(struct io_conn *, |
|||
struct lightningd_state *, |
|||
const char *name, const char *port)) |
|||
{ |
|||
int pfds[2]; |
|||
struct dns_async *d = tal(NULL, struct dns_async); |
|||
struct io_conn *conn; |
|||
|
|||
d->state = state; |
|||
d->init = init; |
|||
d->name = tal_strdup(d, name); |
|||
d->port = tal_strdup(d, port); |
|||
|
|||
/* First fork child to get addresses. */ |
|||
if (pipe(pfds) != 0) { |
|||
log_unusual(state->base_log, "Creating pipes for dns lookup: %s", |
|||
strerror(errno)); |
|||
return NULL; |
|||
} |
|||
|
|||
fflush(stdout); |
|||
d->pid = fork(); |
|||
switch (d->pid) { |
|||
case -1: |
|||
log_unusual(state->base_log, "forking for dns lookup: %s", |
|||
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]); |
|||
conn = io_new_conn(state, pfds[0], init_dns_conn, d); |
|||
io_set_finish(conn, reap_child, d); |
|||
tal_steal(conn, d); |
|||
return d; |
|||
} |
@ -0,0 +1,16 @@ |
|||
#ifndef PETTYCOIN_DNS_H |
|||
#define PETTYCOIN_DNS_H |
|||
#include "config.h" |
|||
#include <ccan/io/io.h> |
|||
#include <ccan/tal/tal.h> |
|||
#include <stdbool.h> |
|||
|
|||
struct lightningd_state; |
|||
struct netaddr; |
|||
struct dns_async *dns_resolve_and_connect(struct lightningd_state *state, |
|||
const char *name, const char *port, |
|||
struct io_plan *(*init)(struct io_conn *, |
|||
struct lightningd_state *, |
|||
const char *name, const char *port)); |
|||
|
|||
#endif /* PETTYCOIN_DNS_H */ |
Loading…
Reference in new issue