3

I have a program which reads from a file which is a list of domain names. It performs asynchronous DNS and then downloads the landing page for each domain using an asynchronous epoll loop.

The program runs fine for thousands of iterations and then bombs out with a *** buffer overflow detected ***: terminated error. Here is the backtrace:

Program received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140737351415616) at pthread_kill.c:44
44  pthread_kill.c: No such file or directory.
(gdb) bt
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737351415616) at pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140737351415616) at pthread_kill.c:80
#2  __GI___pthread_kill (threadid=140737351415616, signo=signo@entry=6) at pthread_kill.c:91
#3  0x00007ffff7db0476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7d967b7 in __GI_abort () at abort.c:79
#5  0x00007ffff7df75e6 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7f48ef4 "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:155
#6  0x00007ffff7ea322a in __GI___fortify_fail (msg=msg@entry=0x7ffff7f48e9a "buffer overflow detected") at fortify_fail.c:26
#7  0x00007ffff7ea1b46 in __GI___chk_fail () at chk_fail.c:28
#8  0x00007ffff7ea316b in __fdelt_chk (d=<optimised out>) at fdelt_chk.c:25
#9  0x00007ffff7f97362 in ares_fds () from /lib/x86_64-linux-gnu/libcares.so.2
#10 0x000055555555682d in wait_ares (channel=0x555556bb32a0) at epoll_recv_with_async_dns.c:80
#11 0x000055555555773c in main (argc=2, argv=0x7fffffffe0a8) at epoll_recv_with_async_dns.c:303

As you can see the backtrace points to a call to ares_fds. The offending line of code is:

nfds = ares_fds(channel, &read_fds, &write_fds);

I fail to see how there is a buffer overflow in that line of code. Any ideas what I can do further to debug this and find and fix the problem. For those interested a minimal reproducer is here below:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <ares.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#define MAXWAITING 1000 /* Max. number of parallel DNS queries */
#define MAXTRIES      3 /* Max. number of tries per domain */
#define DNSTIMEOUT    3000 /* Max. number of ms for first try */
#define SERVERS    "1.0.0.1,8.8.8.8" /* DNS server to use (Cloudflare & Google) */
#define MAXDOMAINS 8192
#define MAX_CONNECTIONS 8192
#define TIMEOUT 10000
int epfd;
int sockfd[MAX_CONNECTIONS];
struct epoll_event event[MAX_CONNECTIONS];
struct sockaddr_in dest[MAX_CONNECTIONS];
char resolved[MAXDOMAINS][254];
char ips[MAXDOMAINS][128];
int current = 0, active = 0, next = 0;
char servers[MAX_CONNECTIONS][128];
char domains[MAX_CONNECTIONS][254];
int i, num_ready, connections = 0, done = 0, total_bytes = 0, total_domains = 0, iterations = 0, count = 0;
static int nwaiting;

static void state_cb(void *data, int s, int read, int write)
{
    //printf("Change state fd %d read:%d write:%d\n", s, read, write);
}

static void callback(void *arg, int status, int timeouts, struct hostent *host)
{
    nwaiting--;

    if(!host || status != ARES_SUCCESS){
        //fprintf(stderr, "Failed to lookup %s\n", ares_strerror(status));
        return;
    }

    char ip[INET6_ADDRSTRLEN];

    if (host->h_addr_list[0] != NULL){
        inet_ntop(host->h_addrtype, host->h_addr_list[0], ip, sizeof(ip));
        strcpy(resolved[current], host->h_name);
        strcpy(ips[current], ip);
        if (current < MAXDOMAINS - 1) current++; else current = 0;
        active++;
        printf("active %d\r", active);
    }
}

static void wait_ares(ares_channel channel)
{
    struct timeval *tvp, tv;
    fd_set read_fds, write_fds;
    int nfds = 0;

    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);

    nfds = ares_fds(channel, &read_fds, &write_fds);
    
    if (nfds > 0) {
    tvp = ares_timeout(channel, NULL, &tv);
        select(nfds, &read_fds, &write_fds, NULL, tvp);
        ares_process(channel, &read_fds, &write_fds);
    }     
}
                
