Fix macOS build
[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 /* 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
21 code in verify.c. Its job is to expand a string to get a list of interfaces,
22 and choose a suitable one (IPv4 or IPv6) for the outgoing address.
23
24 Arguments:
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
32 Returns:     TRUE on success, FALSE on failure, with error message
33                set in addr and transport_return set to PANIC
34 */
35
36 BOOL
37 smtp_get_interface(uschar *istring, int host_af, address_item *addr,
38   uschar **interface, uschar *msg)
39 {
40 const uschar * expint;
41 uschar *iface;
42 int sep = 0;
43
44 if (!istring) return TRUE;
45
46 if (!(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
55 if (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
66 while (isspace(*expint)) expint++;
67 if (*expint == 0) return TRUE;
68
69 while ((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
85 if (iface) *interface = string_copy(iface);
86 return 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
96 code in verify.c. Its job is to find a port number. Note that getservbyname()
97 produces the number in network byte order.
98
99 Arguments:
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
105 Returns:      TRUE on success, FALSE on failure, with error message set
106                 in addr, and transport_return set to PANIC
107 */
108
109 BOOL
110 smtp_get_port(uschar *rstring, address_item *addr, int *port, uschar *msg)
111 {
112 uschar *pstring = expand_string(rstring);
113
114 if (!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
122 if (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
135 else
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
148 return TRUE;
149 }
150
151
152
153
154 #ifdef TCP_FASTOPEN
155 static void
156 tfo_out_check(int sock)
157 {
158 # ifdef __FreeBSD__
159 struct tcp_info tinfo;
160 int val;
161 socklen_t len = sizeof(val);
162
163 /* The observability as of 12.1 is not useful as a client, only telling us that
164 a TFO option was used on SYN.  It could have been a TFO-R, or ignored by the
165 server. */
166
167 /*
168 if (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 */
171 switch (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)
180 struct tcp_info tinfo;
181 socklen_t len = sizeof(tinfo);
182
183 switch (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
239 Returns:      connected socket number, or -1 with errno set
240 */
241
242 int
243 smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface,
244   transport_instance * tb, int timeout, const blob * early_data)
245 {
246 smtp_transport_options_block * ob =
247   (smtp_transport_options_block *)tb->options_block;
248 const uschar * dscp = ob->dscp;
249 int dscp_value;
250 int dscp_level;
251 int dscp_option;
252 int sock;
253 int save_errno = 0;
254 const blob * fastopen_blob = NULL;
255
256
257 #ifndef DISABLE_EVENT
258 deliver_host_address = host->address;
259 deliver_host_port = port;
260 if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1;
261 #endif
262
263 if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
264
265 /* Set TCP_NODELAY; Exim does its own buffering. */
266
267 if (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
272 bomb out, just log it and continue in default traffic class. */
273
274 if (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
289 is the same type (IPv4 or IPv6) as the outgoing address. */
290
291 if (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
300 it, if requested.  If the build supports TFO, request it - and if the caller
301 requested some early-data then include that in the TFO request.  If there is
302 early-data but no TFO support, send it after connecting. */
303
304 else
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
328 if (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
344 else
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
372 void
373 smtp_port_for_connect(host_item * host, int port)
374 {
375 if (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   }
382 else 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
391 detected by checking for a colon in the address. AF_INET6 is defined even on
392 non-IPv6 systems, to enable the code to be less messy. However, on such systems
393 host->address will always be an IPv4 address.
394
395 Arguments:
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
399 Returns:      connected socket number, or -1 with errno set
400 */
401
402 int
403 smtp_connect(smtp_connect_args * sc, const blob * early_data)
404 {
405 int port = sc->host->port;
406 smtp_transport_options_block * ob = sc->ob;
407
408 callout_address = string_sprintf("[%s]:%d", sc->host->address, port);
409
410 HDEBUG(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
423 if (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
450 return 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
460 the buffer of outgoing commands. There is more than one in the buffer only when
461 pipelining.
462
463 Argument:
464   outblock   the SMTP output block
465   mode       further data expected, or plain
466
467 Returns:     TRUE if OK, FALSE on error, with errno set
468 */
469
470 static BOOL
471 flush_buffer(smtp_outblock * outblock, int mode)
472 {
473 int rc;
474 int n = outblock->ptr - outblock->buffer;
475 BOOL more = mode == SCMD_MORE;
476
477 HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
478   more ? " (more expected)" : "");
479
480 #ifndef DISABLE_TLS
481 if (outblock->cctx->tls_ctx)
482   rc = tls_write(outblock->cctx->tls_ctx, outblock->buffer, n, more);
483 else
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 if (rc <= 0)
514   {
515   HDEBUG(D_transport|D_acl) debug_printf_indent("send failed: %s\n", strerror(errno));
516   return FALSE;
517   }
518
519 outblock->ptr = outblock->buffer;
520 outblock->cmd_count = 0;
521 return TRUE;
522 }
523
524
525
526 /*************************************************
527 *             Write SMTP command                 *
528 *************************************************/
529
530 /* The formatted command is left in big_buffer so that it can be reflected in
531 any error message.
532
533 Arguments:
534   sx         SMTP connection, contains buffer for pipelining, and socket
535   mode       buffer, write-with-more-likely, write
536   format     a format, starting with one of
537              of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT.
538              If NULL, flush pipeline buffer only.
539   ...        data for the format
540
541 Returns:     0 if command added to pipelining buffer, with nothing transmitted
542             +n if n commands transmitted (may still have buffered the new one)
543             -1 on error, with errno set
544 */
545
546 int
547 smtp_write_command(void * sx, int mode, const char *format, ...)
548 {
549 smtp_outblock * outblock = &((smtp_context *)sx)->outblock;
550 int rc = 0;
551
552 if (format)
553   {
554   gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
555   va_list ap;
556
557   /* Use taint-unchecked routines for writing into big_buffer, trusting that
558   we'll never expand the results.  Actually, the error-message use - leaving
559   the results in big_buffer for potential later use - is uncomfortably distant.
560   XXX Would be better to assume all smtp commands are short, use normal pool
561   alloc rather than big_buffer, and another global for the data-for-error. */
562
563   va_start(ap, format);
564   if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap))
565     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
566       "SMTP");
567   va_end(ap);
568   string_from_gstring(&gs);
569
570   if (gs.ptr > outblock->buffersize)
571     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
572       "SMTP");
573
574   if (gs.ptr > outblock->buffersize - (outblock->ptr - outblock->buffer))
575     {
576     rc = outblock->cmd_count;                 /* flush resets */
577     if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
578     }
579
580   Ustrncpy(outblock->ptr, gs.s, gs.ptr);
581   outblock->ptr += gs.ptr;
582   outblock->cmd_count++;
583   gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
584
585   /* We want to hide the actual data sent in AUTH transactions from reflections
586   and logs. While authenticating, a flag is set in the outblock to enable this.
587   The AUTH command itself gets any data flattened. Other lines are flattened
588   completely. */
589
590   if (outblock->authenticating)
591     {
592     uschar *p = big_buffer;
593     if (Ustrncmp(big_buffer, "AUTH ", 5) == 0)
594       {
595       p += 5;
596       while (isspace(*p)) p++;
597       while (!isspace(*p)) p++;
598       while (isspace(*p)) p++;
599       }
600     while (*p != 0) *p++ = '*';
601     }
602
603   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> %s\n", big_buffer);
604   }
605
606 if (mode != SCMD_BUFFER)
607   {
608   rc += outblock->cmd_count;                /* flush resets */
609   if (!flush_buffer(outblock, mode)) return -1;
610   }
611
612 return rc;
613 }
614
615
616
617 /*************************************************
618 *          Read one line of SMTP response        *
619 *************************************************/
620
621 /* This function reads one line of SMTP response from the server host. This may
622 not be a complete response - it could be just part of a multiline response. We
623 have to use a buffer for incoming packets, because when pipelining or using
624 LMTP, there may well be more than one response in a single packet. This
625 function is called only from the one that follows.
626
627 Arguments:
628   inblock   the SMTP input block (contains holding buffer, socket, etc.)
629   buffer    where to put the line
630   size      space available for the line
631   timelimit deadline for reading the lime, seconds past epoch
632
633 Returns:    length of a line that has been put in the buffer
634             -1 otherwise, with errno set
635 */
636
637 static int
638 read_response_line(smtp_inblock *inblock, uschar *buffer, int size, time_t timelimit)
639 {
640 uschar *p = buffer;
641 uschar *ptr = inblock->ptr;
642 uschar *ptrend = inblock->ptrend;
643 client_conn_ctx * cctx = inblock->cctx;
644
645 /* Loop for reading multiple packets or reading another packet after emptying
646 a previously-read one. */
647
648 for (;;)
649   {
650   int rc;
651
652   /* If there is data in the input buffer left over from last time, copy
653   characters from it until the end of a line, at which point we can return,
654   having removed any whitespace (which will include CR) at the end of the line.
655   The rules for SMTP say that lines end in CRLF, but there are have been cases
656   of hosts using just LF, and other MTAs are reported to handle this, so we
657   just look for LF. If we run out of characters before the end of a line,
658   carry on to read the next incoming packet. */
659
660   while (ptr < ptrend)
661     {
662     int c = *ptr++;
663     if (c == '\n')
664       {
665       while (p > buffer && isspace(p[-1])) p--;
666       *p = 0;
667       inblock->ptr = ptr;
668       return p - buffer;
669       }
670     *p++ = c;
671     if (--size < 4)
672       {
673       *p = 0;                     /* Leave malformed line for error message */
674       errno = ERRNO_SMTPFORMAT;
675       return -1;
676       }
677     }
678
679   /* Need to read a new input packet. */
680
681   if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timelimit)) <= 0)
682     {
683     DEBUG(D_deliver|D_transport|D_acl|D_v)
684       debug_printf_indent(errno ? "  SMTP(%s)<<\n" : "  SMTP(closed)<<\n",
685         strerror(errno));
686     break;
687     }
688
689   /* Another block of data has been successfully read. Set up the pointers
690   and let the loop continue. */
691
692   ptrend = inblock->ptrend = inblock->buffer + rc;
693   ptr = inblock->buffer;
694   DEBUG(D_transport|D_acl) debug_printf_indent("read response data: size=%d\n", rc);
695   }
696
697 /* Get here if there has been some kind of recv() error; errno is set, but we
698 ensure that the result buffer is empty before returning. */
699
700 *buffer = 0;
701 return -1;
702 }
703
704
705
706
707
708 /*************************************************
709 *              Read SMTP response                *
710 *************************************************/
711
712 /* This function reads an SMTP response with a timeout, and returns the
713 response in the given buffer, as a string. A multiline response will contain
714 newline characters between the lines. The function also analyzes the first
715 digit of the reply code and returns FALSE if it is not acceptable. FALSE is
716 also returned after a reading error. In this case buffer[0] will be zero, and
717 the error code will be in errno.
718
719 Arguments:
720   sx        the SMTP connection (contains input block with holding buffer,
721                 socket, etc.)
722   buffer    where to put the response
723   size      the size of the buffer
724   okdigit   the expected first digit of the response
725   timeout   the timeout to use, in seconds
726
727 Returns:    TRUE if a valid, non-error response was received; else FALSE
728 */
729 /*XXX could move to smtp transport; no other users */
730
731 BOOL
732 smtp_read_response(void * sx0, uschar * buffer, int size, int okdigit,
733    int timeout)
734 {
735 smtp_context * sx = sx0;
736 uschar * ptr = buffer;
737 int count = 0;
738 time_t timelimit = time(NULL) + timeout;
739
740 errno = 0;  /* Ensure errno starts out zero */
741
742 #ifndef DISABLE_PIPE_CONNECT
743 if (sx->pending_BANNER || sx->pending_EHLO)
744   {
745   int rc;
746   if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
747     {
748     DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n");
749     buffer[0] = '\0';
750     if (rc == DEFER) errno = ERRNO_TLSFAILURE;
751     return FALSE;
752     }
753   }
754 #endif
755
756 /* This is a loop to read and concatenate the lines that make up a multi-line
757 response. */
758
759 for (;;)
760   {
761   if ((count = read_response_line(&sx->inblock, ptr, size, timelimit)) < 0)
762     return FALSE;
763
764   HDEBUG(D_transport|D_acl|D_v)
765     debug_printf_indent("  %s %s\n", ptr == buffer ? "SMTP<<" : "      ", ptr);
766
767   /* Check the format of the response: it must start with three digits; if
768   these are followed by a space or end of line, the response is complete. If
769   they are followed by '-' this is a multi-line response and we must look for
770   another line until the final line is reached. The only use made of multi-line
771   responses is to pass them back as error messages. We therefore just
772   concatenate them all within the buffer, which should be large enough to
773   accept any reasonable number of lines. */
774
775   if (count < 3 ||
776      !isdigit(ptr[0]) ||
777      !isdigit(ptr[1]) ||
778      !isdigit(ptr[2]) ||
779      (ptr[3] != '-' && ptr[3] != ' ' && ptr[3] != 0))
780     {
781     errno = ERRNO_SMTPFORMAT;    /* format error */
782     return FALSE;
783     }
784
785   /* If the line we have just read is a terminal line, line, we are done.
786   Otherwise more data has to be read. */
787
788   if (ptr[3] != '-') break;
789
790   /* Move the reading pointer upwards in the buffer and insert \n between the
791   components of a multiline response. Space is left for this by read_response_
792   line(). */
793
794   ptr += count;
795   *ptr++ = '\n';
796   size -= count + 1;
797   }
798
799 #ifdef TCP_FASTOPEN
800   tfo_out_check(sx->cctx.sock);
801 #endif
802
803 /* Return a value that depends on the SMTP return code. On some systems a
804 non-zero value of errno has been seen at this point, so ensure it is zero,
805 because the caller of this function looks at errno when FALSE is returned, to
806 distinguish between an unexpected return code and other errors such as
807 timeouts, lost connections, etc. */
808
809 errno = 0;
810 return buffer[0] == okdigit;
811 }
812
813 /* End of smtp_out.c */
814 /* vi: aw ai sw=2
815 */