Annotation of libwww/Library/src/HTDNS.c, revision 2.32.2.1

2.1       frystyk     1: /*                                                                     HTDNS.c
                      2: **     DOMAIN NAME SERVICE MANAGER
                      3: **
2.32.2.1! duerst      4: **     (c) COPYRIGHT MIT 1995-2003.
2.1       frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
2.32.2.1! duerst      6: **     @(#) $Id: HTDNS.c,v 2.32 2002/03/21 13:42:01 kahan Exp $
2.1       frystyk     7: **
                      8: **     This object manages a cache of hosts we have looked up vis DNS.
                      9: **     The object contains the necessary parts from hostent. For Internet host
                     10: **     hostent->h_addr_list is not an array of char pointers but an array of 
                     11: **     pointers of type in_addr.
                     12: **
                     13: **     13 Sep 95  HFN  Spawned from HTTCP.c and rewritten
2.32.2.1! duerst     14: **      23 Feb 03  MJD  Started working on IDN implementation
2.1       frystyk    15: */
                     16: 
                     17: /* Library include files */
2.26      frystyk    18: #include "wwwsys.h"
2.21      frystyk    19: #include "WWWUtil.h"
2.1       frystyk    20: #include "HTParse.h"
                     21: #include "HTAlert.h"
                     22: #include "HTError.h"
2.21      frystyk    23: #include "HTTrans.h"
2.23      frystyk    24: #include "HTHstMan.h"
2.1       frystyk    25: #include "HTDNS.h"                                      /* Implemented here */
                     26: 
2.25      frystyk    27: #define DNS_TIMEOUT            1800L        /* Default DNS timeout is 30 mn */
2.1       frystyk    28: 
                     29: /* Type definitions and global variables etc. local to this module */
                     30: struct _HTdns {
2.21      frystyk    31:     char *             hostname;                            /* name of host */
2.1       frystyk    32:     time_t             ntime;                              /* Creation time */
                     33:     int                        addrlength;            /* Length of address in bytes */
                     34:     int                        homes;         /* Number of IP addresses on the host */
                     35:     char **            addrlist;      /* List of addresses from name server */
                     36:     double *           weight;                    /* Weight on each address */
                     37: };
                     38: 
2.2       frystyk    39: PRIVATE HTList **CacheTable = NULL;
                     40: PRIVATE time_t DNSTimeout = DNS_TIMEOUT;          /* Timeout on DNS entries */
2.1       frystyk    41: 
                     42: /* ------------------------------------------------------------------------- */
                     43: 
2.20      frystyk    44: PRIVATE void free_object (HTdns * me)
2.1       frystyk    45: {
                     46:     if (me) {
2.17      frystyk    47:        HT_FREE(me->hostname);
2.1       frystyk    48:        if (*me->addrlist)
2.17      frystyk    49:            HT_FREE(*me->addrlist);
                     50:        HT_FREE(me->addrlist);
                     51:        HT_FREE(me->weight);
                     52:        HT_FREE(me);
2.1       frystyk    53:     }
                     54: }
                     55: 
2.21      frystyk    56: PRIVATE BOOL delete_object (HTList * list, HTdns * me)
2.1       frystyk    57: {
2.30      frystyk    58:     HTTRACE(PROT_TRACE, "DNS Delete.. object %p from list %p\n" _ me _ list);
2.1       frystyk    59:     HTList_removeObject(list, (void *) me);
2.20      frystyk    60:     free_object(me);
2.1       frystyk    61:     return YES;
                     62: }
                     63: 
                     64: /*     HTDNS_setTimeout
                     65: **     ----------------
                     66: **     Set the cache timeout for DNS entries. Default is DNS_TIMEOUT
                     67: */
                     68: PUBLIC void HTDNS_setTimeout (time_t timeout)
                     69: {
                     70:     DNSTimeout = timeout;
                     71: }
                     72: 
                     73: /*     HTDNS_timeout
                     74: **     -------------
                     75: **     Get the cache timeout 
                     76: */
                     77: PUBLIC time_t HTDNS_timeout (time_t timeout)
                     78: {
                     79:     return DNSTimeout;
                     80: }
                     81: 
                     82: /*     HTDNS_add
                     83: **     ---------
                     84: **     Add an element to the cache of visited hosts. Note that this function
                     85: **     requires the system implemented structure hostent and not our own
2.21      frystyk    86: **     host_info. The homes variable indicates the number of IP addresses 
                     87: **     found. A host name must NOT contain a port number.
                     88: **     Returns address of new HTdns object
2.1       frystyk    89: */
2.22      frystyk    90: PUBLIC HTdns * HTDNS_add (HTList * list, struct hostent * element,
                     91:                          char *host, int *homes)
