Files
cli/vendor/aws-lc-sys/aws-lc/crypto/bio/dgram.c

484 lines
12 KiB
C

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/bio.h>
#if !defined(OPENSSL_NO_SOCK)
#include <openssl/mem.h>
#include "../internal.h"
#include "./internal.h"
#if !defined(OPENSSL_WINDOWS)
#include <sys/time.h>
static int closesocket(const int sock) { return close(sock); }
#else
OPENSSL_MSVC_PRAGMA(warning(push, 3))
#include <winsock2.h>
OPENSSL_MSVC_PRAGMA(warning(pop))
#endif
typedef struct bio_dgram_data_st {
BIO_ADDR peer;
unsigned int connected;
unsigned int _errno;
} bio_dgram_data;
static socklen_t BIO_ADDR_sockaddr_size(const BIO_ADDR *bap) {
GUARD_PTR(bap);
if (bap->sa.sa_family == AF_INET) {
return sizeof(bap->s_in);
}
#ifdef AF_INET6
if (bap->sa.sa_family == AF_INET6) {
return sizeof(bap->s_in6);
}
#endif
#ifdef AWS_LC_HAS_AF_UNIX
if (bap->sa.sa_family == AF_UNIX) {
return sizeof(bap->s_un);
}
#endif
return sizeof(*bap);
}
static struct sockaddr *BIO_ADDR_sockaddr_noconst(BIO_ADDR *bap) {
GUARD_PTR(bap);
return &bap->sa;
}
static const struct sockaddr *BIO_ADDR_sockaddr(const BIO_ADDR *bap) {
GUARD_PTR(bap);
return &bap->sa;
}
static int dgram_get_errno(void) {
#if defined(OPENSSL_WINDOWS)
return WSAGetLastError();
#else
return errno;
#endif
}
static int dgram_write(BIO *bp, const char *in, const int in_len) {
GUARD_PTR(bp);
GUARD_PTR(in);
ssize_t result;
bio_dgram_data *data = bp->ptr;
GUARD_PTR(data);
if (in_len < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
return -1;
}
bio_clear_socket_error(bp->num);
if (data->connected) {
// With a zero flags argument, send() is equivalent to write(2).
result = send(bp->num, in, in_len, 0);
} else {
// If a peer address has been pre-specified, sendto may return -1 and set
// errno to[EISCONN].
const socklen_t peerlen = BIO_ADDR_sockaddr_size(&data->peer);
result =
sendto(bp->num, in, in_len, 0, BIO_ADDR_sockaddr(&data->peer), peerlen);
}
if (result < INT_MIN || result > INT_MAX) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
return -1;
}
const int ret = result;
BIO_clear_retry_flags(bp);
if (ret <= 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_write(bp);
}
data->_errno = dgram_get_errno();
}
return ret;
}
static int dgram_read(BIO *bp, char *out, const int out_len) {
GUARD_PTR(bp);
GUARD_PTR(out);
GUARD_PTR(bp->ptr);
BIO_ADDR peer;
// Might be modified by call to `recvfrom`.
socklen_t len = sizeof(peer);
bio_dgram_data *data = bp->ptr;
bio_clear_socket_error(bp->num);
if (out_len < 0) {
// out_len is cast to `size_t` below.
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
return -1;
}
// recvfrom may be used to receive data on a socket regardless of whether
// it's connection-oriented.
const ssize_t result = recvfrom(bp->num, out, out_len, 0,
BIO_ADDR_sockaddr_noconst(&peer), &len);
if (result < INT_MIN || result > INT_MAX) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
return -1;
}
const int ret = result;
if (!data->connected && ret >= 0) {
if (1 != BIO_dgram_set_peer(bp, &peer)) {
// The operation does not fail if peer not set.
}
}
BIO_clear_retry_flags(bp);
if (ret < 0) {
if (bio_socket_should_retry(ret)) {
BIO_set_retry_read(bp);
}
data->_errno = dgram_get_errno();
}
return ret;
}
static int dgram_puts(BIO *bp, const char *str) {
GUARD_PTR(bp);
GUARD_PTR(str);
const int len = OPENSSL_strnlen(str, INT_MAX);
return dgram_write(bp, str, len);
}
static int dgram_free(BIO *bp) {
GUARD_PTR(bp);
if (bp->shutdown && bp->init) {
if (0 != closesocket(bp->num)) {
// the show must go on
}
}
bp->init = 0;
bp->num = -1;
bp->flags = 0;
OPENSSL_free(bp->ptr);
bp->ptr = NULL;
return 1;
}
static int dgram_new(BIO *bio) {
bio->init = 0;
bio->num = -1;
bio->ptr = OPENSSL_zalloc(sizeof(bio_dgram_data));
return bio->ptr != NULL;
}
static long dgram_ctrl(BIO *bp, const int cmd, const long num, void *ptr) {
GUARD_PTR(bp);
bio_dgram_data *data = bp->ptr;
long ret = 1;
switch (cmd) {
case BIO_C_SET_FD:
GUARD_PTR(ptr);
int fd = *(int *)ptr;
if (fd < 0) {
// file descriptors must be non-negative.
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
return 0;
}
dgram_free(bp);
dgram_new(bp);
bp->num = fd;
bp->shutdown = (int)num;
bp->init = 1;
break;
case BIO_C_GET_FD:
if (bp->init) {
int *ip = ptr;
if (ip) {
*ip = bp->num;
}
ret = bp->num;
} else {
ret = -1;
}
break;
case BIO_CTRL_GET_CLOSE:
ret = bp->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
bp->shutdown = num != 0;
break;
case BIO_CTRL_FLUSH:
ret = 1;
break;
case BIO_CTRL_DGRAM_SET_CONNECTED:
GUARD_PTR(data);
if (ptr != NULL) {
data->connected = 1;
ret = BIO_ADDR_copy(&data->peer, ptr);
} else {
data->connected = 0;
OPENSSL_cleanse(&data->peer, sizeof(data->peer));
ret = 1;
}
break;
case BIO_CTRL_DGRAM_GET_PEER: {
GUARD_PTR(data);
GUARD_PTR(ptr);
const socklen_t size = BIO_ADDR_sockaddr_size(&data->peer);
if (num == 0 || num >= (long)size) {
OPENSSL_memcpy(ptr, &data->peer, size);
ret = size;
} else {
ret = 0;
}
break;
}
case BIO_CTRL_DGRAM_CONNECT:
case BIO_CTRL_DGRAM_SET_PEER:
GUARD_PTR(data);
GUARD_PTR(ptr);
ret = BIO_ADDR_copy(&data->peer, ptr);
break;
case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: {
GUARD_PTR(data);
int d_errno = 0;
#ifdef OPENSSL_WINDOWS
d_errno = (data->_errno == WSAETIMEDOUT);
#else
/*
* if no data has been transferred and the timeout has been reached,
* then -1 is returned with errno set to EAGAIN or EWOULDBLOCK,
* or EINPROGRESS (for connect(2)) just as if the socket was specified
* to be nonblocking.
*/
d_errno = data->_errno == EAGAIN || data->_errno == EWOULDBLOCK ||
data->_errno == EINPROGRESS;
#endif
if (d_errno) {
ret = 1;
data->_errno = 0;
} else {
ret = 0;
}
break;
}
#if defined(SO_RCVTIMEO)
case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: {
GUARD_PTR(ptr);
struct timeval *tv = (struct timeval *)ptr;
// Check for negative values in timeval
if (tv->tv_sec < 0 || tv->tv_usec < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
ret = -1;
break;
}
#ifdef OPENSSL_WINDOWS
DWORD timeout = (DWORD)(tv->tv_sec * 1000 + tv->tv_usec / 1000);
if (setsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO,
(void *)&timeout, sizeof(timeout)) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#else
if (setsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
sizeof(struct timeval)) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#endif
break;
}
case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: {
GUARD_PTR(ptr);
union {
size_t s;
int i;
} sz = {0};
#ifdef OPENSSL_WINDOWS
int timeout;
struct timeval *tv = (struct timeval *)ptr;
sz.i = sizeof(timeout);
if (getsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO,
(void *)&timeout, &sz.i) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else {
tv->tv_sec = timeout / 1000;
tv->tv_usec = (timeout % 1000) * 1000;
ret = sizeof(*tv);
}
#else
sz.i = sizeof(struct timeval);
if (getsockopt(bp->num, SOL_SOCKET, SO_RCVTIMEO,
ptr, (void *)&sz) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
ret = (int)sz.s;
} else {
ret = sz.i;
}
#endif
break;
}
#endif
#if defined(SO_SNDTIMEO)
case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: {
GUARD_PTR(ptr);
struct timeval *tv = (struct timeval *)ptr;
// Check for negative values in timeval
if (tv->tv_sec < 0 || tv->tv_usec < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_INVALID_ARGUMENT);
ret = -1;
break;
}
#ifdef OPENSSL_WINDOWS
DWORD timeout = (DWORD)(tv->tv_sec * 1000 + tv->tv_usec / 1000);
if (setsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO,
(void *)&timeout, sizeof(timeout)) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#else
if (setsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
sizeof(struct timeval)) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
}
#endif
break;
}
case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: {
GUARD_PTR(ptr);
union {
size_t s;
int i;
} sz = {0};
#ifdef OPENSSL_WINDOWS
int timeout;
struct timeval *tv = (struct timeval *)ptr;
sz.i = sizeof(timeout);
if (getsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO,
(void *)&timeout, &sz.i) != 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else {
tv->tv_sec = timeout / 1000;
tv->tv_usec = (timeout % 1000) * 1000;
ret = sizeof(*tv);
}
#else
sz.i = sizeof(struct timeval);
if (getsockopt(bp->num, SOL_SOCKET, SO_SNDTIMEO,
ptr, (void *)&sz) < 0) {
OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
ret = -1;
} else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
ret = (int)sz.s;
} else {
ret = sz.i;
}
#endif
break;
}
#endif
default:
ret = 0;
break;
}
return ret;
}
static const BIO_METHOD methods_dgramp = {
.type = BIO_TYPE_DGRAM,
.name = "datagram socket",
.bwrite = dgram_write,
.bread = dgram_read,
.bputs = dgram_puts,
.bgets = NULL,
.ctrl = dgram_ctrl,
.create = dgram_new,
.destroy = dgram_free,
.callback_ctrl = NULL,
};
const BIO_METHOD *BIO_s_datagram(void) { return &methods_dgramp; }
BIO *BIO_new_dgram(int fd, int close_flag) {
BIO *ret = BIO_new(BIO_s_datagram());
if (ret == NULL) {
return NULL;
}
int result = BIO_set_fd(ret, fd, close_flag);
if (result <= 0) {
BIO_free(ret);
return NULL;
}
return ret;
}
int BIO_ctrl_dgram_connect(BIO *bp, const BIO_ADDR *peer) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_CONNECT, 0, (BIO_ADDR *)peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_ctrl_set_connected(BIO *bp, const BIO_ADDR *peer) {
const long ret =
BIO_ctrl(bp, BIO_CTRL_DGRAM_SET_CONNECTED, 0, (BIO_ADDR *)peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_recv_timedout(BIO *bp) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP, 0, NULL);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_send_timedout(BIO *bp) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP, 0, NULL);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_get_peer(BIO *bp, BIO_ADDR *peer) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_GET_PEER, 0, peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
int BIO_dgram_set_peer(BIO *bp, const BIO_ADDR *peer) {
const long ret = BIO_ctrl(bp, BIO_CTRL_DGRAM_SET_PEER, 0, (BIO_ADDR *)peer);
if (ret < INT_MIN || ret > INT_MAX) {
return 0;
}
return ret;
}
#endif