Rebase Cradlepoint diff over v2.90

Run these commands only if remote upstream is not added:
  1. git remote add upstream http://thekelleys.org.uk/git/dnsmasq.git
  2. git remote -v show
  3. git fetch upstream

Following are the commands used for generating the diff
between upstream version 2.85 and origin/cp-main:
  1. git diff v2.85 origin/cp-main > ../dnsmasq_v2.85-to-cp_main.gitdiff
  2. patch -p1 < ../dnsmasq_v2.85-to-cp_main.gitdiff
  3. git add . && git commit -m "Rebase Cradlepoint diff over v2.90"
And then fix all the rejects generated while applying ‘patch -p1 < ../dnsmasq_v2.85-to-cp_main.gitdiff’

EDNS_PKTSZ and SAFE_PKTSZ were already present due to backport of CVE
patch:
    Fix up ./man/dnsmasq.8
    Fix up ./src/config.h
    Fix up ./CHANGELOG

As domain is no longer used in upstream, we removed the check for OPT_EDNS_RESTRICT:
    Fix up ./src/forward.c

Fix up ./src/dhcp.c
Fix up ./src/dhcp6.c
Fix up ./src/dns-protocol.h
Fix up ./src/dnsmasq.c
Fix up ./src/dnsmasq.h
Fix up ./src/edns0.c
Fix up ./src/option.c
Fix up ./src/radv.c
Fix up ./src/rfc1035.c
Fix up ./src/util.c
Fix up ./Makefile
Compilation fixes for cache.c