int main(int argc, char *argv[]) {
        
    sigaction(SIGPIPE, &(struct sigaction){SIG_IGN}, NULL);
    FILE * fp;
    char domain[128];
    size_t len = 0;
    ssize_t read;
    ares_channel channel;
    int status, dns_done = 0;
    int optmask;
    
    status = ares_library_init(ARES_LIB_INIT_ALL);
    if (status != ARES_SUCCESS) {
        printf("ares_library_init: %s\n", ares_strerror(status));
        return 1;
    }

    struct ares_options options = {
        .timeout = DNSTIMEOUT,     /* set first query timeout */
        .tries = MAXTRIES       /* set max. number of tries */
    };
    optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;

    status = ares_init_options(&channel, &options, optmask);
    if (status != ARES_SUCCESS) {
        printf("ares_init_options: %s\n", ares_strerror(status));
        return 1;
    }

    status = ares_set_servers_csv(channel, SERVERS);
    if (status != ARES_SUCCESS) {
        printf("ares_set_servers_csv: %s\n", ares_strerror(status));
        return 1;
    }
    
    fp = fopen(argv[1], "r");
    if (!fp)
        exit(EXIT_FAILURE);

    do{
        if (nwaiting >= MAXWAITING || dns_done) {
            do {
                wait_ares(channel);
                
            } while (nwaiting > MAXWAITING);
        }
        if (!dns_done) {
            if (fscanf(fp, "%128s", domain) == 1) {
                ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
                nwaiting++;
            } else {
                dns_done = 1;
            }
        }
    } while (active < MAX_CONNECTIONS);
    
    /*---Open sockets for streaming---*/
    for (i = 0; i < MAX_CONNECTIONS; i++)
    { 
        if ( (sockfd[i] = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0 ) {
            perror("Socket");
            exit(errno);
        }
        count++;
    }

    while (1)
    {
        /*---Do async DNS---*/
        while (/*active < MAXDOMAINS &&*/ nwaiting > 0) {
            //printf("active = %d MAXDOMAINS = %d nwaiting = %d MAXWAITING = %d\n", active, MAXDOMAINS, nwaiting, MAXWAITING);
            if (nwaiting >= MAXWAITING || dns_done) {
                do {
                    wait_ares(channel);
                } while (nwaiting > MAXWAITING);
            }
            if (!dns_done) {
                if (fscanf(fp, "%127s", domain) == 1) {
                    ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
                    nwaiting++;
                } else {
                    dns_done = 1;
                }
            }
        } //while (active < MAXDOMAINS);
        
        if (done && count == 0) break;
    }
    ares_destroy(channel);
    ares_library_cleanup();
    fclose(fp);
    printf("\nFinished without errors\n");
    return 0;
}

The abort doesn't happen if I comment out the section which creates the sockets:

 /*---Open sockets for streaming---*/
    for (i = 0; i < MAX_CONNECTIONS; i++)
    { 
        if ( (sockfd[i] = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0 ) {
            perror("Socket");
            exit(errno);
        }
        count++;
    }

So whatever the problem is it is related to the fact that I have a number of sockets file descriptors. Any ideas?

FURTHER EDIT:

Further debugging seems to show the problem is related to the number of sockets opened. If I reduce the number of sockets created to 1017 the abort no longer happens. While if I create 1018 sockets the program aborts.

James Read
  • 419
  • 2
  • 7
  • 13
  • 1
    See if this might apply: https://github.com/c-ares/c-ares/pull/332. Consider updating to c-ares 1.18.1 (27-oct-2021). Also consider posting to the C-Ares discussion group: https://lists.haxx.se/listinfo/c-ares – paulsm4 Jan 03 '22 at 19:20
  • @paulsm4 I updated. The problem persists. Could it be a memory corruption of some sort? – James Read Jan 03 '22 at 20:49
  • Per our discussion below, it sounds like your question is answered: PROBLEM: ares_fd() occasionally crashes with "buffer overflow detected". CAUSE: ares_fd() depends on FD_SETSIZE limit, which is hard-coded to "1024" on Linux. A large #/TCP connections can easily exceed this limit. WORKAROUNDS: 1) throttle the #/connections, 2) find an alternative to c-ares. – paulsm4 Jan 04 '22 at 16:29
  • 1
    @paulsm4 I ended up developing an ```epoll``` based implementation that gets around this problem. Testing it now. Looks promising. – James Read Jan 04 '22 at 16:37
  • Cool :) Thank you for the update! – paulsm4 Jan 04 '22 at 18:30

2 Answers2

4

It looks like this might be the root cause:

https://c-ares.org/mail/c-ares-archive-2017-08/0002.shtml

>>> The stack trace is shown as above.
>>>
>>> /(gdb) bt/
>>> /#0 0x00007f959c01ac37 in __GI_raise (sig=sig_at_entry=6) at
>>> ../nptl/sysdeps/unix/sysv/linux/raise.c:56/
>>> /#1 0x00007f959c01e028 in __GI_abort () at abort.c:89/
>>> /#2 0x00007f959c0572a4 in __libc_message
>>> (do_abort=do_abort_at_entry=2, fmt=fmt_at_entry=0x7f959c166d70 "*** %s
>>> ***: %s terminated\n")/
>>> / at ../sysdeps/posix/libc_fatal.c:175/
>>> /#3 0x00007f959c0f283c in __GI___fortify_fail (msg=<optimized out>,
>>> msg_at_entry=0x7f959c166d07 "buffer overflow detected") at
>>> fortify_fail.c:38/
>>> /#4 0x00007f959c0f1710 in __GI___chk_fail () at chk_fail.c:28/
>>> /#5 0x00007f959c0f2787 in __fdelt_chk (d=<optimized out>) at
>>> fdelt_chk.c:25/
>>> /#6 0x00007f959c6b69ad in ares_fds () from
>>> /usr/local/multiplier/system/libs/libcares.so.2/
>>> /#7 0x000000000040b448 in rec_c_ares_execute () at
>>> /home/necs/dev/apat/source/recorder/recdns.c:157/
>>> /#8 0x00000000004052f2 in rec_main_thread (data=0x0) at
>>> /home/necs/dev/apat/source/recorder/rec.c:772/
>>> /#9 0x0000000000403de1 in main (argc=7, argv=0x7fff58cde398) at
>>> /home/necs/dev/apat/source/recorder/main.c:129/
>> ...

You are either crossing FD_SETSIZE limit, or have negative number of fds. Glibc checks this internally and causes crash if check will fail: https://github.com/lattera/glibc/blob/master/debug/fdelt_chk.c

Daniel Received on 2017-08-01

Since I'm not sure what platform you're on, I can't recommend a good way to inspect the value before calling ares_fds(), besides keeping track of the previous nfds (the return value immediately before the failure).

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • I added sanity checks to the fds but still get the same behaviour. – James Read Jan 03 '22 at 22:27
  • I'm curious what your "sanity checks" look like. I'm sure they're OK - I'm just curious. I'm also curious why the stack trace says "__fdelt_chk (d=)". I cited [fdelt_chk.c source](https://github.com/lattera/glibc/blob/master/debug/fdelt_chk.c) above. Who knows? Maybe this is actually a gcc bug? Or maybe the FD_SETSIZE limit the library was compiled against is different from the actual FD_SETSIZE on your system? Q: What *IS* FD_SETSIZE on your system? – paulsm4 Jan 03 '22 at 22:46
  • ```if (sizeof(read_fds) < FD_SETSIZE && sizeof(write_fds) < FD_SETSIZE && !(sizeof(read_fds) < 0) && !(sizeof(write_fds) < 0))``` It could probably be put more elegantly but I think it does the job. – James Read Jan 03 '22 at 22:59
  • FD_SETSIZE is 1024. I'm working on Ubuntu. – James Read Jan 03 '22 at 23:00
  • I'm not sure "sizeof(read_fds)" is what you want. Look at line 81 in select.h: https://github.com/openbsd/src/blob/master/sys/sys/select.h. I believe you actually need to know the socket# – paulsm4 Jan 04 '22 at 02:47
  • It seems that nfds is too high. A modified version of my program gave this output just before aborting ```nfds = 32767 size of read_fds = 128, size of write_fds = 128 FD_SETSIZE = 1024``` I guess I need to figure out why nfds is so high and rectify that. Any ideas? – James Read Jan 04 '22 at 03:31
  • Unlike BSD and Windows, FD_SETSIZE is hard-coded to 1024 on Linux. Why is nfds so high? One possibility: if you're burning through a lot of TCP/IP connections ... then you've probably got a lot of sockets in TIME_WAIT. Of course, I have no idea if that actually applies to your use case. Anyway, here's a good article: http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html – paulsm4 Jan 04 '22 at 05:24
  • In fact, I have a version of the code that just does the asynchronous DNS and no HTTP communication with an epoll loop. That version never aborts. So you could be on to something here. Whatever the problem is it only happens when doing HTTP communication. – James Read Jan 04 '22 at 08:13
  • I have made some major edits to my original question. I managed to create a minimal reproducer and to isolate the problem to be associated with the creation of a number of sockets. Hope that helps. – James Read Jan 04 '22 at 11:18
1

As paulsm4 answered the problem is to do with the number of socket descriptors open. To get around this problem I reimplemented the program to use epoll instead of select thus bypassing the problem. Full code listing below:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <ares.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#define MAXWAITING 1000 /* Max. number of parallel DNS queries */
#define MAXTRIES      3 /* Max. number of tries per domain */
#define DNSTIMEOUT    3000 /* Max. number of ms for first try */
#define DNS_MAX_EVENTS 10000
#define DNS_MAX_SERVERS 2
#define SERVERS    "1.0.0.1,8.8.8.8" /* DNS server to use (Cloudflare & Google) */
#define MAXDOMAINS 8192
#define PORT 80
#define MAXBUF 1024
#define MAX_EPOLL_EVENTS 8192
#define MAX_CONNECTIONS 8192
#define TIMEOUT 10000
ares_socket_t dns_client_fds[ARES_GETSOCK_MAXNUM] = {0};
struct epoll_event ev, dns_events[DNS_MAX_EVENTS];
int i,bitmask,nfds, epollfd, timeout, fd_count, ret;
int epfd;
int sockfd[MAX_CONNECTIONS];
struct epoll_event event[MAX_CONNECTIONS];
struct sockaddr_in dest[MAX_CONNECTIONS];
char resolved[MAXDOMAINS][254];
char ips[MAXDOMAINS][128];
int current = 0, active = 0, next = 0;
char servers[MAX_CONNECTIONS][128];
char domains[MAX_CONNECTIONS][254];
char get_buffer[MAX_CONNECTIONS][1024];
char buffer[MAX_CONNECTIONS][MAXBUF];
int buffer_used[MAX_CONNECTIONS];
struct timespec startTime, stopTime;
int i, num_ready, connections = 0, done = 0, total_bytes = 0, total_domains = 0, iterations = 0, count = 0;
FILE * fp;
struct epoll_event events[MAX_EPOLL_EVENTS];
static int nwaiting;

static void state_cb(void *data, int s, int read, int write)
{
    //printf("Change state fd %d read:%d write:%d\n", s, read, write);
}

static void callback(void *arg, int status, int timeouts, struct hostent *host)
{
    nwaiting--;

    if(!host || status != ARES_SUCCESS){
        //fprintf(stderr, "Failed to lookup %s\n", ares_strerror(status));
        return;
    }

    char ip[INET6_ADDRSTRLEN];

    if (host->h_addr_list[0] != NULL){
        inet_ntop(host->h_addrtype, host->h_addr_list[0], ip, sizeof(ip));
        strcpy(resolved[current], host->h_name);
        strcpy(ips[current], ip);
        if (current < MAXDOMAINS - 1) current++; else current = 0;
        active++;
        printf("active %d\r", active);
    }
}

static void wait_ares(ares_channel channel)
{
    nfds=0;
    bitmask=0;
    for (i =0; i < DNS_MAX_SERVERS ; i++) {
        if (dns_client_fds[i] > 0) {
            if (epoll_ctl(epollfd, EPOLL_CTL_DEL, dns_client_fds[i], NULL) < 0) {
                continue;
            }
        }
    }
    memset(dns_client_fds, 0, sizeof(dns_client_fds));
    bitmask = ares_getsock(channel, dns_client_fds, DNS_MAX_SERVERS);
    for (i =0; i < DNS_MAX_SERVERS ; i++) {
       if (dns_client_fds[i] > 0) {
            ev.events = 0;
            if (ARES_GETSOCK_READABLE(bitmask, i)) {
                ev.events |= EPOLLIN;
            }
            if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
                ev.events |= EPOLLOUT;
            }
            ev.data.fd = dns_client_fds[i];
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, dns_client_fds[i], &ev) < 0) {
                if(errno == EEXIST) {
                    nfds++;
                    continue;
                }
                continue;
            }
            nfds++;
        }
    }
    if(nfds==0)
    {
        return;
    }
    timeout = 1000;//millisecs
    fd_count = epoll_wait(epollfd, dns_events, DNS_MAX_EVENTS, timeout);
    if (fd_count < 0) {
        return;
    }
    if (fd_count > 0) {
        for (i = 0; i < fd_count; ++i) {
            ares_process_fd(channel, ((dns_events[i].events) & (EPOLLIN) ? dns_events[i].data.fd:ARES_SOCKET_BAD), ((dns_events[i].events) & (EPOLLOUT)? dns_events[i].data.fd:ARES_SOCKET_BAD));
        }
    } else {
        ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
    }       
}

