diff --git a/examples/http-server/main.c b/examples/http-server/main.c index 3b2a748b..b25e0b5f 100644 --- a/examples/http-server/main.c +++ b/examples/http-server/main.c @@ -20,7 +20,8 @@ static void signal_handler(int signo) { // Simply serve static files from `s_root_dir` static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { if (ev == MG_EV_HTTP_MSG) { - struct mg_http_serve_opts opts = {s_root_dir, s_ssi_pattern, NULL}; + struct mg_http_serve_opts opts = {.root_dir = s_root_dir, + .ssi_pattern = s_ssi_pattern}; mg_http_serve_dir(c, ev_data, &opts); } (void) fn_data; diff --git a/mongoose.c b/mongoose.c index fd0e218b..597d5fec 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1279,7 +1279,11 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, #endif if (is_index && fp == NULL) { #if MG_ENABLE_DIRECTORY_LISTING - listdir(c, hm, opts, t2); + if (opts->enable_dirlist && strcmp(opts->enable_dirlist, "no") == 0) { + mg_http_reply(c, 403, "", "%s", "Denied"); + } else { + listdir(c, hm, opts, t2); + } #else mg_http_reply(c, 403, "", "%s", "Directory listing not supported"); #endif diff --git a/mongoose.h b/mongoose.h index 2bf4b3f7..aeb57d45 100644 --- a/mongoose.h +++ b/mongoose.h @@ -784,9 +784,10 @@ struct mg_http_message { // Parameter for mg_http_serve_dir() struct mg_http_serve_opts { - const char *root_dir; // Web root directory, must be non-NULL - const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml - const char *extra_headers; // Extra HTTP headers to add in responses + const char *root_dir; // Web root directory, must be non-NULL + const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml + const char *extra_headers; // Extra HTTP headers to add in responses + const char *enable_dirlist; // Enable dir listing. Set to "no" to disable }; // Parameter for mg_http_next_multipart diff --git a/src/http.c b/src/http.c index cb1d3aea..b899da32 100644 --- a/src/http.c +++ b/src/http.c @@ -868,7 +868,11 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, #endif if (is_index && fp == NULL) { #if MG_ENABLE_DIRECTORY_LISTING - listdir(c, hm, opts, t2); + if (opts->enable_dirlist && strcmp(opts->enable_dirlist, "no") == 0) { + mg_http_reply(c, 403, "", "%s", "Denied"); + } else { + listdir(c, hm, opts, t2); + } #else mg_http_reply(c, 403, "", "%s", "Directory listing not supported"); #endif diff --git a/src/http.h b/src/http.h index f6272afb..77c6f9f2 100644 --- a/src/http.h +++ b/src/http.h @@ -23,9 +23,10 @@ struct mg_http_message { // Parameter for mg_http_serve_dir() struct mg_http_serve_opts { - const char *root_dir; // Web root directory, must be non-NULL - const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml - const char *extra_headers; // Extra HTTP headers to add in responses + const char *root_dir; // Web root directory, must be non-NULL + const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml + const char *extra_headers; // Extra HTTP headers to add in responses + const char *enable_dirlist; // Enable dir listing. Set to "no" to disable }; // Parameter for mg_http_next_multipart diff --git a/test/data/secret/secret.txt b/test/data/secret/secret.txt new file mode 100644 index 00000000..d97c5ead --- /dev/null +++ b/test/data/secret/secret.txt @@ -0,0 +1 @@ +secret diff --git a/test/unit_test.c b/test/unit_test.c index fb408178..d81b3b6e 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -347,7 +347,9 @@ static void eh1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { } else if (mg_http_match_uri(hm, "/no_reason")) { mg_printf(c, "%s", "HTTP/1.0 200\r\nContent-Length: 2\r\n\r\nok"); } else if (mg_http_match_uri(hm, "/badroot")) { - struct mg_http_serve_opts sopts = {"/BAAADDD!", NULL, NULL}; + struct mg_http_serve_opts sopts; + memset(&sopts, 0, sizeof(sopts)); + sopts.root_dir = "/BAAADDD!"; mg_http_serve_dir(c, hm, &sopts); } else if (mg_http_match_uri(hm, "/creds")) { char user[100], pass[100]; @@ -356,10 +358,18 @@ static void eh1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { } else if (mg_http_match_uri(hm, "/upload")) { mg_http_upload(c, hm, "."); } else if (mg_http_match_uri(hm, "/test/")) { - struct mg_http_serve_opts sopts = {".", NULL, "A: B\r\nC: D\r\n"}; + struct mg_http_serve_opts sopts; + memset(&sopts, 0, sizeof(sopts)); + sopts.root_dir = "."; + sopts.extra_headers = "A: B\r\nC: D\r\n"; mg_http_serve_dir(c, hm, &sopts); } else { - struct mg_http_serve_opts sopts = {"./test/data", "#.shtml", "C: D\r\n"}; + struct mg_http_serve_opts sopts; + memset(&sopts, 0, sizeof(sopts)); + sopts.root_dir = "./test/data"; + sopts.ssi_pattern = "#.shtml"; + sopts.extra_headers = "C: D\r\n"; + sopts.enable_dirlist = "no"; mg_http_serve_dir(c, hm, &sopts); } } else if (ev == MG_EV_WS_MSG) { @@ -555,6 +565,10 @@ static void test_http_server(void) { ASSERT(mg_strstr(mg_str(buf), mg_str(">Index of /test/<")) != NULL); ASSERT(mg_strstr(mg_str(buf), mg_str(">fuzz.c<")) != NULL); + // Directory listing denied + ASSERT(fetch(&mgr, buf, url, "GET /secret/ HTTP/1.0\n\n") == 403); + ASSERT(cmpbody(buf, "Denied\n") == 0); + { // Credentials struct mg_http_message hm; @@ -885,7 +899,9 @@ static void test_http_parse(void) { static void ehr(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; - struct mg_http_serve_opts opts = {"./test/data", NULL, NULL}; + struct mg_http_serve_opts opts; + memset(&opts, 0, sizeof(opts)); + opts.root_dir = "./test/data"; mg_http_serve_dir(c, hm, &opts); } (void) fn_data;