diff options
-rw-r--r-- | include/applets.h | 3 | ||||
-rw-r--r-- | include/usage.h | 15 | ||||
-rw-r--r-- | networking/Config.in | 14 | ||||
-rw-r--r-- | networking/Makefile.in | 1 | ||||
-rw-r--r-- | networking/httpd.c | 1349 |
5 files changed, 1381 insertions, 1 deletions
diff --git a/include/applets.h b/include/applets.h index 5d8e7bb68..177e82352 100644 --- a/include/applets.h +++ b/include/applets.h | |||
@@ -236,6 +236,9 @@ | |||
236 | #ifdef CONFIG_HOSTNAME | 236 | #ifdef CONFIG_HOSTNAME |
237 | APPLET(hostname, hostname_main, _BB_DIR_BIN, _BB_SUID_NEVER) | 237 | APPLET(hostname, hostname_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
238 | #endif | 238 | #endif |
239 | #ifdef CONFIG_HTTPD | ||
240 | APPLET(httpd, httpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER) | ||
241 | #endif | ||
239 | #ifdef CONFIG_HUSH | 242 | #ifdef CONFIG_HUSH |
240 | APPLET_NOUSAGE("hush", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER) | 243 | APPLET_NOUSAGE("hush", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
241 | #endif | 244 | #endif |
diff --git a/include/usage.h b/include/usage.h index b5687115d..dfcc89626 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -775,7 +775,20 @@ | |||
775 | #define hostname_example_usage \ | 775 | #define hostname_example_usage \ |
776 | "$ hostname\n" \ | 776 | "$ hostname\n" \ |
777 | "sage \n" | 777 | "sage \n" |
778 | 778 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | |
779 | #define USAGE_HTTPD_BASIC_AUTH(a) a | ||
780 | #else | ||
781 | #define USAGE_HTTPD_BASIC_AUTH(a) | ||
782 | #endif | ||
783 | #define httpd_trivial_usage \ | ||
784 | "[-p <port>] [-d/-e <string>]" USAGE_HTTPD_BASIC_AUTH(" [-c <conf file>] [-r <realm>]") | ||
785 | #define httpd_full_usage \ | ||
786 | "Listens for incoming http server requests.\n"\ | ||
787 | "Options:\n" \ | ||
788 | "\t-p PORT\tServer port (default 80).\n" \ | ||
789 | USAGE_HTTPD_BASIC_AUTH("\t-c FILE\tSpecifies configuration file. (default httpd.conf)\n\t-r REALM\tAuthentication Realm for Basic Authentication\n") \ | ||
790 | "\t-e STRING\tHtml encode STRING\n" \ | ||
791 | "\t-d STRING\tURL decode STRING\n" | ||
779 | #define hwclock_trivial_usage \ | 792 | #define hwclock_trivial_usage \ |
780 | "[-r|--show] [-s|--hctosys] [-w|--systohc] [-l|--localtime] [-u|--utc]" | 793 | "[-r|--show] [-s|--hctosys] [-w|--systohc] [-l|--localtime] [-u|--utc]" |
781 | #define hwclock_full_usage \ | 794 | #define hwclock_full_usage \ |
diff --git a/networking/Config.in b/networking/Config.in index b622b65f3..ecd3e570a 100644 --- a/networking/Config.in +++ b/networking/Config.in | |||
@@ -29,6 +29,20 @@ config CONFIG_HOSTNAME | |||
29 | help | 29 | help |
30 | Please submit a patch to add help text for this item. | 30 | Please submit a patch to add help text for this item. |
31 | 31 | ||
32 | config CONFIG_HTTPD | ||
33 | bool "httpd" | ||
34 | default n | ||
35 | help | ||
36 | Serve web pages via an HTTP server. | ||
37 | |||
38 | config CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
39 | bool " Enable Basic Authentication and IP address checking" | ||
40 | default n | ||
41 | depends on CONFIG_HTTPD | ||
42 | help | ||
43 | Utilizes /etc/httpd.conf for security settings allowing | ||
44 | ip address filtering and basic authentication on a per url basis. | ||
45 | |||
32 | config CONFIG_IFCONFIG | 46 | config CONFIG_IFCONFIG |
33 | bool "ifconfig" | 47 | bool "ifconfig" |
34 | default n | 48 | default n |
diff --git a/networking/Makefile.in b/networking/Makefile.in index c2ae451a4..7e9a6fdd2 100644 --- a/networking/Makefile.in +++ b/networking/Makefile.in | |||
@@ -26,6 +26,7 @@ NETWORKING-y:= | |||
26 | NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o | 26 | NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o |
27 | NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o | 27 | NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o |
28 | NETWORKING-$(CONFIG_HOSTNAME) += hostname.o | 28 | NETWORKING-$(CONFIG_HOSTNAME) += hostname.o |
29 | NETWORKING-$(CONFIG_HTTPD) += httpd.o | ||
29 | NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o | 30 | NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o |
30 | NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o | 31 | NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o |
31 | NETWORKING-$(CONFIG_IP) += ip.o | 32 | NETWORKING-$(CONFIG_IP) += ip.o |
diff --git a/networking/httpd.c b/networking/httpd.c new file mode 100644 index 000000000..bceb89ba3 --- /dev/null +++ b/networking/httpd.c | |||
@@ -0,0 +1,1349 @@ | |||
1 | /* | ||
2 | * httpd implementation for busybox | ||
3 | * | ||
4 | * Copyright (C) 2002 Glenn Engel <glenne@engel.org> | ||
5 | * | ||
6 | * | ||
7 | * 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 | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | ***************************************************************************** | ||
22 | * | ||
23 | * Typical usage: | ||
24 | * cd /var/www | ||
25 | * httpd | ||
26 | * This is equivalent to | ||
27 | * cd /var/www | ||
28 | * httpd -p 80 -c /etc/httpd.conf -r "Web Server Authentication" | ||
29 | * | ||
30 | * 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 | ||
32 | * 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 | ||
34 | * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set | ||
35 | * the $CGI_foo environment variable to "Hello World". | ||
36 | * | ||
37 | * The server can also be invoked as a url arg decoder and html text encoder | ||
38 | * as follows: | ||
39 | * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" | ||
40 | * bar=`httpd -e "<Hello World>"` # encode as "<Hello World>" | ||
41 | * | ||
42 | * 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 | |||
50 | * | ||
51 | * To open up the server: | ||
52 | * ip:* # Allow any IP address | ||
53 | * /:* # no password required for urls starting with / (all) | ||
54 | * | ||
55 | * Processing of the file stops on the first sucessful match. If the file | ||
56 | * is not found, the server is assumed to be wide open. | ||
57 | * | ||
58 | ***************************************************************************** | ||
59 | * | ||
60 | * Desired enhancements: | ||
61 | * cache httpd.conf | ||
62 | * support tinylogin | ||
63 | * | ||
64 | */ | ||
65 | #include <stdio.h> | ||
66 | #include <ctype.h> /* for isspace */ | ||
67 | #include <stdarg.h> /* for varargs */ | ||
68 | #include <string.h> /* for strerror */ | ||
69 | #include <stdlib.h> /* for malloc */ | ||
70 | #include <time.h> | ||
71 | #include <errno.h> | ||
72 | #include <unistd.h> /* for close */ | ||
73 | #include <signal.h> | ||
74 | #include <sys/types.h> | ||
75 | #include <sys/socket.h> /* for connect and socket*/ | ||
76 | #include <netinet/in.h> /* for sockaddr_in */ | ||
77 | #include <sys/types.h> | ||
78 | #include <sys/stat.h> | ||
79 | #include <sys/wait.h> | ||
80 | #include <fcntl.h> | ||
81 | |||
82 | static const char httpdVersion[] = "busybox httpd/1.13 3-Jan-2003"; | ||
83 | |||
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 | ||
89 | // if something bad happens due to a malformed user request. | ||
90 | // As a result, all memory allocation is checked rigorously | ||
91 | #else | ||
92 | /* standalone */ | ||
93 | #define CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
94 | void show_usage() | ||
95 | { | ||
96 | fprintf(stderr,"Usage: httpd [-p <port>] [-c configFile] [-d/-e <string>] [-r realm]\n"); | ||
97 | } | ||
98 | #endif | ||
99 | |||
100 | /* minimal global vars for busybox */ | ||
101 | #ifndef ENVSIZE | ||
102 | #define ENVSIZE 50 | ||
103 | #endif | ||
104 | int debugHttpd; | ||
105 | static char **envp; | ||
106 | static int envCount; | ||
107 | static char *realm = "Web Server Authentication"; | ||
108 | static char *configFile; | ||
109 | |||
110 | static const char* const suffixTable [] = { | ||
111 | ".htm.html", "text/html", | ||
112 | ".jpg.jpeg", "image/jpeg", | ||
113 | ".gif", "image/gif", | ||
114 | ".png", "image/png", | ||
115 | ".txt.h.c.cc.cpp", "text/plain", | ||
116 | 0,0 | ||
117 | }; | ||
118 | |||
119 | typedef enum | ||
120 | { | ||
121 | HTTP_OK = 200, | ||
122 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ | ||
123 | HTTP_NOT_FOUND = 404, | ||
124 | 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 */ | ||
128 | HTTP_CONTINUE = 100, | ||
129 | HTTP_SWITCHING_PROTOCOLS = 101, | ||
130 | HTTP_CREATED = 201, | ||
131 | HTTP_ACCEPTED = 202, | ||
132 | HTTP_NON_AUTHORITATIVE_INFO = 203, | ||
133 | HTTP_NO_CONTENT = 204, | ||
134 | HTTP_MULTIPLE_CHOICES = 300, | ||
135 | HTTP_MOVED_PERMANENTLY = 301, | ||
136 | HTTP_MOVED_TEMPORARILY = 302, | ||
137 | HTTP_NOT_MODIFIED = 304, | ||
138 | HTTP_PAYMENT_REQUIRED = 402, | ||
139 | HTTP_FORBIDDEN = 403, | ||
140 | HTTP_BAD_GATEWAY = 502, | ||
141 | HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ | ||
142 | HTTP_RESPONSE_SETSIZE=0xffffffff | ||
143 | #endif | ||
144 | } HttpResponseNum; | ||
145 | |||
146 | typedef struct | ||
147 | { | ||
148 | HttpResponseNum type; | ||
149 | const char *name; | ||
150 | const char *info; | ||
151 | } HttpEnumString; | ||
152 | |||
153 | static const HttpEnumString httpResponseNames[] = { | ||
154 | { HTTP_OK, "OK" }, | ||
155 | { HTTP_NOT_IMPLEMENTED, "Not Implemented", | ||
156 | "The requested method is not recognized by this server." }, | ||
157 | { HTTP_UNAUTHORIZED, "Unauthorized", "" }, | ||
158 | { HTTP_NOT_FOUND, "Not Found", | ||
159 | "The requested URL was not found on this server." }, | ||
160 | { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error" | ||
161 | "Internal Server Error" }, | ||
162 | { HTTP_BAD_REQUEST, "Bad Request" , | ||
163 | "Unsupported method.\n" }, | ||
164 | #if 0 | ||
165 | { HTTP_CREATED, "Created" }, | ||
166 | { HTTP_ACCEPTED, "Accepted" }, | ||
167 | { HTTP_NO_CONTENT, "No Content" }, | ||
168 | { HTTP_MULTIPLE_CHOICES, "Multiple Choices" }, | ||
169 | { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, | ||
170 | { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" }, | ||
171 | { HTTP_NOT_MODIFIED, "Not Modified" }, | ||
172 | { HTTP_FORBIDDEN, "Forbidden", "" }, | ||
173 | { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, | ||
174 | { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, | ||
175 | #endif | ||
176 | }; | ||
177 | |||
178 | /**************************************************************************** | ||
179 | * | ||
180 | > $Function: encodeString() | ||
181 | * | ||
182 | * $Description: Given a string, html encode special characters. | ||
183 | * This is used for the -e command line option to provide an easy way | ||
184 | * for scripts to encode result data without confusing browsers. The | ||
185 | * returned string pointer is memory allocated by malloc(). | ||
186 | * | ||
187 | * $Parameters: | ||
188 | * (const char *) string . . The first string to encode. | ||
189 | * | ||
190 | * $Return: (char *) . . . .. . . A pointer to the encoded string. | ||
191 | * | ||
192 | * $Errors: Returns a null string ("") if memory is not available. | ||
193 | * | ||
194 | ****************************************************************************/ | ||
195 | static char *encodeString(const char *string) | ||
196 | { | ||
197 | /* take the simple route and encode everything */ | ||
198 | /* could possibly scan once to get length. */ | ||
199 | int len = strlen(string); | ||
200 | char *out = (char*)malloc(len*5 +1); | ||
201 | char *p=out; | ||
202 | char ch; | ||
203 | if (!out) return ""; | ||
204 | while ((ch = *string++)) | ||
205 | { | ||
206 | // very simple check for what to encode | ||
207 | if (isalnum(ch)) *p++ = ch; | ||
208 | else p += sprintf(p,"&#%d", (unsigned char) ch); | ||
209 | } | ||
210 | *p=0; | ||
211 | return out; | ||
212 | } | ||
213 | |||
214 | /**************************************************************************** | ||
215 | * | ||
216 | > $Function: decodeString() | ||
217 | * | ||
218 | * $Description: Given a URL encoded string, convert it to plain ascii. | ||
219 | * Since decoding always makes strings smaller, the decode is done in-place. | ||
220 | * Thus, callers should strdup() the argument if they do not want the | ||
221 | * argument modified. The return is the original pointer, allowing this | ||
222 | * function to be easily used as arguments to other functions. | ||
223 | * | ||
224 | * $Parameters: | ||
225 | * (char *) string . . . The first string to decode. | ||
226 | * | ||
227 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). | ||
228 | * | ||
229 | * $Errors: None | ||
230 | * | ||
231 | ****************************************************************************/ | ||
232 | static char *decodeString(char *string) | ||
233 | { | ||
234 | /* note that decoded string is always shorter than original */ | ||
235 | char *orig = string; | ||
236 | char *ptr = string; | ||
237 | while (*ptr) | ||
238 | { | ||
239 | if (*ptr == '+') { *string++ = ' '; ptr++; } | ||
240 | else if (*ptr != '%') *string++ = *ptr++; | ||
241 | else | ||
242 | { | ||
243 | unsigned int value; | ||
244 | sscanf(ptr+1,"%2X",&value); | ||
245 | *string++ = value; | ||
246 | ptr += 3; | ||
247 | } | ||
248 | } | ||
249 | *string = '\0'; | ||
250 | return orig; | ||
251 | } | ||
252 | |||
253 | |||
254 | /**************************************************************************** | ||
255 | * | ||
256 | > $Function: addEnv() | ||
257 | * | ||
258 | * $Description: Add an enviornment variable setting to the global list. | ||
259 | * A NAME=VALUE string is allocated, filled, and added to the list of | ||
260 | * environment settings passed to the cgi execution script. | ||
261 | * | ||
262 | * $Parameters: | ||
263 | * (char *) name . . . The environment variable name. | ||
264 | * (char *) value . . The value to which the env variable is set. | ||
265 | * | ||
266 | * $Return: (void) | ||
267 | * | ||
268 | * $Errors: Silently returns if the env runs out of space to hold the new item | ||
269 | * | ||
270 | ****************************************************************************/ | ||
271 | static void addEnv(const char *name, const char *value) | ||
272 | { | ||
273 | char *s; | ||
274 | if (envCount >= ENVSIZE) return; | ||
275 | if (!value) value = ""; | ||
276 | s=(char*)malloc(strlen(name)+strlen(value)+2); | ||
277 | if (s) | ||
278 | { | ||
279 | sprintf(s,"%s=%s",name, value); | ||
280 | envp[envCount++]=s; | ||
281 | envp[envCount]=0; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /**************************************************************************** | ||
286 | * | ||
287 | > $Function: addEnvCgi | ||
288 | * | ||
289 | * $Description: Create environment variables given a URL encoded arg list. | ||
290 | * For each variable setting the URL encoded arg list, create a corresponding | ||
291 | * environment variable. URL encoded arguments have the form | ||
292 | * name1=value1&name2=value2&name3=value3 | ||
293 | * | ||
294 | * $Parameters: | ||
295 | * (char *) pargs . . . . A pointer to the URL encoded arguments. | ||
296 | * | ||
297 | * $Return: None | ||
298 | * | ||
299 | * $Errors: None | ||
300 | * | ||
301 | ****************************************************************************/ | ||
302 | static void addEnvCgi(const char *pargs) | ||
303 | { | ||
304 | char *args; | ||
305 | if (pargs==0) return; | ||
306 | |||
307 | /* args are a list of name=value&name2=value2 sequences */ | ||
308 | args = strdup(pargs); | ||
309 | while (args && *args) | ||
310 | { | ||
311 | char *sep; | ||
312 | char *name=args; | ||
313 | char *value=strchr(args,'='); | ||
314 | char *cginame; | ||
315 | if (!value) break; | ||
316 | *value++=0; | ||
317 | sep=strchr(value,'&'); | ||
318 | if (sep) | ||
319 | { | ||
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 | } | ||
334 | } | ||
335 | |||
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 | |||
364 | /**************************************************************************** | ||
365 | * | ||
366 | > $Function: decodeBase64() | ||
367 | * | ||
368 | > $Description: Decode a base 64 data stream as per rfc1521. | ||
369 | * Note that the rfc states that none base64 chars are to be ignored. | ||
370 | * 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. | ||
372 | * | ||
373 | * $Parameters: | ||
374 | * (void *) outData. . . Where to place the decoded data. | ||
375 | * (size_t) outDataLen . The length of the output data string. | ||
376 | * (void *) inData . . . A pointer to a base64 encoded string. | ||
377 | * (size_t) inDataLen . The length of the input data string. | ||
378 | * | ||
379 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). | ||
380 | * | ||
381 | * $Errors: None | ||
382 | * | ||
383 | ****************************************************************************/ | ||
384 | static size_t decodeBase64(void *outData, size_t outDataLen, | ||
385 | void *inData, size_t inDataLen) | ||
386 | { | ||
387 | int i = 0; | ||
388 | unsigned char *in = inData; | ||
389 | unsigned char *out = outData; | ||
390 | unsigned long ch = 0; | ||
391 | while (inDataLen && outDataLen) | ||
392 | { | ||
393 | unsigned char conv = 0; | ||
394 | unsigned char newch; | ||
395 | |||
396 | while (inDataLen) | ||
397 | { | ||
398 | inDataLen--; | ||
399 | newch = *in++; | ||
400 | if ((newch < '0') || (newch > 'z')) continue; | ||
401 | conv = base64ToBin[newch - 32]; | ||
402 | if (conv == 255) continue; | ||
403 | break; | ||
404 | } | ||
405 | ch = (ch << 6) | conv; | ||
406 | i++; | ||
407 | if (i== 4) | ||
408 | { | ||
409 | if (outDataLen >= 3) | ||
410 | { | ||
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; | ||
418 | } | ||
419 | |||
420 | if ((inDataLen == 0) && (i != 0)) | ||
421 | { | ||
422 | /* error - non multiple of 4 chars on input */ | ||
423 | break; | ||
424 | } | ||
425 | |||
426 | } | ||
427 | |||
428 | /* return the actual number of chars in output array */ | ||
429 | return out-(unsigned char*) outData; | ||
430 | } | ||
431 | #endif | ||
432 | |||
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 | |||
492 | /**************************************************************************** | ||
493 | * | ||
494 | > $Function: openServer() | ||
495 | * | ||
496 | * $Description: create a listen server socket on the designated port. | ||
497 | * | ||
498 | * $Parameters: | ||
499 | * (int) port . . . The port to listen on for connections. | ||
500 | * | ||
501 | * $Return: (int) . . . A connection socket. -1 for errors. | ||
502 | * | ||
503 | * $Errors: None | ||
504 | * | ||
505 | ****************************************************************************/ | ||
506 | static int openServer(int port) | ||
507 | { | ||
508 | struct sockaddr_in lsocket; | ||
509 | int fd; | ||
510 | |||
511 | /* create the socket right now */ | ||
512 | /* inet_addr() returns a value that is already in network order */ | ||
513 | memset(&lsocket, 0, sizeof(lsocket)); | ||
514 | lsocket.sin_family = AF_INET; | ||
515 | lsocket.sin_addr.s_addr = INADDR_ANY; | ||
516 | lsocket.sin_port = htons(port) ; | ||
517 | fd = socket(AF_INET, SOCK_STREAM, 0); | ||
518 | if (fd >= 0) | ||
519 | { | ||
520 | /* 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. */ | ||
522 | int one = 1; | ||
523 | #ifdef SO_REUSEPORT | ||
524 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char*)&one, sizeof(one)) ; | ||
525 | #else | ||
526 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) ; | ||
527 | #endif | ||
528 | if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) | ||
529 | { | ||
530 | listen(fd, 9); | ||
531 | signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */ | ||
532 | } | ||
533 | else | ||
534 | { | ||
535 | perror("failure to bind to server port"); | ||
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; | ||
546 | } | ||
547 | |||
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 | |||
554 | /**************************************************************************** | ||
555 | * | ||
556 | > $Function: sendHeaders() | ||
557 | * | ||
558 | * $Description: Create and send HTTP response headers. | ||
559 | * 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 | ||
561 | * second packet is delayed for any reason. If contentType is null the | ||
562 | * content type is assumed to be text/html | ||
563 | * | ||
564 | * $Parameters: | ||
565 | * (int) s . . . The http socket. | ||
566 | * (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 | * | ||
571 | * $Return: (int) . . . . Always 0 | ||
572 | * | ||
573 | * $Errors: None | ||
574 | * | ||
575 | ****************************************************************************/ | ||
576 | static int sendHeaders(int s, HttpResponseNum responseNum , | ||
577 | const char *contentType, | ||
578 | int contentLength, time_t expire) | ||
579 | { | ||
580 | char buf[1200]; | ||
581 | const char *responseString = ""; | ||
582 | const char *infoString = 0; | ||
583 | unsigned int i; | ||
584 | time_t timer = time(0); | ||
585 | char timeStr[80]; | ||
586 | for (i=0; | ||
587 | i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) | ||
588 | { | ||
589 | if (httpResponseNames[i].type != responseNum) continue; | ||
590 | responseString = httpResponseNames[i].name; | ||
591 | infoString = httpResponseNames[i].info; | ||
592 | break; | ||
593 | } | ||
594 | if (infoString || !contentType) | ||
595 | { | ||
596 | contentType = "text/html"; | ||
597 | } | ||
598 | |||
599 | sprintf(buf, "HTTP/1.0 %d %s\nContent-type: %s\r\n", | ||
600 | responseNum, responseString, contentType); | ||
601 | |||
602 | /* emit the current date */ | ||
603 | strftime(timeStr, sizeof(timeStr), | ||
604 | "%a, %d %b %Y %H:%M:%S GMT",gmtime(&timer)); | ||
605 | sprintf(buf+strlen(buf), "Date: %s\r\n", timeStr); | ||
606 | sprintf(buf+strlen(buf), "Connection: close\r\n"); | ||
607 | if (expire) | ||
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 | |||
614 | if (responseNum == HTTP_UNAUTHORIZED) | ||
615 | { | ||
616 | sprintf(buf+strlen(buf), | ||
617 | "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm); | ||
618 | } | ||
619 | if (contentLength != -1) | ||
620 | { | ||
621 | int len = strlen(buf); | ||
622 | sprintf(buf+len,"Content-length: %d\r\n", contentLength); | ||
623 | } | ||
624 | strcat(buf,"\r\n"); | ||
625 | if (infoString) | ||
626 | { | ||
627 | sprintf(buf+strlen(buf), | ||
628 | "<HEAD><TITLE>%d %s</TITLE></HEAD>\n" | ||
629 | "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n", | ||
630 | responseNum, responseString, | ||
631 | responseNum, responseString, | ||
632 | infoString); | ||
633 | } | ||
634 | #ifdef DEBUG | ||
635 | if (debugHttpd) fprintf(stderr,"Headers:'%s'", buf); | ||
636 | #endif | ||
637 | sendBuf(s, buf,-1); | ||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | /**************************************************************************** | ||
642 | * | ||
643 | > $Function: getLine() | ||
644 | * | ||
645 | * $Description: Read from the socket until an end of line char found. | ||
646 | * | ||
647 | * Characters are read one at a time until an eol sequence is found. | ||
648 | * | ||
649 | * $Parameters: | ||
650 | * (int) s . . . . . The socket fildes. | ||
651 | * (char *) buf . . Where to place the read result. | ||
652 | * (int) maxBuf . . Maximum number of chars to fit in buf. | ||
653 | * | ||
654 | * $Return: (int) . . . . number of characters read. -1 if error. | ||
655 | * | ||
656 | ****************************************************************************/ | ||
657 | static int getLine(int s, char *buf, int maxBuf) | ||
658 | { | ||
659 | int count = 0; | ||
660 | while (recv(s, buf+count, 1, 0) == 1) | ||
661 | { | ||
662 | if (buf[count] == '\r') continue; | ||
663 | if (buf[count] == '\n') | ||
664 | { | ||
665 | buf[count] = 0; | ||
666 | return count; | ||
667 | } | ||
668 | count++; | ||
669 | } | ||
670 | if (count) return count; | ||
671 | else return -1; | ||
672 | } | ||
673 | |||
674 | /**************************************************************************** | ||
675 | * | ||
676 | > $Function: sendCgi() | ||
677 | * | ||
678 | * $Description: Execute a CGI script and send it's stdout back | ||
679 | * | ||
680 | * Environment variables are set up and the script is invoked with pipes | ||
681 | * for stdin/stdout. If a post is being done the script is fed the POST | ||
682 | * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). | ||
683 | * | ||
684 | * $Parameters: | ||
685 | * (int ) s . . . . . . . . The session socket. | ||
686 | * (const char *) url . . . The requested URL (with leading /). | ||
687 | * (const char *urlArgs). . Any URL arguments. | ||
688 | * (const char *body) . . . POST body contents. | ||
689 | * (int bodyLen) . . . . . Length of the post body. | ||
690 | |||
691 | * | ||
692 | * $Return: (char *) . . . . A pointer to the decoded string (same as input). | ||
693 | * | ||
694 | * $Errors: None | ||
695 | * | ||
696 | ****************************************************************************/ | ||
697 | static int sendCgi(int s, const char *url, | ||
698 | const char *request, const char *urlArgs, | ||
699 | const char *body, int bodyLen) | ||
700 | { | ||
701 | int fromCgi[2]; /* pipe for reading data from CGI */ | ||
702 | int toCgi[2]; /* pipe for sending data to CGI */ | ||
703 | |||
704 | char *argp[] = { 0, 0 }; | ||
705 | int pid=0; | ||
706 | int inFd=inFd; | ||
707 | int outFd; | ||
708 | int firstLine=1; | ||
709 | |||
710 | do | ||
711 | { | ||
712 | if (pipe(fromCgi) != 0) | ||
713 | { | ||
714 | break; | ||
715 | } | ||
716 | if (pipe(toCgi) != 0) | ||
717 | { | ||
718 | break; | ||
719 | } | ||
720 | |||
721 | pid = fork(); | ||
722 | if (pid < 0) | ||
723 | { | ||
724 | pid = 0; | ||
725 | break;; | ||
726 | } | ||
727 | |||
728 | if (!pid) | ||
729 | { | ||
730 | /* child process */ | ||
731 | char *script; | ||
732 | char *directory; | ||
733 | inFd=toCgi[0]; | ||
734 | outFd=fromCgi[1]; | ||
735 | |||
736 | dup2(inFd, 0); // replace stdin with the pipe | ||
737 | dup2(outFd, 1); // replace stdout with the pipe | ||
738 | if (!debugHttpd) dup2(outFd, 2); // replace stderr with the pipe | ||
739 | close(toCgi[0]); | ||
740 | close(toCgi[1]); | ||
741 | close(fromCgi[0]); | ||
742 | close(fromCgi[1]); | ||
743 | |||
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 | /* | ||
763 | * Most HTTP servers chdir to the cgi directory. | ||
764 | */ | ||
765 | while (*url == '/') url++; // skip leading slash(s) | ||
766 | directory = strdup( url ); | ||
767 | if ( directory == (char*) 0 ) | ||
768 | script = (char*) (url); /* ignore errors */ | ||
769 | else | ||
770 | { | ||
771 | script = strrchr( directory, '/' ); | ||
772 | if ( script == (char*) 0 ) | ||
773 | script = directory; | ||
774 | else | ||
775 | { | ||
776 | *script++ = '\0'; | ||
777 | (void) chdir( directory ); /* ignore errors */ | ||
778 | } | ||
779 | } | ||
780 | // now run the program. If it fails, use _exit() so no destructors | ||
781 | // get called and make a mess. | ||
782 | execve(script, argp, envp); | ||
783 | |||
784 | #ifdef DEBUG | ||
785 | fprintf(stderr, "exec failed\n"); | ||
786 | #endif | ||
787 | close(2); | ||
788 | close(1); | ||
789 | close(0); | ||
790 | _exit(242); | ||
791 | } /* end child */ | ||
792 | |||
793 | /* parent process */ | ||
794 | inFd=fromCgi[0]; | ||
795 | outFd=toCgi[1]; | ||
796 | close(fromCgi[1]); | ||
797 | close(toCgi[0]); | ||
798 | if (body) write(outFd, body, bodyLen); | ||
799 | close(outFd); | ||
800 | |||
801 | } while (0); | ||
802 | |||
803 | if (pid) | ||
804 | { | ||
805 | int status; | ||
806 | pid_t dead_pid; | ||
807 | |||
808 | while (1) | ||
809 | { | ||
810 | struct timeval timeout; | ||
811 | fd_set readSet; | ||
812 | char buf[160]; | ||
813 | int nfound; | ||
814 | int count; | ||
815 | |||
816 | FD_ZERO(&readSet); | ||
817 | FD_SET(inFd, &readSet); | ||
818 | |||
819 | /* Now wait on the set of sockets! */ | ||
820 | timeout.tv_sec = 0; | ||
821 | timeout.tv_usec = 10000; | ||
822 | nfound = select(inFd+1, &readSet, 0, 0, &timeout); | ||
823 | |||
824 | if (nfound <= 0) | ||
825 | { | ||
826 | dead_pid = waitpid(pid, &status, WNOHANG); | ||
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 | ||
834 | if (debugHttpd) | ||
835 | { | ||
836 | if (WIFEXITED(status)) | ||
837 | fprintf(stderr,"piped has exited with status=%d\n", WEXITSTATUS(status)); | ||
838 | if (WIFSIGNALED(status)) | ||
839 | fprintf(stderr,"piped has exited with signal=%d\n", WTERMSIG(status)); | ||
840 | } | ||
841 | #endif | ||
842 | pid = -1; | ||
843 | break; | ||
844 | } | ||
845 | } | ||
846 | else | ||
847 | { | ||
848 | // There is something to read | ||
849 | count = read(inFd,buf,sizeof(buf)-1); | ||
850 | // If a read returns 0 at this point then some type of error has | ||
851 | // occurred. Bail now. | ||
852 | if (count == 0) break; | ||
853 | if (count > 0) | ||
854 | { | ||
855 | if (firstLine) | ||
856 | { | ||
857 | /* check to see if the user script added headers */ | ||
858 | if (strcmp(buf,"HTTP")!= 0) | ||
859 | { | ||
860 | write(s,"HTTP/1.0 200 OK\n", 16); | ||
861 | } | ||
862 | if (strstr(buf,"ontent-") == 0) | ||
863 | { | ||
864 | write(s,"Content-type: text/plain\n\n", 26); | ||
865 | } | ||
866 | |||
867 | firstLine=0; | ||
868 | } | ||
869 | write(s,buf,count); | ||
870 | #ifdef DEBUG | ||
871 | if (debugHttpd) fprintf(stderr,"cgi read %d bytes\n", count); | ||
872 | #endif | ||
873 | } | ||
874 | } | ||
875 | } | ||
876 | } | ||
877 | return 0; | ||
878 | } | ||
879 | |||
880 | /**************************************************************************** | ||
881 | * | ||
882 | > $Function: sendFile() | ||
883 | * | ||
884 | * $Description: Send a file response to an HTTP request | ||
885 | * | ||
886 | * $Parameters: | ||
887 | * (int) s . . . . . . . The http session socket. | ||
888 | * (const char *) url . . The URL requested. | ||
889 | * | ||
890 | * $Return: (int) . . . . . . Always 0. | ||
891 | * | ||
892 | ****************************************************************************/ | ||
893 | static int sendFile(int s, const char *url) | ||
894 | { | ||
895 | char *suffix = strrchr(url,'.'); | ||
896 | const char *content = "application/octet-stream"; | ||
897 | int f; | ||
898 | |||
899 | if (suffix) | ||
900 | { | ||
901 | const char ** table; | ||
902 | for (table = (const char **) &suffixTable[0]; | ||
903 | *table && (strstr(*table, suffix) == 0); table+=2); | ||
904 | if (table) content = *(table+1); | ||
905 | } | ||
906 | |||
907 | if (*url == '/') url++; | ||
908 | suffix = strchr(url,'?'); | ||
909 | if (suffix) *suffix = 0; | ||
910 | |||
911 | #ifdef DEBUG | ||
912 | fprintf(stderr,"Sending file '%s'\n", url); | ||
913 | #endif | ||
914 | |||
915 | f = open(url,O_RDONLY, 0444); | ||
916 | if (f >= 0) | ||
917 | { | ||
918 | char buf[1450]; | ||
919 | int count; | ||
920 | sendHeaders(s, HTTP_OK, content, -1, 0 ); | ||
921 | while ((count = read(f, buf, sizeof(buf)))) | ||
922 | { | ||
923 | sendBuf(s, buf, count); | ||
924 | } | ||
925 | close(f); | ||
926 | } | ||
927 | else | ||
928 | { | ||
929 | #ifdef DEBUG | ||
930 | fprintf(stderr,"Unable to open '%s'\n", url); | ||
931 | #endif | ||
932 | sendHeaders(s, HTTP_NOT_FOUND, "text/html", -1, 0); | ||
933 | } | ||
934 | |||
935 | return 0; | ||
936 | } | ||
937 | |||
938 | /**************************************************************************** | ||
939 | * | ||
940 | > $Function: checkPerm() | ||
941 | * | ||
942 | * $Description: Check the permission file for access. | ||
943 | * | ||
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. | ||
955 | * Run down /etc/httpd.hosts a line at a time. | ||
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 | * | ||
971 | * $Parameters: | ||
972 | * (const char *) path . . . . The file path or "ip" for ip addresses. | ||
973 | * (const char *) request . . . User information to validate. | ||
974 | * | ||
975 | * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. | ||
976 | * | ||
977 | ****************************************************************************/ | ||
978 | static int checkPerm(const char *path, const char *request) | ||
979 | { | ||
980 | FILE *f=NULL; | ||
981 | int rval; | ||
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 | |||
995 | rval=0; | ||
996 | |||
997 | /* This could stand some work */ | ||
998 | while ( fgets(buf, 80, f) != NULL) | ||
999 | { | ||
1000 | if(buf[0] == '#') continue; | ||
1001 | if(buf[0] == '\0') continue; | ||
1002 | for(p = buf + (strlen(buf) - 1); p >= buf; p--) | ||
1003 | { | ||
1004 | if(isspace(*p)) *p = 0; | ||
1005 | } | ||
1006 | |||
1007 | p = strchr(buf,':'); | ||
1008 | if (!p) continue; | ||
1009 | *p++=0; | ||
1010 | #ifdef DEBUG | ||
1011 | fprintf(stderr,"checkPerm: '%s' ? '%s'\n",buf,path); | ||
1012 | #endif | ||
1013 | if((ipaddr ? strcmp(buf,path) : strncmp(buf, path, strlen(buf))) == 0) | ||
1014 | { | ||
1015 | /* match found. Check request */ | ||
1016 | if ((strcmp("*",p) == 0) || | ||
1017 | (strcmp(p, request) == 0) || | ||
1018 | (ipaddr && (strncmp(p, request, strlen(p)) == 0))) | ||
1019 | { | ||
1020 | rval = 1; | ||
1021 | break; | ||
1022 | } | ||
1023 | |||
1024 | /* reject on first failure for non ipaddresses */ | ||
1025 | if (!ipaddr) break; | ||
1026 | } | ||
1027 | }; | ||
1028 | fclose(f); | ||
1029 | return(rval); | ||
1030 | }; | ||
1031 | |||
1032 | |||
1033 | /**************************************************************************** | ||
1034 | * | ||
1035 | > $Function: handleIncoming() | ||
1036 | * | ||
1037 | * $Description: Handle an incoming http request. | ||
1038 | * | ||
1039 | * $Parameters: | ||
1040 | * (s) s . . . . . The http request socket. | ||
1041 | * | ||
1042 | * $Return: (int) . . . Always 0. | ||
1043 | * | ||
1044 | ****************************************************************************/ | ||
1045 | static int handleIncoming(int s) | ||
1046 | { | ||
1047 | char buf[8192]; | ||
1048 | char url[8192]; /* hold args too initially */ | ||
1049 | char credentials[80]; | ||
1050 | char request[20]; | ||
1051 | long length=0; | ||
1052 | int major; | ||
1053 | int minor; | ||
1054 | char *urlArgs; | ||
1055 | char *body=0; | ||
1056 | |||
1057 | credentials[0] = 0; | ||
1058 | do | ||
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 | ||
1074 | break; | ||
1075 | } | ||
1076 | |||
1077 | /* If no version info, assume 0.9 */ | ||
1078 | if (count != 4) | ||
1079 | { | ||
1080 | major = 0; | ||
1081 | minor = 9; | ||
1082 | } | ||
1083 | |||
1084 | /* extract url args if present */ | ||
1085 | urlArgs = strchr(url,'?'); | ||
1086 | if (urlArgs) | ||
1087 | { | ||
1088 | *urlArgs=0; | ||
1089 | urlArgs++; | ||
1090 | } | ||
1091 | |||
1092 | #ifdef DEBUG | ||
1093 | if (debugHttpd) fprintf(stderr,"url='%s', args=%s\n", url, urlArgs); | ||
1094 | #endif | ||
1095 | |||
1096 | // read until blank line(s) | ||
1097 | blank = 0; | ||
1098 | while ((count = getLine(s, buf, sizeof(buf))) >= 0) | ||
1099 | { | ||
1100 | if (count == 0) | ||
1101 | { | ||
1102 | if (major > 0) break; | ||
1103 | blank++; | ||
1104 | if (blank == 2) break; | ||
1105 | } | ||
1106 | #ifdef DEBUG | ||
1107 | if (debugHttpd) fprintf(stderr,"Header: '%s'\n", buf); | ||
1108 | #endif | ||
1109 | |||
1110 | /* try and do our best to parse more lines */ | ||
1111 | if ((strncmpi(buf, "Content-length:", 15) == 0)) | ||
1112 | { | ||
1113 | sscanf(buf, "%*s %ld", &length); | ||
1114 | } | ||
1115 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
1116 | else if (strncmpi(buf, "Authorization:", 14) == 0) | ||
1117 | { | ||
1118 | /* We only allow Basic credentials. | ||
1119 | * It shows up as "Authorization: Basic <userid:password>" where | ||
1120 | * the userid:password is base64 encoded. | ||
1121 | */ | ||
1122 | char *ptr = buf+14; | ||
1123 | while (*ptr == ' ') ptr++; | ||
1124 | if (strncmpi(ptr, "Basic", 5) != 0) break; | ||
1125 | ptr += 5; | ||
1126 | while (*ptr == ' ') ptr++; | ||
1127 | memset(credentials, 0, sizeof(credentials)); | ||
1128 | decodeBase64(credentials, | ||
1129 | sizeof(credentials)-1, | ||
1130 | ptr, | ||
1131 | strlen(ptr) ); | ||
1132 | |||
1133 | } | ||
1134 | } | ||
1135 | if (!checkPerm(url, credentials)) | ||
1136 | { | ||
1137 | sendHeaders(s, HTTP_UNAUTHORIZED, 0, -1, 0); | ||
1138 | length=-1; | ||
1139 | break; /* no more processing */ | ||
1140 | } | ||
1141 | #else | ||
1142 | } | ||
1143 | #endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ | ||
1144 | |||
1145 | /* we are done if an error occurred */ | ||
1146 | if (length == -1) break; | ||
1147 | |||
1148 | if (strcmp(url,"/") == 0) strcpy(url,"/index.html"); | ||
1149 | |||
1150 | if (length>0) | ||
1151 | { | ||
1152 | body=(char*) malloc(length+1); | ||
1153 | if (body) | ||
1154 | { | ||
1155 | length = read(s,body,length); | ||
1156 | body[length]=0; // always null terminate for safety | ||
1157 | urlArgs=body; | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | if (strstr(url,"..") || strstr(url, "httpd.conf")) | ||
1162 | { | ||
1163 | /* protect from .. path creep */ | ||
1164 | sendHeaders(s, HTTP_NOT_FOUND, "text/html", -1, 0); | ||
1165 | } | ||
1166 | else if (strstr(url,"cgi-bin")) | ||
1167 | { | ||
1168 | sendCgi(s, url, request, urlArgs, body, length); | ||
1169 | } | ||
1170 | else if (strncmpi(request,"GET",3) == 0) | ||
1171 | { | ||
1172 | sendFile(s, url); | ||
1173 | } | ||
1174 | else | ||
1175 | { | ||
1176 | sendHeaders(s, HTTP_NOT_IMPLEMENTED, 0, -1, 0); | ||
1177 | } | ||
1178 | } while (0); | ||
1179 | |||
1180 | #ifdef DEBUG | ||
1181 | if (debugHttpd) fprintf(stderr,"closing socket\n"); | ||
1182 | #endif | ||
1183 | if (body) free(body); | ||
1184 | shutdown(s,SHUT_WR); | ||
1185 | shutdown(s,SHUT_RD); | ||
1186 | close(s); | ||
1187 | |||
1188 | return 0; | ||
1189 | } | ||
1190 | |||
1191 | /**************************************************************************** | ||
1192 | * | ||
1193 | > $Function: miniHttpd() | ||
1194 | * | ||
1195 | * $Description: The main http server function. | ||
1196 | * | ||
1197 | * Given an open socket fildes, listen for new connections and farm out | ||
1198 | * the processing as a forked process. | ||
1199 | * | ||
1200 | * $Parameters: | ||
1201 | * (int) server. . . The server socket fildes. | ||
1202 | * | ||
1203 | * $Return: (int) . . . . Always 0. | ||
1204 | * | ||
1205 | ****************************************************************************/ | ||
1206 | static int miniHttpd(int server) | ||
1207 | { | ||
1208 | fd_set readfd, portfd; | ||
1209 | int nfound; | ||
1210 | |||
1211 | FD_ZERO(&portfd); | ||
1212 | FD_SET(server, &portfd); | ||
1213 | |||
1214 | /* copy the ports we are watching to the readfd set */ | ||
1215 | while (1) | ||
1216 | { | ||
1217 | readfd = portfd ; | ||
1218 | |||
1219 | /* Now wait INDEFINATELY on the set of sockets! */ | ||
1220 | nfound = select(server+1, &readfd, 0, 0, 0); | ||
1221 | |||
1222 | switch (nfound) | ||
1223 | { | ||
1224 | case 0: | ||
1225 | /* select timeout error! */ | ||
1226 | break ; | ||
1227 | case -1: | ||
1228 | /* select error */ | ||
1229 | break; | ||
1230 | default: | ||
1231 | if (FD_ISSET(server, &readfd)) | ||
1232 | { | ||
1233 | char on; | ||
1234 | struct sockaddr_in fromAddr; | ||
1235 | char rmt_ip[20]; | ||
1236 | int addr; | ||
1237 | socklen_t fromAddrLen = sizeof(fromAddr); | ||
1238 | int s = accept(server, | ||
1239 | (struct sockaddr *)&fromAddr, &fromAddrLen) ; | ||
1240 | if (s < 0) | ||
1241 | { | ||
1242 | continue; | ||
1243 | } | ||
1244 | addr = ntohl(fromAddr.sin_addr.s_addr); | ||
1245 | sprintf(rmt_ip,"%u.%u.%u.%u", | ||
1246 | (unsigned char)(addr >> 24), | ||
1247 | (unsigned char)(addr >> 16), | ||
1248 | (unsigned char)(addr >> 8), | ||
1249 | (unsigned char)(addr >> 0)); | ||
1250 | #ifdef DEBUG | ||
1251 | if (debugHttpd) | ||
1252 | { | ||
1253 | fprintf(stderr,"httpMini.cpp: connection from IP=%s, port %d\n", | ||
1254 | rmt_ip, ntohs(fromAddr.sin_port)); | ||
1255 | } | ||
1256 | #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 */ | ||
1264 | on = 1; | ||
1265 | setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, | ||
1266 | sizeof (on)); | ||
1267 | |||
1268 | if (fork() == 0) | ||
1269 | { | ||
1270 | /* This is the spawned thread */ | ||
1271 | handleIncoming(s); | ||
1272 | exit(0); | ||
1273 | } | ||
1274 | close(s); | ||
1275 | } | ||
1276 | } | ||
1277 | } // while (1) | ||
1278 | return 0; | ||
1279 | } | ||
1280 | |||
1281 | int httpd_main(int argc, char *argv[]) | ||
1282 | { | ||
1283 | int server; | ||
1284 | int port = 80; | ||
1285 | int c; | ||
1286 | |||
1287 | /* check if user supplied a port number */ | ||
1288 | for (;;) { | ||
1289 | c = getopt( argc, argv, "p:ve:d:" | ||
1290 | #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH | ||
1291 | "r:c:" | ||
1292 | #endif | ||
1293 | ); | ||
1294 | if (c == EOF) break; | ||
1295 | switch (c) { | ||
1296 | case 'v': | ||
1297 | debugHttpd=1; | ||
1298 | break; | ||
1299 | case 'p': | ||
1300 | port = atoi(optarg); | ||
1301 | break; | ||
1302 | case 'd': | ||
1303 | printf("%s",decodeString(optarg)); | ||
1304 | return 0; | ||
1305 | case 'e': | ||
1306 | printf("%s",encodeString(optarg)); | ||
1307 | return 0; | ||
1308 | case 'r': | ||
1309 | realm = optarg; | ||
1310 | break; | ||
1311 | case 'c': | ||
1312 | configFile = optarg; | ||
1313 | break; | ||
1314 | default: | ||
1315 | fprintf(stderr,"%s\n", httpdVersion); | ||
1316 | show_usage(); | ||
1317 | exit(1); | ||
1318 | } | ||
1319 | } | ||
1320 | |||
1321 | envp = (char**) malloc((ENVSIZE+1)*sizeof(char*)); | ||
1322 | if (envp == 0) perror_exit("envp alloc"); | ||
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 | } | ||
1337 | |||
1338 | miniHttpd(server); | ||
1339 | |||
1340 | return 0; | ||
1341 | } | ||
1342 | |||
1343 | #ifdef HTTPD_STANDALONE | ||
1344 | int main(int argc, char *argv[]) | ||
1345 | { | ||
1346 | return httpd_main(argc, argv); | ||
1347 | } | ||
1348 | |||
1349 | #endif | ||