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