blob: b7ad5ffaf52b54eaaadf53ad33d809911d558478 [file] [log] [blame]
Dave Tapuskab8a824d2014-12-10 19:09:52 -05001/* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include <openssl/base.h>
16
Dave Tapuskab8a824d2014-12-10 19:09:52 -050017#include <string>
18#include <vector>
19
20#include <errno.h>
Adam Langley403c52a2016-07-07 14:52:02 -070021#include <limits.h>
Brian Smith054e6822015-03-27 21:12:01 -100022#include <stddef.h>
David Benjaminf0df86a2015-04-20 11:32:12 -040023#include <stdlib.h>
Adam Langley2b2d66d2015-01-30 17:08:37 -080024#include <string.h>
Dave Tapuskab8a824d2014-12-10 19:09:52 -050025#include <sys/types.h>
Dave Tapuskab8a824d2014-12-10 19:09:52 -050026
27#if !defined(OPENSSL_WINDOWS)
28#include <arpa/inet.h>
29#include <fcntl.h>
30#include <netdb.h>
31#include <netinet/in.h>
32#include <sys/select.h>
Brian Smith33970e62015-01-27 22:32:08 -080033#include <sys/socket.h>
Dave Tapuskab8a824d2014-12-10 19:09:52 -050034#include <unistd.h>
35#else
Brian Smith33970e62015-01-27 22:32:08 -080036#include <io.h>
David Benjamina353cdb2016-06-09 16:48:33 -040037OPENSSL_MSVC_PRAGMA(warning(push, 3))
Adam Langley3e719312015-03-20 16:32:23 -070038#include <winsock2.h>
39#include <ws2tcpip.h>
David Benjamina353cdb2016-06-09 16:48:33 -040040OPENSSL_MSVC_PRAGMA(warning(pop))
Brian Smithefed2212015-01-28 16:20:02 -080041
Brian Smith33970e62015-01-27 22:32:08 -080042typedef int ssize_t;
David Benjamin4fec04b2016-10-10 14:56:47 -040043OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib"))
Dave Tapuskab8a824d2014-12-10 19:09:52 -050044#endif
45
46#include <openssl/err.h>
47#include <openssl/ssl.h>
Gabriel Rednerdcb33832016-04-06 15:47:28 -040048#include <openssl/x509.h>
Dave Tapuskab8a824d2014-12-10 19:09:52 -050049
David Benjamin17cf2cb2016-12-13 01:07:13 -050050#include "../crypto/internal.h"
Dave Tapuskab8a824d2014-12-10 19:09:52 -050051#include "internal.h"
Piotr Sikorac6d30292016-03-18 17:28:36 -070052#include "transport_common.h"
Dave Tapuskab8a824d2014-12-10 19:09:52 -050053
54
Brian Smith33970e62015-01-27 22:32:08 -080055#if !defined(OPENSSL_WINDOWS)
56static int closesocket(int sock) {
57 return close(sock);
58}
59#endif
60
61bool InitSocketLibrary() {
62#if defined(OPENSSL_WINDOWS)
63 WSADATA wsaData;
64 int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
65 if (err != 0) {
66 fprintf(stderr, "WSAStartup failed with error %d\n", err);
67 return false;
68 }
69#endif
70 return true;
71}
72
Dave Tapuskab8a824d2014-12-10 19:09:52 -050073// Connect sets |*out_sock| to be a socket connected to the destination given
74// in |hostname_and_port|, which should be of the form "www.example.com:123".
75// It returns true on success and false otherwise.
76bool Connect(int *out_sock, const std::string &hostname_and_port) {
David Benjamin72acbec2016-06-22 13:01:24 -040077 size_t colon_offset = hostname_and_port.find_last_of(':');
78 const size_t bracket_offset = hostname_and_port.find_last_of(']');
Dave Tapuskab8a824d2014-12-10 19:09:52 -050079 std::string hostname, port;
80
David Benjamin72acbec2016-06-22 13:01:24 -040081 // An IPv6 literal may have colons internally, guarded by square brackets.
82 if (bracket_offset != std::string::npos &&
83 colon_offset != std::string::npos && bracket_offset > colon_offset) {
84 colon_offset = std::string::npos;
85 }
86
Dave Tapuskab8a824d2014-12-10 19:09:52 -050087 if (colon_offset == std::string::npos) {
88 hostname = hostname_and_port;
89 port = "443";
90 } else {
91 hostname = hostname_and_port.substr(0, colon_offset);
92 port = hostname_and_port.substr(colon_offset + 1);
93 }
94
David Benjamin72acbec2016-06-22 13:01:24 -040095 // Handle IPv6 literals.
96 if (hostname.size() >= 2 && hostname[0] == '[' &&
97 hostname[hostname.size() - 1] == ']') {
98 hostname = hostname.substr(1, hostname.size() - 2);
99 }
100
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500101 struct addrinfo hint, *result;
David Benjamin17cf2cb2016-12-13 01:07:13 -0500102 OPENSSL_memset(&hint, 0, sizeof(hint));
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500103 hint.ai_family = AF_UNSPEC;
104 hint.ai_socktype = SOCK_STREAM;
105
106 int ret = getaddrinfo(hostname.c_str(), port.c_str(), &hint, &result);
107 if (ret != 0) {
108 fprintf(stderr, "getaddrinfo returned: %s\n", gai_strerror(ret));
109 return false;
110 }
111
112 bool ok = false;
113 char buf[256];
114
115 *out_sock =
116 socket(result->ai_family, result->ai_socktype, result->ai_protocol);
117 if (*out_sock < 0) {
118 perror("socket");
119 goto out;
120 }
121
122 switch (result->ai_family) {
123 case AF_INET: {
124 struct sockaddr_in *sin =
125 reinterpret_cast<struct sockaddr_in *>(result->ai_addr);
126 fprintf(stderr, "Connecting to %s:%d\n",
127 inet_ntop(result->ai_family, &sin->sin_addr, buf, sizeof(buf)),
128 ntohs(sin->sin_port));
129 break;
130 }
131 case AF_INET6: {
132 struct sockaddr_in6 *sin6 =
133 reinterpret_cast<struct sockaddr_in6 *>(result->ai_addr);
134 fprintf(stderr, "Connecting to [%s]:%d\n",
135 inet_ntop(result->ai_family, &sin6->sin6_addr, buf, sizeof(buf)),
136 ntohs(sin6->sin6_port));
137 break;
138 }
139 }
140
141 if (connect(*out_sock, result->ai_addr, result->ai_addrlen) != 0) {
142 perror("connect");
143 goto out;
144 }
145 ok = true;
146
147out:
148 freeaddrinfo(result);
149 return ok;
150}
151
152bool Accept(int *out_sock, const std::string &port) {
Matt Braithwaite29d8adb2015-09-28 19:45:54 -0700153 struct sockaddr_in6 addr, cli_addr;
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500154 socklen_t cli_addr_len = sizeof(cli_addr);
David Benjamin17cf2cb2016-12-13 01:07:13 -0500155 OPENSSL_memset(&addr, 0, sizeof(addr));
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500156
Matt Braithwaite29d8adb2015-09-28 19:45:54 -0700157 addr.sin6_family = AF_INET6;
Matthew Braithwaiteb3488972016-10-19 15:05:29 -0700158 addr.sin6_addr = IN6ADDR_ANY_INIT;
Matt Braithwaite29d8adb2015-09-28 19:45:54 -0700159 addr.sin6_port = htons(atoi(port.c_str()));
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500160
161 bool ok = false;
David Benjamin6add9f12017-01-06 16:02:39 -0500162#if defined(OPENSSL_WINDOWS)
163 const BOOL enable = TRUE;
164#else
165 const int enable = 1;
166#endif
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500167 int server_sock = -1;
168
169 server_sock =
Matt Braithwaite29d8adb2015-09-28 19:45:54 -0700170 socket(addr.sin6_family, SOCK_STREAM, 0);
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500171 if (server_sock < 0) {
172 perror("socket");
173 goto out;
174 }
175
David Benjamin6add9f12017-01-06 16:02:39 -0500176 if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable,
Steven Valdezbf5bda32016-12-28 10:51:01 -0500177 sizeof(enable)) < 0) {
178 perror("setsockopt");
179 goto out;
180 }
181
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500182 if (bind(server_sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
183 perror("connect");
184 goto out;
185 }
186 listen(server_sock, 1);
187 *out_sock = accept(server_sock, (struct sockaddr*)&cli_addr, &cli_addr_len);
188
189 ok = true;
190
191out:
Brian Smith33970e62015-01-27 22:32:08 -0800192 closesocket(server_sock);
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500193 return ok;
194}
195
David Benjamin225e5ad2016-07-16 14:51:58 +0200196bool VersionFromString(uint16_t *out_version, const std::string &version) {
197 if (version == "ssl3") {
198 *out_version = SSL3_VERSION;
199 return true;
200 } else if (version == "tls1" || version == "tls1.0") {
201 *out_version = TLS1_VERSION;
202 return true;
203 } else if (version == "tls1.1") {
204 *out_version = TLS1_1_VERSION;
205 return true;
206 } else if (version == "tls1.2") {
207 *out_version = TLS1_2_VERSION;
208 return true;
209 } else if (version == "tls1.3") {
210 *out_version = TLS1_3_VERSION;
211 return true;
212 }
213 return false;
214}
215
David Benjamin31168c92016-09-09 16:49:02 -0400216static const char *SignatureAlgorithmToString(uint16_t version, uint16_t sigalg) {
217 const bool is_tls12 = version == TLS1_2_VERSION || version == DTLS1_2_VERSION;
218 switch (sigalg) {
219 case SSL_SIGN_RSA_PKCS1_SHA1:
220 return "rsa_pkcs1_sha1";
221 case SSL_SIGN_RSA_PKCS1_SHA256:
222 return "rsa_pkcs1_sha256";
223 case SSL_SIGN_RSA_PKCS1_SHA384:
224 return "rsa_pkcs1_sha384";
225 case SSL_SIGN_RSA_PKCS1_SHA512:
226 return "rsa_pkcs1_sha512";
227 case SSL_SIGN_ECDSA_SHA1:
228 return "ecdsa_sha1";
229 case SSL_SIGN_ECDSA_SECP256R1_SHA256:
230 return is_tls12 ? "ecdsa_sha256" : "ecdsa_secp256r1_sha256";
231 case SSL_SIGN_ECDSA_SECP384R1_SHA384:
232 return is_tls12 ? "ecdsa_sha384" : "ecdsa_secp384r1_sha384";
233 case SSL_SIGN_ECDSA_SECP521R1_SHA512:
234 return is_tls12 ? "ecdsa_sha512" : "ecdsa_secp521r1_sha512";
235 case SSL_SIGN_RSA_PSS_SHA256:
236 return "rsa_pss_sha256";
237 case SSL_SIGN_RSA_PSS_SHA384:
238 return "rsa_pss_sha384";
239 case SSL_SIGN_RSA_PSS_SHA512:
240 return "rsa_pss_sha512";
David Benjamin69522112017-03-28 15:38:29 -0500241 case SSL_SIGN_ED25519:
242 return "ed25519";
David Benjamin31168c92016-09-09 16:49:02 -0400243 default:
244 return "(unknown)";
245 }
246}
247
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500248void PrintConnectionInfo(const SSL *ssl) {
249 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
250
251 fprintf(stderr, " Version: %s\n", SSL_get_version(ssl));
David Benjamin621f95a2015-08-28 15:08:34 -0400252 fprintf(stderr, " Resumed session: %s\n",
253 SSL_session_reused(ssl) ? "yes" : "no");
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500254 fprintf(stderr, " Cipher: %s\n", SSL_CIPHER_get_name(cipher));
David Benjamin49864a52016-07-13 15:50:26 -0400255 uint16_t curve = SSL_get_curve_id(ssl);
256 if (curve != 0) {
257 fprintf(stderr, " ECDHE curve: %s\n", SSL_get_curve_name(curve));
258 }
David Benjamin31168c92016-09-09 16:49:02 -0400259 uint16_t sigalg = SSL_get_peer_signature_algorithm(ssl);
260 if (sigalg != 0) {
261 fprintf(stderr, " Signature algorithm: %s\n",
262 SignatureAlgorithmToString(SSL_version(ssl), sigalg));
263 }
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500264 fprintf(stderr, " Secure renegotiation: %s\n",
265 SSL_get_secure_renegotiation_support(ssl) ? "yes" : "no");
David Benjamin3995a382016-05-31 16:15:04 -0400266 fprintf(stderr, " Extended master secret: %s\n",
267 SSL_get_extms_support(ssl) ? "yes" : "no");
David Benjamin05709232015-03-23 19:01:33 -0400268
269 const uint8_t *next_proto;
270 unsigned next_proto_len;
271 SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
272 fprintf(stderr, " Next protocol negotiated: %.*s\n", next_proto_len,
273 next_proto);
274
275 const uint8_t *alpn;
276 unsigned alpn_len;
277 SSL_get0_alpn_selected(ssl, &alpn, &alpn_len);
278 fprintf(stderr, " ALPN protocol: %.*s\n", alpn_len, alpn);
Gabriel Rednerdcb33832016-04-06 15:47:28 -0400279
Alessandro Ghedini8d3f1302016-11-14 21:24:18 +0000280 const char *host_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
281 if (host_name != nullptr && SSL_is_server(ssl)) {
282 fprintf(stderr, " Client sent SNI: %s\n", host_name);
283 }
284
Alessandro Ghedini1149ee12016-12-12 14:48:55 +0000285 if (!SSL_is_server(ssl)) {
286 const uint8_t *ocsp_staple;
287 size_t ocsp_staple_len;
288 SSL_get0_ocsp_response(ssl, &ocsp_staple, &ocsp_staple_len);
289 fprintf(stderr, " OCSP staple: %s\n", ocsp_staple_len > 0 ? "yes" : "no");
Alessandro Ghedinif6d64ef2017-02-16 00:57:35 +0000290
291 const uint8_t *sct_list;
292 size_t sct_list_len;
293 SSL_get0_signed_cert_timestamp_list(ssl, &sct_list, &sct_list_len);
294 fprintf(stderr, " SCT list: %s\n", sct_list_len > 0 ? "yes" : "no");
Alessandro Ghedini1149ee12016-12-12 14:48:55 +0000295 }
296
Alessandro Ghedinica307ab2017-03-26 12:19:40 -0500297 fprintf(stderr, " Early data: %s\n",
298 SSL_early_data_accepted(ssl) ? "yes" : "no");
299
Gabriel Rednerdcb33832016-04-06 15:47:28 -0400300 // Print the server cert subject and issuer names.
David Benjamin0cce8632016-10-20 15:13:26 -0400301 bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(ssl));
302 if (peer != nullptr) {
Gabriel Rednerdcb33832016-04-06 15:47:28 -0400303 fprintf(stderr, " Cert subject: ");
David Benjamin0cce8632016-10-20 15:13:26 -0400304 X509_NAME_print_ex_fp(stderr, X509_get_subject_name(peer.get()), 0,
Gabriel Rednerdcb33832016-04-06 15:47:28 -0400305 XN_FLAG_ONELINE);
306 fprintf(stderr, "\n Cert issuer: ");
David Benjamin0cce8632016-10-20 15:13:26 -0400307 X509_NAME_print_ex_fp(stderr, X509_get_issuer_name(peer.get()), 0,
Gabriel Rednerdcb33832016-04-06 15:47:28 -0400308 XN_FLAG_ONELINE);
309 fprintf(stderr, "\n");
Gabriel Rednerdcb33832016-04-06 15:47:28 -0400310 }
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500311}
312
313bool SocketSetNonBlocking(int sock, bool is_non_blocking) {
314 bool ok;
315
316#if defined(OPENSSL_WINDOWS)
317 u_long arg = is_non_blocking;
Brian Smith33970e62015-01-27 22:32:08 -0800318 ok = 0 == ioctlsocket(sock, FIONBIO, &arg);
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500319#else
320 int flags = fcntl(sock, F_GETFL, 0);
321 if (flags < 0) {
322 return false;
323 }
324 if (is_non_blocking) {
325 flags |= O_NONBLOCK;
326 } else {
327 flags &= ~O_NONBLOCK;
328 }
329 ok = 0 == fcntl(sock, F_SETFL, flags);
330#endif
331 if (!ok) {
332 fprintf(stderr, "Failed to set socket non-blocking.\n");
333 }
334 return ok;
335}
336
337// PrintErrorCallback is a callback function from OpenSSL's
338// |ERR_print_errors_cb| that writes errors to a given |FILE*|.
339int PrintErrorCallback(const char *str, size_t len, void *ctx) {
340 fwrite(str, len, 1, reinterpret_cast<FILE*>(ctx));
341 return 1;
342}
343
344bool TransferData(SSL *ssl, int sock) {
345 bool stdin_open = true;
346
347 fd_set read_fds;
348 FD_ZERO(&read_fds);
349
350 if (!SocketSetNonBlocking(sock, true)) {
351 return false;
352 }
353
354 for (;;) {
355 if (stdin_open) {
356 FD_SET(0, &read_fds);
357 }
358 FD_SET(sock, &read_fds);
359
360 int ret = select(sock + 1, &read_fds, NULL, NULL, NULL);
361 if (ret <= 0) {
362 perror("select");
363 return false;
364 }
365
366 if (FD_ISSET(0, &read_fds)) {
367 uint8_t buffer[512];
368 ssize_t n;
369
370 do {
nmittlerf0322b22016-05-19 08:49:59 -0700371 n = BORINGSSL_READ(0, buffer, sizeof(buffer));
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500372 } while (n == -1 && errno == EINTR);
373
374 if (n == 0) {
375 FD_CLR(0, &read_fds);
376 stdin_open = false;
Brian Smith33970e62015-01-27 22:32:08 -0800377#if !defined(OPENSSL_WINDOWS)
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500378 shutdown(sock, SHUT_WR);
Brian Smith33970e62015-01-27 22:32:08 -0800379#else
380 shutdown(sock, SD_SEND);
381#endif
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500382 continue;
383 } else if (n < 0) {
384 perror("read from stdin");
385 return false;
386 }
387
388 if (!SocketSetNonBlocking(sock, false)) {
389 return false;
390 }
391 int ssl_ret = SSL_write(ssl, buffer, n);
392 if (!SocketSetNonBlocking(sock, true)) {
393 return false;
394 }
395
396 if (ssl_ret <= 0) {
397 int ssl_err = SSL_get_error(ssl, ssl_ret);
398 fprintf(stderr, "Error while writing: %d\n", ssl_err);
399 ERR_print_errors_cb(PrintErrorCallback, stderr);
400 return false;
401 } else if (ssl_ret != n) {
402 fprintf(stderr, "Short write from SSL_write.\n");
403 return false;
404 }
405 }
406
407 if (FD_ISSET(sock, &read_fds)) {
408 uint8_t buffer[512];
409 int ssl_ret = SSL_read(ssl, buffer, sizeof(buffer));
410
411 if (ssl_ret < 0) {
412 int ssl_err = SSL_get_error(ssl, ssl_ret);
413 if (ssl_err == SSL_ERROR_WANT_READ) {
414 continue;
415 }
416 fprintf(stderr, "Error while reading: %d\n", ssl_err);
417 ERR_print_errors_cb(PrintErrorCallback, stderr);
418 return false;
419 } else if (ssl_ret == 0) {
420 return true;
421 }
422
423 ssize_t n;
424 do {
nmittlerf0322b22016-05-19 08:49:59 -0700425 n = BORINGSSL_WRITE(1, buffer, ssl_ret);
Dave Tapuskab8a824d2014-12-10 19:09:52 -0500426 } while (n == -1 && errno == EINTR);
427
428 if (n != ssl_ret) {
429 fprintf(stderr, "Short write to stderr.\n");
430 return false;
431 }
432 }
433 }
434}
Adam Langley403c52a2016-07-07 14:52:02 -0700435
436// SocketLineReader wraps a small buffer around a socket for line-orientated
437// protocols.
438class SocketLineReader {
439 public:
440 explicit SocketLineReader(int sock) : sock_(sock) {}
441
442 // Next reads a '\n'- or '\r\n'-terminated line from the socket and, on
443 // success, sets |*out_line| to it and returns true. Otherwise it returns
444 // false.
445 bool Next(std::string *out_line) {
446 for (;;) {
447 for (size_t i = 0; i < buf_len_; i++) {
448 if (buf_[i] != '\n') {
449 continue;
450 }
451
452 size_t length = i;
453 if (i > 0 && buf_[i - 1] == '\r') {
454 length--;
455 }
456
457 out_line->assign(buf_, length);
458 buf_len_ -= i + 1;
David Benjamin17cf2cb2016-12-13 01:07:13 -0500459 OPENSSL_memmove(buf_, &buf_[i + 1], buf_len_);
Adam Langley403c52a2016-07-07 14:52:02 -0700460
461 return true;
462 }
463
464 if (buf_len_ == sizeof(buf_)) {
465 fprintf(stderr, "Received line too long!\n");
466 return false;
467 }
468
469 ssize_t n;
470 do {
471 n = recv(sock_, &buf_[buf_len_], sizeof(buf_) - buf_len_, 0);
472 } while (n == -1 && errno == EINTR);
473
474 if (n < 0) {
475 fprintf(stderr, "Read error from socket\n");
476 return false;
477 }
478
479 buf_len_ += n;
480 }
481 }
482
483 // ReadSMTPReply reads one or more lines that make up an SMTP reply. On
484 // success, it sets |*out_code| to the reply's code (e.g. 250) and
485 // |*out_content| to the body of the reply (e.g. "OK") and returns true.
486 // Otherwise it returns false.
487 //
488 // See https://tools.ietf.org/html/rfc821#page-48
489 bool ReadSMTPReply(unsigned *out_code, std::string *out_content) {
490 out_content->clear();
491
492 // kMaxLines is the maximum number of lines that we'll accept in an SMTP
493 // reply.
494 static const unsigned kMaxLines = 512;
495 for (unsigned i = 0; i < kMaxLines; i++) {
496 std::string line;
497 if (!Next(&line)) {
498 return false;
499 }
500
501 if (line.size() < 4) {
502 fprintf(stderr, "Short line from SMTP server: %s\n", line.c_str());
503 return false;
504 }
505
506 const std::string code_str = line.substr(0, 3);
507 char *endptr;
508 const unsigned long code = strtoul(code_str.c_str(), &endptr, 10);
509 if (*endptr || code > UINT_MAX) {
510 fprintf(stderr, "Failed to parse code from line: %s\n", line.c_str());
511 return false;
512 }
513
514 if (i == 0) {
515 *out_code = code;
516 } else if (code != *out_code) {
517 fprintf(stderr,
518 "Reply code varied within a single reply: was %u, now %u\n",
519 *out_code, static_cast<unsigned>(code));
520 return false;
521 }
522
523 if (line[3] == ' ') {
524 // End of reply.
525 *out_content += line.substr(4, std::string::npos);
526 return true;
527 } else if (line[3] == '-') {
528 // Another line of reply will follow this one.
529 *out_content += line.substr(4, std::string::npos);
530 out_content->push_back('\n');
531 } else {
532 fprintf(stderr, "Bad character after code in SMTP reply: %s\n",
533 line.c_str());
534 return false;
535 }
536 }
537
538 fprintf(stderr, "Rejected SMTP reply of more then %u lines\n", kMaxLines);
539 return false;
540 }
541
542 private:
543 const int sock_;
544 char buf_[512];
545 size_t buf_len_ = 0;
546};
547
548// SendAll writes |data_len| bytes from |data| to |sock|. It returns true on
549// success and false otherwise.
550static bool SendAll(int sock, const char *data, size_t data_len) {
551 size_t done = 0;
552
553 while (done < data_len) {
554 ssize_t n;
555 do {
556 n = send(sock, &data[done], data_len - done, 0);
557 } while (n == -1 && errno == EINTR);
558
559 if (n < 0) {
560 fprintf(stderr, "Error while writing to socket\n");
561 return false;
562 }
563
564 done += n;
565 }
566
567 return true;
568}
569
570bool DoSMTPStartTLS(int sock) {
571 SocketLineReader line_reader(sock);
572
Adam Langley61367ee2016-07-11 12:24:55 -0700573 unsigned code_220 = 0;
Adam Langley403c52a2016-07-07 14:52:02 -0700574 std::string reply_220;
575 if (!line_reader.ReadSMTPReply(&code_220, &reply_220)) {
576 return false;
577 }
578
579 if (code_220 != 220) {
580 fprintf(stderr, "Expected 220 line from SMTP server but got code %u\n",
581 code_220);
582 return false;
583 }
584
585 static const char kHelloLine[] = "EHLO BoringSSL\r\n";
586 if (!SendAll(sock, kHelloLine, sizeof(kHelloLine) - 1)) {
587 return false;
588 }
589
Adam Langley61367ee2016-07-11 12:24:55 -0700590 unsigned code_250 = 0;
Adam Langley403c52a2016-07-07 14:52:02 -0700591 std::string reply_250;
592 if (!line_reader.ReadSMTPReply(&code_250, &reply_250)) {
593 return false;
594 }
595
596 if (code_250 != 250) {
597 fprintf(stderr, "Expected 250 line after EHLO but got code %u\n", code_250);
598 return false;
599 }
600
601 // https://tools.ietf.org/html/rfc1869#section-4.3
Adam Langley505cf392016-08-09 21:16:45 -0700602 if (("\n" + reply_250 + "\n").find("\nSTARTTLS\n") == std::string::npos) {
Adam Langley403c52a2016-07-07 14:52:02 -0700603 fprintf(stderr, "Server does not support STARTTLS\n");
604 return false;
605 }
606
607 static const char kSTARTTLSLine[] = "STARTTLS\r\n";
608 if (!SendAll(sock, kSTARTTLSLine, sizeof(kSTARTTLSLine) - 1)) {
609 return false;
610 }
611
612 if (!line_reader.ReadSMTPReply(&code_220, &reply_220)) {
613 return false;
614 }
615
616 if (code_220 != 220) {
617 fprintf(
618 stderr,
619 "Expected 220 line from SMTP server after STARTTLS, but got code %u\n",
620 code_220);
621 return false;
622 }
623
624 return true;
625}