Bug: 412269080 Change-Id: Iab4951efc53005d9aa5e02067da65561bcffcf1c Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/79568 Reviewed-by: Adam Langley <agl@google.com> Auto-Submit: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
211 lines
5.7 KiB
C++
211 lines
5.7 KiB
C++
// 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>
|
|
|
|
#if !defined(OPENSSL_WINDOWS)
|
|
#include <unistd.h>
|
|
#else
|
|
#include <winsock2.h>
|
|
OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib"))
|
|
#endif
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
#if !defined(OPENSSL_WINDOWS)
|
|
static int closesocket(int sock) {
|
|
return close(sock);
|
|
}
|
|
#endif
|
|
|
|
static int sock_free(BIO *bio) {
|
|
if (bio->shutdown) {
|
|
if (bio->init) {
|
|
closesocket(bio->num);
|
|
}
|
|
bio->init = 0;
|
|
bio->flags = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int sock_read(BIO *b, char *out, int outl) {
|
|
if (out == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
bio_clear_socket_error();
|
|
#if defined(OPENSSL_WINDOWS)
|
|
int ret = recv(b->num, out, outl, 0);
|
|
#else
|
|
int ret = (int)read(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(BIO *b, const char *in, int inl) {
|
|
bio_clear_socket_error();
|
|
#if defined(OPENSSL_WINDOWS)
|
|
int ret = send(b->num, in, inl, 0);
|
|
#else
|
|
int ret = (int)write(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 ret;
|
|
}
|
|
|
|
static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) {
|
|
switch (cmd) {
|
|
case BIO_C_SET_FD:
|
|
sock_free(b);
|
|
b->num = *static_cast<int *>(ptr);
|
|
b->shutdown = static_cast<int>(num);
|
|
b->init = 1;
|
|
return 1;
|
|
case BIO_C_GET_FD:
|
|
if (b->init) {
|
|
int *out = static_cast<int*>(ptr);
|
|
if (out != nullptr) {
|
|
*out = b->num;
|
|
}
|
|
return b->num;
|
|
}
|
|
return -1;
|
|
case BIO_CTRL_GET_CLOSE:
|
|
return b->shutdown;
|
|
case BIO_CTRL_SET_CLOSE:
|
|
b->shutdown = 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",
|
|
sock_write,
|
|
sock_read,
|
|
nullptr /* gets, */,
|
|
sock_ctrl,
|
|
nullptr /* create */,
|
|
sock_free,
|
|
nullptr /* callback_ctrl */,
|
|
};
|
|
|
|
const BIO_METHOD *BIO_s_socket(void) { return &methods_sockp; }
|
|
|
|
BIO *BIO_new_socket(int fd, int close_flag) {
|
|
BIO *ret;
|
|
|
|
ret = BIO_new(BIO_s_socket());
|
|
if (ret == NULL) {
|
|
return NULL;
|
|
}
|
|
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
|