Skip to content

Conversation

theuni
Copy link
Member

@theuni theuni commented Apr 27, 2017

This untangles our current connection logic and moves toward async connections/resolves. Before merging, I'd like to construct a matrix of different connection configs, and make sure that each one has test coverage. I'm PRing now to get a feel for whether this is reviewable as-is or not.

In short, switch from "here's what we know, connect please!" to "walk through the individual connection steps in-order".

This moves the entire process into one function, but the next step will be to break out the individual steps (input checking, resolving, connection, node creation).

See the individual commit messages for more detail.

theuni added 6 commits April 21, 2017 18:18
No functional change, just preparation for the following commits.
ConnectNode is only called by OpenNetworkConnection, and only in one place.
Combine them for preparation of splitting them into functions that are
friendlier for callbacks.
The next commits will move the connection logic out of netbase into net.
This is the next step towards asynchronous connections.

The current code confuses connection steps and tends to obfuscate what's going
on. Here, I've tried to create individual stages. The logic has all moved into
a single function for now:

- Global checks: Should any connection be attempted?
- Input checks: is there something valid to connect to?
- Resolving: If the address string may be valid, attempt to resolve it (if
    allowed by the user's dns settings and proxy config)
- Proxy config
- Socket creation
- Connect to the destination
- If proxied, attempt the remote connection
- Create the node with the resulting connection

With this done, resolves and connections can trigger callbacks so that a
connection attempt requires no return value. At that point, they can be made
asynchronous via libevent.
@gmaxwell
Copy link
Contributor

Vague Concept ACK.

@laanwj
Copy link
Member

laanwj commented May 2, 2017

utACK, going to test this

Copy link
Contributor

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Review turned up a few bugs that should be fixed, though most of them not your fault :/.

src/net.cpp Outdated
if (!fNetworkActive) {
return false;
}
if (IsLocal(addrConnect)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldnt this only be if !pszDest?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, thanks. Will fix.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note: Fixed in 1d94e2e.


addrman.Attempt(addrConnect, fCountFailure);
if (!strDest.empty() && addrDest.IsValid()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Move this into the previous block, and maybe move the addrDest.IsValid() check into it as well. Then you can always require addrDest.IsValid() for non-named addresses as well, which seems reasonable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Generally the point here is to have top-level ifs like if (strDest.empty()) and then put the logic inside of that, instead of repeated ifs for similar branches which are just confusing.

Copy link
Member Author

Choose a reason for hiding this comment

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

I avoided coalescing on purpose. The current code does a bunch of steps at once, depending on what the input is. That's what I'm trying to undo here.

The previous block handles resolving, and will be moved out to a new function as a next step. By keeping the connection flow separated like this, it's trivial to factor out that way.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahh, OK, didnt realize that the next step would be to pull it out.

Copy link
Contributor

Choose a reason for hiding this comment

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

By keeping the connection flow separated like this, it's trivial to factor out that way.

Probably there is something going over my head, but I don't understand the reasoning.

Currently this does:

if (condition) {
  // bunch of code
} else {
  // other code
}
if (equivalent condition) {
  // code that you want to factor out
}
if (equivalent condition) {
  // more code that you want to factor out
}

I don't see why it would be any harder to refactor later with:

if (condition) {
  // bunch of code
  // code that you want to factor out
  // more code that you want to factor out
} else {
  // other code
}

Copy link
Member Author

Choose a reason for hiding this comment

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

While it may make sense to lump a few actions together if they meet the same conditions now because a connection is just one long synchronous operation, my intention is to clarify which conditions really need to be met at which stage, so that we can break that operation up into smaller asynchronous ones.

Some of these tests (like whether or not we resolved successfully) will just move in an obvious way to callbacks, so I guess it makes sense to combine them here. Others though, will need to be re-checked on separate threads.

I'll clarify this in the code by setting some variables rather than repeating conditions, so that it's more clear what the intent is.

I'll rebase while I'm at it.

src/netbase.cpp Outdated
@@ -445,6 +439,19 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
if (!SetSocketNonBlocking(hSocket, true))
return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: needs a string change here for __func__.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, also think we need a CloseSocket here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will fix func.

SetSocketNonBlocking should take care of closing the socket, but an explicit one here wouldn't hurt either.

CloseSocket is really annoying. I'll RAII it directly after this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good, CloseSocket were half the issues here :(

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't seems strange for SetSocketNonBlocking to try to destroy the socket if it doesn't succeed. Maybe consider removing CloseSocket calls from SetSocketNonBlocking after adding the RAII here.

src/net.cpp Outdated
addrman.Attempt(addrDest, fCountFailure);

if (!connected) {
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not strictly your fault, but I think you're exposing a bug in Socks5 here - Socks5 fails to CloseSocket() prior to returning false in one place (Proxy username or password too long), so I think it'd be better to just CloseSocket here instead of further down to make it harder to miss such things.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed.

Copy link
Member

Choose a reason for hiding this comment

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

+1 for the SocketCloser

struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
Copy link
Contributor

Choose a reason for hiding this comment

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

Need a CloseSocket here (but, really, lets just move all the CloseSockets up to OpenNetworkConnection post-CreateSocket instead of them being sprinkled all over everywhere?).

Copy link
Member Author

Choose a reason for hiding this comment

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

Ugh. I'll see how much work it would be to RAII it as part of this pull.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note: fixed in 34133bd.

theuni added 2 commits May 2, 2017 17:31
- Only check IsLocal if pszDest is unset
- Fix function name string in CreateSocket
addrman.Attempt(addrConnect, fCountFailure);
// Create socket and make sure that it is closed in the event of an error
SOCKET hSocket = INVALID_SOCKET;
CSocketCloser sockCloser(hSocket);
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems strange to create the CSocketCloser and then give it a reference to the socket - why not just have the CSocketCloser hold the SOCKET variable (publicly) and then set it to INVALID_SOCKET upon close/release (and return it from release) to make it feel more like a unique_ptr?

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems strange to create the CSocketCloser and then give it a reference to the socket

It is strange, just like CloseSocket taking a reference is strange. But I'd think as long as one of these is taking a reference, the other probably should as well for consistency.

Copy link
Member Author

Choose a reason for hiding this comment

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

I gave it a reference because several calls further down the chain set the value to -1. For example, if ConnectSocketDirectly() fails. If storing a value, CSocketCloser would attempt to close it again at destruction if we weren't careful about resetting it, which would defeat the point of using RAII.

The alternative would be to audit for CloseSocket()s (or make a full-blown RAII CSocket with Close() as a member), which would make for a much larger diff.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

utACK 1d94e2e.

Would be nice to squash review fixes into place.

I don't think this PR is a pure improvement because OpenNetworkConnection is now a big monolith, but there are nice changes here and presumably there will be more cleanup in the future.

src/net.cpp Outdated
@@ -340,7 +340,7 @@ bool CConnman::CheckIncomingNonce(uint64_t nonce)
return true;
}

CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure)
CNode* CConnman::ConnectNode(const CAddress& addrConnect, const char *pszDest, bool fCountFailure)
Copy link
Contributor

Choose a reason for hiding this comment

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

In commit "net: pass a reference into ConnectNode and copy it there" (8245107)

Corresponding change to net.h is missing so this commit doesn't compile.

src/net.cpp Outdated
if (!fNetworkActive) {
return false;
}
if (IsLocal(addrConnect)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: Fixed in 1d94e2e.

src/netbase.cpp Outdated
@@ -445,6 +439,19 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
if (!SetSocketNonBlocking(hSocket, true))
return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't seems strange for SetSocketNonBlocking to try to destroy the socket if it doesn't succeed. Maybe consider removing CloseSocket calls from SetSocketNonBlocking after adding the RAII here.

struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: fixed in 34133bd.

return true;
}

bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocket, int nTimeout)
Copy link
Contributor

Choose a reason for hiding this comment

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

In commit "net: split socket creation out of connection"

This commit drops static, but the function isn't exposed publicly in a header until the next commit.

addrman.Attempt(addrConnect, fCountFailure);
// Create socket and make sure that it is closed in the event of an error
SOCKET hSocket = INVALID_SOCKET;
CSocketCloser sockCloser(hSocket);
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems strange to create the CSocketCloser and then give it a reference to the socket

It is strange, just like CloseSocket taking a reference is strange. But I'd think as long as one of these is taking a reference, the other probably should as well for consistency.


addrman.Attempt(addrConnect, fCountFailure);
if (!strDest.empty() && addrDest.IsValid()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

By keeping the connection flow separated like this, it's trivial to factor out that way.

Probably there is something going over my head, but I don't understand the reasoning.

Currently this does:

if (condition) {
  // bunch of code
} else {
  // other code
}
if (equivalent condition) {
  // code that you want to factor out
}
if (equivalent condition) {
  // more code that you want to factor out
}

I don't see why it would be any harder to refactor later with:

if (condition) {
  // bunch of code
  // code that you want to factor out
  // more code that you want to factor out
} else {
  // other code
}

@theuni
Copy link
Member Author

theuni commented May 18, 2017

Closing this for now. I'm working on getting this fully refactored with libevent instead.

Breaking this out and serializing the logic was very helpful as an intermediary step, but I now agree that it doesn't make much sense to review/merge those changes alone.

@theuni theuni closed this May 18, 2017
laanwj added a commit that referenced this pull request Sep 28, 2017
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of #10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Dec 22, 2019
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 2, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 4, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 12, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 12, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 12, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 12, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 12, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3

ConnectSocket -> ConnectSocketDirectly

Signed-off-by: Pasta <pasta@dashboost.org>
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jan 12, 2020
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3

ConnectSocket -> ConnectSocketDirectly

Signed-off-by: Pasta <pasta@dashboost.org>
gades pushed a commit to cosanta/cosanta-core that referenced this pull request Jun 25, 2021
b887676 net: remove now-unused functions (Cory Fields)
45fd754 net: remove now-superfluous numeric resolve (Cory Fields)
2416dd7 net: separate resolving and conecting (Cory Fields)

Pull request description:

  This is a greatly simplified version of bitcoin#10285, which only aims to address async resolving.

  It essentially breaks up two wrapper functions for things only used in one place (ConnectSocketDirectly/ConnectThroughProxy) in favor of calling them directly. This allows us to fully handle resolves before attempting a connection, as is necessary for async connections.

  As a bonus, I believe the logic is now much easier to follow than before.

Tree-SHA512: f03f618107379edf3efe2a9f3e3677e8f075017ab140a0b4fdc3b8263e6beff148d55256263ab10bc2125ef089ca68e0d8e865beeae176f1eca544e769c976d3

ConnectSocket -> ConnectSocketDirectly

Signed-off-by: Pasta <pasta@dashboost.org>
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants