aboutsummaryrefslogtreecommitdiff
path: root/networking/httpd.c
diff options
context:
space:
mode:
authorGlenn L McGrath <bug1@ihug.co.nz>2003-02-09 06:51:14 +0000
committerGlenn L McGrath <bug1@ihug.co.nz>2003-02-09 06:51:14 +0000
commit06e9565b6c365668dafeef1fdc0e60c9a1154623 (patch)
treec75619aad6d3ca6f4c32cd8d1dffbe4f6c53367b /networking/httpd.c
parent877d418b39421361800afe4e1d4ca69b80edc121 (diff)
downloadbusybox-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.c1948
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 "&#60Hello&#32World&#62" 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
44ip:10.10. # Allow any address that begins with 10.10.
45ip:172.20. # Allow 172.20.x.x
46ip: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 * 49A:172.20. # Allow any address that begins with 172.20
51 * To open up the server: 50A:10.10. # Allow any address that begins with 10.10.
52 * ip:* # Allow any IP address 51A:10.10 # Allow any address that previous set and 10.100-109.X.X
53 * /:* # no password required for urls starting with / (all) 52A:127.0.0.1 # Allow local loopback connections
54 * 53D:* # 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: 59A shortes path and D:from[^*] automaticaly sorting to top.
61 * cache httpd.conf 60All longest path can`t reset user:password if shorted protect setted.
62 * support tinylogin 61
63 * 62A/D may be as a/d or allow/deny - first char case unsensitive parsed only.
63
64Each subdir can have config file.
65For protect as user:pass current subdir and subpathes set from subdir config:
66/:user:pass
67if not, other subpathes for give effect must have path from httpd root
68/current_subdir_path_from_httpd_root/subpath:user:pass
69
70The Deny/Allow IP logic:
71
72 1. Allow all:
73The config don`t set D: lines
74
75 2. Allow from setted only:
76see the begin format example
77
78 3. Set deny, allow from other:
79D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
80D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
81A:* # allow from other, this line not strongly require
82
83 A global and subdirs config merging logic:
84allow rules reducing, deny lines strongled.
85 The algorithm combinations:
86
87 4. If current config have
88A:from
89D:*
90 subdir config A: lines skiping, D:from - moving top
91
92 5. If current config have
93D:from
94A:*
95 result config:
96D:from current
97D:from subdir
98A: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
82static const char httpdVersion[] = "busybox httpd/1.13 3-Jan-2003"; 123static const char httpdVersion[] = "busybox httpd/1.20 31-Jan-2003";
124static const char default_patch_httpd_conf[] = "/etc";
125static const char httpd_conf[] = "httpd.conf";
126static 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
94void 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 */
181const char *applet_name = "httpd";
182
183void 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
104int debugHttpd; 204
105static char **envp; 205#define MAX_POST_SIZE (64*1024) /* 64k. Its Small? May be ;) */
106static int envCount; 206
107static char *realm = "Web Server Authentication"; 207#define MAX_MEMORY_BUFF 8192 /* IO buffer */
108static char *configFile; 208
209typedef struct HT_ACCESS {
210 char *after_colon;
211 struct HT_ACCESS *next;
212 char before_colon[1]; /* really bigger, must last */
213} Htaccess;
214
215typedef 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
247static HttpdConfig *config;
248
249static const char request_GET[] = "GET"; /* size algorithic optimize */
109 250
110static const char* const suffixTable [] = { 251static 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
119typedef enum 273typedef 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
153static const HttpEnumString httpResponseNames[] = { 307static 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
333static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
334static 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 */
346static 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
409static 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 ****************************************************************************/
271static void addEnv(const char *name, const char *value) 665static 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 */
687static 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)
302static void addEnvCgi(const char *pargs) 715static 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
337static 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 ****************************************************************************/
384static size_t decodeBase64(void *outData, size_t outDataLen, 759static 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 ****************************************************************************/
447static 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))
472static 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 ****************************************************************************/
506static int openServer(int port) 807static 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 */
548static 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 ****************************************************************************/
576static int sendHeaders(int s, HttpResponseNum responseNum , 856static 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 ****************************************************************************/
657static int getLine(int s, char *buf, int maxBuf) 923static 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 ****************************************************************************/
697static int sendCgi(int s, const char *url, 964static 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 ****************************************************************************/
893static int sendFile(int s, const char *url) 1183static 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
978static int checkPerm(const char *path, const char *request) 1257static 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 ****************************************************************************/
1045static int handleIncoming(int s) 1333static 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) {
1361BAD_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
1495FORBIDDEN: /* 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
1206static int miniHttpd(int server) 1586static 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
1655static 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
1675static 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
1690int main(int argc, char *argv[])
1691#else
1281int httpd_main(int argc, char *argv[]) 1692int 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
1344int 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}