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