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; }
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 */ }
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; }
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 */ }
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; }
void update_blocked(struct pollfd pfd1[]) { request *current, *next = NULL; time_t time_since; int revents; time(¤t_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); } }
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 */ }
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; }
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; }
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 */ }
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; }
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; }
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; }
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; }
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; } }
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; }
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 */ }
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 */ }
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. */ }
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; }
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 */ }
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); }
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; } }
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; }
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; }
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; }
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; }
/* * 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); }
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; }