Specific check for null pointer
[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 client_conn_ctx * cctx;
528
529 HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
530   more ? " (more expected)" : "");
531
532 if (!(cctx = outblock->cctx))
533   {
534   log_write(0, LOG_MAIN|LOG_PANIC, "null conn-context pointer");
535   errno = 0;
536   return FALSE;
537   }
538
539 #ifndef DISABLE_TLS
540 if (cctx->tls_ctx)              /*XXX have seen a null cctx here, rvfy sending QUIT, hence check above */
541   rc = tls_write(cctx->tls_ctx, outblock->buffer, n, more);
542 else
543 #endif
544
545   {
546   if (outblock->conn_args)
547     {
548     blob early_data = { .data = outblock->buffer, .len = n };
549
550     /* We ignore the more-flag if we're doing a connect with early-data, which
551     means we won't get BDAT+data. A pity, but wise due to the idempotency
552     requirement: TFO with data can, in rare cases, replay the data to the
553     receiver. */
554
555     if (  (cctx->sock = smtp_connect(outblock->conn_args, &early_data))
556        < 0)
557       return FALSE;
558     outblock->conn_args = NULL;
559     rc = n;
560     }
561   else
562     {
563     rc = send(cctx->sock, outblock->buffer, n,
564 #ifdef MSG_MORE
565               more ? MSG_MORE : 0
566 #else
567               0
568 #endif
569              );
570
571 #if defined(__linux__)
572     /* This is a workaround for a current linux kernel bug: as of
573     5.6.8-200.fc31.x86_64  small (<MSS) writes get delayed by about 200ms,
574     This is despite NODELAY being active.
575     https://bugzilla.redhat.com/show_bug.cgi?id=1803806 */
576
577     if (!more)
578       setsockopt(cctx->sock, IPPROTO_TCP, TCP_CORK, &off, sizeof(off));
579 #endif
580     }
581   }
582
583 if (rc <= 0)
584   {
585   HDEBUG(D_transport|D_acl) debug_printf_indent("send failed: %s\n", strerror(errno));
586   return FALSE;
587   }
588
589 outblock->ptr = outblock->buffer;
590 outblock->cmd_count = 0;
591 return TRUE;
592 }
593
594
595
596 /*************************************************
597 *             Write SMTP command                 *
598 *************************************************/
599
600 /* The formatted command is left in big_buffer so that it can be reflected in
601 any error message.
602
603 Arguments:
604   sx         SMTP connection, contains buffer for pipelining, and socket
605   mode       buffer, write-with-more-likely, write
606   format     a format, starting with one of
607              of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT.
608              If NULL, flush pipeline buffer only.
609   ...        data for the format
610
611 Returns:     0 if command added to pipelining buffer, with nothing transmitted
612             +n if n commands transmitted (may still have buffered the new one)
613             -1 on error, with errno set
614 */
615
616 int
617 smtp_write_command(void * sx, int mode, const char *format, ...)
618 {
619 smtp_outblock * outblock = &((smtp_context *)sx)->outblock;
620 int rc = 0;
621
622 if (format)
623   {
624   gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
625   va_list ap;
626
627   /* Use taint-unchecked routines for writing into big_buffer, trusting that
628   we'll never expand the results.  Actually, the error-message use - leaving
629   the results in big_buffer for potential later use - is uncomfortably distant.
630   XXX Would be better to assume all smtp commands are short, use normal pool
631   alloc rather than big_buffer, and another global for the data-for-error. */
632
633   va_start(ap, format);
634   if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap))
635     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
636       "SMTP");
637   va_end(ap);
638   string_from_gstring(&gs);
639
640   if (gs.ptr > outblock->buffersize)
641     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
642       "SMTP");
643
644   if (gs.ptr > outblock->buffersize - (outblock->ptr - outblock->buffer))
645     {
646     rc = outblock->cmd_count;                 /* flush resets */
647     if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
648     }
649
650   Ustrncpy(outblock->ptr, gs.s, gs.ptr);
651   outblock->ptr += gs.ptr;
652   outblock->cmd_count++;
653   gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
654
655   /* We want to hide the actual data sent in AUTH transactions from reflections
656   and logs. While authenticating, a flag is set in the outblock to enable this.
657   The AUTH command itself gets any data flattened. Other lines are flattened
658   completely. */
659
660   if (outblock->authenticating)
661     {
662     uschar *p = big_buffer;
663     if (Ustrncmp(big_buffer, "AUTH ", 5) == 0)
664       {
665       p += 5;
666       while (isspace(*p)) p++;
667       while (!isspace(*p)) p++;
668       while (isspace(*p)) p++;
669       }
670     while (*p) *p++ = '*';
671     }
672
673   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP%c> %s\n",
674     mode == SCMD_BUFFER ? '|' : mode == SCMD_MORE ? '+' : '>',
675     big_buffer);
676   }
677
678 if (mode != SCMD_BUFFER)
679   {
680   rc += outblock->cmd_count;                /* flush resets */
681   if (!flush_buffer(outblock, mode)) return -1;
682   }
683
684 return rc;
685 }
686
687
688
689 /*************************************************
690 *          Read one line of SMTP response        *
691 *************************************************/
692
693 /* This function reads one line of SMTP response from the server host. This may
694 not be a complete response - it could be just part of a multiline response. We
695 have to use a buffer for incoming packets, because when pipelining or using
696 LMTP, there may well be more than one response in a single packet. This
697 function is called only from the one that follows.
698
699 Arguments:
700   inblock   the SMTP input block (contains holding buffer, socket, etc.)
701   buffer    where to put the line
702   size      space available for the line
703   timelimit deadline for reading the lime, seconds past epoch
704
705 Returns:    length of a line that has been put in the buffer
706             -1 otherwise, with errno set, and inblock->ptr adjusted
707 */
708
709 static int
710 read_response_line(smtp_inblock *inblock, uschar *buffer, int size, time_t timelimit)
711 {
712 uschar *p = buffer;
713 uschar *ptr = inblock->ptr;
714 uschar *ptrend = inblock->ptrend;
715 client_conn_ctx * cctx = inblock->cctx;
716
717 /* Loop for reading multiple packets or reading another packet after emptying
718 a previously-read one. */
719
720 for (;;)
721   {
722   int rc;
723
724   /* If there is data in the input buffer left over from last time, copy
725   characters from it until the end of a line, at which point we can return,
726   having removed any whitespace (which will include CR) at the end of the line.
727   The rules for SMTP say that lines end in CRLF, but there are have been cases
728   of hosts using just LF, and other MTAs are reported to handle this, so we
729   just look for LF. If we run out of characters before the end of a line,
730   carry on to read the next incoming packet. */
731
732   while (ptr < ptrend)
733     {
734     int c = *ptr++;
735     if (c == '\n')
736       {
737       while (p > buffer && isspace(p[-1])) p--;
738       *p = 0;
739       inblock->ptr = ptr;
740       return p - buffer;
741       }
742     *p++ = c;
743     if (--size < 4)
744       {
745       *p = 0;                     /* Leave malformed line for error message */
746       errno = ERRNO_SMTPFORMAT;
747       inblock->ptr = ptr;
748       return -1;
749       }
750     }
751
752   /* Need to read a new input packet. */
753
754   if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timelimit)) <= 0)
755     {
756     DEBUG(D_deliver|D_transport|D_acl|D_v)
757       debug_printf_indent(errno ? "  SMTP(%s)<<\n" : "  SMTP(closed)<<\n",
758         strerror(errno));
759     break;
760     }
761
762   /* Another block of data has been successfully read. Set up the pointers
763   and let the loop continue. */
764
765   ptrend = inblock->ptrend = inblock->buffer + rc;
766   ptr = inblock->buffer;
767   DEBUG(D_transport|D_acl) debug_printf_indent("read response data: size=%d\n", rc);
768   }
769
770 /* Get here if there has been some kind of recv() error; errno is set, but we
771 ensure that the result buffer is empty before returning. */
772
773 inblock->ptr = inblock->ptrend = inblock->buffer;
774 *buffer = 0;
775 return -1;
776 }
777
778
779
780
781
782 /*************************************************
783 *              Read SMTP response                *
784 *************************************************/
785
786 /* This function reads an SMTP response with a timeout, and returns the
787 response in the given buffer, as a string. A multiline response will contain
788 newline characters between the lines. The function also analyzes the first
789 digit of the reply code and returns FALSE if it is not acceptable. FALSE is
790 also returned after a reading error. In this case buffer[0] will be zero, and
791 the error code will be in errno.
792
793 Arguments:
794   sx        the SMTP connection (contains input block with holding buffer,
795                 socket, etc.)
796   buffer    where to put the response
797   size      the size of the buffer
798   okdigit   the expected first digit of the response
799   timeout   the timeout to use, in seconds
800
801 Returns:    TRUE if a valid, non-error response was received; else FALSE
802 */
803 /*XXX could move to smtp transport; no other users */
804
805 BOOL
806 smtp_read_response(void * sx0, uschar * buffer, int size, int okdigit,
807    int timeout)
808 {
809 smtp_context * sx = sx0;
810 uschar * ptr = buffer;
811 int count = 0;
812 time_t timelimit = time(NULL) + timeout;
813
814 errno = 0;  /* Ensure errno starts out zero */
815
816 #ifndef DISABLE_PIPE_CONNECT
817 if (sx->pending_BANNER || sx->pending_EHLO)
818   {
819   int rc;
820   if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
821     {
822     DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n");
823     buffer[0] = '\0';
824     if (rc == DEFER) errno = ERRNO_TLSFAILURE;
825     return FALSE;
826     }
827   }
828 #endif
829
830 /* This is a loop to read and concatenate the lines that make up a multi-line
831 response. */
832
833 for (;;)
834   {
835   if ((count = read_response_line(&sx->inblock, ptr, size, timelimit)) < 0)
836     return FALSE;
837
838   HDEBUG(D_transport|D_acl|D_v)
839     debug_printf_indent("  %s %s\n", ptr == buffer ? "SMTP<<" : "      ", ptr);
840
841   /* Check the format of the response: it must start with three digits; if
842   these are followed by a space or end of line, the response is complete. If
843   they are followed by '-' this is a multi-line response and we must look for
844   another line until the final line is reached. The only use made of multi-line
845   responses is to pass them back as error messages. We therefore just
846   concatenate them all within the buffer, which should be large enough to
847   accept any reasonable number of lines. */
848
849   if (count < 3 ||
850      !isdigit(ptr[0]) ||
851      !isdigit(ptr[1]) ||
852      !isdigit(ptr[2]) ||
853      (ptr[3] != '-' && ptr[3] != ' ' && ptr[3] != 0))
854     {
855     errno = ERRNO_SMTPFORMAT;    /* format error */
856     return FALSE;
857     }
858
859   /* If the line we have just read is a terminal line, line, we are done.
860   Otherwise more data has to be read. */
861
862   if (ptr[3] != '-') break;
863
864   /* Move the reading pointer upwards in the buffer and insert \n between the
865   components of a multiline response. Space is left for this by read_response_
866   line(). */
867
868   ptr += count;
869   *ptr++ = '\n';
870   size -= count + 1;
871   }
872
873 #ifdef TCP_FASTOPEN
874 tfo_out_check(sx->cctx.sock);
875 #endif
876
877 /* Return a value that depends on the SMTP return code. On some systems a
878 non-zero value of errno has been seen at this point, so ensure it is zero,
879 because the caller of this function looks at errno when FALSE is returned, to
880 distinguish between an unexpected return code and other errors such as
881 timeouts, lost connections, etc. */
882
883 errno = 0;
884 return buffer[0] == okdigit;
885 }
886
887 /* End of smtp_out.c */
888 /* vi: aw ai sw=2
889 */