Fix ${readsocket } eol-replacement. Bug 2630
[exim.git] / src / src / lookups / readsock.c
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
12 static int
13 internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec,
14   int timeout, BOOL do_tls, uschar ** errmsg)
15 {
16 int sep = ',';
17 uschar * ele;
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   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
132 DEBUG(D_expand|D_lookup) debug_printf_indent("  connected to socket %s\n", sspec);
133 return OK;
134
135 bad:
136   close(cctx->sock);
137   return FAIL;
138 }
139
140 /* All use of allocations will be done against the POOL_SEARCH memory,
141 which 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
149 that connection cacheing at the framework layer works. */
150
151 static void *
152 readsock_open(const uschar * filename, uschar ** errmsg)
153 {
154 client_conn_ctx * cctx = store_get(sizeof(*cctx), FALSE);
155 cctx->sock = -1;
156 cctx->tls_ctx = NULL;
157 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
158 return cctx;
159 }
160
161
162
163
164
165 /*************************************************
166 *         Find entry point for lsearch           *
167 *************************************************/
168
169 /* See local README for interface description */
170
171 static int
172 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
173   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
174   const uschar * opts)
175 {
176 client_conn_ctx * cctx = handle;
177 int sep = ',';
178 struct {
179         BOOL do_shutdown:1;
180         BOOL do_tls:1;
181         BOOL cache:1;
182 } lf = {.do_shutdown = TRUE};
183 uschar * eol = NULL;
184 int timeout = 5;
185 FILE * fp;
186 gstring * yield;
187 int ret = DEFER;
188
189 DEBUG(D_lookup)
190   debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n",
191     filename, keystring, length, opts);
192
193 /* Parse options */
194
195 if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); )
196   if (Ustrncmp(s, "timeout=", 8) == 0)
197     timeout = readconf_readtime(s + 8, 0, FALSE);
198   else if (Ustrncmp(s, "shutdown=", 9) == 0)
199     lf.do_shutdown = Ustrcmp(s + 9, "no") != 0;
200 #ifndef DISABLE_TLS
201   else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0)
202     lf.do_tls = TRUE;
203 #endif
204   else if (Ustrncmp(s, "eol=", 4) == 0)
205     eol = string_unprinting(s + 4);
206   else if (Ustrcmp(s, "cache=yes") == 0)
207     lf.cache = TRUE;
208   else if (Ustrcmp(s, "send=no") == 0)
209     length = 0;
210
211 if (!filename) return FAIL;     /* Server spec is required */
212
213 /* Open the socket, if not cached */
214
215 if (cctx->sock == -1)
216   if (internal_readsock_open(cctx, filename, timeout, lf.do_tls, errmsg) != OK)
217     return ret;
218
219 testharness_pause_ms(100);      /* Allow sequencing of test actions */
220
221 /* Write the request string, if not empty or already done */
222
223 if (length)
224   {
225   if ((
226 #ifndef DISABLE_TLS
227       cctx->tls_ctx ? tls_write(cctx->tls_ctx, keystring, length, FALSE) :
228 #endif
229                       write(cctx->sock, keystring, length)) != length)
230     {
231     *errmsg = string_sprintf("request write to socket "
232       "failed: %s", strerror(errno));
233     goto out;
234     }
235   }
236
237 /* Shut down the sending side of the socket. This helps some servers to
238 recognise that it is their turn to do some work. Just in case some
239 system doesn't have this function, make it conditional. */
240
241 #ifdef SHUT_WR
242 if (!cctx->tls_ctx && lf.do_shutdown)
243   shutdown(cctx->sock, SHUT_WR);
244 #endif
245
246 testharness_pause_ms(100);
247
248 /* Now we need to read from the socket, under a timeout. The function
249 that reads a file can be used.  If we're using a stdio buffered read,
250 and might need later write ops on the socket, the stdio must be in
251 writable mode or the underlying socket goes non-writable. */
252
253 if (!cctx->tls_ctx)
254   fp = fdopen(cctx->sock, lf.do_shutdown ? "rb" : "wb");
255
256 sigalrm_seen = FALSE;
257 ALARM(timeout);
258 yield =
259 #ifndef DISABLE_TLS
260   cctx->tls_ctx ? cat_file_tls(cctx->tls_ctx, NULL, eol) :
261 #endif
262                   cat_file(fp, NULL, eol);
263 ALARM_CLR(0);
264
265 if (sigalrm_seen)
266   { *errmsg = US "socket read timed out"; goto out; }
267
268 *result = yield ? string_from_gstring(yield) : US"";
269 ret = OK;
270 if (!lf.cache) *do_cache = 0;
271
272 out:
273
274 (void) close(cctx->sock);
275 cctx->sock = -1;
276 return ret;
277 }
278
279
280
281 /*************************************************
282 *              Close entry point                 *
283 *************************************************/
284
285 /* See local README for interface description */
286
287 static void
288 readsock_close(void * handle)
289 {
290 client_conn_ctx * cctx = handle;
291 if (cctx->sock < 0) return;
292 #ifndef DISABLE_TLS
293 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
294 #endif
295 close(cctx->sock);
296 cctx->sock = -1;
297 }
298
299
300
301 static lookup_info readsock_lookup_info = {
302   .name = US"readsock",                 /* lookup name */
303   .type = lookup_querystyle,
304   .open = readsock_open,                /* open function */
305   .check = NULL,
306   .find = readsock_find,                /* find function */
307   .close = readsock_close,
308   .tidy = NULL,
309   .quote = NULL,                        /* no quoting function */
310   .version_report = NULL
311 };
312
313
314 #ifdef DYNLOOKUP
315 #define readsock_lookup_module_info _lookup_module_info
316 #endif
317
318 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
319 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
320
321 /* End of lookups/readsock.c */