1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2021 - 2022 */
6 /* Copyright (c) Jeremy Harris 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-only */
11 #include "lf_functions.h"
15 internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
16 int timeout, BOOL do_tls, uschar ** errmsg)
18 const uschar * server_name;
21 if (Ustrncmp(sspec, "inet:", 5) == 0)
27 debug_printf_indent(" new inet socket needed for readsocket\n");
29 server_name = sspec + 5;
30 port_name = Ustrrchr(server_name, ':');
32 /* Sort out the port */
36 /* expand_string_message results in an EXPAND_FAIL, from our
37 only caller. Lack of it gets a SOCK_FAIL; we feed back via errmsg
38 for that, which gets copied to search_error_message. */
40 expand_string_message =
41 string_sprintf("missing port for readsocket %s", sspec);
44 *port_name++ = 0; /* Terminate server name */
46 if (isdigit(*port_name))
49 port = Ustrtol(port_name, &end, 0);
50 if (end != port_name + Ustrlen(port_name))
52 expand_string_message =
53 string_sprintf("invalid port number %s", port_name);
59 struct servent *service_info = getservbyname(CS port_name, "tcp");
62 expand_string_message = string_sprintf("unknown port \"%s\"",
66 port = ntohs(service_info->s_port);
69 /* Not having the request-string here in the open routine means
70 that we cannot do TFO; a pity */
72 cctx->sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
73 timeout, &host, errmsg, NULL);
74 callout_address = NULL;
81 struct sockaddr_un sockun; /* don't call this "sun" ! */
85 debug_printf_indent(" new unix socket needed for readsocket\n");
87 if ((cctx->sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
89 *errmsg = string_sprintf("failed to create socket: %s", strerror(errno));
93 sockun.sun_family = AF_UNIX;
94 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1),
96 server_name = US sockun.sun_path;
100 rc = connect(cctx->sock, (struct sockaddr *) &sockun, sizeof(sockun));
104 *errmsg = US "socket connect timed out";
109 *errmsg = string_sprintf("failed to connect to socket "
110 "%s: %s", sspec, strerror(errno));
113 host.name = server_name;
120 union sockaddr_46 interface_sock;
121 EXIM_SOCKLEN_T size = sizeof(interface_sock);
122 smtp_connect_args conn_args = {.host = &host };
123 tls_support tls_dummy = { .sni = NULL };
126 if (getsockname(cctx->sock, (struct sockaddr *) &interface_sock, &size) == 0)
127 conn_args.sending_ip_address = host_ntoa(-1, &interface_sock, NULL, NULL);
130 *errmsg = string_sprintf("getsockname failed: %s", strerror(errno));
134 if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
136 *errmsg = string_sprintf("TLS connect failed: %s", errstr);
142 DEBUG(D_expand|D_lookup) debug_printf_indent(" connected to socket %s\n", sspec);
150 /* All use of allocations will be done against the POOL_SEARCH memory,
151 which is freed once by search_tidyup(). */
153 /*************************************************
155 *************************************************/
157 /* See local README for interface description */
158 /* We just create a placeholder record with a closed socket, so
159 that connection cacheing at the framework layer works. */
162 readsock_open(const uschar * filename, uschar ** errmsg)
164 client_conn_ctx * cctx = store_get(sizeof(*cctx), GET_UNTAINTED);
166 cctx->tls_ctx = NULL;
167 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
175 /*************************************************
176 * Find entry point for lsearch *
177 *************************************************/
179 /* See local README for interface description */
182 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
183 int length, uschar ** result, uschar ** errmsg, uint * do_cache,
186 client_conn_ctx * cctx = handle;
192 } lf = {.do_shutdown = TRUE};
199 debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n",
200 filename, keystring, length, opts);
204 if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); )
205 if (Ustrncmp(s, "timeout=", 8) == 0)
206 timeout = readconf_readtime(s + 8, 0, FALSE);
207 else if (Ustrncmp(s, "shutdown=", 9) == 0)
208 lf.do_shutdown = Ustrcmp(s + 9, "no") != 0;
210 else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0)
213 else if (Ustrncmp(s, "eol=", 4) == 0)
214 eol = string_unprinting(s + 4);
215 else if (Ustrcmp(s, "cache=yes") == 0)
217 else if (Ustrcmp(s, "send=no") == 0)
220 if (!filename) return FAIL; /* Server spec is required */
222 /* Open the socket, if not cached */
224 if (cctx->sock == -1)
225 if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
228 testharness_pause_ms(100); /* Allow sequencing of test actions */
230 /* Write the request string, if not empty or already done */
236 cctx->tls_ctx ? tls_write(cctx->tls_ctx, keystring, length, FALSE) :
238 write(cctx->sock, keystring, length)) != length)
240 *errmsg = string_sprintf("request write to socket "
241 "failed: %s", strerror(errno));
246 /* Shut down the sending side of the socket. This helps some servers to
247 recognise that it is their turn to do some work. Just in case some
248 system doesn't have this function, make it conditional. */
251 if (!cctx->tls_ctx && lf.do_shutdown)
252 shutdown(cctx->sock, SHUT_WR);
255 testharness_pause_ms(100);
257 /* Now we need to read from the socket, under a timeout. The function
258 that reads a file can be used. If we're using a stdio buffered read,
259 and might need later write ops on the socket, the stdio must be in
260 writable mode or the underlying socket goes non-writable. */
262 sigalrm_seen = FALSE;
269 FILE * fp = fdopen(cctx->sock, "rb");
272 log_write(0, LOG_MAIN|LOG_PANIC, "readsock fdopen: %s\n", strerror(errno));
276 yield = cat_file(fp, NULL, eol);
281 yield = cat_file_tls(cctx->tls_ctx, NULL, eol);
287 { *errmsg = US "socket read timed out"; goto out; }
289 *result = yield ? string_from_gstring(yield) : US"";
291 if (!lf.cache) *do_cache = 0;
295 (void) close(cctx->sock);
302 /*************************************************
303 * Close entry point *
304 *************************************************/
306 /* See local README for interface description */
309 readsock_close(void * handle)
311 client_conn_ctx * cctx = handle;
312 if (cctx->sock < 0) return;
314 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
322 static lookup_info readsock_lookup_info = {
323 .name = US"readsock", /* lookup name */
324 .type = lookup_querystyle,
325 .open = readsock_open, /* open function */
327 .find = readsock_find, /* find function */
328 .close = readsock_close,
330 .quote = NULL, /* no quoting function */
331 .version_report = NULL
336 #define readsock_lookup_module_info _lookup_module_info
339 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
340 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
342 /* End of lookups/readsock.c */