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