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; }
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 handle_input_line(int socket_fd, const char *input_line, int had_cr, int had_lf) { static const char *seperator_seperator = ";"; static const char *terminator_terminator = "\n"; const char *this_stSettings_iTimeOut_str_s = ".THIS.stSettings.iTimeOut="; static unsigned int counter; const char **my_argv = NULL; int argc = create_argv(input_line, had_cr, had_lf, (const char*** )&my_argv); const char *argv1 = (argc > 1) ? my_argv[1] : ""; int is_EAT_cmd = strchr(input_line, ';') != NULL; if (!strncmp(argv1, this_stSettings_iTimeOut_str_s, strlen(this_stSettings_iTimeOut_str_s))) { const char *myarg_1 = &argv1[strlen(this_stSettings_iTimeOut_str_s)]; int timeout; int nvals; nvals = sscanf(myarg_1, "%d", &timeout); if (nvals == 1) { int res = socket_set_timeout(socket_fd, timeout); cmd_buf_printf("%s%s%s", res ? "Error" : "OK", seperator_seperator, terminator_terminator); } else { cmd_buf_printf("Error nvals=%d %s%s", nvals, seperator_seperator, terminator_terminator); } } else if (is_EAT_cmd) { cmd_EAT(argc, my_argv); } else if ((argc > 1) && (0 == strcmp(argv1, "bye"))) { fprintf(stdlog, "%s/%s:%d bye\n", __FILE__, __FUNCTION__, __LINE__); return 1; } else if ((argc > 1) && (0 == strcmp(argv1, "kill"))) { exit(0); } else if (cmd_TCPsim(argc, my_argv)) { ; /* TCPsim command */ } else if (cmd_IcePAP(argc, my_argv)) { ; /* IcePAP command */ } else if (argv1[0] == 'h' || argv1[0] == '?') { fd_printf_crlf(socket_fd, had_cr, "Valid commands :\n"); fd_printf_crlf(socket_fd, had_cr, "bye : Bye\n" "kill : exit(0)\n"); } else if (argc > 2){ fd_printf_crlf(socket_fd, had_cr,"error(%s:%d): invalid command (%s)\n", __FILE__, __LINE__, argv1); } else if (argc == 1) { /* Just a return, print a prompt */ } { int i; for (i=0; i < argc; i++) { free((void *)my_argv[i]); } free(my_argv); } if (PRINT_STDOUT_BIT2()) { fprintf(stdlog, "%s/%s:%d (%u)\n", __FILE__, __FUNCTION__, __LINE__, counter++); } { int flags = had_cr ? PRINT_ADD_CR : 0; const char *buf = get_buf(); fd_printf_crlf(socket_fd, flags, "%s", buf); clear_buf(); } return 0; }
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 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; }