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