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