/* * $Id: irrd_util.c,v 1.12 2002/10/17 20:02:30 ljb Exp $ * originally Id: util.c,v 1.51 1998/08/07 19:48:58 gerald Exp */ #include #include #include #include #include #include #include #include #include "irrd.h" #include #include #include static int find_token (char **, char **); extern key_label_t key_info[][2]; extern trace_t *default_trace; extern m_command_t m_info []; irr_database_t *new_database (char *name) { irr_database_t *database; hash_item_t hash_item; database = New (irr_database_t); memset(database, 0, sizeof(irr_database_t)); database->radix = New_Radix (128); database->hash = HASH_Create (DEF_HASH_SIZE, HASH_KeyOffset, HASH_Offset (&hash_item, &hash_item.key), HASH_DestroyFunction, irr_hash_destroy, 0); database->hash_spec = HASH_Create (DEF_HASH_SIZE, HASH_KeyOffset, HASH_Offset (&hash_item, &hash_item.key), HASH_DestroyFunction, irr_hash_destroy, 0); database->name = strdup (name); database->mirror_fd = -1; database->journal_fd = -1; database->max_journal_bytes = IRR_MAX_JOURNAL_SIZE; database->db_fp = NULL; database->db_syntax = EMPTY_DB; database->obj_filter = 0; /* Any object bit-fields that are 1 will be filtered out * of the DB (including mirroring, updates and reloads). */ pthread_mutex_init (&database->mutex_lock, NULL); pthread_mutex_init (&database->mutex_clean_lock, NULL); /*rwl_init (&database->rwlock);*/ return(database); } /* find_database * Given the name of a database (e.g. "mae-east") return a pointer to * the irr_database_t sructure, or NULL if it does not exist */ irr_database_t *find_database (char *name) { irr_database_t *db; int len; LL_Iterate (IRR.ll_database, db) { len = strlen (db->name); if (strlen (name) > len) len = strlen (name); if (!strncasecmp (db->name, name, len)) return (db); } return (NULL); } /* irr_lock_all * Lock down all IRR database used by this IRR connection */ void irr_lock_all (irr_connection_t *irr) { irr_database_t *database; /* Avoid deadlock, only 1 routine can get all locks at one time */ trace (TRACE, default_trace, "About to lock --lock_all_mutex_lock--\n"); if (pthread_mutex_lock (&IRR.lock_all_mutex_lock) != 0) trace (ERROR, default_trace, "Error locking --lock_all_mutex_lock--: %s\n", strerror (errno)); else trace (TRACE, default_trace, "Locked --lock_all_mutex_lock--\n"); LL_ContIterate (irr->ll_database, database) { irr_lock (database); } if (pthread_mutex_unlock (&IRR.lock_all_mutex_lock) != 0) trace (ERROR, default_trace, "Error unlocking --lock_all_mutex_lock--: %s\n", strerror (errno)); else trace (TRACE, default_trace, "Unlocked --lock_all_mutex_lock--\n"); trace (TRACE, default_trace, "Done with lock_all\n"); } /* irr_unlock_all * Unlock down all IRR database used by this IRR connection */ void irr_unlock_all (irr_connection_t *irr) { irr_database_t *database; LL_ContIterate (irr->ll_database, database) { irr_unlock (database); } } void irr_update_lock (irr_database_t *database) { irr_clean_lock(database); irr_lock(database); } void irr_update_unlock (irr_database_t *database) { irr_unlock(database); irr_clean_unlock(database); } void irr_clean_lock (irr_database_t *database) { if (pthread_mutex_lock (&database->mutex_clean_lock) != 0) trace (ERROR, default_trace, "Error locking clean database %s : %s\n", database->name, strerror (errno)); } void irr_clean_unlock (irr_database_t *database) { if (pthread_mutex_unlock (&database->mutex_clean_lock) != 0) trace (ERROR, default_trace, "Error unlocking clean database %s : %s\n", database->name, strerror (errno)); } void irr_lock (irr_database_t *database) { /*if (rwl_writelock (&database->rwlock) != 0)*/ if (pthread_mutex_lock (&database->mutex_lock) != 0) trace (ERROR, default_trace, "Error locking database %s : %s\n", database->name, strerror (errno)); } void irr_unlock (irr_database_t *database) { if (pthread_mutex_unlock (&database->mutex_lock) != 0) /*if (rwl_writeunlock (&database->rwlock) != 0) */ trace (ERROR, default_trace, "Error unlocking database %s : %s\n", database->name, strerror (errno)); } void Delete_RT_Object (irr_route_object_t *attr) { Delete (attr); } int irr_comp (char *s1, char *s2) { return (strcmp (s1, s2)); } int get_prefix_from_disk (FILE *fp, u_long offset, char *buffer) { char *p, temp[BUFSIZE], *last; if (fseek (fp, offset, SEEK_SET) < 0) trace (NORM, default_trace, "** Error ** fseek failed in get_prefix"); if (fgets (temp, sizeof (temp) - 1, fp) != NULL) { if (strtok_r (temp, " ", &last) != NULL && (p = strtok_r (NULL, " ", &last)) != NULL) { if (*(p + strlen (p) - 1) == '\n') *(p + strlen (p) - 1) = '\0'; strcat (buffer + strlen (buffer), p); return 1; } else trace (NORM, default_trace, "ERROR -- get_prefix_from_disk(): bad route field (%s)\n", temp); } else trace (NORM, default_trace, "ERROR -- get_prefix_from_disk(): offset is bad %lu\n", offset); return -1; } /* copy_irr_object * Copy an object from one .db file to another * this is used in updates and in resyching database */ long copy_irr_object (FILE *src_fp, long offset, irr_database_t *database, u_long obj_length) { char buffer[BUFSIZE]; long start_offset = 0; int bytes_read = 0; int i; if ( database->db_fp == NULL ) return -1L; if ((fseek (src_fp, offset, SEEK_SET) < 0) || (fseek (database->db_fp, 0, SEEK_END) < 0)) trace (NORM, default_trace, "** Error ** fseek failed in copy_irr_object"); start_offset = ftell (database->db_fp); bytes_read = 0; while (fgets (buffer, sizeof (buffer)-1, src_fp) != NULL) { if ((i = strlen (buffer)) < 2) break; if (bytes_read < obj_length) { fwrite (buffer, 1, (size_t) i, database->db_fp); database->bytes += i; } bytes_read += i; } sprintf (buffer, "\n"); fwrite (buffer, 1, strlen (buffer), database->db_fp); database->bytes += strlen (buffer); return start_offset; } /* Delete_IRR_Object * Delete generic object used to hold data during scanning */ void Delete_IRR_Object (irr_object_t *object) { /* ll_as and ll_mbr_by_ref are saved as data for the hash */ if (object->name) Delete (object->name); if (object->nic_hdl) Delete (object->nic_hdl); if (object->ll_mnt_by) LL_Destroy (object->ll_mnt_by); if (object->ll_as) LL_Destroy (object->ll_as); if (object->ll_mbr_by_ref) LL_Destroy (object->ll_mbr_by_ref); if (object->ll_mbr_of) LL_Destroy (object->ll_mbr_of); if (object->ll_prefix) LL_Destroy (object->ll_prefix); Delete (object); } /* New_IRR_Object * Create generic object used to hold data during scanning */ irr_object_t *New_IRR_Object (char *buffer, u_long position, u_long mode) { irr_object_t *irr_object; irr_object = New (irr_object_t); irr_object->offset = position; irr_object->mode = mode; irr_object->type = NO_FIELD; irr_object->ll_prefix = LL_Create (LL_DestroyFunction, free, 0); irr_object->filter_val = XXX_F; return (irr_object); } void Delete_Ref_keys (reference_key_t *ref_item) { Delete (ref_item->key); Delete (ref_item); } void foldin_key_list (LINKED_LIST **ll, char *key, int type) { int found; reference_key_t *ref_item; if (key == NULL) return; if (*ll == NULL) /* this way we know if *ll != NULL, list is non-empty */ *ll = LL_Create (LL_DestroyFunction, Delete_Ref_keys, NULL); found = 0; LL_ContIterate ((*ll), ref_item) { if (!strcmp (ref_item->key, key)) { found = 1; break; } } if (!found) { ref_item = New (reference_key_t); ref_item->key = strdup (key); ref_item->type = type; LL_Add ((*ll), ref_item); } } void pick_off_indirect_references (irr_answer_t *irr_answer, LINKED_LIST **ll) { char *cp, buf[BUFSIZE]; enum STATES state = BLANK_LINE, save_state; int curr_f = NO_FIELD; if (irr_answer->type == ROUTE || irr_answer->type == PERSON) return; if (irr_answer->len == 0 || fseek (irr_answer->fp, irr_answer->offset, SEEK_SET) < 0) return; do { cp = fgets (buf, BUFSIZE - 1, irr_answer->fp); if ((state = get_state (buf, cp, state, &save_state)) == START_F) { curr_f = get_curr_f (buf); /* all fields here must be *single valued*, else code won't work */ if ((irr_answer->type == IPV6_SITE && (curr_f == PREFIX || curr_f == CONTACT)) || (curr_f == ADMIN_C || curr_f == TECH_C)) { cp = buf + key_info[curr_f][irr_answer->db_syntax].len; whitespace_newline_remove (cp); foldin_key_list (ll, cp, curr_f); } } } while (state != BLANK_LINE && state != DB_EOF); return; } void lookup_object_references (irr_connection_t *irr) { irr_answer_t *irr_answer; LINKED_LIST *ll = NULL; reference_key_t *ref; LL_ContIterate (irr->ll_answer, irr_answer) pick_off_indirect_references (irr_answer, &ll); if (ll) { LL_ContIterate (ll, ref) { /* printf("JW: indirect key lookup-(%s,%d)\n",ref->key,ref->type); */ irr_database_find_matches (irr, ref->key, SECONDARY, RAWHOISD_MODE, ref->type, NULL, NULL); } LL_Destroy (ll); } } void lookup_route_exact (irr_connection_t *irr, char *key) { irr_database_t *database; u_long offset, len = 0; char *last, *prefix, *str_orig; u_short origin; while (*key != '\0' && isspace ((int) *key)) key++; if (*key == '\0') return; if (strchr (key, '-') != NULL) prefix = strtok_r (key, "-", &last); else prefix = strtok_r (key, " ", &last); if (prefix == NULL) return; if ((str_orig = strtok_r (NULL, " ", &last)) == NULL) return; while (*str_orig != '\0' && !isdigit ((int) *str_orig)) str_orig++; if (*str_orig == '\0') return; origin = (u_short) atoi (str_orig); LL_Iterate (irr->ll_database, database) { if (seek_route_object (database, prefix, origin, &offset, &len, irr->withdrawn) > 0) break; } if (len > 0) irr_build_answer (irr, database->db_fp, ROUTE, offset, len, NULL, database->db_syntax); } /* convert a string to an unsigned long * return 1 if no errors found * otherwise return -1 */ int convert_to_lu (char *strval, u_long *uval) { char *p; u_long d; /* see if strval points to anything */ if (strval == NULL) return (-1); /* make sure the value is in range and has non-zero digits */ d = strtoul (strval, &p, 10); if (d == ULONG_MAX || p == strval) return (-1); *uval = d; return (1); } irr_hash_string_t *new_irr_hash_string (char *str) { irr_hash_string_t *tmp; tmp = New (irr_hash_string_t); tmp->string = strdup (str); return (tmp); } void delete_irr_hash_string (irr_hash_string_t *str) { Delete (str->string); Delete (str); } /* 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. * 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 pointer * along. * * Return: * length if a token is found (*x points to token, *y first token 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 a rpsl comment */ if (*x == NULL) return -1; /* find the first non-blank character */ *x = *y; while (**x != '\0' && (**x == ' ' || **x == '\t' || **x == '\n')) (*x)++; if (**x == '\0') return -1; /* find the first space at the end of the token */ *y = *x + 1; while (**y != '\0' && isgraph ((int) **y)) (*y)++; return (*y - *x); } /* * -i performs inverse lookups on the given attribute name. * Return 1 if attribute name valid/supported, else return -1. */ int ripe_inverse_attr (irr_connection_t *irr, char **cp) { int attrname_len; char *p, *q; p = q = *cp; if ( (attrname_len = find_token (&p, &q)) < 0) return attrname_len; *cp = q; /* We only support the mnt-by attribute for inverse lookups for now, so lets just do a quick dirty check */ if ( attrname_len == 6 && !strncasecmp (p, "mnt-by", 6)) { irr->inverse_type = MNT_BY; return 1; } return -1; } /* * -s flag has been found, now return the token after the '-s'. * Return 1 if something is found after the -s, else return -1. */ int ripe_set_source (irr_connection_t *irr, char **cp) { int sources_len; char *p, *q; p = q = *cp; if ( (sources_len = find_token (&p, &q)) < 0) return sources_len; *cp = q; if (sources_len >= RIPE_SOURCES_SZ) return -1; strncpy (irr->ripe_sources, p, sources_len); irr->ripe_sources[sources_len] = '\0'; return 1; } /* * convert a "" in a '-t' flag from string to enum IRR_OJBECT. * return 1 if a valid object type is found, else return -1; */ int ripe_obj_type (irr_connection_t *irr, char **cp) { int i, j, slen, ret_code = -1; char *p, *q; p = q = *cp; if ((slen = find_token (&p, &q)) < 0) return slen; *cp = q; for (i = 0; i < IRR_MAX_MCMDS; i++) { j = strlen (m_info[i].command) - 1; if (slen == j && !strncasecmp (p, m_info[i].command, j)) { irr->ripe_type = m_info[i].type; ret_code = 1; break; } } return ret_code; } void ignore_flag_param (irr_connection_t *irr, char **cp) { while (**cp != '\0' && isgraph ((int) **cp)) (*cp)++; } /* * Parse the ripe whois command line and set the corresponding flag * (and flag value if necessary). * This routine moves the cursor pointer used by the ripe command * processing routine. * * RETURN values: * * return 1 if no flags encountered or supplied flags were * parsed without error. * * return -1 if an error is encountered parsing * a flag. */ int parse_ripe_flags (irr_connection_t *irr, char **cp) { int non_flag_token; char buf[BUFSIZE]; char *p = *cp; /* save pointer to input line for trace output */ /* buffer for error messages */ buf[0] = '\0'; for (non_flag_token = 0; **cp != '\0'; (*cp)++) { if (**cp == '-') { (*cp)++; switch (**cp) { case 'F': irr->ripe_flags |= FAST_OUT; break; case 'r': irr->ripe_flags |= RECURS_OFF; break; case 'l': irr->ripe_flags |= LESS_ONE; break; case 'L': irr->ripe_flags |= LESS_ALL; break; case 'm': irr->ripe_flags |= MORE_ONE; break; case 'M': irr->ripe_flags |= MORE_ALL; break; case 'a': irr->ripe_flags |= SOURCES_ALL; break; case 'i': (*cp)++; if (ripe_inverse_attr (irr, cp) > 0) irr->ripe_flags |= INVERSE_ATTR; else { strcpy (buf, "%% Attribute name after \"-i\"" " is invalid or unsupported.\n"); non_flag_token = 1; } break; case 's': (*cp)++; if (ripe_set_source (irr, cp) > 0) irr->ripe_flags |= SET_SOURCE; else { strcpy (buf, "%% DB source after \"-s\"" " flag is too long or invalid.\n"); non_flag_token = 1; } break; case 't': (*cp)++; if (ripe_obj_type (irr, cp) > 0) irr->ripe_flags |= TEMPLATE; else strcpy (buf, "%% Required object type not specified after \"-t\" flag or unrecognized type.\n"); non_flag_token = 1; break; case 'T': (*cp)++; if (ripe_obj_type (irr, cp) > 0) irr->ripe_flags |= OBJ_TYPE; else { strcpy (buf, "%% Required object type not specified after \"-T\" flag or unrecognized type.\n"); non_flag_token = 1; } break; case 'V': ignore_flag_param (irr, cp); break; default : non_flag_token = 1; sprintf (buf, "%% Unrecognized flag \"-%c\".\n", **cp); trace (NORM, default_trace, "ERROR:parse_ripe_flags(): unrecognized flag '%c'\n", **cp); trace (NORM, default_trace, "ERROR:parse_ripe_flags(): rest of input line (%s)", p); (*cp)--; break; } } else if (**cp == ' ' || **cp == '\t') continue; else non_flag_token = 1; if (non_flag_token) break; } if (buf[0] != '\0') { /* Got an error, tell the user */ irr_write (irr, buf, strlen (buf)); irr_write_buffer_flush (irr); return -1; } return 1; } void IRRD_Delete_Node (radix_node_t *node) { LINKED_LIST *ll_attr; ll_attr = (LINKED_LIST *) node->data; if (ll_attr) LL_Destroy (ll_attr); if (node->prefix) Delete_Prefix (node->prefix); Delete (node); } radix_str_t *new_radix_str (radix_node_t *node) { radix_str_t *tmp; tmp = New (radix_str_t); tmp->ptr = node; return (tmp); } void delete_radix_str (radix_str_t *str) { IRRD_Delete_Node (str->ptr); Delete (str); } /* radix_flush * Delete a radix tree. Called by database_clear */ void radix_flush (radix_tree_t *radix_tree) { radix_node_t *node = NULL; radix_str_t tmp; LINKED_LIST *ll_nodes; if (radix_tree == NULL) return; ll_nodes = LL_Create (LL_Intrusive, True, LL_NextOffset, LL_Offset (&tmp, &tmp.next), LL_PrevOffset, LL_Offset (&tmp, &tmp.prev), LL_DestroyFunction, delete_radix_str, 0); RADIX_WALK_ALL (radix_tree->head, node) { LL_Add (ll_nodes, new_radix_str (node)); } RADIX_WALK_END; LL_Destroy (ll_nodes); Delete (radix_tree); } void convert_toupper(char *_z) { while (*_z != '\0') { *_z = toupper((int) *_z); _z++; } } /* convert time_t to something more readable */ void nice_time (long seconds, char *buf) { if (seconds <= 0) { sprintf (buf, "--:--:--"); return; } if (seconds < 200) { sprintf (buf, "%ld seconds", seconds); return; } if (seconds / 3600 > 99) sprintf (buf, "%02lddy%02ldhr", seconds / (3600 * 24), (seconds % (3600 * 24)) / 3600); else sprintf (buf, "%02ld:%02ld:%02ld", seconds / 3600, (seconds / 60) % 60, seconds % 60); return; } /* irr_sort_database * A real hack to alphabetically sort databases after reading the config file * We need to convert linked_list types as the sorting code for intrusive * is broken. Easier to do this than fix the sorting code... * */ static int compare_db (irr_database_t *db1, irr_database_t *db2) { return (strcmp (db1->name, db2->name)); } void irr_sort_database () { LINKED_LIST *ll_tmp; irr_database_t *db = NULL; ll_tmp = LL_Create (LL_CompareFunction, compare_db, NULL); LL_Iterate (IRR.ll_database, db) { LL_Add (ll_tmp, db); } LL_Sort (ll_tmp); /*LL_Clear (IRR.ll_database_alphabetized);*/ db = NULL; LL_Iterate (ll_tmp, db) { LL_Add (IRR.ll_database_alphabetized, db); } LL_Destroy (ll_tmp); } /* Send a message to the user and get a yes or no response. * Terminate execution if user replies 'y' or 'Y' to the message. * * Anything other than 'n' or 'N' is considered a 'yes' * response. * * Input: * -a message to send to the user (msg) * * Return: * -void */ void interactive_io (char *msg) { char buf[BUFSIZE+1]; /* interactive input from the user */ trace (NORM, default_trace, "%s [y/n]\n", msg); printf ("%s [y/n] ", msg); buf[0] = '\0'; fgets (buf, BUFSIZE, stdin); if (buf[0] == 'n' || buf[0] == 'N') { trace (NORM, default_trace, "Terminating execution at user's request.\n"); printf ("Terminating execution at user's request.\n"); exit (0); } trace (NORM, default_trace, "Continuing execution.\n"); printf ("Continuing execution.\n"); } /* 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[BUFSIZE+1]; FILE *fp; /* Sanity checks */ if (dir == NULL) return strdup ("No database directory specified!"); if (strlen (dir) > BUFSIZE) return strdup ("Database directory name is too long! MAX chars (BUFSIZE)"); /* see if we can create a temp file in the directory */ snprintf (file, BUFSIZE, "%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, BUFSIZE, "Could not create the directory: %s", strerror (errno)); return strdup (file); } else { fclose (fp); remove (file); } return NULL; }