1 /*
2  * (C) 2011 Oleg V. Palij <o.palij@gmail.com>
3  * Released under the GNU GPL, see the COPYING file in the source distribution for its full text.
4  */
5 
6 #include <iostream>
7 #include <iomanip>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 // sockaddr_in
11 #include <netinet/in.h>
12 #include <netdb.h>
13 #include <algorithm>
14 #include <unistd.h>
15 #include <errno.h>
16 
17 #include <pthread.h>
18 
19 #include "resolver.hpp"
20 
21 using std::cout;
22 using std::cerr;
23 using std::endl;
24 using std::string;
25 using std::map;
26 using std::vector;
27 
Resolver()28 Resolver::Resolver() {
29    resolve_func = "NONE";
30    max_threads = 0;
31 }
32 
Start()33 void Resolver::Start() {
34    max_threads = MAX_THREADS;
35    init();
36 }
37 
Start(int threads_num)38 void Resolver::Start(int threads_num) {
39    max_threads = threads_num;
40    init();
41 }
42 
init()43 void Resolver::init() {
44 #if !defined(USE_GETHOSTBYADDR_R) && !defined(THREADSAFE_GETNAMEINFO)
45    /* getnameinfo (on *BSD) and gethostbyaddr is notreentrant */
46    max_threads = 1;
47 #endif
48    pthread_mutexattr_init(&mAttr);
49    pthread_mutex_init(&rMutex, &mAttr);
50 
51    pthread_condattr_init(&cAttr);
52    pthread_cond_init(&rCond, &cAttr);
53 
54    //cout << "Starting " << max_threads << " threads" << endl;
55 #if defined(USE_GETNAMEINFO)
56    resolve_func = "GETNAMEINFO";
57 #elif defined(USE_GETHOSTBYADDR_R)
58    resolve_func = "GETHOSTBYADDR_R";
59 #elif defined(USE_GETHOSTBYADDR)
60    resolve_func = "GETHOSTBYADDR";
61 #endif
62    //cout << "Using " << resolve_func << " in " << max_threads << " threads"<< endl;
63 
64    pThreadArgs = new ThreadArgs[max_threads];
65    for(int i = 0; i < max_threads; i++) {
66       pThreadArgs[i].pMain = this;
67       pThreadArgs[i].ThreadNum = i;
68       pthread_create(&pthWorker, NULL, (void *(*) (void *)) &Worker, (void *) &pThreadArgs[i]);
69    }
70 }
71 
~Resolver()72 Resolver::~Resolver() {
73    pthread_mutex_destroy(&rMutex);
74    pthread_mutexattr_destroy(&mAttr);
75 
76    pthread_cond_destroy(&rCond);
77    pthread_condattr_destroy(&cAttr);
78 
79    delete [] pThreadArgs;
80 }
81 
ResolveFunc()82 string Resolver::ResolveFunc() {
83    return resolve_func;
84 }
85 
MaxThreads()86 int Resolver::MaxThreads() {
87    return max_threads;
88 }
89 
ResolveMode()90 string Resolver::ResolveMode() {
91    string result;
92    switch (resolve_mode) {
93       case RESOLVE_SYNC: result = "SYNC"; break;
94       case RESOLVE_ASYNC: result = "ASYNC"; break;
95    }
96    return result;
97 }
98 
IsIP(string ip)99 /* static */ bool Resolver::IsIP(string ip) {
100    struct sockaddr_in sa;
101    int result = inet_pton(AF_INET, ip.c_str(), &(sa.sin_addr));
102    return result != 0;
103 }
104 
StripDomain(string & rName)105 /* static */ void Resolver::StripDomain(string& rName) {
106    if (!IsIP(rName)) {
107       size_t found = rName.find_first_of(".");
108       if (found != string::npos) {
109          rName.erase(rName.begin()+found, rName.end());
110       }
111    }
112 }
113 
Worker(void * pThreadArg)114 /* static */ void Resolver::Worker(void* pThreadArg) {
115    ThreadArgs* pThreadArgs = reinterpret_cast<ThreadArgs*>(pThreadArg);
116    Resolver* pMain = reinterpret_cast<Resolver*>(pThreadArgs->pMain);
117    //int num = pThreadArgs->ThreadNum;
118    pthread_mutex_lock(&pMain->rMutex);
119    //cout << "------- worker (" << num << "): locked" << endl;
120    string name;
121    // /etc/hosts
122    //sethostent(1);
123    while (true) {
124       //cout << "q size = " << pMain->queue.size() << endl;
125       while (not pMain->queue.empty()) {
126          string ip = pMain->queue.front();
127          pMain->queue.pop_front();
128          // workaround for the condition when ip deleted from queue but not resolved yet can be added again
129          pMain->resolved[ip] = ip;
130          pthread_mutex_unlock(&pMain->rMutex);
131          string name = DoResolve(ip);
132          pthread_mutex_lock(&pMain->rMutex);
133          pMain->resolved[ip] = name;
134          //cout << "------- worker (" << num << "): resolved " << ip << " to " << name << ". " << pMain->queue.size() << " to go." << endl;
135       }
136       //cout << "------- worker (" << num << "): waiting for cond" << endl;
137       pthread_cond_wait(&pMain->rCond, &pMain->rMutex);
138       //cout << "------- worker (" << num << "): awaiked " << pMain->queue.size() <<endl;
139    }
140 }
141 
Resolve(string ip)142 string Resolver::Resolve(string ip) {
143    string result;
144    switch (resolve_mode) {
145       case RESOLVE_SYNC: result = ResolveSync(ip); break;
146       case RESOLVE_ASYNC: result = ResolveAsync(ip); break;
147    }
148    return result;
149 }
150 
ResolveAsync(string ip)151 string Resolver::ResolveAsync(string ip) {
152    //cout << "resolve: got " << ip << endl;
153    pthread_mutex_lock(&rMutex);
154    rit = resolved.find(ip);
155    qit = std::find(queue.begin(), queue.end(), ip);
156    string result;
157    bool added = false;
158    // already resolved
159    if (rit != resolved.end()) {
160       //cout << "resolve: already resolved" << endl;
161       result = rit->second;
162    // not resolved and not in queue
163    } else if (qit == queue.end()) {
164       //cout << "resolve: not resolved and not in queue" << endl;
165       queue.push_back(ip);
166       result = ip;
167       added = true;
168    // not resolved and already in queue
169    } else {
170       //cout << "resolve: not resolved and already in queue" << endl;
171       result = ip;
172    }
173    pthread_mutex_unlock(&rMutex);
174    if (added) {
175       //cout << "resolve: signaling" << endl;
176       pthread_cond_signal(&rCond);
177    }
178    return result;
179 }
180 
ResolveSync(string ip)181 string Resolver::ResolveSync(string ip) {
182    return DoResolve(ip);
183 }
184 
DoResolve(string ip)185 /* static */ string Resolver::DoResolve(string ip) {
186    struct in_addr addr;
187    if (!inet_aton(ip.c_str(), &addr)) {
188       return ip;
189    }
190    string result;
191    try {
192       result = DoRealResolve(&addr);
193    }
194    catch (int n) {
195       //cout << "Error resolving " << ip << ": " << n << endl;
196       result = ip;
197    }
198    return result;
199 }
200 
201 #ifdef USE_GETNAMEINFO
DoRealResolve(struct in_addr * addr)202 /* static */ string Resolver::DoRealResolve(struct in_addr* addr) {
203    struct sockaddr_in sin = {0};
204    int res;
205    sin.sin_family = AF_INET;
206    sin.sin_addr = *addr;
207    sin.sin_port = 0;
208 
209    char buf[NI_MAXHOST];
210    res = getnameinfo((struct sockaddr*)&sin, sizeof sin, buf, sizeof buf, NULL, 0, NI_NOFQDN);
211    if (res == 0) {
212       return buf;
213    } else {
214       throw res;
215    }
216 }
217 #elif defined(USE_GETHOSTBYADDR)
DoRealResolve(struct in_addr * addr)218 /* static */ string Resolver::DoRealResolve(struct in_addr* addr) {
219    struct hostent* he;
220    he = gethostbyaddr((const void*)addr, sizeof addr, AF_INET);
221    if (he) {
222       return he->h_name;
223    } else {
224       throw h_errno;
225    }
226 }
227 #elif defined(USE_GETHOSTBYADDR_R)
DoRealResolve(struct in_addr * addr)228 /* static */ string Resolver::DoRealResolve(struct in_addr* addr) {
229    struct hostent hostbuf;
230    struct hostent* hp;
231    size_t hstbuflen = 1024;
232    char* tmphstbuf;
233    int res = 0;
234    int herr;
235    bool error;
236 
237    tmphstbuf = (char*) malloc(hstbuflen);
238    if (!tmphstbuf) abort();
239 #ifdef GETHOSTBYADDR_R_RETURNS_INT
240    while ((res = gethostbyaddr_r((const void*)addr, sizeof addr, AF_INET,
241                                  &hostbuf, tmphstbuf, hstbuflen,
242                                  &hp, &herr)) == ERANGE) {
243 #else
244    while ((hp = gethostbyaddr_r((char*)addr, sizeof addr, AF_INET,
245                            &hostbuf, tmphstbuf, hstbuflen, &herr)) == NULL
246             && errno == ERANGE) {
247 #endif
248        /* Enlarge the buffer.  */
249        hstbuflen *= 2;
250        char* new_tmphstbuf;
251        new_tmphstbuf = static_cast<char *>(realloc(tmphstbuf, hstbuflen));
252        if (new_tmphstbuf == NULL) {
253            free(tmphstbuf);
254            throw -1;
255        } else {
256            tmphstbuf = new_tmphstbuf;
257        }
258    }
259 
260    error = ((res != 0) || (hp == NULL));
261    string result;
262    if (not error) {
263       result = hp->h_name;
264    }
265    free(tmphstbuf);
266    if (error) {
267       throw herr;
268    }
269    return result;
270 }
271 #else
272 /* static */ string Resolver::DoRealResolve(struct in_addr* addr) {
273    throw -1;
274 }
275 #endif
276