version 1.1.1.2, 2021/03/17 00:56:46
|
version 1.1.1.3, 2023/09/27 11:02:07
|
Line 1
|
Line 1
|
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley |
|
|
This program is free software; you can redistribute it and/or modify |
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 |
it under the terms of the GNU General Public License as published by |
Line 178 size_t add_pseudoheader(struct dns_header *header, siz
|
Line 178 size_t add_pseudoheader(struct dns_header *header, siz
|
memcpy(buff, datap, rdlen); |
memcpy(buff, datap, rdlen); |
|
|
/* now, delete OPT RR */ |
/* now, delete OPT RR */ |
plen = rrfilter(header, plen, 0); | plen = rrfilter(header, plen, RRFILTER_EDNS0); |
|
|
/* Now, force addition of a new one */ |
/* Now, force addition of a new one */ |
p = NULL; |
p = NULL; |
Line 264 static void encoder(unsigned char *in, char *out)
|
Line 264 static void encoder(unsigned char *in, char *out)
|
out[3] = char64(in[2]); |
out[3] = char64(in[2]); |
} |
} |
|
|
|
/* OPT_ADD_MAC = MAC is added (if available) |
|
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed |
|
OPT_STRIP_MAC = MAC is removed */ |
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, |
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, |
union mysockaddr *l3, time_t now, int *cacheablep) |
union mysockaddr *l3, time_t now, int *cacheablep) |
{ |
{ |
int maclen, replace = 2; /* can't get mac address, just delete any incoming. */ | int replace = 0, maclen = 0; |
unsigned char mac[DHCP_CHADDR_MAX]; |
unsigned char mac[DHCP_CHADDR_MAX]; |
char encode[18]; /* handle 6 byte MACs */ | char encode[18]; /* handle 6 byte MACs ONLY */ |
|
|
if ((maclen = find_mac(l3, mac, 1, now)) == 6) | if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6) |
{ |
{ |
replace = 1; | if (option_bool(OPT_STRIP_MAC)) |
*cacheablep = 0; | replace = 1; |
| *cacheablep = 0; |
if (option_bool(OPT_MAC_HEX)) | |
print_mac(encode, mac, maclen); | if (option_bool(OPT_MAC_HEX)) |
else | print_mac(encode, mac, maclen); |
{ | else |
encoder(mac, encode); | { |
encoder(mac+3, encode+4); | encoder(mac, encode); |
encode[8] = 0; | encoder(mac+3, encode+4); |
} | encode[8] = 0; |
| } |
} |
} |
|
else if (option_bool(OPT_STRIP_MAC)) |
|
replace = 2; |
|
|
return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace); | if (replace != 0 || maclen == 6) |
| plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace); |
| |
| return plen; |
} |
} |
|
|
|
|
|
/* OPT_ADD_MAC = MAC is added (if available) |
|
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed |
|
OPT_STRIP_MAC = MAC is removed */ |
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, |
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, |
union mysockaddr *l3, time_t now, int *cacheablep) |
union mysockaddr *l3, time_t now, int *cacheablep) |
{ |
{ |
int maclen; | int maclen = 0, replace = 0; |
unsigned char mac[DHCP_CHADDR_MAX]; |
unsigned char mac[DHCP_CHADDR_MAX]; |
| |
if ((maclen = find_mac(l3, mac, 1, now)) != 0) | if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0) |
{ |
{ |
*cacheablep = 0; |
*cacheablep = 0; |
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0); | if (option_bool(OPT_STRIP_MAC)) |
| replace = 1; |
} |
} |
|
else if (option_bool(OPT_STRIP_MAC)) |
|
replace = 2; |
|
|
|
if (replace != 0 || maclen != 0) |
|
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace); |
|
|
return plen; |
return plen; |
} |
} |
|
|
Line 378 static size_t calc_subnet_opt(struct subnet_opt *opt,
|
Line 396 static size_t calc_subnet_opt(struct subnet_opt *opt,
|
return len + 4; |
return len + 4; |
} |
} |
|
|
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable) | /* OPT_CLIENT_SUBNET = client subnet is added |
| OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced |
| OPT_STRIP_ECS = client subnet is removed */ |
| static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, |
| union mysockaddr *source, int *cacheable) |
{ |
{ |
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ |
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ |
|
|
int len; | int replace = 0, len = 0; |
struct subnet_opt opt; |
struct subnet_opt opt; |
|
|
len = calc_subnet_opt(&opt, source, cacheable); | if (option_bool(OPT_CLIENT_SUBNET)) |
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0); | { |
| if (option_bool(OPT_STRIP_ECS)) |
| replace = 1; |
| len = calc_subnet_opt(&opt, source, cacheable); |
| } |
| else if (option_bool(OPT_STRIP_ECS)) |
| replace = 2; |
| else |
| return plen; |
| |
| return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace); |
} |
} |
|
|
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer) |
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer) |
Line 427 int check_source(struct dns_header *header, size_t ple
|
Line 459 int check_source(struct dns_header *header, size_t ple
|
return 1; |
return 1; |
} |
} |
|
|
|
/* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for |
|
* detailed information on packet formating. |
|
*/ |
|
#define UMBRELLA_VERSION 1 |
|
#define UMBRELLA_TYPESZ 2 |
|
|
|
#define UMBRELLA_ASSET 0x0004 |
|
#define UMBRELLA_ASSETSZ sizeof(daemon->umbrella_asset) |
|
#define UMBRELLA_ORG 0x0008 |
|
#define UMBRELLA_ORGSZ sizeof(daemon->umbrella_org) |
|
#define UMBRELLA_IPV4 0x0010 |
|
#define UMBRELLA_IPV6 0x0020 |
|
#define UMBRELLA_DEVICE 0x0040 |
|
#define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device) |
|
|
|
struct umbrella_opt { |
|
u8 magic[4]; |
|
u8 version; |
|
u8 flags; |
|
/* We have 4 possible fields since we'll never send both IPv4 and |
|
* IPv6, so using the larger of the two to calculate max buffer size. |
|
* Each field also has a type header. So the following accounts for |
|
* the type headers and each field size to get a max buffer size. |
|
*/ |
|
u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ]; |
|
}; |
|
|
|
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable) |
|
{ |
|
*cacheable = 0; |
|
|
|
struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}}; |
|
u8 *u = &opt.fields[0]; |
|
int family = source->sa.sa_family; |
|
int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ; |
|
|
|
if (daemon->umbrella_org) |
|
{ |
|
PUTSHORT(UMBRELLA_ORG, u); |
|
PUTLONG(daemon->umbrella_org, u); |
|
} |
|
|
|
PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u); |
|
memcpy(u, get_addrp(source, family), size); |
|
u += size; |
|
|
|
if (option_bool(OPT_UMBRELLA_DEVID)) |
|
{ |
|
PUTSHORT(UMBRELLA_DEVICE, u); |
|
memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ); |
|
u += UMBRELLA_DEVICESZ; |
|
} |
|
|
|
if (daemon->umbrella_asset) |
|
{ |
|
PUTSHORT(UMBRELLA_ASSET, u); |
|
PUTLONG(daemon->umbrella_asset, u); |
|
} |
|
|
|
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1); |
|
} |
|
|
/* Set *check_subnet if we add a client subnet option, which needs to checked |
/* Set *check_subnet if we add a client subnet option, which needs to checked |
in the reply. Set *cacheable to zero if we add an option which the answer |
in the reply. Set *cacheable to zero if we add an option which the answer |
may depend on. */ |
may depend on. */ |
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, |
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, |
union mysockaddr *source, time_t now, int *check_subnet, int *cacheable) | union mysockaddr *source, time_t now, int *cacheable) |
{ |
{ |
*check_subnet = 0; |
|
*cacheable = 1; |
*cacheable = 1; |
|
|
if (option_bool(OPT_ADD_MAC)) | plen = add_mac(header, plen, limit, source, now, cacheable); |
plen = add_mac(header, plen, limit, source, now, cacheable); | plen = add_dns_client(header, plen, limit, source, now, cacheable); |
|
|
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) |
|
plen = add_dns_client(header, plen, limit, source, now, cacheable); |
|
|
|
if (daemon->dns_client_id) |
if (daemon->dns_client_id) |
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, |
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, |
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1); |
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1); |
|
|
|
if (option_bool(OPT_UMBRELLA)) |
|
plen = add_umbrella_opt(header, plen, limit, source, cacheable); |
|
|
if (option_bool(OPT_CLIENT_SUBNET)) | plen = add_source_addr(header, plen, limit, source, cacheable); |
{ | |
plen = add_source_addr(header, plen, limit, source, cacheable); | |
*check_subnet = 1; | |
} | |
| |
return plen; |
return plen; |
} |
} |