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