From 19c64c524614ea7fdbcedcc50d6980f096ccadb9 Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Mon, 13 Apr 2015 14:48:52 +0200 Subject: [PATCH] Improve handling of DNS search domains Signed-off-by: Steven Barth --- README | 1 + src/dhcpv6.c | 3 +- src/odhcp6c.c | 24 ++++++---- src/odhcp6c.h | 5 +- src/ra.c | 128 ++++++++++++++++++++++++++++++-------------------- src/script.c | 24 +++++++++- 6 files changed, 121 insertions(+), 64 deletions(-) diff --git a/README b/README index 9b56b20..c362966 100644 --- a/README +++ b/README @@ -77,6 +77,7 @@ Environment: * RA_ROUTES A space-separated list of routes from the RA Format:
/,gateway,valid,metric * RA_DNS A space-separated list of recursive DNS servers from the RA +* RA_DOMAINS A space-separated list of DNS search domains from the RA * RA_HOPLIMIT Highest hop-limit received in RAs * RA_MTU MTU-value received in RA * RA_REACHABLE ND Reachability time diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 000d999..eeb669b 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -995,7 +995,6 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, odhcp6c_add_state(STATE_DNS, odata, olen); } else if (otype == DHCPV6_OPT_DNS_DOMAIN) { odhcp6c_add_state(STATE_SEARCH, odata, olen); - passthru = false; } else if (otype == DHCPV6_OPT_SNTP_SERVERS) { if (olen % 16 == 0) odhcp6c_add_state(STATE_SNTP_IP, odata, olen); @@ -1142,7 +1141,7 @@ static int dhcpv6_parse_ia(void *opt, void *end) // Update address IA dhcpv6_for_each_option(&ia_hdr[1], end, otype, olen, odata) { - struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0, + struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0, 0, IN6ADDR_ANY_INIT, 0, 0, 0, 0, 0}; entry.iaid = ia_hdr->iaid; diff --git a/src/odhcp6c.c b/src/odhcp6c.c index 4c534c1..b62b222 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -556,11 +556,12 @@ void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len) static struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new) { size_t len, cmplen = offsetof(struct odhcp6c_entry, target) + ((new->length + 7) / 8); - struct odhcp6c_entry *start = odhcp6c_get_state(state, &len); - struct odhcp6c_entry *x = NULL; + uint8_t *start = odhcp6c_get_state(state, &len); - for (struct odhcp6c_entry *c = start; !x && c < &start[len/sizeof(*c)]; ++c) - if (!memcmp(c, new, cmplen)) + for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start; + (uint8_t*)c < &start[len] && &c->auxtarget[c->auxlen] <= &start[len]; + c = (struct odhcp6c_entry*)(&c->auxtarget[c->auxlen])) + if (!memcmp(c, new, cmplen) && !memcmp(c->auxtarget, new->auxtarget, new->auxlen)) return c; return NULL; @@ -572,7 +573,7 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, { size_t len; struct odhcp6c_entry *x = odhcp6c_find_entry(state, new); - struct odhcp6c_entry *start = odhcp6c_get_state(state, &len); + uint8_t *start = odhcp6c_get_state(state, &len); if (x && x->valid > new->valid && new->valid < safe) new->valid = safe; @@ -592,10 +593,10 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, x->t2 = new->t2; x->iaid = new->iaid; } else { - odhcp6c_add_state(state, new, sizeof(*new)); + odhcp6c_add_state(state, new, sizeof(*new) + new->auxlen); } } else if (x) { - odhcp6c_remove_state(state, (x - start) * sizeof(*x), sizeof(*x)); + odhcp6c_remove_state(state, ((uint8_t*)x) - start, sizeof(*x) + x->auxlen); } return true; } @@ -604,8 +605,10 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed) { size_t len; - struct odhcp6c_entry *start = odhcp6c_get_state(state, &len); - for (struct odhcp6c_entry *c = start; c < &start[len / sizeof(*c)]; ++c) { + uint8_t *start = odhcp6c_get_state(state, &len); + for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start; + (uint8_t*)c < &start[len] && &c->auxtarget[c->auxlen] <= &start[len]; + c = (struct odhcp6c_entry*)(&c->auxtarget[c->auxlen])) { if (c->t1 < elapsed) c->t1 = 0; else if (c->t1 != UINT32_MAX) @@ -627,7 +630,7 @@ static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed) c->valid -= elapsed; if (!c->valid) - odhcp6c_remove_state(state, (c - start) * sizeof(*c), sizeof(*c)); + odhcp6c_remove_state(state, ((uint8_t*)c) - start, sizeof(*c) + c->auxlen); } } @@ -641,6 +644,7 @@ void odhcp6c_expire(void) odhcp6c_expire_list(STATE_RA_PREFIX, elapsed); odhcp6c_expire_list(STATE_RA_ROUTE, elapsed); odhcp6c_expire_list(STATE_RA_DNS, elapsed); + odhcp6c_expire_list(STATE_RA_SEARCH, elapsed); odhcp6c_expire_list(STATE_IA_NA, elapsed); odhcp6c_expire_list(STATE_IA_PD, elapsed); } diff --git a/src/odhcp6c.h b/src/odhcp6c.h index 1042136..fff9360 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -255,6 +255,7 @@ enum odhcp6c_state { STATE_RA_ROUTE, STATE_RA_PREFIX, STATE_RA_DNS, + STATE_RA_SEARCH, STATE_AFTR_NAME, STATE_VENDORCLASS, STATE_USERCLASS, @@ -289,7 +290,8 @@ enum odhcp6c_ia_mode { struct odhcp6c_entry { struct in6_addr router; - uint16_t length; + uint8_t auxlen; + uint8_t length; int16_t priority; struct in6_addr target; uint32_t valid; @@ -297,6 +299,7 @@ struct odhcp6c_entry { uint32_t t1; uint32_t t2; uint32_t iaid; + uint8_t auxtarget[]; }; struct odhcp6c_request_prefix { diff --git a/src/ra.c b/src/ra.c index 5809fcd..dcb3e0f 100644 --- a/src/ra.c +++ b/src/ra.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -274,9 +276,11 @@ bool ra_process(void) bool changed = false; uint8_t buf[1500], cmsg_buf[128]; struct nd_router_advert *adv = (struct nd_router_advert*)buf; - struct odhcp6c_entry entry = {IN6ADDR_ANY_INIT, 0, 0, IN6ADDR_ANY_INIT, 0, 0, 0, 0, 0}; + struct odhcp6c_entry *entry = alloca(sizeof(*entry) + 256); const struct in6_addr any = IN6ADDR_ANY_INIT; + memset(entry, 0, sizeof(*entry)); + if (IN6_IS_ADDR_UNSPECIFIED(&lladdr)) { struct sockaddr_in6 addr = {AF_INET6, 0, 0, ALL_IPV6_ROUTERS, if_index}; socklen_t alen = sizeof(addr); @@ -332,15 +336,15 @@ bool ra_process(void) uint32_t router_valid = ntohs(adv->nd_ra_router_lifetime); // Parse default route - entry.target = any; - entry.length = 0; - entry.router = from.sin6_addr; - entry.priority = pref_to_priority(adv->nd_ra_flags_reserved); - if (entry.priority < 0) - entry.priority = pref_to_priority(0); - entry.valid = router_valid; - entry.preferred = entry.valid; - changed |= odhcp6c_update_entry(STATE_RA_ROUTE, &entry, 0, true); + entry->target = any; + entry->length = 0; + entry->router = from.sin6_addr; + entry->priority = pref_to_priority(adv->nd_ra_flags_reserved); + if (entry->priority < 0) + entry->priority = pref_to_priority(0); + entry->valid = router_valid; + entry->preferred = entry->valid; + changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, 0, true); // Parse hoplimit ra_conf_hoplimit(adv->nd_ra_curhoplimit); @@ -356,69 +360,93 @@ bool ra_process(void) uint32_t *mtu = (uint32_t*)&opt->data[2]; ra_conf_mtu(ntohl(*mtu)); } else if (opt->type == ND_OPT_ROUTE_INFORMATION && opt->len <= 3) { - entry.router = from.sin6_addr; - entry.target = any; - entry.priority = pref_to_priority(opt->data[1]); - entry.length = opt->data[0]; + entry->router = from.sin6_addr; + entry->target = any; + entry->priority = pref_to_priority(opt->data[1]); + entry->length = opt->data[0]; uint32_t *valid = (uint32_t*)&opt->data[2]; - entry.valid = ntohl(*valid); - memcpy(&entry.target, &opt->data[6], (opt->len - 1) * 8); + entry->valid = ntohl(*valid); + memcpy(&entry->target, &opt->data[6], (opt->len - 1) * 8); - if (entry.length > 128 || IN6_IS_ADDR_LINKLOCAL(&entry.target) - || IN6_IS_ADDR_LOOPBACK(&entry.target) - || IN6_IS_ADDR_MULTICAST(&entry.target)) + if (entry->length > 128 || IN6_IS_ADDR_LINKLOCAL(&entry->target) + || IN6_IS_ADDR_LOOPBACK(&entry->target) + || IN6_IS_ADDR_MULTICAST(&entry->target)) continue; - if (entry.priority > 0) - changed |= odhcp6c_update_entry(STATE_RA_ROUTE, &entry, 0, true); + if (entry->priority > 0) + changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, 0, true); } else if (opt->type == ND_OPT_PREFIX_INFORMATION && opt->len == 4) { struct nd_opt_prefix_info *pinfo = (struct nd_opt_prefix_info*)opt; - entry.router = any; - entry.target = pinfo->nd_opt_pi_prefix; - entry.priority = 256; - entry.length = pinfo->nd_opt_pi_prefix_len; - entry.valid = ntohl(pinfo->nd_opt_pi_valid_time); - entry.preferred = ntohl(pinfo->nd_opt_pi_preferred_time); - - if (entry.length > 128 || IN6_IS_ADDR_LINKLOCAL(&entry.target) - || IN6_IS_ADDR_LOOPBACK(&entry.target) - || IN6_IS_ADDR_MULTICAST(&entry.target) - || entry.valid < entry.preferred) + entry->router = any; + entry->target = pinfo->nd_opt_pi_prefix; + entry->priority = 256; + entry->length = pinfo->nd_opt_pi_prefix_len; + entry->valid = ntohl(pinfo->nd_opt_pi_valid_time); + entry->preferred = ntohl(pinfo->nd_opt_pi_preferred_time); + + if (entry->length > 128 || IN6_IS_ADDR_LINKLOCAL(&entry->target) + || IN6_IS_ADDR_LOOPBACK(&entry->target) + || IN6_IS_ADDR_MULTICAST(&entry->target) + || entry->valid < entry->preferred) continue; if (pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) - changed |= odhcp6c_update_entry(STATE_RA_ROUTE, &entry, 7200, true); + changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry, 7200, true); if (!(pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) || pinfo->nd_opt_pi_prefix_len != 64) continue; - entry.target.s6_addr32[2] = lladdr.s6_addr32[2]; - entry.target.s6_addr32[3] = lladdr.s6_addr32[3]; + entry->target.s6_addr32[2] = lladdr.s6_addr32[2]; + entry->target.s6_addr32[3] = lladdr.s6_addr32[3]; - changed |= odhcp6c_update_entry(STATE_RA_PREFIX, &entry, 7200, true); + changed |= odhcp6c_update_entry(STATE_RA_PREFIX, entry, 7200, true); } else if (opt->type == ND_OPT_RECURSIVE_DNS && opt->len > 2) { - entry.router = from.sin6_addr; - entry.priority = 0; - entry.length = 128; + entry->router = from.sin6_addr; + entry->priority = 0; + entry->length = 128; uint32_t *valid = (uint32_t*)&opt->data[2]; - entry.valid = ntohl(*valid); - entry.preferred = 0; + entry->valid = ntohl(*valid); + entry->preferred = 0; for (ssize_t i = 0; i < (opt->len - 1) / 2; ++i) { - memcpy(&entry.target, &opt->data[6 + i * sizeof(entry.target)], - sizeof(entry.target)); - changed |= odhcp6c_update_entry(STATE_RA_DNS, &entry, 0, true); + memcpy(&entry->target, &opt->data[6 + i * sizeof(entry->target)], + sizeof(entry->target)); + changed |= odhcp6c_update_entry(STATE_RA_DNS, entry, 0, true); + } + } else if (opt->type == ND_OPT_DNSSL && opt->len > 1) { + uint32_t *valid = (uint32_t*)&opt->data[2]; + uint8_t *buf = &opt->data[6]; + uint8_t *end = &buf[(opt->len - 1) * 8]; + + entry->router = from.sin6_addr; + entry->valid = ntohl(*valid); + + while (buf < end) { + int len = dn_expand(buf, end, buf, (char*)entry->auxtarget, 256); + if (len > 0) { + buf = &buf[len]; + entry->auxlen = strlen((char*)entry->auxtarget); + changed |= odhcp6c_update_entry(STATE_RA_SEARCH, entry, 0, true); + entry->auxlen = 0; + } else { + break; + } } } } - size_t ra_dns_len; - struct odhcp6c_entry *entry = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len); - for (size_t i = 0; i < ra_dns_len / sizeof(*entry); ++i) - if (IN6_ARE_ADDR_EQUAL(&entry[i].router, &from.sin6_addr) && - entry[i].valid > router_valid) - entry[i].valid = router_valid; + int states[2] = {STATE_RA_DNS, STATE_RA_SEARCH}; + for (size_t i = 0; i < 2; ++i) { + size_t ra_dns_len; + uint8_t *start = odhcp6c_get_state(states[i], &ra_dns_len); + for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start; + (uint8_t*)c < &start[ra_dns_len] && &c->auxtarget[c->auxlen] <= &start[ra_dns_len]; + c = (struct odhcp6c_entry*)(&c->auxtarget[c->auxlen])) + if (IN6_ARE_ADDR_EQUAL(&c->router, &from.sin6_addr) && + c->valid > router_valid) + c->valid = router_valid; + } } if (found) diff --git a/src/script.c b/src/script.c index f8d440f..30441b9 100644 --- a/src/script.c +++ b/src/script.c @@ -182,6 +182,25 @@ static void entry_to_env(const char *name, const void *data, size_t len, enum en } +static void search_to_env(const char *name, const uint8_t *start, size_t len) +{ + size_t buf_len = strlen(name); + char *buf = realloc(NULL, buf_len + 2 + len); + char *c = mempcpy(buf, name, buf_len); + *c++ = '='; + + for (struct odhcp6c_entry *e = (struct odhcp6c_entry*)start; + (uint8_t*)e < &start[len] && &e->auxtarget[e->auxlen] <= &start[len]; + e = (struct odhcp6c_entry*)(&e->auxtarget[e->auxlen])) { + c = mempcpy(c, e->auxtarget, e->auxlen); + *c++ = ' '; + } + + c[-1] = '\0'; + putenv(buf); +} + + static void int_to_env(const char *name, int value) { size_t len = 12 + strlen(name); @@ -340,12 +359,14 @@ void script_call(const char *status) uint8_t *s46_lw = odhcp6c_get_state(STATE_S46_LW, &s46_lw_len); uint8_t *passthru = odhcp6c_get_state(STATE_PASSTHRU, &passthru_len); - size_t prefix_len, address_len, ra_pref_len, ra_route_len, ra_dns_len; + size_t prefix_len, address_len, ra_pref_len, + ra_route_len, ra_dns_len, ra_search_len; uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len); uint8_t *address = odhcp6c_get_state(STATE_IA_NA, &address_len); uint8_t *ra_pref = odhcp6c_get_state(STATE_RA_PREFIX, &ra_pref_len); uint8_t *ra_route = odhcp6c_get_state(STATE_RA_ROUTE, &ra_route_len); uint8_t *ra_dns = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len); + uint8_t *ra_search = odhcp6c_get_state(STATE_RA_SEARCH, &ra_search_len); // Don't set environment before forking, because env is leaky. if (fork() == 0) { @@ -372,6 +393,7 @@ void script_call(const char *status) entry_to_env("RA_ADDRESSES", ra_pref, ra_pref_len, ENTRY_ADDRESS); entry_to_env("RA_ROUTES", ra_route, ra_route_len, ENTRY_ROUTE); entry_to_env("RA_DNS", ra_dns, ra_dns_len, ENTRY_HOST); + search_to_env("RA_DOMAINS", ra_search, ra_search_len); int_to_env("RA_HOPLIMIT", ra_conf_hoplimit(0)); int_to_env("RA_MTU", ra_conf_mtu(0)); -- 2.25.1