X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/099afc4f7362d39816cb3555127214548dd9cb35..01603eec64d42431f182b33008206facfc7f800e:/src/src/receive.c diff --git a/src/src/receive.c b/src/src/receive.c index 016a92d6b..9769e8893 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -144,7 +144,7 @@ Returns: available on-root space, in kilobytes All values are -1 if the STATFS functions are not available. */ -int +int_eximarith_t receive_statvfs(BOOL isspool, int *inodeptr) { #ifdef HAVE_STATFS @@ -223,7 +223,7 @@ if (STATVFS(CS path, &statbuf) != 0) /* Disks are getting huge. Take care with computing the size in kilobytes. */ -return (int)(((double)statbuf.F_BAVAIL * (double)statbuf.F_FRSIZE)/1024.0); +return (int_eximarith_t)(((double)statbuf.F_BAVAIL * (double)statbuf.F_FRSIZE)/1024.0); #else /* Unable to find partition sizes in this environment. */ @@ -258,22 +258,23 @@ Returns: FALSE if there isn't enough space, or if the information cannot BOOL receive_check_fs(int msg_size) { -int space, inodes; +int_eximarith_t space; +int inodes; if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0) { space = receive_statvfs(TRUE, &inodes); DEBUG(D_receive) - debug_printf("spool directory space = %dK inodes = %d " - "check_space = %dK inodes = %d msg_size = %d\n", + debug_printf("spool directory space = " PR_EXIM_ARITH "K inodes = %d " + "check_space = " PR_EXIM_ARITH "K inodes = %d msg_size = %d\n", space, inodes, check_spool_space, check_spool_inodes, msg_size); if ((space >= 0 && space < check_spool_space) || (inodes >= 0 && inodes < check_spool_inodes)) { - log_write(0, LOG_MAIN, "spool directory space check failed: space=%d " - "inodes=%d", space, inodes); + log_write(0, LOG_MAIN, "spool directory space check failed: space=" + PR_EXIM_ARITH " inodes=%d", space, inodes); return FALSE; } } @@ -283,15 +284,15 @@ if (check_log_space > 0 || check_log_inodes > 0) space = receive_statvfs(FALSE, &inodes); DEBUG(D_receive) - debug_printf("log directory space = %dK inodes = %d " - "check_space = %dK inodes = %d\n", + debug_printf("log directory space = " PR_EXIM_ARITH "K inodes = %d " + "check_space = " PR_EXIM_ARITH "K inodes = %d\n", space, inodes, check_log_space, check_log_inodes); - if ((space >= 0 && space < check_log_space) || - (inodes >= 0 && inodes < check_log_inodes)) + if ( space >= 0 && space < check_log_space + || inodes >= 0 && inodes < check_log_inodes) { - log_write(0, LOG_MAIN, "log directory space check failed: space=%d " - "inodes=%d", space, inodes); + log_write(0, LOG_MAIN, "log directory space check failed: space=" PR_EXIM_ARITH + " inodes=%d", space, inodes); return FALSE; } } @@ -553,11 +554,9 @@ Returns: TRUE if it did remove something; FALSE otherwise BOOL receive_remove_recipient(uschar *recipient) { -int count; DEBUG(D_receive) debug_printf("receive_remove_recipient(\"%s\") called\n", recipient); -for (count = 0; count < recipients_count; count++) - { +for (int count = 0; count < recipients_count; count++) if (Ustrcmp(recipients_list[count].address, recipient) == 0) { if ((--recipients_count - count) > 0) @@ -565,7 +564,6 @@ for (count = 0; count < recipients_count; count++) (recipients_count - count)*sizeof(recipient_item)); return TRUE; } - } return FALSE; } @@ -573,6 +571,30 @@ return FALSE; +/* Pause for a while waiting for input. If none received in that time, +close the logfile, if we had one open; then if we wait for a long-running +datasource (months, in one use-case) log rotation will not leave us holding +the file copy. */ + +static void +log_close_chk(void) +{ +if (!receive_timeout) + { + struct timeval t; + timesince(&t, &received_time); + if (t.tv_sec > 30*60) + mainlog_close(); + else + { + fd_set r; + FD_ZERO(&r); FD_SET(0, &r); + t.tv_sec = 30*60 - t.tv_sec; t.tv_usec = 0; + if (select(1, &r, NULL, NULL, &t) == 0) mainlog_close(); + } + } +} + /************************************************* * Read data portion of a non-SMTP message * *************************************************/ @@ -621,9 +643,11 @@ register int linelength = 0; if (!f.dot_ends) { - register int last_ch = '\n'; + int last_ch = '\n'; - for (; (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF; last_ch = ch) + for ( ; + log_close_chk(), (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF; + last_ch = ch) { if (ch == 0) body_zerocount++; if (last_ch == '\r' && ch != '\n') @@ -665,7 +689,7 @@ if (!f.dot_ends) ch_state = 1; -while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) +while (log_close_chk(), (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -1173,7 +1197,6 @@ Returns: nothing static void add_acl_headers(int where, uschar *acl_name) { -header_line *h, *next; header_line *last_received = NULL; switch(where) @@ -1194,7 +1217,7 @@ if (acl_removed_headers) { DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers removed by %s ACL:\n", acl_name); - for (h = header_list; h; h = h->next) if (h->type != htype_old) + for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) { const uschar * list = acl_removed_headers; int sep = ':'; /* This is specified as a colon-separated list */ @@ -1215,7 +1238,7 @@ if (acl_removed_headers) if (!acl_added_headers) return; DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers added by %s ACL:\n", acl_name); -for (h = acl_added_headers; h; h = next) +for (header_line * h = acl_added_headers, * next; h; h = next) { next = h->next; @@ -1305,15 +1328,12 @@ if (sender_fullhost) if (LOGGING(dnssec) && sender_host_dnssec) /*XXX sender_helo_dnssec? */ g = string_catn(g, US" DS", 3); g = string_append(g, 2, US" H=", sender_fullhost); - if (LOGGING(incoming_interface) && interface_address != NULL) - { - g = string_cat(g, - string_sprintf(" I=[%s]:%d", interface_address, interface_port)); - } + if (LOGGING(incoming_interface) && interface_address) + g = string_fmt_append(g, " I=[%s]:%d", interface_address, interface_port); } if (f.tcp_in_fastopen && !f.tcp_in_fastopen_logged) { - g = string_catn(g, US" TFO", 4); + g = string_catn(g, US" TFO*", f.tcp_in_fastopen_data ? 5 : 4); f.tcp_in_fastopen_logged = TRUE; } if (sender_ident) @@ -1321,7 +1341,17 @@ if (sender_ident) if (received_protocol) g = string_append(g, 2, US" P=", received_protocol); if (LOGGING(pipelining) && f.smtp_in_pipelining_advertised) - g = string_catn(g, US" L-", f.smtp_in_pipelining_used ? 2 : 3); + { + g = string_catn(g, US" L", 2); +#ifdef EXPERIMENTAL_PIPE_CONNECT + if (f.smtp_in_early_pipe_used) + g = string_catn(g, US"*", 1); + else if (f.smtp_in_early_pipe_advertised) + g = string_catn(g, US".", 1); +#endif + if (!f.smtp_in_pipelining_used) + g = string_catn(g, US"-", 1); + } return g; } @@ -1352,7 +1382,6 @@ run_mime_acl(uschar *acl, BOOL *smtp_yield_ptr, uschar **smtp_reply_ptr, FILE *mbox_file; uschar * rfc822_file_path = NULL; unsigned long mbox_size; -header_line *my_headerlist; uschar *user_msg, *log_msg; int mime_part_count_buffer = -1; uschar * mbox_filename; @@ -1360,7 +1389,8 @@ int rc = OK; /* check if it is a MIME message */ -for (my_headerlist = header_list; my_headerlist; my_headerlist = my_headerlist->next) +for (header_line * my_headerlist = header_list; my_headerlist; + my_headerlist = my_headerlist->next) if ( my_headerlist->type != '*' /* skip deleted headers */ && strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0 ) @@ -1615,7 +1645,6 @@ not. */ BOOL receive_msg(BOOL extract_recip) { -int i; int rc = FAIL; int msg_size = 0; int process_info_len = Ustrlen(process_info); @@ -1658,7 +1687,7 @@ uschar *user_msg, *log_msg; /* Working header pointers */ -header_line *h, *next; +header_line *next; /* Flags for noting the existence of certain headers (only one left) */ @@ -1670,6 +1699,7 @@ header_line *from_header = NULL; header_line *subject_header = NULL; header_line *msgid_header = NULL; header_line *received_header; +BOOL msgid_header_newly_created = FALSE; #ifdef EXPERIMENTAL_DMARC int dmarc_up = 0; @@ -1764,7 +1794,7 @@ single timeout for the whole message. */ else if (receive_timeout > 0) { os_non_restarting_signal(SIGALRM, data_timeout_handler); - alarm(receive_timeout); + ALARM(receive_timeout); } /* SIGTERM and SIGINT are caught always. */ @@ -1819,8 +1849,11 @@ for (;;) if (ptr >= header_size - 4) { int oldsize = header_size; - /* header_size += 256; */ + + if (header_size >= INT_MAX/2) + goto OVERSIZE; header_size *= 2; + if (!store_extend(next->text, oldsize, header_size)) next->text = store_newblock(next->text, header_size, ptr); } @@ -1926,6 +1959,7 @@ for (;;) if (message_size >= header_maxsize) { +OVERSIZE: next->text[ptr] = 0; next->slen = ptr; next->type = htype_other; @@ -1997,7 +2031,8 @@ for (;;) if (nextch == ' ' || nextch == '\t') { next->text[ptr++] = nextch; - message_size++; + if (++message_size >= header_maxsize) + goto OVERSIZE; continue; /* Iterate the loop */ } else if (nextch != EOF) (receive_ungetc)(nextch); /* For next time */ @@ -2120,7 +2155,8 @@ for (;;) the line, stomp on them here. */ if (had_zero > 0) - for (p = next->text; p < next->text + ptr; p++) if (*p == 0) *p = '?'; + for (uschar * p = next->text; p < next->text + ptr; p++) if (*p == 0) + *p = '?'; /* It is perfectly legal to have an empty continuation line at the end of a header, but it is confusing to humans @@ -2222,7 +2258,7 @@ normal case). */ DEBUG(D_receive) { debug_printf(">>Headers received:\n"); - for (h = header_list->next; h; h = h->next) + for (header_line * h = header_list->next; h; h = h->next) debug_printf("%s", h->text); debug_printf("\n"); } @@ -2249,7 +2285,7 @@ if (filter_test != FTEST_NONE && header_list->next == NULL) /* Scan the headers to identify them. Some are merely marked for later processing; some are dealt with here. */ -for (h = header_list->next; h; h = h->next) +for (header_line * h = header_list->next; h; h = h->next) { BOOL is_resent = strncmpic(h->text, US"resent-", 7) == 0; if (is_resent) contains_resent_headers = TRUE; @@ -2463,7 +2499,7 @@ if (extract_recip) /* Now scan the headers */ - for (h = header_list->next; h; h = h->next) + for (header_line * h = header_list->next; h; h = h->next) { if ((h->type == htype_to || h->type == htype_cc || h->type == htype_bcc) && (!contains_resent_headers || strncmpic(h->text, US"resent-", 7) == 0)) @@ -2476,7 +2512,7 @@ if (extract_recip) while (*s != 0) { uschar *ss = parse_find_address_end(s, FALSE); - uschar *recipient, *errmess, *p, *pp; + uschar *recipient, *errmess, *pp; int start, end, domain; /* Check on maximum */ @@ -2492,7 +2528,7 @@ if (extract_recip) of the header. */ pp = recipient = store_get(ss - s + 1); - for (p = s; p < ss; p++) if (*p != '\n') *pp++ = *p; + for (uschar * p = s; p < ss; p++) if (*p != '\n') *pp++ = *p; *pp = 0; #ifdef SUPPORT_I18N @@ -2662,9 +2698,9 @@ any illegal characters therein. */ if ( !msgid_header && ((!sender_host_address && !f.suppress_local_fixups) || f.submission_mode)) { - uschar *p; uschar *id_text = US""; uschar *id_domain = primary_hostname; + header_line * h; /* Permit only letters, digits, dots, and hyphens in the domain */ @@ -2681,7 +2717,7 @@ if ( !msgid_header else if (*new_id_domain) { id_domain = new_id_domain; - for (p = id_domain; *p; p++) + for (uschar * p = id_domain; *p; p++) if (!isalnum(*p) && *p != '.') *p = '-'; /* No need to test '-' ! */ } } @@ -2702,17 +2738,25 @@ if ( !msgid_header else if (*new_id_text) { id_text = new_id_text; - for (p = id_text; *p; p++) if (mac_iscntrl_or_special(*p)) *p = '-'; + for (uschar * p = id_text; *p; p++) if (mac_iscntrl_or_special(*p)) *p = '-'; } } - /* Add the header line - * Resent-* headers are prepended, per RFC 5322 3.6.6. Non-Resent-* are - * appended, to preserve classical expectations of header ordering. */ + /* Add the header line. + Resent-* headers are prepended, per RFC 5322 3.6.6. Non-Resent-* are + appended, to preserve classical expectations of header ordering. */ - header_add_at_position(!resents_exist, NULL, FALSE, htype_id, + h = header_add_at_position_internal(!resents_exist, NULL, FALSE, htype_id, "%sMessage-Id: <%s%s%s@%s>\n", resent_prefix, message_id_external, - (*id_text == 0)? "" : ".", id_text, id_domain); + *id_text == 0 ? "" : ".", id_text, id_domain); + + /* Arrange for newly-created Message-Id to be logged */ + + if (!resents_exist) + { + msgid_header_newly_created = TRUE; + msgid_header = h; + } } /* If we are to log recipients, keep a copy of the raw ones before any possible @@ -2722,7 +2766,7 @@ function may mess with the real recipients. */ if (LOGGING(received_recipients)) { raw_recipients = store_get(recipients_count * sizeof(uschar *)); - for (i = 0; i < recipients_count; i++) + for (int i = 0; i < recipients_count; i++) raw_recipients[i] = string_copy(recipients_list[i].address); raw_recipients_count = recipients_count; } @@ -2731,7 +2775,7 @@ if (LOGGING(received_recipients)) recipients will get here only if the conditions were right (allow_unqualified_ recipient is TRUE). */ -for (i = 0; i < recipients_count; i++) +for (int i = 0; i < recipients_count; i++) recipients_list[i].address = rewrite_address(recipients_list[i].address, TRUE, TRUE, global_rewrite_rules, rewrite_existflags); @@ -2930,7 +2974,7 @@ We start at the second header, skipping our own Received:. This rewriting is documented as happening *after* recipient addresses are taken from the headers by the -t command line option. An added Sender: gets rewritten here. */ -for (h = header_list->next; h; h = h->next) +for (header_line * h = header_list->next; h; h = h->next) { header_line *newh = rewrite_header(h, NULL, NULL, global_rewrite_rules, rewrite_existflags, TRUE); @@ -2969,7 +3013,7 @@ new Received:) has not yet been set. */ DEBUG(D_receive) { debug_printf(">>Headers after rewriting and local additions:\n"); - for (h = header_list->next; h; h = h->next) + for (header_line * h = header_list->next; h; h = h->next) debug_printf("%c %s", h->type, h->text); debug_printf("\n"); } @@ -3042,7 +3086,7 @@ if ((data_fd = Uopen(spool_name, O_RDWR|O_CREAT|O_EXCL, SPOOL_MODE)) < 0) /* Make sure the file's group is the Exim gid, and double-check the mode because the group setting doesn't always get set automatically. */ -if (fchown(data_fd, exim_uid, exim_gid)) +if (0 != exim_fchown(data_fd, exim_uid, exim_gid, spool_name)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed setting ownership on spool file %s: %s", spool_name, strerror(errno)); @@ -3236,16 +3280,15 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) if (recipients_count == 0) debug_printf("*** No recipients\n"); if (bad_addresses) { - error_block *eblock = bad_addresses; debug_printf("*** Bad address(es)\n"); - while (eblock != NULL) - { + for (error_block * eblock = bad_addresses; eblock; eblock = eblock->next) debug_printf(" %s: %s\n", eblock->text1, eblock->text2); - eblock = eblock->next; - } } } + log_write(0, LOG_MAIN|LOG_PANIC, "%s %s found in headers", + message_id, bad_addresses ? "bad addresses" : "no recipients"); + fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); /* If configured to send errors to the sender, but this fails, force @@ -3257,11 +3300,12 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) if (error_handling == ERRORS_SENDER) { if (!moan_to_sender( - (bad_addresses == NULL)? - (extracted_ignored? ERRMESS_IGADDRESS : ERRMESS_NOADDRESS) : - (recipients_list == NULL)? ERRMESS_BADNOADDRESS : ERRMESS_BADADDRESS, - bad_addresses, header_list, spool_data_file, FALSE)) - error_rc = (bad_addresses == NULL)? EXIT_NORECIPIENTS : EXIT_FAILURE; + bad_addresses + ? recipients_list ? ERRMESS_BADADDRESS : ERRMESS_BADNOADDRESS + : extracted_ignored ? ERRMESS_IGADDRESS : ERRMESS_NOADDRESS, + bad_addresses, header_list, spool_data_file, FALSE + ) ) + error_rc = bad_addresses ? EXIT_FAILURE : EXIT_NORECIPIENTS; } else { @@ -3456,13 +3500,12 @@ else #ifndef DISABLE_PRDR if (prdr_requested && recipients_count > 1 && acl_smtp_data_prdr) { - unsigned int c; int all_pass = OK; int all_fail = FAIL; smtp_printf("353 PRDR content analysis beginning\r\n", TRUE); /* Loop through recipients, responses must be in same order received */ - for (c = 0; recipients_count > c; c++) + for (unsigned int c = 0; recipients_count > c; c++) { uschar * addr= recipients_list[c].address; uschar * msg= US"PRDR R=<%s> %s"; @@ -3657,9 +3700,9 @@ if (sigsetjmp(local_scan_env, 1) == 0) had_local_scan_timeout = 0; os_non_restarting_signal(SIGALRM, local_scan_timeout_handler); - if (local_scan_timeout > 0) alarm(local_scan_timeout); + if (local_scan_timeout > 0) ALARM(local_scan_timeout); rc = local_scan(data_fd, &local_scan_data); - alarm(0); + ALARM_CLR(0); os_non_restarting_signal(SIGALRM, sigalrm_handler); f.enable_dollar_recipients = FALSE; @@ -3728,18 +3771,15 @@ the spool file gets corrupted. Ensure that all recipients are qualified. */ if (rc == LOCAL_SCAN_ACCEPT) { if (local_scan_data) - { - uschar *s; - for (s = local_scan_data; *s != 0; s++) if (*s == '\n') *s = ' '; - } - for (i = 0; i < recipients_count; i++) + for (uschar * s = local_scan_data; *s != 0; s++) if (*s == '\n') *s = ' '; + for (int i = 0; i < recipients_count; i++) { recipient_item *r = recipients_list + i; r->address = rewrite_address_qualify(r->address, TRUE); - if (r->errors_to != NULL) + if (r->errors_to) r->errors_to = rewrite_address_qualify(r->errors_to, TRUE); } - if (recipients_count == 0 && blackholed_by == NULL) + if (recipients_count == 0 && !blackholed_by) blackholed_by = US"local_scan"; } @@ -3858,10 +3898,9 @@ file fails, we have failed to accept this message. */ if (host_checking || blackholed_by) { - header_line *h; Uunlink(spool_name); msg_size = 0; /* Compute size for log line */ - for (h = header_list; h; h = h->next) + for (header_line * h = header_list; h; h = h->next) if (h->type != '*') msg_size += h->slen; } @@ -3939,9 +3978,15 @@ if (message_reference) g = add_host_info_for_log(g); -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (LOGGING(tls_cipher) && tls_in.cipher) + { g = string_append(g, 2, US" X=", tls_in.cipher); +# ifdef EXPERIMENTAL_TLS_RESUME + if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED) + g = string_catn(g, US"*", 1); +# endif + } if (LOGGING(tls_certificate_verified) && tls_in.cipher) g = string_append(g, 2, US" CV=", tls_in.certificate_verified ? "yes":"no"); if (LOGGING(tls_peerdn) && tls_in.peerdn) @@ -4007,16 +4052,20 @@ any characters except " \ and CR and so in particular it can contain NL! Therefore, make sure we use a printing-characters only version for the log. Also, allow for domain literals in the message id. */ -if (msgid_header) +if ( LOGGING(msg_id) && msgid_header + && (LOGGING(msg_id_created) || !msgid_header_newly_created) + ) { - uschar *old_id; + uschar * old_id; BOOL save_allow_domain_literals = allow_domain_literals; allow_domain_literals = TRUE; old_id = parse_extract_address(Ustrchr(msgid_header->text, ':') + 1, &errmsg, &start, &end, &domain, FALSE); allow_domain_literals = save_allow_domain_literals; - if (old_id != NULL) - g = string_append(g, 2, US" id=", string_printing(old_id)); + if (old_id) + g = string_append(g, 2, + msgid_header_newly_created ? US" id*=" : US" id=", + string_printing(old_id)); } /* If subject logging is turned on, create suitable printing-character @@ -4024,7 +4073,6 @@ text. By expanding $h_subject: we make use of the MIME decoding. */ if (LOGGING(subject) && subject_header) { - int i; uschar *p = big_buffer; uschar *ss = expand_string(US"$h_subject:"); @@ -4032,7 +4080,7 @@ if (LOGGING(subject) && subject_header) a C-like string, and turn any non-printers into escape sequences. */ *p++ = '\"'; - if (*ss != 0) for (i = 0; i < 100 && ss[i] != 0; i++) + if (*ss != 0) for (int i = 0; i < 100 && ss[i] != 0; i++) { if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; *p++ = ss[i]; @@ -4056,7 +4104,7 @@ if (message_logs && !blackholed_by) { int fd; uschar * m_name = spool_fname(US"msglog", message_subdir, message_id, US""); - + if ( (fd = Uopen(m_name, O_WRONLY|O_APPEND|O_CREAT, SPOOL_MODE)) < 0 && errno == ENOENT ) @@ -4215,7 +4263,7 @@ if(!smtp_reply) if (f.deliver_freeze) log_write(0, LOG_MAIN, "frozen by %s", frozen_by); if (f.queue_only_policy) log_write(L_delay_delivery, LOG_MAIN, "no immediate delivery: queued%s%s by %s", - *queue_name ? " in " : "", *queue_name ? CS queue_name : "", + *queue_name ? " in " : "", *queue_name ? CS queue_name : "", queued_by); } f.receive_call_bombout = FALSE;