3 #include "config_portability.h"
6 #include <stdlib.h> /* REALLOC() */
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}() */
14 #include "diagnostic.h"
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.
28 static void *lock = NULL; /* local lock */
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).
35 * Cached values timeout after CACHE_TIMEOUT seconds.
37 #define CACHE_TIMEOUT 1800
38 static unsigned int cacheCount = 0;
39 static struct hostent **cache = NULL;
40 static double *cacheTimeout = NULL;
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.
48 BestHostName(const struct hostent *hostEntry) {
51 if (hostEntry == NULL) {
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! */
62 /* If we don't have a fully-qualified name, do the best we can. */
63 return hostEntry->h_name;
67 * free a struct hostent *.
70 FreeStructHostent(struct hostent *toFree) {
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]);
83 free(toFree->h_aliases);
85 if (toFree->h_name != NULL) {
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]);
92 free(toFree->h_addr_list);
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
105 CopyStructHostent(const struct hostent *orig) {
114 ret = (struct hostent *)MALLOC(sizeof(struct hostent));
116 ERROR("Out of memory\n");
117 return NULL; /* out of memory */
119 memset((void *)ret, 0, sizeof(struct hostent));
121 /* make room for the name */
122 ret->h_name = strdup(orig->h_name);
123 if (ret->h_name == NULL) {
125 ERROR("Out of memory\n");
126 return NULL; /* out of memory */
129 /* count aliases and copy them */
130 for (i=0; orig->h_aliases != NULL && orig->h_aliases[i] != NULL; i++) {
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");
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");
147 ret->h_aliases[i] = NULL;
149 /* copy the easy stuff */
150 ret->h_addrtype = orig->h_addrtype;
151 ret->h_length = orig->h_length;
153 /* copy the addresses */
154 for (i=0; orig->h_addr_list != NULL && orig->h_addr_list[i] != NULL; i++) {
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");
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");
170 memcpy(ret->h_addr_list[j], orig->h_addr_list[j], ret->h_length);
172 ret->h_addr_list[i] = NULL;
180 * Appends #hostEntry# (not a copy) to the global map cache.
182 * cache is a global structure and need to be protected by locks to be
186 CacheHostent(struct hostent *hostEntry) {
188 struct hostent **extendedCache;
189 double *tmp_touts, now;
193 if (hostEntry == NULL) {
197 /* look for an expired entry */
199 if (!GetNWSLock(&lock)) {
200 ERROR("CacheHostent: couldn't obtain the lock\n");
202 for (ind=0; ind < cacheCount; ind++) {
203 if (now > cacheTimeout[ind]) {
204 FreeStructHostent(cache[ind]);
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");
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");
224 cacheTimeout = tmp_touts;
228 cache[ind] = CopyStructHostent(hostEntry);
229 if (cache[ind] == NULL) {
230 cacheTimeout[ind] = 1;
232 cacheTimeout[ind] = now + CACHE_TIMEOUT;
234 ReleaseNWSLock(&lock);
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.
246 LookupByAddress(IPAddress address) {
248 struct hostent *tmp, *addrEntry;
249 struct in_addr **cachedAddr;
253 /* look if we have it in the cache and it's not expired */
255 if (!GetNWSLock(&lock)) {
256 ERROR("LookupByAddress: couldn't obtain the lock\n");
258 for(i = 0; i < cacheCount; i++) {
259 if (now > cacheTimeout[i]) {
262 for(cachedAddr = (struct in_addr**)cache[i]->h_addr_list;
265 if((**cachedAddr).s_addr == address) {
266 tmp = CopyStructHostent(cache[i]);
267 ReleaseNWSLock(&lock);
273 addr.s_addr = address;
275 tmp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
276 addrEntry = CopyStructHostent(tmp);
279 ReleaseNWSLock(&lock);
281 if(addrEntry != NULL && addrEntry->h_length != sizeof(struct in_addr)) {
282 /* We don't (yet) handle non-in_addr addresses. */
283 FreeStructHostent(addrEntry);
287 /* the NULL case is handled by the functions directly */
288 CacheHostent(addrEntry);
290 /* addrEntry will need to be freed after */
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.
301 LookupByName(const char *name) {
304 char **extendedAliases;
305 struct hostent *tmp, *nameEntry;
309 /* look in the cache for non expired entry */
311 if (!GetNWSLock(&lock)) {
312 ERROR("LookupByName: failed to obtain the lock\n");
314 for(i = 0; i < cacheCount; i++) {
315 if (now > cacheTimeout[i]) {
318 if(strcasecmp(name, cache[i]->h_name) == 0) {
319 tmp = CopyStructHostent(cache[i]);
320 ReleaseNWSLock(&lock);
323 for(cachedName = cache[i]->h_aliases; *cachedName != NULL; cachedName++) {
324 if(strcasecmp(*cachedName, name) == 0) {
325 tmp = CopyStructHostent(cache[i]);
326 ReleaseNWSLock(&lock);
333 tmp = gethostbyname(name);
334 nameEntry = CopyStructHostent(tmp);
337 ReleaseNWSLock(&lock);
339 if(nameEntry == 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. */
346 /* We extend cached entries' h_aliases lists to include nicknames. */
347 for(listLen = 0; nameEntry->h_aliases[listLen] != NULL; listLen++) {
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;
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");
363 for(i = 0; i < cacheCount; i++) {
364 if (now > cacheTimeout[i]) {
367 if(strcmp(nameEntry->h_name, cache[i]->h_name) == 0) {
372 ReleaseNWSLock(&lock);
374 /* update the cache entry */
375 CacheHostent(nameEntry);
377 /* nameEntry will need to be freed */
384 struct sockaddr peer;
385 SOCKLEN_T peer_size = sizeof(peer);
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);
399 PeerName_r(Socket sd) {
400 struct sockaddr peer;
401 SOCKLEN_T peer_size = sizeof(peer);
405 returnValue = strdup("pipe");
406 } else if (getpeername(sd, &peer, &peer_size) < 0) {
407 returnValue = strdup("unknown");
409 if (peer.sa_family != AF_INET) {
410 returnValue = strdup("unknown");
412 returnValue = IPAddressImage_r(((struct sockaddr_in *)&peer)->sin_addr.s_addr);
416 if (returnValue == NULL) {
417 ERROR("PeerName_r: out of memory\n");
425 PeerNamePort(Socket sd) {
427 struct sockaddr peer;
428 SOCKLEN_T peer_size = sizeof(peer);
430 /* connectedPipes is global */
431 if (!GetNWSLock(&lock)) {
432 ERROR("PeerNamePort: failed to obtain the lock\n");
434 tmp = (IsPipe(sd) ? -1 : (getpeername(sd, &peer, &peer_size) < 0) ? -1
435 : ((struct sockaddr_in *)&peer)->sin_port);
436 ReleaseNWSLock(&lock);
441 /* thread safe: we allocate memory for the returned char * */
443 IPAddressImage_r(IPAddress addr) {
444 struct in_addr addrAsInAddr;
445 char *returned, *tmp;
447 addrAsInAddr.s_addr = addr;
449 if (!GetNWSLock(&lock)) {
450 ERROR("IPAddressImage_r: failed to obtain the lock\n");
452 tmp = inet_ntoa(addrAsInAddr);
454 returned = strdup(inet_ntoa(addrAsInAddr));
456 returned = strdup("unknown");
458 ReleaseNWSLock(&lock);
460 if (returned == NULL) {
461 ERROR("IPAddressImage_r: out of memory\n");
468 /* thread safe: we allocate memory for the returned char. NULL have to be
469 * checked by the caller. */
471 IPAddressMachine_r(IPAddress addr) {
472 struct hostent *hostEntry;
475 hostEntry = LookupByAddress(addr);
476 if (hostEntry == NULL) {
480 returnValue = strdup(BestHostName(hostEntry));
482 /* free allocated memory */
483 FreeStructHostent(hostEntry);
491 IPAddressValues(const char *machineOrAddress,
492 IPAddress *addressList,
493 unsigned int atMost) {
494 struct hostent *hostEntry;
495 int i = 0, itsAnAddress = 0;
497 #ifdef HAVE_INET_ATON
500 if (inet_aton(machineOrAddress, &in) != 0) {
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);
515 if(itsAnAddress && (atMost == 1)) {
520 hostEntry = itsAnAddress ? LookupByAddress(temp) : LookupByName(machineOrAddress);
523 if(hostEntry == NULL) {
527 /* if atMost == 0 means we are checking if the address is
533 for(; i < atMost && hostEntry->h_addr_list[i] != NULL; i++) {
534 memcpy(&addressList[i], hostEntry->h_addr_list[i], hostEntry->h_length);
537 FreeStructHostent(hostEntry);
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. */
545 MyMachineName(void) {
547 struct hostent *myEntry;
548 static char returnValue[255] = "";
550 /* If we have a value in returnValue, done */
551 if(returnValue[0] != '\0') {
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");
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");
571 strncpy(returnValue,BestHostName(myEntry),sizeof(returnValue));
572 returnValue[sizeof(returnValue) - 1] = '\0';
573 ReleaseNWSLock(&lock);
574 FreeStructHostent(myEntry);
582 /* DEPRECATED: these are not thread-safe */
584 IPAddressImage(IPAddress addr) {
585 struct in_addr addrAsInAddr;
586 addrAsInAddr.s_addr = addr;
587 return inet_ntoa(addrAsInAddr);
590 IPAddressMachine(IPAddress addr) {
591 struct hostent *hostEntry;
592 static char returnValue[63 + 1];
593 hostEntry = LookupByAddress(addr);
595 (hostEntry == NULL) ? "" : BestHostName(hostEntry),
596 sizeof(returnValue));
597 FreeStructHostent(hostEntry);
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) ?
609 inet_ntoa(((struct sockaddr_in *)&peer)->sin_addr));