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