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