diff --git a/bugfix-allow-binding-mac-with-ipv6.patch b/bugfix-allow-binding-mac-with-ipv6.patch index 58fa9d4b2878c5fc0998499683999c3a39e1cc15..77b862fd6142dbbc092a6fcad157a6f9b7a4fe38 100644 --- a/bugfix-allow-binding-mac-with-ipv6.patch +++ b/bugfix-allow-binding-mac-with-ipv6.patch @@ -1,43 +1,40 @@ -From 53e1a09a06e11317bbde0e236837e5daa8d40593 Mon Sep 17 00:00:00 2001 -From: liaichun -Date: Mon, 20 Apr 2020 16:06:51 +0800 -Subject: [PATCH] bugfix-allow-binding-mac-with-ipv6 +From 5db7b109af8f6967335806b50d628611be7b9cfd Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 15:22:54 +0800 +Subject: [PATCH] bugfix allow binding mac with ipv6 --- - src/dnsmasq.c | 2 +- - src/dnsmasq.h | 4 ++- - src/option.c | 5 +++- - src/rfc3315.c | 35 +++++++++++++++++++++++++- - 4 files changed, 46 insertions(+) + src/dnsmasq.c | 1 + + src/dnsmasq.h | 2 ++ + src/option.c | 3 +++ + src/rfc3315.c | 32 +++++++++++++++++++++++++++++++- + 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/dnsmasq.c b/src/dnsmasq.c -index fc085eb..e44c360 100644 +index bfad87f..b25e0c9 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c -@@ -260,7 +260,7 @@ int main (int argc, char **argv) +@@ -261,6 +261,7 @@ int main (int argc, char **argv) if (daemon->dhcp6) { daemon->doing_ra = option_bool(OPT_RA); -- -+ daemon->bind_mac_with_ip6 = option_bool(OPT_BIND_MAC_IP6); ++ daemon->bind_mac_with_ip6 = option_bool(OPT_BIND_MAC_IP6); + for (context = daemon->dhcp6; context; context = context->next) { - if (context->flags & CONTEXT_DHCP) diff --git a/src/dnsmasq.h b/src/dnsmasq.h -index 68e6287..e7f1a0d 100644 +index 4220798..b9054b9 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h -@@ -269,7 +269,8 @@ struct event_desc { - #define OPT_IGNORE_CLID 59 +@@ -270,6 +270,7 @@ struct event_desc { #define OPT_SINGLE_PORT 60 #define OPT_LEASE_RENEW 61 --#define OPT_LAST 62 -+#define OPT_BIND_MAC_IP6 62 -+#define OPT_LAST 63 + #define OPT_LAST 62 ++#define OPT_BIND_MAC_IP6 63 #define OPTION_BITS (sizeof(unsigned int)*8) #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) -@@ -1049,6 +1050,7 @@ extern struct daemon { +@@ -1051,6 +1052,7 @@ extern struct daemon { int override; int enable_pxe; int doing_ra, doing_dhcp6; @@ -46,24 +43,22 @@ index 68e6287..e7f1a0d 100644 struct dhcp_netid_list *force_broadcast, *bootp_dynamic; struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs; diff --git a/src/option.c b/src/option.c -index 1f698da..f02d389 100644 +index dbe5f90..f8391d0 100644 --- a/src/option.c +++ b/src/option.c -@@ -167,7 +167,8 @@ struct myoption { +@@ -167,6 +167,7 @@ struct myoption { #define LOPT_IGNORE_CLID 358 #define LOPT_SINGLE_PORT 359 #define LOPT_SCRIPT_TIME 360 -- +#define LOPT_BIND_MAC_IP6 361 -+ + #ifdef HAVE_GETOPT_LONG static const struct option opts[] = - #else @@ -339,6 +340,7 @@ static const struct myoption opts[] = { "dumpfile", 1, 0, LOPT_DUMPFILE }, { "dumpmask", 1, 0, LOPT_DUMPMASK }, { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, -+ { "bind-mac-with-ip6", 0, 0 , LOPT_BIND_MAC_IP6 }, ++ { "bind-mac-with-ip6",0, 0, LOPT_BIND_MAC_IP6}, { NULL, 0, 0, 0 } }; @@ -76,7 +71,7 @@ index 1f698da..f02d389 100644 }; diff --git a/src/rfc3315.c b/src/rfc3315.c -index b3f0a0a..a5a092c 100644 +index b3f0a0a..5781809 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -49,6 +49,7 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz); @@ -93,16 +88,15 @@ index b3f0a0a..a5a092c 100644 match_netid(c->filter, solicit_tags, plain_range) && - config_valid(config, c, &addr, state, now)) + config_valid(config, c, &addr, state, now) && -+ check_and_try_preempte_address(state, &addr, now, config)) ++ check_and_try_preempte_address(state, &addr, now, config)) { mark_config_used(state->context, &addr); if (have_config(config, CONFIG_TIME)) -@@ -1259,6 +1261,37 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ - +@@ -1684,6 +1686,34 @@ static int check_address(struct state *state, struct in6_addr *addr) + return 1; } -+static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config) -+{ ++static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config){ + struct dhcp_lease *lease; + + if (!(lease = lease6_find_by_addr(addr, 128, 0))) @@ -110,8 +104,7 @@ index b3f0a0a..a5a092c 100644 + return 1; + } + -+ -+ if(daemon->bind_mac_with_ip6) { ++ if (daemon->bind_mac_with_ip6) { + // break rfc3315 here + // bind mac address with a lease + if ((state->mac) && !(config->flags & CONFIG_CLID) && @@ -125,16 +118,15 @@ index b3f0a0a..a5a092c 100644 + if (lease->clid_len != state->clid_len || + memcmp(lease->clid, state->clid, state->clid_len) != 0 || + lease->iaid != state->iaid) -+ { -+ return 0; -+ } ++ { ++ return 0; ++ } + + return 1; +} -+ - static struct dhcp_netid *add_options(struct state *state, int do_refresh) - { - void *oro; + + /* return true of *addr could have been generated from config. */ + static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr) -- -2.23.0 +1.8.3.1 diff --git a/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch index 8036a880c3c3bb7d5cb2d8f1ca7807eeb3d4c6a8..12d85b8c1aef3bfb54a732284c3ed1c60ce32335 100644 --- a/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch +++ b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch @@ -1,65 +1,2384 @@ -From 068fe05737fe86185b5d55da7de6ea6b2668c911 Mon Sep 17 00:00:00 2001 -From: liaichun -Date: Mon, 20 Apr 2020 16:17:24 +0800 -Subject: [PATCH] bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6 +From 76cc93d2aa547b1247d69b7f20050c1483b891e4 Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 16:20:13 +0800 +Subject: [PATCH] bugfix deal with CONFRIM when binding mac with ipv6 --- - src/rfc3315.c | 30 +++++++++++++++++++++++++++++- - 1 file changed, 29 insertions(+), 1 deletion(-) + ...l-with-CONFRIM-when-binding-mac-with-ipv6.patch | 66 + + src/rfc3315.c | 31 +- + src/rfc3315.c.orig | 2236 ++++++++++++++++++++ + 3 files changed, 2332 insertions(+), 1 deletion(-) + create mode 100644 bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch + create mode 100644 src/rfc3315.c.orig +diff --git a/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch +new file mode 100644 +index 0000000..d474de0 +--- /dev/null ++++ b/bugfix-deal-with-CONFRIM-when-binding-mac-with-ipv6.patch +@@ -0,0 +1,66 @@ ++From 9735f27181d266379c96b1bbf66901b6c83673e7 Mon Sep 17 00:00:00 2001 ++From: xiaoweiwei ++Date: Tue, 28 Jul 2020 11:26:22 +0800 ++Subject: [PATCH] bugfix deal with CONFRIM when binding mac with ipv6 ++ ++--- ++ src/rfc3315.c | 31 ++++++++++++++++++++++++++++++- ++ 1 file changed, 30 insertions(+), 1 deletion(-) ++ ++diff --git a/src/rfc3315.c b/src/rfc3315.c ++index bdf330a..345355c 100644 ++--- a/src/rfc3315.c +++++ b/src/rfc3315.c ++@@ -1030,12 +1030,32 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ ++ ++ case DHCP6CONFIRM: ++ { ++- int good_addr = 0; +++ int good_addr = 0; +++ int find_bind = 0; +++ struct dhcp_config *find_config = NULL; ++ ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPCONFIRM", NULL, NULL); +++ +++ if(daemon->bind_mac_with_ip6) { +++ if(state->mac) { +++ for (find_config = daemon->dhcp_conf; find_config; find_config = find_config->next) +++ if (config_has_mac(find_config, state->mac, state->mac_len, state->mac_type) && have_config(find_config, CONFIG_ADDR6)) { +++ find_bind = 1; +++ break; +++ } +++ } +++ /* requires all mac has binding ipv6 address. */ +++ if (find_bind == 0) { +++ o1 = new_opt6(OPTION6_STATUS_CODE); +++ put_opt6_short(DHCP6NOTONLINK); +++ put_opt6_string(_("confirm failed, no binding found")); +++ end_opt6(o1); +++ return 1; +++ } +++ } ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++@@ -1059,6 +1079,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ ++ log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); ++ return 1; ++ } +++ if (daemon->bind_mac_with_ip6) { +++ if (!is_same_net6(req_addr, &find_config->addr6, 128)) { +++ o1 = new_opt6(OPTION6_STATUS_CODE); +++ put_opt6_short(DHCP6NOTONLINK); +++ put_opt6_string(_("confirm failed, not binding to this address")); +++ end_opt6(o1); +++ return 1; +++ } +++ } ++ ++ good_addr = 1; ++ log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); ++-- ++1.8.3.1 ++ diff --git a/src/rfc3315.c b/src/rfc3315.c -index a5a092c..ef13b3d 100644 +index 5781809..c6032d9 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c -@@ -1033,11 +1033,31 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ +@@ -1032,12 +1032,32 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + case DHCP6CONFIRM: { - int good_addr = 0; -+ int find_bind = 0; -+ struct dhcp_config *find_config = NULL; +- int good_addr = 0; ++ int good_addr = 0; ++ int find_bind = 0; ++ struct dhcp_config *find_config = NULL; /* set reply message type */ *outmsgtypep = DHCP6REPLY; log6_quiet(state, "DHCPCONFIRM", NULL, NULL); + -+ if(daemon->bind_mac_with_ip6) { -+ if(state->mac) { -+ for (find_config = daemon->dhcp_conf; find_config; find_config = find_config->next) -+ if (config_has_mac(find_config, state->mac, state->mac_len, state->mac_type) && have_config(find_config, CONFIG_ADDR6)) { -+ find_bind = 1; -+ break; -+ } -+ } -+ /* requires all mac has binding ipv6 address. */ -+ if (find_bind == 0) { -+ o1 = new_opt6(OPTION6_STATUS_CODE); -+ put_opt6_short(DHCP6NOTONLINK); -+ put_opt6_string(_("confirm failed, no binding found")); -+ end_opt6(o1); -+ return 1; -+ } -+ } ++ if(daemon->bind_mac_with_ip6) { ++ if(state->mac) { ++ for (find_config = daemon->dhcp_conf; find_config; find_config = find_config->next) ++ if (config_has_mac(find_config, state->mac, state->mac_len, state->mac_type) && have_config(find_config, CONFIG_ADDR6)) { ++ find_bind = 1; ++ break; ++ } ++ } ++ /* requires all mac has binding ipv6 address. */ ++ if (find_bind == 0) { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOTONLINK); ++ put_opt6_string(_("confirm failed, no binding found")); ++ end_opt6(o1); ++ return 1; ++ } ++ } for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) { -@@ -1061,7 +1081,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ +@@ -1061,6 +1081,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); return 1; } -- -+ if(daemon->bind_mac_with_ip6) { -+ if (!is_same_net6(&req_addr, &find_config->addr6, 128)) { ++ if (daemon->bind_mac_with_ip6) { ++ if (!is_same_net6(&req_addr, &find_config->addr6, 128)) { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOTONLINK); ++ put_opt6_string(_("confirm failed, not binding to this address")); ++ end_opt6(o1); ++ return 1; ++ } ++ } + + good_addr = 1; + log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); +diff --git a/src/rfc3315.c.orig b/src/rfc3315.c.orig +new file mode 100644 +index 0000000..5781809 +--- /dev/null ++++ b/src/rfc3315.c.orig +@@ -0,0 +1,2236 @@ ++/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2 dated June, 1991, or ++ (at your option) version 3 dated 29 June, 2007. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++ ++#include "dnsmasq.h" ++ ++#ifdef HAVE_DHCP6 ++ ++struct state { ++ unsigned char *clid; ++ int clid_len, ia_type, interface, hostname_auth, lease_allocate; ++ char *client_hostname, *hostname, *domain, *send_domain; ++ struct dhcp_context *context; ++ struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr; ++ unsigned int xid, fqdn_flags, iaid; ++ char *iface_name; ++ void *packet_options, *end; ++ struct dhcp_netid *tags, *context_tags; ++ unsigned char mac[DHCP_CHADDR_MAX]; ++ unsigned int mac_len, mac_type; ++}; ++ ++static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, ++ struct in6_addr *client_addr, int is_unicast, time_t now); ++static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now); ++static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts); ++static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string); ++static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string); ++static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize); ++static void *opt6_next(void *opts, void *end); ++static unsigned int opt6_uint(unsigned char *opt, int offset, int size); ++static void get_context_tag(struct state *state, struct dhcp_context *context); ++static int check_ia(struct state *state, void *opt, void **endp, void **ia_option); ++static int build_ia(struct state *state, int *t1cntr); ++static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz); ++static void mark_context_used(struct state *state, struct in6_addr *addr); ++static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr); ++static int check_address(struct state *state, struct in6_addr *addr); ++static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config); ++static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now); ++static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr); ++static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, ++ unsigned int *min_time, struct in6_addr *addr, time_t now); ++static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now); ++static int add_local_addrs(struct dhcp_context *context); ++static struct dhcp_netid *add_options(struct state *state, int do_refresh); ++static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, ++ unsigned int *preferred_timep, unsigned int lease_time); ++ ++#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2))) ++#define opt6_type(opt) (opt6_uint(opt, -4, 2)) ++#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)])) ++ ++#define opt6_user_vendor_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2+(i)])) ++#define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2))) ++#define opt6_user_vendor_next(opt, end) (opt6_next(((void *) opt) - 2, end)) ++ ++ ++unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, ++ struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr, ++ size_t sz, struct in6_addr *client_addr, time_t now) ++{ ++ struct dhcp_vendor *vendor; ++ int msg_type; ++ struct state state; ++ ++ if (sz <= 4) ++ return 0; ++ ++ msg_type = *((unsigned char *)daemon->dhcp_packet.iov_base); ++ ++ /* Mark these so we only match each at most once, to avoid tangled linked lists */ ++ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) ++ vendor->netid.next = &vendor->netid; ++ ++ reset_counter(); ++ state.context = context; ++ state.interface = interface; ++ state.iface_name = iface_name; ++ state.fallback = fallback; ++ state.ll_addr = ll_addr; ++ state.ula_addr = ula_addr; ++ state.mac_len = 0; ++ state.tags = NULL; ++ state.link_address = NULL; ++ ++ if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr, ++ IN6_IS_ADDR_MULTICAST(client_addr), now)) ++ return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; ++ ++ return 0; ++} ++ ++/* This cost me blood to write, it will probably cost you blood to understand - srk. */ ++static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, ++ struct in6_addr *client_addr, int is_unicast, time_t now) ++{ ++ void *end = inbuff + sz; ++ void *opts = inbuff + 34; ++ int msg_type = *((unsigned char *)inbuff); ++ unsigned char *outmsgtypep; ++ void *opt; ++ struct dhcp_vendor *vendor; ++ ++ /* if not an encapsulated relayed message, just do the stuff */ ++ if (msg_type != DHCP6RELAYFORW) ++ { ++ /* if link_address != NULL if points to the link address field of the ++ innermost nested RELAYFORW message, which is where we find the ++ address of the network on which we can allocate an address. ++ Recalculate the available contexts using that information. ++ ++ link_address == NULL means there's no relay in use, so we try and find the client's ++ MAC address from the local ND cache. */ ++ ++ if (!state->link_address) ++ get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now); ++ else ++ { ++ struct dhcp_context *c; ++ struct shared_network *share = NULL; ++ state->context = NULL; ++ ++ if (!IN6_IS_ADDR_LOOPBACK(state->link_address) && ++ !IN6_IS_ADDR_LINKLOCAL(state->link_address) && ++ !IN6_IS_ADDR_MULTICAST(state->link_address)) ++ for (c = daemon->dhcp6; c; c = c->next) ++ { ++ for (share = daemon->shared_networks; share; share = share->next) ++ { ++ if (share->shared_addr.s_addr != 0) ++ continue; ++ ++ if (share->if_index != 0 || ++ !IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6)) ++ continue; ++ ++ if ((c->flags & CONTEXT_DHCP) && ++ !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && ++ is_same_net6(&share->shared_addr6, &c->start6, c->prefix) && ++ is_same_net6(&share->shared_addr6, &c->end6, c->prefix)) ++ break; ++ } ++ ++ if (share || ++ ((c->flags & CONTEXT_DHCP) && ++ !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) && ++ is_same_net6(state->link_address, &c->start6, c->prefix) && ++ is_same_net6(state->link_address, &c->end6, c->prefix))) ++ { ++ c->preferred = c->valid = 0xffffffff; ++ c->current = state->context; ++ state->context = c; ++ } ++ } ++ ++ if (!state->context) ++ { ++ inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); ++ my_syslog(MS_DHCP | LOG_WARNING, ++ _("no address range available for DHCPv6 request from relay at %s"), ++ daemon->addrbuff); ++ return 0; ++ } ++ } ++ ++ if (!state->context) ++ { ++ my_syslog(MS_DHCP | LOG_WARNING, ++ _("no address range available for DHCPv6 request via %s"), state->iface_name); ++ return 0; ++ } ++ ++ return dhcp6_no_relay(state, msg_type, inbuff, sz, is_unicast, now); ++ } ++ ++ /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option ++ which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */ ++ if (sz < 38) ++ return 0; ++ ++ /* copy header stuff into reply message and set type to reply */ ++ if (!(outmsgtypep = put_opt6(inbuff, 34))) ++ return 0; ++ *outmsgtypep = DHCP6RELAYREPL; ++ ++ /* look for relay options and set tags if found. */ ++ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) ++ { ++ int mopt; ++ ++ if (vendor->match_type == MATCH_SUBSCRIBER) ++ mopt = OPTION6_SUBSCRIBER_ID; ++ else if (vendor->match_type == MATCH_REMOTE) ++ mopt = OPTION6_REMOTE_ID; ++ else ++ continue; ++ ++ if ((opt = opt6_find(opts, end, mopt, 1)) && ++ vendor->len == opt6_len(opt) && ++ memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 && ++ vendor->netid.next != &vendor->netid) ++ { ++ vendor->netid.next = state->tags; ++ state->tags = &vendor->netid; ++ break; ++ } ++ } ++ ++ /* RFC-6939 */ ++ if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3))) ++ { ++ if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) { ++ return 0; ++ } ++ state->mac_type = opt6_uint(opt, 0, 2); ++ state->mac_len = opt6_len(opt) - 2; ++ memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len); ++ } ++ ++ for (opt = opts; opt; opt = opt6_next(opt, end)) ++ { ++ if (opt6_ptr(opt, 0) + opt6_len(opt) > end) ++ return 0; ++ ++ /* Don't copy MAC address into reply. */ ++ if (opt6_type(opt) != OPTION6_CLIENT_MAC) ++ { ++ int o = new_opt6(opt6_type(opt)); ++ if (opt6_type(opt) == OPTION6_RELAY_MSG) ++ { ++ struct in6_addr align; ++ /* the packet data is unaligned, copy to aligned storage */ ++ memcpy(&align, inbuff + 2, IN6ADDRSZ); ++ state->link_address = &align; ++ /* zero is_unicast since that is now known to refer to the ++ relayed packet, not the original sent by the client */ ++ if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now)) ++ return 0; ++ } ++ else ++ put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); ++ end_opt6(o); ++ } ++ } ++ ++ return 1; ++} ++ ++static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_t sz, int is_unicast, time_t now) ++{ ++ void *opt; ++ int i, o, o1, start_opts; ++ struct dhcp_opt *opt_cfg; ++ struct dhcp_netid *tagif; ++ struct dhcp_config *config = NULL; ++ struct dhcp_netid known_id, iface_id, v6_id; ++ unsigned char *outmsgtypep; ++ struct dhcp_vendor *vendor; ++ struct dhcp_context *context_tmp; ++ struct dhcp_mac *mac_opt; ++ unsigned int ignore = 0; ++ ++ state->packet_options = inbuff + 4; ++ state->end = inbuff + sz; ++ state->clid = NULL; ++ state->clid_len = 0; ++ state->lease_allocate = 0; ++ state->context_tags = NULL; ++ state->domain = NULL; ++ state->send_domain = NULL; ++ state->hostname_auth = 0; ++ state->hostname = NULL; ++ state->client_hostname = NULL; ++ state->fqdn_flags = 0x01; /* default to send if we receive no FQDN option */ ++ ++ /* set tag with name == interface */ ++ iface_id.net = state->iface_name; ++ iface_id.next = state->tags; ++ state->tags = &iface_id; ++ ++ /* set tag "dhcpv6" */ ++ v6_id.net = "dhcpv6"; ++ v6_id.next = state->tags; ++ state->tags = &v6_id; ++ ++ /* copy over transaction-id, and save pointer to message type */ ++ if (!(outmsgtypep = put_opt6(inbuff, 4))) ++ return 0; ++ start_opts = save_counter(-1); ++ state->xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16; ++ ++ /* We're going to be linking tags from all context we use. ++ mark them as unused so we don't link one twice and break the list */ ++ for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current) ++ { ++ context_tmp->netid.next = &context_tmp->netid; ++ ++ if (option_bool(OPT_LOG_OPTS)) ++ { ++ inet_ntop(AF_INET6, &context_tmp->start6, daemon->dhcp_buff, ADDRSTRLEN); ++ inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN); ++ if (context_tmp->flags & (CONTEXT_STATIC)) ++ my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"), ++ state->xid, daemon->dhcp_buff, context_tmp->prefix); ++ else ++ my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), ++ state->xid, daemon->dhcp_buff, daemon->dhcp_buff2); ++ } ++ } ++ ++ if ((opt = opt6_find(state->packet_options, state->end, OPTION6_CLIENT_ID, 1))) ++ { ++ state->clid = opt6_ptr(opt, 0); ++ state->clid_len = opt6_len(opt); ++ o = new_opt6(OPTION6_CLIENT_ID); ++ put_opt6(state->clid, state->clid_len); ++ end_opt6(o); ++ } ++ else if (msg_type != DHCP6IREQ) ++ return 0; ++ ++ /* server-id must match except for SOLICIT, CONFIRM and REBIND messages */ ++ if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && msg_type != DHCP6REBIND && ++ (!(opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1)) || ++ opt6_len(opt) != daemon->duid_len || ++ memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) ++ return 0; ++ ++ o = new_opt6(OPTION6_SERVER_ID); ++ put_opt6(daemon->duid, daemon->duid_len); ++ end_opt6(o); ++ ++ if (is_unicast && ++ (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE)) ++ ++ { ++ *outmsgtypep = DHCP6REPLY; ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6USEMULTI); ++ put_opt6_string("Use multicast"); ++ end_opt6(o1); ++ return 1; ++ } ++ ++ /* match vendor and user class options */ ++ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) ++ { ++ int mopt; ++ ++ if (vendor->match_type == MATCH_VENDOR) ++ mopt = OPTION6_VENDOR_CLASS; ++ else if (vendor->match_type == MATCH_USER) ++ mopt = OPTION6_USER_CLASS; ++ else ++ continue; ++ ++ if ((opt = opt6_find(state->packet_options, state->end, mopt, 2))) ++ { ++ void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt)); ++ int offset = 0; ++ ++ if (mopt == OPTION6_VENDOR_CLASS) ++ { ++ if (opt6_len(opt) < 4) ++ continue; ++ ++ if (vendor->enterprise != opt6_uint(opt, 0, 4)) ++ continue; ++ ++ offset = 4; ++ } ++ ++ /* Note that format if user/vendor classes is different to DHCP options - no option types. */ ++ for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end)) ++ for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++) ++ if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0) ++ { ++ vendor->netid.next = state->tags; ++ state->tags = &vendor->netid; ++ break; ++ } ++ } ++ } ++ ++ if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4))) ++ my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), state->xid, opt6_uint(opt, 0, 4)); ++ ++ /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match. ++ Otherwise assume the option is an array, and look for a matching element. ++ If no data given, existence of the option is enough. This code handles ++ V-I opts too. */ ++ for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next) ++ { ++ int match = 0; ++ ++ if (opt_cfg->flags & DHOPT_RFC3925) ++ { ++ for (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_OPTS, 4); ++ opt; ++ opt = opt6_find(opt6_next(opt, state->end), state->end, OPTION6_VENDOR_OPTS, 4)) ++ { ++ void *vopt; ++ void *vend = opt6_ptr(opt, opt6_len(opt)); ++ ++ for (vopt = opt6_find(opt6_ptr(opt, 4), vend, opt_cfg->opt, 0); ++ vopt; ++ vopt = opt6_find(opt6_next(vopt, vend), vend, opt_cfg->opt, 0)) ++ if ((match = match_bytes(opt_cfg, opt6_ptr(vopt, 0), opt6_len(vopt)))) ++ break; ++ } ++ if (match) ++ break; ++ } ++ else ++ { ++ if (!(opt = opt6_find(state->packet_options, state->end, opt_cfg->opt, 1))) ++ continue; ++ ++ match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt)); ++ } ++ ++ if (match) ++ { ++ opt_cfg->netid->next = state->tags; ++ state->tags = opt_cfg->netid; ++ } ++ } ++ ++ if (state->mac_len != 0) ++ { ++ if (option_bool(OPT_LOG_OPTS)) ++ { ++ print_mac(daemon->dhcp_buff, state->mac, state->mac_len); ++ my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff); ++ } ++ ++ for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next) ++ if ((unsigned)mac_opt->hwaddr_len == state->mac_len && ++ ((unsigned)mac_opt->hwaddr_type == state->mac_type || mac_opt->hwaddr_type == 0) && ++ memcmp_masked(mac_opt->hwaddr, state->mac, state->mac_len, mac_opt->mask)) ++ { ++ mac_opt->netid.next = state->tags; ++ state->tags = &mac_opt->netid; ++ } ++ } ++ ++ if ((opt = opt6_find(state->packet_options, state->end, OPTION6_FQDN, 1))) ++ { ++ /* RFC4704 refers */ ++ int len = opt6_len(opt) - 1; ++ ++ state->fqdn_flags = opt6_uint(opt, 0, 1); ++ ++ /* Always force update, since the client has no way to do it itself. */ ++ if (!option_bool(OPT_FQDN_UPDATE) && !(state->fqdn_flags & 0x01)) ++ state->fqdn_flags |= 0x03; ++ ++ state->fqdn_flags &= ~0x04; ++ ++ if (len != 0 && len < 255) ++ { ++ unsigned char *pp, *op = opt6_ptr(opt, 1); ++ char *pq = daemon->dhcp_buff; ++ ++ pp = op; ++ while (*op != 0 && ((op + (*op)) - pp) < len) ++ { ++ memcpy(pq, op+1, *op); ++ pq += *op; ++ op += (*op)+1; ++ *(pq++) = '.'; ++ } ++ ++ if (pq != daemon->dhcp_buff) ++ pq--; ++ *pq = 0; ++ ++ if (legal_hostname(daemon->dhcp_buff)) ++ { ++ struct dhcp_match_name *m; ++ size_t nl = strlen(daemon->dhcp_buff); ++ ++ state->client_hostname = daemon->dhcp_buff; ++ ++ if (option_bool(OPT_LOG_OPTS)) ++ my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), state->xid, state->client_hostname); ++ ++ for (m = daemon->dhcp_name_match; m; m = m->next) ++ { ++ size_t ml = strlen(m->name); ++ char save = 0; ++ ++ if (nl < ml) ++ continue; ++ if (nl > ml) ++ { ++ save = state->client_hostname[ml]; ++ state->client_hostname[ml] = 0; ++ } ++ ++ if (hostname_isequal(state->client_hostname, m->name) && ++ (save == 0 || m->wildcard)) ++ { ++ m->netid->next = state->tags; ++ state->tags = m->netid; ++ } ++ ++ if (save != 0) ++ state->client_hostname[ml] = save; ++ } ++ } ++ } ++ } ++ ++ if (state->clid && ++ (config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len, ++ state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags))) && ++ have_config(config, CONFIG_NAME)) ++ { ++ state->hostname = config->hostname; ++ state->domain = config->domain; ++ state->hostname_auth = 1; ++ } ++ else if (state->client_hostname) ++ { ++ state->domain = strip_hostname(state->client_hostname); ++ ++ if (strlen(state->client_hostname) != 0) ++ { ++ state->hostname = state->client_hostname; ++ ++ if (!config) ++ { ++ /* Search again now we have a hostname. ++ Only accept configs without CLID here, (it won't match) ++ to avoid impersonation by name. */ ++ struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname, run_tag_if(state->tags)); ++ if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) ++ config = new; ++ } ++ } ++ } ++ ++ if (config) ++ { ++ struct dhcp_netid_list *list; ++ ++ for (list = config->netid; list; list = list->next) ++ { ++ list->list->next = state->tags; ++ state->tags = list->list; ++ } ++ ++ /* set "known" tag for known hosts */ ++ known_id.net = "known"; ++ known_id.next = state->tags; ++ state->tags = &known_id; ++ ++ if (have_config(config, CONFIG_DISABLE)) ++ ignore = 1; ++ } ++ else if (state->clid && ++ find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len, ++ state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags))) ++ { ++ known_id.net = "known-othernet"; ++ known_id.next = state->tags; ++ state->tags = &known_id; ++ } ++ ++ tagif = run_tag_if(state->tags); ++ ++ /* if all the netids in the ignore list are present, ignore this client */ ++ if (daemon->dhcp_ignore) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next) ++ if (match_netid(id_list->list, tagif, 0)) ++ ignore = 1; ++ } ++ ++ /* if all the netids in the ignore_name list are present, ignore client-supplied name */ ++ if (!state->hostname_auth) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next) ++ if ((!id_list->list) || match_netid(id_list->list, tagif, 0)) ++ break; ++ if (id_list) ++ state->hostname = NULL; ++ } ++ ++ ++ switch (msg_type) ++ { ++ default: ++ return 0; ++ ++ ++ case DHCP6SOLICIT: ++ { ++ int address_assigned = 0; ++ /* tags without all prefix-class tags */ ++ struct dhcp_netid *solicit_tags; ++ struct dhcp_context *c; ++ ++ *outmsgtypep = DHCP6ADVERTISE; ++ ++ if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0)) ++ { ++ *outmsgtypep = DHCP6REPLY; ++ state->lease_allocate = 1; ++ o = new_opt6(OPTION6_RAPID_COMMIT); ++ end_opt6(o); ++ } ++ ++ log6_quiet(state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL); ++ ++ request_no_address: ++ solicit_tags = tagif; ++ ++ if (ignore) ++ return 0; ++ ++ /* reset USED bits in leases */ ++ lease6_reset(); ++ ++ /* Can use configured address max once per prefix */ ++ for (c = state->context; c; c = c->current) ++ c->flags &= ~CONTEXT_CONF_USED; ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ unsigned int min_time = 0xffffffff; ++ int t1cntr; ++ int ia_counter; ++ /* set unless we're sending a particular prefix-class, when we ++ want only dhcp-ranges with the correct tags set and not those without any tags. */ ++ int plain_range = 1; ++ u32 lease_time; ++ struct dhcp_lease *ltmp; ++ struct in6_addr req_addr, addr; ++ ++ if (!check_ia(state, opt, &ia_end, &ia_option)) ++ continue; ++ ++ /* reset USED bits in contexts - one address per prefix per IAID */ ++ for (c = state->context; c; c = c->current) ++ c->flags &= ~CONTEXT_USED; ++ ++ o = build_ia(state, &t1cntr); ++ if (address_assigned) ++ address_assigned = 2; ++ ++ for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ /* worry about alignment here. */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range))) ++ { ++ lease_time = c->lease_time; ++ /* If the client asks for an address on the same network as a configured address, ++ offer the configured address instead, to make moving to newly-configured ++ addresses automatic. */ ++ if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr, state, now)) ++ { ++ req_addr = addr; ++ mark_config_used(c, &addr); ++ if (have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ } ++ else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) ++ continue; /* not an address we're allowed */ ++ else if (!check_address(state, &req_addr)) ++ continue; /* address leased elsewhere */ ++ ++ /* add address to output packet */ ++ add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now); ++ mark_context_used(state, &req_addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ } ++ ++ /* Suggest configured address(es) */ ++ for (c = state->context; c; c = c->current) ++ if (!(c->flags & CONTEXT_CONF_USED) && ++ match_netid(c->filter, solicit_tags, plain_range) && ++ config_valid(config, c, &addr, state, now) && ++ check_and_try_preempte_address(state, &addr, now, config)) ++ { ++ mark_config_used(state->context, &addr); ++ if (have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ else ++ lease_time = c->lease_time; ++ ++ /* add address to output packet */ ++ add_address(state, c, lease_time, NULL, &min_time, &addr, now); ++ mark_context_used(state, &addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ ++ /* return addresses for existing leases */ ++ ltmp = NULL; ++ while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid))) ++ { ++ req_addr = ltmp->addr6; ++ if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) ++ { ++ add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now); ++ mark_context_used(state, &req_addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ } ++ ++ /* Return addresses for all valid contexts which don't yet have one */ ++ while ((c = address6_allocate(state->context, state->clid, state->clid_len, state->ia_type == OPTION6_IA_TA, ++ state->iaid, ia_counter, solicit_tags, plain_range, &addr))) ++ { ++ add_address(state, c, c->lease_time, NULL, &min_time, &addr, now); ++ mark_context_used(state, &addr); ++ get_context_tag(state, c); ++ address_assigned = 1; ++ } ++ ++ if (address_assigned != 1) ++ { ++ /* If the server will not assign any addresses to any IAs in a ++ subsequent Request from the client, the server MUST send an Advertise ++ message to the client that doesn't include any IA options. */ ++ if (!state->lease_allocate) ++ { ++ save_counter(o); ++ continue; ++ } ++ ++ /* If the server cannot assign any addresses to an IA in the message ++ from the client, the server MUST include the IA in the Reply message ++ with no addresses in the IA and a Status Code option in the IA ++ containing status code NoAddrsAvail. */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("address unavailable")); ++ end_opt6(o1); ++ } ++ ++ end_ia(t1cntr, min_time, 0); ++ end_opt6(o); ++ } ++ ++ if (address_assigned) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("success")); ++ end_opt6(o1); ++ ++ /* If --dhcp-authoritative is set, we can tell client not to wait for ++ other possible servers */ ++ o = new_opt6(OPTION6_PREFERENCE); ++ put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0); ++ end_opt6(o); ++ tagif = add_options(state, 0); ++ } ++ else ++ { ++ /* no address, return error */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("no addresses available")); ++ end_opt6(o1); ++ ++ /* Some clients will ask repeatedly when we're not giving ++ out addresses because we're in stateless mode. Avoid spamming ++ the log in that case. */ ++ for (c = state->context; c; c = c->current) ++ if (!(c->flags & CONTEXT_RA_STATELESS)) ++ { ++ log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available")); ++ break; ++ } ++ } ++ ++ break; ++ } ++ ++ case DHCP6REQUEST: ++ { ++ int address_assigned = 0; ++ int start = save_counter(-1); ++ ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ state->lease_allocate = 1; ++ ++ log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL); ++ ++ if (ignore) ++ return 0; ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ unsigned int min_time = 0xffffffff; ++ int t1cntr; ++ ++ if (!check_ia(state, opt, &ia_end, &ia_option)) ++ continue; ++ ++ if (!ia_option) ++ { ++ /* If we get a request with an IA_*A without addresses, treat it exactly like ++ a SOLICT with rapid commit set. */ ++ save_counter(start); ++ goto request_no_address; ++ } ++ ++ o = build_ia(state, &t1cntr); ++ ++ for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct in6_addr req_addr; ++ struct dhcp_context *dynamic, *c; ++ unsigned int lease_time; ++ int config_ok = 0; ++ ++ /* align. */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if ((c = address6_valid(state->context, &req_addr, tagif, 1))) ++ config_ok = (config_implies(config, c, &req_addr) != NULL); ++ ++ if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c) ++ { ++ if (!dynamic && !config_ok) ++ { ++ /* Static range, not configured. */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("address unavailable")); ++ end_opt6(o1); ++ } ++ else if (!check_address(state, &req_addr)) ++ { ++ /* Address leased to another DUID/IAID */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6UNSPEC); ++ put_opt6_string(_("address in use")); ++ end_opt6(o1); ++ } ++ else ++ { ++ if (!dynamic) ++ dynamic = c; ++ ++ lease_time = dynamic->lease_time; ++ ++ if (config_ok && have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ ++ add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now); ++ get_context_tag(state, dynamic); ++ address_assigned = 1; ++ } ++ } ++ else ++ { ++ /* requested address not on the correct link */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOTONLINK); ++ put_opt6_string(_("not on link")); ++ end_opt6(o1); ++ } ++ } ++ ++ end_ia(t1cntr, min_time, 0); ++ end_opt6(o); ++ } ++ ++ if (address_assigned) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("success")); ++ end_opt6(o1); ++ } ++ else ++ { ++ /* no address, return error */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("no addresses available")); ++ end_opt6(o1); ++ log6_packet(state, "DHCPREPLY", NULL, _("no addresses available")); ++ } ++ ++ tagif = add_options(state, 0); ++ break; ++ } ++ ++ ++ case DHCP6RENEW: ++ { ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPRENEW", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ unsigned int min_time = 0xffffffff; ++ int t1cntr, iacntr; ++ ++ if (!check_ia(state, opt, &ia_end, &ia_option)) ++ continue; ++ ++ o = build_ia(state, &t1cntr); ++ iacntr = save_counter(-1); ++ ++ for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct dhcp_lease *lease = NULL; ++ struct in6_addr req_addr; ++ unsigned int preferred_time = opt6_uint(ia_option, 16, 4); ++ unsigned int valid_time = opt6_uint(ia_option, 20, 4); ++ char *message = NULL; ++ struct dhcp_context *this_context; ++ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if (!(lease = lease6_find(state->clid, state->clid_len, ++ state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, ++ state->iaid, &req_addr))) ++ { ++ /* If the server cannot find a client entry for the IA the server ++ returns the IA containing no addresses with a Status Code option set ++ to NoBinding in the Reply message. */ ++ save_counter(iacntr); ++ t1cntr = 0; ++ ++ log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found")); ++ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOBINDING); ++ put_opt6_string(_("no binding found")); ++ end_opt6(o1); ++ ++ preferred_time = valid_time = 0; ++ break; ++ } ++ ++ ++ if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) || ++ (this_context = address6_valid(state->context, &req_addr, tagif, 1))) ++ { ++ unsigned int lease_time; ++ ++ get_context_tag(state, this_context); ++ ++ if (config_implies(config, this_context, &req_addr) && have_config(config, CONFIG_TIME)) ++ lease_time = config->lease_time; ++ else ++ lease_time = this_context->lease_time; ++ ++ calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time); ++ ++ lease_set_expires(lease, valid_time, now); ++ /* Update MAC record in case it's new information. */ ++ if (state->mac_len != 0) ++ lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0); ++ if (state->ia_type == OPTION6_IA_NA && state->hostname) ++ { ++ char *addr_domain = get_domain6(&req_addr); ++ if (!state->send_domain) ++ state->send_domain = addr_domain; ++ lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); ++ message = state->hostname; ++ } ++ ++ ++ if (preferred_time == 0) ++ message = _("deprecated"); ++ } ++ else ++ { ++ preferred_time = valid_time = 0; ++ message = _("address invalid"); ++ } ++ ++ if (message && (message != state->hostname)) ++ log6_packet(state, "DHCPREPLY", &req_addr, message); ++ else ++ log6_quiet(state, "DHCPREPLY", &req_addr, message); ++ ++ o1 = new_opt6(OPTION6_IAADDR); ++ put_opt6(&req_addr, sizeof(req_addr)); ++ put_opt6_long(preferred_time); ++ put_opt6_long(valid_time); ++ end_opt6(o1); ++ } ++ ++ end_ia(t1cntr, min_time, 1); ++ end_opt6(o); ++ } ++ ++ tagif = add_options(state, 0); ++ break; ++ ++ } ++ ++ case DHCP6CONFIRM: ++ { ++ int good_addr = 0; ++ ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPCONFIRM", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ ++ for (check_ia(state, opt, &ia_end, &ia_option); ++ ia_option; ++ ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct in6_addr req_addr; ++ ++ /* alignment */ ++ memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if (!address6_valid(state->context, &req_addr, tagif, 1)) ++ { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOTONLINK); -+ put_opt6_string(_("confirm failed, not binding to this address")); ++ put_opt6_string(_("confirm failed")); + end_opt6(o1); ++ log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); + return 1; + } ++ ++ good_addr = 1; ++ log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); ++ } ++ } ++ ++ /* No addresses, no reply: RFC 3315 18.2.2 */ ++ if (!good_addr) ++ return 0; ++ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS ); ++ put_opt6_string(_("all addresses still on link")); ++ end_opt6(o1); ++ break; ++ } ++ ++ case DHCP6IREQ: ++ { ++ /* We can't discriminate contexts based on address, as we don't know it. ++ If there is only one possible context, we can use its tags */ ++ if (state->context && state->context->netid.net && !state->context->current) ++ { ++ state->context->netid.next = NULL; ++ state->context_tags = &state->context->netid; ++ } ++ ++ /* Similarly, we can't determine domain from address, but if the FQDN is ++ given in --dhcp-host, we can use that, and failing that we can use the ++ unqualified configured domain, if any. */ ++ if (state->hostname_auth) ++ state->send_domain = state->domain; ++ else ++ state->send_domain = get_domain6(NULL); ++ ++ log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname); ++ if (ignore) ++ return 0; ++ *outmsgtypep = DHCP6REPLY; ++ tagif = add_options(state, 1); ++ break; ++ } ++ ++ ++ case DHCP6RELEASE: ++ { ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPRELEASE", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ int made_ia = 0; ++ ++ for (check_ia(state, opt, &ia_end, &ia_option); ++ ia_option; ++ ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct dhcp_lease *lease; ++ struct in6_addr addr; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, ++ state->iaid, &addr))) ++ lease_prune(lease, now); ++ else ++ { ++ if (!made_ia) ++ { ++ o = new_opt6(state->ia_type); ++ put_opt6_long(state->iaid); ++ if (state->ia_type == OPTION6_IA_NA) ++ { ++ put_opt6_long(0); ++ put_opt6_long(0); ++ } ++ made_ia = 1; ++ } ++ ++ o1 = new_opt6(OPTION6_IAADDR); ++ put_opt6(&addr, IN6ADDRSZ); ++ put_opt6_long(0); ++ put_opt6_long(0); ++ end_opt6(o1); ++ } ++ } ++ ++ if (made_ia) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOBINDING); ++ put_opt6_string(_("no binding found")); ++ end_opt6(o1); ++ ++ end_opt6(o); ++ } ++ } ++ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("release received")); ++ end_opt6(o1); ++ ++ break; ++ } ++ ++ case DHCP6DECLINE: ++ { ++ /* set reply message type */ ++ *outmsgtypep = DHCP6REPLY; ++ ++ log6_quiet(state, "DHCPDECLINE", NULL, NULL); ++ ++ for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) ++ { ++ void *ia_option, *ia_end; ++ int made_ia = 0; ++ ++ for (check_ia(state, opt, &ia_end, &ia_option); ++ ia_option; ++ ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) ++ { ++ struct dhcp_lease *lease; ++ struct in6_addr addr; ++ struct addrlist *addr_list; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ if ((addr_list = config_implies(config, state->context, &addr))) ++ { ++ prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF); ++ inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN); ++ my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), ++ daemon->addrbuff, daemon->dhcp_buff3); ++ addr_list->flags |= ADDRLIST_DECLINED; ++ addr_list->decline_time = now; ++ } ++ else ++ /* make sure this host gets a different address next time. */ ++ for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current) ++ context_tmp->addr_epoch++; ++ ++ if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, ++ state->iaid, &addr))) ++ lease_prune(lease, now); ++ else ++ { ++ if (!made_ia) ++ { ++ o = new_opt6(state->ia_type); ++ put_opt6_long(state->iaid); ++ if (state->ia_type == OPTION6_IA_NA) ++ { ++ put_opt6_long(0); ++ put_opt6_long(0); ++ } ++ made_ia = 1; ++ } ++ ++ o1 = new_opt6(OPTION6_IAADDR); ++ put_opt6(&addr, IN6ADDRSZ); ++ put_opt6_long(0); ++ put_opt6_long(0); ++ end_opt6(o1); ++ } ++ } ++ ++ if (made_ia) ++ { ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOBINDING); ++ put_opt6_string(_("no binding found")); ++ end_opt6(o1); ++ ++ end_opt6(o); ++ } ++ ++ } ++ ++ /* We must answer with 'success' in global section anyway */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6SUCCESS); ++ put_opt6_string(_("success")); ++ end_opt6(o1); ++ break; ++ } ++ ++ } ++ ++ log_tags(tagif, state->xid); ++ log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1)); ++ ++ return 1; ++ ++} ++ ++static struct dhcp_netid *add_options(struct state *state, int do_refresh) ++{ ++ void *oro; ++ /* filter options based on tags, those we want get DHOPT_TAGOK bit set */ ++ struct dhcp_netid *tagif = option_filter(state->tags, state->context_tags, daemon->dhcp_opts6); ++ struct dhcp_opt *opt_cfg; ++ int done_dns = 0, done_refresh = !do_refresh, do_encap = 0; ++ int i, o, o1; ++ ++ oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0); ++ ++ for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) ++ { ++ /* netids match and not encapsulated? */ ++ if (!(opt_cfg->flags & DHOPT_TAGOK)) ++ continue; ++ ++ if (!(opt_cfg->flags & DHOPT_FORCE) && oro) ++ { ++ for (i = 0; i < opt6_len(oro) - 1; i += 2) ++ if (opt6_uint(oro, i, 2) == (unsigned)opt_cfg->opt) ++ break; ++ ++ /* option not requested */ ++ if (i >= opt6_len(oro) - 1) ++ continue; ++ } ++ ++ if (opt_cfg->opt == OPTION6_REFRESH_TIME) ++ done_refresh = 1; ++ ++ if (opt_cfg->opt == OPTION6_DNS_SERVER) ++ done_dns = 1; ++ ++ if (opt_cfg->flags & DHOPT_ADDR6) ++ { ++ int len, j; ++ struct in6_addr *a; ++ ++ for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0; ++ j < opt_cfg->len; j += IN6ADDRSZ, a++) ++ if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) || ++ (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))) ++ len -= IN6ADDRSZ; ++ ++ if (len != 0) ++ { ++ ++ o = new_opt6(opt_cfg->opt); ++ ++ for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++) ++ { ++ struct in6_addr *p = NULL; ++ ++ if (IN6_IS_ADDR_UNSPECIFIED(a)) ++ { ++ if (!add_local_addrs(state->context)) ++ p = state->fallback; ++ } ++ else if (IN6_IS_ADDR_ULA_ZERO(a)) ++ { ++ if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ++ p = state->ula_addr; ++ } ++ else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a)) ++ { ++ if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)) ++ p = state->ll_addr; ++ } ++ else ++ p = a; ++ ++ if (!p) ++ continue; ++ else if (opt_cfg->opt == OPTION6_NTP_SERVER) ++ { ++ if (IN6_IS_ADDR_MULTICAST(p)) ++ o1 = new_opt6(NTP_SUBOPTION_MC_ADDR); ++ else ++ o1 = new_opt6(NTP_SUBOPTION_SRV_ADDR); ++ put_opt6(p, IN6ADDRSZ); ++ end_opt6(o1); ++ } ++ else ++ put_opt6(p, IN6ADDRSZ); + } - good_addr = 1; - log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); - } ++ ++ end_opt6(o); ++ } ++ } ++ else ++ { ++ o = new_opt6(opt_cfg->opt); ++ if (opt_cfg->val) ++ put_opt6(opt_cfg->val, opt_cfg->len); ++ end_opt6(o); ++ } ++ } ++ ++ if (daemon->port == NAMESERVER_PORT && !done_dns) ++ { ++ o = new_opt6(OPTION6_DNS_SERVER); ++ if (!add_local_addrs(state->context)) ++ put_opt6(state->fallback, IN6ADDRSZ); ++ end_opt6(o); ++ } ++ ++ if (state->context && !done_refresh) ++ { ++ struct dhcp_context *c; ++ unsigned int lease_time = 0xffffffff; ++ ++ /* Find the smallest lease tie of all contexts, ++ subject to the RFC-4242 stipulation that this must not ++ be less than 600. */ ++ for (c = state->context; c; c = c->next) ++ if (c->lease_time < lease_time) ++ { ++ if (c->lease_time < 600) ++ lease_time = 600; ++ else ++ lease_time = c->lease_time; ++ } ++ ++ o = new_opt6(OPTION6_REFRESH_TIME); ++ put_opt6_long(lease_time); ++ end_opt6(o); ++ } ++ ++ /* handle vendor-identifying vendor-encapsulated options, ++ dhcp-option = vi-encap:13,17,....... */ ++ for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) ++ opt_cfg->flags &= ~DHOPT_ENCAP_DONE; ++ ++ if (oro) ++ for (i = 0; i < opt6_len(oro) - 1; i += 2) ++ if (opt6_uint(oro, i, 2) == OPTION6_VENDOR_OPTS) ++ do_encap = 1; ++ ++ for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next) ++ { ++ if (opt_cfg->flags & DHOPT_RFC3925) ++ { ++ int found = 0; ++ struct dhcp_opt *oc; ++ ++ if (opt_cfg->flags & DHOPT_ENCAP_DONE) ++ continue; ++ ++ for (oc = daemon->dhcp_opts6; oc; oc = oc->next) ++ { ++ oc->flags &= ~DHOPT_ENCAP_MATCH; ++ ++ if (!(oc->flags & DHOPT_RFC3925) || opt_cfg->u.encap != oc->u.encap) ++ continue; ++ ++ oc->flags |= DHOPT_ENCAP_DONE; ++ if (match_netid(oc->netid, tagif, 1)) ++ { ++ /* option requested/forced? */ ++ if (!oro || do_encap || (oc->flags & DHOPT_FORCE)) ++ { ++ oc->flags |= DHOPT_ENCAP_MATCH; ++ found = 1; ++ } ++ } ++ } ++ ++ if (found) ++ { ++ o = new_opt6(OPTION6_VENDOR_OPTS); ++ put_opt6_long(opt_cfg->u.encap); ++ ++ for (oc = daemon->dhcp_opts6; oc; oc = oc->next) ++ if (oc->flags & DHOPT_ENCAP_MATCH) ++ { ++ o1 = new_opt6(oc->opt); ++ put_opt6(oc->val, oc->len); ++ end_opt6(o1); ++ } ++ end_opt6(o); ++ } ++ } ++ } ++ ++ ++ if (state->hostname) ++ { ++ unsigned char *p; ++ size_t len = strlen(state->hostname); ++ ++ if (state->send_domain) ++ len += strlen(state->send_domain) + 2; ++ ++ o = new_opt6(OPTION6_FQDN); ++ if ((p = expand(len + 2))) ++ { ++ *(p++) = state->fqdn_flags; ++ p = do_rfc1035_name(p, state->hostname, NULL); ++ if (state->send_domain) ++ { ++ p = do_rfc1035_name(p, state->send_domain, NULL); ++ *p = 0; ++ } ++ } ++ end_opt6(o); ++ } ++ ++ ++ /* logging */ ++ if (option_bool(OPT_LOG_OPTS) && oro) ++ { ++ char *q = daemon->namebuff; ++ for (i = 0; i < opt6_len(oro) - 1; i += 2) ++ { ++ char *s = option_string(AF_INET6, opt6_uint(oro, i, 2), NULL, 0, NULL, 0); ++ q += snprintf(q, MAXDNAME - (q - daemon->namebuff), ++ "%d%s%s%s", ++ opt6_uint(oro, i, 2), ++ strlen(s) != 0 ? ":" : "", ++ s, ++ (i > opt6_len(oro) - 3) ? "" : ", "); ++ if ( i > opt6_len(oro) - 3 || (q - daemon->namebuff) > 40) ++ { ++ q = daemon->namebuff; ++ my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), state->xid, daemon->namebuff); ++ } ++ } ++ } ++ ++ return tagif; ++} ++ ++static int add_local_addrs(struct dhcp_context *context) ++{ ++ int done = 0; ++ ++ for (; context; context = context->current) ++ if ((context->flags & CONTEXT_USED) && !IN6_IS_ADDR_UNSPECIFIED(&context->local6)) ++ { ++ /* squash duplicates */ ++ struct dhcp_context *c; ++ for (c = context->current; c; c = c->current) ++ if ((c->flags & CONTEXT_USED) && ++ IN6_ARE_ADDR_EQUAL(&context->local6, &c->local6)) ++ break; ++ ++ if (!c) ++ { ++ done = 1; ++ put_opt6(&context->local6, IN6ADDRSZ); ++ } ++ } ++ ++ return done; ++} ++ ++ ++static void get_context_tag(struct state *state, struct dhcp_context *context) ++{ ++ /* get tags from context if we've not used it before */ ++ if (context->netid.next == &context->netid && context->netid.net) ++ { ++ context->netid.next = state->context_tags; ++ state->context_tags = &context->netid; ++ if (!state->hostname_auth) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next) ++ if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0)) ++ break; ++ if (id_list) ++ state->hostname = NULL; ++ } ++ } ++} ++ ++static int check_ia(struct state *state, void *opt, void **endp, void **ia_option) ++{ ++ state->ia_type = opt6_type(opt); ++ *ia_option = NULL; ++ ++ if (state->ia_type != OPTION6_IA_NA && state->ia_type != OPTION6_IA_TA) ++ return 0; ++ ++ if (state->ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) ++ return 0; ++ ++ if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) ++ return 0; ++ ++ *endp = opt6_ptr(opt, opt6_len(opt)); ++ state->iaid = opt6_uint(opt, 0, 4); ++ *ia_option = opt6_find(opt6_ptr(opt, state->ia_type == OPTION6_IA_NA ? 12 : 4), *endp, OPTION6_IAADDR, 24); ++ ++ return 1; ++} ++ ++ ++static int build_ia(struct state *state, int *t1cntr) ++{ ++ int o = new_opt6(state->ia_type); ++ ++ put_opt6_long(state->iaid); ++ *t1cntr = 0; ++ ++ if (state->ia_type == OPTION6_IA_NA) ++ { ++ /* save pointer */ ++ *t1cntr = save_counter(-1); ++ /* so we can fill these in later */ ++ put_opt6_long(0); ++ put_opt6_long(0); ++ } ++ ++ return o; ++} ++ ++static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz) ++{ ++ if (t1cntr != 0) ++ { ++ /* go back and fill in fields in IA_NA option */ ++ int sav = save_counter(t1cntr); ++ unsigned int t1, t2, fuzz = 0; ++ ++ if (do_fuzz) ++ { ++ fuzz = rand16(); ++ ++ while (fuzz > (min_time/16)) ++ fuzz = fuzz/2; ++ } ++ ++ t1 = (min_time == 0xffffffff) ? 0xffffffff : min_time/2 - fuzz; ++ t2 = (min_time == 0xffffffff) ? 0xffffffff : ((min_time/8)*7) - fuzz; ++ put_opt6_long(t1); ++ put_opt6_long(t2); ++ save_counter(sav); ++ } ++} ++ ++static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, ++ unsigned int *min_time, struct in6_addr *addr, time_t now) ++{ ++ unsigned int valid_time = 0, preferred_time = 0; ++ int o = new_opt6(OPTION6_IAADDR); ++ struct dhcp_lease *lease; ++ ++ /* get client requested times */ ++ if (ia_option) ++ { ++ preferred_time = opt6_uint(ia_option, 16, 4); ++ valid_time = opt6_uint(ia_option, 20, 4); ++ } ++ ++ calculate_times(context, min_time, &valid_time, &preferred_time, lease_time); ++ ++ put_opt6(addr, sizeof(*addr)); ++ put_opt6_long(preferred_time); ++ put_opt6_long(valid_time); ++ end_opt6(o); ++ ++ if (state->lease_allocate) ++ update_leases(state, context, addr, valid_time, now); ++ ++ if ((lease = lease6_find_by_addr(addr, 128, 0))) ++ lease->flags |= LEASE_USED; ++ ++ /* get tags from context if we've not used it before */ ++ if (context->netid.next == &context->netid && context->netid.net) ++ { ++ context->netid.next = state->context_tags; ++ state->context_tags = &context->netid; ++ ++ if (!state->hostname_auth) ++ { ++ struct dhcp_netid_list *id_list; ++ ++ for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next) ++ if ((!id_list->list) || match_netid(id_list->list, &context->netid, 0)) ++ break; ++ if (id_list) ++ state->hostname = NULL; ++ } ++ } ++ ++ log6_quiet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname); ++ ++} ++ ++static void mark_context_used(struct state *state, struct in6_addr *addr) ++{ ++ struct dhcp_context *context; ++ ++ /* Mark that we have an address for this prefix. */ ++ for (context = state->context; context; context = context->current) ++ if (is_same_net6(addr, &context->start6, context->prefix)) ++ context->flags |= CONTEXT_USED; ++} ++ ++static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr) ++{ ++ for (; context; context = context->current) ++ if (is_same_net6(addr, &context->start6, context->prefix)) ++ context->flags |= CONTEXT_CONF_USED; ++} ++ ++/* make sure address not leased to another CLID/IAID */ ++static int check_address(struct state *state, struct in6_addr *addr) ++{ ++ struct dhcp_lease *lease; ++ ++ if (!(lease = lease6_find_by_addr(addr, 128, 0))) ++ return 1; ++ ++ if (lease->clid_len != state->clid_len || ++ memcmp(lease->clid, state->clid, state->clid_len) != 0 || ++ lease->iaid != state->iaid) ++ return 0; ++ ++ return 1; ++} ++ ++static int check_and_try_preempte_address(struct state *state, struct in6_addr *addr, time_t now, struct dhcp_config *config){ ++ struct dhcp_lease *lease; ++ ++ if (!(lease = lease6_find_by_addr(addr, 128, 0))) ++ { ++ return 1; ++ } ++ ++ if (daemon->bind_mac_with_ip6) { ++ // break rfc3315 here ++ // bind mac address with a lease ++ if ((state->mac) && !(config->flags & CONFIG_CLID) && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type)) { ++ lease_prune(lease, now); ++ return 1; ++ } ++ } ++ ++ // what rfc3315 do ++ if (lease->clid_len != state->clid_len || ++ memcmp(lease->clid, state->clid, state->clid_len) != 0 || ++ lease->iaid != state->iaid) ++ { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/* return true of *addr could have been generated from config. */ ++static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr) ++{ ++ int prefix; ++ struct in6_addr wild_addr; ++ struct addrlist *addr_list; ++ ++ if (!config || !(config->flags & CONFIG_ADDR6)) ++ return NULL; ++ ++ for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) ++ { ++ prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128; ++ wild_addr = addr_list->addr.addr6; ++ ++ if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64) ++ { ++ wild_addr = context->start6; ++ setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr6)); ++ } ++ else if (!is_same_net6(&context->start6, addr, context->prefix)) ++ continue; ++ ++ if (is_same_net6(&wild_addr, addr, prefix)) ++ return addr_list; ++ } ++ ++ return NULL; ++} ++ ++static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now) ++{ ++ u64 addrpart, i, addresses; ++ struct addrlist *addr_list; ++ ++ if (!config || !(config->flags & CONFIG_ADDR6)) ++ return 0; ++ ++ for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) ++ if (!(addr_list->flags & ADDRLIST_DECLINED) || ++ difftime(now, addr_list->decline_time) >= (float)DECLINE_BACKOFF) ++ { ++ addrpart = addr6part(&addr_list->addr.addr6); ++ addresses = 1; ++ ++ if (addr_list->flags & ADDRLIST_PREFIX) ++ addresses = (u64)1<<(128-addr_list->prefixlen); ++ ++ if ((addr_list->flags & ADDRLIST_WILDCARD)) ++ { ++ if (context->prefix != 64) ++ continue; ++ ++ *addr = context->start6; ++ } ++ else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix)) ++ *addr = addr_list->addr.addr6; ++ else ++ continue; ++ ++ for (i = 0 ; i < addresses; i++) ++ { ++ setaddr6part(addr, addrpart+i); ++ ++ if (check_address(state, addr)) ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Calculate valid and preferred times to send in leases/renewals. ++ ++ Inputs are: ++ ++ *valid_timep, *preferred_timep - requested times from IAADDR options. ++ context->valid, context->preferred - times associated with subnet address on local interface. ++ context->flags | CONTEXT_DEPRECATE - "deprecated" flag in dhcp-range. ++ lease_time - configured time for context for individual client. ++ *min_time - smallest valid time sent so far. ++ ++ Outputs are : ++ ++ *valid_timep, *preferred_timep - times to be send in IAADDR option. ++ *min_time - smallest valid time sent so far, to calculate T1 and T2. ++ ++ */ ++static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, ++ unsigned int *preferred_timep, unsigned int lease_time) ++{ ++ unsigned int req_preferred = *preferred_timep, req_valid = *valid_timep; ++ unsigned int valid_time = lease_time, preferred_time = lease_time; ++ ++ /* RFC 3315: "A server ignores the lifetimes set ++ by the client if the preferred lifetime is greater than the valid ++ lifetime. */ ++ if (req_preferred <= req_valid) ++ { ++ if (req_preferred != 0) ++ { ++ /* 0 == "no preference from client" */ ++ if (req_preferred < 120u) ++ req_preferred = 120u; /* sanity */ ++ ++ if (req_preferred < preferred_time) ++ preferred_time = req_preferred; ++ } ++ ++ if (req_valid != 0) ++ /* 0 == "no preference from client" */ ++ { ++ if (req_valid < 120u) ++ req_valid = 120u; /* sanity */ ++ ++ if (req_valid < valid_time) ++ valid_time = req_valid; ++ } ++ } ++ ++ /* deprecate (preferred == 0) which configured, or when local address ++ is deprecated */ ++ if ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0) ++ preferred_time = 0; ++ ++ if (preferred_time != 0 && preferred_time < *min_time) ++ *min_time = preferred_time; ++ ++ if (valid_time != 0 && valid_time < *min_time) ++ *min_time = valid_time; ++ ++ *valid_timep = valid_time; ++ *preferred_timep = preferred_time; ++} ++ ++static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now) ++{ ++ struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0); ++#ifdef HAVE_SCRIPT ++ struct dhcp_netid *tagif = run_tag_if(state->tags); ++#endif ++ ++ (void)context; ++ ++ if (!lease) ++ lease = lease6_allocate(addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA); ++ ++ if (lease) ++ { ++ lease_set_expires(lease, lease_time, now); ++ lease_set_iaid(lease, state->iaid); ++ lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0); ++ lease_set_interface(lease, state->interface, now); ++ if (state->hostname && state->ia_type == OPTION6_IA_NA) ++ { ++ char *addr_domain = get_domain6(addr); ++ if (!state->send_domain) ++ state->send_domain = addr_domain; ++ lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); ++ } ++ ++#ifdef HAVE_SCRIPT ++ if (daemon->lease_change_command) ++ { ++ void *class_opt; ++ lease->flags |= LEASE_CHANGED; ++ free(lease->extradata); ++ lease->extradata = NULL; ++ lease->extradata_size = lease->extradata_len = 0; ++ lease->vendorclass_count = 0; ++ ++ if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4))) ++ { ++ void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); ++ lease->vendorclass_count++; ++ /* send enterprise number first */ ++ sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4)); ++ lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0); ++ ++ if (opt6_len(class_opt) >= 6) ++ for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) ++ { ++ lease->vendorclass_count++; ++ lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); ++ } ++ } ++ ++ lease_add_extradata(lease, (unsigned char *)state->client_hostname, ++ state->client_hostname ? strlen(state->client_hostname) : 0, 0); ++ ++ /* space-concat tag set */ ++ if (!tagif && !context->netid.net) ++ lease_add_extradata(lease, NULL, 0, 0); ++ else ++ { ++ if (context->netid.net) ++ lease_add_extradata(lease, (unsigned char *)context->netid.net, strlen(context->netid.net), tagif ? ' ' : 0); ++ ++ if (tagif) ++ { ++ struct dhcp_netid *n; ++ for (n = tagif; n; n = n->next) ++ { ++ struct dhcp_netid *n1; ++ /* kill dupes */ ++ for (n1 = n->next; n1; n1 = n1->next) ++ if (strcmp(n->net, n1->net) == 0) ++ break; ++ if (!n1) ++ lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0); ++ } ++ } ++ } ++ ++ if (state->link_address) ++ inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); ++ ++ lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0); ++ ++ if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2))) ++ { ++ void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); ++ for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end)) ++ lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0); ++ } ++ } ++#endif ++ ++ } ++} ++ ++ ++ ++static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts) ++{ ++ void *opt; ++ char *desc = nest ? "nest" : "sent"; ++ ++ if (!option_bool(OPT_LOG_OPTS) || start_opts == end_opts) ++ return; ++ ++ for (opt = start_opts; opt; opt = opt6_next(opt, end_opts)) ++ { ++ int type = opt6_type(opt); ++ void *ia_options = NULL; ++ char *optname; ++ ++ if (type == OPTION6_IA_NA) ++ { ++ sprintf(daemon->namebuff, "IAID=%u T1=%u T2=%u", ++ opt6_uint(opt, 0, 4), opt6_uint(opt, 4, 4), opt6_uint(opt, 8, 4)); ++ optname = "ia-na"; ++ ia_options = opt6_ptr(opt, 12); ++ } ++ else if (type == OPTION6_IA_TA) ++ { ++ sprintf(daemon->namebuff, "IAID=%u", opt6_uint(opt, 0, 4)); ++ optname = "ia-ta"; ++ ia_options = opt6_ptr(opt, 4); ++ } ++ else if (type == OPTION6_IAADDR) ++ { ++ struct in6_addr addr; ++ ++ /* align */ ++ memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ); ++ inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN); ++ sprintf(daemon->namebuff, "%s PL=%u VL=%u", ++ daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4)); ++ optname = "iaaddr"; ++ ia_options = opt6_ptr(opt, 24); ++ } ++ else if (type == OPTION6_STATUS_CODE) ++ { ++ int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2)); ++ memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2); ++ daemon->namebuff[len + opt6_len(opt) - 2] = 0; ++ optname = "status"; ++ } ++ else ++ { ++ /* account for flag byte on FQDN */ ++ int offset = type == OPTION6_FQDN ? 1 : 0; ++ optname = option_string(AF_INET6, type, opt6_ptr(opt, offset), opt6_len(opt) - offset, daemon->namebuff, MAXDNAME); ++ } ++ ++ my_syslog(MS_DHCP | LOG_INFO, "%u %s size:%3d option:%3d %s %s", ++ xid, desc, opt6_len(opt), type, optname, daemon->namebuff); ++ ++ if (ia_options) ++ log6_opts(1, xid, ia_options, opt6_ptr(opt, opt6_len(opt))); ++ } ++} ++ ++static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string) ++{ ++ if (option_bool(OPT_LOG_OPTS) || !option_bool(OPT_QUIET_DHCP6)) ++ log6_packet(state, type, addr, string); ++} ++ ++static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string) ++{ ++ int clid_len = state->clid_len; ++ ++ /* avoid buffer overflow */ ++ if (clid_len > 100) ++ clid_len = 100; ++ ++ print_mac(daemon->namebuff, state->clid, clid_len); ++ ++ if (addr) ++ { ++ inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1); ++ strcat(daemon->dhcp_buff2, " "); ++ } ++ else ++ daemon->dhcp_buff2[0] = 0; ++ ++ if(option_bool(OPT_LOG_OPTS)) ++ my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s %s", ++ state->xid, ++ type, ++ state->iface_name, ++ daemon->dhcp_buff2, ++ daemon->namebuff, ++ string ? string : ""); ++ else ++ my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s", ++ type, ++ state->iface_name, ++ daemon->dhcp_buff2, ++ daemon->namebuff, ++ string ? string : ""); ++} ++ ++static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize) ++{ ++ u16 opt, opt_len; ++ void *start; ++ ++ if (!opts) ++ return NULL; ++ ++ while (1) ++ { ++ if (end - opts < 4) ++ return NULL; ++ ++ start = opts; ++ GETSHORT(opt, opts); ++ GETSHORT(opt_len, opts); ++ ++ if (opt_len > (end - opts)) ++ return NULL; ++ ++ if (opt == search && (opt_len >= minsize)) ++ return start; ++ ++ opts += opt_len; ++ } ++} ++ ++static void *opt6_next(void *opts, void *end) ++{ ++ u16 opt_len; ++ ++ if (end - opts < 4) ++ return NULL; ++ ++ opts += 2; ++ GETSHORT(opt_len, opts); ++ ++ if (opt_len >= (end - opts)) ++ return NULL; ++ ++ return opts + opt_len; ++} ++ ++static unsigned int opt6_uint(unsigned char *opt, int offset, int size) ++{ ++ /* this worries about unaligned data and byte order */ ++ unsigned int ret = 0; ++ int i; ++ unsigned char *p = opt6_ptr(opt, offset); ++ ++ for (i = 0; i < size; i++) ++ ret = (ret << 8) | *p++; ++ ++ return ret; ++} ++ ++void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, ++ struct in6_addr *peer_address, u32 scope_id, time_t now) ++{ ++ /* ->local is same value for all relays on ->current chain */ ++ ++ union all_addr from; ++ unsigned char *header; ++ unsigned char *inbuff = daemon->dhcp_packet.iov_base; ++ int msg_type = *inbuff; ++ int hopcount; ++ struct in6_addr multicast; ++ unsigned int maclen, mactype; ++ unsigned char mac[DHCP_CHADDR_MAX]; ++ ++ inet_pton(AF_INET6, ALL_SERVERS, &multicast); ++ get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now); ++ ++ /* source address == relay address */ ++ from.addr6 = relay->local.addr6; ++ ++ /* Get hop count from nested relayed message */ ++ if (msg_type == DHCP6RELAYFORW) ++ hopcount = *((unsigned char *)inbuff+1) + 1; ++ else ++ hopcount = 0; ++ ++ /* RFC 3315 HOP_COUNT_LIMIT */ ++ if (hopcount > 32) ++ return; ++ ++ reset_counter(); ++ ++ if ((header = put_opt6(NULL, 34))) ++ { ++ int o; ++ ++ header[0] = DHCP6RELAYFORW; ++ header[1] = hopcount; ++ memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ); ++ memcpy(&header[18], peer_address, IN6ADDRSZ); ++ ++ /* RFC-6939 */ ++ if (maclen != 0) ++ { ++ o = new_opt6(OPTION6_CLIENT_MAC); ++ put_opt6_short(mactype); ++ put_opt6(mac, maclen); ++ end_opt6(o); ++ } ++ ++ o = new_opt6(OPTION6_RELAY_MSG); ++ put_opt6(inbuff, sz); ++ end_opt6(o); ++ ++ for (; relay; relay = relay->current) ++ { ++ union mysockaddr to; ++ ++ to.sa.sa_family = AF_INET6; ++ to.in6.sin6_addr = relay->server.addr6; ++ to.in6.sin6_port = htons(DHCPV6_SERVER_PORT); ++ to.in6.sin6_flowinfo = 0; ++ to.in6.sin6_scope_id = 0; ++ ++ if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) ++ { ++ int multicast_iface; ++ if (!relay->interface || strchr(relay->interface, '*') || ++ (multicast_iface = if_nametoindex(relay->interface)) == 0 || ++ setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1) ++ my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface")); ++ } ++ ++ send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0); ++ ++ if (option_bool(OPT_LOG_OPTS)) ++ { ++ inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); ++ inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); ++ my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff); ++ } ++ ++ /* Save this for replies */ ++ relay->iface_index = scope_id; ++ } ++ } ++} ++ ++unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) ++{ ++ struct dhcp_relay *relay; ++ struct in6_addr link; ++ unsigned char *inbuff = daemon->dhcp_packet.iov_base; ++ ++ /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option ++ which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */ ++ ++ if (sz < 38 || *inbuff != DHCP6RELAYREPL) ++ return 0; ++ ++ memcpy(&link, &inbuff[2], IN6ADDRSZ); ++ ++ for (relay = daemon->relay6; relay; relay = relay->next) ++ if (IN6_ARE_ADDR_EQUAL(&link, &relay->local.addr6) && ++ (!relay->interface || wildcard_match(relay->interface, arrival_interface))) ++ break; ++ ++ reset_counter(); ++ ++ if (relay) ++ { ++ void *opt, *opts = inbuff + 34; ++ void *end = inbuff + sz; ++ for (opt = opts; opt; opt = opt6_next(opt, end)) ++ if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0) ++ { ++ int encap_type = *((unsigned char *)opt6_ptr(opt, 0)); ++ put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); ++ memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ); ++ peer->sin6_scope_id = relay->iface_index; ++ return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; ++ } ++ } ++ ++ return 0; ++} ++ ++#endif -- -2.23.0 +1.8.3.1 diff --git a/dnsmasq-2.77-underflow.patch b/dnsmasq-2.77-underflow.patch index 654208e448561513860d9f20f0d57317385ccccb..743f059ef29958a432294b55678a688344229c50 100644 --- a/dnsmasq-2.77-underflow.patch +++ b/dnsmasq-2.77-underflow.patch @@ -1,41 +1,26 @@ -From 8fbce6094dc3acad11c464723d97d3215951c5d4 Mon Sep 17 00:00:00 2001 -From: liaichun -Date: Mon, 20 Apr 2020 15:26:17 +0800 -Subject: [PATCH] dnsmasq-2.77-underflow +From 4d3ef152a5d0a3cf053d41ea950823bfcc0ea5c2 Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 10:32:50 +0800 +Subject: [PATCH] underflow --- - src/forward.c | 3 +++ src/rfc1035.c | 3 +++ - 2 files changed, 6 insertions(+) + 1 file changed, 3 insertions(+) -diff --git a/src/forward.c b/src/forward.c -index fde554d..47ccd91 100644 ---- a/src/forward.c -+++ b/src/forward.c -@@ -1546,6 +1546,9 @@ void receive_query(struct listener *listen, time_t now) - else if (udp_size < PACKETSZ) - udp_size = PACKETSZ; /* Sanity check - can't reduce below default. RFC 6891 6.2.3 */ - } -+ // Make sure the udp size is not smaller than the incoming message so that we -+ // do not underflow -+ if (udp_size < n) udp_size = n; - - #ifdef HAVE_AUTH - if (auth_dns) diff --git a/src/rfc1035.c b/src/rfc1035.c -index fefe63d..6253794 100644 +index fefe63d..d3cce6f 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -1340,6 +1340,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, size_t len; int rd_bit = (header->hb3 & HB3_RD); -+ // Make sure we do not underflow here too. ++ // Make sure we do not underflow here too + if (qlen > (limit - ((char *)header))) return 0; + /* never answer queries with RD unset, to avoid cache snooping. */ if (ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0 || -- -2.23.0 +1.8.3.1 diff --git a/dnsmasq-2.78-fips.patch b/dnsmasq-2.78-fips.patch index cdda9562cca49969b81ea75b2375f8d4d446f0e7..6454b95b8b53c77e1963856585d06fe89ded5d0a 100644 --- a/dnsmasq-2.78-fips.patch +++ b/dnsmasq-2.78-fips.patch @@ -1,28 +1,26 @@ -From 7d77bd1038de810c3f916d2117742b0d8fa2429c Mon Sep 17 00:00:00 2001 -From: liaichun -Date: Mon, 20 Apr 2020 15:32:27 +0800 -Subject: [PATCH] dnsmasq-2.78-fips +From 8c8ca24806d5ebfe5018279ec84538a17014a918 Mon Sep 17 00:00:00 2001 +From: xiaoweiwei +Date: Tue, 28 Jul 2020 10:57:56 +0800 +Subject: [PATCH] fips --- - src/dnsmasq.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) + src/dnsmasq.c | 3 +++ + 1 file changed, 3 insertions(+) diff --git a/src/dnsmasq.c b/src/dnsmasq.c -index 878167c..fc085eb 100644 +index 2306c48..bfad87f 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c -@@ -876,7 +876,10 @@ int main (int argc, char **argv) +@@ -877,6 +877,9 @@ int main (int argc, char **argv) my_syslog(LOG_INFO, _("DNSSEC validation enabled but all unsigned answers are trusted")); else my_syslog(LOG_INFO, _("DNSSEC validation enabled")); -- + + if (access("/etc/system-fips", F_OK) == 0) + my_syslog(LOG_WARNING, _("DNSSEC support is not FIPS 140-2 compliant")); -+ + daemon->dnssec_no_time_check = option_bool(OPT_DNSSEC_TIME); if (option_bool(OPT_DNSSEC_TIME) && !daemon->back_to_the_future) - my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until receipt of SIGINT")); -- -2.23.0 +1.8.3.1 diff --git a/dnsmasq-2.81.tar.xz b/dnsmasq-2.81.tar.xz deleted file mode 100644 index 8e4453891f44889d3c28fc105b880d92ee6764ef..0000000000000000000000000000000000000000 Binary files a/dnsmasq-2.81.tar.xz and /dev/null differ diff --git a/dnsmasq-2.82.tar.xz b/dnsmasq-2.82.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..7b6a4ca7a55dadf0f04e73165b330d685abc302e Binary files /dev/null and b/dnsmasq-2.82.tar.xz differ diff --git a/dnsmasq.spec b/dnsmasq.spec index a0337a42aafb583b632ca6ac6f0baa1a8cd2825d..2dd55e0a3f4e184c2aaa25e57e25bf0b4a79d88a 100644 --- a/dnsmasq.spec +++ b/dnsmasq.spec @@ -1,5 +1,5 @@ Name: dnsmasq -Version: 2.81 +Version: 2.82 Release: 1 Summary: Dnsmasq provides network infrastructure for small networks License: GPLv2 or GPLv3 @@ -8,7 +8,6 @@ Source0: http://www.thekelleys.org.uk/dnsmasq/%{name}-%{version}.tar.xz Source1: dnsmasq.service Source2: dnsmasq-systemd-sysusers.conf -#patches from the opensource fedora/redhat repository Patch0001: dnsmasq-2.77-underflow.patch Patch0002: dnsmasq-2.78-fips.patch @@ -107,6 +106,12 @@ install -Dpm644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysusersdir}/dnsmasq.conf %{_mandir}/man8/dnsmasq* %changelog +* Tue Aug 25 2020 lunankun - 2.82-1 +- Type:requirement +- Id:NA +- SUG:NA +- DESC:update to 2.82-1 + * Fri Apr 17 2020 openEuler Buildteam - 2.80-1 - Type:bugfix - Id:NA