SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6 (#7008)

* SYNERGY-970 Synergy1. User is not able to connect to server using ipv6 without wrapping IP in square quotes
*Fix connection without brackets
*Fix config screen bug

* SYNERGY-970 Synergy1. User is not able to connect to server using ipv6 without wrapping IP in square quotes
*Update changelog

* SYNERGY-970 Mac client listens on ipv4 only but attempts to connect on ipv6 #6964
*Add logging in case of several IP of hostname

* Synergy 970 synergy1. user is not able to connect to server using ipv6 without wrapping ip in square quotes
*Update changelog

* SYNERGY-885 Mac client listens on ipv4 only but attempts to connect on ipv6
*Fix Sonar warnings

* SUNERGY-885-Mac client listens on ipv4 only but attempts to connect on ipv6
*Update Changelog

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Fix include

* Sunergy-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Fix sonar warnings

* SYNERGY-885 Mac client listens on ipv4 only but attempts to connect on ipv6
*Add loggic to connect to first from reachable server addresses

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Fix unix compilation

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Fix unix compilation

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Fix code smells

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Fix build

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Fix memory leak

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Added logic for temporary ipv6 filtering from connect

* SYNERGY-885 mac client listens on ipv4 only but attempts to connect on ipv6
*Temporary disable ipv6 parsing and resolving tests

Co-authored-by: Andrii Batyiev <andrii-external@symless.com>
This commit is contained in:
Andrey Batyiev 2021-05-20 19:59:05 +03:00 committed by GitHub
parent 3c3b6b9cf2
commit 2569409ba2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 134 additions and 85 deletions

View file

@ -32,6 +32,7 @@ Enhancements:
- #6999 Detect if Linux is running with Wayland, and display a warning message - #6999 Detect if Linux is running with Wayland, and display a warning message
- #7003 Prioritization rules server config - #7003 Prioritization rules server config
- #7004 Add openssl dependency for rpm - #7004 Add openssl dependency for rpm
- #7008 Add logging when hostname have several IP
=========== ===========
v1.13.1-stable v1.13.1-stable

View file

@ -35,7 +35,8 @@ void ClientConnection::update(const QString& line)
if (line.contains("failed to connect to server")) if (line.contains("failed to connect to server"))
{ {
m_checkConnection = false; m_checkConnection = false;
if (!line.contains("server refused client with our name")) if (!line.contains("server refused client with our name") &&
!line.contains("Trying next address"))
{ {
showMessage(getMessage(line)); showMessage(getMessage(line));
} }

View file

@ -21,6 +21,8 @@
#include "common/IInterface.h" #include "common/IInterface.h"
#include "common/stdstring.h" #include "common/stdstring.h"
#include <vector>
class ArchThreadImpl; class ArchThreadImpl;
typedef ArchThreadImpl* ArchThread; typedef ArchThreadImpl* ArchThread;
@ -247,7 +249,8 @@ public:
virtual ArchNetAddress copyAddr(ArchNetAddress) = 0; virtual ArchNetAddress copyAddr(ArchNetAddress) = 0;
//! Convert a name to a network address //! Convert a name to a network address
virtual ArchNetAddress nameToAddr(const std::string&) = 0; virtual std::vector<ArchNetAddress>
nameToAddr(const std::string&) = 0;
//! Destroy a network address //! Destroy a network address
virtual void closeAddr(ArchNetAddress) = 0; virtual void closeAddr(ArchNetAddress) = 0;

View file

@ -670,15 +670,15 @@ ArchNetworkBSD::copyAddr(ArchNetAddress addr)
return new ArchNetAddressImpl(*addr); return new ArchNetAddressImpl(*addr);
} }
ArchNetAddress std::vector<ArchNetAddress>
ArchNetworkBSD::nameToAddr(const std::string& name) ArchNetworkBSD::nameToAddr(const std::string& name)
{ {
// allocate address // allocate address
auto* addr = new ArchNetAddressImpl; std::vector<ArchNetAddressImpl*> addresses;
char ipstr[INET6_ADDRSTRLEN]; char ipstr[INET6_ADDRSTRLEN];
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *p; struct addrinfo *pResult;
struct in6_addr serveraddr; struct in6_addr serveraddr;
int ret; int ret;
@ -698,24 +698,27 @@ ArchNetworkBSD::nameToAddr(const std::string& name)
// done with static buffer // done with static buffer
ARCH->lockMutex(m_mutex); ARCH->lockMutex(m_mutex);
ret = getaddrinfo(name.c_str(), nullptr, &hints, &p); ret = getaddrinfo(name.c_str(), nullptr, &hints, &pResult);
if (ret != 0) { if (ret != 0) {
ARCH->unlockMutex(m_mutex); ARCH->unlockMutex(m_mutex);
delete addr;
throwNameError(ret); throwNameError(ret);
} }
if (p->ai_family == AF_INET) { for(; pResult != nullptr; pResult = pResult->ai_next ) {
addr->m_len = (socklen_t)sizeof(struct sockaddr_in); addresses.push_back(new ArchNetAddressImpl);
} else { if (pResult->ai_family == AF_INET) {
addr->m_len = (socklen_t)sizeof(struct sockaddr_in6); addresses.back()->m_len = (socklen_t)sizeof(struct sockaddr_in);
} else {
addresses.back()->m_len = (socklen_t)sizeof(struct sockaddr_in6);
}
memcpy(&addresses.back()->m_addr, pResult->ai_addr, addresses.back()->m_len);
} }
memcpy(&addr->m_addr, p->ai_addr, addr->m_len); freeaddrinfo(pResult);
freeaddrinfo(p);
ARCH->unlockMutex(m_mutex); ARCH->unlockMutex(m_mutex);
return addr; return addresses;
} }
void void

View file

@ -108,7 +108,7 @@ public:
virtual std::string getHostName(); virtual std::string getHostName();
virtual ArchNetAddress newAnyAddr(EAddressFamily); virtual ArchNetAddress newAnyAddr(EAddressFamily);
virtual ArchNetAddress copyAddr(ArchNetAddress); virtual ArchNetAddress copyAddr(ArchNetAddress);
virtual ArchNetAddress nameToAddr(const std::string&); virtual std::vector<ArchNetAddress> nameToAddr(const std::string&);
virtual void closeAddr(ArchNetAddress); virtual void closeAddr(ArchNetAddress);
virtual std::string addrToName(ArchNetAddress); virtual std::string addrToName(ArchNetAddress);
virtual std::string addrToString(ArchNetAddress); virtual std::string addrToString(ArchNetAddress);

View file

@ -716,36 +716,38 @@ ArchNetworkWinsock::copyAddr(ArchNetAddress addr)
return copy; return copy;
} }
ArchNetAddress std::vector<ArchNetAddress>
ArchNetworkWinsock::nameToAddr(const std::string& name) ArchNetworkWinsock::nameToAddr(const std::string& name)
{ {
// allocate address // allocate address
std::vector<ArchNetAddressImpl*> addresses;
ArchNetAddressImpl* addr = new ArchNetAddressImpl;
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *p; struct addrinfo *pResult;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
int ret = -1; int ret = -1;
ARCH->lockMutex(m_mutex); ARCH->lockMutex(m_mutex);
if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &p)) != 0) { if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &pResult)) != 0) {
ARCH->unlockMutex(m_mutex); ARCH->unlockMutex(m_mutex);
delete addr;
throwNameError(ret); throwNameError(ret);
} }
if (p->ai_family == AF_INET) { for(; pResult != nullptr; pResult = pResult->ai_next ){
addr->m_len = (socklen_t)sizeof(struct sockaddr_in); addresses.push_back(new ArchNetAddressImpl);
} else { if (pResult->ai_family == AF_INET) {
addr->m_len = (socklen_t)sizeof(struct sockaddr_in6); addresses.back()->m_len = (socklen_t)sizeof(struct sockaddr_in);
} else {
addresses.back()->m_len = (socklen_t)sizeof(struct sockaddr_in6);
}
memcpy(&addresses.back()->m_addr, pResult->ai_addr, addresses.back()->m_len);
} }
memcpy(&addr->m_addr, p->ai_addr, addr->m_len); freeaddrinfo(pResult);
freeaddrinfo(p);
ARCH->unlockMutex(m_mutex); ARCH->unlockMutex(m_mutex);
return addr; return addresses;
} }
void void