void make_socket_and_connect (int sock)
{
    if ( (sockfd[sock] = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0 ) {
        perror("Socket");
        exit(errno);
    }
    count++;
    event[sock].events = EPOLLIN|EPOLLOUT;
    event[sock].data.fd = sockfd[sock];
    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd[sock], &event[sock]);
    bzero(&dest[sock], sizeof(dest[sock]));
    dest[sock].sin_family = AF_INET;
    dest[sock].sin_port = htons(PORT);
    if ( inet_pton(AF_INET, servers[sock], &dest[sock].sin_addr.s_addr) == 0 ) {
        printf("\n");
        perror(servers[sock]);
        exit(errno);
    }
    if ( connect(sockfd[sock], (struct sockaddr*)&dest[sock], sizeof(dest[sock])) != 0 ) {
        if(errno != EINPROGRESS) {
            printf("%s\n", servers[sock]);
            perror("Connect again ");
            //exit(errno);
        }
        buffer_used[sock] = 0;
    }
}

int is_valid_ip(char *domain)
{
    if (!strcmp(domain, "255.255.255.255"))
        return 0;
    if (!strcmp(domain, "192.168.1.0"))
        return 0;
    if (!strcmp(domain, "127.0.0.0"))
        return 0;
        
    return 1;
}

void close_socket (int socket)
{
    close(sockfd[socket]);
    count--;
    epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd[socket], &event[socket]);
}

