Tidy up error handling for sockets vs fds
On Windows, sockets and fds are different, so we need to be a little
carefully. The fd functions (which are really a userspace construct
inside the libc) report errors by writing to errno:
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/read?view=msvc-170
While the socket functions (which are really thin wrappers over Windows
HANDLEs) use WSAGetLastError:
https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv
https://learn.microsoft.com/en-us/windows/win32/winsock/error-codes-errno-h-errno-and-wsagetlasterror-2
Moreover, the error values are different, so we shouldn't mix them
together:
https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170
Finally, by borrowing OpenSSL's distinct OPENSSL_NO_SOCK and
OPENSSL_NO_POSIX_IO options, we arguably should account for all
combinations of one or the other being missing. (Ugh.) To account for
that, I've moved bio_fd_should_retry into its own file that isn't
conditioned on anything. It only depends on <errno.h>, which is part of
the C standard library, and used elsewhere already.
Change-Id: I0519d7d68c32062e1220ffca0ab57a9cac9f7e5f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/61729
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index d1cc984..f0c892f 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -96,6 +96,7 @@
bio/bio.c
bio/bio_mem.c
bio/connect.c
+ bio/errno.c
bio/fd.c
bio/file.c
bio/hexdump.c
diff --git a/crypto/bio/connect.c b/crypto/bio/connect.c
index cf52dde..d48d14e 100644
--- a/crypto/bio/connect.c
+++ b/crypto/bio/connect.c
@@ -233,7 +233,7 @@
BIO_clear_retry_flags(bio);
ret = connect(bio->num, (struct sockaddr*) &c->them, c->them_length);
if (ret < 0) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_socket_should_retry(ret)) {
BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY));
c->state = BIO_CONN_S_BLOCKED_CONNECT;
bio->retry_reason = BIO_RR_CONNECT;
@@ -252,7 +252,7 @@
case BIO_CONN_S_BLOCKED_CONNECT:
i = bio_sock_error(bio->num);
if (i) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_socket_should_retry(ret)) {
BIO_set_flags(bio, (BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY));
c->state = BIO_CONN_S_BLOCKED_CONNECT;
bio->retry_reason = BIO_RR_CONNECT;
@@ -366,7 +366,7 @@
ret = (int)recv(bio->num, out, out_len, 0);
BIO_clear_retry_flags(bio);
if (ret <= 0) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_socket_should_retry(ret)) {
BIO_set_retry_read(bio);
}
}
@@ -390,7 +390,7 @@
ret = (int)send(bio->num, in, in_len, 0);
BIO_clear_retry_flags(bio);
if (ret <= 0) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_socket_should_retry(ret)) {
BIO_set_retry_write(bio);
}
}
diff --git a/crypto/bio/errno.c b/crypto/bio/errno.c
new file mode 100644
index 0000000..901ea0c
--- /dev/null
+++ b/crypto/bio/errno.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/bio.h>
+
+#include <errno.h>
+
+#include "internal.h"
+
+
+int bio_errno_should_retry(int return_value) {
+ if (return_value != -1) {
+ return 0;
+ }
+
+ return
+#ifdef EWOULDBLOCK
+ errno == EWOULDBLOCK ||
+#endif
+#ifdef ENOTCONN
+ errno == ENOTCONN ||
+#endif
+#ifdef EINTR
+ errno == EINTR ||
+#endif
+#ifdef EAGAIN
+ errno == EAGAIN ||
+#endif
+#ifdef EPROTO
+ errno == EPROTO ||
+#endif
+#ifdef EINPROGRESS
+ errno == EINPROGRESS ||
+#endif
+#ifdef EALREADY
+ errno == EALREADY ||
+#endif
+ 0;
+}
diff --git a/crypto/bio/fd.c b/crypto/bio/fd.c
index c128e81..3b38db8 100644
--- a/crypto/bio/fd.c
+++ b/crypto/bio/fd.c
@@ -65,9 +65,6 @@
#include <unistd.h>
#else
#include <io.h>
-OPENSSL_MSVC_PRAGMA(warning(push, 3))
-#include <windows.h>
-OPENSSL_MSVC_PRAGMA(warning(pop))
#endif
#include <openssl/err.h>
@@ -77,59 +74,18 @@
#include "../internal.h"
-static int bio_fd_non_fatal_error(int err) {
- if (
-#ifdef EWOULDBLOCK
- err == EWOULDBLOCK ||
-#endif
-#ifdef WSAEWOULDBLOCK
- err == WSAEWOULDBLOCK ||
-#endif
-#ifdef ENOTCONN
- err == ENOTCONN ||
-#endif
-#ifdef EINTR
- err == EINTR ||
-#endif
-#ifdef EAGAIN
- err == EAGAIN ||
-#endif
-#ifdef EPROTO
- err == EPROTO ||
-#endif
-#ifdef EINPROGRESS
- err == EINPROGRESS ||
-#endif
-#ifdef EALREADY
- err == EALREADY ||
-#endif
- 0) {
- return 1;
- }
- return 0;
-}
-
#if defined(OPENSSL_WINDOWS)
- #define BORINGSSL_ERRNO (int)GetLastError()
#define BORINGSSL_CLOSE _close
#define BORINGSSL_LSEEK _lseek
#define BORINGSSL_READ _read
#define BORINGSSL_WRITE _write
#else
- #define BORINGSSL_ERRNO errno
#define BORINGSSL_CLOSE close
#define BORINGSSL_LSEEK lseek
#define BORINGSSL_READ read
#define BORINGSSL_WRITE write
#endif
-int bio_fd_should_retry(int i) {
- if (i == -1) {
- return bio_fd_non_fatal_error(BORINGSSL_ERRNO);
- }
- return 0;
-}
-
BIO *BIO_new_fd(int fd, int close_flag) {
BIO *ret = BIO_new(BIO_s_fd());
if (ret == NULL) {
@@ -161,7 +117,7 @@
ret = (int)BORINGSSL_READ(b->num, out, outl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_errno_should_retry(ret)) {
BIO_set_retry_read(b);
}
}
@@ -173,7 +129,7 @@
int ret = (int)BORINGSSL_WRITE(b->num, in, inl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_errno_should_retry(ret)) {
BIO_set_retry_write(b);
}
}
diff --git a/crypto/bio/internal.h b/crypto/bio/internal.h
index 8ed27da..ea2aa6a 100644
--- a/crypto/bio/internal.h
+++ b/crypto/bio/internal.h
@@ -78,7 +78,9 @@
#endif
-// BIO_ip_and_port_to_socket_and_addr creates a socket and fills in |*out_addr|
+#if !defined(OPENSSL_NO_SOCK)
+
+// bio_ip_and_port_to_socket_and_addr creates a socket and fills in |*out_addr|
// and |*out_addr_length| with the correct values for connecting to |hostname|
// on |port_str|. It returns one on success or zero on error.
int bio_ip_and_port_to_socket_and_addr(int *out_sock,
@@ -87,21 +89,27 @@
const char *hostname,
const char *port_str);
-// BIO_socket_nbio sets whether |sock| is non-blocking. It returns one on
+// bio_socket_nbio sets whether |sock| is non-blocking. It returns one on
// success and zero otherwise.
int bio_socket_nbio(int sock, int on);
-// BIO_clear_socket_error clears the last system socket error.
+// bio_clear_socket_error clears the last system socket error.
//
// TODO(fork): remove all callers of this.
void bio_clear_socket_error(void);
-// BIO_sock_error returns the last socket error on |sock|.
+// bio_sock_error returns the last socket error on |sock|.
int bio_sock_error(int sock);
-// BIO_fd_should_retry returns non-zero if |return_value| indicates an error
+// bio_socket_should_retry returns non-zero if |return_value| indicates an error
+// and the last socket error indicates that it's non-fatal.
+int bio_socket_should_retry(int return_value);
+
+#endif // !OPENSSL_NO_SOCK
+
+// bio_errno_should_retry returns non-zero if |return_value| indicates an error
// and |errno| indicates that it's non-fatal.
-int bio_fd_should_retry(int return_value);
+int bio_errno_should_retry(int return_value);
#if defined(__cplusplus)
diff --git a/crypto/bio/socket.c b/crypto/bio/socket.c
index 6084e96..72c772f 100644
--- a/crypto/bio/socket.c
+++ b/crypto/bio/socket.c
@@ -104,7 +104,7 @@
#endif
BIO_clear_retry_flags(b);
if (ret <= 0) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_socket_should_retry(ret)) {
BIO_set_retry_read(b);
}
}
@@ -120,7 +120,7 @@
#endif
BIO_clear_retry_flags(b);
if (ret <= 0) {
- if (bio_fd_should_retry(ret)) {
+ if (bio_socket_should_retry(ret)) {
BIO_set_retry_write(b);
}
}
diff --git a/crypto/bio/socket_helper.c b/crypto/bio/socket_helper.c
index 366996c..fd8181f 100644
--- a/crypto/bio/socket_helper.c
+++ b/crypto/bio/socket_helper.c
@@ -121,4 +121,13 @@
return error;
}
+int bio_socket_should_retry(int return_value) {
+#if defined(OPENSSL_WINDOWS)
+ return return_value == -1 && WSAGetLastError() == WSAEWOULDBLOCK;
+#else
+ // On POSIX platforms, sockets and fds are the same.
+ return bio_errno_should_retry(return_value);
+#endif
+}
+
#endif // OPENSSL_NO_SOCK