From: Wietse Venema Date: Sun, 2 Jul 2006 05:00:00 +0000 (-0500) Subject: postfix-2.3-RC3 X-Git-Tag: v2.3-RC3^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf46e2a2f98daeaf49d07235505108bd3077a043;p=thirdparty%2Fpostfix.git postfix-2.3-RC3 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 2b522c476..e4a68d886 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -12430,6 +12430,12 @@ Apologies for any names omitted. header and the rest of the message, violating the draft domainkeys spec. +20070602 + + Cleanup: more graceful handling of queue file read/write + errors while processing milter message modification requests. + Files: cleanup/cleanup_milter.c, milter/milter8.c. + Wish list: In the SMTPD policy client (encode or strip) non-printable diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 28d69517a..d4f5d7e85 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -26,11 +26,12 @@ only anonymous ciphers, and will not interoperate with most clients. The SMTP server supports anonymous ciphers when client certificates are not requested or required, and the administrator has not excluded -the "aNULL" OpenSSL cipher type. +the "aNULL" OpenSSL cipher type with smtpd_tls_exclude_ciphers. The SMTP client supports anonymous ciphers when no server certificate is required (notably Postfix 2.3 in "opportunistic" mode) and the -administrator has not excluded the "aNULL" OpenSSL cipher type. +administrator has not excluded the "aNULL" OpenSSL cipher type with +smtp_tls_exclude_ciphers. Instead of cipher lists you can now specify cipher grades. The smtp_tls_mandatory_ciphers, lmtp_tls_mandatory_ciphers and diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c index 656f5a531..4c08799b4 100644 --- a/postfix/src/cleanup/cleanup_milter.c +++ b/postfix/src/cleanup/cleanup_milter.c @@ -65,6 +65,7 @@ /* DIAGNOSTICS /* Fatal errors: memory allocation problem. /* Panic: interface violation. +/* state->errs is updated in case of I/O errors. /* LICENSE /* .ad /* .fi @@ -81,6 +82,7 @@ #include #include /* AF_INET */ #include +#include #ifdef STRCASECMP_IN_STRINGS_H #include @@ -210,9 +212,34 @@ #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) +/* cleanup_milter_set_error - set error flag from errno */ + +static void cleanup_milter_set_error(CLEANUP_STATE *state, int err) +{ + if (err == EFBIG) + state->errs |= CLEANUP_STAT_SIZE; + else + state->errs |= CLEANUP_STAT_WRITE; +} + +/* cleanup_milter_error - return dummy error description */ + +static const char *cleanup_milter_error(CLEANUP_STATE *state, int err) +{ + + /* + * This error text will be ignored by cleanup_milter_apply(). It exists + * only to maintain a consistent error reporting interface to the milter + * infrastructure. + */ + if (err) + cleanup_milter_set_error(state, err); + return ("451 4.3.0 Server internal error"); +} + /* cleanup_add_header - append message header */ -static void cleanup_add_header(void *context, char *name, char *value) +static const char *cleanup_add_header(void *context, char *name, char *value) { const char *myname = "cleanup_add_header"; CLEANUP_STATE *state = (CLEANUP_STATE *) context; @@ -237,14 +264,18 @@ static void cleanup_add_header(void *context, char *name, char *value) * target of the old "header append" pointer record. This reverse pointer * record becomes the new "header append" pointer record. */ - if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } buf = vstring_alloc(100); vstring_sprintf(buf, "%s: %s", name, value); cleanup_out_header(state, buf); vstring_free(buf); - if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path); + if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, (long) state->append_hdr_pt_target); @@ -252,8 +283,10 @@ static void cleanup_add_header(void *context, char *name, char *value) * Pointer flipping: update the old "header append" pointer record value * with the location of the new header record. */ - if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, (long) new_hdr_offset); @@ -265,6 +298,11 @@ static void cleanup_add_header(void *context, char *name, char *value) * written while Postfix received the message. */ state->append_hdr_pt_offset = reverse_ptr_offset; + + /* + * In case of error while doing record output. + */ + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0)); } /* cleanup_find_header - find specific header instance */ @@ -278,7 +316,7 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index, const char *myname = "cleanup_find_header"; off_t curr_offset; /* offset after found record */ off_t ptr_offset; /* pointer to found record */ - VSTRING *ptr_buf; + VSTRING *ptr_buf = 0; int rec_type; int last_type; ssize_t len; @@ -353,9 +391,21 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index, * Thus, header insert operations are relative to the content as delivered, * that is, the content including our own Received: header. */ -#define GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset) \ - if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) \ - msg_fatal("%s: read file %s: %m", myname, cleanup_path); \ +#define CLEANUP_FIND_HEADER_NOTFOUND (-1) +#define CLEANUP_FIND_HEADER_IOERROR (-2) + +#define CLEANUP_FIND_HEADER_RETURN(offs) do { \ + if (ptr_buf) \ + vstring_free(ptr_buf); \ + return (offs); \ + } while (0) + +#define GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset, quit) \ + if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) { \ + msg_warn("%s: read file %s: %m", myname, cleanup_path); \ + cleanup_milter_set_error(state, errno); \ + quit; \ + } \ if (msg_verbose > 1) \ msg_info("%s: read: %ld: %.*s", myname, (long) curr_offset, \ LEN(buf) > 30 ? 30 : LEN(buf), STR(buf)); \ @@ -365,19 +415,28 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index, && rec_type != REC_TYPE_PTR) \ break; - if (vstream_fseek(state->dst, state->data_offset, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); - for (ptr_buf = 0, ptr_offset = 0, last_type = 0; /* void */ ; /* void */ ) { - if ((curr_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, state->data_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + cleanup_milter_set_error(state, errno); + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR); + } + for (ptr_offset = 0, last_type = 0; /* void */ ; /* void */ ) { + if ((curr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + cleanup_milter_set_error(state, errno); + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR); + } /* Caution: this macro terminates the loop at end-of-message. */ /* Don't do complex processing while breaking out of this loop. */ - GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset); + GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset, + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR)); /* Caution: don't assume ptr->header. This may be header-ptr->body. */ if (rec_type == REC_TYPE_PTR) { - if (rec_goto(state->dst, STR(buf)) < 0) - msg_fatal("%s: read file %s: %m", - myname, cleanup_path); + if (rec_goto(state->dst, STR(buf)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + cleanup_milter_set_error(state, errno); + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR); + } /* Save PTR record, in case it points to the start of a header. */ if (allow_ptr_backup) { ptr_offset = curr_offset; @@ -415,7 +474,7 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index, * In case of failure, return negative start position. */ if (index > 0) { - curr_offset = -1; + curr_offset = CLEANUP_FIND_HEADER_NOTFOUND; } /* @@ -431,27 +490,25 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index, } *prec_type = rec_type; } - if (ptr_buf) - vstring_free(ptr_buf); if (msg_verbose) msg_info("%s: index %ld name %s type %d offset %ld", myname, (long) index, header_label ? header_label : "(none)", rec_type, (long) curr_offset); - return (curr_offset); + CLEANUP_FIND_HEADER_RETURN(curr_offset); } /* cleanup_patch_header - patch new header into an existing header */ -static void cleanup_patch_header(CLEANUP_STATE *state, - const char *new_hdr_name, - const char *new_hdr_value, - off_t old_rec_offset, - int rec_type, - VSTRING *old_rec_buf, - ssize_t avail_space, - off_t read_offset) +static const char *cleanup_patch_header(CLEANUP_STATE *state, + const char *new_hdr_name, + const char *new_hdr_value, + off_t old_rec_offset, + int rec_type, + VSTRING *old_rec_buf, + ssize_t avail_space, + off_t read_offset) { const char *myname = "cleanup_patch_header"; VSTRING *buf = vstring_alloc(100); @@ -459,6 +516,11 @@ static void cleanup_patch_header(CLEANUP_STATE *state, off_t saved_read_offset; off_t write_offset; +#define CLEANUP_PATCH_HEADER_RETURN(ret) do { \ + vstring_free(buf); \ + return (ret); \ + } while (0) + if (msg_verbose) msg_info("%s: \"%s\" \"%s\" at %ld", myname, new_hdr_name, new_hdr_value, (long) old_rec_offset); @@ -498,8 +560,10 @@ static void cleanup_patch_header(CLEANUP_STATE *state, * Write the new header to a new location after the end of the queue * file. */ - if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } vstring_sprintf(buf, "%s: %s", new_hdr_name, new_hdr_value); cleanup_out_header(state, buf); if (msg_verbose > 1) @@ -526,10 +590,14 @@ static void cleanup_patch_header(CLEANUP_STATE *state, */ while (rec_type != REC_TYPE_PTR && avail_space < REC_TYPE_PTR_SIZE) { /* Read existing text or pointer record. */ - if (vstream_fseek(state->dst, read_offset, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); - if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) - msg_fatal("%s: read file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, read_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } + if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } if (msg_verbose > 1) msg_info("%s: %ld: read %.*s", myname, (long) read_offset, LEN(buf) > 30 ? 30 : LEN(buf), STR(buf)); @@ -538,12 +606,16 @@ static void cleanup_patch_header(CLEANUP_STATE *state, msg_panic("%s: non-text/ptr record type %d in header, file %s", myname, rec_type, cleanup_path); saved_read_offset = read_offset; - if ((read_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path); + if ((read_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } avail_space += (read_offset - saved_read_offset); /* Save the text or pointer record. */ - if ((write_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if ((write_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } CLEANUP_OUT_BUF(state, rec_type, buf); if (msg_verbose > 1) msg_info("%s: %ld: write %.*s", myname, (long) write_offset, @@ -570,25 +642,32 @@ static void cleanup_patch_header(CLEANUP_STATE *state, * the queue file before the next record. In other words, we must always * follow pointer records otherwise we get out of sync with the data. */ - if (vstream_fseek(state->dst, old_rec_offset, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, old_rec_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, (long) new_hdr_offset); if (msg_verbose > 1) msg_info("%s: %ld: write PTR %ld", myname, (long) old_rec_offset, (long) new_hdr_offset); + /* + * In case of error while doing record output. + */ + CLEANUP_PATCH_HEADER_RETURN(CLEANUP_OUT_OK(state) ? 0 : + cleanup_milter_error(state, 0)); + /* * Note: state->append_hdr_pt_target never changes. */ - vstring_free(buf); } /* cleanup_ins_header - insert message header */ -static void cleanup_ins_header(void *context, ssize_t index, - char *new_hdr_name, - char *new_hdr_value) +static const char *cleanup_ins_header(void *context, ssize_t index, + char *new_hdr_name, + char *new_hdr_value) { const char *myname = "cleanup_ins_header"; CLEANUP_STATE *state = (CLEANUP_STATE *) context; @@ -597,6 +676,12 @@ static void cleanup_ins_header(void *context, ssize_t index, int old_rec_type; off_t read_offset; ssize_t avail_space; + const char *ret; + +#define CLEANUP_INS_HEADER_RETURN(ret) do { \ + vstring_free(old_rec_buf); \ + return (ret); \ + } while (0) if (msg_verbose) msg_info("%s: %ld \"%s\" \"%s\"", @@ -625,29 +710,35 @@ static void cleanup_ins_header(void *context, ssize_t index, old_rec_buf, &old_rec_type, ALLOW_PTR_BACKUP, DONT_SKIP_HEADERS); + if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, 0)); if (old_rec_offset < 0) { - cleanup_add_header(context, new_hdr_name, new_hdr_value); + CLEANUP_INS_HEADER_RETURN(cleanup_add_header(context, new_hdr_name, + new_hdr_value)); } else { if (old_rec_type == REC_TYPE_PTR) { read_offset = -1; avail_space = -1; } else { - if ((read_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: read file %s: %m", myname, cleanup_path); + if ((read_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, errno)); + } avail_space = LEN(old_rec_buf); } - cleanup_patch_header(state, new_hdr_name, new_hdr_value, - old_rec_offset, old_rec_type, old_rec_buf, - avail_space, read_offset); + ret = cleanup_patch_header(state, new_hdr_name, new_hdr_value, + old_rec_offset, old_rec_type, old_rec_buf, + avail_space, read_offset); + CLEANUP_INS_HEADER_RETURN(ret); } - vstring_free(old_rec_buf); } /* cleanup_upd_header - modify or append message header */ -static void cleanup_upd_header(void *context, ssize_t index, - char *new_hdr_name, - char *new_hdr_value) +static const char *cleanup_upd_header(void *context, ssize_t index, + char *new_hdr_name, + char *new_hdr_value) { const char *myname = "cleanup_upd_header"; CLEANUP_STATE *state = (CLEANUP_STATE *) context; @@ -659,6 +750,7 @@ static void cleanup_upd_header(void *context, ssize_t index, int rec_type; int last_type; int jumped; + const char *ret; if (msg_verbose) msg_info("%s: %ld \"%s\" \"%s\"", @@ -684,33 +776,54 @@ static void cleanup_upd_header(void *context, ssize_t index, #define DONT_SAVE_RECORD 0 #define NO_PTR_BACKUP 0 +#define CLEANUP_UPD_HEADER_RETURN(ret) do { \ + vstring_free(rec_buf); \ + return (ret); \ + } while (0) + rec_buf = vstring_alloc(100); old_rec_offset = cleanup_find_header(state, index, new_hdr_name, rec_buf, &last_type, NO_PTR_BACKUP, SKIP_ONE_HEADER); + if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0)); if (old_rec_offset < 0) { - cleanup_add_header(context, new_hdr_name, new_hdr_value); + CLEANUP_UPD_HEADER_RETURN(cleanup_add_header(context, new_hdr_name, + new_hdr_value)); } else { /* Find the end of this header. */ avail_space = LEN(rec_buf); - if ((read_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: read file %s: %m", myname, cleanup_path); - for (jumped = 0; /* void */ ; /* void */ ) { + if ((read_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, errno)); + } + for (jumped = 0, ret = 0; ret == 0; /* void */ ) { + if (CLEANUP_OUT_OK(state) == 0) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0)); saved_read_offset = read_offset; /* Caution: this macro terminates the loop at end-of-message. */ /* Don't do complex processing while breaking out of this loop. */ - GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, read_offset); - if ((read_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: read file %s: %m", myname, cleanup_path); + GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, read_offset, + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0))); + if ((read_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, errno)); + } if (rec_type == REC_TYPE_PTR) { if (jumped == 0) { /* Enough contiguous space for writing a PTR record. */ avail_space += read_offset - saved_read_offset; jumped = 1; } - if (rec_goto(state->dst, STR(rec_buf)) < 0) - msg_fatal("%s: read file %s: %m", myname, cleanup_path); + if (rec_goto(state->dst, STR(rec_buf)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, + errno)); + } /* Don't update last_type; PTR may follow REC_TYPE_CONT. */ continue; } @@ -721,16 +834,17 @@ static void cleanup_upd_header(void *context, ssize_t index, avail_space += read_offset - saved_read_offset; last_type = rec_type; } - cleanup_patch_header(state, new_hdr_name, new_hdr_value, - old_rec_offset, DONT_SAVE_RECORD, (VSTRING *) 0, - avail_space, saved_read_offset); + ret = cleanup_patch_header(state, new_hdr_name, new_hdr_value, + old_rec_offset, DONT_SAVE_RECORD, (VSTRING *) 0, + avail_space, saved_read_offset); + CLEANUP_UPD_HEADER_RETURN(ret); } - vstring_free(rec_buf); } /* cleanup_del_header - delete message header */ -static void cleanup_del_header(void *context, ssize_t index, char *hdr_name) +static const char *cleanup_del_header(void *context, ssize_t index, + char *hdr_name) { const char *myname = "cleanup_del_header"; CLEANUP_STATE *state = (CLEANUP_STATE *) context; @@ -765,6 +879,11 @@ static void cleanup_del_header(void *context, ssize_t index, char *hdr_name) header_offset = cleanup_find_header(state, index, hdr_name, rec_buf, &last_type, NO_PTR_BACKUP, SKIP_ONE_HEADER); + if (header_offset == CLEANUP_FIND_HEADER_IOERROR) { + vstring_free(rec_buf); + /* Warning and errno->error mapping are done elsewhere. */ + return (cleanup_milter_error(state, 0)); + } /* Memory usage for header offsets is limited by header_size_limit. */ if (header_offset > 0) { ssize_t off_len = 1; @@ -772,15 +891,26 @@ static void cleanup_del_header(void *context, ssize_t index, char *hdr_name) off_t *off_list = (off_t *) mymalloc(off_len * sizeof(*off_list)); int n; +#define CLEANUP_DEL_HEADER_RETURN(ret) do { \ + vstring_free(rec_buf); \ + myfree((char *) off_list); \ + return (ret); \ + } while (0) + off_list[0] = header_offset; for (;;) { curr_offset = vstream_ftell(state->dst); /* Caution: this macro terminates the loop at end-of-message. */ /* Don't do complex processing while breaking out of this loop. */ - GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, curr_offset); + GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, curr_offset, + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, 0))); if (rec_type == REC_TYPE_PTR) { - if (rec_goto(state->dst, STR(rec_buf)) < 0) - msg_fatal("%s: read file %s: %m", myname, cleanup_path); + if (rec_goto(state->dst, STR(rec_buf)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, + errno)); + } /* Don't update last_type; PTR may follow REC_TYPE_CONT. */ continue; } @@ -797,17 +927,25 @@ static void cleanup_del_header(void *context, ssize_t index, char *hdr_name) last_type = rec_type; } /* Mark the header text records as deleted. */ - for (n = 0; n < off_used; n++) - if (rec_put_type(state->dst, REC_TYPE_DTXT, off_list[n]) < 0) - msg_fatal("%s: write file %s: %m", myname, cleanup_path); + for (n = 0; n < off_used; n++) { + if (rec_put_type(state->dst, REC_TYPE_DTXT, off_list[n]) < 0) { + msg_warn("%s: write file %s: %m", myname, cleanup_path); + CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, errno)); + } + } myfree((char *) off_list); } vstring_free(rec_buf); + + /* + * In case of error while doing record output. + */ + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0)); } /* cleanup_add_rcpt - append recipient address */ -static void cleanup_add_rcpt(void *context, char *rcpt) +static const char *cleanup_add_rcpt(void *context, char *rcpt) { const char *myname = "cleanup_add_rcpt"; CLEANUP_STATE *state = (CLEANUP_STATE *) context; @@ -841,11 +979,15 @@ static void cleanup_add_rcpt(void *context, char *rcpt) */ #define NO_DSN_ORCPT ((char *) 0) - if ((new_rcpt_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if ((new_rcpt_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } cleanup_addr_bcc(state, rcpt); - if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path); + if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, (long) state->append_rcpt_pt_target); @@ -853,8 +995,10 @@ static void cleanup_add_rcpt(void *context, char *rcpt) * Pointer flipping: update the old "recipient append" pointer record * value to the location of the new recipient record. */ - if (vstream_fseek(state->dst, state->append_rcpt_pt_offset, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, state->append_rcpt_pt_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, (long) new_rcpt_offset); @@ -866,11 +1010,16 @@ static void cleanup_add_rcpt(void *context, char *rcpt) * record that was written while Postfix received the message. */ state->append_rcpt_pt_offset = reverse_ptr_offset; + + /* + * In case of error while doing record output. + */ + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0)); } /* cleanup_del_rcpt - remove recipient and all its expansions */ -static void cleanup_del_rcpt(void *context, char *rcpt) +static const char *cleanup_del_rcpt(void *context, char *rcpt) { const char *myname = "cleanup_del_rcpt"; CLEANUP_STATE *state = (CLEANUP_STATE *) context; @@ -908,26 +1057,48 @@ static void cleanup_del_rcpt(void *context, char *rcpt) * XXX Remove the (dsn_orcpt, dsn_notify, orcpt, recip) tuple from the * duplicate recipient filter. */ - if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } +#define CLEANUP_DEL_RCPT_RETURN(ret) do { \ + if (orig_rcpt != 0) \ + myfree(orig_rcpt); \ + if (dsn_orcpt != 0) \ + myfree(dsn_orcpt); \ + vstring_free(buf); \ + return (ret); \ + } while (0) + buf = vstring_alloc(100); - while (CLEANUP_OUT_OK(state)) { - if ((curr_offset = vstream_ftell(state->dst)) < 0) - msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path); - if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) <= 0) - msg_fatal("%s: read file %s: %m", myname, cleanup_path); + for (;;) { + if (CLEANUP_OUT_OK(state) == 0) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, 0)); + if ((curr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } + if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) <= 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } if (rec_type == REC_TYPE_END) break; /* Skip over message content. */ if (rec_type == REC_TYPE_MESG) { - if (vstream_fseek(state->dst, state->xtra_offset, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, state->xtra_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } continue; } start = STR(buf); if (rec_type == REC_TYPE_PTR) { - if (rec_goto(state->dst, start) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if (rec_goto(state->dst, start) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } continue; } /* Map attribute names to pseudo record type. */ @@ -960,11 +1131,13 @@ static void cleanup_del_rcpt(void *context, char *rcpt) break; case REC_TYPE_RCPT: /* rewritten RCPT TO address */ if (strcmp(orig_rcpt ? orig_rcpt : start, rcpt) == 0) { - if (vstream_fseek(state->dst, curr_offset, SEEK_SET) < 0) - msg_fatal("%s: seek file %s: %m", myname, cleanup_path); + if (vstream_fseek(state->dst, curr_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } if (REC_PUT_BUF(state->dst, REC_TYPE_DRCP, buf) < 0) { msg_warn("%s: write queue file: %m", state->queue_id); - state->errs |= CLEANUP_STAT_WRITE; + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); } count++; } @@ -983,20 +1156,17 @@ static void cleanup_del_rcpt(void *context, char *rcpt) break; } } - if (orig_rcpt != 0) /* can't happen */ - myfree(orig_rcpt); - if (dsn_orcpt != 0) /* can't happen */ - myfree(dsn_orcpt); - vstring_free(buf); if (msg_verbose) msg_info("%s: deleted %d records for recipient \"%s\"", myname, count, rcpt); + + CLEANUP_DEL_RCPT_RETURN(0); } /* cleanup_repl_body - replace message body */ -static void cleanup_repl_body(void *context, VSTRING *body) +static const char *cleanup_repl_body(void *context, VSTRING *body) { const char *myname = "cleanup_repl_body"; @@ -1088,6 +1258,13 @@ static const char *cleanup_milter_apply(CLEANUP_STATE *state, const char *resp) if (msg_verbose) msg_info("%s: %s", myname, resp); + + /* + * We don't report errors that were already reported by the content + * editing call-back routines. See cleanup_milter_error() above. + */ + if (CLEANUP_OUT_OK(state) == 0) + return (0); switch (resp[0]) { case 'H': /* XXX Should log the reason here. */ @@ -1293,7 +1470,8 @@ void cleanup_milter_emul_data(CLEANUP_STATE *state, MILTERS *milters) #ifdef TEST /* - * Queue file editing driver for regression tests. + * Queue file editing driver for regression tests. In this case it is OK to + * report fatal errors after I/O errors. */ #include #include diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index c42b00c76..ad6f60c84 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,8 +20,8 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20060630" -#define MAIL_VERSION_NUMBER "2.3-RC2" +#define MAIL_RELEASE_DATE "20060702" +#define MAIL_VERSION_NUMBER "2.3-RC3" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION MAIL_VERSION_NUMBER diff --git a/postfix/src/milter/milter.c b/postfix/src/milter/milter.c index 94bfa0142..2ab2a516e 100644 --- a/postfix/src/milter/milter.c +++ b/postfix/src/milter/milter.c @@ -37,15 +37,15 @@ /* ins_header, del_header, add_rcpt, /* del_rcpt, repl_body, context) /* MILTERS *milters; -/* void (*add_header) (void *context, char *name, char *value); -/* void (*upd_header) (void *context, ssize_t index, +/* const char *(*add_header) (void *context, char *name, char *value); +/* const char *(*upd_header) (void *context, ssize_t index, /* char *name, char *value); -/* void (*ins_header) (void *context, ssize_t index, +/* const char *(*ins_header) (void *context, ssize_t index, /* char *name, char *value); -/* void (*del_header) (void *context, ssize_t index, char *name); -/* void (*add_rcpt) (void *context, char *rcpt); -/* void (*del_rcpt) (void *context, char *rcpt); -/* void (*repl_body) (void *context, VSTRING *body); +/* const char *(*del_header) (void *context, ssize_t index, char *name); +/* const char *(*add_rcpt) (void *context, char *rcpt); +/* const char *(*del_rcpt) (void *context, char *rcpt); +/* const char *(*repl_body) (void *context, VSTRING *body); /* void *context; /* /* const char *milter_conn_event(milters, client_name, client_addr, @@ -272,13 +272,13 @@ void milter_macro_callback(MILTERS *milters, /* milter_edit_callback - specify queue file edit call-back information */ void milter_edit_callback(MILTERS *milters, - void (*add_header) (void *, char *, char *), - void (*upd_header) (void *, ssize_t, char *, char *), - void (*ins_header) (void *, ssize_t, char *, char *), - void (*del_header) (void *, ssize_t, char *), - void (*add_rcpt) (void *, char *), - void (*del_rcpt) (void *, char *), - void (*repl_body) (void *, VSTRING *), + const char *(*add_header) (void *, char *, char *), + const char *(*upd_header) (void *, ssize_t, char *, char *), + const char *(*ins_header) (void *, ssize_t, char *, char *), + const char *(*del_header) (void *, ssize_t, char *), + const char *(*add_rcpt) (void *, char *), + const char *(*del_rcpt) (void *, char *), + const char *(*repl_body) (void *, VSTRING *), void *chg_context) { milters->add_header = add_header; @@ -838,9 +838,9 @@ int main(int argc, char **argv) msg_warn("deleting existing milters"); milter_free(milters); } - milters = milter_create(args[0], var_milt_conn_time, - var_milt_cmd_time, var_milt_msg_time, - var_milt_protocol, var_milt_def_action, + milters = milter_create(args[0], var_milt_conn_time, + var_milt_cmd_time, var_milt_msg_time, + var_milt_protocol, var_milt_def_action, conn_macros, helo_macros, mail_macros, rcpt_macros, data_macros, eod_macros, unk_macros); diff --git a/postfix/src/milter/milter.h b/postfix/src/milter/milter.h index 2cf63e5e3..9e480193a 100644 --- a/postfix/src/milter/milter.h +++ b/postfix/src/milter/milter.h @@ -61,21 +61,21 @@ typedef struct MILTERS { char *eod_macros; /* macros for END-OF-DATA command */ char *unk_macros; /* macros for unknown command */ void *chg_context; /* context for queue file changes */ - void (*add_header) (void *, char *, char *); - void (*upd_header) (void *, ssize_t, char *, char *); - void (*del_header) (void *, ssize_t, char *); - void (*ins_header) (void *, ssize_t, char *, char *); - void (*add_rcpt) (void *, char *); - void (*del_rcpt) (void *, char *); - void (*repl_body) (void *, VSTRING *); + const char *(*add_header) (void *, char *, char *); + const char *(*upd_header) (void *, ssize_t, char *, char *); + const char *(*del_header) (void *, ssize_t, char *); + const char *(*ins_header) (void *, ssize_t, char *, char *); + const char *(*add_rcpt) (void *, char *); + const char *(*del_rcpt) (void *, char *); + const char *(*repl_body) (void *, VSTRING *); } MILTERS; typedef const char *(*MILTER_MAC_LOOKUP_FN) (const char *, void *); -typedef void (*MILTER_ADD_HEADER_FN) (void *, char *, char *); -typedef void (*MILTER_EDIT_HEADER_FN) (void *, ssize_t, char *, char *); -typedef void (*MILTER_DEL_HEADER_FN) (void *, ssize_t, char *); -typedef void (*MILTER_EDIT_RCPT_FN) (void *, char *); -typedef void (*MILTER_EDIT_BODY_FN) (void *, VSTRING *); +typedef const char *(*MILTER_ADD_HEADER_FN) (void *, char *, char *); +typedef const char *(*MILTER_EDIT_HEADER_FN) (void *, ssize_t, char *, char *); +typedef const char *(*MILTER_DEL_HEADER_FN) (void *, ssize_t, char *); +typedef const char *(*MILTER_EDIT_RCPT_FN) (void *, char *); +typedef const char *(*MILTER_EDIT_BODY_FN) (void *, VSTRING *); extern MILTERS *milter_create(const char *, int, int, int, const char *, const char *, diff --git a/postfix/src/milter/milter8.c b/postfix/src/milter/milter8.c index 8b5c44220..e926ac2d1 100644 --- a/postfix/src/milter/milter8.c +++ b/postfix/src/milter/milter8.c @@ -19,7 +19,7 @@ /* MILTER *milter8_receive(stream) /* VSTREAM *stream; /* DESCRIPTION -/* This modulde implements the MTA side of the Sendmail 8 mail +/* This module implements the MTA side of the Sendmail 8 mail /* filter protocol. /* /* milter8_create() creates a MILTER data structure with virtual @@ -625,7 +625,7 @@ static int vmilter8_read_data(MILTER8 *milter, ssize_t data_len, va_list ap) /* * Sanity checks. We may have excess data when the sender is confused. We - * may have a negative count when we're confused outselves. + * may have a negative count when we're confused ourselves. */ if (data_left > 0) { msg_warn("%s: left-over data %ld bytes", myname, (long) data_left); @@ -863,11 +863,12 @@ static const char *milter8_event(MILTER8 *milter, int event, const char *smfir_name; MILTERS *parent; UINT32_TYPE index; + const char *edit_resp; #define DONT_SKIP_REPLY 0 /* - * Skip this event if it is not defined for my protocol version. + * Skip this event if it doesn't exist in the protocol that I announced. */ #ifndef USE_LIBMILTER_INCLUDES if ((skip_event_flag & milter->np_mask) != 0) { @@ -880,9 +881,9 @@ static const char *milter8_event(MILTER8 *milter, int event, #endif /* - * Send the macros even when the corresponding list is empty. This is not - * a problem because we're sending macros and event parameters in one - * transaction. + * Send the macros for this event, even when we're not reporting the + * event itself. This does not introduce a performance problem because + * we're sending macros and event parameters in one VSTREAM transaction. */ if (msg_verbose) { VSTRING *buf = vstring_alloc(100); @@ -921,7 +922,8 @@ static const char *milter8_event(MILTER8 *milter, int event, } /* - * Size the command data. + * Compute the command data size. This is necessary because the protocol + * sends length before content. */ va_start(ap, macros); data_len = vmilter8_size_data(ap); @@ -938,8 +940,8 @@ static const char *milter8_event(MILTER8 *milter, int event, /* * Special feature: don't wait for one reply per header. This allows us - * to send multiple headers in one transaction, and improves over-all - * performance. + * to send multiple headers in one VSTREAM transaction, and improves + * over-all performance. */ if (skip_reply) { if (msg_verbose) @@ -960,9 +962,9 @@ static const char *milter8_event(MILTER8 *milter, int event, if (milter8_read_cmd(milter, &cmd, &data_size) != 0) return (milter->def_reply); if (msg_verbose) - msg_info("reply: %s %d", + msg_info("reply: %s data %ld bytes", (smfir_name = str_name_code(smfir_table, cmd)) != 0 ? - smfir_name : "unknown", data_size); + smfir_name : "unknown", (long) data_size); switch (cmd) { /* @@ -1051,7 +1053,7 @@ static const char *milter8_event(MILTER8 *milter, int event, #endif milter->state = MILTER8_STAT_REJECT_CON; return (milter8_def_reply(milter, - "451 4.7.1 Service unavailable - try again later")); + "451 4.7.1 Service unavailable - try again later")); } else { return ("451 4.7.1 Service unavailable - try again later"); } @@ -1136,6 +1138,9 @@ static const char *milter8_event(MILTER8 *milter, int event, MILTER8_DATA_END) != 0) return (milter->def_reply); parent = milter->m.parent; + /* XXX Sendmail 8 compatibility. */ + if (index == 0) + index = 1; if ((ssize_t) index < 1) { msg_warn("milter %s: bad change header index: %ld", milter->m.name, (long) index); @@ -1149,12 +1154,16 @@ static const char *milter8_event(MILTER8 *milter, int event, return (milter->def_reply); } if (STR(milter->body)[0]) - parent->upd_header(parent->chg_context, (ssize_t) index, - STR(milter->buf), - STR(milter->body)); + edit_resp = parent->upd_header(parent->chg_context, + (ssize_t) index, + STR(milter->buf), + STR(milter->body)); else - parent->del_header(parent->chg_context, (ssize_t) index, - STR(milter->buf)); + edit_resp = parent->del_header(parent->chg_context, + (ssize_t) index, + STR(milter->buf)); + if (edit_resp) + return (milter8_def_reply(milter, edit_resp)); continue; #endif @@ -1168,8 +1177,11 @@ static const char *milter8_event(MILTER8 *milter, int event, MILTER8_DATA_END) != 0) return (milter->def_reply); parent = milter->m.parent; - parent->add_header(parent->chg_context, STR(milter->buf), - STR(milter->body)); + edit_resp = parent->add_header(parent->chg_context, + STR(milter->buf), + STR(milter->body)); + if (edit_resp) + return (milter8_def_reply(milter, edit_resp)); continue; /* @@ -1193,8 +1205,12 @@ static const char *milter8_event(MILTER8 *milter, int event, return (milter->def_reply); } parent = milter->m.parent; - parent->ins_header(parent->chg_context, (ssize_t) index + 1, - STR(milter->buf), STR(milter->body)); + edit_resp = parent->ins_header(parent->chg_context, + (ssize_t) index + 1, + STR(milter->buf), + STR(milter->body)); + if (edit_resp) + return (milter8_def_reply(milter, edit_resp)); continue; #endif @@ -1207,7 +1223,10 @@ static const char *milter8_event(MILTER8 *milter, int event, MILTER8_DATA_END) != 0) return (milter->def_reply); parent = milter->m.parent; - parent->add_rcpt(parent->chg_context, STR(milter->buf)); + edit_resp = parent->add_rcpt(parent->chg_context, + STR(milter->buf)); + if (edit_resp) + return (milter8_def_reply(milter, edit_resp)); continue; /* @@ -1219,7 +1238,10 @@ static const char *milter8_event(MILTER8 *milter, int event, MILTER8_DATA_END) != 0) return (milter->def_reply); parent = milter->m.parent; - parent->del_rcpt(parent->chg_context, STR(milter->buf)); + edit_resp = parent->del_rcpt(parent->chg_context, + STR(milter->buf)); + if (edit_resp) + return (milter8_def_reply(milter, edit_resp)); continue; /* @@ -1233,7 +1255,10 @@ static const char *milter8_event(MILTER8 *milter, int event, MILTER8_DATA_END) != 0) return (milter->def_reply); parent = milter->m.parent; - parent->repl_body(parent->chg_context, milter->body); + edit_resp = parent->repl_body(parent->chg_context, + milter->body); + if (edit_resp) + return (milter8_def_reply(milter, edit_resp)); continue; #endif } diff --git a/postfix/src/milter/test-milter.c b/postfix/src/milter/test-milter.c index 3ff1586d6..a954024fc 100644 --- a/postfix/src/milter/test-milter.c +++ b/postfix/src/milter/test-milter.c @@ -7,14 +7,24 @@ * * Specifies a non-default reply. The default is to always continue. * + * -d Enable libmilter debugging. + * * -c connect|helo|mail|rcpt|data|header|eoh|body|eom|unknown|close|abort * * When to send the non-default reply. The default is "connect". * + * -i "index header-label header-value" + * + * Insert header at specified position. + * * -p inet:port@host|unix:/path/name * * The mail filter listen endpoint. * + * -r "index header-label header-value" + * + * Replace header at specified position. + * * -C count * * Terminate after count connections. @@ -86,6 +96,14 @@ static char *reply_code; static char *reply_dsn; static char *reply_message; +static char *ins_hdr; +static int ins_idx; +static char *ins_val; + +static char *chg_hdr; +static int chg_idx; +static char *chg_val; + static int test_reply(SMFICTX *ctx, int code) { if (code == SMFIR_REPLYCODE) { @@ -184,25 +202,20 @@ static sfsistat test_eoh(SMFICTX *ctx) static sfsistat test_body(SMFICTX *ctx, unsigned char *data, size_t data_len) { - printf("test_body %ld bytes\n", (long) data_len); + if (verbose == 0) + printf("test_body %ld bytes\n", (long) data_len); + else + printf("%*s", data_len, data); return (test_reply(ctx, test_body_reply)); } static sfsistat test_eom(SMFICTX *ctx) { printf("test_eom\n"); -#if 0 - if (smfi_insheader(ctx, 1, "Received", "insert at 1") == MI_FAILURE) + if (ins_hdr && smfi_insheader(ctx, ins_idx, ins_hdr, ins_val) == MI_FAILURE) fprintf(stderr, "smfi_insheader failed"); -#endif -#if 0 - if (smfi_chgheader(ctx, "Received", 1, "change received #1") == MI_FAILURE) - fprintf(stderr, "smfi_chgheader failed"); -#endif -#if 0 - if (smfi_chgheader(ctx, "date", 0, "change date #0") == MI_FAILURE) + if (chg_hdr && smfi_chgheader(ctx, chg_hdr, chg_idx, chg_val) == MI_FAILURE) fprintf(stderr, "smfi_chgheader failed"); -#endif return (test_reply(ctx, test_eom_reply)); } @@ -265,6 +278,22 @@ static struct smfiDesc smfilter = #endif }; +static void parse_hdr_info(const char *optarg, int *idx, + char **hdr, char **value) +{ + int len; + + len = strlen(optarg) + 1; + if ((*hdr = malloc(len)) == 0 || (*value = malloc(len)) == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + if (sscanf(optarg, "%d %s %[^\n]", idx, *hdr, *value) != 3) { + fprintf(stderr, "bad header info: %s\n", optarg); + exit(1); + } +} + int main(int argc, char **argv) { char *action = 0; @@ -273,7 +302,7 @@ int main(int argc, char **argv) int ch; int code; - while ((ch = getopt(argc, argv, "a:c:d:p:vC:")) > 0) { + while ((ch = getopt(argc, argv, "a:c:d:i:p:r:vC:")) > 0) { switch (ch) { case 'a': action = optarg; @@ -287,12 +316,26 @@ int main(int argc, char **argv) exit(1); } break; + case 'i': + if (ins_hdr) { + fprintf(stderr, "too many -i options\n"); + exit(1); + } + parse_hdr_info(optarg, &ins_idx, &ins_hdr, &ins_val); + break; case 'p': if (smfi_setconn(optarg) == MI_FAILURE) { fprintf(stderr, "smfi_setconn failed\n"); exit(1); } break; + case 'r': + if (chg_hdr) { + fprintf(stderr, "too many -r options\n"); + exit(1); + } + parse_hdr_info(optarg, &chg_idx, &chg_hdr, &chg_val); + break; case 'v': verbose++; break; @@ -301,7 +344,13 @@ int main(int argc, char **argv) break; default: fprintf(stderr, - "usage: %s [-a action] [-c command] [-C conn_count] [-d debug] -p port [-v]\n", + "usage: %s [-dv] \n" + "\t[-a action] non-default action\n" + "\t[-c command] non-default action trigger\n" + "\t[-i 'index label value'] insert header\n" + "\t-p port milter application\n" + "\t[-r 'index label value'] replace header\n" + "\t[-C conn_count] when to exit\n", argv[0]); exit(1); }