2022-06-06 13:08:05 +00:00
|
|
|
#include "links.h"
|
|
|
|
|
|
|
|
struct dnsquery_addr {
|
|
|
|
struct status stat;
|
|
|
|
unsigned char *url;
|
|
|
|
int redirect_cnt;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct dnsquery_doh {
|
|
|
|
struct dnsquery *q;
|
|
|
|
struct dnsquery_addr ipv4;
|
|
|
|
struct dnsquery_addr ipv6;
|
|
|
|
int in_progress;
|
|
|
|
uttime ttl;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define skip_bytes(n) do { if (len < (n)) return; len -= (n); start += (n); } while (0)
|
|
|
|
#define get_byte(b) do { if (!len--) return; (b) = *start++; } while (0)
|
|
|
|
#define get_word(w) do { unsigned char lo, hi; get_byte(hi); get_byte(lo); (w) = (hi << 8) | lo; } while (0)
|
|
|
|
#define get_dword(d) do { unsigned short lw, hw; get_word(hw); get_word(lw); (d) = ((unsigned)hw << 16) | lw; } while (0)
|
|
|
|
#define skip_name() do while (1) { \
|
|
|
|
unsigned char n; \
|
|
|
|
get_byte(n); \
|
|
|
|
if ((n & 0xc0) == 0) { \
|
|
|
|
if (n == 0) \
|
|
|
|
break; \
|
|
|
|
skip_bytes(n); \
|
|
|
|
continue; \
|
|
|
|
} else if ((n & 0xc0) == 0xc0) { \
|
|
|
|
skip_bytes(1); \
|
|
|
|
break; \
|
|
|
|
} else { \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static void parse_dns_reply(struct dnsquery *q, unsigned char *start, size_t len)
|
|
|
|
{
|
|
|
|
unsigned short status, qdcount, ancount, i;
|
|
|
|
struct dnsquery_doh *doh = q->doh;
|
|
|
|
int preference = q->addr_preference;
|
|
|
|
#ifdef SUPPORT_IPV6
|
|
|
|
if (preference == ADDR_PREFERENCE_DEFAULT)
|
|
|
|
preference = ADDR_PREFERENCE_IPV6;
|
|
|
|
#endif
|
|
|
|
if (!support_ipv6) preference = ADDR_PREFERENCE_IPV4_ONLY;
|
|
|
|
skip_bytes(2);
|
|
|
|
get_word(status);
|
|
|
|
if (status & 0xf)
|
|
|
|
return;
|
|
|
|
get_word(qdcount);
|
|
|
|
get_word(ancount);
|
|
|
|
skip_bytes(4);
|
|
|
|
for (i = 0; i < qdcount; i++) {
|
|
|
|
skip_name();
|
|
|
|
skip_bytes(4);
|
|
|
|
}
|
|
|
|
for (i = 0; i < ancount; i++) {
|
|
|
|
unsigned short typ, cls, rdlength;
|
|
|
|
unsigned ttl;
|
|
|
|
skip_name();
|
|
|
|
get_word(typ);
|
|
|
|
get_word(cls);
|
|
|
|
get_dword(ttl);
|
|
|
|
if (ttl < doh->ttl)
|
|
|
|
doh->ttl = ttl;
|
|
|
|
get_word(rdlength);
|
|
|
|
skip_bytes(rdlength);
|
|
|
|
/*fprintf(stderr, "typ %04x, class %04x, length %04x\n", typ, cls, rdlength);*/
|
|
|
|
if (q->addr) {
|
|
|
|
if (cls == 1 && typ == 1 && rdlength == 4) {
|
|
|
|
add_address(q->addr, AF_INET, start - rdlength, 0, preference);
|
|
|
|
}
|
|
|
|
#ifdef SUPPORT_IPV6
|
|
|
|
if (cls == 1 && typ == 0x1c && rdlength == 16) {
|
|
|
|
add_address(q->addr, AF_INET6, start - rdlength, 0, preference);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void end_doh_lookup(struct dnsquery_doh *doh)
|
|
|
|
{
|
|
|
|
uttime ttl;
|
|
|
|
struct dnsquery *q;
|
|
|
|
if (--doh->in_progress)
|
|
|
|
return;
|
|
|
|
q = doh->q;
|
|
|
|
ttl = doh->ttl;
|
|
|
|
mem_free(doh);
|
|
|
|
if (ttl * 1000 / 1000 != ttl)
|
|
|
|
ttl = -1;
|
|
|
|
else
|
|
|
|
ttl *= 1000;
|
|
|
|
end_dns_lookup(q, q->addr ? !q->addr->n : 1, ttl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void doh_end(struct status *stat, void *doh_)
|
|
|
|
{
|
|
|
|
struct dnsquery_doh *doh = (struct dnsquery_doh *)doh_;
|
|
|
|
struct dnsquery_addr *da = get_struct(stat, struct dnsquery_addr, stat);
|
|
|
|
struct cache_entry *ce;
|
|
|
|
|
|
|
|
if (stat->state >= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (stat->state == S__OK && (ce = stat->ce)) {
|
|
|
|
if (ce->redirect && da->redirect_cnt++ < MAX_REDIRECTS) {
|
|
|
|
unsigned char *u;
|
|
|
|
u = join_urls(da->url, ce->redirect);
|
|
|
|
if (!strchr(cast_const_char u, POST_CHAR)) {
|
|
|
|
unsigned char *pc = cast_uchar strchr(cast_const_char da->url, POST_CHAR);
|
|
|
|
if (pc)
|
|
|
|
add_to_strn(&u, pc);
|
|
|
|
}
|
|
|
|
mem_free(da->url);
|
|
|
|
da->url = u;
|
|
|
|
load_url(u, NULL, &da->stat, PRI_DOH, NC_RELOAD, 1, 1, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ce->http_code == 200) {
|
|
|
|
unsigned char *start;
|
|
|
|
size_t len;
|
|
|
|
get_file_by_term(NULL, ce, &start, &len, NULL);
|
|
|
|
parse_dns_reply(doh->q, start, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mem_free(da->url);
|
|
|
|
end_doh_lookup(doh);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_host_name(unsigned char **r, int *l, unsigned char *name)
|
|
|
|
{
|
|
|
|
while (*name) {
|
|
|
|
size_t len = strcspn(cast_const_char name, ".");
|
|
|
|
if (!len || len > 63)
|
|
|
|
return -1;
|
|
|
|
add_chr_to_str(r, l, (unsigned char)len);
|
|
|
|
add_bytes_to_str(r, l, name, len);
|
|
|
|
name += len;
|
|
|
|
if (*name == '.')
|
|
|
|
name++;
|
|
|
|
}
|
|
|
|
add_chr_to_str(r, l, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void do_doh_lookup(struct dnsquery *q)
|
|
|
|
{
|
|
|
|
int p;
|
|
|
|
struct dnsquery_doh *doh;
|
|
|
|
int bad_name = 0;
|
|
|
|
|
|
|
|
q->doh = doh = mem_calloc(sizeof(struct dnsquery_doh));
|
|
|
|
doh->q = q;
|
|
|
|
doh->ttl = -1;
|
|
|
|
|
|
|
|
for (p = 0; p < 2; p++) {
|
|
|
|
struct dnsquery_addr *da = !p ? &doh->ipv4 : &doh->ipv6;
|
|
|
|
unsigned char *u;
|
|
|
|
int ul;
|
|
|
|
unsigned char *r;
|
|
|
|
int rl;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!p) {
|
|
|
|
#ifdef SUPPORT_IPV6
|
|
|
|
if (q->addr_preference == ADDR_PREFERENCE_IPV6_ONLY)
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
#ifndef SUPPORT_IPV6
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
if (q->addr_preference == ADDR_PREFERENCE_IPV4_ONLY)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
doh->in_progress++;
|
|
|
|
da->stat.end = doh_end;
|
|
|
|
da->stat.data = doh;
|
|
|
|
|
|
|
|
u = init_str();
|
|
|
|
ul = 0;
|
|
|
|
memset(q->addr, 0, sizeof(struct lookup_result));
|
|
|
|
if (!casecmp(dns_over_https, cast_uchar "http://", 7) || !casecmp(dns_over_https, cast_uchar "https://", 8)) {
|
|
|
|
add_to_str(&u, &ul, dns_over_https);
|
|
|
|
} else {
|
2022-06-06 13:14:24 +00:00
|
|
|
int ipv6 = 0;
|
2022-06-06 13:08:05 +00:00
|
|
|
add_to_str(&u, &ul, cast_uchar "https://");
|
2022-06-06 13:14:24 +00:00
|
|
|
#ifdef SUPPORT_IPV6
|
|
|
|
if (!numeric_ipv6_address(dns_over_https, NULL, NULL))
|
|
|
|
ipv6 = 1;
|
|
|
|
#endif
|
|
|
|
if (ipv6)
|
|
|
|
add_chr_to_str(&u, &ul, '[');
|
2022-06-06 13:08:05 +00:00
|
|
|
add_to_str(&u, &ul, dns_over_https);
|
2022-06-06 13:14:24 +00:00
|
|
|
if (ipv6)
|
|
|
|
add_chr_to_str(&u, &ul, ']');
|
2022-06-06 13:08:05 +00:00
|
|
|
add_to_str(&u, &ul, cast_uchar "/dns-query");
|
|
|
|
}
|
|
|
|
add_chr_to_str(&u, &ul, POST_CHAR);
|
|
|
|
add_to_str(&u, &ul, cast_uchar "application/dns-message\n");
|
|
|
|
|
|
|
|
r = init_str();
|
|
|
|
rl = 0;
|
|
|
|
add_bytes_to_str(&r, &rl, cast_uchar "\0\0\1\0\0\1\0\0\0\0\0\0", 12);
|
|
|
|
if (add_host_name(&r, &rl, q->name))
|
|
|
|
bad_name = 1;
|
|
|
|
if (!p)
|
|
|
|
add_bytes_to_str(&r, &rl, cast_uchar "\0\1\0\1", 4);
|
|
|
|
else
|
|
|
|
add_bytes_to_str(&r, &rl, cast_uchar "\0\34\0\1", 4);
|
|
|
|
|
|
|
|
for (i = 0; i < rl; i++) {
|
|
|
|
unsigned char p[3];
|
|
|
|
sprintf(cast_char p, "%02x", (int)r[i]);
|
|
|
|
add_bytes_to_str(&u, &ul, p, 2);
|
|
|
|
}
|
|
|
|
mem_free(r);
|
|
|
|
da->url = u;
|
|
|
|
}
|
|
|
|
for (p = 0; p < 2; p++) {
|
|
|
|
struct dnsquery_addr *da = !p ? &doh->ipv4 : &doh->ipv6;
|
|
|
|
if (da->url) {
|
|
|
|
if (bad_name) {
|
|
|
|
mem_free(da->url);
|
|
|
|
end_doh_lookup(doh);
|
|
|
|
} else {
|
|
|
|
load_url(da->url, NULL, &da->stat, PRI_DOH, NC_RELOAD, 1, 1, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|