/*
* $Id: read_conf.c,v 1.20 2002/10/17 20:12:01 ljb Exp $
*/
#include <stdio.h>
#include <string.h>
#include <strings.h> /* bzero */
#include <errno.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include <sys/stat.h>
#include <netdb.h>
#include <unistd.h>
#include <read_conf.h>
#include <hdr_comm.h>
#include <irr_defs.h>
#ifndef HAVE_SNPRINTF
extern int snprintf(char *, size_t, const char *, /*args*/ ...);
#endif
extern config_info_t ci;
static int find_token (char **, char **);
static void db_line (char *, src_t *);
static char *char_line (char *, char *);
static int port_line (char *, int *);
static char *free_mem (char *);
static source_t *find_src_object (src_t *, char *);
static char *get_footer_msg (char *);
static char *myconcat (char *, char *);
static int string_trim (char *, char *);
static char *make_pgpname (char *);
static int add_acl (char *, char *, acl **);
/* Control the process of parsing the conf file and intializing
* the 'ci' struct with relevant values. We are interested in
* the following values:
*
* config value name default
* ----------------- -------
* irrd_port 43
* irrd_host localhost
* super_passwd NULL ie, no super password accepted unless it is configured
* submission_log_dir 'irr_directory' irrd.conf value if present
* log_dir == NULL means no logging
* irr_directory DEF_DBCACHE see irr_defs.h
* pgp_dir NULL ie, user's ~/.pgp is assumed
* db_admin NULL ie, do not email new maintainer requests
* 'ci' (sources struc) empty list
*
* Function assumes 'ci' has bee set to all 0's. Command line options
* override the irrd.conf options so if a value is set then the corresponding
* irrd.conf value will be ignored.
*
* Result:
* 1 if irrd.conf file was opened successfully
* -1 otherwise
* intialize the 'ci' struct
*/
int parse_irrd_conf_file (char *config_fname, trace_t *tr) {
FILE *fin;
char buf[MAXLINE+1], *p, *q, *s, *irr_dir = NULL;
char val[MAXLINE+1];
int len, offset;
config_info_t temp_ci = {0};
src_t sstart;
source_t *obj;
regmatch_t rm[9];
regex_t re_accept, re_auth, re_rpstrust;
/* rpsdist <db> accept <host> */
char *accept = "^[ \t]*(([[:graph:]]+)[ \t]+)?accept[ \t]+([[:graph:]]+)[ \t]*\n?$";
/* rpsdist_database <db> authoritative <pgppass> */
char *auth = "^[ \t]*([[:graph:]]+)[ \t]+authoritative[ \t]+"
"((\"(.+)\")|([^\" \t\n]+))[ \t]*\n?$";
/* rpsdist_database <db> trusted <host> <port> */
char *rpstrust = "^[ \t]*([[:graph:]]+)[ \t]+trusted[ \t]+"
"([[:graph:]]+)[ \t]+([[:digit:]]+)[ \t]*\n?$";
/* compile our regex's */
regcomp (&re_accept, accept, REG_EXTENDED|REG_ICASE);
regcomp (&re_auth, auth, REG_EXTENDED|REG_ICASE);
regcomp (&re_rpstrust, rpstrust, REG_EXTENDED|REG_ICASE);
/* load the defaults */
sstart.first = sstart.last = NULL;
temp_ci.irrd_port = 43;
temp_ci.irrd_host = strdup ("localhost");
temp_ci.log_dir = NULL; /* default is 'irr_directory' value */
temp_ci.db_dir = strdup (DEF_DBCACHE);
temp_ci.pgp_dir = NULL;
temp_ci.db_admin = NULL; /* email addr to send new maintainer req's */
temp_ci.footer_msg = NULL; /* footer text */
if (ci.footer_msg != NULL) {
if (ci.footer_msg[0] != (char ) '"') {
strcpy (buf, "\"");
strcat (buf, ci.footer_msg);
strcat (buf, "\"");
s = get_footer_msg (buf);
}
else
s = get_footer_msg (ci.footer_msg);
free (ci.footer_msg);
ci.footer_msg = strdup (s);
}
/* load the default config file name */
if (config_fname == NULL)
config_fname = "/usr/local/etc/irrd.conf";
/* open the config file */
if ((fin = fopen (config_fname, "r")) == NULL) {
fprintf (stderr, "Error opening IRRd conf file \"%s\": %s\n",
config_fname, strerror(errno));
return -1;
}
/* pick off the value of relavent fields */
while (fgets (buf, MAXLINE, fin) != NULL) {
p = q = buf;
if (find_token (&p, &q) > 0) {
len = (int) (q - p);
if (len > 5 && !strncmp (p, "irr_database", len)) {
/* authoritative */
db_line (q, &sstart);
}
else if (len > 15 && !strncmp (p, "rpsdist_database", len)) {
db_line (q, &sstart);
/* rpsdist trusted */
if (regexec (&re_rpstrust, q, 8, rm, 0) == 0) {
/* DB name */
*(q + rm[1].rm_eo) = '\0';
if ((obj = find_src_object (&sstart, q + rm[1].rm_so)) != NULL) {
obj->rpsdist_flag = 1;
obj->rpsdist_trusted = 1;
/* host */
*(q + rm[2].rm_eo) = '\0';
obj->rpsdist_host = strdup (q + rm[2].rm_so);
/* port */
*(q + rm[3].rm_eo) = '\0';
obj->rpsdist_port = strdup (q + rm[3].rm_so);
}
}
/* authoritative and pgppass */
else if (regexec (&re_auth, q, 9, rm, 0) == 0) {
/* DB name */
*(q + rm[1].rm_eo) = '\0';
if ((obj = find_src_object (&sstart, q + rm[1].rm_so)) != NULL) {
obj->rpsdist_flag = 1;
obj->rpsdist_auth = 1;
offset = 5;
if (rm[4].rm_so != -1)
offset = 4;
*(q + rm[offset].rm_eo) = '\0';
strcpy (val, (char *) (q + rm[offset].rm_so));
obj->rpsdist_pgppass = strdup (val);
}
}
}
if (regexec (&re_accept, q, 4, rm, 0) == 0) {
/* optional DB name(2), address (3) */
*(q + rm[3].rm_eo) = '\0';
if( rm[2].rm_so != -1 ){
*(q + rm[2].rm_eo) = '\0';
add_acl((q + rm[3].rm_so), (q + rm[2].rm_so), &ci.acls);
}
else
add_acl((q + rm[3].rm_so), NULL, &ci.acls);
}
else if (len > 4 && !strncmp (p, "irr_port", len))
temp_ci.irrd_port = port_line (q, &temp_ci.irrd_port);
else if (len > 4 && !strncmp (p, "irr_host", len))
temp_ci.irrd_host = char_line (q, temp_ci.irrd_host);
else if (len > 3 && !strncmp (p, "override_cryptpw", len))
temp_ci.super_passwd = char_line (q, temp_ci.super_passwd);
else if (len > 4 && !strncmp (p, "pgp_dir", len))
temp_ci.pgp_dir = char_line (q, temp_ci.pgp_dir);
else if (len > 5 && !strncmp (p, "irr_directory", len))
irr_dir = char_line (q, irr_dir);
else if (len > 3 && !strncmp (p, "db_admin", len))
temp_ci.db_admin = char_line (q, temp_ci.db_admin);
else if (len > 4 && !strncmp (p, "debug", len))
config_trace_local (tr, q);
else if (len > 10 && ci.footer_msg == NULL &&
!strncmp (p, "response_footer", len))
temp_ci.footer_msg = myconcat (temp_ci.footer_msg, get_footer_msg (q));
else if (len > 10 && ci.notify_header_msg == NULL &&
!strncmp (p, "response_notify_header", len))
temp_ci.notify_header_msg =
myconcat (temp_ci.notify_header_msg, get_footer_msg (q));
else if (len > 10 && ci.forward_header_msg == NULL &&
!strncmp (p, "response_forward_header", len))
temp_ci.forward_header_msg =
myconcat (temp_ci.forward_header_msg, get_footer_msg (q));
else if (len > 5 && !strncmp (p, "irr_database", len)) {
}
}
}
/* If the user has specified command line options,
* then use those instead of the irrd.conf options
*/
if (ci.srcs == NULL)
ci.srcs = sstart.first;
if (ci.irrd_port == 0)
ci.irrd_port = temp_ci.irrd_port;
if (ci.irrd_host == NULL)
ci.irrd_host = temp_ci.irrd_host;
else
free_mem (temp_ci.irrd_host);
if (ci.super_passwd == NULL)
ci.super_passwd = temp_ci.super_passwd;
else
free_mem (temp_ci.super_passwd);
if (ci.footer_msg == NULL) {
if (temp_ci.footer_msg != NULL) {
ci.footer_msg = strdup (temp_ci.footer_msg);
free_mem (temp_ci.footer_msg);
}
}
if (ci.notify_header_msg == NULL) {
if (temp_ci.notify_header_msg != NULL) {
ci.notify_header_msg = strdup (temp_ci.notify_header_msg);
free_mem (temp_ci.notify_header_msg);
}
}
if (ci.forward_header_msg == NULL) {
if (temp_ci.forward_header_msg != NULL) {
ci.forward_header_msg = strdup (temp_ci.forward_header_msg);
free_mem (temp_ci.forward_header_msg);
}
}
/* need the db cache directory for rps_dist communication */
if (irr_dir != NULL)
ci.db_dir = strdup (irr_dir);
/* see if the cache dir exists. we do not need
* it except for default ack and trans logs
* and pgp dir */
if ((s = dir_chks (ci.db_dir, 0)) != NULL) {
ci.db_dir = NULL;
trace (NORM, tr, "irr_dir set to NULL:%s\n", s);
free (s);
}
/* pgp directory processing, used by RPS-DIST */
if (ci.pgp_dir == NULL) {
if (temp_ci.pgp_dir == NULL) {
ci.pgp_dir = make_pgpname (irr_dir);
}
else
ci.pgp_dir = temp_ci.pgp_dir;
}
else
free_mem (temp_ci.pgp_dir);
/* see if the pgp dir exists. */
if ((s = dir_chks (ci.pgp_dir, 0)) != NULL) {
ci.pgp_dir = NULL;
trace (NORM, tr, "pgp_dir set to NULL:%s\n", s);
free (s);
}
/* what the hey, we only leak a little memory */
if (ci.log_dir == NULL) {
if (temp_ci.log_dir == NULL)
ci.log_dir = irr_dir;
else
ci.log_dir = temp_ci.log_dir;
}
else {
free_mem (irr_dir);
free_mem (temp_ci.log_dir);
}
/* see if the log dir exists. */
if ((s = dir_chks (ci.log_dir, 0)) != NULL) {
ci.log_dir = NULL;
trace (NORM, tr, "log_dir set to NULL:%s\n", s);
free (s);
}
if (ci.db_admin == NULL)
ci.db_admin = temp_ci.db_admin;
else
free_mem (temp_ci.db_admin);
fclose (fin);
regfree (&re_accept);
regfree (&re_auth);
regfree (&re_rpstrust);
return 1;
}
/* Given an irrd.conf line starting with 'irr_port', return
* the port value.
*
* Return:
* the port value
* echo back *port if there is a missing token after
* 'irr_port'
*/
int port_line (char *line, int *port) {
char *p, *q;
p = q = line;
if (find_token (&p, &q) > 0) {
*q = '\0';
return atoi (p);
}
return *port;
}
/* Given an irrd.conf line starting with 'irr_host', return
* the host value.
*
* Routine should be invoked as 'host = host_line (line, host);'
* as 'host' is freed.
*
* Routine was originally written for 'irrd_host' but can be used for
* any char * valued conf datum.
*
* Return:
* char *host value for the irrd.conf file if there are no errors
* echo back input 'host' value if there is a missing token
*/
char *char_line (char *line, char *host) {
char *p, *q;
p = q = line;
if (find_token (&p, &q) > 0) {
*q = '\0';
free_mem (host);
return strdup (p);
}
else
return host;
}
/* Given an irrd.conf line of 'irr_database' create a list
* entry with the DB name and whether the DB is authoritative.
*
* Return:
* void
* a list element appended to list pointed to by 'start' with
* the DB name and authoritative field initialized
*/
void db_line (char *line, src_t *start) {
char *p, *q, *r, *source, c;
int len, auth;
source_t *obj;
/* first find the DB name */
p = q = line;
if (find_token (&p, &q) > 0) {
auth = 0;
source = p;
r = q;
/* now find the keyword following the DB name */
if (find_token (&p, &q) > 0) {
len = (int) (q - p);
if (len > 1 && !strncmp (p, "authoritative", len))
auth = 1;
}
c = *r;
*r = '\0';
if ((obj = find_src_object (start, source)) != NULL) {
if (auth)
obj->authoritative = auth;
}
else {
obj = create_source_obj (source, auth);
add_src_obj (start, obj);
}
*r = c;
}
return;
}
/* Find the source 'source' in the source list pointed
* to by 'start'.
*
* Return:
* pointer to element if 'source' is found
* NULL otherwise
*/
source_t *find_src_object (src_t *start, char *source) {
source_t *obj;
obj = start->first;
while (obj != NULL) {
if (!strcasecmp (obj->source, source))
break;
obj = obj->next;
}
return obj;
}
/* Create a source_t list element with value 'source' and
* 'authoritative'.
*
* Return:
* void
*/
source_t *create_source_obj (char *source, int authoritative) {
source_t *obj;
char *z;
if (source == NULL)
return NULL;
obj = (source_t *) malloc (sizeof (source_t));
memset ((char *) obj, 0, sizeof (source_t));
obj->source = strdup (source);
obj->authoritative = authoritative;
for (z = obj->source; z != NULL && *z != '\0'; *z = toupper ((int) *z), z++);
return (obj);
}
/* Add a source_t element to the list pointed to by 'start'.
*
* Return:
* void
*/
void add_src_obj (src_t *start, source_t *obj) {
obj->next = NULL;
if (start->first == NULL)
start->first = obj;
else
start->last->next = obj;
start->last = obj;
}
/* JW later make this routine globally accessible; many routines use
* this routine (ie, reduce code size).
*/
/* This routine finds a token in the string. *x will
* point to the first character in the string and *y will
* point to the first character after the token. A token
* is a printable character string. A '\n' is not considered
* part of a legal token.
* It will look for !'s in
* the string and assume everything after is a comment.
* Invoke this routine by setting 'x' and 'y' to the beginning
* of the target string like this: 'x = y = target_string;
* if (find_token (&x, &y) > 0) ...
* each successive call to find_token () will move the char
* pointers along.
*
* Return:
* 1 if a token is found (*x points to token, *y first char after)
* -1 if no token is found (*x and *y are to be ignored)
*/
int find_token (char **x, char **y) {
/* It's possible the target string is NULL
* or we are in an rpsl comment
*/
if (*y == NULL || **y == '!')
return -1;
*x = *y;
/* find the start of a token, ie, first printable character */
while (**x == ' ' || **x == '\t') (*x)++;
if (**x == '\0' || **x == '!' || **x == '\n')
return -1;
/* find the first char at the end of the token */
*y = *x + 1;
while (**y != '\0' && (isgraph ((int) **y) && **y != '!')) (*y)++;
return 1;
}
/*
* Free the memory pointed to by *p. p may be NULL.
* Function should be invoked as 'x = free_mem (x)'
*
* Return:
* NULL
*/
char *free_mem (char *p) {
if (p != NULL)
free (p);
return NULL;
}
/* Return a footer message line. The line should
* be enclosed in quotes, eg, "Welcome to IRRd.".
* If quotes are not supplied, return the string
* starting with the first non-blank character.
*
* Return:
* String enclosed in quotes minus the quotes if in form "..."
* String verbatim if quotes are missing
* NULL otherwise
*/
char *get_footer_msg (char *s) {
char *q, *r;
int i = 0;
char buf[1024];
if (s == NULL)
return NULL;
/* Find the first printable character */
for (; isspace ((int) *s); s++);
/* we have a "..." enclosed string */
if ((q = strchr (s, '"')) != NULL &&
(r = strrchr (++q, '"')) != NULL) {
for (; q < r && i < 1022; q++, i++) {
if (*q == '\\' && *(q + 1) == 'n') {
memcpy (&buf[i], "\n", 1);
q++;
}
else
memcpy (&buf[i], q, 1);
}
if (i == 0)
return NULL;
else {
if (buf[i - 1] != '\n')
memcpy (&buf[i++], "\n", 1);
buf[i] = '\0';
return strdup (buf);
}
}
else {
/* Return whatever the user has typed in
* starting with the first non-blank character
*/
if (*s == '\0')
return NULL;
else {
if (*(s + strlen (s) - 1) != '\n') {
strcpy (buf, s);
strcat (buf, "\n");
return strdup (buf);
}
return strdup (s);
}
}
}
char *myconcat (char *x, char *y) {
char buf[2048];
if (x == NULL)
buf[0] = '\0';
else {
strcpy (buf, x);
free_mem (x);
}
if (y != NULL)
strcat (buf, y);
if (buf[0] == '\0')
return NULL;
else
return strdup (buf);
}
/* return string 's' with spaces removed in 't' and
* the length of 't'.
*/
int string_trim (char *s, char *t) {
int n;
regmatch_t rm[2];
regex_t re;
char *str_trim = "^[ \t]*(([^ \t])|([^ \t].*[^ \t]))[ \t]*$";
regcomp (&re, str_trim, REG_EXTENDED);
if (regexec (&re, s, 2, rm, 0) == 0) {
/* directory is longer than our buffer */
if ((n = (rm[1].rm_eo - rm[1].rm_so)) > (MAXLINE - 1))
return 0;
strncpy (t, (char *) (s + rm[1].rm_so), n);
t[n] = '\0';
return n;
}
return 0;
}
char *make_pgpname (char *dir) {
int n;
char pgpname[MAXLINE];
if (dir == NULL)
return NULL;
if (!(n = string_trim (dir, pgpname)))
return NULL;
if (n > (MAXLINE - 7))
return NULL;
strcat (pgpname, "/.pgp/");
return strdup (pgpname);
}
/* Perform basic dir tests and return a textual
* description of anything we find wrong such as
* insufficient permissions.
*
* Results will tell if (dir) exists and if we have
* read/write permissions. Function will try to
* create (dir) if it doesn't exist and the
* (creat_dir) flag is set.
*
* Input:
* -name of the dir to check (dir)
* -flag to indicate if (dir) should be created if it
* doesn't exist
*
* Return:
* -a text string describing some problem
* -NULL otherwise
*/
char *dir_chks (char *dir, int creat_dir) {
char file[MAXLINE+1];
FILE *fp;
/* Sanity checks */
if (dir == NULL)
return strdup ("No directory specified!");
if (strlen (dir) > MAXLINE)
return strdup ("Directory name is too long! MAX chars (MAXLINE)");
/* see if we can create a temp file in the directory */
snprintf (file, MAXLINE, "%s/cache-test.%d", dir, (int) getpid ());
if ((fp = fopen (file, "w+")) == NULL) {
/* dir does not exist, try to make it */
if (creat_dir &&
errno == ENOENT) {
if (!mkdir (dir, 00755))
return NULL;
}
/* we have permission problems or ... */
snprintf (file, MAXLINE,
"Could not create the directory: %s", strerror (errno));
return strdup (file);
}
else {
fclose (fp);
remove (file);
}
return NULL;
}
/* host can either be the dot notation or name of the
* party you wish to allow
*/
static int add_acl(char * host, char * db_name, acl ** acl_list){
acl * tmp = *acl_list;
/* find end */
while(tmp && tmp->next) tmp=tmp->next;
/* insert */
if(!tmp){
if( (tmp = (acl *)malloc(sizeof(acl))) == NULL){
return 0;
}
*acl_list = tmp;
}
else{
if( (tmp->next = (acl *)malloc(sizeof(acl))) == NULL){
return 0;
}
tmp = tmp->next;
}
/* set data */
bzero(tmp, sizeof(acl));
tmp->host = strdup(host);
tmp->db_name = db_name;
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1