FreeBSD: better support for TFO
[exim.git] / src / src / smtp_out.c
CommitLineData
059ec3d9
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge 1995 - 2018 */
059ec3d9
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
8/* A number of functions for driving outgoing SMTP calls. */
9
10
11#include "exim.h"
7eb6c37c 12#include "transports/smtp.h"
059ec3d9
PH
13
14
15
16/*************************************************
17* Find an outgoing interface *
18*************************************************/
19
20/* This function is called from the smtp transport and also from the callout
21code in verify.c. Its job is to expand a string to get a list of interfaces,
22and choose a suitable one (IPv4 or IPv6) for the outgoing address.
23
24Arguments:
25 istring string interface setting, may be NULL, meaning "any", in
26 which case the function does nothing
27 host_af AF_INET or AF_INET6 for the outgoing IP address
28 addr the mail address being handled (for setting errors)
059ec3d9
PH
29 interface point this to the interface
30 msg to add to any error message
31
32Returns: TRUE on success, FALSE on failure, with error message
33 set in addr and transport_return set to PANIC
34*/
35
36BOOL
37smtp_get_interface(uschar *istring, int host_af, address_item *addr,
6f6dedcc 38 uschar **interface, uschar *msg)
059ec3d9 39{
55414b25 40const uschar * expint;
059ec3d9
PH
41uschar *iface;
42int sep = 0;
43
fb05276a 44if (!istring) return TRUE;
059ec3d9 45
fb05276a 46if (!(expint = expand_string(istring)))
059ec3d9 47 {
8768d548 48 if (f.expand_string_forcedfail) return TRUE;
059ec3d9
PH
49 addr->transport_return = PANIC;
50 addr->message = string_sprintf("failed to expand \"interface\" "
51 "option for %s: %s", msg, expand_string_message);
52 return FALSE;
53 }
54
f3ebb786
JH
55if (is_tainted(expint))
56 {
57 log_write(0, LOG_MAIN|LOG_PANIC,
58 "attempt to use tainted value '%s' from '%s' for interface",
59 expint, istring);
60 addr->transport_return = PANIC;
61 addr->message = string_sprintf("failed to expand \"interface\" "
62 "option for %s: configuration error", msg);
63 return FALSE;
64 }
65
059ec3d9
PH
66while (isspace(*expint)) expint++;
67if (*expint == 0) return TRUE;
68
69while ((iface = string_nextinlist(&expint, &sep, big_buffer,
fb05276a 70 big_buffer_size)))
059ec3d9
PH
71 {
72 if (string_is_ip_address(iface, NULL) == 0)
73 {
74 addr->transport_return = PANIC;
75 addr->message = string_sprintf("\"%s\" is not a valid IP "
76 "address for the \"interface\" option for %s",
77 iface, msg);
78 return FALSE;
79 }
80
81 if (((Ustrchr(iface, ':') == NULL)? AF_INET:AF_INET6) == host_af)
82 break;
83 }
84
fb05276a 85if (iface) *interface = string_copy(iface);
059ec3d9
PH
86return TRUE;
87}
88
89
90
91/*************************************************
92* Find an outgoing port *
93*************************************************/
94
95/* This function is called from the smtp transport and also from the callout
96code in verify.c. Its job is to find a port number. Note that getservbyname()
97produces the number in network byte order.
98
99Arguments:
100 rstring raw (unexpanded) string representation of the port
101 addr the mail address being handled (for setting errors)
102 port stick the port in here
103 msg for adding to error message
104
105Returns: TRUE on success, FALSE on failure, with error message set
106 in addr, and transport_return set to PANIC
107*/
108
109BOOL
110smtp_get_port(uschar *rstring, address_item *addr, int *port, uschar *msg)
111{
112uschar *pstring = expand_string(rstring);
113
00580051 114if (!pstring)
059ec3d9
PH
115 {
116 addr->transport_return = PANIC;
117 addr->message = string_sprintf("failed to expand \"%s\" (\"port\" option) "
118 "for %s: %s", rstring, msg, expand_string_message);
119 return FALSE;
120 }
121
122if (isdigit(*pstring))
123 {
124 uschar *end;
125 *port = Ustrtol(pstring, &end, 0);
126 if (end != pstring + Ustrlen(pstring))
127 {
128 addr->transport_return = PANIC;
129 addr->message = string_sprintf("invalid port number for %s: %s", msg,
130 pstring);
131 return FALSE;
132 }
133 }
134
135else
136 {
137 struct servent *smtp_service = getservbyname(CS pstring, "tcp");
00580051 138 if (!smtp_service)
059ec3d9
PH
139 {
140 addr->transport_return = PANIC;
141 addr->message = string_sprintf("TCP port \"%s\" is not defined for %s",
142 pstring, msg);
143 return FALSE;
144 }
145 *port = ntohs(smtp_service->s_port);
146 }
147
148return TRUE;
149}
150
151
152
153
1ccd5f67
JH
154#ifdef TCP_FASTOPEN
155static void
156tfo_out_check(int sock)
157{
1ccd5f67 158struct tcp_info tinfo;
73a10da9
JH
159int val;
160socklen_t len = sizeof(val);
161
162# ifdef __FreeBSD__
163/* The observability as of 12.1 is not useful as a client, only telling us that
164a TFO option was used on SYN. It could have been a TFO-R, or ignored by the
165server. */
166
167/*
168if (tcp_out_fastopen == TFO_ATTEMPTED_NODATA || tcp_out_fastopen == TFO_ATTEMPTED_DATA)
169 if (getsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &val, &len) == 0 && val != 0) {}
170*/
171switch (tcp_out_fastopen)
172 {
173 case TFO_ATTEMPTED_NODATA: tcp_out_fastopen = TFO_USED_NODATA; break;
174 case TFO_ATTEMPTED_DATA: tcp_out_fastopen = TFO_USED_DATA; break;
175 default: break; /* compiler quietening */
176 }
177
178# else /* Linux & Apple */
179# if defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED)
1ccd5f67 180
ee8b8090
JH
181switch (tcp_out_fastopen)
182 {
183 /* This is a somewhat dubious detection method; totally undocumented so likely
184 to fail in future kernels. There seems to be no documented way. What we really
185 want to know is if the server sent smtp-banner data before our ACK of his SYN,ACK
186 hit him. What this (possibly?) detects is whether we sent a TFO cookie with our
187 SYN, as distinct from a TFO request. This gets a false-positive when the server
188 key is rotated; we send the old one (which this test sees) but the server returns
189 the new one and does not send its SMTP banner before we ACK his SYN,ACK.
190 To force that rotation case:
191 '# echo -n "00000000-00000000-00000000-0000000" >/proc/sys/net/ipv4/tcp_fastopen_key'
192 The kernel seems to be counting unack'd packets. */
193
194 case TFO_ATTEMPTED_NODATA:
195 if ( getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
196 && tinfo.tcpi_state == TCP_SYN_SENT
197 && tinfo.tcpi_unacked > 1
198 )
199 {
200 DEBUG(D_transport|D_v)
201 debug_printf("TCP_FASTOPEN tcpi_unacked %d\n", tinfo.tcpi_unacked);
202 tcp_out_fastopen = TFO_USED_NODATA;
203 }
204 break;
1ccd5f67 205
ee8b8090
JH
206 /* When called after waiting for received data we should be able
207 to tell if data we sent was accepted. */
1ccd5f67 208
ee8b8090
JH
209 case TFO_ATTEMPTED_DATA:
210 if ( getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
211 && tinfo.tcpi_state == TCP_ESTABLISHED
212 )
213 if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
214 {
215 DEBUG(D_transport|D_v) debug_printf("TFO: data was acked\n");
216 tcp_out_fastopen = TFO_USED_DATA;
217 }
218 else
1ccd5f67
JH
219 {
220 DEBUG(D_transport|D_v) debug_printf("TFO: had to retransmit\n");
06e272a3 221 tcp_out_fastopen = TFO_NOT_USED;
1ccd5f67 222 }
ee8b8090 223 break;
56dbf856
JH
224
225 default: break; /* compiler quietening */
ee8b8090 226 }
73a10da9
JH
227# endif
228# endif /* Linux & Apple */
1ccd5f67
JH
229}
230#endif
231
232
0ab63f3d 233/* Arguments as for smtp_connect(), plus
b536a578
JH
234 early_data if non-NULL, idenmpotent data to be sent -
235 preferably in the TCP SYN segment
0ab63f3d
JH
236
237Returns: connected socket number, or -1 with errno set
238*/
239
059ec3d9 240int
7eb6c37c 241smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface,
0ab63f3d 242 transport_instance * tb, int timeout, const blob * early_data)
059ec3d9 243{
7eb6c37c
JH
244smtp_transport_options_block * ob =
245 (smtp_transport_options_block *)tb->options_block;
246const uschar * dscp = ob->dscp;
9e4f5962
PP
247int dscp_value;
248int dscp_level;
249int dscp_option;
059ec3d9 250int sock;
7eb6c37c 251int save_errno = 0;
10ac8d7f 252const blob * fastopen_blob = NULL;
1ccd5f67 253
059ec3d9 254
0cbf2b82 255#ifndef DISABLE_EVENT
7eb6c37c
JH
256deliver_host_address = host->address;
257deliver_host_port = port;
258if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1;
a7538db1
JH
259#endif
260
059ec3d9
PH
261if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
262
263/* Set TCP_NODELAY; Exim does its own buffering. */
264
d4ff61d1
JH
265if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, US &on, sizeof(on)))
266 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 267 debug_printf_indent("failed to set NODELAY: %s ", strerror(errno));
059ec3d9 268
9e4f5962
PP
269/* Set DSCP value, if we can. For now, if we fail to set the value, we don't
270bomb out, just log it and continue in default traffic class. */
271
272if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value))
273 {
274 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 275 debug_printf_indent("DSCP \"%s\"=%x ", dscp, dscp_value);
9e4f5962
PP
276 if (setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)) < 0)
277 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 278 debug_printf_indent("failed to set DSCP: %s ", strerror(errno));
9e4f5962
PP
279 /* If the kernel supports IPv4 and IPv6 on an IPv6 socket, we need to set the
280 option for both; ignore failures here */
281 if (host_af == AF_INET6 &&
282 dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value))
9e4f5962 283 (void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value));
9e4f5962
PP
284 }
285
059ec3d9
PH
286/* Bind to a specific interface if requested. Caller must ensure the interface
287is the same type (IPv4 or IPv6) as the outgoing address. */
288
7eb6c37c 289if (interface && ip_bind(sock, host_af, interface, 0) < 0)
059ec3d9
PH
290 {
291 save_errno = errno;
292 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 293 debug_printf_indent("unable to bind outgoing SMTP call to %s: %s", interface,
059ec3d9
PH
294 strerror(errno));
295 }
296
297/* Connect to the remote host, and add keepalive to the socket before returning
0ab63f3d 298it, if requested. If the build supports TFO, request it - and if the caller
ee8b8090
JH
299requested some early-data then include that in the TFO request. If there is
300early-data but no TFO support, send it after connecting. */
059ec3d9 301
0ab63f3d
JH
302else
303 {
0ab63f3d 304#ifdef TCP_FASTOPEN
3fb3231c 305 if (verify_check_given_host(CUSS &ob->hosts_try_fastopen, host) == OK)
10ac8d7f 306 fastopen_blob = early_data ? early_data : &tcp_fastopen_nodata;
0ab63f3d
JH
307#endif
308
10ac8d7f 309 if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0)
0ab63f3d 310 save_errno = errno;
10ac8d7f 311 else if (early_data && !fastopen_blob && early_data->data && early_data->len)
ee8b8090
JH
312 {
313 HDEBUG(D_transport|D_acl|D_v)
314 debug_printf("sending %ld nonTFO early-data\n", (long)early_data->len);
315
316#ifdef TCP_QUICKACK
317 (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
318#endif
0ab63f3d
JH
319 if (send(sock, early_data->data, early_data->len, 0) < 0)
320 save_errno = errno;
ee8b8090 321 }
0ab63f3d 322 }
059ec3d9
PH
323
324/* Either bind() or connect() failed */
325
326if (save_errno != 0)
327 {
8e669ac1 328 HDEBUG(D_transport|D_acl|D_v)
4deaf07d 329 {
adb21834 330 debug_printf_indent(" failed: %s", CUstrerror(save_errno));
8e669ac1 331 if (save_errno == ETIMEDOUT)
4deaf07d 332 debug_printf(" (timeout=%s)", readconf_printtime(timeout));
8e669ac1
PH
333 debug_printf("\n");
334 }
f1e894f3 335 (void)close(sock);
059ec3d9
PH
336 errno = save_errno;
337 return -1;
338 }
339
ee8b8090 340/* Both bind() and connect() succeeded, and any early-data */
059ec3d9
PH
341
342else
343 {
41c7c167
PH
344 union sockaddr_46 interface_sock;
345 EXIM_SOCKLEN_T size = sizeof(interface_sock);
ee8b8090 346
adb21834 347 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" connected\n");
41c7c167
PH
348 if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0)
349 sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
350 else
351 {
352 log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
353 "getsockname() failed: %s", strerror(errno));
354 close(sock);
355 return -1;
356 }
ee8b8090 357
7eb6c37c 358 if (ob->keepalive) ip_keepalive(sock, host->address, TRUE);
1ccd5f67 359#ifdef TCP_FASTOPEN
ee8b8090 360 tfo_out_check(sock);
1ccd5f67 361#endif
059ec3d9
PH
362 return sock;
363 }
364}
365
58c30e47
JH
366
367
368
369
370void
371smtp_port_for_connect(host_item * host, int port)
372{
373if (host->port != PORT_NONE)
374 {
375 HDEBUG(D_transport|D_acl|D_v)
376 debug_printf_indent("Transport port=%d replaced by host-specific port=%d\n", port,
377 host->port);
378 port = host->port;
379 }
380else host->port = port; /* Set the port actually used */
381}
382
383
7eb6c37c
JH
384/*************************************************
385* Connect to remote host *
386*************************************************/
387
388/* Create a socket, and connect it to a remote host. IPv6 addresses are
389detected by checking for a colon in the address. AF_INET6 is defined even on
390non-IPv6 systems, to enable the code to be less messy. However, on such systems
391host->address will always be an IPv4 address.
392
7eb6c37c 393Arguments:
ee8b8090
JH
394 sc details for making connection: host, af, interface, transport
395 early_data if non-NULL, data to be sent - preferably in the TCP SYN segment
7eb6c37c
JH
396
397Returns: connected socket number, or -1 with errno set
398*/
399
400int
ee8b8090 401smtp_connect(smtp_connect_args * sc, const blob * early_data)
7eb6c37c 402{
ee8b8090
JH
403int port = sc->host->port;
404smtp_transport_options_block * ob = sc->ob;
7eb6c37c 405
ee8b8090 406callout_address = string_sprintf("[%s]:%d", sc->host->address, port);
055e2cb4 407
7eb6c37c
JH
408HDEBUG(D_transport|D_acl|D_v)
409 {
410 uschar * s = US" ";
ee8b8090 411 if (sc->interface) s = string_sprintf(" from %s ", sc->interface);
f0989ec0 412#ifdef SUPPORT_SOCKS
7eb6c37c
JH
413 if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s);
414#endif
ee8b8090 415 debug_printf_indent("Connecting to %s %s%s... ", sc->host->name, callout_address, s);
7eb6c37c
JH
416 }
417
418/* Create and connect the socket */
419
f0989ec0 420#ifdef SUPPORT_SOCKS
7eb6c37c 421if (ob->socks_proxy)
ee8b8090
JH
422 {
423 int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface,
424 sc->tblock, ob->connect_timeout);
425
426 if (sock >= 0)
427 {
428 if (early_data && early_data->data && early_data->len)
429 if (send(sock, early_data->data, early_data->len, 0) < 0)
430 {
431 int save_errno = errno;
432 HDEBUG(D_transport|D_acl|D_v)
433 {
434 debug_printf_indent("failed: %s", CUstrerror(save_errno));
435 if (save_errno == ETIMEDOUT)
436 debug_printf(" (timeout=%s)", readconf_printtime(ob->connect_timeout));
437 debug_printf("\n");
438 }
439 (void)close(sock);
440 sock = -1;
441 errno = save_errno;
442 }
443 }
444 return sock;
445 }
7eb6c37c
JH
446#endif
447
ee8b8090
JH
448return smtp_sock_connect(sc->host, sc->host_af, port, sc->interface,
449 sc->tblock, ob->connect_timeout, early_data);
7eb6c37c
JH
450}
451
059ec3d9
PH
452
453/*************************************************
454* Flush outgoing command buffer *
455*************************************************/
456
457/* This function is called only from smtp_write_command() below. It flushes
458the buffer of outgoing commands. There is more than one in the buffer only when
459pipelining.
460
461Argument:
462 outblock the SMTP output block
925ac8e4 463 mode further data expected, or plain
059ec3d9
PH
464
465Returns: TRUE if OK, FALSE on error, with errno set
466*/
467
468static BOOL
4e910c01 469flush_buffer(smtp_outblock * outblock, int mode)
059ec3d9
PH
470{
471int rc;
58fc5fb2 472int n = outblock->ptr - outblock->buffer;
925ac8e4 473BOOL more = mode == SCMD_MORE;
059ec3d9 474
4e910c01 475HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
925ac8e4 476 more ? " (more expected)" : "");
4e910c01 477
01603eec 478#ifndef DISABLE_TLS
74f1a423
JH
479if (outblock->cctx->tls_ctx)
480 rc = tls_write(outblock->cctx->tls_ctx, outblock->buffer, n, more);
059ec3d9
PH
481else
482#endif
ee8b8090
JH
483
484 {
485 if (outblock->conn_args)
486 {
487 blob early_data = { .data = outblock->buffer, .len = n };
488
489 /* We ignore the more-flag if we're doing a connect with early-data, which
490 means we won't get BDAT+data. A pity, but wise due to the idempotency
491 requirement: TFO with data can, in rare cases, replay the data to the
492 receiver. */
493
494 if ( (outblock->cctx->sock = smtp_connect(outblock->conn_args, &early_data))
495 < 0)
496 return FALSE;
497 outblock->conn_args = NULL;
498 rc = n;
499 }
500 else
501
502 rc = send(outblock->cctx->sock, outblock->buffer, n,
4e910c01 503#ifdef MSG_MORE
ee8b8090 504 more ? MSG_MORE : 0
4e910c01 505#else
ee8b8090 506 0
4e910c01 507#endif
ee8b8090
JH
508 );
509 }
059ec3d9 510
059ec3d9
PH
511if (rc <= 0)
512 {
e1d04f48 513 HDEBUG(D_transport|D_acl) debug_printf_indent("send failed: %s\n", strerror(errno));
059ec3d9
PH
514 return FALSE;
515 }
516
517outblock->ptr = outblock->buffer;
518outblock->cmd_count = 0;
519return TRUE;
520}
521
522
523
524/*************************************************
525* Write SMTP command *
526*************************************************/
527
528/* The formatted command is left in big_buffer so that it can be reflected in
529any error message.
530
531Arguments:
251b9eb4 532 sx SMTP connection, contains buffer for pipelining, and socket
4e910c01 533 mode buffer, write-with-more-likely, write
059ec3d9
PH
534 format a format, starting with one of
535 of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT.
6d5c916c 536 If NULL, flush pipeline buffer only.
059ec3d9
PH
537 ... data for the format
538
539Returns: 0 if command added to pipelining buffer, with nothing transmitted
540 +n if n commands transmitted (may still have buffered the new one)
541 -1 on error, with errno set
542*/
543
544int
251b9eb4 545smtp_write_command(void * sx, int mode, const char *format, ...)
059ec3d9 546{
251b9eb4 547smtp_outblock * outblock = &((smtp_context *)sx)->outblock;
059ec3d9 548int rc = 0;
059ec3d9 549
6d5c916c 550if (format)
059ec3d9 551 {
d12746bc
JH
552 gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
553 va_list ap;
554
f3ebb786
JH
555 /* Use taint-unchecked routines for writing into big_buffer, trusting that
556 we'll never expand the results. Actually, the error-message use - leaving
557 the results in big_buffer for potential later use - is uncomfortably distant.
558 XXX Would be better to assume all smtp commands are short, use normal pool
559 alloc rather than big_buffer, and another global for the data-for-error. */
560
6d5c916c 561 va_start(ap, format);
f3ebb786 562 if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap))
6d5c916c
JH
563 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
564 "SMTP");
565 va_end(ap);
d12746bc 566 string_from_gstring(&gs);
6d5c916c 567
d12746bc 568 if (gs.ptr > outblock->buffersize)
6d5c916c
JH
569 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
570 "SMTP");
571
d12746bc 572 if (gs.ptr > outblock->buffersize - (outblock->ptr - outblock->buffer))
6d5c916c
JH
573 {
574 rc = outblock->cmd_count; /* flush resets */
4e910c01 575 if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
6d5c916c 576 }
059ec3d9 577
f3ebb786 578 Ustrncpy(outblock->ptr, gs.s, gs.ptr);
d12746bc 579 outblock->ptr += gs.ptr;
6d5c916c 580 outblock->cmd_count++;
d12746bc 581 gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
059ec3d9 582
6d5c916c
JH
583 /* We want to hide the actual data sent in AUTH transactions from reflections
584 and logs. While authenticating, a flag is set in the outblock to enable this.
585 The AUTH command itself gets any data flattened. Other lines are flattened
586 completely. */
059ec3d9 587
6d5c916c 588 if (outblock->authenticating)
059ec3d9 589 {
6d5c916c
JH
590 uschar *p = big_buffer;
591 if (Ustrncmp(big_buffer, "AUTH ", 5) == 0)
592 {
593 p += 5;
594 while (isspace(*p)) p++;
595 while (!isspace(*p)) p++;
596 while (isspace(*p)) p++;
597 }
598 while (*p != 0) *p++ = '*';
059ec3d9 599 }
059ec3d9 600
e1d04f48 601 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> %s\n", big_buffer);
6d5c916c 602 }
059ec3d9 603
4e910c01 604if (mode != SCMD_BUFFER)
059ec3d9
PH
605 {
606 rc += outblock->cmd_count; /* flush resets */
4e910c01 607 if (!flush_buffer(outblock, mode)) return -1;
059ec3d9
PH
608 }
609
610return rc;
611}
612
613
614
615/*************************************************
616* Read one line of SMTP response *
617*************************************************/
618
619/* This function reads one line of SMTP response from the server host. This may
620not be a complete response - it could be just part of a multiline response. We
621have to use a buffer for incoming packets, because when pipelining or using
622LMTP, there may well be more than one response in a single packet. This
623function is called only from the one that follows.
624
625Arguments:
626 inblock the SMTP input block (contains holding buffer, socket, etc.)
627 buffer where to put the line
628 size space available for the line
0a5441fc 629 timelimit deadline for reading the lime, seconds past epoch
059ec3d9
PH
630
631Returns: length of a line that has been put in the buffer
632 -1 otherwise, with errno set
633*/
634
635static int
0a5441fc 636read_response_line(smtp_inblock *inblock, uschar *buffer, int size, time_t timelimit)
059ec3d9
PH
637{
638uschar *p = buffer;
639uschar *ptr = inblock->ptr;
640uschar *ptrend = inblock->ptrend;
74f1a423 641client_conn_ctx * cctx = inblock->cctx;
059ec3d9
PH
642
643/* Loop for reading multiple packets or reading another packet after emptying
644a previously-read one. */
645
646for (;;)
647 {
648 int rc;
649
650 /* If there is data in the input buffer left over from last time, copy
651 characters from it until the end of a line, at which point we can return,
652 having removed any whitespace (which will include CR) at the end of the line.
653 The rules for SMTP say that lines end in CRLF, but there are have been cases
654 of hosts using just LF, and other MTAs are reported to handle this, so we
655 just look for LF. If we run out of characters before the end of a line,
656 carry on to read the next incoming packet. */
657
658 while (ptr < ptrend)
659 {
660 int c = *ptr++;
661 if (c == '\n')
662 {
663 while (p > buffer && isspace(p[-1])) p--;
664 *p = 0;
665 inblock->ptr = ptr;
666 return p - buffer;
667 }
668 *p++ = c;
669 if (--size < 4)
670 {
671 *p = 0; /* Leave malformed line for error message */
672 errno = ERRNO_SMTPFORMAT;
673 return -1;
674 }
675 }
676
677 /* Need to read a new input packet. */
678
0a5441fc 679 if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timelimit)) <= 0)
de6273b4 680 {
eebcfa1c 681 DEBUG(D_deliver|D_transport|D_acl|D_v)
170f4904
JH
682 debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n",
683 strerror(errno));
de6273b4
JH
684 break;
685 }
059ec3d9
PH
686
687 /* Another block of data has been successfully read. Set up the pointers
688 and let the loop continue. */
689
690 ptrend = inblock->ptrend = inblock->buffer + rc;
691 ptr = inblock->buffer;
e1d04f48 692 DEBUG(D_transport|D_acl) debug_printf_indent("read response data: size=%d\n", rc);
059ec3d9
PH
693 }
694
695/* Get here if there has been some kind of recv() error; errno is set, but we
696ensure that the result buffer is empty before returning. */
697
698*buffer = 0;
699return -1;
700}
701
702
703
704
705
706/*************************************************
707* Read SMTP response *
708*************************************************/
709
710/* This function reads an SMTP response with a timeout, and returns the
711response in the given buffer, as a string. A multiline response will contain
712newline characters between the lines. The function also analyzes the first
713digit of the reply code and returns FALSE if it is not acceptable. FALSE is
714also returned after a reading error. In this case buffer[0] will be zero, and
715the error code will be in errno.
716
717Arguments:
251b9eb4
JH
718 sx the SMTP connection (contains input block with holding buffer,
719 socket, etc.)
059ec3d9
PH
720 buffer where to put the response
721 size the size of the buffer
722 okdigit the expected first digit of the response
2760b518 723 timeout the timeout to use, in seconds
059ec3d9
PH
724
725Returns: TRUE if a valid, non-error response was received; else FALSE
726*/
ee8b8090 727/*XXX could move to smtp transport; no other users */
059ec3d9
PH
728
729BOOL
fc243e94 730smtp_read_response(void * sx0, uschar * buffer, int size, int okdigit,
059ec3d9
PH
731 int timeout)
732{
ee8b8090 733smtp_context * sx = sx0;
fc243e94 734uschar * ptr = buffer;
dca6d121 735int count = 0;
0a5441fc 736time_t timelimit = time(NULL) + timeout;
059ec3d9
PH
737
738errno = 0; /* Ensure errno starts out zero */
739
81344b40 740#ifndef DISABLE_PIPE_CONNECT
ee8b8090 741if (sx->pending_BANNER || sx->pending_EHLO)
dca6d121
JH
742 {
743 int rc;
fc243e94 744 if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
ee8b8090
JH
745 {
746 DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n");
fc243e94
JH
747 buffer[0] = '\0';
748 if (rc == DEFER) errno = ERRNO_TLSFAILURE;
ee8b8090
JH
749 return FALSE;
750 }
dca6d121 751 }
ee8b8090
JH
752#endif
753
4c04137d 754/* This is a loop to read and concatenate the lines that make up a multi-line
059ec3d9
PH
755response. */
756
757for (;;)
758 {
0a5441fc 759 if ((count = read_response_line(&sx->inblock, ptr, size, timelimit)) < 0)
059ec3d9
PH
760 return FALSE;
761
762 HDEBUG(D_transport|D_acl|D_v)
ee8b8090 763 debug_printf_indent(" %s %s\n", ptr == buffer ? "SMTP<<" : " ", ptr);
059ec3d9
PH
764
765 /* Check the format of the response: it must start with three digits; if
766 these are followed by a space or end of line, the response is complete. If
767 they are followed by '-' this is a multi-line response and we must look for
768 another line until the final line is reached. The only use made of multi-line
769 responses is to pass them back as error messages. We therefore just
770 concatenate them all within the buffer, which should be large enough to
771 accept any reasonable number of lines. */
772
773 if (count < 3 ||
774 !isdigit(ptr[0]) ||
775 !isdigit(ptr[1]) ||
776 !isdigit(ptr[2]) ||
777 (ptr[3] != '-' && ptr[3] != ' ' && ptr[3] != 0))
778 {
779 errno = ERRNO_SMTPFORMAT; /* format error */
780 return FALSE;
781 }
782
783 /* If the line we have just read is a terminal line, line, we are done.
784 Otherwise more data has to be read. */
785
786 if (ptr[3] != '-') break;
787
788 /* Move the reading pointer upwards in the buffer and insert \n between the
789 components of a multiline response. Space is left for this by read_response_
790 line(). */
791
792 ptr += count;
793 *ptr++ = '\n';
794 size -= count + 1;
795 }
796
ee8b8090
JH
797#ifdef TCP_FASTOPEN
798 tfo_out_check(sx->cctx.sock);
799#endif
800
059ec3d9
PH
801/* Return a value that depends on the SMTP return code. On some systems a
802non-zero value of errno has been seen at this point, so ensure it is zero,
803because the caller of this function looks at errno when FALSE is returned, to
804distinguish between an unexpected return code and other errors such as
805timeouts, lost connections, etc. */
806
807errno = 0;
808return buffer[0] == okdigit;
809}
810
811/* End of smtp_out.c */
7eb6c37c
JH
812/* vi: aw ai sw=2
813*/