Annotation of libwww/Library/src/HTDNS.c, revision 2.32
2.1 frystyk 1: /* HTDNS.c
2: ** DOMAIN NAME SERVICE MANAGER
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.32 ! kahan 6: ** @(#) $Id: HTDNS.c,v 2.31 2000/01/06 10:48:50 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
14: */
15:
16: /* Library include files */
2.26 frystyk 17: #include "wwwsys.h"
2.21 frystyk 18: #include "WWWUtil.h"
2.1 frystyk 19: #include "HTParse.h"
20: #include "HTAlert.h"
21: #include "HTError.h"
2.21 frystyk 22: #include "HTTrans.h"
2.23 frystyk 23: #include "HTHstMan.h"
2.1 frystyk 24: #include "HTDNS.h" /* Implemented here */
25:
2.25 frystyk 26: #define DNS_TIMEOUT 1800L /* Default DNS timeout is 30 mn */
2.1 frystyk 27:
28: /* Type definitions and global variables etc. local to this module */
29: struct _HTdns {
2.21 frystyk 30: char * hostname; /* name of host */
2.1 frystyk 31: time_t ntime; /* Creation time */
32: int addrlength; /* Length of address in bytes */
33: int homes; /* Number of IP addresses on the host */
34: char ** addrlist; /* List of addresses from name server */
35: double * weight; /* Weight on each address */
36: };
37:
2.2 frystyk 38: PRIVATE HTList **CacheTable = NULL;
39: PRIVATE time_t DNSTimeout = DNS_TIMEOUT; /* Timeout on DNS entries */
2.1 frystyk 40:
41: /* ------------------------------------------------------------------------- */
42:
2.20 frystyk 43: PRIVATE void free_object (HTdns * me)
2.1 frystyk 44: {
45: if (me) {
2.17 frystyk 46: HT_FREE(me->hostname);
2.1 frystyk 47: if (*me->addrlist)
2.17 frystyk 48: HT_FREE(*me->addrlist);
49: HT_FREE(me->addrlist);
50: HT_FREE(me->weight);
51: HT_FREE(me);
2.1 frystyk 52: }
53: }
54:
2.21 frystyk 55: PRIVATE BOOL delete_object (HTList * list, HTdns * me)
2.1 frystyk 56: {
2.30 frystyk 57: HTTRACE(PROT_TRACE, "DNS Delete.. object %p from list %p\n" _ me _ list);
2.1 frystyk 58: HTList_removeObject(list, (void *) me);
2.20 frystyk 59: free_object(me);
2.1 frystyk 60: return YES;
61: }
62:
63: /* HTDNS_setTimeout
64: ** ----------------
65: ** Set the cache timeout for DNS entries. Default is DNS_TIMEOUT
66: */
67: PUBLIC void HTDNS_setTimeout (time_t timeout)
68: {
69: DNSTimeout = timeout;
70: }
71:
72: /* HTDNS_timeout
73: ** -------------
74: ** Get the cache timeout
75: */
76: PUBLIC time_t HTDNS_timeout (time_t timeout)
77: {
78: return DNSTimeout;
79: }
80:
81: /* HTDNS_add
82: ** ---------
83: ** Add an element to the cache of visited hosts. Note that this function
84: ** requires the system implemented structure hostent and not our own
2.21 frystyk 85: ** host_info. The homes variable indicates the number of IP addresses
86: ** found. A host name must NOT contain a port number.
87: ** Returns address of new HTdns object
2.1 frystyk 88: */
2.22 frystyk 89: PUBLIC HTdns * HTDNS_add (HTList * list, struct hostent * element,
90: char *host, int *homes)
2.1 frystyk 91: {
92: HTdns *me;
2.21 frystyk 93: char *addr = NULL;
2.1 frystyk 94: char **index = element->h_addr_list;
95: int cnt = 1;
96:
97: while(*index++) cnt++;
2.17 frystyk 98: if ((me = (HTdns *) HT_CALLOC(1, sizeof(HTdns))) == NULL ||
99: (me->addrlist = (char **) HT_CALLOC(1, cnt*sizeof(char*))) == NULL ||
100: (addr = (char *) HT_CALLOC(1, cnt*element->h_length)) == NULL)
101: HT_OUTOFMEM("HTDNS_add");
2.1 frystyk 102: StrAllocCopy(me->hostname, host);
103: me->ntime = time(NULL);
104: index = element->h_addr_list;
105: cnt = 0;
106: while (*index) {
107: *(me->addrlist+cnt) = addr+cnt*element->h_length;
108: memcpy((void *) *(me->addrlist+cnt++), *index++, element->h_length);
109: }
110: me->homes = cnt;
111: *homes = cnt;
2.17 frystyk 112: if ((me->weight = (double *) HT_CALLOC(me->homes, sizeof(double))) == NULL)
113: HT_OUTOFMEM("HTDNS_add");
2.1 frystyk 114: me->addrlength = element->h_length;
2.30 frystyk 115: HTTRACE(PROT_TRACE, "DNS Add..... `%s\' with %d home(s) to %p\n" _
116: host _ *homes _ list);
2.1 frystyk 117: HTList_addObject(list, (void *) me);
118: return me;
119: }
120:
121:
122: /* HTDNS_updateWeights
123: ** -------------------
124: ** This function calculates the weights of the different IP addresses
125: ** on a multi homed host. Each weight is calculated as
126: **
127: ** w(n+1) = w(n)*a + (1-a) * deltatime
128: ** a = exp(-1/Neff)
129: ** Neff is the effective number of samples used
130: ** deltatime is time spend on making a connection
131: **
132: ** A short window (low Neff) gives a high sensibility, but this is
133: ** required as we can't expect a lot of data to test on.
2.21 frystyk 134: ** "current" is the index returned by HTGetHostByName()
2.1 frystyk 135: */
2.24 frystyk 136: PUBLIC BOOL HTDNS_updateWeigths(HTdns *dns, int current, ms_t deltatime)
2.1 frystyk 137: {
138: if (dns) {
139: int cnt;
2.19 frystyk 140: const double passive = 0.9; /* Factor for all passive IP_addrs */
2.1 frystyk 141: #if 0
2.19 frystyk 142: const int Neff = 3;
143: const double alpha = exp(-1.0/Neff);
2.1 frystyk 144: #else
2.19 frystyk 145: const double alpha = 0.716531310574; /* Doesn't need the math lib */
2.1 frystyk 146: #endif
147: for (cnt=0; cnt<dns->homes; cnt++) {
148: if (cnt == current) {
149: *(dns->weight+current) = *(dns->weight+current)*alpha + (1.0-alpha)*deltatime;
2.16 frystyk 150: if (*(dns->weight+current) < 0.0) *(dns->weight+current) = 0.0;
2.1 frystyk 151: } else {
152: *(dns->weight+cnt) = *(dns->weight+cnt) * passive;
153: }
2.30 frystyk 154: HTTRACE(PROT_TRACE, "DNS weight.. Home %d has weight %4.2f\n" _ cnt _
2.1 frystyk 155: *(dns->weight+cnt));
156: }
157: return YES;
158: }
2.30 frystyk 159: HTTRACE(PROT_TRACE, "DNS weight.. Object %p not found'\n" _ dns);
2.1 frystyk 160: return NO;
161: }
162:
163: /* HTDNS_delete
164: ** ------------
165: ** Remove an element from the cache
166: */
2.19 frystyk 167: PUBLIC BOOL HTDNS_delete (const char * host)
2.1 frystyk 168: {
169: HTList *list;
170: int hash = 0;
2.19 frystyk 171: const char *ptr;
2.1 frystyk 172: if (!host || !CacheTable) return NO;
173: for(ptr=host; *ptr; ptr++)
2.29 frystyk 174: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_M_HASH_SIZE);
2.1 frystyk 175: if ((list = CacheTable[hash])) { /* We have the list, find the entry */
176: HTdns *pres;
177: while ((pres = (HTdns *) HTList_nextObject(list))) {
178: if (!strcmp(pres->hostname, host)) {
179: delete_object(CacheTable[hash], pres);
180: break;
181: }
182: }
183: }
184: return YES;
185: }
186:
187: /* HTDNS_deleteAll
188: ** ---------------
189: ** Destroys the cache completely
190: */
191: PUBLIC BOOL HTDNS_deleteAll (void)
192: {
193: int cnt;
194: HTList *cur;
195: if (!CacheTable) return NO;
2.29 frystyk 196: for (cnt=0; cnt<HT_M_HASH_SIZE; cnt++) {
2.1 frystyk 197: if ((cur = CacheTable[cnt])) {
198: HTdns *pres;
199: while ((pres = (HTdns *) HTList_nextObject(cur)) != NULL)
2.20 frystyk 200: free_object(pres);
2.1 frystyk 201: }
202: HTList_delete(CacheTable[cnt]);
203: CacheTable[cnt] = NULL;
2.2 frystyk 204: }
2.21 frystyk 205: HT_FREE(CacheTable);
2.2 frystyk 206: return YES;
207: }
2.1 frystyk 208:
209: /* HTGetHostByName
210: ** ---------------
211: ** Resolve the host name using internal DNS cache. As we want to refer
212: ** a specific host when timing the connection the weight function must
213: ** use the 'current' value as returned.
214: ** Returns:
215: ** >0 Number of homes
216: ** -1 Error
217: */
2.23 frystyk 218: PUBLIC int HTGetHostByName (HTHost * host, char *hostname, HTRequest* request)
2.1 frystyk 219: {
2.23 frystyk 220: SockA *sin = HTHost_getSockAddr(host);
2.1 frystyk 221: int homes = -1;
222: HTList *list; /* Current list in cache */
223: HTdns *pres = NULL;
2.23 frystyk 224: if (!host || !hostname) {
2.30 frystyk 225: HTTRACE(PROT_TRACE, "HostByName.. Bad argument\n");
2.1 frystyk 226: return -1;
227: }
2.23 frystyk 228: HTHost_setHome(host, 0);
2.1 frystyk 229:
230: /* Find a hash for this host */
231: {
232: int hash = 0;
233: char *ptr;
2.23 frystyk 234: for(ptr=hostname; *ptr; ptr++)
2.29 frystyk 235: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_M_HASH_SIZE);
2.1 frystyk 236: if (!CacheTable) {
2.29 frystyk 237: if ((CacheTable = (HTList* *) HT_CALLOC(HT_M_HASH_SIZE, sizeof(HTList *))) == NULL)
2.17 frystyk 238: HT_OUTOFMEM("HTDNS_init");
2.1 frystyk 239: }
240: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
241: list = CacheTable[hash];
242: }
243:
244: /* Search the cache */
245: {
246: HTList *cur = list;
247: while ((pres = (HTdns *) HTList_nextObject(cur))) {
2.23 frystyk 248: if (!strcmp(pres->hostname, hostname)) {
2.1 frystyk 249: if (time(NULL) > pres->ntime + DNSTimeout) {
2.30 frystyk 250: HTTRACE(PROT_TRACE, "HostByName.. Refreshing cache\n");
2.1 frystyk 251: delete_object(list, pres);
252: pres = NULL;
253: }
254: break;
255: }
256: }
257: }
258: if (pres) {
259: /*
260: ** Find the best home. We still want to do this as we use it as a
261: ** fall back for persistent connections
262: */
263: homes = pres->homes;
264: if (pres->homes > 1) {
265: int cnt = 0;
2.28 frystyk 266: double best_weight = 1e30; /* Pretty bad */
2.1 frystyk 267: while (cnt < pres->homes) {
268: if (*(pres->weight+cnt) < best_weight) {
269: best_weight = *(pres->weight+cnt);
2.23 frystyk 270: HTHost_setHome(host, cnt);
2.1 frystyk 271: }
272: cnt++;
273: }
274: }
2.23 frystyk 275: host->dns = pres;
276: memcpy((void *) &sin->sin_addr, *(pres->addrlist+HTHost_home(host)),
2.14 frystyk 277: pres->addrlength);
2.1 frystyk 278: } else {
279: struct hostent *hostelement; /* see netdb.h */
2.10 frystyk 280: HTAlertCallback *cbf = HTAlert_find(HT_PROG_DNS);
2.1 frystyk 281: #ifdef HT_REENTRANT
282: int thd_errno;
283: char buffer[HOSTENT_MAX];
284: struct hostent result; /* For gethostbyname_r */
2.31 kahan 285: #endif
2.32 ! kahan 286: #ifdef HAVE_GETHOSTBYNAME_R_3
2.31 kahan 287: struct hostent_data hdata;
288: #endif
289:
2.23 frystyk 290: if (cbf) (*cbf)(request, HT_PROG_DNS, HT_MSG_NULL,NULL,hostname,NULL);
2.32 ! kahan 291: #ifdef HAVE_GETHOSTBYNAME_R_5
2.23 frystyk 292: hostelement = gethostbyname_r(hostname, &result, buffer,
2.1 frystyk 293: HOSTENT_MAX, &thd_errno);
2.32 ! kahan 294: #elif defined(HAVE_GETHOSTBYNAME_R_6)
2.31 kahan 295: gethostbyname_r(hostname, &result, buffer,
296: HOSTENT_MAX, &hostelement, &thd_errno);
297:
2.32 ! kahan 298: #elif defined(HAVE_GETHOSTBYNAME_R_3)
2.31 kahan 299: if (gethostbyname_r(hostname, &result, &hdata) == 0) {
300: hostelement = &result;
301: }
302: else {
303: hostelement = NULL;
304: }
2.1 frystyk 305: #else
2.23 frystyk 306: if (cbf) (*cbf)(request, HT_PROG_DNS, HT_MSG_NULL,NULL,hostname,NULL);
307: hostelement = gethostbyname(hostname);
2.1 frystyk 308: #endif
309: if (!hostelement) {
2.27 frystyk 310: HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO,
311: "gethostbyname");
2.1 frystyk 312: return -1;
313: }
2.23 frystyk 314: host->dns = HTDNS_add(list, hostelement, hostname, &homes);
2.14 frystyk 315: memcpy((void *) &sin->sin_addr, *hostelement->h_addr_list,
316: hostelement->h_length);
2.1 frystyk 317: }
318: return homes;
319: }
320:
321:
322: /*
323: ** Get host name of the machine on the other end of a socket.
324: **
325: */
2.9 frystyk 326: PUBLIC char * HTGetHostBySock (int soc)
2.1 frystyk 327: {
328: struct sockaddr addr;
329: int len = sizeof(struct sockaddr);
330: struct in_addr *iaddr;
331: char *name = NULL;
332: struct hostent * phost; /* Pointer to host -- See netdb.h */
333: #ifdef HT_REENTRANT
334: int thd_errno;
335: char buffer[HOSTENT_MAX];
336: struct hostent result; /* For gethostbyaddr_r */
337: #endif
2.31 kahan 338: #ifdef HAVE_GETHOSTBYADDR_R_5
339: struct hostent_data hdata;
340: #endif
2.1 frystyk 341:
342: #ifdef DECNET /* Decnet ain't got no damn name server 8#OO */
343: return NULL;
344: #else
345: if (getpeername(soc, &addr, &len) < 0)
346: return NULL;
347: iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
348:
2.31 kahan 349: #ifdef HAVE_GETHOSTBYADDR_R_7
2.1 frystyk 350: phost = gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
351: &result, buffer, HOSTENT_MAX, &thd_errno);
2.31 kahan 352: #elif defined(HAVE_GETHOSTBYADDR_R_8)
353: gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
354: &result, buffer, HOSTENT_MAX, &phost, &thd_errno);
355: #elif defined(HAVE_GETHOSTBYADDR_R_5)
356: if(gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
357: &result, &hdata)==0) {
358: phost=&result;
359: }
360: else {
361: phost = NULL;
362: }
2.1 frystyk 363: #else
364: phost = gethostbyaddr((char *) iaddr, sizeof(struct in_addr), AF_INET);
365: #endif
366: if (!phost) {
2.30 frystyk 367: HTTRACE(PROT_TRACE, "TCP......... Can't find internet node name for peer!!\n");
2.1 frystyk 368: return NULL;
369: }
370: StrAllocCopy(name, phost->h_name);
2.30 frystyk 371: HTTRACE(PROT_TRACE, "TCP......... Peer name is `%s'\n" _ name);
2.1 frystyk 372: return name;
373:
374: #endif /* not DECNET */
375: }
Webmaster