From 02fe2728e3a30e18042d8c88b966efcc68fc563c Mon Sep 17 00:00:00 2001 From: Petr Jelinek Date: Tue, 23 Aug 2011 21:22:53 +0200 Subject: [PATCH] Detect DNS reply changes and invalidate connections to IPs no longer present in latest reply --- include/bouncer.h | 1 + include/objects.h | 1 + src/dnslookup.c | 45 ++++++++++++++++++++++++++++++++-- src/objects.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ src/util.c | 18 ++++++++++++++ 5 files changed, 125 insertions(+), 2 deletions(-) diff --git a/include/bouncer.h b/include/bouncer.h index df84fa8..2773fc8 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -157,6 +157,7 @@ void pga_copy(PgAddr *a, const struct sockaddr *sa); bool pga_pton(PgAddr *a, const char *s, int port); const char *pga_ntop(const PgAddr *a, char *dst, int dstlen); const char *pga_str(const PgAddr *a, char *dst, int dstlen); +int pga_cmp_addr(const PgAddr *a, const PgAddr *b); /* * Stats, kept per-pool. diff --git a/include/objects.h b/include/objects.h index a5b9619..92e46f2 100644 --- a/include/objects.h +++ b/include/objects.h @@ -67,6 +67,7 @@ int get_active_client_count(void); int get_active_server_count(void); void tag_database_dirty(PgDatabase *db); +void tag_host_addr_dirty(const char *host, const struct sockaddr *sa); void for_each_server(PgPool *pool, void (*func)(PgSocket *sk)); void reuse_just_freed_objects(void); diff --git a/src/dnslookup.c b/src/dnslookup.c index 2643fe5..b462484 100644 --- a/src/dnslookup.c +++ b/src/dnslookup.c @@ -70,6 +70,7 @@ struct DNSRequest { struct addrinfo *result; struct addrinfo *current; + struct addrinfo *oldres; usec_t res_ttl; }; @@ -384,8 +385,11 @@ static int req_cmp(uintptr_t arg, struct AANode *node) static void req_reset(struct DNSRequest *req) { req->done = false; - if (req->result) - freeaddrinfo(req->result); + if (req->result) { + if (req->oldres) + freeaddrinfo(req->oldres); + req->oldres = req->result; + } req->result = req->current = NULL; } @@ -400,6 +404,10 @@ static void req_free(struct AANode *node, void *arg) free(ucb); } req_reset(req); + if (req->oldres) { + freeaddrinfo(req->oldres); + req->oldres = NULL; + } free(req->name); free(req); } @@ -482,6 +490,36 @@ nomem: return NULL; } +static int cmp_addrinfo(const struct addrinfo *a1, const struct addrinfo *a2) +{ + if (a1->ai_family != a2->ai_family) + return a1->ai_family-a2->ai_family; + if (a1->ai_addrlen != a2->ai_addrlen) + return a1->ai_addrlen-a2->ai_addrlen; + + return memcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen); +} + +/* check if new dns reply is missing some IP compared to old one */ +static void check_req_result_changes(struct DNSRequest *req) +{ + struct addrinfo *ai, *aj; + + for (ai = req->oldres; ai; ai = ai->ai_next) { + bool found = false; + for (aj = req->result; aj; aj = aj->ai_next) { + if (cmp_addrinfo(ai, aj) == 0) { + found = true; + break; + } + } + + /* missing IP (possible DNS failover) make connections to it dirty */ + if (!found) + tag_host_addr_dirty(req->name, ai->ai_addr); + } +} + /* struct addrinfo -> deliver_info() */ static void got_result_gai(int result, struct addrinfo *res, void *arg) { @@ -492,6 +530,9 @@ static void got_result_gai(int result, struct addrinfo *res, void *arg) if (result == 0) { req->result = res; req->current = res; + + if (req->oldres) + check_req_result_changes(req); } else { /* lookup failed */ log_warning("lookup failed: %s: result=%d", req->name, result); diff --git a/src/objects.c b/src/objects.c index 15e6410..86437b3 100644 --- a/src/objects.c +++ b/src/objects.c @@ -1246,11 +1246,49 @@ void for_each_server(PgPool *pool, void (*func)(PgSocket *sk)) func(container_of(item, PgSocket, head)); } +static void for_each_server_filtered(PgPool *pool, void (*func)(PgSocket *sk), bool (*filter)(PgSocket *sk, void *arg), void *filter_arg) +{ + struct List *item; + PgSocket *sk; + + statlist_for_each(item, &pool->idle_server_list) { + sk = container_of(item, PgSocket, head); + if (filter(sk, filter_arg)) + func(sk); + } + + statlist_for_each(item, &pool->used_server_list) { + sk = container_of(item, PgSocket, head); + if (filter(sk, filter_arg)) + func(sk); + } + + statlist_for_each(item, &pool->tested_server_list) { + sk = container_of(item, PgSocket, head); + if (filter(sk, filter_arg)) + func(sk); + } + + statlist_for_each(item, &pool->active_server_list) { + sk = container_of(item, PgSocket, head); + if (filter(sk, filter_arg)) + func(sk); + } + + statlist_for_each(item, &pool->new_server_list) { + sk = container_of(item, PgSocket, head); + if (filter(sk, filter_arg)) + func(sk); + } +} + + static void tag_dirty(PgSocket *sk) { sk->close_needed = 1; } + void tag_database_dirty(PgDatabase *db) { struct List *item; @@ -1263,6 +1301,30 @@ void tag_database_dirty(PgDatabase *db) } } +static bool server_remote_addr_filter(PgSocket *sk, void *arg) { + PgAddr *addr = arg; + + return (pga_cmp_addr(&sk->remote_addr, addr) == 0); +} + +void tag_host_addr_dirty(const char *host, const struct sockaddr *sa) +{ + struct List *item; + PgPool *pool; + PgAddr addr; + + memset(&addr, 0, sizeof(addr)); + pga_copy(&addr, sa); + + statlist_for_each(item, &pool_list) { + pool = container_of(item, PgPool, head); + if (pool->db->host && strcmp(host, pool->db->host) == 0) { + for_each_server_filtered(pool, tag_dirty, server_remote_addr_filter, &addr); + } + } +} + + /* move objects from justfree_* to free_* lists */ void reuse_just_freed_objects(void) { diff --git a/src/util.c b/src/util.c index afa9683..f5e93cc 100644 --- a/src/util.c +++ b/src/util.c @@ -346,6 +346,24 @@ static inline unsigned pga_family(const PgAddr *a) return a->sa.sa_family; } +int pga_cmp_addr(const PgAddr *a, const PgAddr *b) +{ + if (pga_family(a) != pga_family(b)) + return pga_family(a)-pga_family(b); + + switch (pga_family(a)) { + case AF_INET: + return memcmp(&a->sin.sin_addr, &b->sin.sin_addr, sizeof(a->sin.sin_addr)); + break; + case AF_INET6: + return memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, sizeof(a->sin6.sin6_addr)); + break; + default: + log_error("pga_cmp_addr: unsupported family"); + return 0; + } +} + /* convert pgaddr to string */ const char *pga_ntop(const PgAddr *a, char *dst, int dstlen) { -- 2.40.0