Use RAII for addrinfo lifetime management#5399
Conversation
|
If this development is deemed appropriate, the following simplification can through a separate branch. diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp
index 56cc4a76a..f18f106ef 100644
--- a/src/cli/tls_client.cpp
+++ b/src/cli/tls_client.cpp
@@ -397,11 +397,8 @@ class TLS_Client final : public Command {
throw CLI_Error("getaddrinfo failed for " + host);
}
- socket_type fd = 0;
- bool success = false;
-
for(const addrinfo* rp = res.get(); rp != nullptr; rp = rp->ai_next) {
- fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ socket_type fd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if(fd == invalid_socket()) {
continue;
@@ -412,16 +409,10 @@ class TLS_Client final : public Command {
continue;
}
- success = true;
- break;
- }
-
- if(!success) {
- // no address succeeded
- throw CLI_Error("Connecting to host failed");
+ return fd;
}
-
- return fd;
+ // no address succeeded
+ throw CLI_Error("Connecting to host failed");
} |
63f65a0 to
49b3d32
Compare
49b3d32 to
11695c8
Compare
randombit
left a comment
There was a problem hiding this comment.
I'm a little skeptical of the nullptr checks to freeaddrinfo in that the most recent references I could find to any platforms actually having a problem with this are FreeBSD circa 2004 and Android circa 2014, and
Patches that complicate the code in order to support obsolete operating systems will likely be rejected.
That said it's a pretty minor complication and indeed - bizarrely IMO - latest SUS does not explicitly allow NULL to be passed to freeaddrinfo, though IMO this wording (emphasis mine) allows it
The freeaddrinfo() function shall support the freeing of arbitrary sublists of an addrinfo list originally returned by getaddrinfo().
Since how do you represent an arbitrary sublist of length zero but with a nullptr?
I completely agree with this principle. In business life, I see that maintaining dependency on old platforms significantly increases maintenance costs in the long run. Actually, my assessment here is largely in line with yours. Since while (ai) {
next = ai->ai_next;
free(ai);
ai = next;
}During development and look again today, what particularly caught my attention was that some examples did not allow early termination with a call to https://linux.die.net/man/3/freeaddrinfo struct addrinfo hints;
// ...
s = getaddrinfo(NULL, argv[1], &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
close(sfd);
}
if (rp == NULL) { /* No address succeeded */
fprintf(stderr, "Could not bind\n");
exit(EXIT_FAILURE);
}
freeaddrinfo(result); /* No longer needed */I also saw this issue specifically addressed in the following projects (2018, 2019, 2023):
As you mentioned, it has a very small cost (a single if). However, I am aware that this could be an overly protective approach. I am considering addressing the above comment as a separate PR. If you prefer, we can remove this change or leave it as is. Thank you for your time, review, and sharing your knowledge. Best regards. |
Hello,
It has been over a year since my PR #4660, and I honestly don't remember many of the details. While revisiting that branch, I identified changes that could independently contribute to the project and decided to submit them as separate PRs.
This PR applies RAII-based lifetime management to addrinfo resources returned by
getaddrinfo(). In the current codebase,::freeaddrinfo()is called manually, which can leak if an exception is thrown before the cleanup call is reached.The changes are based on the approach discussed with @reneme in this review comment (Since it is marked as resolved, you cannot directly move to the comment.): a
std::unique_ptrwith a stateless lambda deleter combined withBotan::out_ptr()fromstl_util.h.Quotes:
@KaganCanSit
@reneme
The using declarations are defined as private members in each class since there is currently no shared internal header across socket files. (
socket_utils.hexists but is external and not included, I think there is a reason for this.)I edited the
bogo_shim.cppfile to ensure full compatibility with the others and to allow direct intervention whenBotan::out_ptris removed. However, if I am mistaken, please let me know.Compile and Test
I am happy to make further adjustments based on your feedback.
Best regards.