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