SPDX: license tags (mostly by guesswork)
[exim.git] / src / src / lookups / readsock.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
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 */
9
10 #include "../exim.h"
11 #include "lf_functions.h"
12
13
14 static int
15 internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
16   int timeout, BOOL do_tls, uschar ** errmsg)
17 {
18 const uschar * server_name;
19 host_item host;
20
21 if (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
79 else
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
118 if (do_tls)
119   {
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 };
124   uschar * errstr;
125
126   if (getsockname(cctx->sock, (struct sockaddr *) &interface_sock, &size) == 0)
127     conn_args.sending_ip_address = host_ntoa(-1, &interface_sock, NULL, NULL);
128   else
129     {
130     *errmsg = string_sprintf("getsockname failed: %s", strerror(errno));
131     goto bad;
132     }
133
134   if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
135     {
136     *errmsg = string_sprintf("TLS connect failed: %s", errstr);
137     goto bad;
138     }
139   }
140 #endif
141
142 DEBUG(D_expand|D_lookup) debug_printf_indent("  connected to socket %s\n", sspec);
143 return OK;
144
145 bad:
146   close(cctx->sock);
147   return FAIL;
148 }
149
150 /* All use of allocations will be done against the POOL_SEARCH memory,
151 which is freed once by search_tidyup(). */
152
153 /*************************************************
154 *              Open entry point                  *
155 *************************************************/
156
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. */
160
161 static void *
162 readsock_open(const uschar * filename, uschar ** errmsg)
163 {
164 client_conn_ctx * cctx = store_get(sizeof(*cctx), GET_UNTAINTED);
165 cctx->sock = -1;
166 cctx->tls_ctx = NULL;
167 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
168 return cctx;
169 }
170
171
172
173
174
175 /*************************************************
176 *         Find entry point for lsearch           *
177 *************************************************/
178
179 /* See local README for interface description */
180
181 static int
182 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
183   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
184   const uschar * opts)
185 {
186 client_conn_ctx * cctx = handle;
187 int sep = ',';
188 struct {
189         BOOL do_shutdown:1;
190         BOOL do_tls:1;
191         BOOL cache:1;
192 } lf = {.do_shutdown = TRUE};
193 uschar * eol = NULL;
194 int timeout = 5;
195 gstring * yield;
196 int ret = DEFER;
197
198 DEBUG(D_lookup)
199   debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n",
200     filename, keystring, length, opts);
201
202 /* Parse options */
203
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;
209 #ifndef DISABLE_TLS
210   else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0)
211     lf.do_tls = TRUE;
212 #endif
213   else if (Ustrncmp(s, "eol=", 4) == 0)
214     eol = string_unprinting(s + 4);
215   else if (Ustrcmp(s, "cache=yes") == 0)
216     lf.cache = TRUE;
217   else if (Ustrcmp(s, "send=no") == 0)
218     length = 0;
219
220 if (!filename) return FAIL;     /* Server spec is required */
221
222 /* Open the socket, if not cached */
223
224 if (cctx->sock == -1)
225   if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
226     return ret;
227
228 testharness_pause_ms(100);      /* Allow sequencing of test actions */
229
230 /* Write the request string, if not empty or already done */
231
232 if (length)
233   {
234   if ((
235 #ifndef DISABLE_TLS
236       cctx->tls_ctx ? tls_write(cctx->tls_ctx, keystring, length, FALSE) :
237 #endif
238                       write(cctx->sock, keystring, length)) != length)
239     {
240     *errmsg = string_sprintf("request write to socket "
241       "failed: %s", strerror(errno));
242     goto out;
243     }
244   }
245
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. */
249
250 #ifdef SHUT_WR
251 if (!cctx->tls_ctx && lf.do_shutdown)
252   shutdown(cctx->sock, SHUT_WR);
253 #endif
254
255 testharness_pause_ms(100);
256
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. */
261
262 sigalrm_seen = FALSE;
263 #ifdef DISABLE_TLS
264 if (TRUE)
265 #else
266 if (!cctx->tls_ctx)
267 #endif
268   {
269   FILE * fp = fdopen(cctx->sock, "rb");
270   if (!fp)
271     {
272     log_write(0, LOG_MAIN|LOG_PANIC, "readsock fdopen: %s\n", strerror(errno));
273     goto out;
274     }
275   ALARM(timeout);
276   yield = cat_file(fp, NULL, eol);
277   }
278 else
279   {
280   ALARM(timeout);
281   yield = cat_file_tls(cctx->tls_ctx, NULL, eol);
282   }
283
284 ALARM_CLR(0);
285
286 if (sigalrm_seen)
287   { *errmsg = US "socket read timed out"; goto out; }
288
289 *result = yield ? string_from_gstring(yield) : US"";
290 ret = OK;
291 if (!lf.cache) *do_cache = 0;
292
293 out:
294
295 (void) close(cctx->sock);
296 cctx->sock = -1;
297 return ret;
298 }
299
300
301
302 /*************************************************
303 *              Close entry point                 *
304 *************************************************/
305
306 /* See local README for interface description */
307
308 static void
309 readsock_close(void * handle)
310 {
311 client_conn_ctx * cctx = handle;
312 if (cctx->sock < 0) return;
313 #ifndef DISABLE_TLS
314 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
315 #endif
316 close(cctx->sock);
317 cctx->sock = -1;
318 }
319
320
321
322 static lookup_info readsock_lookup_info = {
323   .name = US"readsock",                 /* lookup name */
324   .type = lookup_querystyle,
325   .open = readsock_open,                /* open function */
326   .check = NULL,
327   .find = readsock_find,                /* find function */
328   .close = readsock_close,
329   .tidy = NULL,
330   .quote = NULL,                        /* no quoting function */
331   .version_report = NULL
332 };
333
334
335 #ifdef DYNLOOKUP
336 #define readsock_lookup_module_info _lookup_module_info
337 #endif
338
339 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
340 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
341
342 /* End of lookups/readsock.c */