Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do the SOCKS5 handshake reliably #28649

Merged
merged 2 commits into from Nov 7, 2023

Conversation

vasild
Copy link
Contributor

@vasild vasild commented Oct 13, 2023

The Socks5() function which does the SOCKS5 handshake with the SOCKS5 proxy sends bytes to the socket without retrying partial writes.

send(2) may write only part of the provided data and return. In this case the caller is responsible for retrying the operation with the remaining data. Change Socks5() to do that. There is already a method Sock::SendComplete() which does exactly that, so use it in Socks5().

A minor complication for this PR is that Sock::SendComplete() takes std::string argument whereas Socks5() has std::vector<uint8_t>. Thus the necessity for the first commit. It is possible to do also in other ways - convert the data in Socks5() to std::string or have just one Sock::SendComplete() that takes void* and change the callers to pass str.data(), str.size() or vec.data(), vec.size().

This came up while testing #27375.

@DrahtBot
Copy link
Contributor

DrahtBot commented Oct 13, 2023

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK jonatack, pinheadmz, achow101
Concept ACK laanwj

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #28690 (build: Introduce internal kernel library by TheCharlatan)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@pinheadmz
Copy link
Member

concept ACK 608f8aa

Reviewed all code changes and built locally. I don't see anything wrong with this simple update, it makes a lot of sense and is implemented cleanly. Later this week I'll try to test it if I can, I know its covering a sort of intermittent failure but there might be a way to see it work in action, maybe in combination with #27375

@jonatack
Copy link
Contributor

Concept ACK. In 70917bd would it make sense to use Span?

