Annotation of libwww/Library/src/HTDNS.c, revision 2.20
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.
6: **
7: ** This object manages a cache of hosts we have looked up vis DNS.
8: ** The object contains the necessary parts from hostent. For Internet host
9: ** hostent->h_addr_list is not an array of char pointers but an array of
10: ** pointers of type in_addr.
11: **
12: ** 13 Sep 95 HFN Spawned from HTTCP.c and rewritten
13: */
14:
15: /* Library include files */
2.19 frystyk 16: #include "sysdep.h"
2.1 frystyk 17: #include "HTUtils.h"
18: #include "HTString.h"
19: #include "HTAtom.h"
20: #include "HTList.h"
21: #include "HTParse.h"
22: #include "HTAlert.h"
23: #include "HTError.h"
24: #include "HTNetMan.h"
2.20 ! frystyk 25: #include "HTSocket.h"
2.1 frystyk 26: #include "HTDNS.h" /* Implemented here */
27:
28: #define TCP_TIMEOUT 3600L /* Default TCP timeout i 1 h */
29: #define DNS_TIMEOUT 172800L /* Default DNS timeout is 48 h */
30: #define HASH_SIZE 67
31:
32: /* Type definitions and global variables etc. local to this module */
33: struct _HTdns {
34: char * hostname; /* name of host + maybe port */
35: time_t ntime; /* Creation time */
36:
37: char * server; /* Server class */
38: int version; /* Server version */
39:
2.12 frystyk 40: int active; /* Semaphor on Socket usage */
2.1 frystyk 41: time_t expires; /* Socket expires time */
2.11 frystyk 42: SOCKET sockfd; /* Persistent connection */
2.1 frystyk 43:
44: int addrlength; /* Length of address in bytes */
45: int homes; /* Number of IP addresses on the host */
46: char ** addrlist; /* List of addresses from name server */
47: double * weight; /* Weight on each address */
48: };
49:
2.2 frystyk 50: PRIVATE HTList **CacheTable = NULL;
51: PRIVATE HTList *PersSock = NULL; /* List of persistent sockets */
52: PRIVATE time_t DNSTimeout = DNS_TIMEOUT; /* Timeout on DNS entries */
53: PRIVATE time_t TCPTimeout = TCP_TIMEOUT; /* Timeout on TCP sockets */
2.1 frystyk 54:
55: /* ------------------------------------------------------------------------- */
56:
2.20 ! frystyk 57: PRIVATE void free_object (HTdns * me)
2.1 frystyk 58: {
59: if (me) {
2.17 frystyk 60: HT_FREE(me->hostname);
61: HT_FREE(me->server);
2.12 frystyk 62: if (me->sockfd != INVSOC && me->active <= 0) {
2.1 frystyk 63: NETCLOSE(me->sockfd);
2.2 frystyk 64: HTEvent_UnRegister(me->sockfd, (SockOps) FD_ALL);
65: HTList_removeObject(PersSock, me);
66: }
2.1 frystyk 67: if (*me->addrlist)
2.17 frystyk 68: HT_FREE(*me->addrlist);
69: HT_FREE(me->addrlist);
70: HT_FREE(me->weight);
71: HT_FREE(me);
2.1 frystyk 72: }
73: }
74:
75: PRIVATE BOOL delete_object (HTList * list, HTdns *me)
76: {
77: if (PROT_TRACE)
2.18 eric 78: HTTrace("DNS Delete.. object %p from list %p\n", me, list);
2.1 frystyk 79: HTList_removeObject(list, (void *) me);
2.20 ! frystyk 80: free_object(me);
2.1 frystyk 81: return YES;
82: }
83:
84: /* HTDNS_setTimeout
85: ** ----------------
86: ** Set the cache timeout for DNS entries. Default is DNS_TIMEOUT
87: */
88: PUBLIC void HTDNS_setTimeout (time_t timeout)
89: {
90: DNSTimeout = timeout;
91: }
92:
93: /* HTDNS_timeout
94: ** -------------
95: ** Get the cache timeout
96: */
97: PUBLIC time_t HTDNS_timeout (time_t timeout)
98: {
99: return DNSTimeout;
100: }
101:
102: /* HTDNS_setSockTimeout
103: ** --------------------
104: ** Set the cache timeout for DNS entries. Default is DNS_TIMEOUT
105: */
106: PUBLIC void HTDNS_setSockTimeout (time_t timeout)
107: {
108: TCPTimeout = timeout;
109: }
110:
111: /* HTDNS_sockTimeout
112: ** -----------------
113: ** Get the cache timeout
114: */
115: PUBLIC time_t HTDNS_sockTimeout (time_t timeout)
116: {
117: return TCPTimeout;
118: }
119:
120: /* HTDNS_serverClass
121: ** -----------------
122: */
123: PUBLIC char *HTDNS_serverClass (HTdns *dns)
124: {
125: return dns ? dns->server : NULL;
126: }
127:
2.4 frystyk 128: PUBLIC void HTDNS_setServerClass (HTdns * dns, char *s_class)
2.1 frystyk 129: {
2.4 frystyk 130: if (dns && s_class) StrAllocCopy(dns->server, s_class);
2.1 frystyk 131: }
132:
133: /* HTDNS_serverVersion
134: ** -------------------
135: */
136: PUBLIC int HTDNS_serverVersion (HTdns *dns)
137: {
138: return dns ? dns->version : 0;
139: }
140:
2.5 frystyk 141: PUBLIC void HTDNS_setServerVersion (HTdns * dns, int version)
2.1 frystyk 142: {
2.5 frystyk 143: if (dns) dns->version = version;
144: }
145:
2.1 frystyk 146: /* Persistent Connections
147: ** ----------------------
148: */
2.11 frystyk 149: PUBLIC SOCKET HTDNS_socket(HTdns *dns)
2.1 frystyk 150: {
151: return dns ? dns->sockfd : INVSOC;
152: }
153:
2.2 frystyk 154: /*
155: ** We don't want more than MaxSockets-2 connections to be persistent in
156: ** order to avoid deadlock.
157: */
2.11 frystyk 158: PUBLIC BOOL HTDNS_setSocket(HTdns *dns, SOCKET socket)
2.1 frystyk 159: {
2.2 frystyk 160: if (!dns) return NO;
161: if (!PersSock) PersSock = HTList_new();
162: if (socket == INVSOC) {
163: dns->sockfd = socket;
2.12 frystyk 164: dns->active = 0;
165: if (dns->active < 0) dns->active = 0;
2.2 frystyk 166: dns->expires = 0;
167: HTList_removeObject(PersSock, dns);
168: return YES;
169: } else if (HTList_count(PersSock) < HTNet_maxSocket()-2) {
2.1 frystyk 170: dns->sockfd = socket;
2.12 frystyk 171: if (dns->active <= 0) dns->active = 1;
2.1 frystyk 172: dns->expires = time(NULL) + TCPTimeout; /* Default timeout */
2.2 frystyk 173: HTList_addObject(PersSock, dns);
174: return YES;
2.15 frystyk 175: } else {
176: if (PROT_TRACE)
2.18 eric 177: HTTrace("DNS Socket.. not room for persistent socket %d\n",
2.15 frystyk 178: socket);
2.1 frystyk 179: }
2.12 frystyk 180: if (PROT_TRACE)
2.18 eric 181: HTTrace("DNS Socket.. semaphor is %d for soc %d\n",
2.12 frystyk 182: dns->active, dns->sockfd);
2.2 frystyk 183: return NO;
2.1 frystyk 184: }
185:
2.6 frystyk 186: PUBLIC int HTDNS_socketCount (void)
187: {
188: return PersSock ? HTList_count(PersSock) : 0;
189: }
190:
2.1 frystyk 191: PUBLIC void HTDNS_clearActive (HTdns *dns)
192: {
2.2 frystyk 193: if (dns) {
2.12 frystyk 194: dns->active--;
195: if (dns->active < 0) dns->active = 0;
2.2 frystyk 196: if (PROT_TRACE)
2.18 eric 197: HTTrace("DNS Clear... semaphor is %d for soc %d\n",
2.12 frystyk 198: dns->active, dns->sockfd);
2.2 frystyk 199: }
2.1 frystyk 200: }
201:
202: /* Persistent Connection Expiration
203: ** --------------------------------
2.2 frystyk 204: ** Should normally not be used. If, then use calendar time.
2.1 frystyk 205: */
206: PUBLIC void HTDNS_setSockExpires (HTdns * dns, time_t expires)
207: {
208: if (dns) dns->expires = expires;
209: }
210:
211: PUBLIC time_t HTDNS_sockExpires (HTdns * dns)
212: {
213: return dns ? dns->expires : -1;
214: }
215:
216: /* HTDNS_add
217: ** ---------
218: ** Add an element to the cache of visited hosts. Note that this function
219: ** requires the system implemented structure hostent and not our own
220: ** host_info. The homes variable indicates the number of
221: ** IP addresses found. host is a host name possibly with a port number
222: ** returns address of new HTdns object
223: */
224: PRIVATE HTdns * HTDNS_add (HTList * list, struct hostent * element,
225: char *host, int *homes)
226: {
227: HTdns *me;
228: char *addr;
229: char **index = element->h_addr_list;
230: int cnt = 1;
231:
232: while(*index++) cnt++;
2.17 frystyk 233: if ((me = (HTdns *) HT_CALLOC(1, sizeof(HTdns))) == NULL ||
234: (me->addrlist = (char **) HT_CALLOC(1, cnt*sizeof(char*))) == NULL ||
235: (addr = (char *) HT_CALLOC(1, cnt*element->h_length)) == NULL)
236: HT_OUTOFMEM("HTDNS_add");
2.1 frystyk 237: StrAllocCopy(me->hostname, host);
238: me->ntime = time(NULL);
239: me->sockfd = INVSOC;
240: index = element->h_addr_list;
241: cnt = 0;
242: while (*index) {
243: *(me->addrlist+cnt) = addr+cnt*element->h_length;
244: memcpy((void *) *(me->addrlist+cnt++), *index++, element->h_length);
245: }
246: me->homes = cnt;
247: *homes = cnt;
2.17 frystyk 248: if ((me->weight = (double *) HT_CALLOC(me->homes, sizeof(double))) == NULL)
249: HT_OUTOFMEM("HTDNS_add");
2.1 frystyk 250: me->addrlength = element->h_length;
251: if (PROT_TRACE)
2.18 eric 252: HTTrace("DNS Add..... `%s\' with %d home(s) to %p\n",
2.1 frystyk 253: host, *homes, list);
254: HTList_addObject(list, (void *) me);
255: return me;
256: }
257:
258:
259: /* HTDNS_updateWeights
260: ** -------------------
261: ** This function calculates the weights of the different IP addresses
262: ** on a multi homed host. Each weight is calculated as
263: **
264: ** w(n+1) = w(n)*a + (1-a) * deltatime
265: ** a = exp(-1/Neff)
266: ** Neff is the effective number of samples used
267: ** deltatime is time spend on making a connection
268: **
269: ** A short window (low Neff) gives a high sensibility, but this is
270: ** required as we can't expect a lot of data to test on.
271: ** host is a host name possibly with port number. current is the index
272: ** returned by HTGetHostByName()
273: */
274: PUBLIC BOOL HTDNS_updateWeigths(HTdns *dns, int current, time_t deltatime)
275: {
276: if (dns) {
277: int cnt;
2.19 frystyk 278: const double passive = 0.9; /* Factor for all passive IP_addrs */
2.1 frystyk 279: #if 0
2.19 frystyk 280: const int Neff = 3;
281: const double alpha = exp(-1.0/Neff);
2.1 frystyk 282: #else
2.19 frystyk 283: const double alpha = 0.716531310574; /* Doesn't need the math lib */
2.1 frystyk 284: #endif
285: for (cnt=0; cnt<dns->homes; cnt++) {
286: if (cnt == current) {
287: *(dns->weight+current) = *(dns->weight+current)*alpha + (1.0-alpha)*deltatime;
2.16 frystyk 288: if (*(dns->weight+current) < 0.0) *(dns->weight+current) = 0.0;
2.1 frystyk 289: } else {
290: *(dns->weight+cnt) = *(dns->weight+cnt) * passive;
291: }
292: if (PROT_TRACE)
2.18 eric 293: HTTrace("DNS Weigths. Home %d has weight %4.2f\n", cnt,
2.1 frystyk 294: *(dns->weight+cnt));
295: }
296: return YES;
297: }
298: if (PROT_TRACE)
2.18 eric 299: HTTrace("DNS Weigths. Object %p not found'\n", dns);
2.1 frystyk 300: return NO;
301: }
302:
303: /* HTDNS_delete
304: ** ------------
305: ** Remove an element from the cache
306: ** Host must be a host name possibly with a port number
307: */
2.19 frystyk 308: PUBLIC BOOL HTDNS_delete (const char * host)
2.1 frystyk 309: {
310: HTList *list;
311: int hash = 0;
2.19 frystyk 312: const char *ptr;
2.1 frystyk 313: if (!host || !CacheTable) return NO;
314: for(ptr=host; *ptr; ptr++)
315: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
316: if ((list = CacheTable[hash])) { /* We have the list, find the entry */
317: HTdns *pres;
318: while ((pres = (HTdns *) HTList_nextObject(list))) {
319: if (!strcmp(pres->hostname, host)) {
320: delete_object(CacheTable[hash], pres);
321: break;
322: }
323: }
324: }
325: return YES;
326: }
327:
328: /* HTDNS_deleteAll
329: ** ---------------
330: ** Destroys the cache completely
331: */
332: PUBLIC BOOL HTDNS_deleteAll (void)
333: {
334: int cnt;
335: HTList *cur;
336: if (!CacheTable) return NO;
337: for (cnt=0; cnt<HASH_SIZE; cnt++) {
338: if ((cur = CacheTable[cnt])) {
339: HTdns *pres;
340: while ((pres = (HTdns *) HTList_nextObject(cur)) != NULL)
2.20 ! frystyk 341: free_object(pres);
2.1 frystyk 342: }
343: HTList_delete(CacheTable[cnt]);
344: CacheTable[cnt] = NULL;
2.2 frystyk 345: }
346: if (PersSock) { /* Remove list of persistent connection */
347: HTList_delete(PersSock);
348: PersSock = NULL;
349: }
350: return YES;
351: }
2.1 frystyk 352:
2.2 frystyk 353: /* HTDNS_closeSocket
354: ** -----------------
355: ** This function is registered when the socket is idle so that we get
356: ** a notification if the socket closes at the other end. At this point
2.19 frystyk 357: ** we can't use the request object as it might have been HT_FREEd a long
2.2 frystyk 358: ** time ago.
359: */
360: PUBLIC int HTDNS_closeSocket(SOCKET soc, HTRequest * request, SockOps ops)
361: {
362: if (PROT_TRACE)
2.18 eric 363: HTTrace("DNS Close... called with socket %d with ops %x\n",
2.2 frystyk 364: soc, (unsigned) ops);
365: if (ops == FD_READ && PersSock) {
366: HTList *cur = PersSock;
367: HTdns *pres;
368: while ((pres = (HTdns *) HTList_nextObject(cur))) {
369: if (pres->sockfd == soc) {
2.20 ! frystyk 370: if (PROT_TRACE) HTTrace("DNS Close... socket %d\n",soc);
2.2 frystyk 371: NETCLOSE(soc);
372: HTDNS_setSocket(pres, INVSOC);
2.20 ! frystyk 373: HTChannel_delete (soc);
2.2 frystyk 374: break;
375: }
376: }
2.15 frystyk 377: if (!pres)
2.18 eric 378: if (PROT_TRACE)HTTrace("DNS Close... socket NOT FOUND!\n");
2.1 frystyk 379: }
2.2 frystyk 380: HTEvent_UnRegister(soc, (SockOps) FD_ALL);
381: return HT_OK;
2.1 frystyk 382: }
383:
384: /* HTGetHostByName
385: ** ---------------
386: ** Resolve the host name using internal DNS cache. As we want to refer
387: ** a specific host when timing the connection the weight function must
388: ** use the 'current' value as returned.
389: ** Host must be a host name possibly with a port number
390: ** Returns:
391: ** >0 Number of homes
392: ** 0 Wait for persistent socket
393: ** -1 Error
394: */
395: PUBLIC int HTGetHostByName (HTNet *net, char *host)
396: {
397: SockA *sin = &net->sock_addr;
398: int homes = -1;
399: HTList *list; /* Current list in cache */
400: HTdns *pres = NULL;
401: if (!net || !host) {
402: if (PROT_TRACE)
2.18 eric 403: HTTrace("HostByName.. Bad argument\n");
2.1 frystyk 404: return -1;
405: }
406: net->home = 0;
407:
408: /* Find a hash for this host */
409: {
410: int hash = 0;
411: char *ptr;
412: for(ptr=host; *ptr; ptr++)
413: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
414: if (!CacheTable) {
2.17 frystyk 415: if ((CacheTable = (HTList* *) HT_CALLOC(HASH_SIZE, sizeof(HTList *))) == NULL)
416: HT_OUTOFMEM("HTDNS_init");
2.1 frystyk 417: }
418: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
419: list = CacheTable[hash];
420: }
421:
422: /* Search the cache */
423: {
424: HTList *cur = list;
425: while ((pres = (HTdns *) HTList_nextObject(cur))) {
426: if (!strcmp(pres->hostname, host)) {
427: if (time(NULL) > pres->ntime + DNSTimeout) {
428: if (PROT_TRACE)
2.18 eric 429: HTTrace("HostByName.. Refreshing cache\n");
2.1 frystyk 430: delete_object(list, pres);
431: pres = NULL;
432: }
433: break;
434: }
435: }
436: }
437: if (pres) {
2.20 ! frystyk 438: if (PROT_TRACE) HTTrace("HostByName.. '%s\' found in cache\n", host);
2.1 frystyk 439:
440: /* See if we have an open connection already */
441: if (pres->sockfd != INVSOC) {
2.13 frystyk 442: if (pres->active > 0) { /* Warm connection in use */
2.2 frystyk 443: net->sockfd = pres->sockfd; /* Assign always */
2.12 frystyk 444: pres->active++;
445: if (PROT_TRACE)
2.18 eric 446: HTTrace("HostByName.. semaphor is %d for soc %d\n",
2.12 frystyk 447: pres->active, pres->sockfd);
2.20 ! frystyk 448: if (HTChannel_mode(pres->sockfd, NULL) == HT_CH_PLAIN) {
2.1 frystyk 449: if (PROT_TRACE)
2.18 eric 450: HTTrace("HostByName.. waiting for socket %d\n",
2.20 ! frystyk 451: pres->sockfd);
2.12 frystyk 452: net->dns = pres;
2.1 frystyk 453: return 0; /* Wait for clear connection */
454: }
455: } else if (pres->expires < time(NULL)) { /* Gotton cold */
456: if (PROT_TRACE)
2.18 eric 457: HTTrace("HostByName.. Closing %d\n", pres->sockfd);
2.1 frystyk 458: NETCLOSE(pres->sockfd);
2.2 frystyk 459: HTEvent_UnRegister(pres->sockfd, (SockOps) FD_ALL);
460: HTDNS_setSocket(pres, INVSOC);
461: } else { /* Warm connection is idle and ready :-) */
462: HTEvent_UnRegister(pres->sockfd, (SockOps) FD_ALL);
2.12 frystyk 463: pres->active = 1;
464: if (PROT_TRACE)
2.18 eric 465: HTTrace("HostByName.. semaphor is %d for soc %d\n",
2.12 frystyk 466: pres->active, pres->sockfd);
2.1 frystyk 467: net->sockfd = pres->sockfd;
468: }
469: }
470:
471: /*
472: ** Find the best home. We still want to do this as we use it as a
473: ** fall back for persistent connections
474: */
475: homes = pres->homes;
476: if (pres->homes > 1) {
477: int cnt = 0;
478: double best_weight = 1e30; /* Pretty good */
479: while (cnt < pres->homes) {
480: if (*(pres->weight+cnt) < best_weight) {
481: best_weight = *(pres->weight+cnt);
482: net->home = cnt;
483: }
484: cnt++;
485: }
486: }
487: net->dns = pres;
2.14 frystyk 488: memcpy((void *) &sin->sin_addr, *(pres->addrlist+net->home),
489: pres->addrlength);
2.1 frystyk 490: } else {
491: struct hostent *hostelement; /* see netdb.h */
492: char *port = strchr(host, ':');
2.10 frystyk 493: HTAlertCallback *cbf = HTAlert_find(HT_PROG_DNS);
2.1 frystyk 494: #ifdef HT_REENTRANT
495: int thd_errno;
496: char buffer[HOSTENT_MAX];
497: struct hostent result; /* For gethostbyname_r */
498: if (port) *port='\0';
2.10 frystyk 499: if (cbf) (*cbf)(net->request, HT_PROG_DNS, HT_MSG_NULL,NULL,host,NULL);
2.1 frystyk 500: hostelement = gethostbyname_r(host, &result, buffer,
501: HOSTENT_MAX, &thd_errno);
502: #else
503: if (port) *port='\0';
2.10 frystyk 504: if (cbf) (*cbf)(net->request, HT_PROG_DNS, HT_MSG_NULL,NULL,host,NULL);
2.1 frystyk 505: hostelement = gethostbyname(host);
506: #endif
507: if (!hostelement) {
508: if (PROT_TRACE)
2.18 eric 509: HTTrace("HostByName.. Can't find node `%s'.\n", host);
2.1 frystyk 510: return -1;
511: }
512: if (port) *port=':'; /* Put ':' back in */
513: net->dns = HTDNS_add(list, hostelement, host, &homes);
2.14 frystyk 514: memcpy((void *) &sin->sin_addr, *hostelement->h_addr_list,
515: hostelement->h_length);
2.1 frystyk 516: }
517: return homes;
518: }
519:
520:
521: /*
522: ** Get host name of the machine on the other end of a socket.
523: **
524: */
2.9 frystyk 525: PUBLIC char * HTGetHostBySock (int soc)
2.1 frystyk 526: {
527: struct sockaddr addr;
528: int len = sizeof(struct sockaddr);
529: struct in_addr *iaddr;
530: char *name = NULL;
531: struct hostent * phost; /* Pointer to host -- See netdb.h */
532: #ifdef HT_REENTRANT
533: int thd_errno;
534: char buffer[HOSTENT_MAX];
535: struct hostent result; /* For gethostbyaddr_r */
536: #endif
537:
538: #ifdef DECNET /* Decnet ain't got no damn name server 8#OO */
539: return NULL;
540: #else
541: if (getpeername(soc, &addr, &len) < 0)
542: return NULL;
543: iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
544:
545: #ifdef HT_REENTRANT
546: phost = gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
547: &result, buffer, HOSTENT_MAX, &thd_errno);
548: #else
549: phost = gethostbyaddr((char *) iaddr, sizeof(struct in_addr), AF_INET);
550: #endif
551: if (!phost) {
552: if (PROT_TRACE)
2.18 eric 553: HTTrace("TCP......... Can't find internet node name for peer!!\n");
2.1 frystyk 554: return NULL;
555: }
556: StrAllocCopy(name, phost->h_name);
2.18 eric 557: if (PROT_TRACE) HTTrace("TCP......... Peer name is `%s'\n", name);
2.1 frystyk 558: return name;
559:
560: #endif /* not DECNET */
561: }
Webmaster