2012-05-06 00:41:52 +01:00
|
|
|
|
|
|
|
#include "dnsblast.h"
|
|
|
|
|
|
|
|
static unsigned long long
|
|
|
|
get_nanoseconds(void)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
|
|
return tv.tv_sec * 1000000000LL + tv.tv_usec * 1000LL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
init_context(Context * const context, const int sock,
|
|
|
|
const struct addrinfo * const ai, const _Bool fuzz)
|
|
|
|
{
|
|
|
|
const unsigned long long now = get_nanoseconds();
|
|
|
|
*context = (Context) {
|
|
|
|
.received_packets = 0UL, .sent_packets = 0UL,
|
|
|
|
.last_status_update = now, .startup_date = now,
|
|
|
|
.sock = sock, .ai = ai, .fuzz = fuzz, .sending = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
DNS_Header * const question_header = (DNS_Header *) context->question;
|
|
|
|
*question_header = (DNS_Header) {
|
|
|
|
.flags = htons(FLAGS_OPCODE_QUERY | FLAGS_RECURSION_DESIRED),
|
|
|
|
.qdcount = htons(1U), .ancount = 0U, .nscount = 0U, .arcount = 0U
|
|
|
|
};
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
find_name_component_len(const char *name)
|
|
|
|
{
|
|
|
|
int name_pos = 0;
|
|
|
|
|
|
|
|
while (name[name_pos] != '.' && name[name_pos] != 0) {
|
|
|
|
if (name_pos >= UCHAR_MAX) {
|
|
|
|
return EOF;
|
|
|
|
}
|
|
|
|
name_pos++;
|
|
|
|
}
|
|
|
|
return name_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
encode_name(unsigned char ** const encoded_ptr, size_t encoded_size,
|
|
|
|
const char * const name)
|
|
|
|
{
|
|
|
|
unsigned char *encoded = *encoded_ptr;
|
|
|
|
const char *name_current = name;
|
|
|
|
int name_current_pos;
|
|
|
|
|
|
|
|
assert(encoded_size > (size_t) 0U);
|
|
|
|
encoded_size--;
|
|
|
|
for (;;) {
|
2012-05-15 03:57:47 +01:00
|
|
|
name_current_pos = find_name_component_len(name_current);
|
2012-05-06 00:41:52 +01:00
|
|
|
if (name_current_pos == EOF ||
|
|
|
|
encoded_size <= (size_t) name_current_pos) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*encoded++ = (unsigned char) name_current_pos;
|
|
|
|
memcpy(encoded, name_current, name_current_pos);
|
|
|
|
encoded_size -= name_current_pos - (size_t) 1U;
|
|
|
|
encoded += name_current_pos;
|
|
|
|
if (name_current[name_current_pos] == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
name_current += name_current_pos + 1U;
|
|
|
|
}
|
|
|
|
*encoded++ = 0;
|
|
|
|
*encoded_ptr = encoded;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fuzz(unsigned char * const question, const size_t packet_size)
|
|
|
|
{
|
|
|
|
int p = REFUZZ_PROBABILITY;
|
|
|
|
|
|
|
|
do {
|
|
|
|
question[rand() % packet_size] = rand() % 0xff;
|
|
|
|
} while (rand() < p && (p = p / 2) > 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
blast(Context * const context, const char * const name, const uint16_t type)
|
|
|
|
{
|
|
|
|
unsigned char * const question = context->question;
|
|
|
|
DNS_Header * const question_header = (DNS_Header *) question;
|
|
|
|
unsigned char * const question_data = question + sizeof *question_header;
|
|
|
|
const size_t sizeof_question_data =
|
|
|
|
sizeof question - sizeof *question_header;
|
|
|
|
|
|
|
|
question_header->id = context->id++;
|
|
|
|
unsigned char *msg = question_data;
|
|
|
|
assert(sizeof_question_data > (size_t) 2U);
|
|
|
|
encode_name(&msg, sizeof_question_data - (size_t) 2U, name);
|
|
|
|
PUT_HTONS(msg, type);
|
|
|
|
PUT_HTONS(msg, CLASS_IN);
|
|
|
|
const size_t packet_size = (size_t) (msg - question);
|
|
|
|
|
|
|
|
if (context->fuzz != 0) {
|
|
|
|
fuzz(question, packet_size);
|
|
|
|
}
|
|
|
|
while (sendto(context->sock, question, packet_size, 0,
|
|
|
|
context->ai->ai_addr, context->ai->ai_addrlen)
|
|
|
|
!= (ssize_t) packet_size) {
|
|
|
|
if (errno != EAGAIN && errno != EINTR) {
|
|
|
|
perror("sendto");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
context->sent_packets++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
usage(void) {
|
|
|
|
puts("\nUsage: dnsblast [fuzz] <host> [<count>] [<pps>] [<port>]\n");
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct addrinfo *
|
|
|
|
resolve(const char * const host, const char * const port)
|
|
|
|
{
|
|
|
|
struct addrinfo *ai, hints;
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof hints);
|
|
|
|
hints = (struct addrinfo) {
|
|
|
|
.ai_family = AF_UNSPEC, .ai_flags = 0, .ai_socktype = SOCK_DGRAM,
|
|
|
|
.ai_protocol = IPPROTO_UDP
|
|
|
|
};
|
|
|
|
const int gai_err = getaddrinfo(host, port, &hints, &ai);
|
|
|
|
if (gai_err != 0) {
|
|
|
|
fprintf(stderr, "[%s:%s]: [%s]\n", host, port, gai_strerror(gai_err));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
return ai;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_random_name(char * const name, size_t name_size)
|
|
|
|
{
|
|
|
|
const char charset_alnum[36] = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
|
|
|
|
|
|
assert(name_size > (size_t) 8U);
|
|
|
|
const int r1 = rand(), r2 = rand();
|
|
|
|
name[0] = charset_alnum[(r1) % sizeof charset_alnum];
|
|
|
|
name[1] = charset_alnum[(r1 >> 16) % sizeof charset_alnum];
|
|
|
|
name[2] = charset_alnum[(r2) % sizeof charset_alnum];
|
|
|
|
name[3] = charset_alnum[(r2 >> 16) % sizeof charset_alnum];
|
|
|
|
name[4] = '.'; name[5] = 'c'; name[6] = 'o'; name[7] = 'm';
|
|
|
|
name[8] = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
get_random_type(void)
|
|
|
|
{
|
|
|
|
const size_t weighted_types_len =
|
|
|
|
sizeof weighted_types / sizeof weighted_types[0];
|
|
|
|
size_t i = 0U;
|
|
|
|
const int rnd = rand();
|
|
|
|
int pos = RAND_MAX;
|
|
|
|
|
|
|
|
do {
|
|
|
|
pos -= weighted_types[i].weight;
|
|
|
|
if (rnd > pos) {
|
|
|
|
return weighted_types[i].type;
|
|
|
|
}
|
|
|
|
} while (++i < weighted_types_len);
|
|
|
|
|
|
|
|
return weighted_types[rand() % weighted_types_len].type;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_sock(const char * const host, const char * const port,
|
|
|
|
struct addrinfo ** const ai_ref)
|
|
|
|
{
|
|
|
|
int flag = 1;
|
|
|
|
int sock;
|
|
|
|
|
|
|
|
*ai_ref = resolve(host, port);
|
|
|
|
sock = socket((*ai_ref)->ai_family, (*ai_ref)->ai_socktype,
|
|
|
|
(*ai_ref)->ai_protocol);
|
|
|
|
if (sock == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE,
|
|
|
|
&(int[]) { MAX_UDP_BUFFER_SIZE }, sizeof (int));
|
|
|
|
setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE,
|
|
|
|
&(int[]) { MAX_UDP_BUFFER_SIZE }, sizeof (int));
|
2014-04-12 21:39:05 +01:00
|
|
|
#if defined(IP_PMTUDISC_OMIT)
|
|
|
|
setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER,
|
|
|
|
&(int[]) { IP_PMTUDISC_OMIT }, sizeof (int));
|
|
|
|
#elif defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
|
2012-05-06 00:41:52 +01:00
|
|
|
setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER,
|
|
|
|
&(int[]) { IP_PMTUDISC_DONT }, sizeof (int));
|
|
|
|
#elif defined(IP_DONTFRAG)
|
|
|
|
setsockopt(sock, IPPROTO_IP, IP_DONTFRAG, &(int[]) { 0 }, sizeof (int));
|
|
|
|
#endif
|
|
|
|
assert(ioctl(sock, FIONBIO, &flag) == 0);
|
|
|
|
|
|
|
|
return sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
receive(Context * const context)
|
|
|
|
{
|
|
|
|
unsigned char buf[MAX_UDP_DATA_SIZE];
|
|
|
|
|
|
|
|
while (recv(context->sock, buf, sizeof buf, 0) == (ssize_t) -1) {
|
|
|
|
if (errno == EAGAIN) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
assert(errno == EINTR);
|
|
|
|
}
|
|
|
|
context->received_packets++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
update_status(const Context * const context)
|
|
|
|
{
|
|
|
|
const unsigned long long now = get_nanoseconds();
|
|
|
|
const unsigned long long elapsed = now - context->startup_date;
|
|
|
|
unsigned long long rate =
|
|
|
|
context->received_packets * 1000000000ULL / elapsed;
|
|
|
|
if (rate > context->pps) {
|
|
|
|
rate = context->pps;
|
|
|
|
}
|
|
|
|
printf("Sent: [%lu] - Received: [%lu] - Reply rate: [%llu pps] - "
|
|
|
|
"Ratio: [%.2f%%] \r",
|
|
|
|
context->sent_packets, context->received_packets, rate,
|
|
|
|
(double) context->received_packets * 100.0 /
|
|
|
|
(double) context->sent_packets);
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
periodically_update_status(Context * const context)
|
|
|
|
{
|
|
|
|
unsigned long long now = get_nanoseconds();
|
|
|
|
|
|
|
|
if (now - context->last_status_update < UPDATE_STATUS_PERIOD) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
update_status(context);
|
|
|
|
context->last_status_update = now;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
empty_receive_queue(Context * const context)
|
|
|
|
{
|
|
|
|
while (receive(context) == 0)
|
|
|
|
;
|
|
|
|
periodically_update_status(context);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
throttled_receive(Context * const context)
|
|
|
|
{
|
|
|
|
unsigned long long now = get_nanoseconds(), now2;
|
|
|
|
const unsigned long long elapsed = now - context->startup_date;
|
|
|
|
const unsigned long long max_packets =
|
|
|
|
context->pps * elapsed / 1000000000UL;
|
|
|
|
|
|
|
|
if (context->sending == 1 && context->sent_packets <= max_packets) {
|
|
|
|
empty_receive_queue(context);
|
|
|
|
}
|
|
|
|
const unsigned long long excess = context->sent_packets - max_packets;
|
|
|
|
const unsigned long long time_to_wait = excess / context->pps;
|
|
|
|
int remaining_time = (int) (time_to_wait * 1000ULL);
|
|
|
|
int ret;
|
|
|
|
struct pollfd pfd = { .fd = context->sock,
|
|
|
|
.events = POLLIN | POLLERR };
|
|
|
|
if (context->sending == 0) {
|
|
|
|
remaining_time = -1;
|
|
|
|
} else if (remaining_time < 0) {
|
|
|
|
remaining_time = 0;
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
ret = poll(&pfd, (nfds_t) 1, remaining_time);
|
|
|
|
if (ret == 0) {
|
|
|
|
periodically_update_status(context);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (ret == -1) {
|
|
|
|
if (errno != EAGAIN && errno != EINTR) {
|
|
|
|
perror("poll");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
assert(ret == 1);
|
|
|
|
empty_receive_queue(context);
|
|
|
|
now2 = get_nanoseconds();
|
|
|
|
remaining_time -= (now2 - now) / 1000;
|
|
|
|
now = now2;
|
|
|
|
} while (remaining_time > 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char name[100U] = ".";
|
|
|
|
Context context;
|
|
|
|
struct addrinfo *ai;
|
|
|
|
const char *host;
|
|
|
|
const char *port = "domain";
|
|
|
|
unsigned long pps = ULONG_MAX;
|
|
|
|
unsigned long send_count = ULONG_MAX;
|
|
|
|
int sock;
|
|
|
|
uint16_t type;
|
|
|
|
_Bool fuzz = 0;
|
|
|
|
|
|
|
|
if (argc < 2 || argc > 6) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if (strcasecmp(argv[1], "fuzz") == 0) {
|
|
|
|
fuzz = 1;
|
|
|
|
argv++;
|
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
if (argc < 1) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
host = argv[1];
|
|
|
|
if (argc > 2) {
|
|
|
|
send_count = strtoul(argv[2], NULL, 10);
|
|
|
|
}
|
|
|
|
if (argc > 3) {
|
|
|
|
pps = strtoul(argv[3], NULL, 10);
|
|
|
|
}
|
|
|
|
if (argc > 4) {
|
|
|
|
port = argv[4];
|
|
|
|
}
|
|
|
|
if ((sock = get_sock(host, port, &ai)) == -1) {
|
|
|
|
perror("Oops");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
init_context(&context, sock, ai, fuzz);
|
|
|
|
context.pps = pps;
|
|
|
|
srand(0U);
|
|
|
|
assert(send_count > 0UL);
|
|
|
|
do {
|
|
|
|
if (rand() > REPEATED_NAME_PROBABILITY) {
|
|
|
|
get_random_name(name, sizeof name);
|
|
|
|
}
|
|
|
|
type = get_random_type();
|
|
|
|
blast(&context, name, type);
|
|
|
|
throttled_receive(&context);
|
|
|
|
} while (--send_count > 0UL);
|
|
|
|
update_status(&context);
|
|
|
|
|
|
|
|
context.sending = 0;
|
|
|
|
while (context.sent_packets != context.received_packets) {
|
|
|
|
throttled_receive(&context);
|
|
|
|
}
|
|
|
|
freeaddrinfo(ai);
|
|
|
|
assert(close(sock) == 0);
|
|
|
|
update_status(&context);
|
|
|
|
putchar('\n');
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|