#include #include "plugin.h" #include "ini.h" dictionary cgi_bin = NULL; static int ini_handle(void *user_data, const char *section, const char *name, const char *value) { UNUSED(user_data); if (EQU(section, "CGI")) { dput(cgi_bin, name, strdup(value)); LOG("put %s for %s\n", value, name); } else { return 0; } return 1; } void init() { use_raw_body(); cgi_bin = dict(); char* cnf = config_dir(); char* file = __s("%s/cgi.ini", cnf); // read ini file if (ini_parse(file, ini_handle, NULL) < 0) { LOG("Can't load '%s'\n", file); } free(cnf); free(file); } void destroy() { if(cgi_bin) freedict(cgi_bin); } static void add_vars(list* l, char* k, char* v) { if(!v || !l || !k) return; char* data = __s("%s=%s", k, v); list_put_s(l, data); free(data); } static void write_request_body(antd_request_t* rq, int fd) { char* tmp = (char*)dvalue(rq->request, "METHOD"); if(!tmp || EQU(tmp,"GET") || EQU(tmp,"HEAD")) return; int clen = -1; dictionary header = (dictionary) dvalue(rq->request,"REQUEST_HEADER"); tmp = (char*)dvalue(header, "Content-Length"); if(tmp) clen = atoi(tmp); if(clen == -1) return; // read data and write to the fd char buf[BUFFLEN]; int readlen = clen > BUFFLEN?BUFFLEN:clen; int read = 0, stat = 1; while(readlen > 0 && stat > 0) { stat = antd_recv(rq->client, buf, readlen); if(stat > 0) { read += stat; readlen = (clen - read) > BUFFLEN?BUFFLEN:(clen-read); write(fd, buf, stat); } } } static char * get_cgi_bin(antd_request_t* rq) { char* tmp = (char*)dvalue(rq->request, "RESOURCE_PATH"); if(!tmp) return NULL; tmp = ext(tmp); if(!tmp) return NULL; char* bin = (char*)dvalue(cgi_bin,tmp); free(tmp); return bin; } static list get_env_vars(antd_request_t* rq) { char* tmp = NULL; char* sub = NULL; plugin_header_t* __plugin__ = meta(); dictionary request = (dictionary) rq->request; dictionary header = (dictionary) dvalue(rq->request,"REQUEST_HEADER"); list env_vars = list_init(); add_vars(&env_vars, "GATEWAY_INTERFACE", "CGI/1.1"); add_vars(&env_vars, "SERVER_SOFTWARE",SERVER_NAME); tmp = (char*)dvalue(request, "REQUEST_QUERY"); if(!tmp) add_vars(&env_vars, "QUERY_STRING", ""); else { sub = strchr(tmp,'?'); if(sub) { sub++; add_vars(&env_vars, "QUERY_STRING", sub); } else add_vars(&env_vars, "QUERY_STRING", ""); } tmp = (char*)dvalue(request, "METHOD"); if(tmp) add_vars(&env_vars, "REQUEST_METHOD", tmp); tmp = (char*)dvalue(header, "Content-Type"); if(tmp) add_vars(&env_vars, "CONTENT_TYPE", tmp); else add_vars(&env_vars, "CONTENT_TYPE", ""); tmp = (char*)dvalue(header, "Content-Length"); if(tmp) add_vars(&env_vars, "CONTENT_LENGTH", tmp); else add_vars(&env_vars, "CONTENT_LENGTH", ""); add_vars(&env_vars, "DOCUMENT_ROOT", __plugin__->htdocs); tmp = (char*) dvalue(request, "REQUEST_PATH"); if(tmp) add_vars(&env_vars, "PATH_INFO", tmp); else add_vars(&env_vars, "PATH_INFO", ""); tmp = (char*) dvalue(header,"REMOTE_ADDR"); add_vars(&env_vars, "REMOTE_ADDR", tmp); add_vars(&env_vars, "REMOTE_HOST", tmp); add_vars(&env_vars, "SERVER_NAME", SERVER_NAME); add_vars(&env_vars, "SERVER_PORT", (char*) dvalue(header, "SERVER_PORT")); add_vars(&env_vars, "SERVER_PROTOCOL", "HTTP/1.1"); // add remaining header to the vars association it; for_each_assoc(it, header) { tmp = __s("HTTP_%s", it->key); char *s = tmp; while (*s) { if(*s == '-') *s = '_'; else if(*s != '_') *s = toupper((char) *s); s++; } add_vars(&env_vars, tmp, (char*)it->value); free(tmp); } tmp = (char*)dvalue(request, "RESOURCE_PATH"); if(tmp) { add_vars(&env_vars, "SCRIPT_NAME", tmp); tmp = __s("%s/%s",__plugin__->htdocs, tmp); add_vars(&env_vars, "SCRIPT_FILENAME", tmp); add_vars(&env_vars, "PATH_TRANSLATED", tmp); free(tmp); } else { add_vars(&env_vars, "SCRIPT_FILENAME", ""); add_vars(&env_vars, "PATH_TRANSLATED", ""); add_vars(&env_vars, "SCRIPT_NAME", ""); } // redirect status for php add_vars(&env_vars,"REDIRECT_STATUS","200"); return env_vars; } void* handle(void* data) { antd_request_t *rq = (antd_request_t *)data; void *cl = (void *)rq->client; pid_t pid = 0, wpid; int inpipefd[2]; int outpipefd[2]; char buf[BUFFLEN]; int status; antd_task_t* task = NULL; task = antd_create_task(NULL, data, NULL); task->priority++; list env_vars = NULL; char* bin = get_cgi_bin(rq); if(!bin) { LOG("No cgi bin found\n"); unknow(cl); return task; } env_vars = get_env_vars(rq); // now exec the cgi bin item np = env_vars; int size = list_size(env_vars); char** envs = (char**) malloc((size+1)*sizeof(*envs)); envs[size] = NULL; int i = 0; while(np) { envs[i] = np->value.s; np = np->next; i++; } // PIPE pipe(inpipefd); pipe(outpipefd); pid = fork(); if (pid == 0) { // Child dup2(outpipefd[0], STDIN_FILENO); dup2(inpipefd[1], STDOUT_FILENO); dup2(inpipefd[1], STDERR_FILENO); //ask kernel to deliver SIGTERM in case the parent dies //prctl(PR_SET_PDEATHSIG, SIGTERM); char *argv[] = { bin, 0 }; execve(argv[0], &argv[0], envs); // Nothing below this line should be executed by child process. If so, // it means that the execl function wasn't successfull, so lets exit: _exit(1); } // The code below will be executed only by parent. //close unused pipe ends close(outpipefd[0]); close(inpipefd[1]); // Now, we can write to outpipefd[1] and read from inpipefd[0] : write_request_body(rq, outpipefd[1]); LOG("Wait for respond\n"); set_status(cl, 200, "OK"); wpid = 0; do { memset(buf, 0, sizeof(buf)); int r = read(inpipefd[0], buf, BUFFLEN-1); if(r > 0) { __t(cl, buf); } wpid = waitpid(pid, &status, WNOHANG); } while(wpid == 0); free(envs); list_free(&env_vars); return task; }