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