/* * $Id: commands.c,v 1.40 2002/10/22 18:47:47 ljb Exp $ * originally Id: commands.c,v 1.101 1998/08/07 00:13:37 gerald Exp */ #include #include #include "mrt.h" #include "trace.h" #include #include #include "config_file.h" #include #include #include "radix.h" #include "hash.h" #include "stack.h" #include #include "irrd.h" #include #ifndef NT #include #endif /* NT */ extern trace_t *default_trace; extern char *obj_template[]; /* m_info [] uses 'enum IRR_OBJECTS' in scan.h as the second * field in the struct. This means changes to m_info [] * or 'enum IRR_OBJECTS' need to be coordinated. For example, * if a new object type is being added then a new entry should * be appended to this array and a new type added to 'enum IRR_OBJECTS'. * * key_info [] in scan.c and object_templates [] in templates.c * also depend on 'enum IRR_OBJECTS' and so changes to any one * of these structs needs to be coordinated. */ m_command_t m_info [] = { { "an,", AUT_NUM }, /* 0 */ { "aut-num,", AUT_NUM }, /* 1 */ { "am,", AS_MACRO }, /* 2 */ { "as-macro,", AS_MACRO }, /* 3 */ { "as-set,", AS_SET }, /* 4 */ { "route-set,", RS_SET }, /* 5 */ { "cm,", COMMUNITY }, /* 6 */ { "community,", COMMUNITY }, /* 7 */ { "mt,", MAINT }, /* 8 */ { "mntner,", MAINT }, /* 9 */ { "ir,", INET_RTR }, /* 10 */ { "inet-rtr,", INET_RTR }, /* 11 */ { "ro,", ROLE }, /* 12 */ { "role,", ROLE }, /* 13 */ { "pn,", PERSON }, /* 14 */ { "person,", PERSON }, /* 15 */ { "in,", INETNUM }, /* 16 */ { "inetnum,", INETNUM }, /* 17 */ { "rt,", ROUTE}, /* 18 */ { "route,", ROUTE}, /* 19 */ { "filter-set,", FILTER_SET}, /* 20 */ { "rtr-set,", RTR_SET}, /* 21 */ { "peering-set,", PEERING_SET}, /* 22 */ { "key-cert,", KEY_CERT}, /* 23 */ { "dictionary,", DICTIONARY}, /* 24 */ { "repository,", REPOSITORY}, /* 25 */ { "i6,", INET6NUM}, /* 26 */ { "inet6num,", INET6NUM}, /* 27 */ { "dom-prefix,", DOMAIN_PREFIX},/* 28 */ { "dp,", DOMAIN_PREFIX}, /* 29 */ { "domain,", DOMAIN}, /* 30 */ { "dn,", DOMAIN} /* 31 */ /* defined in irrd.h #define IRR_MAX_MCMDS 32 */ }; /* 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 */ static void return_version (irr_connection_t *irr); void update_autnum_list (int expand_flag, LINKED_LIST *ll_macro_examined, LINKED_LIST *ll_aslist, LINKED_LIST *ll_as, STACK **stack, char *db, irr_connection_t *irr); void irr_exact (irr_connection_t *irr, prefix_t *prefix, int flag); void irr_less_all (irr_connection_t *irr, prefix_t *prefix, int flag, int mode); void irr_show_sources (irr_connection_t *irr); int irr_set_sources (irr_connection_t *irr, char *sources, int mode); int irr_set_ALL_sources (irr_connection_t *irr, int mode); void irr_more_all (irr_connection_t *irr, prefix_t *prefix, int mode); void irr_ripewhois(irr_connection_t *irr); void irr_m_command (irr_connection_t *irr); void irr_mntbyobjs (irr_connection_t *irr, enum IRR_OBJECTS type, int mode, char *key); void show_gas_answer (irr_connection_t *irr, char *key); void irr_journal_range (irr_connection_t *irr, char *db); void irr_journal_add_answer (irr_connection_t *irr); /* irr_process_command * read/parse the !command and call the appropriate handler */ void irr_process_command (irr_connection_t * irr) { char tmp[BUFSIZE]; char *return_str = NULL; int update_done = 0, i; int n_updates; int tmp_fd; if (irr->state == IRR_MODE_LOAD_UPDATE) { irr->begin_line = !irr->line_cont; i = strlen (irr->cp); irr->line_cont = (*(irr->cp + i - 1) != '\n'); /* This section of code requires a bit of explanation. The * irr_read_command () function in telnet.c uses a fixed * size memory buffer. So to get around the problem of a * !us...!ue line exceeding the buffer size, irr_read_command () * may send an incomplete line (ie, a line *not* terminated by a * '\n'). This means we need to detect line continuation so we * can check for the update termination sequence "!ue\n" as * "!ue\n" could occur on a boundry at the end of a read and * the begining of the next read. Finally, irr_read_command () * will send lines terminate by a "\n" unless the line fills the * buffer. This simplifies the processing of the code below. */ if (irr->begin_line) { irr->ue_test[0] = '\0'; if (irr->line_cont) { if (i < 4) { strcpy (irr->ue_test, irr->cp); return; } } else if (!strncasecmp (irr->cp, "!ue", 3)) update_done = 1; } else if (!irr->line_cont && i < 4) { i = strlen (irr->ue_test); strcat (irr->ue_test, irr->cp); if (!strncasecmp (irr->ue_test, "!ue", 3)) update_done = 1; irr->ue_test[i] = '\0'; } if (update_done) { fwrite ("\n%END\n", 1, 6, irr->update_fp); irr->state = 0; irr_update_lock (irr->database); /* rewind */ if (fseek (irr->update_fp, 0L, SEEK_SET) < 0) { trace (ERROR, default_trace, "IRRd ERROR: !ue file rewind positioning" " error.\n"); irr_update_unlock (irr->database); irr_send_error (irr, "IRRd ERROR: Transaction aborted! " "!ue file rewind positioning error."); fclose (irr->update_fp); unlink (irr->update_file_name); return; } /* atomic transaction support */ if (atomic_trans) { /* build the transaction file which can be used to * restore the DB to it's original state before the transaction */ if ((return_str = build_transaction_file (irr->database, irr->update_fp, irr->update_file_name, tmp, &n_updates)) != NULL) { trace (ERROR, default_trace, "Could not create transaction files: " "%s\n", return_str); irr_update_unlock (irr->database); irr_send_error (irr, "IRRd ERROR: Transaction aborted! " "Could not build transaction files."); fclose (irr->update_fp); unlink (irr->update_file_name); return; } } /* update the DB */ return_str = scan_irr_file (irr->database, "update", 1, irr->update_fp); /* rollback the DB to it's original state if the transaction * could not be applied successfully in it's entirety */ if (atomic_trans) { if (return_str != NULL) { /* restore the DB to it's original state * then rebuild the indexes */ db_rollback (irr->database, tmp); if (!irr_reload_database (irr->database->name, NULL, NULL)) trace (ERROR, default_trace, "DB rollback operation: index " "rebuild failed for DB (%s)\n", irr->database->name); } /* update the *.JOURNAL file for authoritative or mirror DB's; * rpsdist will manage the journal in the other case */ else if (((irr->database->flags & IRR_AUTHORITATIVE) || irr->database->mirror_prefix != NULL) && !update_journal (irr->update_fp, irr->database, n_updates)) { trace (ERROR, default_trace, "error in updating journal for DB (%s) " "rolling back transaction\n", irr->database->name); db_rollback (irr->database, tmp); journal_rollback (irr->database, tmp); return_str = "Transaction abort! Internal journaling error."; } /* remove the transaction file */ remove (tmp); } fclose (irr->update_fp); irr_update_unlock (irr->database); /* Send the client the transaction result */ if (return_str == NULL) { irr_send_okay(irr); irr->database->last_update = time (NULL); } else irr_send_error (irr, return_str); /* remove the update file */ remove (irr->update_file_name); } else { /* save the update to file */ if (irr->ue_test[0] != '\0') { fwrite (irr->ue_test, 1, strlen (irr->ue_test), irr->update_fp); irr->ue_test[0] = '\0'; } fwrite (irr->cp, 1, strlen (irr->cp), irr->update_fp); } return; } trace (NORM, default_trace, "Command: %s\n", irr->cp); if ( strlen(irr->cp) > IRR_MAXCMDLEN ) { trace (ERROR, default_trace, "query exceeds max length\n",irr->cp); irr_send_error (irr, "Command/Query exceeds max length!"); return; } if (irr->stay_open == 0 && (!strncmp (irr->cp, "!!", 2) || !strncmp (irr->cp, "-k", 2))) { irr->stay_open = 1; irr->short_fields = 1; /* this is what rawhoisd does */ return; } /* quit or stay open */ if (!strcasecmp (irr->cp, "quit") || !strcasecmp (irr->cp, "exit") || !strcasecmp (irr->cp, "!q") || !strcasecmp (irr->cp, "q") || !strcasecmp (irr->cp, "-k")) { irr->stay_open = 0; return; } /* update !us - DB we're authoritative for * ADD DEL object * terminated by a !ue * * We assume database files have already gone through irr_checker and * irr_auth. We return success or failure info to be used * by irr_notify * */ if ( !strncasecmp (irr->cp, "!us", 3) ){ irr->cp += 3; irr->state = 0; /* reset state */ /* see if we know of this DB */ if ((irr->database = find_database (irr->cp)) == NULL) { trace (ERROR, default_trace, "Database not found %s\n",irr->cp); irr_send_error (irr, "Database not found!"); return; } /* check authoritative if !us*/ if ( !(irr->database->flags & IRR_AUTHORITATIVE) ) { trace (ERROR, default_trace, "Request to update non-authoritative database %s with !us\n",irr->cp); irr_send_error (irr, "Request to update non-authoritative database!"); return; } /* check security -- general */ if ((irr->database->access_list > 0) && (!apply_access_list (irr->database->access_list, irr->from))) { trace (NORM | INFO, default_trace, "Updated access to %s denied for %s...\n", irr->database->name, prefix_toa (irr->from)); irr_send_error (irr, "General access permission to denied!"); return; } /* * check security -- write access */ if (irr->database->write_access_list == 0) { if (!prefix_is_loopback (irr->from)) { trace (NORM | INFO, default_trace, "No write access list sepcified -- only loopback allowed\n"); trace (NORM | INFO, default_trace, "Updated access to %s denied for %s...\n", irr->database->name, prefix_toa (irr->from)); irr_send_error (irr, "Write permission denied!"); return; } } if ((irr->database->write_access_list > 0) && (!apply_access_list (irr->database->write_access_list, irr->from))) { trace (NORM | INFO, default_trace, "Updated access to %s denied for %s...\n", irr->database->name, prefix_toa (irr->from)); irr_send_error (irr, "Write permission denied!"); return; } /* trace (NORM, default_trace, "START update from %s to %s\n", prefix_toa(irr->from), irr->database->name); */ sprintf (irr->update_file_name, "%s/%s.update.XXXXXX", IRR.database_dir, irr->database->name); /* create a file to dump the update to */ if ((tmp_fd = mkstemp (irr->update_file_name)) < 0) { irr_send_error (irr, "IRRd error: initial !us file mkstemp () error"); trace (ERROR, default_trace, "IRRd error: initial !us mkstemp () error\n"); return; } if ((irr->update_fp = fdopen (tmp_fd, "r+")) == NULL) { /* open up the temp update file */ snprintf (tmp, BUFSIZE, "IRRd error: initial !us file fdopen () error:" " %s\n", strerror (errno)); irr_send_error (irr, tmp); trace (ERROR, default_trace, "%s", tmp); return; } irr->line_cont = 0; irr->ue_test[0] = '\0'; irr->state = IRR_MODE_LOAD_UPDATE; irr_send_okay (irr); return; } /* withdrawn routes global flag */ /* =1 -> show withdrawns's; =0 -> don't show */ if (!strncmp (irr->cp, "!uwd", 4)) { irr->cp +=4; if (!strncmp (irr->cp, "=1", 2)) { irr->withdrawn = 1; irr_send_okay(irr); } else if (!strncmp (irr->cp, "=0", 2)) { irr->withdrawn = 0; irr_send_okay(irr); } else { irr_send_error (irr, NULL); } return; } /* full object flag */ /* =1 -> show full object (default) ; =0 -> show only */ /* affects the !r command only */ if (!strncmp (irr->cp, "!ufo", 4)) { irr->cp +=4; if (!strncasecmp (irr->cp, "=1", 2)) { irr->full_obj = 1; irr_send_okay(irr); } else if (!strncmp (irr->cp, "=0", 2)) { irr->full_obj = 0; irr_send_okay(irr); } else { irr_send_error (irr, NULL); } return; } /* long or short fields global flag */ /* =1 -> short fields (fast!); =0 -> long fields (default)*/ /* this will go away in rpsl because short fields are not used */ if (!strncmp (irr->cp, "!uF", 3)) { irr->cp +=3; if (!strncmp (irr->cp, "=0", 2)) { irr->short_fields = 0; irr->ripe_flags &= ~(FAST_OUT | RECURS_OFF); irr_send_okay(irr); } else if (!strncmp (irr->cp, "=1", 2)) { irr->short_fields = 1; irr->ripe_flags |= FAST_OUT | RECURS_OFF; irr_send_okay(irr); } else irr_send_error (irr, NULL); return; } /* set timeout, !t (for programs like Roe, Aoe, etc */ if (!strncasecmp (irr->cp, "!t", 2)) { irr->cp +=2; irr->timeout = atoi (irr->cp); if ((irr->timeout < 0) || (irr->timeout > 30*60)) { irr_send_error (irr, NULL); irr->timeout = 5*60; } else irr_send_okay (irr); return; } /* reload a database * !B | ALL * !eg, !Brgnet,ripe */ if (!strncasecmp (irr->cp, "!B", 2)) { irr_database_t *database; char *name, names[BUFSIZE], *last, *tmp_dir; irr->cp +=2; strcpy (names, irr->cp); if (!strcasecmp ("ALL", names)) { names[0] = '\0'; LL_IntrIterate (IRR.ll_database, database) { strcat (names,","); strcat (names, database->name); } } /* expect the DB's to be in our irrd.conf defined tmp dir */ tmp_dir = ((IRR.tmp_dir == NULL) ? "/var/tmp" : IRR.tmp_dir); name = strtok_r(names, ",", &last); while (name != NULL) { /* do we know about this DB? */ if ((irr->database = find_database (name)) == NULL) { trace (ERROR, default_trace, "Database not found %s\n", name); irr_send_error (irr, NULL); break; } /* general access */ if ((irr->database->access_list > 0) && (!apply_access_list(irr->database->access_list, irr->from))) { trace (NORM | INFO, default_trace, "Reload of %s denied for %s...\n", irr->database->name, prefix_toa(irr->from)); irr_send_error (irr, NULL); break; } /* write access */ if ((irr->database->write_access_list > 0) && (!apply_access_list(irr->database->write_access_list, irr->from))) { trace (NORM | INFO, default_trace, "Reload/write access to %s denied for %s...\n", irr->database->name, prefix_toa(irr->from)); irr_send_error (irr, NULL); break; } /* atomic reload */ if (!irr_reload_database (name, NULL, tmp_dir)) irr_send_error (irr, NULL); else irr_send_okay (irr); name = strtok_r(NULL, ",", &last); } return; } /* Get list of objects associated with a maintainer. !omaint-as237 */ if (!strncasecmp (irr->cp, "!o", 2)) { irr->cp += 2; irr->ll_answer = LL_Create (LL_DestroyFunction, free, 0); irr_lock_all (irr); irr_mntbyobjs (irr, NO_FIELD, RAWHOISD_MODE, irr->cp); return; } /* Get routes with specified origin. !gas1234 */ if (!strncasecmp (irr->cp, "!gas", 4)) { char gas_key[BUFSIZE]; irr->cp += 4; make_gas_key (gas_key, irr->cp); show_gas_answer (irr, gas_key); return; } /* AS-SET/RS-SET expansion !iAS-ESNETEU[,1] */ if (!strncasecmp (irr->cp, "!i", 2)) { irr->cp += 2; irr_set_expand (irr, irr->cp); return; } /* rawhoisd match command !man,as1243 */ if (!strncasecmp (irr->cp, "!m", 2)) { irr->cp += 2; irr_m_command (irr); return; } /* return a version */ if ((!strncasecmp (irr->cp, "!ver", 4)) || (!strcasecmp (irr->cp, "!v")) || (!strcasecmp (irr->cp, "!-v")) || (!strcasecmp (irr->cp, "version"))) { irr->cp += 5; return_version(irr); return; } /* name of tool, !nroe -- this should already be logged earlier */ if (!strncasecmp (irr->cp, "!n", 2)) { irr_send_okay (irr); return; } /* Route searches. * Default finds exact prefix/len match. * o - return origin of exact match(es) * l - one-level less specific * eg, !r141.211.128/24,l */ if (!strncasecmp (irr->cp, "!r", 2)) { char *cp = NULL; prefix_t *prefix; irr->cp += 2; cp = strrchr(irr->cp, ','); if (cp == NULL) { if ((prefix = ascii2prefix (AF_INET, irr->cp)) == NULL) irr_send_error (irr, NULL); else { irr_exact (irr, prefix, SHOW_FULL_OBJECT); Delete_Prefix (prefix); } } else if (!strncmp (",l", cp, 2)) { *cp = '\0'; if ((prefix = ascii2prefix (AF_INET, irr->cp)) == NULL) { irr_send_error (irr, NULL); } else { irr_less_all (irr, prefix, SEARCH_ONE_LEVEL, RAWHOISD_MODE); Delete_Prefix (prefix); } } else if (!strncmp (",L", cp, 2)) { *cp = '\0'; if ((prefix = ascii2prefix (AF_INET, irr->cp)) == NULL) { irr_send_error (irr, NULL); } else { irr_less_all (irr, prefix, SEARCH_ALL_LEVELS, RAWHOISD_MODE); Delete_Prefix (prefix); } } else if (!strncasecmp (",o", cp, 2)) { *cp = '\0'; if ((prefix = ascii2prefix (AF_INET, irr->cp)) == NULL) { irr_send_error (irr, NULL); } else { irr_exact (irr, prefix, SHOW_JUST_ORIGIN); Delete_Prefix (prefix); } } else if (!strncasecmp (",M", cp, 2)) { *cp = '\0'; if ((prefix = ascii2prefix (AF_INET, irr->cp)) == NULL) { irr_send_error (irr, NULL); } else { irr_more_all (irr, prefix, RAWHOISD_MODE); Delete_Prefix (prefix); } } else { /* error */ irr_send_answer (irr); return; } return; } /* s command Set the sources to the specified list. * Default is all sources. eg, !sradb,ans */ if (!strncasecmp (irr->cp, "!s", 2)) { irr->cp += 2; if (!strncasecmp (irr->cp, "-lc", 3)) irr_show_sources (irr); else if (!strncasecmp (irr->cp, "-*", 2)) irr_set_ALL_sources (irr, RAWHOISD_MODE); else irr_set_sources (irr, irr->cp, RAWHOISD_MODE); return; } /* j command. Show the journal ranges. See irr_journal_range for more info. -* is valid, just like !s, but is implemented in the function. */ if (!strncasecmp (irr->cp, "!j", 2)) { irr_journal_range (irr, irr->cp+2); return; } /* we are a mirror server, e.g. -g RADB:1:232-LAST */ if (!strncasecmp (irr->cp, "-g", 2)) { irr_service_mirror_request (irr, irr->cp+2); return; } /* Process ripe whois commands. * ie, command does not start with "!". */ if (strncasecmp(irr->cp, "!", 1)) { irr_ripewhois(irr); return; } /* error -- command unrecognized */ sprintf (tmp, "F\n"); irr_write_nobuffer (irr, tmp, strlen (tmp)); trace (NORM, default_trace, "Returned F -- unrecognized command: %s\n", irr->cp); } /* do a inverse lookup on mnt-by name to get all associated objects */ void irr_mntbyobjs (irr_connection_t *irr, enum IRR_OBJECTS obj_type, int mode, char *key) { char maint_key[BUFSIZE]; irr_database_t *db; hash_spec_t *hash_sval; maint_objlist_t *maint_obj_p; LINKED_LIST *ll; ll = LL_Create (LL_DestroyFunction, Delete_hash_spec, 0); make_mntobj_key (maint_key, key); LL_ContIterate (irr->ll_database, db) { if ((hash_sval = fetch_hash_spec (db, maint_key, UNPACK)) != NULL) { LL_Iterate (hash_sval->ll_1, maint_obj_p) { if (obj_type == NO_FIELD || obj_type == maint_obj_p->type) irr_build_answer (irr, db->db_fp, maint_obj_p->type, maint_obj_p->offset, maint_obj_p->len, NULL, db->db_syntax); } LL_Add (ll, hash_sval); } } send_dbobjs_answer (irr, DISK_INDEX, mode); irr_unlock_all (irr); irr_write_buffer_flush (irr); LL_Destroy (irr->ll_answer); LL_Destroy (ll); } /* !m... command, eg "!man,as1234" */ void irr_m_command (irr_connection_t *irr) { int found = 0, i; for (i = 0; i < IRR_MAX_MCMDS; i++) { if (!strncasecmp (irr->cp, m_info[i].command, strlen (m_info[i].command))) { found = 1; irr->cp += strlen (m_info[i].command); irr->ll_answer = LL_Create (LL_DestroyFunction, free, 0); irr_lock_all (irr); if (m_info[i].type == ROUTE) lookup_route_exact (irr, irr->cp); else irr_database_find_matches (irr, irr->cp, PRIMARY, RAWHOISD_MODE|TYPE_MODE, m_info[i].type, NULL, NULL); break; } } if (found) { send_dbobjs_answer (irr, DISK_INDEX, RAWHOISD_MODE); irr_unlock_all (irr); irr_write_buffer_flush (irr); LL_Destroy (irr->ll_answer); } else { char tmp[4]; sprintf (tmp, "D\n"); irr_write_nobuffer (irr, tmp, strlen (tmp)); } return; } /* irr_ripewhois * Ripe whois commands, eg "whois AS2" */ void irr_ripewhois (irr_connection_t *irr) { prefix_t *prefix; int lookup_mode; char *key = irr->cp; enum IRR_OBJECTS lookup_type; /* New ripe flag stuff */ /* want to retain fast output and recursive-off choices for ripe stay open mode while turning all other flags off */ irr->ripe_flags &= (FAST_OUT | RECURS_OFF); if (parse_ripe_flags (irr, &key) < 0) return; if (irr->ripe_flags & TEMPLATE) { if (irr->ripe_type >= 0 && irr->ripe_type < IRR_MAX_KEYS) irr_write_nobuffer (irr, obj_template[irr->ripe_type], strlen (obj_template[irr->ripe_type])); return; } if (irr->ripe_flags & FAST_OUT) irr->short_fields = 1; if ((irr->ripe_flags & SOURCES_ALL) && (irr_set_ALL_sources (irr, RIPEWHOIS_MODE) == 0)) return; if ((irr->ripe_flags & SET_SOURCE) && (irr_set_sources (irr, irr->ripe_sources, RIPEWHOIS_MODE) == 0)) return; irr_lock_all (irr); irr->ll_answer = LL_Create (LL_DestroyFunction, free, 0); if (*key >= '0' && *key <= '9' && (((irr->ripe_flags & OBJ_TYPE) == 0) || irr->ripe_type == ROUTE)) { if (strchr (key, ':') != NULL) prefix = ascii2prefix (AF_INET6, key); else prefix = ascii2prefix (AF_INET, key); if (prefix) { /* New RIPE stuff */ if (irr->ripe_flags & LESS_ALL) irr_less_all (irr, prefix, SEARCH_ALL_LEVELS, RIPEWHOIS_MODE); else if (irr->ripe_flags & (MORE_ONE | MORE_ALL)) irr_more_all (irr, prefix, RIPEWHOIS_MODE); else irr_less_all (irr, prefix, SEARCH_ONE_LEVEL, RIPEWHOIS_MODE); Delete_Prefix (prefix); } } if (irr->ripe_flags & OBJ_TYPE) { lookup_mode = TYPE_MODE; lookup_type = irr->ripe_type; } else { lookup_mode = 0; lookup_type = NO_FIELD; } if ( (irr->ripe_flags & INVERSE_ATTR) && irr->inverse_type == MNT_BY) /* check for inverse mnt-by lookup */ irr_mntbyobjs(irr, lookup_type, RIPEWHOIS_MODE, key); else { irr_database_find_matches (irr, key, PRIMARY, lookup_mode, lookup_type, NULL, NULL); if ((irr->ripe_flags & (FAST_OUT | RECURS_OFF | OBJ_TYPE | INVERSE_ATTR)) == 0) lookup_object_references (irr); /* dupe ripe whois behavior */ send_dbobjs_answer (irr, DISK_INDEX, RIPEWHOIS_MODE); irr_unlock_all (irr); irr_write_buffer_flush (irr); LL_Destroy (irr->ll_answer); } } void show_gas_answer (irr_connection_t *irr, char *key) { irr_database_t *db; int empty_answer = 1; hash_spec_t *hash_item; LINKED_LIST *ll; irr->ll_answer = LL_Create (LL_DestroyFunction, free, 0); ll = LL_Create (LL_DestroyFunction, Delete_hash_spec, 0); irr_lock_all (irr); LL_ContIterate (irr->ll_database, db) { if ((hash_item = fetch_hash_spec (db, key, FAST)) != NULL) { if (hash_item->len1 > 0) { if (!empty_answer) /* need to add a space between AS'es */ irr_build_answer (irr, NULL, NO_FIELD, 0, 1, " ", RIPE181); irr_build_answer (irr, db->db_fp, NO_FIELD, 0, hash_item->len1 - 1, hash_item->gas_answer, RIPE181); } LL_Add (ll, hash_item); empty_answer = 0; } } /* tack on a carriage return */ if (!empty_answer) irr_build_answer (irr, NULL, NO_FIELD, 0, 1, "\n", RIPE181); send_dbobjs_answer (irr, MEM_INDEX, RAWHOISD_MODE); irr_unlock_all (irr); irr_write_buffer_flush (irr); LL_Destroy (irr->ll_answer); LL_Destroy (ll); } /* Route searches. L - all level less specific eg, !r141.211.128/24,L Route searches. l - one-level less specific eg, !r141.211.128/24,l Both these searches are inclusive (ie, the supplied route will be returned if it exits. flag=0 -> "l" search; flag=1 -> "L" search */ void irr_less_all (irr_connection_t *irr, prefix_t *prefix, int flag, int mode) { radix_node_t *node = NULL; LINKED_LIST *ll_attr; irr_route_object_t *rt_object; irr_answer_t *irr_answer; irr_database_t *database; prefix_t *tmp_prefix = NULL; int route_found = 0; if (mode == RAWHOISD_MODE) { irr->ll_answer = LL_Create (LL_DestroyFunction, free, 0); irr_lock_all (irr); } LL_ContIterate (irr->ll_database, database) { route_found = 0; tmp_prefix = prefix; /* first check if this node exists */ if ((node = route_search_exact (database, database->radix, prefix)) != NULL) { ll_attr = (LINKED_LIST *) node->data; LL_Iterate (ll_attr, rt_object) { if (mode == RIPEWHOIS_MODE || irr->withdrawn == 1 || rt_object->withdrawn == 0) { if (mode == RAWHOISD_MODE && irr->full_obj == 0) irr_build_key_answer (irr, database->db_fp, database->name, ROUTE, rt_object->offset, rt_object->origin); else irr_build_answer (irr, database->db_fp, ROUTE, rt_object->offset, rt_object->len, NULL, database->db_syntax); } } } /* now check all less specific */ while ((flag == SEARCH_ALL_LEVELS || node == NULL) && (node = route_search_best (database, database->radix, tmp_prefix)) != NULL) { if (node != NULL) { tmp_prefix = node->prefix; ll_attr = (LINKED_LIST *) node->data; LL_Iterate (ll_attr, rt_object) { if (mode == RIPEWHOIS_MODE || irr->withdrawn == 1 || rt_object->withdrawn == 0) { route_found = 1; if (mode == RAWHOISD_MODE && irr->full_obj == 0) irr_build_key_answer (irr, database->db_fp, database->name, ROUTE, rt_object->offset, rt_object->origin); else { irr_build_answer (irr, database->db_fp, ROUTE, rt_object->offset, rt_object->len, NULL, database->db_syntax); } } } } /* break out after one loop for "l" case */ if (route_found && (flag == SEARCH_ONE_LEVEL)) break; } } if (mode == RAWHOISD_MODE) { if (irr->full_obj == 0) { send_dbobjs_answer (irr, MEM_INDEX, RAWHOISD_MODE); LL_ContIterate (irr->ll_answer, irr_answer) { free (irr_answer->blob); } } else send_dbobjs_answer (irr, DISK_INDEX, RAWHOISD_MODE); irr_unlock_all (irr); irr_write_buffer_flush (irr); LL_Destroy(irr->ll_answer); } } /* Route searches. M - all more specific eg, !r199.208.0.0/16,M */ /* Also does m - one level only more specific */ void irr_more_all (irr_connection_t *irr, prefix_t *prefix, int mode) { radix_node_t *node, *start_node, *last_node; LINKED_LIST *ll_attr; irr_route_object_t *rt_object; irr_answer_t *irr_answer; irr_database_t *database; if (prefix->bitlen < 16) { irr_send_error (irr, "We only allow more searches >= /16"); return; } if (mode == RAWHOISD_MODE) { irr->ll_answer = LL_Create (LL_DestroyFunction, free, 0); irr_lock_all (irr); } LL_ContIterate (irr->ll_database, database) { last_node = NULL; start_node = NULL; node = NULL; /* memory -- find the prefix, or the best large node */ start_node = radix_search_exact_raw (database->radix, prefix); if (start_node != NULL) { RADIX_WALK (start_node, node) { trace (TRACE, default_trace, " -M %s\n", prefix_toax (node->prefix)); if ((node->prefix != NULL) && (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, rt_object) { if ((irr->withdrawn == 1) || (rt_object->withdrawn == 0)) { if (irr->full_obj == 0) irr_build_key_answer (irr, database->db_fp, database->name, ROUTE, rt_object->offset, rt_object->origin); else irr_build_answer (irr, database->db_fp, ROUTE, rt_object->offset, rt_object->len, NULL, database->db_syntax); } } } } RADIX_WALK_END; } } if (mode == RAWHOISD_MODE) { if (irr->full_obj == 0) { send_dbobjs_answer (irr, MEM_INDEX, RAWHOISD_MODE); LL_ContIterate (irr->ll_answer, irr_answer) { free (irr_answer->blob); } } else send_dbobjs_answer (irr, DISK_INDEX, RAWHOISD_MODE); irr_unlock_all (irr); irr_write_buffer_flush (irr); LL_Destroy(irr->ll_answer); } } /* Route searches. o - return origin of exact match(es) eg, !r141.211.128/24,o * flag == 1 means just return the ASorigin. If not 1, return the full object */ void irr_exact (irr_connection_t *irr, prefix_t *prefix, int flag) { radix_node_t *node = NULL; LINKED_LIST *ll_attr; irr_route_object_t *rt_object; irr_database_t *database; irr_answer_t *irr_answer; char buffer[BUFSIZE], *p; int first = 1; if (flag == SHOW_FULL_OBJECT) irr->ll_answer = LL_Create (LL_DestroyFunction, free, 0); else buffer[0] = '\0'; irr_lock_all (irr); LL_ContIterate (irr->ll_database, database) { node = route_search_exact (database, database->radix, prefix); if (node != NULL) { ll_attr = (LINKED_LIST *) node->data; LL_Iterate (ll_attr, rt_object) { if (irr->withdrawn == 1 || rt_object->withdrawn == 0) { if (flag == SHOW_JUST_ORIGIN) { if (irr->full_obj == 0) { if (first != 1) irr_add_answer (irr, "\n"); first = 0; irr_add_answer (irr, "%s AS%d", database->name, rt_object->origin); } else { /* irr_add_answer (irr, "AS%d", rt_object->origin); */ p = buffer + strlen (buffer); sprintf (p, "AS%d ", rt_object->origin); } } else { if (irr->full_obj == 1) irr_build_answer (irr, database->db_fp, ROUTE, rt_object->offset, rt_object->len, NULL, database->db_syntax); else irr_build_key_answer (irr, database->db_fp, database->name, ROUTE, rt_object->offset, rt_object->origin); } } } } } if (flag == SHOW_JUST_ORIGIN) { if (irr->full_obj == 1 && strlen (buffer) > 0) irr_add_answer (irr, "%s", buffer); irr_send_answer (irr); } else { if (irr->full_obj == 1) send_dbobjs_answer (irr, DISK_INDEX, RAWHOISD_MODE); else { send_dbobjs_answer (irr, MEM_INDEX, RAWHOISD_MODE); LL_ContIterate (irr->ll_answer, irr_answer) { free (irr_answer->blob); } } } irr_unlock_all (irr); irr_write_buffer_flush (irr); if (flag == SHOW_FULL_OBJECT) LL_Destroy(irr->ll_answer); } /* s command Set the sources to the specified list. * eg, !sradb,ans * * RETURN values: * * return number of sources set */ int irr_set_sources (irr_connection_t *irr, char *sources, int mode) { irr_database_t *database, *dup_db; int found = 0, dup; int ret_code = 0, old_ret_code; char tmp[BUFSIZE], buf[BUFSIZE], *last; int space_left = BUFSIZE - 1; char *db; buf[0] = buf[BUFSIZE - 1] = '\0'; db = strtok_r(sources, ",", &last); while (db != NULL) { old_ret_code = ret_code; dup = 0; LL_IntrIterate (IRR.ll_database, database) { if (!strcasecmp (database->name, db)) { /* access-control check */ if ((database->access_list > 0) && (!apply_access_list (database->access_list, irr->from))) { trace (NORM | INFO, default_trace, "Access to %s denied...\n", prefix_toa (irr->from)); if (mode == RIPEWHOIS_MODE) { snprintf (tmp, BUFSIZE, "%% Access denied for db source \"%s\".\n", database->name); strncat (buf, tmp, space_left); space_left = MAX((0), (space_left - strlen(tmp))); } } else { /* hose the current list on our first DB match only */ if (!found) { LL_Clear (irr->ll_database); found = 1; } /* Don't add duplicate sources to the list */ LL_Iterate (irr->ll_database, dup_db) { if (!strcmp (dup_db->name, database->name)) { dup = 1; break; } } /* add database->name if it has not yet been added */ if (!dup) { LL_Add (irr->ll_database, database); ret_code++; } } break; } } if (old_ret_code == ret_code && mode == RIPEWHOIS_MODE) { if (dup) snprintf (tmp, BUFSIZE, "%% Duplicate source \"%s\" ignored.\n", db); else snprintf (tmp, BUFSIZE, "%% Source \"%s\" not found.\n", db); strncat (buf, tmp, space_left); space_left = MAX((0), (space_left - strlen(tmp))); } db = strtok_r(NULL, ",", &last); } if (mode == RAWHOISD_MODE) { if (ret_code == 0) irr_send_error (irr, NULL); else irr_send_okay (irr); } else if (buf[0] != '\0') /* in RIPEWHOIS_MODE and errors, tell user */ irr_write_nobuffer (irr, buf, strlen (buf)); return (ret_code); } /* Set db query list to all sources * ie, !s-* * * RETURN values: * * return number of sources set */ int irr_set_ALL_sources (irr_connection_t *irr, int mode) { irr_database_t *database; int ret_code = 0; char tmp[BUFSIZE], buf[BUFSIZE]; buf[0] = '\0'; LL_Clear (irr->ll_database); LL_IntrIterate (IRR.ll_database, database) { /* access-control check */ if ((database->access_list > 0) && (!apply_access_list (database->access_list, irr->from))) { trace (NORM | INFO, default_trace, "Access to %s denied for %s...\n", database->name, prefix_toa (irr->from)); if (mode == RIPEWHOIS_MODE) { sprintf (tmp, "%% Access denied for db source \"%s\".\n", database->name); strcat (buf, tmp); } } else { LL_Add (irr->ll_database, database); ret_code++; } } if (mode == RAWHOISD_MODE) { if (ret_code == 0) irr_send_error (irr, NULL); else irr_send_okay (irr); } else if (buf[0] != '\0') /* in RIPEWHOIS_MODE and errors, tell user */ irr_write_nobuffer (irr, buf, strlen (buf)); return (ret_code); } /* !s-lc */ void irr_show_sources (irr_connection_t *irr) { irr_database_t *database; int first = 1; LL_Iterate (irr->ll_database, database) { if (first != 1) { irr_add_answer (irr, ",");} first = 0; irr_add_answer (irr, "%s", database->name); } irr_send_answer (irr); } /* Return a version string. eg, !ver */ static void return_version (irr_connection_t *irr) { irr_add_answer (irr, "# IRRd -- version %s ", IRRD_VERSION); irr_send_answer (irr); } /* * irr_journal_range * !jRADB,RIPE,FOO,BAR * !j-* * Use irr_add_answer/irr_sender answer to return: * A * RADB:Y:1000-2000:1500 * RIPE:N:0-666 * FOO:X: * BAR:X: * C * * Y means that the database is mirrorable. * N means that the database is not mirrorable, but we're reporting * the current serial number. You can use this to check for updates. * The first number will _always_ be zero. The second number may be * zero if the CURRENTSERIAL file doesn't exist. * X means that the database doesn't exist, or we're denying information * about an existing database for administrative reasons. * * Returned DB's are canonicalized to upper case. */ void irr_journal_range (irr_connection_t *irr, char *db) { char *p_db, *last; p_db = strtok_r(db, ",", &last); /* Iterate through the list of dbs */ while (p_db != NULL) { /* Special case, "-*" */ if (!strncmp(p_db, "-*", 2)) { LL_IntrIterate (IRR.ll_database, irr->database) { irr_journal_add_answer(irr); } } else { /* Does the database exist? */ if ((irr->database = find_database(p_db)) == NULL) { char db_canon[BUFSIZE]; db_canon[BUFSIZE - 1] = '\0'; strncpy (db_canon, db, BUFSIZE-1); convert_toupper(db_canon); trace (ERROR, default_trace, "Database not found %s\n", db_canon); irr_add_answer (irr, "%s:X:Database does not exist.\n", db_canon); p_db = strtok_r(NULL, ",", &last); continue; } irr_journal_add_answer(irr); } p_db = strtok_r(NULL, ",", &last); } irr_send_answer (irr); } void irr_journal_add_answer (irr_connection_t *irr) { enum { UNDETERMINED, DENIED, READONLY, MIRRORABLE } status = UNDETERMINED; char db_canon[BUFSIZE], *p_last_export; u_long oldest_serial, current_serial, last_export = 0L; db_canon[BUFSIZE - 1] = '\0'; strncpy (db_canon, irr->database->name, BUFSIZE-1); convert_toupper(db_canon); /* If we're going to administratively deny access, do it here */ /* Is it not authoritative or not a mirror? */ if (!(((irr->database->flags & IRR_AUTHORITATIVE) == IRR_AUTHORITATIVE) || (irr->database->mirror_prefix != NULL))) { status = READONLY; } else { status = MIRRORABLE; } /* We can set the db to be not-mirrorable even if it is for administrative reasons. This would be a good spot to check for ACLs on mirroring. */ if ((irr->database->mirror_access_list > 0) && !apply_access_list (irr->database->mirror_access_list, irr->from)) { status = READONLY; /* We don't really need to log the fact that they can't mirror the db. We're telling them they can't. We'll log it if they request mirroring. */ } /* Fetch the lowest journal number */ if (status == MIRRORABLE) { if (find_oldest_serial(irr->database->name, JOURNAL_OLD, &oldest_serial) != 1) if (find_oldest_serial(irr->database->name, JOURNAL_NEW, &oldest_serial) != 1) oldest_serial = 0; } else { oldest_serial = 0; } /* Fetch the current serial number */ current_serial = irr->database->serial_number; /* Get the last export from the StatusFile, if any */ if ((p_last_export = GetStatusString(IRR.statusfile, db_canon, "lastexport")) != NULL) { last_export = atol(p_last_export); } if (last_export != 0L) { irr_add_answer(irr, "%s:%s:%lu-%lu:%lu\n", db_canon, ( status == READONLY ) ? "N" : "Y", oldest_serial, current_serial, last_export); } else { irr_add_answer(irr, "%s:%s:%lu-%lu\n", db_canon, ( status == READONLY ) ? "N" : "Y", oldest_serial, current_serial); } }