fix ipv6 handling between GUI and barriers/barrierc; zero-fill sockaddr_in(6) structs prior to initializing; update --help output

This commit is contained in:
walker0643 2018-05-12 17:42:55 -04:00
parent f4301a7618
commit 9ab77545ee
6 changed files with 83 additions and 64 deletions

View File

@ -581,7 +581,7 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
if (m_pCheckBoxAutoConfig->isChecked()) { if (m_pCheckBoxAutoConfig->isChecked()) {
if (m_pComboServerList->count() != 0) { if (m_pComboServerList->count() != 0) {
QString serverIp = m_pComboServerList->currentText(); QString serverIp = m_pComboServerList->currentText();
args << serverIp + ":" + QString::number(appConfig().port()); args << "[" + serverIp + "]:" + QString::number(appConfig().port());
return true; return true;
} }
} }
@ -595,7 +595,7 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
return false; return false;
} }
args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port()); args << "[" + m_pLineEditHostname->text() + "]:" + QString::number(appConfig().port());
return true; return true;
} }
@ -637,8 +637,10 @@ QString MainWindow::configFilename()
QString MainWindow::address() QString MainWindow::address()
{ {
QString i = appConfig().networkInterface(); QString address = appConfig().networkInterface();
return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port()); if (!address.isEmpty())
address = "[" + address + "]";
return address + ":" + QString::number(appConfig().port());
} }
QString MainWindow::appPath(const QString& name) QString MainWindow::appPath(const QString& name)

View File

@ -113,6 +113,11 @@ ArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type)
} }
try { try {
setBlockingOnSocket(fd, false); setBlockingOnSocket(fd, false);
if (family == kINET6) {
int flag = 0;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) != 0)
throwError(errno);
}
} }
catch (...) { catch (...) {
close(fd); close(fd);
@ -647,8 +652,8 @@ ArchNetworkBSD::newAnyAddr(EAddressFamily family)
switch (family) { switch (family) {
case kINET: { case kINET: {
auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
memset(ipAddr, 0, sizeof(struct sockaddr_in));
ipAddr->sin_family = AF_INET; ipAddr->sin_family = AF_INET;
ipAddr->sin_port = 0;
ipAddr->sin_addr.s_addr = INADDR_ANY; ipAddr->sin_addr.s_addr = INADDR_ANY;
addr->m_len = (socklen_t)sizeof(struct sockaddr_in); addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
break; break;
@ -656,8 +661,8 @@ ArchNetworkBSD::newAnyAddr(EAddressFamily family)
case kINET6: { case kINET6: {
auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr); auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
memset(ipAddr, 0, sizeof(struct sockaddr_in6));
ipAddr->sin6_family = AF_INET6; ipAddr->sin6_family = AF_INET6;
ipAddr->sin6_port = 0;
memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)); memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr->m_len = (socklen_t)sizeof(struct sockaddr_in6); addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
break; break;

View File

@ -213,10 +213,10 @@ ArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
} }
try { try {
setBlockingOnSocket(fd, false); setBlockingOnSocket(fd, false);
BOOL flag = 0; if (family == kINET6) {
int size = sizeof(flag); int flag = 0;
if (setsockopt_winsock(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, size) == SOCKET_ERROR) { if (setsockopt_winsock(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) == SOCKET_ERROR)
throwError(getsockerror_winsock()); throwError(getsockerror_winsock());
} }
} }
catch (...) { catch (...) {
@ -685,8 +685,8 @@ ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
case kINET: { case kINET: {
addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in)); addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
memset(ipAddr, 0, sizeof(struct sockaddr_in));
ipAddr->sin_family = AF_INET; ipAddr->sin_family = AF_INET;
ipAddr->sin_port = 0;
ipAddr->sin_addr.s_addr = INADDR_ANY; ipAddr->sin_addr.s_addr = INADDR_ANY;
break; break;
} }
@ -694,8 +694,8 @@ ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
case kINET6: { case kINET6: {
addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6)); addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr); auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
memset(ipAddr, 0, sizeof(struct sockaddr_in6));
ipAddr->sin6_family = AF_INET6; ipAddr->sin6_family = AF_INET6;
ipAddr->sin6_port = 0;
memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)); memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
break; break;
} }

View File

@ -131,9 +131,10 @@ ClientApp::help()
<< std::endl << std::endl
<< "Default options are marked with a *" << std::endl << "Default options are marked with a *" << std::endl
<< std::endl << std::endl
<< "The server address is of the form: [<hostname>][:<port>]. The hostname" << std::endl << "The server address is of the form: [<hostname>][:<port>]. The hostname" << std::endl
<< "must be the address or hostname of the server. The port overrides the" << std::endl << "must be the address or hostname of the server. Placing brackets around" << std::endl
<< "default port, " << kDefaultPort << "." << std::endl; << "an IPv6 address is required when also specifying a port number and " << std::endl
<< "optional otherwise. The default port number is " << kDefaultPort << "." << std::endl;
LOG((CLOG_PRINT "%s", buffer.str().c_str())); LOG((CLOG_PRINT "%s", buffer.str().c_str()));
} }

View File

