Exemple #1
0
int add_cgi_env(request * req, const char *key, const char *value,
                int http_prefix)
{
    char *p;
    unsigned int prefix_len;

    if (http_prefix) {
        prefix_len = 5;
    } else {
        prefix_len = 0;
    }

    if (req->cgi_env_index < CGI_ENV_MAX) {
        p = env_gen_extra(key, value, prefix_len);
        if (!p) {
            log_error_doc(req);
            fprintf(stderr,
                    "Unable to generate additional CGI environment "
                    "variable -- ran out of memory!\n");
            return 0;
        }
        if (prefix_len)
            memcpy(p, "HTTP_", 5);
        req->cgi_env[req->cgi_env_index++] = p;
        return 1;
    }
    log_error_doc(req);
    fprintf(stderr, "Unable to generate additional CGI Environment "
            "variable \"%s%s=%s\" -- not enough space!\n",
            (prefix_len ? "HTTP_" : ""), key, value);
    return 0;
}
Exemple #2
0
int process_get(request * req)
{
    int bytes_written;
    volatile int bytes_to_write;

    bytes_to_write = req->filesize - req->filepos;
    if (bytes_to_write > SOCKETBUF_SIZE)
        bytes_to_write = SOCKETBUF_SIZE;


    if (sigsetjmp(env, 1) == 0) {
        handle_sigbus = 1;
        bytes_written = write(req->fd, req->data_mem + req->filepos,
                              bytes_to_write);
        handle_sigbus = 0;
        
    } else {
        
        log_error_doc(req);
        /* sending an error here is inappropriate
         * if we are here, the file is mmapped, and thus,
         * a content-length has been sent. If we send fewer bytes
         * the client knows there has been a problem.
         * We run the risk of accidentally sending the right number
         * of bytes (or a few too many) and the client
         * won't be the wiser.
         */
        req->status = DEAD;
        fprintf(stderr, "%sGot SIGBUS in write(2)!\n", get_commonlog_time());
        return 0;
    }

    if (bytes_written < 0) {
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;
        
        else {
            if (errno != EPIPE) {
                log_error_doc(req);
                
                perror("write");
                
            }
            req->status = DEAD;
            return 0;
        }
    }
    req->filepos += bytes_written;

    if (req->filepos == req->filesize) { 
        return 0;
    } else
        return 1;               
}
int process_get(request * req)
{
    int bytes_written;
    volatile int bytes_to_write;

    bytes_to_write = req->filesize - req->filepos;
    if (bytes_to_write > SOCKETBUF_SIZE)
        bytes_to_write = SOCKETBUF_SIZE;


    if (sigsetjmp(env, 1) == 0) {
        handle_sigbus = 1;
        bytes_written = write(req->fd, req->data_mem + req->filepos,
                              bytes_to_write);
        handle_sigbus = 0;
        /* OK, SIGBUS **after** this point is very bad! */
    } else {
        /* sigbus! */
        log_error_doc(req);
        /* sending an error here is inappropriate
         * if we are here, the file is mmapped, and thus,
         * a content-length has been sent. If we send fewer bytes
         * the client knows there has been a problem.
         * We run the risk of accidentally sending the right number
         * of bytes (or a few too many) and the client
         * won't be the wiser.
         */
        req->status = DEAD;
        fprintf(stderr, "%sGot SIGBUS in write(2)!\n", get_commonlog_time());
        return 0;
    }

    if (bytes_written < 0) {
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;
        /* request blocked at the pipe level, but keep going */
        else {
            if (errno != EPIPE) {
                log_error_doc(req);
                /* Can generate lots of log entries, */
                perror("write");
                /* OK to disable if your logs get too big */
            }
            req->status = DEAD;
            return 0;
        }
    }
    req->filepos += bytes_written;

    if (req->filepos == req->filesize) { /* EOF */
        return 0;
    } else
        return 1;               /* more to do */
}
Exemple #4
0
int write_from_pipe(request * req)
{
	int bytes_written, bytes_to_write = req->header_end - req->header_line;

	if (bytes_to_write == 0) {
		if (req->cgi_status == CGI_DONE)
			return 0;

		req->status = PIPE_READ;
		req->header_end = req->header_line = req->buffer;
		return 1;
	}

	bytes_written = write(req->fd, req->header_line, bytes_to_write);
	if (bytes_written == -1) {
		if (errno == EWOULDBLOCK || errno == EAGAIN)
			return -1;          /* request blocked at the pipe level, but keep going */
		else if (errno == EINTR)
			return 1;
		else {
			req->status = DEAD;
			send_r_error(req);  /* maybe superfluous */
			log_error_doc(req);
			perror("pipe write");
			return 0;
		}
	}

	req->header_line += bytes_written;
	req->filepos += bytes_written;

	return 1;
}
Exemple #5
0
int process_get(request * req)
{
    int bytes_written, bytes_to_write;

    bytes_to_write = req->filesize - req->filepos;
    if (bytes_to_write > SOCKETBUF_SIZE)
        bytes_to_write = SOCKETBUF_SIZE;

    bytes_written = write(req->fd, req->data_mem + req->filepos,
                          bytes_to_write);

    if (bytes_written < 0) {
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;
        /* request blocked at the pipe level, but keep going */
        else {
            if (errno != EPIPE) {
                log_error_doc(req);
                /* Can generate lots of log entries, */
                perror("write");
                /* OK to disable if your logs get too big */
            }
            req->status = DEAD;
            return 0;
        }
    }
    req->filepos += bytes_written;

    if (req->filepos == req->filesize) { /* EOF */
        return 0;
    } else
        return 1;               /* more to do */
}
Exemple #6
0
int req_write(request * req, const char *msg)
{
    unsigned int msg_len;

    msg_len = strlen(msg);

    if (!msg_len || req->status > DONE)
        return req->buffer_end;

    if (req->buffer_end + msg_len > BUFFER_SIZE) {
        log_error_doc(req);
        fprintf(stderr, "There is not enough room in the buffer to"
                " copy %u bytes (%d available). Shutting down connection.\n",
                msg_len,
                BUFFER_SIZE - req->buffer_end);
#ifdef FASCIST_LOGGING
        *(req->buffer + req->buffer_end) = '\0';
        fprintf(stderr, "The request looks like this:\n%s\n",
                req->buffer);
#endif
        req->status = DEAD;
        return -1;
    }
    memcpy(req->buffer + req->buffer_end, msg, msg_len);
    req->buffer_end += msg_len;
    return req->buffer_end;
}
Exemple #7
0
void update_blocked(struct pollfd pfd1[])
{
    request *current, *next = NULL;
    time_t time_since;
    int revents;

    time(&current_time);
    for (current = request_block; current; current = next) {
        time_since = current_time - current->time_last;
        next = current->next;

        // FIXME::  the first below has the chance of leaking memory!
        //  (setting status to DEAD not DONE....)
        /* hmm, what if we are in "the middle" of a request and not
         * just waiting for a new one... perhaps check to see if anything
         * has been read via header position, etc... */
        revents = pfds[current->pollfd_id].revents;
        if (revents & (POLLNVAL|POLLERR)) {
            /* socket returned error */
            log_error_time();
            fprintf(stderr, "Socket %d returned "
                    "POLLNVAL|POLLERR (%s%s) ",
                    current->fd,
                    revents & POLLNVAL ? "POLLNVAL ":"",
                    revents & POLLERR ? "POLLERR ":"");
            current->status = DEAD;
        } else if (time_since > REQUEST_TIMEOUT) {
            log_error_doc(current);
            fputs("connection timed out\n", stderr);
            current->status = TIMED_OUT; /* connection timed out */
        } else if (current->kacount < ka_max && /* we *are* in a keepalive */
            (time_since >= ka_timeout) && /* ka timeout has passed */
            !current->logline) { /* haven't read anything yet */
            log_error_doc(current);
            fputs("connection timed out\n", stderr);
            current->status = TIMED_OUT; /* connection timed out */
        } else if (revents == 0) {                /* still blocked */
            pfd1[pfd_len].fd = pfds[current->pollfd_id].fd;
            pfd1[pfd_len].events = pfds[current->pollfd_id].events;
            current->pollfd_id = pfd_len++;
            continue;
        }
        ready_request(current);
    }
}
Exemple #8
0
int process_get(request * req)
{
	int bytes_written, bytes_to_write;

	bytes_to_write = req->filesize - req->filepos;
#ifdef USE_NLS
	if (req->method != M_HEAD)
	{
		if (bytes_to_write>BYTES_TO_WRITE)
			bytes_to_write = BYTES_TO_WRITE;
	
		if (req->bytesconverted<req->filepos)
			req->bytesconverted = req->filepos;
	
		if (req->cp_table)
		{
			nls_convert(req->data_mem + req->bytesconverted,req->cp_table,
					req->filepos + bytes_to_write - req->bytesconverted );
			req->bytesconverted = req->filepos + bytes_to_write;
		}
	}
#endif

#ifdef SERVER_SSL
	if(req->ssl == NULL){
#endif /*SERVER_SSL*/
	bytes_written = write(req->fd, req->data_mem + req->filepos,
						  bytes_to_write);
#ifdef SERVER_SSL
	}else{
		bytes_written = SSL_write(req->ssl, req->data_mem + req->filepos, bytes_to_write);
#if 0
		printf("SSL_write\n");
#endif /*0*/
	}
#endif /*SERVER_SSL*/
	if (bytes_written == -1) {
		if (errno == EWOULDBLOCK || errno == EAGAIN)
			return -1;			/* request blocked at the pipe level, but keep going */
		else {
			if (errno != EPIPE) {
				log_error_doc(req);	/* Can generate lots of log entries, */
#if 0
				perror("write");	/* OK to disable if your logs get too big */
#endif
			}
			return 0;
		}
	}
	req->filepos += bytes_written;
	if (req->filepos == req->filesize)	/* EOF */
		return 0;
	else
		return 1;				/* more to do */
}
Exemple #9
0
int io_shuffle_sendfile(request * req)
{
    int bytes_written;
    unsigned int bytes_to_write;

    if (req->method == M_HEAD) {
        return complete_response(req);
    }

    bytes_to_write = (req->ranges->stop - req->ranges->start) + 1;

    if (bytes_to_write > system_bufsize)
        bytes_to_write = system_bufsize;

retrysendfile:
    if (bytes_to_write == 0) {
        /* shouldn't get here, but... */
        bytes_written = 0;
    } else {
        bytes_written = sendfile(req->fd, req->data_fd, &(req->filepos), bytes_to_write);
        if (bytes_written < 0) {
            if (errno == EWOULDBLOCK || errno == EAGAIN) {
                return -1;          /* request blocked at the pipe level, but keep going */
            } else if (errno == EINTR) {
                goto retrysendfile;
            } else {
                req->status = DEAD;
#ifdef QUIET_DISCONNECT
                if (0)
#else
                if (errno != EPIPE && errno != ECONNRESET)
#endif
                {
                    log_error_doc(req);
                    perror("sendfile write");
                }
            }
            return 0;
        } /* bytes_written */
    } /* bytes_to_write */

    /* sendfile automatically updates req->filepos,
     * don't touch!
     * req->filepos += bytes_written;
     */
    req->ranges->start += bytes_written;
    req->bytes_written += bytes_written;

    if (req->ranges->stop + 1 == req->ranges->start) {
        return complete_response(req);
    }
    return 1;
}
Exemple #10
0
int read_from_pipe(request * req)
{
	int bytes_read, bytes_to_read =
		BUFFER_SIZE - (req->header_end - req->buffer);

	if (bytes_to_read == 0) {   /* buffer full */
		if (req->cgi_status == CGI_PARSE) { /* got+parsed header */
			req->cgi_status = CGI_BUFFER;
			*req->header_end = '\0'; /* points to end of read data */
			/* Could the above statement overwrite data???
			   No, because req->header_end points to where new data
			   should begin, not where old data is.
			 */
			return process_cgi_header(req); /* cgi_status will change */
		}
		req->status = PIPE_WRITE;
		return 1;
	}

	bytes_read = read(req->data_fd, req->header_end, bytes_to_read);

	if (bytes_read == -1) {
		if (errno == EINTR)
			return 1;
		else if (errno == EWOULDBLOCK || errno == EAGAIN)
			return -1;          /* request blocked at the pipe level, but keep going */
		else {
			req->status = DEAD;
			log_error_doc(req);
			perror("pipe read");
			return 0;
		}
	} else if (bytes_read == 0) { /* eof, write rest of buffer */
		req->status = PIPE_WRITE;
		if (req->cgi_status == CGI_PARSE) { /* hasn't processed header yet */
			req->cgi_status = CGI_DONE;
			*req->header_end = '\0'; /* points to end of read data */
			return process_cgi_header(req); /* cgi_status will change */
		}
		req->cgi_status = CGI_DONE;
		return 1;
	}
	req->header_end += bytes_read;
	return 1;
}
Exemple #11
0
int process_header_end(request * req)
{
    if (!req->logline) {
        send_r_error(req);
        return 0;
    }

    /* Percent-decode request */
    if (unescape_uri(req->request_uri, &(req->query_string)) == 0) {
        log_error_doc(req);
        fputs("Problem unescaping uri\n", stderr);
        send_r_bad_request(req);
        return 0;
    }

    /* clean pathname */
    clean_pathname(req->request_uri);

    if (req->request_uri[0] != '/') {
        send_r_bad_request(req);
        return 0;
    }

    if (translate_uri(req) == 0) { /* unescape, parse uri */
        SQUASH_KA(req);
        return 0;               /* failure, close down */
    }

    if (req->method == M_POST) {
        req->post_data_fd = create_temporary_file(1, NULL, 0);
        if (req->post_data_fd == 0)
            return(0);
        return(1); /* success */
    }

    if (req->is_cgi) {
        return init_cgi(req);
    }

    req->status = WRITE;
    return init_get(req);       /* get and head */
}
Exemple #12
0
int write_from_pipe(request * req)
{
    int bytes_written;
    unsigned int bytes_to_write = req->header_end - req->header_line;

    if (bytes_to_write == 0) {
        if (req->cgi_status == CGI_DONE)
            return 0;

        req->status = PIPE_READ;
        req->header_end = req->header_line = req->buffer;
        return 1;
    }

    bytes_written = write(req->fd, req->header_line, bytes_to_write);

    if (bytes_written == -1) {
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;          /* request blocked at the pipe level, but keep going */
        else if (errno == EINTR)
            return 1;
        else {
            req->status = DEAD;
            log_error_doc(req);
            perror("pipe write");
            return 0;
        }
    }

    req->header_line += bytes_written;
    req->filepos += bytes_written;
    req->bytes_written += bytes_written;

    /* if there won't be anything to write next time, switch state */
    if ((unsigned) bytes_written == bytes_to_write) {
        req->status = PIPE_READ;
        req->header_end = req->header_line = req->buffer;
    }

    return 1;
}
Exemple #13
0
Fichier : pipe.c Projet : gpg/boa
int io_shuffle(request * req)
{
    off_t bytes_to_read;
    off_t bytes_written, bytes_to_write;

    if (req->method == M_HEAD) {
        return complete_response(req);
    }

    /* FIXME: This function doesn't take into account req->filesize
     * when *reading* into the buffer. Grr.
     * June 09, 2004: jdn, I don't think it's a problem anymore,
     * because the ranges are verified against the filesize,
     * and we cap bytes_to_read at bytes_to_write.
     */
    bytes_to_read = BUFFER_SIZE - req->buffer_end - 256;

    bytes_to_write = (req->ranges->stop - req->ranges->start) + 1;

    if (bytes_to_read > bytes_to_write)
        bytes_to_read = bytes_to_write;

    if (bytes_to_read > 0 && req->data_fd) {
        off_t bytes_read;
        off_t temp;

        temp = lseek(req->data_fd, req->ranges->start, SEEK_SET);
        if (temp < 0) {
            req->status = DEAD;
            log_error_doc(req);
            perror("ioshuffle lseek");
            return 0;
        }

      restartread:
        bytes_read =
            read(req->data_fd, req->buffer + req->buffer_end,
                 bytes_to_read);

        if (bytes_read == -1) {
            if (errno == EINTR)
                goto restartread;
            else if (errno == EWOULDBLOCK || errno == EAGAIN) {
                /* not a fatal error, don't worry about it */
                /* buffer is empty, we're blocking on read! */
                if (req->buffer_end - req->buffer_start == 0)
                    return -1;
            } else {
                req->status = DEAD;
                log_error_doc(req);
                perror("ioshuffle read");
                return 0;
            }
        } else if (bytes_read == 0) { /* eof, write rest of buffer */
            close(req->data_fd);
            req->data_fd = 0;
        } else {
            req->buffer_end += bytes_read;

            req->ranges->start += bytes_read;

            if ((req->ranges->stop + 1 - req->ranges->start) == 0) {
                return complete_response(req);
            }
        }
    }

    bytes_to_write = req->buffer_end - req->buffer_start;
    if (bytes_to_write == 0) {
        if (req->data_fd == 0)
            return 0;           /* done */
        req->buffer_end = req->buffer_start = 0;
        return 1;
    }

  restartwrite:
    bytes_written =
        write(req->fd, req->buffer + req->buffer_start, bytes_to_write);

    if (bytes_written == -1) {
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;          /* request blocked at the pipe level, but keep going */
        else if (errno == EINTR)
            goto restartwrite;
        else {
            req->status = DEAD;
            log_error_doc(req);
            perror("ioshuffle write");
            return 0;
        }
    } else if (bytes_written == 0) {
    }

    req->buffer_start += bytes_written;
    req->bytes_written += bytes_written;

    if (bytes_to_write == bytes_written) {
        req->buffer_end = req->buffer_start = 0;
    }

    return 1;
}
Exemple #14
0
Fichier : pipe.c Projet : gpg/boa
int io_shuffle_sendfile(request * req)
{
    off_t sendfile_offset;
    off_t bytes_written;
    off_t bytes_to_write;

    if (req->method == M_HEAD) {
        return complete_response(req);
    }

    /* XXX trouble if range is exactly 4G on a 32-bit machine? */
    bytes_to_write = (req->ranges->stop - req->ranges->start) + 1;

    if (bytes_to_write > system_bufsize)
        bytes_to_write = system_bufsize;

retrysendfile:
    if (bytes_to_write == 0) {
        /* shouldn't get here, but... */
        bytes_written = 0;
    } else {
	/* arg 3 of sendfile should have type "off_t *"
	 * struct range element start has type "unsigned long"
	 * Where POSIX got the idea that an offset into a file
	 * should be signed, I'll never know.
	 */
	sendfile_offset = req->ranges->start;
	if (sendfile_offset < 0) {
		req->status = DEAD;
		log_error_doc(req);
		fprintf(stderr, "impossible offset (%lu) requested of sendfile\n",
				 req->ranges->start);
		return 0;
	}
        bytes_written = sendfile(req->fd, req->data_fd,
                                 &sendfile_offset,
                                 bytes_to_write);
	if (sendfile_offset < 0) {
		req->status = DEAD;
		log_error_doc(req);
		fprintf(stderr,
			"bad craziness in sendfile offset, returned %ld\n",
			(long) sendfile_offset);
		return 0;
	}
	req->ranges->start = sendfile_offset;
        if (bytes_written < 0) {
	    if (errno == ENOSYS) {
		return io_shuffle(req);
	    } else if (errno == EWOULDBLOCK || errno == EAGAIN) {
                /* request blocked at the pipe level, but keep going */
                return -1;
            } else if (errno == EINTR) {
                goto retrysendfile;
            } else {
                req->status = DEAD;
#ifdef QUIET_DISCONNECT
                if (0)
#else
                if (errno != EPIPE && errno != ECONNRESET)
#endif
                {
                    log_error_doc(req);
                    perror("sendfile write");
                }
            }
            return 0;
        } else if (bytes_written == 0) {
            /* not sure how to handle this.
             * For now, treat it like it is blocked.
             */
            return -1;
        }/* bytes_written */
    } /* bytes_to_write */

    /* sendfile automatically updates req->ranges->start
     * don't touch!
     * req->ranges->start += bytes_written;
     */
    req->bytes_written += bytes_written;

    if (req->ranges->stop + 1 <= req->ranges->start) {
        return complete_response(req);
    }
    return 1;
}
Exemple #15
0
int init_cgi(request * req)
{
#ifndef EXCLUDE_CGI
    int child_pid;
    int pipes[2];
    int use_pipes = 0;
#endif

    SQUASH_KA(req);

#ifndef EXCLUDE_CGI
    if (req->cgi_type) {
        if (complete_env(req) == 0) {
            return 0;
        }
    }
    DEBUG(DEBUG_CGI_ENV) {
        int i;
        for (i = 0; i < req->cgi_env_index; ++i)
            log_error_time();
            fprintf(stderr, "%s - environment variable for cgi: \"%s\"\n",
                    __FILE__, req->cgi_env[i]);
    }

    /* we want to use pipes whenever it's a CGI or directory */
    /* otherwise (NPH, gunzip) we want no pipes */
    if (req->cgi_type == CGI ||
        (!req->cgi_type &&
         (req->pathname[strlen(req->pathname) - 1] == '/'))) {
        use_pipes = 1;
        if (pipe(pipes) == -1) {
            log_error_doc(req);
            perror("pipe");
            return 0;
        }

        /* set the read end of the socket to non-blocking */
        if (set_nonblock_fd(pipes[0]) == -1) {
            log_error_doc(req);
            perror("cgi-fcntl");
            close(pipes[0]);
            close(pipes[1]);
            return 0;
        }
    }

    child_pid = fork();
    switch (child_pid) {
    case -1:
        /* fork unsuccessful */
        /* FIXME: There is a problem here. send_r_error (called by
         * boa_perror) would work for NPH and CGI, but not for GUNZIP.  
         * Fix that. 
         */
        boa_perror(req, "fork failed");
        if (use_pipes) {
            close(pipes[0]);
            close(pipes[1]);
        }
        return 0;
        break;
    case 0:
        /* child */
        reset_signals();

        if (req->cgi_type == CGI || req->cgi_type == NPH) {
            char *c;
            unsigned int l;
            char *newpath, *oldpath;

            c = strrchr(req->pathname, '/');
            if (!c) {
                /* there will always be a '.' */
                log_error_doc(req);
                fprintf(stderr,
                        "unable to find '/' in req->pathname: \"%s\"\n",
                        req->pathname);
                if (use_pipes)
                    close(pipes[1]);
                _exit(EXIT_FAILURE);
            }

            *c = '\0';

            if (chdir(req->pathname) != 0) {
                int saved_errno = errno;
                log_error_doc(req);
                fprintf(stderr, "Could not chdir to \"%s\":",
                        req->pathname);
                errno = saved_errno;
                perror("chdir");
                if (use_pipes)
                    close(pipes[1]);
                _exit(EXIT_FAILURE);
            }

            oldpath = req->pathname;
            req->pathname = ++c;
            l = strlen(req->pathname) + 3;
            /* prefix './' */
            newpath = malloc(sizeof (char) * l);
            if (!newpath) {
                /* there will always be a '.' */
                log_error_doc(req);
                perror("unable to malloc for newpath");
                if (use_pipes)
                    close(pipes[1]);
                _exit(EXIT_FAILURE);
            }
            newpath[0] = '.';
            newpath[1] = '/';
            memcpy(&newpath[2], req->pathname, l - 2); /* includes the trailing '\0' */
            free(oldpath);
            req->pathname = newpath;
        }
        if (use_pipes) {
            /* close the 'read' end of the pipes[] */
            close(pipes[0]);
            /* tie CGI's STDOUT to our write end of pipe */
            if (dup2(pipes[1], STDOUT_FILENO) == -1) {
                log_error_doc(req);
                perror("dup2 - pipes");
                _exit(EXIT_FAILURE);
            }
            close(pipes[1]);
        } else {
            /* tie stdout to socket */
            if (dup2(req->fd, STDOUT_FILENO) == -1) {
                log_error_doc(req);
                perror("dup2 - fd");
                _exit(EXIT_FAILURE);
            }
            close(req->fd);
        }
        /* Switch socket flags back to blocking */
        if (set_block_fd(STDOUT_FILENO) == -1) {
            log_error_doc(req);
            perror("cgi-fcntl");
            _exit(EXIT_FAILURE);
        }
        /* tie post_data_fd to POST stdin */
        if (req->method == M_POST) { /* tie stdin to file */
// davidhsu ----------------------
#ifndef NEW_POST
            lseek(req->post_data_fd, SEEK_SET, 0);
            dup2(req->post_data_fd, STDIN_FILENO);
            close(req->post_data_fd);
#endif
//-------------------------------	
			
        }

#ifdef USE_SETRLIMIT
        /* setrlimit stuff.
         * This is neat!
         * RLIMIT_STACK    max stack size
         * RLIMIT_CORE     max core file size
         * RLIMIT_RSS      max resident set size
         * RLIMIT_NPROC    max number of processes
         * RLIMIT_NOFILE   max number of open files
         * RLIMIT_MEMLOCK  max locked-in-memory address space
         * RLIMIT_AS       address space (virtual memory) limit
         *
         * RLIMIT_CPU      CPU time in seconds
         * RLIMIT_DATA     max data size
         *
         * Currently, we only limit the CPU time and the DATA segment
         * We also "nice" the process.
         *
         * This section of code adapted from patches sent in by Steve Thompson
         * (no email available)
         */

        {
            struct rlimit rl;
            int retval;

            if (cgi_rlimit_cpu) {
                rl.rlim_cur = rl.rlim_max = cgi_rlimit_cpu;
                retval = setrlimit(RLIMIT_CPU, &rl);
                if (retval == -1) {
                    log_error_time();
                    fprintf(stderr,
                            "setrlimit(RLIMIT_CPU,%d): %s\n",
                            rlimit_cpu, strerror(errno));
                    _exit(EXIT_FAILURE);
                }
            }

            if (cgi_limit_data) {
                rl.rlim_cur = rl.rlim_max = cgi_rlimit_data;
                retval = setrlimit(RLIMIT_DATA, &rl);
                if (retval == -1) {
                    log_error_time();
                    fprintf(stderr,
                            "setrlimit(RLIMIT_DATA,%d): %s\n",
                            rlimit_data, strerror(errno));
                    _exit(EXIT_FAILURE);
                }
            }

            if (cgi_nice) {
                retval = nice(cgi_nice);
                if (retval == -1) {
                    log_error_time();
                    perror("nice");
                    _exit(EXIT_FAILURE);
                }
            }
        }
#endif

        umask(cgi_umask);       /* change umask *again* u=rwx,g=rxw,o= */

        /*
         * tie STDERR to cgi_log_fd
         * cgi_log_fd will automatically close, close-on-exec rocks!
         * if we don't tie STDERR (current log_error) to cgi_log_fd,
         *  then we ought to tie it to /dev/null
         *  FIXME: we currently don't tie it to /dev/null, we leave it
         *  tied to whatever 'error_log' points to.  This means CGIs can
         *  scribble on the error_log, probably a bad thing.
         */
        if (cgi_log_fd) {
            dup2(cgi_log_fd, STDERR_FILENO);
        }

        if (req->cgi_type) {
            char *aargv[CGI_ARGC_MAX + 1];
            create_argv(req, aargv);
            execve(req->pathname, aargv, req->cgi_env);
        } else {
            if (req->pathname[strlen(req->pathname) - 1] == '/')
                execl(dirmaker, dirmaker, req->pathname, req->request_uri,
                      (void *) NULL);
#ifdef GUNZIP
            else
                execl(GUNZIP, GUNZIP, "--stdout", "--decompress",
                      req->pathname, (void *) NULL);
#endif
        }
        /* execve failed */
        log_error_doc(req);
        fprintf(stderr, "Unable to execve/execl pathname: \"%s\"",
                req->pathname);
        perror("");
        _exit(EXIT_FAILURE);
        break;

    default:
        /* parent */
        /* if here, fork was successful */
        if (verbose_cgi_logs) {
            log_error_time();
            fprintf(stderr, "Forked child \"%s\" pid %d\n",
                    req->pathname, child_pid);
        }

        if (req->method == M_POST) {

// davidhsu ----------------
#ifndef NEW_POST
             close(req->post_data_fd); /* child closed it too */
             req->post_data_fd = 0;
#else			 
		if (req->post_data) {
			free(req->post_data);
			req->post_data = NULL;
		}
		req->post_data_len = 0;
		req->post_data_idx = 0;
#endif		
//------------------------

        }

        /* NPH, GUNZIP, etc... all go straight to the fd */
        if (!use_pipes)
            return 0;

        close(pipes[1]);
        req->data_fd = pipes[0];

        req->status = PIPE_READ;
        if (req->cgi_type == CGI) {
            req->cgi_status = CGI_PARSE; /* got to parse cgi header */
            /* for cgi_header... I get half the buffer! */
            req->header_line = req->header_end =
                (req->buffer + BUFFER_SIZE / 2);
        } else {
            req->cgi_status = CGI_BUFFER;
            /* I get all the buffer! */
            req->header_line = req->header_end = req->buffer;
        }

        /* reset req->filepos for logging (it's used in pipe.c) */
        /* still don't know why req->filesize might be reset though */
        req->filepos = 0;
        break;
    }
#endif //!EXCLUDE_CGI

    return 1;
}
Exemple #16
0
static void create_argv(request * req, char **aargv)
{
    char *p, *q, *r;
    int aargc;

    q = req->query_string;
    aargv[0] = req->pathname;

    /* here, we handle a special "indexed" query string.
     * Taken from the CGI/1.1 SPEC:
     * This is identified by a GET or HEAD request with a query string
     * with no *unencoded* '=' in it.
     * For such a request, I'm supposed to parse the search string
     * into words, according to the following rules:

     search-string = search-word *( "+" search-word )
     search-word   = 1*schar
     schar         = xunreserved | escaped | xreserved
     xunreserved   = alpha | digit | xsafe | extra
     xsafe         = "$" | "-" | "_" | "."
     xreserved     = ";" | "/" | "?" | ":" | "@" | "&"

     After parsing, each word is URL-decoded, optionally encoded in a system
     defined manner, and then the argument list
     is set to the list of words.


     Thus, schar is alpha|digit|"$"|"-"|"_"|"."|";"|"/"|"?"|":"|"@"|"&"

     As of this writing, escape.pl escapes the following chars:

     "-", "_", ".", "!", "~", "*", "'", "(", ")",
     "0".."9", "A".."Z", "a".."z",
     ";", "/", "?", ":", "@", "&", "=", "+", "\$", ","

     Which therefore means
     "=", "+", "~", "!", "*", "'", "(", ")", ","
     are *not* escaped and should be?
     Wait, we don't do any escaping, and nor should we.
     According to the RFC draft, we unescape and then re-escape
     in a "system defined manner" (here: none).

     The CGI/1.1 draft (03, latest is 1999???) is very unclear here.

     I am using the latest published RFC, 2396, for what does and does
     not need escaping.

     Since boa builds the argument list and does not call /bin/sh,
     (boa uses execve for CGI)
     */

    if (q && !strchr(q, '=')) {
        /* we have an 'index' style */
        q = strdup(q);
        if (!q) {
            log_error_doc(req);
            fputs("unable to strdup 'q' in create_argv!\n", stderr);
            _exit(EXIT_FAILURE);
        }
        for (aargc = 1; q && (aargc < CGI_ARGC_MAX);) {
            r = q;
            /* for an index-style CGI, + is used to separate arguments
             * an escaped '+' is of no concern to us
             */
            if ((p = strchr(q, '+'))) {
                *p = '\0';
                q = p + 1;
            } else {
                q = NULL;
            }
            if (unescape_uri(r, NULL)) {
                /* printf("parameter %d: %s\n",aargc,r); */
                aargv[aargc++] = r;
            }
        }
        aargv[aargc] = NULL;
    } else {
        aargv[1] = NULL;
    }
}
Exemple #17
0
static int complete_env(request * req)
#endif
{
#ifndef EXCLUDE_CGI
    int i;

    for (i = 0; common_cgi_env[i]; i++)
        req->cgi_env[i] = common_cgi_env[i];

    {
        const char *w;
        switch (req->method) {
        case M_POST:
            w = "POST";
            break;
        case M_HEAD:
            w = "HEAD";
            break;
        case M_GET:
            w = "GET";
            break;
        default:
            w = "UNKNOWN";
            break;
        }
        my_add_cgi_env(req, "REQUEST_METHOD", w);
    }

    if (req->header_host)
        my_add_cgi_env(req, "HTTP_HOST", req->header_host);
    my_add_cgi_env(req, "SERVER_ADDR", req->local_ip_addr);
    my_add_cgi_env(req, "SERVER_PROTOCOL",
                   http_ver_string(req->http_version));
    my_add_cgi_env(req, "REQUEST_URI", req->request_uri);

    if (req->path_info)
        my_add_cgi_env(req, "PATH_INFO", req->path_info);

    if (req->path_translated)
        /* while path_translated depends on path_info,
         * there are cases when path_translated might
         * not exist when path_info does
         */
        my_add_cgi_env(req, "PATH_TRANSLATED", req->path_translated);

    my_add_cgi_env(req, "SCRIPT_NAME", req->script_name);

    if (req->query_string)
        my_add_cgi_env(req, "QUERY_STRING", req->query_string);
    my_add_cgi_env(req, "REMOTE_ADDR", req->remote_ip_addr);
    my_add_cgi_env(req, "REMOTE_PORT", simple_itoa(req->remote_port));

    if (req->method == M_POST) {
        if (req->content_type) {
            my_add_cgi_env(req, "CONTENT_TYPE", req->content_type);
        } else {
            my_add_cgi_env(req, "CONTENT_TYPE", default_type);
        }
        if (req->content_length) {
            my_add_cgi_env(req, "CONTENT_LENGTH", req->content_length);
        }
    }
#ifdef ACCEPT_ON
    if (req->accept[0])
        my_add_cgi_env(req, "HTTP_ACCEPT", req->accept);
#endif

    if (req->cgi_env_index < CGI_ENV_MAX + 1) {
        req->cgi_env[req->cgi_env_index] = NULL; /* terminate */
        return 1;
    }
    log_error_doc(req);
    fprintf(stderr, "Not enough space in CGI environment for remainder"
            " of variables.\n");

#endif //!EXCLUDE_CGI

    return 0;
}
Exemple #18
0
Fichier : get.c Projet : gpg/boa
static int index_directory(request * req, char *dest_filename)
{
    DIR *request_dir;
    FILE *fdstream;
    struct dirent *dirbuf;
    off_t bytes = 0;
    char *escname = NULL;

    if (chdir(req->pathname) == -1) {
        if (errno == EACCES || errno == EPERM) {
            send_r_forbidden(req);
        } else {
            log_error_doc(req);
            perror("chdir");
            send_r_bad_request(req);
        }
        return -1;
    }

    request_dir = opendir(".");
    if (request_dir == NULL) {
        int errno_save = errno;
        send_r_error(req);
        log_error_doc(req);
        fprintf(stderr, "opendir failed on directory \"%s\": ", req->pathname);
        errno = errno_save;
        perror("opendir");
        return -1;
    }

    fdstream = fopen(dest_filename, "w");
    if (fdstream == NULL) {
        boa_perror(req, "dircache fopen");
        closedir(request_dir);
        return -1;
    }

    bytes += fprintf(fdstream,
                     "<HTML><HEAD>\n<TITLE>Index of %s</TITLE>\n</HEAD>\n\n",
                     req->request_uri);
    bytes += fprintf(fdstream, "<BODY>\n\n<H2>Index of %s</H2>\n\n<PRE>\n",
                     req->request_uri);

    while ((dirbuf = readdir(request_dir))) {
        if (!strcmp(dirbuf->d_name, "."))
            continue;

        if (!strcmp(dirbuf->d_name, "..")) {
            bytes += fprintf(fdstream,
                             " [DIR] <A HREF=\"../\">Parent Directory</A>\n");
            continue;
        }

        /* FIXME: ought to use (as-yet unwritten) html_escape_string */
        escname = escape_string(dirbuf->d_name, NULL);
        if (escname != NULL) {
            bytes += fprintf(fdstream, " <A HREF=\"%s\">%s</A>\n",
                             escname, dirbuf->d_name);
            free(escname);
            escname = NULL;
        }
    }
    closedir(request_dir);
    bytes += fprintf(fdstream, "</PRE>\n\n</BODY>\n</HTML>\n");

    fclose(fdstream);

    if (chdir(server_root) == -1)
        perror("chdir to server root failed");

    req->filesize = bytes;      /* for logging transfer size */
    return 0;                   /* success */
}
Exemple #19
0
int process_get(request * req)
{
    int bytes_written;
    volatile int bytes_to_write;

    bytes_to_write = req->filesize - req->filepos;
    if (bytes_to_write > SOCKETBUF_SIZE)
        bytes_to_write = SOCKETBUF_SIZE;


    if (sigsetjmp(env, 1) == 0) {
        handle_sigbus = 1;
#ifdef SERVER_SSL
	if(req->ssl == NULL){
#endif /*SERVER_SSL*/
        bytes_written = write(req->fd, req->data_mem + req->filepos,
                              bytes_to_write);
#ifdef SERVER_SSL
	}else{
		bytes_written = SSL_write(req->ssl, req->data_mem + req->filepos, bytes_to_write);
#if 0
		printf("SSL_write\n");
#endif /*0*/
	}
#endif /*SERVER_SSL*/
        handle_sigbus = 0;
        /* OK, SIGBUS **after** this point is very bad! */
    } else {
        /* sigbus! */
        log_error_doc(req);
        /* sending an error here is inappropriate
         * if we are here, the file is mmapped, and thus,
         * a content-length has been sent. If we send fewer bytes
         * the client knows there has been a problem.
         * We run the risk of accidentally sending the right number
         * of bytes (or a few too many) and the client
         * won't be the wiser.
         */
        req->status = DEAD;
        fprintf(stderr, "%sGot SIGBUS in write(2)!\n", get_commonlog_time());
        return 0;
    }

    if (bytes_written < 0) {
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;
        /* request blocked at the pipe level, but keep going */
        else {
            if (errno != EPIPE) {
                log_error_doc(req);
                /* Can generate lots of log entries, */
                perror("write");
                /* OK to disable if your logs get too big */
            }
            req->status = DEAD;
            return 0;
        }
    }
    req->filepos += bytes_written;
	req->busy_flag = BUSY_FLAG_AUDIO|BUSY_FLAG_VIDEO;
    if (req->filepos == req->filesize) { /* EOF */

#ifdef DAVINCI_IPCAM
        if (req->http_stream == URI_STREAM_MJPEG) {
#if 1
#if 0
            while (audio_get(req) > 0);
            if (req->audio_length >= AUDIO_SEND_SIZE) {
                audio_send(req);
                return 1;
            }
#else
            if (audio_get(req, FMT_MJPEG) > 0)
			return 1;
#endif
#else
		req->busy_flag &= ~BUSY_FLAG_AUDIO;
#endif
            if (req->serial_lock) {
                GetAVData(AV_OP_UNLOCK_MJPEG, req->serial_lock, NULL);
                req->serial_lock = 0;
            }
            GetAVData(AV_OP_GET_MJPEG_SERIAL, -1, &req->av_data);
            if (req->av_data.serial < req->serial_book) {
                req->busy_flag &= ~BUSY_FLAG_VIDEO;
                return 1;
            }
            GetAVData(AV_OP_LOCK_MJPEG, req->av_data.serial, &req->av_data );
            req->data_mem = req->av_data.ptr;
            req->filesize = req->av_data.size+16;
            req->filepos = 0;
            req->serial_lock = req->av_data.serial;
            req->serial_book = req->av_data.serial+1;
            reset_output_buffer(req);
            req_write(req, "\r\n");
            print_mjpeg_headers(req);
            return 1;
        }
        if (req->http_stream == URI_STREAM_MPEG4 || req->http_stream == URI_STREAM_AVC) {
            int ret;
#if 1
#if 0
		while (audio_get(req) > 0);
		if (req->audio_length > AUDIO_SEND_SIZE) {
			req->busy_flag |= BUSY_FLAG_AUDIO;
			audio_send(req);
      		      return 1;
		}
#else
            if (audio_get(req, FMT_MPEG4) > 0)
			return 1;
#endif
#else
		req->busy_flag &= ~BUSY_FLAG_AUDIO;
#endif
            ret = GetAVData(AV_OP_LOCK_MP4, req->serial_book, &req->av_data);
            if (ret == RET_SUCCESS) {
                GetAVData(AV_OP_UNLOCK_MP4, req->serial_lock, NULL);
                req->data_mem = req->av_data.ptr;
                req->filesize = req->av_data.size+16;
                req->filepos = 0;

                req->serial_lock = req->av_data.serial;
                req->serial_book = req->av_data.serial+1;

                reset_output_buffer(req);
                req_write(req, "\r\n");
				if (req->http_stream == URI_STREAM_AVC)
                { print_avc_headers(req); }
				else
				{ print_mpeg4_headers(req); }
                return 1;
            }
            else if (ret == RET_NO_VALID_DATA) {
                req->busy_flag &= ~BUSY_FLAG_VIDEO;
                return 1;
            }
            else {
                GetAVData(AV_OP_GET_MPEG4_SERIAL, -1, &req->av_data );
                req->serial_book = req->av_data.serial;
                dbg("lock error ret=%d\n", ret);
                return 1;
            }
        }
        if (req->http_stream == URI_STREAM_MPEG4CIF || req->http_stream == URI_STREAM_AVCCIF) {
            int ret;
#if 1
#if 0
		while (audio_get(req) > 0);
		if (req->audio_length > AUDIO_SEND_SIZE) {
			audio_send(req);
      		      return 1;
		}
#else
            if (audio_get(req, FMT_MPEG4_EXT) > 0)
			return 1;
#endif
#else
		req->busy_flag &= ~BUSY_FLAG_AUDIO;
#endif
            ret = GetAVData(AV_OP_LOCK_MP4_CIF, req->serial_book, &req->av_data);
            if (ret == RET_SUCCESS) {
                GetAVData(AV_OP_UNLOCK_MP4_CIF, req->serial_lock, NULL);
                req->data_mem = req->av_data.ptr;
                req->filesize = req->av_data.size+16;
                req->filepos = 0;

                req->serial_lock = req->av_data.serial;
                req->serial_book = req->av_data.serial+1;

                reset_output_buffer(req);
                req_write(req, "\r\n");
				if (req->http_stream == URI_STREAM_AVCCIF)
                { print_avc_headers(req); }
				else
				{ print_mpeg4_headers(req); }
                return 1;
            }
            else if (ret == RET_NO_VALID_DATA) {
                req->busy_flag &= ~BUSY_FLAG_VIDEO;
                return 1;
            }
            else {
                GetAVData(AV_OP_GET_MPEG4_CIF_SERIAL, -1, &req->av_data );
                req->serial_book = req->av_data.serial;
                dbg("lock error ret=%d\n", ret);
                return 1;
            }
        }
#endif  // DAVINCI_IPCAM
        return 0;
    } else
        return 1;               /* more to do */
}
Exemple #20
0
Fichier : get.c Projet : gpg/boa
int process_get(request * req)
{
    off_t bytes_written;
    volatile off_t bytes_to_write;

    if (req->method == M_HEAD) {
        return complete_response(req);
    }

    bytes_to_write = (req->ranges->stop - req->ranges->start) + 1;

    if (bytes_to_write > system_bufsize)
        bytes_to_write = system_bufsize;

    if (setjmp(env) == 0) {
        handle_sigbus = 1;
        bytes_written = write(req->fd, req->data_mem + req->ranges->start,
                              bytes_to_write);
        handle_sigbus = 0;
        /* OK, SIGBUS **after** this point is very bad! */
    } else {
        /* sigbus! */
        req->status = DEAD;
        log_error_doc(req);
        fprintf(stderr, "%sGot SIGBUS in write(2)!\n",
                get_commonlog_time());
        /* sending an error here is inappropriate
         * if we are here, the file is mmapped, and thus,
         * a content-length has been sent. If we send fewer bytes
         * the client knows there has been a problem.
         * We run the risk of accidentally sending the right number
         * of bytes (or a few too many) and the client
         * won't be the wiser.
         */
        return 0;
    }

    if (bytes_written < 0) {
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;
        /* request blocked at the pipe level, but keep going */
        else {
#ifdef QUIET_DISCONNECT
            if (errno != EPIPE) {
#else
            if (1) {
#endif
                log_error_doc(req);
                /* Can generate lots of log entries, */
                perror("write");
                /* OK to disable if your logs get too big */
            }
            req->status = DEAD;
            return 0;
        }
    }

    req->bytes_written += bytes_written;
    req->ranges->start += bytes_written;

    if ((req->ranges->stop + 1 - req->ranges->start) == 0) {
        return complete_response(req);
    }

    return 1;               /* more to do */
}

/*
 * Name: get_dir
 * Description: Called from process_get if the request is a directory.
 * statbuf must describe directory on input, since we may need its
 *   device, inode, and mtime.
 * statbuf is updated, since we may need to check mtimes of a cache.
 * returns:
 *  -1 error
 *  0  cgi (either gunzip or auto-generated)
 *  >0  file descriptor of file
 */

int get_dir(request * req, struct stat *statbuf)
{

    char pathname_with_index[MAX_PATH_LENGTH];
    int data_fd;

    if (directory_index) {      /* look for index.html first?? */
        unsigned int l1, l2;

        l1 = strlen(req->pathname);
        l2 = strlen(directory_index);
#ifdef GUNZIP
        if (l1 + l2 + 3 + 1 > sizeof(pathname_with_index)) { /* for .gz */
#else
        if (l1 + l2 + 1 > sizeof(pathname_with_index)) {
#endif

            errno = ENOMEM;
            boa_perror(req, "pathname_with_index not large enough for pathname + index");
            return -1;
        }
        memcpy(pathname_with_index, req->pathname, l1); /* doesn't copy NUL */
        memcpy(pathname_with_index + l1, directory_index, l2 + 1); /* does */

        data_fd = open(pathname_with_index, O_RDONLY|O_LARGEFILE);

        if (data_fd != -1) {    /* user's index file */
            /* We have to assume that directory_index will fit, because
             * if it doesn't, well, that's a huge configuration problem.
             * this is only the 'index.html' pathname for mime type
             */
            memcpy(req->request_uri, directory_index, l2 + 1); /* for mimetype */
            fstat(data_fd, statbuf);
            return data_fd;
        }
        if (errno == EACCES) {
            send_r_forbidden(req);
            return -1;
        } else if (errno != ENOENT) {
            /* if there is an error *other* than EACCES or ENOENT */
            send_r_not_found(req);
            return -1;
        }
#ifdef GUNZIP
        /* if we are here, trying index.html didn't work
         * try index.html.gz
         */
        strcat(pathname_with_index, ".gz");
        data_fd = open(pathname_with_index, O_RDONLY|O_LARGEFILE);
        if (data_fd != -1) {    /* user's index file */
            close(data_fd);

            req->response_status = R_REQUEST_OK;
            SQUASH_KA(req);
            if (req->pathname)
                free(req->pathname);
            req->pathname = strdup(pathname_with_index);
            if (!req->pathname) {
                boa_perror(req, "strdup of pathname_with_index for .gz files " __FILE__ ":" STR(__LINE__));
                return 0;
            }
            if (req->http_version != HTTP09) {
                req_write(req, http_ver_string(req->http_version));
                req_write(req, " 200 OK-GUNZIP" CRLF);
                print_http_headers(req);
                print_last_modified(req);
                req_write(req, "Content-Type: ");
                req_write(req, get_mime_type(directory_index));
                req_write(req, CRLF CRLF);
                req_flush(req);
            }
            if (req->method == M_HEAD)
                return 0;
            return init_cgi(req);
        }
#endif
    }

    /* only here if index.html, index.html.gz don't exist */
    if (dirmaker != NULL) {     /* don't look for index.html... maybe automake? */
        req->response_status = R_REQUEST_OK;
        SQUASH_KA(req);

        /* the indexer should take care of all headers */
        if (req->http_version != HTTP09) {
            req_write(req, http_ver_string(req->http_version));
            req_write(req, " 200 OK" CRLF);
            print_http_headers(req);
            print_last_modified(req);
            req_write(req, "Content-Type: text/html" CRLF CRLF);
            req_flush(req);
        }
        if (req->method == M_HEAD)
            return 0;

        return init_cgi(req);
        /* in this case, 0 means success */
    } else if (cachedir) {
        return get_cachedir_file(req, statbuf);
    } else {                    /* neither index.html nor autogenerate are allowed */
        send_r_forbidden(req);
        return -1;              /* nothing worked */
    }
}

static int get_cachedir_file(request * req, struct stat *statbuf)
{

    char pathname_with_index[MAX_PATH_LENGTH];
    int data_fd;
    time_t real_dir_mtime;

    real_dir_mtime = statbuf->st_mtime;
    /* the sizeof() doesn't need a -1 because snprintf will
     * include the NUL when calculating if the size is enough
     */
    snprintf(pathname_with_index, sizeof(pathname_with_index),
             "%s/dir.%d." PRINTF_OFF_T_ARG, cachedir,
             (int) statbuf->st_dev, statbuf->st_ino);
    data_fd = open(pathname_with_index, O_RDONLY|O_LARGEFILE);

    if (data_fd != -1) {        /* index cache */

        fstat(data_fd, statbuf);
        if (statbuf->st_mtime > real_dir_mtime) {
            statbuf->st_mtime = real_dir_mtime; /* lie */
            strcpy(req->request_uri, directory_index); /* for mimetype */
            return data_fd;
        }
        close(data_fd);
        unlink(pathname_with_index); /* cache is stale, delete it */
    }
    if (index_directory(req, pathname_with_index) == -1)
        return -1;

    data_fd = open(pathname_with_index, O_RDONLY|O_LARGEFILE); /* Last chance */
    if (data_fd != -1) {
        strcpy(req->request_uri, directory_index); /* for mimetype */
        fstat(data_fd, statbuf);
        statbuf->st_mtime = real_dir_mtime; /* lie */
        return data_fd;
    }

    boa_perror(req, "re-opening dircache");
    return -1;                  /* Nothing worked. */

}
Exemple #21
0
int read_header(request * req)
{
    int bytes;
    unsigned char *check, *buffer;

    check = req->client_stream + req->parse_pos;
    buffer = req->client_stream;
    bytes = req->client_stream_pos;

#ifdef VERY_FASCIST_LOGGING
    if (check < (buffer + bytes)) {
        buffer[bytes] = '\0';
        log_error_time();
        fprintf(stderr, "%s:%d - Parsing headers (\"%s\")\n",
                __FILE__, __LINE__, check);
    }
#endif
    while (check < (buffer + bytes)) {
        /* check for illegal characters here
         * Anything except CR, LF, and US-ASCII - control is legal
         * We accept tab but don't do anything special with it.
         */
        if (*check != '\r' && *check != '\n' && *check != '\t' &&
            (*check < 32 || *check > 127)) {
            log_error_doc(req);
            fprintf(stderr, "Illegal character (%d) in stream.\n", (unsigned int) *check);
            send_r_bad_request(req);
            return 0;
        }
        switch (req->status) {
        case READ_HEADER:
            if (*check == '\r') {
                req->status = ONE_CR;
                req->header_end = check;
            } else if (*check == '\n') {
                req->status = ONE_LF;
                req->header_end = check;
            }
            break;

        case ONE_CR:
            if (*check == '\n')
                req->status = ONE_LF;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        case ONE_LF:
            /* if here, we've found the end (for sure) of a header */
            if (*check == '\r') /* could be end o headers */
                req->status = TWO_CR;
            else if (*check == '\n')
                req->status = BODY_READ;
            else
                req->status = READ_HEADER;
            break;

        case TWO_CR:
            if (*check == '\n')
                req->status = BODY_READ;
            else if (*check != '\r')
                req->status = READ_HEADER;
            break;

        default:
            break;
        }

#ifdef VERY_FASCIST_LOGGING
        log_error_time();
        fprintf(stderr, "status, check: %d, %d\n", req->status, *check);
#endif

        req->parse_pos++;       /* update parse position */
        check++;

        if (req->status == ONE_LF) {
            *req->header_end = '\0';

            if (req->header_end - req->header_line >= MAX_HEADER_LENGTH) {
                log_error_doc(req);
                fprintf(stderr, "Header too long at %d bytes: \"%s\"\n",
                        req->header_end - req->header_line,
                        req->header_line);
                send_r_bad_request(req);
                return 0;
            }

            /* terminate string that begins at req->header_line */

            if (req->logline) {
                if (process_option_line(req) == 0) {
                    /* errors already logged */
                    return 0;
                }
            } else {
                if (process_logline(req) == 0)
                    /* errors already logged */
                    return 0;
                if (req->http_version == HTTP09)
                    return process_header_end(req);
            }
            /* set header_line to point to beginning of new header */
            req->header_line = check;
        } else if (req->status == BODY_READ) {
#ifdef VERY_FASCIST_LOGGING
            int retval;
            log_error_time();
            fprintf(stderr, "%s:%d -- got to body read.\n",
                    __FILE__, __LINE__);
            retval = process_header_end(req);
#else
            int retval = process_header_end(req);
#endif
            /* process_header_end inits non-POST cgi's */

            if (retval && req->method == M_POST) {
                /* for body_{read,write}, set header_line to start of data,
                   and header_end to end of data */
                req->header_line = check;
                req->header_end =
                    req->client_stream + req->client_stream_pos;

                req->status = BODY_WRITE;
                /* so write it */
                /* have to write first, or read will be confused
                 * because of the special case where the
                 * filesize is less than we have already read.
                 */

                /*

                   As quoted from RFC1945:

                   A valid Content-Length is required on all HTTP/1.0 POST requests. An
                   HTTP/1.0 server should respond with a 400 (bad request) message if it
                   cannot determine the length of the request message's content.

                 */

                if (req->content_length) {
                    int content_length;

                    content_length = boa_atoi(req->content_length);
                    /* Is a content-length of 0 legal? */
                    if (content_length < 0) {
                        log_error_doc(req);
                        fprintf(stderr,
                                "Invalid Content-Length [%s] on POST!\n",
                                req->content_length);
                        send_r_bad_request(req);
                        return 0;
                    }
                    if (single_post_limit
                        && content_length > single_post_limit) {
                        log_error_doc(req);
                        fprintf(stderr,
                                "Content-Length [%d] > SinglePostLimit [%d] on POST!\n",
                                content_length, single_post_limit);
                        send_r_bad_request(req);
                        return 0;
                    }
                    req->filesize = content_length;
                    req->filepos = 0;
                    if (req->header_end - req->header_line > req->filesize) {
                        req->header_end = req->header_line + req->filesize;
                    }
                } else {
                    log_error_doc(req);
                    fprintf(stderr, "Unknown Content-Length POST!\n");
                    send_r_bad_request(req);
                    return 0;
                }
            }                   /* either process_header_end failed or req->method != POST */
            return retval;      /* 0 - close it done, 1 - keep on ready */
        }                       /* req->status == BODY_READ */
    }                           /* done processing available buffer */

#ifdef VERY_FASCIST_LOGGING
    log_error_time();
    fprintf(stderr, "%s:%d - Done processing buffer.  Status: %d\n",
            __FILE__, __LINE__, req->status);
#endif

    if (req->status < BODY_READ) {
        /* only reached if request is split across more than one packet */
        unsigned int buf_bytes_left;

        buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos;
        if (buf_bytes_left < 1 || buf_bytes_left > CLIENT_STREAM_SIZE) {
            log_error_doc(req);
            fputs("No space left in client stream buffer, closing\n",
                  stderr);
            req->response_status = 400; 
            req->status = DEAD;
            return 0;
        }

        bytes =
            read(req->fd, buffer + req->client_stream_pos, buf_bytes_left);

        if (bytes < 0) {
            if (errno == EINTR)
                return 1;
            else if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */
                return -1;
            log_error_doc(req);
            perror("header read"); /* don't need to save errno because log_error_doc does */
            req->response_status = 400; 
            return 0;
        } else if (bytes == 0) {
#ifndef QUIET_DISCONNECT
            log_error_doc(req);
            fputs("client unexpectedly closed connection.\n", stderr);
#endif
            req->response_status = 400;
            return 0;
        }

        /* bytes is positive */
        req->client_stream_pos += bytes;

#ifdef FASCIST_LOGGING1
        log_error_time();
        req->client_stream[req->client_stream_pos] = '\0';
        fprintf(stderr, "%s:%d -- We read %d bytes: \"%s\"\n",
                __FILE__, __LINE__, bytes,
#ifdef VERY_FASCIST_LOGGING2
                req->client_stream + req->client_stream_pos - bytes);
#else
                "");
#endif
#endif

        return 1;
    }
Exemple #22
0
int process_header_end(request * req)
{
    if (!req->logline) {
        log_error_doc(req);
        fputs("No logline in process_header_end\n", stderr);
        send_r_error(req);
        return 0;
    }

    /* Percent-decode request */
    if (unescape_uri(req->request_uri, &(req->query_string)) == 0) {
        log_error_doc(req);
        fputs("URI contains bogus characters\n", stderr);
        send_r_bad_request(req);
        return 0;
    }

    /* clean pathname */
    clean_pathname(req->request_uri);

    if (req->request_uri[0] != '/') {
        log_error("URI does not begin with '/'\n");
        send_r_bad_request(req);
        return 0;
    }

    if (vhost_root) {
        char *c;
        if (!req->header_host) {
            req->host = strdup(default_vhost);
        } else {
            req->host = strdup(req->header_host);
        }
        if (!req->host) {
            log_error_doc(req);
            fputs("unable to strdup default_vhost/req->header_host\n", stderr);
            send_r_error(req);
            return 0;
        }
        strlower(req->host);
        /* check for port, and remove
         * we essentially ignore the port, because we cannot
         * as yet report a different port than the one we are
         * listening on
         */
        c = strchr(req->host, ':');
        if (c)
            *c = '\0';

        if (check_host(req->host) < 1) {
            log_error_doc(req);
            fputs("host invalid!\n", stderr);
            send_r_bad_request(req);
            return 0;
        }
    }

    if (translate_uri(req) == 0) { /* unescape, parse uri */
        /* errors already logged */
        SQUASH_KA(req);
        return 0;               /* failure, close down */
    }

    if (req->method == M_POST) {
        req->post_data_fd = create_temporary_file(1, NULL, 0);
        if (req->post_data_fd == 0) {
            /* errors already logged */
            send_r_error(req);
            return 0;
        }
        if (fcntl(req->post_data_fd, F_SETFD, 1) == -1) {
            log_error_doc(req);
            fputs("unable to set close-on-exec for req->post_data_fd!\n", stderr);
            close(req->post_data_fd);
            req->post_data_fd = 0;
            send_r_error(req);
            return 0;
        }
        return 1;             /* success */
    }

    if (req->cgi_type) {
        return init_cgi(req);
    }
    
    req->status = WRITE;
//    return complete_response(req);
    return init_control(req);
//    return init_get(req);       /* get and head */
}
Exemple #23
0
int process_option_line(request * req)
{
    char c, *value, *line = req->header_line;

    /* Start by aggressively hacking the in-place copy of the header line */

#ifdef FASCIST_LOGGING
    log_error_time();
    fprintf(stderr, "%s:%d - Parsing \"%s\"\n", __FILE__, __LINE__, line);
#endif

    value = strchr(line, ':');
    if (value == NULL) {
        log_error_doc(req);
        fprintf(stderr, "header \"%s\" does not contain ':'\n", line);
        return 0;
    }
    *value++ = '\0';            /* overwrite the : */
    to_upper(line);             /* header types are case-insensitive */

    /* the code below *does* catch '\0' due to the c = *value test */
    while ((c = *value) && (c == ' ' || c == '\t'))
        value++;

    /* if c == '\0' there was no 'value' for the key */
    if (c == '\0') {
        return 0;
    }

    switch (line[0]) {
    case 'A':
        if (!memcmp(line, "ACCEPT", 7)) {
#ifdef ACCEPT_ON
            add_accept_header(req, value);
#endif
            return 1;
        }
        break;
    case 'C':
        if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type) {
            req->content_type = value;
            return 1;
        } else if (!memcmp(line, "CONTENT_LENGTH", 15)
                   && !req->content_length) {
            req->content_length = value;
            return 1;
        } else if (!memcmp(line, "CONNECTION", 11) &&
                   ka_max && req->keepalive != KA_STOPPED) {
            req->keepalive = (!strncasecmp(value, "Keep-Alive", 10) ?
                              KA_ACTIVE : KA_STOPPED);
            return 1;
        }
        break;
    case 'H':
        if (!memcmp(line, "HOST", 5) && !req->header_host) {
            req->header_host = value; /* may be complete garbage! */
            return 1;
        }
        break;
    case 'I':
        if (!memcmp(line, "IF_MODIFIED_SINCE", 18)
            && !req->if_modified_since) {
            req->if_modified_since = value;
            return 1;
        }
        break;
    case 'R':
        /* Need agent and referer for logs */
        if (!memcmp(line, "REFERER", 8)) {
            req->header_referer = value;
            if (!add_cgi_env(req, "REFERER", value, 1)) {
                /* errors already logged */
                return 0;
            }
        } else if (!memcmp(line, "RANGE", 6)) {
            if (req->ranges && req->ranges->stop == INT_MAX) {
                /* there was an error parsing, ignore */
                return 1;
            } else if (!range_parse(req, value)) {
                /* unable to parse range */
                send_r_invalid_range(req);
                return 0;
            }                   /* req->ranges */
        }
        break;
    case 'U':
        if (!memcmp(line, "USER_AGENT", 11)) {
            req->header_user_agent = value;
            if (!add_cgi_env(req, "USER_AGENT", value, 1)) {
                /* errors already logged */
                return 0;
            }
            return 1;
        }
        break;
    default:                   /* no default */
        break;
    }                           /* switch */

    return add_cgi_env(req, line, value, 1);
}
Exemple #24
0
static void fdset_update(void)
{
    request *current, *next;

    for(current = request_block;current;current = next) {
        time_t time_since = current_time - current->time_last;
        next = current->next;

        /* hmm, what if we are in "the middle" of a request and not
         * just waiting for a new one... perhaps check to see if anything
         * has been read via header position, etc... */
        if (current->kacount < ka_max && /* we *are* in a keepalive */
            (time_since >= ka_timeout) && /* ka timeout */
            !current->logline)  /* haven't read anything yet */
            current->status = DEAD; /* connection keepalive timed out */
        else if (time_since > REQUEST_TIMEOUT) {
            log_error_doc(current);
            fputs("connection timed out\n", stderr);
            current->status = DEAD;
        }
        if (current->buffer_end && current->status < DEAD) {
            if (FD_ISSET(current->fd, &block_write_fdset))
                ready_request(current);
            else {
                BOA_FD_SET(current->fd, &block_write_fdset);
            }
        } else {
            switch (current->status) {
            case WRITE:
            case PIPE_WRITE:
                if (FD_ISSET(current->fd, &block_write_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->fd, &block_write_fdset);
                }
                break;
            case BODY_WRITE:
                if (FD_ISSET(current->post_data_fd, &block_write_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->post_data_fd, &block_write_fdset);
                }
                break;
            case PIPE_READ:
                if (FD_ISSET(current->data_fd, &block_read_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->data_fd, &block_read_fdset);
                }
                break;
            case DONE:
                if (FD_ISSET(current->fd, &block_write_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->fd, &block_write_fdset);
                }
                break;
            case DEAD:
                ready_request(current);
                break;
            default:
                if (FD_ISSET(current->fd, &block_read_fdset))
                    ready_request(current);
                else {
                    BOA_FD_SET(current->fd, &block_read_fdset);
                }
                break;
            }
        }
        current = next;
    }
}
Exemple #25
0
Fichier : pipe.c Projet : gpg/boa
int read_from_pipe(request * req)
{
    off_t bytes_read; /* signed */
    off_t bytes_to_read; /* unsigned */ /* XXX really? */

    bytes_to_read = BUFFER_SIZE - (req->header_end - req->buffer - 1);

    if (bytes_to_read == 0) {   /* buffer full */
        if (req->cgi_status == CGI_PARSE) { /* got+parsed header */
            req->cgi_status = CGI_BUFFER;
            *req->header_end = '\0'; /* points to end of read data */
            /* Could the above statement overwrite data???
               No, because req->header_end points to where new data
               should begin, not where old data is.
             */
            return process_cgi_header(req); /* cgi_status will change */
        }
        req->status = PIPE_WRITE;
        return 1;
    }

    bytes_read = read(req->data_fd, req->header_end, bytes_to_read);
#ifdef FASCIST_LOGGING
    if (bytes_read > 0) {
        *(req->header_end + bytes_read) = '\0';
        fprintf(stderr, "pipe.c - read %d bytes: \"%s\"\n",
                bytes_read, req->header_end);
    } else
        fprintf(stderr, "pipe.c - read %d bytes\n", bytes_read);
    fprintf(stderr, "status, cgi_status: %d, %d\n", req->status,
            req->cgi_status);
#endif

    if (bytes_read == -1) {
        if (errno == EINTR)
            return 1;
        else if (errno == EWOULDBLOCK || errno == EAGAIN)
            return -1;          /* request blocked at the pipe level, but keep going */
        else {
            req->status = DEAD;
            log_error_doc(req);
            perror("pipe read");
            return 0;
        }
    }
    *(req->header_end + bytes_read) = '\0';

    if (bytes_read == 0) {      /* eof, write rest of buffer */
        req->status = PIPE_WRITE;
        if (req->cgi_status == CGI_PARSE) { /* hasn't processed header yet */
            req->cgi_status = CGI_DONE;
            *req->header_end = '\0'; /* points to end of read data */
            return process_cgi_header(req); /* cgi_status will change */
        }
        req->cgi_status = CGI_DONE;
        return 1;
    }

    req->header_end += bytes_read;

    if (req->cgi_status != CGI_PARSE)
        return write_from_pipe(req); /* why not try and flush the buffer now? */
    else {
        char *c, *buf;

        buf = req->header_line;

        c = strstr(buf, "\n\r\n");
        if (c == NULL) {
            c = strstr(buf, "\n\n");
            if (c == NULL) {
                return 1;
            }
        }
        req->cgi_status = CGI_DONE;
        *req->header_end = '\0'; /* points to end of read data */
        return process_cgi_header(req); /* cgi_status will change */
    }
    return 1;
}
Exemple #26
0
Fichier : get.c Projet : gpg/boa
int init_get(request * req)
{
    int data_fd, saved_errno;
    struct stat statbuf;
    volatile off_t bytes_free;

    data_fd = open(req->pathname, O_RDONLY|O_LARGEFILE);
    saved_errno = errno;        /* might not get used */

    while (use_lang_rewrite && data_fd == -1 && errno == ENOENT) {
         /* We cannot open that file - Check whether we can rewrite it
          * to a different language suffix.  We only support filenames
          * of the format: "foo.ll.html" as an alias for "foo.html". */
        unsigned int len;

        len = strlen(req->pathname);
        if (len < 6 || strcmp (req->pathname + len - 5, ".html"))
            break;  /* does not end in ".html" */
        if (len > 8 && req->pathname[len-8] == '.'
            && req->pathname[len-7] >= 'a' && req->pathname[len-7] <= 'z'
            && req->pathname[len-6] >= 'a' && req->pathname[len-6] <= 'z') {
            /* The request was for a language dependent file.  Strip
             * it and try the generic form. */
            char save_name[8];

            strcpy (save_name, req->pathname + len - 7);
            strcpy (req->pathname + len - 7, "html");
            data_fd = open(req->pathname, O_RDONLY);
            if (data_fd == -1)
                strcpy (req->pathname + len - 7, save_name);
            break;
        }
        else if ( 0 ) {
            /* Fixme: Other items to try from the list of accepted_languages */
            data_fd = open(req->pathname, O_RDONLY);
        }
        else
            break;
    }


#ifdef GUNZIP
    if (data_fd == -1 && errno == ENOENT) {
        /* cannot open */
        /* it's either a gunzipped file or a directory */
        char gzip_pathname[MAX_PATH_LENGTH];
        unsigned int len;

        len = strlen(req->pathname);

        if (len + 4 > sizeof(gzip_pathname)) {
            log_error_doc(req);
            fprintf(stderr, "Pathname + .gz too long! (%s)\n", req->pathname);
            send_r_bad_request(req);
            return 0;
        }

        memcpy(gzip_pathname, req->pathname, len);
        memcpy(gzip_pathname + len, ".gz", 3);
        gzip_pathname[len + 3] = '\0';
        data_fd = open(gzip_pathname, O_RDONLY|O_LARGEFILE);
        if (data_fd != -1) {
            close(data_fd);

            req->response_status = R_REQUEST_OK;
            if (req->pathname)
                free(req->pathname);
            req->pathname = strdup(gzip_pathname);
            if (!req->pathname) {
                boa_perror(req, "strdup req->pathname for gzipped filename " __FILE__ ":" STR(__LINE__));
                return 0;
            }
            if (req->http_version != HTTP09) {
                req_write(req, http_ver_string(req->http_version));
                req_write(req, " 200 OK-GUNZIP" CRLF);
                print_http_headers(req);
                print_content_type(req);
                print_last_modified(req);
                req_write(req, CRLF);
                req_flush(req);
            }
            if (req->method == M_HEAD)
                return 0;

            return init_cgi(req);
        }
    }
#endif

    if (data_fd == -1) {
        log_error_doc(req);
        errno = saved_errno;
        perror("document open");

        if (saved_errno == ENOENT)
            send_r_not_found(req);
        else if (saved_errno == EACCES)
            send_r_forbidden(req);
        else
            send_r_bad_request(req);
        return 0;
    }

#ifdef ACCESS_CONTROL
    if (!access_allow(req->pathname)) {
      send_r_forbidden(req);
      return 0;
    }
#endif

    fstat(data_fd, &statbuf);

    if (S_ISDIR(statbuf.st_mode)) { /* directory */
        close(data_fd);         /* close dir */

        if (req->pathname[strlen(req->pathname) - 1] != '/') {
            char buffer[3 * MAX_PATH_LENGTH + 128];
            unsigned int len;

#ifdef ALLOW_LOCAL_REDIRECT
            len = strlen(req->request_uri);
            if (len + 2 > sizeof(buffer)) {
                send_r_error(req);
                return 0;
            }
            memcpy(buffer, req->request_uri, len);
            buffer[len] = '/';
            buffer[len+1] = '\0';
#else
            char *host = server_name;
            unsigned int l2;
            char *port = NULL;
            const char *prefix = hsts_header? "https://" : "http://";
            static unsigned int l3 = 0;
            static unsigned int l4 = 0;

            if (l4 == 0) {
                l4 = strlen(prefix);
            }
            len = strlen(req->request_uri);
            if (!port && server_port != 80 && !no_redirect_port) {
                port = strdup(simple_itoa(server_port));
                if (port == NULL) {
                    errno = ENOMEM;
                    boa_perror(req, "Unable to perform simple_itoa conversion on server port!");
                    return 0;
                }
                l3 = strlen(port);
            }
            /* l3 and l4 are done */

            if (req->host) {
                /* only shows up in vhost mode */
                /* what about the port? (in vhost_mode?) */
                /* we don't currently report ports that differ
                 * from out "bound" (listening) port, so we don't care
                 */
                host = req->host;
            }
            l2 = strlen(host);

            if (server_port != 80 && !no_redirect_port) {
                if (l4 + l2 + 1 + l3 + len + 1 > sizeof(buffer)) {
                    errno = ENOMEM;
                    boa_perror(req, "buffer not large enough for directory redirect");
                    return 0;
                }
                memcpy(buffer, prefix, l4);
                memcpy(buffer + l4, host, l2);
                buffer[l4 + l2] = ':';
                memcpy(buffer + l4 + l2 + 1, port, l3);
                memcpy(buffer + l4 + l2 + 1 + l3, req->request_uri, len);
                buffer[l4 + l2 + 1 + l3 + len] = '/';
                buffer[l4 + l2 + 1 + l3 + len + 1] = '\0';
            } else {
                if (l4 + l2 + len + 1 > sizeof(buffer)) {
                    errno = ENOMEM;
                    boa_perror(req, "buffer not large enough for directory redirect");
                    return 0;
                }
                memcpy(buffer, prefix, l4);
                memcpy(buffer + l4, host, l2);
                memcpy(buffer + l4 + l2, req->request_uri, len);
                buffer[l4 + l2 + len] = '/';
                buffer[l4 + l2 + len + 1] = '\0';
            }
#endif /* ALLOW LOCAL REDIRECT */
            send_r_moved_perm(req, buffer);
            return 0;
        }
        data_fd = get_dir(req, &statbuf); /* updates statbuf */

        if (data_fd < 0)      /* couldn't do it */
            return 0;           /* errors reported by get_dir */
        else if (data_fd == 0 || data_fd == 1)
            return data_fd;
        /* else, data_fd contains the fd of the file... */
    }

    if (!S_ISREG(statbuf.st_mode)) { /* regular file */
        log_error_doc(req);
        fprintf(stderr, "Resulting file is not a regular file.\n");
        send_r_bad_request(req);
        close(data_fd);
        return 0;
    }

    /* If-UnModified-Since asks
     *  is the file newer than date located in time_cval
     *  yes -> return 412
     *   no -> return 200
     *
     * If-Modified-Since asks
     *  is the file date less than or same as the date located in time_cval
     *  yes -> return 304
     *  no  -> return 200
     *
     * If-Unmodified-Since overrides If-Modified-Since
     */

    /*
    if (req->headers[H_IF_UNMODIFIED_SINCE] &&
        modified_since(&(statbuf.st_mtime),
                       req->headers[H_IF_UNMODIFIED_SINCE])) {
        send_r_precondition_failed(req);
        return 0;
    } else
    */
    if (req->if_modified_since &&
        !modified_since(&(statbuf.st_mtime), req->if_modified_since)) {
        send_r_not_modified(req);
        close(data_fd);
        return 0;
    }

    req->filesize = statbuf.st_size;
    req->last_modified = statbuf.st_mtime;

    /* ignore if-range without range */
    if (req->header_ifrange && !req->ranges)
        req->header_ifrange = NULL;

    /* we don't support it yet */
    req->header_ifrange = NULL;

    /* parse ranges now */
    /* we have to wait until req->filesize exists to fix them up */
    /* fixup handles handles communicating with the client */
    /* ranges_fixup logs as appropriate, and sends
     * send_r_invalid_range on error.
     */

    if (req->filesize == 0) {
        if (req->http_version < HTTP11) {
            send_r_request_ok(req);
            close(data_fd);
            return 0;
        }
        send_r_no_content(req);
        close(data_fd);
        return 0;
    }

    if (req->ranges && !ranges_fixup(req)) {
        close(data_fd);
        return 0;
    }

    /* if no range has been set, use default range */
#if 0
    DEBUG(DEBUG_RANGE) {
        log_error_time();
        fprintf(stderr, "if-range: %s\time_cval: %d\tmtime: %d\n",
                req->header_ifrange, req->time_cval, statbuf->st_mtime);
    }
#endif

    /*
     If the entity tag given in the If-Range header matches the current
     entity tag for the entity, then the server should provide the
     specified sub-range of the entity using a 206 (Partial content)
     response.

     If the entity tag does not match, then the server should
     return the entire entity using a 200 (OK) response.
     */
    /* IF we have range data *and* no if-range or if-range matches... */

#ifdef MAX_FILE_MMAP
    if (req->filesize > MAX_FILE_MMAP) {
        req->data_fd = data_fd;
        req->status = IOSHUFFLE;
    } else
#endif
    {
        /* NOTE: I (Jon Nelson) tried performing a read(2)
         * into the output buffer provided the file data would
         * fit, before mmapping, and if successful, writing that
         * and stopping there -- all to avoid the cost
         * of a mmap.  Oddly, it was *slower* in benchmarks.
         */
        req->mmap_entry_var = find_mmap(data_fd, &statbuf);
        if (req->mmap_entry_var == NULL) {
            req->data_fd = data_fd;
            req->status = IOSHUFFLE;
        } else {
            req->data_mem = req->mmap_entry_var->mmap;
            close(data_fd);             /* close data file */
        }
    }

    if (!req->ranges) {
        req->ranges = range_pool_pop();
        req->ranges->start = 0;
        req->ranges->stop = -1;
        if (!ranges_fixup(req)) {
            return 0;
        }
        send_r_request_ok(req);
    } else {
        /* FIXME: support if-range header here, by the following logic:
         * if !req->header_ifrange || st_mtime > header_ifrange,
         *   send_r_partial_content
         * else
         *   reset-ranges, etc...
         */
        if (!req->header_ifrange) {
            send_r_partial_content(req);
        } else {
            /* either no if-range or the if-range does not match */
            ranges_reset(req);
            req->ranges = range_pool_pop();
            req->ranges->start = 0;
            req->ranges->stop = -1;
            if (!ranges_fixup(req)) {
                return 0;
            }
            send_r_request_ok(req);
        }
    }

    if (req->method == M_HEAD) {
        return complete_response(req);
    }

    bytes_free = 0;
    if (req->data_mem) {
        /* things can really go tilt if req->buffer_end > BUFFER_SIZE,
         * but basically that can't happen
         */

        /* We lose statbuf here, so make sure response has been sent */
        bytes_free = BUFFER_SIZE - req->buffer_end;
        /* 256 bytes for the **trailing** headers */

        /* bytes is now how much the buffer can hold
         * after the headers
         */
    }

    if (req->data_mem && bytes_free > 256) {
        unsigned int want;
        Range *r;

        r = req->ranges;

        want = (r->stop - r->start) + 1;

        if (bytes_free > want)
            bytes_free = want;
        else {
            /* bytes_free <= want */
            ;
        }

        if (setjmp(env) == 0) {
            handle_sigbus = 1;
            memcpy(req->buffer + req->buffer_end,
                   req->data_mem + r->start, bytes_free);
            handle_sigbus = 0;
            /* OK, SIGBUS **after** this point is very bad! */
        } else {
            /* sigbus! */
            log_error_doc(req);
            reset_output_buffer(req);
            send_r_error(req);
            log_error("Got SIGBUS in memcpy\n");
            return 0;
        }
        req->buffer_end += bytes_free;
        req->bytes_written += bytes_free;
        r->start += bytes_free;
        if (bytes_free == want) {
            /* this will fit due to the 256 extra bytes_free */
            return complete_response(req);
        }
    }

    /* We lose statbuf here, so make sure response has been sent */
    return 1;
}
Exemple #27
0
int init_get2(request * req)
{
	int data_fd;
	//Brad add begin for update content length
	char *content_length_orig1;
	char *content_length_orig2;
	int orig_char_length=0;
	int exact_char_length=0;
	int byte_shift=0;
	int exact_size=0;
	int total_length_shift=0;
	char *exact_content=NULL;
	int head_offset=0;
	int first_offset=0;
	int antecedent_segment=0;
	int subsequent_segment=0;
	//Brad add end for update content length
#ifdef GUNZIP
	char buf[MAX_PATH_LENGTH];
#endif
	struct stat statbuf;
	SQUASH_KA(req);

	complete_env(req);

	middle_segment=0;
	req->cgi_env[req->cgi_env_index] = NULL;     /* terminate cgi env */
	if ((strstr(req->request_uri,".htm")==NULL) &&
	    (strstr(req->request_uri,".asp")==NULL)) {
			return 1;
	}

	data_fd = open(req->pathname, O_RDONLY);
	if (data_fd == -1) {		/* cannot open */
#ifdef GUNZIP
		sprintf(buf, "%s.gz", req->pathname);
		data_fd = open(buf, O_RDONLY);
		if (data_fd == -1) {
#endif
			int errno_save = errno;
			log_error_doc(req);
			errno = errno_save;
#if 0
			perror("document open");
#endif
//			syslog(LOG_ERR, "Error opening %s for %s: %s\n", req->pathname,
//					req->remote_ip_addr, strerror(errno_save));
			errno = errno_save;

			if (errno == ENOENT)
				send_r_not_found(req);
			else if (errno == EACCES)
				send_r_forbidden(req);
			else
				send_r_bad_request(req);
			return 0;
#ifdef GUNZIP
		}
		close(data_fd);

		req->response_status = R_REQUEST_OK;
		if (!req->simple) {			
			req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
			print_http_headers(req);
			print_content_type(req);
			print_last_modified(req);
			req_write(req, "\r\n");
			req_flush(req);
		}
		if (req->method == M_HEAD)
			return 0;
		if (req->pathname)
			free(req->pathname);
		req->pathname = strdup(buf);
		return init_cgi(req);	/* 1 - OK, 2 - die */
#endif
	}
	fstat(data_fd, &statbuf);
	if (S_ISDIR(statbuf.st_mode)) {
		close(data_fd);			/* close dir */

		if (req->pathname[strlen(req->pathname) - 1] != '/') {
			char buffer[3 * MAX_PATH_LENGTH + 128];

			if (server_port != 80)
				sprintf(buffer, "http://%s:%d%s/", req->host?req->host:server_name, server_port,
						req->request_uri);
			else
				sprintf(buffer, "http://%s%s/", req->host?req->host:server_name, req->request_uri);

			send_redirect_perm(req, buffer);

			return 0;
		}
		data_fd = get_dir(req, &statbuf);	/* updates statbuf */

		if (data_fd == -1) {		/* couldn't do it */
			return 0;			/* errors reported by get_dir */
		}
		else if (data_fd == 0) {
			return 1;
		}
	}

//start modify here : tony
#if 0	
	if (req->if_modified_since &&
		!modified_since(&(statbuf.st_mtime), req->if_modified_since)) {
		send_r_not_modified(req);
		close(data_fd);
		return 0;
	}
#endif


	req->filesize = statbuf.st_size;
//	req->last_modified = statbuf.st_mtime;

	if (req->method == M_HEAD) {
		send_r_request_ok(req);
		close(data_fd);
		return 0;
	}
	/* MAP_OPTIONS: see compat.h */
	req->data_mem = mmap(0, req->filesize, 
#ifdef USE_NLS			
			PROT_READ|PROT_WRITE
#else
			PROT_READ
#endif
			, MAP_OPTIONS,data_fd, 0);

	close(data_fd);				/* close data file */

	if ((long) req->data_mem == -1) {
		boa_perror(req, "mmap");
		return 0;
	}
	
	send_r_request_ok(req);		/* All's well */

	{
		//parse and send asp page
		char *left,*right,*last_right=req->data_mem;
		int bob;
		first_offset=req->buffer_end;     //Brad add for update content length
		while (1) {
			left=strstr(last_right,"<%");
			if (left!=NULL)
				right=strstr(left,"%>");

			if ((left!=NULL) && (right!=NULL)) {
				bob=(unsigned int)left-(unsigned int)last_right;
#ifdef SUPPORT_ASP
				while((bob+req->buffer_end+10)>(req->max_buffer_size)) {    //Brad modify
					int ret;
					ret=allocNewBuffer(req);	
					if (ret==-1) {
						bob=req->max_buffer_size- req->buffer_end;
						printf("will break\n");
						break;
					}
				}
#endif
				antecedent_segment =antecedent_segment+bob;		//Brad add for update content length
				if (bob>=0) {
					memcpy(req->buffer + req->buffer_end, req->data_mem + req->filepos, bob);
					last_right=right+2;
					req->buffer_end += bob;
					req->filepos += (bob+(unsigned int)last_right-(unsigned int)left);
					handleScript(req,left,right);
				}
			}
			else {
				bob=(unsigned int)req->data_mem+req->filesize-(unsigned int)last_right;
#ifdef SUPPORT_ASP
				while((bob+req->buffer_end+10)>req->max_buffer_size) {  //Brad modify
					int ret;
					ret=allocNewBuffer(req);
					if (ret==-1) {
						bob=req->max_buffer_size- req->buffer_end;
						break;
					}
				}
#endif				
				subsequent_segment = subsequent_segment+bob;    //Brad add for update content length
				if (bob > 0) {
					memcpy(req->buffer + req->buffer_end, req->data_mem + req->filepos, bob);
					req->buffer_end += bob;
					req->filepos += bob;
				}
				break;
			}
		}
	}
//Brad add begin for update content length
	exact_content = req->buffer+first_offset;
	exact_size = antecedent_segment+middle_segment+subsequent_segment;
	//fprintf(stderr, "the exact total length of asp file=%d\n", exact_size);
	
	content_length_orig1 = strstr(req->buffer, "Content-Length:");
	content_length_orig2 = strstr(content_length_orig1, "\r\n");
	content_length_orig1 = content_length_orig1 + strlen("Content-Length: ");
	orig_char_length = content_length_orig2 - content_length_orig1;
	//fprintf(stderr, "the orig_char_length=%d\n", orig_char_length);
	exact_char_length = strlen(simple_itoa(exact_size));
	//fprintf(stderr, "the exact_char_length=%d\n", exact_char_length);
	if(orig_char_length == exact_char_length) {
		//fprintf(stderr, "Update the content length with the same char length!\n");
		memcpy(content_length_orig1, simple_itoa(exact_size),exact_char_length); 
	}else if(orig_char_length < exact_char_length) {
		//fprintf(stderr, " Update the content length with shift to later bytes!\n");
		byte_shift = exact_char_length - orig_char_length;
		head_offset = first_offset- (content_length_orig2 - req->buffer);
		total_length_shift = head_offset+exact_size;
		memmove((content_length_orig2+byte_shift), content_length_orig2, total_length_shift);
		memcpy(content_length_orig1, simple_itoa(exact_size),exact_char_length); 
		req->buffer_end = req->buffer_end+byte_shift; 
	}else {
		//fprintf(stderr, "Update the content length with shift to preceding bytes!\n");
		byte_shift = orig_char_length - exact_char_length;
		head_offset = first_offset- (content_length_orig2 - req->buffer);
		total_length_shift = head_offset+exact_size;
		memmove((content_length_orig2-byte_shift), content_length_orig2, total_length_shift);
		memcpy(content_length_orig1, simple_itoa(exact_size),exact_char_length); 
		req->buffer_end = req->buffer_end-byte_shift;  
	}	
//Brad add end for update content length
	if (req->filepos == req->filesize) {
//		req->status = CLOSE;
		return 0; /* done! */
	}

	/* We lose statbuf here, so make sure response has been sent */
	return 1;
}
Exemple #28
0
int init_get(request * req)
{
    int data_fd, saved_errno, dynamic=0;
    struct stat statbuf;
    volatile int bytes;

    data_fd = open(req->pathname, O_RDONLY);
    saved_errno = errno; /* might not get used */

#ifdef GUNZIP
    if (data_fd == -1 && errno == ENOENT) {
        /* cannot open */
        /* it's either a gunzipped file or a directory */
        char gzip_pathname[MAX_PATH_LENGTH];
        int len;

        len = strlen(req->pathname);

        memcpy(gzip_pathname, req->pathname, len);
        memcpy(gzip_pathname + len, ".gz", 3);
        gzip_pathname[len + 3] = '\0';
        data_fd = open(gzip_pathname, O_RDONLY);
        if (data_fd != -1) {
            close(data_fd);

            req->response_status = R_REQUEST_OK;
            if (req->pathname)
                free(req->pathname);
            req->pathname = strdup(gzip_pathname);
            if (!req->pathname) {
                log_error_time();
                perror("strdup");
                send_r_error(req);
                return 0;
            }
            if (!req->simple) {
                req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
                print_http_headers(req);
                print_content_type(req);
                print_last_modified(req);
                req_write(req, "\r\n");
                req_flush(req);
            }
            if (req->method == M_HEAD)
                return 0;

            return init_cgi(req);
        }
    }
#endif

    if (data_fd == -1) {
        log_error_doc(req);
        errno = saved_errno;
        perror("document open");

        if (saved_errno == ENOENT)
            send_r_not_found(req);
        else if (saved_errno == EACCES)
            send_r_forbidden(req);
        else
            send_r_bad_request(req);
        return 0;
    }

    fstat(data_fd, &statbuf);

    if (S_ISDIR(statbuf.st_mode)) { /* directory */
        close(data_fd);         /* close dir */

        if (req->pathname[strlen(req->pathname) - 1] != '/') {
            char buffer[3 * MAX_PATH_LENGTH + 128];

            if (server_port != 80)
                sprintf(buffer, "http://%s:%d%s/", server_name,
                        server_port, req->request_uri);
            else
                sprintf(buffer, "http://%s%s/", server_name,
                        req->request_uri);
            send_r_moved_perm(req, buffer);
            return 0;
        }
        data_fd = get_dir(req, &statbuf); /* updates statbuf */

        if (data_fd == -1)      /* couldn't do it */
            return 0;           /* errors reported by get_dir */
        else if (data_fd <= 1)
            /* data_fd == 0 -> close it down, 1 -> continue */
            return data_fd;
        /* else, data_fd contains the fd of the file... */
    }
#ifdef DAVINCI_IPCAM
    if (req->http_uri && (req->http_uri->uri_flag & URI_FLAG_NEED_PARSE))
        dynamic = 1;
#endif
    if (req->if_modified_since && !dynamic &&
        !modified_since(&(statbuf.st_mtime), req->if_modified_since)) {
        send_r_not_modified(req);
        close(data_fd);
        return 0;
    }
    req->filesize = statbuf.st_size;
    req->last_modified = statbuf.st_mtime;

    if (req->method == M_HEAD || req->filesize == 0) {
        send_r_request_ok(req);
        close(data_fd);
        return 0;
    }

    if (req->filesize > MAX_FILE_MMAP) {
        send_r_request_ok(req); /* All's well */
        req->status = PIPE_READ;
        req->cgi_status = CGI_BUFFER;
        req->data_fd = data_fd;
        req_flush(req);         /* this should *always* complete due to
                                   the size of the I/O buffers */
        req->header_line = req->header_end = req->buffer;
        return 1;
    }

    if (req->filesize == 0) {  /* done */
        send_r_request_ok(req);     /* All's well *so far* */
        close(data_fd);
        return 1;
    }

    /* NOTE: I (Jon Nelson) tried performing a read(2)
     * into the output buffer provided the file data would
     * fit, before mmapping, and if successful, writing that
     * and stopping there -- all to avoid the cost
     * of a mmap.  Oddly, it was *slower* in benchmarks.
     */
    req->mmap_entry_var = find_mmap(data_fd, &statbuf);
    if (req->mmap_entry_var == NULL) {
        req->buffer_end = 0;
        if (errno == ENOENT)
            send_r_not_found(req);
        else if (errno == EACCES)
            send_r_forbidden(req);
        else
            send_r_bad_request(req);
        close(data_fd);
        return 0;
    }
    req->data_mem = req->mmap_entry_var->mmap;
    close(data_fd);             /* close data file */

    if ((long) req->data_mem == -1) {
        boa_perror(req, "mmap");
        return 0;
    }

#ifdef DAVINCI_IPCAM
    if (dynamic) {
            char *addr = (char *)malloc(req->filesize + 1025);
            if (addr) {
                req->mem_flag |= MFLAG_IS_MEMORY;
                req->mmap_ptr = req->data_mem;
                req->mmap_size = req->filesize;
                memcpy(addr+1024, req->data_mem, req->filesize);
                addr[req->filesize+1024] = '\0';
                req->data_mem = addr;
                req->filesize = html_argument_parse(req->authority, addr+1024, req->data_mem);
                send_request_ok_no_cache(req);     /* All's well */
                return 1;
            }
    }
#endif  // DAVINCI_IPCAM

    send_r_request_ok(req);     /* All's well */

    bytes = BUFFER_SIZE - req->buffer_end;

    /* bytes is now how much the buffer can hold
     * after the headers
     */

    if (bytes > 0) {
        if (bytes > req->filesize)
            bytes = req->filesize;

        if (sigsetjmp(env, 1) == 0) {
            handle_sigbus = 1;
            memcpy(req->buffer + req->buffer_end, req->data_mem, bytes);
            handle_sigbus = 0;
            /* OK, SIGBUS **after** this point is very bad! */
        } else {
            /* sigbus! */
            log_error_doc(req);
            reset_output_buffer(req);
            send_r_error(req);
            fprintf(stderr, "%sGot SIGBUS in memcpy!\n", get_commonlog_time());
            return 0;
        }
        req->buffer_end += bytes;
        req->filepos += bytes;
        if (req->filesize == req->filepos) {
            req_flush(req);
            req->status = DONE;
        }
    }

    /* We lose statbuf here, so make sure response has been sent */
    return 1;
}
Exemple #29
0
/*
 * Name: boa_perror
 *
 * Description: logs an error to user and error file both
 *
 */
void boa_perror(request * req, const char *message)
{
    log_error_doc(req);
    perror(message);            /* don't need to save errno because log_error_doc does */
    send_r_error(req);
}
Exemple #30
0
int init_get(request * req)
{
    int data_fd, saved_errno;
    struct stat statbuf;
    volatile unsigned int bytes_free;

//fprintf(stderr, "###[%s %d] req->pathname=%s###\n", __FUNCTION__, __LINE__, req->pathname);
//fprintf(stderr, "###[%s %d] req->client_stream=%s###\n", __FUNCTION__, __LINE__, req->client_stream);
//fprintf(stderr, "###[%s %d] req->logline=%s###\n", __FUNCTION__, __LINE__, req->logline);
//fprintf(stderr, "###[%s %d] req->request_uri=%s###\n", __FUNCTION__, __LINE__, req->request_uri);
//fprintf(stderr, "###[%s %d] req->host=%s###\n", __FUNCTION__, __LINE__, req->host);

	/* A special GET request: "GET /boaform/formWlanRedirect?redirect-url=wlbasic.htm&wlan_id=0 HTTP/1.1" */
	if (strstr(req->request_uri, "formWlanRedirect")) {
		char *redirectUrl, *strWlanId, *ptr;
		extern void formWlanRedirect2(request *wp, char *redirectUrl, char *strWlanId);
		if ((ptr = strstr(req->client_stream, "redirect-url="))) {
			redirectUrl = ptr + strlen("redirect-url=");
			if ((ptr = strstr(redirectUrl, "&wlan_id="))) {
				*ptr = '\0';
				strWlanId = ptr + strlen("&wlan_id=");
				if ((ptr = strstr(strWlanId, " HTTP"))) {
					*ptr = '\0';
					//fprintf(stderr, "###[%s %d] redirectUrl=%s strWlanId=%s###\n", __FUNCTION__, __LINE__, redirectUrl, strWlanId);
					formWlanRedirect2(req, redirectUrl, strWlanId);
					return 0;
				}
			}
		}
	}

    data_fd = open(req->pathname, O_RDONLY);
    saved_errno = errno;        /* might not get used */

#ifdef GUNZIP
    if (data_fd == -1 && errno == ENOENT) {
        /* cannot open */
        /* it's either a gunzipped file or a directory */
        char gzip_pathname[MAX_PATH_LENGTH];
        unsigned int len;

        len = strlen(req->pathname);

        if (len + 4 > sizeof(gzip_pathname)) {
            log_error_doc(req);
            fprintf(stderr, "Pathname + .gz too long! (%s)\n", req->pathname);
            send_r_bad_request(req);
            return 0;
        }

        memcpy(gzip_pathname, req->pathname, len);
        memcpy(gzip_pathname + len, ".gz", 3);
        gzip_pathname[len + 3] = '\0';
        data_fd = open(gzip_pathname, O_RDONLY);
        if (data_fd != -1) {
            close(data_fd);

            req->response_status = R_REQUEST_OK;
            if (req->pathname)
                free(req->pathname);
            req->pathname = strdup(gzip_pathname);
            if (!req->pathname) {
                boa_perror(req, "strdup req->pathname for gzipped filename " __FILE__ ":" STR(__LINE__));
                return 0;
            }
            if (req->http_version != HTTP09) {
                req_write(req, http_ver_string(req->http_version));
                req_write(req, " 200 OK-GUNZIP" CRLF);
                print_http_headers(req);
                print_content_type(req);
                print_last_modified(req);
                req_write(req, CRLF);
                req_flush(req);
            }
            if (req->method == M_HEAD)
                return 0;

            return init_cgi(req);
        }
    }
#endif

    if (data_fd == -1) {


        log_error_doc(req);
        errno = saved_errno;
	DEBUG(DEBUG_BOA) {
        perror("document open");
        fprintf(stderr, "req->pathname=%s\n", (req->pathname ? req->pathname : "null"));
	}
#if 0
        if (saved_errno == ENOENT)
            send_r_not_found(req);
        else if (saved_errno == EACCES)
            send_r_forbidden(req);
        else
            send_r_bad_request(req);
#else
	send_redirect_perm(req,"home.htm");
#endif

	return 0;
    }