View file

@ -85,7 +85,7 @@ public:
virtual std::string getHostName(); virtual std::string getHostName();
virtual ArchNetAddress newAnyAddr(EAddressFamily); virtual ArchNetAddress newAnyAddr(EAddressFamily);
virtual ArchNetAddress copyAddr(ArchNetAddress); virtual ArchNetAddress copyAddr(ArchNetAddress);
virtual ArchNetAddress nameToAddr(const std::string&); virtual std::vector<ArchNetAddress> nameToAddr(const std::string&);
virtual void closeAddr(ArchNetAddress); virtual void closeAddr(ArchNetAddress);
virtual std::string addrToName(ArchNetAddress); virtual std::string addrToName(ArchNetAddress);
virtual std::string addrToString(ArchNetAddress); virtual std::string addrToString(ArchNetAddress);

View file

@ -122,7 +122,7 @@ Client::~Client()
} }
void void
Client::connect() Client::connect(size_t addressIndex)
{ {
if (m_stream != NULL) { if (m_stream != NULL) {
return; return;
@ -138,10 +138,10 @@ Client::connect()
// has changed (which can happen frequently if this is a laptop // has changed (which can happen frequently if this is a laptop
// being shuttled between various networks). patch by Brent // being shuttled between various networks). patch by Brent
// Priddy. // Priddy.
m_serverAddress.resolve(); m_resolvedAddressesCount = m_serverAddress.resolve(addressIndex);
// m_serverAddress will be null if the hostname address is not reolved // m_serverAddress will be null if the hostname address is not reolved
if (m_serverAddress.getAddress() != NULL) { if (m_serverAddress.getAddress() != nullptr) {
// to help users troubleshoot, show server host name (issue: 60) // to help users troubleshoot, show server host name (issue: 60)
LOG((CLOG_NOTE "connecting to '%s': %s:%i", LOG((CLOG_NOTE "connecting to '%s': %s:%i",
m_serverAddress.getHostname().c_str(), m_serverAddress.getHostname().c_str(),

View file

@ -75,7 +75,7 @@ public:
Starts an attempt to connect to the server. This is ignored if Starts an attempt to connect to the server. This is ignored if
the client is trying to connect or is already connected. the client is trying to connect or is already connected.
*/ */
void connect(); void connect(size_t addressIndex = 0);
//! Disconnect //! Disconnect
/*! /*!
@ -135,6 +135,9 @@ public:
//! Return drag file list //! Return drag file list
DragFileList getDragFileList() { return m_dragFileList; } DragFileList getDragFileList() { return m_dragFileList; }
//! Return last resolved adresses count
size_t getLastResolvedAddressesCount() const { return m_resolvedAddressesCount; }
//@} //@}
// IScreen overrides // IScreen overrides
@ -229,4 +232,5 @@ private:
bool m_enableClipboard; bool m_enableClipboard;
size_t m_maximumClipboardSize; size_t m_maximumClipboardSize;
lib::synergy::ClientArgs m_args; lib::synergy::ClientArgs m_args;
size_t m_resolvedAddressesCount = 0;
}; };

View file

@ -31,18 +31,7 @@
// name re-resolution adapted from a patch by Brent Priddy. // name re-resolution adapted from a patch by Brent Priddy.
NetworkAddress::NetworkAddress() :
m_address(NULL),
m_hostname(),
m_port(0)
{
// note -- make no calls to Network socket interface here;
// we're often called prior to Network::init().
}
NetworkAddress::NetworkAddress(int port) : NetworkAddress::NetworkAddress(int port) :
m_address(NULL),
m_hostname(),
m_port(port) m_port(port)
{ {
checkPort(); checkPort();
@ -51,15 +40,13 @@ NetworkAddress::NetworkAddress(int port) :
} }
NetworkAddress::NetworkAddress(const NetworkAddress& addr) : NetworkAddress::NetworkAddress(const NetworkAddress& addr) :
m_address(addr.m_address != NULL ? ARCH->copyAddr(addr.m_address) : NULL),
m_hostname(addr.m_hostname), m_hostname(addr.m_hostname),
m_port(addr.m_port) m_port(addr.m_port)
{ {
// do nothing *this = addr;
} }
NetworkAddress::NetworkAddress(const String& hostname, int port) : NetworkAddress::NetworkAddress(const String& hostname, int port) :
m_address(NULL),
m_hostname(hostname), m_hostname(hostname),
m_port(port) m_port(port)
{ {
@ -119,34 +106,39 @@ NetworkAddress::NetworkAddress(const String& hostname, int port) :
NetworkAddress::~NetworkAddress() NetworkAddress::~NetworkAddress()
{ {
if (m_address != NULL) { if (m_address != nullptr) {
ARCH->closeAddr(m_address); ARCH->closeAddr(m_address);
m_address = nullptr;
} }
} }
NetworkAddress& NetworkAddress&
NetworkAddress::operator=(const NetworkAddress& addr) NetworkAddress::operator=(const NetworkAddress& addr)
{ {
ArchNetAddress newAddr = NULL; if (m_address != nullptr) {
if (addr.m_address != NULL) { ARCH->closeAddr(m_address);
m_address = nullptr;
}
ArchNetAddress newAddr = nullptr;
if (addr.m_address != nullptr) {
newAddr = ARCH->copyAddr(addr.m_address); newAddr = ARCH->copyAddr(addr.m_address);
} }
if (m_address != NULL) { m_address = newAddr;
ARCH->closeAddr(m_address);
}
m_address = newAddr;
m_hostname = addr.m_hostname; m_hostname = addr.m_hostname;
m_port = addr.m_port; m_port = addr.m_port;
return *this; return *this;
} }
void size_t
NetworkAddress::resolve() NetworkAddress::resolve(size_t index)
{ {
size_t resolvedAddressesCount = 0;
// discard previous address // discard previous address
if (m_address != NULL) { if (m_address != nullptr) {
ARCH->closeAddr(m_address); ARCH->closeAddr(m_address);
m_address = NULL; m_address = nullptr;
} }
try { try {
@ -154,9 +146,34 @@ NetworkAddress::resolve()
// up the name. // up the name.
if (m_hostname.empty()) { if (m_hostname.empty()) {
m_address = ARCH->newAnyAddr(IArchNetwork::kINET); m_address = ARCH->newAnyAddr(IArchNetwork::kINET);
resolvedAddressesCount = 1;
} }
else { else {
m_address = ARCH->nameToAddr(m_hostname); // Logic for temporary filtring only ipv4 addresses
std::vector<ArchNetAddress> ipv4OnlyAddresses;
{
auto adresses = ARCH->nameToAddr(m_hostname);
for (auto address : adresses) {
if (ARCH->getAddrFamily(address) == IArchNetwork::kINET) {
ipv4OnlyAddresses.emplace_back(address);
}
}
}
resolvedAddressesCount = ipv4OnlyAddresses.size();
assert(resolvedAddressesCount > 0);
if (index < resolvedAddressesCount - 1) {
m_address = ipv4OnlyAddresses[index];
}
else {
m_address = ipv4OnlyAddresses[resolvedAddressesCount - 1];
}
for(auto address : ipv4OnlyAddresses) {
if(m_address != address) {
ARCH->closeAddr(address);
}
}
} }
} }
catch (XArchNetworkNameUnknown&) { catch (XArchNetworkNameUnknown&) {
@ -174,6 +191,8 @@ NetworkAddress::resolve()
// set port in address // set port in address
ARCH->setAddrPort(m_address, m_port); ARCH->setAddrPort(m_address, m_port);
return resolvedAddressesCount;
} }
bool bool
@ -191,7 +210,7 @@ NetworkAddress::operator!=(const NetworkAddress& addr) const
bool bool
NetworkAddress::isValid() const NetworkAddress::isValid() const
{ {
return (m_address != NULL); return (m_address != nullptr);
} }
const ArchNetAddress& const ArchNetAddress&

View file

@ -31,7 +31,7 @@ public:
/*! /*!
Constructs the invalid address Constructs the invalid address
*/ */
NetworkAddress(); NetworkAddress() = default;
/*! /*!
Construct the wildcard address with the given port. \c port must Construct the wildcard address with the given port. \c port must
@ -66,8 +66,10 @@ public:
times and is done automatically by the c'tor taking a hostname. times and is done automatically by the c'tor taking a hostname.
Throws XSocketAddress if resolution is unsuccessful, after which Throws XSocketAddress if resolution is unsuccessful, after which
\c isValid returns false until the next call to this method. \c isValid returns false until the next call to this method.
index - determine index of IP we would like to use from resolved addresses
Returns count of successfully resolved addressed.
*/ */
void resolve(); size_t resolve(size_t index = 0);
//@} //@}
//! @name accessors //! @name accessors
@ -117,7 +119,7 @@ private:
void checkPort(); void checkPort();
private: private:
ArchNetAddress m_address; ArchNetAddress m_address = nullptr;
String m_hostname; String m_hostname;
int m_port; int m_port = 0;
}; };

View file

@ -304,17 +304,28 @@ ClientApp::handleClientFailed(const Event& e, void*)
Client::FailInfo* info = Client::FailInfo* info =
static_cast<Client::FailInfo*>(e.getData()); static_cast<Client::FailInfo*>(e.getData());
updateStatus(String("Failed to connect to server: ") + info->m_what); if (m_lastServerAddressIndex + 1 < m_client->getLastResolvedAddressesCount()) {
if (!args().m_restartable || !info->m_retry) { m_lastServerAddressIndex++;
LOG((CLOG_ERR "failed to connect to server: %s", info->m_what.c_str())); updateStatus(String("Failed to connect to server: ") + info->m_what + " Trying next address...");
m_events->addEvent(Event(Event::kQuit)); LOG((CLOG_NOTE "Failed to connect to server: %s. Trying next address...", info->m_what.c_str()));
}
else {
LOG((CLOG_WARN "failed to connect to server: %s", info->m_what.c_str()));
if (!m_suspended) { if (!m_suspended) {
scheduleClientRestart(nextRestartTimeout()); scheduleClientRestart(nextRestartTimeout());
} }
} }
else {
m_lastServerAddressIndex = 0;
updateStatus(String("Failed to connect to server: ") + info->m_what);
if (!args().m_restartable || !info->m_retry) {
LOG((CLOG_ERR "failed to connect to server: %s", info->m_what.c_str()));
m_events->addEvent(Event(Event::kQuit));
}
else {
LOG((CLOG_WARN "failed to connect to server: %s", info->m_what.c_str()));
if (!m_suspended) {
scheduleClientRestart(nextRestartTimeout());
}
}
}
delete info; delete info;
} }
@ -405,7 +416,7 @@ ClientApp::startClient()
LOG((CLOG_NOTE "started client")); LOG((CLOG_NOTE "started client"));
} }
m_client->connect(); m_client->connect(m_lastServerAddressIndex);
updateStatus(); updateStatus();
return true; return true;

View file

@ -82,6 +82,7 @@ public:
private: private:
Client* m_client; Client* m_client;
synergy::Screen*m_clientScreen; synergy::Screen* m_clientScreen;
NetworkAddress* m_serverAddress; NetworkAddress* m_serverAddress;
size_t m_lastServerAddressIndex = 0;
}; };

View file

@ -94,15 +94,16 @@ TEST(NetworkAddress, hostname_valid_parsing)
const std::initializer_list<std::tuple<String, int, String>> validTestCases = { const std::initializer_list<std::tuple<String, int, String>> validTestCases = {
std::make_tuple(String("127.0.0.1"), validPort, "127.0.0.1"), std::make_tuple(String("127.0.0.1"), validPort, "127.0.0.1"),
std::make_tuple(String("127.0.0.1:") + portStr, 0, "127.0.0.1"), std::make_tuple(String("127.0.0.1:") + portStr, 0, "127.0.0.1"),
std::make_tuple(String(":") + portStr, 0, ""),
std::make_tuple(String("localhost"), validPort, "localhost"), std::make_tuple(String("localhost"), validPort, "localhost"),
std::make_tuple(String("localhost:") + portStr, 0, "localhost"), std::make_tuple(String("localhost:") + portStr, 0, "localhost"),
std::make_tuple(String(""), validPort, ""), std::make_tuple(String(""), validPort, ""),
std::make_tuple(String("[::1]:") + portStr, 0, "::1"), std::make_tuple(String(":") + portStr, 0, ""),
std::make_tuple(String("[fe80::a156:9f36:793:7bfb%14]:") + portStr, 0, "fe80::a156:9f36:793:7bfb%14"), //Temporary disabled tests for ipv6
std::make_tuple(String("::1"), validPort, "::1"), //std::make_tuple(String("[::1]:") + portStr, 0, "::1"),
std::make_tuple(String("fe80::a156:9f36:793:7bfb%14"), validPort, "fe80::a156:9f36:793:7bfb%14"), //std::make_tuple(String("[fe80::a156:9f36:793:7bfb%14]:") + portStr, 0, "fe80::a156:9f36:793:7bfb%14"),
std::make_tuple(String("fe80:0000:0000:0000:a156:9f36:793:7bfb%14"), validPort, "fe80:0000:0000:0000:a156:9f36:793:7bfb%14"), //std::make_tuple(String("::1"), validPort, "::1"),
//std::make_tuple(String("fe80::a156:9f36:793:7bfb%14"), validPort, "fe80::a156:9f36:793:7bfb%14"),
//std::make_tuple(String("fe80:0000:0000:0000:a156:9f36:793:7bfb%14"), validPort, "fe80:0000:0000:0000:a156:9f36:793:7bfb%14"),
}; };
for (const auto &caseParams : validTestCases) { for (const auto &caseParams : validTestCases) {
@ -118,11 +119,12 @@ TEST(NetworkAddress, hostname_valid_parsing)
const std::initializer_list<String> nonValidTestCases = { const std::initializer_list<String> nonValidTestCases = {
":nonValidPort", ":nonValidPort",
":", ":",
"[::1]:", //Temporary disabled tests for ipv6
"[::1]:nonValidPort", //"[::1]:",
"fe80::1", //"[::1]:nonValidPort",
"[::1]:-1", //"fe80::1",
"[::1]:65536" //"[::1]:-1",
//"[::1]:65536"
}; };
for (const auto &caseParam : nonValidTestCases) { for (const auto &caseParam : nonValidTestCases) {