/* ** Opens a local file usien whatever means are available on the current ** platform. If we have unix file descriptors then use that as we can use ** select on them. Otherwise use ANSI C file descriptors. ** ** returns HT_ERROR Error has occured or interrupted ** HT_OK if connected ** HT_WOULD_BLOCK if operation would have blocked */ PUBLIC int HTFileOpen (HTNet * net, char * local, HTLocalMode mode) { HTRequest * request = HTNet_request(net); HTHost * host = HTNet_host(net); #ifdef NO_UNIX_IO FILE * fp = NULL; #else SOCKET sockfd = INVSOC; #endif /* NO_UNIX_IO */ #ifndef NO_UNIX_IO int status = -1; /* JTD:5/30/96 - must init status to -1 */ if ((sockfd = open(local, mode)) == -1) { HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "open"); return HT_ERROR; } /* If non-blocking protocol then change socket status ** I use fcntl() so that I can ask the status before I set it. ** See W. Richard Stevens (Advan. Prog. in UNIX env, p.364) ** Be CAREFULL with the old `O_NDELAY' - it wont work as read() ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD ** and does NOT work on SVR4 systems. O_NONBLOCK is POSIX. */ if (!HTNet_preemptive(net)) { #ifdef HAVE_FCNTL if ((status = fcntl(sockfd, F_GETFL, 0)) != -1) { #ifdef O_NONBLOCK status |= O_NONBLOCK;/* POSIX */ #else #ifdef F_NDELAY status |= F_NDELAY; /* BSD */ #endif /* F_NDELAY */ #endif /* O_NONBLOCK */ status = fcntl(sockfd, F_SETFL, status); } #endif /* HAVE_FCNTL */ HTTRACE(PROT_TRACE, "HTFileOpen.. `%s\' opened %d as %sblocking socket\n" _ local _ sockfd _ status == -1 ? "" : "NON-"); } /* #endif - HAVE_FCNTL <- wrong location, moved up JTD:5/30/96 */ #else /* !NO_UNIX_IO */ #ifdef VMS if ((fp = fopen(local, mode,"shr=put","shr=upd")) == NULL) { HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "fopen"); return HT_ERROR; } #else if ((fp = fopen(local, mode)) == NULL) { HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "fopen"); return HT_ERROR; } #endif /* VMS */ HTTRACE(PROT_TRACE, "HTDoOpen.... `%s\' opened using FILE %p\n" _ local _ fp); #endif /* !NO_UNIX_IO */ /* ** Associate the channel with the host and create ** an input and and output stream */ #ifdef NO_UNIX_IO HTHost_setChannel(host, HTChannel_new(INVSOC, fp, YES)); #else HTHost_setChannel(host, HTChannel_new(sockfd, NULL, YES)); #endif /* NO_UNIX_IO */ HTHost_getInput(host, HTNet_transport(net), NULL, 0); HTHost_getOutput(host, HTNet_transport(net), NULL, 0); return HT_OK; }
/* HTFile_readDir ** -------------- ** Reads the directory "path" ** Returns: ** HT_ERROR Error ** HT_FORBIDDEN Directory reading not allowed ** HT_LOADED Successfully read the directory */ PRIVATE int HTFile_readDir (HTRequest * request, file_info *file) { #ifdef HAVE_READDIR DIR * dp; struct stat file_info; HTParentAnchor * anchor = HTRequest_anchor(request); char *url = HTAnchor_physical(anchor); char fullname[HT_MAX_PATH+1]; char *name; HTTRACE(PROT_TRACE, "Reading..... directory\n"); if (dir_access == HT_DIR_FORBID) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN, NULL, 0, "HTFile_readDir"); return HT_FORBIDDEN; } /* Initialize path name for stat() */ if (*(name = (url+strlen(url)-1)) != '/') { char *newurl = NULL; StrAllocCopy(newurl, url); StrAllocCat(newurl, "/"); HT_FREE(file->local); file->local = HTWWWToLocal(newurl, "", HTRequest_userProfile(request)); HT_FREE(newurl); } strcpy(fullname, file->local); name = fullname+strlen(fullname); /* Point to end of fullname */ /* Check if access is enabled */ if (dir_access == HT_DIR_SELECTIVE) { strcpy(name, DEFAULT_DIR_FILE); if (HT_STAT(fullname, &file_info)) { HTTRACE(PROT_TRACE, "Read dir.... `%s\' not found\n" _ DEFAULT_DIR_FILE); HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN, NULL, 0, "HTFile_readDir"); return HT_FORBIDDEN; } } if ((dp = opendir(file->local))) { struct dirent * dirbuf; HTDir *dir = HTDir_new(request, dir_show, dir_key); char datestr[20]; char sizestr[10]; HTFileMode mode; #ifdef HT_REENTRANT struct dirent result; /* For readdir_r */ #endif #ifdef HAVE_READDIR_R_2 while ((dirbuf = (struct dirent *) readdir_r(dp, &result))) #elif defined(HAVE_READDIR_R_3) while (readdir_r(dp, &result, &dirbuf) == 0) #else while ((dirbuf = readdir(dp))) #endif /* HAVE_READDIR_R_2 */ { /* Current and parent directories are never shown in list */ #ifdef HAVE_DIRENT_INO if (!dirbuf->d_ino || !strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, "..")) #else if (!strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, "..")) #endif continue; /* Make a lstat on the file */ strcpy(name, dirbuf->d_name); if (HT_LSTAT(fullname, &file_info)) { HTTRACE(PROT_TRACE, "Read dir.... lstat failed: %s\n" _ fullname); continue; } /* Convert stat info to fit our setup */ if (((mode_t) file_info.st_mode & S_IFMT) == S_IFDIR) { #ifdef VMS char *dot = strstr(name, ".DIR"); /* strip .DIR part... */ if (dot) *dot = '\0'; #endif /* VMS */ mode = HT_IS_DIR; if (dir_show & HT_DS_SIZE) strcpy(sizestr, "-"); } else { mode = HT_IS_FILE; if (dir_show & HT_DS_SIZE) HTNumToStr(file_info.st_size, sizestr, 10); } if (dir_show & HT_DS_DATE) HTDateDirStr(&file_info.st_mtime, datestr, 20); /* Add to the list */ if (HTDir_addElement(dir, name, datestr, sizestr, mode) != YES) break; } closedir(dp); HTDir_free(dir); return HT_LOADED; } else { HTRequest_addSystemError(request, ERR_FATAL, errno, NO, "opendir"); return HT_ERROR; } #else return HT_ERROR; /* needed for WWW_MSWINDOWS */ #endif /* HAVE_READDIR */ }
/* Write to the socket ** ** According to Solaris 2.3 man on write: ** ** o If O_NONBLOCK and O_NDELAY are clear, write() blocks ** until the data can be accepted. ** ** o If O_NONBLOCK or O_NDELAY is set, write() does not ** block the process. If some data can be written ** without blocking the process, write() writes what it ** can and returns the number of bytes written. Other- ** wise, if O_NONBLOCK is set, it returns - 1 and sets ** errno to EAGAIN or if O_NDELAY is set, it returns 0. ** ** According to SunOS 4.1.1 man on write: ** ** + If the descriptor is marked for non-blocking I/O ** using fcntl() to set the FNONBLOCK or O_NONBLOCK ** flag (defined in <sys/fcntl.h>), write() requests ** for {PIPE_BUF} (see pathconf(2V)) or fewer bytes ** either succeed completely and return nbyte, or ** return -1 and set errno to EAGAIN. A write() request ** for greater than {PIPE_BUF} bytes either transfers ** what it can and returns the number of bytes written, ** or transfers no data and returns -1 and sets errno ** to EAGAIN. If a write() request is greater than ** {PIPE_BUF} bytes and all data previously written to ** the pipe has been read, write() transfers at least ** {PIPE_BUF} bytes. */ PRIVATE int HTWriter_write (HTOutputStream * me, const char * buf, int len) { HTHost * host = me->host; SOCKET soc = HTChannel_socket(HTHost_channel(host)); HTNet * net = HTHost_getWriteNet(host); int b_write; char * wrtp; const char *limit = buf+len; /* If we don't have a Net object then return right away */ if (!net) { HTTRACE(STREAM_TRACE, "Write Socket No Net object %d (offset %d)\n" _ soc _ me->offset); return HT_ERROR; } #ifdef NOT_ASCII if (len && !me->ascbuf) { /* Generate new buffer */ const char *orig = buf; char *dest; int cnt; if ((me->ascbuf = (char *) HT_MALLOC(len)) == NULL) HT_OUTOFMEM("HTWriter_write"); dest = me->ascbuf; for (cnt=0; cnt<len; cnt++) { *dest = TOASCII(*orig); dest++, orig++; } wrtp = me->ascbuf; limit = me->ascbuf+len; } #else if (!me->offset) wrtp = (char *) buf; else { wrtp = (char *) buf + me->offset; len -= me->offset; me->offset = 0; } #endif /* Write data to the network */ while (wrtp < limit) { if ((b_write = NETWRITE(soc, wrtp, len)) < 0) { #ifdef EAGAIN if (socerrno == EAGAIN || socerrno == EWOULDBLOCK)/* POSIX, SVR4 */ #else if (socerrno == EWOULDBLOCK) /* BSD */ #endif { HTHost_register(host, net, HTEvent_WRITE); me->offset = wrtp - buf; HTTRACE(STREAM_TRACE, "Write Socket WOULD BLOCK %d (offset %d)\n" _ soc _ me->offset); return HT_WOULD_BLOCK; #ifdef EINTR } else if (socerrno == EINTR) { /* ** EINTR A signal was caught during the write opera- ** tion and no data was transferred. */ HTTRACE(STREAM_TRACE, "Write Socket call interrupted - try again\n"); continue; #endif } else { host->broken_pipe = YES; #ifdef EPIPE if (socerrno == EPIPE) { /* JK: an experimental bug solution proposed by Olga and Mikhael */ HTTRACE(STREAM_TRACE, "Write Socket got EPIPE\n"); HTHost_unregister(host, net, HTEvent_WRITE); HTHost_register(host, net, HTEvent_CLOSE); /* @@ JK: seems that some functions check the errors as part of the flow control */ HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "NETWRITE"); return HT_CLOSED; } #endif /* EPIPE */ /* all errors that aren't EPIPE */ HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "NETWRITE"); return HT_ERROR; } } /* We do this unconditionally, should we check to see if we ever blocked? */ HTTRACEDATA(wrtp, b_write, "Writing to socket %d" _ soc); HTNet_addBytesWritten(net, b_write); wrtp += b_write; len -= b_write; HTTRACE(STREAM_TRACE, "Write Socket %d bytes written to %d\n" _ b_write _ soc); { HTAlertCallback *cbf = HTAlert_find(HT_PROG_WRITE); if (cbf) { int tw = HTNet_bytesWritten(net); (*cbf)(net->request, HT_PROG_WRITE, HT_MSG_NULL, NULL, &tw, NULL); } } } #ifdef NOT_ASCII HT_FREE(me->ascbuf); #endif return HT_OK; }
/* HTGetHostByName ** --------------- ** Resolve the host name using internal DNS cache. As we want to refer ** a specific host when timing the connection the weight function must ** use the 'current' value as returned. ** Returns: ** >0 Number of homes ** -1 Error */ PUBLIC int HTGetHostByName (HTHost * host, char *hostname, HTRequest* request) { SockA *sin = HTHost_getSockAddr(host); int homes = -1; HTList *list; /* Current list in cache */ HTdns *pres = NULL; if (!host || !hostname) { HTTRACE(PROT_TRACE, "HostByName.. Bad argument\n"); return -1; } HTHost_setHome(host, 0); /* Find a hash for this host */ { int hash = 0; char *ptr; for(ptr=hostname; *ptr; ptr++) hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_M_HASH_SIZE); if (!CacheTable) { if ((CacheTable = (HTList* *) HT_CALLOC(HT_M_HASH_SIZE, sizeof(HTList *))) == NULL) HT_OUTOFMEM("HTDNS_init"); } if (!CacheTable[hash]) CacheTable[hash] = HTList_new(); list = CacheTable[hash]; } /* Search the cache */ { HTList *cur = list; while ((pres = (HTdns *) HTList_nextObject(cur))) { if (!strcmp(pres->hostname, hostname)) { if (time(NULL) > pres->ntime + DNSTimeout) { HTTRACE(PROT_TRACE, "HostByName.. Refreshing cache\n"); delete_object(list, pres); pres = NULL; } break; } } } if (pres) { /* ** Find the best home. We still want to do this as we use it as a ** fall back for persistent connections */ homes = pres->homes; if (pres->homes > 1) { int cnt = 0; double best_weight = 1e30; /* Pretty bad */ while (cnt < pres->homes) { if (*(pres->weight+cnt) < best_weight) { best_weight = *(pres->weight+cnt); HTHost_setHome(host, cnt); } cnt++; } } host->dns = pres; memcpy((void *) &sin->sin_addr, *(pres->addrlist+HTHost_home(host)), pres->addrlength); } else { struct hostent *hostelement; /* see netdb.h */ HTAlertCallback *cbf = HTAlert_find(HT_PROG_DNS); #ifdef HT_REENTRANT int thd_errno; char buffer[HOSTENT_MAX]; struct hostent result; /* For gethostbyname_r */ #endif #ifdef HAVE_GETHOSTBYNAME_R_3 struct hostent_data hdata; #endif if (cbf) (*cbf)(request, HT_PROG_DNS, HT_MSG_NULL,NULL,hostname,NULL); #ifdef HAVE_GETHOSTBYNAME_R_5 hostelement = gethostbyname_r(hostname, &result, buffer, HOSTENT_MAX, &thd_errno); #elif defined(HAVE_GETHOSTBYNAME_R_6) gethostbyname_r(hostname, &result, buffer, HOSTENT_MAX, &hostelement, &thd_errno); #elif defined(HAVE_GETHOSTBYNAME_R_3) if (gethostbyname_r(hostname, &result, &hdata) == 0) { hostelement = &result; } else { hostelement = NULL; } #else if (cbf) (*cbf)(request, HT_PROG_DNS, HT_MSG_NULL,NULL,hostname,NULL); hostelement = gethostbyname(hostname); #endif if (!hostelement) { HTRequest_addSystemError(request, ERR_FATAL, socerrno, NO, "gethostbyname"); return -1; } host->dns = HTDNS_add(list, hostelement, hostname, &homes); memcpy((void *) &sin->sin_addr, *hostelement->h_addr_list, hostelement->h_length); } return homes; }
PRIVATE int HTSSLReader_read (HTInputStream * me) { HTHost * host = me->host; SOCKET soc = HTChannel_socket(me->ch); HTNet * net = HTHost_getReadNet(host); HTRequest * request = HTNet_request(net); int status; if (!net->readStream) { HTTRACE(STREAM_TRACE, "HTSSLReader. No read stream for net object %p\n" _ net); return HT_ERROR; } /* Setting SSL */ if (!me->htssl) { if ((me->htssl = HTSSL_new(soc)) == NULL) { HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "SSLREAD"); return HT_ERROR; } } /* Read from socket if we got rid of all the data previously read */ do { /* Don't read if we have to push unwritten data from last call */ if (me->write >= me->read) { me->b_read = 0; me->data[0] ='\0'; me->b_read = HTSSL_read(me->htssl, soc, me->data, INPUT_BUFFER_SIZE); status = HTSSL_getError(me->htssl, me->b_read); HTTRACE(STREAM_TRACE, "HTSSLReader. SSL returned %d\n" _ status); /* Check what we got done */ switch (status) { case SSL_ERROR_NONE: HTTRACEDATA(me->data, me->b_read, "Reading from socket %d" _ soc); me->write = me->data; me->read = me->data + me->b_read; HTTRACE(STREAM_TRACE, "HTSSLReader. %d bytes read from socket %d\n" _ me->b_read _ soc); /* Make progress notification */ if (request) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_READ); if (HTNet_rawBytesCount(net)) HTNet_addBytesRead(net, me->b_read); if (cbf) { int tr = HTNet_bytesRead(net); (*cbf)(request, HT_PROG_READ, HT_MSG_NULL, NULL, &tr, NULL); } } break; case SSL_ERROR_WANT_READ: HTTRACE(STREAM_TRACE, "HTSSLReader. WOULD BLOCK fd %d\n" _ soc); HTHost_register(host, net, HTEvent_READ); /* ** There seems to be a bug as even though it says "read finished" ** it doesn't say that it wants to write. We therefore have to make ** an explicit flush to make sure that we don't block forever. */ HTHost_forceFlush(host); return HT_WOULD_BLOCK; case SSL_ERROR_WANT_WRITE: return HTHost_forceFlush(host); case SSL_ERROR_WANT_X509_LOOKUP: /* @@@ what to do here? @@@ */ return HT_OK; case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SSL: case SSL_ERROR_SYSCALL: HTTRACE(PROT_TRACE, "HTSSLReader. FIN received on socket %d\n" _ soc); HTHost_unregister(host, net, HTEvent_READ); HTHost_register(host, net, HTEvent_CLOSE); HTSSL_close(me->htssl); HTSSL_free(me->htssl); me->htssl = NULL; return HT_CLOSED; } } #ifdef FIND_SIGNATURES { char * ptr = me->data; int len = me->b_read; while ((ptr = strnstr(ptr, &len, "HTTP/1.1 200 OK")) != NULL) { HTTRACE(STREAM_TRACE, "HTSSLReader. Signature found at 0x%x of 0x%x.\n" _ ptr - me->data _ me->b_read); ptr++; len--; } } #endif /* FIND_SIGNATURES */ #ifdef NOT_ASCII { char *p = me->data; while (p < me->read) { *p = FROMASCII(*p); p++; } } #endif /* NOT_ASCII */ /* Now push the data down the stream */ if ((status = (*net->readStream->isa->put_block) (net->readStream, me->write, me->b_read)) != HT_OK) { if (status == HT_WOULD_BLOCK) { HTTRACE(STREAM_TRACE, "HTSSLReader. Target WOULD BLOCK\n"); HTHost_unregister(host, net, HTEvent_READ); return HT_WOULD_BLOCK; } else if (status == HT_PAUSE) { HTTRACE(STREAM_TRACE, "HTSSLReader. Target PAUSED\n"); HTHost_unregister(host, net, HTEvent_READ); return HT_PAUSE; /* CONTINUE code or stream code means data was consumed */ } else if (status == HT_CONTINUE || status > 0) { if (status == HT_CONTINUE) { HTTRACE(STREAM_TRACE, "HTSSLReader. CONTINUE\n"); } else HTTRACE(STREAM_TRACE, "HTSSLReader. Target returns %d\n" _ status); /* me->write = me->read; */ return status; } else { /* We have a real error */ HTTRACE(STREAM_TRACE, "HTSSLReader. Target ERROR %d\n" _ status); return status; } } me->write = me->read; { int remaining = HTHost_remainingRead(host); if (remaining > 0) { HTTRACE(STREAM_TRACE, "HTSSLReader. DIDN'T CONSUME %d BYTES: `%s\'\n" _ remaining _ me->read); HTHost_setConsumed(host, remaining); } } } while (net->preemptive); HTHost_register(host, net, HTEvent_READ); return HT_WOULD_BLOCK; }