ftp.nice.ch/pub/next/unix/network/www/httpd.1.5-export.NIHS.bs.gnutar.gz#/httpd_1.5-export/src/http_send.c

This is http_send.c in view mode; [Download] [Up]

/************************************************************************
 * NCSA HTTPd Server
 * Software Development Group
 * National Center for Supercomputing Applications
 * University of Illinois at Urbana-Champaign
 * 605 E. Springfield, Champaign, IL 61820
 * httpd@ncsa.uiuc.edu
 *
 * Copyright  (C)  1995, Board of Trustees of the University of Illinois
 *
 ************************************************************************
 * 
 * http_send.c,v 1.21 1995/11/10 02:01:47 blong Exp
 *
 ************************************************************************
 *
 * http_send.c: handles sending of regular files and determining which
 *		type of request it is if its not for a regular file
 *
 * Based on NCSA HTTPd 1.3 by Rob McCool
 * 
 * 04-08-95  blong
 *	Fixed security hole which allowed a trailing slash on CGI_MAGIC_TYPE
 *	cgi anywhere scripts to send back the script contents.  Now the
 *	trailing slash is added to the PATH_INFO, and the script is run.
 *	Oh yeah, and don't forget about directories.
 *
 * 09-01-95  blong
 *	Fixed bug under AIX 3.2.5 where last part of file is garbled using
 *	fwrite, but works fine with write.  I didn't say I understood it,
 *	but the fix seems to work.
 */


#include "config.h"
#include "portability.h"

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include "constants.h"
#include "fdwrap.h"
#include "http_send.h"
#include "cgi.h"
#include "imagemap.h"
#include "http_mime.h"
#include "http_log.h"
#include "http_request.h"
#include "http_config.h"
#include "http_include.h"
#include "http_alias.h"
#include "http_access.h"
#include "http_dir.h"
#include "httpd.h"
#include "util.h"


int num_includes;

static void (*exit_callback)();

void send_node(per_request *reqInfo) 
{
    struct stat finfo;
    char pa[MAX_STRING_LEN];
    int length = 0;
    register x = 0;
    int allow;
    char allow_options;
    int ErrReturn = 0;

    exit_callback = NULL;

/* Remove all but 1 of the trailing slashes from the filename in order
   to fix security hole.  Place them in the path alias (pa) array */

 
    length = strlen(reqInfo->filename);
    while ((length>1) && (reqInfo->filename[length-1] == '/') && 
	   (reqInfo->filename[length-2] == '/') && (x < MAX_STRING_LEN)) {
	pa[x] = '/';
	x++;
	reqInfo->filename[length-1] = '\0';
	length--;
    }
    pa[x] = '\0';
    if(stat(reqInfo->filename,&finfo) == -1) {
	if ((ErrReturn = extract_path_info(reqInfo,pa,&finfo))) {
	    if(ErrReturn == ENOENT) {
		log_reason(reqInfo,"file does not exist",reqInfo->filename);
		die(reqInfo,SC_NOT_FOUND,reqInfo->url);
	    } else {
		log_reason(reqInfo,"(3) file permissions deny server access",
			   reqInfo->filename);
		die(reqInfo,SC_FORBIDDEN,reqInfo->url);
	    }
	}
    }
    evaluate_access(reqInfo,&finfo,&allow,&allow_options);
    if(!allow) {
        log_reason(reqInfo,"client denied by server configuration",
		   reqInfo->filename);
        die(reqInfo,SC_FORBIDDEN,reqInfo->url);
    }

    if (S_ISDIR(finfo.st_mode)) {
	send_dir(reqInfo,&finfo,pa,allow_options);
    } else if (S_ISREG(finfo.st_mode)) {
	probe_content_type(reqInfo,reqInfo->filename);
	if (!strcmp(content_type, CGI_MAGIC_TYPE))
	    send_cgi(reqInfo,&finfo,pa,allow_options);
#ifdef IMAGEMAP_SUPPORT
	else if (!strcmp(content_type, IMAGEMAP_MAGIC_TYPE))
	    send_imagemap(reqInfo,&finfo,pa,allow_options);
#endif /* IMAGEMAP_SUPPORT */
	else
	    send_file(reqInfo,&finfo,pa,allow_options);
    } else {
	log_reason(reqInfo,"improper file type",reqInfo->filename);
	/* device driver or pipe, no permission */
	die(reqInfo,SC_FORBIDDEN,reqInfo->url); 
    }
}

