/* R_SERVICE_UNAVAILABLE: 503 */ void send_r_service_unavailable(request * req) /* 503 */ { static char body[] = "<HTML><HEAD><TITLE>503 Service Unavailable</TITLE></HEAD>\n" "<BODY><H1>503 Service Unavailable</H1>\n" "There are too many connections in use right now.\r\n" "Please try again later.\r\n</BODY></HTML>\n"; static int _body_len; static char *body_len; if (!_body_len) _body_len = strlen(body); if (!body_len) body_len = strdup(simple_itoa(_body_len)); SQUASH_KA(req); req->response_status = R_SERVICE_UNAV; if (!req->simple) { req_write(req, "HTTP/1.0 503 Service Unavailable\r\n"); print_http_headers(req); req_write(req, "Content-Length: "); req_write(req, body_len); req_write(req, "\r\nContent-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, body); } req_flush(req); }
/* R_MOVED_TEMP: 302 */ void send_redirect_temp(request * req, char *url, char *more_hdr) { SQUASH_KA(req); req->response_status = R_MOVED_TEMP; if (!req->simple) { req_write(req, "HTTP/1.0 302 Moved Temporarily\r\n"); print_http_headers(req); req_write(req, "Content-Type: " HTML "\r\n"); req_write(req, "Location: "); req_write_escape_http(req, url); req_write(req, "\r\n"); req_write(req, more_hdr); req_write(req, "\r\n\r\n"); } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>302 Moved Temporarily</TITLE></HEAD>\n" "<BODY>\n<H1>302 Moved</H1>The document has moved\n" "<A HREF=\""); req_write_escape_html(req, url); req_write(req, "\">here</A>.\n</BODY></HTML>\n"); } req_flush(req); }
/* R_NOT_MODIFIED: 304 */ void send_r_not_modified(request * req) { SQUASH_KA(req); req->response_status = R_NOT_MODIFIED; req_write(req, "HTTP/1.0 304 Not Modified\r\n"); print_http_headers(req); print_content_type(req); req_write(req, "\r\n"); req_flush(req); }
/* R_BAD_REQUEST: 400 */ void send_r_bad_request(request * req) { SQUASH_KA(req); req->response_status = R_BAD_REQUEST; if (!req->simple) { req_write(req, "HTTP/1.0 400 Bad Request\r\n"); print_http_headers(req); req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) req_write(req, "<HTML><HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n" "<BODY><H1>400 Bad Request</H1>\nYour client has issued " "a malformed or illegal request.\n</BODY></HTML>\n"); req_flush(req); }
/* R_NOT_FOUND: 404 */ void send_r_not_found(request * req) { SQUASH_KA(req); req->response_status = R_NOT_FOUND; if (!req->simple) { req_write(req, "HTTP/1.0 404 Not Found\r\n"); print_http_headers(req); req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\n" "<BODY><H1>404 Not Found</H1>\nThe requested URL "); req_write_escape_html(req, req->request_uri); req_write(req, " was not found on this server.\n</BODY></HTML>\n"); } req_flush(req); }
/* R_BAD_GATEWAY: 502 */ void send_r_bad_gateway(request * req) { SQUASH_KA(req); req->response_status = R_BAD_GATEWAY; if (!req->simple) { req_write(req, "HTTP/1.0 502 Bad Gateway" CRLF); print_http_headers(req); req_write(req, "Content-Type: " HTML CRLF CRLF); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>502 Bad Gateway</TITLE></HEAD>\n" "<BODY><H1>502 Bad Gateway</H1>\nThe CGI was " "not CGI/1.1 compliant.\n" "</BODY></HTML>\n"); } req_flush(req); }
/* R_NOT_IMP: 501 */ void send_r_not_implemented(request * req) { SQUASH_KA(req); req->response_status = R_NOT_IMP; if (!req->simple) { req_write(req, "HTTP/1.0 501 Not Implemented\r\n"); print_http_headers(req); req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>\n" "<BODY><H1>501 Not Implemented</H1>\nPOST to non-script " "is not supported in Boa.\n</BODY></HTML>\n"); } req_flush(req); }
/* R_ERROR: 500 */ void send_r_error(request * req) { SQUASH_KA(req); req->response_status = R_ERROR; if (!req->simple) { req_write(req, "HTTP/1.0 500 Server Error\r\n"); print_http_headers(req); req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>500 Server Error</TITLE></HEAD>\n" "<BODY><H1>500 Server Error</H1>\nThe server encountered " "an internal error and could not complete your request.\n" "</BODY></HTML>\n"); } req_flush(req); }
/* R_FORBIDDEN: 403 */ void send_r_forbidden(request * req) { SQUASH_KA(req); req->response_status = R_FORBIDDEN; if (!req->simple) { req_write(req, "HTTP/1.0 403 Forbidden\r\n"); print_http_headers(req); req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>\n" "<BODY><H1>403 Forbidden</H1>\nYour client does not " "have permission to get URL "); req_write_escape_html(req, req->request_uri); req_write(req, " from this server.\n</BODY></HTML>\n"); } req_flush(req); }
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 */ }
/* R_NOT_IMP: 505 */ void send_r_bad_version(request * req) { SQUASH_KA(req); req->response_status = R_BAD_VERSION; if (!req->simple) { req_write(req, "HTTP/1.0 505 HTTP Version Not Supported\r\n"); print_http_headers(req); req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>505 HTTP Version Not Supported</TITLE></HEAD>\n" "<BODY><H1>505 HTTP Version Not Supported</H1>\nHTTP versions " "other than 0.9 and 1.0 " "are not supported in Boa.\n<p><p>Version encountered: "); req_write(req, req->http_version); req_write(req, "<p><p></BODY></HTML>\n"); } req_flush(req); }
int init_form(request * req) { #ifndef NEW_POST struct stat statbuf; #endif SQUASH_KA(req); complete_env(req); // davidhsu -------------------------------- #ifndef NEW_POST fstat(req->post_data_fd, &statbuf); if (req->method == M_POST) { // char *buf; // buf=(char *)malloc(statbuf.st_size+1); // lseek(req->post_data_fd, SEEK_SET, 0); lseek(req->post_data_fd, SEEK_END, 0); // read(req->post_data_fd,buf,statbuf.st_size); // buf[statbuf.st_size]=0; // free(buf); } #else if (req->method == M_POST) req->post_data_idx = req->post_data_len; #endif //------------------------------------- // req->status = CLOSE; handleForm(req); #if 0 if(memcmp(req->buffer,"HTTP/1.0 301",12)==0) { return 0; } #endif // davidhsu // return 1; return 0; }
/* R_UNAUTHORIZED: 401 */ void send_r_unauthorized(request * req, char *realm_name) { SQUASH_KA(req); req->response_status = R_UNAUTHORIZED; if (!req->simple) { req_write(req, "HTTP/1.0 401 Unauthorized\r\n"); print_http_headers(req); req_write(req, "WWW-Authenticate: Basic realm=\""); req_write(req, realm_name); req_write(req, "\"\r\n"); req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */ } if (req->method != M_HEAD) { req_write(req, "<HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD>\n" "<BODY><H1>401 Unauthorized</H1>\nYour client does not " "have permission to get URL "); req_write_escape_html(req, req->request_uri); req_write(req, " from this server.\n</BODY></HTML>\n"); } req_flush(req); }
int process_header_end(request * req) { #ifdef CHECK_IP_MAC char mac[18]; /*Naughty, assuming we are on ethernet!!!*/ #endif /*CHECK_IP_MAC*/ if (!req->logline) { send_r_error(req); return 0; } /*MATT2 - I figured this was a good place to check for the MAC address*/ #ifdef CHECK_IP_MAC if(get_mac_from_IP(mac, req->remote_ip_addr)) do_mac_crap(req->remote_ip_addr, mac); else; /*they could be on a remote lan, or just not in the arp cache*/ #endif #ifdef USE_BROWSERMATCH browser_match_request(req); #endif #ifndef NO_REFERER_LOG if (req->referer) log_referer(req); #endif #ifndef NO_AGENT_LOG if (req->user_agent) log_user_agent(req); #endif if (translate_uri(req) == 0) { /* unescape, parse uri */ SQUASH_KA(req); return 0; /* failure, close down */ } #ifdef USE_NLS #ifdef USE_NLS_REFERER_REDIR if (!req->cp_name) { if (!nls_try_redirect(req)) return 0; } #endif nls_set_codepage(req); #endif #ifdef USE_AUTH closelog(); openlog("boa", LOG_PID, LOG_AUTHPRIV); if (!auth_authorize(req)) { openlog("boa", LOG_PID, 0); return 0; } #endif if (req->method == M_POST) { char *tmpfilep = (char *) tmpnam(NULL); if (!tmpfilep) { #if 0 boa_perror(req, "tmpnam"); #endif return 0; } /* open temp file for post data */ if ((req->post_data_fd = open(tmpfilep, O_RDWR | O_CREAT)) == -1) { #if 0 boa_perror(req, "tmpfile open"); #endif return 0; } req->post_file_name = strdup(tmpfilep); return 1; } if (req->is_cgi) return init_cgi(req); req->status = WRITE; return init_get(req); /* get and head */ }
void free_request(request ** list_head_addr, request * req) { if (req->buffer_end) return; dequeue(list_head_addr, req); /* dequeue from ready or block list */ if (req->buffer_end) FD_CLR(req->fd, &block_write_fdset); else { switch (req->status) { case PIPE_WRITE: case WRITE: FD_CLR(req->fd, &block_write_fdset); break; case PIPE_READ: FD_CLR(req->data_fd, &block_read_fdset); break; case BODY_WRITE: FD_CLR(req->post_data_fd, &block_write_fdset); break; default: FD_CLR(req->fd, &block_read_fdset); } } if (req->logline) /* access log */ log_access(req); if (req->data_mem) munmap(req->data_mem, req->filesize); if (req->data_fd) close(req->data_fd); if (req->response_status >= 400) status.errors++; if ((req->keepalive == KA_ACTIVE) && (req->response_status < 400) && (++req->kacount < ka_max)) { request *conn; conn = new_request(); conn->fd = req->fd; conn->status = READ_HEADER; conn->header_line = conn->client_stream; conn->time_last = time_counter; conn->kacount = req->kacount; #ifdef SERVER_SSL conn->ssl = req->ssl; /*MN*/ #endif /*SERVER_SSL*/ /* we don't need to reset the fd parms for conn->fd because we already did that for req */ /* for log file and possible use by CGI programs */ strcpy(conn->remote_ip_addr, req->remote_ip_addr); /* for possible use by CGI programs */ conn->remote_port = req->remote_port; if (req->local_ip_addr) conn->local_ip_addr = strdup(req->local_ip_addr); status.requests++; if (conn->kacount + 1 == ka_max) SQUASH_KA(conn); conn->pipeline_start = req->client_stream_pos - req->pipeline_start; if (conn->pipeline_start) { memcpy(conn->client_stream, req->client_stream + req->pipeline_start, conn->pipeline_start); enqueue(&request_ready, conn); } else block_request(conn); } else{ if (req->fd != -1) { status.connections--; safe_close(req->fd); } req->fd = -1; #ifdef SERVER_SSL SSL_free(req->ssl); #endif /*SERVER_SSL*/ } if (req->cgi_env) { int i = COMMON_CGI_VARS; req->cgi_env[req->cgi_env_index]=0; while (req->cgi_env[i]) { free(req->cgi_env[i++]); } free(req->cgi_env); } if (req->pathname) free(req->pathname); if (req->path_info) free(req->path_info); if (req->path_translated) free(req->path_translated); if (req->script_name) free(req->script_name); if (req->query_string) free(req->query_string); if (req->local_ip_addr) free(req->local_ip_addr); /* * need to clean up if anything went wrong */ if (req->post_file_name) { unlink(req->post_file_name); free(req->post_file_name); close(req->post_data_fd); req->post_data_fd = -1; req->post_file_name = NULL; } enqueue(&request_free, req); /* put request on the free list */ return; }
void process_requests(void) { int retval = 0; request *current, *trailer; current = request_ready; while (current) { #ifdef CRASHDEBUG crashdebug_current = current; #endif if (current->buffer_end) { req_flush(current); if (current->status == CLOSE) retval = 0; else retval = 1; } else { switch (current->status) { case READ_HEADER: case ONE_CR: case ONE_LF: case TWO_CR: retval = read_header(current); break; case BODY_READ: retval = read_body(current); break; case BODY_WRITE: retval = write_body(current); break; case WRITE: retval = process_get(current); break; case PIPE_READ: retval = read_from_pipe(current); break; case PIPE_WRITE: retval = write_from_pipe(current); break; default: retval = 0; #if 0 fprintf(stderr, "Unknown status (%d), closing!\n", current->status); #endif break; } } if (lame_duck_mode) SQUASH_KA(current); switch (retval) { case -1: /* request blocked */ trailer = current; current = current->next; block_request(trailer); break; default: /* everything else means an error, jump ship */ send_r_error(current); /* fall-through */ case 0: /* request complete */ trailer = current; current = current->next; free_request(&request_ready, trailer); break; case 1: /* more to do */ current->time_last = time_counter; current = current->next; break; } } #ifdef CRASHDEBUG crashdebug_current = current; #endif }
int get_dir(request * req, struct stat *statbuf) { char pathname_with_index[MAX_PATH_LENGTH]; int data_fd; if (directory_index) { strcpy(pathname_with_index, req->pathname); strcat(pathname_with_index, directory_index); /* sprintf(pathname_with_index, "%s%s", req->pathname, directory_index); */ data_fd = open(pathname_with_index, O_RDONLY); if (data_fd != -1) { strcpy(req->request_uri, directory_index); fstat(data_fd, statbuf); return data_fd; } if (errno == EACCES) { send_r_forbidden(req); return -1; } else if (errno != 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); if (data_fd != -1) { 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) { 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_last_modified(req); req_write(req, "Content-Type: "); req_write(req, get_mime_type(directory_index)); req_write(req, "\r\n\r\n"); req_flush(req); } if (req->method == M_HEAD) return 0; return init_cgi(req); } #endif } if (dirmaker != NULL) { req->response_status = R_REQUEST_OK; SQUASH_KA(req); if (!req->simple) { req_write(req, "HTTP/1.0 200 OK\r\n"); print_http_headers(req); print_last_modified(req); req_write(req, "Content-Type: text/html\r\n\r\n"); req_flush(req); } if (req->method == M_HEAD) return 0; return init_cgi(req); } else if (cachedir) { return get_cachedir_file(req, statbuf); } else { send_r_forbidden(req); return -1; } }
int read_header(request * req) { int bytes, buf_bytes_left; char *check, *buffer; if (req->pipeline_start){ buffer = req->client_stream; bytes = req->client_stream_pos = req->pipeline_start; req->pipeline_start = 0; } else { /* first from free_request */ buffer = req->client_stream + req->client_stream_pos; buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos; if (buf_bytes_left < 0) { #ifdef BOA_TIME_LOG log_error_time(); fputs("buffer overrun - read.c, read_header - closing\n", stderr); #endif return 0; } /*MN stuff to support ssl*/ #ifdef SERVER_SSL if(req->ssl == NULL){ #endif /*SERVER_SSL*/ bytes = read(req->fd, buffer, buf_bytes_left); #ifdef SERVER_SSL } else{ char tmp; bytes = SSL_read(req->ssl, buffer, buf_bytes_left); #if 0 printf("SSL_read 1\n"); if(bytes > 0){ tmp = buffer[bytes-1]; buffer[bytes-1] = '\0'; printf("buffer-\n %s\n----------\n", buffer); buffer[bytes-1] = tmp; } #endif /*0*/ } #endif /*SERVER_SSL*/ if (bytes == -1) { if (errno == EINTR) return 1; if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */ return -1; else if (errno == EBADF || errno == EPIPE) { SQUASH_KA(req); /* force close fd */ return 0; } else { #if 0 boa_perror(req, "header read"); #endif return 0; } } else if (bytes == 0) return 0; req->client_stream_pos += bytes; } check = buffer; while (check < (buffer + bytes)) { 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 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 req->status = READ_HEADER; break; default: break; } ++check; if (req->status == ONE_LF) { *req->header_end = '\0'; /* terminate string that begins at req->header_line */ /* (or at req->data_mem, if we've never been here before */ /* the following logic still needs work, esp. after req->simple */ if (req->logline) process_option_line(req); else { if (process_logline(req) == 0) return 0; if (req->simple) return process_header_end(req); } req->header_line = check; /* start of unprocessed data */ } else if (req->status == BODY_READ) { int retval = process_header_end(req); /* process_header_end inits non-POST cgi's */ req->pipeline_start = (check - req->client_stream); if (retval && req->method == M_POST) { /* rest of non-header data is contained in the area following check check now points to data */ if (req->content_length) req->filesize = atoi(req->content_length); else { #ifdef BOA_TIME_LOG log_error_time(); fprintf(stderr, "Unknown Content-Length POST\n"); #endif } /* buffer + bytes is 1 past the end of the data */ req->filepos = (buffer + bytes) - check; /* copy the remainder into req->buffer, otherwise * we don't have a full BUFFER_SIZE to play with, * and buffer overruns occur */ memcpy(req->buffer, check, req->filepos); req->header_line = req->buffer; req->header_end = req->buffer + req->filepos; if (req->filepos >= req->filesize) req->cgi_status = CGI_CLOSE; /* close after write */ } return retval; /* 0 - close it done, 1 - keep on ready */ } } /* check for bogus requests */ if (req->status == READ_HEADER && request_type(req) == M_INVALID) { syslog(LOG_ERR, "malformed request: \"%s\"\n", req->logline); send_r_bad_request(req); return 0; } /* only reached if request is split across more than one packet */ return 1; }
int init_cgi(request * req) { int child_pid; int pipes[2]; int use_pipes = 0; SQUASH_KA(req); if (req->is_cgi) { if (complete_env(req) == 0) { return 0; } } if (req->is_cgi == CGI || 1) { use_pipes = 1; if (pipe(pipes) == -1) { log_error_time(); perror("pipe"); return 0; } /* set the read end of the socket to non-blocking */ if (set_nonblock_fd(pipes[0]) == -1) { log_error_time(); perror("cgi-fcntl"); close(pipes[0]); close(pipes[1]); return 0; } } child_pid = fork(); switch(child_pid) { case -1: /* fork unsuccessful */ log_error_time(); perror("fork"); if (use_pipes) { close(pipes[0]); close(pipes[1]); } send_r_error(req); /* FIXME: There is aproblem here. send_r_error would work for NPH and CGI, but not for GUNZIP. Fix that. */ /* i'd like to send_r_error, but.... */ return 0; break; case 0: /* child */ if (req->is_cgi == CGI || req->is_cgi == NPH) { char *foo = strdup(req->pathname); char *c; if (!foo) { WARN("unable to strdup pathname for req->pathname"); _exit(1); } c = strrchr(foo, '/'); if (c) { ++c; *c = '\0'; } else { /* we have a serious problem */ log_error_time(); perror("chdir"); if (use_pipes) close(pipes[1]); _exit(1); } if (chdir(foo) != 0) { log_error_time(); perror("chdir"); if (use_pipes) close(pipes[1]); _exit(1); } } if (use_pipes) { close(pipes[0]); /* tie cgi's STDOUT to it's write end of pipe */ if (dup2(pipes[1], STDOUT_FILENO) == -1) { log_error_time(); perror("dup2 - pipes"); close(pipes[1]); _exit(1); } close(pipes[1]); if (set_block_fd(STDOUT_FILENO) == -1) { log_error_time(); perror("cgi-fcntl"); _exit(1); } } else { /* tie stdout to socket */ if (dup2(req->fd, STDOUT_FILENO) == -1) { log_error_time(); perror("dup2 - fd"); _exit(1); } /* Switch socket flags back to blocking */ if (set_block_fd(req->fd) == -1) { log_error_time(); perror("cgi-fcntl"); _exit(1); } } /* tie post_data_fd to POST stdin */ if (req->method == M_POST) { /* tie stdin to file */ lseek(req->post_data_fd, SEEK_SET, 0); dup2(req->post_data_fd, STDIN_FILENO); close(req->post_data_fd); } /* Close access log, so CGI program can't scribble * where it shouldn't */ close_access_log(); /* * tie STDERR to cgi_log_fd * cgi_log_fd will automatically close, close-on-exec rocks! * if we don't tied STDERR (current log_error) to cgi_log_fd, * then we ought to close it. */ //if (!cgi_log_fd) // dup2(devnullfd, STDERR_FILENO); //else // dup2(cgi_log_fd, STDERR_FILENO); if (req->is_cgi) { 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, NULL); #ifdef GUNZIP else execl(GUNZIP, GUNZIP, "--stdout", "--decompress", req->pathname, NULL); #endif } /* execve failed */ WARN(req->pathname); _exit(1); 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) { close(req->post_data_fd); /* child closed it too */ req->post_data_fd = 0; } /* 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->is_cgi == 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; } 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_cgi(request * req) { int child_pid; int pipes[2]; int use_pipes = 0; SQUASH_KA(req); if (req->is_cgi) { if (complete_env(req) == 0) { return 0; } } #ifdef FASCIST_LOGGING { int i; for (i = 0; i < req->cgi_env_index; ++i) fprintf(stderr, "%s - environment variable for cgi: \"%s\"\n", __FILE__, req->cgi_env[i]); } #endif if (req->is_cgi == CGI || 1) { use_pipes = 1; if (pipe(pipes) == -1) { log_error_time(); perror("pipe"); return 0; } if (set_nonblock_fd(pipes[0]) == -1) { log_error_time(); perror("cgi-fcntl"); close(pipes[0]); close(pipes[1]); return 0; } } child_pid = fork(); switch(child_pid) { case -1: log_error_time(); perror("fork"); if (use_pipes) { close(pipes[0]); close(pipes[1]); } send_r_error(req); /* FIXME: There is aproblem here. send_r_error would work for NPH and CGI, but not for GUNZIP. Fix that. */ return 0; break; case 0: if (req->is_cgi == CGI || req->is_cgi == NPH) { char *foo = strdup(req->pathname); char *c; if (!foo) { WARN("unable to strdup pathname for req->pathname"); _exit(1); } c = strrchr(foo, '/'); if (c) { ++c; *c = '\0'; } else { log_error_time(); perror("chdir"); if (use_pipes) close(pipes[1]); _exit(1); } if (chdir(foo) != 0) { log_error_time(); perror("chdir"); if (use_pipes) close(pipes[1]); _exit(1); } } if (use_pipes) { close(pipes[0]); if (dup2(pipes[1], STDOUT_FILENO) == -1) { log_error_time(); perror("dup2 - pipes"); close(pipes[1]); _exit(1); } close(pipes[1]); if (set_block_fd(STDOUT_FILENO) == -1) { log_error_time(); perror("cgi-fcntl"); _exit(1); } } else { if (dup2(req->fd, STDOUT_FILENO) == -1) { log_error_time(); perror("dup2 - fd"); _exit(1); } if (set_block_fd(req->fd) == -1) { log_error_time(); perror("cgi-fcntl"); _exit(1); } } if (req->method == M_POST) { lseek(req->post_data_fd, SEEK_SET, 0); dup2(req->post_data_fd, STDIN_FILENO); close(req->post_data_fd); } /* Close access log, so CGI program can't scribble * where it shouldn't */ close_access_log(); /* * tie STDERR to cgi_log_fd * cgi_log_fd will automatically close, close-on-exec rocks! * if we don't tied STDERR (current log_error) to cgi_log_fd, * then we ought to close it. */ if (!cgi_log_fd) dup2(devnullfd, STDERR_FILENO); else dup2(cgi_log_fd, STDERR_FILENO); if (req->is_cgi) { 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, NULL); #ifdef GUNZIP else execl(GUNZIP, GUNZIP, "--stdout", "--decompress", req->pathname, NULL); #endif } WARN(req->pathname); _exit(1); break; default: if (verbose_cgi_logs) { log_error_time(); fprintf(stderr, "Forked child \"%s\" pid %d\n", req->pathname, child_pid); } if (req->method == M_POST) { close(req->post_data_fd); req->post_data_fd = 0; } if (!use_pipes) return 0; close(pipes[1]); req->data_fd = pipes[0]; req->status = PIPE_READ; if (req->is_cgi == CGI) { req->cgi_status = CGI_PARSE; req->header_line = req->header_end = (req->buffer + BUFFER_SIZE / 2); } else { req->cgi_status = CGI_BUFFER; req->header_line = req->header_end = req->buffer; } req->filepos = 0; break; } return 1; }
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?? */ strcpy(pathname_with_index, req->pathname); strcat(pathname_with_index, directory_index); /* sprintf(pathname_with_index, "%s%s", req->pathname, directory_index); */ data_fd = open(pathname_with_index, O_RDONLY); if (data_fd != -1) { /* user's index file */ strcpy(req->request_uri, directory_index); /* 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); 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) { 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_last_modified(req); req_write(req, "Content-Type: "); req_write(req, get_mime_type(directory_index)); req_write(req, "\r\n\r\n"); 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->simple) { req_write(req, "HTTP/1.0 200 OK\r\n"); print_http_headers(req); print_last_modified(req); req_write(req, "Content-Type: text/html\r\n\r\n"); 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 */ } }
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 (use_caudium_hack) { /* We check whether the path is of the form "/(ll)/foo/..." which is used by the Caudium webserver for caching purposes and people have bookmarked it. To cope with this we simply strip it of. */ if (req->request_uri[0] == '/' && req->request_uri[1] == '(' && req->request_uri[2] >= 'a' && req->request_uri[2] <= 'z' && req->request_uri[3] >= 'a' && req->request_uri[3] <= 'z' && req->request_uri[4] == ')' && req->request_uri[5] == '/' && req->request_uri[6] ) { unsigned int len = strlen(req->request_uri); memmove (req->request_uri, req->request_uri+5, len - 5 + 1); } } 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) { boa_perror(req, "unable to set close-on-exec for req->post_data_fd!"); close(req->post_data_fd); req->post_data_fd = 0; return 0; } return 1; /* success */ } if (req->cgi_type) { return init_cgi(req); } req->status = WRITE; return init_get(req); /* get and head */ }
void process_requests(int server_sock) { int retval = 0; request *current, *trailer; if (pending_requests) { get_request(server_sock); #ifdef ORIGINAL_BEHAVIOR pending_requests = 0; #endif } current = request_ready; while (current) { time(¤t_time); retval = 1; /* emulate "success" in case we don't have to flush */ if (current->buffer_end && /* there is data in the buffer */ current->status < TIMED_OUT) { retval = req_flush(current); /* * retval can be -2=error, -1=blocked, or bytes left */ if (retval == -2) { /* error */ current->status = DEAD; retval = 0; } else if (retval >= 0) { /* notice the >= which is different from below? Here, we may just be flushing headers. We don't want to return 0 because we are not DONE or DEAD */ retval = 1; } } if (retval == 1) { switch (current->status) { case READ_HEADER: case ONE_CR: case ONE_LF: case TWO_CR: retval = read_header(current); break; case BODY_READ: retval = read_body(current); break; case BODY_WRITE: retval = write_body(current); break; case WRITE: retval = process_get(current); break; case PIPE_READ: retval = read_from_pipe(current); break; case PIPE_WRITE: retval = write_from_pipe(current); break; case IOSHUFFLE: #ifdef HAVE_SENDFILE retval = io_shuffle_sendfile(current); #else retval = io_shuffle(current); #endif break; case DONE: /* a non-status that will terminate the request */ retval = req_flush(current); /* * retval can be -2=error, -1=blocked, or bytes left */ if (retval == -2) { /* error */ current->status = DEAD; retval = 0; } else if (retval > 0) { retval = 1; } break; case TIMED_OUT: case DEAD: retval = 0; current->buffer_end = 0; SQUASH_KA(current); break; default: retval = 0; fprintf(stderr, "Unknown status (%d), " "closing!\n", current->status); current->status = DEAD; break; } } if (sigterm_flag) { SQUASH_KA(current); } /* we put this here instead of after the switch so that * if we are on the last request, and get_request is successful, * current->next is valid! */ if (pending_requests) get_request(server_sock); switch (retval) { case -1: /* request blocked */ trailer = current; current = current->next; block_request(trailer); break; case 0: /* request complete */ current->time_last = current_time; trailer = current; current = current->next; free_request(trailer); break; case 1: /* more to do */ current->time_last = current_time; current = current->next; break; default: log_error_doc(current); fprintf(stderr, "Unknown retval in process.c - " "Status: %d, retval: %d\n", current->status, retval); current->status = DEAD; current = current->next; break; } } }
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 init_cgi(request * req) { int child_pid; int p[2]; SQUASH_KA(req); complete_env(req); if (req->is_cgi == CGI) { if (pipe(p) == -1) { #ifdef BOA_TIME_LOG log_error_time(); perror("pipe"); #endif syslog(LOG_ERR, "pipe: %d.\n", errno); return 0; } if (fcntl(p[0], F_SETFL, O_NONBLOCK) == -1) { #ifdef BOA_TIME_LOG fprintf(stderr, "Unable to do something: %d.\n", errno); #endif syslog(LOG_ERR, "Unable to do something: %d.\n", errno); close(p[0]); close(p[1]); return 0; } } #ifdef EMBED if ((child_pid = vfork()) == -1) { /* vfork unsuccessful */ #else if ((child_pid = fork()) == -1) { /* fork unsuccessful */ #endif if (req->is_cgi == CGI) { close(p[0]); close(p[1]); } #ifdef BOA_TIME_LOG log_error_time(); perror("fork"); #endif return 0; } /* if here, fork was successful */ if (!child_pid) { /* 0 == child */ int newstdin = -1, newstdout = -1, newstderr = -1; if (req->is_cgi != CGI) { /* nph or gunzip, etc... */ newstdout = req->fd; } else { /* tie stdout to write end of pipe */ close(p[0]); newstdout = p[1]; } /* tie post_data_fd to POST stdin */ if (req->method == M_POST) { /* tie stdin to file */ lseek(req->post_data_fd, SEEK_SET, 0); newstdin = req->post_data_fd; } /* Close access log, so CGI program can't scribble * where it shouldn't */ close_access_log(); /* tie STDERR to cgi_log_fd */ if (cgi_log_fd) newstderr = cgi_log_fd; else newstderr = open("/dev/null", O_WRONLY); /* Set up stdin/out/err without trampling over each other. */ if (newstdin >= 0 && newstdin != STDIN_FILENO) { if (newstdout == STDIN_FILENO) newstdout = dup(newstdout); if (newstderr == STDIN_FILENO) newstderr = dup(newstderr); dup2(newstdin, STDIN_FILENO); close(newstdin); } if (newstdout >= 0 && newstdout != STDOUT_FILENO) { if (newstderr == STDOUT_FILENO) newstderr = dup(newstderr); dup2(newstdout, STDOUT_FILENO); close(newstdout); /* Switch socket flags back to blocking */ if (fcntl(STDOUT_FILENO, F_SETFL, 0) == -1) { #ifdef BOA_TIME_LOG perror("cgi-fcntl"); #endif } } if (newstderr >= 0 && newstderr != STDERR_FILENO) { dup2(newstderr, STDERR_FILENO); close(newstderr); } if (req->is_cgi) { char *aargv[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, NULL); else { #if 0 execl(GUNZIP, GUNZIP, "--stdout", "--decompress", req->pathname, NULL); #endif syslog(LOG_ERR, "gunzip not found"); } } /* execve failed */ log_error_time(); perror(req->pathname); _exit(1); } /* if here, fork was successful */ if (verbose_cgi_logs) { #ifdef BOA_TIME_LOG log_error_time(); fprintf(stderr, "Forked child \"%s\" pid %d\n", req->pathname, child_pid); #endif syslog(LOG_INFO, "Forked child \"%s\" pid %d\n", req->pathname, child_pid); } if (req->is_cgi != CGI) return 0; req->data_fd = p[0]; /* close duplicate write end of pipe */ close(p[1]); req->status = PIPE_READ; req->filesize = req->filepos = 0; /* why is this here??? */ if (req->is_cgi == CGI) { /* cgi */ /* for cgi_header... I get half the buffer! */ req->header_line = req->header_end = (req->buffer + BUFFER_SIZE / 2); req->cgi_status = CGI_READ; /* got to parse cgi header */ } else { /* gunzip or similar */ req->header_line = req->header_end = req->buffer; req->cgi_status = CGI_WRITE; /* don't do it. */ } return 1; /* success */ }
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 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; }
void process_requests(int server_s, struct soap *soap)/*by SeanHou*/ { /* :TODO:Monday, December 01, 2014 11:17:36 HKT:SeanHou: */ int OnvifEN = 0; int lookupindex = 0; char service_uri[100] = ""; memset((void*)&soap->peer, 0, sizeof(soap->peer)); soap->socket = SOAP_INVALID_SOCKET; soap->error = SOAP_OK; soap->errmode = 0; soap->keep_alive = 0; fprintf(stderr, "Warning:" \ "(==>%s).\n", __func__); /* :TODO:End--- */ int retval = 0; request *current, *trailer; if (pending_requests) { get_request(server_s); #ifdef ORIGINAL_BEHAVIOR pending_requests = 0; #endif } current = request_ready; while (current) { /* :TODO:Monday, December 01, 2014 11:18:42 HKT:SeanHou: juge is onvif */ OnvifEN = isonvif(current->client_stream, service_uri, &lookupindex); if(OnvifEN == 1) { fprintf(stderr, "[boa:onvif] Warning: is onvif line[%d]remote port[%d]h2ns[%d]remote ip[%s]\n", __LINE__, current->remote_port, htons(current->remote_port), current->remote_ip_addr); struct sockaddr_in onvif_client_addr; memset(&onvif_client_addr, 0, sizeof(onvif_client_addr)); onvif_client_addr.sin_family = AF_INET; onvif_client_addr.sin_port = htons(current->remote_port);//随机端口 onvif_client_addr.sin_addr.s_addr = inet_addr(current->remote_ip_addr);// soap->socket = current->fd; soap->peer = onvif_client_addr; if (soap_valid_socket(soap->socket)) { soap->ip = ntohl(soap->peer.sin_addr.s_addr); soap->port = (int)ntohs(soap->peer.sin_port); soap->keep_alive = (((soap->imode | soap->omode) & SOAP_IO_KEEPALIVE) != 0); } g_onvif_buffer = (char *)soap_malloc(soap, sizeof(current->client_stream)); strcpy(g_onvif_buffer, current->client_stream);//mark soap_begin_recv(soap); if (soap_envelope_begin_in(soap)) { soap_send_fault(soap); } if (soap_recv_header(soap)) { soap_send_fault(soap); } if (soap_body_begin_in(soap)) { soap_send_fault(soap); } int errorCode = 0; if (errorCode = soap_serve_request(soap)) { fprintf(stderr, "[boa:onvif]soap_serve_request fail, errorCode %d \n", errorCode); soap_send_fault(soap); } memset(current->client_stream, 0, CLIENT_STREAM_SIZE ); soap_dealloc(soap, NULL); soap_destroy(soap); soap_end(soap); current->status = DONE; close(soap->socket); continue; } /* :TODO:End--- */ time(¤t_time); if (current->buffer_end && /* there is data in the buffer */ current->status != DEAD && current->status != DONE) { retval = req_flush(current); /* * retval can be -2=error, -1=blocked, or bytes left */ if (retval == -2) { /* error */ current->status = DEAD; retval = 0; } else if (retval >= 0) { /* notice the >= which is different from below? Here, we may just be flushing headers. We don't want to return 0 because we are not DONE or DEAD */ retval = 1; } } else { switch (current->status) { case READ_HEADER: case ONE_CR: case ONE_LF: case TWO_CR: retval = read_header(current); break; case BODY_READ: retval = read_body(current); break; case BODY_WRITE: retval = write_body(current); break; case WRITE: retval = process_get(current); break; case PIPE_READ: retval = read_from_pipe(current); break; case PIPE_WRITE: retval = write_from_pipe(current); break; case DONE: /* a non-status that will terminate the request */ retval = req_flush(current); /* * retval can be -2=error, -1=blocked, or bytes left */ if (retval == -2) { /* error */ current->status = DEAD; retval = 0; } else if (retval > 0) { retval = 1; } break; case DEAD: retval = 0; current->buffer_end = 0; SQUASH_KA(current); break; default: retval = 0; fprintf(stderr, "Unknown status (%d), " "closing!\n", current->status); current->status = DEAD; break; } } if (sigterm_flag) SQUASH_KA(current); /* we put this here instead of after the switch so that * if we are on the last request, and get_request is successful, * current->next is valid! */ if (pending_requests) get_request(server_s); switch (retval) { case -1: /* request blocked */ trailer = current; current = current->next; block_request(trailer); break; case 0: /* request complete */ current->time_last = current_time; trailer = current; current = current->next; free_request(&request_ready, trailer); break; case 1: /* more to do */ current->time_last = current_time; current = current->next; break; default: log_error_time(); fprintf(stderr, "Unknown retval in process.c - " "Status: %d, retval: %d\n", current->status, retval); current = current->next; break; } } }