Browse Source

gossip: Try to detect broken ISP resolvers and discard broken replies

This is a best effort attempt to skip connection attempts if we detect a broken
ISP resolver. A broken ISP resolver is a resolver that will replace NXDOMAIN
replies with a dummy response. This is best effort in that it'll only detect a
single fixed dummy reply, it'll check only on startup, and will not detect if we
switched networks. It should be good enough for most cases, and in the worst
case it will result in a connection attempt that does not complete.

Signed-off-by: Christian Decker <decker.christian@gmail.com>
Reported-by: Glenn Willen <@gwillen>
ppa-0.6.1
Christian Decker 6 years ago
parent
commit
4a5cff8490
  1. 10
      common/wireaddr.c
  2. 1
      common/wireaddr.h
  3. 49
      gossipd/gossip.c

10
common/wireaddr.c

@ -290,6 +290,7 @@ static bool separate_address_and_port(const tal_t *ctx, const char *arg,
bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname,
const u16 port, bool *no_dns,
struct sockaddr *broken_reply,
const char **err_msg)
{
struct sockaddr_in6 *sa6;
@ -342,6 +343,12 @@ bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname,
*err_msg = gai_strerror(gai_err);
return false;
}
if (broken_reply != NULL && memeq(addrinfo->ai_addr, addrinfo->ai_addrlen, broken_reply, tal_len(broken_reply))) {
res = false;
goto cleanup;
}
/* Use only the first found address */
if (addrinfo->ai_family == AF_INET) {
sa4 = (struct sockaddr_in *) addrinfo->ai_addr;
@ -353,6 +360,7 @@ bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname,
res = true;
}
cleanup:
/* Clean up */
freeaddrinfo(addrinfo);
return res;
@ -392,7 +400,7 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport,
/* Resolve with getaddrinfo */
if (!res)
res = wireaddr_from_hostname(addr, ip, port, no_dns, err_msg);
res = wireaddr_from_hostname(addr, ip, port, no_dns, NULL, err_msg);
finish:
if (!res && err_msg && !*err_msg)

1
common/wireaddr.h

@ -89,6 +89,7 @@ char *fmt_wireaddr_without_port(const tal_t *ctx, const struct wireaddr *a);
* we wanted to do a DNS lookup. */
bool wireaddr_from_hostname(struct wireaddr *addr, const char *hostname,
const u16 port, bool *no_dns,
struct sockaddr *broken_reply,
const char **err_msg);
void wireaddr_from_ipv4(struct wireaddr *addr,

49
gossipd/gossip.c

@ -172,6 +172,10 @@ struct daemon {
/* @see lightningd.config.use_dns */
bool use_dns;
/* The address that the broken response returns instead of
* NXDOMAIN. NULL if we have not detected a broken resolver. */
struct sockaddr *broken_resolver_response;
};
/* Peers we're trying to reach. */
@ -360,6 +364,37 @@ new_local_peer_state(struct peer *peer, const struct crypto_state *cs)
return lps;
}
/**
* Some ISP resolvers will reply with a dummy IP to queries that would otherwise
* result in an NXDOMAIN reply. This just checks whether we have one such
* resolver upstream and remembers its reply so we can try to filter future
* dummies out.
*/
static bool broken_resolver(struct daemon *daemon)
{
struct addrinfo *addrinfo;
struct addrinfo hints;
char *hostname = "nxdomain-test.doesntexist";
int err;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_ADDRCONFIG;
err = getaddrinfo(hostname, tal_fmt(tmpctx, "%d", 42),
&hints, &addrinfo);
daemon->broken_resolver_response =
tal_free(daemon->broken_resolver_response);
if (err == 0) {
daemon->broken_resolver_response = tal_dup(daemon, struct sockaddr, addrinfo->ai_addr);
freeaddrinfo(addrinfo);
}
return daemon->broken_resolver_response != NULL;
}
static struct peer *new_peer(const tal_t *ctx,
struct daemon *daemon,
const struct pubkey *their_id,
@ -2855,6 +2890,11 @@ static struct io_plan *gossip_init(struct daemon_conn *master,
} else
daemon->proxyaddr = NULL;
if (broken_resolver(daemon)) {
status_trace("Broken DNS resolver detected, will check for "
"dummy replies");
}
/* Load stored gossip messages */
gossip_store_load(daemon->rstate, daemon->rstate->store);
@ -3061,7 +3101,8 @@ static const char *seedname(const tal_t *ctx, const struct pubkey *id)
}
static struct wireaddr_internal *
seed_resolve_addr(const tal_t *ctx, const struct pubkey *id)
seed_resolve_addr(const tal_t *ctx, const struct pubkey *id,
struct sockaddr *broken_reply)
{
struct wireaddr_internal *a;
const char *addr;
@ -3072,7 +3113,7 @@ seed_resolve_addr(const tal_t *ctx, const struct pubkey *id)
a = tal(ctx, struct wireaddr_internal);
a->itype = ADDR_INTERNAL_WIREADDR;
if (!wireaddr_from_hostname(&a->u.wireaddr, addr, DEFAULT_PORT, NULL,
NULL)) {
broken_reply, NULL)) {
status_trace("Could not resolve %s", addr);
return tal_free(a);
} else {
@ -3170,7 +3211,8 @@ static void try_reach_peer(struct daemon *daemon, const struct pubkey *id,
wireaddr_from_unresolved(a, seedname(tmpctx, id),
DEFAULT_PORT);
} else if (daemon->use_dns) {
a = seed_resolve_addr(tmpctx, id);
a = seed_resolve_addr(tmpctx, id,
daemon->broken_resolver_response);
}
}
@ -3685,6 +3727,7 @@ int main(int argc, char *argv[])
timers_init(&daemon->timers, time_mono());
daemon->broadcast_interval = 30000;
daemon->last_announce_timestamp = 0;
daemon->broken_resolver_response = NULL;
/* stdin == control */
daemon_conn_init(daemon, &daemon->master, STDIN_FILENO, recv_req,
master_gone);

Loading…
Cancel
Save