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. */
10 #include "lf_functions.h"
14 internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
15 int timeout, BOOL do_tls, uschar ** errmsg)
17 const uschar * server_name;
20 if (Ustrncmp(sspec, "inet:", 5) == 0)
26 debug_printf_indent(" new inet socket needed for readsocket\n");
28 server_name = sspec + 5;
29 port_name = Ustrrchr(server_name, ':');
31 /* Sort out the port */
35 /* expand_string_message results in an EXPAND_FAIL, from our
36 only caller. Lack of it gets a SOCK_FAIL; we feed back via errmsg
37 for that, which gets copied to search_error_message. */
39 expand_string_message =
40 string_sprintf("missing port for readsocket %s", sspec);
43 *port_name++ = 0; /* Terminate server name */
45 if (isdigit(*port_name))
48 port = Ustrtol(port_name, &end, 0);
49 if (end != port_name + Ustrlen(port_name))
51 expand_string_message =
52 string_sprintf("invalid port number %s", port_name);
58 struct servent *service_info = getservbyname(CS port_name, "tcp");
61 expand_string_message = string_sprintf("unknown port \"%s\"",
65 port = ntohs(service_info->s_port);
68 /* Not having the request-string here in the open routine means
69 that we cannot do TFO; a pity */
71 cctx->sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
72 timeout, &host, errmsg, NULL);
73 callout_address = NULL;
80 struct sockaddr_un sockun; /* don't call this "sun" ! */
84 debug_printf_indent(" new unix socket needed for readsocket\n");
86 if ((cctx->sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
88 *errmsg = string_sprintf("failed to create socket: %s", strerror(errno));
92 sockun.sun_family = AF_UNIX;
93 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1),
95 server_name = US sockun.sun_path;
99 rc = connect(cctx->sock, (struct sockaddr *) &sockun, sizeof(sockun));
103 *errmsg = US "socket connect timed out";
108 *errmsg = string_sprintf("failed to connect to socket "
109 "%s: %s", sspec, strerror(errno));
112 host.name = server_name;
119 union sockaddr_46 interface_sock;
120 EXIM_SOCKLEN_T size = sizeof(interface_sock);
121 smtp_connect_args conn_args = {.host = &host };
122 tls_support tls_dummy = { .sni = NULL };
125 if (getsockname(cctx->sock, (struct sockaddr *) &interface_sock, &size) == 0)
126 conn_args.sending_ip_address = host_ntoa(-1, &interface_sock, NULL, NULL);
129 *errmsg = string_sprintf("getsockname failed: %s", strerror(errno));
133 if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
135 *errmsg = string_sprintf("TLS connect failed: %s", errstr);
141 DEBUG(D_expand|D_lookup) debug_printf_indent(" connected to socket %s\n", sspec);
149 /* All use of allocations will be done against the POOL_SEARCH memory,
150 which is freed once by search_tidyup(). */
152 /*************************************************
154 *************************************************/
156 /* See local README for interface description */
157 /* We just create a placeholder record with a closed socket, so
158 that connection cacheing at the framework layer works. */
161 readsock_open(const uschar * filename, uschar ** errmsg)
163 client_conn_ctx * cctx = store_get(sizeof(*cctx), GET_UNTAINTED);
165 cctx->tls_ctx = NULL;
166 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
174 /*************************************************
175 * Find entry point for lsearch *
176 *************************************************/
178 /* See local README for interface description */
181 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
182 int length, uschar ** result, uschar ** errmsg, uint * do_cache,
185 client_conn_ctx * cctx = handle;
191 } lf = {.do_shutdown = TRUE};
198 debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n",
199 filename, keystring, length, opts);
203 if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); )
204 if (Ustrncmp(s, "timeout=", 8) == 0)
205 timeout = readconf_readtime(s + 8, 0, FALSE);
206 else if (Ustrncmp(s, "shutdown=", 9) == 0)
207 lf.do_shutdown = Ustrcmp(s + 9, "no") != 0;
209 else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0)
212 else if (Ustrncmp(s, "eol=", 4) == 0)
213 eol = string_unprinting(s + 4);
214 else if (Ustrcmp(s, "cache=yes") == 0)
216 else if (Ustrcmp(s, "send=no") == 0)
219 if (!filename) return FAIL; /* Server spec is required */
221 /* Open the socket, if not cached */
223 if (cctx->sock == -1)
224 if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
227 testharness_pause_ms(100); /* Allow sequencing of test actions */
229 /* Write the request string, if not empty or already done */
235 cctx->tls_ctx ? tls_write(cctx->tls_ctx, keystring, length, FALSE) :
237 write(cctx->sock, keystring, length)) != length)
239 *errmsg = string_sprintf("request write to socket "
240 "failed: %s", strerror(errno));
245 /* Shut down the sending side of the socket. This helps some servers to
246 recognise that it is their turn to do some work. Just in case some
247 system doesn't have this function, make it conditional. */
250 if (!cctx->tls_ctx && lf.do_shutdown)
251 shutdown(cctx->sock, SHUT_WR);
254 testharness_pause_ms(100);
256 /* Now we need to read from the socket, under a timeout. The function
257 that reads a file can be used. If we're using a stdio buffered read,
258 and might need later write ops on the socket, the stdio must be in
259 writable mode or the underlying socket goes non-writable. */
261 sigalrm_seen = FALSE;
268 FILE * fp = fdopen(cctx->sock, "rb");
271 log_write(0, LOG_MAIN|LOG_PANIC, "readsock fdopen: %s\n", strerror(errno));
275 yield = cat_file(fp, NULL, eol);
280 yield = cat_file_tls(cctx->tls_ctx, NULL, eol);
286 { *errmsg = US "socket read timed out"; goto out; }
288 *result = yield ? string_from_gstring(yield) : US"";
290 if (!lf.cache) *do_cache = 0;
294 (void) close(cctx->sock);
301 /*************************************************
302 * Close entry point *
303 *************************************************/
305 /* See local README for interface description */
308 readsock_close(void * handle)
310 client_conn_ctx * cctx = handle;
311 if (cctx->sock < 0) return;
313 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
321 static lookup_info readsock_lookup_info = {
322 .name = US"readsock", /* lookup name */
323 .type = lookup_querystyle,
324 .open = readsock_open, /* open function */
326 .find = readsock_find, /* find function */
327 .close = readsock_close,
329 .quote = NULL, /* no quoting function */
330 .version_report = NULL
335 #define readsock_lookup_module_info _lookup_module_info
338 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
339 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
341 /* End of lookups/readsock.c */