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

Revert back to BR socket code instead of poll based BTC socket #1408

Merged
merged 2 commits into from
Aug 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 110 additions & 15 deletions src/spv/bitcoin/BRPeer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,31 +893,126 @@ static double _peerGetMempoolTime (BRPeerContext *ctx) {
return value;
}

static void *_peerThreadRoutine(void *arg)
static int _BRPeerOpenSocket(BRPeer *peer, int domain, double timeout, int *error)
{
BRPeer *peer = (BRPeer *)arg;
BRPeerContext *ctx = (BRPeerContext *)arg;
int error = 0;
SOCKET socket;
BRPeerContext *ctx = (BRPeerContext *)peer;
struct sockaddr_storage addr;
struct timeval tv;
fd_set fds;
socklen_t addrLen, optLen;
int count, arg = 0, err = 0, on = 1, r = 1;
SOCKET sock = INVALID_SOCKET;

threadCleanup guard(ctx->threadCleanup, ctx->info);
ctx->lock.lock();
sock = ctx->socket = socket(domain, SOCK_STREAM, IPPROTO_TCP);
ctx->lock.unlock();

const auto hostName{BRPeerHostString(peer)};
bool connected{false};
CService resolved;
if (sock == INVALID_SOCKET) {
err = WSAGetLastError();
r = 0;
}
else {
#ifdef WIN32
int set = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
#else
tv.tv_sec = 1; // one second timeout for send/receive, so thread doesn't block for too long
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (sockopt_arg_type) &tv, sizeof(tv));
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (sockopt_arg_type) &tv, sizeof(tv));
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (sockopt_arg_type) &on, sizeof(on));
#ifdef SO_NOSIGPIPE // BSD based systems have a SO_NOSIGPIPE socket option to supress SIGPIPE signals
setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
#endif
#endif

if (Lookup(hostName.c_str(), resolved, peer->port, fNameLookup && !HaveNameProxy())) {
CAddress addrConnect{resolved, NODE_NONE};
#ifdef WIN32
u_long nBlock = 1; // Non-Blocking
if (ioctlsocket(sock, FIONBIO, &nBlock) == SOCKET_ERROR) {
r = 0;
}
#else
arg = fcntl(sock, F_GETFL, NULL);
if (arg < 0 || fcntl(sock, F_SETFL, arg | O_NONBLOCK) == SOCKET_ERROR) r = 0; // temporarily set socket non-blocking
#endif
if (! r) err = WSAGetLastError();
}

if (addrConnect.IsValid()) {
ctx->socket = CreateSocket(addrConnect);
if (r) {
memset(&addr, 0, sizeof(addr));

if (domain == PF_INET6) {
((struct sockaddr_in6 *)&addr)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)&addr)->sin6_addr = *(struct in6_addr *)&peer->address;
((struct sockaddr_in6 *)&addr)->sin6_port = htons(peer->port);
addrLen = sizeof(struct sockaddr_in6);
}
else {
((struct sockaddr_in *)&addr)->sin_family = AF_INET;
((struct sockaddr_in *)&addr)->sin_addr = *(struct in_addr *)&peer->address.u32[3];
((struct sockaddr_in *)&addr)->sin_port = htons(peer->port);
addrLen = sizeof(struct sockaddr_in);
}

if (ctx->socket != INVALID_SOCKET) {
connected = ConnectSocketDirectly(addrConnect, const_cast<const SOCKET&>(ctx->socket), nConnectTimeout, false);
#ifdef WIN32
if (connect(sock, (struct sockaddr *)&addr, addrLen) == SOCKET_ERROR) err = WSAGetLastError();
#else
if (connect(sock, (struct sockaddr *)&addr, addrLen) < 0) err = errno;
#endif

// Connection in progress.
#ifdef WIN32
if (err == WSAEINPROGRESS || err == WSAEWOULDBLOCK || err == WSAEINVAL) {
#else
if (err == EINPROGRESS) {
#endif
err = 0;
optLen = sizeof(err);
tv.tv_sec = timeout;
tv.tv_usec = (long)(timeout*1000000) % 1000000;
FD_ZERO(&fds);
FD_SET(sock, &fds);
count = select(sock + 1, NULL, &fds, NULL, &tv);

if (count <= 0 || getsockopt(sock, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&err, &optLen) == SOCKET_ERROR || err) {
if (count == 0) err = ETIMEDOUT;
if (count < 0 || ! err) err = WSAGetLastError();
r = 0;
}
}
else if (err) {
r = 0;
}

if (r) peer_log(peer, "socket connected");

#ifdef WIN32
u_long nZero = 0;
ioctlsocket(sock, FIONBIO, &nZero);
#else
fcntl(sock, F_SETFL, arg);
#endif
}
if (! r && err) peer_log(peer, "connect error: %d: %s", err, strerror(err));
if (error && err) *error = err;
return r;
}

static void *_peerThreadRoutine(void *arg)
{
BRPeer *peer = (BRPeer *)arg;
BRPeerContext *ctx = (BRPeerContext *)arg;
int error = 0;
SOCKET socket;

threadCleanup guard(ctx->threadCleanup, ctx->info);

int domain{PF_INET6};
if (_BRPeerIsIPv4(peer)) {
domain = PF_INET;
}

auto connected = _BRPeerOpenSocket(peer, domain, CONNECT_TIMEOUT, &error);
if (connected) {
struct timeval tv;
double time = 0, msgTimeout;
Expand Down