/* * started writing (limited) CGI support for webfsd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "httpd.h" /* ---------------------------------------------------------------------- */ extern char **environ; static char *env_wlist[] = { "PATH", "HOME", NULL }; static void env_add(struct strlist **list, char *name, char *value) { char *line; line = malloc(strlen(name) + strlen(value) + 2); sprintf(line,"%s=%s",name,value); if (debug) fprintf(stderr,"cgi: env %s\n",line); list_add(list,line,1); } static char** env_convert(struct strlist *list) { struct strlist *elem; char **env; int i; for (i = 2, elem = list; NULL != elem; elem = elem->next) i++; env = malloc(sizeof(char*)*i); for (i = 0, elem = list; NULL != elem; elem = elem->next) env[i++] = elem->line; env[i++] = NULL; return env; } static void env_copy(struct strlist **list) { int i,j,l; for (i = 0; environ[i] != NULL; i++) { for (j = 0; env_wlist[j] != NULL; j++) { l = strlen(env_wlist[j]); if (0 == strncmp(environ[i],env_wlist[j],l) && environ[i][l] == '=') { env_add(list,env_wlist[j],environ[i]+l+1); break; } } } } /* ---------------------------------------------------------------------- */ void cgi_request(struct REQUEST *req) { struct sockaddr_storage addr; struct strlist *env = NULL, *item; char host[65],serv[9]; char filename[1024], *h, *argv[2], envname[128]; int pid,p[2],i,length; if (debug) fprintf(stderr,"%03d: is cgi request\n",req->fd); if (-1 == pipe(p)) { mkerror(req,500,0); return; } pid = fork(); switch (pid) { case -1: /* error */ if (debug) perror("fork"); mkerror(req,500,0); return; case 0: break; default: /* parent - webfsd */ close(p[1]); req->cgipid = pid; req->cgipipe = p[0]; req->state = STATE_CGI_HEADER; close_on_exec(req->cgipipe); fcntl(req->cgipipe,F_SETFL,O_NONBLOCK); return; } /* -------- below is the child process (cgi) code -------- */ /* lookup local socket (before it gets closed) */ length = sizeof(addr); getsockname(req->fd,(struct sockaddr*)&addr,&length); getnameinfo((struct sockaddr*)&addr,length,host,64,serv,8, NI_NUMERICHOST | NI_NUMERICSERV); /* setup file descriptors */ dup2(p[1],1); /* pipe -> stdout */ if (have_tty) { int devnull = open("/dev/null",O_RDWR); dup2(devnull,0); /* stdin */ dup2(devnull,2); /* stderr */ close(devnull); } else { /* nothing -- already attached to /dev/null */ } close_on_exec(p[0]); close_on_exec(p[1]); /* setup environment */ env_copy(&env); env_add(&env,"DOCUMENT_ROOT",doc_root); env_add(&env,"GATEWAY_INTERFACE","CGI/1.1"); env_add(&env,"QUERY_STRING",req->query); env_add(&env,"REQUEST_URI",req->uri); env_add(&env,"REMOTE_ADDR",req->peerhost); env_add(&env,"REMOTE_PORT",req->peerserv); env_add(&env,"REQUEST_METHOD",req->type); env_add(&env,"SERVER_ADMIN","root@localhost"); env_add(&env,"SERVER_NAME",server_host); env_add(&env,"SERVER_PROTOCOL","HTTP/1.1"); env_add(&env,"SERVER_SOFTWARE",server_name); env_add(&env,"SERVER_ADDR",host); env_add(&env,"SERVER_PORT",serv); for (item = req->header; NULL != item; item = item->next) { strcpy(envname,"HTTP_"); if (1 != sscanf(item->line,"%120[-A-Za-z]: %n",envname+5,&length)) continue; for (i = 0; envname[i]; i++) { if (isalpha(envname[i])) envname[i] = toupper(envname[i]); if ('-' == envname[i]) envname[i] = '_'; } env_add(&env,envname,item->line+length); } h = req->path + strlen(cgipath); h = strchr(h,'/'); if (h) { env_add(&env,"PATH_INFO",h); *h = 0; } else { env_add(&env,"PATH_INFO",""); } env_add(&env,"SCRIPT_NAME",req->path); snprintf(filename,sizeof(filename)-1,"%s%s",doc_root,req->path); env_add(&env,"SCRIPT_FILENAME",filename); /* start cgi app */ argv[0] = filename; argv[1] = NULL; execve(filename,argv,env_convert(env)); /* exec failed ... */ printf("Content-Type: text/plain\n" "\n" "execve %s: %s\n", filename,strerror(errno)); exit(1); } /* ---------------------------------------------------------------------- */ void cgi_read_header(struct REQUEST *req) { struct strlist *list = NULL; char *h,*next,*status = NULL; int rc; restart: rc = read(req->cgipipe, req->cgibuf+req->cgilen, MAX_HEADER-req->cgilen); switch (rc) { case -1: if (errno == EAGAIN) return; if (errno == EINTR) goto restart; /* fall through */ case 0: mkerror(req,500,0); return; default: req->cgilen += rc; req->cgibuf[req->cgilen] = 0; } /* header complete ?? */ if (NULL != (h = strstr(req->cgibuf,"\r\n\r\n")) || NULL != (h = strstr(req->cgibuf,"\n\n"))) { /* parse cgi header */ for (h = req->cgibuf;; h = next) { next = strstr(h,"\n"); next[0] = 0; if (next[-1] == '\r') next[-1] = 0; next++; if (0 == strlen(h)) break; if (debug) fprintf(stderr,"%03d: cgi: hdr %s\n",req->fd,h); if (0 == strncasecmp(h,"Status: ",8)) { status = h+8; if (debug) fprintf(stderr,"%03d: cgi: status %s\n",req->fd,status); continue; } if (0 == strncasecmp(h,"Server:",7) || 0 == strncasecmp(h,"Connection:",11) || 0 == strncasecmp(h,"Accept-Ranges:",14) || 0 == strncasecmp(h,"Date:",5)) /* webfsd adds them -- filter out */ continue; list_add(&list,h,0); } mkcgi(req, status ? status : "200 OK", list); list_free(&list); req->cgipos = next - req->cgibuf; if (debug) fprintf(stderr,"%03d: cgi: pos=%d len=%d\n",req->fd, req->cgipos, req->cgilen); return; } if (req->cgilen == MAX_HEADER) { mkerror(req,400,0); return; } return; }