Annotation of libwww/Library/src/HTDNS.c, revision 2.1
2.1 ! frystyk 1: /* HTDNS.c
! 2: ** DOMAIN NAME SERVICE MANAGER
! 3: **
! 4: ** (c) COPYRIGHT MIT 1995.
! 5: ** Please first read the full copyright statement in the file COPYRIGH.
! 6: **
! 7: ** This object manages a cache of hosts we have looked up vis DNS.
! 8: ** The object contains the necessary parts from hostent. For Internet host
! 9: ** hostent->h_addr_list is not an array of char pointers but an array of
! 10: ** pointers of type in_addr.
! 11: **
! 12: ** 13 Sep 95 HFN Spawned from HTTCP.c and rewritten
! 13: */
! 14:
! 15: /* Library include files */
! 16: #include "tcp.h"
! 17: #include "HTUtils.h"
! 18: #include "HTString.h"
! 19: #include "HTAtom.h"
! 20: #include "HTList.h"
! 21: #include "HTParse.h"
! 22: #include "HTAlert.h"
! 23: #include "HTError.h"
! 24: #include "HTNetMan.h"
! 25: #include "HTDNS.h" /* Implemented here */
! 26:
! 27: #define TCP_TIMEOUT 3600L /* Default TCP timeout i 1 h */
! 28: #define DNS_TIMEOUT 172800L /* Default DNS timeout is 48 h */
! 29: #define HASH_SIZE 67
! 30:
! 31: /* Type definitions and global variables etc. local to this module */
! 32: struct _HTdns {
! 33: char * hostname; /* name of host + maybe port */
! 34: time_t ntime; /* Creation time */
! 35:
! 36: char * server; /* Server class */
! 37: int version; /* Server version */
! 38: HTTCPType type; /* Connection type */
! 39:
! 40: BOOL active; /* Socket in use */
! 41: time_t expires; /* Socket expires time */
! 42: SOCKFD sockfd; /* Persistent connection */
! 43:
! 44: int addrlength; /* Length of address in bytes */
! 45: int homes; /* Number of IP addresses on the host */
! 46: char ** addrlist; /* List of addresses from name server */
! 47: double * weight; /* Weight on each address */
! 48: };
! 49:
! 50: PRIVATE HTList **CacheTable = NULL;
! 51: PRIVATE time_t DNSTimeout = DNS_TIMEOUT; /* Timeout on DNS entries */
! 52: PRIVATE time_t TCPTimeout = TCP_TIMEOUT; /* Timeout on TCP sockets */
! 53:
! 54: /* ------------------------------------------------------------------------- */
! 55:
! 56: PRIVATE void free_object (HTdns * me)
! 57: {
! 58: if (me) {
! 59: FREE(me->hostname);
! 60: FREE(me->server);
! 61: if (me->sockfd != INVSOC && !me->active)
! 62: NETCLOSE(me->sockfd);
! 63: if (*me->addrlist)
! 64: free(*me->addrlist);
! 65: FREE(me->addrlist);
! 66: FREE(me->weight);
! 67: free(me);
! 68: }
! 69: }
! 70:
! 71: PRIVATE BOOL delete_object (HTList * list, HTdns *me)
! 72: {
! 73: if (PROT_TRACE)
! 74: fprintf(TDEST,"DNS Delete.. object %p from list %p\n", me, list);
! 75: HTList_removeObject(list, (void *) me);
! 76: free_object(me);
! 77: return YES;
! 78: }
! 79:
! 80: /* HTDNS_setTimeout
! 81: ** ----------------
! 82: ** Set the cache timeout for DNS entries. Default is DNS_TIMEOUT
! 83: */
! 84: PUBLIC void HTDNS_setTimeout (time_t timeout)
! 85: {
! 86: DNSTimeout = timeout;
! 87: }
! 88:
! 89: /* HTDNS_timeout
! 90: ** -------------
! 91: ** Get the cache timeout
! 92: */
! 93: PUBLIC time_t HTDNS_timeout (time_t timeout)
! 94: {
! 95: return DNSTimeout;
! 96: }
! 97:
! 98: /* HTDNS_setSockTimeout
! 99: ** --------------------
! 100: ** Set the cache timeout for DNS entries. Default is DNS_TIMEOUT
! 101: */
! 102: PUBLIC void HTDNS_setSockTimeout (time_t timeout)
! 103: {
! 104: TCPTimeout = timeout;
! 105: }
! 106:
! 107: /* HTDNS_sockTimeout
! 108: ** -----------------
! 109: ** Get the cache timeout
! 110: */
! 111: PUBLIC time_t HTDNS_sockTimeout (time_t timeout)
! 112: {
! 113: return TCPTimeout;
! 114: }
! 115:
! 116: /* HTDNS_serverClass
! 117: ** -----------------
! 118: */
! 119: PUBLIC char *HTDNS_serverClass (HTdns *dns)
! 120: {
! 121: return dns ? dns->server : NULL;
! 122: }
! 123:
! 124: PUBLIC void HTDNS_setServerClass (HTdns * dns, char *class)
! 125: {
! 126: if (dns && class) StrAllocCopy(dns->server, class);
! 127: }
! 128:
! 129: /* HTDNS_serverVersion
! 130: ** -------------------
! 131: */
! 132: PUBLIC int HTDNS_serverVersion (HTdns *dns)
! 133: {
! 134: return dns ? dns->version : 0;
! 135: }
! 136:
! 137: PUBLIC void HTDNS_setServerVersion (HTdns * dns, int version, HTTCPType type)
! 138: {
! 139: if (dns) {
! 140: dns->version = version;
! 141: dns->type = type;
! 142: }
! 143: }
! 144:
! 145: /* Persistent Connections
! 146: ** ----------------------
! 147: */
! 148: PUBLIC SOCKFD HTDNS_socket(HTdns *dns)
! 149: {
! 150: return dns ? dns->sockfd : INVSOC;
! 151: }
! 152:
! 153: PUBLIC void HTDNS_setSocket(HTdns *dns, SOCKFD socket)
! 154: {
! 155: if (dns) {
! 156: dns->sockfd = socket;
! 157: dns->active = YES;
! 158: dns->expires = time(NULL) + TCPTimeout; /* Default timeout */
! 159: }
! 160: }
! 161:
! 162: PUBLIC void HTDNS_clearActive (HTdns *dns)
! 163: {
! 164: if (dns) dns->active = NO;
! 165: }
! 166:
! 167: /* Persistent Connection Expiration
! 168: ** --------------------------------
! 169: ** Absolute value
! 170: */
! 171: PUBLIC void HTDNS_setSockExpires (HTdns * dns, time_t expires)
! 172: {
! 173: if (dns) dns->expires = expires;
! 174: }
! 175:
! 176: PUBLIC time_t HTDNS_sockExpires (HTdns * dns)
! 177: {
! 178: return dns ? dns->expires : -1;
! 179: }
! 180:
! 181: /* HTDNS_add
! 182: ** ---------
! 183: ** Add an element to the cache of visited hosts. Note that this function
! 184: ** requires the system implemented structure hostent and not our own
! 185: ** host_info. The homes variable indicates the number of
! 186: ** IP addresses found. host is a host name possibly with a port number
! 187: ** returns address of new HTdns object
! 188: */
! 189: PRIVATE HTdns * HTDNS_add (HTList * list, struct hostent * element,
! 190: char *host, int *homes)
! 191: {
! 192: HTdns *me;
! 193: char *addr;
! 194: char **index = element->h_addr_list;
! 195: int cnt = 1;
! 196:
! 197: while(*index++) cnt++;
! 198: if ((me = (HTdns *) calloc(1, sizeof(HTdns))) == NULL ||
! 199: (me->addrlist = (char **) calloc(1, cnt*sizeof(char*))) == NULL ||
! 200: (addr = (char *) calloc(1, cnt*element->h_length)) == NULL)
! 201: outofmem(__FILE__, "HTDNS_add");
! 202: StrAllocCopy(me->hostname, host);
! 203: me->ntime = time(NULL);
! 204: me->sockfd = INVSOC;
! 205: index = element->h_addr_list;
! 206: cnt = 0;
! 207: while (*index) {
! 208: *(me->addrlist+cnt) = addr+cnt*element->h_length;
! 209: memcpy((void *) *(me->addrlist+cnt++), *index++, element->h_length);
! 210: }
! 211: me->homes = cnt;
! 212: *homes = cnt;
! 213: if ((me->weight = (double *) calloc(me->homes, sizeof(double))) == NULL)
! 214: outofmem(__FILE__, "HTDNS_add");
! 215: me->addrlength = element->h_length;
! 216: if (PROT_TRACE)
! 217: fprintf(TDEST, "DNS Add..... `%s\' with %d home(s) to %p\n",
! 218: host, *homes, list);
! 219: HTList_addObject(list, (void *) me);
! 220: return me;
! 221: }
! 222:
! 223:
! 224: /* HTDNS_updateWeights
! 225: ** -------------------
! 226: ** This function calculates the weights of the different IP addresses
! 227: ** on a multi homed host. Each weight is calculated as
! 228: **
! 229: ** w(n+1) = w(n)*a + (1-a) * deltatime
! 230: ** a = exp(-1/Neff)
! 231: ** Neff is the effective number of samples used
! 232: ** deltatime is time spend on making a connection
! 233: **
! 234: ** A short window (low Neff) gives a high sensibility, but this is
! 235: ** required as we can't expect a lot of data to test on.
! 236: ** host is a host name possibly with port number. current is the index
! 237: ** returned by HTGetHostByName()
! 238: */
! 239: PUBLIC BOOL HTDNS_updateWeigths(HTdns *dns, int current, time_t deltatime)
! 240: {
! 241: if (dns) {
! 242: int cnt;
! 243: CONST double passive = 0.9; /* Factor for all passive IP_addrs */
! 244: #if 0
! 245: CONST int Neff = 3;
! 246: CONST double alpha = exp(-1.0/Neff);
! 247: #else
! 248: CONST double alpha = 0.716531310574; /* Doesn't need the math lib */
! 249: #endif
! 250: for (cnt=0; cnt<dns->homes; cnt++) {
! 251: if (cnt == current) {
! 252: *(dns->weight+current) = *(dns->weight+current)*alpha + (1.0-alpha)*deltatime;
! 253: } else {
! 254: *(dns->weight+cnt) = *(dns->weight+cnt) * passive;
! 255: }
! 256: if (PROT_TRACE)
! 257: fprintf(TDEST, "DNS Weigths. Home %d has weight %4.2f\n", cnt,
! 258: *(dns->weight+cnt));
! 259: }
! 260: return YES;
! 261: }
! 262: if (PROT_TRACE)
! 263: fprintf(TDEST, "DNS Weigths. Object %p not found'\n", dns);
! 264: return NO;
! 265: }
! 266:
! 267: /* HTDNS_delete
! 268: ** ------------
! 269: ** Remove an element from the cache
! 270: ** Host must be a host name possibly with a port number
! 271: */
! 272: PUBLIC BOOL HTDNS_delete (CONST char * host)
! 273: {
! 274: HTList *list;
! 275: int hash = 0;
! 276: CONST char *ptr;
! 277: if (!host || !CacheTable) return NO;
! 278: for(ptr=host; *ptr; ptr++)
! 279: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
! 280: if ((list = CacheTable[hash])) { /* We have the list, find the entry */
! 281: HTdns *pres;
! 282: while ((pres = (HTdns *) HTList_nextObject(list))) {
! 283: if (!strcmp(pres->hostname, host)) {
! 284: delete_object(CacheTable[hash], pres);
! 285: break;
! 286: }
! 287: }
! 288: }
! 289: return YES;
! 290: }
! 291:
! 292: /* HTDNS_deleteAll
! 293: ** ---------------
! 294: ** Destroys the cache completely
! 295: */
! 296: PUBLIC BOOL HTDNS_deleteAll (void)
! 297: {
! 298: int cnt;
! 299: HTList *cur;
! 300: if (!CacheTable) return NO;
! 301: for (cnt=0; cnt<HASH_SIZE; cnt++) {
! 302: if ((cur = CacheTable[cnt])) {
! 303: HTdns *pres;
! 304: while ((pres = (HTdns *) HTList_nextObject(cur)) != NULL)
! 305: free_object(pres);
! 306: }
! 307: HTList_delete(CacheTable[cnt]);
! 308: CacheTable[cnt] = NULL;
! 309:
! 310: }
! 311: return YES;
! 312: }
! 313:
! 314: /* HTGetHostByName
! 315: ** ---------------
! 316: ** Resolve the host name using internal DNS cache. As we want to refer
! 317: ** a specific host when timing the connection the weight function must
! 318: ** use the 'current' value as returned.
! 319: ** Host must be a host name possibly with a port number
! 320: ** Returns:
! 321: ** >0 Number of homes
! 322: ** 0 Wait for persistent socket
! 323: ** -1 Error
! 324: */
! 325: PUBLIC int HTGetHostByName (HTNet *net, char *host)
! 326: {
! 327: SockA *sin = &net->sock_addr;
! 328: int homes = -1;
! 329: HTList *list; /* Current list in cache */
! 330: HTdns *pres = NULL;
! 331: if (!net || !host) {
! 332: if (PROT_TRACE)
! 333: fprintf(TDEST, "HostByName.. Bad argument\n");
! 334: return -1;
! 335: }
! 336: net->home = 0;
! 337:
! 338: /* Find a hash for this host */
! 339: {
! 340: int hash = 0;
! 341: char *ptr;
! 342: for(ptr=host; *ptr; ptr++)
! 343: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
! 344: if (!CacheTable) {
! 345: CacheTable = (HTList**) calloc(HASH_SIZE, sizeof(HTList *));
! 346: if (!CacheTable) outofmem(__FILE__, "HTDNS_init");
! 347: }
! 348: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
! 349: list = CacheTable[hash];
! 350: }
! 351:
! 352: /* Search the cache */
! 353: {
! 354: HTList *cur = list;
! 355: while ((pres = (HTdns *) HTList_nextObject(cur))) {
! 356: if (!strcmp(pres->hostname, host)) {
! 357: if (time(NULL) > pres->ntime + DNSTimeout) {
! 358: if (PROT_TRACE)
! 359: fprintf(TDEST, "HostByName.. Refreshing cache\n");
! 360: delete_object(list, pres);
! 361: pres = NULL;
! 362: }
! 363: break;
! 364: }
! 365: }
! 366: }
! 367: if (pres) {
! 368: if (PROT_TRACE)
! 369: fprintf(TDEST, "HostByName.. '%s\' found in cache\n", host);
! 370:
! 371: /* See if we have an open connection already */
! 372: if (pres->sockfd != INVSOC) {
! 373: if (pres->active) { /* Warm connection in use */
! 374: net->sockfd = pres->sockfd;
! 375: if (!(pres->type & HT_TCP_INTERLEAVE)) {
! 376: if (PROT_TRACE)
! 377: fprintf(TDEST, "HostByName.. waiting for socket\n");
! 378: return 0; /* Wait for clear connection */
! 379: }
! 380: } else if (pres->expires < time(NULL)) { /* Gotton cold */
! 381: if (PROT_TRACE)
! 382: fprintf(TDEST, "HostByName.. Closing %d\n", pres->sockfd);
! 383: NETCLOSE(pres->sockfd);
! 384: pres->sockfd = INVSOC;
! 385: pres->expires = 0;
! 386: } else { /* Warm connection is ready :-) */
! 387: net->sockfd = pres->sockfd;
! 388: pres->active = YES;
! 389: }
! 390: }
! 391:
! 392: /*
! 393: ** Find the best home. We still want to do this as we use it as a
! 394: ** fall back for persistent connections
! 395: */
! 396: homes = pres->homes;
! 397: if (pres->homes > 1) {
! 398: int cnt = 0;
! 399: double best_weight = 1e30; /* Pretty good */
! 400: while (cnt < pres->homes) {
! 401: if (*(pres->weight+cnt) < best_weight) {
! 402: best_weight = *(pres->weight+cnt);
! 403: net->home = cnt;
! 404: }
! 405: cnt++;
! 406: }
! 407: }
! 408: net->dns = pres;
! 409: memcpy(&sin->sin_addr, *(pres->addrlist+net->home), pres->addrlength);
! 410: } else {
! 411: struct hostent *hostelement; /* see netdb.h */
! 412: char *port = strchr(host, ':');
! 413: #ifdef HT_REENTRANT
! 414: int thd_errno;
! 415: char buffer[HOSTENT_MAX];
! 416: struct hostent result; /* For gethostbyname_r */
! 417: if (port) *port='\0';
! 418: HTProgress(net->request, HT_PROG_DNS, host);
! 419: hostelement = gethostbyname_r(host, &result, buffer,
! 420: HOSTENT_MAX, &thd_errno);
! 421: #else
! 422: if (port) *port='\0';
! 423: HTProgress(net->request, HT_PROG_DNS, host);
! 424: hostelement = gethostbyname(host);
! 425: #endif
! 426: if (!hostelement) {
! 427: if (PROT_TRACE)
! 428: fprintf(TDEST, "HostByName.. Can't find node `%s'.\n", host);
! 429: return -1;
! 430: }
! 431: if (port) *port=':'; /* Put ':' back in */
! 432: net->dns = HTDNS_add(list, hostelement, host, &homes);
! 433: memcpy(&sin->sin_addr,*hostelement->h_addr_list,hostelement->h_length);
! 434: }
! 435: return homes;
! 436: }
! 437:
! 438:
! 439: /*
! 440: ** Get host name of the machine on the other end of a socket.
! 441: **
! 442: */
! 443: PUBLIC char * HTGetHostBySock ARGS1(int, soc)
! 444: {
! 445: struct sockaddr addr;
! 446: int len = sizeof(struct sockaddr);
! 447: struct in_addr *iaddr;
! 448: char *name = NULL;
! 449: struct hostent * phost; /* Pointer to host -- See netdb.h */
! 450: #ifdef HT_REENTRANT
! 451: int thd_errno;
! 452: char buffer[HOSTENT_MAX];
! 453: struct hostent result; /* For gethostbyaddr_r */
! 454: #endif
! 455:
! 456: #ifdef DECNET /* Decnet ain't got no damn name server 8#OO */
! 457: return NULL;
! 458: #else
! 459: if (getpeername(soc, &addr, &len) < 0)
! 460: return NULL;
! 461: iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
! 462:
! 463: #ifdef HT_REENTRANT
! 464: phost = gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
! 465: &result, buffer, HOSTENT_MAX, &thd_errno);
! 466: #else
! 467: phost = gethostbyaddr((char *) iaddr, sizeof(struct in_addr), AF_INET);
! 468: #endif
! 469: if (!phost) {
! 470: if (PROT_TRACE)
! 471: fprintf(TDEST, "TCP......... Can't find internet node name for peer!!\n");
! 472: return NULL;
! 473: }
! 474: StrAllocCopy(name, phost->h_name);
! 475: if (PROT_TRACE) fprintf(TDEST, "TCP......... Peer name is `%s'\n", name);
! 476: return name;
! 477:
! 478: #endif /* not DECNET */
! 479: }
! 480:
! 481:
! 482:
Webmaster