2.1       frystyk    92: {
                     93:     HTdns *me;
2.21      frystyk    94:     char *addr = NULL;
2.1       frystyk    95:     char **index = element->h_addr_list;
                     96:     int cnt = 1;
                     97: 
                     98:     while(*index++) cnt++;
2.17      frystyk    99:     if ((me = (HTdns *) HT_CALLOC(1, sizeof(HTdns))) == NULL ||
                    100:        (me->addrlist = (char **) HT_CALLOC(1, cnt*sizeof(char*))) == NULL ||
                    101:        (addr = (char *) HT_CALLOC(1, cnt*element->h_length)) == NULL)
                    102:        HT_OUTOFMEM("HTDNS_add");
2.1       frystyk   103:     StrAllocCopy(me->hostname, host);
                    104:     me->ntime = time(NULL);
                    105:     index = element->h_addr_list;
                    106:     cnt = 0;
                    107:     while (*index) {
                    108:        *(me->addrlist+cnt) = addr+cnt*element->h_length;
                    109:        memcpy((void *) *(me->addrlist+cnt++), *index++, element->h_length);
                    110:     }
                    111:     me->homes = cnt;
                    112:     *homes = cnt;
2.17      frystyk   113:     if ((me->weight = (double *) HT_CALLOC(me->homes, sizeof(double))) == NULL)
                    114:         HT_OUTOFMEM("HTDNS_add");
2.1       frystyk   115:     me->addrlength = element->h_length;
2.30      frystyk   116:     HTTRACE(PROT_TRACE, "DNS Add..... `%s\' with %d home(s) to %p\n" _ 
                    117:                host _ *homes _ list);
2.1       frystyk   118:     HTList_addObject(list, (void *) me);
                    119:     return me;
                    120: }
                    121: 
                    122: 
                    123: /*     HTDNS_updateWeights
                    124: **     -------------------
                    125: **     This function calculates the weights of the different IP addresses
                    126: **     on a multi homed host. Each weight is calculated as
                    127: **
                    128: **             w(n+1) = w(n)*a + (1-a) * deltatime
                    129: **             a = exp(-1/Neff)
                    130: **             Neff is the effective number of samples used
                    131: **             deltatime is time spend on making a connection
                    132: **
                    133: **     A short window (low Neff) gives a high sensibility, but this is
                    134: **     required as we can't expect a lot of data to test on.
2.21      frystyk   135: **     "current" is the index returned by HTGetHostByName()
2.1       frystyk   136: */
2.24      frystyk   137: PUBLIC BOOL HTDNS_updateWeigths(HTdns *dns, int current, ms_t deltatime)
2.1       frystyk   138: {
                    139:     if (dns) {
                    140:        int cnt;
2.19      frystyk   141:        const double passive = 0.9;       /* Factor for all passive IP_addrs */
2.1       frystyk   142: #if 0
2.19      frystyk   143:        const int Neff = 3;
                    144:        const double alpha = exp(-1.0/Neff);
2.1       frystyk   145: #else
2.19      frystyk   146:        const double alpha = 0.716531310574;    /* Doesn't need the math lib */
2.1       frystyk   147: #endif
                    148:        for (cnt=0; cnt<dns->homes; cnt++) {
                    149:            if (cnt == current) {
                    150:                *(dns->weight+current) = *(dns->weight+current)*alpha + (1.0-alpha)*deltatime;
2.16      frystyk   151:                if (*(dns->weight+current) < 0.0) *(dns->weight+current) = 0.0;
2.1       frystyk   152:            } else {
                    153:                *(dns->weight+cnt) = *(dns->weight+cnt) * passive;
                    154:            }
2.30      frystyk   155:            HTTRACE(PROT_TRACE, "DNS weight.. Home %d has weight %4.2f\n" _ cnt _ 
2.1       frystyk   156:                        *(dns->weight+cnt));
                    157:        }
                    158:        return YES;
                    159:     }
2.30      frystyk   160:     HTTRACE(PROT_TRACE, "DNS weight.. Object %p not found'\n" _ dns);
2.1       frystyk   161:     return NO;
                    162: }
                    163: 
                    164: /*     HTDNS_delete
                    165: **     ------------
                    166: **     Remove an element from the cache
                    167: */
