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>
#ifdef MAIN
#define my_log(__level, __format, ...) SCPrint(TRUE, stdout, CFSTR(__format "\n"), ## __VA_ARGS__)
#endif // MAIN
#include "dnsinfo.h"
#include "dnsinfo_private.h"
#include "dnsinfo_create.h"
static uint32_t _dnsinfo_flatfile_flags;
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 *)(void *)sa)->sin_port = port;
break;
case AF_INET6 :
((struct sockaddr_in6 *)(void *)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 *)(void *)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 *)(void *)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;
}
__private_extern__
void
_dnsinfo_flatfile_set_flags(uint32_t flags)
{
_dnsinfo_flatfile_flags = flags;
return;
}
static void
_dnsinfo_flatfile_update_flags(dns_create_resolver_t *_resolver)
{
uint32_t new_flags;
uint32_t old_flags;
_dns_resolver_buf_t *resolver = (_dns_resolver_buf_t *)*_resolver;
old_flags = ntohl(resolver->resolver.flags);
new_flags = old_flags | _dnsinfo_flatfile_flags;
_dns_resolver_set_flags(_resolver, new_flags);
return;
}
static dns_create_resolver_t
_dnsinfo_flatfile_create_resolver(const char *dir, const char *path)
{
char *buf;
uint32_t config_flags = 0;
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) {
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;
strlcpy(line, buf, len+1);
lineptr = line;
word = strsep(&lineptr, sep);
if (word == NULL) {
continue;
}
if (word[0] == ';' || word[0] == '#') {
continue;
}
token = -1;
for (size_t 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: {
while (word != NULL) {
if (word[0] != '\0') {
if (strcasecmp(word, "scoped") == 0) {
config_flags |= DNS_RESOLVER_FLAGS_SCOPED;
} else if (strcasecmp(word, "a") == 0) {
config_flags |= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS;
} else if (strcasecmp(word, "aaaa") == 0) {
config_flags |= DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS;
}
}
word = strsep(&lineptr, sep);
}
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, word);
}
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 > (long)UINT32_MAX) break;
_dns_resolver_set_order(&res, (uint32_t)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 > (long)UINT32_MAX) break;
_dns_resolver_set_timeout(&res, (uint32_t)number);
break;
}
}
}
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);
}
if (res != NULL) {
if (config_flags != 0) {
_dns_resolver_set_flags(&res, config_flags);
} else {
_dnsinfo_flatfile_update_flags(&res);
}
}
done :
if (line != NULL) free(line);
fclose(f);
return res;
}
__private_extern__
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_logging.h"
#include "dnsinfo_copy.c"
int
main(int argc, char **argv)
{
dns_config_t *dns_config = NULL;
_dns_config_buf_t *dns_config_buf = NULL;
dns_create_config_t dns_create_config;
dns_create_resolver_t dns_create_resolver;
dns_create_resolver = _dnsinfo_flatfile_create_resolver(NULL, _PATH_RESCONF);
_dns_resolver_free(&dns_create_resolver);
dns_create_config = _dns_configuration_create();
if (dns_create_config != NULL) {
size_t n;
_dnsinfo_flatfile_add_resolvers(&dns_create_config);
n = sizeof(_dns_config_buf_t);
n += ntohl(((_dns_config_buf_t *)dns_create_config)->n_attribute);
dns_config_buf = _dns_configuration_buffer_create((void *)dns_create_config, n);
_dns_configuration_free(&dns_create_config);
}
if (dns_config_buf != NULL) {
dns_config = _dns_configuration_buffer_expand(dns_config_buf);
if (dns_config == NULL) {
_dns_configuration_buffer_free(&dns_config_buf);
}
}
if (dns_config != NULL) {
_dns_configuration_log(dns_config, TRUE, NULL);
free(dns_config);
}
return 0;
}
#endif