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

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

Webmaster