void get_domain_and_ip(int id)
{
    //close_socket(id);
    active--;
get_domain_name:
    strcpy(servers[id], ips[next]);
    strcpy(domains[id], resolved[next]);
    if (next < (MAXDOMAINS - 1)) next++; else next = 0;
    if (is_valid_ip(servers[id]))
    {
        make_socket_and_connect(id);
        total_domains++;
    }
    else
        goto get_domain_name;                
}

void get_domain_and_ip_without_connect(int id)
{
get_domain_name2:
    strcpy(servers[id], ips[next]);
    strcpy(domains[id], resolved[next]);
    if (next < (MAXDOMAINS - 1)) next++; else next = 0;
    if (!is_valid_ip(servers[id]))
        goto get_domain_name2;                
}

void get_time()
{
    clock_gettime(CLOCK_MONOTONIC, &stopTime);
    uint64_t msElapsed = (stopTime.tv_nsec - startTime.tv_nsec) / 1000000 + (stopTime.tv_sec - startTime.tv_sec) * 1000;
    double seconds = (double)msElapsed / 1000.0;
    iterations++;
    fprintf(stderr, "iterations=%d total domains=%d elapsed=%2.2fs domains/s=%2.2f KB=%d Mbit/s=%2.2f num_ready=%d count=%d active=%d end\r"
            , iterations, total_domains, seconds, total_domains/seconds, total_bytes/1024, 8*total_bytes/seconds/1024/1204, num_ready, count, active);
}

