#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <lwres/lwres.h>
#include <lwres/result.h>
static lwres_result_t
lwres_conf_parsenameserver(lwres_conf_t *confdata, FILE *fp);
static lwres_result_t
lwres_conf_parsedomain(lwres_conf_t *confdata, FILE *fp);
static lwres_result_t
lwres_conf_parsesearch(lwres_conf_t *confdata, FILE *fp);
static lwres_result_t
lwres_conf_parseoption(lwres_conf_t *confdata, FILE *fp);
static void
lwres_resetaddr(lwres_addr_t *addr);
static lwres_result_t
lwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero);
static int
eatline(FILE *fp) {
int ch;
ch = fgetc(fp);
while (ch != '\n' && ch != EOF)
ch = fgetc(fp);
return (ch);
}
static int
eatwhite(FILE *fp) {
int ch;
ch = fgetc(fp);
while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
ch = fgetc(fp);
if (ch == ';' || ch == '#')
ch = eatline(fp);
return (ch);
}
static int
getword(FILE *fp, char *buffer, size_t size) {
int ch;
char *p = buffer;
assert(buffer != NULL);
assert(size > 0U);
*p = '\0';
ch = eatwhite(fp);
if (ch == EOF)
return (EOF);
do {
*p = '\0';
if (ch == EOF || isspace((unsigned char)ch))
break;
else if ((size_t) (p - buffer) == size - 1)
return (EOF);
*p++ = (char)ch;
ch = fgetc(fp);
} while (1);
return (ch);
}
static void
lwres_resetaddr(lwres_addr_t *addr) {
assert(addr != NULL);
memset(addr, 0, sizeof(*addr));
}
void
lwres_conf_init(lwres_conf_t *confdata, int lwresflags) {
int i;
confdata->nsnext = 0;
confdata->domainname = NULL;
confdata->searchnxt = 0;
confdata->ndots = 1;
confdata->flags = lwresflags;
for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++)
lwres_resetaddr(&confdata->nameservers[i]);
for (i = 0; i < LWRES_CONFMAXSEARCH; i++)
confdata->search[i] = NULL;
}
void
lwres_conf_clear(lwres_conf_t *confdata) {
int i;
for (i = 0; i < confdata->nsnext; i++)
lwres_resetaddr(&confdata->nameservers[i]);
free(confdata->domainname);
confdata->domainname = NULL;
for (i = 0; i < confdata->searchnxt; i++) {
free(confdata->search[i]);
confdata->search[i] = NULL;
}
confdata->nsnext = 0;
confdata->domainname = NULL;
confdata->searchnxt = 0;
confdata->ndots = 1;
}
static lwres_result_t
lwres_conf_parsenameserver(lwres_conf_t *confdata, FILE *fp) {
char word[LWRES_CONFMAXLINELEN];
int res, use_ipv4, use_ipv6;
lwres_addr_t address;
if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS)
return (LWRES_R_SUCCESS);
res = getword(fp, word, sizeof(word));
if (strlen(word) == 0U)
return (LWRES_R_FAILURE);
else if (res == ' ' || res == '\t')
res = eatwhite(fp);
if (res != EOF && res != '\n')
return (LWRES_R_FAILURE);
res = lwres_create_addr(word, &address, 1);
use_ipv4 = confdata->flags & LWRES_USEIPV4;
use_ipv6 = confdata->flags & LWRES_USEIPV6;
if (res == LWRES_R_SUCCESS &&
((address.family == LWRES_ADDRTYPE_V4 && use_ipv4) ||
(address.family == LWRES_ADDRTYPE_V6 && use_ipv6))) {
confdata->nameservers[confdata->nsnext++] = address;
}
return (LWRES_R_SUCCESS);
}
static lwres_result_t
lwres_conf_parsedomain(lwres_conf_t *confdata, FILE *fp) {
char word[LWRES_CONFMAXLINELEN];
int res, i;
res = getword(fp, word, sizeof(word));
if (strlen(word) == 0U)
return (LWRES_R_FAILURE);
else if (res == ' ' || res == '\t')
res = eatwhite(fp);
if (res != EOF && res != '\n')
return (LWRES_R_FAILURE);
free(confdata->domainname);
for (i = 0; i < LWRES_CONFMAXSEARCH; i++) {
free(confdata->search[i]);
confdata->search[i] = NULL;
}
confdata->searchnxt = 0;
confdata->domainname = strdup(word);
if (confdata->domainname == NULL)
return (LWRES_R_FAILURE);
return (LWRES_R_SUCCESS);
}
static lwres_result_t
lwres_conf_parsesearch(lwres_conf_t *confdata, FILE *fp) {
int idx, delim;
char word[LWRES_CONFMAXLINELEN];
free(confdata->domainname);
confdata->domainname = NULL;
for (idx = 0; idx < LWRES_CONFMAXSEARCH; idx++) {
free(confdata->search[idx]);
confdata->search[idx] = NULL;
}
confdata->searchnxt = 0;
delim = getword(fp, word, sizeof(word));
if (strlen(word) == 0U)
return (LWRES_R_FAILURE);
idx = 0;
while (strlen(word) > 0U) {
if (confdata->searchnxt == LWRES_CONFMAXSEARCH)
goto ignore;
confdata->search[idx] = strdup(word);
if (confdata->search[idx] == NULL)
return (LWRES_R_FAILURE);
idx++;
confdata->searchnxt++;
ignore:
if (delim == EOF || delim == '\n')
break;
else
delim = getword(fp, word, sizeof(word));
}
return (LWRES_R_SUCCESS);
}
static lwres_result_t
lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) {
struct in_addr v4;
struct in6_addr v6;
char buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") +
sizeof("%4294967295")];
char *percent;
size_t n;
n = strlcpy(buf, buffer, sizeof(buf));
if (n >= sizeof(buf))
return (LWRES_R_FAILURE);
percent = strchr(buf, '%');
if (percent != NULL)
*percent = 0;
if (inet_aton(buffer, &v4) == 1) {
if (convert_zero) {
unsigned char zeroaddress[] = {0, 0, 0, 0};
unsigned char loopaddress[] = {127, 0, 0, 1};
if (memcmp(&v4, zeroaddress, 4) == 0)
memmove(&v4, loopaddress, 4);
}
addr->family = LWRES_ADDRTYPE_V4;
addr->length = sizeof(v4);
addr->zone = 0;
memcpy(addr->address, &v4, sizeof(v4));
} else if (inet_pton(AF_INET6, buf, &v6) == 1) {
addr->family = LWRES_ADDRTYPE_V6;
addr->length = sizeof(v6);
memcpy(addr->address, &v6, sizeof(v6));
if (percent != NULL) {
unsigned long zone;
char *ep;
percent++;
zone = if_nametoindex(percent);
if (zone != 0U) {
addr->zone = zone;
return (LWRES_R_SUCCESS);
}
zone = strtoul(percent, &ep, 10);
if (ep != percent && *ep == 0)
addr->zone = zone;
else
return (LWRES_R_FAILURE);
} else
addr->zone = 0;
} else
return (LWRES_R_FAILURE);
return (LWRES_R_SUCCESS);
}
static lwres_result_t
lwres_conf_parseoption(lwres_conf_t *confdata, FILE *fp) {
int delim;
long ndots;
char *p;
char word[LWRES_CONFMAXLINELEN];
delim = getword(fp, word, sizeof(word));
if (strlen(word) == 0U)
return (LWRES_R_FAILURE);
while (strlen(word) > 0U) {
if (strncmp("ndots:", word, 6) == 0) {
ndots = strtol(word + 6, &p, 10);
if (*p != '\0')
return (LWRES_R_FAILURE);
if (ndots < 0 || ndots > 0xff)
return (LWRES_R_FAILURE);
confdata->ndots = (uint8_t)ndots;
}
if (delim == EOF || delim == '\n')
break;
else
delim = getword(fp, word, sizeof(word));
}
return (LWRES_R_SUCCESS);
}
lwres_result_t
lwres_conf_parse(lwres_conf_t *confdata, const char *filename) {
FILE *fp = NULL;
char word[256];
lwres_result_t rval, ret;
int stopchar;
assert(filename != NULL);
assert(strlen(filename) > 0U);
assert(confdata != NULL);
errno = 0;
if ((fp = fopen(filename, "r")) == NULL)
return (LWRES_R_NOTFOUND);
ret = LWRES_R_SUCCESS;
do {
stopchar = getword(fp, word, sizeof(word));
if (stopchar == EOF)
break;
if (strlen(word) == 0U)
rval = LWRES_R_SUCCESS;
else if (strcmp(word, "nameserver") == 0)
rval = lwres_conf_parsenameserver(confdata, fp);
else if (strcmp(word, "domain") == 0)
rval = lwres_conf_parsedomain(confdata, fp);
else if (strcmp(word, "search") == 0)
rval = lwres_conf_parsesearch(confdata, fp);
else if (strcmp(word, "options") == 0)
rval = lwres_conf_parseoption(confdata, fp);
else {
rval = LWRES_R_SUCCESS;
stopchar = eatline(fp);
if (stopchar == EOF) {
break;
}
}
if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS)
ret = rval;
} while (1);
fclose(fp);
return (ret);
}