static void pg_show(struct req *req, const char *fullpath) { char *manpath; const char *file; if ((file = strchr(fullpath, '/')) == NULL) { pg_error_badrequest( "You did not specify a page to show."); return; } manpath = mandoc_strndup(fullpath, file - fullpath); file++; if ( ! validate_manpath(req, manpath)) { pg_error_badrequest( "You specified an invalid manpath."); free(manpath); return; } /* * Begin by chdir()ing into the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ if (chdir(manpath) == -1) { fprintf(stderr, "chdir %s: %s\n", manpath, strerror(errno)); pg_error_internal(); free(manpath); return; } if (strcmp(manpath, "mandoc")) { free(req->q.manpath); req->q.manpath = manpath; } else free(manpath); if ( ! validate_filename(file)) { pg_error_badrequest( "You specified an invalid manual file."); return; } resp_begin_html(200, NULL); resp_searchform(req); resp_show(req, file); resp_end_html(); }
int main(void) { struct req req; struct itimerval itimer; const char *path; const char *querystring; int i; /* Poor man's ReDoS mitigation. */ itimer.it_value.tv_sec = 2; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 2; itimer.it_interval.tv_usec = 0; if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) { warn("setitimer"); pg_error_internal(); return EXIT_FAILURE; } /* * First we change directory into the MAN_DIR so that * subsequent scanning for manpath directories is rooted * relative to the same position. */ if (chdir(MAN_DIR) == -1) { warn("MAN_DIR: %s", MAN_DIR); pg_error_internal(); return EXIT_FAILURE; } memset(&req, 0, sizeof(struct req)); req.q.equal = 1; parse_manpath_conf(&req); /* Parse the path info and the query string. */ if ((path = getenv("PATH_INFO")) == NULL) path = ""; else if (*path == '/') path++; if (*path != '\0') { parse_path_info(&req, path); if (req.q.manpath == NULL || access(path, F_OK) == -1) path = ""; } else if ((querystring = getenv("QUERY_STRING")) != NULL) parse_query_string(&req, querystring); /* Validate parsed data and add defaults. */ if (req.q.manpath == NULL) req.q.manpath = mandoc_strdup(req.p[0]); else if ( ! validate_manpath(&req, req.q.manpath)) { pg_error_badrequest( "You specified an invalid manpath."); return EXIT_FAILURE; } if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) { pg_error_badrequest( "You specified an invalid architecture."); return EXIT_FAILURE; } /* Dispatch to the three different pages. */ if ('\0' != *path) pg_show(&req, path); else if (NULL != req.q.query) pg_search(&req); else pg_index(&req); free(req.q.manpath); free(req.q.arch); free(req.q.sec); free(req.q.query); for (i = 0; i < (int)req.psz; i++) free(req.p[i]); free(req.p); return EXIT_SUCCESS; }
int main(void) { struct req req; struct itimerval itimer; const char *path; const char *querystring; int i; /* Poor man's ReDoS mitigation. */ itimer.it_value.tv_sec = 2; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 2; itimer.it_interval.tv_usec = 0; if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) { fprintf(stderr, "setitimer: %s\n", strerror(errno)); pg_error_internal(); return(EXIT_FAILURE); } /* Scan our run-time environment. */ if (NULL == (scriptname = getenv("SCRIPT_NAME"))) scriptname = ""; if ( ! validate_urifrag(scriptname)) { fprintf(stderr, "unsafe SCRIPT_NAME \"%s\"\n", scriptname); pg_error_internal(); return(EXIT_FAILURE); } /* * First we change directory into the MAN_DIR so that * subsequent scanning for manpath directories is rooted * relative to the same position. */ if (-1 == chdir(MAN_DIR)) { fprintf(stderr, "MAN_DIR: %s: %s\n", MAN_DIR, strerror(errno)); pg_error_internal(); return(EXIT_FAILURE); } memset(&req, 0, sizeof(struct req)); pathgen(&req); /* Next parse out the query string. */ if (NULL != (querystring = getenv("QUERY_STRING"))) http_parse(&req, querystring); if (req.q.manpath == NULL) req.q.manpath = mandoc_strdup(req.p[0]); else if ( ! validate_manpath(&req, req.q.manpath)) { pg_error_badrequest( "You specified an invalid manpath."); return(EXIT_FAILURE); } if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) { pg_error_badrequest( "You specified an invalid architecture."); return(EXIT_FAILURE); } /* Dispatch to the three different pages. */ path = getenv("PATH_INFO"); if (NULL == path) path = ""; else if ('/' == *path) path++; if ('\0' != *path) pg_show(&req, path); else if (NULL != req.q.query) pg_search(&req); else pg_index(&req); free(req.q.manpath); free(req.q.arch); free(req.q.sec); free(req.q.query); for (i = 0; i < (int)req.psz; i++) free(req.p[i]); free(req.p); return(EXIT_SUCCESS); }
/* * If PATH_INFO is not a file name, translate it to a query. */ static void parse_path_info(struct req *req, const char *path) { char *dir[4]; int i; req->isquery = 0; req->q.equal = 1; req->q.manpath = mandoc_strdup(path); req->q.arch = NULL; /* Mandatory manual page name. */ if ((req->q.query = strrchr(req->q.manpath, '/')) == NULL) { req->q.query = req->q.manpath; req->q.manpath = NULL; } else *req->q.query++ = '\0'; /* Optional trailing section. */ if ((req->q.sec = strrchr(req->q.query, '.')) != NULL) { if(isdigit((unsigned char)req->q.sec[1])) { *req->q.sec++ = '\0'; req->q.sec = mandoc_strdup(req->q.sec); } else req->q.sec = NULL; } /* Handle the case of name[.section] only. */ if (req->q.manpath == NULL) return; req->q.query = mandoc_strdup(req->q.query); /* Split directory components. */ dir[i = 0] = req->q.manpath; while ((dir[i + 1] = strchr(dir[i], '/')) != NULL) { if (++i == 3) { pg_error_badrequest( "You specified too many directory components."); exit(EXIT_FAILURE); } *dir[i]++ = '\0'; } /* Optional manpath. */ if ((i = validate_manpath(req, req->q.manpath)) == 0) req->q.manpath = NULL; else if (dir[1] == NULL) return; /* Optional section. */ if (strncmp(dir[i], "man", 3) == 0) { free(req->q.sec); req->q.sec = mandoc_strdup(dir[i++] + 3); } if (dir[i] == NULL) { if (req->q.manpath == NULL) free(dir[0]); return; } if (dir[i + 1] != NULL) { pg_error_badrequest( "You specified an invalid directory component."); exit(EXIT_FAILURE); } /* Optional architecture. */ if (i) { req->q.arch = mandoc_strdup(dir[i]); if (req->q.manpath == NULL) free(dir[0]); } else req->q.arch = dir[0]; }