2.19      frystyk   168: PUBLIC BOOL HTDNS_delete (const char * host)
2.1       frystyk   169: {
                    170:     HTList *list;
                    171:     int hash = 0;
2.19      frystyk   172:     const char *ptr;
2.1       frystyk   173:     if (!host || !CacheTable) return NO;
                    174:     for(ptr=host; *ptr; ptr++)
2.29      frystyk   175:        hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_M_HASH_SIZE);
2.1       frystyk   176:     if ((list = CacheTable[hash])) {    /* We have the list, find the entry */
                    177:        HTdns *pres;
                    178:        while ((pres = (HTdns *) HTList_nextObject(list))) {
                    179:            if (!strcmp(pres->hostname, host)) {
                    180:                delete_object(CacheTable[hash], pres);
                    181:                break;
                    182:            }
                    183:        }
                    184:     }
                    185:     return YES;
                    186: }
                    187: 
                    188: /*     HTDNS_deleteAll
                    189: **     ---------------
                    190: **     Destroys the cache completely
                    191: */
                    192: PUBLIC BOOL HTDNS_deleteAll (void)
                    193: {
                    194:     int cnt;
                    195:     HTList *cur;
                    196:     if (!CacheTable) return NO;
2.29      frystyk   197:     for (cnt=0; cnt<HT_M_HASH_SIZE; cnt++) {
2.1       frystyk   198:        if ((cur = CacheTable[cnt])) { 
                    199:            HTdns *pres;
                    200:            while ((pres = (HTdns *) HTList_nextObject(cur)) != NULL)
2.20      frystyk   201:                free_object(pres);
2.1       frystyk   202:        }
                    203:        HTList_delete(CacheTable[cnt]);
                    204:        CacheTable[cnt] = NULL;
2.2       frystyk   205:     }
2.21      frystyk   206:     HT_FREE(CacheTable);
2.2       frystyk   207:     return YES;
                    208: }
2.1       frystyk   209: 
2.32.2.1! duerst    210: /*     Decode one hex character
        !           211: */
        !           212: /* copied from HTSRC.c, where it is not used */
        !           213: PRIVATE long from_hex (char c)
        !           214: {
        !           215:     return               (c>='0')&&(c<='9') ? c-'0'
        !           216:                        : (c>='A')&&(c<='F') ? c-'A'+10
        !           217:                        : (c>='a')&&(c<='f') ? c-'a'+10
        !           218:                        :                      0;
        !           219: }
        !           220: 
2.1       frystyk   221: /*     HTGetHostByName
                    222: **     ---------------
                    223: **     Resolve the host name using internal DNS cache. As we want to refer   
                    224: **     a specific host when timing the connection the weight function must
                    225: **     use the 'current' value as returned.
                    226: **      Returns:
                    227: **             >0      Number of homes
                    228: **             -1      Error
                    229: */
