/* HTErrorSysAdd ** ** Add a system error message to the error list in HTRequest. syscall ** is the name of the system call, e.g. "close". The message put to the ** list is that corresponds to the errno. This function also replaces ** HTInetStatus, which is called from within. ** ** See also HTErrorAdd. ** ** Returns always < 0 */ PUBLIC int HTErrorSysAdd ARGS4(HTRequest *, request, HTErrSeverity, severity, BOOL, ignore, char *, syscall) { if (!request) { if (TRACE) fprintf(stderr, "HTErrorSys.. Bad argument!\n"); return -1; } if (syscall) { HTInetStatus(syscall); } else HTInetStatus("Unspecified System Call"); { char temp[100]; char *errmsg = NULL; sprintf(temp, error_info[HTERR_SYSTEM].msg, syscall); StrAllocCopy(errmsg, temp); StrAllocCat(errmsg, HTErrnoString()); HTErrorAdd(request, severity, ignore, HTERR_SYSTEM, (void *) errmsg, (int) strlen(errmsg), syscall); free(errmsg); } return (-HTERR_SYSTEM); }
/* Push data from a socket down a stream ** ------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream, i.e., containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** ** State of socket and target stream on entry: ** socket (file_number) assumed open, ** target (sink) assumed valid. ** ** Return values: ** HT_INTERRUPTED Interruption or error after some data received. ** -2 Unexpected disconnect before any data received. ** -1 Interruption or error before any data received, or ** (UNIX) other read error before any data received, or ** download cancelled. ** HT_LOADED Normal close of socket (end of file indication ** received), or ** unexpected disconnect after some data received, or ** other read error after some data received, or ** (not UNIX) other read error before any data received. ** ** State of socket and target stream on return depends on return value: ** HT_INTERRUPTED socket still open, target aborted. ** -2 socket still open, target stream still valid. ** -1 socket still open, target aborted. ** otherwise socket closed, target stream still valid. */ PUBLIC int HTCopy ARGS4( HTParentAnchor *, anchor, int, file_number, void*, handle GCC_UNUSED, HTStream*, sink) { HTStreamClass targetClass; BOOL suppress_readprogress = NO; int bytes; int rv = 0; /* Push the data down the stream */ targetClass = *(sink->isa); /* Copy pointers to procedures */ /* Push binary from socket down sink ** ** This operation could be put into a main event loop */ HTReadProgress(bytes = 0, 0); for (;;) { int status; if (LYCancelDownload) { LYCancelDownload = FALSE; (*targetClass._abort)(sink, NULL); rv = -1; goto finished; } if (HTCheckForInterrupt()) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } #ifdef USE_SSL if (handle) status = SSL_read((SSL *)handle, input_buffer, INPUT_BUFFER_SIZE); else status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); #else status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); #endif /* USE_SSL */ if (status <= 0) { if (status == 0) { break; } else if (status == HT_INTERRUPTED) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } else if (SOCKET_ERRNO == ENOTCONN || #ifdef _WINDOWS /* 1997/11/10 (Mon) 16:57:18 */ SOCKET_ERRNO == ETIMEDOUT || #endif SOCKET_ERRNO == ECONNRESET || SOCKET_ERRNO == EPIPE) { /* * Arrrrgh, HTTP 0/1 compatibility problem, maybe. */ if (bytes <= 0) { /* * Don't have any data, so let the calling * function decide what to do about it. - FM */ rv = -2; goto finished; } else { #ifdef UNIX /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected server disconnect."); CTRACE((tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n")); status = 0; break; #else /* !UNIX */ /* * Treat what we've gotten already * as the complete transmission. - FM */ CTRACE((tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n")); status = 0; break; #endif /* UNIX */ } #ifdef UNIX } else { /* status < 0 and other errno */ /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected read error."); if (bytes) { (void)NETCLOSE(file_number); rv = HT_LOADED; } else { (*targetClass._abort)(sink, NULL); rv = -1; } goto finished; #endif } break; } /* * Suppress ReadProgress messages when collecting a redirection * message, at least initially (unless/until anchor->content_type * gets changed, probably by the MIME message parser). That way * messages put up by the HTTP module or elsewhere can linger in * the statusline for a while. - kw */ suppress_readprogress = (anchor && anchor->content_type && !strcmp(anchor->content_type, "message/x-http-redirection")); #ifdef NOT_ASCII { char * p; for (p = input_buffer; p < input_buffer+status; p++) { *p = FROMASCII(*p); } } #endif /* NOT_ASCII */ (*targetClass.put_block)(sink, input_buffer, status); bytes += status; if (!suppress_readprogress) HTReadProgress(bytes, anchor ? anchor->content_length : 0); HTDisplayPartial(); } /* next bufferload */ _HTProgress(TRANSFER_COMPLETE); (void)NETCLOSE(file_number); rv = HT_LOADED; finished: HTFinishDisplayPartial(); return(rv); }
/* Load Document from HTTP Server HTLoadHTTP() ** ============================== ** ** Given a hypertext address, this routine loads a document. ** ** ** On entry, ** arg is the hypertext reference of the article to be loaded. ** gate is nill if no gateway, else the gateway address. ** ** On exit, ** returns >=0 If no error, a good socket number ** <0 Error. ** ** The socket must be closed by the caller after the document has been ** read. ** */ PUBLIC int HTLoadHTTP ARGS4 (CONST char *, arg, CONST char *, gate, HTAnchor *, anAnchor, int, diag) { int s; /* Socket number for returned data */ char *command; /* The whole command */ int status; /* tcp return */ char host[256]; /* Hold on to the host */ SockA soc_address; /* Binary network address */ SockA * sin = &soc_address; if (!arg) return -3; /* Bad if no name sepcified */ if (!*arg) return -2; /* Bad if name had zero length */ /* Set up defaults: */ #ifdef DECNET sin->sdn_family = AF_DECnet; /* Family = DECnet, host order */ sin->sdn_objnum = DNP_OBJ; /* Default: http object number */ #else /* Internet */ sin->sin_family = AF_INET; /* Family = internet, host order */ sin->sin_port = htons(TCP_PORT); /* Default: http port */ #endif if (TRACE) { if (gate) fprintf(stderr, "HTTPAccess: Using gateway %s for %s\n", gate, arg); else fprintf(stderr, "HTTPAccess: Direct access for %s\n", arg); } /* Get node name and optional port number: */ { char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST); strcpy(host, p1); int status = HTParseInet(sin, p1); /* TBL 920622 */ free(p1); if (status) return status; /* No such host for example */ } /* Now, let's get a socket set up from the server for the sgml data: */ #ifdef DECNET s = socket(AF_DECnet, SOCK_STREAM, 0); #else s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #endif status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address)); if (status < 0) { #ifndef DECNET /* This code is temporary backward-compatibility. It should go away when no server runs on port 2784 alone */ if (sin->sin_port == htons(TCP_PORT)) { /* Try the old one */ if (TRACE) printf ( "HTTP: Port %d doesn't answer (errno = %d). Trying good old port %d...\n", TCP_PORT, errno, OLD_TCP_PORT); sin->sin_port = htons(OLD_TCP_PORT); /* First close current socket and open a clean one */ status = NETCLOSE (s); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address)); } if (status < 0) #endif { if (TRACE) fprintf(stderr, "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno); /* free(command); BUG OUT TBL 921121 */ return HTInetStatus("connect"); } } if (TRACE) fprintf(stderr, "HTTP connected, socket %d\n", s); /* Ask that node for the document, ** omitting the host name & anchor if not gatewayed. */ if (gate) { command = malloc(4 + strlen(arg)+ 2 + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, "GET "); strcat(command, arg); } else if ( HTTP11 ) { /* not gatewayed */ /* Switch this to HTTP 1.1 */ char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); command = malloc(4 + strlen(arg) + 11 + 6 + strlen(host) + 2 + 20 + 2 + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, "GET "); strcat(command, arg); strcat(command, " HTTP/1.1\r\n"); strcat(command, "Host: "); strcat(command, host); strcat(command, "\r\n"); strcat(command, "Connection: close\r\n"); } else { char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); command = malloc(4 + strlen(p1)+ 2 + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, "GET "); strcat(command, p1); free(p1); } strcat(command, "\r\n"); /* Include CR for telnet compat. */ if (TRACE) fprintf(stderr, "HTTP writing command `%s' to socket %d\n", command, s); #ifdef NOT_ASCII { char * p; for(p = command; *p; p++) { *p = TOASCII(*p); } } #endif status = NETWRITE(s, command, (int)strlen(command)); free(command); if (status<0) { if (TRACE) fprintf(stderr, "HTTPAccess: Unable to send command.\n"); return HTInetStatus("send"); } /* Skip the HTTP 1.1 headers */ if ( HTTP11 ) { char buffer[2]; int endhdr = 0; while ( 1 ) { int rc = read(s, buffer, 1) ; if ( TRACE ) fprintf(stderr,"%c",buffer[0]); if ( rc < 1 ) { if (TRACE) fprintf(stderr, "HTTPAccess: EOF reading headers.\n"); return HTInetStatus("send"); } if ( buffer[0] == '\r' && endhdr == 0 ) endhdr = 1; else if ( buffer[0] == '\n' && endhdr == 1 ) endhdr = 2; else if ( buffer[0] == '\r' && endhdr == 2 ) endhdr = 3; else if ( buffer[0] == '\n' && endhdr == 3 ) break; else endhdr = 0; } } /* Now load the data */ { char *url = HTParse(arg, "", PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); int compressed; int fmt = diag ? WWW_PLAINTEXT : HTFileFormat (url, WWW_HTML, &compressed); HTParseFormat(fmt, (HTParentAnchor *) anAnchor, s, compressed); free (url); } if (TRACE) fprintf(stderr, "HTTP: close socket %d.\n", s); status = NETCLOSE(s); return HT_LOADED; /* Good return */ }
PRIVATE int server_loop() #endif { int tcp_status; /* <0 if error, in general */ int timeout = -1; /* No timeout required but code exists */ for(;;) { /* If it's a master socket, then find a slave: */ if (role == master) { #ifdef SELECT fd_set read_chans; fd_set write_chans; fd_set except_chans; int nfound; /* Number of ready channels */ struct timeval max_wait; /* timeout in form for select() */ FD_ZERO(&write_chans); /* Clear the write mask */ FD_ZERO(&except_chans); /* Clear the exception mask */ /* If timeout is required, the timeout structure is set up. Otherwise ** (timeout<0) a zero is passed instead of a pointer to the struct timeval. */ if (timeout>=0) { max_wait.tv_sec = timeout/100; max_wait.tv_usec = (timeout%100)*10000; } for (com_soc=(-1); com_soc<0;) { /* Loop while connections keep coming */ /* The read mask expresses interest in the master channel for incoming ** connections) or any slave channel (for incoming messages). */ /* Wait for incoming connection or message */ read_chans = open_sockets; /* Read on all active channels */ if (TRACE) printf( "Daemon: Waiting for connection or message. (Mask=%x hex, max=%x hex).\n", *(int *)(&read_chans), num_sockets); nfound=select(num_sockets, &read_chans, &write_chans, &except_chans, timeout >= 0 ? &max_wait : 0); if (nfound<0) return HTInetStatus("select()"); if (nfound==0) return 0; /* Timeout */ /* We give priority to existing connected customers. When there are ** no outstanding commands from them, we look for new customers. */ /* If a message has arrived on one of the channels, take that channel: */ { int i; for(i=0; i<num_sockets; i++) if (i != master_soc) if (FD_ISSET(i, &read_chans)) { if (TRACE) printf( "Message waiting on socket %d\n", i); com_soc = i; /* Got one! */ break; } if (com_soc>=0) break; /* Found input socket */ } /* block */ /* If an incoming connection has arrived, accept the new socket: */ if (FD_ISSET(master_soc, &read_chans)) { CTRACE(tfp, "Daemon: New incoming connection:\n"); tcp_status = accept(master_soc, (struct sockaddr *)&soc_address, &soc_addrlen); if (tcp_status<0) return HTInetStatus("accept"); CTRACE(tfp, "Daemon: Accepted new socket %d\n", tcp_status); FD_SET(tcp_status, &open_sockets); if ((tcp_status+1) > num_sockets) num_sockets=tcp_status+1; } /* end if new connection */ } /* loop on event */ #else /* SELECT not supported */ if (com_soc<0) { /* No slaves: must accept */ CTRACE(tfp, "Daemon: Waiting for incoming connection...\n"); tcp_status = accept(master_soc, &rsoc->mdp.soc_tcp.soc_address, &rsoc->mdp.soc_tcp.soc_addrlen); if (tcp_status<0) return HTInetStatus("accept"); com_soc = tcp_status; /* socket number */ CTRACE(tfp, "Daemon: Accepted socket %d\n", tcp_status); } /* end if no slaves */ #endif } /* end if master */ /* com_soc is now valid for read */ { struct sockaddr_in addr; int namelen = sizeof(addr); getpeername(com_soc, (struct sockaddr*)&addr, &namelen); strncpy(HTClientHost, inet_ntoa(addr.sin_addr), sizeof(HTClientHost)); } /* Read the message now on whatever channel there is: */ CTRACE(tfp,"Daemon: Reading socket %d from host %s\n", com_soc, HTClientHost); tcp_status=HTHandle(com_soc); if(tcp_status<=0) { /* EOF or error */ if (tcp_status<0) { /* error */ CTRACE(tfp, "Daemon: Error %d handling incoming message (errno=%d).\n", tcp_status, errno); /* DONT return HTInetStatus("netread"); error */ } else { CTRACE(tfp, "Daemon: Socket %d disconnected by peer\n", com_soc); } if (role==master) { NETCLOSE(com_soc); FD_CLR(com_soc, &open_sockets); } else { /* Not multiclient mode */ #ifdef VM return -69; #else return -ECONNRESET; #endif } } else {/* end if handler left socket open */ NETCLOSE(com_soc); FD_CLR(com_soc, &open_sockets); } }; /* for loop */ /*NOTREACHED*/ } /* end server_loop */
/* Bind to a TCP port ** ------------------ ** ** On entry, ** tsap is a string explaining where to take data from. ** "" means data is taken from stdin. ** "*:1729" means "listen to anyone on port 1729" ** ** On exit, ** returns Negative value if error. */ int do_bind ARGS1(CONST char *, tsap) { FD_ZERO(&open_sockets); /* Clear our record of open sockets */ num_sockets = 0; /* Deal with PASSIVE socket: ** ** A passive TSAP is one which has been created by the inet daemon. ** It is indicated by a void TSAP name. In this case, the inet ** daemon has started this process and given it, as stdin, the connection ** which it is to use. */ if (*tsap == 0) { /* void tsap => passive */ dynamic_allocation = FALSE; /* not dynamically allocated */ role = passive; /* Passive: started by daemon */ #ifdef vms { unsigned short channel; /* VMS I/O channel */ struct string_descriptor { /* This is NOT a proper descriptor*/ int size; /* but it will work. */ char *ptr; /* Should be word,byte,byte,long */ } sys_input = {10, "SYS$INPUT:"}; int status; /* Returned status of assign */ extern int sys$assign(); status = sys$assign(&sys_input, &channel, 0, 0); com_soc = channel; /* The channel is stdin */ CTRACE(tfp, "IP: Opened PASSIVE socket %d\n", channel); return 1 - (status&1); } #else com_soc = 0; /* The channel is stdin */ CTRACE(tfp, "IP: PASSIVE socket 0 assumed from inet daemon\n"); return 0; /* Good */ #endif /* Parse the name (if not PASSIVE) */ } else { /* Non-void TSAP */ char *p; /* pointer to string */ char *q; struct hostent *phost; /* Pointer to host - See netdb.h */ char buffer[256]; /* One we can play with */ register struct sockaddr_in* sin = &soc_address; strcpy(buffer, tsap); p = buffer; /* Set up defaults: */ sin->sin_family = AF_INET; /* Family = internet, host order */ sin->sin_port = 0; /* Default: new port, */ dynamic_allocation = TRUE; /* dynamically allocated */ role = passive; /* by default */ /* Check for special characters: */ if (*p == WILDCARD) { /* Any node */ role = master; p++; } /* Strip off trailing port number if any: */ for(q=p; *q; q++) if (*q==':') { int status; *q++ = 0; /* Terminate node string */ sin->sin_port = htons((unsigned short)HTCardinal( &status, &q, (unsigned int)65535)); if (status<0) return status; if (*q) return -2; /* Junk follows port number */ dynamic_allocation = FALSE; break; /* Exit for loop before we skip the zero */ } /*if*/ /* Get node name: */ if (*p == 0) { sin->sin_addr.s_addr = INADDR_ANY; /* Default: any address */ } else if (*p>='0' && *p<='9') { /* Numeric node address: */ sin->sin_addr.s_addr = inet_addr(p); /* See arpa/inet.h */ } else { /* Alphanumeric node name: */ phost=gethostbyname(p); /* See netdb.h */ if (!phost) { CTRACE(tfp, "IP: Can't find internet node name `%s'.\n",p); return HTInetStatus("gethostbyname"); /* Fail? */ } memcpy(&sin->sin_addr, phost->h_addr, phost->h_length); } CTRACE(tfp, "Daemon: Parsed address as port %d, inet %d.%d.%d.%d\n", (unsigned int)ntohs(sin->sin_port), (int)*((unsigned char *)(&sin->sin_addr)+0), (int)*((unsigned char *)(&sin->sin_addr)+1), (int)*((unsigned char *)(&sin->sin_addr)+2), (int)*((unsigned char *)(&sin->sin_addr)+3)); } /* scope of p */ /* Master socket for server: */ if (role == master) { /* Create internet socket */ master_soc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (master_soc<0) return HTInetStatus("socket"); CTRACE(tfp, "IP: Opened socket number %d\n", master_soc); /* If the port number was not specified, then we search for a free one. */ if (dynamic_allocation) { unsigned short try; for (try=FIRST_TCP_PORT; try<=LAST_TCP_PORT; try++) { soc_address.sin_port = htons(try); if (bind(master_soc, (struct sockaddr*)&soc_address, /* Cast to generic sockaddr */ sizeof(soc_address)) == 0) break; if (try == LAST_TCP_PORT) return HTInetStatus("bind"); } CTRACE(tfp, "IP: Bound to port %u.\n", ntohs(soc_address.sin_port)); } else { /* Port was specified */ if (bind(master_soc, (struct sockaddr*)&soc_address, /* Case to generic address */ sizeof(soc_address))<0) return HTInetStatus("bind"); } if (listen(master_soc, LISTEN_BACKLOG)<0) return HTInetStatus("listen"); CTRACE(tfp, "Daemon: Master socket(), bind() and listen() all OK\n"); FD_SET(master_soc, &open_sockets); if ((master_soc+1) > num_sockets) num_sockets=master_soc+1; return master_soc; } /* if master */ return -1; /* unimplemented role */ } /* do_bind */
/**************************************************************************** * name: HtLoadHTTPANN (PRIVATE) * purpose: Issue a command to a group annotation server. * inputs: * - char *arg: * - char *data: * - int len: * - char *com: * returns: * * remarks: * ****************************************************************************/ static int HtLoadHTTPANN(char *arg, char *data, int len, char *com) { int s; /* Socket number for returned data */ char *command; /* The whole command */ int status; /* tcp return */ SockA soc_address; /* Binary network address */ SockA *sin = &soc_address; char *tptr; int fmt, compressed; int command_len; HTParentAnchor *anchor; /* * Set up defaults: */ #ifdef DECNET sin->sdn_family = AF_DECnet; /* Family = DECnet, host order */ sin->sdn_objnum = DNP_OBJ; /* Default: http object number */ #else /* Internet */ sin->sin_family = AF_INET; /* Family = internet, host order */ sin->sin_port = htons(TCP_PORT); /* Default: http port */ #endif tptr = HTParse(arg, "", PARSE_HOST); status = HTParseInet(sin, tptr); free(tptr); if (status) { return(status); } /* * Now, let's get a socket set up from the server for the data. */ #ifdef DECNET s = socket(AF_DECnet, SOCK_STREAM, 0); #else s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #endif status = connect (s, (struct sockaddr*)&soc_address,sizeof(soc_address)); if (status < 0) { return(HTInetStatus("connect")); } /* If there's an anchor at this point, leave it in. */ tptr = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION|PARSE_ANCHOR); #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr, "HTParse(%s) returns:\n\t(%s)\n", arg, tptr); } #endif command_len = strlen(com) + strlen(tptr); command = malloc(command_len + len + 1); if (command == NULL) outofmem(__FILE__, "HTLoadHTTP"); strcpy(command, com); strcat(command, tptr); if (len != 0) { char *bptr; bptr = (char *)(command + command_len); /* bcopy(data, bptr, len);*/ memcpy(bptr, data, len); command_len += len; } else { command_len++; } free(tptr); status = NETWRITE(s, command, command_len); free(command); if (status < 0) { return(HTInetStatus("send")); } tptr = HTParse(arg, "", PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION); #if 0 /* fmt = HTFileFormat (tptr, WWW_HTML, &compressed); */ fmt = WWW_HTML; anchor = HTAnchor_parent(HTAnchor_findAddress(arg)); HTParseFormat(fmt, anchor, s, 0); #endif free(tptr); status = NETCLOSE(s); return(HT_LOADED); }