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