Copyright updates:
[exim.git] / src / src / lookups / readsock.c
CommitLineData
1950cf85
JH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
5/* Copyright (c) Jeremy Harris 2020 */
6/* See the file NOTICE for conditions of use and distribution. */
7
8#include "../exim.h"
9#include "lf_functions.h"
10
11
12static int
13internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
14 int timeout, BOOL do_tls, uschar ** errmsg)
15{
16int sep = ',';
17uschar * ele;
18const uschar * server_name;
19host_item host;
20
21if (Ustrncmp(sspec, "inet:", 5) == 0)
22 {
23 int port;
24 uschar * port_name;
25
26 DEBUG(D_lookup)
27 debug_printf_indent(" new inet socket needed for readsocket\n");
28
29 server_name = sspec + 5;
30 port_name = Ustrrchr(server_name, ':');
31
32 /* Sort out the port */
33
34 if (!port_name)
35 {
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. */
39
40 expand_string_message =
41 string_sprintf("missing port for readsocket %s", sspec);
42 return FAIL;
43 }
44 *port_name++ = 0; /* Terminate server name */
45
46 if (isdigit(*port_name))
47 {
48 uschar *end;
49 port = Ustrtol(port_name, &end, 0);
50 if (end != port_name + Ustrlen(port_name))
51 {
52 expand_string_message =
53 string_sprintf("invalid port number %s", port_name);
54 return FAIL;
55 }
56 }
57 else
58 {
59 struct servent *service_info = getservbyname(CS port_name, "tcp");
60 if (!service_info)
61 {
62 expand_string_message = string_sprintf("unknown port \"%s\"",
63 port_name);
64 return FAIL;
65 }
66 port = ntohs(service_info->s_port);
67 }
68
69 /* Not having the request-string here in the open routine means
70 that we cannot do TFO; a pity */
71
72 cctx->sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
73 timeout, &host, errmsg, NULL);
74 callout_address = NULL;
75 if (cctx->sock < 0)
76 return FAIL;
77 }
78
79else
80 {
81 struct sockaddr_un sockun; /* don't call this "sun" ! */
82 int rc;
83
84 DEBUG(D_lookup)
85 debug_printf_indent(" new unix socket needed for readsocket\n");
86
87 if ((cctx->sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
88 {
89 *errmsg = string_sprintf("failed to create socket: %s", strerror(errno));
90 return FAIL;
91 }
92
93 sockun.sun_family = AF_UNIX;
94 sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1),
95 sspec);
96 server_name = US sockun.sun_path;
97
98 sigalrm_seen = FALSE;
99 ALARM(timeout);
100 rc = connect(cctx->sock, (struct sockaddr *)(&sockun), sizeof(sockun));
101 ALARM_CLR(0);
102 if (sigalrm_seen)
103 {
104 *errmsg = US "socket connect timed out";
105 goto bad;
106 }
107 if (rc < 0)
108 {
109 *errmsg = string_sprintf("failed to connect to socket "
110 "%s: %s", sspec, strerror(errno));
111 goto bad;
112 }
113 host.name = server_name;
114 host.address = US"";
115 }
116
117#ifndef DISABLE_TLS
118if (do_tls)
119 {
120 smtp_connect_args conn_args = {.host = &host };
121 tls_support tls_dummy = {.sni=NULL};
122 uschar * errstr;
123
124 if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
125 {
126 *errmsg = string_sprintf("TLS connect failed: %s", errstr);
127 goto bad;
128 }
129 }
130#endif
131
132DEBUG(D_expand|D_lookup) debug_printf_indent(" connected to socket %s\n", sspec);
133return OK;
134
135bad:
136 close(cctx->sock);
137 return FAIL;
138}
139
140/* All use of allocations will be done against the POOL_SEARCH memory,
141which is freed once by search_tidyup(). */
142
143/*************************************************
144* Open entry point *
145*************************************************/
146
147/* See local README for interface description */
148/* We just create a placeholder record with a closed socket, so
149that connection cacheing at the framework layer works. */
150
151static void *
152readsock_open(const uschar * filename, uschar ** errmsg)
153{
154client_conn_ctx * cctx = store_get(sizeof(*cctx), FALSE);
155cctx->sock = -1;
156cctx->tls_ctx = NULL;
157DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
158return cctx;
159}
160
161
162
163
164
165/*************************************************
166* Find entry point for lsearch *
167*************************************************/
168
169/* See local README for interface description */
170
171static int
172readsock_find(void * handle, const uschar * filename, const uschar * keystring,
173 int length, uschar ** result, uschar ** errmsg, uint * do_cache,
174 const uschar * opts)
175{
176client_conn_ctx * cctx = handle;
177int sep = ',';
178struct {
179 BOOL do_shutdown:1;
180 BOOL do_tls:1;
181 BOOL cache:1;
182} lf = {.do_shutdown = TRUE};
183uschar * eol = NULL;
184int timeout = 5;
185FILE * fp;
186gstring * yield;
187int ret = DEFER;
188
189DEBUG(D_lookup) debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n", filename, keystring, length, opts);
190
191/* Parse options */
192
193if (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;
198#ifndef DISABLE_TLS
199 else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0)
200 lf.do_tls = TRUE;
201#endif
202 else if (Ustrncmp(s, "eol=", 4) == 0)
203 eol = s + 4;
204 else if (Ustrcmp(s, "cache=yes") == 0)
205 lf.cache = TRUE;
206 else if (Ustrcmp(s, "send=no") == 0)
207 length = 0;
208
209if (!filename) return FAIL; /* Server spec is required */
210
211/* Open the socket, if not cached */
212
213if (cctx->sock == -1)
214 if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
215 return ret;
216
217testharness_pause_ms(100); /* Allow sequencing of test actions */
218
219/* Write the request string, if not empty or already done */
220
221if (length)
222 {
223 if ((
224#ifndef DISABLE_TLS
225 cctx->tls_ctx ? tls_write(cctx->tls_ctx, keystring, length, FALSE) :
226#endif
227 write(cctx->sock, keystring, length)) != length)
228 {
229 *errmsg = string_sprintf("request write to socket "
230 "failed: %s", strerror(errno));
231 goto out;
232 }
233 }
234
235/* Shut down the sending side of the socket. This helps some servers to
236recognise that it is their turn to do some work. Just in case some
237system doesn't have this function, make it conditional. */
238
239#ifdef SHUT_WR
240if (!cctx->tls_ctx && lf.do_shutdown)
241 shutdown(cctx->sock, SHUT_WR);
242#endif
243
244testharness_pause_ms(100);
245
246/* Now we need to read from the socket, under a timeout. The function
247that reads a file can be used. If we're using a stdio buffered read,
248and might need later write ops on the socket, the stdio must be in
249writable mode or the underlying socket goes non-writable. */
250
251if (!cctx->tls_ctx)
252 fp = fdopen(cctx->sock, lf.do_shutdown ? "rb" : "wb");
253
254sigalrm_seen = FALSE;
255ALARM(timeout);
256yield =
257#ifndef DISABLE_TLS
258 cctx->tls_ctx ? cat_file_tls(cctx->tls_ctx, NULL, eol) :
259#endif
260 cat_file(fp, NULL, eol);
261ALARM_CLR(0);
262
263if (sigalrm_seen)
264 { *errmsg = US "socket read timed out"; goto out; }
265
266*result = yield ? string_from_gstring(yield) : US"";
267ret = OK;
268if (!lf.cache) *do_cache = 0;
269
270out:
271
272(void) close(cctx->sock);
273cctx->sock = -1;
274return ret;
275}
276
277
278
279/*************************************************
280* Close entry point *
281*************************************************/
282
283/* See local README for interface description */
284
285static void
286readsock_close(void * handle)
287{
288client_conn_ctx * cctx = handle;
289if (cctx->sock < 0) return;
290#ifndef DISABLE_TLS
291if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
292#endif
293close(cctx->sock);
294cctx->sock = -1;
295}
296
297
298
299static lookup_info readsock_lookup_info = {
300 .name = US"readsock", /* lookup name */
301 .type = lookup_querystyle,
302 .open = readsock_open, /* open function */
303 .check = NULL,
304 .find = readsock_find, /* find function */
305 .close = readsock_close,
306 .tidy = NULL,
307 .quote = NULL, /* no quoting function */
308 .version_report = NULL
309};
310
311
312#ifdef DYNLOOKUP
313#define readsock_lookup_module_info _lookup_module_info
314#endif
315
316static lookup_info *_lookup_list[] = { &readsock_lookup_info };
317lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
318
319/* End of lookups/readsock.c */