Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
document last changes
[simgrid.git] / src / nws_portability / dnsutil.c
1 /* $Id$ */
2
3 #include "config_portability.h"
4
5 #include <stdio.h>
6 #include <stdlib.h>     /* REALLOC() */
7 #include <string.h>
8 #include <sys/types.h>  /* sometimes required for #include <sys/socket.h> */
9 #include <sys/socket.h> /* AF_INET */
10 #include <netinet/in.h> /* struct in_addr */
11 #include <arpa/inet.h>  /* inet_addr() inet_ntoa() */
12 #include <netdb.h>      /* {end,set}hostent() gethostby{addr,name}() */
13
14 #include "diagnostic.h"
15 #include "osutil.h"
16 #include "dnsutil.h"
17 #include "protocol.h"
18
19
20 /*
21 ** NOTE: The man pages for {end,set}hostent() seem to imply that endhostent()
22 ** needs/should only be called after sethostent(1).  Below, we call
23 ** endhostent() after calling sethostent(0).  So far, this hasn't seemed to
24 ** cause any problems, and it also appears to have squished a bug on some
25 ** version of Unix where the O/S DNS cache was losing entries.
26 */
27
28 static void *lock = NULL;               /* local lock */
29
30 /*
31  * We cache host entries locally to avoid going to the DNS too often.  This
32  * also gets around an old Solaris bug which leaks memory whenever dlopen is
33  * called (such as on the dynamic DNS lookup library). 
34  *
35  * Cached values timeout after CACHE_TIMEOUT seconds.
36  */
37 #define CACHE_TIMEOUT 1800
38 static unsigned int cacheCount = 0;
39 static struct hostent **cache = NULL;
40 static double *cacheTimeout = NULL;
41
42
43 /*
44 ** Looks in the name and alias entries of #hostEntry# for a fully-qualified
45 ** name.  Returns the fqn if found; otherwise, returns the name entry.
46 */
47 static const char *
48 BestHostName(const struct hostent *hostEntry) {
49         int i;
50
51         if (hostEntry == NULL) {
52                 return NULL;
53         }
54
55         if (!strchr(hostEntry->h_name, '.')) {
56                 for (i = 0; hostEntry->h_aliases[i] != NULL; i++) {
57                         if (strchr(hostEntry->h_aliases[i], '.'))
58                                 return hostEntry->h_aliases[i]; /* found! */
59                 }
60         }
61
62         /* If we don't have a fully-qualified name, do the best we can.  */
63         return hostEntry->h_name;
64 }
65
66 /* 
67  * free a struct hostent *. 
68  */
69 void
70 FreeStructHostent(struct hostent *toFree) {
71         int i;
72
73         /* sanity check */
74         if (toFree == NULL) {
75                 return;
76         }
77
78         /* let's start to free */
79         if (toFree->h_aliases != NULL) {
80                 for (i=0; toFree->h_aliases[i] != NULL; i++) {
81                         free(toFree->h_aliases[i]);
82                 }
83                 free(toFree->h_aliases);
84         }
85         if (toFree->h_name != NULL) {
86                 free(toFree->h_name);
87         }
88         if (toFree->h_addr_list != NULL) {
89                 for (i=0; toFree->h_addr_list[i] != NULL; i++) {
90                         free(toFree->h_addr_list[i]);
91                 }
92                 free(toFree->h_addr_list);
93         }
94         free(toFree);
95
96         return;
97 }
98
99 /*
100  * copy a struct hostent * into a newly allocated struct hostent * that
101  * you need to free when you are done using. Returns NULL in case of
102  * errors.
103  */
104 struct hostent *
105 CopyStructHostent(const struct hostent *orig) {
106         struct hostent *ret;
107         int i,j;
108
109         /* sanity check */
110         if (orig == NULL) {
111                 return NULL;
112         }
113
114         ret = (struct hostent *)MALLOC(sizeof(struct hostent));
115         if (ret == NULL) {
116                 ERROR("Out of memory\n");
117                 return NULL;            /* out of memory */
118         }
119         memset((void *)ret, 0,  sizeof(struct hostent));
120         
121         /* make room for the name */
122         ret->h_name = strdup(orig->h_name);
123         if (ret->h_name == NULL) {
124                 free(ret);
125                 ERROR("Out of memory\n");
126                 return NULL;            /* out of memory */
127         }
128
129         /* count aliases and copy them */
130         for (i=0; orig->h_aliases != NULL && orig->h_aliases[i] != NULL; i++) {
131                 ;
132         }
133         ret->h_aliases = (char **)MALLOC(sizeof(char *) * (i+1));
134         if (ret->h_aliases == NULL) {
135                 FreeStructHostent(ret);
136                 ERROR("Out of memory\n");
137                 return NULL;
138         }
139         for (j=0; j < i; j++) {
140                 ret->h_aliases[j] = strdup(orig->h_aliases[j]);
141                 if (ret->h_aliases[j] == NULL) {
142                         FreeStructHostent(ret);
143                         ERROR("Out of memory\n");
144                         return NULL;
145                 }
146         }
147         ret->h_aliases[i] = NULL;
148
149         /* copy the easy stuff */
150         ret->h_addrtype = orig->h_addrtype;
151         ret->h_length = orig->h_length;
152
153         /* copy the addresses */
154         for (i=0; orig->h_addr_list != NULL && orig->h_addr_list[i] != NULL; i++) {
155                 ;
156         }
157         ret->h_addr_list = (char **)MALLOC(sizeof(struct in_addr *) * (i+1));
158         if (ret->h_addr_list == NULL) {
159                 FreeStructHostent(ret);
160                 ERROR("Out of memory\n");
161                 return NULL;
162         }
163         for (j=0; j < i; j++) {
164                 ret->h_addr_list[j] = (char *)MALLOC(ret->h_length + 1);
165                 if (ret->h_addr_list[j] == NULL) {
166                         FreeStructHostent(ret);
167                         ERROR("Out of memory\n");
168                         return NULL;
169                 }
170                 memcpy(ret->h_addr_list[j], orig->h_addr_list[j], ret->h_length);
171         }
172         ret->h_addr_list[i] = NULL;
173
174         /* done */
175         return ret;
176 }
177
178
179 /*
180  * Appends #hostEntry# (not a copy) to the global map cache. 
181  *
182  * cache is a global structure and need to be protected by locks to be
183  * thread safe.
184  */
185 void
186 CacheHostent(struct hostent *hostEntry) {
187         int ind;
188         struct hostent **extendedCache;
189         double *tmp_touts, now;
190
191
192         /* sanity check */
193         if (hostEntry == NULL) {
194                 return;
195         }
196         
197         /* look for an expired entry */
198         now = CurrentTime();
199         if (!GetNWSLock(&lock)) {
200                 ERROR("CacheHostent: couldn't obtain the lock\n");
201         }
202         for (ind=0; ind < cacheCount; ind++) {
203                 if (now > cacheTimeout[ind]) {
204                         FreeStructHostent(cache[ind]);
205                         break;
206                 }
207         }
208
209         if (ind >= cacheCount) {
210                 /* no found, we need to add memory */
211                 extendedCache = (struct hostent**)REALLOC(cache, sizeof(struct hostent *) * (cacheCount + 1));
212                 if(extendedCache == NULL) {
213                         ReleaseNWSLock(&lock);
214                         ERROR("Out of memory\n");
215                         return;
216                 }
217                 cache = extendedCache;
218                 tmp_touts = (double *)REALLOC(cacheTimeout, sizeof(double) * (cacheCount +1));
219                 if(tmp_touts == NULL) {
220                         ReleaseNWSLock(&lock);
221                         ERROR("Out of memory\n");
222                         return; 
223                 }
224                 cacheTimeout = tmp_touts;
225                 ind = cacheCount++;
226         }
227
228         cache[ind] = CopyStructHostent(hostEntry);
229         if (cache[ind] == NULL) {
230                 cacheTimeout[ind] = 1;
231         } else {
232                 cacheTimeout[ind] = now + CACHE_TIMEOUT;
233         }
234         ReleaseNWSLock(&lock);
235
236         return;
237 }
238
239
240 /*
241  * Searches the DNS mapping cache for #address#, adding a new entry 
242  * if needed.  Returns a copy of the the mapping entry, or 
243  * NULL on error. The memory returned needs to be freed.
244  */
245 struct hostent*
246 LookupByAddress(IPAddress address) {
247         struct in_addr addr;
248         struct hostent *tmp, *addrEntry;
249         struct in_addr **cachedAddr;
250         int i;
251         double now;
252
253         /* look if we have it in the cache and it's not expired */
254         now = CurrentTime();
255         if (!GetNWSLock(&lock)) {
256                 ERROR("LookupByAddress: couldn't obtain the lock\n");
257         }
258         for(i = 0; i < cacheCount; i++) {
259                 if (now > cacheTimeout[i]) {
260                         continue;
261                 }
262                 for(cachedAddr = (struct in_addr**)cache[i]->h_addr_list;
263                                 *cachedAddr != NULL;
264                                 cachedAddr++) {
265                         if((**cachedAddr).s_addr == address) {
266                                 tmp = CopyStructHostent(cache[i]);
267                                 ReleaseNWSLock(&lock);
268                                 return tmp;
269                         }
270                 }
271         }
272
273         addr.s_addr = address;
274         sethostent(0);
275         tmp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
276         addrEntry = CopyStructHostent(tmp);
277         endhostent();
278         
279         ReleaseNWSLock(&lock);
280
281         if(addrEntry != NULL && addrEntry->h_length != sizeof(struct in_addr)) {
282                 /* We don't (yet) handle non-in_addr addresses. */
283                 FreeStructHostent(addrEntry);
284                 addrEntry = NULL;
285         }
286
287         /* the NULL case is handled by the functions directly */
288         CacheHostent(addrEntry);
289
290         /* addrEntry will need to be freed after */
291         return addrEntry;
292 }
293
294
295 /*
296  * Searches the DNS mapping cache for #name#, adding a new entry if needed.
297  * Returns a pointer to the mapping entry, or NULL on error. The returned
298  * value need to be freed.
299  */
300 struct hostent*
301 LookupByName(const char *name) {
302
303         char **cachedName;
304         char **extendedAliases;
305         struct hostent *tmp, *nameEntry;
306         double now;
307         int i, listLen;
308
309         /* look in the cache for non expired entry */
310         now = CurrentTime();
311         if (!GetNWSLock(&lock)) {
312                 ERROR("LookupByName: failed to obtain the lock\n");
313         }
314         for(i = 0; i < cacheCount; i++) {
315                 if (now > cacheTimeout[i]) {
316                         continue;
317                 }
318                 if(strcasecmp(name, cache[i]->h_name) == 0) {
319                         tmp = CopyStructHostent(cache[i]);
320                         ReleaseNWSLock(&lock);
321                         return tmp;
322                 }
323                 for(cachedName = cache[i]->h_aliases; *cachedName != NULL; cachedName++) {
324                         if(strcasecmp(*cachedName, name) == 0) {
325                                 tmp = CopyStructHostent(cache[i]);
326                                 ReleaseNWSLock(&lock);
327                                 return tmp;
328                         }
329                 }
330         }
331
332         sethostent(0);
333         tmp = gethostbyname(name);
334         nameEntry = CopyStructHostent(tmp);
335         endhostent();
336
337         ReleaseNWSLock(&lock);
338
339         if(nameEntry == NULL) {
340                 return NULL;
341         } else if(nameEntry->h_length != sizeof(struct in_addr)) {
342                 FreeStructHostent(nameEntry);
343                 return NULL; /* We don't (yet) handle non-in_addr addresses. */
344         }
345
346         /* We extend cached entries' h_aliases lists to include nicknames. */
347         for(listLen = 0; nameEntry->h_aliases[listLen] != NULL; listLen++) {
348                 ;
349         }
350         extendedAliases = (char **)REALLOC(nameEntry->h_aliases, sizeof(char **) * (listLen + 2));
351         if(extendedAliases != NULL) {
352                 extendedAliases[listLen] = strdup(name);
353                 if (extendedAliases[listLen] != NULL) {
354                         extendedAliases[listLen + 1] = NULL;
355                         nameEntry->h_aliases = extendedAliases;
356                 }
357         }
358
359         /* let's invalidate the old (if there is) entry in the cache */
360         if (!GetNWSLock(&lock)) {
361                 ERROR("LookupByName: failed to obtain the lock\n");
362         }
363         for(i = 0; i < cacheCount; i++) {
364                 if (now > cacheTimeout[i]) {
365                         continue;
366                 }
367                 if(strcmp(nameEntry->h_name, cache[i]->h_name) == 0) {
368                         cacheTimeout[i] = 1;
369                         break;
370                 }
371         }
372         ReleaseNWSLock(&lock);
373
374         /* update the cache entry */
375         CacheHostent(nameEntry);
376
377         /* nameEntry will need to be freed */
378         return nameEntry;
379 }
380
381 /* thread safe */
382 IPAddress
383 Peer(Socket sd) {
384         struct sockaddr peer;
385         SOCKLEN_T peer_size = sizeof(peer);
386         int returnValue = 0;
387
388         if (!IsPipe(sd) && (getpeername(sd, &peer, &peer_size) == 0)) {
389                 if (peer.sa_family == AF_INET) {
390                         returnValue = (((struct sockaddr_in *)&peer)->sin_addr.s_addr);
391                 }
392         }
393
394         return returnValue;
395 }
396
397 /* thread safe */
398 char *
399 PeerName_r(Socket sd) {
400         struct sockaddr peer;
401         SOCKLEN_T peer_size = sizeof(peer);
402         char *returnValue;
403
404         if (IsPipe(sd)) {
405                 returnValue = strdup("pipe");
406         } else if (getpeername(sd, &peer, &peer_size) < 0) {
407                 returnValue = strdup("unknown");
408         } else {
409                 if (peer.sa_family != AF_INET) {
410                         returnValue = strdup("unknown");
411                 } else {
412                         returnValue = IPAddressImage_r(((struct sockaddr_in *)&peer)->sin_addr.s_addr);
413                 }
414         }
415
416         if (returnValue == NULL) {
417                 ERROR("PeerName_r: out of memory\n");
418         }
419         return returnValue;
420 }
421
422
423 /* thread safe */
424 unsigned short
425 PeerNamePort(Socket sd) {
426         unsigned short tmp;
427         struct sockaddr peer;
428         SOCKLEN_T peer_size = sizeof(peer);
429
430         /* connectedPipes is global */
431         if (!GetNWSLock(&lock)) {
432                 ERROR("PeerNamePort: failed to obtain the lock\n");
433         }
434         tmp = (IsPipe(sd) ? -1 : (getpeername(sd, &peer, &peer_size) < 0) ?  -1
435                         : ((struct sockaddr_in *)&peer)->sin_port);
436         ReleaseNWSLock(&lock);
437         return tmp;
438 }
439
440
441 /* thread safe: we allocate memory for the returned char * */
442 char *
443 IPAddressImage_r(IPAddress addr) {
444         struct in_addr addrAsInAddr;
445         char *returned, *tmp;
446
447         addrAsInAddr.s_addr = addr;
448
449         if (!GetNWSLock(&lock)) {
450                 ERROR("IPAddressImage_r: failed to obtain the lock\n");
451         }
452         tmp = inet_ntoa(addrAsInAddr);
453         if (tmp != NULL) {
454                 returned = strdup(inet_ntoa(addrAsInAddr));
455         } else {
456                 returned = strdup("unknown");
457         }
458         ReleaseNWSLock(&lock);
459
460         if (returned == NULL) {
461                 ERROR("IPAddressImage_r: out of memory\n");
462         }
463
464         return returned;
465 }
466
467
468 /* thread safe: we allocate memory for the returned char. NULL have to be
469  * checked by the caller. */
470 char *
471 IPAddressMachine_r(IPAddress addr) {
472         struct hostent *hostEntry;
473         char *returnValue;
474
475         hostEntry = LookupByAddress(addr);
476         if (hostEntry == NULL) {
477                 return NULL;
478         }
479
480         returnValue = strdup(BestHostName(hostEntry));
481
482         /* free allocated memory */
483         FreeStructHostent(hostEntry);
484
485         return returnValue;
486 }
487
488
489 /* thread safe. */
490 int
491 IPAddressValues(const char *machineOrAddress,
492                 IPAddress *addressList,
493                 unsigned int atMost) {
494         struct hostent *hostEntry;
495         int i = 0, itsAnAddress = 0;
496         IPAddress temp = 0;
497 #ifdef HAVE_INET_ATON
498         struct in_addr in;
499
500         if (inet_aton(machineOrAddress, &in) != 0) {
501                 itsAnAddress = 1;
502                 temp = in.s_addr;
503         }
504 #else
505         /* inet_addr() has the weird behavior of returning an unsigned
506          * quantity but using -1 as an error value.  Furthermore, the
507          * value returned is sometimes int and sometimes long,
508          * complicating the test.  Once inet_aton() is more widely
509          * available, we should switch to using it instead.  */
510         temp = inet_addr(machineOrAddress);
511         if (temp != -1) {
512                 itsAnAddress = 1;
513         }
514 #endif
515         if(itsAnAddress && (atMost == 1)) {
516                 *addressList = temp;
517                 return 1;
518         }
519
520         hostEntry = itsAnAddress ?  LookupByAddress(temp) : LookupByName(machineOrAddress);
521
522         /* sanity check */
523         if(hostEntry == NULL) {
524                 return 0;
525         } 
526
527         /* if atMost == 0 means we are checking if the address is
528          * correct. It is */
529         if(atMost == 0) {
530                 i = 1;
531         } 
532
533         for(; i < atMost && hostEntry->h_addr_list[i] != NULL; i++) {
534                 memcpy(&addressList[i], hostEntry->h_addr_list[i], hostEntry->h_length);
535         }
536
537         FreeStructHostent(hostEntry);
538         return i;
539 }
540
541
542 /* well, the name is always the same so we can use a static variable.
543  * Changed to never return NULL but at the very worse localhost. */
544 const char *
545 MyMachineName(void) {
546
547         struct hostent *myEntry;
548         static char returnValue[255] = "";
549
550         /* If we have a value in returnValue, done */
551         if(returnValue[0] != '\0') {
552                 return returnValue;
553         }
554
555         /* try the simple case first */
556         if(gethostname(returnValue, sizeof(returnValue)) == -1) {
557                 ERROR("gethostname() failed! using localhost instead.\n");
558                 /* setting the name to a safe bet */
559                 if (!GetNWSLock(&lock)) {
560                         ERROR("MyMachineName: failed to obtain the lock\n");
561                 }
562                 strncpy(returnValue, "localhost", sizeof(returnValue));
563                 ReleaseNWSLock(&lock);
564         } else if(!strchr(returnValue, '.')) {
565                 /* Okay, that didn't work; try the DNS. */
566                 myEntry = LookupByName(returnValue);
567                 if(myEntry != NULL) {
568                         if (!GetNWSLock(&lock)) {
569                                 ERROR("MyMachineName: failed to obtain the lock\n");
570                         }
571                         strncpy(returnValue,BestHostName(myEntry),sizeof(returnValue));
572                         returnValue[sizeof(returnValue) - 1] = '\0';
573                         ReleaseNWSLock(&lock);
574                         FreeStructHostent(myEntry);
575                 }
576         }
577
578         return returnValue;
579 }
580
581
582 /* DEPRECATED: these are not thread-safe */
583 const char *
584 IPAddressImage(IPAddress addr) {
585         struct in_addr addrAsInAddr;
586         addrAsInAddr.s_addr = addr;
587         return inet_ntoa(addrAsInAddr);
588 }
589 const char *
590 IPAddressMachine(IPAddress addr) {
591         struct hostent *hostEntry;
592         static char returnValue[63 + 1];
593         hostEntry = LookupByAddress(addr);
594         strncpy(returnValue,
595                 (hostEntry == NULL) ? "" : BestHostName(hostEntry),
596                 sizeof(returnValue));
597         FreeStructHostent(hostEntry);
598         return returnValue;
599 }
600
601 const char *
602 PeerName(Socket sd) {
603    struct sockaddr peer;
604    SOCKLEN_T peer_size = sizeof(peer);
605    static char returnValue[200];
606    strcpy(returnValue, IsPipe(sd) ? "pipe" :
607                 (getpeername(sd, &peer, &peer_size) < 0) ?
608                 "(unknown)" :
609                 inet_ntoa(((struct sockaddr_in *)&peer)->sin_addr));
610    return returnValue;
611 }
612
613