mongoose/src/mg_resolv.c
Deomid Ryabkov f63d833a33 Update STM32 build image
* Update CubeL4 to 1.13.0
 * Rebuild OurTLS with `-DMBEDTLS_X509_CA_CHAIN_ON_DISK`
 * Add `-Wextra` and fix build issues

CL: Update STM32 build image

PUBLISHED_FROM=a7eacff7580bab6c6e40bdaf6164df575a717c55
2018-09-11 11:49:08 +00:00

293 lines
8.0 KiB
C

/*
* Copyright (c) 2014 Cesanta Software Limited
* All rights reserved
*/
#if MG_ENABLE_ASYNC_RESOLVER
#include "mg_internal.h"
#include "mg_resolv.h"
#ifndef MG_DEFAULT_NAMESERVER
#define MG_DEFAULT_NAMESERVER "8.8.8.8"
#endif
struct mg_resolve_async_request {
char name[1024];
int query;
mg_resolve_callback_t callback;
void *data;
time_t timeout;
int max_retries;
enum mg_resolve_err err;
/* state */
time_t last_time;
int retries;
};
/*
* Find what nameserver to use.
*
* Return 0 if OK, -1 if error
*/
static int mg_get_ip_address_of_nameserver(char *name, size_t name_len) {
int ret = -1;
#ifdef _WIN32
int i;
LONG err;
HKEY hKey, hSub;
wchar_t subkey[512], value[128],
*key = L"SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces";
if ((err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey)) !=
ERROR_SUCCESS) {
fprintf(stderr, "cannot open reg key %S: %ld\n", key, err);
ret = -1;
} else {
for (ret = -1, i = 0; 1; i++) {
DWORD subkey_size = sizeof(subkey), type, len = sizeof(value);
if (RegEnumKeyExW(hKey, i, subkey, &subkey_size, NULL, NULL, NULL,
NULL) != ERROR_SUCCESS) {
break;
}
if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS &&
((RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value,
&len) == ERROR_SUCCESS &&
value[0] != '\0') ||
(RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value,
&len) == ERROR_SUCCESS &&
value[0] != '\0'))) {
/*
* See https://github.com/cesanta/mongoose/issues/176
* The value taken from the registry can be empty, a single
* IP address, or multiple IP addresses separated by comma.
* If it's empty, check the next interface.
* If it's multiple IP addresses, take the first one.
*/
wchar_t *comma = wcschr(value, ',');
if (comma != NULL) {
*comma = '\0';
}
/* %S will convert wchar_t -> char */
snprintf(name, name_len, "%S", value);
ret = 0;
RegCloseKey(hSub);
break;
}
}
RegCloseKey(hKey);
}
#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME)
FILE *fp;
char line[512];
if ((fp = mg_fopen(MG_RESOLV_CONF_FILE_NAME, "r")) == NULL) {
ret = -1;
} else {
/* Try to figure out what nameserver to use */
for (ret = -1; fgets(line, sizeof(line), fp) != NULL;) {
unsigned int a, b, c, d;
if (sscanf(line, "nameserver %u.%u.%u.%u", &a, &b, &c, &d) == 4) {
snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d);
ret = 0;
break;
}
}
(void) fclose(fp);
}
#else
snprintf(name, name_len, "%s", MG_DEFAULT_NAMESERVER);
#endif /* _WIN32 */
return ret;
}
int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) {
#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME)
/* TODO(mkm) cache /etc/hosts */
FILE *fp;
char line[1024];
char *p;
char alias[256];
unsigned int a, b, c, d;
int len = 0;
if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) {
return -1;
}
for (; fgets(line, sizeof(line), fp) != NULL;) {
if (line[0] == '#') continue;
if (sscanf(line, "%u.%u.%u.%u%n", &a, &b, &c, &d, &len) == 0) {
/* TODO(mkm): handle ipv6 */
continue;
}
for (p = line + len; sscanf(p, "%s%n", alias, &len) == 1; p += len) {
if (strcmp(alias, name) == 0) {
usa->sin.sin_addr.s_addr = htonl(a << 24 | b << 16 | c << 8 | d);
fclose(fp);
return 0;
}
}
}
fclose(fp);
#else
(void) name;
(void) usa;
#endif
return -1;
}
static void mg_resolve_async_eh(struct mg_connection *nc, int ev,
void *data MG_UD_ARG(void *user_data)) {
time_t now = (time_t) mg_time();
struct mg_resolve_async_request *req;
struct mg_dns_message *msg;
#if !MG_ENABLE_CALLBACK_USERDATA
void *user_data = nc->user_data;
#endif
if (ev != MG_EV_POLL) {
DBG(("ev=%d user_data=%p", ev, user_data));
}
req = (struct mg_resolve_async_request *) user_data;
if (req == NULL) {
return;
}
switch (ev) {
case MG_EV_POLL:
if (req->retries > req->max_retries) {
req->err = MG_RESOLVE_EXCEEDED_RETRY_COUNT;
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
break;
}
if (nc->flags & MG_F_CONNECTING) break;
/* fallthrough */
case MG_EV_CONNECT:
if (req->retries == 0 || now - req->last_time >= req->timeout) {
mg_send_dns_query(nc, req->name, req->query);
req->last_time = now;
req->retries++;
}
break;
case MG_EV_RECV:
msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg));
if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) == 0 &&
msg->num_answers > 0) {
req->callback(msg, req->data, MG_RESOLVE_OK);
nc->user_data = NULL;
MG_FREE(req);
} else {
req->err = MG_RESOLVE_NO_ANSWERS;
}
MG_FREE(msg);
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
break;
case MG_EV_SEND:
/*
* If a send error occurs, prevent closing of the connection by the core.
* We will retry after timeout.
*/
nc->flags &= ~MG_F_CLOSE_IMMEDIATELY;
mbuf_remove(&nc->send_mbuf, nc->send_mbuf.len);
break;
case MG_EV_TIMER:
req->err = MG_RESOLVE_TIMEOUT;
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
break;
case MG_EV_CLOSE:
/* If we got here with request still not done, fire an error callback. */
if (req != NULL) {
char addr[32];
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP);
#ifdef MG_LOG_DNS_FAILURES
LOG(LL_ERROR, ("Failed to resolve '%s', server %s", req->name, addr));
#endif
req->callback(NULL, req->data, req->err);
nc->user_data = NULL;
MG_FREE(req);
}
break;
}
}
int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query,
mg_resolve_callback_t cb, void *data) {
struct mg_resolve_async_opts opts;
memset(&opts, 0, sizeof(opts));
return mg_resolve_async_opt(mgr, name, query, cb, data, opts);
}
int mg_resolve_async_opt(struct mg_mgr *mgr, const char *name, int query,
mg_resolve_callback_t cb, void *data,
struct mg_resolve_async_opts opts) {
struct mg_resolve_async_request *req;
struct mg_connection *dns_nc;
const char *nameserver = opts.nameserver;
char dns_server_buff[17], nameserver_url[26];
if (nameserver == NULL) {
nameserver = mgr->nameserver;
}
DBG(("%s %d %p", name, query, opts.dns_conn));
/* resolve with DNS */
req = (struct mg_resolve_async_request *) MG_CALLOC(1, sizeof(*req));
if (req == NULL) {
return -1;
}
strncpy(req->name, name, sizeof(req->name));
req->name[sizeof(req->name) - 1] = '\0';
req->query = query;
req->callback = cb;
req->data = data;
/* TODO(mkm): parse defaults out of resolve.conf */
req->max_retries = opts.max_retries ? opts.max_retries : 2;
req->timeout = opts.timeout ? opts.timeout : 5;
/* Lazily initialize dns server */
if (nameserver == NULL) {
if (mg_get_ip_address_of_nameserver(dns_server_buff,
sizeof(dns_server_buff)) != -1) {
nameserver = dns_server_buff;
} else {
nameserver = MG_DEFAULT_NAMESERVER;
}
}
snprintf(nameserver_url, sizeof(nameserver_url), "udp://%s:53", nameserver);
dns_nc = mg_connect(mgr, nameserver_url, MG_CB(mg_resolve_async_eh, NULL));
if (dns_nc == NULL) {
MG_FREE(req);
return -1;
}
dns_nc->user_data = req;
if (opts.dns_conn != NULL) {
*opts.dns_conn = dns_nc;
}
return 0;
}
void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) {
MG_FREE((char *) mgr->nameserver);
mgr->nameserver = NULL;
if (nameserver != NULL) {
mgr->nameserver = strdup(nameserver);
}
}
#endif /* MG_ENABLE_ASYNC_RESOLVER */