Change-Id: Ibb8d162526168e5963aedb98b6333ded5f054a82
diff --git a/src/opendns.c b/src/opendns.c
new file mode 100644
index 0000000..d0b4348
--- /dev/null
+++ b/src/opendns.c
@@ -0,0 +1,231 @@
+
+#include "dnsmasq.h"
+#include "opendns.h"
+
+const unsigned char *
+opendns_ssid_to_device_id(size_t * const device_id_len_p,
+                          const unsigned char * const ssid,
+                          const size_t ssid_len)
+{
+    unsigned char *map = daemon->ssid_device_id_map.map;
+    unsigned char *map_tmp;
+    unsigned char *ssid_tmp;
+    unsigned char *device_id_tmp;
+    size_t         ssid_tmp_len;
+    size_t         device_id_len;
+
+    if (map == NULL || ssid == NULL) {
+        return NULL;
+    }
+    map_tmp = map;
+    while ((ssid_tmp_len = (size_t) *map_tmp) > 0U) {
+        ssid_tmp = ++map_tmp;
+        map_tmp += ssid_tmp_len;
+        device_id_len = (size_t) *map_tmp;
+        device_id_tmp = ++map_tmp;
+        if (ssid_tmp_len == ssid_len &&
+            memcmp(ssid_tmp, ssid, ssid_len) == 0) {
+            *device_id_len_p = device_id_len;
+            return device_id_tmp;
+        }
+        map_tmp += device_id_len;
+    }
+    return NULL;
+}
+
+static int
+opendns_parse_hex_char(unsigned char * const device_id,
+                       size_t * const device_id_pos_p, int * const state_p,
+                       unsigned char * const val_p, size_t device_id_len_max,
+                       const int c)
+{
+    unsigned char c_val;
+
+    switch (*state_p) {
+    case 0:
+    case 1:
+        if (isspace(c) || (c == ':' && *state_p == 0)) {
+            break;
+        }
+        if (c == '#') {
+            *state_p = 2;
+            break;
+        }
+        if (!isxdigit(c)) {
+            return -1;
+        }
+        c_val = (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10;
+        if (*state_p == 0) {
+            *val_p = c_val * 16U;
+            *state_p = 1;
+        } else {
+            *val_p |= c_val;
+            device_id[(*device_id_pos_p)++] = *val_p;
+            if (*device_id_pos_p >= device_id_len_max) {
+                return 0;
+            }
+            *state_p = 0;
+        }
+        break;
+    case 2:
+        if (c == '\n') {
+            *state_p = 0;
+        }
+    }
+    return 1;
+}
+
+size_t
+opendns_parse_hex(const char * const device_id_hex,
+                  unsigned char * const device_id,
+                  const size_t device_id_len_max)
+{
+    const char   *p = device_id_hex;
+    size_t        device_id_pos = (size_t) 0U;
+    int           c;
+    int           ret;
+    int           state = 0;
+    unsigned char val = 0U;
+
+    if (device_id_hex == NULL) {
+        return (size_t) 0U;
+    }
+    while ((c = tolower((int) (unsigned char) *p)) != 0) {
+        ret = opendns_parse_hex_char(device_id, &device_id_pos,
+                                     &state, &val, device_id_len_max, c);
+        if (ret < 0) {
+            return (size_t) 0U;
+        }
+        if (ret == 0) {
+            break;
+        }
+        p++;
+    }
+    return device_id_pos;
+}
+
+char *
+opendns_parse_device_id_opt(char * const arg)
+{
+    struct ssid_device_id_map * const ssid_device_id_map =
+        &daemon->ssid_device_id_map;
+    unsigned char *device_id;
+    unsigned char *tmp_map;
+    char          *device_id_hex;
+    char          *sep;
+    char          *ssid;
+    size_t         device_id_hex_len;
+    size_t         device_id_len;
+    size_t         device_id_len_max;
+    size_t         pos;
+    size_t         ssid_len;
+    size_t         tmp_map_size;
+
+    if ((sep = strrchr(arg, ',')) == NULL ||
+        *(device_id_hex = sep + 1) == 0) {
+        return _("missing device id");
+    }
+    *sep = 0;
+    if (*(ssid = arg) == 0) {
+        return _("missing SSID");
+    }
+    ssid_len = (size_t) (sep - ssid);
+    if (ssid_len > 255U) {
+        return _("SSID too long");
+    }
+    device_id_hex_len = strlen(device_id_hex);
+    device_id_len_max = (device_id_hex_len + 1U) / 2U;
+    device_id = safe_malloc(device_id_len_max);
+    if (device_id_len_max > 255U) {
+        free(device_id);
+        return _("device id too long");
+    }
+    device_id_len = opendns_parse_hex(device_id_hex, device_id,
+                                      device_id_len_max);
+    if (device_id_len <= 0U) {
+        free(device_id);
+        return _("unable to parse a hex device id");
+    }
+    if (device_id_len > device_id_len_max) {
+        free(device_id);
+        return _("parsed device id too long");
+    }
+    tmp_map_size = ssid_device_id_map->map_size + 1U +
+        ssid_len + 1U + device_id_len;
+    if (ssid_device_id_map->map == NULL) {
+        tmp_map_size++;
+    }
+    if ((tmp_map = realloc(ssid_device_id_map->map,
+                           tmp_map_size)) == NULL) {
+        die(_("could not get memory"), NULL, EC_NOMEM);
+    }
+    if (ssid_device_id_map->map_size <= 0U) {
+        pos = 0U;
+    } else {
+        pos = ssid_device_id_map->map_size - 1U;
+    }
+    tmp_map[pos++] = (unsigned char) ssid_len;
+    memcpy(&tmp_map[pos], ssid, ssid_len);
+    pos += ssid_len;
+    tmp_map[pos++] = (unsigned char) device_id_len;
+    memcpy(&tmp_map[pos], device_id, device_id_len);
+    pos += device_id_len;
+    free(device_id);
+    tmp_map[pos] = 0U;
+    ssid_device_id_map->map = tmp_map;
+    ssid_device_id_map->map_size = tmp_map_size;
+
+    return NULL;
+}
+
+int
+opendns_pop_tag_from_query(unsigned char * const packet,
+                           size_t * const packet_size_p,
+                           unsigned char * * const edns_options,
+                           size_t *edns_options_len_p)
+{
+    static unsigned char edns_options_tpl[2U + 2U + 7U + 255U] = {
+        0U, 4U, 0U, 0U, 'O', 'p', 'e', 'n', 'D', 'N', 'S'
+    };
+    const unsigned char *device_id;
+    const unsigned char *tag;
+    unsigned char       *tmp;
+    size_t               edns_options_len;
+    size_t               device_id_len;
+    size_t               packet_size = *packet_size_p;
+    size_t               tag_len;
+
+    if (packet_size <= 0U) {
+        *edns_options = NULL;
+        *edns_options_len_p = 0U;
+        return -1;
+    }
+    tag_len = (size_t) packet[packet_size - 1U];
+    if (tag_len >= packet_size) {
+        *edns_options = NULL;
+        *edns_options_len_p = 0U;
+        return -1;
+    }
+    tag = &packet[packet_size - tag_len - 1U];
+    if ((device_id = opendns_ssid_to_device_id(&device_id_len,
+                                                 tag, tag_len)) == NULL) {
+        return -1;
+    }
+    edns_options_len = 2U + 2U + sizeof "OpenDNS" - 1U + device_id_len;
+    if (edns_options_len > sizeof edns_options_tpl) {
+        return -1;
+    }
+    memcpy(edns_options_tpl + 2U + 2U + sizeof "OpenDNS" - 1U,
+           device_id, device_id_len);
+    if (packet_size <= 1U + device_id_len) {
+        return -1;
+    }
+    tmp = edns_options_tpl + 2U;
+    PUTSHORT(sizeof "OpenDNS" - 1U + device_id_len, tmp);
+    *edns_options = edns_options_tpl;
+    *edns_options_len_p = edns_options_len;
+    packet_size -= 1U + tag_len;
+    *packet_size_p = packet_size;
+
+    return 0;
+}