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