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