2.23      frystyk   230: PUBLIC int HTGetHostByName (HTHost * host, char *hostname, HTRequest* request)
2.1       frystyk   231: {
2.23      frystyk   232:     SockA *sin = HTHost_getSockAddr(host);
2.1       frystyk   233:     int homes = -1;
                    234:     HTList *list;                                  /* Current list in cache */
                    235:     HTdns *pres = NULL;
2.23      frystyk   236:     if (!host || !hostname) {
2.30      frystyk   237:        HTTRACE(PROT_TRACE, "HostByName.. Bad argument\n");
2.1       frystyk   238:        return -1;
                    239:     }
2.23      frystyk   240:     HTHost_setHome(host, 0);
2.1       frystyk   241:     
2.32.2.1! duerst    242:     /*
        !           243:     **
        !           244:     ** Code to deal with International Domain Names
        !           245:     **
        !           246:     */
        !           247:     /* unescape %-escapes from URI */
        !           248:     {
        !           249:        char *ptr, *ptr2;
        !           250:        for(ptr=ptr2=hostname; *ptr; ) {
        !           251:                if (*ptr=='%' && isxdigit(*(ptr+1)) && isxdigit(*(ptr+2))) {
        !           252:                        /* isxdigit() includes check for end of string */
        !           253:                    unsigned char unesc = (unsigned char) (from_hex(*(ptr+1))*16 + from_hex(*(ptr+2)));
        !           254:                    if (unesc=='.')  *ptr2++ = *ptr++;   /* just copy, because reserved, for security */
        !           255:                    else {
        !           256:                        *ptr2++ = (char) unesc;
        !           257:                        ptr += 3;
        !           258:                    }
        !           259:                } else *ptr2++ = *ptr++;   /* just copy */
        !           260:        }
        !           261:        *ptr2=*ptr;
        !           262:     }
        !           263:     /* still to do: check for valid UTF-8 */
        !           264:     /* still to do: call stringprep/nameprep */
        !           265:     /*
        !           266:     ** End of code to deal with International Domain Names
        !           267:     */
        !           268: 
2.1       frystyk   269:     /* Find a hash for this host */
                    270:     {
                    271:        int hash = 0;
                    272:        char *ptr;
2.23      frystyk   273:        for(ptr=hostname; *ptr; ptr++)
2.29      frystyk   274:            hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_M_HASH_SIZE);
2.1       frystyk   275:        if (!CacheTable) {
2.29      frystyk   276:            if ((CacheTable = (HTList* *) HT_CALLOC(HT_M_HASH_SIZE, sizeof(HTList *))) == NULL)
2.17      frystyk   277:                HT_OUTOFMEM("HTDNS_init");
2.1       frystyk   278:        }
                    279:        if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
                    280:        list = CacheTable[hash];
                    281:     }
                    282: 
                    283:     /* Search the cache */
                    284:     {
                    285:        HTList *cur = list;
                    286:        while ((pres = (HTdns *) HTList_nextObject(cur))) {
2.23      frystyk   287:            if (!strcmp(pres->hostname, hostname)) {
2.1       frystyk   288:                if (time(NULL) > pres->ntime + DNSTimeout) {
2.30      frystyk   289:                    HTTRACE(PROT_TRACE, "HostByName.. Refreshing cache\n");
2.1       frystyk   290:                    delete_object(list, pres);
                    291:                    pres = NULL;
                    292:                }
                    293:                break;
                    294:            }
                    295:        }
                    296:     }
                    297:     if (pres) {
                    298:        /*
                    299:        ** Find the best home. We still want to do this as we use it as a
                    300:        ** fall back for persistent connections
                    301:        */
                    302:        homes = pres->homes;
                    303:        if (pres->homes > 1) {
                    304:            int cnt = 0;
2.28      frystyk   305:            double best_weight = 1e30;                        /* Pretty bad */
2.1       frystyk   306:            while (cnt < pres->homes) {
                    307:                if (*(pres->weight+cnt) < best_weight) {
                    308:                    best_weight = *(pres->weight+cnt);
2.23      frystyk   309:                    HTHost_setHome(host, cnt);
2.1       frystyk   310:                }
                    311:                cnt++;
                    312:            }
                    313:        }
2.23      frystyk   314:        host->dns = pres;
                    315:        memcpy((void *) &sin->sin_addr, *(pres->addrlist+HTHost_home(host)),
2.14      frystyk   316:               pres->addrlength);
2.1       frystyk   317:     } else {
                    318:        struct hostent *hostelement;                          /* see netdb.h */
2.10      frystyk   319:        HTAlertCallback *cbf = HTAlert_find(HT_PROG_DNS);
2.1       frystyk   320: #ifdef HT_REENTRANT
                    321:        int thd_errno;
                    322:        char buffer[HOSTENT_MAX];
                    323:        struct hostent result;                        /* For gethostbyname_r */
2.31      kahan     324: #endif
2.32      kahan     325: #ifdef HAVE_GETHOSTBYNAME_R_3
2.31      kahan     326:         struct hostent_data hdata;
                    327: #endif
                    328: 
2.23      frystyk   329:        if (cbf) (*cbf)(request, HT_PROG_DNS, HT_MSG_NULL,NULL,hostname,NULL);
2.32      kahan     330: #ifdef HAVE_GETHOSTBYNAME_R_5
2.23      frystyk   331:        hostelement = gethostbyname_r(hostname, &result, buffer,
2.1       frystyk   332:                                      HOSTENT_MAX, &thd_errno);
2.32      kahan     333: #elif defined(HAVE_GETHOSTBYNAME_R_6)
2.31      kahan     334:        gethostbyname_r(hostname, &result, buffer,
                    335:                        HOSTENT_MAX, &hostelement, &thd_errno);
                    336: 
2.32      kahan     337: #elif defined(HAVE_GETHOSTBYNAME_R_3)
2.31      kahan     338:         if (gethostbyname_r(hostname, &result, &hdata) == 0) {
                    339:            hostelement = &result;
                    340:        }
                    341:        else {
                    342:            hostelement = NULL;
                    343:        }
2.1       frystyk   344: #else
2.23      frystyk   345:        if (cbf) (*cbf)(request, HT_PROG_DNS, HT_MSG_NULL,NULL,hostname,NULL);
                    346:        hostelement = gethostbyname(hostname);
2.1       frystyk   347: #endif
                    348:        if (!hostelement) {
2.27      frystyk   349:             HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO,
                    350:                                     "gethostbyname");
2.1       frystyk   351:            return -1;
                    352:        }       
