2020-12-05 11:26:32 +00:00
|
|
|
#include "dns.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "str.h"
|
|
|
|
#include "timer.h"
|
2022-01-07 15:48:09 +00:00
|
|
|
#include "url.h"
|
2020-12-05 11:26:32 +00:00
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
struct dns_data {
|
|
|
|
struct dns_data *next;
|
|
|
|
struct mg_connection *c;
|
2021-12-21 21:50:18 +00:00
|
|
|
int64_t expire;
|
2020-12-05 11:26:32 +00:00
|
|
|
uint16_t txnid;
|
|
|
|
};
|
|
|
|
|
2020-12-22 09:44:59 +00:00
|
|
|
static struct dns_data *s_reqs; // Active DNS requests
|
|
|
|
|
|
|
|
static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
|
|
|
|
struct mg_dns *, bool);
|
|
|
|
|
|
|
|
static void mg_dns_free(struct dns_data *d) {
|
|
|
|
LIST_DELETE(struct dns_data, &s_reqs, d);
|
2020-12-05 11:26:32 +00:00
|
|
|
free(d);
|
|
|
|
}
|
|
|
|
|
2020-12-22 09:44:59 +00:00
|
|
|
void mg_resolve_cancel(struct mg_connection *c) {
|
|
|
|
struct dns_data *tmp, *d;
|
|
|
|
for (d = s_reqs; d != NULL; d = tmp) {
|
2020-12-05 11:26:32 +00:00
|
|
|
tmp = d->next;
|
2020-12-22 09:44:59 +00:00
|
|
|
if (d->c == c) mg_dns_free(d);
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-28 05:25:29 +00:00
|
|
|
static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
|
|
|
|
char *to, size_t tolen, int depth) {
|
2020-12-05 11:26:32 +00:00
|
|
|
size_t i = 0, j = 0;
|
2020-12-17 19:14:17 +00:00
|
|
|
if (tolen > 0) to[0] = '\0';
|
2020-12-11 14:02:52 +00:00
|
|
|
if (depth > 5) return 0;
|
2020-12-28 05:25:29 +00:00
|
|
|
while (ofs + i + 1 < len) {
|
|
|
|
size_t n = s[ofs + i];
|
|
|
|
if (n == 0) {
|
2020-12-05 11:26:32 +00:00
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
}
|
2020-12-28 05:25:29 +00:00
|
|
|
if (n & 0xc0) {
|
|
|
|
size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len
|
|
|
|
if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 &&
|
|
|
|
mg_dns_parse_name_depth(s, len, ptr, to, tolen, depth + 1) == 0)
|
|
|
|
return 0;
|
|
|
|
i += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ofs + i + n + 1 >= len) return 0;
|
|
|
|
if (j > 0) {
|
|
|
|
if (j < tolen) to[j] = '.';
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n);
|
2020-12-05 11:26:32 +00:00
|
|
|
j += n;
|
|
|
|
i += n + 1;
|
2020-12-28 05:25:29 +00:00
|
|
|
if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
2020-12-17 19:14:17 +00:00
|
|
|
if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term
|
2020-12-05 11:26:32 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2021-10-22 19:41:26 +01:00
|
|
|
static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs,
|
|
|
|
char *dst, size_t dstlen) {
|
2020-12-28 05:25:29 +00:00
|
|
|
return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
|
|
|
|
bool is_question, struct mg_dns_rr *rr) {
|
|
|
|
const uint8_t *s = buf + ofs, *e = &buf[len];
|
|
|
|
|
|
|
|
memset(rr, 0, sizeof(*rr));
|
2021-04-13 19:42:47 +01:00
|
|
|
if (len < sizeof(struct mg_dns_header)) return 0; // Too small
|
2021-10-22 08:56:45 +01:00
|
|
|
if (len > 512) return 0; // Too large, we don't expect that
|
|
|
|
if (s >= e) return 0; // Overflow
|
2020-12-28 05:25:29 +00:00
|
|
|
|
2020-12-28 06:32:55 +00:00
|
|
|
if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0)
|
|
|
|
return 0;
|
2020-12-28 05:25:29 +00:00
|
|
|
s += rr->nlen + 4;
|
|
|
|
if (s > e) return 0;
|
2021-10-22 08:56:45 +01:00
|
|
|
rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]);
|
|
|
|
rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
|
|
|
|
if (is_question) return (size_t) (rr->nlen + 4);
|
2020-12-28 05:25:29 +00:00
|
|
|
|
|
|
|
s += 6;
|
|
|
|
if (s > e) return 0;
|
2021-10-22 08:56:45 +01:00
|
|
|
rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
|
2020-12-28 05:25:29 +00:00
|
|
|
if (s + rr->alen > e) return 0;
|
2021-10-22 08:56:45 +01:00
|
|
|
return (size_t) (rr->nlen + rr->alen + 10);
|
2020-12-28 05:25:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
|
|
|
|
const struct mg_dns_header *h = (struct mg_dns_header *) buf;
|
|
|
|
struct mg_dns_rr rr;
|
|
|
|
size_t i, n, ofs = sizeof(*h);
|
|
|
|
memset(dm, 0, sizeof(*dm));
|
|
|
|
|
|
|
|
if (len < sizeof(*h)) return 0; // Too small, headers dont fit
|
2020-12-25 04:32:56 +00:00
|
|
|
if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity
|
|
|
|
if (mg_ntohs(h->num_answers) > 10) return 0; // Sanity
|
2020-12-28 05:25:29 +00:00
|
|
|
dm->txnid = mg_ntohs(h->txnid);
|
|
|
|
|
2020-12-11 14:02:52 +00:00
|
|
|
for (i = 0; i < mg_ntohs(h->num_questions); i++) {
|
2020-12-28 05:25:29 +00:00
|
|
|
if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false;
|
|
|
|
// LOG(LL_INFO, ("Q %zu %zu", ofs, n));
|
|
|
|
ofs += n;
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < mg_ntohs(h->num_answers); i++) {
|
2020-12-28 05:25:29 +00:00
|
|
|
// LOG(LL_INFO, ("A -- %zu %zu %s", ofs, n, dm->name));
|
|
|
|
if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false;
|
|
|
|
mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
|
|
|
|
ofs += n;
|
|
|
|
|
|
|
|
if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) {
|
2020-12-22 09:44:59 +00:00
|
|
|
dm->addr.is_ip6 = false;
|
2020-12-28 05:25:29 +00:00
|
|
|
memcpy(&dm->addr.ip, &buf[ofs - 4], 4);
|
2020-12-22 09:44:59 +00:00
|
|
|
dm->resolved = true;
|
|
|
|
break; // Return success
|
2020-12-28 05:25:29 +00:00
|
|
|
} else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) {
|
2020-12-22 09:44:59 +00:00
|
|
|
dm->addr.is_ip6 = true;
|
2020-12-28 05:25:29 +00:00
|
|
|
memcpy(&dm->addr.ip6, &buf[ofs - 16], 16);
|
2020-12-20 16:55:33 +00:00
|
|
|
dm->resolved = true;
|
|
|
|
break; // Return success
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-20 16:55:33 +00:00
|
|
|
return true;
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
|
|
|
|
void *fn_data) {
|
2020-12-22 09:44:59 +00:00
|
|
|
struct dns_data *d, *tmp;
|
2020-12-05 11:26:32 +00:00
|
|
|
if (ev == MG_EV_POLL) {
|
2021-12-21 21:50:18 +00:00
|
|
|
int64_t now = *(int64_t *) ev_data;
|
2021-01-21 10:00:18 +00:00
|
|
|
for (d = s_reqs; d != NULL; d = tmp) {
|
2020-12-05 11:26:32 +00:00
|
|
|
tmp = d->next;
|
|
|
|
// LOG(LL_DEBUG, ("%lu %lu dns poll", d->expire, now));
|
|
|
|
if (now > d->expire) mg_error(d->c, "DNS timeout");
|
|
|
|
}
|
|
|
|
} else if (ev == MG_EV_READ) {
|
|
|
|
struct mg_dns_message dm;
|
|
|
|
int resolved = 0;
|
2020-12-20 16:55:33 +00:00
|
|
|
if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) {
|
|
|
|
char *s = mg_hexdump(c->recv.buf, c->recv.len);
|
|
|
|
LOG(LL_ERROR, ("Unexpected DNS response:\n%s\n", s));
|
|
|
|
free(s);
|
|
|
|
} else {
|
2020-12-24 16:52:33 +00:00
|
|
|
LOG(LL_VERBOSE_DEBUG, ("%s %d", dm.name, dm.resolved));
|
2020-12-22 09:44:59 +00:00
|
|
|
for (d = s_reqs; d != NULL; d = tmp) {
|
2020-12-05 11:26:32 +00:00
|
|
|
tmp = d->next;
|
2020-12-20 16:55:33 +00:00
|
|
|
// LOG(LL_INFO, ("d %p %hu %hu", d, d->txnid, dm.txnid));
|
2020-12-05 11:26:32 +00:00
|
|
|
if (dm.txnid != d->txnid) continue;
|
|
|
|
if (d->c->is_resolving) {
|
|
|
|
d->c->is_resolving = 0;
|
2020-12-20 16:55:33 +00:00
|
|
|
if (dm.resolved) {
|
2021-01-24 13:57:40 +00:00
|
|
|
char buf[100];
|
2020-12-22 09:44:59 +00:00
|
|
|
dm.addr.port = d->c->peer.port; // Save port
|
|
|
|
d->c->peer = dm.addr; // Copy resolved address
|
2021-01-24 13:57:40 +00:00
|
|
|
LOG(LL_DEBUG, ("%lu %s resolved to %s", d->c->id, dm.name,
|
|
|
|
mg_ntoa(&d->c->peer, buf, sizeof(buf))));
|
2020-12-20 16:55:33 +00:00
|
|
|
mg_connect_resolved(d->c);
|
2020-12-22 09:44:59 +00:00
|
|
|
#if MG_ENABLE_IPV6
|
|
|
|
} else if (dm.addr.is_ip6 == false && dm.name[0] != '\0') {
|
|
|
|
struct mg_str x = mg_str(dm.name);
|
|
|
|
mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
|
|
|
|
#endif
|
2020-12-20 16:55:33 +00:00
|
|
|
} else {
|
|
|
|
mg_error(d->c, "%s DNS lookup failed", dm.name);
|
|
|
|
}
|
2020-12-05 11:26:32 +00:00
|
|
|
} else {
|
2020-12-21 12:26:44 +00:00
|
|
|
LOG(LL_ERROR, ("%lu already resolved", d->c->id));
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
2020-12-22 09:44:59 +00:00
|
|
|
mg_dns_free(d);
|
2020-12-05 11:26:32 +00:00
|
|
|
resolved = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!resolved) LOG(LL_ERROR, ("stray DNS reply"));
|
|
|
|
c->recv.len = 0;
|
|
|
|
} else if (ev == MG_EV_CLOSE) {
|
2020-12-22 09:44:59 +00:00
|
|
|
for (d = s_reqs; d != NULL; d = tmp) {
|
2020-12-05 11:26:32 +00:00
|
|
|
tmp = d->next;
|
2022-01-14 08:50:01 +00:00
|
|
|
mg_error(d->c, "DNS error");
|
2020-12-22 09:44:59 +00:00
|
|
|
mg_dns_free(d);
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-21 10:00:18 +00:00
|
|
|
(void) fn_data;
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
|
|
|
|
Make private functions static and add missing prototypes.
Fixes:
mongoose/mongoose.c:180:8: warning: no previous prototype for ‘mg_dns_parse_name’ [-Wmissing-prototypes]
180 | size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs, char *dst,
| ^~~~~~~~~~~~~~~~~
mongoose/mongoose.c:306:6: warning: no previous prototype for ‘mg_dns_send’ [-Wmissing-prototypes]
306 | void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
| ^~~~~~~~~~~
mongoose/mongoose.c:925:6: warning: no previous prototype for ‘mg_http_parse_headers’ [-Wmissing-prototypes]
925 | void mg_http_parse_headers(const char *s, const char *end,
| ^~~~~~~~~~~~~~~~~~~~~
mongoose/mongoose.c:1125:7: warning: no previous prototype for ‘mg_http_etag’ [-Wmissing-prototypes]
1125 | char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
| ^~~~~~~~~~~~
mongoose/mongoose.c:2578:6: warning: no previous prototype for ‘mg_sha1_transform’ [-Wmissing-prototypes]
2578 | void mg_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) {
| ^~~~~~~~~~~~~~~~~
mongoose/mongoose.c:2976:8: warning: no previous prototype for ‘mg_open_listener’ [-Wmissing-prototypes]
2976 | SOCKET mg_open_listener(const char *url, struct mg_addr *addr) {
| ^~~~~~~~~~~~~~~~
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2021-10-12 18:26:20 -06:00
|
|
|
static void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
|
2021-10-22 19:41:26 +01:00
|
|
|
uint16_t txnid, bool ipv6) {
|
2020-12-05 11:26:32 +00:00
|
|
|
struct {
|
|
|
|
struct mg_dns_header header;
|
|
|
|
uint8_t data[256];
|
|
|
|
} pkt;
|
|
|
|
size_t i, n;
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
2020-12-28 05:25:29 +00:00
|
|
|
pkt.header.txnid = mg_htons(txnid);
|
2020-12-05 11:26:32 +00:00
|
|
|
pkt.header.flags = mg_htons(0x100);
|
2020-12-22 09:44:59 +00:00
|
|
|
pkt.header.num_questions = mg_htons(1);
|
2020-12-05 11:26:32 +00:00
|
|
|
for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
|
|
|
|
if (name->ptr[i] == '.' || i >= name->len) {
|
2021-10-22 08:56:45 +01:00
|
|
|
pkt.data[n] = (uint8_t) (i - n);
|
2020-12-05 11:26:32 +00:00
|
|
|
memcpy(&pkt.data[n + 1], name->ptr + n, i - n);
|
|
|
|
n = i + 1;
|
|
|
|
}
|
|
|
|
if (i >= name->len) break;
|
|
|
|
}
|
|
|
|
memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
|
|
|
|
n += 5;
|
2020-12-22 09:44:59 +00:00
|
|
|
if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
|
|
|
|
// memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
|
|
|
|
// n += 6;
|
2020-12-05 11:26:32 +00:00
|
|
|
mg_send(c, &pkt, sizeof(pkt.header) + n);
|
|
|
|
#if 0
|
|
|
|
// Immediately after A query, send AAAA query. Whatever reply comes first,
|
|
|
|
// we'll use it. Note: we cannot send two queries in a single packet.
|
|
|
|
// https://stackoverflow.com/questions/4082081/requesting-a-and-aaaa-records-in-single-dns-query
|
|
|
|
pkt.data[n - 3] = 0x1c; // AAAA query
|
|
|
|
mg_send(c, &pkt, sizeof(pkt.header) + n);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-12-22 09:44:59 +00:00
|
|
|
static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
|
|
|
|
struct mg_dns *dnsc, bool ipv6) {
|
2020-12-05 11:26:32 +00:00
|
|
|
struct dns_data *d = NULL;
|
2020-12-22 09:44:59 +00:00
|
|
|
if (dnsc->url == NULL) {
|
|
|
|
mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
|
|
|
|
} else if (dnsc->c == NULL) {
|
|
|
|
dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
|
|
|
|
if (dnsc->c != NULL) {
|
|
|
|
dnsc->c->pfn = dns_cb;
|
|
|
|
// dnsc->c->is_hexdumping = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dnsc->c == NULL) {
|
|
|
|
mg_error(c, "resolver");
|
|
|
|
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
|
|
|
|
mg_error(c, "resolve OOM");
|
|
|
|
} else {
|
2021-04-22 15:21:16 +01:00
|
|
|
char buf[100];
|
2021-10-22 08:56:45 +01:00
|
|
|
d->txnid = s_reqs ? (uint16_t) (s_reqs->txnid + 1) : 1;
|
2020-12-22 09:44:59 +00:00
|
|
|
d->next = s_reqs;
|
|
|
|
s_reqs = d;
|
2021-12-21 21:50:18 +00:00
|
|
|
d->expire = mg_millis() + (int64_t) ms;
|
2020-12-22 09:44:59 +00:00
|
|
|
d->c = c;
|
|
|
|
c->is_resolving = 1;
|
2021-04-22 15:21:16 +01:00
|
|
|
LOG(LL_VERBOSE_DEBUG,
|
|
|
|
("%lu resolving %.*s @ %s, txnid %hu", c->id, (int) name->len,
|
|
|
|
name->ptr, mg_ntoa(&dnsc->c->peer, buf, sizeof(buf)), d->txnid));
|
2020-12-22 09:44:59 +00:00
|
|
|
mg_dns_send(dnsc->c, name, d->txnid, ipv6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 15:00:10 +00:00
|
|
|
void mg_resolve(struct mg_connection *c, const char *url) {
|
|
|
|
struct mg_str host = mg_url_host(url);
|
|
|
|
c->peer.port = mg_htons(mg_url_port(url));
|
|
|
|
if (mg_aton(host, &c->peer)) {
|
|
|
|
// host is an IP address, do not fire name resolution
|
2020-12-05 11:26:32 +00:00
|
|
|
mg_connect_resolved(c);
|
|
|
|
} else {
|
2022-01-07 15:00:10 +00:00
|
|
|
// host is not an IP, send DNS resolution request
|
|
|
|
mg_sendnsreq(c, &host, c->mgr->dnstimeout, &c->mgr->dns4, false);
|
2020-12-05 11:26:32 +00:00
|
|
|
}
|
|
|
|
}
|