blob: b083d645ca6f02d5807f50cade5135a3f8f8d517 [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 *
Denis Vlasenko8b458372006-11-21 21:23:21 +000027 * Doc:
28 * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
29 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +000030 * The server can also be invoked as a url arg decoder and html text encoder
31 * as follows:
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000032 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
Glenn L McGrathc9163fe2003-05-13 16:20:11 +000033 * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000034 * Note that url encoding for arguments is not the same as html encoding for
Eric Andersenaff114c2004-04-14 17:51:38 +000035 * presentation. -d decodes a url-encoded argument while -e encodes in html
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000036 * for page display.
Glenn L McGrath58c708a2003-01-05 04:01:56 +000037 *
38 * httpd.conf has the following format:
Eric Andersenc7bda1c2004-03-15 08:29:22 +000039 *
Glenn L McGrathb65422c2003-09-08 10:59:27 +000040 * A:172.20. # Allow address from 172.20.0.0/16
41 * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
42 * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000043 * A:127.0.0.1 # Allow local loopback connections
44 * D:* # Deny from other IP connections
45 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
46 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
47 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
48 * .au:audio/basic # additional mime type for audio.au files
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +000049 * *.php:/path/php # running cgi.php scripts through an interpreter
Eric Andersenc7bda1c2004-03-15 08:29:22 +000050 *
Eric Andersenaff114c2004-04-14 17:51:38 +000051 * A/D may be as a/d or allow/deny - first char case insensitive
Glenn L McGrath393183d2003-05-26 14:07:50 +000052 * Deny IP rules take precedence over allow rules.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000053 *
54 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000055 * The Deny/Allow IP logic:
Eric Andersenc7bda1c2004-03-15 08:29:22 +000056 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000057 * - Default is to allow all. No addresses are denied unless
Eric Andersen97a1de12004-08-26 22:22:50 +000058 * denied with a D: rule.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000059 * - Order of Deny/Allow rules is significant
60 * - Deny rules take precedence over allow rules.
61 * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
Eric Andersen97a1de12004-08-26 22:22:50 +000062 * addresses.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000063 * - Specification of Allow all (A:*) is a no-op
Eric Andersenc7bda1c2004-03-15 08:29:22 +000064 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000065 * Example:
66 * 1. Allow only specified addresses
Glenn L McGrathb65422c2003-09-08 10:59:27 +000067 * A:172.20 # Allow any address that begins with 172.20.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000068 * A:10.10. # Allow any address that begins with 10.10.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000069 * A:127.0.0.1 # Allow local loopback connections
70 * D:* # Deny from other IP connections
Eric Andersenc7bda1c2004-03-15 08:29:22 +000071 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000072 * 2. Only deny specified addresses
73 * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
74 * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
75 * A:* # (optional line added for clarity)
Eric Andersenc7bda1c2004-03-15 08:29:22 +000076 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000077 * If a sub directory contains a config file it is parsed and merged with
Glenn L McGrathb65422c2003-09-08 10:59:27 +000078 * any existing settings as if it was appended to the original configuration.
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000079 *
80 * subdir paths are relative to the containing subdir and thus cannot
81 * affect the parent rules.
82 *
83 * Note that since the sub dir is parsed in the forked thread servicing the
84 * subdir http request, any merge is discarded when the process exits. As a
85 * result, the subdir settings only have a lifetime of a single request.
86 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +000087 *
88 * If -c is not set, an attempt will be made to open the default
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000089 * root configuration file. If -c is set and the file is not found, the
90 * server exits with an error.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000091 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +000092*/
Glenn L McGrath58c708a2003-01-05 04:01:56 +000093
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000094#include "libbb.h"
Glenn L McGrath58c708a2003-01-05 04:01:56 +000095
Denis Vlasenko69981422007-01-07 21:25:12 +000096/* amount of buffering in a pipe */
97#ifndef PIPE_BUF
98# define PIPE_BUF 4096
99#endif
100
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000101static const char default_path_httpd_conf[] ALIGN1 = "/etc";
102static const char httpd_conf[] ALIGN1 = "httpd.conf";
Glenn L McGrath06e95652003-02-09 06:51:14 +0000103
Eric Andersen07f2fea2004-10-08 08:03:29 +0000104#define TIMEOUT 60
105
Eric Andersenaff114c2004-04-14 17:51:38 +0000106// Note: busybox xfuncs are not used because we want the server to keep running
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000107// if something bad happens due to a malformed user request.
Glenn L McGrath06e95652003-02-09 06:51:14 +0000108// As a result, all memory allocation after daemonize
109// is checked rigorously
110
111//#define DEBUG 1
Denis Vlasenko69981422007-01-07 21:25:12 +0000112#define DEBUG 0
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +0000113
Glenn L McGrath06e95652003-02-09 06:51:14 +0000114#define MAX_MEMORY_BUFF 8192 /* IO buffer */
115
116typedef struct HT_ACCESS {
117 char *after_colon;
118 struct HT_ACCESS *next;
119 char before_colon[1]; /* really bigger, must last */
120} Htaccess;
121
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000122typedef struct HT_ACCESS_IP {
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000123 unsigned ip;
124 unsigned mask;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000125 int allow_deny;
126 struct HT_ACCESS_IP *next;
127} Htaccess_IP;
128
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000129struct globals {
130 int server_socket;
131 int accepted_socket;
132 volatile smallint alarm_signaled;
133 smallint flg_deny_all;
134 const char *g_query;
135 const char *configFile;
136 const char *home_httpd;
137 unsigned rmt_ip;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000138
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000139 const char *found_mime_type;
140 const char *found_moved_temporarily;
141 off_t ContentLength; /* -1 - unknown */
142 time_t last_mod;
143 Htaccess_IP *ip_a_d; /* config allow/deny lines */
144
145 USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000146 USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000147 USE_FEATURE_HTTPD_CGI(char *referer;)
Denis Vlasenko6cd84da2007-07-21 14:57:54 +0000148 USE_FEATURE_HTTPD_CGI(char *user_agent;)
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000149
Denis Vlasenko55a99402006-09-30 20:41:44 +0000150#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000151 char *rmt_ip_str; /* for set env REMOTE_ADDR */
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000152#endif
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000153 unsigned tcp_port; /* server initial port and for
154 set env REMOTE_PORT */
Denis Vlasenko55a99402006-09-30 20:41:44 +0000155#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000156 Htaccess *g_auth; /* config user:password lines */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000157#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000158#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000159 Htaccess *mime_a; /* config mime types */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000160#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000161#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000162 Htaccess *script_i; /* config script interpreters */
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +0000163#endif
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000164 char iobuf[MAX_MEMORY_BUFF];
165};
166#define G (*ptr_to_globals)
167#define server_socket (G.server_socket )
168#define accepted_socket (G.accepted_socket)
169#define alarm_signaled (G.alarm_signaled )
170#define flg_deny_all (G.flg_deny_all )
171#define g_query (G.g_query )
172#define configFile (G.configFile )
173#define home_httpd (G.home_httpd )
174#define rmt_ip (G.rmt_ip )
175#define found_mime_type (G.found_mime_type)
176#define found_moved_temporarily (G.found_moved_temporarily)
177#define ContentLength (G.ContentLength )
178#define last_mod (G.last_mod )
179#define ip_a_d (G.ip_a_d )
180#define g_realm (G.g_realm )
181#define remoteuser (G.remoteuser )
182#define referer (G.referer )
Denis Vlasenko6cd84da2007-07-21 14:57:54 +0000183#define user_agent (G.user_agent )
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000184#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
185#define rmt_ip_str (G.rmt_ip_str )
186#endif
187#define tcp_port (G.tcp_port )
188#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
189#define g_auth (G.g_auth )
190#endif
191#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
192#define mime_a (G.mime_a )
193#endif
194#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
195#define script_i (G.script_i )
196#endif
197#define iobuf (G.iobuf )
198#define INIT_G() do { \
199 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
200 USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
201 tcp_port = 80; \
202 ContentLength = -1; \
203} while (0)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000204
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000205static const char request_GET[] ALIGN1 = "GET"; /* size algorithmic optimize */
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000206
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000207static const char *const suffixTable[] = {
Eric Andersenaff114c2004-04-14 17:51:38 +0000208/* Warning: shorted equivalent suffix in one line must be first */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000209 ".htm.html", "text/html",
210 ".jpg.jpeg", "image/jpeg",
211 ".gif", "image/gif",
212 ".png", "image/png",
213 ".txt.h.c.cc.cpp", "text/plain",
214 ".css", "text/css",
215 ".wav", "audio/wav",
216 ".avi", "video/x-msvideo",
217 ".qt.mov", "video/quicktime",
218 ".mpe.mpeg", "video/mpeg",
219 ".mid.midi", "audio/midi",
220 ".mp3", "audio/mpeg",
Glenn L McGrath06e95652003-02-09 06:51:14 +0000221#if 0 /* unpopular */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000222 ".au", "audio/basic",
223 ".pac", "application/x-ns-proxy-autoconfig",
224 ".vrml.wrl", "model/vrml",
Glenn L McGrath06e95652003-02-09 06:51:14 +0000225#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000226 0, "application/octet-stream" /* default */
227};
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000228
Denis Vlasenko55a99402006-09-30 20:41:44 +0000229typedef enum {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000230 HTTP_OK = 200,
231 HTTP_MOVED_TEMPORARILY = 302,
232 HTTP_BAD_REQUEST = 400, /* malformed syntax */
233 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
234 HTTP_NOT_FOUND = 404,
235 HTTP_FORBIDDEN = 403,
236 HTTP_REQUEST_TIMEOUT = 408,
237 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
238 HTTP_INTERNAL_SERVER_ERROR = 500,
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000239#if 0 /* future use */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000240 HTTP_CONTINUE = 100,
241 HTTP_SWITCHING_PROTOCOLS = 101,
242 HTTP_CREATED = 201,
243 HTTP_ACCEPTED = 202,
244 HTTP_NON_AUTHORITATIVE_INFO = 203,
245 HTTP_NO_CONTENT = 204,
246 HTTP_MULTIPLE_CHOICES = 300,
247 HTTP_MOVED_PERMANENTLY = 301,
248 HTTP_NOT_MODIFIED = 304,
249 HTTP_PAYMENT_REQUIRED = 402,
250 HTTP_BAD_GATEWAY = 502,
251 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
252 HTTP_RESPONSE_SETSIZE = 0xffffffff
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000253#endif
254} HttpResponseNum;
255
Denis Vlasenko55a99402006-09-30 20:41:44 +0000256typedef struct {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000257 HttpResponseNum type;
258 const char *name;
259 const char *info;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000260} HttpEnumString;
261
262static const HttpEnumString httpResponseNames[] = {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000263 { HTTP_OK, "OK", NULL },
264 { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
265 { HTTP_REQUEST_TIMEOUT, "Request Timeout",
266 "No request appeared within a reasonable time period." },
267 { HTTP_NOT_IMPLEMENTED, "Not Implemented",
268 "The requested method is not recognized by this server." },
Denis Vlasenko55a99402006-09-30 20:41:44 +0000269#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000270 { HTTP_UNAUTHORIZED, "Unauthorized", "" },
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000271#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000272 { HTTP_NOT_FOUND, "Not Found",
273 "The requested URL was not found on this server." },
274 { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
275 { HTTP_FORBIDDEN, "Forbidden", "" },
276 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
277 "Internal Server Error" },
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000278#if 0 /* not implemented */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000279 { HTTP_CREATED, "Created" },
280 { HTTP_ACCEPTED, "Accepted" },
281 { HTTP_NO_CONTENT, "No Content" },
282 { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
283 { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
284 { HTTP_NOT_MODIFIED, "Not Modified" },
285 { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
286 { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000287#endif
288};
289
Glenn L McGrath06e95652003-02-09 06:51:14 +0000290
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000291static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
Denis Vlasenko0bb993f2006-11-21 00:06:28 +0000292
293
294#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000295
296
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000297static int scan_ip(const char **ep, unsigned *ip, unsigned char endc)
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000298{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000299 const char *p = *ep;
300 int auto_mask = 8;
301 int j;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000302
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000303 *ip = 0;
304 for (j = 0; j < 4; j++) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000305 unsigned octet;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000306
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000307 if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
308 return -auto_mask;
309 octet = 0;
310 while (*p >= '0' && *p <= '9') {
311 octet *= 10;
312 octet += *p - '0';
313 if (octet > 255)
314 return -auto_mask;
315 p++;
316 }
317 if (*p == '.')
318 p++;
319 if (*p != '/' && *p != 0)
320 auto_mask += 8;
321 *ip = ((*ip) << 8) | octet;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000322 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000323 if (*p != 0) {
324 if (*p != endc)
325 return -auto_mask;
326 p++;
327 if (*p == 0)
328 return -auto_mask;
329 }
330 *ep = p;
331 return auto_mask;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000332}
333
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000334static int scan_ip_mask(const char *ipm, unsigned *ip, unsigned *mask)
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000335{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000336 int i;
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000337 unsigned msk;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000338
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000339 i = scan_ip(&ipm, ip, '/');
340 if (i < 0)
341 return i;
342 if (*ipm) {
343 const char *p = ipm;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000344
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000345 i = 0;
346 while (*p) {
Denis Vlasenko92758142006-10-03 19:56:34 +0000347 if (*p < '0' || *p > '9') {
348 if (*p == '.') {
349 i = scan_ip(&ipm, mask, 0);
350 return i != 32;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000351 }
Denis Vlasenko92758142006-10-03 19:56:34 +0000352 return -1;
353 }
354 i *= 10;
355 i += *p - '0';
356 p++;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000357 }
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000358 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000359 if (i > 32 || i < 0)
Denis Vlasenko92758142006-10-03 19:56:34 +0000360 return -1;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000361 msk = 0x80000000;
362 *mask = 0;
363 while (i > 0) {
364 *mask |= msk;
365 msk >>= 1;
366 i--;
367 }
368 return 0;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000369}
370
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000371#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
372 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
373 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000374static void free_config_lines(Htaccess **pprev)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000375{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000376 Htaccess *prev = *pprev;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000377
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000378 while (prev) {
379 Htaccess *cur = prev;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000380
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000381 prev = cur->next;
382 free(cur);
383 }
384 *pprev = NULL;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000385}
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000386#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000387
Glenn L McGrath06e95652003-02-09 06:51:14 +0000388/* flag */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000389#define FIRST_PARSE 0
390#define SUBDIR_PARSE 1
391#define SIGNALED_PARSE 2
392#define FIND_FROM_HTTPD_ROOT 3
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +0000393/****************************************************************************
394 *
395 > $Function: parse_conf()
396 *
397 * $Description: parse configuration file into in-memory linked list.
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000398 *
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +0000399 * The first non-white character is examined to determine if the config line
400 * is one of the following:
401 * .ext:mime/type # new mime type not compiled into httpd
402 * [adAD]:from # ip address allow/deny, * for wildcard
403 * /path:user:pass # username/password
404 *
405 * Any previous IP rules are discarded.
406 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
407 * are also discarded. That is, previous settings are retained if flag is
408 * SUBDIR_PARSE.
409 *
410 * $Parameters:
411 * (const char *) path . . null for ip address checks, path for password
412 * checks.
413 * (int) flag . . . . . . the source of the parse request.
414 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000415 * $Return: (None)
Glenn L McGrath4fe3ff82003-05-19 05:56:16 +0000416 *
417 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000418static void parse_conf(const char *path, int flag)
419{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000420 FILE *f;
Denis Vlasenko55a99402006-09-30 20:41:44 +0000421#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000422 Htaccess *prev;
423#endif
424#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
425 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
426 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000427 Htaccess *cur;
Glenn L McGrathbaaa6e92003-09-15 15:00:43 +0000428#endif
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000429
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000430 const char *cf = configFile;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000431 char buf[160];
432 char *p0 = NULL;
433 char *c, *p;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000434
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000435 /* free previous ip setup if present */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000436 Htaccess_IP *pip = ip_a_d;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000437
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000438 while (pip) {
439 Htaccess_IP *cur_ipl = pip;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000440
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000441 pip = cur_ipl->next;
442 free(cur_ipl);
443 }
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000444 ip_a_d = NULL;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000445
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000446 flg_deny_all = 0;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000447
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000448#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
449 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
450 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000451 /* retain previous auth and mime config only for subdir parse */
452 if (flag != SUBDIR_PARSE) {
Denis Vlasenko55a99402006-09-30 20:41:44 +0000453#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000454 free_config_lines(&g_auth);
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000455#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000456#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000457 free_config_lines(&mime_a);
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000458#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000459#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000460 free_config_lines(&script_i);
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +0000461#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +0000462 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000463#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000464
465 if (flag == SUBDIR_PARSE || cf == NULL) {
466 cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
467 if (cf == NULL) {
468 if (flag == FIRST_PARSE)
469 bb_error_msg_and_die(bb_msg_memory_exhausted);
470 return;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000471 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000472 sprintf((char *)cf, "%s/%s", path, httpd_conf);
Glenn L McGrath393183d2003-05-26 14:07:50 +0000473 }
474
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000475 while ((f = fopen(cf, "r")) == NULL) {
476 if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
477 /* config file not found, no changes to config */
478 return;
479 }
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000480 if (configFile && flag == FIRST_PARSE) /* if -c option given */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000481 bb_perror_msg_and_die("%s", cf);
482 flag = FIND_FROM_HTTPD_ROOT;
483 cf = httpd_conf;
484 }
485
Denis Vlasenko55a99402006-09-30 20:41:44 +0000486#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000487 prev = g_auth;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000488#endif
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000489 /* This could stand some work */
Denis Vlasenko55a99402006-09-30 20:41:44 +0000490 while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000491 c = NULL;
492 for (p = p0; *p0 != 0 && *p0 != '#'; p0++) {
493 if (!isspace(*p0)) {
494 *p++ = *p0;
495 if (*p0 == ':' && c == NULL)
496 c = p;
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000497 }
Glenn L McGrathb65422c2003-09-08 10:59:27 +0000498 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000499 *p = 0;
500
501 /* test for empty or strange line */
502 if (c == NULL || *c == 0)
503 continue;
504 p0 = buf;
505 if (*p0 == 'd')
506 *p0 = 'D';
507 if (*c == '*') {
508 if (*p0 == 'D') {
509 /* memorize deny all */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000510 flg_deny_all = 1;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000511 }
512 /* skip default other "word:*" config lines */
513 continue;
514 }
515
516 if (*p0 == 'a')
517 *p0 = 'A';
518 else if (*p0 != 'D' && *p0 != 'A'
Denis Vlasenko55a99402006-09-30 20:41:44 +0000519#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000520 && *p0 != '/'
521#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000522#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000523 && *p0 != '.'
524#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000525#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000526 && *p0 != '*'
527#endif
528 )
529 continue;
530 if (*p0 == 'A' || *p0 == 'D') {
531 /* storing current config IP line */
Denis Vlasenko9b1381f2007-01-03 02:56:00 +0000532 pip = xzalloc(sizeof(Htaccess_IP));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000533 if (pip) {
Denis Vlasenko55a99402006-09-30 20:41:44 +0000534 if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000535 /* syntax IP{/mask} error detected, protect all */
536 *p0 = 'D';
537 pip->mask = 0;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000538 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000539 pip->allow_deny = *p0;
540 if (*p0 == 'D') {
541 /* Deny:form_IP move top */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000542 pip->next = ip_a_d;
543 ip_a_d = pip;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000544 } else {
545 /* add to bottom A:form_IP config line */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000546 Htaccess_IP *prev_IP = ip_a_d;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000547
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000548 if (prev_IP == NULL) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000549 ip_a_d = pip;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000550 } else {
551 while (prev_IP->next)
552 prev_IP = prev_IP->next;
553 prev_IP->next = pip;
554 }
555 }
556 }
557 continue;
558 }
Denis Vlasenko55a99402006-09-30 20:41:44 +0000559#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000560 if (*p0 == '/') {
561 /* make full path from httpd root / curent_path / config_line_path */
562 cf = flag == SUBDIR_PARSE ? path : "";
563 p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
564 if (p0 == NULL)
565 continue;
566 c[-1] = 0;
567 sprintf(p0, "/%s%s", cf, buf);
568
569 /* another call bb_simplify_path */
570 cf = p = p0;
571
572 do {
573 if (*p == '/') {
574 if (*cf == '/') { /* skip duplicate (or initial) slash */
575 continue;
576 } else if (*cf == '.') {
577 if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
578 continue;
579 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
580 ++cf;
581 if (p > p0) {
Denis Vlasenko92758142006-10-03 19:56:34 +0000582 while (*--p != '/') /* omit previous dir */;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000583 }
584 continue;
585 }
586 }
587 }
588 *++p = *cf;
589 } while (*++cf);
590
591 if ((p == p0) || (*p != '/')) { /* not a trailing slash */
592 ++p; /* so keep last character */
593 }
594 *p = 0;
595 sprintf(p0, "%s:%s", p0, c);
596 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000597#endif
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000598
Denis Vlasenko3bba5452006-12-30 17:57:03 +0000599#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
600 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
601 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000602 /* storing current config line */
Denis Vlasenko9b1381f2007-01-03 02:56:00 +0000603 cur = xzalloc(sizeof(Htaccess) + strlen(p0));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000604 if (cur) {
605 cf = strcpy(cur->before_colon, p0);
606 c = strchr(cf, ':');
607 *c++ = 0;
608 cur->after_colon = c;
Denis Vlasenko55a99402006-09-30 20:41:44 +0000609#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000610 if (*cf == '.') {
611 /* config .mime line move top for overwrite previous */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000612 cur->next = mime_a;
613 mime_a = cur;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000614 continue;
615 }
Glenn L McGrath06e95652003-02-09 06:51:14 +0000616#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000617#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000618 if (*cf == '*' && cf[1] == '.') {
619 /* config script interpreter line move top for overwrite previous */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000620 cur->next = script_i;
621 script_i = cur;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000622 continue;
623 }
"Vladimir N. Oleynik"4333a092006-01-31 13:53:30 +0000624#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +0000625#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000626 free(p0);
627 if (prev == NULL) {
628 /* first line */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000629 g_auth = prev = cur;
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000630 } else {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000631 /* sort path, if current lenght eq or bigger then move up */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000632 Htaccess *prev_hti = g_auth;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000633 size_t l = strlen(cf);
634 Htaccess *hti;
635
636 for (hti = prev_hti; hti; hti = hti->next) {
637 if (l >= strlen(hti->before_colon)) {
638 /* insert before hti */
639 cur->next = hti;
640 if (prev_hti != hti) {
641 prev_hti->next = cur;
642 } else {
643 /* insert as top */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000644 g_auth = cur;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000645 }
646 break;
647 }
648 if (prev_hti != hti)
649 prev_hti = prev_hti->next;
650 }
651 if (!hti) { /* not inserted, add to bottom */
652 prev->next = cur;
653 prev = cur;
654 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000655 }
Glenn L McGrathc9163fe2003-05-13 16:20:11 +0000656#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000657 }
Glenn L McGrathbaaa6e92003-09-15 15:00:43 +0000658#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000659 }
660 fclose(f);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000661}
662
Denis Vlasenko55a99402006-09-30 20:41:44 +0000663#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000664/****************************************************************************
665 *
666 > $Function: encodeString()
667 *
668 * $Description: Given a string, html encode special characters.
669 * This is used for the -e command line option to provide an easy way
670 * for scripts to encode result data without confusing browsers. The
671 * returned string pointer is memory allocated by malloc().
672 *
673 * $Parameters:
674 * (const char *) string . . The first string to encode.
675 *
676 * $Return: (char *) . . . .. . . A pointer to the encoded string.
677 *
678 * $Errors: Returns a null string ("") if memory is not available.
679 *
680 ****************************************************************************/
681static char *encodeString(const char *string)
682{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000683 /* take the simple route and encode everything */
684 /* could possibly scan once to get length. */
685 int len = strlen(string);
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000686 char *out = xmalloc(len * 6 + 1);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000687 char *p = out;
688 char ch;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000689
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000690 while ((ch = *string++)) {
691 // very simple check for what to encode
692 if (isalnum(ch)) *p++ = ch;
693 else p += sprintf(p, "&#%d;", (unsigned char) ch);
694 }
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000695 *p = '\0';
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000696 return out;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000697}
Denis Vlasenkob3a07152006-11-16 18:04:43 +0000698#endif /* FEATURE_HTTPD_ENCODE_URL_STR */
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000699
700/****************************************************************************
701 *
702 > $Function: decodeString()
703 *
704 * $Description: Given a URL encoded string, convert it to plain ascii.
705 * Since decoding always makes strings smaller, the decode is done in-place.
706 * Thus, callers should strdup() the argument if they do not want the
707 * argument modified. The return is the original pointer, allowing this
708 * function to be easily used as arguments to other functions.
709 *
710 * $Parameters:
711 * (char *) string . . . The first string to decode.
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +0000712 * (int) option_d . . 1 if called for httpd -d
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000713 *
714 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
715 *
716 * $Errors: None
717 *
718 ****************************************************************************/
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +0000719static char *decodeString(char *orig, int option_d)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000720{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000721 /* note that decoded string is always shorter than original */
722 char *string = orig;
723 char *ptr = string;
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +0000724 char c;
Eric Andersena3bb3e62003-06-26 09:05:32 +0000725
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +0000726 while ((c = *ptr++) != '\0') {
727 unsigned value1, value2;
728
729 if (option_d && c == '+') {
Denis Vlasenko601ae132006-11-28 23:37:46 +0000730 *string++ = ' ';
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +0000731 continue;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000732 }
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +0000733 if (c != '%') {
734 *string++ = c;
735 continue;
736 }
737 if (sscanf(ptr, "%1X", &value1) != 1
738 || sscanf(ptr+1, "%1X", &value2) != 1
739 ) {
740 if (!option_d)
741 return NULL;
742 *string++ = '%';
743 continue;
744 }
745 value1 = value1 * 16 + value2;
746 if (!option_d && (value1 == '/' || value1 == '\0')) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000747 /* caller takes it as indication of invalid
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +0000748 * (dangerous wrt exploits) chars */
749 return orig + 1;
750 }
751 *string++ = value1;
752 ptr += 2;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000753 }
754 *string = '\0';
755 return orig;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000756}
757
758
Denis Vlasenko55a99402006-09-30 20:41:44 +0000759#if ENABLE_FEATURE_HTTPD_CGI
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000760/****************************************************************************
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000761 * setenv helpers
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000762 ****************************************************************************/
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000763static void setenv1(const char *name, const char *value)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000764{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000765 if (!value)
766 value = "";
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000767 setenv(name, value, 1);
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000768}
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000769static void setenv_long(const char *name, long value)
Glenn L McGrath06e95652003-02-09 06:51:14 +0000770{
Denis Vlasenkoe867b7c2006-11-16 16:12:09 +0000771 char buf[sizeof(value)*3 + 1];
772 sprintf(buf, "%ld", value);
773 setenv(name, buf, 1);
Glenn L McGrath06e95652003-02-09 06:51:14 +0000774}
Eric Andersena3bb3e62003-06-26 09:05:32 +0000775#endif
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000776
Denis Vlasenko55a99402006-09-30 20:41:44 +0000777#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000778/****************************************************************************
779 *
780 > $Function: decodeBase64()
781 *
782 > $Description: Decode a base 64 data stream as per rfc1521.
783 * Note that the rfc states that none base64 chars are to be ignored.
784 * Since the decode always results in a shorter size than the input, it is
785 * OK to pass the input arg as an output arg.
786 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000787 * $Parameter:
788 * (char *) Data . . . . A pointer to a base64 encoded string.
789 * Where to place the decoded data.
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000790 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000791 * $Return: void
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000792 *
793 * $Errors: None
794 *
795 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000796static void decodeBase64(char *Data)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000797{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000798 const unsigned char *in = (const unsigned char *)Data;
799 // The decoded size will be at most 3/4 the size of the encoded
Denis Vlasenko088b9592007-04-18 21:14:46 +0000800 unsigned ch = 0;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000801 int i = 0;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000802
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000803 while (*in) {
804 int t = *in++;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000805
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000806 if (t >= '0' && t <= '9')
807 t = t - '0' + 52;
808 else if (t >= 'A' && t <= 'Z')
809 t = t - 'A';
810 else if (t >= 'a' && t <= 'z')
811 t = t - 'a' + 26;
812 else if (t == '+')
813 t = 62;
814 else if (t == '/')
815 t = 63;
816 else if (t == '=')
817 t = 0;
818 else
819 continue;
Glenn L McGrath874e3382003-05-14 12:11:36 +0000820
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000821 ch = (ch << 6) | t;
822 i++;
823 if (i == 4) {
824 *Data++ = (char) (ch >> 16);
825 *Data++ = (char) (ch >> 8);
826 *Data++ = (char) ch;
827 i = 0;
828 }
829 }
Denis Vlasenko088b9592007-04-18 21:14:46 +0000830 *Data = '\0';
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000831}
832#endif
833
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000834
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000835/****************************************************************************
836 *
837 > $Function: openServer()
838 *
839 * $Description: create a listen server socket on the designated port.
840 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000841 * $Return: (int) . . . A connection socket. -1 for errors.
842 *
843 * $Errors: None
844 *
845 ****************************************************************************/
Denis Vlasenko56258b62007-06-23 23:14:02 +0000846#if BB_MMU
Glenn L McGrath06e95652003-02-09 06:51:14 +0000847static int openServer(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000848{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000849 int fd;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000850
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000851 /* create the socket right now */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000852 fd = create_and_bind_stream_or_die(NULL, tcp_port);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000853 xlisten(fd, 9);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000854 return fd;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000855}
Denis Vlasenko56258b62007-06-23 23:14:02 +0000856#endif
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000857
858/****************************************************************************
859 *
860 > $Function: sendHeaders()
861 *
862 * $Description: Create and send HTTP response headers.
863 * The arguments are combined and sent as one write operation. Note that
864 * IE will puke big-time if the headers are not sent in one packet and the
Glenn L McGrath06e95652003-02-09 06:51:14 +0000865 * second packet is delayed for any reason.
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000866 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000867 * $Parameter:
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000868 * (HttpResponseNum) responseNum . . . The result code to send.
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000869 *
Glenn L McGrath06e95652003-02-09 06:51:14 +0000870 * $Return: (int) . . . . writing errors
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000871 *
872 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000873static int sendHeaders(HttpResponseNum responseNum)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000874{
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000875 char *buf = iobuf;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000876 const char *responseString = "";
877 const char *infoString = 0;
878 const char *mime_type;
Denis Vlasenkob64eed62007-01-14 17:06:11 +0000879 unsigned i;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000880 time_t timer = time(0);
881 char timeStr[80];
882 int len;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000883
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000884 for (i = 0; i < ARRAY_SIZE(httpResponseNames); i++) {
Glenn L McGrath06e95652003-02-09 06:51:14 +0000885 if (httpResponseNames[i].type == responseNum) {
886 responseString = httpResponseNames[i].name;
887 infoString = httpResponseNames[i].info;
888 break;
889 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000890 }
891 /* error message is HTML */
892 mime_type = responseNum == HTTP_OK ?
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000893 found_mime_type : "text/html";
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000894
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000895 /* emit the current date */
896 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
897 len = sprintf(buf,
Denis Vlasenko69981422007-01-07 21:25:12 +0000898 "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
899 "Date: %s\r\nConnection: close\r\n",
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000900 responseNum, responseString, mime_type, timeStr);
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000901
Denis Vlasenko55a99402006-09-30 20:41:44 +0000902#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000903 if (responseNum == HTTP_UNAUTHORIZED) {
Denis Vlasenko8e858e22007-03-07 09:35:43 +0000904 len += sprintf(buf+len,
905 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000906 g_realm);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000907 }
Glenn L McGrath3d2405c2003-02-10 22:28:21 +0000908#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000909 if (responseNum == HTTP_MOVED_TEMPORARILY) {
910 len += sprintf(buf+len, "Location: %s/%s%s\r\n",
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000911 found_moved_temporarily,
912 (g_query ? "?" : ""),
913 (g_query ? g_query : ""));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000914 }
Eric Andersen07f2fea2004-10-08 08:03:29 +0000915
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000916 if (ContentLength != -1) { /* file */
917 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&last_mod));
Denis Vlasenkocf30cc82006-11-24 14:53:18 +0000918 len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000919 timeStr, "Content-length:", ContentLength);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000920 }
921 strcat(buf, "\r\n");
922 len += 2;
923 if (infoString) {
924 len += sprintf(buf+len,
925 "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
926 "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
927 responseNum, responseString,
928 responseNum, responseString, infoString);
929 }
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +0000930 if (DEBUG)
931 fprintf(stderr, "headers: '%s'\n", buf);
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000932 i = accepted_socket;
Denis Vlasenkob64eed62007-01-14 17:06:11 +0000933 if (i == 0) i++; /* write to fd# 1 in inetd mode */
934 return full_write(i, buf, len);
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000935}
936
937/****************************************************************************
938 *
939 > $Function: getLine()
940 *
941 * $Description: Read from the socket until an end of line char found.
942 *
943 * Characters are read one at a time until an eol sequence is found.
944 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000945 * $Return: (int) . . . . number of characters read. -1 if error.
946 *
947 ****************************************************************************/
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000948static int getLine(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000949{
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +0000950 int count = 0;
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000951 char *buf = iobuf;
Glenn L McGrath06e95652003-02-09 06:51:14 +0000952
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000953 while (read(accepted_socket, buf + count, 1) == 1) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000954 if (buf[count] == '\r') continue;
955 if (buf[count] == '\n') {
956 buf[count] = 0;
957 return count;
958 }
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +0000959 if (count < (MAX_MEMORY_BUFF-1)) /* check overflow */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000960 count++;
961 }
962 if (count) return count;
963 else return -1;
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000964}
965
Denis Vlasenko55a99402006-09-30 20:41:44 +0000966#if ENABLE_FEATURE_HTTPD_CGI
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000967/****************************************************************************
968 *
969 > $Function: sendCgi()
970 *
971 * $Description: Execute a CGI script and send it's stdout back
972 *
973 * Environment variables are set up and the script is invoked with pipes
974 * for stdin/stdout. If a post is being done the script is fed the POST
975 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
976 *
977 * $Parameters:
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000978 * (const char *) url . . . . . . The requested URL (with leading /).
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +0000979 * (int bodyLen) . . . . . . . . Length of the post body.
980 * (const char *cookie) . . . . . For set HTTP_COOKIE.
981 * (const char *content_type) . . For set CONTENT_TYPE.
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000982 *
983 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
984 *
985 * $Errors: None
986 *
987 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +0000988static int sendCgi(const char *url,
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +0000989 const char *request, int bodyLen, const char *cookie,
990 const char *content_type)
Glenn L McGrath58c708a2003-01-05 04:01:56 +0000991{
Denis Vlasenkoe5d37cc2007-08-11 20:20:02 +0000992 struct { int rd; int wr; } fromCgi; /* CGI -> httpd pipe */
993 struct { int rd; int wr; } toCgi; /* httpd -> CGI pipe */
Denis Vlasenko77e44d62007-06-09 23:49:05 +0000994 char *fullpath;
995 char *argp[] = { NULL, NULL };
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +0000996 int pid = 0;
997 int inFd;
998 int outFd;
Denis Vlasenkob5368bf2007-02-13 23:42:54 +0000999 int buf_count;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001000 int status;
Denis Vlasenko69981422007-01-07 21:25:12 +00001001 size_t post_read_size, post_read_idx;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001002
Denis Vlasenkoe5d37cc2007-08-11 20:20:02 +00001003 if (pipe(&fromCgi.rd) != 0)
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001004 return 0;
Denis Vlasenkoe5d37cc2007-08-11 20:20:02 +00001005 if (pipe(&toCgi.rd) != 0)
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001006 return 0;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001007
Denis Vlasenko80281fe2007-03-07 22:16:38 +00001008/*
1009 * Note: We can use vfork() here in the no-mmu case, although
1010 * the child modifies the parent's variables, due to:
1011 * 1) The parent does not use the child-modified variables.
1012 * 2) The allocated memory (in the child) is freed when the process
1013 * exits. This happens instantly after the child finishes,
1014 * since httpd is run from inetd (and it can't run standalone
1015 * in uClinux).
1016 */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001017
1018// FIXME: setenv leaks memory! (old values of env vars are leaked)
1019// Thus we have a bug on NOMMU.
1020// Need to use this instead:
1021// [malloc +] putenv + (in child: exec) + (in parent: unsetenv [+ free])
1022
Denis Vlasenko473dae02007-04-11 07:04:23 +00001023#if !BB_MMU
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001024 fullpath = NULL;
Denis Vlasenko80281fe2007-03-07 22:16:38 +00001025 pid = vfork();
1026#else
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001027 pid = fork();
Denis Vlasenko80281fe2007-03-07 22:16:38 +00001028#endif
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001029 if (pid < 0)
1030 return 0;
Denis Vlasenkof7996f32007-01-11 17:20:00 +00001031
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001032 if (!pid) {
1033 /* child process */
1034 char *script;
Denis Vlasenkoa3055842007-02-11 19:51:06 +00001035 char *purl;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001036
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001037 if (accepted_socket > 1)
1038 close(accepted_socket);
1039 if (server_socket > 1)
1040 close(server_socket);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001041
Denis Vlasenkoe5d37cc2007-08-11 20:20:02 +00001042 xmove_fd(toCgi.rd, 0); /* replace stdin with the pipe */
1043 xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */
1044 close(fromCgi.rd);
1045 close(toCgi.wr);
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001046 /* Huh? User seeing stderr can be a security problem.
1047 * If CGI really wants that, it can always dup2(1,2). */
1048 /* dup2(1, 2); */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001049
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001050 /*
1051 * Find PATH_INFO.
1052 */
Denis Vlasenkoa3055842007-02-11 19:51:06 +00001053 xfunc_error_retval = 242;
1054 purl = xstrdup(url);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001055 script = purl;
1056 while ((script = strchr(script + 1, '/')) != NULL) {
1057 /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
1058 struct stat sb;
Denis Vlasenko9f609292006-11-05 19:47:33 +00001059
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001060 *script = '\0';
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001061 if (is_directory(purl + 1, 1, &sb) == 0) {
1062 /* not directory, found script.cgi/PATH_INFO */
1063 *script = '/';
1064 break;
1065 }
1066 *script = '/'; /* is directory, find next '/' */
1067 }
1068 setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001069 setenv1("REQUEST_METHOD", request);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001070 if (g_query) {
1071 char *uri = alloca(strlen(purl) + 2 + strlen(g_query));
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001072 if (uri)
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001073 sprintf(uri, "%s?%s", purl, g_query);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001074 setenv1("REQUEST_URI", uri);
1075 } else {
1076 setenv1("REQUEST_URI", purl);
1077 }
1078 if (script != NULL)
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001079 *script = '\0'; /* cut off /PATH_INFO */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001080
1081 /* SCRIPT_FILENAME required by PHP in CGI mode */
1082 fullpath = concat_path_file(home_httpd, purl);
1083 setenv1("SCRIPT_FILENAME", fullpath);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001084 /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
1085 setenv1("SCRIPT_NAME", purl);
Denis Vlasenko428f7ae2006-11-21 21:35:14 +00001086 /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
1087 * QUERY_STRING: The information which follows the ? in the URL
1088 * which referenced this script. This is the query information.
1089 * It should not be decoded in any fashion. This variable
1090 * should always be set when there is query information,
1091 * regardless of command line decoding. */
Denis Vlasenkoa773af32007-01-03 23:02:18 +00001092 /* (Older versions of bbox seem to do some decoding) */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001093 setenv1("QUERY_STRING", g_query);
1094 putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
Denis Vlasenkoab2aea42007-01-29 22:51:58 +00001095 putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
1096 putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001097 /* Having _separate_ variables for IP and port defeats
1098 * the purpose of having socket abstraction. Which "port"
1099 * are you using on Unix domain socket?
1100 * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
1101 * Oh well... */
1102 {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001103 char *p = rmt_ip_str ? : (char*)"";
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001104 char *cp = strrchr(p, ':');
1105 if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
1106 cp = NULL;
1107 if (cp) *cp = '\0'; /* delete :PORT */
1108 setenv1("REMOTE_ADDR", p);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001109 if (cp) *cp = ':';
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001110 }
Denis Vlasenko6cd84da2007-07-21 14:57:54 +00001111 setenv1("HTTP_USER_AGENT", user_agent);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001112#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001113 setenv_long("REMOTE_PORT", tcp_port);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001114#endif
1115 if (bodyLen)
1116 setenv_long("CONTENT_LENGTH", bodyLen);
1117 if (cookie)
1118 setenv1("HTTP_COOKIE", cookie);
1119 if (content_type)
1120 setenv1("CONTENT_TYPE", content_type);
1121#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001122 if (remoteuser) {
1123 setenv1("REMOTE_USER", remoteuser);
Denis Vlasenkoab2aea42007-01-29 22:51:58 +00001124 putenv((char*)"AUTH_TYPE=Basic");
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001125 }
1126#endif
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001127 if (referer)
1128 setenv1("HTTP_REFERER", referer);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001129
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001130 /* set execve argp[0] without path */
Denis Vlasenkodc757aa2007-06-30 08:04:05 +00001131 argp[0] = (char*)bb_basename(purl);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001132 /* but script argp[0] must have absolute path and chdiring to this */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001133 script = strrchr(fullpath, '/');
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001134 if (!script)
1135 goto error_execing_cgi;
1136 *script = '\0';
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001137 if (chdir(fullpath) == 0) {
Denis Vlasenkoa773af32007-01-03 23:02:18 +00001138 // Now run the program. If it fails,
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001139 // use _exit() so no destructors
1140 // get called and make a mess.
1141#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
1142 char *interpr = NULL;
1143 char *suffix = strrchr(purl, '.');
1144
1145 if (suffix) {
1146 Htaccess *cur;
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001147 for (cur = script_i; cur; cur = cur->next) {
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001148 if (strcmp(cur->before_colon + 1, suffix) == 0) {
1149 interpr = cur->after_colon;
1150 break;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001151 }
1152 }
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001153 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001154#endif
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001155 *script = '/';
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001156#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001157 if (interpr)
1158 execv(interpr, argp);
1159 else
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001160#endif
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001161 execv(fullpath, argp);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001162 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001163 error_execing_cgi:
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001164 /* send to stdout (even if we are not from inetd) */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001165 accepted_socket = 1;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001166 sendHeaders(HTTP_NOT_FOUND);
1167 _exit(242);
1168 } /* end child */
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001169
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001170 /* parent process */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001171#if !BB_MMU
1172 free(fullpath);
1173#endif
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001174
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001175 buf_count = 0;
Denis Vlasenko69981422007-01-07 21:25:12 +00001176 post_read_size = 0;
1177 post_read_idx = 0; /* for gcc */
Denis Vlasenkoe5d37cc2007-08-11 20:20:02 +00001178 inFd = fromCgi.rd;
1179 outFd = toCgi.wr;
1180 close(fromCgi.wr);
1181 close(toCgi.rd);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001182 signal(SIGPIPE, SIG_IGN);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001183
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001184 while (1) {
1185 fd_set readSet;
1186 fd_set writeSet;
1187 char wbuf[128];
1188 int nfound;
1189 int count;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001190
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001191 FD_ZERO(&readSet);
1192 FD_ZERO(&writeSet);
1193 FD_SET(inFd, &readSet);
Denis Vlasenko69981422007-01-07 21:25:12 +00001194 if (bodyLen > 0 || post_read_size > 0) {
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001195 FD_SET(outFd, &writeSet);
1196 nfound = outFd > inFd ? outFd : inFd;
Denis Vlasenko69981422007-01-07 21:25:12 +00001197 if (post_read_size == 0) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001198 FD_SET(accepted_socket, &readSet);
1199 if (nfound < accepted_socket)
1200 nfound = accepted_socket;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001201 }
1202 /* Now wait on the set of sockets! */
Denis Vlasenko69981422007-01-07 21:25:12 +00001203 nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001204 } else {
1205 if (!bodyLen) {
Denis Vlasenko69981422007-01-07 21:25:12 +00001206 close(outFd); /* no more POST data to CGI */
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001207 bodyLen = -1;
1208 }
Denis Vlasenko69981422007-01-07 21:25:12 +00001209 nfound = select(inFd + 1, &readSet, NULL, NULL, NULL);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001210 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001211
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001212 if (nfound <= 0) {
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001213 if (waitpid(pid, &status, WNOHANG) <= 0) {
Denis Vlasenko69981422007-01-07 21:25:12 +00001214 /* Weird. CGI didn't exit and no fd's
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001215 * are ready, yet select returned?! */
Denis Vlasenko69981422007-01-07 21:25:12 +00001216 continue;
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001217 }
Denis Vlasenko69981422007-01-07 21:25:12 +00001218 close(inFd);
1219 if (DEBUG && WIFEXITED(status))
1220 bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
1221 if (DEBUG && WIFSIGNALED(status))
1222 bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
1223 break;
1224 }
1225
1226 if (post_read_size > 0 && FD_ISSET(outFd, &writeSet)) {
1227 /* Have data from peer and can write to CGI */
1228 // huh? why full_write? what if we will block?
1229 // (imagine that CGI does not read its stdin...)
1230 count = full_write(outFd, wbuf + post_read_idx, post_read_size);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001231 if (count > 0) {
Denis Vlasenko69981422007-01-07 21:25:12 +00001232 post_read_idx += count;
1233 post_read_size -= count;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001234 } else {
Denis Vlasenko69981422007-01-07 21:25:12 +00001235 post_read_size = bodyLen = 0; /* broken pipe to CGI */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001236 }
Denis Vlasenko69981422007-01-07 21:25:12 +00001237 } else if (bodyLen > 0 && post_read_size == 0
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001238 && FD_ISSET(accepted_socket, &readSet)
Denis Vlasenko69981422007-01-07 21:25:12 +00001239 ) {
1240 /* We expect data, prev data portion is eaten by CGI
1241 * and there *is* data to read from the peer
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001242 * (POSTDATA?) */
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001243 count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001244 count = safe_read(accepted_socket, wbuf, count);
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001245 if (count > 0) {
Denis Vlasenko69981422007-01-07 21:25:12 +00001246 post_read_size = count;
1247 post_read_idx = 0;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001248 bodyLen -= count;
1249 } else {
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001250 bodyLen = 0; /* closed */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001251 }
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001252 }
Denis Vlasenko69981422007-01-07 21:25:12 +00001253
Denis Vlasenko69981422007-01-07 21:25:12 +00001254#define PIPESIZE PIPE_BUF
Eric Andersen97a1de12004-08-26 22:22:50 +00001255#if PIPESIZE >= MAX_MEMORY_BUFF
1256# error "PIPESIZE >= MAX_MEMORY_BUFF"
1257#endif
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001258 if (FD_ISSET(inFd, &readSet)) {
1259 /* There is something to read from CGI */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001260 int s = accepted_socket;
1261 char *rbuf = iobuf;
Denis Vlasenkoa3ee69f2006-11-21 00:07:31 +00001262
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001263 /* Are we still buffering CGI output? */
1264 if (buf_count >= 0) {
Denis Vlasenkoe5d37cc2007-08-11 20:20:02 +00001265 /* According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
1266 * CGI scripts MUST send their own header terminated by
1267 * empty line, then data. That's why we have only one
1268 * <cr><lf> pair here. We will output "200 OK" line
1269 * if needed, but CGI still has to provide blank line
1270 * between header and body */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001271 static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n";
Denis Vlasenkoe5d37cc2007-08-11 20:20:02 +00001272
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001273 /* Must use safe_read, not full_read, because
1274 * CGI may output a few first bytes and then wait
1275 * for POSTDATA without closing stdout.
1276 * With full_read we may wait here forever. */
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001277 count = safe_read(inFd, rbuf + buf_count, PIPESIZE - 8);
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001278 if (count <= 0) {
1279 /* eof (or error) and there was no "HTTP",
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001280 * so write it, then write received data */
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001281 if (buf_count) {
1282 full_write(s, HTTP_200, sizeof(HTTP_200)-1);
1283 full_write(s, rbuf, buf_count);
1284 }
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001285 break; /* closed */
Denis Vlasenko69981422007-01-07 21:25:12 +00001286 }
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001287 buf_count += count;
1288 count = 0;
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001289 /* "Status" header format is: "Status: 302 Redirected\r\n" */
Denis Vlasenko41855482007-08-11 20:26:02 +00001290 if (buf_count >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
1291 /* send "HTTP/1.0 " */
1292 if (full_write(s, HTTP_200, 9) != 9)
1293 break;
1294 rbuf += 8; /* skip "Status: " */
1295 count = buf_count - 8;
1296 buf_count = -1; /* buffering off */
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001297 } else if (buf_count >= 4) {
1298 /* Did CGI add "HTTP"? */
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001299 if (memcmp(rbuf, HTTP_200, 4) != 0) {
1300 /* there is no "HTTP", do it ourself */
1301 if (full_write(s, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
1302 break;
1303 }
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001304 /* Commented out:
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001305 if (!strstr(rbuf, "ontent-")) {
1306 full_write(s, "Content-type: text/plain\r\n\r\n", 28);
1307 }
Denis Vlasenko1ec15cd2007-08-11 20:20:43 +00001308 * Counter-example of valid CGI without Content-type:
1309 * echo -en "HTTP/1.0 302 Found\r\n"
1310 * echo -en "Location: http://www.busybox.net\r\n"
1311 * echo -en "\r\n"
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001312 */
1313 count = buf_count;
1314 buf_count = -1; /* buffering off */
Denis Vlasenkoa3055842007-02-11 19:51:06 +00001315 }
Denis Vlasenkob5368bf2007-02-13 23:42:54 +00001316 } else {
1317 count = safe_read(inFd, rbuf, PIPESIZE);
1318 if (count <= 0)
1319 break; /* eof (or error) */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001320 }
Denis Vlasenko69981422007-01-07 21:25:12 +00001321 if (full_write(s, rbuf, count) != count)
1322 break;
1323 if (DEBUG)
1324 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1325 } /* if (FD_ISSET(inFd)) */
1326 } /* while (1) */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001327 return 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001328}
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001329#endif /* FEATURE_HTTPD_CGI */
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001330
1331/****************************************************************************
1332 *
1333 > $Function: sendFile()
1334 *
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001335 * $Description: Send a file response to a HTTP request
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001336 *
Glenn L McGrath06e95652003-02-09 06:51:14 +00001337 * $Parameter:
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001338 * (const char *) url . . The URL requested.
1339 *
1340 * $Return: (int) . . . . . . Always 0.
1341 *
1342 ****************************************************************************/
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +00001343static int sendFile(const char *url)
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001344{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001345 char * suffix;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001346 int f;
1347 const char *const *table;
1348 const char *try_suffix;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001349
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001350 suffix = strrchr(url, '.');
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001351
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001352 for (table = suffixTable; *table; table += 2)
1353 if (suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
1354 try_suffix += strlen(suffix);
1355 if (*try_suffix == 0 || *try_suffix == '.')
1356 break;
1357 }
1358 /* also, if not found, set default as "application/octet-stream"; */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001359 found_mime_type = table[1];
Denis Vlasenko55a99402006-09-30 20:41:44 +00001360#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001361 if (suffix) {
1362 Htaccess * cur;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001363
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001364 for (cur = mime_a; cur; cur = cur->next) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001365 if (strcmp(cur->before_colon, suffix) == 0) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001366 found_mime_type = cur->after_colon;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001367 break;
1368 }
1369 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001370 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001371#endif /* FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001372
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001373 if (DEBUG)
1374 fprintf(stderr, "sending file '%s' content-type: %s\n",
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001375 url, found_mime_type);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001376
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001377 f = open(url, O_RDONLY);
1378 if (f >= 0) {
1379 int count;
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001380 char *buf = iobuf;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001381
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001382 sendHeaders(HTTP_OK);
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001383 /* TODO: sendfile() */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001384 while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001385 int fd = accepted_socket;
Denis Vlasenkob64eed62007-01-14 17:06:11 +00001386 if (fd == 0) fd++; /* write to fd# 1 in inetd mode */
1387 if (full_write(fd, buf, count) != count)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001388 break;
1389 }
1390 close(f);
1391 } else {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001392 if (DEBUG)
1393 bb_perror_msg("cannot open '%s'", url);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001394 sendHeaders(HTTP_NOT_FOUND);
1395 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001396
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001397 return 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001398}
1399
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001400static int checkPermIP(void)
1401{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001402 Htaccess_IP * cur;
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001403
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001404 /* This could stand some work */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001405 for (cur = ip_a_d; cur; cur = cur->next) {
Denis Vlasenkob64eed62007-01-14 17:06:11 +00001406#if ENABLE_FEATURE_HTTPD_CGI && DEBUG
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001407 fprintf(stderr, "checkPermIP: '%s' ? ", rmt_ip_str);
Denis Vlasenkob64eed62007-01-14 17:06:11 +00001408#endif
1409#if DEBUG
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001410 fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
1411 (unsigned char)(cur->ip >> 24),
1412 (unsigned char)(cur->ip >> 16),
1413 (unsigned char)(cur->ip >> 8),
1414 (unsigned char)(cur->ip),
1415 (unsigned char)(cur->mask >> 24),
1416 (unsigned char)(cur->mask >> 16),
1417 (unsigned char)(cur->mask >> 8),
1418 (unsigned char)(cur->mask)
1419 );
Denis Vlasenko3bba5452006-12-30 17:57:03 +00001420#endif
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001421 if ((rmt_ip & cur->mask) == cur->ip)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001422 return cur->allow_deny == 'A'; /* Allow/Deny */
1423 }
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001424
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001425 /* if unconfigured, return 1 - access from all */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001426 return !flg_deny_all;
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001427}
1428
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001429/****************************************************************************
1430 *
1431 > $Function: checkPerm()
1432 *
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001433 * $Description: Check the permission file for access password protected.
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001434 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001435 * If config file isn't present, everything is allowed.
Glenn L McGrath06e95652003-02-09 06:51:14 +00001436 * Entries are of the form you can see example from header source
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001437 *
1438 * $Parameters:
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001439 * (const char *) path . . . . The file path.
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001440 * (const char *) request . . . User information to validate.
1441 *
1442 * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
1443 *
1444 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +00001445
Denis Vlasenko55a99402006-09-30 20:41:44 +00001446#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001447static int checkPerm(const char *path, const char *request)
1448{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001449 Htaccess * cur;
1450 const char *p;
1451 const char *p0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001452
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001453 const char *prev = NULL;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001454
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001455 /* This could stand some work */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001456 for (cur = g_auth; cur; cur = cur->next) {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001457 size_t l;
1458
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001459 p0 = cur->before_colon;
1460 if (prev != NULL && strcmp(prev, p0) != 0)
1461 continue; /* find next identical */
1462 p = cur->after_colon;
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001463 if (DEBUG)
1464 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001465
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001466 l = strlen(p0);
1467 if (strncmp(p0, path, l) == 0
1468 && (l == 1 || path[l] == '/' || path[l] == '\0')
1469 ) {
1470 char *u;
1471 /* path match found. Check request */
1472 /* for check next /path:user:password */
1473 prev = p0;
1474 u = strchr(request, ':');
1475 if (u == NULL) {
1476 /* bad request, ':' required */
1477 break;
1478 }
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +00001479
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001480 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
1481 char *cipher;
1482 char *pp;
Eric Andersen35e643b2003-07-28 07:40:39 +00001483
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001484 if (strncmp(p, request, u-request) != 0) {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001485 /* user uncompared */
1486 continue;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001487 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001488 pp = strchr(p, ':');
1489 if (pp && pp[1] == '$' && pp[2] == '1' &&
1490 pp[3] == '$' && pp[4]) {
1491 pp++;
1492 cipher = pw_encrypt(u+1, pp);
1493 if (strcmp(cipher, pp) == 0)
1494 goto set_remoteuser_var; /* Ok */
1495 /* unauthorized */
1496 continue;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001497 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001498 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001499
1500 if (strcmp(p, request) == 0) {
1501set_remoteuser_var:
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001502 remoteuser = strdup(request);
1503 if (remoteuser)
1504 remoteuser[(u - request)] = 0;
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001505 return 1; /* Ok */
1506 }
1507 /* unauthorized */
Glenn L McGrath1dc0cca2003-10-03 10:50:56 +00001508 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001509 } /* for */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001510
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001511 return prev == NULL;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001512}
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001513
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001514#endif /* FEATURE_HTTPD_BASIC_AUTH */
Glenn L McGrathc9163fe2003-05-13 16:20:11 +00001515
Eric Andersen07f2fea2004-10-08 08:03:29 +00001516/****************************************************************************
1517 *
Mike Frysingerbb12d6f2006-01-03 23:59:01 +00001518 > $Function: handle_sigalrm()
Eric Andersen07f2fea2004-10-08 08:03:29 +00001519 *
Mike Frysingerbb12d6f2006-01-03 23:59:01 +00001520 * $Description: Handle timeouts
Eric Andersen07f2fea2004-10-08 08:03:29 +00001521 *
1522 ****************************************************************************/
1523
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001524static void handle_sigalrm(int sig)
Eric Andersen07f2fea2004-10-08 08:03:29 +00001525{
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001526 sendHeaders(HTTP_REQUEST_TIMEOUT);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001527 alarm_signaled = 1;
Eric Andersen07f2fea2004-10-08 08:03:29 +00001528}
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001529
1530/****************************************************************************
1531 *
1532 > $Function: handleIncoming()
1533 *
1534 * $Description: Handle an incoming http request.
1535 *
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001536 ****************************************************************************/
Glenn L McGrath06e95652003-02-09 06:51:14 +00001537static void handleIncoming(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001538{
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001539 char *buf = iobuf;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001540 char *url;
1541 char *purl;
1542 int blank = -1;
1543 char *test;
1544 struct stat sb;
1545 int ip_allowed;
Denis Vlasenko55a99402006-09-30 20:41:44 +00001546#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001547 const char *prequest = request_GET;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001548 unsigned long length = 0;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001549 char *cookie = 0;
1550 char *content_type = 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001551#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001552 fd_set s_fd;
1553 struct timeval tv;
1554 int retval;
1555 struct sigaction sa;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001556
Denis Vlasenko55a99402006-09-30 20:41:44 +00001557#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001558 int credentials = -1; /* if not required this is Ok */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001559#endif
1560
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001561 sa.sa_handler = handle_sigalrm;
1562 sigemptyset(&sa.sa_mask);
1563 sa.sa_flags = 0; /* no SA_RESTART */
1564 sigaction(SIGALRM, &sa, NULL);
Eric Andersen07f2fea2004-10-08 08:03:29 +00001565
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001566 do {
1567 int count;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001568
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001569 (void) alarm(TIMEOUT);
1570 if (getLine() <= 0)
1571 break; /* closed */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001572
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001573 purl = strpbrk(buf, " \t");
1574 if (purl == NULL) {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001575 BAD_REQUEST:
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001576 sendHeaders(HTTP_BAD_REQUEST);
1577 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001578 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001579 *purl = '\0';
Denis Vlasenko55a99402006-09-30 20:41:44 +00001580#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001581 if (strcasecmp(buf, prequest) != 0) {
1582 prequest = "POST";
1583 if (strcasecmp(buf, prequest) != 0) {
1584 sendHeaders(HTTP_NOT_IMPLEMENTED);
1585 break;
1586 }
1587 }
1588#else
1589 if (strcasecmp(buf, request_GET) != 0) {
1590 sendHeaders(HTTP_NOT_IMPLEMENTED);
1591 break;
1592 }
1593#endif
1594 *purl = ' ';
1595 count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001596
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001597 if (count < 1 || buf[0] != '/') {
1598 /* Garbled request/URL */
1599 goto BAD_REQUEST;
1600 }
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001601 url = alloca(strlen(buf) + sizeof("/index.html"));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001602 if (url == NULL) {
1603 sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
1604 break;
1605 }
1606 strcpy(url, buf);
1607 /* extract url args if present */
1608 test = strchr(url, '?');
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001609 g_query = NULL;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001610 if (test) {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001611 *test++ = '\0';
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001612 g_query = test;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001613 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001614
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001615 test = decodeString(url, 0);
1616 if (test == NULL)
1617 goto BAD_REQUEST;
Denis Vlasenkoa35c9e92006-11-29 15:58:50 +00001618 if (test == url+1) {
1619 /* '/' or NUL is encoded */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001620 sendHeaders(HTTP_NOT_FOUND);
1621 break;
1622 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001623
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001624 /* algorithm stolen from libbb bb_simplify_path(),
Denis Vlasenko16abcd92007-04-13 23:59:52 +00001625 * but don't strdup and reducing trailing slash and protect out root */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001626 purl = test = url;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001627 do {
1628 if (*purl == '/') {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001629 /* skip duplicate (or initial) slash */
1630 if (*test == '/') {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001631 continue;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001632 }
1633 if (*test == '.') {
1634 /* skip extra '.' */
Denis Vlasenko16abcd92007-04-13 23:59:52 +00001635 if (test[1] == '/' || !test[1]) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001636 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00001637 }
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001638 /* '..': be careful */
Denis Vlasenko16abcd92007-04-13 23:59:52 +00001639 if (test[1] == '.' && (test[2] == '/' || !test[2])) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001640 ++test;
1641 if (purl == url) {
1642 /* protect out root */
1643 goto BAD_REQUEST;
1644 }
Denis Vlasenko92758142006-10-03 19:56:34 +00001645 while (*--purl != '/') /* omit previous dir */;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00001646 continue;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001647 }
1648 }
1649 }
1650 *++purl = *test;
1651 } while (*++test);
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001652 *++purl = '\0'; /* so keep last character */
1653 test = purl; /* end ptr */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001654
1655 /* If URL is directory, adding '/' */
1656 if (test[-1] != '/') {
1657 if (is_directory(url + 1, 1, &sb)) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001658 found_moved_temporarily = url;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001659 }
1660 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001661 if (DEBUG)
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001662 fprintf(stderr, "url='%s', args=%s\n", url, g_query);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001663
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001664 test = url;
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001665 ip_allowed = checkPermIP();
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001666 while (ip_allowed && (test = strchr(test + 1, '/')) != NULL) {
1667 /* have path1/path2 */
1668 *test = '\0';
1669 if (is_directory(url + 1, 1, &sb)) {
1670 /* may be having subdir config */
1671 parse_conf(url + 1, SUBDIR_PARSE);
1672 ip_allowed = checkPermIP();
1673 }
1674 *test = '/';
1675 }
1676 if (blank >= 0) {
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001677 /* read until blank line for HTTP version specified, else parse immediate */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001678 while (1) {
1679 alarm(TIMEOUT);
1680 count = getLine();
1681 if (count <= 0)
1682 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001683
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001684 if (DEBUG)
1685 fprintf(stderr, "header: '%s'\n", buf);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001686
Denis Vlasenko55a99402006-09-30 20:41:44 +00001687#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001688 /* try and do our best to parse more lines */
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001689 if ((STRNCASECMP(buf, "Content-length:") == 0)) {
1690 /* extra read only for POST */
1691 if (prequest != request_GET) {
1692 test = buf + sizeof("Content-length:")-1;
Denis Vlasenko3bba5452006-12-30 17:57:03 +00001693 if (!test[0])
1694 goto bail_out;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001695 errno = 0;
1696 /* not using strtoul: it ignores leading munis! */
1697 length = strtol(test, &test, 10);
1698 /* length is "ulong", but we need to pass it to int later */
1699 /* so we check for negative or too large values in one go: */
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001700 /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001701 if (test[0] || errno || length > INT_MAX)
1702 goto bail_out;
1703 }
Denis Vlasenko6cd84da2007-07-21 14:57:54 +00001704 } else if (STRNCASECMP(buf, "Cookie:") == 0) {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001705 cookie = strdup(skip_whitespace(buf + sizeof("Cookie:")-1));
Denis Vlasenko82bd9ee2007-07-21 15:07:22 +00001706 } else if (STRNCASECMP(buf, "Content-Type:") == 0) {
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001707 content_type = strdup(skip_whitespace(buf + sizeof("Content-Type:")-1));
Denis Vlasenko6cd84da2007-07-21 14:57:54 +00001708 } else if (STRNCASECMP(buf, "Referer:") == 0) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001709 referer = strdup(skip_whitespace(buf + sizeof("Referer:")-1));
Denis Vlasenko6cd84da2007-07-21 14:57:54 +00001710 } else if (STRNCASECMP(buf, "User-Agent:") == 0) {
1711 user_agent = strdup(skip_whitespace(buf + sizeof("User-Agent:")-1));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001712 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001713#endif
1714
Denis Vlasenko55a99402006-09-30 20:41:44 +00001715#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001716 if (STRNCASECMP(buf, "Authorization:") == 0) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001717 /* We only allow Basic credentials.
1718 * It shows up as "Authorization: Basic <userid:password>" where
1719 * the userid:password is base64 encoded.
1720 */
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001721 test = skip_whitespace(buf + sizeof("Authorization:")-1);
1722 if (STRNCASECMP(test, "Basic") != 0)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001723 continue;
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001724 test += sizeof("Basic")-1;
1725 /* decodeBase64() skips whitespace itself */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001726 decodeBase64(test);
1727 credentials = checkPerm(url, test);
1728 }
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001729#endif /* FEATURE_HTTPD_BASIC_AUTH */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001730
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001731 } /* while extra header reading */
1732 }
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001733 alarm(0);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001734 if (alarm_signaled)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001735 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001736
Denis Vlasenkodc757aa2007-06-30 08:04:05 +00001737 if (strcmp(bb_basename(url), httpd_conf) == 0 || ip_allowed == 0) {
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001738 /* protect listing [/path]/httpd_conf or IP deny */
Denis Vlasenko55a99402006-09-30 20:41:44 +00001739#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001740 FORBIDDEN: /* protect listing /cgi-bin */
Glenn L McGrath06e95652003-02-09 06:51:14 +00001741#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001742 sendHeaders(HTTP_FORBIDDEN);
1743 break;
1744 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00001745
Denis Vlasenko55a99402006-09-30 20:41:44 +00001746#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001747 if (credentials <= 0 && checkPerm(url, ":") == 0) {
1748 sendHeaders(HTTP_UNAUTHORIZED);
1749 break;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001750 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001751#endif
1752
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001753 if (found_moved_temporarily) {
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001754 sendHeaders(HTTP_MOVED_TEMPORARILY);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001755 /* clear unforked memory flag */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001756 found_moved_temporarily = NULL;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001757 break;
1758 }
1759
1760 test = url + 1; /* skip first '/' */
1761
Denis Vlasenko55a99402006-09-30 20:41:44 +00001762#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001763 if (strncmp(test, "cgi-bin", 7) == 0) {
1764 if (test[7] == '/' && test[8] == 0)
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001765 goto FORBIDDEN; /* protect listing cgi-bin/ */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001766 sendCgi(url, prequest, length, cookie, content_type);
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001767 break;
1768 }
Denis Vlasenko1ccd96f2007-03-05 19:24:33 +00001769#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
1770 {
1771 char *suffix = strrchr(test, '.');
1772 if (suffix) {
1773 Htaccess *cur;
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001774 for (cur = script_i; cur; cur = cur->next) {
Denis Vlasenko1ccd96f2007-03-05 19:24:33 +00001775 if (strcmp(cur->before_colon + 1, suffix) == 0) {
1776 sendCgi(url, prequest, length, cookie, content_type);
1777 goto bail_out;
1778 }
1779 }
1780 }
1781 }
1782#endif
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001783 if (prequest != request_GET) {
1784 sendHeaders(HTTP_NOT_IMPLEMENTED);
1785 break;
1786 }
Denis Vlasenko5d148e22006-11-21 00:12:09 +00001787#endif /* FEATURE_HTTPD_CGI */
1788 if (purl[-1] == '/')
1789 strcpy(purl, "index.html");
1790 if (stat(test, &sb) == 0) {
1791 /* It's a dir URL and there is index.html */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001792 ContentLength = sb.st_size;
1793 last_mod = sb.st_mtime;
Denis Vlasenko5d148e22006-11-21 00:12:09 +00001794 }
1795#if ENABLE_FEATURE_HTTPD_CGI
1796 else if (purl[-1] == '/') {
1797 /* It's a dir URL and there is no index.html
1798 * Try cgi-bin/index.cgi */
1799 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
1800 purl[0] = '\0';
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001801 g_query = url;
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001802 sendCgi("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
1803 break;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001804 }
1805 }
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001806#endif /* FEATURE_HTTPD_CGI */
Denis Vlasenko6c85ddc2006-11-21 00:08:39 +00001807 sendFile(test);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001808 ContentLength = -1;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001809 } while (0);
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001810
Denis Vlasenko3bba5452006-12-30 17:57:03 +00001811#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001812 bail_out:
Denis Vlasenko3bba5452006-12-30 17:57:03 +00001813#endif
Denis Vlasenko0bb993f2006-11-21 00:06:28 +00001814
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001815 if (DEBUG)
1816 fprintf(stderr, "closing socket\n\n");
1817#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001818 free(cookie);
1819 free(content_type);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001820 free(referer);
1821 referer = NULL;
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001822# if ENABLE_FEATURE_HTTPD_BASIC_AUTH
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001823 free(remoteuser);
1824 remoteuser = NULL;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001825# endif
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001826#endif
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001827 shutdown(accepted_socket, SHUT_WR);
Eric Andersend8746cd2004-02-24 07:28:38 +00001828
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001829 /* Properly wait for remote to closed */
Denis Vlasenko55a99402006-09-30 20:41:44 +00001830 FD_ZERO(&s_fd);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001831 FD_SET(accepted_socket, &s_fd);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001832
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001833 do {
Denis Vlasenko55a99402006-09-30 20:41:44 +00001834 tv.tv_sec = 2;
1835 tv.tv_usec = 0;
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001836 retval = select(accepted_socket + 1, &s_fd, NULL, NULL, &tv);
1837 } while (retval > 0 && read(accepted_socket, buf, sizeof(iobuf) > 0));
Eric Andersend8746cd2004-02-24 07:28:38 +00001838
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001839 shutdown(accepted_socket, SHUT_RD);
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001840 /* In inetd case, we close fd 1 (stdout) here. We will exit soon anyway */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001841 close(accepted_socket);
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001842}
1843
Denis Vlasenko56258b62007-06-23 23:14:02 +00001844#if BB_MMU
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001845/****************************************************************************
1846 *
1847 > $Function: miniHttpd()
1848 *
1849 * $Description: The main http server function.
1850 *
1851 * Given an open socket fildes, listen for new connections and farm out
1852 * the processing as a forked process.
1853 *
1854 * $Parameters:
1855 * (int) server. . . The server socket fildes.
1856 *
1857 * $Return: (int) . . . . Always 0.
1858 *
1859 ****************************************************************************/
1860static int miniHttpd(int server)
1861{
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001862 fd_set readfd, portfd;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001863
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001864 FD_ZERO(&portfd);
1865 FD_SET(server, &portfd);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001866
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001867 /* copy the ports we are watching to the readfd set */
1868 while (1) {
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001869 int s;
1870 union {
1871 struct sockaddr sa;
1872 struct sockaddr_in sin;
1873 USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
1874 } fromAddr;
1875 socklen_t fromAddrLen = sizeof(fromAddr);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001876
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001877 /* Now wait INDEFINITELY on the set of sockets! */
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001878 readfd = portfd;
1879 if (select(server + 1, &readfd, 0, 0, 0) <= 0)
1880 continue;
1881 if (!FD_ISSET(server, &readfd))
1882 continue;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001883 s = accept(server, &fromAddr.sa, &fromAddrLen);
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001884 if (s < 0)
1885 continue;
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001886 accepted_socket = s;
1887 rmt_ip = 0;
1888 tcp_port = 0;
Denis Vlasenko55a99402006-09-30 20:41:44 +00001889#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001890 free(rmt_ip_str);
1891 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
"Vladimir N. Oleynik"6b903a22005-12-20 11:02:54 +00001892#if DEBUG
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001893 bb_error_msg("connection from '%s'", rmt_ip_str);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001894#endif
Denis Vlasenkob3a07152006-11-16 18:04:43 +00001895#endif /* FEATURE_HTTPD_CGI */
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001896 if (fromAddr.sa.sa_family == AF_INET) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001897 rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
1898 tcp_port = ntohs(fromAddr.sin.sin_port);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001899 }
1900#if ENABLE_FEATURE_IPV6
1901 if (fromAddr.sa.sa_family == AF_INET6) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001902 //rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
1903 tcp_port = ntohs(fromAddr.sin6.sin6_port);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001904 }
1905#endif
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001906
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001907 /* set the KEEPALIVE option to cull dead connections */
Denis Vlasenko703e2022007-01-22 14:12:08 +00001908 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
Denis Vlasenko04291bc2006-11-21 10:15:25 +00001909
1910 if (DEBUG || fork() == 0) {
1911 /* child */
Denis Vlasenko55a99402006-09-30 20:41:44 +00001912#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001913 /* protect reload config, may be confuse checking */
1914 signal(SIGHUP, SIG_IGN);
Glenn L McGrathc9163fe2003-05-13 16:20:11 +00001915#endif
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001916 handleIncoming();
Denis Vlasenko04291bc2006-11-21 10:15:25 +00001917 if (!DEBUG)
1918 exit(0);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001919 }
Denis Vlasenko6c5e5a02006-11-10 23:28:57 +00001920 close(s);
Denis Vlasenko04291bc2006-11-21 10:15:25 +00001921 } /* while (1) */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001922 return 0;
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001923}
Denis Vlasenko56258b62007-06-23 23:14:02 +00001924#endif
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001925
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001926/* from inetd */
1927static int miniHttpd_inetd(void)
Glenn L McGrath58c708a2003-01-05 04:01:56 +00001928{
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001929 union {
1930 struct sockaddr sa;
1931 struct sockaddr_in sin;
1932 USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
1933 } fromAddr;
1934 socklen_t fromAddrLen = sizeof(fromAddr);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001935
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001936 getpeername(0, &fromAddr.sa, &fromAddrLen);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001937 rmt_ip = 0;
1938 tcp_port = 0;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001939#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001940 free(rmt_ip_str);
1941 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddrLen);
Glenn L McGrathb65422c2003-09-08 10:59:27 +00001942#endif
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001943 if (fromAddr.sa.sa_family == AF_INET) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001944 rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
1945 tcp_port = ntohs(fromAddr.sin.sin_port);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001946 }
1947#if ENABLE_FEATURE_IPV6
1948 if (fromAddr.sa.sa_family == AF_INET6) {
Denis Vlasenko77e44d62007-06-09 23:49:05 +00001949 //rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
1950 tcp_port = ntohs(fromAddr.sin6.sin6_port);
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001951 }
1952#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001953 handleIncoming();
1954 return 0;
Glenn L McGrath06e95652003-02-09 06:51:14 +00001955}
Glenn L McGrath06e95652003-02-09 06:51:14 +00001956
Denis Vlasenko55a99402006-09-30 20:41:44 +00001957#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
Glenn L McGrath06e95652003-02-09 06:51:14 +00001958static void sighup_handler(int sig)
1959{
1960 /* set and reset */
1961 struct sigaction sa;
1962
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001963 parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001964 sa.sa_handler = sighup_handler;
1965 sigemptyset(&sa.sa_mask);
1966 sa.sa_flags = SA_RESTART;
1967 sigaction(SIGHUP, &sa, NULL);
Glenn L McGrath06e95652003-02-09 06:51:14 +00001968}
1969#endif
1970
Denis Vlasenko9f609292006-11-05 19:47:33 +00001971enum {
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00001972 c_opt_config_file = 0,
1973 d_opt_decode_url,
1974 h_opt_home_httpd,
1975 USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
Denis Vlasenko9f609292006-11-05 19:47:33 +00001976 USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
1977 USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
1978 USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001979 p_opt_port ,
1980 p_opt_inetd ,
1981 p_opt_foreground,
Denis Vlasenko9f609292006-11-05 19:47:33 +00001982 OPT_CONFIG_FILE = 1 << c_opt_config_file,
1983 OPT_DECODE_URL = 1 << d_opt_decode_url,
1984 OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
1985 OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
1986 OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
1987 OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
1988 OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
Denis Vlasenko0871bc82006-11-16 16:17:02 +00001989 OPT_PORT = 1 << p_opt_port,
1990 OPT_INETD = 1 << p_opt_inetd,
1991 OPT_FOREGROUND = 1 << p_opt_foreground,
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00001992};
Eric Andersena3bb3e62003-06-26 09:05:32 +00001993
Eric Andersena3bb3e62003-06-26 09:05:32 +00001994
Bernhard Reutner-Fischerfebe3c42007-04-04 20:52:03 +00001995int httpd_main(int argc, char **argv);
1996int httpd_main(int argc, char **argv)
Glenn L McGrath06e95652003-02-09 06:51:14 +00001997{
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001998 unsigned opt;
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00001999 char *url_for_decode;
2000 USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002001 const char *s_port;
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00002002 USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
2003 USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002004 USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
Eric Andersen35e643b2003-07-28 07:40:39 +00002005
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +00002006#if ENABLE_LOCALE_SUPPORT
2007 /* Undo busybox.c: we want to speak English in http (dates etc) */
2008 setlocale(LC_TIME, "C");
2009#endif
2010
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002011 INIT_G();
2012 home_httpd = xrealloc_getcwd_or_warn(NULL);
2013 /* We do not "absolutize" path given by -h (home) opt.
2014 * If user gives relative path in -h, $SCRIPT_FILENAME can end up
2015 * relative too. */
Denis Vlasenko53091ec2007-03-26 13:35:09 +00002016 opt = getopt32(argc, argv, "c:d:h:"
2017 USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
2018 USE_FEATURE_HTTPD_BASIC_AUTH("r:")
2019 USE_FEATURE_HTTPD_AUTH_MD5("m:")
2020 USE_FEATURE_HTTPD_SETUID("u:")
2021 "p:if",
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002022 &(configFile), &url_for_decode, &home_httpd
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00002023 USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002024 USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
"Vladimir N. Oleynik"9a515402006-02-15 13:27:18 +00002025 USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00002026 USE_FEATURE_HTTPD_SETUID(, &s_ugid)
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002027 , &s_port
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002028 );
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002029 if (opt & OPT_DECODE_URL) {
2030 printf("%s", decodeString(url_for_decode, 1));
2031 return 0;
2032 }
Denis Vlasenko55a99402006-09-30 20:41:44 +00002033#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002034 if (opt & OPT_ENCODE_URL) {
2035 printf("%s", encodeString(url_for_encode));
2036 return 0;
2037 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00002038#endif
Denis Vlasenko55a99402006-09-30 20:41:44 +00002039#if ENABLE_FEATURE_HTTPD_AUTH_MD5
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002040 if (opt & OPT_MD5) {
Denis Vlasenko55a99402006-09-30 20:41:44 +00002041 puts(pw_encrypt(pass, "$1$"));
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002042 return 0;
Glenn L McGrath06e95652003-02-09 06:51:14 +00002043 }
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002044#endif
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002045 if (opt & OPT_PORT)
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002046 tcp_port = xatou16(s_port);
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002047
Denis Vlasenko55a99402006-09-30 20:41:44 +00002048#if ENABLE_FEATURE_HTTPD_SETUID
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002049 if (opt & OPT_SETUID) {
Denis Vlasenko3bba5452006-12-30 17:57:03 +00002050 if (!get_uidgid(&ugid, s_ugid, 1))
2051 bb_error_msg_and_die("unrecognized user[:group] "
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00002052 "name '%s'", s_ugid);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002053 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00002054#endif
Glenn L McGrath58c708a2003-01-05 04:01:56 +00002055
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002056 xchdir(home_httpd);
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002057 if (!(opt & OPT_INETD)) {
Denis Vlasenko56258b62007-06-23 23:14:02 +00002058#if BB_MMU
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00002059 signal(SIGCHLD, SIG_IGN);
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002060 server_socket = openServer();
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002061#if ENABLE_FEATURE_HTTPD_SETUID
2062 /* drop privileges */
2063 if (opt & OPT_SETUID) {
2064 if (ugid.gid != (gid_t)-1) {
2065 if (setgroups(1, &ugid.gid) == -1)
Denis Vlasenko8e858e22007-03-07 09:35:43 +00002066 bb_perror_msg_and_die("setgroups");
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002067 xsetgid(ugid.gid);
2068 }
2069 xsetuid(ugid.uid);
Denis Vlasenkode59c0f2006-10-05 22:50:22 +00002070 }
Glenn L McGrath58c708a2003-01-05 04:01:56 +00002071#endif
Denis Vlasenko56258b62007-06-23 23:14:02 +00002072#else /* BB_MMU */
2073 bb_error_msg_and_die("-i is required");
2074#endif
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002075 }
Glenn L McGrath06e95652003-02-09 06:51:14 +00002076
Denis Vlasenko55a99402006-09-30 20:41:44 +00002077#if ENABLE_FEATURE_HTTPD_CGI
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002078 {
2079 char *p = getenv("PATH");
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002080 /* env strings themself are not freed, no need to strdup(p): */
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002081 clearenv();
2082 if (p)
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002083 putenv(p - 5);
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002084 if (!(opt & OPT_INETD))
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002085 setenv_long("SERVER_PORT", tcp_port);
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002086 }
Glenn L McGrathfe538ba2003-09-10 23:35:45 +00002087#endif
2088
Denis Vlasenko55a99402006-09-30 20:41:44 +00002089#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002090 sighup_handler(0);
Glenn L McGrath06e95652003-02-09 06:51:14 +00002091#else
Denis Vlasenko8b8c75e2006-09-26 10:07:41 +00002092 parse_conf(default_path_httpd_conf, FIRST_PARSE);
Glenn L McGrath06e95652003-02-09 06:51:14 +00002093#endif
2094
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002095 if (opt & OPT_INETD)
2096 return miniHttpd_inetd();
2097
Denis Vlasenko56258b62007-06-23 23:14:02 +00002098#if BB_MMU
Denis Vlasenko0871bc82006-11-16 16:17:02 +00002099 if (!(opt & OPT_FOREGROUND))
Denis Vlasenko5a142022007-03-26 13:20:54 +00002100 bb_daemonize(0); /* don't change current directory */
Denis Vlasenko77e44d62007-06-09 23:49:05 +00002101 return miniHttpd(server_socket);
Denis Vlasenko56258b62007-06-23 23:14:02 +00002102#else
2103 return 0; /* not reached */
2104#endif
Glenn L McGrath06e95652003-02-09 06:51:14 +00002105}