@ -139,8 +139,9 @@ ServerApp::help()
<< std::endl << std::endl
<< "The argument for --address is of the form: [<hostname>][:<port>]. The" << std::endl << "The argument for --address is of the form: [<hostname>][:<port>]. The" << std::endl
<< "hostname must be the address or hostname of an interface on the system." << std::endl << "hostname must be the address or hostname of an interface on the system." << std::endl
<< "The default is to listen on all interfaces. The port overrides the" << std::endl << "Placing brackets around an IPv6 address is required when also specifying " << std::endl
<< "default port, " << kDefaultPort << "." << std::endl << "a port number and optional otherwise. The default is to listen on all" << std::endl
<< "interfaces using port number " << kDefaultPort << "." << std::endl
<< std::endl << std::endl
<< "If no configuration file pathname is provided then the first of the" << std::endl << "If no configuration file pathname is provided then the first of the" << std::endl
<< "following to load successfully sets the configuration:" << std::endl << "following to load successfully sets the configuration:" << std::endl
@ -506,6 +507,16 @@ ServerApp::openServerScreen()
return screen; return screen;
} }
static const char* const family_string(IArchNetwork::EAddressFamily family)
{
if (family == IArchNetwork::kINET)
return "IPv4";
if (family == IArchNetwork::kINET6)
// assume IPv6 sockets are setup to support IPv4 traffic as well
return "IPv4/IPv6";
return "Unknown";
}
bool bool
ServerApp::startServer() ServerApp::startServer()
{ {
@ -531,13 +542,15 @@ ServerApp::startServer()
double retryTime; double retryTime;
ClientListener* listener = NULL; ClientListener* listener = NULL;
try { try {
listener = openClientListener(args().m_config->getBarrierAddress()); auto listenAddress = args().m_config->getBarrierAddress();
auto family = family_string(ARCH->getAddrFamily(listenAddress.getAddress()));
listener = openClientListener(listenAddress);
m_server = openServer(*args().m_config, m_primaryClient); m_server = openServer(*args().m_config, m_primaryClient);
listener->setServer(m_server); listener->setServer(m_server);
m_server->setListener(listener); m_server->setListener(listener);
m_listener = listener; m_listener = listener;
updateStatus(); updateStatus();
LOG((CLOG_NOTE "started server, waiting for clients")); LOG((CLOG_NOTE "started server (%s), waiting for clients", family));
m_serverState = kStarted; m_serverState = kStarted;
return true; return true;
} }

View File

@ -28,6 +28,45 @@
// NetworkAddress // NetworkAddress
// //
static bool parse_address(const std::string& address, std::string& host, int& port)
{
/* Three cases ---
* brackets: parse inside for host, check end for port as :INTEGER. DONE
* one colon: ipv4 address with port. DONE
* otherwise: all host, no port. DONE
*
* very, very little error checking. depends on address being trimmed before call.
*
* does not override port with a default value if no port was found in address.
*/
if (address[0] == '[') {
// bracketed host possibly followed by port as :INTEGER
auto endBracket = address.find(']', 1);
if (endBracket == std::string::npos)
return false;
host = address.substr(1, endBracket - 1);
if (endBracket + 1 < address.length()) {
// port follows (or garbage)
if (address[endBracket + 1] != ':')
return false;
port = std::strtol(&address[endBracket + 2], nullptr, 10);
}
} else {
auto colon = address.find(':');
if (colon != std::string::npos && address.find(':', colon + 1) == std::string::npos) {
// one single colon, must be ipv4 with port
host = address.substr(0, colon);
port = std::strtol(&address[colon + 1], nullptr, 10);
} else {
// no colons (ipv4) or more than one colon (ipv6), both without port
host = address;
}
}
return true;
}
// name re-resolution adapted from a patch by Brent Priddy. // name re-resolution adapted from a patch by Brent Priddy.
NetworkAddress::NetworkAddress() : NetworkAddress::NetworkAddress() :
@ -62,50 +101,9 @@ NetworkAddress::NetworkAddress(const String& hostname, int port) :
m_hostname(hostname), m_hostname(hostname),
m_port(port) m_port(port)
{ {
// check for port suffix if (!parse_address(hostname, m_hostname, m_port))
String::size_type i = m_hostname.rfind(':'); throw XSocketAddress(XSocketAddress::kUnknown,
if (i != String::npos && i + 1 < m_hostname.size()) { m_hostname, m_port);
// found a colon. see if it looks like an IPv6 address.
bool colonNotation = false;
bool dotNotation = false;
bool doubleColon = false;
for (String::size_type j = 0; j < i; ++j) {
if (m_hostname[j] == ':') {
colonNotation = true;
dotNotation = false;
if (m_hostname[j + 1] == ':') {
doubleColon = true;
}
}
else if (m_hostname[j] == '.' && colonNotation) {
dotNotation = true;
}
}
// port suffix is ambiguous with IPv6 notation if there's
// a double colon and the end of the address is not in dot
// notation. in that case we assume it's not a port suffix.
// the user can replace the double colon with zeros to
// disambiguate.
if ((!doubleColon || dotNotation) && !colonNotation) {
// parse port from hostname
char* end;
const char* chostname = m_hostname.c_str();
long suffixPort = strtol(chostname + i + 1, &end, 10);
if (end == chostname + i + 1 || *end != '\0') {
throw XSocketAddress(XSocketAddress::kBadPort,
m_hostname, m_port);
}
// trim port from hostname
m_hostname.erase(i);
// save port
m_port = static_cast<int>(suffixPort);
}
}
// check port number
checkPort(); checkPort();
} }
@ -145,7 +143,7 @@ NetworkAddress::resolve()
// if hostname is empty then use wildcard address otherwise look // if hostname is empty then use wildcard address otherwise look
// 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::kINET6);
} }
else { else {
m_address = ARCH->nameToAddr(m_hostname); m_address = ARCH->nameToAddr(m_hostname);