Debug: indent lowlevel connect result
[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 193 break;
56dbf856
JH
194
195 default: break; /* compiler quietening */
ee8b8090 196 }
1ccd5f67
JH
197# endif
198}
199#endif
200
201
0ab63f3d 202/* Arguments as for smtp_connect(), plus
b536a578
JH
203 early_data if non-NULL, idenmpotent data to be sent -
204 preferably in the TCP SYN segment
0ab63f3d
JH
205
206Returns: connected socket number, or -1 with errno set
207*/
208
059ec3d9 209int
7eb6c37c 210smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface,
0ab63f3d 211 transport_instance * tb, int timeout, const blob * early_data)
059ec3d9 212{
7eb6c37c
JH
213smtp_transport_options_block * ob =
214 (smtp_transport_options_block *)tb->options_block;
215const uschar * dscp = ob->dscp;
9e4f5962
PP
216int dscp_value;
217int dscp_level;
218int dscp_option;
059ec3d9 219int sock;
7eb6c37c 220int save_errno = 0;
10ac8d7f 221const blob * fastopen_blob = NULL;
1ccd5f67 222
059ec3d9 223
0cbf2b82 224#ifndef DISABLE_EVENT
7eb6c37c
JH
225deliver_host_address = host->address;
226deliver_host_port = port;
227if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1;
a7538db1
JH
228#endif
229
059ec3d9
PH
230if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1;
231
232/* Set TCP_NODELAY; Exim does its own buffering. */
233
d4ff61d1
JH
234if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, US &on, sizeof(on)))
235 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 236 debug_printf_indent("failed to set NODELAY: %s ", strerror(errno));
059ec3d9 237
9e4f5962
PP
238/* Set DSCP value, if we can. For now, if we fail to set the value, we don't
239bomb out, just log it and continue in default traffic class. */
240
241if (dscp && dscp_lookup(dscp, host_af, &dscp_level, &dscp_option, &dscp_value))
242 {
243 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 244 debug_printf_indent("DSCP \"%s\"=%x ", dscp, dscp_value);
9e4f5962
PP
245 if (setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)) < 0)
246 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 247 debug_printf_indent("failed to set DSCP: %s ", strerror(errno));
9e4f5962
PP
248 /* If the kernel supports IPv4 and IPv6 on an IPv6 socket, we need to set the
249 option for both; ignore failures here */
250 if (host_af == AF_INET6 &&
251 dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value))
9e4f5962 252 (void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value));
9e4f5962
PP
253 }
254
059ec3d9
PH
255/* Bind to a specific interface if requested. Caller must ensure the interface
256is the same type (IPv4 or IPv6) as the outgoing address. */
257
7eb6c37c 258if (interface && ip_bind(sock, host_af, interface, 0) < 0)
059ec3d9
PH
259 {
260 save_errno = errno;
261 HDEBUG(D_transport|D_acl|D_v)
e1d04f48 262 debug_printf_indent("unable to bind outgoing SMTP call to %s: %s", interface,
059ec3d9
PH
263 strerror(errno));
264 }
265
266/* Connect to the remote host, and add keepalive to the socket before returning
0ab63f3d 267it, if requested. If the build supports TFO, request it - and if the caller
ee8b8090
JH
268requested some early-data then include that in the TFO request. If there is
269early-data but no TFO support, send it after connecting. */
059ec3d9 270
0ab63f3d
JH
271else
272 {
0ab63f3d 273#ifdef TCP_FASTOPEN
3fb3231c 274 if (verify_check_given_host(CUSS &ob->hosts_try_fastopen, host) == OK)
10ac8d7f 275 fastopen_blob = early_data ? early_data : &tcp_fastopen_nodata;
0ab63f3d
JH
276#endif
277
10ac8d7f 278 if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0)
0ab63f3d 279 save_errno = errno;
10ac8d7f 280 else if (early_data && !fastopen_blob && early_data->data && early_data->len)
ee8b8090
JH
281 {
282 HDEBUG(D_transport|D_acl|D_v)
283 debug_printf("sending %ld nonTFO early-data\n", (long)early_data->len);
284
285#ifdef TCP_QUICKACK
286 (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
287#endif
0ab63f3d
JH
288 if (send(sock, early_data->data, early_data->len, 0) < 0)
289 save_errno = errno;
ee8b8090 290 }
0ab63f3d 291 }
059ec3d9
PH
292
293/* Either bind() or connect() failed */
294
295if (save_errno != 0)
296 {
8e669ac1 297 HDEBUG(D_transport|D_acl|D_v)
4deaf07d 298 {
adb21834 299 debug_printf_indent(" failed: %s", CUstrerror(save_errno));
8e669ac1 300 if (save_errno == ETIMEDOUT)
4deaf07d 301 debug_printf(" (timeout=%s)", readconf_printtime(timeout));
8e669ac1
PH
302 debug_printf("\n");
303 }
f1e894f3 304 (void)close(sock);
059ec3d9
PH
305 errno = save_errno;
306 return -1;
307 }
308
ee8b8090 309/* Both bind() and connect() succeeded, and any early-data */
059ec3d9
PH
310
311else
312 {
41c7c167
PH
313 union sockaddr_46 interface_sock;
314 EXIM_SOCKLEN_T size = sizeof(interface_sock);
ee8b8090 315
adb21834 316 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" connected\n");
41c7c167
PH
317 if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0)
318 sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
319 else
320 {
321 log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
322 "getsockname() failed: %s", strerror(errno));
323 close(sock);
324 return -1;
325 }
ee8b8090 326
7eb6c37c 327 if (ob->keepalive) ip_keepalive(sock, host->address, TRUE);
1ccd5f67 328#ifdef TCP_FASTOPEN
ee8b8090 329 tfo_out_check(sock);
1ccd5f67 330#endif
059ec3d9
PH
331 return sock;
332 }
333}
334
58c30e47
JH
335
336
337
338
339void
340smtp_port_for_connect(host_item * host, int port)
341{
342if (host->port != PORT_NONE)
343 {
344 HDEBUG(D_transport|D_acl|D_v)
345 debug_printf_indent("Transport port=%d replaced by host-specific port=%d\n", port,
346 host->port);
347 port = host->port;
348 }
349else host->port = port; /* Set the port actually used */
350}
351
352
7eb6c37c
JH
353/*************************************************
354* Connect to remote host *
355*************************************************/
356
357/* Create a socket, and connect it to a remote host. IPv6 addresses are
358detected by checking for a colon in the address. AF_INET6 is defined even on
359non-IPv6 systems, to enable the code to be less messy. However, on such systems
360host->address will always be an IPv4 address.
361
7eb6c37c 362Arguments:
ee8b8090
JH
363 sc details for making connection: host, af, interface, transport
364 early_data if non-NULL, data to be sent - preferably in the TCP SYN segment
7eb6c37c
JH
365
366Returns: connected socket number, or -1 with errno set
367*/
368
369int
ee8b8090 370smtp_connect(smtp_connect_args * sc, const blob * early_data)
7eb6c37c 371{
ee8b8090
JH
372int port = sc->host->port;
373smtp_transport_options_block * ob = sc->ob;
7eb6c37c 374
ee8b8090 375callout_address = string_sprintf("[%s]:%d", sc->host->address, port);
055e2cb4 376
7eb6c37c
JH
377HDEBUG(D_transport|D_acl|D_v)
378 {
379 uschar * s = US" ";
ee8b8090 380 if (sc->interface) s = string_sprintf(" from %s ", sc->interface);
f0989ec0 381#ifdef SUPPORT_SOCKS
7eb6c37c
JH
382 if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s);
383#endif
ee8b8090 384 debug_printf_indent("Connecting to %s %s%s... ", sc->host->name, callout_address, s);
7eb6c37c
JH
385 }
386
387/* Create and connect the socket */
388
f0989ec0 389#ifdef SUPPORT_SOCKS
7eb6c37c 390if (ob->socks_proxy)
ee8b8090
JH
391 {
392 int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface,
393 sc->tblock, ob->connect_timeout);
394
395 if (sock >= 0)
396 {
397 if (early_data && early_data->data && early_data->len)
398 if (send(sock, early_data->data, early_data->len, 0) < 0)
399 {
400 int save_errno = errno;
401 HDEBUG(D_transport|D_acl|D_v)
402 {
403 debug_printf_indent("failed: %s", CUstrerror(save_errno));
404 if (save_errno == ETIMEDOUT)
405 debug_printf(" (timeout=%s)", readconf_printtime(ob->connect_timeout));
406 debug_printf("\n");
407 }
408 (void)close(sock);
409 sock = -1;
410 errno = save_errno;
411 }
412 }
413 return sock;
414 }
7eb6c37c
JH
415#endif
416
ee8b8090
JH
417return smtp_sock_connect(sc->host, sc->host_af, port, sc->interface,
418 sc->tblock, ob->connect_timeout, early_data);
7eb6c37c
JH
419}
420
059ec3d9
PH
421
422/*************************************************
423* Flush outgoing command buffer *
424*************************************************/
425
426/* This function is called only from smtp_write_command() below. It flushes
427the buffer of outgoing commands. There is more than one in the buffer only when
428pipelining.
429
430Argument:
431 outblock the SMTP output block
925ac8e4 432 mode further data expected, or plain
059ec3d9
PH
433
434Returns: TRUE if OK, FALSE on error, with errno set
435*/
436
437static BOOL
4e910c01 438flush_buffer(smtp_outblock * outblock, int mode)
059ec3d9
PH
439{
440int rc;
58fc5fb2 441int n = outblock->ptr - outblock->buffer;
925ac8e4 442BOOL more = mode == SCMD_MORE;
059ec3d9 443
4e910c01 444HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
925ac8e4 445 more ? " (more expected)" : "");
4e910c01 446
01603eec 447#ifndef DISABLE_TLS
74f1a423
JH
448if (outblock->cctx->tls_ctx)
449 rc = tls_write(outblock->cctx->tls_ctx, outblock->buffer, n, more);
059ec3d9
PH
450else
451#endif
ee8b8090
JH
452
453 {
454 if (outblock->conn_args)
455 {
456 blob early_data = { .data = outblock->buffer, .len = n };
457
458 /* We ignore the more-flag if we're doing a connect with early-data, which
459 means we won't get BDAT+data. A pity, but wise due to the idempotency
460 requirement: TFO with data can, in rare cases, replay the data to the
461 receiver. */
462
463 if ( (outblock->cctx->sock = smtp_connect(outblock->conn_args, &early_data))
464 < 0)
465 return FALSE;
466 outblock->conn_args = NULL;
467 rc = n;
468 }
469 else
470
471 rc = send(outblock->cctx->sock, outblock->buffer, n,
4e910c01 472#ifdef MSG_MORE
ee8b8090 473 more ? MSG_MORE : 0
4e910c01 474#else
ee8b8090 475 0
4e910c01 476#endif
ee8b8090
JH
477 );
478 }
059ec3d9 479
059ec3d9
PH
480if (rc <= 0)
481 {
e1d04f48 482 HDEBUG(D_transport|D_acl) debug_printf_indent("send failed: %s\n", strerror(errno));
059ec3d9
PH
483 return FALSE;
484 }
485
486outblock->ptr = outblock->buffer;
487outblock->cmd_count = 0;
488return TRUE;
489}
490
491
492
493/*************************************************
494* Write SMTP command *
495*************************************************/
496
497/* The formatted command is left in big_buffer so that it can be reflected in
498any error message.
499
500Arguments:
251b9eb4 501 sx SMTP connection, contains buffer for pipelining, and socket
4e910c01 502 mode buffer, write-with-more-likely, write
059ec3d9
PH
503 format a format, starting with one of
504 of HELO, MAIL FROM, RCPT TO, DATA, ".", or QUIT.
6d5c916c 505 If NULL, flush pipeline buffer only.
059ec3d9
PH
506 ... data for the format
507
508Returns: 0 if command added to pipelining buffer, with nothing transmitted
509 +n if n commands transmitted (may still have buffered the new one)
510 -1 on error, with errno set
511*/
512
513int
251b9eb4 514smtp_write_command(void * sx, int mode, const char *format, ...)
059ec3d9 515{
251b9eb4 516smtp_outblock * outblock = &((smtp_context *)sx)->outblock;
059ec3d9 517int rc = 0;
059ec3d9 518
6d5c916c 519if (format)
059ec3d9 520 {
d12746bc
JH
521 gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
522 va_list ap;
523
6d5c916c 524 va_start(ap, format);
d12746bc 525 if (!string_vformat(&gs, FALSE, CS format, ap))
6d5c916c
JH
526 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
527 "SMTP");
528 va_end(ap);
d12746bc 529 string_from_gstring(&gs);
6d5c916c 530
d12746bc 531 if (gs.ptr > outblock->buffersize)
6d5c916c
JH
532 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing "
533 "SMTP");
534
d12746bc 535 if (gs.ptr > outblock->buffersize - (outblock->ptr - outblock->buffer))
6d5c916c
JH
536 {
537 rc = outblock->cmd_count; /* flush resets */
4e910c01 538 if (!flush_buffer(outblock, SCMD_FLUSH)) return -1;
6d5c916c 539 }
059ec3d9 540
d12746bc
JH
541 Ustrncpy(CS outblock->ptr, gs.s, gs.ptr);
542 outblock->ptr += gs.ptr;
6d5c916c 543 outblock->cmd_count++;
d12746bc 544 gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */
059ec3d9 545
6d5c916c
JH
546 /* We want to hide the actual data sent in AUTH transactions from reflections
547 and logs. While authenticating, a flag is set in the outblock to enable this.
548 The AUTH command itself gets any data flattened. Other lines are flattened
549 completely. */
059ec3d9 550
6d5c916c 551 if (outblock->authenticating)
059ec3d9 552 {
6d5c916c
JH
553 uschar *p = big_buffer;
554 if (Ustrncmp(big_buffer, "AUTH ", 5) == 0)
555 {
556 p += 5;
557 while (isspace(*p)) p++;
558 while (!isspace(*p)) p++;
559 while (isspace(*p)) p++;
560 }
561 while (*p != 0) *p++ = '*';
059ec3d9 562 }
059ec3d9 563
e1d04f48 564 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> %s\n", big_buffer);
6d5c916c 565 }
059ec3d9 566
4e910c01 567if (mode != SCMD_BUFFER)
059ec3d9
PH
568 {
569 rc += outblock->cmd_count; /* flush resets */
4e910c01 570 if (!flush_buffer(outblock, mode)) return -1;
059ec3d9
PH
571 }
572
573return rc;
574}
575
576
577
578/*************************************************
579* Read one line of SMTP response *
580*************************************************/
581
582/* This function reads one line of SMTP response from the server host. This may
583not be a complete response - it could be just part of a multiline response. We
584have to use a buffer for incoming packets, because when pipelining or using
585LMTP, there may well be more than one response in a single packet. This
586function is called only from the one that follows.
587
588Arguments:
589 inblock the SMTP input block (contains holding buffer, socket, etc.)
590 buffer where to put the line
591 size space available for the line
0a5441fc 592 timelimit deadline for reading the lime, seconds past epoch
059ec3d9
PH
593
594Returns: length of a line that has been put in the buffer
595 -1 otherwise, with errno set
596*/
597
598static int
0a5441fc 599read_response_line(smtp_inblock *inblock, uschar *buffer, int size, time_t timelimit)
059ec3d9
PH
600{
601uschar *p = buffer;
602uschar *ptr = inblock->ptr;
603uschar *ptrend = inblock->ptrend;
74f1a423 604client_conn_ctx * cctx = inblock->cctx;
059ec3d9
PH
605
606/* Loop for reading multiple packets or reading another packet after emptying
607a previously-read one. */
608
609for (;;)
610 {
611 int rc;
612
613 /* If there is data in the input buffer left over from last time, copy
614 characters from it until the end of a line, at which point we can return,
615 having removed any whitespace (which will include CR) at the end of the line.
616 The rules for SMTP say that lines end in CRLF, but there are have been cases
617 of hosts using just LF, and other MTAs are reported to handle this, so we
618 just look for LF. If we run out of characters before the end of a line,
619 carry on to read the next incoming packet. */
620
621 while (ptr < ptrend)
622 {
623 int c = *ptr++;
624 if (c == '\n')
625 {
626 while (p > buffer && isspace(p[-1])) p--;
627 *p = 0;
628 inblock->ptr = ptr;
629 return p - buffer;
630 }
631 *p++ = c;
632 if (--size < 4)
633 {
634 *p = 0; /* Leave malformed line for error message */
635 errno = ERRNO_SMTPFORMAT;
636 return -1;
637 }
638 }
639
640 /* Need to read a new input packet. */
641
0a5441fc 642 if((rc = ip_recv(cctx, inblock->buffer, inblock->buffersize, timelimit)) <= 0)
de6273b4 643 {
eebcfa1c 644 DEBUG(D_deliver|D_transport|D_acl|D_v)
170f4904
JH
645 debug_printf_indent(errno ? " SMTP(%s)<<\n" : " SMTP(closed)<<\n",
646 strerror(errno));
de6273b4
JH
647 break;
648 }
059ec3d9
PH
649
650 /* Another block of data has been successfully read. Set up the pointers
651 and let the loop continue. */
652
653 ptrend = inblock->ptrend = inblock->buffer + rc;
654 ptr = inblock->buffer;
e1d04f48 655 DEBUG(D_transport|D_acl) debug_printf_indent("read response data: size=%d\n", rc);
059ec3d9
PH
656 }
657
658/* Get here if there has been some kind of recv() error; errno is set, but we
659ensure that the result buffer is empty before returning. */
660
661*buffer = 0;
662return -1;
663}
664
665
666
667
668
669/*************************************************
670* Read SMTP response *
671*************************************************/
672
673/* This function reads an SMTP response with a timeout, and returns the
674response in the given buffer, as a string. A multiline response will contain
675newline characters between the lines. The function also analyzes the first
676digit of the reply code and returns FALSE if it is not acceptable. FALSE is
677also returned after a reading error. In this case buffer[0] will be zero, and
678the error code will be in errno.
679
680Arguments:
251b9eb4
JH
681 sx the SMTP connection (contains input block with holding buffer,
682 socket, etc.)
059ec3d9
PH
683 buffer where to put the response
684 size the size of the buffer
685 okdigit the expected first digit of the response
2760b518 686 timeout the timeout to use, in seconds
059ec3d9
PH
687
688Returns: TRUE if a valid, non-error response was received; else FALSE
689*/
ee8b8090 690/*XXX could move to smtp transport; no other users */
059ec3d9
PH
691
692BOOL
fc243e94 693smtp_read_response(void * sx0, uschar * buffer, int size, int okdigit,
059ec3d9
PH
694 int timeout)
695{
ee8b8090 696smtp_context * sx = sx0;
fc243e94 697uschar * ptr = buffer;
dca6d121 698int count = 0;
0a5441fc 699time_t timelimit = time(NULL) + timeout;
059ec3d9
PH
700
701errno = 0; /* Ensure errno starts out zero */
702
4e48d56c 703#ifdef SUPPORT_PIPE_CONNECT
ee8b8090 704if (sx->pending_BANNER || sx->pending_EHLO)
dca6d121
JH
705 {
706 int rc;
fc243e94 707 if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
ee8b8090
JH
708 {
709 DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n");
fc243e94
JH
710 buffer[0] = '\0';
711 if (rc == DEFER) errno = ERRNO_TLSFAILURE;
ee8b8090
JH
712 return FALSE;
713 }
dca6d121 714 }
ee8b8090
JH
715#endif
716
4c04137d 717/* This is a loop to read and concatenate the lines that make up a multi-line
059ec3d9
PH
718response. */
719
720for (;;)
721 {
0a5441fc 722 if ((count = read_response_line(&sx->inblock, ptr, size, timelimit)) < 0)
059ec3d9
PH
723 return FALSE;
724
725 HDEBUG(D_transport|D_acl|D_v)
ee8b8090 726 debug_printf_indent(" %s %s\n", ptr == buffer ? "SMTP<<" : " ", ptr);
059ec3d9
PH
727
728 /* Check the format of the response: it must start with three digits; if
729 these are followed by a space or end of line, the response is complete. If
730 they are followed by '-' this is a multi-line response and we must look for
731 another line until the final line is reached. The only use made of multi-line
732 responses is to pass them back as error messages. We therefore just
733 concatenate them all within the buffer, which should be large enough to
734 accept any reasonable number of lines. */
735
736 if (count < 3 ||
737 !isdigit(ptr[0]) ||
738 !isdigit(ptr[1]) ||
739 !isdigit(ptr[2]) ||
740 (ptr[3] != '-' && ptr[3] != ' ' && ptr[3] != 0))
741 {
742 errno = ERRNO_SMTPFORMAT; /* format error */
743 return FALSE;
744 }
745
746 /* If the line we have just read is a terminal line, line, we are done.
747 Otherwise more data has to be read. */
748
749 if (ptr[3] != '-') break;
750
751 /* Move the reading pointer upwards in the buffer and insert \n between the
752 components of a multiline response. Space is left for this by read_response_
753 line(). */
754
755 ptr += count;
756 *ptr++ = '\n';
757 size -= count + 1;
758 }
759
ee8b8090
JH
760#ifdef TCP_FASTOPEN
761 tfo_out_check(sx->cctx.sock);
762#endif
763
059ec3d9
PH
764/* Return a value that depends on the SMTP return code. On some systems a
765non-zero value of errno has been seen at this point, so ensure it is zero,
766because the caller of this function looks at errno when FALSE is returned, to
767distinguish between an unexpected return code and other errors such as
768timeouts, lost connections, etc. */
769
770errno = 0;
771return buffer[0] == okdigit;
772}
773
774/* End of smtp_out.c */
7eb6c37c
JH
775/* vi: aw ai sw=2
776*/