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