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