Annotation of libwww/Library/src/HTDNS.c, revision 2.16
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;
2.16 ! frystyk 301: if (*(dns->weight+current) < 0.0) *(dns->weight+current) = 0.0;
2.1 frystyk 302: } else {
303: *(dns->weight+cnt) = *(dns->weight+cnt) * passive;
304: }
305: if (PROT_TRACE)
2.8 frystyk 306: TTYPrint(TDEST, "DNS Weigths. Home %d has weight %4.2f\n", cnt,
2.1 frystyk 307: *(dns->weight+cnt));
308: }
309: return YES;
310: }
311: if (PROT_TRACE)
2.8 frystyk 312: TTYPrint(TDEST, "DNS Weigths. Object %p not found'\n", dns);
2.1 frystyk 313: return NO;
314: }
315:
316: /* HTDNS_delete
317: ** ------------
318: ** Remove an element from the cache
319: ** Host must be a host name possibly with a port number
320: */
321: PUBLIC BOOL HTDNS_delete (CONST char * host)
322: {
323: HTList *list;
324: int hash = 0;
325: CONST char *ptr;
326: if (!host || !CacheTable) return NO;
327: for(ptr=host; *ptr; ptr++)
328: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
329: if ((list = CacheTable[hash])) { /* We have the list, find the entry */
330: HTdns *pres;
331: while ((pres = (HTdns *) HTList_nextObject(list))) {
332: if (!strcmp(pres->hostname, host)) {
333: delete_object(CacheTable[hash], pres);
334: break;
335: }
336: }
337: }
338: return YES;
339: }
340:
341: /* HTDNS_deleteAll
342: ** ---------------
343: ** Destroys the cache completely
344: */
345: PUBLIC BOOL HTDNS_deleteAll (void)
346: {
347: int cnt;
348: HTList *cur;
349: if (!CacheTable) return NO;
350: for (cnt=0; cnt<HASH_SIZE; cnt++) {
351: if ((cur = CacheTable[cnt])) {
352: HTdns *pres;
353: while ((pres = (HTdns *) HTList_nextObject(cur)) != NULL)
354: free_object(pres);
355: }
356: HTList_delete(CacheTable[cnt]);
357: CacheTable[cnt] = NULL;
2.2 frystyk 358: }
359: if (PersSock) { /* Remove list of persistent connection */
360: HTList_delete(PersSock);
361: PersSock = NULL;
362: }
363: return YES;
364: }
2.1 frystyk 365:
2.2 frystyk 366: /* HTDNS_closeSocket
367: ** -----------------
368: ** This function is registered when the socket is idle so that we get
369: ** a notification if the socket closes at the other end. At this point
370: ** we can't use the request object as it might have been freed a long
371: ** time ago.
372: */
373: PUBLIC int HTDNS_closeSocket(SOCKET soc, HTRequest * request, SockOps ops)
374: {
375: if (PROT_TRACE)
2.8 frystyk 376: TTYPrint(TDEST, "DNS Close... called with socket %d with ops %x\n",
2.2 frystyk 377: soc, (unsigned) ops);
378: if (ops == FD_READ && PersSock) {
379: HTList *cur = PersSock;
380: HTdns *pres;
381: while ((pres = (HTdns *) HTList_nextObject(cur))) {
382: if (pres->sockfd == soc) {
2.15 frystyk 383: if (PROT_TRACE)TTYPrint(TDEST, "DNS Close... socket %d\n",soc);
2.2 frystyk 384: NETCLOSE(soc);
385: HTDNS_setSocket(pres, INVSOC);
386: break;
387: }
388: }
2.15 frystyk 389: if (!pres)
390: if (PROT_TRACE)TTYPrint(TDEST, "DNS Close... socket NOT FOUND!\n");
2.1 frystyk 391: }
2.2 frystyk 392: HTEvent_UnRegister(soc, (SockOps) FD_ALL);
393: return HT_OK;
2.1 frystyk 394: }
395:
396: /* HTGetHostByName
397: ** ---------------
398: ** Resolve the host name using internal DNS cache. As we want to refer
399: ** a specific host when timing the connection the weight function must
400: ** use the 'current' value as returned.
401: ** Host must be a host name possibly with a port number
402: ** Returns:
403: ** >0 Number of homes
404: ** 0 Wait for persistent socket
405: ** -1 Error
406: */
407: PUBLIC int HTGetHostByName (HTNet *net, char *host)
408: {
409: SockA *sin = &net->sock_addr;
410: int homes = -1;
411: HTList *list; /* Current list in cache */
412: HTdns *pres = NULL;
413: if (!net || !host) {
414: if (PROT_TRACE)
2.8 frystyk 415: TTYPrint(TDEST, "HostByName.. Bad argument\n");
2.1 frystyk 416: return -1;
417: }
418: net->home = 0;
419:
420: /* Find a hash for this host */
421: {
422: int hash = 0;
423: char *ptr;
424: for(ptr=host; *ptr; ptr++)
425: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
426: if (!CacheTable) {
427: CacheTable = (HTList**) calloc(HASH_SIZE, sizeof(HTList *));
428: if (!CacheTable) outofmem(__FILE__, "HTDNS_init");
429: }
430: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
431: list = CacheTable[hash];
432: }
433:
434: /* Search the cache */
435: {
436: HTList *cur = list;
437: while ((pres = (HTdns *) HTList_nextObject(cur))) {
438: if (!strcmp(pres->hostname, host)) {
439: if (time(NULL) > pres->ntime + DNSTimeout) {
440: if (PROT_TRACE)
2.8 frystyk 441: TTYPrint(TDEST, "HostByName.. Refreshing cache\n");
2.1 frystyk 442: delete_object(list, pres);
443: pres = NULL;
444: }
445: break;
446: }
447: }
448: }
449: if (pres) {
450: if (PROT_TRACE)
2.8 frystyk 451: TTYPrint(TDEST, "HostByName.. '%s\' found in cache\n", host);
2.1 frystyk 452:
453: /* See if we have an open connection already */
454: if (pres->sockfd != INVSOC) {
2.13 frystyk 455: if (pres->active > 0) { /* Warm connection in use */
2.2 frystyk 456: net->sockfd = pres->sockfd; /* Assign always */
2.12 frystyk 457: pres->active++;
458: if (PROT_TRACE)
459: TTYPrint(TDEST,"HostByName.. semaphor is %d for soc %d\n",
460: pres->active, pres->sockfd);
461: if (pres->type == HT_TCP_PLAIN) {
2.1 frystyk 462: if (PROT_TRACE)
2.12 frystyk 463: TTYPrint(TDEST, "HostByName.. waiting for socket %d\n",
464: pres->sockfd);
465: net->dns = pres;
2.1 frystyk 466: return 0; /* Wait for clear connection */
467: }
468: } else if (pres->expires < time(NULL)) { /* Gotton cold */
469: if (PROT_TRACE)
2.8 frystyk 470: TTYPrint(TDEST, "HostByName.. Closing %d\n", pres->sockfd);
2.1 frystyk 471: NETCLOSE(pres->sockfd);
2.2 frystyk 472: HTEvent_UnRegister(pres->sockfd, (SockOps) FD_ALL);
473: HTDNS_setSocket(pres, INVSOC);
474: } else { /* Warm connection is idle and ready :-) */
475: HTEvent_UnRegister(pres->sockfd, (SockOps) FD_ALL);
2.12 frystyk 476: pres->active = 1;
477: if (PROT_TRACE)
478: TTYPrint(TDEST,"HostByName.. semaphor is %d for soc %d\n",
479: pres->active, pres->sockfd);
2.1 frystyk 480: net->sockfd = pres->sockfd;
481: }
482: }
483:
484: /*
485: ** Find the best home. We still want to do this as we use it as a
486: ** fall back for persistent connections
487: */
488: homes = pres->homes;
489: if (pres->homes > 1) {
490: int cnt = 0;
491: double best_weight = 1e30; /* Pretty good */
492: while (cnt < pres->homes) {
493: if (*(pres->weight+cnt) < best_weight) {
494: best_weight = *(pres->weight+cnt);
495: net->home = cnt;
496: }
497: cnt++;
498: }
499: }
500: net->dns = pres;
2.14 frystyk 501: memcpy((void *) &sin->sin_addr, *(pres->addrlist+net->home),
502: pres->addrlength);
2.1 frystyk 503: } else {
504: struct hostent *hostelement; /* see netdb.h */
505: char *port = strchr(host, ':');
2.10 frystyk 506: HTAlertCallback *cbf = HTAlert_find(HT_PROG_DNS);
2.1 frystyk 507: #ifdef HT_REENTRANT
508: int thd_errno;
509: char buffer[HOSTENT_MAX];
510: struct hostent result; /* For gethostbyname_r */
511: if (port) *port='\0';
2.10 frystyk 512: if (cbf) (*cbf)(net->request, HT_PROG_DNS, HT_MSG_NULL,NULL,host,NULL);
2.1 frystyk 513: hostelement = gethostbyname_r(host, &result, buffer,
514: HOSTENT_MAX, &thd_errno);
515: #else
516: if (port) *port='\0';
2.10 frystyk 517: if (cbf) (*cbf)(net->request, HT_PROG_DNS, HT_MSG_NULL,NULL,host,NULL);
2.1 frystyk 518: hostelement = gethostbyname(host);
519: #endif
520: if (!hostelement) {
521: if (PROT_TRACE)
2.8 frystyk 522: TTYPrint(TDEST, "HostByName.. Can't find node `%s'.\n", host);
2.1 frystyk 523: return -1;
524: }
525: if (port) *port=':'; /* Put ':' back in */
526: net->dns = HTDNS_add(list, hostelement, host, &homes);
2.14 frystyk 527: memcpy((void *) &sin->sin_addr, *hostelement->h_addr_list,
528: hostelement->h_length);
2.1 frystyk 529: }
530: return homes;
531: }
532:
533:
534: /*
535: ** Get host name of the machine on the other end of a socket.
536: **
537: */
2.9 frystyk 538: PUBLIC char * HTGetHostBySock (int soc)
2.1 frystyk 539: {
540: struct sockaddr addr;
541: int len = sizeof(struct sockaddr);
542: struct in_addr *iaddr;
543: char *name = NULL;
544: struct hostent * phost; /* Pointer to host -- See netdb.h */
545: #ifdef HT_REENTRANT
546: int thd_errno;
547: char buffer[HOSTENT_MAX];
548: struct hostent result; /* For gethostbyaddr_r */
549: #endif
550:
551: #ifdef DECNET /* Decnet ain't got no damn name server 8#OO */
552: return NULL;
553: #else
554: if (getpeername(soc, &addr, &len) < 0)
555: return NULL;
556: iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
557:
558: #ifdef HT_REENTRANT
559: phost = gethostbyaddr_r((char *) iaddr, sizeof(struct in_addr), AF_INET,
560: &result, buffer, HOSTENT_MAX, &thd_errno);
561: #else
562: phost = gethostbyaddr((char *) iaddr, sizeof(struct in_addr), AF_INET);
563: #endif
564: if (!phost) {
565: if (PROT_TRACE)
2.8 frystyk 566: TTYPrint(TDEST, "TCP......... Can't find internet node name for peer!!\n");
2.1 frystyk 567: return NULL;
568: }
569: StrAllocCopy(name, phost->h_name);
2.8 frystyk 570: if (PROT_TRACE) TTYPrint(TDEST, "TCP......... Peer name is `%s'\n", name);
2.1 frystyk 571: return name;
572:
573: #endif /* not DECNET */
574: }
Webmaster