.code
${readsocket{/socket/name}{request string}{3s}}
.endd
+
The third argument is a list of options, of which the first element is the timeout
and must be present if the argument is given.
Further elements are options of form &'name=value'&.
-One option type is currently recognised, defining whether (the default)
+Two option types is currently recognised: shutdown and tls.
+The first defines whether (the default)
or not a shutdown is done on the connection after sending the request.
Example, to not do so (preferred, eg. by some webservers):
.code
${readsocket{/socket/name}{request string}{3s:shutdown=no}}
.endd
+.new
+The second, tls, controls the use of TLS on the connection. Example:
+.code
+${readsocket{/socket/name}{request string}{3s:tls=yes}}
+.endd
+The default is to not use TLS.
+If it is enabled, a shutdown as descripbed above is never done.
+.wen
+
A fourth argument allows you to change any newlines that are in the data
that is read, in the same way as for &%readfile%& (see above). This example
turns them into spaces:
connection is using TLS; with cutthrough connections this is quite likely.
JH/16 Fix ARC verification to do AS checks in reverse order.
+JH/16 Support a "tls" option on the ${readsocket } expansion item.
Exim version 4.91
when individual headers are wrapped onto multiple lines; with previous
facilities hard to parse.
+ 2. The ${readsocket } expansion item now takes a "tls" option, doing the
+ obvious thing.
+
Version 4.91
--------------
}
+#ifdef SUPPORT_TLS
+static gstring *
+cat_file_tls(void * tls_ctx, gstring * yield, uschar * eol)
+{
+int rc;
+uschar * s;
+uschar buffer[1024];
+
+while ((rc = tls_read(tls_ctx, buffer, sizeof(buffer))) > 0)
+ for (s = buffer; rc--; s++)
+ yield = eol && *s == '\n'
+ ? string_cat(yield, eol) : string_catn(yield, s, 1);
+
+/* We assume that all errors, and any returns of zero bytes,
+are actually EOF. */
+
+(void) string_from_gstring(yield);
+return yield;
+}
+#endif
/*************************************************
int timeout = 5;
int save_ptr = yield->ptr;
FILE *f;
- uschar *arg;
- uschar *sub_arg[4];
+ uschar * arg;
+ uschar * sub_arg[4];
+ uschar * server_name = NULL;
+ host_item host;
BOOL do_shutdown = TRUE;
+#ifdef SUPPORT_TLS
+ BOOL do_tls = FALSE;
+ void * tls_ctx = NULL;
+#endif
blob reqstr;
if (expand_forbid & RDO_READSOCK)
while ((item = string_nextinlist(&list, &sep, NULL, 0)))
if (Ustrncmp(item, US"shutdown=", 9) == 0)
- if (Ustrcmp(item + 9, US"no") == 0)
- do_shutdown = FALSE;
+ { if (Ustrcmp(item + 9, US"no") == 0) do_shutdown = FALSE; }
+#ifdef SUPPORT_TLS
+ else if (Ustrncmp(item, US"tls=", 4) == 0)
+ { if (Ustrcmp(item + 9, US"no") != 0) do_tls = TRUE; }
+#endif
}
- else sub_arg[3] = NULL; /* No eol if no timeout */
+ else
+ sub_arg[3] = NULL; /* No eol if no timeout */
/* If skipping, we don't actually do anything. Otherwise, arrange to
connect to either an IP or a Unix socket. */
if (Ustrncmp(sub_arg[0], "inet:", 5) == 0)
{
int port;
- uschar * server_name = sub_arg[0] + 5;
- uschar * port_name = Ustrrchr(server_name, ':');
+ uschar * port_name;
+
+ server_name = sub_arg[0] + 5;
+ port_name = Ustrrchr(server_name, ':');
/* Sort out the port */
}
fd = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
- timeout, NULL, &expand_string_message, &reqstr);
+ timeout, &host, &expand_string_message,
+ do_tls ? NULL : &reqstr);
callout_address = NULL;
if (fd < 0)
goto SOCK_FAIL;
- reqstr.len = 0;
+ if (!do_tls) reqstr.len = 0;
}
/* Handle a Unix domain socket */
sockun.sun_family = AF_UNIX;
sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1),
sub_arg[0]);
+ server_name = sockun.sun_path;
sigalrm_seen = FALSE;
alarm(timeout);
"%s: %s", sub_arg[0], strerror(errno));
goto SOCK_FAIL;
}
+ host.name = server_name;
+ host.address = US"";
}
DEBUG(D_expand) debug_printf_indent("connected to socket %s\n", sub_arg[0]);
+#ifdef SUPPORT_TLS
+ if (do_tls)
+ {
+ tls_support tls_dummy = {0};
+ uschar * errstr;
+
+ if (!(tls_ctx = tls_client_start(fd, &host, NULL, NULL, NULL,
+ &tls_dummy, &errstr)))
+ {
+ expand_string_message = string_sprintf("TLS connect failed: %s", errstr);
+ goto SOCK_FAIL;
+ }
+ }
+#endif
+
/* Allow sequencing of test actions */
if (running_in_test_harness) millisleep(100);
{
DEBUG(D_expand) debug_printf_indent("writing \"%s\" to socket\n",
reqstr.data);
- if (write(fd, reqstr.data, reqstr.len) != reqstr.len)
+ if ( (
+#ifdef SUPPORT_TLS
+ tls_ctx ? tls_write(tls_ctx, reqstr.data, reqstr.len, FALSE) :
+#endif
+ write(fd, reqstr.data, reqstr.len)) != reqstr.len)
{
expand_string_message = string_sprintf("request write to socket "
"failed: %s", strerror(errno));
system doesn't have this function, make it conditional. */
#ifdef SHUT_WR
- if (do_shutdown) shutdown(fd, SHUT_WR);
+ if (!tls_ctx && do_shutdown) shutdown(fd, SHUT_WR);
#endif
if (running_in_test_harness) millisleep(100);
/* Now we need to read from the socket, under a timeout. The function
that reads a file can be used. */
- f = fdopen(fd, "rb");
+ if (!tls_ctx)
+ f = fdopen(fd, "rb");
sigalrm_seen = FALSE;
alarm(timeout);
- yield = cat_file(f, yield, sub_arg[3]);
+ yield =
+#ifdef SUPPORT_TLS
+ tls_ctx ? cat_file_tls(tls_ctx, yield, sub_arg[3]) :
+#endif
+ cat_file(f, yield, sub_arg[3]);
alarm(0);
- (void)fclose(f);
+
+#ifdef SUPPORT_TLS
+ if (tls_ctx)
+ {
+ tls_close(tls_ctx, TRUE);
+ close(fd);
+ }
+ else
+#endif
+ (void)fclose(f);
/* After a timeout, we restore the pointer in the result, that is,
make sure we add nothing from the socket. */
DEBUG(D_transport|D_v)
debug_printf("non-TFO mode connection attempt to %s, %lu data\n",
address, (unsigned long)fastopen_blob->len);
+ /*XXX also seen on successful TFO, sigh */
tcp_out_fastopen = fastopen_blob->len > 0 ? 2 : 1;
}
else if (errno == EINPROGRESS) /* expected if we had no cookie for peer */
Arguments:
type SOCK_DGRAM or SOCK_STREAM
af AF_INET6 or AF_INET for the socket type
- address the remote address, in text form
+ hostname host name, or ip address (as text)
portlo,porthi the remote port range
timeout a timeout
connhost if not NULL, host_item to be filled in with connection details
BOOL trigger_sni_changes;
BOOL have_set_peerdn;
const struct host_item *host;
- gnutls_x509_crt_t peercert;
+ gnutls_x509_crt_t peercert;
uschar *peerdn;
uschar *ciphersuite;
uschar *received_sni;
Arguments:
fd the fd of the connection
- host connected host (for messages)
+ host connected host (for messages and option-tests)
addr the first address (not used)
tb transport (always smtp)
tlsa_dnsa non-NULL, either request or require dane for this host, and
#endif
tls_support * tlsp, uschar ** errstr)
{
-smtp_transport_options_block *ob =
- (smtp_transport_options_block *)tb->options_block;
+smtp_transport_options_block *ob = tb
+ ? (smtp_transport_options_block *)tb->options_block
+ : &smtp_transport_option_defaults;
int rc;
exim_gnutls_state_st * state = NULL;
uschar *cipher_list = NULL;
#endif
#ifndef DISABLE_EVENT
-if (tb->event_action)
+if (tb && tb->event_action)
{
state->event_action = tb->event_action;
gnutls_session_set_ptr(state->session, state);
Arguments:
ct_ctx client context pointer, or NULL for the one global server context
shutdown 1 if TLS close-alert is to be sent,
- 2 if also response to be waited for
+ 2 if also response to be waited for
Returns: nothing
*/
len size of buffer
Returns: the number of bytes read
- -1 after a failed read
+ -1 after a failed read, including EOF
*/
int
if ( tlsp == &tls_out
&& ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
- /* client, wanting hostname check */
+ /* client, wanting hostname check */
{
#ifdef EXIM_HAVE_OPENSSL_CHECKHOST
{
if (!cbinfo->is_server) /* client */
return OK;
- /* server */
+ /* server */
if (tls_install_selfsign(sctx, errstr) != OK)
return DEFER;
}
if (verify_check_host(&tls_verify_hosts) == OK)
{
rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
- FALSE, verify_callback_server, errstr);
+ FALSE, verify_callback_server, errstr);
if (rc != OK) return rc;
server_verify_optional = FALSE;
}
else if (verify_check_host(&tls_try_verify_hosts) == OK)
{
rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
- TRUE, verify_callback_server, errstr);
+ TRUE, verify_callback_server, errstr);
if (rc != OK) return rc;
server_verify_optional = TRUE;
}
Argument:
fd the fd of the connection
- host connected host (for messages)
- addr the first address
+ host connected host (for messages and option-tests)
+ addr the first address (for some randomness; can be NULL)
tb transport (always smtp)
tlsa_dnsa tlsa lookup, if DANE, else null
- tlsp record details of channel configuration
+ tlsp record details of channel configuration here; must be non-NULL
errstr error string pointer
Returns: Pointer to TLS session context, or NULL on error
#endif
tls_support * tlsp, uschar ** errstr)
{
-smtp_transport_options_block * ob =
- (smtp_transport_options_block *)tb->options_block;
+smtp_transport_options_block * ob = tb
+ ? (smtp_transport_options_block *)tb->options_block
+ : &smtp_transport_option_defaults;
exim_openssl_client_tls_ctx * exim_client_ctx;
static uschar peerdn[256];
uschar * expciphers;
#endif
#ifndef DISABLE_EVENT
-client_static_cbinfo->event_action = tb->event_action;
+client_static_cbinfo->event_action = tb ? tb->event_action : NULL;
#endif
/* There doesn't seem to be a built-in timeout on connection. */
len size of buffer
Returns: the number of bytes read
- -1 after a failed read
+ -1 after a failed read, including EOF
Only used by the client-side TLS.
*/
--- /dev/null
+# Exim test configuration 2019
+
+.include DIR/aux-var/tls_conf_prefix
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+log_selector = +tls_peerdn
+
+tls_advertise_hosts = *
+
+tls_certificate = DIR/aux-fixed/cert1
+tls_privatekey = DIR/aux-fixed/cert1
+
+# End
--- /dev/null
+# Exim test configuration 2119
+
+.include DIR/aux-var/tls_conf_prefix
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+log_selector = +tls_peerdn
+
+tls_advertise_hosts = *
+
+tls_certificate = DIR/aux-fixed/cert1
+tls_privatekey = DIR/aux-fixed/cert1
+
+# End
--- /dev/null
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port 1225
--- /dev/null
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port 1225
--- /dev/null
+# ${readsocket (IPv4 TLS)
+need_ipv4
+#
+exim -DSERVER=server -tls-on-connect -bd -oX PORT_D
+****
+#
+#
+millisleep 500
+exim -be
+1 >>${readsocket{inet:thisloop:PORT_D}{QUIT\n}{2s:tls=yes}}<<
+****
+millisleep 500
+#
+killdaemon
--- /dev/null
+# ${readsocket (IPv4 TLS)
+need_ipv4
+#
+exim -DSERVER=server -tls-on-connect -bd -oX PORT_D
+****
+#
+#
+millisleep 500
+exim -be
+1 >>${readsocket{inet:thisloop:PORT_D}{QUIT\n}{2s:tls=yes}}<<
+****
+millisleep 500
+#
+killdaemon
--- /dev/null
+1999-03-02 09:44:33 [NULL] SSL verify error: depth=0 error=self signed certificate cert=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock
+1999-03-02 09:44:33 [NULL] SSL verify error: certificate name mismatch: DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" H="thisloop"
+
+******** SERVER ********
--- /dev/null
+> 1 >>220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+221 myhost.test.ex closing connection\r
+<<
+>
--- /dev/null
+> 1 >>220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+221 myhost.test.ex closing connection\r
+<<
+>