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