/* * $id: uii_commands.c,v 1.1.1.1 1998/08/10 20:06:56 dogcow Exp $ * originally Id: uii_commands.c,v 1.19 1998/06/15 16:20:36 gerald Exp */ #include #include #include "mrt.h" #include "trace.h" #include #include #include "config_file.h" #include #include #include "irrd.h" #include #include #ifndef SETPGRP_VOID #endif #ifndef localtime_r /* Not present on SunOS, at least. */ #define localtime_r(a,b) localtime(a) #endif extern trace_t *default_trace; /* Handler routines for IRR lookups * the show_xxx functions are human readable UII commands * the irr_xxx are used by the RAWhoisd machine telnet interface */ void uii_show_ip_less (uii_connection_t *uii, prefix_t *prefix, int flag); void uii_show_ip_exact (uii_connection_t *uii, prefix_t *prefix); void uii_show_ip_more (uii_connection_t *uii, prefix_t *prefix); static void run_cmd (char *cmd, FILE **in, FILE **out); void show_database (uii_connection_t *uii) { irr_database_t *database; char buf[BUFSIZE], tmp[BUFSIZE], *p_last_export; int total_size, total_rt, total_aut; uii_add_bulk_output (uii, "Listening on port %d (fd=%d)", IRR.irr_port, IRR.sockfd); if (IRR.whois_port > 0) { uii_add_bulk_output (uii, ", and port %d (fd=%d)", IRR.whois_port, IRR.sockfd); } uii_add_bulk_output (uii, "\r\n"); uii_add_bulk_output (uii, "Memory-only indexing\r\n"); if (IRR.database_syntax == RIPE181) { uii_add_bulk_output (uii, "RIPE181 Syntax\r\n\r\n"); } else if (IRR.database_syntax == RPSL) { uii_add_bulk_output (uii, "RPSL Syntax\r\n\r\n"); } else if (IRR.database_syntax == EMPTY_DB) { uii_add_bulk_output (uii, "Unknown Syntax, all DB's empty\r\n\r\n"); } else if (IRR.database_syntax == MIXED) { uii_add_bulk_output (uii, "RIPE181 and RPSL Syntaxes\r\n\r\n"); } else { uii_add_bulk_output (uii, "** Unknown Database Syntax\r\n\r\n"); } uii_add_bulk_output (uii, "Default Database Query Order: "); LL_Iterate (IRR.ll_database, database) { uii_add_bulk_output (uii, "%s ", database->name); } uii_add_bulk_output (uii, "\r\n\r\n"); uii_add_bulk_output (uii, "%10s %-9s %6s %10s %8s %s", "Database", "Size (kb)", "Rt Obj", "AutNum Obj", "Serial #", "Last Export #\r\n"); uii_add_bulk_output (uii, "%6s %-9s %6s %10s %4s %s", "-------------", "---------", "------", "----------", "--------", "-------------\r\n"); total_size = total_rt = total_aut = 0; LL_Iterate (IRR.ll_database_alphabetized, database) { sprintf (buf, "%8.1f", ((float) database->bytes / 1000.0)); uii_add_bulk_output (uii, " %-12s %9s", database->name, buf); strcpy (tmp, database->name); convert_toupper (tmp); p_last_export = GetStatusString (IRR.statusfile, tmp, "lastexport"); uii_add_bulk_output (uii, " %6d %10d %8d %13s\r\n", database->num_objects[ROUTE], database->num_objects[AUT_NUM], database->serial_number, (p_last_export == NULL) ? "" : p_last_export); total_size += database->bytes; total_rt += database->num_objects[ROUTE]; total_aut += database->num_objects[AUT_NUM]; } sprintf (buf, "%8.1f", ((float) total_size / 1000.0)); uii_add_bulk_output (uii, " %-12s %9s %6d %10d\r\n", "TOTAL", buf, total_rt, total_aut); uii_add_bulk_output (uii, "\r\n"); LL_Iterate (IRR.ll_database_alphabetized, database) { uii_add_bulk_output (uii, "%s ", database->name); if (database->flags & IRR_AUTHORITATIVE) uii_add_bulk_output (uii, " AUTHORITATIVE"); if (database->obj_filter > 0) uii_add_bulk_output (uii, " (filter 0x%x)", database->obj_filter); uii_add_bulk_output (uii, "\r\n"); /* mirroring */ if (database->mirror_prefix != NULL) { nice_time (time_left (database->mirror_timer), buf); uii_add_bulk_output (uii, " Mirroring %s:%d (Next in %s)\r\n", prefix_toa (database->mirror_prefix), database->mirror_port, buf); if (database->mirror_error_message[0] != '\0') { if (database->mirror_error_message[strlen(database->mirror_error_message) -1] == '\n') database->mirror_error_message[strlen(database->mirror_error_message) -1] = '\0'; uii_add_bulk_output (uii, " %s \r\n", database->mirror_error_message); } if (database->mirror_fd != -1) { uii_add_bulk_output (uii, " MIRRORING (%d bytes) (%d seconds)\r\n", database->mirror_update_size, time (NULL) - database->mirror_started); } else if (database->last_mirrored <= 0) { uii_add_bulk_output (uii, " Never mirrored\r\n"); } else { char tmpt[BUFSIZE]; strftime (tmpt, BUFSIZE, "%T %m/%d/%Y", localtime_r ((time_t *) &database->last_mirrored, &my_tm)); uii_add_bulk_output (uii, " Last mirrored %s\r\n", tmpt); uii_add_bulk_output (uii, " %d bytes, %d change(s)\r\n", database->mirror_update_size, database->num_changes); } } /* * mirroring not turned on * Either loaded, or email */ else { char tmpt[BUFSIZE]; if (database->flags & IRR_AUTHORITATIVE) { if (database->last_update == 0) uii_add_bulk_output (uii, " Last email/tcp update Never\r\n"); else { strftime (tmpt, BUFSIZE, "%T %m/%d/%Y", localtime_r ((time_t *) &database->last_update, &my_tm)); uii_add_bulk_output (uii, " Last email/tcp update %s\r\n", tmpt); } } if (database->time_loaded == 0) uii_add_bulk_output (uii, " Last loaded Never\r\n"); else { strftime (tmpt, BUFSIZE, "%T %m/%d/%Y", localtime_r ((time_t *) &database->time_loaded, &my_tm)); uii_add_bulk_output (uii, " Last loaded %s\r\n", tmpt); } } if (database->export_timer != NULL) { nice_time (time_left (database->export_timer), buf); uii_add_bulk_output (uii, " Next export in %s\r\n", buf); } if (database->no_dbclean) { uii_add_bulk_output (uii, " Cleaning disabled\r\n"); } else if (database->clean_timer != NULL) { nice_time (time_left (database->clean_timer), buf); uii_add_bulk_output (uii, " Next dbclean in %s\r\n", buf); } } uii_send_bulk_data (uii); } /* uii_irr_reload * Reload the database */ void uii_irr_reload (uii_connection_t *uii, char *name) { if (name != NULL) { irr_reload_database (name, uii, NULL); Delete (name); } } #ifndef NT /* Invoke irrdcacher to fetch a DB and call 'irr_reload_database ()' * to reload it. * * sample uii command: 'irrdcacher bell' * * Function can be called from the uii or from 'irr_load_data ()' * which is invoked in main.c on bootstrap. 'irr_load_data ()' * loads the DB atomically * * Function relies on irrdcacher and wget being installed and in * the search path. The search path can be augmented with the * 'irr_path' config command. There are no assumptions about * the availability of irrdcacher and if found this routine will * parse the irrdcacher output to determine the result. * * Input: * -data struct for communicating with UII users (uii) * -DB name to fetch (name) * * Return: * -1 if the DB wsa fetched and reloaded without error * -0 otherwise */ int uii_irr_irrdcacher (uii_connection_t *uii, char *name) { int ret_code = 0; FILE *pipeout; char cmd[BUFSIZE + 1], cs[BUFSIZE + 1], wflag[BUFSIZE + 1], *tmp_dir; irr_database_t *db; regex_t url_re = {0}, good_re = {0}, notfound_re = {0}; char *ftp_url = "^ftp://[^ \t/]+/[[:graph:]]+$"; char *good = "^Successful operation"; char *notfound = "not found[ \t]*\n$"; /* sanity checks */ /* do we know about this DB? */ if (name == NULL || (db = find_database (name)) == NULL) { if (uii != NULL) uii_send_data (uii, "Unknown DB (%s)\r\n", ((name == NULL) ? "NULL" : name)); goto ABORT_IRRDCACHER; } /* it's a mistake to fetch an authoritative DB */ else if (db->flags & IRR_AUTHORITATIVE) { if (uii != NULL) uii_send_data (uii, "ERROR: (%s) is declared \"authoritative\" \r\n", name); goto ABORT_IRRDCACHER; } /* see if a remote ftp dir has been configured */ else if (db->remote_ftp_url == NULL) { if (uii != NULL) { uii_send_data (uii, "No ftp remote directory has been configured for (%s)\r\n", name); uii_send_data (uii, "Type \"config\" and then \"irr_database %s remote_ftp_url \"\r\n", name); } goto ABORT_IRRDCACHER; } /* compile our reg ex's */ regcomp (&url_re, ftp_url, REG_EXTENDED|REG_NOSUB); regcomp (&good_re, good, REG_EXTENDED|REG_NOSUB); regcomp (¬found_re, notfound, REG_EXTENDED|REG_NOSUB); /* do we have a well formed ftp URL? */ if (regexec (&url_re, db->remote_ftp_url, 0, NULL, 0)) { if (uii != NULL) { uii_send_data (uii, "Invalid ftp URL for (%s)!\r\n", db->name); uii_send_data (uii, "Expect (ftp:///)\r\n", name); } goto ABORT_IRRDCACHER; } /* build the irrdcacher command invokation */ /* IRR.path is used to add a component to the current path, ie, * might be needed to find irrdcacher and wget */ cmd[0] = wflag[0] = '\0'; if (IRR.path != NULL) { sprintf (cmd, "PATH=${PATH}%s%s;export PATH;", ((*IRR.path == ':') ? "" : ":"), IRR.path); sprintf (wflag, "-w %s", IRR.path); } #ifdef HAVE_WGET { char *p; if (wflag[0] == '\0') sprintf (wflag, "-w "); sprintf (&wflag[strlen (wflag)], ":%s", WGET_CMD); if ((p = strrchr (wflag, '/')) != NULL) *p = '\0'; } #endif trace (NORM, default_trace, "JW: wflag (%s)\n", wflag); /* dir for irrdcacher to use to place to bring the files onto our host */ if ((tmp_dir = IRR.tmp_dir) == NULL) tmp_dir = "/var/tmp"; /* build the irrdcacher invokation */ #ifdef HAVE_IRRDCACHER sprintf (&cmd[strlen (cmd)], "%s", IRRDCACHER_CMD); #else sprintf (&cmd[strlen (cmd)], "%s", "irrdcacher"); #endif sprintf (&cmd[strlen (cmd)], " -S %s -s %s %s.db.gz ", wflag, db->remote_ftp_url, name); /* we need the *.CURRENTSERIAL file also for mirrored files */ if (db->mirror_prefix != NULL) { /* need to make the DB name upper case */ strcpy (cs, name); convert_toupper (cs); sprintf (&cmd[strlen (cmd)], " %s.CURRENTSERIAL", cs); } trace (NORM, default_trace, "JW: cmd (%s)\n", cmd); if (uii != NULL) uii_send_data (uii, "Fetching DB remotely. Please be patient...\r\n"); /* invoke irrdcacher */ run_cmd (cmd, NULL, &pipeout); if (pipeout == NULL) { if (uii != NULL) uii_send_data (uii, "irrdcacher not found or no \"sh\" found.\r\n"); goto ABORT_IRRDCACHER; } /* parse the irrdcacher output, look at the last line only */ cmd[0] = '\0'; while (fgets (cmd, BUFSIZE, pipeout) != NULL) { trace (NORM, default_trace, "irrdcacher: %s", cmd); if (uii != NULL) uii_send_data (uii, "%s", cmd); } fclose (pipeout); /* move the DB into our cache and reload */ if (regexec (&good_re, cmd, 0, NULL, 0) == 0) ret_code = irr_reload_database (name, uii, tmp_dir); /* "sh" not installed */ else if (cmd[0] == '\0') { if (uii != NULL) uii_send_data (uii, "/bin/sh not installed. Could not run irrdacher.\r\n"); } /* irrdcacher not found */ else if (regexec (¬found_re, cmd, 0, NULL, 0) == 0) { if (uii != NULL) { uii_send_data (uii, "Couldn't find irrdcacher on your system\r\n"); uii_send_data (uii, "irrd requires both 'irrdcacher' and 'wget' for remote ftp access\r\n"); uii_send_data (uii, "Reset your path with \"irr_path\". eg, irr_path /usr/mybin\r\n"); } } else if (uii != NULL) { uii_send_data (uii, "%s\n", cmd); uii_send_data (uii, "Operation unsuccessful!\n"); } ABORT_IRRDCACHER: /* clean up */ Delete (name); regfree (&good_re); regfree (&url_re); regfree (¬found_re); return ret_code; } #endif /* NT */ void uii_export_database (uii_connection_t *uii, char *name) { irr_database_t *db; db = find_database (name); if (IRR.ftp_dir == NULL) { uii_send_data (uii, "Error -- no ftp directory configured.\r\n"); Delete (name); return; } if (db != NULL) { uii_send_data (uii, "Exporting %s database to %s...\r\n", name, IRR.ftp_dir); if (irr_database_export (db) != 1) { uii_send_data (uii, "ERROR exporting %s database -- see log for more information\r\n", name); } else { uii_send_data (uii, "Export successful for %s\r\n", name); if (db->export_timer != NULL) Timer_Reset_Time (db->export_timer); } } else uii_send_data (uii, "Could not find %s database...\r\n", name); Delete (name); return; } void uii_irr_clean (uii_connection_t *uii, char *name) { irr_database_t *db; db = find_database (name); if (db != NULL) { uii_send_data (uii, "Cleaning %s database...\r\n", name); irr_database_clean (db); if (db->clean_timer != NULL) Timer_Reset_Time (db->clean_timer); } else uii_send_data (uii, "Could not find %s database...\r\n", name); Delete (name); return; } void uii_irr_mirror_last (uii_connection_t *uii, char *name) { uii_irr_mirror (uii, name, 0); } void uii_irr_mirror_serial (uii_connection_t *uii, char *name, int serial) { uii_irr_mirror (uii, name, serial); } void uii_irr_mirror (uii_connection_t *uii, char *name, int serial) { irr_database_t *database; database = find_database (name); if (database == NULL) { config_notice (ERROR, uii, "Database not found\r\n"); Delete (name); return; } if (database->mirror_prefix == NULL) { uii_send_data (uii, "Don't know how to mirror %s\r\n", database->name); return; } uii_send_data (uii, "Mirroring %s database...\r\n", name); uii_send_data (uii, "Current serial number %d\r\n", database->serial_number); if (serial > 0) uii_send_data (uii, "serial to: %d\n", serial); else uii_send_data (uii, "serial to: LAST\n"); uii_send_data (uii, "Starting mirror in the background......\r\n", database->serial_number); if (request_mirror (database, uii, serial) == -1) { uii_send_data (uii, "\r\n** ERROR ** mirroring database!\r\n"); uii_send_data (uii, "See logfile for more information\r\n"); Delete (name); return; } if (database->mirror_timer != NULL) Timer_Reset_Time (database->mirror_timer); Delete (name); return; } /* mainly for debugging, but would be nice to be able to set the serial number */ void uii_set_serial (uii_connection_t *uii, char *name, int serial) { irr_database_t *database; if ((database = find_database (name)) != NULL) { database->serial_number = serial; Delete (name); return; } uii_send_data (uii, "%s database not found\r\n", name); Delete (name); } void uii_show_ip (uii_connection_t *uii, prefix_t *prefix, int num, char *lessmore) { if (num == 0) { uii_show_ip_exact (uii, prefix); return; } if (!strcmp (lessmore, "less")) { uii_show_ip_less (uii, prefix, 1); return; } else { uii_show_ip_more (uii, prefix); return; } /*NOTREACHED*/ return; } void uii_fetch_irr_object (uii_connection_t *uii, irr_database_t *database, u_long offset) { char buffer[BUFSIZE+1]; fseek (database->db_fp, offset, SEEK_SET); while (fgets (buffer, BUFSIZE, database->db_fp) != NULL) { buffer[strlen(buffer) -1] = '\0'; /* lose newline */ if (strlen (buffer) < 2) break; uii_add_bulk_output (uii, "%s\r\n", buffer); } } void uii_show_ip_exact (uii_connection_t *uii, prefix_t *prefix) { radix_node_t *node; LINKED_LIST *ll_attr; irr_route_object_t *attr; irr_database_t *database; int first = 1; LL_Iterate (IRR.ll_database, database) { irr_lock (database); node = radix_search_exact (database->radix, prefix); if (node != NULL) { ll_attr = (LINKED_LIST *) node->data; LL_Iterate (ll_attr, attr) { if (first != 1) uii_add_bulk_output (uii, "\r\n"); first = 0; uii_fetch_irr_object (uii, database, attr->offset); } } irr_unlock (database); } uii_send_bulk_data (uii); } void uii_show_ip_less (uii_connection_t *uii, prefix_t *prefix, int flag) { radix_node_t *node; LINKED_LIST *ll_attr; irr_route_object_t *attr; irr_database_t *database; prefix_t *tmp_prefix; int first = 1; LL_Iterate (IRR.ll_database, database) { irr_lock (database); tmp_prefix = prefix; /* first check if this node exists */ if ((node = radix_search_exact (database->radix, prefix)) != NULL) { ll_attr = (LINKED_LIST *) node->data; LL_Iterate (ll_attr, attr) { if (first != 1) uii_add_bulk_output (uii, "\r\n"); first = 0; uii_fetch_irr_object (uii, database, attr->offset); } } /* now check all less specific */ while ((flag == 1 || node == NULL) && (node = radix_search_best (database->radix, tmp_prefix, 0)) != NULL) { if (node != NULL) { tmp_prefix = node->prefix; ll_attr = (LINKED_LIST *) node->data; LL_Iterate (ll_attr, attr) { if (first != 1) uii_add_bulk_output (uii, "\r\n"); first = 0; uii_fetch_irr_object (uii, database, attr->offset); } } /* break out after one loop for "l" case */ if (flag == 0) break; } irr_unlock (database); } uii_send_bulk_data (uii); } /* Route searches. M - all more specific eg, !r199.208.0.0/16,M */ /* Also does m - one level only more specific */ void uii_show_ip_more (uii_connection_t *uii, prefix_t *prefix) { radix_node_t *node, *start_node, *last_node; LINKED_LIST *ll_attr; irr_route_object_t *attr; irr_database_t *database; int first = 1; int len; LL_Iterate (IRR.ll_database, database) { irr_lock (database); last_node = NULL; start_node = NULL; node = NULL; /* first, try to find this prefix exactly */ start_node = radix_search_exact (database->radix, prefix); /* fine, try to find something less specific */ if (start_node == NULL) { start_node = radix_search_best (database->radix, prefix, 0); } /* yuck -- walk up the radix tree until find more specific that * does not match */ if (start_node == NULL) { prefix_t *tmp_prefix; len = prefix->bitlen; prefix->bitlen = 32; tmp_prefix = prefix; while ((start_node = radix_search_best (database->radix, tmp_prefix, 0)) != NULL) { if (!comp_with_mask ((void *) prefix_tochar (start_node->prefix), (void *) prefix_tochar (prefix), len)) break; tmp_prefix = start_node->prefix; last_node = start_node; } prefix->bitlen = len; if (start_node == NULL) start_node = last_node; } if (start_node != NULL) { RADIX_WALK (start_node, node) { if ((node->prefix->bitlen >= prefix->bitlen) && (comp_with_mask ((void *) prefix_tochar (node->prefix), (void *) prefix_tochar (prefix), prefix->bitlen))) { ll_attr = (LINKED_LIST *) node->data; LL_Iterate (ll_attr, attr) { if (first != 1) { uii_add_bulk_output (uii, "\r\n"); } first = 0; uii_fetch_irr_object (uii, database, attr->offset); } } } RADIX_WALK_END; } irr_unlock (database); } uii_send_bulk_data (uii); } /* uii_delete_route * mainly for testing/debugging */ int uii_delete_route (uii_connection_t *uii, char *name, prefix_t *prefix, int as) { irr_database_t *db; irr_object_t *irr_object; db = find_database (name); if (db == NULL) { uii_send_data (uii, "Could not find %s database...\r\n", name); Delete (name); return (-1); } Delete (name); irr_object = New (irr_object_t); irr_object->type = ROUTE; irr_object->name = prefix_toax (prefix); irr_object->origin = as; if ((irr_object = load_irr_object (db, irr_object)) == NULL) { uii_send_data (uii, "Could not find find %s...\r\n", prefix_toax (prefix)); return (-1); } if (delete_irr_route (db, irr_object->name, irr_object)) uii_send_data (uii, "Deleted\r\n"); else uii_send_data (uii, "Deleted failed!\r\n"); return (1); } int uii_read_update_file (uii_connection_t *uii, char *file, char *name) { irr_database_t *db; u_long new; FILE *fp; db = find_database (name); if (db == NULL) { uii_send_data (uii, "Could not find %s database...\r\n", name); Delete (name); return (-1); } Delete (name); if ((fp = fopen (file, "r")) == NULL) { uii_send_data (uii, "Could not open %s\r\n", file); return (-1); } /* just kill of the start line */ valid_start_line (db, fp, &new); irr_update_lock (db); if (scan_irr_file (db, "load", 2, fp) != NULL) { trace (NORM, default_trace, "Read of updates failed: serial number unchanged: %d\n", db->serial_number); } irr_update_unlock (db); fclose (fp); return (1); } void run_cmd (char *cmd, FILE **in, FILE **out) { int pin[2], pout[2]; int pid; int omask, pstat; pid_t ppid; extern int errno; if (in != NULL) *in = NULL; if (out != NULL) *out = NULL; if (in != NULL) pipe (pin); if (out != NULL) pipe (pout); /* if (fork() == 0) { */ pid = fork(); if (pid == 0) { /* We're the child */ if (in != NULL) { close (pin[1]); dup2 (pin[0], 0); close (pin[0]); } if (out != NULL) { close (pout[0]); dup2 (pout[1], 1); dup2 (pout[1], 2); close (pout[1]); } execlp("/bin/sh", "sh", "-c", cmd, NULL); _exit(127); } /* Only get here if we're the parent */ if (out != NULL) { close (pout[1]); *out = fdopen (pout[0], "r"); } if (in != NULL) { close (pin[0]); *in = fdopen (pin[1], "w"); } omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); do { ppid = waitpid(pid, (int *) &pstat, 0); } while (pid == -1 && errno == EINTR); (void)sigsetmask(omask); } int kill_irrd (uii_connection_t *uii) { uii_send_data (uii, "Are you sure (yes/no)? "); if (uii_yes_no (uii)) { uii_send_data (uii, "Exit requested\n"); exit(0); /* XXX this code does not work -ljb */ /* need to mutex_init(irr->lock_all_mutex_lock) ? */ #ifdef notdef irr = New (irr_connection_t); irr->ll_database = IRR.ll_database; irr_lock_all (irr); mrt_set_force_exit (MRT_FORCE_EXIT); #endif } return (0); }