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-or-later */
11 #include "lf_functions.h"
15 internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
16 int timeout, uschar * 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;
119 if (!tls_client_adjunct_start(&host, cctx, do_tls, errmsg))
123 DEBUG(D_expand|D_lookup) debug_printf_indent(" connected to socket %s\n", sspec);
131 /* All use of allocations will be done against the POOL_SEARCH memory,
132 which is freed once by search_tidyup(). */
134 /*************************************************
136 *************************************************/
138 /* See local README for interface description */
139 /* We just create a placeholder record with a closed socket, so
140 that connection cacheing at the framework layer works. */
143 readsock_open(const uschar * filename, uschar ** errmsg)
145 client_conn_ctx * cctx = store_get(sizeof(*cctx), GET_UNTAINTED);
147 cctx->tls_ctx = NULL;
148 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
156 /*************************************************
157 * Find entry point for lsearch *
158 *************************************************/
160 /* See local README for interface description */
163 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
164 int length, uschar ** result, uschar ** errmsg, uint * do_cache,
167 client_conn_ctx * cctx = handle;
172 uschar * do_tls; /* NULL, empty-string, or SNI */
173 } lf = {.do_shutdown = TRUE};
180 debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n",
181 filename, keystring, length, opts);
185 if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); )
186 if (Ustrncmp(s, "timeout=", 8) == 0)
187 timeout = readconf_readtime(s + 8, 0, FALSE);
188 else if (Ustrncmp(s, "shutdown=", 9) == 0)
189 lf.do_shutdown = Ustrcmp(s + 9, "no") != 0;
191 else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0 && !lf.do_tls)
193 else if (Ustrncmp(s, "sni=", 4) == 0)
196 else if (Ustrncmp(s, "eol=", 4) == 0)
197 eol = string_unprinting(s + 4);
198 else if (Ustrcmp(s, "cache=yes") == 0)
200 else if (Ustrcmp(s, "send=no") == 0)
203 if (!filename) return FAIL; /* Server spec is required */
205 /* Open the socket, if not cached */
207 if (cctx->sock == -1)
208 if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
211 testharness_pause_ms(100); /* Allow sequencing of test actions */
213 /* Write the request string, if not empty or already done */
219 cctx->tls_ctx ? tls_write(cctx->tls_ctx, keystring, length, FALSE) :
221 write(cctx->sock, keystring, length)) != length)
223 *errmsg = string_sprintf("request write to socket "
224 "failed: %s", strerror(errno));
229 /* Shut down the sending side of the socket. This helps some servers to
230 recognise that it is their turn to do some work. Just in case some
231 system doesn't have this function, make it conditional. */
234 if (!cctx->tls_ctx && lf.do_shutdown)
235 shutdown(cctx->sock, SHUT_WR);
238 testharness_pause_ms(100);
240 /* Now we need to read from the socket, under a timeout. The function
241 that reads a file can be used. If we're using a stdio buffered read,
242 and might need later write ops on the socket, the stdio must be in
243 writable mode or the underlying socket goes non-writable. */
245 sigalrm_seen = FALSE;
252 FILE * fp = fdopen(cctx->sock, "rb");
255 log_write(0, LOG_MAIN|LOG_PANIC, "readsock fdopen: %s\n", strerror(errno));
259 yield = cat_file(fp, NULL, eol);
264 yield = cat_file_tls(cctx->tls_ctx, NULL, eol);
270 { *errmsg = US "socket read timed out"; goto out; }
272 *result = yield ? string_from_gstring(yield) : US"";
274 if (!lf.cache) *do_cache = 0;
278 (void) close(cctx->sock);
285 /*************************************************
286 * Close entry point *
287 *************************************************/
289 /* See local README for interface description */
292 readsock_close(void * handle)
294 client_conn_ctx * cctx = handle;
295 if (cctx->sock < 0) return;
297 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
305 static lookup_info readsock_lookup_info = {
306 .name = US"readsock", /* lookup name */
307 .type = lookup_querystyle,
308 .open = readsock_open, /* open function */
310 .find = readsock_find, /* find function */
311 .close = readsock_close,
313 .quote = NULL, /* no quoting function */
314 .version_report = NULL
319 #define readsock_lookup_module_info _lookup_module_info
322 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
323 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
325 /* End of lookups/readsock.c */