Docs: tweaks
[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 );
ff966302 512 }
ee8b8090 513 }
059ec3d9 514
059ec3d9
PH
515if (rc <= 0)
516 {
e1d04f48 517 HDEBUG(D_transport|D_acl) debug_printf_indent("send failed: %s\n", strerror(errno));
059ec3d9
PH
518 return FALSE;
519 }
520
521outblock->ptr = outblock->buffer;
522outblock->cmd_count = 0;
523return TRUE;
524}
525
526
527
528/*************************************************
529* Write SMTP command *
530*************************************************/
531
532/* The formatted command is left in big_buffer so that it can be reflected in
533any error message.
534
535Arguments:
251b9eb4 536 sx SMTP connection, contains buffer for pipelining, and socket
4e910c01 537 mode buffer, write-with-more-likely, write
059ec3d9
PH
538 format a format, starting with one of
539 of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT.
6d5c916c 540 If NULL, flush pipeline buffer only.
059ec3d9
PH
541 ... data for the format
542
543Returns: 0 if command added to pipelining buffer, with nothing transmitted
544 +n if n commands transmitted (may still have buffered the new one)
545 -1 on error, with errno set
546*/
547
548int
251b9eb4 549smtp_write_command(void * sx, int mode, const char *format, ...)
059ec3d9 550{
251b9eb4 551smtp_outblock * outblock = &((smtp_context *)sx)->outblock;
059ec3d9 552int rc = 0;
059ec3d9 553
6d5c916c 554if (format)
059ec3d9 555 {
d12746bc
JH
556 gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
557 va_list ap;
558
f3ebb786
JH
559 /* Use taint-unchecked routines for writing into big_buffer, trusting that
560 we'll never expand the results. Actually, the error-message use - leaving
561 the results in big_buffer for potential later use - is uncomfortably distant.
562 XXX Would be better to assume all smtp commands are short, use normal pool
563 alloc rather than big_buffer, and another global for the data-for-error. */
564
6d5c916c 565 va_start(ap, format);
f3ebb786 566 if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap))
6d5c916c
JH
567 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
568 "SMTP");
569 va_end(ap);
d12746bc 570 string_from_gstring(&gs);
6d5c916c 571
d12746bc 572 if (gs.ptr > outblock->buffersize)
6d5c916c
JH
573 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
574 "SMTP");
575
d12746bc 576 if (gs.ptr > outblock->buffersize - (outblock->ptr - outblock->buffer))
6d5c916c
JH
577 {
578 rc = outblock->cmd_count; /* flush resets */
4e910c01 579 if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
6d5c916c 580 }
059ec3d9 581
f3ebb786 582 Ustrncpy(outblock->ptr, gs.s, gs.ptr);
d12746bc 583 outblock->ptr += gs.ptr;
6d5c916c 584 outblock->cmd_count++;
d12746bc 585 gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
059ec3d9 586
6d5c916c
JH
587 /* We want to hide the actual data sent in AUTH transactions from reflections
588 and logs. While authenticating, a flag is set in the outblock to enable this.
589 The AUTH command itself gets any data flattened. Other lines are flattened
590 completely. */
059ec3d9 591
6d5c916c 592 if (outblock->authenticating)
059ec3d9 593 {
6d5c916c
JH
594 uschar *p = big_buffer;
595 if (Ustrncmp(big_buffer, "AUTH ", 5) == 0)
596 {
597 p += 5;
598 while (isspace(*p)) p++;
599 while (!isspace(*p)) p++;
600 while (isspace(*p)) p++;
601 }
602 while (*p != 0) *p++ = '*';
059ec3d9 603 }
059ec3d9 604
e1d04f48 605 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> %s\n", big_buffer);
6d5c916c 606 }
059ec3d9 607
4e910c01 608if (mode != SCMD_BUFFER)
059ec3d9
PH
609 {
610 rc += outblock->cmd_count; /* flush resets */
4e910c01 611 if (!flush_buffer(outblock, mode)) return -1;
059ec3d9
PH
612 }
613
614return rc;
615}
616
617
618
619/*************************************************
620* Read one line of SMTP response *
621*************************************************/
622
623/* This function reads one line of SMTP response from the server host. This may
624not be a complete response - it could be just part of a multiline response. We
625have to use a buffer for incoming packets, because when pipelining or using
626LMTP, there may well be more than one response in a single packet. This
627function is called only from the one that follows.
628
629Arguments:
630 inblock the SMTP input block (contains holding buffer, socket, etc.)
631 buffer where to put the line
632 size space available for the line
0a5441fc 633 timelimit deadline for reading the lime, seconds past epoch
059ec3d9
PH
634
635Returns: length of a line that has been put in the buffer
636 -1 otherwise, with errno set
637*/
638
639static int
0a5441fc 640read_response_line(smtp_inblock *inblock, uschar *buffer, int size, time_t timelimit)
059ec3d9
PH
641{
642uschar *p = buffer;
643uschar *ptr = inblock->ptr;
644uschar *ptrend = inblock->ptrend;
74f1a423 645client_conn_ctx * cctx = inblock->cctx;
059ec3d9
PH
646
647/* Loop for reading multiple packets or reading another packet after emptying
648a previously-read one. */
649
650for (;;)
651 {
652 int rc;
653
654 /* If there is data in the input buffer left over from last time, copy
655 characters from it until the end of a line, at which point we can return,
656 having removed any whitespace (which will include CR) at the end of the line.
657 The rules for SMTP say that lines end in CRLF, but there are have been cases
658 of hosts using just LF, and other MTAs are reported to handle this, so we
659 just look for LF. If we run out of characters before the end of a line,
660 carry on to read the next incoming packet. */
661
662 while (ptr < ptrend)
663 {
664 int c = *ptr++;
665 if (c == '\n')
666 {
667 while (p > buffer && isspace(p[-1])) p--;
668 *p = 0;
669 inblock->ptr = ptr;
670 return p - buffer;
671 }
672 *p++ = c;
673 if (--size < 4)
674 {
675 *p = 0; /* Leave malformed line for error message */
676 errno = ERRNO_SMTPFORMAT;
677 return -1;
678 }
679 }
680
681 /* Need to read a new input packet. */
682
0a5441fc 683 if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timelimit)) <= 0)
de6273b4 684 {
eebcfa1c 685 DEBUG(D_deliver|D_transport|D_acl|D_v)
170f4904
JH
686 debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n",
687 strerror(errno));
de6273b4
JH
688 break;
689 }
059ec3d9
PH
690
691 /* Another block of data has been successfully read. Set up the pointers
692 and let the loop continue. */
693
694 ptrend = inblock->ptrend = inblock->buffer + rc;
695 ptr = inblock->buffer;
e1d04f48 696 DEBUG(D_transport|D_acl) debug_printf_indent("read response data: size=%d\n", rc);
059ec3d9
PH
697 }
698
699/* Get here if there has been some kind of recv() error; errno is set, but we
700ensure that the result buffer is empty before returning. */
701
702*buffer = 0;
703return -1;
704}
705
706
707
708
709
710/*************************************************
711* Read SMTP response *
712*************************************************/
713
714/* This function reads an SMTP response with a timeout, and returns the
715response in the given buffer, as a string. A multiline response will contain
716newline characters between the lines. The function also analyzes the first
717digit of the reply code and returns FALSE if it is not acceptable. FALSE is
718also returned after a reading error. In this case buffer[0] will be zero, and
719the error code will be in errno.
720
721Arguments:
251b9eb4
JH
722 sx the SMTP connection (contains input block with holding buffer,
723 socket, etc.)
059ec3d9
PH
724 buffer where to put the response
725 size the size of the buffer
726 okdigit the expected first digit of the response
2760b518 727 timeout the timeout to use, in seconds
059ec3d9
PH
728
729Returns: TRUE if a valid, non-error response was received; else FALSE
730*/
ee8b8090 731/*XXX could move to smtp transport; no other users */
059ec3d9
PH
732
733BOOL
fc243e94 734smtp_read_response(void * sx0, uschar * buffer, int size, int okdigit,
059ec3d9
PH
735 int timeout)
736{
ee8b8090 737smtp_context * sx = sx0;
fc243e94 738uschar * ptr = buffer;
dca6d121 739int count = 0;
0a5441fc 740time_t timelimit = time(NULL) + timeout;
059ec3d9
PH
741
742errno = 0; /* Ensure errno starts out zero */
743
81344b40 744#ifndef DISABLE_PIPE_CONNECT
ee8b8090 745if (sx->pending_BANNER || sx->pending_EHLO)
dca6d121
JH
746 {
747 int rc;
fc243e94 748 if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
ee8b8090
JH
749 {
750 DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n");
fc243e94
JH
751 buffer[0] = '\0';
752 if (rc == DEFER) errno = ERRNO_TLSFAILURE;
ee8b8090
JH
753 return FALSE;
754 }
dca6d121 755 }
ee8b8090
JH
756#endif
757
4c04137d 758/* This is a loop to read and concatenate the lines that make up a multi-line
059ec3d9
PH
759response. */
760
761for (;;)
762 {
0a5441fc 763 if ((count = read_response_line(&sx->inblock, ptr, size, timelimit)) < 0)
059ec3d9
PH
764 return FALSE;
765
766 HDEBUG(D_transport|D_acl|D_v)
ee8b8090 767 debug_printf_indent(" %s %s\n", ptr == buffer ? "SMTP<<" : " ", ptr);
059ec3d9
PH
768
769 /* Check the format of the response: it must start with three digits; if
770 these are followed by a space or end of line, the response is complete. If
771 they are followed by '-' this is a multi-line response and we must look for
772 another line until the final line is reached. The only use made of multi-line
773 responses is to pass them back as error messages. We therefore just
774 concatenate them all within the buffer, which should be large enough to
775 accept any reasonable number of lines. */
776
777 if (count < 3 ||
778 !isdigit(ptr[0]) ||
779 !isdigit(ptr[1]) ||
780 !isdigit(ptr[2]) ||
781 (ptr[3] != '-' && ptr[3] != ' ' && ptr[3] != 0))
782 {
783 errno = ERRNO_SMTPFORMAT; /* format error */
784 return FALSE;
785 }
786
787 /* If the line we have just read is a terminal line, line, we are done.
788 Otherwise more data has to be read. */
789
790 if (ptr[3] != '-') break;
791
792 /* Move the reading pointer upwards in the buffer and insert \n between the
793 components of a multiline response. Space is left for this by read_response_
794 line(). */
795
796 ptr += count;
797 *ptr++ = '\n';
798 size -= count + 1;
799 }
800
ee8b8090
JH
801#ifdef TCP_FASTOPEN
802 tfo_out_check(sx->cctx.sock);
803#endif
804
059ec3d9
PH
805/* Return a value that depends on the SMTP return code. On some systems a
806non-zero value of errno has been seen at this point, so ensure it is zero,
807because the caller of this function looks at errno when FALSE is returned, to
808distinguish between an unexpected return code and other errors such as
809timeouts, lost connections, etc. */
810
811errno = 0;
812return buffer[0] == okdigit;
813}
814
815/* End of smtp_out.c */
7eb6c37c
JH
816/* vi: aw ai sw=2
817*/