src/util/sock.h Outdated
/**
* Convenience method, equivalent to `SendComplete(data.data(), data.size(), timeout, interrupt)`.
*/
virtual void SendComplete(const std::vector<uint8_t>& data,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving a discussion from the main thread here

would it make sense to use Span?

Maybe. What would be the purpose? To unite the two SendComplete(const std::string& and SendComplete(const std::vector<uint8_t>& into one method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, possibly, when I see range-like objects with data and size attributes, it reminds me of Span and this section from src/span.h:

 * - Due to Span's automatic creation from range-like objects (arrays, and data
 *   types that expose a data() and size() member function), functions that
 *   accept a Span as input parameter can be called with any compatible
 *   range-like object. For example, this works:

Feel free to ignore that and the following:

could update the headers a little

--- a/src/test/fuzz/socks5.cpp
+++ b/src/test/fuzz/socks5.cpp
@@ -2,13 +2,12 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-#include <netaddress.h>
 #include <netbase.h>
 #include <test/fuzz/FuzzedDataProvider.h>
 #include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
 #include <test/fuzz/util/net.h>
 #include <test/util/setup_common.h>
+#include <util/threadinterrupt.h>

--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -2,7 +2,6 @@
 
-#include <common/system.h>
 #include <compat/compat.h>

--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -6,7 +6,6 @@
 
 #include <compat/compat.h>
-#include <util/threadinterrupt.h>
 #include <util/time.h>
 
 @ -14,6 +13,8 @@
 #include <unordered_map>
 
+class CThreadInterrupt;
+

Copy link
Contributor

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 608f8aa

src/util/sock.h Outdated
/**
* Convenience method, equivalent to `SendComplete(data.data(), data.size(), timeout, interrupt)`.
*/
virtual void SendComplete(const std::vector<uint8_t>& data,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, possibly, when I see range-like objects with data and size attributes, it reminds me of Span and this section from src/span.h:

 * - Due to Span's automatic creation from range-like objects (arrays, and data
 *   types that expose a data() and size() member function), functions that
 *   accept a Span as input parameter can be called with any compatible
 *   range-like object. For example, this works:

Feel free to ignore that and the following:

could update the headers a little

--- a/src/test/fuzz/socks5.cpp
+++ b/src/test/fuzz/socks5.cpp
@@ -2,13 +2,12 @@
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
-#include <netaddress.h>
 #include <netbase.h>
 #include <test/fuzz/FuzzedDataProvider.h>
 #include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
 #include <test/fuzz/util/net.h>
 #include <test/util/setup_common.h>
+#include <util/threadinterrupt.h>

--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -2,7 +2,6 @@
 
-#include <common/system.h>
 #include <compat/compat.h>

--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -6,7 +6,6 @@
 
 #include <compat/compat.h>
-#include <util/threadinterrupt.h>
 #include <util/time.h>
 
 @ -14,6 +13,8 @@
 #include <unordered_map>
 
+class CThreadInterrupt;
+

@laanwj
Copy link
Member

laanwj commented Oct 31, 2023

Concept ACK. Good catch!

@laanwj laanwj added the P2P label Oct 31, 2023
This would make it easier to pass other than `std::string` types,
to be used in the `Socks5()` function.
`send(2)` can be interrupted or for another reason it may not fully
complete sending all the bytes. We should be ready to retry the send
with the remaining bytes. This is what `Sock::SendComplete()` does,
thus use it in `Socks5()`.

Since `Sock::SendComplete()` takes a `CThreadInterrupt` argument,
change also the recv part of `Socks5()` to use `CThreadInterrupt`
instead of a boolean.

Easier reviewed with `git show -b` (ignore white-space changes).
@vasild
Copy link
Contributor Author

vasild commented Oct 31, 2023

608f8aa1c4...af0fca530e: use a Span for Sock::SendComplete(), looks better, thanks for the suggestion.

Copy link
Contributor

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK af0fca5

/**
* Convenience method, equivalent to `SendComplete(MakeUCharSpan(data), timeout, interrupt)`.
*/
virtual void SendComplete(Span<const char> data,
std::chrono::milliseconds timeout,
CThreadInterrupt& interrupt) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first commit could be smaller and simpler/clearer (feel free to ignore).

@@ -283,13 +283,6 @@ void Sock::SendComplete(Span<const unsigned char> data,
     }
 }
 
-void Sock::SendComplete(Span<const char> data,
-                        std::chrono::milliseconds timeout,
-                        CThreadInterrupt& interrupt) const
-{
-    SendComplete(MakeUCharSpan(data), timeout, interrupt);
-}
-
 std::string Sock::RecvUntilTerminator(uint8_t terminator,
                                       std::chrono::milliseconds timeout,
                                       CThreadInterrupt& interrupt,
diff --git a/src/util/sock.h b/src/util/sock.h
index 65e7ffc1650..17bab33feaa 100644
--- a/src/util/sock.h
+++ b/src/util/sock.h
@@ -228,16 +228,11 @@ public:
      * @throws std::runtime_error if the operation cannot be completed. In this case only some of
      * the data will be written to the socket.
      */
-    virtual void SendComplete(Span<const unsigned char> data,
-                              std::chrono::milliseconds timeout,
-                              CThreadInterrupt& interrupt) const;
-
-    /**
-     * Convenience method, equivalent to `SendComplete(MakeUCharSpan(data), timeout, interrupt)`.
-     */
-    virtual void SendComplete(Span<const char> data,
-                              std::chrono::milliseconds timeout,
-                              CThreadInterrupt& interrupt) const;
+    virtual void SendComplete(Span<const unsigned char> data, std::chrono::milliseconds timeout, CThreadInterrupt& interrupt) const;
+    virtual void SendComplete(Span<const char> data, std::chrono::milliseconds timeout, CThreadInterrupt& interrupt) const
+    {
+        SendComplete(MakeUCharSpan(data), timeout, interrupt);
+    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to keep all implementation in one place and to keep the interface separate from the implementation.

Copy link
Member

@pinheadmz pinheadmz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK af0fca5

Built and ran tests locally. Confirmed the changes since my last ACK are using Span instead of data and len

Show Signature
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

ACK af0fca530e4d8311bcb24a14c416e5ad7c30ff78
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmVC0dcACgkQ5+KYS2KJ
yToWvw/+JIyhSZGWsQz2aeiSo/NPEFUShQLyI6MB/dNiotx/WvbzwR5PkUWzg067
hJnFgdo3sS89WTLDoqpewspXgXsDcWyhaKBep8jifnxAWow+jw+DdjZO9UqnJZwN
g4T/URcXWVjYyIQY4C9OftUxfuRmIK8cUk6BDGSDNg2dxhdcaoez3DjcUHJuFFRa
vL/b96E9wuT3qHBXlCMvjvVE/hVOeSXk7sjGuGZMgtPsYl2TdH9VF1WSGh6rIAD6
Tf70Gf3smCj8samcs+TWvifqD4DTS4186Pu6F3P9WdVeyI+nZxirU2hizeQTRzmN
a8f4YcGDHNuHHSnX2es7ZelDWqfl0DfyrOVSiYr5Llac/R3Jwpb05lLw9tCktby6
AOW1Z8y7i3BAQfK6fLqJubGksr0JPiYs7HO0g3Funeu8mId7LZsSqXd6LRdgfmRj
/C8b+SlDFPd2VNa2r/X46hlBiIw+UbQ6CxO/IEkH6KbMrM7ybi3GsSrknclh9pZ3
ToUdhEkZJLjiO/O0gxQ+W+5aSJI6m79ReSndFxnbC0WHJgAz2osyxaDgr/Fljdyp
IHY0D4UjPMUVLAHwGRgbbGeePgYyO8CshI+78rIxTbbAoBS7QU0O+nDhWtd3Bfv4
+rFQnZQ8cB/J5fiINYj+T6DNmNso8IU2US4bVLvpVCyxc7rsIis=
=WJhV
-----END PGP SIGNATURE-----

pinheadmz's public key is on keybase

@achow101
Copy link
Member

achow101 commented Nov 7, 2023

ACK af0fca5

@achow101 achow101 merged commit 0528cfd into bitcoin:master Nov 7, 2023
16 checks passed
@vasild vasild deleted the reliable_socks5_handshake branch April 19, 2024 13:46
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Apr 24, 2024
This would make it easier to pass other than `std::string` types,
to be used in the `Socks5()` function.

Github-Pull: bitcoin#28649
Rebased-From: 1b19d11
luke-jr pushed a commit to bitcoinknots/bitcoin that referenced this pull request Apr 24, 2024
`send(2)` can be interrupted or for another reason it may not fully
complete sending all the bytes. We should be ready to retry the send
with the remaining bytes. This is what `Sock::SendComplete()` does,
thus use it in `Socks5()`.

Since `Sock::SendComplete()` takes a `CThreadInterrupt` argument,
change also the recv part of `Socks5()` to use `CThreadInterrupt`
instead of a boolean.

Easier reviewed with `git show -b` (ignore white-space changes).

Github-Pull: bitcoin#28649
Rebased-From: af0fca5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants