Merge pull request #378 from CopernicaMarketingSoftware/poll-replace-select

select only supports upto fd 1024, which can cause stack smashing if using higher ones
This commit is contained in:
Emiel Bruijntjes 2020-10-30 12:45:40 +01:00 committed by GitHub
commit 7c07ab12e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 66 deletions

View File

@ -11,6 +11,11 @@
*/ */
#pragma once #pragma once
/**
* Includes
*/
#include <poll.h>
/** /**
* Begin of namespace * Begin of namespace
*/ */
@ -26,7 +31,7 @@ private:
* Set with just one filedescriptor * Set with just one filedescriptor
* @var fd_set * @var fd_set
*/ */
fd_set _set; pollfd _fd;
/** /**
* The socket filedescriptor * The socket filedescriptor
@ -39,13 +44,10 @@ public:
* Constructor * Constructor
* @param fd the filedescriptor that we're waiting on * @param fd the filedescriptor that we're waiting on
*/ */
Poll(int fd) : _socket(fd) Poll(int fd)
{ {
// initialize the set // set the fd
FD_ZERO(&_set); _fd.fd = fd;
// add the one socket
FD_SET(_socket, &_set);
} }
/** /**
@ -60,66 +62,45 @@ public:
virtual ~Poll() = default; virtual ~Poll() = default;
/** /**
* Wait until the filedescriptor becomes readable * Check if a file descriptor is readable.
* @param block block until readable
* @return bool * @return bool
*/ */
bool readable(bool block) bool readable()
{ {
// wait for the socket // check for readable
if (block) return select(_socket + 1, &_set, nullptr, nullptr, nullptr) > 0; _fd.events = POLLIN;
_fd.revents = 0;
// we do not want to block, so we use a small timeout
struct timeval timeout; // poll the fd with no timeout
return poll(&_fd, 1, 0) > 0;
// no timeout at all
timeout.tv_sec = timeout.tv_usec = 0;
// no timeout at all
return select(_socket + 1, &_set, nullptr, nullptr, &timeout) > 0;
} }
/** /**
* Wait until the filedescriptor becomes writable * Wait until the filedescriptor becomes writable
* @param block block until readable
* @return bool * @return bool
*/ */
bool writable(bool block) bool writable()
{ {
// wait for the socket // check for readable
if (block) return select(_socket + 1, nullptr, &_set, nullptr, nullptr) > 0; _fd.events = POLLOUT;
_fd.revents = 0;
// we do not want to block, so we use a small timeout // poll the fd with no timeout
struct timeval timeout; return poll(&_fd, 1, 0) > 0;
// no timeout at all
timeout.tv_sec = timeout.tv_usec = 0;
// no timeout at all
return select(_socket + 1, nullptr, &_set, nullptr, &timeout) > 0;
} }
/** /**
* Wait until a filedescriptor becomes active (readable or writable) * Wait until a filedescriptor becomes active (readable or writable)
* @param block block until readable
* @return bool * @return bool
*/ */
bool active(bool block) bool active()
{ {
// accommodate restrict qualifier on fd_set params // check for readable
fd_set set2 = _set; _fd.events = POLLIN | POLLOUT;
_fd.revents = 0;
// wait for the socket // poll the fd with no timeout
if (block) return select(_socket + 1, &_set, &set2, nullptr, nullptr) > 0; return poll(&_fd, 1, 0) > 0;
// we do not want to block, so we use a small timeout
struct timeval timeout;
// no timeout at all
timeout.tv_sec = timeout.tv_usec = 0;
// no timeout at all
return select(_socket + 1, &_set, &set2, nullptr, &timeout) > 0;
} }
}; };

View File

@ -220,8 +220,8 @@ private:
// object to poll a socket // object to poll a socket
Poll poll(_socket); Poll poll(_socket);
// wait until socket is readable, but do not block // check if socket is readable
return poll.readable(false); return poll.readable();
} }
/** /**
@ -233,8 +233,8 @@ private:
// object to poll a socket // object to poll a socket
Poll poll(_socket); Poll poll(_socket);
// wait until socket is writable, but do not block // check if socket is writable
return poll.writable(false); return poll.writable();
} }
/** /**

View File

@ -24,6 +24,7 @@
#include "sslhandshake.h" #include "sslhandshake.h"
#include <thread> #include <thread>
#include <netinet/in.h> #include <netinet/in.h>
#include <poll.h>
/** /**
* Set up namespace * Set up namespace
@ -99,8 +100,8 @@ private:
// get address info // get address info
AddressInfo addresses(_hostname.data(), _port); AddressInfo addresses(_hostname.data(), _port);
// an fdset to monitor for writability // the pollfd structure, needed for poll()
fd_set writeset; pollfd fd;
// iterate over the addresses // iterate over the addresses
for (size_t i = 0; i < addresses.size(); ++i) for (size_t i = 0; i < addresses.size(); ++i)
@ -117,17 +118,13 @@ private:
// try to connect non-blocking // try to connect non-blocking
if (connect(_socket, addresses[i]->ai_addr, addresses[i]->ai_addrlen) == 0) break; if (connect(_socket, addresses[i]->ai_addr, addresses[i]->ai_addrlen) == 0) break;
// we set the timeout to a timeout, with 5 seconds as the default // set the struct members
struct timeval timeout{_timeout,0}; fd.fd = _socket;
fd.events = POLLOUT;
// reset the fdset fd.revents = 0;
FD_ZERO(&writeset);
// perform the poll, with a very long time to allow the event to occur
// set the fd to monitor for writing int ret = poll(&fd, 1, _timeout * 1000);
FD_SET(_socket, &writeset);
// perform a select, wait for something to happen on one of the fds
int ret = select(_socket + 1, nullptr, &writeset, nullptr, &timeout);
// log the error for the time being // log the error for the time being
if (ret == 0) _error = "connection timed out"; if (ret == 0) _error = "connection timed out";