| // Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <openssl/bio.h> | |
| #if !defined(OPENSSL_NO_SOCK) | |
| #include <fcntl.h> | |
| #include <string.h> | |
| #include <limits.h> | |
| #include <algorithm> | |
| #if !defined(OPENSSL_WINDOWS) | |
| #include <unistd.h> | |
| #else | |
| #include <winsock2.h> | |
| OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib")) | |
| #endif | |
| #include "internal.h" | |
| using namespace bssl; | |
| #if !defined(OPENSSL_WINDOWS) | |
| static int closesocket(int sock) { return close(sock); } | |
| #endif | |
| static int sock_free(BIO *bio) { | |
| if (BIO_get_shutdown(bio)) { | |
| if (BIO_get_init(bio)) { | |
| closesocket(FromOpaque(bio)->num); | |
| } | |
| BIO_set_init(bio, 0); | |
| BIO_clear_retry_flags(bio); | |
| } | |
| return 1; | |
| } | |
| static int sock_read(BIO *b, char *out, int outl) { | |
| bio_clear_socket_error(); | |
| #if defined(OPENSSL_WINDOWS) | |
| int ret = recv(FromOpaque(b)->num, out, outl, 0); | |
| #else | |
| int ret = (int)read(FromOpaque(b)->num, out, outl); | |
| #endif | |
| BIO_clear_retry_flags(b); | |
| if (ret <= 0) { | |
| if (bio_socket_should_retry(ret)) { | |
| BIO_set_retry_read(b); | |
| } | |
| } | |
| return ret; | |
| } | |
| static int sock_write_ex(BIO *b, const char *in, size_t inl, | |
| size_t *out_written) { | |
| bio_clear_socket_error(); | |
| #if defined(OPENSSL_WINDOWS) | |
| inl = std::min(inl, size_t{INT_MAX}); | |
| int ret = send(FromOpaque(b)->num, in, static_cast<int>(inl), 0); | |
| #else | |
| ssize_t ret = write(FromOpaque(b)->num, in, inl); | |
| #endif | |
| BIO_clear_retry_flags(b); | |
| if (ret <= 0) { | |
| if (bio_socket_should_retry(ret)) { | |
| BIO_set_retry_write(b); | |
| } | |
| return 0; | |
| } | |
| *out_written = ret; | |
| return 1; | |
| } | |
| static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) { | |
| switch (cmd) { | |
| case BIO_C_SET_FD: | |
| sock_free(b); | |
| FromOpaque(b)->num = *static_cast<int *>(ptr); | |
| BIO_set_shutdown(b, static_cast<int>(num)); | |
| BIO_set_init(b, 1); | |
| return 1; | |
| case BIO_C_GET_FD: | |
| if (BIO_get_init(b)) { | |
| int *out = static_cast<int *>(ptr); | |
| if (out != nullptr) { | |
| *out = FromOpaque(b)->num; | |
| } | |
| return FromOpaque(b)->num; | |
| } | |
| return -1; | |
| case BIO_CTRL_GET_CLOSE: | |
| return BIO_get_shutdown(b); | |
| case BIO_CTRL_SET_CLOSE: | |
| BIO_set_shutdown(b, static_cast<int>(num)); | |
| return 1; | |
| case BIO_CTRL_FLUSH: | |
| return 1; | |
| default: | |
| return 0; | |
| } | |
| } | |
| static const BIO_METHOD methods_sockp = { | |
| BIO_TYPE_SOCKET, "socket", | |
| /*bwrite=*/nullptr, sock_write_ex, | |
| sock_read, nullptr /* gets, */, | |
| sock_ctrl, nullptr /* create */, | |
| sock_free, nullptr /* callback_ctrl */, | |
| }; | |
| const BIO_METHOD *BIO_s_socket() { return &methods_sockp; } | |
| BIO *BIO_new_socket(int fd, int close_flag) { | |
| BIO *ret; | |
| ret = BIO_new(BIO_s_socket()); | |
| if (ret == nullptr) { | |
| return nullptr; | |
| } | |
| BIO_set_fd(ret, fd, close_flag); | |
| return ret; | |
| } | |
| // These functions are provided solely for compatibility with software that | |
| // tries to copy and then modify `BIO_s_socket`. See bio.h for details. | |
| // PostgreSQL's use makes several fragile assumptions on `BIO_s_socket`: | |
| // | |
| // - We do not store anything in `BIO_set_data`. (Broken in upstream OpenSSL, | |
| // which broke PostgreSQL.) | |
| // - We do not store anything in `BIO_set_app_data`. | |
| // - `BIO_s_socket` is implemented internally using the non-`size_t`-clean | |
| // I/O functions rather than the `size_t`-clean ones. | |
| // - `BIO_METHOD` never gains another function pointer that is used in concert | |
| // with any of the functions here. | |
| // | |
| // Some other projects doing similar things use `BIO_meth_get_read` and | |
| // `BIO_meth_get_write` and in turn assume that `BIO_s_socket` has not been | |
| // ported to the `size_t`-clean `BIO_read_ex` and `BIO_write_ex`. (Not yet | |
| // implemented in BoringSSL.) | |
| // | |
| // This is hopelessly fragile. PostgreSQL 18 will include a fix to stop using | |
| // these APIs, but older versions and other software remain impacted, so we | |
| // implement these functions, but only support `BIO_s_socket`. For now they just | |
| // return the underlying functions, but if we ever need to break the above | |
| // assumptions, we can return an older, frozen version of `BIO_s_socket`. | |
| // Limiting to exactly one allowed `BIO_METHOD` lets us do this. | |
| // | |
| // These functions are also deprecated in upstream OpenSSL. See | |
| // https://github.com/openssl/openssl/issues/26047 | |
| // | |
| // TODO(davidben): Once Folly and all versions of PostgreSQL we care about are | |
| // updated or patched, remove these functions. | |
| int (*BIO_meth_get_write(const BIO_METHOD *method))(BIO *, const char *, int) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return method->bwrite; | |
| } | |
| int (*BIO_meth_get_read(const BIO_METHOD *method))(BIO *, char *, int) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return method->bread; | |
| } | |
| int (*BIO_meth_get_gets(const BIO_METHOD *method))(BIO *, char *, int) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return method->bgets; | |
| } | |
| int (*BIO_meth_get_puts(const BIO_METHOD *method))(BIO *, const char *) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return nullptr; | |
| } | |
| long (*BIO_meth_get_ctrl(const BIO_METHOD *method))(BIO *, int, long, void *) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return method->ctrl; | |
| } | |
| int (*BIO_meth_get_create(const BIO_METHOD *method))(BIO *) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return method->create; | |
| } | |
| int (*BIO_meth_get_destroy(const BIO_METHOD *method))(BIO *) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return method->destroy; | |
| } | |
| long (*BIO_meth_get_callback_ctrl(const BIO_METHOD *method))(BIO *, int, | |
| bio_info_cb) { | |
| BSSL_CHECK(method == BIO_s_socket()); | |
| return method->callback_ctrl; | |
| } | |
| #endif // OPENSSL_NO_SOCK |