void send_file(per_request *reqInfo, struct stat *fi, 
               char *path_args, char allow_options) 
{
    FILE *f;

    if ((reqInfo->method != M_GET) && (reqInfo->method != M_HEAD)) {
	sprintf(error_msg,"%s to non-script",methods[reqInfo->method]);
	die(reqInfo,SC_NOT_IMPLEMENTED,error_msg);
    }
    set_content_type(reqInfo,reqInfo->filename);

    if((allow_options & OPT_INCLUDES) && (!content_encoding[0])) {
#ifdef XBITHACK
        if((fi->st_mode & S_IXUSR) ||
           (!strcmp(content_type,INCLUDES_MAGIC_TYPE))) {
#else
	if(!strcmp(content_type,INCLUDES_MAGIC_TYPE)) {
#endif /* XBITHACK */
	    reqInfo->bytes_sent = 0;
	    send_parsed_file(reqInfo,path_args,
			     allow_options & OPT_INCNOEXEC);
	    log_transaction(reqInfo);
	    return;
	}
    }
    if (path_args[0]) {
	strcat(reqInfo->filename,path_args);
	strcat(reqInfo->url,path_args);
	sprintf(error_msg,"No file matching URL: %s",reqInfo->url);
	log_reason(reqInfo, error_msg, reqInfo->filename);
	die(reqInfo,SC_NOT_FOUND,reqInfo->url);
    }
	
    if(!(f=FOpen(reqInfo->filename,"r"))) {
      if (errno == EACCES) {
	log_reason(reqInfo,"(1) file permissions deny server access",
		   reqInfo->filename);
	/* we've already established that it exists */
	die(reqInfo,SC_FORBIDDEN,reqInfo->url); 
      } else {
	/* We know an error occured, of an unexpected variety. 
	 * This could be due to no more file descriptors.  We have this
	 * child exit after this stage so that errors of state are 
	 * swept under the carpet.
	 */
	standalone = 0;
	sprintf(error_msg,"File Open error, errno=%d",errno);
	log_reason(reqInfo,error_msg,reqInfo->filename);
	die(reqInfo,SC_SERVER_ERROR,error_msg);
      }
    }
    reqInfo->bytes_sent = 0;
    if(!no_headers) {
	set_content_length(reqInfo,fi->st_size);
	if (set_last_modified(reqInfo,fi->st_mtime)) {
	    FClose(f);
	    return;
	}
	send_http_header(reqInfo);
    }

    num_includes = 0;	
    if(!header_only) 
	send_fp(reqInfo,f,NULL);
    log_transaction(reqInfo);
    FClose(f);
}

/* Globals for speed */
static char ifile[HUGE_STRING_LEN];
static char temp_name[HUGE_STRING_LEN];

void send_dir(per_request *reqInfo,struct stat *finfo, char *pa, 
	      char allow_options) {
  char *name_ptr, *end_ptr;

/* Path Alias (pa) array should now have the trailing slash */
  /*  if (pa[0] != '/') { */
  if ((reqInfo->filename[strlen(reqInfo->filename) - 1] != '/') && 
      (pa[0] != '/')) {
    char url[HUGE_STRING_LEN];
    strcpy_dir(ifile,reqInfo->url);
    construct_url(url,reqInfo->hostInfo,ifile);
    escape_url(url);
    die(reqInfo,SC_REDIRECT_PERM,url);
  }

  /* Don't allow PATH_INFO to directory indexes as a compromise for 
     error messages for files which don't exist */

  if ((pa[0] != '\0') || (strlen(pa) > 1)) {
        strcat(reqInfo->filename,pa);
        strcat(reqInfo->url,pa);
        sprintf(error_msg,"No file matching URL: %s",reqInfo->url);
        log_reason(reqInfo, error_msg, reqInfo->filename);
        die(reqInfo,SC_NOT_FOUND,reqInfo->url);
  }
    
  strncpy(temp_name, reqInfo->hostInfo->index_names, HUGE_STRING_LEN-1);
  end_ptr = name_ptr = temp_name;

  while (*name_ptr) {
    
    while (*name_ptr && isspace (*name_ptr)) ++name_ptr;
    end_ptr = name_ptr;
    if (strchr(end_ptr, ' ') ) {
      end_ptr = strchr(name_ptr, ' ');
      *end_ptr = '\0';
      end_ptr++;
    } else
      end_ptr += strlen(end_ptr);
    make_full_path(reqInfo->filename,name_ptr,ifile);
    if(stat(ifile,finfo) == -1) {
      if(! *end_ptr && (allow_options & OPT_INDEXES)) {
        if (pa[0]) {
          strcat(reqInfo->filename,pa);
	  strcat(reqInfo->url,pa);
	  log_reason(reqInfo,"file does not exist",reqInfo->filename);
	  die(reqInfo,SC_NOT_FOUND,reqInfo->url);
	}
	if ((reqInfo->method != M_GET) && (reqInfo->method != M_HEAD)) {
	  sprintf(error_msg,"%s to non-script",methods[reqInfo->method]);
	  die(reqInfo,SC_NOT_IMPLEMENTED,error_msg);
	}	
	index_directory(reqInfo);
	return;
      } else if (! *end_ptr) {
	log_reason(reqInfo,"(2) file permissions deny server access",
		   reqInfo->filename);
	die(reqInfo,SC_FORBIDDEN,reqInfo->url);
      }
    } else {
      strcpy(reqInfo->filename,ifile);
      probe_content_type(reqInfo,reqInfo->filename);
      if(!strcmp(content_type,CGI_MAGIC_TYPE))
	send_cgi(reqInfo,finfo,pa,allow_options);
      else
	send_file(reqInfo,finfo,pa, allow_options);
      return;
    }
    name_ptr = end_ptr;
  }	 
}

/* Search down given translated URL searching for actual file name and filling
   in path_args string.  Doesn't make any claims about file type, must be 
   handled elsewhere.
   Returns 0 on success, errno on failure
   */
int extract_path_info(per_request *reqInfo, char *path_args,
		       struct stat *finfo)
  {
    register int x,max;
    char t[HUGE_STRING_LEN];

    max=count_dirs(reqInfo->filename);
    for(x=max ; x > 0 ; x--) {
        make_dirstr(reqInfo->filename,x+1,t);
        if(!(stat(t,finfo))) {
	  int l=strlen(t);
	  strcat(path_args,&(reqInfo->filename[l]));
	  reqInfo->filename[l] = '\0';
	  reqInfo->url[strlen(reqInfo->url) - strlen(path_args)] = '\0';
	  return 0;
        }
    }
    return errno;
}

void send_fd_timed_out(int sigcode) 
{
    char errstr[MAX_STRING_LEN];

    if(exit_callback) (*exit_callback)();
    if (sigcode != SIGPIPE) {
	sprintf(errstr,"httpd: send timed out for %s, URL: %s", 
		gCurrentRequest->remote_name,gCurrentRequest->url);
    }
    else {
	sprintf(errstr,"httpd: send aborted for %s, URL: %s", 
		gCurrentRequest->remote_name,gCurrentRequest->url);
    }
    log_error(errstr,gCurrentRequest->hostInfo->error_log);
    log_transaction(gCurrentRequest);
    alarm(0);
    signal(SIGALRM,SIG_IGN);
    signal(SIGPIPE,SIG_IGN);
    CloseAll();  /* close all spurious file descriptors */

    if (!standalone) {
   	fclose(stdin); 
    	fclose(stdout); 
	exit(0);
    } else {
#ifdef NO_SIGLONGJMP
      longjmp(jmpbuffer,1);
#else
      siglongjmp(jmpbuffer,1);
#endif /* NO_SIGLONGJMP */
    }
}

/*
  We'll make it return the number of bytes sent
  so that we know if we need to send a body by default
*/
long send_fp(per_request *reqInfo, FILE *f, void (*onexit)())
{
    char buf[IOBUFSIZE];
    long total_bytes_sent;
    register int n,o,w;

    exit_callback = onexit;
    signal(SIGALRM,send_fd_timed_out);
    signal(SIGPIPE,send_fd_timed_out);

    total_bytes_sent = 0;
    fflush(reqInfo->out);
    while (1) {
        alarm(timeout);
        if((n=fread(buf,sizeof(char),IOBUFSIZE,f)) < 1) {
	   if (errno != EINTR) break;
        }

        o=0;
        if(reqInfo->bytes_sent != -1)
            reqInfo->bytes_sent += n;
        while(n) {
/*   Seems some systems have broken fwrite's (like AIX 3.2.5 on PowerPC)
 *   this should be a drop in replacement, maybe even be faster.
 *   For now, we'll just replace, but may have to #define one or the other
 *   depending on the system.
 */
/*            w=fwrite(&buf[o],sizeof(char),n,reqInfo->out); */
	    if ((w=write(fileno(reqInfo->out),&buf[o],n)) < 0) {
	      if (errno != EINTR) break;
	    }
            n-=w;
            o+=w;
	    total_bytes_sent += w;
        }
    }
/*    fflush(reqInfo->out); */
    alarm(0);
    signal(SIGALRM,SIG_IGN);
    signal(SIGPIPE,SIG_IGN);
    return total_bytes_sent;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.