diff options
author | Alexandr Nedvedicky <sashan@openssl.org> | 2024-01-26 08:05:47 +0100 |
---|---|---|
committer | Tomas Mraz <tomas@openssl.org> | 2024-04-16 16:37:18 +0200 |
commit | dfeaa1aa4e8849509e5472e9f197a48d46ace63c (patch) | |
tree | 9d1169460758cb5e750122a9893bcdf831f1e61a /crypto | |
parent | 0052d297f7eccc05d8310dbcaa81f0355b60c375 (diff) |
OpenSSL 3.2.0, QUIC, macOS, error 56 on connected UDP socket
current `translate_msg()` function attempts to set `->msg_name`
(and `->msg_namelen`) with `BIO`'s peer name (connection destination)
regardless if underlying socket is connected or not. Such implementation
uncovers differences in socket implementation between various OSes.
As we have learned hard way `sendmsg()` and `sendmmsg()` on `OpenBSD`
and (`MacOS` too) fail to send messages with `->msg_name` being
set on connected socket. In such case the caller receives
`EISCON` errro.
I think `translate_msg()` caller should provide a hint to indicate
whether we deal with connected (or un-connected) socket. For
connected sockets the peer's name should not be set/filled
by `translate_msg()`. On the other hand if socket is un-connected,
then `translate_msg()` must populate `->msg_name` and `->msg_namelen`
members.
The caller can use `getpeername(2)` to see if socket is
connected. If `getpeername()` succeeds then we must be dealing
with connected socket and `translate_msg()` must not set
`->msg_name` and `->msg_namelen` members. If `getpeername(2)`
fails, then `translate_msg()` must provide peer's name (destination
address) in `->msg_name` and set `->msg_namelen` accordingly.
The propposed fix introduces `is_connected()` function,
which applies `getpeername()` to socket bound to `BIO` instance.
The `dgram_sendmmsg()` uses `is_connected()` as a hint
for `translate_msg()` function, so msghdr gets initialized
with respect to socket state.
The change also modifies existing `test/quic_client_test.c`
so it also covers the case of connected socket. To keep
things simple we can introduce optional argument `connect_first`
to `./quic_client_test` function. Without `connect_first`
the test run as usual. With `connect_first` the test creates
and connects socket first. Then it passes such socket to
`BIO` sub-system to perform `QUIC` connect test as usual.
Fixes #23251
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23396)
(cherry picked from commit c062403abd71550057b3647b01cc8af4cc2fc18c)
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/bio/bss_dgram.c | 28 |
1 files changed, 21 insertions, 7 deletions
diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c index 7dbdfe181a..f6d688b353 100644 --- a/crypto/bio/bss_dgram.c +++ b/crypto/bio/bss_dgram.c @@ -560,6 +560,8 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr) socklen_t addr_len; BIO_ADDR addr; # endif + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); data = (bio_dgram_data *)b->ptr; @@ -577,6 +579,10 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr) b->shutdown = (int)num; b->init = 1; dgram_update_local_addr(b); + if (getpeername(b->num, (struct sockaddr *)&ss, &ss_len) == 0) { + BIO_ADDR_make(&data->peer, BIO_ADDR_sockaddr((BIO_ADDR *)&ss)); + data->connected = 1; + } # if defined(SUPPORT_LOCAL_ADDR) if (data->local_addr_enabled) { if (enable_local_addr(b, 1) < 1) @@ -1067,19 +1073,27 @@ static void translate_msg_win(BIO *b, WSAMSG *mh, WSABUF *iov, static void translate_msg(BIO *b, struct msghdr *mh, struct iovec *iov, unsigned char *control, BIO_MSG *msg) { + bio_dgram_data *data; + iov->iov_base = msg->data; iov->iov_len = msg->data_len; - /* macOS requires msg_namelen be 0 if msg_name is NULL */ - mh->msg_name = msg->peer != NULL ? &msg->peer->sa : NULL; - if (msg->peer != NULL && dgram_get_sock_family(b) == AF_INET) - mh->msg_namelen = sizeof(struct sockaddr_in); + data = (bio_dgram_data *)b->ptr; + if (data->connected == 0) { + /* macOS requires msg_namelen be 0 if msg_name is NULL */ + mh->msg_name = msg->peer != NULL ? &msg->peer->sa : NULL; + if (msg->peer != NULL && dgram_get_sock_family(b) == AF_INET) + mh->msg_namelen = sizeof(struct sockaddr_in); # if OPENSSL_USE_IPV6 - else if (msg->peer != NULL && dgram_get_sock_family(b) == AF_INET6) - mh->msg_namelen = sizeof(struct sockaddr_in6); + else if (msg->peer != NULL && dgram_get_sock_family(b) == AF_INET6) + mh->msg_namelen = sizeof(struct sockaddr_in6); # endif - else + else + mh->msg_namelen = 0; + } else { + mh->msg_name = NULL; mh->msg_namelen = 0; + } mh->msg_iov = iov; mh->msg_iovlen = 1; |