1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Jeremy Harris 2020 */
6 /* See the file NOTICE for conditions of use and distribution. */
9 #include "lf_functions.h"
13 internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
14 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 smtp_connect_args conn_args = {.host = &host };
121 tls_support tls_dummy = {.sni=NULL};
124 if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
126 *errmsg = string_sprintf("TLS connect failed: %s", errstr);
132 DEBUG(D_expand|D_lookup) debug_printf_indent(" connected to socket %s\n", sspec);
140 /* All use of allocations will be done against the POOL_SEARCH memory,
141 which is freed once by search_tidyup(). */
143 /*************************************************
145 *************************************************/
147 /* See local README for interface description */
148 /* We just create a placeholder record with a closed socket, so
149 that connection cacheing at the framework layer works. */
152 readsock_open(const uschar * filename, uschar ** errmsg)
154 client_conn_ctx * cctx = store_get(sizeof(*cctx), FALSE);
156 cctx->tls_ctx = NULL;
157 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
165 /*************************************************
166 * Find entry point for lsearch *
167 *************************************************/
169 /* See local README for interface description */
172 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
173 int length, uschar ** result, uschar ** errmsg, uint * do_cache,
176 client_conn_ctx * cctx = handle;
182 } lf = {.do_shutdown = TRUE};
189 DEBUG(D_lookup) debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n", filename, keystring, length, opts);
193 if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); )
194 if (Ustrncmp(s, "timeout=", 8) == 0)
195 timeout = readconf_readtime(s + 8, 0, FALSE);
196 else if (Ustrncmp(s, "shutdown=", 9) == 0)
197 lf.do_shutdown = Ustrcmp(s + 9, "no") != 0;
199 else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0)
202 else if (Ustrncmp(s, "eol=", 4) == 0)
204 else if (Ustrcmp(s, "cache=yes") == 0)
206 else if (Ustrcmp(s, "send=no") == 0)
209 if (!filename) return FAIL; /* Server spec is required */
211 /* Open the socket, if not cached */
213 if (cctx->sock == -1)
214 if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
217 testharness_pause_ms(100); /* Allow sequencing of test actions */
219 /* Write the request string, if not empty or already done */
225 cctx->tls_ctx ? tls_write(cctx->tls_ctx, keystring, length, FALSE) :
227 write(cctx->sock, keystring, length)) != length)
229 *errmsg = string_sprintf("request write to socket "
230 "failed: %s", strerror(errno));
235 /* Shut down the sending side of the socket. This helps some servers to
236 recognise that it is their turn to do some work. Just in case some
237 system doesn't have this function, make it conditional. */
240 if (!cctx->tls_ctx && lf.do_shutdown)
241 shutdown(cctx->sock, SHUT_WR);
244 testharness_pause_ms(100);
246 /* Now we need to read from the socket, under a timeout. The function
247 that reads a file can be used. If we're using a stdio buffered read,
248 and might need later write ops on the socket, the stdio must be in
249 writable mode or the underlying socket goes non-writable. */
252 fp = fdopen(cctx->sock, lf.do_shutdown ? "rb" : "wb");
254 sigalrm_seen = FALSE;
258 cctx->tls_ctx ? cat_file_tls(cctx->tls_ctx, NULL, eol) :
260 cat_file(fp, NULL, eol);
264 { *errmsg = US "socket read timed out"; goto out; }
266 *result = yield ? string_from_gstring(yield) : US"";
268 if (!lf.cache) *do_cache = 0;
272 (void) close(cctx->sock);
279 /*************************************************
280 * Close entry point *
281 *************************************************/
283 /* See local README for interface description */
286 readsock_close(void * handle)
288 client_conn_ctx * cctx = handle;
289 if (cctx->sock < 0) return;
291 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
299 static lookup_info readsock_lookup_info = {
300 .name = US"readsock", /* lookup name */
301 .type = lookup_querystyle,
302 .open = readsock_open, /* open function */
304 .find = readsock_find, /* find function */
305 .close = readsock_close,
307 .quote = NULL, /* no quoting function */
308 .version_report = NULL
313 #define readsock_lookup_module_info _lookup_module_info
316 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
317 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
319 /* End of lookups/readsock.c */