Annotation of libwww/Library/src/HTTCP.c, revision 2.70
2.31 frystyk 1: /* HTTCP.c
2: ** GENERIC COMMUNICATION CODE
3: **
2.42 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.31 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
1.1 timbl 6: **
7: ** This code is in common between client and server sides.
8: **
9: ** 16 Jan 92 TBL Fix strtol() undefined on CMU Mach.
10: ** 25 Jun 92 JFG Added DECNET option through TCP socket emulation.
2.7 duns 11: ** 13 Sep 93 MD Added correct return of vmserrorno for HTInetStatus.
12: ** Added decoding of vms error message for MULTINET.
2.13 frystyk 13: ** 31 May 94 HF Added cache on host id's; now use inet_ntoa() to
14: ** HTInetString and some other fixes. Added HTDoConnect
15: ** and HTDoAccept
1.1 timbl 16: */
17:
2.36 frystyk 18: /* Library include files */
19: #include "tcp.h"
2.13 frystyk 20: #include "HTUtils.h"
2.36 frystyk 21: #include "HTString.h"
2.13 frystyk 22: #include "HTAtom.h"
23: #include "HTList.h"
24: #include "HTParse.h"
2.50 frystyk 25: #include "HTAlert.h"
2.13 frystyk 26: #include "HTError.h"
2.58 frystyk 27: #include "HTReqMan.h"
2.54 frystyk 28: #include "HTNetMan.h"
29: #include "HTDNS.h"
2.36 frystyk 30: #include "HTTCP.h" /* Implemented here */
2.29 frystyk 31:
2.36 frystyk 32: /* VMS stuff */
33: #ifdef VMS
34: #ifndef MULTINET
35: #define FD_SETSIZE 32
36: #else /* Multinet */
37: #define FD_SETSIZE 256
38: #endif /* Multinet */
39: #endif /* VMS */
40:
2.13 frystyk 41: /* Macros and other defines */
2.24 frystyk 42: /* x seconds penalty on a multi-homed host if IP-address is down */
43: #define TCP_PENALTY 1200
44:
45: /* x seconds penalty on a multi-homed host if IP-address is timed out */
46: #define TCP_DELAY 600
47:
2.36 frystyk 48: #ifndef RESOLV_CONF
49: #define RESOLV_CONF "/etc/resolv.conf"
50: #endif
51:
2.54 frystyk 52: PRIVATE char *hostname = NULL; /* The name of this host */
2.19 frystyk 53: PRIVATE char *mailaddress = NULL; /* Current mail address */
2.11 duns 54:
2.13 frystyk 55: /* ------------------------------------------------------------------------- */
1.1 timbl 56:
2.36 frystyk 57: /*
58: ** Returns the string equivalent to the errno passed in the argument.
2.37 frystyk 59: ** We can't use errno directly as we have both errno and socerrno. The
2.36 frystyk 60: ** result is a static buffer.
1.1 timbl 61: */
2.63 frystyk 62: PUBLIC CONST char * HTErrnoString (int errornumber)
1.1 timbl 63: {
2.41 frystyk 64:
2.40 frystyk 65: #ifdef HAVE_STRERROR
2.36 frystyk 66: return strerror(errornumber);
2.34 roeber 67: #else
2.36 frystyk 68: #ifdef VMS
2.12 luotonen 69: static char buf[60];
2.36 frystyk 70: sprintf(buf,"Unix errno = %ld dec, VMS error = %lx hex", errornumber,
71: vaxc$errno);
2.12 luotonen 72: return buf;
2.41 frystyk 73: #else
2.40 frystyk 74: #ifdef _WINDOWS
2.41 frystyk 75: static char buf[60];
76: sprintf(buf, "Unix errno = %ld dec, WinSock erro = %ld", errornumber, WSAGetLastError());
77: return buf;
2.40 frystyk 78: #else
2.36 frystyk 79: return (errornumber < sys_nerr ? sys_errlist[errornumber]:"Unknown error");
2.40 frystyk 80: #endif /* WINDOWS */
81: #endif /* VMS */
2.51 frystyk 82: #endif /* HAVE_STRERROR */
2.12 luotonen 83: }
84:
2.36 frystyk 85:
86: /* Debug error message
2.12 luotonen 87: */
2.63 frystyk 88: PUBLIC int HTInetStatus (int errnum, char * where)
2.12 luotonen 89: {
2.40 frystyk 90: #if ! (defined(VMS) || defined(WINDOWS))
2.12 luotonen 91:
2.27 frystyk 92: if (PROT_TRACE)
2.61 frystyk 93: TTYPrint(TDEST, "TCP errno... %d after call to %s() failed.\n............ %s\n", errno, where, HTErrnoString(errnum));
1.1 timbl 94:
2.36 frystyk 95: #else /* VMS */
2.40 frystyk 96: #ifdef VMS
2.61 frystyk 97: if (PROT_TRACE) TTYPrint(TDEST, " Unix error number = %ld dec\n", errno);
98: if (PROT_TRACE) TTYPrint(TDEST, " VMS error = %lx hex\n", vaxc$errno);
2.40 frystyk 99: #endif
100: #ifdef WINDOWS
2.61 frystyk 101: if (PROT_TRACE) TTYPrint(TDEST, " Unix error number = %ld dec\n", errno);
102: if (PROT_TRACE) TTYPrint(TDEST, " NT error = %lx hex\n", WSAGetLastError());
2.40 frystyk 103: #endif
2.12 luotonen 104:
2.36 frystyk 105: #ifdef MULTINET
2.61 frystyk 106: if (PROT_TRACE) TTYPrint(TDEST, " Multinet error = %lx hex\n", socket_errno);
107: if (PROT_TRACE) TTYPrint(TDEST, " Error String = %s\n", vms_errno_string());
2.36 frystyk 108: #endif /* MULTINET */
2.12 luotonen 109:
2.36 frystyk 110: #endif /* VMS */
2.7 duns 111:
2.36 frystyk 112: #ifdef VMS
2.11 duns 113: /* errno happen to be zero if vaxc$errno <> 0 */
114: return -vaxc$errno;
2.7 duns 115: #else
1.1 timbl 116: return -errno;
2.7 duns 117: #endif
1.1 timbl 118: }
119:
120:
121: /* Parse a cardinal value parse_cardinal()
122: ** ----------------------
123: **
124: ** On entry,
125: ** *pp points to first character to be interpreted, terminated by
126: ** non 0:9 character.
127: ** *pstatus points to status already valid
128: ** maxvalue gives the largest allowable value.
129: **
130: ** On exit,
131: ** *pp points to first unread character
132: ** *pstatus points to status updated iff bad
133: */
134:
2.63 frystyk 135: PUBLIC unsigned int HTCardinal (int * pstatus,
136: char ** pp,
137: unsigned int max_value)
1.1 timbl 138: {
2.36 frystyk 139: unsigned int n=0;
1.1 timbl 140: if ( (**pp<'0') || (**pp>'9')) { /* Null string is error */
141: *pstatus = -3; /* No number where one expeceted */
142: return 0;
143: }
144: while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0';
145:
146: if (n>max_value) {
147: *pstatus = -4; /* Cardinal outside range */
148: return 0;
149: }
150:
151: return n;
152: }
153:
2.19 frystyk 154: /* ------------------------------------------------------------------------- */
2.27 frystyk 155: /* SIGNAL HANDLING */
156: /* ------------------------------------------------------------------------- */
157:
2.70 ! frystyk 158: #ifdef WWWLIB_SIG
2.27 frystyk 159: /* HTSetSignal
160: ** This function sets up signal handlers. This might not be necessary to
161: ** call if the application has its own handlers.
162: */
163: #include <signal.h>
2.56 frystyk 164: PUBLIC void HTSetSignal (void)
2.27 frystyk 165: {
166: /* On some systems (SYSV) it is necessary to catch the SIGPIPE signal
167: ** when attemting to connect to a remote host where you normally should
168: ** get `connection refused' back
169: */
170: if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
2.61 frystyk 171: if (PROT_TRACE) TTYPrint(TDEST, "HTSignal.... Can't catch SIGPIPE\n");
2.27 frystyk 172: } else {
2.61 frystyk 173: if (PROT_TRACE) TTYPrint(TDEST, "HTSignal.... Ignoring SIGPIPE\n");
2.27 frystyk 174: }
175: }
2.70 ! frystyk 176: #else
! 177: #ifdef WWW_WIN_DLL
! 178: PUBLIC void HTSetSignal (void) {}
! 179: #endif /* WWW_WIN_DLL */
2.27 frystyk 180: #endif /* WWWLIB_SIG */
181:
182: /* ------------------------------------------------------------------------- */
2.19 frystyk 183: /* HOST NAME FUNCTIONS */
184: /* ------------------------------------------------------------------------- */
185:
186: /* Produce a string for an Internet address
187: ** ----------------------------------------
188: **
189: ** On exit,
190: ** returns a pointer to a static string which must be copied if
2.41 frystyk 191: ** it is to be kept.
2.19 frystyk 192: */
2.63 frystyk 193: PUBLIC CONST char * HTInetString (SockA * sin)
2.19 frystyk 194: {
2.53 frystyk 195: #ifndef DECNET /* Function only used below for a trace message */
2.41 frystyk 196: #if 0
197: /* This dumps core on some Sun systems :-(. The problem is now, that
198: the current implememtation only works for IP-addresses and not in
199: other address spaces. */
200: return inet_ntoa(sin->sin_addr);
201: #endif
2.19 frystyk 202: static char string[16];
203: sprintf(string, "%d.%d.%d.%d",
204: (int)*((unsigned char *)(&sin->sin_addr)+0),
205: (int)*((unsigned char *)(&sin->sin_addr)+1),
206: (int)*((unsigned char *)(&sin->sin_addr)+2),
207: (int)*((unsigned char *)(&sin->sin_addr)+3));
208: return string;
2.53 frystyk 209: #else
210: return "";
211: #endif /* Decnet */
2.19 frystyk 212: }
213:
1.1 timbl 214: /* Parse a network node address and port
215: ** -------------------------------------
2.54 frystyk 216: ** It is assumed that any portnumber and numeric host address
217: ** is given in decimal notation. Separation character is '.'
218: ** Any port number given in host name overrides all other values.
219: ** 'host' might be modified.
220: ** Returns:
221: ** >0 Number of homes
222: ** 0 Wait for persistent socket
223: ** -1 Error
1.1 timbl 224: */
2.54 frystyk 225: PRIVATE int HTParseInet (HTNet * net, char * host)
1.1 timbl 226: {
2.54 frystyk 227: int status = 1;
228: SockA *sin = &net->sock_addr;
2.27 frystyk 229:
1.1 timbl 230: #ifdef DECNET
231: /* read Decnet node name. @@ Should know about DECnet addresses, but it's
232: probably worth waiting until the Phase transition from IV to V. */
233:
234: sin->sdn_nam.n_len = min(DN_MAXNAML, strlen(host)); /* <=6 in phase 4 */
235: strncpy (sin->sdn_nam.n_name, host, sin->sdn_nam.n_len + 1);
236:
2.61 frystyk 237: if (PROT_TRACE) TTYPrint(TDEST,
1.1 timbl 238: "DECnet: Parsed address as object number %d on host %.6s...\n",
239: sin->sdn_objnum, host);
2.13 frystyk 240: #else /* Internet */
241: {
242: char *strptr = host;
243: while (*strptr) {
2.54 frystyk 244: if (*strptr == ':') {
245: *strptr = '\0'; /* Don't want port number in numeric host */
2.13 frystyk 246: break;
247: }
2.54 frystyk 248: if (!isdigit(*strptr) && *strptr != '.')
249: break;
250: strptr++;
2.13 frystyk 251: }
2.54 frystyk 252: if (!*strptr) {
2.51 frystyk 253: #ifdef GUSI
254: sin->sin_addr = inet_addr(host); /* See netinet/in.h */
255: #else
2.13 frystyk 256: sin->sin_addr.s_addr = inet_addr(host); /* See arpa/inet.h */
2.51 frystyk 257: #endif
2.54 frystyk 258: } else
259: status = HTGetHostByName(net, host);
260:
2.27 frystyk 261: if (PROT_TRACE) {
2.54 frystyk 262: if (status > 0)
2.61 frystyk 263: TTYPrint(TDEST, "ParseInet... as port %d on %s with %d homes\n",
2.54 frystyk 264: (int) ntohs(sin->sin_port), HTInetString(sin), status);
1.1 timbl 265: }
266: }
2.54 frystyk 267: #endif /* Internet vs. Decnet */
2.24 frystyk 268: return status;
1.1 timbl 269: }
270:
271:
2.24 frystyk 272: /* HTGetDomainName
273: ** Returns the current domain name without the local host name.
274: ** The response is pointing to a static area that might be changed
2.36 frystyk 275: ** using HTSetHostName().
276: **
277: ** Returns NULL on error, "" if domain name is not found
2.24 frystyk 278: */
2.56 frystyk 279: PUBLIC CONST char *HTGetDomainName (void)
2.24 frystyk 280: {
281: CONST char *host = HTGetHostName();
282: char *domain;
283: if (host && *host) {
284: if ((domain = strchr(host, '.')) != NULL)
285: return ++domain;
286: else
2.36 frystyk 287: return "";
2.24 frystyk 288: } else
289: return NULL;
290: }
291:
292:
2.19 frystyk 293: /* HTSetHostName
294: ** Sets the current hostname inclusive domain name.
295: ** If this is not set then the default approach is used using
296: ** HTGetHostname().
297: */
2.63 frystyk 298: PUBLIC void HTSetHostName (char * host)
2.19 frystyk 299: {
2.24 frystyk 300: if (host && *host) {
301: char *strptr;
2.19 frystyk 302: StrAllocCopy(hostname, host);
2.24 frystyk 303: strptr = hostname;
304: while (*strptr) {
305: *strptr = TOLOWER(*strptr);
306: strptr++;
307: }
308: if (*(hostname+strlen(hostname)-1) == '.') /* Remove trailing dot */
309: *(hostname+strlen(hostname)-1) = '\0';
310: } else {
2.61 frystyk 311: if (PROT_TRACE) TTYPrint(TDEST, "SetHostName. Bad argument ignored\n");
2.19 frystyk 312: }
313: }
314:
315:
316: /* HTGetHostName
2.18 frystyk 317: ** Returns the name of this host. It uses the following algoritm:
318: **
319: ** 1) gethostname()
320: ** 2) if the hostname doesn't contain any '.' try to read
321: ** /etc/resolv.conf. If there is no domain line in this file then
322: ** 3) Try getdomainname and do as the man pages say for resolv.conf (sun)
2.49 frystyk 323: ** If there is no domain line in this file, then it is derived
324: ** from the domain name set by the domainname(1) command, usually
325: ** by removing the first component. For example, if the domain-
326: ** name is set to ``foo.podunk.edu'' then the default domain name
327: ** used will be ``pudunk.edu''.
2.18 frystyk 328: **
329: ** This is the same procedure as used by res_init() and sendmail.
2.16 frystyk 330: **
331: ** Return: hostname on success else NULL
332: */
2.56 frystyk 333: PUBLIC CONST char * HTGetHostName (void)
1.1 timbl 334: {
2.18 frystyk 335: BOOL got_it = NO;
336: FILE *fp;
2.16 frystyk 337: char name[MAXHOSTNAMELEN+1];
2.18 frystyk 338: if (hostname) { /* If already done */
339: if (*hostname)
340: return hostname;
341: else
342: return NULL; /* We couldn't get the last time */
343: }
2.16 frystyk 344: *(name+MAXHOSTNAMELEN) = '\0';
2.52 frystyk 345:
346: #ifndef NO_GETHOSTNAME
2.16 frystyk 347: if (gethostname(name, MAXHOSTNAMELEN)) { /* Maybe without domain */
2.27 frystyk 348: if (PROT_TRACE)
2.61 frystyk 349: TTYPrint(TDEST, "HostName.... Can't get host name\n");
2.16 frystyk 350: return NULL;
351: }
2.27 frystyk 352: if (PROT_TRACE)
2.61 frystyk 353: TTYPrint(TDEST, "HostName.... Local host name is `%s\'\n", name);
2.16 frystyk 354: StrAllocCopy(hostname, name);
2.24 frystyk 355: {
356: char *strptr = strchr(hostname, '.');
357: if (strptr != NULL) /* We have it all */
358: got_it = YES;
359: }
2.16 frystyk 360:
2.52 frystyk 361: #ifndef NO_RESOLV_CONF
2.18 frystyk 362: /* Now try the resolver config file */
2.24 frystyk 363: if (!got_it && (fp = fopen(RESOLV_CONF, "r")) != NULL) {
2.18 frystyk 364: char buffer[80];
365: *(buffer+79) = '\0';
366: while (fgets(buffer, 79, fp)) {
367: if (!strncasecomp(buffer, "domain", 6)) {
368: char *domainstr = buffer+6;
369: char *end;
370: while (*domainstr == ' ' || *domainstr == '\t')
371: domainstr++;
372: end = domainstr;
373: while (*end && !isspace(*end))
374: end++;
375: *end = '\0';
376: if (*domainstr) {
377: StrAllocCat(hostname, ".");
378: StrAllocCat(hostname, domainstr);
379: got_it = YES;
380: break;
381: }
382: }
383: }
384: fclose(fp);
2.16 frystyk 385: }
2.52 frystyk 386: #endif /* NO_RESOLV_CONF */
2.16 frystyk 387:
2.52 frystyk 388: #ifndef NO_GETDOMAINNAME
2.18 frystyk 389: /* If everything else has failed then try getdomainname */
390: if (!got_it) {
391: if (getdomainname(name, MAXHOSTNAMELEN)) {
2.27 frystyk 392: if (PROT_TRACE)
2.61 frystyk 393: TTYPrint(TDEST, "HostName.... Can't get domain name\n");
2.24 frystyk 394: StrAllocCopy(hostname, "");
2.18 frystyk 395: return NULL;
396: }
397:
398: /* If the host name and the first part of the domain name are different
399: then use the former as it is more exact (I guess) */
400: if (strncmp(name, hostname, (int) strlen(hostname))) {
401: char *domain = strchr(name, '.');
2.50 frystyk 402: if (!domain)
403: domain = name;
404: StrAllocCat(hostname, domain);
2.18 frystyk 405: }
2.16 frystyk 406: }
2.36 frystyk 407: #endif /* NO_GETDOMAINNAME */
2.23 duns 408:
2.24 frystyk 409: {
410: char *strptr = hostname;
411: while (*strptr) {
412: *strptr = TOLOWER(*strptr);
413: strptr++;
414: }
415: if (*(hostname+strlen(hostname)-1) == '.') /* Remove trailing dot */
416: *(hostname+strlen(hostname)-1) = '\0';
417: }
2.52 frystyk 418: #endif /* NO_GETHOSTNAME */
419:
2.27 frystyk 420: if (PROT_TRACE)
2.61 frystyk 421: TTYPrint(TDEST, "HostName.... Full host name is `%s\'\n", hostname);
2.18 frystyk 422: return hostname;
2.13 frystyk 423: }
424:
2.19 frystyk 425:
2.32 frystyk 426: /*
427: ** Free the host name. Called from HTLibTerminate
428: */
2.56 frystyk 429: PUBLIC void HTFreeHostName (void)
2.32 frystyk 430: {
431: FREE(hostname);
432: }
433:
434:
2.19 frystyk 435: /* HTSetMailAddress
436: ** Sets the current mail address plus host name and domain name.
437: ** If this is not set then the default approach is used using
2.27 frystyk 438: ** HTGetMailAddress(). If the argument is NULL or "" then HTGetMailAddress
439: ** returns NULL on a succeding request.
2.19 frystyk 440: */
2.63 frystyk 441: PUBLIC void HTSetMailAddress (char * address)
2.19 frystyk 442: {
2.27 frystyk 443: if (!address || !*address)
444: StrAllocCopy(mailaddress, "");
445: else
2.19 frystyk 446: StrAllocCopy(mailaddress, address);
2.59 frystyk 447: if (WWWTRACE)
2.61 frystyk 448: TTYPrint(TDEST, "SetMailAdr.. Set mail address to `%s\'\n",
2.27 frystyk 449: mailaddress);
2.19 frystyk 450: }
451:
452:
453: /* HTGetMailAddress
454: **
455: ** Get the mail address of the current user on the current host. The
456: ** domain name used is the one initialized in HTSetHostName or
457: ** HTGetHostName. The login name is determined using (ordered):
458: **
459: ** getlogin
460: ** getpwuid(getuid())
461: **
462: ** The weakness about the last attempt is if the user has multiple
463: ** login names each with the same user ID. If this fails as well then:
464: **
465: ** LOGNAME environment variable
466: ** USER environment variable
467: **
468: ** Returns NULL if error else pointer to static string
469: */
2.56 frystyk 470: PUBLIC CONST char * HTGetMailAddress (void)
2.19 frystyk 471: {
2.47 frystyk 472: #ifdef HT_REENTRANT
473: char name[LOGNAME_MAX+1]; /* For getlogin_r */
474: #endif
2.19 frystyk 475: char *login;
476: CONST char *domain;
477: struct passwd *pw_info;
2.21 frystyk 478: if (mailaddress) {
479: if (*mailaddress)
480: return mailaddress;
481: else
482: return NULL; /* No luck the last time so we wont try again */
483: }
2.23 duns 484:
2.36 frystyk 485: #ifdef VMS
2.23 duns 486: if ((login = (char *) cuserid(NULL)) == NULL) {
2.61 frystyk 487: if (PROT_TRACE) TTYPrint(TDEST, "MailAddress. cuserid returns NULL\n");
2.36 frystyk 488: }
489: #else
2.40 frystyk 490: #ifdef WIN32
2.41 frystyk 491: login = getenv("USERNAME") ;
2.40 frystyk 492: #else
2.41 frystyk 493: #ifdef _WINDOWS
2.36 frystyk 494: login = "PCUSER"; /* @@@ COULD BE BETTER @@@ */
2.51 frystyk 495: #else
496: #ifdef GUSI
497: if ((login = getenv("LOGNAME")) == NULL)
498: login = "MACUSER";
2.36 frystyk 499: #else /* Unix like... */
2.47 frystyk 500: #ifdef HT_REENTRANT
501: if ((login = (char *) getlogin_r(name, LOGNAME_MAX)) == NULL) {
502: #else
2.34 roeber 503: if ((login = (char *) getlogin()) == NULL) {
2.47 frystyk 504: #endif
2.36 frystyk 505: if (PROT_TRACE)
2.61 frystyk 506: TTYPrint(TDEST, "MailAddress. getlogin returns NULL\n");
2.36 frystyk 507: if ((pw_info = getpwuid(getuid())) == NULL) {
508: if (PROT_TRACE)
2.61 frystyk 509: TTYPrint(TDEST, "MailAddress. getpwid returns NULL\n");
2.36 frystyk 510: if ((login = getenv("LOGNAME")) == NULL) {
511: if (PROT_TRACE)
2.61 frystyk 512: TTYPrint(TDEST, "MailAddress. LOGNAME not found\n");
2.36 frystyk 513: if ((login = getenv("USER")) == NULL) {
514: if (PROT_TRACE)
2.61 frystyk 515: TTYPrint(TDEST,"MailAddress. USER not found\n");
2.36 frystyk 516: return NULL; /* I GIVE UP */
517: }
518: }
519: } else
520: login = pw_info->pw_name;
521: }
2.51 frystyk 522: #endif /* GUSI */
523: #endif /* _WINDOWS */
524: #endif /* WIN32 */
525: #endif /* VMS */
2.34 roeber 526:
2.19 frystyk 527: if (login) {
528: StrAllocCopy(mailaddress, login);
529: StrAllocCat(mailaddress, "@");
530: if ((domain = HTGetHostName()) != NULL)
531: StrAllocCat(mailaddress, domain);
2.21 frystyk 532: else {
533: *mailaddress = '\0';
534: return NULL; /* Domain name not available */
535: }
2.19 frystyk 536: return mailaddress;
537: }
538: return NULL;
539: }
2.32 frystyk 540:
541:
542: /*
543: ** Free the mail address. Called from HTLibTerminate
544: */
2.56 frystyk 545: PUBLIC void HTFreeMailAddress (void)
2.32 frystyk 546: {
547: FREE(mailaddress);
548: }
549:
2.19 frystyk 550:
551: /* ------------------------------------------------------------------------- */
552: /* CONNECTION ESTABLISHMENT MANAGEMENT */
553: /* ------------------------------------------------------------------------- */
2.13 frystyk 554:
555: /* HTDoConnect()
556: **
557: ** Note: Any port indication in URL, e.g., as `host:port' overwrites
2.54 frystyk 558: ** the default port value.
2.13 frystyk 559: **
2.40 frystyk 560: ** returns HT_ERROR Error has occured or interrupted
561: ** HT_OK if connected
562: ** HT_WOULD_BLOCK if operation would have blocked
2.13 frystyk 563: */
2.54 frystyk 564: PUBLIC int HTDoConnect (HTNet * net, char * url, u_short default_port)
2.13 frystyk 565: {
566: int status;
2.65 frystyk 567: HTRequest * request = net->request;
2.55 frystyk 568: char *fullhost = HTParse(url, "", PARSE_HOST);
2.13 frystyk 569: char *at_sign;
570: char *host;
571:
2.54 frystyk 572: /* if there's an @ then use the stuff after it as a hostname */
2.55 frystyk 573: if ((at_sign = strchr(fullhost, '@')) != NULL)
2.13 frystyk 574: host = at_sign+1;
575: else
2.55 frystyk 576: host = fullhost;
2.24 frystyk 577: if (!*host) {
2.65 frystyk 578: HTRequest_addError(request, ERR_FATAL, NO, HTERR_NO_HOST,
2.24 frystyk 579: NULL, 0, "HTDoConnect");
2.55 frystyk 580: free(fullhost);
2.40 frystyk 581: return HT_ERROR;
2.27 frystyk 582: }
2.13 frystyk 583:
2.55 frystyk 584: /* Jump into the state machine */
585: while (1) {
586: switch (net->tcpstate) {
587: case TCP_BEGIN:
588: {
589: char *port = strchr(host, ':');
590: SockA *sin = &net->sock_addr;
591: memset((void *) sin, '\0', sizeof(SockA));
592: if (port++ && isdigit(*port)) {
2.54 frystyk 593: #ifdef DECNET
2.55 frystyk 594: sin->sdn_family = AF_DECnet;
595: sin->sdn_objnum=(unsigned char)(strtol(port,(char**)0,10));
596: #else
597: sin->sin_family = AF_INET;
598: sin->sin_port = htons(atol(port));
2.54 frystyk 599: #endif
2.55 frystyk 600: } else {
2.13 frystyk 601: #ifdef DECNET
2.55 frystyk 602: sin->sdn_family = AF_DECnet;
603: net->sock_addr.sdn_objnum = DNP_OBJ;
2.13 frystyk 604: #else /* Internet */
2.55 frystyk 605: sin->sin_family = AF_INET;
606: sin->sin_port = htons(default_port);
2.13 frystyk 607: #endif
2.55 frystyk 608: }
609: }
2.44 frystyk 610: if (PROT_TRACE)
2.61 frystyk 611: TTYPrint(TDEST, "HTDoConnect. Looking up `%s\'\n", host);
2.55 frystyk 612: net->tcpstate = TCP_DNS;
613: break;
614:
615: case TCP_DNS:
616: if ((status = HTParseInet(net, host)) < 0) {
2.27 frystyk 617: if (PROT_TRACE)
2.61 frystyk 618: TTYPrint(TDEST, "HTDoConnect. Can't locate `%s\'\n", host);
2.65 frystyk 619: HTRequest_addError(request, ERR_FATAL, NO,HTERR_NO_REMOTE_HOST,
2.27 frystyk 620: (void *) host, strlen(host), "HTDoConnect");
2.55 frystyk 621: net->tcpstate = TCP_ERROR;
2.27 frystyk 622: break;
2.55 frystyk 623: }
624:
625: /*
626: ** Wait for a persistent connection. When we return, we check
627: ** that the socket hasn't been closed in the meantime
628: */
629: if (!status) {
2.56 frystyk 630: net->tcpstate = TCP_NEED_CONNECT;
2.55 frystyk 631: free(fullhost);
632: HTNet_wait(net);
2.54 frystyk 633: return HT_PERSISTENT;
2.55 frystyk 634: }
2.54 frystyk 635:
2.55 frystyk 636: if (!net->retry && status > 1) /* If multiple homes */
637: net->retry = status;
638: if (net->sockfd != INVSOC) { /* Reusing socket */
2.54 frystyk 639: if (PROT_TRACE)
2.61 frystyk 640: TTYPrint(TDEST, "HTDoConnect. REUSING SOCKET %d\n",
2.55 frystyk 641: net->sockfd);
642: net->tcpstate = TCP_CONNECTED;
643: } else
644: net->tcpstate = TCP_NEED_SOCKET;
645: break;
646:
647: case TCP_NEED_SOCKET:
2.27 frystyk 648: #ifdef DECNET
2.36 frystyk 649: if ((net->sockfd=socket(AF_DECnet, SOCK_STREAM, 0))==INVSOC)
2.27 frystyk 650: #else
2.55 frystyk 651: if ((net->sockfd=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP))==INVSOC)
2.27 frystyk 652: #endif
653: {
2.65 frystyk 654: HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "socket");
2.55 frystyk 655: net->tcpstate = TCP_ERROR;
2.27 frystyk 656: break;
657: }
658: if (PROT_TRACE)
2.61 frystyk 659: TTYPrint(TDEST, "HTDoConnect. Created socket %d\n",net->sockfd);
2.27 frystyk 660:
2.28 frystyk 661: /* If non-blocking protocol then change socket status
2.50 frystyk 662: ** I use FCNTL so that I can ask the status before I set it.
663: ** See W. Richard Stevens (Advan. Prog. in UNIX environment, p.364)
664: ** Be CAREFULL with the old `O_NDELAY' - it will not work as read()
665: ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD and
666: ** does NOT work on SVR4 systems. O_NONBLOCK is POSIX.
667: */
2.53 frystyk 668: if (!net->preemtive) {
2.64 frystyk 669: #ifdef _WINDOWS
2.41 frystyk 670: { /* begin windows scope */
2.65 frystyk 671: HTRequest * rq = request;
2.41 frystyk 672: long levents = FD_READ | FD_WRITE | FD_ACCEPT |
673: FD_CONNECT | FD_CLOSE ;
674: int rv = 0 ;
675:
2.64 frystyk 676: #ifdef WWW_WIN_ASYNC
2.41 frystyk 677: /* N.B WSAAsyncSelect() turns on non-blocking I/O */
2.64 frystyk 678: rv = WSAAsyncSelect( net->sockfd, rq->hwnd,
679: rq->winMsg, levents);
680: if (rv == SOCKET_ERROR) {
681: status = -1 ;
682: if (PROT_TRACE)
683: TTYPrint(TDEST, "HTDoConnect. WSAAsyncSelect() fails: %d\n",
684: WSAGetLastError());
685: } /* error returns */
686: #else
687: int enable = 1;
688: status = IOCTL(net->sockfd, FIONBIO, &enable);
689: #endif
2.41 frystyk 690: } /* end scope */
691: #else
692: #if defined(VMS)
2.36 frystyk 693: {
694: int enable = 1;
695: status = IOCTL(net->sockfd, FIONBIO, &enable);
696: }
697: #else
2.27 frystyk 698: if((status = FCNTL(net->sockfd, F_GETFL, 0)) != -1) {
2.41 frystyk 699: status |= O_NONBLOCK; /* POSIX */
2.27 frystyk 700: status = FCNTL(net->sockfd, F_SETFL, status);
701: }
2.41 frystyk 702: #endif /* VMS */
703: #endif /* WINDOW */
2.43 frystyk 704: if (PROT_TRACE) {
705: if (status == -1)
2.61 frystyk 706: TTYPrint(TDEST, "HTDoConnect. Only blocking works\n");
2.43 frystyk 707: else
2.61 frystyk 708: TTYPrint(TDEST, "HTDoConnect. Non-blocking socket\n");
2.43 frystyk 709: }
2.57 frystyk 710: } else if (PROT_TRACE)
2.61 frystyk 711: TTYPrint(TDEST, "HTDoConnect. Blocking socket\n");
2.56 frystyk 712:
2.27 frystyk 713: /* If multi-homed host then start timer on connection */
2.55 frystyk 714: if (net->retry) net->connecttime = time(NULL);
2.65 frystyk 715:
716: /* Progress */
717: {
718: HTAlertCallback *cbf = HTAlert_find(HT_PROG_CONNECT);
719: if (cbf)
720: (*cbf)(request,HT_PROG_CONNECT,HT_MSG_NULL,NULL,NULL,NULL);
721: }
2.56 frystyk 722: net->tcpstate = TCP_NEED_CONNECT;
2.55 frystyk 723: break;
2.54 frystyk 724:
2.56 frystyk 725: case TCP_NEED_CONNECT:
2.55 frystyk 726: status = connect(net->sockfd, (struct sockaddr *) &net->sock_addr,
727: sizeof(net->sock_addr));
728: /*
729: * According to the Sun man page for connect:
730: * EINPROGRESS The socket is non-blocking and the con-
731: * nection cannot be completed immediately.
732: * It is possible to select(2) for comple-
733: * tion by selecting the socket for writ-
734: * ing.
735: * According to the Motorola SVR4 man page for connect:
736: * EAGAIN The socket is non-blocking and the con-
737: * nection cannot be completed immediately.
738: * It is possible to select for completion
739: * by selecting the socket for writing.
740: * However, this is only possible if the
741: * socket STREAMS module is the topmost
742: * module on the protocol stack with a
743: * write service procedure. This will be
744: * the normal case.
745: */
746: #ifdef _WINSOCKAPI_
747: if (status == SOCKET_ERROR)
748: #else
749: if (status < 0)
750: #endif
751: {
2.27 frystyk 752: #ifdef EAGAIN
2.55 frystyk 753: if (socerrno==EINPROGRESS || socerrno==EAGAIN)
2.41 frystyk 754: #else
2.55 frystyk 755: #ifdef _WINSOCKAPI_
756: if (socerrno==WSAEWOULDBLOCK)
2.40 frystyk 757: #else
2.55 frystyk 758: if (socerrno==EINPROGRESS)
759: #endif /* _WINSOCKAPI_ */
2.27 frystyk 760: #endif /* EAGAIN */
2.55 frystyk 761: {
762: if (PROT_TRACE)
2.65 frystyk 763: TTYPrint(TDEST,"HTDoConnect. WOULD BLOCK `%s'\n",host);
764: HTEvent_Register(net->sockfd, request, (SockOps)FD_CONNECT,
2.55 frystyk 765: net->cbf, net->priority);
766: free(fullhost);
767: return HT_WOULD_BLOCK;
768: }
769: if (socerrno == EISCONN) {
770: net->tcpstate = TCP_CONNECTED;
771: break;
772: }
2.58 frystyk 773: #ifdef _WINSOCKAPI_
2.59 frystyk 774: if (socerrno == WSAEBADF) /* We lost the socket */
2.58 frystyk 775: #else
776: if (socerrno == EBADF) /* We lost the socket */
777: #endif
778: {
2.55 frystyk 779: net->tcpstate = TCP_NEED_SOCKET;
780: break;
2.27 frystyk 781: }
2.55 frystyk 782: if (net->retry) {
783: net->connecttime -= time(NULL);
2.54 frystyk 784: /* Added EINVAL `invalid argument' as this is what I
785: get back from a non-blocking connect where I should
786: get `connection refused' on BSD. SVR4 gives SIG_PIPE */
2.58 frystyk 787: #if defined(__srv4__) || defined (_WINSOCKAPI_)
2.55 frystyk 788: if (socerrno==ECONNREFUSED || socerrno==ETIMEDOUT ||
789: socerrno==ENETUNREACH || socerrno==EHOSTUNREACH ||
790: socerrno==EHOSTDOWN)
791: #else
2.54 frystyk 792: if (socerrno==ECONNREFUSED || socerrno==ETIMEDOUT ||
793: socerrno==ENETUNREACH || socerrno==EHOSTUNREACH ||
794: socerrno==EHOSTDOWN || socerrno==EINVAL)
2.35 roeber 795: #endif
2.54 frystyk 796: net->connecttime += TCP_DELAY;
797: else
798: net->connecttime += TCP_PENALTY;
2.55 frystyk 799: HTDNS_updateWeigths(net->dns, net->home, net->connecttime);
800: }
801: net->tcpstate = TCP_ERROR;
802: } else
803: net->tcpstate = TCP_CONNECTED;
804: break;
805:
806: case TCP_CONNECTED:
807: HTEvent_UnRegister(net->sockfd, (SockOps) FD_CONNECT);
808: if (net->retry) {
809: net->connecttime -= time(NULL);
2.54 frystyk 810: HTDNS_updateWeigths(net->dns, net->home, net->connecttime);
2.27 frystyk 811: }
2.55 frystyk 812: net->retry = 0;
813: free(fullhost);
814: net->tcpstate = TCP_BEGIN;
815: return HT_OK;
816: break;
817:
2.56 frystyk 818: case TCP_NEED_BIND:
819: case TCP_NEED_LISTEN:
2.55 frystyk 820: case TCP_ERROR:
2.61 frystyk 821: if (PROT_TRACE) TTYPrint(TDEST, "HTDoConnect. Connect failed\n");
2.55 frystyk 822: if (net->sockfd != INVSOC) {
2.53 frystyk 823: HTEvent_UnRegister(net->sockfd, (SockOps) FD_ALL);
2.55 frystyk 824: NETCLOSE(net->sockfd);
825: net->sockfd = INVSOC;
826: if (HTDNS_socket(net->dns) != INVSOC) { /* Inherited socket */
827: HTDNS_setSocket(net->dns, INVSOC);
828: net->tcpstate = TCP_NEED_SOCKET;
829: break;
830: }
831: }
832:
833: /* Do we have more homes to try? */
834: if (--net->retry > 0) {
2.65 frystyk 835: HTRequest_addSystemError(request, ERR_NON_FATAL, socerrno, NO,
2.55 frystyk 836: "connect");
837: net->tcpstate = TCP_DNS;
2.24 frystyk 838: break;
839: }
2.65 frystyk 840: HTRequest_addSystemError(request, ERR_FATAL,socerrno,NO,"connect");
2.55 frystyk 841: HTDNS_delete(host);
2.54 frystyk 842: net->retry = 0;
2.55 frystyk 843: free (fullhost);
844: net->tcpstate = TCP_BEGIN;
845: return HT_ERROR;
846: break;
2.24 frystyk 847: }
2.55 frystyk 848: }
2.13 frystyk 849: }
850:
2.56 frystyk 851: /* HTDoAccept()
852: ** ------------
853: ** This function makes a non-blocking accept which will turn up as ready
854: ** read in the select.
855: ** Returns
856: ** HT_ERROR Error has occured or interrupted
857: ** HT_OK if connected
858: ** HT_WOULD_BLOCK if operation would have blocked
2.13 frystyk 859: */
2.67 frystyk 860: PUBLIC int HTDoAccept (HTNet * net, SOCKET * newfd)
2.13 frystyk 861: {
862: int status;
2.56 frystyk 863: int size = sizeof(net->sock_addr);
2.65 frystyk 864: HTRequest *request = net->request;
2.36 frystyk 865: if (net->sockfd==INVSOC) {
2.61 frystyk 866: if (PROT_TRACE) TTYPrint(TDEST, "HTDoAccept.. Invalid socket\n");
2.56 frystyk 867: return HT_ERROR;
2.13 frystyk 868: }
2.65 frystyk 869:
870: /* Progress report */
871: {
872: HTAlertCallback *cbf = HTAlert_find(HT_PROG_ACCEPT);
873: if (cbf) (*cbf)(request, HT_PROG_ACCEPT, HT_MSG_NULL,NULL, NULL, NULL);
874: }
2.56 frystyk 875: status = accept(net->sockfd, (struct sockaddr *) &net->sock_addr, &size);
876: #ifdef _WINSOCKAPI_
877: if (status == SOCKET_ERROR)
878: #else
879: if (status < 0)
880: #endif
2.23 duns 881: {
2.56 frystyk 882: #ifdef EAGAIN
883: if (socerrno==EINPROGRESS || socerrno==EAGAIN)
884: #else
885: #ifdef _WINSOCKAPI_
886: if (socerrno==WSAEWOULDBLOCK)
887: #else
888: if (socerrno==EINPROGRESS)
889: #endif /* _WINSOCKAPI_ */
890: #endif /* EAGAIN */
891: {
892: if (PROT_TRACE)
2.61 frystyk 893: TTYPrint(TDEST,"HTDoAccept.. WOULD BLOCK %d\n", net->sockfd);
2.65 frystyk 894: HTEvent_Register(net->sockfd, request, (SockOps) FD_ACCEPT,
2.56 frystyk 895: net->cbf, net->priority);
896: return HT_WOULD_BLOCK;
897: }
2.65 frystyk 898: HTRequest_addSystemError(request, ERR_WARN, socerrno, YES, "accept");
2.61 frystyk 899: if (PROT_TRACE) TTYPrint(TDEST, "HTDoAccept.. Accept failed\n");
2.56 frystyk 900: if (HTDNS_socket(net->dns) != INVSOC) { /* Inherited socket */
901: HTDNS_setSocket(net->dns, INVSOC);
902: }
903: return HT_ERROR;
2.23 duns 904: }
2.56 frystyk 905: *newfd = status;
906: HTEvent_UnRegister(status, (SockOps) FD_ACCEPT);
907: return HT_OK;
908: }
909:
910:
911: /* HTDoListen
912: ** ----------
913: ** Listens on the specified port. 0 means that we chose it here
914: ** If master==INVSOC then we listen on all local interfaces (using a
915: ** wildcard). If !INVSOC then use this as the local interface
916: ** returns HT_ERROR Error has occured or interrupted
917: ** HT_OK if connected
918: */
2.67 frystyk 919: PUBLIC int HTDoListen (HTNet * net, u_short port, SOCKET master, int backlog)
2.56 frystyk 920: {
921: int status;
922:
923: /* Jump into the state machine */
924: while (1) {
925: switch (net->tcpstate) {
926: case TCP_BEGIN:
927: {
928: SockA *sin = &net->sock_addr;
929: memset((void *) sin, '\0', sizeof(SockA));
930: #ifdef DECNET
931: sin->sdn_family = AF_DECnet;
932: sin->sdn_objnum = port;
2.36 frystyk 933: #else
2.56 frystyk 934: sin->sin_family = AF_INET;
935: if (master != INVSOC) {
936: int len = sizeof(SockA);
937: if (getsockname(master, (struct sockaddr *) sin, &len)<0) {
2.65 frystyk 938: HTRequest_addSystemError(net->request, ERR_FATAL,
939: socerrno, NO, "getsockname");
2.56 frystyk 940: net->tcpstate = TCP_ERROR;
941: break;
942: }
943: } else
944: sin->sin_addr.s_addr = INADDR_ANY;
945: sin->sin_port = htons(port);
946: #endif
947: }
948: if (PROT_TRACE)
2.61 frystyk 949: TTYPrint(TDEST, "HTDoListen.. Listen on port %d\n", port);
2.56 frystyk 950: net->tcpstate = TCP_NEED_SOCKET;
951: break;
952:
953: case TCP_NEED_SOCKET:
954: #ifdef DECNET
955: if ((net->sockfd=socket(AF_DECnet, SOCK_STREAM, 0))==INVSOC)
956: #else
957: if ((net->sockfd=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP))==INVSOC)
958: #endif
959: {
2.65 frystyk 960: HTRequest_addSystemError(net->request, ERR_FATAL, socerrno,
961: NO, "socket");
2.56 frystyk 962: net->tcpstate = TCP_ERROR;
963: break;
964: }
965: if (PROT_TRACE)
2.61 frystyk 966: TTYPrint(TDEST, "HTDoListen.. Created socket %d\n",net->sockfd);
2.56 frystyk 967:
968: /* If non-blocking protocol then change socket status
969: ** I use FCNTL so that I can ask the status before I set it.
970: ** See W. Richard Stevens (Advan. Prog. in UNIX environment, p.364)
971: ** Be CAREFULL with the old `O_NDELAY' - it will not work as read()
972: ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD and
973: ** does NOT work on SVR4 systems. O_NONBLOCK is POSIX.
974: */
975: if (!net->preemtive) {
976: #ifdef _WINDOWS
977: { /* begin windows scope */
978: long levents = FD_READ | FD_WRITE | FD_ACCEPT |
979: FD_CONNECT | FD_CLOSE ;
980: int rv = 0 ;
981:
2.64 frystyk 982: #ifdef WWW_WIN_ASYNC
2.56 frystyk 983: /* N.B WSAAsyncSelect() turns on non-blocking I/O */
2.69 frystyk 984: rv = WSAAsyncSelect(net->sockfd, net->request->hwnd,
2.66 frystyk 985: net->request->winMsg, levents);
2.64 frystyk 986: if (rv == SOCKET_ERROR) {
987: status = -1 ;
988: if (PROT_TRACE)
989: TTYPrint(TDEST, "HTDoListen.. WSAAsyncSelect() fails: %d\n",
990: WSAGetLastError());
2.56 frystyk 991: } /* error returns */
2.64 frystyk 992: #else
993: int enable = 1 ;
994: status = IOCTL(net->sockfd, FIONBIO, &enable);
995: #endif
2.56 frystyk 996: } /* end scope */
997: #else
998: #if defined(VMS)
999: {
1000: int enable = 1;
1001: status = IOCTL(net->sockfd, FIONBIO, &enable);
1002: }
1003: #else
1004: if((status = FCNTL(net->sockfd, F_GETFL, 0)) != -1) {
1005: status |= O_NONBLOCK; /* POSIX */
1006: status = FCNTL(net->sockfd, F_SETFL, status);
1007: }
1008: #endif /* VMS */
1009: #endif /* WINDOW */
1010: if (PROT_TRACE) {
1011: if (status == -1)
2.61 frystyk 1012: TTYPrint(TDEST, "HTDoListen.. Blocking socket\n");
2.56 frystyk 1013: else
2.61 frystyk 1014: TTYPrint(TDEST, "HTDoListen.. Non-blocking socket\n");
2.56 frystyk 1015: }
1016: }
1017: net->tcpstate = TCP_NEED_BIND;
1018: break;
1019:
1020: case TCP_NEED_BIND:
1021: status = bind(net->sockfd, (struct sockaddr *) &net->sock_addr,
1022: sizeof(net->sock_addr));
1023: #ifdef _WINSOCKAPI_
1024: if (status == SOCKET_ERROR)
1025: #else
1026: if (status < 0)
1027: #endif
1028: {
1029: if (PROT_TRACE)
2.61 frystyk 1030: TTYPrint(TDEST, "Bind........ failed %d\n", socerrno);
2.56 frystyk 1031: net->tcpstate = TCP_ERROR;
1032: } else
1033: net->tcpstate = TCP_NEED_LISTEN;
1034: break;
1035:
1036: case TCP_NEED_LISTEN:
2.67 frystyk 1037: status = listen(net->sockfd, backlog);
2.56 frystyk 1038: #ifdef _WINSOCKAPI_
1039: if (status == SOCKET_ERROR)
1040: #else
1041: if (status < 0)
2.36 frystyk 1042: #endif
2.56 frystyk 1043: net->tcpstate = TCP_ERROR;
1044: else
1045: net->tcpstate = TCP_CONNECTED;
1046: break;
1047:
1048: case TCP_CONNECTED:
1049: net->tcpstate = TCP_BEGIN;
1050: if (PROT_TRACE)
2.68 frystyk 1051: TTYPrint(TDEST,"HTDoListen.. Bind and listen on port %d %s\n",
2.56 frystyk 1052: (int) ntohs(net->sock_addr.sin_port),
1053: HTInetString(&net->sock_addr));
1054: return HT_OK;
1055: break;
2.13 frystyk 1056:
2.56 frystyk 1057: case TCP_NEED_CONNECT:
1058: case TCP_DNS:
1059: case TCP_ERROR:
2.61 frystyk 1060: if (PROT_TRACE) TTYPrint(TDEST, "HTDoListen.. Connect failed\n");
2.62 frystyk 1061: HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "HTDoListen");
2.56 frystyk 1062: net->tcpstate = TCP_BEGIN;
1063: return HT_ERROR;
1064: break;
1065: }
2.38 frystyk 1066: }
1.1 timbl 1067: }
1068:
Webmaster