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