summaryrefslogtreecommitdiff
path: root/networking/httpd.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-08-21 10:26:55 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-08-21 10:26:55 +0000
commite58e8d944417f5255352c6594784017ff7c6fa27 (patch)
treea348127b1da388cc6665b08a8586c1794b283601 /networking/httpd.c
parent45946f8b513d9c292613ac08c3ddf4a89b915752 (diff)
downloadbusybox-w32-e58e8d944417f5255352c6594784017ff7c6fa27.tar.gz
busybox-w32-e58e8d944417f5255352c6594784017ff7c6fa27.tar.bz2
busybox-w32-e58e8d944417f5255352c6594784017ff7c6fa27.zip
httpd: add optional support for error pages
(by Pierre Metras <genepi@sympatico.ca>)
Diffstat (limited to 'networking/httpd.c')
-rw-r--r--networking/httpd.c238
1 files changed, 153 insertions, 85 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index 070e2a915..7e60fc252 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -42,6 +42,7 @@
42 * A:10.0.0.0/255.255.255.128 # Allow any address that previous set 42 * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
43 * A:127.0.0.1 # Allow local loopback connections 43 * A:127.0.0.1 # Allow local loopback connections
44 * D:* # Deny from other IP connections 44 * D:* # Deny from other IP connections
45 * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page
45 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ 46 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
46 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ 47 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
47 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ 48 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
@@ -84,6 +85,10 @@
84 * subdir http request, any merge is discarded when the process exits. As a 85 * subdir http request, any merge is discarded when the process exits. As a
85 * result, the subdir settings only have a lifetime of a single request. 86 * result, the subdir settings only have a lifetime of a single request.
86 * 87 *
88 * Custom error pages can contain an absolute path or be relative to
89 * 'home_httpd'. Error pages are to be static files (no CGI or script). Error
90 * page can only be defined in the root configuration file and are not taken
91 * into account in local (directories) config files.
87 * 92 *
88 * If -c is not set, an attempt will be made to open the default 93 * If -c is not set, an attempt will be made to open the default
89 * root configuration file. If -c is set and the file is not found, the 94 * root configuration file. If -c is set and the file is not found, the
@@ -131,6 +136,84 @@ typedef struct Htaccess_IP {
131 int allow_deny; 136 int allow_deny;
132} Htaccess_IP; 137} Htaccess_IP;
133 138
139enum {
140 HTTP_OK = 200,
141 HTTP_MOVED_TEMPORARILY = 302,
142 HTTP_BAD_REQUEST = 400, /* malformed syntax */
143 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
144 HTTP_NOT_FOUND = 404,
145 HTTP_FORBIDDEN = 403,
146 HTTP_REQUEST_TIMEOUT = 408,
147 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
148 HTTP_INTERNAL_SERVER_ERROR = 500,
149 HTTP_CONTINUE = 100,
150#if 0 /* future use */
151 HTTP_SWITCHING_PROTOCOLS = 101,
152 HTTP_CREATED = 201,
153 HTTP_ACCEPTED = 202,
154 HTTP_NON_AUTHORITATIVE_INFO = 203,
155 HTTP_NO_CONTENT = 204,
156 HTTP_MULTIPLE_CHOICES = 300,
157 HTTP_MOVED_PERMANENTLY = 301,
158 HTTP_NOT_MODIFIED = 304,
159 HTTP_PAYMENT_REQUIRED = 402,
160 HTTP_BAD_GATEWAY = 502,
161 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
162 HTTP_RESPONSE_SETSIZE = 0xffffffff
163#endif
164};
165
166static const uint16_t http_response_type[] = {
167 HTTP_OK,
168 HTTP_MOVED_TEMPORARILY,
169 HTTP_REQUEST_TIMEOUT,
170 HTTP_NOT_IMPLEMENTED,
171#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
172 HTTP_UNAUTHORIZED,
173#endif
174 HTTP_NOT_FOUND,
175 HTTP_BAD_REQUEST,
176 HTTP_FORBIDDEN,
177 HTTP_INTERNAL_SERVER_ERROR,
178#if 0 /* not implemented */
179 HTTP_CREATED,
180 HTTP_ACCEPTED,
181 HTTP_NO_CONTENT,
182 HTTP_MULTIPLE_CHOICES,
183 HTTP_MOVED_PERMANENTLY,
184 HTTP_NOT_MODIFIED,
185 HTTP_BAD_GATEWAY,
186 HTTP_SERVICE_UNAVAILABLE,
187#endif
188};
189
190static const struct {
191 const char *name;
192 const char *info;
193} http_response[ARRAY_SIZE(http_response_type)] = {
194 { "OK", NULL },
195 { "Found", "Directories must end with a slash" }, /* ?? */
196 { "Request Timeout", "No request appeared within 60 seconds" },
197 { "Not Implemented", "The requested method is not recognized" },
198#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
199 { "Unauthorized", "" },
200#endif
201 { "Not Found", "The requested URL was not found" },
202 { "Bad Request", "Unsupported method" },
203 { "Forbidden", "" },
204 { "Internal Server Error", "Internal Server Error" },
205#if 0 /* not implemented */
206 { "Created" },
207 { "Accepted" },
208 { "No Content" },
209 { "Multiple Choices" },
210 { "Moved Permanently" },
211 { "Not Modified" },
212 { "Bad Gateway", "" },
213 { "Service Unavailable", "" },
214#endif
215};
216
134struct globals { 217struct globals {
135 int verbose; /* must be int (used by getopt32) */ 218 int verbose; /* must be int (used by getopt32) */
136 smallint flg_deny_all; 219 smallint flg_deny_all;
@@ -167,6 +250,9 @@ struct globals {
167#define hdr_buf bb_common_bufsiz1 250#define hdr_buf bb_common_bufsiz1
168 char *hdr_ptr; 251 char *hdr_ptr;
169 int hdr_cnt; 252 int hdr_cnt;
253#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
254 const char *http_error_page[ARRAY_SIZE(http_response_type)];
255#endif
170}; 256};
171#define G (*ptr_to_globals) 257#define G (*ptr_to_globals)
172#define verbose (G.verbose ) 258#define verbose (G.verbose )
@@ -192,6 +278,7 @@ struct globals {
192#define iobuf (G.iobuf ) 278#define iobuf (G.iobuf )
193#define hdr_ptr (G.hdr_ptr ) 279#define hdr_ptr (G.hdr_ptr )
194#define hdr_cnt (G.hdr_cnt ) 280#define hdr_cnt (G.hdr_cnt )
281#define http_error_page (G.http_error_page )
195#define INIT_G() do { \ 282#define INIT_G() do { \
196 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ 283 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
197 USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ 284 USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
@@ -200,70 +287,13 @@ struct globals {
200} while (0) 287} while (0)
201 288
202 289
203typedef enum {
204 HTTP_OK = 200,
205 HTTP_MOVED_TEMPORARILY = 302,
206 HTTP_BAD_REQUEST = 400, /* malformed syntax */
207 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
208 HTTP_NOT_FOUND = 404,
209 HTTP_FORBIDDEN = 403,
210 HTTP_REQUEST_TIMEOUT = 408,
211 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
212 HTTP_INTERNAL_SERVER_ERROR = 500,
213#if 0 /* future use */
214 HTTP_CONTINUE = 100,
215 HTTP_SWITCHING_PROTOCOLS = 101,
216 HTTP_CREATED = 201,
217 HTTP_ACCEPTED = 202,
218 HTTP_NON_AUTHORITATIVE_INFO = 203,
219 HTTP_NO_CONTENT = 204,
220 HTTP_MULTIPLE_CHOICES = 300,
221 HTTP_MOVED_PERMANENTLY = 301,
222 HTTP_NOT_MODIFIED = 304,
223 HTTP_PAYMENT_REQUIRED = 402,
224 HTTP_BAD_GATEWAY = 502,
225 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
226 HTTP_RESPONSE_SETSIZE = 0xffffffff
227#endif
228} HttpResponseNum;
229
230typedef struct {
231 HttpResponseNum type;
232 const char *name;
233 const char *info;
234} HttpEnumString;
235
236static const HttpEnumString httpResponseNames[] = {
237 { HTTP_OK, "OK", NULL },
238 { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
239 { HTTP_REQUEST_TIMEOUT, "Request Timeout",
240 "No request appeared within a reasonable time period." },
241 { HTTP_NOT_IMPLEMENTED, "Not Implemented",
242 "The requested method is not recognized by this server." },
243#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
244 { HTTP_UNAUTHORIZED, "Unauthorized", "" },
245#endif
246 { HTTP_NOT_FOUND, "Not Found",
247 "The requested URL was not found on this server." },
248 { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
249 { HTTP_FORBIDDEN, "Forbidden", "" },
250 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
251 "Internal Server Error" },
252#if 0 /* not implemented */
253 { HTTP_CREATED, "Created" },
254 { HTTP_ACCEPTED, "Accepted" },
255 { HTTP_NO_CONTENT, "No Content" },
256 { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
257 { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
258 { HTTP_NOT_MODIFIED, "Not Modified" },
259 { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
260 { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
261#endif
262};
263 290
264 291
265#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) 292#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
266 293
294/* Prototypes */
295static void send_file_and_exit(const char *url, int headers) ATTRIBUTE_NORETURN;
296
267static void free_llist(has_next_ptr **pptr) 297static void free_llist(has_next_ptr **pptr)
268{ 298{
269 has_next_ptr *cur = *pptr; 299 has_next_ptr *cur = *pptr;
@@ -382,11 +412,13 @@ static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
382 * .ext:mime/type # new mime type not compiled into httpd 412 * .ext:mime/type # new mime type not compiled into httpd
383 * [adAD]:from # ip address allow/deny, * for wildcard 413 * [adAD]:from # ip address allow/deny, * for wildcard
384 * /path:user:pass # username/password 414 * /path:user:pass # username/password
415 * Ennn:error.html # error page for status nnn
385 * 416 *
386 * Any previous IP rules are discarded. 417 * Any previous IP rules are discarded.
387 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules 418 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
388 * are also discarded. That is, previous settings are retained if flag is 419 * are also discarded. That is, previous settings are retained if flag is
389 * SUBDIR_PARSE. 420 * SUBDIR_PARSE.
421 * Error pages are only parsed on the main config file.
390 * 422 *
391 * path Path where to look for httpd.conf (without filename). 423 * path Path where to look for httpd.conf (without filename).
392 * flag Type of the parse request. 424 * flag Type of the parse request.
@@ -486,18 +518,6 @@ static void parse_conf(const char *path, int flag)
486 518
487 if (*p0 == 'a') 519 if (*p0 == 'a')
488 *p0 = 'A'; 520 *p0 = 'A';
489 else if (*p0 != 'D' && *p0 != 'A'
490#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
491 && *p0 != '/'
492#endif
493#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
494 && *p0 != '.'
495#endif
496#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
497 && *p0 != '*'
498#endif
499 )
500 continue;
501 if (*p0 == 'A' || *p0 == 'D') { 521 if (*p0 == 'A' || *p0 == 'D') {
502 /* storing current config IP line */ 522 /* storing current config IP line */
503 pip = xzalloc(sizeof(Htaccess_IP)); 523 pip = xzalloc(sizeof(Htaccess_IP));
@@ -527,6 +547,30 @@ static void parse_conf(const char *path, int flag)
527 } 547 }
528 continue; 548 continue;
529 } 549 }
550
551#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
552 if (flag == FIRST_PARSE && *p0 == 'E') {
553 int i;
554 /* error status code */
555 int status = atoi(++p0);
556 /* c already points at the character following ':' in parse loop */
557 // c = strchr(p0, ':'); c++;
558 if (status < HTTP_CONTINUE) {
559 bb_error_msg("config error '%s' in '%s'", buf, cf);
560 continue;
561 }
562
563 /* then error page; find matching status */
564 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
565 if (http_response_type[i] == status) {
566 http_error_page[i] = concat_path_file((*c == '/') ? NULL : home_httpd, c);
567 break;
568 }
569 }
570 continue;
571 }
572#endif
573
530#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 574#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
531 if (*p0 == '/') { 575 if (*p0 == '/') {
532 /* make full path from httpd root / current_path / config_line_path */ 576 /* make full path from httpd root / current_path / config_line_path */
@@ -813,24 +857,29 @@ static void log_and_exit(void)
813 * IE will puke big-time if the headers are not sent in one packet and the 857 * IE will puke big-time if the headers are not sent in one packet and the
814 * second packet is delayed for any reason. 858 * second packet is delayed for any reason.
815 * responseNum - the result code to send. 859 * responseNum - the result code to send.
816 * Return result of write().
817 */ 860 */
818static void send_headers(HttpResponseNum responseNum) 861static void send_headers(int responseNum)
819{ 862{
820 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT"; 863 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
821 864
822 const char *responseString = ""; 865 const char *responseString = "";
823 const char *infoString = NULL; 866 const char *infoString = NULL;
824 const char *mime_type; 867 const char *mime_type;
868#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
869 const char *error_page = 0;
870#endif
825 unsigned i; 871 unsigned i;
826 time_t timer = time(0); 872 time_t timer = time(0);
827 char tmp_str[80]; 873 char tmp_str[80];
828 int len; 874 int len;
829 875
830 for (i = 0; i < ARRAY_SIZE(httpResponseNames); i++) { 876 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
831 if (httpResponseNames[i].type == responseNum) { 877 if (http_response_type[i] == responseNum) {
832 responseString = httpResponseNames[i].name; 878 responseString = http_response[i].name;
833 infoString = httpResponseNames[i].info; 879 infoString = http_response[i].info;
880#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
881 error_page = http_error_page[i];
882#endif
834 break; 883 break;
835 } 884 }
836 } 885 }
@@ -862,6 +911,20 @@ static void send_headers(HttpResponseNum responseNum)
862 (g_query ? g_query : "")); 911 (g_query ? g_query : ""));
863 } 912 }
864 913
914#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
915 if (error_page && !access(error_page, R_OK)) {
916 strcat(iobuf, "\r\n");
917 len += 2;
918
919 if (DEBUG)
920 fprintf(stderr, "headers: '%s'\n", iobuf);
921 full_write(1, iobuf, len);
922 if (DEBUG)
923 fprintf(stderr, "writing error page: '%s'\n", error_page);
924 return send_file_and_exit(error_page, FALSE);
925 }
926#endif
927
865 if (ContentLength != -1) { /* file */ 928 if (ContentLength != -1) { /* file */
866 strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); 929 strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
867 len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", 930 len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
@@ -885,8 +948,8 @@ static void send_headers(HttpResponseNum responseNum)
885 } 948 }
886} 949}
887 950
888static void send_headers_and_exit(HttpResponseNum responseNum) ATTRIBUTE_NORETURN; 951static void send_headers_and_exit(int responseNum) ATTRIBUTE_NORETURN;
889static void send_headers_and_exit(HttpResponseNum responseNum) 952static void send_headers_and_exit(int responseNum)
890{ 953{
891 send_headers(responseNum); 954 send_headers(responseNum);
892 log_and_exit(); 955 log_and_exit();
@@ -1279,9 +1342,12 @@ static void send_cgi_and_exit(
1279 1342
1280/* 1343/*
1281 * Send a file response to a HTTP request, and exit 1344 * Send a file response to a HTTP request, and exit
1345 *
1346 * Parameters:
1347 * const char *url The requested URL (with leading /).
1348 * headers Don't send headers before if FALSE.
1282 */ 1349 */
1283static void send_file_and_exit(const char *url) ATTRIBUTE_NORETURN; 1350static void send_file_and_exit(const char *url, int headers)
1284static void send_file_and_exit(const char *url)
1285{ 1351{
1286 static const char *const suffixTable[] = { 1352 static const char *const suffixTable[] = {
1287 /* Warning: shorter equivalent suffix in one line must be first */ 1353 /* Warning: shorter equivalent suffix in one line must be first */
@@ -1350,10 +1416,12 @@ static void send_file_and_exit(const char *url)
1350 if (f < 0) { 1416 if (f < 0) {
1351 if (DEBUG) 1417 if (DEBUG)
1352 bb_perror_msg("cannot open '%s'", url); 1418 bb_perror_msg("cannot open '%s'", url);
1353 send_headers_and_exit(HTTP_NOT_FOUND); 1419 if (headers)
1420 send_headers_and_exit(HTTP_NOT_FOUND);
1354 } 1421 }
1355 1422
1356 send_headers(HTTP_OK); 1423 if (headers)
1424 send_headers(HTTP_OK);
1357 1425
1358 /* If you want to know about EPIPE below 1426 /* If you want to know about EPIPE below
1359 * (happens if you abort downloads from local httpd): */ 1427 * (happens if you abort downloads from local httpd): */
@@ -1789,7 +1857,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
1789 * } 1857 * }
1790 */ 1858 */
1791 1859
1792 send_file_and_exit(tptr); 1860 send_file_and_exit(tptr, TRUE);
1793} 1861}
1794 1862
1795/* 1863/*