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