/* * $Id: notify.c,v 1.22 2002/10/17 20:25:56 ljb Exp $ */ #include #include #include #include #include #include #include #include #include #include extern char *test_addr; /* '\0' terminated set of email addrs. * Theses addrs are used to notify users * that authorised changes have occured * to their objects. */ char notify_addrs[MAX_ADDR_SIZE]; char *nnext; int nndx[MAX_NDX_SIZE]; int num_notify; /* '\0' terminated set of email addrs. * These addrs are used to notify users of * unauthorised updates. */ char forward_addrs[MAX_ADDR_SIZE]; char *fnext; int fndx[MAX_NDX_SIZE]; int num_forward; /* For uniform treatment, even though * there is only one sender, make it * like a forward_addr or notify_addr case */ char sender_addrs[MAX_ADDR_SIZE]; char *snext; int sndx[1]; int num_sender; char buf[MAXLINE]; /* local yokel's */ static void perform_rpsdist_trans (trace_t *, FILE *, ret_info_t *, int, char *, long, char *IRRd_HOST, int IRRd_PORT, char *dbpdir); static void perform_transactions (trace_t *, FILE *, ret_info_t *, int, char *, int, char *); /* static void perform_transactions_new (trace_t *, FILE *, ret_info_t *, int, char *, int, char *); */ static void remove_tempfiles (trace_t *); static FILE *rpsdist_fopen (char *); static char *send_rps_dist_trans (trace_t *, FILE *, ret_info_t *, char *, char *, long, char *, int); static char *init_jentries (trace_t *, char *, long, char *, FILE *); static char *init_irrd_updates (trace_t *, FILE *, FILE *, ret_info_t *, char *); static char *init_pgp_updates (trace_t *, FILE *, ret_info_t *, char *, int *); static long get_irrd_cs (trace_t *, char *, char *, int); static void pgp_files_backout (char *); static char *rpsdist_timestamp (char *); /* Control the action of performing the udpates and sending * out notifications. Briefly, for auth failures all referenced * upd_to: fields will be notified. For error-free updates (ie, * no syntax and no auth errors) notifications will be sent to * all referenced notify: and mnt_nfy: field addresses. The * sender is always notified unless the '-x' command line flag is set * (see below). * * notify () will send the updates to IRRd. If IRRd encounters * an error and/or cannot apply the update the IRRd error message * will be relayed in the notification. * * 'null_notification' = 1 causes notify () to skip * all notifications. In this case, notify () will send the * updates to IRRd but no notifications will be sent. '*tmpfname' * is the file template for 'mkstemp ()' to use to build * the notification files. There will be one notification file * per unique notify/updto email address. * * 'null_submission' means the user sent in an empty submission. * For an email submission all we have is the email header and an * empty body. For a TCP submission all we have is an empty body * and a return IP address. Note that 'null_submission' would * have been better name 'non_null_lines' since 'null_submission' * equal to 0 means we have a NULL submission. * * Return: * void * Results of the updates are included in the notifications. */ void notify (trace_t *tr, char *tmpfname, FILE *fin, int null_submission, int null_notification, int dump_stdout, char *web_origin_str, int rps_dist_flag, char *IRRd_HOST, int IRRd_PORT, char *db_admin, char *pgpdir, char *dbdir, char *tlogfn, long tlog_fpos, FILE *ack_fd, char * from_ip) { ret_info_t rstart; trans_info_t trans_info; char buf[MAXLINE]; irrd_result_t *p; long offset = 0; char pbuf[256]; /*fprintf (dfile, "Enter notify()\n");*/ rstart.first = rstart.last = NULL; if (rps_dist_flag) perform_rpsdist_trans (tr, fin, &rstart, null_submission, tlogfn, tlog_fpos, IRRd_HOST, IRRd_PORT, dbdir); else perform_transactions (tr, fin, &rstart, null_submission, IRRd_HOST, IRRd_PORT, pgpdir); /* JW want to go to this when we make irr_submit/IRRd transaction compliant perform_transactions_new (tr, fin, &rstart, null_submission, IRRd_HOST, IRRd_PORT, pgpdir); */ p = rstart.first; /* just to be safe rewind */ if (fseek (fin, 0L, SEEK_SET) != 0) { fprintf (stderr, "ERROR: cannot rewind input file, exit!\n"); exit (0); } /* init global data structures */ nnext = notify_addrs; fnext = forward_addrs; snext = sender_addrs; num_notify = num_forward = num_sender = 0; /* pointer to array of file names and handles to * notify/forward mail replies. */ num_hdls = 0; while (fgets (buf, MAXLINE - 1, fin) != NULL) { if (strncmp (HDR_START, buf, strlen (HDR_START) - 1)) continue; /* fprintf (dfile, "found a start header %s strlen-(%d)\n", buf, strlen (buf));*/ if (parse_header (tr, fin, &offset, &trans_info)) break; /* illegal hdr field found or EOF * (ie, no object after hdr) */ /* Make sure sender is not getting duplicate notifications */ if (trans_info.hdr_fields & FROM_F) remove_sender (trans_info.sender_addrs, trans_info.notify_addrs, &(trans_info.nnext)); else if (dump_stdout) { /* We have a tcp user, ie, an IRRj user */ trans_info.hdr_fields |= FROM_F; if(from_ip == NULL) from_ip = "localhost"; sprintf(pbuf,"TCP(%s)", from_ip); strcpy (trans_info.sender_addrs, pbuf); trans_info.snext += strlen (trans_info.sender_addrs) + 1; } trans_info.web_origin_str = web_origin_str; if (web_origin_str != NULL) { sprintf(pbuf,"%s Route Registry Update", trans_info.source ? trans_info.source : ""); trans_info.subject = strdup (pbuf); } /* JW this is for debug only */ /* print_hdr_struct (dfile, &trans_info); */ chk_email_fields (&trans_info); if (!(trans_info.hdr_fields & OP_F)) trans_info.op = strdup ("UPDATE"); if (!(trans_info.hdr_fields & OBJ_KEY_F)) trans_info.obj_key = strdup (""); if (!(trans_info.hdr_fields & OBJ_TYPE_F)) trans_info.obj_type = strdup (""); if (chk_hdr_flds (trans_info.hdr_fields)) { build_notify_responses (tr, tmpfname, fin, &trans_info, p, db_admin, MAX_IRRD_OBJ_SIZE, null_notification); if ((trans_info.hdr_fields & OLD_OBJ_FILE_F) && (*trans_info.old_obj_fname < '0' || *trans_info.old_obj_fname > '9')) unlink (trans_info.old_obj_fname); } /*else fprintf (dfile, "nofify () bad hdr file found, skipping...\n");*/ free_ti_mem (&trans_info); p = p->next; } send_notifies (tr, null_notification, ack_fd, dump_stdout); remove_tempfiles (tr); /*fprintf (dfile, "Exit notify ()\n");*/ } /* Close and remove all the notification temp files. * * Return: * void */ void remove_tempfiles (trace_t *tr) { int i; for (i = 0; i < num_hdls; i++) { fclose (msg_hdl[i].fp); remove (msg_hdl[i].fname); } } /* Move to this function in phase transition 2 when we convert * irr_submit/irrd to transaction * * 'submission_count_lines' is a count of the non-null lines * in the submission. Equal to 0 means the user supplied an * empty submission. * * Return: * */ void perform_transactions_new (trace_t *tr, FILE *fin, ret_info_t *start, int submission_count_lines, char *IRRd_HOST, int IRRd_PORT, char *pgpdir) { int all_noop; char *ret_code; irrd_result_t *p; /* rewind */ fseek (fin, 0L, SEEK_SET); /* JW:!!!!: Need to have pick_off_header_info () check for all NOOP's * want to avoid sending a transaction in which all objects are NOOP's */ /* Loop through the submission file, initializing the 'ti' * struct with the header info and added to the linked list of 'ti's * pointed to by 'start'. If any errors (user or server) * are encountered then abort the transaction. */ if (pick_off_header_info (tr, fin, submission_count_lines, start, &all_noop)) { trace (NORM, tr, "Submission error(s) found. Abort transaction.\n"); /* We are aborting the transaction. So set the proper * user return information to let the user know that the objects * with no errors are being skipped because of transaction * semantics. */ reinit_return_list (tr, start, SKIP_RESULT); } else if (submission_count_lines == 0) trace (ERROR, tr, "NULL submission.\n"); /* if all the obj's in the trans are NOOP's then don't send to irrd/rps-dist */ else if (!all_noop) { trace (NORM, tr, "calling irrd_trans ().\n"); /* Send the transaction to rps dist to be added to the DB */ ret_code = irrd_transaction_new (tr, (char *) WARN_TAG, fin, start, IRRd_HOST, IRRd_PORT); trace (NORM, tr, "return from irrd_trans ().\n"); /* Remove any key certificate files from our temp directory area */ for (p = start->first; p != NULL; p = p->next) { /* check for no IRRd errors and we have a key-cert object */ if (!put_transaction_code_new (tr, p, ret_code) && is_keycert_obj (p)) update_pgp_ring_new (tr, p, pgpdir); if (p->keycertfn != NULL) remove (p->keycertfn); } } trace (NORM, tr, "exit perform_transactions_new ().\n"); } /* * * 'submission_count_lines' is a count of the non-null lines * in the submission. Equal to 0 means the user supplied an * empty submission. * * Return: * */ void perform_rpsdist_trans (trace_t *tr, FILE *fin, ret_info_t *start, int submission_count_lines, char *tlogfn, long tlog_fpos, char *IRRd_HOST, int IRRd_PORT, char *dbdir) { int all_noop; char *ret_code; irrd_result_t *p; /* rewind */ fseek (fin, 0L, SEEK_SET); /* JW:!!!!: need a check to make sure all the sources are consistent, * ie, cannot accept a transaction that spans multiple source DB's */ /* Loop through the submission file, initializing the 'ti' * struct with the header info and added to the linked list of 'ti's * pointed to by 'start'. If any errors (user or server) * are encountered then abort the transaction. */ if (pick_off_header_info (tr, fin, submission_count_lines, start, &all_noop)) { trace (NORM, tr, "Submission error(s) found. Abort transaction.\n"); /* We are aborting the transaction. So set the proper * user return information to let the user know that the objects * with no errors are being skipped because of transaction * semantics. */ reinit_return_list (tr, start, SKIP_RESULT); } else if (submission_count_lines == 0) trace (NORM, tr, "Empty submission.\n"); /* if all the obj's in the trans are NOOP's then don't send to irrd/rps-dist */ else if (!all_noop) { /* send the transaction to RPS-DIST */ ret_code = send_rps_dist_trans (tr, fin, start, dbdir, tlogfn, tlog_fpos, IRRd_HOST, IRRd_PORT); if (ret_code == NULL) trace (NORM, tr, "perform_rpsdist_trans (): back from send_rps_dist_trans (), good trans!\n"); else trace (NORM, tr, "perform_rpsdist_trans (): back from send_rps_dist_trans () bad trans (%s)\n", ret_code); /* Place the irrd result in our linked list for notifications */ for (p = start->first; p != NULL; p = p->next) { if (!(p->svr_res & NOOP_RESULT)) put_transaction_code_new (tr, p, ret_code); /* remove any inital PGP public key files */ if (p->keycertfn != NULL) remove (p->keycertfn); } } trace (NORM, tr, "exit perform_rpsdist_trans ()\n"); return; } /* Build the communication files and send the names to RPS-DIST. Routine * expects that there are no errors in the transaction (auth, syntax, ...) * and that there is at least one valid update to be committed by IRRd * (ie, not a file of all NOOP's or an empty submission). * * Input: * -the canonicalized object file (prepended with pipeline headers) (fin) * -pointer to the 'source' DB for this transaction (start) * -pointer to the absolute directory path of the DB cache. This is where * the RPS-DIST communication files will go (dbdir) * -name of the bcc transaction log (tlogfn) * -starting file position within the transaction log of this transaction. * The user's original entry is needed to build the journal entry for * RPS-DIST (tlog_fpos) * -IRRd host. Might be needed to contact IRRd to determine trans * outcome (IRRd_HOST) * -IRRd port. Might be needed to contact IRRd to determine trans * outcome (IRRd_PORT) * * Return: * -NULL to indicate the transaction was successfully committed by IRRd * -A text message indicating a transaction error occured and was rolled * back. Also is possible transaction outcome can not be determined if * RPS-DIST times out and IRRd cannot be contacted. */ char *send_rps_dist_trans (trace_t *tr, FILE *fin, ret_info_t *start, char *dbdir, char *tlogfn, long tlog_fpos, char *IRRd_HOST, int IRRd_PORT) { int pgp_updates_count; char *ret_code; char template_n [1024], irrd_n[1024], pgp_n[1024], journal_n[1024]; FILE *irrd_f = NULL, *pgp_f = NULL, *journal_f = NULL; /* sanity check */ if (dbdir == NULL) { trace (ERROR, tr, "send_rps_dist_trans () Unspecified DB cache. Abort rps_dist transaction!\n"); return "SERVER ERROR: Unspecified DB cache. Please set the 'irr_directory' in your irrd.conf file"; } /* create the irrd, pgp and journal files */ sprintf (template_n, "%s/irr_submit.XXXXXX", dbdir); strcpy (irrd_n, template_n); strcpy (pgp_n, template_n); strcpy (journal_n, template_n); if (((irrd_f = rpsdist_fopen (irrd_n)) == NULL) || ((pgp_f = rpsdist_fopen (pgp_n)) == NULL) || ((journal_f = rpsdist_fopen (journal_n)) == NULL)) { trace (ERROR, tr, "send_rps_dist_trans () rps dist file creation error. Abort rps_dist transaction!\n"); ret_code = "SERVER ERROR: rps dist file creation error"; goto ABORT_TRANS; } trace (NORM, tr, "Debug. comm file names:\n"); trace (NORM, tr, "Debug. irrd (%s)\n", irrd_n); trace (NORM, tr, "Debug. pgp (%s)\n", pgp_n); trace (NORM, tr, "Debug. jentry (%s)\n", journal_n); /* initialize the irrd, pgp and journal files */ /* This is the !us...!ue data */ if ((ret_code = init_irrd_updates (tr, fin, irrd_f, start, (char *) WARN_TAG))) goto ABORT_TRANS; fclose (irrd_f); irrd_f = NULL; /* This is the PGP update data for the server's local pgp ring */ if ((ret_code = init_pgp_updates (tr, pgp_f, start, template_n, &pgp_updates_count))) goto ABORT_TRANS; fclose (pgp_f); pgp_f = NULL; /* 'NULL' file name is a flag value for RPS-DIST that there are * no pgp updates */ if (pgp_updates_count == 0) { remove (pgp_n); strcpy (pgp_n, "NULL"); } /* This is the journal entry for the user's submission */ if ((ret_code = init_jentries (tr, tlogfn, tlog_fpos, start->first->source, journal_f))) goto ABORT_TRANS; fclose (journal_f); trace (NORM, tr, "calling rps_dist_trans ()\n"); /* trace (NORM, tr, "Debug. Bye-bye\n"); exit (0); */ /* Send the transaction to rps dist to be added to the DB */ ret_code = rpsdist_transaction (tr, irrd_n, pgp_n, journal_n, start->first->source, "localhost", 7777); if (ret_code != NULL) trace (NORM, tr, "return from irrd_trans (%s).\n", ret_code); else trace (NORM, tr, "return from irrd_trans (NULL return) Yuck!\n"); /* "C\n means IRRd commited the transaction */ if (!strcmp (ret_code, "C\n")) ret_code = NULL; /* "cs" means we did not get an IRRd trans result. So get the "cs" * from IRRd to see if the trans succeeded */ else if ((*ret_code < '0' || *ret_code >'9') && (get_irrd_cs (tr, start->first->source, IRRd_HOST, IRRd_PORT) < atol (ret_code))) ret_code = "Transaction result not known. Query IRRd to determine transaction result or resubmit your transaction at a later time."; return ret_code; ABORT_TRANS: trace (ERROR, tr, "send_rps_dist_trans () Aborting transaction! (%s)\n", ret_code); /* clean up our rps dist communication files */ if (irrd_f != NULL) fclose (irrd_f); if (pgp_f != NULL) fclose (pgp_f); if (journal_f != NULL) fclose (journal_f); if (strcmp (irrd_n, "")) remove (irrd_n); if (strcmp (pgp_n, "NULL") && strcmp (pgp_n, "")) { pgp_files_backout (pgp_n); remove (pgp_n); } if (strcmp (journal_n, "")) remove (journal_n); return ret_code; } /* Get the latest current serial for DB 'source'. * * Input: * -the DB source to get the current serial for (source) * -the IRRd host (host) * -the IRRd port (port) * * Return: * -the latest current serial * -0 if there was any type of error */ long get_irrd_cs (trace_t *tr, char *source, char *host, int port) { char *data; regmatch_t cs_rm[2]; regex_t cs_re; char *cs = "^[^:]+:[^:]+:[[:digit:]]+-([[:digit:]]+)\n$"; /* get the !j output from IRRd */ data = irrd_curr_serial (tr, source, host, port); /* now parse the output and return the currentserial */ if (data != NULL) { regcomp (&cs_re, cs, REG_EXTENDED); if (regexec (&cs_re, data, 2, cs_rm, 0) == 0) { *(data + cs_rm[1].rm_eo) = '\0'; return atol ((char *) (data + cs_rm[1].rm_so)); } } return 0; } /* Open up a streams file. * * Return: * A stream file pointer if the operation was a success. * NULL if the file could not be opened. */ FILE *rpsdist_fopen (char *fname) { FILE *fp; int fd; fd = mkstemp (fname); if (fd == -1) return NULL; if ((fp = fdopen (fd, "w")) == NULL) { close(fd); return NULL; } return fp; } /* Generate an RFC 2769 timestamp (pg. 13). Timestamps are of * the form: * YYYYMMDD HH:MM:SS [+/-]hh:mm * * eg, 20000717 12:28:51 +04:00 * * Input: * void * * Return: * an RPS DIST RFC 2769 timestamp */ char *rpsdist_timestamp (char *ts) { time_t now; now = time (NULL); strftime (ts, 256, "%Y%m%d %H:%M:%S ", gmtime (&now)); /* UTC_OFFSET is the [+/-]hh:mm value, ie, the amount * we are away from UTC time */ strcat (ts, UTC_OFFSET); return ts; } /* Remove any PGP key files from the RPS-DIST cache area. * See init_pgp_updates () for the file format. * * Input: * -name of the PGP control file which has the fully qualified * path name of the PGP key files (pgpfn) * * Return: * void */ void pgp_files_backout (char *pgpfn) { int rm = 0; char buf[1024]; FILE *fin; /* sanity check */ if (pgpfn == NULL || (fin = fopen (pgpfn, "r")) == NULL) return; /* we're hosed ... */ /* remove any PGP key files we may have created in the * RPS-DIST cache area */ while (fgets (buf, MAXLINE - 1, fin) != NULL) { if (rm) { buf[strlen (buf) - 1] = '\0'; /* get rid of the '\n' */ remove (buf); } rm = !strcmp ("ADD", buf); } fclose (fin); } /* Build the RPS-DIST journal entry. See RFC 2769, pg 30 for a * description of the journal format. * * Input * -the name of the trace log where the initial user copy is (logfn) * -the file position with the trace log where the submission begins (fpos) * -the journal entry output file for RPS-DIST (fout) * * Return: * NULL if no errors occur in building the ouput file (fout) * otherwise a string message explaining the error condition */ char *init_jentries (trace_t *tr, char *logfn, long fpos, char *source, FILE *fout) { int first_line = 1, in_headers = 1, pgp_reg = 0; int passwd = 0, in_sig = 0, need_sig = 1, in_blankline = 0; regex_t mailfromre, pgpbegre, pgpsigre, pgpendre, blanklinere, passwdre, EOT; char curline[MAXLINE], ts[256], *ret_code; FILE *fin; char *mailfrom = "^From[ \t]"; char *pgpbegin = "^-----BEGIN PGP SIGNED MESSAGE-----"; char *pgpsig = "^-----BEGIN PGP SIGNATURE-----"; char *pgpend = "^-----END PGP SIGNATURE-----"; char *blankline = "^[ \t]*\n$"; char *password = "^password:|^override:"; char *eot = "^---\n$"; /* sanity check */ if (logfn == NULL) { ret_code = "SERVER ERROR: Can't find the initial submission log."; goto JENTRY_ABORT; } /* open the transaction carbon copy log file */ if ((fin = fopen (logfn, "r")) == NULL) { ret_code = "SERVER ERROR: RPS-DIST PGP fopen () error."; goto JENTRY_ABORT; } /* compile our regular expressions */ regcomp (&mailfromre, mailfrom, REG_EXTENDED|REG_NOSUB); regcomp (&pgpbegre, pgpbegin, REG_EXTENDED|REG_NOSUB); regcomp (&pgpsigre, pgpsig, REG_EXTENDED|REG_NOSUB); regcomp (&pgpendre, pgpend, REG_EXTENDED|REG_NOSUB); regcomp (&blanklinere, blankline, REG_EXTENDED|REG_NOSUB); regcomp (&passwdre, password, REG_EXTENDED|REG_ICASE|REG_NOSUB); regcomp (&EOT, eot, REG_EXTENDED|REG_NOSUB); /* seek to the beginning of the submission */ fseek (fin, fpos, SEEK_SET); /* prepend the redistribution header (RFC 2769, section A.2) */ fprintf (fout, "transaction-label: %s\n", source); fprintf (fout, "sequence: 1234567890\n"); fprintf (fout, "timestamp: %s\n", rpsdist_timestamp (ts)); fprintf (fout, "integrity: authorized\n\n"); /* copy the transaction "as-is" from the user for the RPS-DIST * journal file */ while (fgets (curline, MAXLINE - 1, fin) != NULL) { if (first_line && regexec (&mailfromre, curline, 0, 0, 0) != 0) in_headers = 0; first_line = 0; /* skip email header to conform to the RPS-DIST redistribution encoding */ if (in_headers && regexec (&blanklinere, curline, 0, 0, 0) == 0) in_headers = 0; if (in_headers) continue; in_blankline = 0; /* check for an end of transaction line '---' */ if (regexec (&EOT, curline, 0, 0, 0) == 0) break; /* all done, exit transaction */ /* check for a password, eg, 'password: foo' and skip to * conform to the RPS-DIST redistribution encoding */ if (!pgp_reg && regexec (&passwdre, curline, 0, 0, 0) == 0) { passwd = 1; continue; /* skip the regular signature begin */ } /* check for a '-----BEGIN PGP SIGNED MESSAGE-----' and skip it to * conform to the RPS-DIST redistribution encoding */ if (!pgp_reg && regexec (&pgpbegre, curline, 0, 0, 0) == 0) { pgp_reg = 1; continue; /* skip the regular signature */ } /* check for a '-----BEGIN PGP SIGNATURE-----' and skip if a regular * signature to conform to the RPS-DIST redistribution encoding */ if (regexec (&pgpsigre, curline, 0, 0, 0) == 0) { in_sig = 1; if (!pgp_reg) { fputs ("\nsignature:\n", fout); need_sig = 0; } } /* skip the PGP sig for regular sig's and keep for detached */ if (in_sig) { /* skip pgp regular sig's */ if (pgp_reg) continue; else fputs ("+", fout); } /* check for a '-----END PGP SIGNATURE-----', stop adding * '+'s for RPSL line continuation at the beginning of each line */ if (in_sig && regexec (&pgpendre, curline, 0, 0, 0) == 0) in_sig = 0; /* check for a blank line as the last line. need to know * when adding the 'signature' meta-object as the server * add's a blank line after transactions. on the last * transaction in the file there will not be a blank line * as the last line (ie, from the server). */ if (regexec (&blanklinere, curline, 0, 0, 0) == 0) in_blankline = 1; /* write a line to our journal */ fputs (curline, fout); } /* add a 'signature' meta-object if one is needed */ if (need_sig) { if (!in_blankline) fputs ("\n", fout); if (pgp_reg) fputs ("signature:\n \n", fout); else if (passwd) fputs ("signature:\n \n", fout); else fputs ("signature:\n \n", fout); } /* make purify happy */ regfree (&mailfromre); regfree (&pgpbegre); regfree (&pgpsigre); regfree (&pgpendre); regfree (&blanklinere); regfree (&passwdre); regfree (&EOT); fclose (fin); return NULL; JENTRY_ABORT: /* if we get here, someth'in went wrong */ sprintf (curline, "init_jentries() %s Abort rps_dist transaction!", ret_code); trace (ERROR, tr, "%s\n", curline); return strdup (curline); } /* Make the !us...!ue file for submission to IRRd by RPS-DIST. * * Input: * -pipeline input file with all the canonicalized objects (fin) * note that each canonicalized object is preceeded with pipeline * header info. * -routine expects a file to be opened and ready for writing (fout) * -a pointer to the begining of the linked list of updates (start) * * Return: * NULL if no errors occur in building the ouput file (fout) * otherwise a string message explaining the error condition */ char *init_irrd_updates (trace_t *tr, FILE *fin, FILE *fout, ret_info_t *start, char *warn_tag) { int n, line_cont, line_begin; irrd_result_t *p; char *ret_code; char *OP[] = {"ADD\n\n", "DEL\n\n"}, buf[MAXLINE]; /* !us */ fprintf (fout, "!us%s\n", start->first->source); /* Loop through the submission file and build the * !us...!ue IRRd update file */ for (p = start->first; p != NULL; p = p->next) { /* Skip NOOP operations */ if (!(p->svr_res & NOOP_RESULT)) { /* determine the operation, 'ADD' or 'DEL' ... */ n = 0; if (!strcmp (p->op, DEL_OP)) n = 1; /* ... and write the operation to the IRRd update file */ if (fputs (OP[n], fout) == EOF) { ret_code = "SERVER ERROR: RPS-DIST IRRd operation disk error."; goto IRRd_ABORT; } /* seek to the beginning of object */ fseek (fin, p->offset, SEEK_SET); /* line cont in the fgets () sense, not in the rpsl sense. * ie, the current input line was larger than our memory buffer */ line_cont = 0; /* copy the canonicalized submission object from the input * file to the IRR update file */ while (fgets (buf, MAXLINE - 1, fin) != NULL) { n = strlen (buf); line_begin = !line_cont; line_cont = (buf[n - 1] != '\n'); /* skip possible 'WARNING:...' lines at the end of the object */ if (line_begin && !strncmp (buf, warn_tag, strlen (warn_tag))) continue; fputs (buf, fout); if (line_begin && buf[0] == '\n') /* looking for a blank line */ break; } } } /* !ue */ fprintf (fout, "!ue\n"); return NULL; IRRd_ABORT: /* if we get here, someth'in went wrong */ sprintf (buf, "init_irrd_updates() %s Abort rps_dist transaction!", ret_code); trace (ERROR, tr, "%s\n", buf); return strdup (buf); } /* Build the RPS-DIST PGP file. RPS-DIST will use the file built * in this routine to update the server's local PGP rings. The format * of the file will be like this: * * DEL * 0x.... * * ADD * * ... * * Note: the reason for not writing 'ADD' keys directly to the file * is because it is easier to give pgp a file name to add the key * then to read it and send it to pgp's stdin. * * Input: * -routine expects a file to be opened and ready for writing (fout) * -a pointer to the begining of the linked list of updates (start) * * Return: * -a count of the number of PGP udpates (pgp_updates_count) * -NULL if there were no errors encountered * -otherwise a string message explaining the error condition */ char *init_pgp_updates (trace_t *tr, FILE *fout, ret_info_t *start, char *template_n, int *pgp_updates_count) { int n; irrd_result_t *p; char *ret_code; char *OP[] = {"ADD\n", "DEL\n"}, buf[MAXLINE], fn[1024]; FILE *fin = NULL, *dst = NULL; /* keep track of the number of PGP udates we find */ *pgp_updates_count = 0; /* Loop through the submission list and collect data on the PGP updates */ for (p = start->first; p != NULL; p = p->next) { if (!(p->svr_res & NOOP_RESULT) && is_keycert_obj (p)) { /* determine the operation, 'ADD' or 'DEL' ... */ n = 0; if (!strcmp (p->op, DEL_OP)) n = 1; /* ... and write the operation to the PGP update file */ if (fputs (OP[n], fout) == EOF) { ret_code = "SERVER ERROR: RPS-DIST PGP operation disk error."; goto PGP_ABORT; } /* 'DEL' operation */ if (n) fprintf (fout, "0x%s\n\n", (p->obj_key + 7)); /* 'ADD' operation */ else { /* make sure the public key exists ... */ if (p->keycertfn == NULL) { ret_code = "SERVER ERROR: RPS-DIST PGP missing keycert file."; goto PGP_ABORT; } /* ... now open it */ if ((fin = fopen (p->keycertfn, "r")) == NULL) { ret_code = "SERVER ERROR: RPS-DIST PGP fopen () error."; goto PGP_ABORT; } /* open the output public key file */ strcpy (fn, template_n); if ((dst = rpsdist_fopen (fn)) == NULL) { ret_code = "SERVER ERROR: RPS-DIST PGP file creation error."; goto PGP_ABORT; } /* transfer the public key to an rps-dist PGP key update file */ while (fgets (buf, MAXLINE - 1, fin) != NULL) fputs (buf, dst); /* write the PGP key filename to the RPS-DIST PGP control file */ fputs (fn, fout); /* This is the old code * open the public key file * if ((fin = fopen (p->keycertfn, "r")) == NULL) { ret_code = "SERVER ERROR: RPS-DIST PGP fopen () error."; goto PGP_ABORT; } * transfer the public key to the rps-dist PGP update file * while (fgets (buf, MAXLINE - 1, fin) != NULL) fputs (buf, fout); End of old code */ /* close and remove the input public key file */ fputs ("\n", fout); fclose (fin); /* removal of the initial public key files is done in rpsdist_trans () */ fclose (dst); fin = dst = NULL; } /* count the number of PGP updates, there could be 0 */ (*pgp_updates_count)++; } } return NULL; PGP_ABORT: /* if we get here, someth'in went wrong */ if (fin != NULL) fclose (fin); if (dst != NULL) fclose (dst); sprintf (buf, "init_pgp_updates () %s Abort rps_dist transaction!", ret_code); trace (ERROR, tr, "%s\n", buf); return strdup (buf); } /* This is the old guy. We are migrating away from this version of performing * the transactions. However this works and is what we will use for now. * * Our migration path will be like this: * * (old guy) -> (irr_submit, irrd transaction compliant) -> (rpsdist) * * To get to the next step, this routine should be phased out and * perform_transactions_new () should be used. * Then to get to the last step perform_transactions_new () should be * phased out in favor of perform_rpsdist_trans (). */ void perform_transactions (trace_t *tr, FILE *fin, ret_info_t *start, int null_submission, char *IRRd_HOST, int IRRd_PORT, char *pgpdir) { char buf[MAXLINE], *ret_code; int fd, num_trans = 0; int open_conn = 0, abort_trans = 0; long offset; trans_info_t ti; irrd_result_t *p; /* pgp_data_t pdat; */ /* fprintf (dfile, "\n----\nEnter perform_transactions()\n");*/ /* rewind */ fseek (fin, 0L, SEEK_SET); while (fgets (buf, MAXLINE - 1, fin) != NULL) { if (strncmp (HDR_START, buf, strlen (HDR_START) - 1)) continue; /* JW commented out to dup rawhoisd if (!abort_trans) */ offset = ftell (fin); /* fprintf (dfile, "HDR_START offset (%ld)\n", offset);*/ /* illegal hdr field found or EOF or no object after hdr */ if (parse_header (tr, fin, &offset, &ti)) { abort_trans = 1; /* fprintf (dfile, "calling update_trans_outcome_list (internal error)...\n"); */ update_trans_outcome_list (tr, start, &ti, offset, INTERNAL_ERROR_RESULT, "\" Internal error: malformed header!\"\n"); free_ti_mem (&ti); continue; } else if (update_has_errors (&ti)) abort_trans = 1; else if (null_submission == 0) { update_trans_outcome_list (tr, start, &ti, offset, NULL_SUBMISSION, NULL); continue; } update_trans_outcome_list (tr, start, &ti, offset, 0, NULL); free_ti_mem (&ti); } /* JW commented out to dup rawhoisd * want to bring back for transaction semantic support if (abort_trans) reinit_return_list (dfile, start, SKIP_RESULT); else { */ for (p = start->first; p != NULL; p = p->next) { /* JW want to bring back in later, dup rawhoisd if (p->svr_res & NOOP_RESULT) continue; */ /* JW take next 3 sections out to reverse rawhoisd behavior */ if (p->svr_res & INTERNAL_ERROR_RESULT || p->svr_res & NULL_SUBMISSION) { trace (ERROR, tr, "Internal error or NULL submission. Object not added to IRRd.\n"); continue; } if (p->svr_res & USER_ERROR) { trace (NORM, tr, "Syntax or authorization error. Object not added to IRRd.\n"); continue; } if (p->svr_res & NOOP_RESULT) { trace (NORM, tr, "NOOP object. Object not added to IRRd.\n"); continue; } /* what the eff is this segment doing? */ if (EOF == fseek (fin, p->offset, SEEK_SET)) fprintf (stderr, "ERROR: fseek (%ld)\n", p->offset); else { fgets (buf, MAXLINE - 1, fin); /*fprintf (dfile, "irrd_trans () line: %s", buf);*/ fseek (fin, p->offset, SEEK_SET); } /* fprintf (dfile, "perform_trans () calling irrd_transaction ()...\n");*/ ret_code = irrd_transaction (tr, (char *) WARN_TAG, &fd, fin, p->op, p->source, ++num_trans, &open_conn, IRRd_HOST, IRRd_PORT); /* check for no IRRd errors and we have a key-cert object */ if (!put_transaction_code (tr, p, ret_code) && is_keycert_obj (p) && p->op != NULL) { update_pgp_ring_new (tr, p, pgpdir); /* if (strcmp (p->op, DEL_OP)) { pgp_add (tr, pgpdir, p->keycertfn, &pdat); pgp_free (&pdat); } else pgp_del (tr, pgpdir, p->obj_key + 7); */ } } if (open_conn) { end_irrd_session (tr, fd); /* send '!q' */ fflush (fin); /* JW only needed when irrd commands are sent to terminal */ close_connection (fd); } /* Remove any key certificate files from our temp directory area */ for (p = start->first; p != NULL; p = p->next) if (p->keycertfn != NULL) remove (p->keycertfn); /* JW want to bring back in later, dup rawhoisd } */ /* fprintf (dfile, "Exit perform_transactions()\n----\n");*/ }