dnsinfo_flatfile.c [plain text]
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
#include <netdb.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <sys/dir.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "dnsinfo.h"
#include "dnsinfo_private.h"
#include "dnsinfo_create.h"
enum {
TOKEN_DOMAIN,
TOKEN_FLAGS,
TOKEN_INTERFACE,
TOKEN_NAMESERVER,
TOKEN_OPTIONS,
TOKEN_PORT,
TOKEN_SEARCH,
TOKEN_SEARCH_ORDER,
TOKEN_SORTLIST,
TOKEN_TIMEOUT,
TOKEN_MAX
};
static const struct {
const char *name;
int token;
int max_count;
} tokens [] = {
{ "domain", TOKEN_DOMAIN, 1 },
{ "flags", TOKEN_FLAGS, 1 },
{ "interface", TOKEN_INTERFACE, 1 },
{ "nameserver", TOKEN_NAMESERVER, MAXNS },
{ "options", TOKEN_OPTIONS, 1 },
{ "port", TOKEN_PORT, 1 },
{ "search", TOKEN_SEARCH, 1 },
{ "search_order", TOKEN_SEARCH_ORDER, 1 },
{ "sortlist", TOKEN_SORTLIST, 1 },
{ "timeout", TOKEN_TIMEOUT, 1 },
};
static struct sockaddr *
_dnsinfo_parse_address(char *nameserver)
{
struct addrinfo *ai;
struct addrinfo hints;
int res;
struct sockaddr *sa = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
res = getaddrinfo(nameserver, NULL, &hints, &ai);
if (res == 0) {
if ((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6)) {
sa = malloc(ai->ai_addrlen);
memcpy(sa, ai->ai_addr, ai->ai_addrlen);
}
freeaddrinfo(ai);
}
return sa;
}
static struct sockaddr *
_dnsinfo_parse_nameserver(char *token)
{
char *dot;
long number;
struct sockaddr *sa;
sa = _dnsinfo_parse_address(token);
if (sa != NULL) {
return sa;
}
dot = strrchr(token, '.');
if (dot == NULL) {
return NULL;
}
number = strtol(dot + 1, NULL, 10);
if ((number < 0) || (number > UINT16_MAX)) {
return NULL;
}
*dot = '\0';
sa = _dnsinfo_parse_address(token);
if (sa != NULL) {
in_port_t port = htons(number);
switch (sa->sa_family) {
case AF_INET :
((struct sockaddr_in *)sa)->sin_port = port;
break;
case AF_INET6 :
((struct sockaddr_in6 *)sa)->sin6_port = port;
break;
}
}
return sa;
}
static dns_sortaddr_t *
_dnsinfo_parse_sortaddr(char *token)
{
struct in_addr addr;
struct in_addr mask;
struct sockaddr *sa;
char *slash;
dns_sortaddr_t *sortaddr = NULL;
slash = strchr(token, '/');
if (slash != NULL) {
*slash = '\0';
}
sa = _dnsinfo_parse_address(token);
if (sa == NULL) {
goto done;
} else if (sa->sa_family != AF_INET) {
goto done;
} else {
addr = ((struct sockaddr_in *)sa)->sin_addr;
free(sa);
sa = NULL;
}
if (slash != NULL) {
sa = _dnsinfo_parse_address(slash + 1);
if (sa == NULL) {
goto done;
} else if (sa->sa_family != AF_INET) {
goto done;
} else {
mask = ((struct sockaddr_in *)sa)->sin_addr;
free(sa);
sa = NULL;
}
} else {
in_addr_t a;
in_addr_t m;
a = ntohl(addr.s_addr);
if (IN_CLASSA(a)) {
m = IN_CLASSA_NET;
} else if (IN_CLASSB(a)) {
m = IN_CLASSB_NET;
} else if (IN_CLASSC(a)) {
m = IN_CLASSC_NET;
} else {
goto done;
}
mask.s_addr = htonl(m);
}
sortaddr = malloc(sizeof(*sortaddr));
sortaddr->address = addr;
sortaddr->mask = mask;
done :
if (sa != NULL) free(sa);
return sortaddr;
}
static dns_create_resolver_t
_dnsinfo_flatfile_create_resolver(const char *dir, const char *path)
{
char *buf;
FILE *f;
char filename[FILENAME_MAX];
size_t len = 0;
char *line = NULL;
dns_create_resolver_t res = NULL;
const char *sep = " \t";
int token_count[TOKEN_MAX] = { 0 };
filename[0] = 0;
if (dir != NULL) {
strlcpy(filename, dir, sizeof(filename));
strlcat(filename, "/", sizeof(filename));
}
strlcat(filename, path, sizeof(filename));
f = fopen(filename, "r");
if (f == NULL) return NULL;
while ((buf = fgetln(f, &len)) != NULL) {
int i;
char *lineptr;
int max_count;
int token;
char *word;
if (len == 0) continue;
if (buf[len-1] == '\n') buf[len-1] = '\0';
line = reallocf(line, len+1);
if (line == NULL) continue;
strncpy(line, buf, len);
line[len] = '\0';
lineptr = line;
word = strsep(&lineptr, sep);
if (word == NULL) {
continue;
}
if (word[0] == ';' || word[0] == '#') {
continue;
}
token = -1;
for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) {
if (strcasecmp(word, tokens[i].name) == 0) {
token = tokens[i].token;
max_count = tokens[i].max_count;
break;
}
}
if (token == -1) {
continue;
}
word = strsep(&lineptr, sep);
if (word == NULL) {
continue;
}
if (++token_count[token] > max_count) {
continue;
}
if (res == NULL) {
res = _dns_resolver_create();
if (res == NULL) {
goto done;
}
}
switch (token) {
case TOKEN_DOMAIN: {
size_t len;
len = strlen(word);
while ((len > 0) && (word[len - 1] == '.')) {
word[--len] = '\0';
}
if (len > 0) {
_dns_resolver_set_domain(&res, word);
}
break;
}
case TOKEN_FLAGS: {
uint32_t flags = 0;
while (word != NULL) {
if (word[0] != '\0') {
if (strcasecmp(word, "scoped") == 0) {
flags |= DNS_RESOLVER_FLAGS_SCOPED;
}
}
word = strsep(&lineptr, sep);
}
if (flags != 0) {
_dns_resolver_set_flags(&res, flags);
}
break;
}
case TOKEN_INTERFACE: {
unsigned int if_index;
if_index = if_nametoindex(word);
if (if_index > 0) {
_dns_resolver_set_if_index(&res, if_index);
}
break;
}
case TOKEN_NAMESERVER: {
struct sockaddr *sa;
sa = _dnsinfo_parse_nameserver(word);
if (sa != NULL) {
_dns_resolver_add_nameserver(&res, sa);
free(sa);
}
break;
}
case TOKEN_OPTIONS: {
char *options = NULL;
while (word != NULL) {
if (word[0] != '\0') {
if (options == NULL) {
options = malloc(len+1);
if (options == NULL) break;
strlcpy(options, word, len+1);
} else {
strlcat(options, " ", len+1);
strlcat(options, word, len+1);
}
}
word = strsep(&lineptr, sep);
}
if (options != NULL) {
_dns_resolver_set_options(&res, options);
free(options);
}
break;
}
case TOKEN_PORT: {
long number = -1;
number = strtol(word, NULL, 0);
if (number < 0 || number > UINT16_MAX) break;
_dns_resolver_set_port(&res, number);
break;
}
case TOKEN_SEARCH: {
int n = 0;
while ((word != NULL) && (n++ < MAXDNSRCH)) {
size_t len;
len = strlen(word);
while ((len > 0) && (word[len - 1] == '.')) {
word[--len] = '\0';
}
if (len > 0) {
_dns_resolver_add_search(&res, word);
}
word = strsep(&lineptr, sep);
}
break;
}
case TOKEN_SEARCH_ORDER: {
long number = -1;
number = strtol(word, NULL, 0);
if (number < 0 || number > UINT32_MAX) break;
_dns_resolver_set_order(&res, number);
break;
}
case TOKEN_SORTLIST: {
int n = 0;
while ((word != NULL) && (n++ < MAXRESOLVSORT)) {
dns_sortaddr_t *sortaddr;
sortaddr = _dnsinfo_parse_sortaddr(word);
if (sortaddr == NULL) break;
_dns_resolver_add_sortaddr(&res, sortaddr);
free(sortaddr);
word = strsep(&lineptr, sep);
}
break;
}
case TOKEN_TIMEOUT: {
long number = -1;
number = strtol(word, NULL, 0);
if (number < 0 || number > UINT32_MAX) break;
_dns_resolver_set_timeout(&res, number);
break;
}
}
}
if (line != NULL) free(line);
if ((res != NULL) && (token_count[TOKEN_DOMAIN] == 0)) {
const char *domain;
domain = strrchr(path, '/');
if (domain == NULL) {
domain = path;
} else {
domain = domain + 1;
}
_dns_resolver_set_domain(&res, domain);
}
done :
fclose(f);
return res;
}
void
_dnsinfo_flatfile_add_resolvers(dns_create_config_t *config)
{
struct dirent *de;
DIR *dp;
dns_create_resolver_t res;
dp = opendir(_PATH_RESOLVER_DIR);
if (dp == NULL) {
return;
}
while ((de = readdir(dp)) != NULL) {
if (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0) continue;
res = _dnsinfo_flatfile_create_resolver(_PATH_RESOLVER_DIR, de->d_name);
if (res != NULL) {
_dns_configuration_add_resolver(config, res);
_dns_resolver_free(&res);
}
}
closedir(dp);
return;
}
#ifdef MAIN
#undef MAIN
#include "dnsinfo_copy.c"
int
main(int argc, char **argv)
{
uint8_t *buf;
dns_config_t *config;
dns_create_config_t create_config;
_dns_config_buf_t *config_buf;
uint32_t n_config;
uint32_t n_padding;
dns_create_resolver_t resolver;
resolver = _dnsinfo_flatfile_create_resolver(NULL, _PATH_RESCONF);
create_config = _dns_configuration_create();
_dnsinfo_flatfile_add_resolvers(&create_config);
config_buf = (_dns_config_buf_t *)create_config;
n_config = sizeof(_dns_config_buf_t) + ntohl(config_buf->n_attribute);
n_padding = ntohl(config_buf->n_padding);
buf = malloc(n_config + n_padding);
bcopy((void *)config_buf, buf, n_config);
bzero(&buf[n_config], n_padding);
config = expand_config((_dns_config_buf_t *)buf);
return 0;
}
#endif