Annotation of libwww/Library/src/HTTCP.c, revision 2.24
1.1 timbl 1: /* Generic Communication Code HTTCP.c
2: ** ==========================
3: **
4: ** This code is in common between client and server sides.
5: **
6: ** 16 Jan 92 TBL Fix strtol() undefined on CMU Mach.
7: ** 25 Jun 92 JFG Added DECNET option through TCP socket emulation.
2.7 duns 8: ** 13 Sep 93 MD Added correct return of vmserrorno for HTInetStatus.
9: ** Added decoding of vms error message for MULTINET.
2.13 frystyk 10: ** 31 May 94 HF Added cache on host id's; now use inet_ntoa() to
11: ** HTInetString and some other fixes. Added HTDoConnect
12: ** and HTDoAccept
1.1 timbl 13: */
14:
2.23 duns 15: #ifndef VMS
2.19 frystyk 16: #include <pwd.h>
2.23 duns 17: #endif /* not VMS */
18:
2.13 frystyk 19: #include "tcp.h" /* Defines SHORT_NAMES if necessary */
2.19 frystyk 20:
2.13 frystyk 21: #include "HTUtils.h"
22: #include "HTAtom.h"
23: #include "HTList.h"
24: #include "HTParse.h"
25: #include "HTAccess.h"
26: #include "HTError.h"
2.24 ! frystyk 27: #include "HTTCP.h" /* Implemented here */
1.1 timbl 28:
2.23 duns 29: #ifdef VMS
30: #include "HTVMSUtils.h"
31: #endif /* VMS */
32:
1.1 timbl 33: #ifdef SHORT_NAMES
34: #define HTInetStatus HTInStat
2.12 luotonen 35: #define HTErrnoString HTErrnoS
1.1 timbl 36: #define HTInetString HTInStri
37: #define HTParseInet HTPaInet
38: #endif
39:
2.8 luotonen 40:
2.11 duns 41: /* VMS stuff */
42: #ifdef VMS
43: #ifndef MULTINET
44: #define FD_SETSIZE 32
45: #else /* Multinet */
46: #define FD_SETSIZE 256
47: #endif /* Multinet */
48: #endif /* VMS */
49:
2.13 frystyk 50: /* Macros and other defines */
2.24 ! frystyk 51: /* x seconds penalty on a multi-homed host if IP-address is down */
! 52: #define TCP_PENALTY 1200
! 53:
! 54: /* x seconds penalty on a multi-homed host if IP-address is timed out */
! 55: #define TCP_DELAY 600
! 56:
! 57: /* Max number of non-blocking accepts */
2.13 frystyk 58: #define MAX_ACCEPT_POLL 30
59: #define FCNTL(r, s, t) fcntl(r, s, t)
60:
2.18 frystyk 61: #ifndef RESOLV_CONF
62: #define RESOLV_CONF "/etc/resolv.conf"
63: #endif
64:
2.13 frystyk 65: /* Globals */
66: PUBLIC unsigned int HTConCacheSize = 512; /* Number of cached servers */
67:
68: /* Type definitions and global variables etc. local to this module */
69:
70: /* This structure is a cache of hosts to whom we have connected over time.
71: The structure contains the necessary parts from hostent. For Internet host
72: hostent->h_addr_list is not an array of char pointers but an array of
73: pointers of type in_addr. */
74: typedef struct _host_info {
75: HTAtom * hostname; /* Official name of host */
76: int hits; /* Total number of hits on this host */
77: int addrlength; /* Length of address in bytes */
2.24 ! frystyk 78: int homes; /* Number of IP addresses on the host */
2.13 frystyk 79: int offset; /* Offset value of active IP address */
80: char ** addrlist; /* List of addresses from name server */
81: float * weight; /* Weight on each address */
82: } host_info;
83:
84: PRIVATE char *hostname = NULL; /* The name of this host */
2.19 frystyk 85: PRIVATE char *mailaddress = NULL; /* Current mail address */
2.13 frystyk 86: PRIVATE HTList *hostcache = NULL; /* List of servers that we have talked to */
87: PRIVATE unsigned int HTCacheSize = 0; /* Current size of cache */
2.11 duns 88:
2.13 frystyk 89: /* ------------------------------------------------------------------------- */
1.1 timbl 90:
91: /* Encode INET status (as in sys/errno.h) inet_status()
92: ** ------------------
93: **
94: ** On entry,
95: ** where gives a description of what caused the error
96: ** global errno gives the error number in the unix way.
97: **
98: ** On return,
99: ** returns a negative status in the unix way.
100: */
101: #ifndef PCNFS
2.7 duns 102: #ifdef VMS
2.10 duns 103: #ifndef __DECC
1.1 timbl 104: extern int uerrno; /* Deposit of error info (as per errno.h) */
2.7 duns 105: extern volatile noshare int socket_errno; /* socket VMS error info
106: (used for translation of vmserrno) */
1.1 timbl 107: extern volatile noshare int vmserrno; /* Deposit of VMS error info */
108: extern volatile noshare int errno; /* noshare to avoid PSECT conflict */
2.10 duns 109: #endif /* not DECC */
2.11 duns 110: #endif /* VMS */
111:
1.1 timbl 112: #ifndef errno
113: extern int errno;
114: #endif /* errno */
115:
116: #ifndef VM
2.7 duns 117: #ifndef VMS
1.1 timbl 118: #ifndef NeXT
119: #ifndef THINK_C
120: extern char *sys_errlist[]; /* see man perror on cernvax */
121: extern int sys_nerr;
122: #endif /* think c */
123: #endif /* NeXT */
2.7 duns 124: #endif /* VMS */
1.1 timbl 125: #endif /* VM */
126:
127: #endif /* PCNFS */
128:
2.12 luotonen 129:
130: /*
131: * Returns the string equivalent of the current errno.
132: */
133: PUBLIC CONST char * HTErrnoString NOARGS
1.1 timbl 134: {
2.11 duns 135: #ifndef VMS
2.7 duns 136:
1.1 timbl 137: #ifdef VM
2.12 luotonen 138: return "(Error number not translated)"; /* What Is the VM equiv? */
1.1 timbl 139: #define ER_NO_TRANS_DONE
140: #endif
2.12 luotonen 141:
142: #if defined(NeXT) || defined(THINK_C)
143: return strerror(errno);
1.1 timbl 144: #define ER_NO_TRANS_DONE
145: #endif
2.12 luotonen 146:
147: #ifndef ER_NO_TRANS_DONE
148: return (errno < sys_nerr ? sys_errlist[errno] : "Unknown error");
1.1 timbl 149: #endif
150:
2.12 luotonen 151: #else /* VMS */
152:
153: static char buf[60];
154: sprintf(buf,"Unix errno = %ld dec, VMS error = %lx hex",errno,vaxc$errno);
155: return buf;
156:
1.1 timbl 157: #endif
2.12 luotonen 158: }
159:
160:
161: /* Report Internet Error
162: ** ---------------------
163: */
164: PUBLIC int HTInetStatus ARGS1(char *, where)
165: {
166: #ifndef VMS
167:
168: CTRACE(tfp,
169: "TCP errno... %d after call to %s() failed.\n............ %s\n",
170: errno, where, HTErrnoString());
1.1 timbl 171:
2.11 duns 172: #else /* VMS */
2.12 luotonen 173:
2.11 duns 174: CTRACE(tfp, " Unix error number = %ld dec\n", errno);
175: CTRACE(tfp, " VMS error = %lx hex\n", vaxc$errno);
2.12 luotonen 176:
2.11 duns 177: #ifdef MULTINET
178: CTRACE(tfp, " Multinet error = %lx hex\n", socket_errno);
2.23 duns 179: CTRACE(tfp, " Error String = %s\n", vms_errno_string());
2.11 duns 180: #endif /* MULTINET */
2.12 luotonen 181:
2.11 duns 182: #endif /* VMS */
2.7 duns 183:
184: #ifdef VMS
2.11 duns 185: /* errno happen to be zero if vaxc$errno <> 0 */
186: return -vaxc$errno;
2.7 duns 187: #else
1.1 timbl 188: return -errno;
2.7 duns 189: #endif
1.1 timbl 190: }
191:
192:
193: /* Parse a cardinal value parse_cardinal()
194: ** ----------------------
195: **
196: ** On entry,
197: ** *pp points to first character to be interpreted, terminated by
198: ** non 0:9 character.
199: ** *pstatus points to status already valid
200: ** maxvalue gives the largest allowable value.
201: **
202: ** On exit,
203: ** *pp points to first unread character
204: ** *pstatus points to status updated iff bad
205: */
206:
207: PUBLIC unsigned int HTCardinal ARGS3
208: (int *, pstatus,
209: char **, pp,
210: unsigned int, max_value)
211: {
212: int n;
213: if ( (**pp<'0') || (**pp>'9')) { /* Null string is error */
214: *pstatus = -3; /* No number where one expeceted */
215: return 0;
216: }
217:
218: n=0;
219: while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0';
220:
221: if (n>max_value) {
222: *pstatus = -4; /* Cardinal outside range */
223: return 0;
224: }
225:
226: return n;
227: }
228:
2.19 frystyk 229: /* ------------------------------------------------------------------------- */
230: /* HOST CACHE MANAGEMENT */
231: /* ------------------------------------------------------------------------- */
1.1 timbl 232:
2.13 frystyk 233: /* HTTCPCacheRemoveElement
234: **
235: ** Remove the element specified from the cache
236: */
237: PRIVATE void HTTCPCacheRemoveElement ARGS1(host_info *, element)
238: {
239: if (!hostcache) {
240: if (TRACE)
241: fprintf(stderr, "HostCache... Remove not done, no cache\n");
242: return;
243: }
244: if (TRACE) fprintf(stderr, "HostCache... Remove `%s' from cache\n",
245: HTAtom_name(element->hostname));
246: HTList_removeObject(hostcache, element);
2.22 frystyk 247: if (*element->addrlist)
248: free(*element->addrlist);
2.13 frystyk 249: if (element->addrlist)
250: free(element->addrlist);
251: if (element->weight)
252: free(element->weight);
253: free(element);
254: }
255:
256:
257: /* HTTCPCacheRemoveHost
258: **
259: ** Removes the corresponding entrance in the cache
260: */
261: PRIVATE void HTTCPCacheRemoveHost ARGS1(char *, host)
262: {
263: HTAtom *hostatom = HTAtom_for(host);
264: HTList *cur = hostcache;
265: host_info *pres = NULL;
266: if (!hostcache) {
267: fprintf(stderr, "HostCache... Remove host not done, no cache\n");
268: return;
269: }
270: while ((pres = (host_info *) HTList_nextObject(cur)) != NULL) {
271: if (pres->hostname == hostatom) {
272: break;
273: }
274: }
275: if (pres)
276: HTTCPCacheRemoveElement(pres);
277: }
278:
279:
280: /* HTTCPCacheGarbage
281: **
282: ** Remove the element with the lowest hit rate
283: */
284: PRIVATE void HTTCPCacheGarbage NOARGS
285: {
286: HTList *cur = hostcache;
287: host_info *pres, *worst_match = NULL;
288: unsigned int worst_hits = 30000; /* Should use UINT_MAX :-( */
289: if (!hostcache) {
290: if (TRACE)
291: fprintf(stderr, "HostCache... Garbage collection not done, no cache\n");
292: return;
293: }
294:
295: /* Seek for worst element */
296: while ((pres = (host_info *) HTList_nextObject(cur))) {
297: if (!worst_match || pres->hits <= worst_hits) {
298: worst_match = pres;
299: worst_hits = pres->hits;
300: }
301: }
302: if (worst_match)
303: HTTCPCacheRemoveElement(worst_match);
304: }
305:
306:
307: /* HTTCPCacheAddElement
308: **
309: ** Add an element to the cache of visited hosts. Note that this function
310: ** requires the system implemented structure hostent and not our own
2.24 ! frystyk 311: ** host_info. The homes variable indicates the number of
! 312: ** IP addresses found.
2.13 frystyk 313: **
314: ** Returns new element if OK NULL if error
315: */
316: PRIVATE host_info *HTTCPCacheAddElement ARGS2(HTAtom *, host,
317: struct hostent *, element)
318: {
319: host_info *new;
2.22 frystyk 320: char *addr;
321: char **index = element->h_addr_list;
322: int cnt = 1;
2.13 frystyk 323: if (!host || !element) {
324: if (TRACE)
325: fprintf(stderr, "HostCache... Bad argument to add to cache\n");
326: return NULL;
327: }
2.22 frystyk 328: while(*index++)
329: cnt++;
2.13 frystyk 330: if ((new = (host_info *) calloc(1, sizeof(host_info))) == NULL ||
2.22 frystyk 331: (new->addrlist = (char **) calloc(1, cnt*sizeof(char*))) == NULL ||
332: (addr = (char *) calloc(1, cnt*element->h_length)) == NULL)
2.18 frystyk 333: outofmem(__FILE__, "HTTCPCacheAddElement");
2.13 frystyk 334: new->hostname = host;
2.22 frystyk 335: index = element->h_addr_list;
336: cnt = 0;
337: while (*index) {
338: *(new->addrlist+cnt) = addr+cnt*element->h_length;
339: memcpy((void *) *(new->addrlist+cnt++), *index++, element->h_length);
340: }
2.24 ! frystyk 341: new->homes = cnt;
! 342: if ((new->weight = (float *) calloc(new->homes, sizeof(float))) == NULL)
! 343: outofmem(__FILE__, "HTTCPCacheAddElement");
! 344:
2.13 frystyk 345: new->addrlength = element->h_length;
346: if (!hostcache)
347: hostcache = HTList_new();
348:
349: if (TRACE) {
2.24 ! frystyk 350: if (new->homes == 1)
! 351: fprintf(stderr, "HostCache... Adding single-homed host `%s'\n",
! 352: HTAtom_name(host));
2.13 frystyk 353: else
2.24 ! frystyk 354: fprintf(stderr, "HostCache... Adding host `%s' with %d homes\n",
! 355: HTAtom_name(host), new->homes);
2.13 frystyk 356: }
357: HTList_addObject(hostcache, (void *) new);
358: HTCacheSize++; /* Update number of elements */
359: return new;
360: }
361:
362:
363: /* HTTCPAddrWeights
364: **
365: ** This function calculates the weights of the different IP addresses
366: ** on a multi homed host. Each weight is calculated as
367: **
368: ** w(n+1) = w(n)*a + (1-a) * deltatime
369: ** a = exp(-1/Neff)
370: ** Neff is the effective number of samples used
371: ** deltatime is time spend on making a connection
372: **
373: ** A short window (low Neff) gives a high sensibility, but this is
374: ** required as we can't expect a lot of data to test on.
375: **
376: */
377: PUBLIC void HTTCPAddrWeights ARGS2(char *, host, time_t, deltatime)
378: {
379: HTAtom *hostatom = HTAtom_for(host);
380: HTList *cur = hostcache;
381: host_info *pres = NULL;
382: if (!hostcache) {
383: fprintf(stderr, "HostCache... Weights not calculated, no cache\n");
384: return;
385: }
2.24 ! frystyk 386: /* Skip any port number from host name */
! 387: if (strchr(host, ':')) {
! 388: char *newhost = NULL;
! 389: char *strptr;
! 390: StrAllocCopy(newhost, host);
! 391: strptr = strchr(newhost, ':');
! 392: *strptr = '\0';
! 393: hostatom = HTAtom_for(newhost);
! 394: free(newhost);
! 395: } else
! 396: hostatom = HTAtom_for(host);
! 397:
2.13 frystyk 398: while ((pres = (host_info *) HTList_nextObject(cur)) != NULL) {
399: if (pres->hostname == hostatom) {
400: break;
401: }
402: }
2.24 ! frystyk 403: if (pres) {
2.13 frystyk 404: int cnt;
2.14 frystyk 405: CONST float passive = 0.9; /* Factor for all passive IP_addrs */
2.13 frystyk 406: #if 0
2.14 frystyk 407: CONST int Neff = 3;
408: CONST float alpha = exp(-1.0/Neff);
2.13 frystyk 409: #else
2.24 ! frystyk 410: CONST float alpha = 0.716531310574; /* Doesn't need the math lib */
2.13 frystyk 411: #endif
2.24 ! frystyk 412: for (cnt=0; cnt<pres->homes; cnt++) {
2.13 frystyk 413: if (cnt == pres->offset) {
414: *(pres->weight+pres->offset) = *(pres->weight+pres->offset)*alpha + (1.0-alpha)*deltatime;
415: } else {
416: *(pres->weight+cnt) = *(pres->weight+cnt) * passive;
417: }
2.24 ! frystyk 418: if (PROT_TRACE)
! 419: fprintf(stderr, "AddrWeights. Home %d has weight %4.2f\n", cnt,
! 420: *(pres->weight+cnt));
2.13 frystyk 421: }
422: } else if (TRACE) {
2.24 ! frystyk 423: fprintf(stderr, "HostCache... Weights not calculated, host not found in cache: `%s\'\n", host);
2.13 frystyk 424: }
425: }
426:
2.19 frystyk 427: /* ------------------------------------------------------------------------- */
428: /* HOST NAME FUNCTIONS */
429: /* ------------------------------------------------------------------------- */
430:
431: #ifndef DECNET /* Function only used below for a trace message */
432:
433: /* Produce a string for an Internet address
434: ** ----------------------------------------
435: **
436: ** On exit,
437: ** returns a pointer to a static string which must be copied if
438: ** it is to be kept.
439: */
440:
441: PUBLIC CONST char * HTInetString ARGS1(SockA *, sin)
442: {
443: #if 0
2.24 ! frystyk 444: /* This dumps core on some Sun systems :-(. The problem is now, that
2.19 frystyk 445: the current implememtation only works for IP-addresses and not in
446: other address spaces. */
447: return inet_ntoa(sin->sin_addr);
448: #endif
449: static char string[16];
450: sprintf(string, "%d.%d.%d.%d",
451: (int)*((unsigned char *)(&sin->sin_addr)+0),
452: (int)*((unsigned char *)(&sin->sin_addr)+1),
453: (int)*((unsigned char *)(&sin->sin_addr)+2),
454: (int)*((unsigned char *)(&sin->sin_addr)+3));
455: return string;
456: }
457: #endif /* Decnet */
458:
2.13 frystyk 459:
460: /* HTGetHostByName
461: **
462: ** Searched first the local cache then asks the DNS for an address of
463: ** the host specified.
464: **
2.24 ! frystyk 465: ** Returns: >0 if OK the number of homes are returned
! 466: ** -1 if error
2.13 frystyk 467: */
2.24 ! frystyk 468: PUBLIC int HTGetHostByName ARGS3(char *, host, SockA *, sin,
! 469: BOOL, use_cur)
2.13 frystyk 470: {
471: HTAtom *hostatom = HTAtom_for(host);
472: host_info *pres = NULL;
473: if (!hostcache)
474: hostcache = HTList_new(); /* First time through */
475: else {
476: HTList *cur = hostcache; /* Search cache */
477: while ((pres = (host_info *) HTList_nextObject(cur)) != NULL) {
478: if (pres->hostname == hostatom) {
479: if (TRACE)
2.18 frystyk 480: fprintf(stderr, "HostByName.. Host `%s\' found in cache.\n", host);
2.13 frystyk 481: break;
482: }
483: }
484: }
485:
486: /* If the host was not found in the cache, then do gethostbyname.
2.24 ! frystyk 487: If we are talking to a multi homed host then take the IP address with
! 488: the lowest weight. If `use_cur'=YES then use current IP-address */
2.13 frystyk 489: if (pres) {
2.24 ! frystyk 490: if (pres->homes > 1 && !use_cur) {
2.13 frystyk 491: int cnt;
492: float best_weight = 1e30; /* Should be FLT_MAX :-( */
2.24 ! frystyk 493: for (cnt=0; cnt<pres->homes; cnt++) {
2.13 frystyk 494: if (*(pres->weight+cnt) < best_weight) {
495: best_weight = *(pres->weight+cnt);
496: pres->offset = cnt;
497: }
498: }
2.24 ! frystyk 499: }
! 500: pres->hits++; /* Update total number of hits on this host */
2.13 frystyk 501: } else { /* Go and ask for it */
502: struct hostent *hostelement; /* see netdb.h */
503: #ifdef MVS /* Outstanding problem with crash in MVS gethostbyname */
504: if (TRACE)
2.24 ! frystyk 505: fprintf(stderr, "HTTCP on MVS gethostbyname(%s)\n", host);
2.13 frystyk 506: #endif
507: if ((hostelement = gethostbyname(host)) == NULL) {
2.18 frystyk 508: if (TRACE) fprintf(stderr, "HostByName.. Can't find internet node name `%s'.\n", host);
2.13 frystyk 509: return -1;
510: }
511:
512: /* Add element to the cache and maybe do garbage collection */
513: if (HTCacheSize >= HTConCacheSize)
514: HTTCPCacheGarbage();
515: if ((pres = HTTCPCacheAddElement(hostatom, hostelement)) == NULL) {
516: return -1;
517: }
518: }
519:
520: /* Update socket structure using the element with the lowest weight. On
521: single homed hosts it means the first value */
522: memcpy(&sin->sin_addr, *(pres->addrlist+pres->offset), pres->addrlength);
2.24 ! frystyk 523: return pres->homes;
2.13 frystyk 524: }
525:
526:
2.19 frystyk 527: /*
528: ** Get host name of the machine on the other end of a socket.
529: **
530: */
531: PUBLIC char * HTGetHostBySock ARGS1(int, soc)
532: {
533: struct sockaddr addr;
534: int len = sizeof(struct sockaddr);
535: struct in_addr *iaddr;
536: struct hostent * phost; /* Pointer to host -- See netdb.h */
537: char *name = NULL;
538:
539: #ifdef DECNET /* Decnet ain't got no damn name server 8#OO */
540: return NULL;
541: #else
542: if (getpeername(soc, &addr, &len) < 0)
543: return NULL;
544:
545: iaddr = &(((struct sockaddr_in *)&addr)->sin_addr);
546: phost=gethostbyaddr((char*)iaddr,
547: sizeof(struct in_addr),
548: AF_INET);
549: if (!phost) {
550: if (TRACE) fprintf(stderr,
551: "TCP......... Can't find internet node name for peer!!\n");
552: return NULL;
553: }
554: StrAllocCopy(name, phost->h_name);
555: if (TRACE) fprintf(stderr, "TCP......... Peer name is `%s'\n", name);
556:
557: return name;
558:
559: #endif /* not DECNET */
560: }
561:
562:
1.1 timbl 563: /* Parse a network node address and port
564: ** -------------------------------------
565: **
566: ** On entry,
567: ** str points to a string with a node name or number,
568: ** with optional trailing colon and port number.
569: ** sin points to the binary internet or decnet address field.
570: **
2.24 ! frystyk 571: ** On exit, -1 If error
! 572: ** >0 If OK the number of homes on the host
1.1 timbl 573: ** *sin is filled in. If no port is specified in str, that
574: ** field is left unchanged in *sin.
2.13 frystyk 575: **
576: ** NOTE: It is assumed that any portnumber and numeric host address
577: ** is given in decimal notation. Separation character is '.'
1.1 timbl 578: */
2.24 ! frystyk 579: PUBLIC int HTParseInet ARGS3(SockA *, sin, CONST char *, str,
! 580: BOOL, use_cur)
1.1 timbl 581: {
2.13 frystyk 582: char *host = NULL;
2.24 ! frystyk 583: int status = 0;
2.13 frystyk 584: StrAllocCopy(host, str); /* Take a copy we can mutilate */
1.1 timbl 585:
2.13 frystyk 586: /* Parse port number if present. */
587: {
588: char *port;
589: if ((port=strchr(host, ':'))) {
590: *port++ = 0; /* Chop off port */
591: if (isdigit(*port)) {
1.1 timbl 592: #ifdef DECNET
2.13 frystyk 593: sin->sdn_objnum = (unsigned char)(strtol(port, (char**)0, 10));
594: #else /* Internet */
595: sin->sin_port = htons(atol(port));
1.1 timbl 596: #endif
2.13 frystyk 597: } else {
2.24 ! frystyk 598: if (PROT_TRACE)
! 599: fprintf(stderr, "ParseInet... No port indicated\n");
! 600: free(host);
! 601: return -1;
2.13 frystyk 602: }
1.1 timbl 603: }
2.13 frystyk 604: }
1.1 timbl 605:
2.13 frystyk 606: /* Parse Internet host */
1.1 timbl 607: #ifdef DECNET
608: /* read Decnet node name. @@ Should know about DECnet addresses, but it's
609: probably worth waiting until the Phase transition from IV to V. */
610:
611: sin->sdn_nam.n_len = min(DN_MAXNAML, strlen(host)); /* <=6 in phase 4 */
612: strncpy (sin->sdn_nam.n_name, host, sin->sdn_nam.n_len + 1);
613:
614: if (TRACE) fprintf(stderr,
615: "DECnet: Parsed address as object number %d on host %.6s...\n",
616: sin->sdn_objnum, host);
617:
2.13 frystyk 618: #else /* Internet */
1.1 timbl 619:
2.13 frystyk 620: /* Parse host number if present */
621: {
622: BOOL numeric = YES;
623: char *strptr = host;
624: while (*strptr) {
625: if (!isdigit(*strptr) && *strptr != '.') {
626: numeric = NO;
627: break;
628: }
629: ++strptr;
630: }
631: if (numeric) {
632: sin->sin_addr.s_addr = inet_addr(host); /* See arpa/inet.h */
633: } else {
2.24 ! frystyk 634: if ((status = HTGetHostByName(host, sin, use_cur)) < 0) {
2.13 frystyk 635: free(host);
636: return -1;
637: }
638: }
639: if (TRACE) {
640: fprintf(stderr, "ParseInet... Parsed address as port %d on %s\n",
641: (int) ntohs(sin->sin_port),
642: HTInetString(sin));
1.1 timbl 643: }
644: }
645: #endif /* Internet vs. Decnet */
2.13 frystyk 646: free(host);
2.24 ! frystyk 647: return status;
1.1 timbl 648: }
649:
650:
2.16 frystyk 651: #ifdef OLD_CODE
1.1 timbl 652: /* Derive the name of the host on which we are
653: ** -------------------------------------------
654: **
655: */
2.8 luotonen 656: PRIVATE void get_host_details NOARGS
1.1 timbl 657:
658: #ifndef MAXHOSTNAMELEN
659: #define MAXHOSTNAMELEN 64 /* Arbitrary limit */
660: #endif
661:
662: {
663: char name[MAXHOSTNAMELEN+1]; /* The name of this host */
664: struct hostent * phost; /* Pointer to host -- See netdb.h */
665: int namelength = sizeof(name);
666:
667: if (hostname) return; /* Already done */
668: gethostname(name, namelength); /* Without domain */
2.18 frystyk 669: if (TRACE) fprintf(stderr, "TCP......... Local host name is %s\n", name);
1.1 timbl 670: StrAllocCopy(hostname, name);
671:
672: #ifndef DECNET /* Decnet ain't got no damn name server 8#OO */
673: phost=gethostbyname(name); /* See netdb.h */
674: if (!phost) {
675: if (TRACE) fprintf(stderr,
2.9 luotonen 676: "TCP......... Can't find my own internet node address for `%s'!!\n",
1.1 timbl 677: name);
678: return; /* Fail! */
679: }
680: StrAllocCopy(hostname, phost->h_name);
2.18 frystyk 681: if (TRACE)
682: fprintf(stderr, "TCP......... Full local host name is %s\n", hostname);
2.8 luotonen 683:
684: #ifdef NEED_HOST_ADDRESS /* no -- needs name server! */
1.1 timbl 685: memcpy(&HTHostAddress, &phost->h_addr, phost->h_length);
686: if (TRACE) fprintf(stderr, " Name server says that I am `%s' = %s\n",
687: hostname, HTInetString(&HTHostAddress));
2.8 luotonen 688: #endif /* NEED_HOST_ADDRESS */
1.1 timbl 689:
690: #endif /* not Decnet */
691: }
2.16 frystyk 692: #endif /* OLD_CODE */
1.1 timbl 693:
2.19 frystyk 694:
2.24 ! frystyk 695: /* HTGetDomainName
! 696: ** Returns the current domain name without the local host name.
! 697: ** The response is pointing to a static area that might be changed
! 698: ** using HTSetHostName(). Returns NULL on error
! 699: */
! 700: PUBLIC CONST char *HTGetDomainName NOARGS
! 701: {
! 702: CONST char *host = HTGetHostName();
! 703: char *domain;
! 704: if (host && *host) {
! 705: if ((domain = strchr(host, '.')) != NULL)
! 706: return ++domain;
! 707: else
! 708: return host;
! 709: } else
! 710: return NULL;
! 711: }
! 712:
! 713:
! 714:
2.19 frystyk 715: /* HTSetHostName
716: ** Sets the current hostname inclusive domain name.
717: ** If this is not set then the default approach is used using
718: ** HTGetHostname().
719: */
720: PUBLIC void HTSetHostName ARGS1(char *, host)
721: {
2.24 ! frystyk 722: if (host && *host) {
! 723: char *strptr;
2.19 frystyk 724: StrAllocCopy(hostname, host);
2.24 ! frystyk 725: strptr = hostname;
! 726: while (*strptr) {
! 727: *strptr = TOLOWER(*strptr);
! 728: strptr++;
! 729: }
! 730: if (*(hostname+strlen(hostname)-1) == '.') /* Remove trailing dot */
! 731: *(hostname+strlen(hostname)-1) = '\0';
! 732: } else {
2.19 frystyk 733: if (TRACE) fprintf(stderr, "SetHostName. Bad argument ignored\n");
734: }
735: }
736:
737:
738: /* HTGetHostName
2.18 frystyk 739: ** Returns the name of this host. It uses the following algoritm:
740: **
741: ** 1) gethostname()
742: ** 2) if the hostname doesn't contain any '.' try to read
743: ** /etc/resolv.conf. If there is no domain line in this file then
744: ** 3) Try getdomainname and do as the man pages say for resolv.conf (sun)
745: ** If there is no domain line in this file, then it is derived
746: ** from the domain name set by the domainname(1) command, usually
747: ** by removing the first component. For example, if the domain-
748: ** name is set to ``foo.podunk.edu'' then the default domain name
749: ** used will be ``pudunk.edu''.
750: **
751: ** This is the same procedure as used by res_init() and sendmail.
2.16 frystyk 752: **
753: ** Return: hostname on success else NULL
754: */
2.19 frystyk 755: PUBLIC CONST char * HTGetHostName NOARGS
1.1 timbl 756: {
2.18 frystyk 757: BOOL got_it = NO;
758: FILE *fp;
2.16 frystyk 759: char name[MAXHOSTNAMELEN+1];
2.18 frystyk 760: if (hostname) { /* If already done */
761: if (*hostname)
762: return hostname;
763: else
764: return NULL; /* We couldn't get the last time */
765: }
2.16 frystyk 766: *(name+MAXHOSTNAMELEN) = '\0';
767: if (gethostname(name, MAXHOSTNAMELEN)) { /* Maybe without domain */
768: if (TRACE)
769: fprintf(stderr, "HostName.... Can't get host name\n");
770: return NULL;
771: }
2.18 frystyk 772: if (TRACE)
2.24 ! frystyk 773: fprintf(stderr, "HostName.... Local host name is `%s\'\n", name);
2.16 frystyk 774: StrAllocCopy(hostname, name);
2.24 ! frystyk 775: {
! 776: char *strptr = strchr(hostname, '.');
! 777: if (strptr != NULL) /* We have it all */
! 778: got_it = YES;
! 779: }
2.16 frystyk 780:
2.23 duns 781: #ifndef VMS
2.18 frystyk 782: /* Now try the resolver config file */
2.24 ! frystyk 783: if (!got_it && (fp = fopen(RESOLV_CONF, "r")) != NULL) {
2.18 frystyk 784: char buffer[80];
785: *(buffer+79) = '\0';
786: while (fgets(buffer, 79, fp)) {
787: if (!strncasecomp(buffer, "domain", 6)) {
788: char *domainstr = buffer+6;
789: char *end;
790: while (*domainstr == ' ' || *domainstr == '\t')
791: domainstr++;
792: end = domainstr;
793: while (*end && !isspace(*end))
794: end++;
795: *end = '\0';
796: if (*domainstr) {
797: StrAllocCat(hostname, ".");
798: StrAllocCat(hostname, domainstr);
799: got_it = YES;
800: break;
801: }
802: }
803: }
804: fclose(fp);
2.16 frystyk 805: }
806:
2.18 frystyk 807: /* If everything else has failed then try getdomainname */
808: if (!got_it) {
809: if (getdomainname(name, MAXHOSTNAMELEN)) {
810: if (TRACE)
811: fprintf(stderr, "HostName.... Can't get domain name\n");
2.24 ! frystyk 812: StrAllocCopy(hostname, "");
2.18 frystyk 813: return NULL;
814: }
815:
816: /* If the host name and the first part of the domain name are different
817: then use the former as it is more exact (I guess) */
818: if (strncmp(name, hostname, (int) strlen(hostname))) {
819: char *domain = strchr(name, '.');
820: if (!domain)
821: domain = name;
822: StrAllocCat(hostname, domain);
823: }
2.16 frystyk 824: }
2.23 duns 825: #endif /* not VMS */
826:
2.24 ! frystyk 827: {
! 828: char *strptr = hostname;
! 829: while (*strptr) {
! 830: *strptr = TOLOWER(*strptr);
! 831: strptr++;
! 832: }
! 833: if (*(hostname+strlen(hostname)-1) == '.') /* Remove trailing dot */
! 834: *(hostname+strlen(hostname)-1) = '\0';
! 835: }
2.18 frystyk 836: if (TRACE)
2.24 ! frystyk 837: fprintf(stderr, "HostName.... Full host name is `%s\'\n", hostname);
2.18 frystyk 838: return hostname;
839:
840: #ifndef DECNET /* Decnet ain't got no damn name server 8#OO */
841: #ifdef OLD_CODE
842: /* Now we try to get information on the domain */
2.16 frystyk 843: {
844: struct hostent *hostelement;
845: if ((hostelement = gethostbyname(hostname)) == NULL) {
846: if (TRACE)
847: fprintf(stderr, "HostName.... Can't find host name on DNS\n");
848: FREE(hostname);
849: return NULL;
850: }
2.17 frystyk 851: StrAllocCopy(hostname, (char *) hostelement->h_name);
2.16 frystyk 852: }
2.18 frystyk 853: #endif /* OLD_CODE */
2.16 frystyk 854: #endif /* not Decnet */
2.13 frystyk 855: }
856:
2.19 frystyk 857:
858: /* HTSetMailAddress
859: ** Sets the current mail address plus host name and domain name.
860: ** If this is not set then the default approach is used using
861: ** HTGetMailAddress().
862: */
863: PUBLIC void HTSetMailAddress ARGS1(char *, address)
864: {
865: if (address && *address)
866: StrAllocCopy(mailaddress, address);
867: else {
868: if (TRACE) fprintf(stderr, "SetMailAddress. Bad argument ignored\n");
869: }
870: }
871:
872:
873: /* HTGetMailAddress
874: **
875: ** Get the mail address of the current user on the current host. The
876: ** domain name used is the one initialized in HTSetHostName or
877: ** HTGetHostName. The login name is determined using (ordered):
878: **
879: ** getlogin
880: ** getpwuid(getuid())
881: **
882: ** The weakness about the last attempt is if the user has multiple
883: ** login names each with the same user ID. If this fails as well then:
884: **
885: ** LOGNAME environment variable
886: ** USER environment variable
887: **
888: ** Returns NULL if error else pointer to static string
889: */
890: PUBLIC CONST char * HTGetMailAddress NOARGS
891: {
892: char *login;
893: CONST char *domain;
894: struct passwd *pw_info;
2.21 frystyk 895: if (mailaddress) {
896: if (*mailaddress)
897: return mailaddress;
898: else
899: return NULL; /* No luck the last time so we wont try again */
900: }
2.23 duns 901:
902: #ifdef VMS
903: if ((login = (char *) cuserid(NULL)) == NULL) {
904: if (TRACE) fprintf(stderr, "MailAddress. cuserid returns NULL\n");
905: }
906:
907: #else /* not VMS */
2.19 frystyk 908: if ((login = (char *) getlogin()) == NULL) {
909: if (TRACE) fprintf(stderr, "MailAddress. getlogin returns NULL\n");
910: if ((pw_info = getpwuid(getuid())) == NULL) {
911: if (TRACE) fprintf(stderr, "MailAddress. getpwid returns NULL\n");
912: if ((login = getenv("LOGNAME")) == NULL) {
913: if (TRACE) fprintf(stderr, "MailAddress. LOGNAME not found\n");
914: if ((login = getenv("USER")) == NULL) {
915: if (TRACE) fprintf(stderr,"MailAddress. USER not found\n");
916: return NULL; /* I GIVE UP */
917: }
918: }
919: } else
920: login = pw_info->pw_name;
921: }
2.23 duns 922: #endif /* not VMS */
923:
2.19 frystyk 924: if (login) {
925: StrAllocCopy(mailaddress, login);
926: StrAllocCat(mailaddress, "@");
927: if ((domain = HTGetHostName()) != NULL)
928: StrAllocCat(mailaddress, domain);
2.21 frystyk 929: else {
930: *mailaddress = '\0';
931: return NULL; /* Domain name not available */
932: }
2.19 frystyk 933: return mailaddress;
934: }
935: return NULL;
936: }
937:
938: /* ------------------------------------------------------------------------- */
939: /* CONNECTION ESTABLISHMENT MANAGEMENT */
940: /* ------------------------------------------------------------------------- */
2.13 frystyk 941:
942: /* HTDoConnect()
943: **
944: ** Note: Any port indication in URL, e.g., as `host:port' overwrites
945: ** the default_port value.
946: **
947: ** Returns 0 if OK, -1 on error
948: */
2.24 ! frystyk 949: PUBLIC int HTDoConnect ARGS5(HTNetInfo *, net, char *, url,
! 950: u_short, default_port, u_long *, addr,
! 951: BOOL, use_cur)
2.13 frystyk 952: {
953: time_t deltatime;
2.24 ! frystyk 954: int hosts;
2.13 frystyk 955: int status;
956: SockA sock_addr; /* SockA is defined in tcp.h */
957: char *p1 = HTParse(url, "", PARSE_HOST);
958: char *at_sign;
959: char *host;
960:
961: /* if theres an @ then use the stuff after it as a hostname */
962: if((at_sign = strchr(p1,'@')) != NULL)
963: host = at_sign+1;
964: else
965: host = p1;
2.24 ! frystyk 966: if (!*host) {
! 967: HTErrorAdd(net->request, ERR_FATAL, NO, HTERR_NO_HOST,
! 968: NULL, 0, "HTDoConnect");
! 969: free(p1);
! 970: return -1;
! 971: } else
! 972: if (TRACE) fprintf(stderr, "HTDoConnect. Looking up `%s\'\n", host);
2.13 frystyk 973:
974: /* Set up defaults */
975: memset((void *) &sock_addr, '\0', sizeof(sock_addr));
976: #ifdef DECNET
977: sock_addr.sdn_family = AF_DECnet; /* Family = DECnet, host order */
978: sock_addr.sdn_objnum = DNP_OBJ; /* Default: http object number */
979: #else /* Internet */
980: sock_addr.sin_family = AF_INET;
981: sock_addr.sin_port = htons(default_port);
982: #endif
983:
2.24 ! frystyk 984: /* If we are trying to connect to a multi-homed host then loop here until
! 985: success or we have tried all IP-addresses */
! 986: do {
! 987: if ((hosts = HTParseInet(&sock_addr, host, use_cur)) < 0) {
! 988: if (TRACE) fprintf(stderr, "HTDoConnect. Can't locate remote host `%s\'\n", host);
! 989: HTErrorAdd(net->request, ERR_FATAL, NO, HTERR_NO_REMOTE_HOST,
! 990: (void *) host, strlen(host), "HTDoConnect");
! 991: goto errorend;
! 992: }
2.13 frystyk 993:
994: #ifdef DECNET
2.24 ! frystyk 995: if ((net->sockfd = socket(AF_DECnet, SOCK_STREAM, 0)) < 0)
2.13 frystyk 996: #else
2.24 ! frystyk 997: if ((net->sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
2.13 frystyk 998: #endif
2.24 ! frystyk 999: {
! 1000: HTErrorSysAdd(net->request, ERR_FATAL, NO, "socket");
! 1001: goto errorend;
! 1002: }
! 1003: if (addr)
! 1004: *addr = ntohl(sock_addr.sin_addr.s_addr);
! 1005: if (PROT_TRACE)
! 1006: fprintf(stderr, "HTDoConnect. Created socket number %d\n",
! 1007: net->sockfd);
! 1008:
! 1009: /* If multi-homed host then start timer on connection */
! 1010: if (hosts > 1)
! 1011: deltatime = time(NULL);
! 1012:
! 1013: status = connect(net->sockfd, (struct sockaddr *) &sock_addr,
! 1014: sizeof(sock_addr));
! 1015: if (hosts > 1) {
! 1016: deltatime = time(NULL) - deltatime;
! 1017: if (status < 0) {
! 1018: HTErrorSysAdd(net->request, ERR_NON_FATAL, NO, "connect");
! 1019: if (errno==ECONNREFUSED || errno==ETIMEDOUT ||
! 1020: errno==ENETUNREACH || errno==EHOSTUNREACH ||
! 1021: errno==EHOSTDOWN)
! 1022: deltatime += TCP_DELAY;
! 1023: else
! 1024: deltatime += TCP_PENALTY;
! 1025: if (NETCLOSE(net->sockfd) < 0)
! 1026: HTErrorSysAdd(net->request, ERR_FATAL, NO, "close");
! 1027: HTTCPAddrWeights(host, deltatime);
! 1028: } else {
! 1029: HTTCPAddrWeights(host, deltatime);
! 1030: break;
! 1031: }
! 1032: } else if (status < 0) {
! 1033: HTErrorSysAdd(net->request, ERR_FATAL, NO, "connect");
! 1034: HTTCPCacheRemoveHost(host);
! 1035: if (NETCLOSE(net->sockfd) < 0)
! 1036: HTErrorSysAdd(net->request, ERR_FATAL, NO, "close");
! 1037: goto errorend;
! 1038: }
! 1039: } while (net->addressCount++ < hosts-1);
2.13 frystyk 1040:
2.24 ! frystyk 1041: if (hosts > 1 && net->addressCount >= hosts) {
! 1042: if (PROT_TRACE) fprintf(stderr, "HTDoConnect. None of the %d addresses on multi-homed host is accessible\n", hosts);
! 1043: goto errorend;
2.13 frystyk 1044: }
1045:
1046: free(p1);
2.24 ! frystyk 1047: net->addressCount = 0;
2.13 frystyk 1048: return status;
2.24 ! frystyk 1049:
! 1050: errorend:
! 1051: free (p1);
! 1052: net->addressCount = 0;
! 1053: net->sockfd = -1;
! 1054: return -1;
2.13 frystyk 1055: }
1056:
1057:
1058: /* HTDoAccept()
1059: **
1060: ** This function makes a non-blocking accept on a port and polls every
1061: ** second until MAX_ACCEPT_POLL or interrupted by user.
1062: **
1063: ** BUGS Interrupted is not yet implemented!!!
1064: **
1065: ** Returns 0 if OK, -1 on error
1066: */
2.15 frystyk 1067: PUBLIC int HTDoAccept ARGS1(HTNetInfo *, net)
2.13 frystyk 1068: {
1069: SockA soc_address; /* SockA is defined in tcp.h */
1070: int status;
1071: int cnt;
1072: int soc_addrlen = sizeof(soc_address);
2.15 frystyk 1073: if (net->sockfd < 0) {
2.13 frystyk 1074: if (TRACE) fprintf(stderr, "HTDoAccept.. Bad socket number\n");
1075: return -1;
1076: }
1077:
1078: /* First make the socket non-blocking */
2.23 duns 1079: #ifdef VMS
1080: #ifdef MULTINET
1081: {
1082: int enable = 1;
1083: status = socket_ioctl(net->sockfd, FIONBIO, &enable);
1084: }
1085: #endif /* MULTINET */
1086: #else /* not VMS */
2.15 frystyk 1087: if((status = FCNTL(net->sockfd, F_GETFL, 0)) != -1) {
2.23 duns 1088: status |= FNDELAY; /* O_NDELAY; */
2.15 frystyk 1089: status = FCNTL(net->sockfd, F_SETFL, status);
2.13 frystyk 1090: }
2.23 duns 1091: #endif /* not VMS */
2.13 frystyk 1092: if (status == -1) {
2.15 frystyk 1093: HTErrorSysAdd(net->request, ERR_FATAL, NO, "fcntl");
2.13 frystyk 1094: return -1;
1095: }
1096:
1097: /* Now poll every sekund */
1098: for(cnt=0; cnt<MAX_ACCEPT_POLL; cnt++) {
2.15 frystyk 1099: if ((status = accept(net->sockfd, (struct sockaddr*) &soc_address,
2.13 frystyk 1100: &soc_addrlen)) >= 0) {
1101: if (TRACE) fprintf(stderr,
1102: "HTDoAccept.. Accepted new socket %d\n",
1103: status);
1104: return status;
1105: } else
2.15 frystyk 1106: HTErrorSysAdd(net->request, ERR_WARNING, YES, "accept");
2.13 frystyk 1107: sleep(1);
1108: }
1109:
1110: /* If nothing has happened */
1111: if (TRACE)
1112: fprintf(stderr, "HTDoAccept.. Timed out, no connection!\n");
2.15 frystyk 1113: HTErrorAdd(net->request, ERR_FATAL, NO, HTERR_TIME_OUT, NULL, 0,
1114: "HTDoAccept");
2.13 frystyk 1115: return -1;
1.1 timbl 1116: }
1117:
Webmaster