2.23      frystyk   353:        host->dns = HTDNS_add(list, hostelement, hostname, &homes);
2.14      frystyk   354:        memcpy((void *) &sin->sin_addr, *hostelement->h_addr_list,
                    355:               hostelement->h_length);
2.1       frystyk   356:     }
                    357:     return homes;
                    358: }
                    359: 
                    360: 
                    361: /*
                    362: **     Get host name of the machine on the other end of a socket.
                    363: **
                    364: */
2.9       frystyk   365: PUBLIC char * HTGetHostBySock (int soc)
2.1       frystyk   366: {
                    367:     struct sockaddr addr;
                    368:     int len = sizeof(struct sockaddr);
                    369:     struct in_addr *iaddr;
                    370:     char *name = NULL;
                    371:     struct hostent * phost;            /* Pointer to host -- See netdb.h */
                    372: #ifdef HT_REENTRANT
                    373:     int thd_errno;
                    374:     char buffer[HOSTENT_MAX];
                    375:     struct hostent result;                           /* For gethostbyaddr_r */
                    376: #endif
2.31      kahan     377: #ifdef HAVE_GETHOSTBYADDR_R_5
                    378:     struct hostent_data hdata;
                    379: #endif
2.1       frystyk   380: 
                    381: #ifdef DECNET  /* Decnet ain't got no damn name server 8#OO */
                    382:     return NULL;
                    383: #else
                    384:     if (getpeername(soc, &addr, &len) < 0)
                    385:        return NULL;
                    386:     iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
                    387: 
2.31      kahan     388: #ifdef HAVE_GETHOSTBYADDR_R_7
2.1       frystyk   389:     phost = gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
                    390:                            &result, buffer, HOSTENT_MAX, &thd_errno);
2.31      kahan     391: #elif defined(HAVE_GETHOSTBYADDR_R_8)
                    392:     gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
                    393:                    &result, buffer, HOSTENT_MAX, &phost, &thd_errno);
                    394: #elif defined(HAVE_GETHOSTBYADDR_R_5)
                    395:     if(gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
                    396:                       &result, &hdata)==0) {
                    397:        phost=&result;
                    398:     }
                    399:     else {
                    400:        phost = NULL;
                    401:     }
2.1       frystyk   402: #else
                    403:     phost = gethostbyaddr((char *) iaddr, sizeof(struct in_addr), AF_INET);
                    404: #endif
                    405:     if (!phost) {
2.30      frystyk   406:        HTTRACE(PROT_TRACE, "TCP......... Can't find internet node name for peer!!\n");
2.1       frystyk   407:        return NULL;
                    408:     }
                    409:     StrAllocCopy(name, phost->h_name);
2.30      frystyk   410:     HTTRACE(PROT_TRACE, "TCP......... Peer name is `%s'\n" _ name);
2.1       frystyk   411:     return name;
                    412: 
                    413: #endif /* not DECNET */
                    414: }

Webmaster