1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
/*
* Async DNS resolver
* Copyright (C) 2009-2010 Unix Solutions Ltd.
*
* Released under MIT license.
* See LICENSE-MIT.txt for license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "log.h"
/* FreeBSD have problems with pathread_cancel so do not use async resolver */
#ifdef __FreeBSD__
int async_resolve_host(char *host, int port, struct sockaddr_in *sockaddr, int msec_timeout, int *active) {
msec_timeout = msec_timeout;
active = active;
struct hostent *hostinfo = gethostbyname(host);
if (hostinfo == NULL) {
int local_h_errno = h_errno;
LOGf("gethostbyname(%s) returned %s", host, hstrerror(local_h_errno));
return 1; // error
}
sockaddr->sin_family = AF_INET;
sockaddr->sin_port = htons(port);
sockaddr->sin_addr = *(struct in_addr *)hostinfo->h_addr;
// LOGf("Not using async resolver! Resolved %s to %s\n", host, inet_ntoa(sockaddr->sin_addr));
return 0;
}
#else
struct url_resolver {
char *host;
int port;
struct sockaddr_in *sockaddr;
int *status;
};
static void resolver_cleanup(void *p) {
if (p)
freeaddrinfo(p);
}
static void *resolver_thread(void *indata) {
struct url_resolver *uri = indata;
*(uri->status) = 0;
char *host = uri->host;
int port = uri->port;
struct sockaddr_in *sockaddr = uri->sockaddr;
int h_err;
struct addrinfo hints, *addrinfo = NULL;
memset(&hints, 0, sizeof hints);
int state;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &state);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &state);
pthread_cleanup_push(resolver_cleanup, NULL);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
h_err = getaddrinfo(host, NULL, &hints, &addrinfo);
pthread_cleanup_pop(0);
if (h_err == 0) {
int num_addrs = 0;
struct addrinfo *p;
for (p=addrinfo; p!=NULL; p=p->ai_next) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
sockaddr->sin_family = AF_INET;
sockaddr->sin_port = htons(port);
sockaddr->sin_addr = ipv4->sin_addr;
char IP[INET_ADDRSTRLEN];
inet_ntop(p->ai_family, &(sockaddr->sin_addr), IP, sizeof IP);
num_addrs++;
// LOGf("Resolved[%d] %s to %s", num_addrs, host, IP);
}
freeaddrinfo(addrinfo);
*(uri->status) = 1;
} else {
*(uri->status) = 2;
}
return NULL;
}
// Returns
// 0 on success
// 1 on error
// 2 on timeout
int async_resolve_host(char *host, int port, struct sockaddr_in *sockaddr, int msec_timeout, int *active) {
pthread_t dns_thread;
struct url_resolver uri;
int status = 0;
uri.host = host;
uri.port = port;
uri.sockaddr = sockaddr;
uri.status = &status;
int lactive = 1;
if (!active)
active = &lactive;
if (pthread_create(&dns_thread, NULL, resolver_thread, &uri)) {
log_perror("Failed to create resolver thread", errno);
return 1;
}
pthread_detach(dns_thread);
#define DNS_SLEEP 20000
long int waitcount = msec_timeout * 1000;
while (!status) {
if (!active) {
// LOG("Client !active, cancel resolver");
pthread_cancel(dns_thread);
return 2;
}
usleep(DNS_SLEEP);
waitcount = waitcount - DNS_SLEEP;
if (waitcount <= 0) {
// LOGf("Timed out resolving: %s", host);
pthread_cancel(dns_thread);
return 2; // Timed out
}
}
if (status == 1)
return 0; // Ok, resolved
return 1; // Error resolving
}
#endif
|