diff --git a/common/wireaddr.c b/common/wireaddr.c index 5c803d227..d5e998298 100644 --- a/common/wireaddr.c +++ b/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) diff --git a/common/wireaddr.h b/common/wireaddr.h index e001330e1..f8c1d0720 100644 --- a/common/wireaddr.h +++ b/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, diff --git a/gossipd/gossip.c b/gossipd/gossip.c index ff8745fec..9ae4c8928 100644 --- a/gossipd/gossip.c +++ b/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);