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