diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index bba71b76d1..23de6f4018 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -12120,6 +12120,17 @@ contain the trailing slash. If &$config_file$& does not contain a slash, .vindex "&$config_file$&" The name of the main configuration file Exim is using. +.vitem &$connection_in_id$& +.vindex "&$connection_in_id$&" +When a incoming connecting in received, this variable contains a unique +id representing epoch+pid+localhost_number. See also &$message_exim_id$& + +.vitem &$connection_out_id$& +.vindex "&$connection_out_id$&" +Like &$connection_in_id$&, this variable creates a unique id for the +outgoing connection. This id is preserved when multiple deliveries +use the same connection. + .vitem &$dmarc_domain_policy$& &&& &$dmarc_status$& &&& &$dmarc_status_text$& &&& diff --git a/src/src/daemon.c b/src/src/daemon.c index b4629bad59..beb5f56d61 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -381,6 +381,7 @@ if (pid == 0) store_pool = POOL_PERM; interface_address = string_copy(interface_address); + connection_in_id = create_connection_id(); store_pool = old_pool; /* Check for a tls-on-connect port */ diff --git a/src/src/deliver.c b/src/src/deliver.c index 467813800f..97138a4ec9 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -433,6 +433,7 @@ for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next) addr2->special_action = addr->special_action; addr2->message = addr->message; addr2->user_message = addr->user_message; + addr2->connection_out_id = addr->connection_out_id; /* todo: is this needed? */ } } @@ -880,12 +881,14 @@ const uschar * save_domain = deliver_domain; uschar * save_local = deliver_localpart; const uschar * save_host = deliver_host; const uschar * save_address = deliver_host_address; +unsigned long save_connection_out_id = connection_out_id; const int save_port = deliver_host_port; router_name = addr->router ? addr->router->name : NULL; deliver_domain = addr->domain; deliver_localpart = addr->local_part; deliver_host = addr->host_used ? addr->host_used->name : NULL; +connection_out_id = addr->connection_out_id; if (!addr->transport) { @@ -916,6 +919,7 @@ deliver_host = save_host; deliver_localpart = save_local; deliver_domain = save_domain; router_name = transport_name = NULL; +connection_out_id = save_connection_out_id; } #endif /*DISABLE_EVENT*/ @@ -3610,6 +3614,8 @@ while (!done) ptr += sizeof(addr->delivery_time); memcpy(&addr->flags, ptr, sizeof(addr->flags)); ptr += sizeof(addr->flags); + memcpy(&addr->connection_out_id, ptr, sizeof(addr->connection_out_id)); + ptr += sizeof(addr->connection_out_id); addr->message = *ptr ? string_copy(ptr) : NULL; while(*ptr++); addr->user_message = *ptr ? string_copy(ptr) : NULL; @@ -4928,6 +4934,8 @@ all pipes, so I do not see a reason to use non-blocking IO here ptr += sizeof(addr->delivery_time); memcpy(ptr, &addr->flags, sizeof(addr->flags)); ptr += sizeof(addr->flags); + memcpy(ptr, &addr->connection_out_id, sizeof(addr->connection_out_id)); + ptr += sizeof(addr->connection_out_id); if (!addr->message) *ptr++ = 0; else ptr += sprintf(CS ptr, "%.1024s", addr->message) + 1; diff --git a/src/src/exim.c b/src/src/exim.c index dfd6df76cb..72ab4d0e56 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2705,7 +2705,7 @@ for (i = 1; i < argc; i++) union sockaddr_46 interface_sock; EXIM_SOCKLEN_T size = sizeof(interface_sock); - if (argc != i + 6) + if (argc != 6 && argc != 7) exim_fail("exim: too many or too few arguments after -MC\n"); if (msg_action_arg >= 0) @@ -2721,6 +2721,9 @@ for (i = 1; i < argc; i++) queue_run_pid = passed_qr_pid; queue_run_pipe = passed_qr_pipe; + if (argc == 7) + connection_out_id = Uatol(argv[++i]); + if (!mac_ismsgid(argv[i])) exim_fail("exim: malformed message id %s after -MC option\n", argv[i]); diff --git a/src/src/expand.c b/src/src/expand.c index 4af4a3652c..8a270e0938 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -442,7 +442,8 @@ enum vtypes { vtype_load_avg, /* value not used; result is int from os_getloadavg */ vtype_pspace, /* partition space; value is T/F for spool/log */ vtype_pinodes, /* partition inodes; value is T/F for spool/log */ - vtype_cert /* SSL certificate */ + vtype_cert, /* SSL certificate */ + vtype_connid /* valid: uint64 connection id encoding */ #ifndef DISABLE_DKIM ,vtype_dkim /* Lookup of value in DKIM signature */ #endif @@ -517,6 +518,8 @@ static var_entry var_table[] = { { "compile_number", vtype_stringptr, &version_cnumber }, { "config_dir", vtype_stringptr, &config_main_directory }, { "config_file", vtype_stringptr, &config_main_filename }, + { "connection_in_id", vtype_connid, &connection_in_id }, + { "connection_out_id", vtype_connid, &connection_out_id }, { "csa_status", vtype_stringptr, &csa_status }, #ifdef EXPERIMENTAL_DCC { "dcc_header", vtype_stringptr, &dcc_header }, @@ -2075,6 +2078,8 @@ switch (vp->type) return dkim_exim_expand_query((int)(long)val); #endif + case vtype_connid: + return string_format_connection_id(*(unsigned long *)val); } return NULL; /* Unknown variable. Silences static checkers. */ diff --git a/src/src/functions.h b/src/src/functions.h index be929a7108..e328a77108 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -510,6 +510,8 @@ extern uschar *string_copy_dnsdomain(uschar *); extern uschar *string_copy_malloc(const uschar *); extern uschar *string_dequote(const uschar **); extern uschar *string_format_size(int, uschar *); +extern unsigned long create_connection_id(); +extern uschar *string_format_connection_id(unsigned long connection_id); extern int string_interpret_escape(const uschar **); extern int string_is_ip_address(const uschar *, int *); #ifdef SUPPORT_I18N diff --git a/src/src/globals.c b/src/src/globals.c index a5711c73b2..0cad5650ab 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -651,6 +651,9 @@ int av_failed = FALSE; /* boolean but accessed as vtype_int*/ uschar *av_scanner = US"sophie:/var/run/sophie"; /* AV scanner */ #endif +uschar *base62_alpha = + US"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + #if BASE_62 == 62 uschar *base62_chars= US"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -723,6 +726,8 @@ uid_t config_uid = CONFIGURE_OWNER; uid_t config_uid = 0; #endif +unsigned long connection_in_id = 0; +unsigned long connection_out_id= 0; int connection_max_messages= -1; uschar *continue_proxy_cipher = NULL; uschar *continue_hostname = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index b570078c3a..5f08aa0fbc 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -367,6 +367,7 @@ extern int av_failed; /* TRUE if the AV process failed */ extern uschar *av_scanner; /* AntiVirus scanner to use for the malware condition */ #endif +extern uschar *base62_alpha; /* Table of 62 base-62 characters */ extern uschar *base62_chars; /* Table of base-62 characters */ extern uschar *bi_command; /* Command for -bi option */ extern uschar *big_buffer; /* Used for various temp things */ @@ -428,6 +429,9 @@ extern uschar *continue_host_address; /* IP address for ditto */ extern int continue_sequence; /* Sequence num for continued delivery */ extern uschar *continue_transport; /* Transport for continued delivery */ +extern unsigned long connection_in_id; /* Incoming connection ID */ +extern unsigned long connection_out_id;/* Outgoing connection ID */ + extern uschar *csa_status; /* Client SMTP Authorization result */ typedef struct { diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 12ed5bc612..45ab590f5e 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -257,6 +257,7 @@ const blob * fastopen_blob = NULL; #ifndef DISABLE_EVENT deliver_host_address = host->address; deliver_host_port = port; +connection_out_id = create_connection_id(); if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1; #endif diff --git a/src/src/string.c b/src/src/string.c index fbdc0246d4..ad43ff1421 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -181,6 +181,47 @@ else return buffer; } +/************************************************* +* Create connection_(in|out)_id uint64 * +*************************************************/ + +/* + Format is based on message_exim_id: + * 30 bits epoch with msb stripped, making it + impossible to represent dated before 2004 + * 22 bits pid + * 12 bits localhost_number & usec + + total is uint64 +*/ + +unsigned long +create_connection_id() +{ +struct timeval tv; +(void)gettimeofday(&tv, NULL); +exim_wait_tick(&tv, 5000); + +return (unsigned long)(tv.tv_sec & 0x3fffffff) << (22 + 12) | + (host_number * 200 + tv.tv_usec / 5000) << 22 | getpid(); +} + +uschar * +string_format_connection_id(unsigned long connection_id) +{ +static uschar yield[12]; + +uschar *p = yield + sizeof(yield) - 1; +*p = 0; + +for (int i=10;i>=0;i--) + { + *(--p) = base62_alpha[connection_id % 62]; + connection_id /= 62; + } + + return string_copy(yield); +} #ifndef COMPILE_UTILITY diff --git a/src/src/structs.h b/src/src/structs.h index aa394ac734..bc734a0024 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -564,6 +564,7 @@ typedef struct address_item { uschar *message; /* error message */ uschar *user_message; /* error message that can be sent over SMTP or quoted in bounce message */ + unsigned long connection_out_id;/* connection_id returned from forked child */ uschar *onetime_parent; /* saved original parent for onetime */ uschar **pipe_expandn; /* numeric expansions for pipe from filter */ uschar *return_filename; /* name of return file */ diff --git a/src/src/transport.c b/src/src/transport.c index d9eba1621f..c357a32415 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1872,7 +1872,7 @@ void transport_do_pass_socket(const uschar *transport_name, const uschar *hostname, const uschar *hostaddress, uschar *id, int socket_fd) { -int i = 20; +int i = 21; const uschar **argv; /* Set up the calling arguments; use the standard function for the basics, @@ -1910,6 +1910,7 @@ argv[i++] = US transport_name; argv[i++] = US hostname; argv[i++] = US hostaddress; argv[i++] = string_sprintf("%d", continue_sequence + 1); +argv[i++] = string_sprintf("%ld", connection_out_id); argv[i++] = id; argv[i++] = NULL; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 5c5f37d2b5..1daa9346ec 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1133,6 +1133,7 @@ while (count-- > 0) /* The address was accepted */ addr->host_used = sx->conn_args.host; + addr->connection_out_id = connection_out_id; DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__); if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),