diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2003-02-09 06:51:14 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2003-02-09 06:51:14 +0000 |
commit | 06e9565b6c365668dafeef1fdc0e60c9a1154623 (patch) | |
tree | c75619aad6d3ca6f4c32cd8d1dffbe4f6c53367b /networking/httpd.c | |
parent | 877d418b39421361800afe4e1d4ca69b80edc121 (diff) | |
download | busybox-w32-06e9565b6c365668dafeef1fdc0e60c9a1154623.tar.gz busybox-w32-06e9565b6c365668dafeef1fdc0e60c9a1154623.tar.bz2 busybox-w32-06e9565b6c365668dafeef1fdc0e60c9a1154623.zip |
New applet, inetd, make httpd features more configurable, update authors, last_patch_80
from Vladimir N. Oleynik
Diffstat (limited to 'networking/httpd.c')
-rw-r--r-- | networking/httpd.c | 1948 |
1 files changed, 1208 insertions, 740 deletions
diff --git a/networking/httpd.c b/networking/httpd.c index bceb89ba3..e61518c20 100644 --- a/networking/httpd.c +++ b/networking/httpd.c | |||
@@ -1,8 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * httpd implementation for busybox | 2 | * httpd implementation for busybox |
3 | * | 3 | * |
4 | * Copyright (C) 2002 Glenn Engel <glenne@engel.org> | 4 | * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org> |
5 | * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> | ||
5 | * | 6 | * |
7 | * simplify patch stolen from libbb without using strdup | ||
6 | * | 8 | * |
7 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
@@ -20,100 +22,252 @@ | |||
20 | * | 22 | * |
21 | ***************************************************************************** | 23 | ***************************************************************************** |
22 | * | 24 | * |
23 | * Typical usage: | 25 | * Typical usage: |
24 | * cd /var/www | 26 | * for non root user |
25 | * httpd | 27 | * httpd -p 8080 -h $HOME/public_html |
26 | * This is equivalent to | 28 | * or for daemon start from rc script with uid=0: |
27 | * cd /var/www | 29 | * httpd -u www |
28 | * httpd -p 80 -c /etc/httpd.conf -r "Web Server Authentication" | 30 | * This is equivalent if www user have uid=80 to |
31 | * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication" | ||
32 | * | ||
29 | * | 33 | * |
30 | * When a url contains "cgi-bin" it is assumed to be a cgi script. The | 34 | * When a url contains "cgi-bin" it is assumed to be a cgi script. The |
31 | * server changes directory to the location of the script and executes it | 35 | * server changes directory to the location of the script and executes it |
32 | * after setting QUERY_STRING and other environment variables. If url args | 36 | * after setting QUERY_STRING and other environment variables. If url args |
33 | * are included in the url or as a post, the args are placed into decoded | 37 | * are included in the url or as a post, the args are placed into decoded |
34 | * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set | 38 | * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set |
35 | * the $CGI_foo environment variable to "Hello World". | 39 | * the $CGI_foo environment variable to "Hello World" while |
40 | * CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV enabled. | ||
36 | * | 41 | * |
37 | * The server can also be invoked as a url arg decoder and html text encoder | 42 | * The server can also be invoked as a url arg decoder and html text encoder |
38 | * as follows: | 43 | * as follows: |
39 | * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" | 44 | * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" |
40 | * bar=`httpd -e "<Hello World>"` # encode as "<Hello World>" | 45 | * bar=`httpd -e "<Hello World>"` # encode as "%3CHello%20World%3E" |
41 | * | 46 | * |
42 | * httpd.conf has the following format: | 47 | * httpd.conf has the following format: |
43 | |||
44 | ip:10.10. # Allow any address that begins with 10.10. | ||
45 | ip:172.20. # Allow 172.20.x.x | ||
46 | ip:127.0.0.1 # Allow local loopback connections | ||
47 | /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin | ||
48 | /:admin:setup # Require user admin, pwd setup on urls starting with / | ||
49 | 48 | ||
50 | * | 49 | A:172.20. # Allow any address that begins with 172.20 |
51 | * To open up the server: | 50 | A:10.10. # Allow any address that begins with 10.10. |
52 | * ip:* # Allow any IP address | 51 | A:10.10 # Allow any address that previous set and 10.100-109.X.X |
53 | * /:* # no password required for urls starting with / (all) | 52 | A:127.0.0.1 # Allow local loopback connections |
54 | * | 53 | D:* # Deny from other IP connections |
55 | * Processing of the file stops on the first sucessful match. If the file | 54 | /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ |
56 | * is not found, the server is assumed to be wide open. | 55 | /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ |
57 | * | 56 | /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ |
58 | ***************************************************************************** | 57 | .au:audio/basic # additional mime type for audio.au files |
59 | * | 58 | |
60 | * Desired enhancements: | 59 | A shortes path and D:from[^*] automaticaly sorting to top. |
61 | * cache httpd.conf | 60 | All longest path can`t reset user:password if shorted protect setted. |
62 | * support tinylogin | 61 | |
63 | * | 62 | A/D may be as a/d or allow/deny - first char case unsensitive parsed only. |
63 | |||
64 | Each subdir can have config file. | ||
65 | For protect as user:pass current subdir and subpathes set from subdir config: | ||
66 | /:user:pass | ||
67 | if not, other subpathes for give effect must have path from httpd root | ||
68 | /current_subdir_path_from_httpd_root/subpath:user:pass | ||
69 | |||
70 | The Deny/Allow IP logic: | ||
71 | |||
72 | 1. Allow all: | ||
73 | The config don`t set D: lines | ||
74 | |||
75 | 2. Allow from setted only: | ||
76 | see the begin format example | ||
77 | |||
78 | 3. Set deny, allow from other: | ||
79 | D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255 | ||
80 | D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255 | ||
81 | A:* # allow from other, this line not strongly require | ||
82 | |||
83 | A global and subdirs config merging logic: | ||
84 | allow rules reducing, deny lines strongled. | ||
85 | The algorithm combinations: | ||
86 | |||
87 | 4. If current config have | ||
88 | A:from | ||
89 | D:* | ||
90 | subdir config A: lines skiping, D:from - moving top | ||
91 | |||
92 | 5. If current config have | ||
93 | D:from | ||
94 | A:* | ||
95 | result config: | ||
96 | D:from current | ||
97 | D:from subdir | ||
98 | A:from subdir | ||
99 | and seting D:* if subdir config have this | ||
100 | |||
101 | If -c don`t setted, used httpd root config, else httpd root config skiped. | ||
102 | Exited with fault if can`t open start config. | ||
103 | For set wide open server, use -c /dev/null ;=) | ||
64 | */ | 104 | */ |
105 | |||
65 | #include <stdio.h> | 106 | #include <stdio.h> |
66 | #include <ctype.h> /* for isspace */ | 107 | #include <ctype.h> /* for isspace */ |
67 | #include <stdarg.h> /* for varargs */ | 108 | #include <string.h> |
68 | #include <string.h> /* for strerror */ | ||
69 | #include <stdlib.h> /* for malloc */ | 109 | #include <stdlib.h> /* for malloc */ |
70 | #include <time.h> | 110 | #include <time.h> |
71 | #include <errno.h> | ||
72 | #include <unistd.h> /* for close */ | 111 | #include <unistd.h> /* for close */ |
73 | #include <signal.h> | 112 | #include <signal.h> |
74 | #include <sys/types.h> | 113 | #include <sys/types.h> |
75 | #include <sys/socket.h> /* for connect and socket*/ | 114 | #include <sys/socket.h> /* for connect and socket*/ |
76 | #include <netinet/in.h> /* for sockaddr_in */ | 115 | #include <netinet/in.h> /* for sockaddr_in */ |
77 | #include <sys/types.h> | 116 | #include <sys/time.h> |
78 | #include <sys/stat.h> | 117 | #include <sys/stat.h> |
79 | #include <sys/wait.h> | 118 | #include <sys/wait.h> |
80 | #include <fcntl.h> | 119 | #include <fcntl.h> /* for open modes */ |
120 | #include "busybox.h" | ||
121 | |||
81 | 122 | ||
82 | static const char httpdVersion[] = "busybox httpd/1.13 3-Jan-2003"; | 123 | static const char httpdVersion[] = "busybox httpd/1.20 31-Jan-2003"; |
124 | static const char default_patch_httpd_conf[] = "/etc"; | ||
125 | static const char httpd_conf[] = "httpd.conf"; | ||
126 | static const char home[] = "/www"; | ||
83 | 127 | ||
84 | // #define DEBUG 1 | ||
85 | #ifndef HTTPD_STANDALONE | ||
86 | #include <config.h> | ||
87 | #include <busybox.h> | ||
88 | // Note: xfuncs are not used because we want the server to keep running | 128 | // Note: xfuncs are not used because we want the server to keep running |
89 | // if something bad happens due to a malformed user request. | 129 | // if something bad happens due to a malformed user request. |
90 | // As a result, all memory allocation is checked rigorously | 130 | // As a result, all memory allocation after daemonize |
91 | #else | 131 | // is checked rigorously |
92 | /* standalone */ | 132 | |
133 | //#define DEBUG 1 | ||
134 | |||
135 | /* Configure options, disabled by default as nonstandart httpd feature */ | ||
136 | //#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV | ||
137 | //#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR | ||
138 | //#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR | ||
139 | |||
140 | /* disabled as not necessary feature */ | ||
141 | //#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
142 | //#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
143 | //#define CONFIG_FEATURE_HTTPD_SETUID | ||
144 | //#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
145 | |||
146 | /* If seted this you can use this server from internet superserver only */ | ||
147 | //#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
148 | |||
149 | /* You can use this server as standalone, require libbb.a for linking */ | ||
150 | //#define HTTPD_STANDALONE | ||
151 | |||
152 | /* Config options, disable this for do very small module */ | ||
153 | //#define CONFIG_FEATURE_HTTPD_CGI | ||
154 | //#define CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
155 | |||
156 | #ifdef HTTPD_STANDALONE | ||
157 | /* standalone, enable all features */ | ||
158 | #undef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
159 | /* unset config option for remove warning as redefined */ | ||
160 | #undef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
161 | #undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV | ||
162 | #undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR | ||
163 | #undef CONFIG_FEATURE_HTTPD_DECODE_URL_STR | ||
164 | #undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
165 | #undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
166 | #undef CONFIG_FEATURE_HTTPD_CGI | ||
167 | #undef CONFIG_FEATURE_HTTPD_SETUID | ||
168 | #undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
169 | /* enable all features now */ | ||
93 | #define CONFIG_FEATURE_HTTPD_BASIC_AUTH | 170 | #define CONFIG_FEATURE_HTTPD_BASIC_AUTH |
94 | void show_usage() | 171 | #define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV |
172 | #define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR | ||
173 | #define CONFIG_FEATURE_HTTPD_DECODE_URL_STR | ||
174 | #define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
175 | #define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
176 | #define CONFIG_FEATURE_HTTPD_CGI | ||
177 | #define CONFIG_FEATURE_HTTPD_SETUID | ||
178 | #define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
179 | |||
180 | /* require from libbb.a for linking */ | ||
181 | const char *applet_name = "httpd"; | ||
182 | |||
183 | void show_usage(void) | ||
95 | { | 184 | { |
96 | fprintf(stderr,"Usage: httpd [-p <port>] [-c configFile] [-d/-e <string>] [-r realm]\n"); | 185 | fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] " |
186 | "[-r realm] [-u user]\n", applet_name); | ||
187 | exit(1); | ||
97 | } | 188 | } |
98 | #endif | 189 | #endif |
99 | 190 | ||
100 | /* minimal global vars for busybox */ | 191 | #ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY |
101 | #ifndef ENVSIZE | 192 | #undef CONFIG_FEATURE_HTTPD_SETUID /* use inetd user.group config settings */ |
102 | #define ENVSIZE 50 | 193 | #undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP /* so is not daemon */ |
194 | /* inetd set stderr to accepted socket and we can`t true see debug messages */ | ||
195 | #undef DEBUG | ||
196 | #endif | ||
197 | |||
198 | /* CGI environ size */ | ||
199 | #ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV | ||
200 | #define ENVSIZE 50 /* set max 35 CGI_variable */ | ||
201 | #else | ||
202 | #define ENVSIZE 15 /* minimal requires */ | ||
103 | #endif | 203 | #endif |
104 | int debugHttpd; | 204 | |
105 | static char **envp; | 205 | #define MAX_POST_SIZE (64*1024) /* 64k. Its Small? May be ;) */ |
106 | static int envCount; | 206 | |
107 | static char *realm = "Web Server Authentication"; | 207 | #define MAX_MEMORY_BUFF 8192 /* IO buffer */ |
108 | static char *configFile; | 208 | |
209 | typedef struct HT_ACCESS { | ||
210 | char *after_colon; | ||
211 | struct HT_ACCESS *next; | ||
212 | char before_colon[1]; /* really bigger, must last */ | ||
213 | } Htaccess; | ||
214 | |||
215 | typedef struct | ||
216 | { | ||
217 | #ifdef CONFIG_FEATURE_HTTPD_CGI | ||
218 | char *envp[ENVSIZE+1]; | ||
219 | int envCount; | ||
220 | #endif | ||
221 | char buf[MAX_MEMORY_BUFF]; | ||
222 | |||
223 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
224 | const char *realm; | ||
225 | #endif | ||
226 | const char *configFile; | ||
227 | |||
228 | char rmt_ip[16]; /* for set env REMOTE_ADDR */ | ||
229 | unsigned port; /* server initial port and for | ||
230 | set env REMOTE_PORT */ | ||
231 | |||
232 | const char *found_mime_type; | ||
233 | off_t ContentLength; /* -1 - unknown */ | ||
234 | time_t last_mod; | ||
235 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
236 | int accepted_socket; | ||
237 | #define a_c_r config->accepted_socket | ||
238 | #define a_c_w config->accepted_socket | ||
239 | int debugHttpd; /* if seted, don`t stay daemon */ | ||
240 | #else | ||
241 | #define a_c_r 0 | ||
242 | #define a_c_w 1 | ||
243 | #endif | ||
244 | Htaccess *Httpd_conf_parsed; | ||
245 | } HttpdConfig; | ||
246 | |||
247 | static HttpdConfig *config; | ||
248 | |||
249 | static const char request_GET[] = "GET"; /* size algorithic optimize */ | ||
109 | 250 | ||
110 | static const char* const suffixTable [] = { | 251 | static const char* const suffixTable [] = { |
252 | /* Warning: shorted equalent suffix in one line must be first */ | ||
111 | ".htm.html", "text/html", | 253 | ".htm.html", "text/html", |
112 | ".jpg.jpeg", "image/jpeg", | 254 | ".jpg.jpeg", "image/jpeg", |
113 | ".gif", "image/gif", | 255 | ".gif", "image/gif", |
114 | ".png", "image/png", | 256 | ".png", "image/png", |
115 | ".txt.h.c.cc.cpp", "text/plain", | 257 | ".txt.h.c.cc.cpp", "text/plain", |
116 | 0,0 | 258 | ".css", "text/css", |
259 | ".wav", "audio/wav", | ||
260 | ".avi", "video/x-msvideo", | ||
261 | ".qt.mov", "video/quicktime", | ||
262 | ".mpe.mpeg", "video/mpeg", | ||
263 | ".mid.midi", "audio/midi", | ||
264 | ".mp3", "audio/mpeg", | ||
265 | #if 0 /* unpopular */ | ||
266 | ".au", "audio/basic", | ||
267 | ".pac", "application/x-ns-proxy-autoconfig", | ||
268 | ".vrml.wrl", "model/vrml", | ||
269 | #endif | ||
270 | 0, "application/octet-stream" /* default */ | ||
117 | }; | 271 | }; |
118 | 272 | ||
119 | typedef enum | 273 | typedef enum |
@@ -121,9 +275,10 @@ typedef enum | |||
121 | HTTP_OK = 200, | 275 | HTTP_OK = 200, |
122 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ | 276 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ |
123 | HTTP_NOT_FOUND = 404, | 277 | HTTP_NOT_FOUND = 404, |
278 | HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ | ||
279 | HTTP_BAD_REQUEST = 400, /* malformed syntax */ | ||
280 | HTTP_FORBIDDEN = 403, | ||
124 | HTTP_INTERNAL_SERVER_ERROR = 500, | 281 | HTTP_INTERNAL_SERVER_ERROR = 500, |
125 | HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ | ||
126 | HTTP_BAD_REQUEST = 400, /* malformed syntax */ | ||
127 | #if 0 /* future use */ | 282 | #if 0 /* future use */ |
128 | HTTP_CONTINUE = 100, | 283 | HTTP_CONTINUE = 100, |
129 | HTTP_SWITCHING_PROTOCOLS = 101, | 284 | HTTP_SWITCHING_PROTOCOLS = 101, |
@@ -136,7 +291,6 @@ typedef enum | |||
136 | HTTP_MOVED_TEMPORARILY = 302, | 291 | HTTP_MOVED_TEMPORARILY = 302, |
137 | HTTP_NOT_MODIFIED = 304, | 292 | HTTP_NOT_MODIFIED = 304, |
138 | HTTP_PAYMENT_REQUIRED = 402, | 293 | HTTP_PAYMENT_REQUIRED = 402, |
139 | HTTP_FORBIDDEN = 403, | ||
140 | HTTP_BAD_GATEWAY = 502, | 294 | HTTP_BAD_GATEWAY = 502, |
141 | HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ | 295 | HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ |
142 | HTTP_RESPONSE_SETSIZE=0xffffffff | 296 | HTTP_RESPONSE_SETSIZE=0xffffffff |
@@ -152,15 +306,16 @@ typedef struct | |||
152 | 306 | ||
153 | static const HttpEnumString httpResponseNames[] = { | 307 | static const HttpEnumString httpResponseNames[] = { |
154 | { HTTP_OK, "OK" }, | 308 | { HTTP_OK, "OK" }, |
155 | { HTTP_NOT_IMPLEMENTED, "Not Implemented", | 309 | { HTTP_NOT_IMPLEMENTED, "Not Implemented", |
156 | "The requested method is not recognized by this server." }, | 310 | "The requested method is not recognized by this server." }, |
157 | { HTTP_UNAUTHORIZED, "Unauthorized", "" }, | 311 | { HTTP_UNAUTHORIZED, "Unauthorized", "" }, |
158 | { HTTP_NOT_FOUND, "Not Found", | 312 | { HTTP_NOT_FOUND, "Not Found", |
159 | "The requested URL was not found on this server." }, | 313 | "The requested URL was not found on this server." }, |
314 | { HTTP_BAD_REQUEST, "Bad Request" , | ||
315 | "Unsupported method." }, | ||
316 | { HTTP_FORBIDDEN, "Forbidden", "" }, | ||
160 | { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error" | 317 | { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error" |
161 | "Internal Server Error" }, | 318 | "Internal Server Error" }, |
162 | { HTTP_BAD_REQUEST, "Bad Request" , | ||
163 | "Unsupported method.\n" }, | ||
164 | #if 0 | 319 | #if 0 |
165 | { HTTP_CREATED, "Created" }, | 320 | { HTTP_CREATED, "Created" }, |
166 | { HTTP_ACCEPTED, "Accepted" }, | 321 | { HTTP_ACCEPTED, "Accepted" }, |
@@ -169,12 +324,247 @@ static const HttpEnumString httpResponseNames[] = { | |||
169 | { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, | 324 | { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, |
170 | { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" }, | 325 | { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" }, |
171 | { HTTP_NOT_MODIFIED, "Not Modified" }, | 326 | { HTTP_NOT_MODIFIED, "Not Modified" }, |
172 | { HTTP_FORBIDDEN, "Forbidden", "" }, | ||
173 | { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, | 327 | { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, |
174 | { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, | 328 | { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, |
175 | #endif | 329 | #endif |
176 | }; | 330 | }; |
177 | 331 | ||
332 | |||
333 | static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT"; | ||
334 | static const char Content_length[] = "Content-length:"; | ||
335 | |||
336 | |||
337 | /* | ||
338 | * sotring to: | ||
339 | * .ext:mime/type | ||
340 | * /path:user:pass | ||
341 | * /path/subdir:user:pass | ||
342 | * D:from | ||
343 | * A:from | ||
344 | * D:* | ||
345 | */ | ||
346 | static int conf_sort(const void *p1, const void *p2) | ||
347 | { | ||
348 | const Htaccess *cl1 = *(const Htaccess **)p1; | ||
349 | const Htaccess *cl2 = *(const Htaccess **)p2; | ||
350 | char c1 = cl1->before_colon[0]; | ||
351 | char c2 = cl2->before_colon[0]; | ||
352 | int test; | ||
353 | |||
354 | #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
355 | /* .ext line up before other lines for simlify algorithm */ | ||
356 | test = c2 == '.'; | ||
357 | if(c1 == '.') | ||
358 | return -(!test); | ||
359 | if(test) | ||
360 | return test; | ||
361 | #endif | ||
362 | |||
363 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
364 | test = c1 == '/'; | ||
365 | /* /path line up before A/D lines for simlify algorithm */ | ||
366 | if(test) { | ||
367 | if(c2 != '/') | ||
368 | return -test; | ||
369 | /* a shortes path with user:pass must be first */ | ||
370 | return strlen(cl1->before_colon) - strlen(cl2->before_colon); | ||
371 | } else if(c2 == '/') | ||
372 | return !test; | ||
373 | #endif | ||
374 | |||
375 | /* D:from must move top */ | ||
376 | test = c2 == 'D' && cl2->after_colon[0] != 0; | ||
377 | if(c1 == 'D' && cl1->after_colon[0] != 0) { | ||
378 | return -(!test); | ||
379 | } | ||
380 | if(test) | ||
381 | return test; | ||
382 | |||
383 | /* next lines - A:from */ | ||
384 | test = c2 == 'A' && cl2->after_colon[0] != 0; | ||
385 | if(c1 == 'A' && cl1->after_colon[0] != 0) { | ||
386 | return -(!test); | ||
387 | } | ||
388 | if(test) | ||
389 | return test; | ||
390 | |||
391 | /* end lines - D:* */ | ||
392 | test = c2 == 'D' && cl2->after_colon[0] == 0; | ||
393 | if(c1 == 'D' && cl1->after_colon[0] == 0) { | ||
394 | return -(!test); | ||
395 | } | ||
396 | #ifdef DEBUG | ||
397 | if(!test) | ||
398 | error_msg_and_die("sort: can`t found compares!"); | ||
399 | #endif | ||
400 | return test; | ||
401 | } | ||
402 | |||
403 | |||
404 | /* flag */ | ||
405 | #define FIRST_PARSE 0 | ||
406 | #define SUBDIR_PARSE 1 | ||
407 | #define SIGNALED_PARSE 2 | ||
408 | |||
409 | static void parse_conf(const char *path, int flag) | ||
410 | { | ||
411 | #define bc cur->before_colon[0] | ||
412 | #define ac cur->after_colon[0] | ||
413 | FILE *f; | ||
414 | Htaccess *prev; | ||
415 | Htaccess *cur; | ||
416 | const char *cf = config->configFile; | ||
417 | char buf[80]; | ||
418 | char *p0 = NULL; | ||
419 | int deny_all = 0; /* default A:* */ | ||
420 | int n = 0; /* count config lines */ | ||
421 | |||
422 | if(flag == SUBDIR_PARSE || cf == NULL) { | ||
423 | cf = p0 = alloca(strlen(path) + sizeof(httpd_conf) + 2); | ||
424 | if(p0 == NULL) { | ||
425 | if(flag == FIRST_PARSE) | ||
426 | error_msg_and_die(memory_exhausted); | ||
427 | return; | ||
428 | } | ||
429 | sprintf(p0, "%s/%s", path, httpd_conf); | ||
430 | } | ||
431 | |||
432 | while((f = fopen(cf, "r")) == NULL) { | ||
433 | if(flag != FIRST_PARSE) | ||
434 | return; /* subdir config not found */ | ||
435 | if(p0 == NULL) /* if -c option gived */ | ||
436 | perror_msg_and_die("%s", cf); | ||
437 | p0 = NULL; | ||
438 | cf = httpd_conf; /* set -c ./httpd_conf */ | ||
439 | } | ||
440 | |||
441 | prev = config->Httpd_conf_parsed; | ||
442 | if(flag != SUBDIR_PARSE) { | ||
443 | /* free previous setuped */ | ||
444 | while( prev ) { | ||
445 | cur = prev; | ||
446 | prev = cur->next; | ||
447 | free(cur); | ||
448 | } | ||
449 | config->Httpd_conf_parsed = prev; /* eq NULL */ | ||
450 | } else { | ||
451 | /* parse previous IP logic for merge */ | ||
452 | for(cur = prev; cur; cur = cur->next) { | ||
453 | if(bc == 'D' && ac == 0) | ||
454 | deny_all++; | ||
455 | n++; | ||
456 | /* find last for set prev->next of merging */ | ||
457 | if(cur != prev) | ||
458 | prev = prev->next; | ||
459 | } | ||
460 | } | ||
461 | |||
462 | /* This could stand some work */ | ||
463 | while ( (p0 = fgets(buf, 80, f)) != NULL) { | ||
464 | char *p; | ||
465 | char *colon; | ||
466 | |||
467 | for(p = colon = p0; *p; p++) { | ||
468 | if(*p == '#') { | ||
469 | *p = 0; | ||
470 | break; | ||
471 | } | ||
472 | if(isspace(*p)) { | ||
473 | if(p != p0) { | ||
474 | *p = 0; | ||
475 | break; | ||
476 | } | ||
477 | p0++; | ||
478 | } | ||
479 | else if(*p == ':' && colon <= p0) | ||
480 | colon = p; | ||
481 | } | ||
482 | |||
483 | /* test for empty or strange line */ | ||
484 | if (colon <= p0 || colon[1] == 0) | ||
485 | continue; | ||
486 | if(colon[1] == '*') | ||
487 | colon[1] = 0; /* Allow all */ | ||
488 | if(*p0 == 'a') | ||
489 | *p0 = 'A'; | ||
490 | if(*p0 == 'd') | ||
491 | *p0 = 'D'; | ||
492 | if(*p0 != 'A' && *p0 != 'D' | ||
493 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
494 | && *p0 != '/' | ||
495 | #endif | ||
496 | #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
497 | && *p0 != '.' | ||
498 | #endif | ||
499 | ) | ||
500 | continue; | ||
501 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
502 | if(*p0 == '/' && colon[1] == 0) { | ||
503 | /* skip /path:* */ | ||
504 | continue; | ||
505 | } | ||
506 | #endif | ||
507 | |||
508 | if(*p0 == 'A' || *p0 == 'D') { | ||
509 | if(colon[1] == 0) { | ||
510 | if(*p0 == 'A' || deny_all != 0) | ||
511 | continue; /* skip default A:* or double D:* */ | ||
512 | } | ||
513 | if(deny_all != 0 && *p0 == 'A') | ||
514 | continue; // if previous setted rule D:* skip all subdir A: | ||
515 | } | ||
516 | |||
517 | /* storing current config line */ | ||
518 | cur = calloc(1, sizeof(Htaccess) + (p-p0)); | ||
519 | if(cur) { | ||
520 | if(*(colon-1) == '/' && (colon-1) != p0) | ||
521 | colon[-1] = 0; // remove last / from /path/ | ||
522 | cur->after_colon = strcpy(cur->before_colon, p0); | ||
523 | cur->after_colon += (colon-p0); | ||
524 | *cur->after_colon++ = 0; | ||
525 | |||
526 | if(prev == NULL) { | ||
527 | /* first line */ | ||
528 | config->Httpd_conf_parsed = prev = cur; | ||
529 | } else { | ||
530 | prev->next = cur; | ||
531 | prev = cur; | ||
532 | } | ||
533 | n++; | ||
534 | } | ||
535 | } | ||
536 | fclose(f); | ||
537 | |||
538 | if(n > 1) { | ||
539 | /* sorting conf lines */ | ||
540 | Htaccess ** pcur; /* array for qsort */ | ||
541 | |||
542 | prev = config->Httpd_conf_parsed; | ||
543 | pcur = alloca((n + 1) * sizeof(Htaccess *)); | ||
544 | if(pcur == NULL) { | ||
545 | if(flag == FIRST_PARSE) | ||
546 | error_msg_and_die(memory_exhausted); | ||
547 | return; | ||
548 | } | ||
549 | n = 0; | ||
550 | for(cur = prev; cur; cur = cur->next) | ||
551 | pcur[n++] = cur; | ||
552 | pcur[n] = NULL; | ||
553 | |||
554 | qsort(pcur, n, sizeof(Htaccess *), conf_sort); | ||
555 | |||
556 | /* storing sorted config */ | ||
557 | config->Httpd_conf_parsed = *pcur; | ||
558 | for(cur = *pcur; cur; cur = cur->next) { | ||
559 | #ifdef DEBUG | ||
560 | error_msg("%s: %s:%s", cf, cur->before_colon, cur->after_colon); | ||
561 | #endif | ||
562 | cur->next = *++pcur; | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | |||
567 | #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR | ||
178 | /**************************************************************************** | 568 | /**************************************************************************** |
179 | * | 569 | * |
180 | > $Function: encodeString() | 570 | > $Function: encodeString() |
@@ -197,20 +587,22 @@ static char *encodeString(const char *string) | |||
197 | /* take the simple route and encode everything */ | 587 | /* take the simple route and encode everything */ |
198 | /* could possibly scan once to get length. */ | 588 | /* could possibly scan once to get length. */ |
199 | int len = strlen(string); | 589 | int len = strlen(string); |
200 | char *out = (char*)malloc(len*5 +1); | 590 | char *out = malloc(len*5 +1); |
201 | char *p=out; | 591 | char *p=out; |
202 | char ch; | 592 | char ch; |
593 | |||
203 | if (!out) return ""; | 594 | if (!out) return ""; |
204 | while ((ch = *string++)) | 595 | while ((ch = *string++)) { |
205 | { | ||
206 | // very simple check for what to encode | 596 | // very simple check for what to encode |
207 | if (isalnum(ch)) *p++ = ch; | 597 | if (isalnum(ch)) *p++ = ch; |
208 | else p += sprintf(p,"&#%d", (unsigned char) ch); | 598 | else p += sprintf(p, "&#%d", (unsigned char) ch); |
209 | } | 599 | } |
210 | *p=0; | 600 | *p=0; |
211 | return out; | 601 | return out; |
212 | } | 602 | } |
603 | #endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */ | ||
213 | 604 | ||
605 | #ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR | ||
214 | /**************************************************************************** | 606 | /**************************************************************************** |
215 | * | 607 | * |
216 | > $Function: decodeString() | 608 | > $Function: decodeString() |
@@ -236,12 +628,11 @@ static char *decodeString(char *string) | |||
236 | char *ptr = string; | 628 | char *ptr = string; |
237 | while (*ptr) | 629 | while (*ptr) |
238 | { | 630 | { |
239 | if (*ptr == '+') { *string++ = ' '; ptr++; } | 631 | if (*ptr == '+') { *string++ = ' '; ptr++; } |
240 | else if (*ptr != '%') *string++ = *ptr++; | 632 | else if (*ptr != '%') *string++ = *ptr++; |
241 | else | 633 | else { |
242 | { | ||
243 | unsigned int value; | 634 | unsigned int value; |
244 | sscanf(ptr+1,"%2X",&value); | 635 | sscanf(ptr+1, "%2X", &value); |
245 | *string++ = value; | 636 | *string++ = value; |
246 | ptr += 3; | 637 | ptr += 3; |
247 | } | 638 | } |
@@ -249,8 +640,10 @@ static char *decodeString(char *string) | |||
249 | *string = '\0'; | 640 | *string = '\0'; |
250 | return orig; | 641 | return orig; |
251 | } | 642 | } |
643 | #endif /* CONFIG_FEATURE_HTTPD_DECODE_URL_STR */ | ||
252 | 644 | ||
253 | 645 | ||
646 | #ifdef CONFIG_FEATURE_HTTPD_CGI | ||
254 | /**************************************************************************** | 647 | /**************************************************************************** |
255 | * | 648 | * |
256 | > $Function: addEnv() | 649 | > $Function: addEnv() |
@@ -260,28 +653,47 @@ static char *decodeString(char *string) | |||
260 | * environment settings passed to the cgi execution script. | 653 | * environment settings passed to the cgi execution script. |
261 | * | 654 | * |
262 | * $Parameters: | 655 | * $Parameters: |
263 | * (char *) name . . . The environment variable name. | 656 | * (char *) name_before_underline - The first part environment variable name. |
264 | * (char *) value . . The value to which the env variable is set. | 657 | * (char *) name_after_underline - The second part environment variable name. |
658 | * (char *) value . . The value to which the env variable is set. | ||
265 | * | 659 | * |
266 | * $Return: (void) | 660 | * $Return: (void) |
267 | * | 661 | * |
268 | * $Errors: Silently returns if the env runs out of space to hold the new item | 662 | * $Errors: Silently returns if the env runs out of space to hold the new item |
269 | * | 663 | * |
270 | ****************************************************************************/ | 664 | ****************************************************************************/ |
271 | static void addEnv(const char *name, const char *value) | 665 | static void addEnv(const char *name_before_underline, |
666 | const char *name_after_underline, const char *value) | ||
272 | { | 667 | { |
273 | char *s; | 668 | char *s; |
274 | if (envCount >= ENVSIZE) return; | 669 | |
275 | if (!value) value = ""; | 670 | if (config->envCount >= ENVSIZE) |
276 | s=(char*)malloc(strlen(name)+strlen(value)+2); | 671 | return; |
277 | if (s) | 672 | if (!value) |
278 | { | 673 | value = ""; |
279 | sprintf(s,"%s=%s",name, value); | 674 | s = malloc(strlen(name_before_underline) + strlen(name_after_underline) + |
280 | envp[envCount++]=s; | 675 | strlen(value) + 3); |
281 | envp[envCount]=0; | 676 | if (s) { |
677 | const char *underline = *name_after_underline ? "_" : ""; | ||
678 | |||
679 | sprintf(s,"%s%s%s=%s", name_before_underline, underline, | ||
680 | name_after_underline, value); | ||
681 | config->envp[config->envCount++] = s; | ||
682 | config->envp[config->envCount] = 0; | ||
282 | } | 683 | } |
283 | } | 684 | } |
284 | 685 | ||
686 | /* set environs SERVER_PORT and REMOTE_PORT */ | ||
687 | static void addEnvPort(const char *port_name) | ||
688 | { | ||
689 | char buf[16]; | ||
690 | |||
691 | sprintf(buf, "%u", config->port); | ||
692 | addEnv(port_name, "PORT", buf); | ||
693 | } | ||
694 | #endif /* CONFIG_FEATURE_HTTPD_CGI */ | ||
695 | |||
696 | #ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV | ||
285 | /**************************************************************************** | 697 | /**************************************************************************** |
286 | * | 698 | * |
287 | > $Function: addEnvCgi | 699 | > $Function: addEnvCgi |
@@ -289,7 +701,8 @@ static void addEnv(const char *name, const char *value) | |||
289 | * $Description: Create environment variables given a URL encoded arg list. | 701 | * $Description: Create environment variables given a URL encoded arg list. |
290 | * For each variable setting the URL encoded arg list, create a corresponding | 702 | * For each variable setting the URL encoded arg list, create a corresponding |
291 | * environment variable. URL encoded arguments have the form | 703 | * environment variable. URL encoded arguments have the form |
292 | * name1=value1&name2=value2&name3=value3 | 704 | * name1=value1&name2=value2&name3=&ignores |
705 | * from this example, name3 set empty value, tail without '=' skiping | ||
293 | * | 706 | * |
294 | * $Parameters: | 707 | * $Parameters: |
295 | * (char *) pargs . . . . A pointer to the URL encoded arguments. | 708 | * (char *) pargs . . . . A pointer to the URL encoded arguments. |
@@ -302,65 +715,29 @@ static void addEnv(const char *name, const char *value) | |||
302 | static void addEnvCgi(const char *pargs) | 715 | static void addEnvCgi(const char *pargs) |
303 | { | 716 | { |
304 | char *args; | 717 | char *args; |
718 | char *memargs; | ||
305 | if (pargs==0) return; | 719 | if (pargs==0) return; |
306 | 720 | ||
307 | /* args are a list of name=value&name2=value2 sequences */ | 721 | /* args are a list of name=value&name2=value2 sequences */ |
308 | args = strdup(pargs); | 722 | memargs = args = strdup(pargs); |
309 | while (args && *args) | 723 | while (args && *args) { |
310 | { | 724 | const char *name = args; |
311 | char *sep; | 725 | char *value = strchr(args, '='); |
312 | char *name=args; | 726 | |
313 | char *value=strchr(args,'='); | 727 | if (!value) /* &XXX without '=' */ |
314 | char *cginame; | 728 | break; |
315 | if (!value) break; | 729 | *value++ = 0; |
316 | *value++=0; | 730 | args = strchr(value, '&'); |
317 | sep=strchr(value,'&'); | 731 | if (args) |
318 | if (sep) | 732 | *args++ = 0; |
319 | { | 733 | addEnv("CGI", name, decodeString(value)); |
320 | *sep=0; | ||
321 | args=sep+1; | ||
322 | } | ||
323 | else | ||
324 | { | ||
325 | sep = value + strlen(value); | ||
326 | args = 0; /* no more */ | ||
327 | } | ||
328 | cginame=(char*)malloc(strlen(decodeString(name))+5); | ||
329 | if (!cginame) break; | ||
330 | sprintf(cginame,"CGI_%s",name); | ||
331 | addEnv(cginame,decodeString(value)); | ||
332 | free(cginame); | ||
333 | } | 734 | } |
735 | free(memargs); | ||
334 | } | 736 | } |
737 | #endif /* CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV */ | ||
335 | 738 | ||
336 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
337 | static const unsigned char base64ToBin[] = { | ||
338 | 255 /* ' ' */, 255 /* '!' */, 255 /* '"' */, 255 /* '#' */, | ||
339 | 1 /* '$' */, 255 /* '%' */, 255 /* '&' */, 255 /* ''' */, | ||
340 | 255 /* '(' */, 255 /* ')' */, 255 /* '*' */, 62 /* '+' */, | ||
341 | 255 /* ',' */, 255 /* '-' */, 255 /* '.' */, 63 /* '/' */, | ||
342 | 52 /* '0' */, 53 /* '1' */, 54 /* '2' */, 55 /* '3' */, | ||
343 | 56 /* '4' */, 57 /* '5' */, 58 /* '6' */, 59 /* '7' */, | ||
344 | 60 /* '8' */, 61 /* '9' */, 255 /* ':' */, 255 /* ';' */, | ||
345 | 255 /* '<' */, 00 /* '=' */, 255 /* '>' */, 255 /* '?' */, | ||
346 | 255 /* '@' */, 00 /* 'A' */, 01 /* 'B' */, 02 /* 'C' */, | ||
347 | 03 /* 'D' */, 04 /* 'E' */, 05 /* 'F' */, 06 /* 'G' */, | ||
348 | 7 /* 'H' */, 8 /* 'I' */, 9 /* 'J' */, 10 /* 'K' */, | ||
349 | 11 /* 'L' */, 12 /* 'M' */, 13 /* 'N' */, 14 /* 'O' */, | ||
350 | 15 /* 'P' */, 16 /* 'Q' */, 17 /* 'R' */, 18 /* 'S' */, | ||
351 | 19 /* 'T' */, 20 /* 'U' */, 21 /* 'V' */, 22 /* 'W' */, | ||
352 | 23 /* 'X' */, 24 /* 'Y' */, 25 /* 'Z' */, 255 /* '[' */, | ||
353 | 255 /* '\' */, 255 /* ']' */, 255 /* '^' */, 255 /* '_' */, | ||
354 | 255 /* '`' */, 26 /* 'a' */, 27 /* 'b' */, 28 /* 'c' */, | ||
355 | 29 /* 'd' */, 30 /* 'e' */, 31 /* 'f' */, 32 /* 'g' */, | ||
356 | 33 /* 'h' */, 34 /* 'i' */, 35 /* 'j' */, 36 /* 'k' */, | ||
357 | 37 /* 'l' */, 38 /* 'm' */, 39 /* 'n' */, 40 /* 'o' */, | ||
358 | 41 /* 'p' */, 42 /* 'q' */, 43 /* 'r' */, 44 /* 's' */, | ||
359 | 45 /* 't' */, 46 /* 'u' */, 47 /* 'v' */, 48 /* 'w' */, | ||
360 | 49 /* 'x' */, 50 /* 'y' */, 51 /* 'z' */, 255 /* '{' */, | ||
361 | 255 /* '|' */, 255 /* '}' */, 255 /* '~' */, 255 /* '' */ | ||
362 | }; | ||
363 | 739 | ||
740 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
364 | /**************************************************************************** | 741 | /**************************************************************************** |
365 | * | 742 | * |
366 | > $Function: decodeBase64() | 743 | > $Function: decodeBase64() |
@@ -370,186 +747,96 @@ static const unsigned char base64ToBin[] = { | |||
370 | * Since the decode always results in a shorter size than the input, it is | 747 | * Since the decode always results in a shorter size than the input, it is |
371 | * OK to pass the input arg as an output arg. | 748 | * OK to pass the input arg as an output arg. |
372 | * | 749 | * |
373 | * $Parameters: | 750 | * $Parameter: |
374 | * (void *) outData. . . Where to place the decoded data. | 751 | * (char *) Data . . . . A pointer to a base64 encoded string. |
375 | * (size_t) outDataLen . The length of the output data string. | 752 | * Where to place the decoded data. |
376 | * (void *) inData . . . A pointer to a base64 encoded string. | ||
377 | * (size_t) inDataLen . The length of the input data string. | ||
378 | * | 753 | * |
379 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). | 754 | * $Return: void |
380 | * | 755 | * |
381 | * $Errors: None | 756 | * $Errors: None |
382 | * | 757 | * |
383 | ****************************************************************************/ | 758 | ****************************************************************************/ |
384 | static size_t decodeBase64(void *outData, size_t outDataLen, | 759 | static void decodeBase64(char *Data) |
385 | void *inData, size_t inDataLen) | ||
386 | { | 760 | { |
387 | int i = 0; | 761 | int i = 0; |
388 | unsigned char *in = inData; | 762 | static const char base64ToBin[] = |
389 | unsigned char *out = outData; | 763 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
764 | |||
765 | const unsigned char *in = Data; | ||
766 | // The decoded size will be at most 3/4 the size of the encoded | ||
390 | unsigned long ch = 0; | 767 | unsigned long ch = 0; |
391 | while (inDataLen && outDataLen) | 768 | |
392 | { | 769 | while (*in) { |
393 | unsigned char conv = 0; | 770 | unsigned char conv = 0; |
394 | unsigned char newch; | 771 | |
395 | 772 | while (*in) { | |
396 | while (inDataLen) | 773 | const char *p64; |
397 | { | 774 | |
398 | inDataLen--; | 775 | p64 = strchr(base64ToBin, *in++); |
399 | newch = *in++; | 776 | if(p64 == NULL) |
400 | if ((newch < '0') || (newch > 'z')) continue; | 777 | continue; |
401 | conv = base64ToBin[newch - 32]; | 778 | conv = (p64 - base64ToBin); |
402 | if (conv == 255) continue; | ||
403 | break; | 779 | break; |
404 | } | 780 | } |
405 | ch = (ch << 6) | conv; | 781 | ch = (ch << 6) | conv; |
406 | i++; | 782 | i++; |
407 | if (i== 4) | 783 | if (i== 4) { |
408 | { | 784 | *Data++ = (char) (ch >> 16); |
409 | if (outDataLen >= 3) | 785 | *Data++ = (char) (ch >> 8); |
410 | { | 786 | *Data++ = (char) ch; |
411 | *(out++) = (unsigned char) (ch >> 16); | ||
412 | *(out++) = (unsigned char) (ch >> 8); | ||
413 | *(out++) = (unsigned char) ch; | ||
414 | outDataLen-=3; | ||
415 | } | ||
416 | |||
417 | i = 0; | 787 | i = 0; |
418 | } | 788 | } |
419 | |||
420 | if ((inDataLen == 0) && (i != 0)) | ||
421 | { | ||
422 | /* error - non multiple of 4 chars on input */ | ||
423 | break; | ||
424 | } | ||
425 | |||
426 | } | 789 | } |
427 | 790 | *Data = 0; | |
428 | /* return the actual number of chars in output array */ | ||
429 | return out-(unsigned char*) outData; | ||
430 | } | 791 | } |
431 | #endif | 792 | #endif |
432 | 793 | ||
433 | /**************************************************************************** | ||
434 | * | ||
435 | > $Function: perror_and_exit() | ||
436 | * | ||
437 | > $Description: A helper function to print an error and exit. | ||
438 | * | ||
439 | * $Parameters: | ||
440 | * (const char *) msg . . . A 'context' message to include. | ||
441 | * | ||
442 | * $Return: None | ||
443 | * | ||
444 | * $Errors: None | ||
445 | * | ||
446 | ****************************************************************************/ | ||
447 | static void perror_exit(const char *msg) | ||
448 | { | ||
449 | perror(msg); | ||
450 | exit(1); | ||
451 | } | ||
452 | |||
453 | |||
454 | /**************************************************************************** | ||
455 | * | ||
456 | > $Function: strncmpi() | ||
457 | * | ||
458 | * $Description: compare two strings without regard to case. | ||
459 | * | ||
460 | * $Parameters: | ||
461 | * (char *) a . . . . . The first string. | ||
462 | * (char *) b . . . . . The second string. | ||
463 | * (int) n . . . . . . The number of chars to compare. | ||
464 | * | ||
465 | * $Return: (int) . . . . . . 0 if strings equal. 1 if a>b, -1 if b < a. | ||
466 | * | ||
467 | * $Errors: None | ||
468 | * | ||
469 | ****************************************************************************/ | ||
470 | #define __toupper(c) ((('a' <= (c))&&((c) <= 'z')) ? ((c) - 'a' + 'A') : (c)) | ||
471 | #define __tolower(c) ((('A' <= (c))&&((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) | ||
472 | static int strncmpi(const char *a, const char *b,int n) | ||
473 | { | ||
474 | char a1,b1; | ||
475 | a1 = b1 = 0; | ||
476 | |||
477 | while(n-- && ((a1 = *a++) != '\0') && ((b1 = *b++) != '\0')) | ||
478 | { | ||
479 | if(a1 == b1) continue; /* No need to convert */ | ||
480 | a1 = __tolower(a1); | ||
481 | b1 = __tolower(b1); | ||
482 | if(a1 != b1) break; /* No match, abort */ | ||
483 | } | ||
484 | if (n>=0) | ||
485 | { | ||
486 | if(a1 > b1) return 1; | ||
487 | if(a1 < b1) return -1; | ||
488 | } | ||
489 | return 0; | ||
490 | } | ||
491 | 794 | ||
795 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
492 | /**************************************************************************** | 796 | /**************************************************************************** |
493 | * | 797 | * |
494 | > $Function: openServer() | 798 | > $Function: openServer() |
495 | * | 799 | * |
496 | * $Description: create a listen server socket on the designated port. | 800 | * $Description: create a listen server socket on the designated port. |
497 | * | 801 | * |
498 | * $Parameters: | ||
499 | * (int) port . . . The port to listen on for connections. | ||
500 | * | ||
501 | * $Return: (int) . . . A connection socket. -1 for errors. | 802 | * $Return: (int) . . . A connection socket. -1 for errors. |
502 | * | 803 | * |
503 | * $Errors: None | 804 | * $Errors: None |
504 | * | 805 | * |
505 | ****************************************************************************/ | 806 | ****************************************************************************/ |
506 | static int openServer(int port) | 807 | static int openServer(void) |
507 | { | 808 | { |
508 | struct sockaddr_in lsocket; | 809 | struct sockaddr_in lsocket; |
509 | int fd; | 810 | int fd; |
510 | 811 | ||
511 | /* create the socket right now */ | 812 | /* create the socket right now */ |
512 | /* inet_addr() returns a value that is already in network order */ | 813 | /* inet_addr() returns a value that is already in network order */ |
513 | memset(&lsocket, 0, sizeof(lsocket)); | 814 | memset(&lsocket, 0, sizeof(lsocket)); |
514 | lsocket.sin_family = AF_INET; | 815 | lsocket.sin_family = AF_INET; |
515 | lsocket.sin_addr.s_addr = INADDR_ANY; | 816 | lsocket.sin_addr.s_addr = INADDR_ANY; |
516 | lsocket.sin_port = htons(port) ; | 817 | lsocket.sin_port = htons(config->port) ; |
517 | fd = socket(AF_INET, SOCK_STREAM, 0); | 818 | fd = socket(AF_INET, SOCK_STREAM, 0); |
518 | if (fd >= 0) | 819 | if (fd >= 0) { |
519 | { | ||
520 | /* tell the OS it's OK to reuse a previous address even though */ | 820 | /* tell the OS it's OK to reuse a previous address even though */ |
521 | /* it may still be in a close down state. Allows bind to succeed. */ | 821 | /* it may still be in a close down state. Allows bind to succeed. */ |
522 | int one = 1; | 822 | int on = 1; |
523 | #ifdef SO_REUSEPORT | 823 | #ifdef SO_REUSEPORT |
524 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char*)&one, sizeof(one)) ; | 824 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ; |
525 | #else | 825 | #else |
526 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) ; | 826 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ; |
527 | #endif | 827 | #endif |
528 | if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) | 828 | if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) { |
529 | { | ||
530 | listen(fd, 9); | 829 | listen(fd, 9); |
531 | signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */ | 830 | signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */ |
831 | } else { | ||
832 | perror_msg_and_die("bind"); | ||
532 | } | 833 | } |
533 | else | 834 | } else { |
534 | { | 835 | perror_msg_and_die("create socket"); |
535 | perror("failure to bind to server port"); | 836 | } |
536 | shutdown(fd,0); | ||
537 | close(fd); | ||
538 | fd = -1; | ||
539 | } | ||
540 | } | ||
541 | else | ||
542 | { | ||
543 | fprintf(stderr,"httpd: unable to create socket \n"); | ||
544 | } | ||
545 | return fd; | 837 | return fd; |
546 | } | 838 | } |
547 | 839 | #endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ | |
548 | static int sendBuf(int s, char *buf, int len) | ||
549 | { | ||
550 | if (len == -1) len = strlen(buf); | ||
551 | return send(s, buf, len, 0); | ||
552 | } | ||
553 | 840 | ||
554 | /**************************************************************************** | 841 | /**************************************************************************** |
555 | * | 842 | * |
@@ -558,84 +845,65 @@ static int sendBuf(int s, char *buf, int len) | |||
558 | * $Description: Create and send HTTP response headers. | 845 | * $Description: Create and send HTTP response headers. |
559 | * The arguments are combined and sent as one write operation. Note that | 846 | * The arguments are combined and sent as one write operation. Note that |
560 | * IE will puke big-time if the headers are not sent in one packet and the | 847 | * IE will puke big-time if the headers are not sent in one packet and the |
561 | * second packet is delayed for any reason. If contentType is null the | 848 | * second packet is delayed for any reason. |
562 | * content type is assumed to be text/html | ||
563 | * | 849 | * |
564 | * $Parameters: | 850 | * $Parameter: |
565 | * (int) s . . . The http socket. | ||
566 | * (HttpResponseNum) responseNum . . . The result code to send. | 851 | * (HttpResponseNum) responseNum . . . The result code to send. |
567 | * (const char *) contentType . . . . A string indicating the type. | ||
568 | * (int) contentLength . . . . . . . . Content length. -1 if unknown. | ||
569 | * (time_t) expire . . . . . . . . . . Expiration time (secs since 1970) | ||
570 | * | 852 | * |
571 | * $Return: (int) . . . . Always 0 | 853 | * $Return: (int) . . . . writing errors |
572 | * | ||
573 | * $Errors: None | ||
574 | * | 854 | * |
575 | ****************************************************************************/ | 855 | ****************************************************************************/ |
576 | static int sendHeaders(int s, HttpResponseNum responseNum , | 856 | static int sendHeaders(HttpResponseNum responseNum) |
577 | const char *contentType, | ||
578 | int contentLength, time_t expire) | ||
579 | { | 857 | { |
580 | char buf[1200]; | 858 | char *buf = config->buf; |
581 | const char *responseString = ""; | 859 | const char *responseString = ""; |
582 | const char *infoString = 0; | 860 | const char *infoString = 0; |
583 | unsigned int i; | 861 | unsigned int i; |
584 | time_t timer = time(0); | 862 | time_t timer = time(0); |
585 | char timeStr[80]; | 863 | char timeStr[80]; |
586 | for (i=0; | 864 | int len; |
587 | i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) | 865 | |
588 | { | 866 | for (i = 0; |
589 | if (httpResponseNames[i].type != responseNum) continue; | 867 | i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) { |
590 | responseString = httpResponseNames[i].name; | 868 | if (httpResponseNames[i].type == responseNum) { |
591 | infoString = httpResponseNames[i].info; | 869 | responseString = httpResponseNames[i].name; |
592 | break; | 870 | infoString = httpResponseNames[i].info; |
871 | break; | ||
872 | } | ||
593 | } | 873 | } |
594 | if (infoString || !contentType) | 874 | if (responseNum != HTTP_OK) { |
595 | { | 875 | config->found_mime_type = "text/html"; // error message is HTML |
596 | contentType = "text/html"; | ||
597 | } | 876 | } |
598 | |||
599 | sprintf(buf, "HTTP/1.0 %d %s\nContent-type: %s\r\n", | ||
600 | responseNum, responseString, contentType); | ||
601 | 877 | ||
602 | /* emit the current date */ | 878 | /* emit the current date */ |
603 | strftime(timeStr, sizeof(timeStr), | 879 | strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer)); |
604 | "%a, %d %b %Y %H:%M:%S GMT",gmtime(&timer)); | 880 | len = sprintf(buf, |
605 | sprintf(buf+strlen(buf), "Date: %s\r\n", timeStr); | 881 | "HTTP/1.0 %d %s\nContent-type: %s\r\n" |
606 | sprintf(buf+strlen(buf), "Connection: close\r\n"); | 882 | "Date: %s\r\nConnection: close\r\n", |
607 | if (expire) | 883 | responseNum, responseString, config->found_mime_type, timeStr); |
608 | { | ||
609 | strftime(timeStr, sizeof(timeStr), | ||
610 | "%a, %d %b %Y %H:%M:%S GMT",gmtime(&expire)); | ||
611 | sprintf(buf+strlen(buf), "Expire: %s\r\n", timeStr); | ||
612 | } | ||
613 | 884 | ||
614 | if (responseNum == HTTP_UNAUTHORIZED) | 885 | if (responseNum == HTTP_UNAUTHORIZED) { |
615 | { | 886 | len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n", |
616 | sprintf(buf+strlen(buf), | 887 | config->realm); |
617 | "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm); | ||
618 | } | 888 | } |
619 | if (contentLength != -1) | 889 | if (config->ContentLength != -1) { /* file */ |
620 | { | 890 | strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod)); |
621 | int len = strlen(buf); | 891 | len += sprintf(buf+len, "Last-Modified: %s\r\n%s %ld\r\n", |
622 | sprintf(buf+len,"Content-length: %d\r\n", contentLength); | 892 | timeStr, Content_length, config->ContentLength); |
623 | } | 893 | } |
624 | strcat(buf,"\r\n"); | 894 | strcat(buf, "\r\n"); |
625 | if (infoString) | 895 | len += 2; |
626 | { | 896 | if (infoString) { |
627 | sprintf(buf+strlen(buf), | 897 | len += sprintf(buf+len, |
628 | "<HEAD><TITLE>%d %s</TITLE></HEAD>\n" | 898 | "<HEAD><TITLE>%d %s</TITLE></HEAD>\n" |
629 | "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n", | 899 | "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n", |
630 | responseNum, responseString, | 900 | responseNum, responseString, |
631 | responseNum, responseString, | 901 | responseNum, responseString, infoString); |
632 | infoString); | ||
633 | } | 902 | } |
634 | #ifdef DEBUG | 903 | #ifdef DEBUG |
635 | if (debugHttpd) fprintf(stderr,"Headers:'%s'", buf); | 904 | if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf); |
636 | #endif | 905 | #endif |
637 | sendBuf(s, buf,-1); | 906 | return full_write(a_c_w, buf, len); |
638 | return 0; | ||
639 | } | 907 | } |
640 | 908 | ||
641 | /**************************************************************************** | 909 | /**************************************************************************** |
@@ -647,30 +915,29 @@ static int sendHeaders(int s, HttpResponseNum responseNum , | |||
647 | * Characters are read one at a time until an eol sequence is found. | 915 | * Characters are read one at a time until an eol sequence is found. |
648 | * | 916 | * |
649 | * $Parameters: | 917 | * $Parameters: |
650 | * (int) s . . . . . The socket fildes. | ||
651 | * (char *) buf . . Where to place the read result. | 918 | * (char *) buf . . Where to place the read result. |
652 | * (int) maxBuf . . Maximum number of chars to fit in buf. | ||
653 | * | 919 | * |
654 | * $Return: (int) . . . . number of characters read. -1 if error. | 920 | * $Return: (int) . . . . number of characters read. -1 if error. |
655 | * | 921 | * |
656 | ****************************************************************************/ | 922 | ****************************************************************************/ |
657 | static int getLine(int s, char *buf, int maxBuf) | 923 | static int getLine(char *buf) |
658 | { | 924 | { |
659 | int count = 0; | 925 | int count = 0; |
660 | while (recv(s, buf+count, 1, 0) == 1) | 926 | |
661 | { | 927 | while (read(a_c_r, buf + count, 1) == 1) { |
662 | if (buf[count] == '\r') continue; | 928 | if (buf[count] == '\r') continue; |
663 | if (buf[count] == '\n') | 929 | if (buf[count] == '\n') { |
664 | { | ||
665 | buf[count] = 0; | 930 | buf[count] = 0; |
666 | return count; | 931 | return count; |
667 | } | 932 | } |
668 | count++; | 933 | if(count < (MAX_MEMORY_BUFF-1)) /* check owerflow */ |
934 | count++; | ||
669 | } | 935 | } |
670 | if (count) return count; | 936 | if (count) return count; |
671 | else return -1; | 937 | else return -1; |
672 | } | 938 | } |
673 | 939 | ||
940 | #ifdef CONFIG_FEATURE_HTTPD_CGI | ||
674 | /**************************************************************************** | 941 | /**************************************************************************** |
675 | * | 942 | * |
676 | > $Function: sendCgi() | 943 | > $Function: sendCgi() |
@@ -682,193 +949,215 @@ static int getLine(int s, char *buf, int maxBuf) | |||
682 | * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). | 949 | * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). |
683 | * | 950 | * |
684 | * $Parameters: | 951 | * $Parameters: |
685 | * (int ) s . . . . . . . . The session socket. | ||
686 | * (const char *) url . . . The requested URL (with leading /). | 952 | * (const char *) url . . . The requested URL (with leading /). |
687 | * (const char *urlArgs). . Any URL arguments. | 953 | * (const char *urlArgs). . Any URL arguments. |
688 | * (const char *body) . . . POST body contents. | 954 | * (const char *body) . . . POST body contents. |
689 | * (int bodyLen) . . . . . Length of the post body. | 955 | * (int bodyLen) . . . . . Length of the post body. |
690 | 956 | * (const char *cookie) . . For set HTTP_COOKIE. | |
957 | |||
691 | * | 958 | * |
692 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). | 959 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). |
693 | * | 960 | * |
694 | * $Errors: None | 961 | * $Errors: None |
695 | * | 962 | * |
696 | ****************************************************************************/ | 963 | ****************************************************************************/ |
697 | static int sendCgi(int s, const char *url, | 964 | static int sendCgi(const char *url, |
698 | const char *request, const char *urlArgs, | 965 | const char *request, const char *urlArgs, |
699 | const char *body, int bodyLen) | 966 | const char *body, int bodyLen, const char *cookie) |
700 | { | 967 | { |
701 | int fromCgi[2]; /* pipe for reading data from CGI */ | 968 | int fromCgi[2]; /* pipe for reading data from CGI */ |
702 | int toCgi[2]; /* pipe for sending data to CGI */ | 969 | int toCgi[2]; /* pipe for sending data to CGI */ |
703 | 970 | ||
704 | char *argp[] = { 0, 0 }; | 971 | static char * argp[] = { 0, 0 }; |
705 | int pid=0; | 972 | int pid = 0; |
706 | int inFd=inFd; | 973 | int inFd; |
707 | int outFd; | 974 | int outFd; |
708 | int firstLine=1; | 975 | int firstLine = 1; |
709 | 976 | ||
710 | do | 977 | do { |
711 | { | 978 | if (pipe(fromCgi) != 0) { |
712 | if (pipe(fromCgi) != 0) | ||
713 | { | ||
714 | break; | 979 | break; |
715 | } | 980 | } |
716 | if (pipe(toCgi) != 0) | 981 | if (pipe(toCgi) != 0) { |
717 | { | ||
718 | break; | 982 | break; |
719 | } | 983 | } |
720 | 984 | ||
721 | pid = fork(); | 985 | pid = fork(); |
722 | if (pid < 0) | 986 | if (pid < 0) { |
723 | { | ||
724 | pid = 0; | 987 | pid = 0; |
725 | break;; | 988 | break; |
726 | } | 989 | } |
727 | 990 | ||
728 | if (!pid) | 991 | if (!pid) { |
729 | { | ||
730 | /* child process */ | 992 | /* child process */ |
731 | char *script; | 993 | char *script; |
732 | char *directory; | 994 | char *purl = strdup( url ); |
733 | inFd=toCgi[0]; | 995 | char realpath_buff[MAXPATHLEN]; |
734 | outFd=fromCgi[1]; | 996 | |
997 | if(purl == NULL) | ||
998 | _exit(242); | ||
999 | |||
1000 | inFd = toCgi[0]; | ||
1001 | outFd = fromCgi[1]; | ||
735 | 1002 | ||
736 | dup2(inFd, 0); // replace stdin with the pipe | 1003 | dup2(inFd, 0); // replace stdin with the pipe |
737 | dup2(outFd, 1); // replace stdout with the pipe | 1004 | dup2(outFd, 1); // replace stdout with the pipe |
738 | if (!debugHttpd) dup2(outFd, 2); // replace stderr with the pipe | 1005 | |
1006 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1007 | if (!config->debugHttpd) | ||
1008 | #endif | ||
1009 | dup2(outFd, 2); // replace stderr with the pipe | ||
1010 | |||
739 | close(toCgi[0]); | 1011 | close(toCgi[0]); |
740 | close(toCgi[1]); | 1012 | close(toCgi[1]); |
741 | close(fromCgi[0]); | 1013 | close(fromCgi[0]); |
742 | close(fromCgi[1]); | 1014 | close(fromCgi[1]); |
743 | 1015 | ||
744 | #if 0 | ||
745 | fcntl(0,F_SETFD, 1); | ||
746 | fcntl(1,F_SETFD, 1); | ||
747 | fcntl(2,F_SETFD, 1); | ||
748 | #endif | ||
749 | |||
750 | script = (char*) malloc(strlen(url)+2); | ||
751 | if (!script) _exit(242); | ||
752 | sprintf(script,".%s",url); | ||
753 | |||
754 | envCount=0; | ||
755 | addEnv("SCRIPT_NAME",script); | ||
756 | addEnv("REQUEST_METHOD",request); | ||
757 | addEnv("QUERY_STRING",urlArgs); | ||
758 | addEnv("SERVER_SOFTWARE",httpdVersion); | ||
759 | if (strncmpi(request,"POST",4)==0) addEnvCgi(body); | ||
760 | else addEnvCgi(urlArgs); | ||
761 | |||
762 | /* | 1016 | /* |
763 | * Most HTTP servers chdir to the cgi directory. | 1017 | * Find PATH_INFO. |
764 | */ | 1018 | */ |
765 | while (*url == '/') url++; // skip leading slash(s) | 1019 | script = purl; |
766 | directory = strdup( url ); | 1020 | while((script = strchr( script + 1, '/' )) != NULL) { |
767 | if ( directory == (char*) 0 ) | 1021 | /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */ |
768 | script = (char*) (url); /* ignore errors */ | 1022 | struct stat sb; |
769 | else | 1023 | |
770 | { | 1024 | *script = '\0'; |
771 | script = strrchr( directory, '/' ); | 1025 | if(is_directory(purl + 1, 1, &sb) == 0) { |
772 | if ( script == (char*) 0 ) | 1026 | /* not directory, found script.cgi/PATH_INFO */ |
773 | script = directory; | 1027 | *script = '/'; |
774 | else | 1028 | break; |
775 | { | ||
776 | *script++ = '\0'; | ||
777 | (void) chdir( directory ); /* ignore errors */ | ||
778 | } | 1029 | } |
1030 | *script = '/'; /* is directory, find next '/' */ | ||
1031 | } | ||
1032 | addEnv("PATH", "INFO", script); /* set /PATH_INFO or NULL */ | ||
1033 | addEnv("PATH", "", getenv("PATH")); | ||
1034 | addEnv("REQUEST", "METHOD", request); | ||
1035 | if(urlArgs) { | ||
1036 | char *uri = alloca(strlen(purl) + 2 + strlen(urlArgs)); | ||
1037 | if(uri) | ||
1038 | sprintf(uri, "%s?%s", purl, urlArgs); | ||
1039 | addEnv("REQUEST", "URI", uri); | ||
1040 | } else { | ||
1041 | addEnv("REQUEST", "URI", purl); | ||
1042 | } | ||
1043 | if(script != NULL) | ||
1044 | *script = '\0'; /* reduce /PATH_INFO */ | ||
1045 | /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ | ||
1046 | addEnv("SCRIPT_NAME", "", purl); | ||
1047 | addEnv("QUERY_STRING", "", urlArgs); | ||
1048 | addEnv("SERVER", "SOFTWARE", httpdVersion); | ||
1049 | addEnv("SERVER", "PROTOCOL", "HTTP/1.0"); | ||
1050 | addEnv("GATEWAY_INTERFACE", "", "CGI/1.1"); | ||
1051 | #ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV | ||
1052 | addEnv("REMOTE", "ADDR", config->rmt_ip); | ||
1053 | addEnvPort("REMOTE"); | ||
1054 | #else | ||
1055 | addEnv("REMOTE_ADDR", "", config->rmt_ip); | ||
1056 | #endif | ||
1057 | if(bodyLen) { | ||
1058 | char sbl[32]; | ||
1059 | |||
1060 | sprintf(sbl, "%d", bodyLen); | ||
1061 | addEnv("CONTENT_LENGTH", "", sbl); | ||
1062 | } | ||
1063 | if(cookie) | ||
1064 | addEnv("HTTP_COOKIE", "", cookie); | ||
1065 | |||
1066 | #ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV | ||
1067 | if (request != request_GET) { | ||
1068 | addEnvCgi(body); | ||
1069 | } else { | ||
1070 | addEnvCgi(urlArgs); | ||
779 | } | 1071 | } |
1072 | #endif | ||
1073 | |||
1074 | /* set execve argp[0] without path */ | ||
1075 | argp[0] = strrchr( purl, '/' ) + 1; | ||
1076 | /* but script argp[0] must have absolute path and chdiring to this */ | ||
1077 | if(realpath(purl + 1, realpath_buff) != NULL) { | ||
1078 | script = strrchr(realpath_buff, '/'); | ||
1079 | if(script) { | ||
1080 | *script = '\0'; | ||
1081 | if(chdir(realpath_buff) == 0) { | ||
1082 | *script = '/'; | ||
780 | // now run the program. If it fails, use _exit() so no destructors | 1083 | // now run the program. If it fails, use _exit() so no destructors |
781 | // get called and make a mess. | 1084 | // get called and make a mess. |
782 | execve(script, argp, envp); | 1085 | execve(realpath_buff, argp, config->envp); |
783 | 1086 | } | |
784 | #ifdef DEBUG | 1087 | } |
785 | fprintf(stderr, "exec failed\n"); | 1088 | } |
1089 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1090 | config->accepted_socket = 1; /* send to stdout */ | ||
786 | #endif | 1091 | #endif |
787 | close(2); | 1092 | sendHeaders(HTTP_NOT_FOUND); |
788 | close(1); | ||
789 | close(0); | ||
790 | _exit(242); | 1093 | _exit(242); |
791 | } /* end child */ | 1094 | } /* end child */ |
792 | 1095 | ||
1096 | } while (0); | ||
1097 | |||
1098 | if (pid) { | ||
793 | /* parent process */ | 1099 | /* parent process */ |
794 | inFd=fromCgi[0]; | 1100 | int status; |
795 | outFd=toCgi[1]; | 1101 | |
1102 | inFd = fromCgi[0]; | ||
1103 | outFd = toCgi[1]; | ||
796 | close(fromCgi[1]); | 1104 | close(fromCgi[1]); |
797 | close(toCgi[0]); | 1105 | close(toCgi[0]); |
798 | if (body) write(outFd, body, bodyLen); | 1106 | if (body) full_write(outFd, body, bodyLen); |
799 | close(outFd); | 1107 | close(outFd); |
800 | 1108 | ||
801 | } while (0); | 1109 | while (1) { |
802 | |||
803 | if (pid) | ||
804 | { | ||
805 | int status; | ||
806 | pid_t dead_pid; | ||
807 | |||
808 | while (1) | ||
809 | { | ||
810 | struct timeval timeout; | 1110 | struct timeval timeout; |
811 | fd_set readSet; | 1111 | fd_set readSet; |
812 | char buf[160]; | 1112 | char buf[160]; |
813 | int nfound; | 1113 | int nfound; |
814 | int count; | 1114 | int count; |
815 | 1115 | ||
816 | FD_ZERO(&readSet); | 1116 | FD_ZERO(&readSet); |
817 | FD_SET(inFd, &readSet); | 1117 | FD_SET(inFd, &readSet); |
818 | 1118 | ||
819 | /* Now wait on the set of sockets! */ | 1119 | /* Now wait on the set of sockets! */ |
820 | timeout.tv_sec = 0; | 1120 | timeout.tv_sec = 0; |
821 | timeout.tv_usec = 10000; | 1121 | timeout.tv_usec = 10000; |
822 | nfound = select(inFd+1, &readSet, 0, 0, &timeout); | 1122 | nfound = select(inFd + 1, &readSet, 0, 0, &timeout); |
823 | 1123 | ||
824 | if (nfound <= 0) | 1124 | if (nfound <= 0) { |
825 | { | 1125 | if (waitpid(pid, &status, WNOHANG) > 0) { |
826 | dead_pid = waitpid(pid, &status, WNOHANG); | 1126 | close(inFd); |
827 | if (dead_pid != 0) | ||
828 | { | ||
829 | close(fromCgi[0]); | ||
830 | close(fromCgi[1]); | ||
831 | close(toCgi[0]); | ||
832 | close(toCgi[1]); | ||
833 | #ifdef DEBUG | 1127 | #ifdef DEBUG |
834 | if (debugHttpd) | 1128 | if (config->debugHttpd) { |
835 | { | ||
836 | if (WIFEXITED(status)) | 1129 | if (WIFEXITED(status)) |
837 | fprintf(stderr,"piped has exited with status=%d\n", WEXITSTATUS(status)); | 1130 | error_msg("piped has exited with status=%d", WEXITSTATUS(status)); |
838 | if (WIFSIGNALED(status)) | 1131 | if (WIFSIGNALED(status)) |
839 | fprintf(stderr,"piped has exited with signal=%d\n", WTERMSIG(status)); | 1132 | error_msg("piped has exited with signal=%d", WTERMSIG(status)); |
840 | } | 1133 | } |
841 | #endif | 1134 | #endif |
842 | pid = -1; | 1135 | pid = -1; |
843 | break; | 1136 | break; |
844 | } | 1137 | } |
845 | } | 1138 | } else { |
846 | else | 1139 | int s = a_c_w; |
847 | { | 1140 | |
848 | // There is something to read | 1141 | // There is something to read |
849 | count = read(inFd,buf,sizeof(buf)-1); | 1142 | count = full_read(inFd, buf, sizeof(buf)-1); |
850 | // If a read returns 0 at this point then some type of error has | 1143 | // If a read returns 0 at this point then some type of error has |
851 | // occurred. Bail now. | 1144 | // occurred. Bail now. |
852 | if (count == 0) break; | 1145 | if (count == 0) break; |
853 | if (count > 0) | 1146 | if (count > 0) { |
854 | { | 1147 | if (firstLine) { |
855 | if (firstLine) | ||
856 | { | ||
857 | /* check to see if the user script added headers */ | 1148 | /* check to see if the user script added headers */ |
858 | if (strcmp(buf,"HTTP")!= 0) | 1149 | if (strncmp(buf, "HTTP/1.0 200 OK\n", 4) != 0) { |
859 | { | 1150 | full_write(s, "HTTP/1.0 200 OK\n", 16); |
860 | write(s,"HTTP/1.0 200 OK\n", 16); | ||
861 | } | 1151 | } |
862 | if (strstr(buf,"ontent-") == 0) | 1152 | if (strstr(buf, "ontent-") == 0) { |
863 | { | 1153 | full_write(s, "Content-type: text/plain\n\n", 26); |
864 | write(s,"Content-type: text/plain\n\n", 26); | ||
865 | } | 1154 | } |
866 | |||
867 | firstLine=0; | 1155 | firstLine=0; |
868 | } | 1156 | } |
869 | write(s,buf,count); | 1157 | full_write(s, buf, count); |
870 | #ifdef DEBUG | 1158 | #ifdef DEBUG |
871 | if (debugHttpd) fprintf(stderr,"cgi read %d bytes\n", count); | 1159 | if (config->debugHttpd) |
1160 | fprintf(stderr, "cgi read %d bytes\n", count); | ||
872 | #endif | 1161 | #endif |
873 | } | 1162 | } |
874 | } | 1163 | } |
@@ -876,6 +1165,7 @@ static int sendCgi(int s, const char *url, | |||
876 | } | 1165 | } |
877 | return 0; | 1166 | return 0; |
878 | } | 1167 | } |
1168 | #endif /* CONFIG_FEATURE_HTTPD_CGI */ | ||
879 | 1169 | ||
880 | /**************************************************************************** | 1170 | /**************************************************************************** |
881 | * | 1171 | * |
@@ -883,55 +1173,66 @@ static int sendCgi(int s, const char *url, | |||
883 | * | 1173 | * |
884 | * $Description: Send a file response to an HTTP request | 1174 | * $Description: Send a file response to an HTTP request |
885 | * | 1175 | * |
886 | * $Parameters: | 1176 | * $Parameter: |
887 | * (int) s . . . . . . . The http session socket. | ||
888 | * (const char *) url . . The URL requested. | 1177 | * (const char *) url . . The URL requested. |
1178 | * (char *) buf . . . . . The stack buffer. | ||
889 | * | 1179 | * |
890 | * $Return: (int) . . . . . . Always 0. | 1180 | * $Return: (int) . . . . . . Always 0. |
891 | * | 1181 | * |
892 | ****************************************************************************/ | 1182 | ****************************************************************************/ |
893 | static int sendFile(int s, const char *url) | 1183 | static int sendFile(const char *url, char *buf) |
894 | { | 1184 | { |
895 | char *suffix = strrchr(url,'.'); | 1185 | char * suffix; |
896 | const char *content = "application/octet-stream"; | ||
897 | int f; | 1186 | int f; |
1187 | const char * const * table; | ||
1188 | const char * try_suffix; | ||
898 | 1189 | ||
899 | if (suffix) | 1190 | suffix = strrchr(url, '.'); |
900 | { | 1191 | |
901 | const char ** table; | 1192 | for (table = suffixTable; *table; table += 2) |
902 | for (table = (const char **) &suffixTable[0]; | 1193 | if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) { |
903 | *table && (strstr(*table, suffix) == 0); table+=2); | 1194 | try_suffix += strlen(suffix); |
904 | if (table) content = *(table+1); | 1195 | if(*try_suffix == 0 || *try_suffix == '.') |
1196 | break; | ||
1197 | } | ||
1198 | /* also, if not found, set default as "application/octet-stream"; */ | ||
1199 | config->found_mime_type = *(table+1); | ||
1200 | #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
1201 | if (suffix) { | ||
1202 | Htaccess * cur; | ||
1203 | |||
1204 | for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) { | ||
1205 | if(strcmp(cur->before_colon, suffix) == 0) { | ||
1206 | config->found_mime_type = cur->after_colon; | ||
1207 | break; | ||
1208 | } | ||
1209 | } | ||
905 | } | 1210 | } |
906 | 1211 | #endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */ | |
907 | if (*url == '/') url++; | ||
908 | suffix = strchr(url,'?'); | ||
909 | if (suffix) *suffix = 0; | ||
910 | 1212 | ||
911 | #ifdef DEBUG | 1213 | #ifdef DEBUG |
912 | fprintf(stderr,"Sending file '%s'\n", url); | 1214 | if (config->debugHttpd) |
1215 | fprintf(stderr, "Sending file '%s' Content-type: %s\n", | ||
1216 | url, config->found_mime_type); | ||
913 | #endif | 1217 | #endif |
914 | 1218 | ||
915 | f = open(url,O_RDONLY, 0444); | 1219 | f = open(url, O_RDONLY); |
916 | if (f >= 0) | 1220 | if (f >= 0) { |
917 | { | 1221 | int count; |
918 | char buf[1450]; | 1222 | |
919 | int count; | 1223 | sendHeaders(HTTP_OK); |
920 | sendHeaders(s, HTTP_OK, content, -1, 0 ); | 1224 | while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) { |
921 | while ((count = read(f, buf, sizeof(buf)))) | 1225 | full_write(a_c_w, buf, count); |
922 | { | 1226 | } |
923 | sendBuf(s, buf, count); | 1227 | close(f); |
924 | } | 1228 | } else { |
925 | close(f); | ||
926 | } | ||
927 | else | ||
928 | { | ||
929 | #ifdef DEBUG | 1229 | #ifdef DEBUG |
930 | fprintf(stderr,"Unable to open '%s'\n", url); | 1230 | if (config->debugHttpd) |
1231 | perror_msg("Unable to open '%s'", url); | ||
931 | #endif | 1232 | #endif |
932 | sendHeaders(s, HTTP_NOT_FOUND, "text/html", -1, 0); | 1233 | sendHeaders(HTTP_NOT_FOUND); |
933 | } | 1234 | } |
934 | 1235 | ||
935 | return 0; | 1236 | return 0; |
936 | } | 1237 | } |
937 | 1238 | ||
@@ -941,93 +1242,85 @@ static int sendFile(int s, const char *url) | |||
941 | * | 1242 | * |
942 | * $Description: Check the permission file for access. | 1243 | * $Description: Check the permission file for access. |
943 | * | 1244 | * |
944 | * Both IP addresses as well as url pathnames can be specified. If an IP | ||
945 | * address check is desired, the 'path' should be specified as "ip" and the | ||
946 | * dotted decimal IP address placed in request. | ||
947 | * | ||
948 | * For url pathnames, place the url (with leading /) in 'path' and any | ||
949 | * authentication information in request. e.g. "user:pass" | ||
950 | * | ||
951 | ******* | ||
952 | * | ||
953 | * Keep the algorithm simple. | ||
954 | * If config file isn't present, everything is allowed. | 1245 | * If config file isn't present, everything is allowed. |
955 | * Run down /etc/httpd.hosts a line at a time. | 1246 | * Entries are of the form you can see example from header source |
956 | * Stop if match is found. | ||
957 | * Entries are of the form: | ||
958 | * ip:10.10 # any address that begins with 10.10 | ||
959 | * dir:user:pass # dir security for dirs that start with 'dir' | ||
960 | * | ||
961 | * httpd.conf has the following format: | ||
962 | * ip:10.10. # Allow any address that begins with 10.10. | ||
963 | * ip:172.20. # Allow 172.20.x.x | ||
964 | * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin | ||
965 | * /:foo:bar # Require user foo, pwd bar on urls starting with / | ||
966 | * | ||
967 | * To open up the server: | ||
968 | * ip:* # Allow any IP address | ||
969 | * /:* # no password required for urls starting with / (all) | ||
970 | * | 1247 | * |
971 | * $Parameters: | 1248 | * $Parameters: |
972 | * (const char *) path . . . . The file path or "ip" for ip addresses. | 1249 | * (const char *) path . . . . The file path or NULL for ip addresses. |
973 | * (const char *) request . . . User information to validate. | 1250 | * (const char *) request . . . User information to validate. |
974 | * | 1251 | * |
975 | * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. | 1252 | * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. |
976 | * | 1253 | * |
977 | ****************************************************************************/ | 1254 | ****************************************************************************/ |
1255 | |||
1256 | |||
978 | static int checkPerm(const char *path, const char *request) | 1257 | static int checkPerm(const char *path, const char *request) |
979 | { | 1258 | { |
980 | FILE *f=NULL; | 1259 | Htaccess * cur; |
981 | int rval; | 1260 | const char *p; |
982 | char buf[80]; | ||
983 | char *p; | ||
984 | int ipaddr=0; | ||
985 | |||
986 | /* If httpd.conf not there assume anyone can get in */ | ||
987 | if (configFile) f = fopen(configFile,"r"); | ||
988 | if(f == NULL) f = fopen("/etc/httpd.conf","r"); | ||
989 | if(f == NULL) f = fopen("httpd.conf","r"); | ||
990 | if(f == NULL) { | ||
991 | return(1); | ||
992 | } | ||
993 | if (strcmp("ip",path) == 0) ipaddr=1; | ||
994 | 1261 | ||
995 | rval=0; | 1262 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH |
1263 | int ipaddr = path == NULL; | ||
1264 | const char *prev = NULL; | ||
1265 | #else | ||
1266 | # define ipaddr 1 | ||
1267 | #endif | ||
996 | 1268 | ||
997 | /* This could stand some work */ | 1269 | /* This could stand some work */ |
998 | while ( fgets(buf, 80, f) != NULL) | 1270 | for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) { |
999 | { | 1271 | const char *p0 = cur->before_colon; |
1000 | if(buf[0] == '#') continue; | 1272 | |
1001 | if(buf[0] == '\0') continue; | 1273 | if(*p0 == 'A' || *p0 == 'D') { |
1002 | for(p = buf + (strlen(buf) - 1); p >= buf; p--) | 1274 | if(!ipaddr) |
1003 | { | 1275 | continue; |
1004 | if(isspace(*p)) *p = 0; | 1276 | } else { |
1005 | } | 1277 | if(ipaddr) |
1006 | 1278 | continue; | |
1007 | p = strchr(buf,':'); | 1279 | #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES |
1008 | if (!p) continue; | 1280 | if(*p0 == '.') |
1009 | *p++=0; | 1281 | continue; |
1282 | #endif | ||
1283 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
1284 | if(prev != NULL && strcmp(prev, p0) != 0) | ||
1285 | continue; /* find next identical */ | ||
1286 | #endif | ||
1287 | } | ||
1288 | |||
1289 | p = cur->after_colon; | ||
1010 | #ifdef DEBUG | 1290 | #ifdef DEBUG |
1011 | fprintf(stderr,"checkPerm: '%s' ? '%s'\n",buf,path); | 1291 | if (config->debugHttpd) |
1292 | fprintf(stderr,"checkPerm: '%s' ? '%s'\n", | ||
1293 | (ipaddr ? p : p0), request); | ||
1012 | #endif | 1294 | #endif |
1013 | if((ipaddr ? strcmp(buf,path) : strncmp(buf, path, strlen(buf))) == 0) | 1295 | if(ipaddr) { |
1014 | { | 1296 | if(strncmp(p, request, strlen(p)) != 0) |
1015 | /* match found. Check request */ | 1297 | continue; |
1016 | if ((strcmp("*",p) == 0) || | 1298 | return *p0 == 'A'; /* Allow/Deny */ |
1017 | (strcmp(p, request) == 0) || | ||
1018 | (ipaddr && (strncmp(p, request, strlen(p)) == 0))) | ||
1019 | { | ||
1020 | rval = 1; | ||
1021 | break; | ||
1022 | } | ||
1023 | 1299 | ||
1024 | /* reject on first failure for non ipaddresses */ | ||
1025 | if (!ipaddr) break; | ||
1026 | } | 1300 | } |
1027 | }; | 1301 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH |
1028 | fclose(f); | 1302 | else { |
1029 | return(rval); | 1303 | int l = strlen(p0); |
1030 | }; | 1304 | |
1305 | if(strncmp(p0, path, l) == 0 && | ||
1306 | (l == 1 || path[l] == '/' || path[l] == 0)) { | ||
1307 | /* path match found. Check request */ | ||
1308 | if (strcmp(p, request) == 0) | ||
1309 | return 1; /* Ok */ | ||
1310 | /* unauthorized, but check next /path:user:password */ | ||
1311 | prev = p0; | ||
1312 | } | ||
1313 | } | ||
1314 | #endif | ||
1315 | } /* for */ | ||
1316 | |||
1317 | #ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
1318 | /* if uncofigured, return 1 - access from all */ | ||
1319 | return 1; | ||
1320 | #else | ||
1321 | return prev == NULL; | ||
1322 | #endif | ||
1323 | } | ||
1031 | 1324 | ||
1032 | 1325 | ||
1033 | /**************************************************************************** | 1326 | /**************************************************************************** |
@@ -1036,156 +1329,242 @@ static int checkPerm(const char *path, const char *request) | |||
1036 | * | 1329 | * |
1037 | * $Description: Handle an incoming http request. | 1330 | * $Description: Handle an incoming http request. |
1038 | * | 1331 | * |
1039 | * $Parameters: | ||
1040 | * (s) s . . . . . The http request socket. | ||
1041 | * | ||
1042 | * $Return: (int) . . . Always 0. | ||
1043 | * | ||
1044 | ****************************************************************************/ | 1332 | ****************************************************************************/ |
1045 | static int handleIncoming(int s) | 1333 | static void handleIncoming(void) |
1046 | { | 1334 | { |
1047 | char buf[8192]; | 1335 | char *buf = config->buf; |
1048 | char url[8192]; /* hold args too initially */ | 1336 | char *url; |
1049 | char credentials[80]; | 1337 | char *purl; |
1050 | char request[20]; | 1338 | int blank = -1; |
1051 | long length=0; | ||
1052 | int major; | ||
1053 | int minor; | ||
1054 | char *urlArgs; | 1339 | char *urlArgs; |
1055 | char *body=0; | 1340 | #ifdef CONFIG_FEATURE_HTTPD_CGI |
1341 | const char *prequest = request_GET; | ||
1342 | char *body = 0; | ||
1343 | long length=0; | ||
1344 | char *cookie = 0; | ||
1345 | #endif | ||
1346 | char *test; | ||
1347 | struct stat sb; | ||
1056 | 1348 | ||
1057 | credentials[0] = 0; | 1349 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH |
1058 | do | 1350 | int credentials = -1; /* if not requred this is Ok */ |
1059 | { | ||
1060 | int count = getLine(s, buf, sizeof(buf)); | ||
1061 | int blank; | ||
1062 | if (count <= 0) break; | ||
1063 | count = sscanf(buf, "%9s %1000s HTTP/%d.%d", request, | ||
1064 | url, &major, &minor); | ||
1065 | |||
1066 | if (count < 2) | ||
1067 | { | ||
1068 | /* Garbled request/URL */ | ||
1069 | #if 0 | ||
1070 | genHttpHeader(&requestInfo, | ||
1071 | HTTP_BAD_REQUEST, requestInfo.dataType, | ||
1072 | HTTP_LENGTH_UNKNOWN); | ||
1073 | #endif | 1351 | #endif |
1352 | |||
1353 | do { | ||
1354 | int count; | ||
1355 | |||
1356 | if (getLine(buf) <= 0) | ||
1357 | break; /* closed */ | ||
1358 | |||
1359 | purl = strpbrk(buf, " \t"); | ||
1360 | if(purl == NULL) { | ||
1361 | BAD_REQUEST: | ||
1362 | sendHeaders(HTTP_BAD_REQUEST); | ||
1074 | break; | 1363 | break; |
1075 | } | 1364 | } |
1076 | 1365 | *purl = 0; | |
1077 | /* If no version info, assume 0.9 */ | 1366 | #ifdef CONFIG_FEATURE_HTTPD_CGI |
1078 | if (count != 4) | 1367 | if(strcasecmp(buf, prequest) != 0) { |
1079 | { | 1368 | prequest = "POST"; |
1080 | major = 0; | 1369 | if(strcasecmp(buf, prequest) != 0) { |
1081 | minor = 9; | 1370 | sendHeaders(HTTP_NOT_IMPLEMENTED); |
1371 | break; | ||
1372 | } | ||
1373 | } | ||
1374 | #else | ||
1375 | if(strcasecmp(buf, request_GET) != 0) { | ||
1376 | sendHeaders(HTTP_NOT_IMPLEMENTED); | ||
1377 | break; | ||
1082 | } | 1378 | } |
1379 | #endif | ||
1380 | *purl = ' '; | ||
1381 | count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank); | ||
1083 | 1382 | ||
1383 | if (count < 1 || buf[0] != '/') { | ||
1384 | /* Garbled request/URL */ | ||
1385 | goto BAD_REQUEST; | ||
1386 | } | ||
1387 | url = alloca(strlen(buf) + 12); /* + sizeof("/index.html\0") */ | ||
1388 | if(url == NULL) { | ||
1389 | sendHeaders(HTTP_INTERNAL_SERVER_ERROR); | ||
1390 | break; | ||
1391 | } | ||
1392 | strcpy(url, buf); | ||
1084 | /* extract url args if present */ | 1393 | /* extract url args if present */ |
1085 | urlArgs = strchr(url,'?'); | 1394 | urlArgs = strchr(url, '?'); |
1086 | if (urlArgs) | 1395 | if (urlArgs) { |
1087 | { | 1396 | *urlArgs++ = 0; /* next code can set '/' to this pointer, |
1088 | *urlArgs=0; | 1397 | but CGI script can`t be a directory */ |
1089 | urlArgs++; | 1398 | } |
1399 | |||
1400 | /* algorithm stolen from libbb simplify_path(), | ||
1401 | but don`t strdup and reducing trailing slash */ | ||
1402 | purl = test = url; | ||
1403 | |||
1404 | do { | ||
1405 | if (*purl == '/') { | ||
1406 | if (*test == '/') { /* skip duplicate (or initial) slash */ | ||
1407 | continue; | ||
1408 | } else if (*test == '.') { | ||
1409 | if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */ | ||
1410 | continue; | ||
1411 | } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) { | ||
1412 | ++test; | ||
1413 | if (purl == url) { | ||
1414 | /* protect out root */ | ||
1415 | goto BAD_REQUEST; | ||
1416 | } | ||
1417 | while (*--purl != '/'); /* omit previous dir */ | ||
1418 | continue; | ||
1419 | } | ||
1420 | } | ||
1421 | } | ||
1422 | *++purl = *test; | ||
1423 | } while (*++test); | ||
1424 | |||
1425 | *++purl = 0; /* so keep last character */ | ||
1426 | test = purl; /* end ptr */ | ||
1427 | |||
1428 | /* If URL is directory, adding '/' */ | ||
1429 | if(test[-1] != '/') { | ||
1430 | if ( is_directory(url + 1, 1, &sb) ) { | ||
1431 | *test++ = '/'; | ||
1432 | *test = 0; | ||
1433 | purl = test; /* end ptr */ | ||
1434 | } | ||
1090 | } | 1435 | } |
1091 | |||
1092 | #ifdef DEBUG | 1436 | #ifdef DEBUG |
1093 | if (debugHttpd) fprintf(stderr,"url='%s', args=%s\n", url, urlArgs); | 1437 | if (config->debugHttpd) |
1094 | #endif | 1438 | fprintf(stderr, "url='%s', args=%s\n", url, urlArgs); |
1095 | 1439 | #endif | |
1096 | // read until blank line(s) | 1440 | |
1097 | blank = 0; | 1441 | test = url; |
1098 | while ((count = getLine(s, buf, sizeof(buf))) >= 0) | 1442 | while((test = strchr( test + 1, '/' )) != NULL) { |
1099 | { | 1443 | /* have path1/path2 */ |
1100 | if (count == 0) | 1444 | *test = '\0'; |
1101 | { | 1445 | if( is_directory(url + 1, 1, &sb) ) { |
1102 | if (major > 0) break; | 1446 | /* may be having subdir config */ |
1103 | blank++; | 1447 | parse_conf(url + 1, SUBDIR_PARSE); |
1104 | if (blank == 2) break; | 1448 | } |
1105 | } | 1449 | *test = '/'; |
1450 | } | ||
1451 | |||
1452 | // read until blank line for HTTP version specified, else parse immediate | ||
1453 | while (blank >= 0 && (count = getLine(buf)) > 0) { | ||
1454 | |||
1106 | #ifdef DEBUG | 1455 | #ifdef DEBUG |
1107 | if (debugHttpd) fprintf(stderr,"Header: '%s'\n", buf); | 1456 | if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf); |
1108 | #endif | 1457 | #endif |
1109 | 1458 | ||
1459 | #ifdef CONFIG_FEATURE_HTTPD_CGI | ||
1110 | /* try and do our best to parse more lines */ | 1460 | /* try and do our best to parse more lines */ |
1111 | if ((strncmpi(buf, "Content-length:", 15) == 0)) | 1461 | if ((strncasecmp(buf, Content_length, 15) == 0)) { |
1112 | { | 1462 | if(prequest != request_GET) |
1113 | sscanf(buf, "%*s %ld", &length); | 1463 | length = strtol(buf + 15, 0, 0); // extra read only for POST |
1114 | } | 1464 | } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) { |
1465 | for(test = buf + 7; isspace(*test); test++) | ||
1466 | ; | ||
1467 | cookie = strdup(test); | ||
1468 | } | ||
1469 | #endif | ||
1470 | |||
1115 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | 1471 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH |
1116 | else if (strncmpi(buf, "Authorization:", 14) == 0) | 1472 | if (strncasecmp(buf, "Authorization:", 14) == 0) { |
1117 | { | ||
1118 | /* We only allow Basic credentials. | 1473 | /* We only allow Basic credentials. |
1119 | * It shows up as "Authorization: Basic <userid:password>" where | 1474 | * It shows up as "Authorization: Basic <userid:password>" where |
1120 | * the userid:password is base64 encoded. | 1475 | * the userid:password is base64 encoded. |
1121 | */ | 1476 | */ |
1122 | char *ptr = buf+14; | 1477 | for(test = buf + 14; isspace(*test); test++) |
1123 | while (*ptr == ' ') ptr++; | 1478 | ; |
1124 | if (strncmpi(ptr, "Basic", 5) != 0) break; | 1479 | if (strncasecmp(test, "Basic", 5) != 0) |
1125 | ptr += 5; | 1480 | continue; |
1126 | while (*ptr == ' ') ptr++; | 1481 | |
1127 | memset(credentials, 0, sizeof(credentials)); | 1482 | test += 5; /* decodeBase64() skiping space self */ |
1128 | decodeBase64(credentials, | 1483 | decodeBase64(test); |
1129 | sizeof(credentials)-1, | 1484 | credentials = checkPerm(url, test); |
1130 | ptr, | ||
1131 | strlen(ptr) ); | ||
1132 | |||
1133 | } | 1485 | } |
1486 | #endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ | ||
1487 | |||
1488 | } /* while extra header reading */ | ||
1489 | |||
1490 | |||
1491 | if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || | ||
1492 | checkPerm(NULL, config->rmt_ip) == 0) { | ||
1493 | /* protect listing [/path]/httpd_conf or IP deny */ | ||
1494 | #ifdef CONFIG_FEATURE_HTTPD_CGI | ||
1495 | FORBIDDEN: /* protect listing /cgi-bin */ | ||
1496 | #endif | ||
1497 | sendHeaders(HTTP_FORBIDDEN); | ||
1498 | break; | ||
1134 | } | 1499 | } |
1135 | if (!checkPerm(url, credentials)) | 1500 | |
1136 | { | 1501 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH |
1137 | sendHeaders(s, HTTP_UNAUTHORIZED, 0, -1, 0); | 1502 | if (credentials <= 0 && checkPerm(url, ":") == 0) { |
1138 | length=-1; | 1503 | sendHeaders(HTTP_UNAUTHORIZED); |
1139 | break; /* no more processing */ | 1504 | break; |
1140 | } | ||
1141 | #else | ||
1142 | } | 1505 | } |
1143 | #endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ | 1506 | #endif |
1144 | 1507 | ||
1145 | /* we are done if an error occurred */ | 1508 | test = url + 1; /* skip first '/' */ |
1146 | if (length == -1) break; | ||
1147 | 1509 | ||
1148 | if (strcmp(url,"/") == 0) strcpy(url,"/index.html"); | 1510 | #ifdef CONFIG_FEATURE_HTTPD_CGI |
1511 | /* if strange Content-Length */ | ||
1512 | if (length < 0 || length > MAX_POST_SIZE) | ||
1513 | break; | ||
1149 | 1514 | ||
1150 | if (length>0) | 1515 | if (length > 0) { |
1151 | { | 1516 | body = malloc(length + 1); |
1152 | body=(char*) malloc(length+1); | 1517 | if (body) { |
1153 | if (body) | 1518 | length = full_read(a_c_r, body, length); |
1154 | { | 1519 | if(length < 0) // closed |
1155 | length = read(s,body,length); | 1520 | length = 0; |
1156 | body[length]=0; // always null terminate for safety | 1521 | body[length] = 0; // always null terminate for safety |
1157 | urlArgs=body; | 1522 | urlArgs = body; |
1158 | } | 1523 | } |
1159 | } | 1524 | } |
1160 | 1525 | ||
1161 | if (strstr(url,"..") || strstr(url, "httpd.conf")) | 1526 | if (strncmp(test, "cgi-bin", 7) == 0) { |
1162 | { | 1527 | if(test[7] == 0 || (test[7] == '/' && test[8] == 0)) |
1163 | /* protect from .. path creep */ | 1528 | goto FORBIDDEN; // protect listing cgi-bin |
1164 | sendHeaders(s, HTTP_NOT_FOUND, "text/html", -1, 0); | 1529 | sendCgi(url, prequest, urlArgs, body, length, cookie); |
1165 | } | 1530 | } else { |
1166 | else if (strstr(url,"cgi-bin")) | 1531 | if (prequest != request_GET) |
1167 | { | 1532 | sendHeaders(HTTP_NOT_IMPLEMENTED); |
1168 | sendCgi(s, url, request, urlArgs, body, length); | 1533 | else { |
1169 | } | 1534 | #endif /* CONFIG_FEATURE_HTTPD_CGI */ |
1170 | else if (strncmpi(request,"GET",3) == 0) | 1535 | if(purl[-1] == '/') |
1171 | { | 1536 | strcpy(purl, "index.html"); |
1172 | sendFile(s, url); | 1537 | if ( stat(test, &sb ) == 0 ) { |
1173 | } | 1538 | config->ContentLength = sb.st_size; |
1174 | else | 1539 | config->last_mod = sb.st_mtime; |
1175 | { | 1540 | } |
1176 | sendHeaders(s, HTTP_NOT_IMPLEMENTED, 0, -1, 0); | 1541 | sendFile(test, buf); |
1542 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1543 | /* unset if non inetd looped */ | ||
1544 | config->ContentLength = -1; | ||
1545 | #endif | ||
1546 | |||
1547 | #ifdef CONFIG_FEATURE_HTTPD_CGI | ||
1548 | } | ||
1177 | } | 1549 | } |
1550 | #endif | ||
1551 | |||
1178 | } while (0); | 1552 | } while (0); |
1179 | 1553 | ||
1180 | #ifdef DEBUG | 1554 | |
1181 | if (debugHttpd) fprintf(stderr,"closing socket\n"); | 1555 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY |
1182 | #endif | 1556 | /* from inetd don`t looping: freeing, closing automatic from exit always */ |
1183 | if (body) free(body); | 1557 | # ifdef DEBUG |
1184 | shutdown(s,SHUT_WR); | 1558 | if (config->debugHttpd) fprintf(stderr, "closing socket\n"); |
1185 | shutdown(s,SHUT_RD); | 1559 | # endif |
1186 | close(s); | 1560 | # ifdef CONFIG_FEATURE_HTTPD_CGI |
1187 | 1561 | free(body); | |
1188 | return 0; | 1562 | free(cookie); |
1563 | # endif | ||
1564 | shutdown(a_c_w, SHUT_WR); | ||
1565 | shutdown(a_c_r, SHUT_RD); | ||
1566 | close(config->accepted_socket); | ||
1567 | #endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ | ||
1189 | } | 1568 | } |
1190 | 1569 | ||
1191 | /**************************************************************************** | 1570 | /**************************************************************************** |
@@ -1203,24 +1582,23 @@ static int handleIncoming(int s) | |||
1203 | * $Return: (int) . . . . Always 0. | 1582 | * $Return: (int) . . . . Always 0. |
1204 | * | 1583 | * |
1205 | ****************************************************************************/ | 1584 | ****************************************************************************/ |
1585 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1206 | static int miniHttpd(int server) | 1586 | static int miniHttpd(int server) |
1207 | { | 1587 | { |
1208 | fd_set readfd, portfd; | 1588 | fd_set readfd, portfd; |
1209 | int nfound; | 1589 | int nfound; |
1210 | 1590 | ||
1211 | FD_ZERO(&portfd); | 1591 | FD_ZERO(&portfd); |
1212 | FD_SET(server, &portfd); | 1592 | FD_SET(server, &portfd); |
1213 | 1593 | ||
1214 | /* copy the ports we are watching to the readfd set */ | 1594 | /* copy the ports we are watching to the readfd set */ |
1215 | while (1) | 1595 | while (1) { |
1216 | { | 1596 | readfd = portfd; |
1217 | readfd = portfd ; | 1597 | |
1218 | |||
1219 | /* Now wait INDEFINATELY on the set of sockets! */ | 1598 | /* Now wait INDEFINATELY on the set of sockets! */ |
1220 | nfound = select(server+1, &readfd, 0, 0, 0); | 1599 | nfound = select(server + 1, &readfd, 0, 0, 0); |
1221 | 1600 | ||
1222 | switch (nfound) | 1601 | switch (nfound) { |
1223 | { | ||
1224 | case 0: | 1602 | case 0: |
1225 | /* select timeout error! */ | 1603 | /* select timeout error! */ |
1226 | break ; | 1604 | break ; |
@@ -1228,48 +1606,41 @@ static int miniHttpd(int server) | |||
1228 | /* select error */ | 1606 | /* select error */ |
1229 | break; | 1607 | break; |
1230 | default: | 1608 | default: |
1231 | if (FD_ISSET(server, &readfd)) | 1609 | if (FD_ISSET(server, &readfd)) { |
1232 | { | 1610 | int on; |
1233 | char on; | ||
1234 | struct sockaddr_in fromAddr; | 1611 | struct sockaddr_in fromAddr; |
1235 | char rmt_ip[20]; | 1612 | |
1236 | int addr; | 1613 | unsigned int addr; |
1237 | socklen_t fromAddrLen = sizeof(fromAddr); | 1614 | socklen_t fromAddrLen = sizeof(fromAddr); |
1238 | int s = accept(server, | 1615 | int s = accept(server, |
1239 | (struct sockaddr *)&fromAddr, &fromAddrLen) ; | 1616 | (struct sockaddr *)&fromAddr, &fromAddrLen); |
1240 | if (s < 0) | 1617 | |
1241 | { | 1618 | if (s < 0) { |
1242 | continue; | 1619 | continue; |
1243 | } | 1620 | } |
1621 | config->accepted_socket = s; | ||
1244 | addr = ntohl(fromAddr.sin_addr.s_addr); | 1622 | addr = ntohl(fromAddr.sin_addr.s_addr); |
1245 | sprintf(rmt_ip,"%u.%u.%u.%u", | 1623 | sprintf(config->rmt_ip, "%u.%u.%u.%u", |
1246 | (unsigned char)(addr >> 24), | 1624 | (unsigned char)(addr >> 24), |
1247 | (unsigned char)(addr >> 16), | 1625 | (unsigned char)(addr >> 16), |
1248 | (unsigned char)(addr >> 8), | 1626 | (unsigned char)(addr >> 8), |
1249 | (unsigned char)(addr >> 0)); | 1627 | addr & 0xff); |
1628 | config->port = ntohs(fromAddr.sin_port); | ||
1250 | #ifdef DEBUG | 1629 | #ifdef DEBUG |
1251 | if (debugHttpd) | 1630 | if (config->debugHttpd) { |
1252 | { | 1631 | error_msg("connection from IP=%s, port %u\n", |
1253 | fprintf(stderr,"httpMini.cpp: connection from IP=%s, port %d\n", | 1632 | config->rmt_ip, config->port); |
1254 | rmt_ip, ntohs(fromAddr.sin_port)); | ||
1255 | } | 1633 | } |
1256 | #endif | 1634 | #endif |
1257 | if(checkPerm("ip", rmt_ip) == 0) | ||
1258 | { | ||
1259 | close(s); | ||
1260 | continue; | ||
1261 | } | ||
1262 | |||
1263 | /* set the KEEPALIVE option to cull dead connections */ | 1635 | /* set the KEEPALIVE option to cull dead connections */ |
1264 | on = 1; | 1636 | on = 1; |
1265 | setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, | 1637 | setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on)); |
1266 | sizeof (on)); | ||
1267 | 1638 | ||
1268 | if (fork() == 0) | 1639 | if (config->debugHttpd || fork() == 0) { |
1269 | { | ||
1270 | /* This is the spawned thread */ | 1640 | /* This is the spawned thread */ |
1271 | handleIncoming(s); | 1641 | handleIncoming(); |
1272 | exit(0); | 1642 | if(!config->debugHttpd) |
1643 | exit(0); | ||
1273 | } | 1644 | } |
1274 | close(s); | 1645 | close(s); |
1275 | } | 1646 | } |
@@ -1278,72 +1649,169 @@ static int miniHttpd(int server) | |||
1278 | return 0; | 1649 | return 0; |
1279 | } | 1650 | } |
1280 | 1651 | ||
1652 | #else | ||
1653 | /* from inetd */ | ||
1654 | |||
1655 | static int miniHttpd(void) | ||
1656 | { | ||
1657 | struct sockaddr_in fromAddrLen; | ||
1658 | socklen_t sinlen = sizeof (struct sockaddr_in); | ||
1659 | unsigned int addr; | ||
1660 | |||
1661 | getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen); | ||
1662 | addr = ntohl(fromAddrLen.sin_addr.s_addr); | ||
1663 | sprintf(config->rmt_ip, "%u.%u.%u.%u", | ||
1664 | (unsigned char)(addr >> 24), | ||
1665 | (unsigned char)(addr >> 16), | ||
1666 | (unsigned char)(addr >> 8), | ||
1667 | addr & 0xff); | ||
1668 | config->port = ntohs(fromAddrLen.sin_port); | ||
1669 | handleIncoming(); | ||
1670 | return 0; | ||
1671 | } | ||
1672 | #endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */ | ||
1673 | |||
1674 | #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP | ||
1675 | static void sighup_handler(int sig) | ||
1676 | { | ||
1677 | /* set and reset */ | ||
1678 | struct sigaction sa; | ||
1679 | |||
1680 | sa.sa_handler = sighup_handler; | ||
1681 | sigemptyset(&sa.sa_mask); | ||
1682 | sa.sa_flags = SA_RESTART; | ||
1683 | sigaction(SIGHUP, &sa, NULL); | ||
1684 | parse_conf(default_patch_httpd_conf, | ||
1685 | sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); | ||
1686 | } | ||
1687 | #endif | ||
1688 | |||
1689 | #ifdef HTTPD_STANDALONE | ||
1690 | int main(int argc, char *argv[]) | ||
1691 | #else | ||
1281 | int httpd_main(int argc, char *argv[]) | 1692 | int httpd_main(int argc, char *argv[]) |
1693 | #endif | ||
1282 | { | 1694 | { |
1695 | const char *home_httpd = home; | ||
1696 | |||
1697 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1283 | int server; | 1698 | int server; |
1284 | int port = 80; | 1699 | #endif |
1285 | int c; | 1700 | |
1286 | 1701 | #ifdef CONFIG_FEATURE_HTTPD_SETUID | |
1702 | long uid = -1; | ||
1703 | #endif | ||
1704 | |||
1705 | config = xcalloc(1, sizeof(*config)); | ||
1706 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
1707 | config->realm = "Web Server Authentication"; | ||
1708 | #endif | ||
1709 | |||
1710 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1711 | config->port = 80; | ||
1712 | #endif | ||
1713 | |||
1714 | config->ContentLength = -1; | ||
1715 | |||
1287 | /* check if user supplied a port number */ | 1716 | /* check if user supplied a port number */ |
1288 | for (;;) { | 1717 | for (;;) { |
1289 | c = getopt( argc, argv, "p:ve:d:" | 1718 | int c = getopt( argc, argv, "c:h:" |
1719 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1720 | "p:v" | ||
1721 | #endif | ||
1722 | #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR | ||
1723 | "e:" | ||
1724 | #endif | ||
1725 | #ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR | ||
1726 | "d:" | ||
1727 | #endif | ||
1290 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | 1728 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH |
1291 | "r:c:" | 1729 | "r:" |
1730 | #endif | ||
1731 | #ifdef CONFIG_FEATURE_HTTPD_SETUID | ||
1732 | "u:" | ||
1292 | #endif | 1733 | #endif |
1293 | ); | 1734 | ); |
1294 | if (c == EOF) break; | 1735 | if (c == EOF) break; |
1295 | switch (c) { | 1736 | switch (c) { |
1737 | case 'c': | ||
1738 | config->configFile = optarg; | ||
1739 | break; | ||
1740 | case 'h': | ||
1741 | home_httpd = optarg; | ||
1742 | break; | ||
1743 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1296 | case 'v': | 1744 | case 'v': |
1297 | debugHttpd=1; | 1745 | config->debugHttpd = 1; |
1298 | break; | 1746 | break; |
1299 | case 'p': | 1747 | case 'p': |
1300 | port = atoi(optarg); | 1748 | config->port = atoi(optarg); |
1749 | if(config->port <= 0 || config->port > 0xffff) | ||
1750 | error_msg_and_die("invalid %s for -p", optarg); | ||
1301 | break; | 1751 | break; |
1752 | #endif | ||
1753 | #ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR | ||
1302 | case 'd': | 1754 | case 'd': |
1303 | printf("%s",decodeString(optarg)); | 1755 | printf("%s", decodeString(optarg)); |
1304 | return 0; | 1756 | return 0; |
1757 | #endif | ||
1758 | #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR | ||
1305 | case 'e': | 1759 | case 'e': |
1306 | printf("%s",encodeString(optarg)); | 1760 | printf("%s", encodeString(optarg)); |
1307 | return 0; | 1761 | return 0; |
1762 | #endif | ||
1763 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
1308 | case 'r': | 1764 | case 'r': |
1309 | realm = optarg; | 1765 | config->realm = optarg; |
1310 | break; | 1766 | break; |
1311 | case 'c': | 1767 | #endif |
1312 | configFile = optarg; | 1768 | #ifdef CONFIG_FEATURE_HTTPD_SETUID |
1769 | case 'u': | ||
1770 | { | ||
1771 | char *e; | ||
1772 | |||
1773 | uid = strtol(optarg, &e, 0); | ||
1774 | if(*e != '\0') { | ||
1775 | /* not integer */ | ||
1776 | uid = my_getpwnam(optarg); | ||
1777 | } | ||
1778 | } | ||
1313 | break; | 1779 | break; |
1780 | #endif | ||
1314 | default: | 1781 | default: |
1315 | fprintf(stderr,"%s\n", httpdVersion); | 1782 | error_msg("%s", httpdVersion); |
1316 | show_usage(); | 1783 | show_usage(); |
1317 | exit(1); | ||
1318 | } | 1784 | } |
1319 | } | 1785 | } |
1320 | 1786 | ||
1321 | envp = (char**) malloc((ENVSIZE+1)*sizeof(char*)); | 1787 | if(chdir(home_httpd)) { |
1322 | if (envp == 0) perror_exit("envp alloc"); | 1788 | perror_msg_and_die("can`t chdir to %s", home_httpd); |
1323 | |||
1324 | server = openServer(port); | ||
1325 | if (server < 0) exit(1); | ||
1326 | |||
1327 | if (!debugHttpd) | ||
1328 | { | ||
1329 | /* remember our current pwd, daemonize, chdir back */ | ||
1330 | char *dir = (char *) malloc(256); | ||
1331 | if (dir == 0) perror_exit("out of memory for getpwd"); | ||
1332 | if (getcwd(dir, 256) == 0) perror_exit("getcwd failed"); | ||
1333 | if (daemon(0, 1) < 0) perror_exit("daemon"); | ||
1334 | chdir(dir); | ||
1335 | free(dir); | ||
1336 | } | 1789 | } |
1790 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1791 | server = openServer(); | ||
1792 | # ifdef CONFIG_FEATURE_HTTPD_SETUID | ||
1793 | /* drop privilegies */ | ||
1794 | if(uid > 0) | ||
1795 | setuid(uid); | ||
1796 | # endif | ||
1797 | # ifdef CONFIG_FEATURE_HTTPD_CGI | ||
1798 | addEnvPort("SERVER"); | ||
1799 | # endif | ||
1800 | #endif | ||
1337 | 1801 | ||
1338 | miniHttpd(server); | 1802 | #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP |
1339 | 1803 | sighup_handler(0); | |
1340 | return 0; | 1804 | #else |
1341 | } | 1805 | parse_conf(default_patch_httpd_conf, FIRST_PARSE); |
1342 | 1806 | #endif | |
1343 | #ifdef HTTPD_STANDALONE | ||
1344 | int main(int argc, char *argv[]) | ||
1345 | { | ||
1346 | return httpd_main(argc, argv); | ||
1347 | } | ||
1348 | 1807 | ||
1808 | #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY | ||
1809 | if (!config->debugHttpd) { | ||
1810 | if (daemon(1, 0) < 0) /* don`t change curent directory */ | ||
1811 | perror_msg_and_die("daemon"); | ||
1812 | } | ||
1813 | return miniHttpd(server); | ||
1814 | #else | ||
1815 | return miniHttpd(); | ||
1349 | #endif | 1816 | #endif |
1817 | } | ||