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

Fix protection failure when calling select() on a file descriptor larger than FD_SETSIZE #114

Closed
wants to merge 3 commits into from
Closed
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
98 changes: 81 additions & 17 deletions servus/dnssd/servus.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
#include <arpa/inet.h>
#include <sys/time.h>
#include <unistd.h>
#include <poll.h>
#else
#include <winsock2.h>
#endif
#include <dns_sd.h>

Expand All @@ -41,12 +44,35 @@ class Servus : public servus::Servus::Impl
, _in(0)
, _result(servus::Servus::Result::PENDING)
{
#ifdef _MSC_VER
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
const int error = WSAGetLastError();
char errorMessage[256] = {0};

FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)errorMessage,
sizeof(errorMessage),
NULL
);

WARN << "WSAStartup error (" << error << "): " << errorMessage;

_result = servus::Servus::Result::POLL_ERROR;
}
#endif
}

virtual ~Servus()
{
withdraw();
endBrowsing();
#ifdef _MSC_VER
WSACleanup();
#endif
}

std::string getClassName() const { return "dnssd"; }
Expand Down Expand Up @@ -114,6 +140,9 @@ class Servus : public servus::Servus::Impl
DNSServiceRef _in; //!< used to browse()
int32_t _result;
std::string _browsedName;
#ifdef _MSC_VER
WSADATA wsaData;
#endif

servus::Servus::Result _browse(const ::servus::Servus::Interface addr)
{
Expand Down Expand Up @@ -160,41 +189,35 @@ class Servus : public servus::Servus::Impl
}
}

servus::Servus::Result _handleEvents(DNSServiceRef service,
const int32_t timeout = -1)
servus::Servus::Result _handleEvents(DNSServiceRef service, const int32_t timeout = -1)
{
assert(service);
if (!service)
return servus::Servus::Result(kDNSServiceErr_Unknown);

const int fd = DNSServiceRefSockFD(service);
const int nfds = fd + 1;

assert(fd >= 0);
if (fd < 0)
return servus::Servus::Result(kDNSServiceErr_BadParam);

#ifndef _MSC_VER
struct pollfd fds;
fds.fd = fd;
fds.events = POLLIN;

while (_result == servus::Servus::Result::PENDING)
{
fd_set fdSet;
FD_ZERO(&fdSet);
FD_SET(fd, &fdSet);

struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;

const int result =
::select(nfds, &fdSet, 0, 0, timeout < 0 ? 0 : &tv);
const int result = poll(&fds, 1, timeout);
switch (result)
{
case 0: // timeout
_result = kDNSServiceErr_NoError;
break;

case -1: // error
WARN << "Select error: " << strerror(errno) << " (" << errno
<< ")" << std::endl;
WARN << "Poll error: " << strerror(errno) << " (" << errno
<< ")" << std::endl;
if (errno != EINTR)
{
withdraw();
Expand All @@ -203,23 +226,64 @@ class Servus : public servus::Servus::Impl
break;

default:
if (FD_ISSET(fd, &fdSet))
if (fds.revents & POLLIN)
{
const DNSServiceErrorType error =
DNSServiceProcessResult(service);

if (error != kDNSServiceErr_NoError)
{
WARN << "DNSServiceProcessResult error: " << error
<< std::endl;
<< std::endl;
withdraw();
_result = error;
}
}
break;
}
}
#else
WSAPOLLFD fds;
fds.fd = fd;
fds.events = POLLIN;

while (_result == servus::Servus::Result::PENDING)
{
const int result = WSAPoll(&fds, 1, timeout);
switch (result)
{
case 0: // timeout
_result = kDNSServiceErr_NoError;
break;

case -1: // error
WARN << "WSAPoll error: " << WSAGetLastError()
<< std::endl;
if (WSAGetLastError() != WSAEINTR)
{
withdraw();
_result = WSAGetLastError();
}
break;

default:
if (fds[0].revents & POLLIN)
{
const DNSServiceErrorType error =
DNSServiceProcessResult(service);

if (error != kDNSServiceErr_NoError)
{
WARN << "DNSServiceProcessResult error: " << error
<< std::endl;
withdraw();
_result = error;
}
}
break;
}
}
#endif
const servus::Servus::Result result(_result);
_result = servus::Servus::Result::PENDING; // reset for next operation
return result;
Expand Down