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