ssize_t send_data(int id)
{
    ssize_t nByte = send(sockfd[id], get_buffer[id] + buffer_used[id], strlen(get_buffer[id]) - buffer_used[id], 0);
    return nByte;
}

ssize_t recv_data(int id)
{
    ssize_t nByte = recv(sockfd[id], buffer[id], sizeof(buffer[id]), 0);
    return nByte;
}

int wait()
{
    int ret = epoll_wait(epfd, events, MAX_EPOLL_EVENTS, TIMEOUT/*timeout*/);
    return ret;
}
                
int main(int argc, char *argv[]) {
        
    sigaction(SIGPIPE, &(struct sigaction){SIG_IGN}, NULL);
    FILE * fp;
    char domain[254];
    size_t len = 0;
    ssize_t read;
    ares_channel channel;
    int status, dns_done = 0;
    int optmask;
    
    status = ares_library_init(ARES_LIB_INIT_ALL);
    if (status != ARES_SUCCESS) {
        printf("ares_library_init: %s\n", ares_strerror(status));
        return 1;
    }

    struct ares_options options = {
        .timeout = DNSTIMEOUT,     /* set first query timeout */
        .tries = MAXTRIES       /* set max. number of tries */
    };
    optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;

    status = ares_init_options(&channel, &options, optmask);
    if (status != ARES_SUCCESS) {
        printf("ares_init_options: %s\n", ares_strerror(status));
        return 1;
    }

    status = ares_set_servers_csv(channel, SERVERS);
    if (status != ARES_SUCCESS) {
        printf("ares_set_servers_csv: %s\n", ares_strerror(status));
        return 1;
    }
    
    memset(dns_client_fds, 0, sizeof(dns_client_fds));
    memset((char *)&ev, 0, sizeof(struct epoll_event));
    memset((char *)&dns_events[0], 0, sizeof(dns_events));

    epollfd = epoll_create(DNS_MAX_SERVERS);
    
    fp = fopen(argv[1], "r");
    if (!fp)
        exit(EXIT_FAILURE);

    do{
        if (nwaiting >= MAXWAITING || dns_done) {
            do {
                wait_ares(channel);
                
            } while (nwaiting > MAXWAITING);
        }
        if (!dns_done) {
            if (fscanf(fp, "%253s", domain) == 1) {
                ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
                nwaiting++;
            } else {
                //fprintf(stderr, "done sending\n");
                dns_done = 1;
            }
        }
    } while (active < MAX_CONNECTIONS);
    
    /*---Open sockets for streaming---*/
    for (i = 0; i < MAX_CONNECTIONS; i++)
    { 
        if ( (sockfd[i] = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0 ) {
            perror("Socket");
            exit(errno);
        }
        count++;
    }

    /*---Add sockets to epoll---*/
    epfd = epoll_create1(0);
    for (i = 0; i < MAX_CONNECTIONS; i++)
    {
        event[i].events = EPOLLIN|EPOLLOUT; 
        event[i].data.fd = sockfd[i];
        epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd[i], &event[i]);
    }
    
    /*---Initialize server address/port structs---*/
    for (i = 0; i < MAX_CONNECTIONS; i++)
    {
        get_domain_and_ip_without_connect(i);
        //printf("%s %s\n", servers[i], domains[i]);
        bzero(&dest[i], sizeof(dest[i]));
        dest[i].sin_family = AF_INET;
        dest[i].sin_port = htons(PORT);
        if ( inet_pton(AF_INET, servers[i], &dest[i].sin_addr.s_addr) == 0 ) {
           perror(servers[i]);
           exit(errno);
        }
    }
    
    /*---Connect to servers---*/
    for (i = 0; i < MAX_CONNECTIONS; i++)
    {
        if ( connect(sockfd[i], (struct sockaddr*)&dest[i], sizeof(dest[i])) != 0 ) {
            if(errno != EINPROGRESS) {
                perror("Connect ");
                //exit(errno);
            }
            buffer_used[i] = 0;
        }
    }
    clock_gettime(CLOCK_MONOTONIC, &startTime);
    while (1)
    {
        /*---Do async DNS---*/
        while (active < MAXDOMAINS && nwaiting > 0) {
            //printf("active = %d MAXDOMAINS = %d nwaiting = %d MAXWAITING = %d\n", active, MAXDOMAINS, nwaiting, MAXWAITING);
            if (nwaiting >= MAXWAITING || dns_done) {
                do {
                    wait_ares(channel);
                } while (nwaiting > MAXWAITING);
            }
            if (!dns_done) {
                if (fscanf(fp, "%253s", domain) == 1) {
                    ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
                    nwaiting++;
                } else {
                    //fprintf(stderr, "done sending\n");
                    dns_done = 1;
                }
            }
        } //while (active < MAXDOMAINS);
        /*---Wait to be able to send---*/
        num_ready = wait();
        get_time();
        if (!num_ready) break;
        for(i = 0; i < num_ready; i++) {
            int index;
            if(events[i].events & EPOLLOUT) {
                for (int j = 0; j < MAX_CONNECTIONS; j++)
                {
                    if (events[i].data.fd == sockfd[j])
                    {
                        index = j;
                        break;
                    }
                }
                snprintf(get_buffer[index], sizeof(get_buffer[index]), 
                "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\r\n\r\n", "/", domains[i]);
                ssize_t nByte = 0;
                if (buffer_used[index] < strlen(get_buffer[index]))
                    nByte = send_data(index);
                if (nByte > 0)
                {
                    buffer_used[index] += nByte;
                    total_bytes += nByte;
                }
                if (nByte == -1 && errno == EPIPE)
                {
                    get_domain_and_ip(index);
                }
            }
            if(events[i].events & EPOLLIN) {
                for (int j = 0; j < MAX_CONNECTIONS; j++)
                {
                    if (events[i].data.fd == sockfd[j])
                    {
                        index = j;
                        break;
                    }
                }
                bzero(buffer[index], MAXBUF);
                ssize_t nByte = recv_data(index);
                //if (nByte > 0) printf("Received: %s from %s at %s \n", buffer[index], domains[index], servers[index]);
                if (nByte > 0) total_bytes += nByte;
                if (nByte == 0)
                {
                    close_socket(index);
                    if (!done)
                    {
                        get_domain_and_ip(index);
                    }
                }
            }
        }
        get_time();
        if (done && count == 0) break;
    }
    ares_destroy(channel);
    ares_library_cleanup();
    fclose(fp);
    printf("\nFinished without errors\n");
    return 0;
}
James Read
  • 419
  • 2
  • 7
  • 13