blob: d0b43481d9687a3fedcc543211bdffcb5296ad80 [file] [log] [blame]
#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;
}