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