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