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