/* * $Id: read_conf.c,v 1.20 2002/10/17 20:12:01 ljb Exp $ */ #include #include #include /* bzero */ #include #include #include #include #include #include #include #include #include #include #include #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 accept */ char *accept = "^[ \t]*(([[:graph:]]+)[ \t]+)?accept[ \t]+([[:graph:]]+)[ \t]*\n?$"; /* rpsdist_database authoritative */ char *auth = "^[ \t]*([[:graph:]]+)[ \t]+authoritative[ \t]+" "((\"(.+)\")|([^\" \t\n]+))[ \t]*\n?$"; /* rpsdist_database trusted */ 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; }