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

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

Webmaster