Copyright updates:
[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   smtp_connect_args conn_args = {.host = &host };
120   tls_support tls_dummy = {.sni=NULL};
121   uschar * errstr;
122
123   if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
124     {
125     *errmsg = string_sprintf("TLS connect failed: %s", errstr);
126     goto bad;
127     }
128   }
129 #endif
130
131 DEBUG(D_expand|D_lookup) debug_printf_indent("  connected to socket %s\n", sspec);
132 return OK;
133
134 bad:
135   close(cctx->sock);
136   return FAIL;
137 }
138
139 /* All use of allocations will be done against the POOL_SEARCH memory,
140 which is freed once by search_tidyup(). */
141
142 /*************************************************
143 *              Open entry point                  *
144 *************************************************/
145
146 /* See local README for interface description */
147 /* We just create a placeholder record with a closed socket, so
148 that connection cacheing at the framework layer works. */
149
150 static void *
151 readsock_open(const uschar * filename, uschar ** errmsg)
152 {
153 client_conn_ctx * cctx = store_get(sizeof(*cctx), FALSE);
154 cctx->sock = -1;
155 cctx->tls_ctx = NULL;
156 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
157 return cctx;
158 }
159
160
161
162
163
164 /*************************************************
165 *         Find entry point for lsearch           *
166 *************************************************/
167
168 /* See local README for interface description */
169
170 static int
171 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
172   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
173   const uschar * opts)
174 {
175 client_conn_ctx * cctx = handle;
176 int sep = ',';
177 struct {
178         BOOL do_shutdown:1;
179         BOOL do_tls:1;
180         BOOL cache:1;
181 } lf = {.do_shutdown = TRUE};
182 uschar * eol = NULL;
183 int timeout = 5;
184 gstring * yield;
185 int ret = DEFER;
186
187 DEBUG(D_lookup)
188   debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n",
189     filename, keystring, length, opts);
190
191 /* Parse options */
192
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;
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 = string_unprinting(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
209 if (!filename) return FAIL;     /* Server spec is required */
210
211 /* Open the socket, if not cached */
212
213 if (cctx->sock == -1)
214   if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
215     return ret;
216
217 testharness_pause_ms(100);      /* Allow sequencing of test actions */
218
219 /* Write the request string, if not empty or already done */
220
221 if (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
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. */
238
239 #ifdef SHUT_WR
240 if (!cctx->tls_ctx && lf.do_shutdown)
241   shutdown(cctx->sock, SHUT_WR);
242 #endif
243
244 testharness_pause_ms(100);
245
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. */
250
251 sigalrm_seen = FALSE;
252 #ifdef DISABLE_TLS
253 if (TRUE)
254 #else
255 if (!cctx->tls_ctx)
256 #endif
257   {
258   FILE * fp = fdopen(cctx->sock, lf.do_shutdown ? "rb" : "wb");
259   ALARM(timeout);
260   yield = cat_file(fp, NULL, eol);
261   }
262 else
263   {
264   ALARM(timeout);
265   yield = cat_file_tls(cctx->tls_ctx, NULL, eol);
266   }
267
268 ALARM_CLR(0);
269
270 if (sigalrm_seen)
271   { *errmsg = US "socket read timed out"; goto out; }
272
273 *result = yield ? string_from_gstring(yield) : US"";
274 ret = OK;
275 if (!lf.cache) *do_cache = 0;
276
277 out:
278
279 (void) close(cctx->sock);
280 cctx->sock = -1;
281 return ret;
282 }
283
284
285
286 /*************************************************
287 *              Close entry point                 *
288 *************************************************/
289
290 /* See local README for interface description */
291
292 static void
293 readsock_close(void * handle)
294 {
295 client_conn_ctx * cctx = handle;
296 if (cctx->sock < 0) return;
297 #ifndef DISABLE_TLS
298 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
299 #endif
300 close(cctx->sock);
301 cctx->sock = -1;
302 }
303
304
305
306 static lookup_info readsock_lookup_info = {
307   .name = US"readsock",                 /* lookup name */
308   .type = lookup_querystyle,
309   .open = readsock_open,                /* open function */
310   .check = NULL,
311   .find = readsock_find,                /* find function */
312   .close = readsock_close,
313   .tidy = NULL,
314   .quote = NULL,                        /* no quoting function */
315   .version_report = NULL
316 };
317
318
319 #ifdef DYNLOOKUP
320 #define readsock_lookup_module_info _lookup_module_info
321 #endif
322
323 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
324 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
325
326 /* End of lookups/readsock.c */