blob: 08b40e014fdebd83cbfce2329b19204e1aac1783 [file] [log] [blame]
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +00001/* vi: set sw=4 ts=4: */
Glenn L McGrath58c708a2003-01-05 04:01:56 +00002/*
3 * httpd implementation for busybox
4 *
Glenn L McGrath06e95652003-02-09 06:51:14 +00005 * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
"Vladimir N. Oleynik"79af7d52006-01-26 10:58:12 +00006 * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
Glenn L McGrath58c708a2003-01-05 04:01:56 +00007 *
Glenn L McGrath06e95652003-02-09 06:51:14 +00008 * simplify patch stolen from libbb without using strdup
Glenn L McGrath58c708a2003-01-05 04:01:56 +00009 *
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +000010 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Glenn L McGrath58c708a2003-01-05 04:01:56 +000011 *
12 *****************************************************************************
13 *
Glenn L McGrath06e95652003-02-09 06:51:14 +000014 * Typical usage:
15 * for non root user
16 * httpd -p 8080 -h $HOME/public_html
17 * or for daemon start from rc script with uid=0:
18 * httpd -u www
19 * This is equivalent if www user have uid=80 to
20 * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
21 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +000022 *
23 * When a url contains "cgi-bin" it is assumed to be a cgi script. The
24 * server changes directory to the location of the script and executes it
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +000025 * after setting QUERY_STRING and other environment variables.
Glenn L McGrath58c708a2003-01-05 04:01:56 +000026 *
27 * The server can also be invoked as a url arg decoder and html text encoder
28 * as follows:
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000029 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
Glenn L McGrathc9163fe2003-05-13 16:20:11 +000030 * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000031 * Note that url encoding for arguments is not the same as html encoding for
Eric Andersenaff114c2004-04-14 17:51:38 +000032 * presentation. -d decodes a url-encoded argument while -e encodes in html
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000033 * for page display.
Glenn L McGrath58c708a2003-01-05 04:01:56 +000034 *
35 * httpd.conf has the following format:
Eric Andersenc7bda1c2004-03-15 08:29:22 +000036 *
Glenn L McGrathb65422c2003-09-08 10:59:27 +000037 * A:172.20. # Allow address from 172.20.0.0/16
38 * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
39 * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000040 * A:127.0.0.1 # Allow local loopback connections
41 * D:* # Deny from other IP connections
42 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
43 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
44 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
45 * .au:audio/basic # additional mime type for audio.au files
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +000046 * *.php:/path/php # running cgi.php scripts through an interpreter
Eric Andersenc7bda1c2004-03-15 08:29:22 +000047 *
Eric Andersenaff114c2004-04-14 17:51:38 +000048 * A/D may be as a/d or allow/deny - first char case insensitive
Glenn L McGrath393183d2003-05-26 14:07:50 +000049 * Deny IP rules take precedence over allow rules.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000050 *
51 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000052 * The Deny/Allow IP logic:
Eric Andersenc7bda1c2004-03-15 08:29:22 +000053 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000054 * - Default is to allow all. No addresses are denied unless
Eric Andersen97a1de12004-08-26 22:22:50 +000055 * denied with a D: rule.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000056 * - Order of Deny/Allow rules is significant
57 * - Deny rules take precedence over allow rules.
58 * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
Eric Andersen97a1de12004-08-26 22:22:50 +000059 * addresses.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000060 * - Specification of Allow all (A:*) is a no-op
Eric Andersenc7bda1c2004-03-15 08:29:22 +000061 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000062 * Example:
63 * 1. Allow only specified addresses
Glenn L McGrathb65422c2003-09-08 10:59:27 +000064 * A:172.20 # Allow any address that begins with 172.20.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000065 * A:10.10. # Allow any address that begins with 10.10.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000066 * A:127.0.0.1 # Allow local loopback connections
67 * D:* # Deny from other IP connections
Eric Andersenc7bda1c2004-03-15 08:29:22 +000068 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000069 * 2. Only deny specified addresses
70 * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
71 * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
72 * A:* # (optional line added for clarity)
Eric Andersenc7bda1c2004-03-15 08:29:22 +000073 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000074 * If a sub directory contains a config file it is parsed and merged with
Glenn L McGrathb65422c2003-09-08 10:59:27 +000075 * any existing settings as if it was appended to the original configuration.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000076 *
77 * subdir paths are relative to the containing subdir and thus cannot
78 * affect the parent rules.
79 *
80 * Note that since the sub dir is parsed in the forked thread servicing the
81 * subdir http request, any merge is discarded when the process exits. As a
82 * result, the subdir settings only have a lifetime of a single request.
83 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +000084 *
85 * If -c is not set, an attempt will be made to open the default
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000086 * root configuration file. If -c is set and the file is not found, the
87 * server exits with an error.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000088 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000089*/
Glenn L McGrath58c708a2003-01-05 04:01:56 +000090
Glenn L McGrath06e95652003-02-09 06:51:14 +000091
Glenn L McGrath06e95652003-02-09 06:51:14 +000092#include "busybox.h"
Glenn L McGrath58c708a2003-01-05 04:01:56 +000093
Glenn L McGrath58c708a2003-01-05 04:01:56 +000094
Eric Andersen07f2fea2004-10-08 08:03:29 +000095static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
Glenn L McGrathc9163fe2003-05-13 16:20:11 +000096static const char default_path_httpd_conf[] = "/etc";
Glenn L McGrath06e95652003-02-09 06:51:14 +000097static const char httpd_conf[] = "httpd.conf";
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000098static const char home[] = "./";
Glenn L McGrath06e95652003-02-09 06:51:14 +000099
Eric Andersen07f2fea2004-10-08 08:03:29 +0000100#define TIMEOUT 60
101
Eric Andersenaff114c2004-04-14 17:51:38 +0000102// Note: busybox xfuncs are not used because we want the server to keep running
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000103// if something bad happens due to a malformed user request.
Glenn L McGrath06e95652003-02-09 06:51:14 +0000104// As a result, all memory allocation after daemonize
105// is checked rigorously
106
107//#define DEBUG 1
108
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +0000109#ifndef DEBUG
110# define DEBUG 0
111#endif
112
Glenn L McGrath06e95652003-02-09 06:51:14 +0000113#define MAX_MEMORY_BUFF 8192 /* IO buffer */
114
115typedef struct HT_ACCESS {
116 char *after_colon;
117 struct HT_ACCESS *next;
118 char before_colon[1]; /* really bigger, must last */
119} Htaccess;
120
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000121typedef struct HT_ACCESS_IP {
122 unsigned int ip;
123 unsigned int mask;
124 int allow_deny;
125 struct HT_ACCESS_IP *next;
126} Htaccess_IP;
127
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000128typedef struct {
129 char buf[MAX_MEMORY_BUFF];
Glenn L McGrath06e95652003-02-09 06:51:14 +0000130
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000131 USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
132 USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000133
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000134 const char *query;
Eric Andersen07f2fea2004-10-08 08:03:29 +0000135
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000136 USE_FEATURE_HTTPD_CGI(char *referer;)
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000137
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000138 const char *configFile;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000139
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000140 unsigned int rmt_ip;
Denis Vlasenko55a99402006-09-30 20:41:44 +0000141#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000142 char rmt_ip_str[16]; /* for set env REMOTE_ADDR */
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000143#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000144 unsigned port; /* server initial port and for
145 set env REMOTE_PORT */
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +0000146 const char *found_mime_type;
147 const char *found_moved_temporarily;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000148
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000149 off_t ContentLength; /* -1 - unknown */
150 time_t last_mod;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000151
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000152 Htaccess_IP *ip_a_d; /* config allow/deny lines */
153 int flg_deny_all;
Denis Vlasenko55a99402006-09-30 20:41:44 +0000154#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000155 Htaccess *auth; /* config user:password lines */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000156#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000157#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000158 Htaccess *mime_a; /* config mime types */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000159#endif
160
Denis Vlasenko9f609292006-11-05 19:47:33 +0000161 int server_socket;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000162 int accepted_socket;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000163 volatile int alarm_signaled;
Eric Andersen07f2fea2004-10-08 08:03:29 +0000164
Denis Vlasenko55a99402006-09-30 20:41:44 +0000165#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000166 Htaccess *script_i; /* config script interpreters */
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +0000167#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000168} HttpdConfig;
169
170static HttpdConfig *config;
171
Mike Frysingerfa6c4842006-05-26 01:48:17 +0000172static const char request_GET[] = "GET"; /* size algorithmic optimize */
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000173
174static const char* const suffixTable [] = {
Eric Andersenaff114c2004-04-14 17:51:38 +0000175/* Warning: shorted equivalent suffix in one line must be first */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000176 ".htm.html", "text/html",
177 ".jpg.jpeg", "image/jpeg",
178 ".gif", "image/gif",
179 ".png", "image/png",
180 ".txt.h.c.cc.cpp", "text/plain",
181 ".css", "text/css",
182 ".wav", "audio/wav",
183 ".avi", "video/x-msvideo",
184 ".qt.mov", "video/quicktime",
185 ".mpe.mpeg", "video/mpeg",
186 ".mid.midi", "audio/midi",
187 ".mp3", "audio/mpeg",
Glenn L McGrath06e95652003-02-09 06:51:14 +0000188#if 0 /* unpopular */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000189 ".au", "audio/basic",
190 ".pac", "application/x-ns-proxy-autoconfig",
191 ".vrml.wrl", "model/vrml",
Glenn L McGrath06e95652003-02-09 06:51:14 +0000192#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000193 0, "application/octet-stream" /* default */
194};
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000195
Denis Vlasenko55a99402006-09-30 20:41:44 +0000196typedef enum {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000197 HTTP_OK = 200,
198 HTTP_MOVED_TEMPORARILY = 302,
199 HTTP_BAD_REQUEST = 400, /* malformed syntax */
200 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
201 HTTP_NOT_FOUND = 404,
202 HTTP_FORBIDDEN = 403,
203 HTTP_REQUEST_TIMEOUT = 408,
204 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
205 HTTP_INTERNAL_SERVER_ERROR = 500,
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000206#if 0 /* future use */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000207 HTTP_CONTINUE = 100,
208 HTTP_SWITCHING_PROTOCOLS = 101,
209 HTTP_CREATED = 201,
210 HTTP_ACCEPTED = 202,
211 HTTP_NON_AUTHORITATIVE_INFO = 203,
212 HTTP_NO_CONTENT = 204,
213 HTTP_MULTIPLE_CHOICES = 300,
214 HTTP_MOVED_PERMANENTLY = 301,
215 HTTP_NOT_MODIFIED = 304,
216 HTTP_PAYMENT_REQUIRED = 402,
217 HTTP_BAD_GATEWAY = 502,
218 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
219 HTTP_RESPONSE_SETSIZE = 0xffffffff
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000220#endif
221} HttpResponseNum;
222
Denis Vlasenko55a99402006-09-30 20:41:44 +0000223typedef struct {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000224 HttpResponseNum type;
225 const char *name;
226 const char *info;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000227} HttpEnumString;
228
229static const HttpEnumString httpResponseNames[] = {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000230 { HTTP_OK, "OK", NULL },
231 { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
232 { HTTP_REQUEST_TIMEOUT, "Request Timeout",
233 "No request appeared within a reasonable time period." },
234 { HTTP_NOT_IMPLEMENTED, "Not Implemented",
235 "The requested method is not recognized by this server." },
Denis Vlasenko55a99402006-09-30 20:41:44 +0000236#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000237 { HTTP_UNAUTHORIZED, "Unauthorized", "" },
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000238#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000239 { HTTP_NOT_FOUND, "Not Found",
240 "The requested URL was not found on this server." },
241 { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
242 { HTTP_FORBIDDEN, "Forbidden", "" },
243 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
244 "Internal Server Error" },
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000245#if 0 /* not implemented */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000246 { HTTP_CREATED, "Created" },
247 { HTTP_ACCEPTED, "Accepted" },
248 { HTTP_NO_CONTENT, "No Content" },
249 { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
250 { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
251 { HTTP_NOT_MODIFIED, "Not Modified" },
252 { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
253 { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000254#endif
255};
256
Glenn L McGrath06e95652003-02-09 06:51:14 +0000257
258static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
Denis Vlasenko0bb993f2006-11-21 00:06:28 +0000259
260
261#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000262
263
Denis Vlasenko55a99402006-09-30 20:41:44 +0000264static int scan_ip(const char **ep, unsigned int *ip, unsigned char endc)
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000265{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000266 const char *p = *ep;
267 int auto_mask = 8;
268 int j;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000269
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000270 *ip = 0;
271 for (j = 0; j < 4; j++) {
272 unsigned int octet;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000273
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000274 if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
275 return -auto_mask;
276 octet = 0;
277 while (*p >= '0' && *p <= '9') {
278 octet *= 10;
279 octet += *p - '0';
280 if (octet > 255)
281 return -auto_mask;
282 p++;
283 }
284 if (*p == '.')
285 p++;
286 if (*p != '/' && *p != 0)
287 auto_mask += 8;
288 *ip = ((*ip) << 8) | octet;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000289 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000290 if (*p != 0) {
291 if (*p != endc)
292 return -auto_mask;
293 p++;
294 if (*p == 0)
295 return -auto_mask;
296 }
297 *ep = p;
298 return auto_mask;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000299}
300
Denis Vlasenko55a99402006-09-30 20:41:44 +0000301static int scan_ip_mask(const char *ipm, unsigned int *ip, unsigned int *mask)
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000302{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000303 int i;
304 unsigned int msk;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000305
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000306 i = scan_ip(&ipm, ip, '/');
307 if (i < 0)
308 return i;
309 if (*ipm) {
310 const char *p = ipm;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000311
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000312 i = 0;
313 while (*p) {
Denis Vlasenko92758142006-10-03 19:56:34 +0000314 if (*p < '0' || *p > '9') {
315 if (*p == '.') {
316 i = scan_ip(&ipm, mask, 0);
317 return i != 32;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000318 }
Denis Vlasenko92758142006-10-03 19:56:34 +0000319 return -1;
320 }
321 i *= 10;
322 i += *p - '0';
323 p++;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000324 }
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000325 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000326 if (i > 32 || i < 0)
Denis Vlasenko92758142006-10-03 19:56:34 +0000327 return -1;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000328 msk = 0x80000000;
329 *mask = 0;
330 while (i > 0) {
331 *mask |= msk;
332 msk >>= 1;
333 i--;
334 }
335 return 0;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000336}
337
Denis Vlasenko55a99402006-09-30 20:41:44 +0000338#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000339static void free_config_lines(Htaccess **pprev)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000340{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000341 Htaccess *prev = *pprev;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000342
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000343 while (prev) {
344 Htaccess *cur = prev;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000345
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000346 prev = cur->next;
347 free(cur);
348 }
349 *pprev = NULL;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000350}
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000351#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000352
Glenn L McGrath06e95652003-02-09 06:51:14 +0000353/* flag */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000354#define FIRST_PARSE 0
355#define SUBDIR_PARSE 1
356#define SIGNALED_PARSE 2
357#define FIND_FROM_HTTPD_ROOT 3
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +0000358/****************************************************************************
359 *
360 > $Function: parse_conf()
361 *
362 * $Description: parse configuration file into in-memory linked list.
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000363 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +0000364 * The first non-white character is examined to determine if the config line
365 * is one of the following:
366 * .ext:mime/type # new mime type not compiled into httpd
367 * [adAD]:from # ip address allow/deny, * for wildcard
368 * /path:user:pass # username/password
369 *
370 * Any previous IP rules are discarded.
371 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
372 * are also discarded. That is, previous settings are retained if flag is
373 * SUBDIR_PARSE.
374 *
375 * $Parameters:
376 * (const char *) path . . null for ip address checks, path for password
377 * checks.
378 * (int) flag . . . . . . the source of the parse request.
379 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000380 * $Return: (None)
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +0000381 *
382 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000383static void parse_conf(const char *path, int flag)
384{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000385 FILE *f;
Denis Vlasenko55a99402006-09-30 20:41:44 +0000386#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000387 Htaccess *prev, *cur;
Denis Vlasenko5d499e12006-10-29 19:07:01 +0000388#elif ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000389 Htaccess *cur;
Glenn L McGrathbaaa6e92003-09-15 15:00:43 +0000390#endif
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000391
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000392 const char *cf = config->configFile;
393 char buf[160];
394 char *p0 = NULL;
395 char *c, *p;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000396
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000397 /* free previous ip setup if present */
398 Htaccess_IP *pip = config->ip_a_d;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000399
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000400 while (pip) {
401 Htaccess_IP *cur_ipl = pip;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000402
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000403 pip = cur_ipl->next;
404 free(cur_ipl);
405 }
406 config->ip_a_d = NULL;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000407
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000408 config->flg_deny_all = 0;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000409
Denis Vlasenko55a99402006-09-30 20:41:44 +0000410#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000411 /* retain previous auth and mime config only for subdir parse */
412 if (flag != SUBDIR_PARSE) {
Denis Vlasenko55a99402006-09-30 20:41:44 +0000413#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000414 free_config_lines(&config->auth);
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000415#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000416#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000417 free_config_lines(&config->mime_a);
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000418#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000419#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000420 free_config_lines(&config->script_i);
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +0000421#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000422 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000423#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000424
425 if (flag == SUBDIR_PARSE || cf == NULL) {
426 cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
427 if (cf == NULL) {
428 if (flag == FIRST_PARSE)
429 bb_error_msg_and_die(bb_msg_memory_exhausted);
430 return;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000431 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000432 sprintf((char *)cf, "%s/%s", path, httpd_conf);
Glenn L McGrath393183d2003-05-26 14:07:50 +0000433 }
434
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000435 while ((f = fopen(cf, "r")) == NULL) {
436 if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
437 /* config file not found, no changes to config */
438 return;
439 }
440 if (config->configFile && flag == FIRST_PARSE) /* if -c option given */
441 bb_perror_msg_and_die("%s", cf);
442 flag = FIND_FROM_HTTPD_ROOT;
443 cf = httpd_conf;
444 }
445
Denis Vlasenko55a99402006-09-30 20:41:44 +0000446#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000447 prev = config->auth;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000448#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000449 /* This could stand some work */
Denis Vlasenko55a99402006-09-30 20:41:44 +0000450 while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000451 c = NULL;
452 for (p = p0; *p0 != 0 && *p0 != '#'; p0++) {
453 if (!isspace(*p0)) {
454 *p++ = *p0;
455 if (*p0 == ':' && c == NULL)
456 c = p;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000457 }
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000458 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000459 *p = 0;
460
461 /* test for empty or strange line */
462 if (c == NULL || *c == 0)
463 continue;
464 p0 = buf;
465 if (*p0 == 'd')
466 *p0 = 'D';
467 if (*c == '*') {
468 if (*p0 == 'D') {
469 /* memorize deny all */
470 config->flg_deny_all++;
471 }
472 /* skip default other "word:*" config lines */
473 continue;
474 }
475
476 if (*p0 == 'a')
477 *p0 = 'A';
478 else if (*p0 != 'D' && *p0 != 'A'
Denis Vlasenko55a99402006-09-30 20:41:44 +0000479#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000480 && *p0 != '/'
481#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000482#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000483 && *p0 != '.'
484#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000485#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000486 && *p0 != '*'
487#endif
488 )
489 continue;
490 if (*p0 == 'A' || *p0 == 'D') {
491 /* storing current config IP line */
492 pip = calloc(1, sizeof(Htaccess_IP));
493 if (pip) {
Denis Vlasenko55a99402006-09-30 20:41:44 +0000494 if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000495 /* syntax IP{/mask} error detected, protect all */
496 *p0 = 'D';
497 pip->mask = 0;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000498 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000499 pip->allow_deny = *p0;
500 if (*p0 == 'D') {
501 /* Deny:form_IP move top */
502 pip->next = config->ip_a_d;
503 config->ip_a_d = pip;
504 } else {
505 /* add to bottom A:form_IP config line */
506 Htaccess_IP *prev_IP = config->ip_a_d;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000507
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000508 if (prev_IP == NULL) {
509 config->ip_a_d = pip;
510 } else {
511 while (prev_IP->next)
512 prev_IP = prev_IP->next;
513 prev_IP->next = pip;
514 }
515 }
516 }
517 continue;
518 }
Denis Vlasenko55a99402006-09-30 20:41:44 +0000519#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000520 if (*p0 == '/') {
521 /* make full path from httpd root / curent_path / config_line_path */
522 cf = flag == SUBDIR_PARSE ? path : "";
523 p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
524 if (p0 == NULL)
525 continue;
526 c[-1] = 0;
527 sprintf(p0, "/%s%s", cf, buf);
528
529 /* another call bb_simplify_path */
530 cf = p = p0;
531
532 do {
533 if (*p == '/') {
534 if (*cf == '/') { /* skip duplicate (or initial) slash */
535 continue;
536 } else if (*cf == '.') {
537 if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
538 continue;
539 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
540 ++cf;
541 if (p > p0) {
Denis Vlasenko92758142006-10-03 19:56:34 +0000542 while (*--p != '/') /* omit previous dir */;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000543 }
544 continue;
545 }
546 }
547 }
548 *++p = *cf;
549 } while (*++cf);
550
551 if ((p == p0) || (*p != '/')) { /* not a trailing slash */
552 ++p; /* so keep last character */
553 }
554 *p = 0;
555 sprintf(p0, "%s:%s", p0, c);
556 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000557#endif
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000558
Denis Vlasenko55a99402006-09-30 20:41:44 +0000559#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000560 /* storing current config line */
561 cur = calloc(1, sizeof(Htaccess) + strlen(p0));
562 if (cur) {
563 cf = strcpy(cur->before_colon, p0);
564 c = strchr(cf, ':');
565 *c++ = 0;
566 cur->after_colon = c;
Denis Vlasenko55a99402006-09-30 20:41:44 +0000567#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000568 if (*cf == '.') {
569 /* config .mime line move top for overwrite previous */
570 cur->next = config->mime_a;
571 config->mime_a = cur;
572 continue;
573 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000574#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000575#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000576 if (*cf == '*' && cf[1] == '.') {
577 /* config script interpreter line move top for overwrite previous */
578 cur->next = config->script_i;
579 config->script_i = cur;
580 continue;
581 }
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +0000582#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000583#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000584 free(p0);
585 if (prev == NULL) {
586 /* first line */
587 config->auth = prev = cur;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000588 } else {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000589 /* sort path, if current lenght eq or bigger then move up */
590 Htaccess *prev_hti = config->auth;
591 size_t l = strlen(cf);
592 Htaccess *hti;
593
594 for (hti = prev_hti; hti; hti = hti->next) {
595 if (l >= strlen(hti->before_colon)) {
596 /* insert before hti */
597 cur->next = hti;
598 if (prev_hti != hti) {
599 prev_hti->next = cur;
600 } else {
601 /* insert as top */
602 config->auth = cur;
603 }
604 break;
605 }
606 if (prev_hti != hti)
607 prev_hti = prev_hti->next;
608 }
609 if (!hti) { /* not inserted, add to bottom */
610 prev->next = cur;
611 prev = cur;
612 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000613 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000614#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000615 }
Glenn L McGrathbaaa6e92003-09-15 15:00:43 +0000616#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000617 }
618 fclose(f);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000619}
620
Denis Vlasenko55a99402006-09-30 20:41:44 +0000621#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000622/****************************************************************************
623 *
624 > $Function: encodeString()
625 *
626 * $Description: Given a string, html encode special characters.
627 * This is used for the -e command line option to provide an easy way
628 * for scripts to encode result data without confusing browsers. The
629 * returned string pointer is memory allocated by malloc().
630 *
631 * $Parameters:
632 * (const char *) string . . The first string to encode.
633 *
634 * $Return: (char *) . . . .. . . A pointer to the encoded string.
635 *
636 * $Errors: Returns a null string ("") if memory is not available.
637 *
638 ****************************************************************************/
639static char *encodeString(const char *string)
640{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000641 /* take the simple route and encode everything */
642 /* could possibly scan once to get length. */
643 int len = strlen(string);
644 char *out = malloc(len * 6 + 1);
645 char *p = out;
646 char ch;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000647
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000648 if (!out) return "";
649 while ((ch = *string++)) {
650 // very simple check for what to encode
651 if (isalnum(ch)) *p++ = ch;
652 else p += sprintf(p, "&#%d;", (unsigned char) ch);
653 }
654 *p = 0;
655 return out;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000656}
Denis Vlasenkob3a07152006-11-16 18:04:43 +0000657#endif /* FEATURE_HTTPD_ENCODE_URL_STR */
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000658
659/****************************************************************************
660 *
661 > $Function: decodeString()
662 *
663 * $Description: Given a URL encoded string, convert it to plain ascii.
664 * Since decoding always makes strings smaller, the decode is done in-place.
665 * Thus, callers should strdup() the argument if they do not want the
666 * argument modified. The return is the original pointer, allowing this
667 * function to be easily used as arguments to other functions.
668 *
669 * $Parameters:
670 * (char *) string . . . The first string to decode.
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000671 * (int) flag . . . 1 if require decode '+' as ' ' for CGI
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000672 *
673 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
674 *
675 * $Errors: None
676 *
677 ****************************************************************************/
Eric Andersena3bb3e62003-06-26 09:05:32 +0000678static char *decodeString(char *orig, int flag_plus_to_space)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000679{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000680 /* note that decoded string is always shorter than original */
681 char *string = orig;
682 char *ptr = string;
Eric Andersena3bb3e62003-06-26 09:05:32 +0000683
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000684 while (*ptr) {
685 if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; }
686 else if (*ptr != '%') *string++ = *ptr++;
687 else {
688 unsigned int value1, value2;
"Vladimir N. Oleynik"0bf67e82005-12-26 17:26:59 +0000689
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000690 ptr++;
691 if (sscanf(ptr, "%1X", &value1) != 1 ||
692 sscanf(ptr+1, "%1X", &value2) != 1) {
693 if (!flag_plus_to_space)
694 return NULL;
695 *string++ = '%';
696 } else {
697 value1 = value1 * 16 + value2;
698 if (value1 == '/' || value1 == 0)
699 return orig+1;
700 *string++ = value1;
701 ptr += 2;
702 }
703 }
704 }
705 *string = '\0';
706 return orig;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000707}
708
709
Denis Vlasenko55a99402006-09-30 20:41:44 +0000710#if ENABLE_FEATURE_HTTPD_CGI
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000711/****************************************************************************
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000712 * setenv helpers
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000713 ****************************************************************************/
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000714static void setenv1(const char *name, const char *value)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000715{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000716 if (!value)
717 value = "";
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000718 setenv(name, value, 1);
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000719}
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000720static void setenv_long(const char *name, long value)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000721{
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000722 char buf[sizeof(value)*3 + 1];
723 sprintf(buf, "%ld", value);
724 setenv(name, buf, 1);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000725}
Eric Andersena3bb3e62003-06-26 09:05:32 +0000726#endif
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000727
Denis Vlasenko55a99402006-09-30 20:41:44 +0000728#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000729/****************************************************************************
730 *
731 > $Function: decodeBase64()
732 *
733 > $Description: Decode a base 64 data stream as per rfc1521.
734 * Note that the rfc states that none base64 chars are to be ignored.
735 * Since the decode always results in a shorter size than the input, it is
736 * OK to pass the input arg as an output arg.
737 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000738 * $Parameter:
739 * (char *) Data . . . . A pointer to a base64 encoded string.
740 * Where to place the decoded data.
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000741 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000742 * $Return: void
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000743 *
744 * $Errors: None
745 *
746 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000747static void decodeBase64(char *Data)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000748{
Glenn L McGrath06e95652003-02-09 06:51:14 +0000749
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000750 const unsigned char *in = (const unsigned char *)Data;
751 // The decoded size will be at most 3/4 the size of the encoded
752 unsigned long ch = 0;
753 int i = 0;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000754
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000755 while (*in) {
756 int t = *in++;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000757
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000758 if (t >= '0' && t <= '9')
759 t = t - '0' + 52;
760 else if (t >= 'A' && t <= 'Z')
761 t = t - 'A';
762 else if (t >= 'a' && t <= 'z')
763 t = t - 'a' + 26;
764 else if (t == '+')
765 t = 62;
766 else if (t == '/')
767 t = 63;
768 else if (t == '=')
769 t = 0;
770 else
771 continue;
Glenn L McGrath874e3382003-05-14 12:11:36 +0000772
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000773 ch = (ch << 6) | t;
774 i++;
775 if (i == 4) {
776 *Data++ = (char) (ch >> 16);
777 *Data++ = (char) (ch >> 8);
778 *Data++ = (char) ch;
779 i = 0;
780 }
781 }
782 *Data = 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000783}
784#endif
785
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000786
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000787/****************************************************************************
788 *
789 > $Function: openServer()
790 *
791 * $Description: create a listen server socket on the designated port.
792 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000793 * $Return: (int) . . . A connection socket. -1 for errors.
794 *
795 * $Errors: None
796 *
797 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000798static int openServer(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000799{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000800 struct sockaddr_in lsocket;
801 int fd;
802 int on = 1;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000803
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000804 /* create the socket right now */
805 /* inet_addr() returns a value that is already in network order */
806 memset(&lsocket, 0, sizeof(lsocket));
807 lsocket.sin_family = AF_INET;
808 lsocket.sin_addr.s_addr = INADDR_ANY;
809 lsocket.sin_port = htons(config->port);
810 fd = xsocket(AF_INET, SOCK_STREAM, 0);
811 /* tell the OS it's OK to reuse a previous address even though */
812 /* it may still be in a close down state. Allows bind to succeed. */
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000813#ifdef SO_REUSEPORT
Denis Vlasenko55a99402006-09-30 20:41:44 +0000814 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on));
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000815#else
Denis Vlasenko55a99402006-09-30 20:41:44 +0000816 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000817#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000818 xbind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket));
819 xlisten(fd, 9);
820 signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */
821 return fd;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000822}
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000823
824/****************************************************************************
825 *
826 > $Function: sendHeaders()
827 *
828 * $Description: Create and send HTTP response headers.
829 * The arguments are combined and sent as one write operation. Note that
830 * IE will puke big-time if the headers are not sent in one packet and the
Glenn L McGrath06e95652003-02-09 06:51:14 +0000831 * second packet is delayed for any reason.
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000832 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000833 * $Parameter:
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000834 * (HttpResponseNum) responseNum . . . The result code to send.
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000835 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000836 * $Return: (int) . . . . writing errors
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000837 *
838 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000839static int sendHeaders(HttpResponseNum responseNum)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000840{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000841 char *buf = config->buf;
842 const char *responseString = "";
843 const char *infoString = 0;
844 const char *mime_type;
845 unsigned int i;
846 time_t timer = time(0);
847 char timeStr[80];
848 int len;
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000849 enum {
850 numNames = sizeof(httpResponseNames) / sizeof(httpResponseNames[0])
851 };
Glenn L McGrath06e95652003-02-09 06:51:14 +0000852
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000853 for (i = 0; i < numNames; i++) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000854 if (httpResponseNames[i].type == responseNum) {
855 responseString = httpResponseNames[i].name;
856 infoString = httpResponseNames[i].info;
857 break;
858 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000859 }
860 /* error message is HTML */
861 mime_type = responseNum == HTTP_OK ?
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +0000862 config->found_mime_type : "text/html";
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000863
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000864 /* emit the current date */
865 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
866 len = sprintf(buf,
867 "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
868 "Date: %s\r\nConnection: close\r\n",
869 responseNum, responseString, mime_type, timeStr);
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000870
Denis Vlasenko55a99402006-09-30 20:41:44 +0000871#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000872 if (responseNum == HTTP_UNAUTHORIZED) {
873 len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
874 config->realm);
875 }
Glenn L McGrath3d2405c2003-02-10 22:28:21 +0000876#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000877 if (responseNum == HTTP_MOVED_TEMPORARILY) {
878 len += sprintf(buf+len, "Location: %s/%s%s\r\n",
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +0000879 config->found_moved_temporarily,
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000880 (config->query ? "?" : ""),
881 (config->query ? config->query : ""));
882 }
Eric Andersen07f2fea2004-10-08 08:03:29 +0000883
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000884 if (config->ContentLength != -1) { /* file */
885 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
Denis Vlasenko5c759602006-10-28 12:37:16 +0000886 len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"\r\n",
Denis Vlasenko0bb993f2006-11-21 00:06:28 +0000887 timeStr, "Content-length:", (off_t) config->ContentLength);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000888 }
889 strcat(buf, "\r\n");
890 len += 2;
891 if (infoString) {
892 len += sprintf(buf+len,
893 "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
894 "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
895 responseNum, responseString,
896 responseNum, responseString, infoString);
897 }
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +0000898 if (DEBUG)
899 fprintf(stderr, "headers: '%s'\n", buf);
Denis Vlasenko0871bc82006-11-16 16:17:02 +0000900 return full_write(config->accepted_socket, buf, len);
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000901}
902
903/****************************************************************************
904 *
905 > $Function: getLine()
906 *
907 * $Description: Read from the socket until an end of line char found.
908 *
909 * Characters are read one at a time until an eol sequence is found.
910 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000911 * $Return: (int) . . . . number of characters read. -1 if error.
912 *
913 ****************************************************************************/
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000914static int getLine(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000915{
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +0000916 int count = 0;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000917 char *buf = config->buf;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000918
Denis Vlasenko0871bc82006-11-16 16:17:02 +0000919 while (read(config->accepted_socket, buf + count, 1) == 1) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000920 if (buf[count] == '\r') continue;
921 if (buf[count] == '\n') {
922 buf[count] = 0;
923 return count;
924 }
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +0000925 if (count < (MAX_MEMORY_BUFF-1)) /* check overflow */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000926 count++;
927 }
928 if (count) return count;
929 else return -1;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000930}
931
Denis Vlasenko55a99402006-09-30 20:41:44 +0000932#if ENABLE_FEATURE_HTTPD_CGI
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000933/****************************************************************************
934 *
935 > $Function: sendCgi()
936 *
937 * $Description: Execute a CGI script and send it's stdout back
938 *
939 * Environment variables are set up and the script is invoked with pipes
940 * for stdin/stdout. If a post is being done the script is fed the POST
941 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
942 *
943 * $Parameters:
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000944 * (const char *) url . . . . . . The requested URL (with leading /).
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000945 * (int bodyLen) . . . . . . . . Length of the post body.
946 * (const char *cookie) . . . . . For set HTTP_COOKIE.
947 * (const char *content_type) . . For set CONTENT_TYPE.
Glenn L McGrath06e95652003-02-09 06:51:14 +0000948
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000949 *
950 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
951 *
952 * $Errors: None
953 *
954 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000955static int sendCgi(const char *url,
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +0000956 const char *request, int bodyLen, const char *cookie,
957 const char *content_type)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000958{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000959 int fromCgi[2]; /* pipe for reading data from CGI */
960 int toCgi[2]; /* pipe for sending data to CGI */
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000961
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000962 static char * argp[] = { 0, 0 };
963 int pid = 0;
964 int inFd;
965 int outFd;
966 int firstLine = 1;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +0000967 int status;
968 size_t post_readed_size, post_readed_idx;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000969
Denis Vlasenko0bb993f2006-11-21 00:06:28 +0000970 if (pipe(fromCgi) != 0)
971 return 0;
972 if (pipe(toCgi) != 0)
973 return 0;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +0000974
Denis Vlasenko0bb993f2006-11-21 00:06:28 +0000975 pid = fork();
976 if (pid < 0)
977 return 0;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +0000978
979 if (!pid) {
980 /* child process */
981 char *script;
982 char *purl = strdup(url);
983 char realpath_buff[MAXPATHLEN];
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000984
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +0000985 if (purl == NULL)
986 _exit(242);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000987
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +0000988 inFd = toCgi[0];
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +0000989 outFd = fromCgi[1];
Glenn L McGrath06e95652003-02-09 06:51:14 +0000990
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +0000991 dup2(inFd, 0); // replace stdin with the pipe
992 dup2(outFd, 1); // replace stdout with the pipe
993 if (!DEBUG)
994 dup2(outFd, 2); // replace stderr with the pipe
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000995
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +0000996 close(toCgi[0]);
997 close(toCgi[1]);
998 close(fromCgi[0]);
999 close(fromCgi[1]);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001000
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001001 close(config->accepted_socket);
1002 close(config->server_socket);
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001003
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001004 /*
1005 * Find PATH_INFO.
1006 */
1007 script = purl;
1008 while ((script = strchr(script + 1, '/')) != NULL) {
1009 /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
1010 struct stat sb;
Denis Vlasenko9f609292006-11-05 19:47:33 +00001011
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001012 *script = '\0';
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001013 if (is_directory(purl + 1, 1, &sb) == 0) {
1014 /* not directory, found script.cgi/PATH_INFO */
1015 *script = '/';
1016 break;
1017 }
1018 *script = '/'; /* is directory, find next '/' */
1019 }
1020 setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */
1021 /* setenv1("PATH", getenv("PATH")); redundant */
1022 setenv1("REQUEST_METHOD", request);
1023 if (config->query) {
1024 char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
1025 if (uri)
1026 sprintf(uri, "%s?%s", purl, config->query);
1027 setenv1("REQUEST_URI", uri);
1028 } else {
1029 setenv1("REQUEST_URI", purl);
1030 }
1031 if (script != NULL)
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001032 *script = '\0'; /* cut off /PATH_INFO */
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001033 /* SCRIPT_FILENAME required by PHP in CGI mode */
1034 if (!realpath(purl + 1, realpath_buff))
1035 goto error_execing_cgi;
1036 setenv1("SCRIPT_FILENAME", realpath_buff);
1037 /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
1038 setenv1("SCRIPT_NAME", purl);
1039 setenv1("QUERY_STRING", config->query);
1040 setenv1("SERVER_SOFTWARE", httpdVersion);
1041 putenv("SERVER_PROTOCOL=HTTP/1.0");
1042 putenv("GATEWAY_INTERFACE=CGI/1.1");
1043 setenv1("REMOTE_ADDR", config->rmt_ip_str);
1044#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
1045 setenv_long("REMOTE_PORT", config->port);
1046#endif
1047 if (bodyLen)
1048 setenv_long("CONTENT_LENGTH", bodyLen);
1049 if (cookie)
1050 setenv1("HTTP_COOKIE", cookie);
1051 if (content_type)
1052 setenv1("CONTENT_TYPE", content_type);
1053#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1054 if (config->remoteuser) {
1055 setenv1("REMOTE_USER", config->remoteuser);
1056 putenv("AUTH_TYPE=Basic");
1057 }
1058#endif
1059 if (config->referer)
1060 setenv1("HTTP_REFERER", config->referer);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001061
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001062 /* set execve argp[0] without path */
1063 argp[0] = strrchr(purl, '/') + 1;
1064 /* but script argp[0] must have absolute path and chdiring to this */
1065 script = strrchr(realpath_buff, '/');
1066 if (!script)
1067 goto error_execing_cgi;
1068 *script = '\0';
1069 if (chdir(realpath_buff) == 0) {
1070 // now run the program. If it fails,
1071 // use _exit() so no destructors
1072 // get called and make a mess.
1073#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
1074 char *interpr = NULL;
1075 char *suffix = strrchr(purl, '.');
1076
1077 if (suffix) {
1078 Htaccess *cur;
1079 for (cur = config->script_i; cur; cur = cur->next) {
1080 if (strcmp(cur->before_colon + 1, suffix) == 0) {
1081 interpr = cur->after_colon;
1082 break;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001083 }
1084 }
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001085 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001086#endif
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001087 *script = '/';
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001088#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001089 if (interpr)
1090 execv(interpr, argp);
1091 else
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001092#endif
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001093 execv(realpath_buff, argp);
1094 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001095 error_execing_cgi:
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001096 /* send to stdout (even if we are not from inetd) */
1097 config->accepted_socket = 1;
1098 sendHeaders(HTTP_NOT_FOUND);
1099 _exit(242);
1100 } /* end child */
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001101
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001102 /* parent process */
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001103
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001104 post_readed_size = 0;
1105 post_readed_idx = 0;
Denis Vlasenko5d148e22006-11-21 00:12:09 +00001106 inFd = fromCgi[0];
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001107 outFd = toCgi[1];
1108 close(fromCgi[1]);
1109 close(toCgi[0]);
1110 signal(SIGPIPE, SIG_IGN);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001111
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001112 while (1) {
1113 fd_set readSet;
1114 fd_set writeSet;
1115 char wbuf[128];
1116 int nfound;
1117 int count;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001118
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001119 FD_ZERO(&readSet);
1120 FD_ZERO(&writeSet);
1121 FD_SET(inFd, &readSet);
1122 if (bodyLen > 0 || post_readed_size > 0) {
1123 FD_SET(outFd, &writeSet);
1124 nfound = outFd > inFd ? outFd : inFd;
1125 if (post_readed_size == 0) {
1126 FD_SET(config->accepted_socket, &readSet);
1127 if (nfound < config->accepted_socket)
1128 nfound = config->accepted_socket;
1129 }
1130 /* Now wait on the set of sockets! */
1131 nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL);
1132 } else {
1133 if (!bodyLen) {
1134 close(outFd);
1135 bodyLen = -1;
1136 }
1137 nfound = select(inFd + 1, &readSet, 0, 0, NULL);
1138 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001139
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001140 if (nfound <= 0) {
1141 if (waitpid(pid, &status, WNOHANG) > 0) {
1142 close(inFd);
1143 if (DEBUG && WIFEXITED(status))
1144 bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
1145 if (DEBUG && WIFSIGNALED(status))
1146 bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
1147 break;
1148 }
1149 } else if (post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) {
1150 count = full_write(outFd, wbuf + post_readed_idx, post_readed_size);
1151 if (count > 0) {
1152 post_readed_size -= count;
1153 post_readed_idx += count;
1154 if (post_readed_size == 0)
1155 post_readed_idx = 0;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001156 } else {
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001157 post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001158 }
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001159 } else if (bodyLen > 0 && post_readed_size == 0 && FD_ISSET(config->accepted_socket, &readSet)) {
1160 count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
1161 count = safe_read(config->accepted_socket, wbuf, count);
1162 if (count > 0) {
1163 post_readed_size += count;
1164 bodyLen -= count;
1165 } else {
1166 bodyLen = 0; /* closed */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001167 }
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001168 }
1169 if (FD_ISSET(inFd, &readSet)) {
1170 int s = config->accepted_socket;
1171 char *rbuf = config->buf;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001172
Eric Andersen97a1de12004-08-26 22:22:50 +00001173#ifndef PIPE_BUF
1174# define PIPESIZE 4096 /* amount of buffering in a pipe */
1175#else
1176# define PIPESIZE PIPE_BUF
1177#endif
1178#if PIPESIZE >= MAX_MEMORY_BUFF
1179# error "PIPESIZE >= MAX_MEMORY_BUFF"
1180#endif
1181
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +00001182 /* There is something to read */
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001183 count = safe_read(inFd, rbuf, PIPESIZE);
1184 if (count == 0)
1185 break; /* closed */
1186 if (count > 0) {
1187 if (firstLine) {
1188 rbuf[count] = 0;
1189 /* check to see if the user script added headers */
1190 if (strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) {
1191 full_write(s, "HTTP/1.0 200 OK\r\n", 17);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001192 }
Denis Vlasenko5d148e22006-11-21 00:12:09 +00001193 /* Sometimes CGI is writing to pipe in small chunks
1194 * and we don't see Content-type (because the read
1195 * is too short) and we emit bogus "text/plain"!
1196 * Is it a bug or CGI *has to* write it in one piece? */
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001197 if (strstr(rbuf, "ontent-") == 0) {
1198 full_write(s, "Content-type: text/plain\r\n\r\n", 28);
1199 }
1200 firstLine = 0;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001201 }
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001202 if (full_write(s, rbuf, count) != count)
1203 break;
1204
1205 if (DEBUG)
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +00001206 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001207 }
1208 }
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001209 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001210 return 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001211}
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001212#endif /* FEATURE_HTTPD_CGI */
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001213
1214/****************************************************************************
1215 *
1216 > $Function: sendFile()
1217 *
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001218 * $Description: Send a file response to a HTTP request
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001219 *
Glenn L McGrath06e95652003-02-09 06:51:14 +00001220 * $Parameter:
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001221 * (const char *) url . . The URL requested.
1222 *
1223 * $Return: (int) . . . . . . Always 0.
1224 *
1225 ****************************************************************************/
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +00001226static int sendFile(const char *url)
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001227{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001228 char * suffix;
1229 int f;
1230 const char * const * table;
1231 const char * try_suffix;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001232
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001233 suffix = strrchr(url, '.');
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001234
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001235 for (table = suffixTable; *table; table += 2)
1236 if (suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
1237 try_suffix += strlen(suffix);
1238 if (*try_suffix == 0 || *try_suffix == '.')
1239 break;
1240 }
1241 /* also, if not found, set default as "application/octet-stream"; */
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +00001242 config->found_mime_type = table[1];
Denis Vlasenko55a99402006-09-30 20:41:44 +00001243#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001244 if (suffix) {
1245 Htaccess * cur;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001246
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001247 for (cur = config->mime_a; cur; cur = cur->next) {
1248 if (strcmp(cur->before_colon, suffix) == 0) {
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +00001249 config->found_mime_type = cur->after_colon;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001250 break;
1251 }
1252 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001253 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001254#endif /* FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001255
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001256 if (DEBUG)
1257 fprintf(stderr, "sending file '%s' content-type: %s\n",
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +00001258 url, config->found_mime_type);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001259
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001260 f = open(url, O_RDONLY);
1261 if (f >= 0) {
1262 int count;
1263 char *buf = config->buf;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001264
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001265 sendHeaders(HTTP_OK);
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001266 /* TODO: sendfile() */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001267 while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001268 if (full_write(config->accepted_socket, buf, count) != count)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001269 break;
1270 }
1271 close(f);
1272 } else {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001273 if (DEBUG)
1274 bb_perror_msg("cannot open '%s'", url);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001275 sendHeaders(HTTP_NOT_FOUND);
1276 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001277
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001278 return 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001279}
1280
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001281static int checkPermIP(void)
1282{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001283 Htaccess_IP * cur;
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001284
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001285 /* This could stand some work */
1286 for (cur = config->ip_a_d; cur; cur = cur->next) {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001287 if (DEBUG)
1288 fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
1289 if (DEBUG)
1290 fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001291 (unsigned char)(cur->ip >> 24),
1292 (unsigned char)(cur->ip >> 16),
1293 (unsigned char)(cur->ip >> 8),
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001294 cur->ip & 0xff,
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001295 (unsigned char)(cur->mask >> 24),
1296 (unsigned char)(cur->mask >> 16),
1297 (unsigned char)(cur->mask >> 8),
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001298 cur->mask & 0xff);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001299 if ((config->rmt_ip & cur->mask) == cur->ip)
1300 return cur->allow_deny == 'A'; /* Allow/Deny */
1301 }
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001302
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001303 /* if unconfigured, return 1 - access from all */
1304 return !config->flg_deny_all;
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001305}
1306
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001307/****************************************************************************
1308 *
1309 > $Function: checkPerm()
1310 *
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001311 * $Description: Check the permission file for access password protected.
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001312 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001313 * If config file isn't present, everything is allowed.
Glenn L McGrath06e95652003-02-09 06:51:14 +00001314 * Entries are of the form you can see example from header source
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001315 *
1316 * $Parameters:
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001317 * (const char *) path . . . . The file path.
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001318 * (const char *) request . . . User information to validate.
1319 *
1320 * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
1321 *
1322 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +00001323
Denis Vlasenko55a99402006-09-30 20:41:44 +00001324#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001325static int checkPerm(const char *path, const char *request)
1326{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001327 Htaccess * cur;
1328 const char *p;
1329 const char *p0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001330
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001331 const char *prev = NULL;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001332
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001333 /* This could stand some work */
1334 for (cur = config->auth; cur; cur = cur->next) {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001335 size_t l;
1336
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001337 p0 = cur->before_colon;
1338 if (prev != NULL && strcmp(prev, p0) != 0)
1339 continue; /* find next identical */
1340 p = cur->after_colon;
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001341 if (DEBUG)
1342 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001343
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001344 l = strlen(p0);
1345 if (strncmp(p0, path, l) == 0
1346 && (l == 1 || path[l] == '/' || path[l] == '\0')
1347 ) {
1348 char *u;
1349 /* path match found. Check request */
1350 /* for check next /path:user:password */
1351 prev = p0;
1352 u = strchr(request, ':');
1353 if (u == NULL) {
1354 /* bad request, ':' required */
1355 break;
1356 }
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +00001357
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001358 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
1359 char *cipher;
1360 char *pp;
Eric Andersen35e643b2003-07-28 07:40:39 +00001361
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001362 if (strncmp(p, request, u-request) != 0) {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001363 /* user uncompared */
1364 continue;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001365 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001366 pp = strchr(p, ':');
1367 if (pp && pp[1] == '$' && pp[2] == '1' &&
1368 pp[3] == '$' && pp[4]) {
1369 pp++;
1370 cipher = pw_encrypt(u+1, pp);
1371 if (strcmp(cipher, pp) == 0)
1372 goto set_remoteuser_var; /* Ok */
1373 /* unauthorized */
1374 continue;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001375 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001376 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001377
1378 if (strcmp(p, request) == 0) {
1379set_remoteuser_var:
1380 config->remoteuser = strdup(request);
1381 if (config->remoteuser)
1382 config->remoteuser[(u - request)] = 0;
1383 return 1; /* Ok */
1384 }
1385 /* unauthorized */
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +00001386 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001387 } /* for */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001388
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001389 return prev == NULL;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001390}
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001391
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001392#endif /* FEATURE_HTTPD_BASIC_AUTH */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +00001393
Eric Andersen07f2fea2004-10-08 08:03:29 +00001394/****************************************************************************
1395 *
Mike Frysingerbb12d6f2006-01-03 23:59:01 +00001396 > $Function: handle_sigalrm()
Eric Andersen07f2fea2004-10-08 08:03:29 +00001397 *
Mike Frysingerbb12d6f2006-01-03 23:59:01 +00001398 * $Description: Handle timeouts
Eric Andersen07f2fea2004-10-08 08:03:29 +00001399 *
1400 ****************************************************************************/
1401
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001402static void handle_sigalrm(int sig)
Eric Andersen07f2fea2004-10-08 08:03:29 +00001403{
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001404 sendHeaders(HTTP_REQUEST_TIMEOUT);
1405 config->alarm_signaled = sig;
Eric Andersen07f2fea2004-10-08 08:03:29 +00001406}
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001407
1408/****************************************************************************
1409 *
1410 > $Function: handleIncoming()
1411 *
1412 * $Description: Handle an incoming http request.
1413 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001414 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +00001415static void handleIncoming(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001416{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001417 char *buf = config->buf;
1418 char *url;
1419 char *purl;
1420 int blank = -1;
1421 char *test;
1422 struct stat sb;
1423 int ip_allowed;
Denis Vlasenko55a99402006-09-30 20:41:44 +00001424#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001425 const char *prequest = request_GET;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001426 unsigned long length = 0;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001427 char *cookie = 0;
1428 char *content_type = 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001429#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001430 fd_set s_fd;
1431 struct timeval tv;
1432 int retval;
1433 struct sigaction sa;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001434
Denis Vlasenko55a99402006-09-30 20:41:44 +00001435#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001436 int credentials = -1; /* if not required this is Ok */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001437#endif
1438
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001439 sa.sa_handler = handle_sigalrm;
1440 sigemptyset(&sa.sa_mask);
1441 sa.sa_flags = 0; /* no SA_RESTART */
1442 sigaction(SIGALRM, &sa, NULL);
Eric Andersen07f2fea2004-10-08 08:03:29 +00001443
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001444 do {
1445 int count;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001446
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001447 (void) alarm(TIMEOUT);
1448 if (getLine() <= 0)
1449 break; /* closed */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001450
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001451 purl = strpbrk(buf, " \t");
1452 if (purl == NULL) {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001453 BAD_REQUEST:
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001454 sendHeaders(HTTP_BAD_REQUEST);
1455 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001456 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001457 *purl = '\0';
Denis Vlasenko55a99402006-09-30 20:41:44 +00001458#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001459 if (strcasecmp(buf, prequest) != 0) {
1460 prequest = "POST";
1461 if (strcasecmp(buf, prequest) != 0) {
1462 sendHeaders(HTTP_NOT_IMPLEMENTED);
1463 break;
1464 }
1465 }
1466#else
1467 if (strcasecmp(buf, request_GET) != 0) {
1468 sendHeaders(HTTP_NOT_IMPLEMENTED);
1469 break;
1470 }
1471#endif
1472 *purl = ' ';
1473 count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001474
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001475 if (count < 1 || buf[0] != '/') {
1476 /* Garbled request/URL */
1477 goto BAD_REQUEST;
1478 }
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001479 url = alloca(strlen(buf) + sizeof("/index.html"));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001480 if (url == NULL) {
1481 sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
1482 break;
1483 }
1484 strcpy(url, buf);
1485 /* extract url args if present */
1486 test = strchr(url, '?');
Denis Vlasenko5d148e22006-11-21 00:12:09 +00001487 config->query = NULL;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001488 if (test) {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001489 *test++ = '\0';
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001490 config->query = test;
1491 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001492
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001493 test = decodeString(url, 0);
1494 if (test == NULL)
1495 goto BAD_REQUEST;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001496 /* FIXME: bug? should be "url+1"? */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001497 if (test == (buf+1)) {
1498 sendHeaders(HTTP_NOT_FOUND);
1499 break;
1500 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001501
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001502 /* algorithm stolen from libbb bb_simplify_path(),
Denis Vlasenko55a99402006-09-30 20:41:44 +00001503 but don't strdup and reducing trailing slash and protect out root */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001504 purl = test = url;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001505 do {
1506 if (*purl == '/') {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001507 /* skip duplicate (or initial) slash */
1508 if (*test == '/') {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001509 continue;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001510 }
1511 if (*test == '.') {
1512 /* skip extra '.' */
1513 if (test[1] == '/' || test[1] == 0) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001514 continue;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001515 } else
1516 /* '..': be careful */
1517 if (test[1] == '.' && (test[2] == '/' || test[2] == 0)) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001518 ++test;
1519 if (purl == url) {
1520 /* protect out root */
1521 goto BAD_REQUEST;
1522 }
Denis Vlasenko92758142006-10-03 19:56:34 +00001523 while (*--purl != '/') /* omit previous dir */;
1524 continue;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001525 }
1526 }
1527 }
1528 *++purl = *test;
1529 } while (*++test);
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001530 *++purl = '\0'; /* so keep last character */
1531 test = purl; /* end ptr */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001532
1533 /* If URL is directory, adding '/' */
1534 if (test[-1] != '/') {
1535 if (is_directory(url + 1, 1, &sb)) {
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +00001536 config->found_moved_temporarily = url;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001537 }
1538 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001539 if (DEBUG)
1540 fprintf(stderr, "url='%s', args=%s\n", url, config->query);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001541
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001542 test = url;
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001543 ip_allowed = checkPermIP();
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001544 while (ip_allowed && (test = strchr(test + 1, '/')) != NULL) {
1545 /* have path1/path2 */
1546 *test = '\0';
1547 if (is_directory(url + 1, 1, &sb)) {
1548 /* may be having subdir config */
1549 parse_conf(url + 1, SUBDIR_PARSE);
1550 ip_allowed = checkPermIP();
1551 }
1552 *test = '/';
1553 }
1554 if (blank >= 0) {
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001555 /* read until blank line for HTTP version specified, else parse immediate */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001556 while (1) {
1557 alarm(TIMEOUT);
1558 count = getLine();
1559 if (count <= 0)
1560 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001561
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001562 if (DEBUG)
1563 fprintf(stderr, "header: '%s'\n", buf);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001564
Denis Vlasenko55a99402006-09-30 20:41:44 +00001565#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001566 /* try and do our best to parse more lines */
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001567 if ((STRNCASECMP(buf, "Content-length:") == 0)) {
1568 /* extra read only for POST */
1569 if (prequest != request_GET) {
1570 test = buf + sizeof("Content-length:")-1;
1571 if (!test[0]) goto bail_out;
1572 errno = 0;
1573 /* not using strtoul: it ignores leading munis! */
1574 length = strtol(test, &test, 10);
1575 /* length is "ulong", but we need to pass it to int later */
1576 /* so we check for negative or too large values in one go: */
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001577 /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001578 if (test[0] || errno || length > INT_MAX)
1579 goto bail_out;
1580 }
1581 } else if ((STRNCASECMP(buf, "Cookie:") == 0)) {
1582 cookie = strdup(skip_whitespace(buf + sizeof("Cookie:")-1));
1583 } else if ((STRNCASECMP(buf, "Content-Type:") == 0)) {
1584 content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1));
1585 } else if ((STRNCASECMP(buf, "Referer:") == 0)) {
1586 config->referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001587 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001588#endif
1589
Denis Vlasenko55a99402006-09-30 20:41:44 +00001590#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001591 if (STRNCASECMP(buf, "Authorization:") == 0) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001592 /* We only allow Basic credentials.
1593 * It shows up as "Authorization: Basic <userid:password>" where
1594 * the userid:password is base64 encoded.
1595 */
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001596 test = skip_whitespace(buf + sizeof("Authorization:")-1);
1597 if (STRNCASECMP(test, "Basic") != 0)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001598 continue;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001599 test += sizeof("Basic")-1;
1600 /* decodeBase64() skips whitespace itself */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001601 decodeBase64(test);
1602 credentials = checkPerm(url, test);
1603 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001604#endif /* FEATURE_HTTPD_BASIC_AUTH */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001605
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001606 } /* while extra header reading */
1607 }
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001608 alarm(0);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001609 if (config->alarm_signaled)
1610 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001611
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001612 if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001613 /* protect listing [/path]/httpd_conf or IP deny */
Denis Vlasenko55a99402006-09-30 20:41:44 +00001614#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001615 FORBIDDEN: /* protect listing /cgi-bin */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001616#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001617 sendHeaders(HTTP_FORBIDDEN);
1618 break;
1619 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001620
Denis Vlasenko55a99402006-09-30 20:41:44 +00001621#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001622 if (credentials <= 0 && checkPerm(url, ":") == 0) {
1623 sendHeaders(HTTP_UNAUTHORIZED);
1624 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001625 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001626#endif
1627
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +00001628 if (config->found_moved_temporarily) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001629 sendHeaders(HTTP_MOVED_TEMPORARILY);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001630 /* clear unforked memory flag */
Denis Vlasenkod4f3d1a2006-11-16 16:20:12 +00001631 config->found_moved_temporarily = NULL;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001632 break;
1633 }
1634
1635 test = url + 1; /* skip first '/' */
1636
Denis Vlasenko55a99402006-09-30 20:41:44 +00001637#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001638 if (strncmp(test, "cgi-bin", 7) == 0) {
1639 if (test[7] == '/' && test[8] == 0)
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001640 goto FORBIDDEN; /* protect listing cgi-bin/ */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001641 sendCgi(url, prequest, length, cookie, content_type);
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001642 break;
1643 }
1644 if (prequest != request_GET) {
1645 sendHeaders(HTTP_NOT_IMPLEMENTED);
1646 break;
1647 }
Denis Vlasenko5d148e22006-11-21 00:12:09 +00001648#endif /* FEATURE_HTTPD_CGI */
1649 if (purl[-1] == '/')
1650 strcpy(purl, "index.html");
1651 if (stat(test, &sb) == 0) {
1652 /* It's a dir URL and there is index.html */
1653 config->ContentLength = sb.st_size;
1654 config->last_mod = sb.st_mtime;
1655 }
1656#if ENABLE_FEATURE_HTTPD_CGI
1657 else if (purl[-1] == '/') {
1658 /* It's a dir URL and there is no index.html
1659 * Try cgi-bin/index.cgi */
1660 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
1661 purl[0] = '\0';
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001662 config->query = url;
1663 sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
1664 break;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001665 }
1666 }
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001667#endif /* FEATURE_HTTPD_CGI */
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001668 sendFile(test);
1669 config->ContentLength = -1;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001670 } while (0);
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001671
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001672 bail_out:
1673
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001674 if (DEBUG)
1675 fprintf(stderr, "closing socket\n\n");
1676#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001677 free(cookie);
1678 free(content_type);
Denis Vlasenkoa5342b42006-11-17 18:26:57 +00001679 free(config->referer);
1680 config->referer = NULL;
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001681# if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenkoa5342b42006-11-17 18:26:57 +00001682 free(config->remoteuser);
1683 config->remoteuser = NULL;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001684# endif
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001685#endif
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001686 shutdown(config->accepted_socket, SHUT_WR);
Eric Andersend8746cd2004-02-24 07:28:38 +00001687
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001688 /* Properly wait for remote to closed */
Denis Vlasenko55a99402006-09-30 20:41:44 +00001689 FD_ZERO(&s_fd);
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001690 FD_SET(config->accepted_socket, &s_fd);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001691
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001692 do {
Denis Vlasenko55a99402006-09-30 20:41:44 +00001693 tv.tv_sec = 2;
1694 tv.tv_usec = 0;
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001695 retval = select(config->accepted_socket + 1, &s_fd, NULL, NULL, &tv);
1696 } while (retval > 0 && read(config->accepted_socket, buf, sizeof(config->buf) > 0));
Eric Andersend8746cd2004-02-24 07:28:38 +00001697
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001698 shutdown(config->accepted_socket, SHUT_RD);
1699 /* In inetd case, we close fd 1 (stdout) here. We will exit soon anyway */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001700 close(config->accepted_socket);
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001701}
1702
1703/****************************************************************************
1704 *
1705 > $Function: miniHttpd()
1706 *
1707 * $Description: The main http server function.
1708 *
1709 * Given an open socket fildes, listen for new connections and farm out
1710 * the processing as a forked process.
1711 *
1712 * $Parameters:
1713 * (int) server. . . The server socket fildes.
1714 *
1715 * $Return: (int) . . . . Always 0.
1716 *
1717 ****************************************************************************/
1718static int miniHttpd(int server)
1719{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001720 fd_set readfd, portfd;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001721
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001722 FD_ZERO(&portfd);
1723 FD_SET(server, &portfd);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001724
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001725 /* copy the ports we are watching to the readfd set */
1726 while (1) {
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001727 int on, s;
1728 socklen_t fromAddrLen;
1729 struct sockaddr_in fromAddr;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001730
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001731 /* Now wait INDEFINITELY on the set of sockets! */
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001732 readfd = portfd;
1733 if (select(server + 1, &readfd, 0, 0, 0) <= 0)
1734 continue;
1735 if (!FD_ISSET(server, &readfd))
1736 continue;
1737 fromAddrLen = sizeof(fromAddr);
1738 s = accept(server, (struct sockaddr *)&fromAddr, &fromAddrLen);
1739 if (s < 0)
1740 continue;
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001741 config->accepted_socket = s;
1742 config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
Denis Vlasenko55a99402006-09-30 20:41:44 +00001743#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001744 sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
1745 (unsigned char)(config->rmt_ip >> 24),
1746 (unsigned char)(config->rmt_ip >> 16),
1747 (unsigned char)(config->rmt_ip >> 8),
1748 config->rmt_ip & 0xff);
1749 config->port = ntohs(fromAddr.sin_port);
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +00001750#if DEBUG
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001751 bb_error_msg("connection from IP=%s, port %u",
1752 config->rmt_ip_str, config->port);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001753#endif
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001754#endif /* FEATURE_HTTPD_CGI */
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001755
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001756 /* set the KEEPALIVE option to cull dead connections */
1757 on = 1;
1758 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +00001759#if !DEBUG
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001760 if (fork() == 0)
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +00001761#endif
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001762 {
1763 /* This is the spawned thread */
Denis Vlasenko55a99402006-09-30 20:41:44 +00001764#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001765 /* protect reload config, may be confuse checking */
1766 signal(SIGHUP, SIG_IGN);
Glenn L McGrathc9163fe2003-05-13 16:20:11 +00001767#endif
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001768 handleIncoming();
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +00001769#if !DEBUG
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001770 exit(0);
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +00001771#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001772 }
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001773 close(s);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001774 } // while (1)
1775 return 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001776}
1777
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001778/* from inetd */
1779static int miniHttpd_inetd(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001780{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001781 struct sockaddr_in fromAddrLen;
Denis Vlasenko55a99402006-09-30 20:41:44 +00001782 socklen_t sinlen = sizeof(struct sockaddr_in);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001783
Denis Vlasenko55a99402006-09-30 20:41:44 +00001784 getpeername(0, (struct sockaddr *)&fromAddrLen, &sinlen);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001785 config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
Denis Vlasenko55a99402006-09-30 20:41:44 +00001786#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001787 sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
1788 (unsigned char)(config->rmt_ip >> 24),
1789 (unsigned char)(config->rmt_ip >> 16),
1790 (unsigned char)(config->rmt_ip >> 8),
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001791 config->rmt_ip & 0xff);
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001792#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001793 config->port = ntohs(fromAddrLen.sin_port);
1794 handleIncoming();
1795 return 0;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001796}
Glenn L McGrath06e95652003-02-09 06:51:14 +00001797
Denis Vlasenko55a99402006-09-30 20:41:44 +00001798#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
Glenn L McGrath06e95652003-02-09 06:51:14 +00001799static void sighup_handler(int sig)
1800{
1801 /* set and reset */
1802 struct sigaction sa;
1803
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001804 parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001805 sa.sa_handler = sighup_handler;
1806 sigemptyset(&sa.sa_mask);
1807 sa.sa_flags = SA_RESTART;
1808 sigaction(SIGHUP, &sa, NULL);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001809}
1810#endif
1811
Denis Vlasenko9f609292006-11-05 19:47:33 +00001812enum {
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00001813 c_opt_config_file = 0,
1814 d_opt_decode_url,
1815 h_opt_home_httpd,
1816 USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
Denis Vlasenko9f609292006-11-05 19:47:33 +00001817 USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
1818 USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
1819 USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001820 p_opt_port ,
1821 p_opt_inetd ,
1822 p_opt_foreground,
Denis Vlasenko9f609292006-11-05 19:47:33 +00001823 OPT_CONFIG_FILE = 1 << c_opt_config_file,
1824 OPT_DECODE_URL = 1 << d_opt_decode_url,
1825 OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
1826 OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
1827 OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
1828 OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
1829 OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001830 OPT_PORT = 1 << p_opt_port,
1831 OPT_INETD = 1 << p_opt_inetd,
1832 OPT_FOREGROUND = 1 << p_opt_foreground,
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00001833};
Eric Andersena3bb3e62003-06-26 09:05:32 +00001834
Denis Vlasenko55a99402006-09-30 20:41:44 +00001835static const char httpd_opts[] = "c:d:h:"
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00001836 USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
1837 USE_FEATURE_HTTPD_BASIC_AUTH("r:")
1838 USE_FEATURE_HTTPD_AUTH_MD5("m:")
1839 USE_FEATURE_HTTPD_SETUID("u:")
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001840 "p:if";
"Vladimir N. Oleynik"27d42a02005-12-02 09:46:04 +00001841
Eric Andersena3bb3e62003-06-26 09:05:32 +00001842
Glenn L McGrath06e95652003-02-09 06:51:14 +00001843int httpd_main(int argc, char *argv[])
Glenn L McGrath06e95652003-02-09 06:51:14 +00001844{
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001845 unsigned opt;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001846 const char *home_httpd = home;
1847 char *url_for_decode;
1848 USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001849 const char *s_port;
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00001850 USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
1851 USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001852 USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
Eric Andersen35e643b2003-07-28 07:40:39 +00001853
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +00001854#if ENABLE_LOCALE_SUPPORT
1855 /* Undo busybox.c: we want to speak English in http (dates etc) */
1856 setlocale(LC_TIME, "C");
1857#endif
1858
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001859 config = xzalloc(sizeof(*config));
Denis Vlasenko55a99402006-09-30 20:41:44 +00001860#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001861 config->realm = "Web Server Authentication";
Glenn L McGrath06e95652003-02-09 06:51:14 +00001862#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001863 config->port = 80;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001864 config->ContentLength = -1;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001865
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001866 opt = getopt32(argc, argv, httpd_opts,
Eric Andersena3bb3e62003-06-26 09:05:32 +00001867 &(config->configFile), &url_for_decode, &home_httpd
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00001868 USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
1869 USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
1870 USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00001871 USE_FEATURE_HTTPD_SETUID(, &s_ugid)
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001872 , &s_port
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001873 );
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001874 if (opt & OPT_DECODE_URL) {
1875 printf("%s", decodeString(url_for_decode, 1));
1876 return 0;
1877 }
Denis Vlasenko55a99402006-09-30 20:41:44 +00001878#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001879 if (opt & OPT_ENCODE_URL) {
1880 printf("%s", encodeString(url_for_encode));
1881 return 0;
1882 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001883#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +00001884#if ENABLE_FEATURE_HTTPD_AUTH_MD5
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001885 if (opt & OPT_MD5) {
Denis Vlasenko55a99402006-09-30 20:41:44 +00001886 puts(pw_encrypt(pass, "$1$"));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001887 return 0;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001888 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001889#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001890 if (opt & OPT_PORT)
Denis Vlasenko13858992006-10-08 12:49:22 +00001891 config->port = xatou16(s_port);
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001892
Denis Vlasenko55a99402006-09-30 20:41:44 +00001893#if ENABLE_FEATURE_HTTPD_SETUID
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001894 if (opt & OPT_SETUID) {
1895 char *e;
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00001896 // FIXME: what the default group should be?
1897 ugid.gid = -1;
1898 ugid.uid = strtoul(s_ugid, &e, 0);
1899 if (*e == ':') {
1900 e++;
1901 ugid.gid = strtoul(e, &e, 0);
1902 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001903 if (*e != '\0') {
1904 /* not integer */
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00001905 if (!uidgid_get(&ugid, s_ugid))
1906 bb_error_msg_and_die("unrecognized user[:group] "
1907 "name '%s'", s_ugid);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001908 }
1909 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001910#endif
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001911
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001912 xchdir(home_httpd);
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001913 if (!(opt & OPT_INETD)) {
1914 config->server_socket = openServer();
1915#if ENABLE_FEATURE_HTTPD_SETUID
1916 /* drop privileges */
1917 if (opt & OPT_SETUID) {
1918 if (ugid.gid != (gid_t)-1) {
1919 if (setgroups(1, &ugid.gid) == -1)
1920 bb_perror_msg_and_die("setgroups");
1921 xsetgid(ugid.gid);
1922 }
1923 xsetuid(ugid.uid);
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00001924 }
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001925#endif
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001926 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001927
Denis Vlasenko55a99402006-09-30 20:41:44 +00001928#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001929 {
1930 char *p = getenv("PATH");
1931 if (p) {
1932 p = xstrdup(p);
1933 }
1934 clearenv();
1935 if (p)
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001936 setenv1("PATH", p);
1937 if (!(opt & OPT_INETD))
1938 setenv_long("SERVER_PORT", config->port);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001939 }
Glenn L McGrathfe538ba2003-09-10 23:35:45 +00001940#endif
1941
Denis Vlasenko55a99402006-09-30 20:41:44 +00001942#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001943 sighup_handler(0);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001944#else
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001945 parse_conf(default_path_httpd_conf, FIRST_PARSE);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001946#endif
1947
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001948 if (opt & OPT_INETD)
1949 return miniHttpd_inetd();
1950
1951 if (!(opt & OPT_FOREGROUND))
1952 xdaemon(1, 0); /* don't change current directory */
Denis Vlasenko9f609292006-11-05 19:47:33 +00001953 return miniHttpd(config->server_socket);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001954}