tidying
[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 const uschar * server_name;
17 host_item host;
18
19 if (Ustrncmp(sspec, "inet:", 5) == 0)
20   {
21   int port;
22   uschar * port_name;
23
24   DEBUG(D_lookup)
25     debug_printf_indent("  new inet socket needed for readsocket\n");
26
27   server_name = sspec + 5;
28   port_name = Ustrrchr(server_name, ':');
29
30   /* Sort out the port */
31
32   if (!port_name)
33     {
34     /* expand_string_message results in an EXPAND_FAIL, from our
35     only caller.  Lack of it gets a SOCK_FAIL; we feed back via errmsg
36     for that, which gets copied to search_error_message. */
37
38     expand_string_message =
39       string_sprintf("missing port for readsocket %s", sspec);
40     return FAIL;
41     }
42   *port_name++ = 0;           /* Terminate server name */
43
44   if (isdigit(*port_name))
45     {
46     uschar *end;
47     port = Ustrtol(port_name, &end, 0);
48     if (end != port_name + Ustrlen(port_name))
49       {
50       expand_string_message =
51         string_sprintf("invalid port number %s", port_name);
52       return FAIL;
53       }
54     }
55   else
56     {
57     struct servent *service_info = getservbyname(CS port_name, "tcp");
58     if (!service_info)
59       {
60       expand_string_message = string_sprintf("unknown port \"%s\"",
61         port_name);
62       return FAIL;
63       }
64     port = ntohs(service_info->s_port);
65     }
66
67   /* Not having the request-string here in the open routine means
68   that we cannot do TFO; a pity */
69
70   cctx->sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port,
71           timeout, &host, errmsg, NULL);
72   callout_address = NULL;
73   if (cctx->sock < 0)
74     return FAIL;
75   }
76
77 else
78   {
79   struct sockaddr_un sockun;         /* don't call this "sun" ! */
80   int rc;
81
82   DEBUG(D_lookup)
83     debug_printf_indent("  new unix socket needed for readsocket\n");
84
85   if ((cctx->sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
86     {
87     *errmsg = string_sprintf("failed to create socket: %s", strerror(errno));
88     return FAIL;
89     }
90
91   sockun.sun_family = AF_UNIX;
92   sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1),
93     sspec);
94   server_name = US sockun.sun_path;
95
96   sigalrm_seen = FALSE;
97   ALARM(timeout);
98   rc = connect(cctx->sock, (struct sockaddr *)(&sockun), sizeof(sockun));
99   ALARM_CLR(0);
100   if (sigalrm_seen)
101     {
102     *errmsg = US "socket connect timed out";
103     goto bad;
104     }
105   if (rc < 0)
106     {
107     *errmsg = string_sprintf("failed to connect to socket "
108       "%s: %s", sspec, strerror(errno));
109     goto bad;
110     }
111   host.name = server_name;
112   host.address = US"";
113   }
114
115 #ifndef DISABLE_TLS
116 if (do_tls)
117   {
118   smtp_connect_args conn_args = {.host = &host };
119   tls_support tls_dummy = {.sni=NULL};
120   uschar * errstr;
121
122   if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr))
123     {
124     *errmsg = string_sprintf("TLS connect failed: %s", errstr);
125     goto bad;
126     }
127   }
128 #endif
129
130 DEBUG(D_expand|D_lookup) debug_printf_indent("  connected to socket %s\n", sspec);
131 return OK;
132
133 bad:
134   close(cctx->sock);
135   return FAIL;
136 }
137
138 /* All use of allocations will be done against the POOL_SEARCH memory,
139 which is freed once by search_tidyup(). */
140
141 /*************************************************
142 *              Open entry point                  *
143 *************************************************/
144
145 /* See local README for interface description */
146 /* We just create a placeholder record with a closed socket, so
147 that connection cacheing at the framework layer works. */
148
149 static void *
150 readsock_open(const uschar * filename, uschar ** errmsg)
151 {
152 client_conn_ctx * cctx = store_get(sizeof(*cctx), FALSE);
153 cctx->sock = -1;
154 cctx->tls_ctx = NULL;
155 DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n");
156 return cctx;
157 }
158
159
160
161
162
163 /*************************************************
164 *         Find entry point for lsearch           *
165 *************************************************/
166
167 /* See local README for interface description */
168
169 static int
170 readsock_find(void * handle, const uschar * filename, const uschar * keystring,
171   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
172   const uschar * opts)
173 {
174 client_conn_ctx * cctx = handle;
175 int sep = ',';
176 struct {
177         BOOL do_shutdown:1;
178         BOOL do_tls:1;
179         BOOL cache:1;
180 } lf = {.do_shutdown = TRUE};
181 uschar * eol = NULL;
182 int timeout = 5;
183 FILE * fp;
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 if (!cctx->tls_ctx)
252   fp = fdopen(cctx->sock, lf.do_shutdown ? "rb" : "wb");
253
254 sigalrm_seen = FALSE;
255 ALARM(timeout);
256 yield =
257 #ifndef DISABLE_TLS
258   cctx->tls_ctx ? cat_file_tls(cctx->tls_ctx, NULL, eol) :
259 #endif
260                   cat_file(fp, NULL, eol);
261 ALARM_CLR(0);
262
263 if (sigalrm_seen)
264   { *errmsg = US "socket read timed out"; goto out; }
265
266 *result = yield ? string_from_gstring(yield) : US"";
267 ret = OK;
268 if (!lf.cache) *do_cache = 0;
269
270 out:
271
272 (void) close(cctx->sock);
273 cctx->sock = -1;
274 return ret;
275 }
276
277
278
279 /*************************************************
280 *              Close entry point                 *
281 *************************************************/
282
283 /* See local README for interface description */
284
285 static void
286 readsock_close(void * handle)
287 {
288 client_conn_ctx * cctx = handle;
289 if (cctx->sock < 0) return;
290 #ifndef DISABLE_TLS
291 if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE);
292 #endif
293 close(cctx->sock);
294 cctx->sock = -1;
295 }
296
297
298
299 static 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
316 static lookup_info *_lookup_list[] = { &readsock_lookup_info };
317 lookup_module_info readsock_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
318
319 /* End of lookups/readsock.c */