2006-04-12 23:39:26 +05:30
|
|
|
/* vi: set sw=4 ts=4: */
|
2003-01-05 09:31:56 +05:30
|
|
|
/*
|
|
|
|
* httpd implementation for busybox
|
|
|
|
*
|
2003-02-09 12:21:14 +05:30
|
|
|
* Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
|
2006-01-26 16:28:12 +05:30
|
|
|
* Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2010-08-16 23:44:46 +05:30
|
|
|
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
|
|
|
*****************************************************************************
|
|
|
|
*
|
2003-02-09 12:21:14 +05:30
|
|
|
* Typical usage:
|
2010-07-25 06:50:25 +05:30
|
|
|
* For non root user:
|
|
|
|
* httpd -p 8080 -h $HOME/public_html
|
|
|
|
* For daemon start from rc script with uid=0:
|
|
|
|
* httpd -u www
|
|
|
|
* which is equivalent to (assuming user www has uid 80):
|
|
|
|
* httpd -p 80 -u 80 -h $PWD -c /etc/httpd.conf -r "Web Server Authentication"
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2010-07-25 06:50:25 +05:30
|
|
|
* When an url starts with "/cgi-bin/" it is assumed to be a cgi script.
|
|
|
|
* The server changes directory to the location of the script and executes it
|
2003-10-03 16:20:56 +05:30
|
|
|
* after setting QUERY_STRING and other environment variables.
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2011-04-10 06:38:22 +05:30
|
|
|
* If directory URL is given, no index.html is found and CGI support is enabled,
|
|
|
|
* cgi-bin/index.cgi will be run. Directory to list is ../$QUERY_STRING.
|
|
|
|
* See httpd_indexcgi.c for an example GCI code.
|
|
|
|
*
|
2006-11-22 02:53:21 +05:30
|
|
|
* Doc:
|
|
|
|
* "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html
|
|
|
|
*
|
2009-09-10 05:16:02 +05:30
|
|
|
* The applet can also be invoked as an url arg decoder and html text encoder
|
2003-01-05 09:31:56 +05:30
|
|
|
* as follows:
|
2010-07-25 06:50:25 +05:30
|
|
|
* foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
|
|
|
|
* bar=`httpd -e "<Hello World>"` # encode as "<Hello World>"
|
2003-05-19 11:26:16 +05:30
|
|
|
* Note that url encoding for arguments is not the same as html encoding for
|
2009-05-03 22:23:22 +05:30
|
|
|
* presentation. -d decodes an url-encoded argument while -e encodes in html
|
2003-05-19 11:26:16 +05:30
|
|
|
* for page display.
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
|
|
|
* httpd.conf has the following format:
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2008-07-21 04:55:32 +05:30
|
|
|
* H:/serverroot # define the server root. It will override -h
|
2003-09-08 16:29:27 +05:30
|
|
|
* A:172.20. # Allow address from 172.20.0.0/16
|
|
|
|
* A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
|
|
|
|
* A:10.0.0.0/255.255.255.128 # Allow any address that previous set
|
2003-05-19 11:26:16 +05:30
|
|
|
* A:127.0.0.1 # Allow local loopback connections
|
|
|
|
* D:* # Deny from other IP connections
|
2007-08-21 15:56:55 +05:30
|
|
|
* E404:/path/e404.html # /path/e404.html is the 404 (not found) error page
|
2007-12-29 07:46:23 +05:30
|
|
|
* I:index.html # Show index.html when a directory is requested
|
2007-10-18 18:24:39 +05:30
|
|
|
*
|
|
|
|
* P:/url:[http://]hostname[:port]/new/path
|
|
|
|
* # When /urlXXXXXX is requested, reverse proxy
|
|
|
|
* # it to http://hostname[:port]/new/pathXXXXXX
|
|
|
|
*
|
2003-05-19 11:26:16 +05:30
|
|
|
* /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
|
|
|
|
* /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
|
|
|
|
* /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
|
2011-11-29 18:21:11 +05:30
|
|
|
* /adm:root:* # or user root, pwd from /etc/passwd on urls starting with /adm/
|
|
|
|
* /wiki:*:* # or any user from /etc/passwd with according pwd on urls starting with /wiki/
|
2003-05-19 11:26:16 +05:30
|
|
|
* .au:audio/basic # additional mime type for audio.au files
|
2009-04-22 19:19:16 +05:30
|
|
|
* *.php:/path/php # run xxx.php through an interpreter
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2008-06-13 15:23:06 +05:30
|
|
|
* A/D may be as a/d or allow/deny - only first char matters.
|
|
|
|
* Deny/Allow IP logic:
|
|
|
|
* - Default is to allow all (Allow all (A:*) is a no-op).
|
2003-05-19 11:26:16 +05:30
|
|
|
* - Deny rules take precedence over allow rules.
|
2008-06-13 15:23:06 +05:30
|
|
|
* - "Deny all" rule (D:*) is applied last.
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2003-05-19 11:26:16 +05:30
|
|
|
* Example:
|
|
|
|
* 1. Allow only specified addresses
|
2003-09-08 16:29:27 +05:30
|
|
|
* A:172.20 # Allow any address that begins with 172.20.
|
2003-05-19 11:26:16 +05:30
|
|
|
* A:10.10. # Allow any address that begins with 10.10.
|
|
|
|
* A:127.0.0.1 # Allow local loopback connections
|
|
|
|
* D:* # Deny from other IP connections
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2003-05-19 11:26:16 +05:30
|
|
|
* 2. Only deny specified addresses
|
|
|
|
* D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
|
|
|
|
* D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
|
|
|
|
* A:* # (optional line added for clarity)
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2011-04-10 06:38:22 +05:30
|
|
|
* If a sub directory contains config file, it is parsed and merged with
|
2003-09-08 16:29:27 +05:30
|
|
|
* any existing settings as if it was appended to the original configuration.
|
2003-05-19 11:26:16 +05:30
|
|
|
*
|
|
|
|
* subdir paths are relative to the containing subdir and thus cannot
|
|
|
|
* affect the parent rules.
|
|
|
|
*
|
|
|
|
* Note that since the sub dir is parsed in the forked thread servicing the
|
|
|
|
* subdir http request, any merge is discarded when the process exits. As a
|
|
|
|
* result, the subdir settings only have a lifetime of a single request.
|
|
|
|
*
|
2007-08-21 15:56:55 +05:30
|
|
|
* Custom error pages can contain an absolute path or be relative to
|
|
|
|
* 'home_httpd'. Error pages are to be static files (no CGI or script). Error
|
|
|
|
* page can only be defined in the root configuration file and are not taken
|
|
|
|
* into account in local (directories) config files.
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
|
|
|
* If -c is not set, an attempt will be made to open the default
|
2003-05-19 11:26:16 +05:30
|
|
|
* root configuration file. If -c is set and the file is not found, the
|
|
|
|
* server exits with an error.
|
2007-08-18 00:50:07 +05:30
|
|
|
*/
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:config HTTPD
|
2017-07-19 01:31:24 +05:30
|
|
|
//config: bool "httpd (32 kb)"
|
2016-11-23 13:35:14 +05:30
|
|
|
//config: default y
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: HTTP server.
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_RANGES
|
|
|
|
//config: bool "Support 'Ranges:' header"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Makes httpd emit "Accept-Ranges: bytes" header and understand
|
|
|
|
//config: "Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted
|
|
|
|
//config: downloads, seeking in multimedia players etc.
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_SETUID
|
|
|
|
//config: bool "Enable -u <user> option"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: This option allows the server to run as a specific user
|
|
|
|
//config: rather than defaulting to the user that starts the server.
|
|
|
|
//config: Use of this option requires special privileges to change to a
|
|
|
|
//config: different user.
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_BASIC_AUTH
|
2017-07-27 17:04:51 +05:30
|
|
|
//config: bool "Enable HTTP authentication"
|
2016-11-23 13:35:14 +05:30
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Utilizes password settings from /etc/httpd.conf for basic
|
|
|
|
//config: authentication on a per url basis.
|
|
|
|
//config: Example for httpd.conf file:
|
|
|
|
//config: /adm:toor:PaSsWd
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_AUTH_MD5
|
2017-07-27 17:04:51 +05:30
|
|
|
//config: bool "Support MD5-encrypted passwords in HTTP authentication"
|
2016-11-23 13:35:14 +05:30
|
|
|
//config: default y
|
|
|
|
//config: depends on FEATURE_HTTPD_BASIC_AUTH
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Enables encrypted passwords, and wildcard user/passwords
|
|
|
|
//config: in httpd.conf file.
|
|
|
|
//config: User '*' means 'any system user name is ok',
|
|
|
|
//config: password of '*' means 'use system password for this user'
|
|
|
|
//config: Examples:
|
|
|
|
//config: /adm:toor:$1$P/eKnWXS$aI1aPGxT.dJD5SzqAKWrF0
|
|
|
|
//config: /adm:root:*
|
|
|
|
//config: /wiki:*:*
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_CGI
|
|
|
|
//config: bool "Support Common Gateway Interface (CGI)"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: This option allows scripts and executables to be invoked
|
|
|
|
//config: when specific URLs are requested.
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
|
2017-01-10 19:28:54 +05:30
|
|
|
//config: bool "Support running scripts through an interpreter"
|
2016-11-23 13:35:14 +05:30
|
|
|
//config: default y
|
|
|
|
//config: depends on FEATURE_HTTPD_CGI
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: This option enables support for running scripts through an
|
|
|
|
//config: interpreter. Turn this on if you want PHP scripts to work
|
|
|
|
//config: properly. You need to supply an additional line in your
|
|
|
|
//config: httpd.conf file:
|
|
|
|
//config: *.php:/path/to/your/php
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
|
|
|
|
//config: bool "Set REMOTE_PORT environment variable for CGI"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on FEATURE_HTTPD_CGI
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Use of this option can assist scripts in generating
|
|
|
|
//config: references that contain a unique port number.
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_ENCODE_URL_STR
|
|
|
|
//config: bool "Enable -e option (useful for CGIs written as shell scripts)"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: This option allows html encoding of arbitrary strings for display
|
|
|
|
//config: by the browser. Output goes to stdout.
|
|
|
|
//config: For example, httpd -e "<Hello World>" produces
|
|
|
|
//config: "<Hello World>".
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_ERROR_PAGES
|
2017-01-10 19:28:54 +05:30
|
|
|
//config: bool "Support custom error pages"
|
2016-11-23 13:35:14 +05:30
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: This option allows you to define custom error pages in
|
|
|
|
//config: the configuration file instead of the default HTTP status
|
|
|
|
//config: error pages. For instance, if you add the line:
|
|
|
|
//config: E404:/path/e404.html
|
|
|
|
//config: in the config file, the server will respond the specified
|
|
|
|
//config: '/path/e404.html' file instead of the terse '404 NOT FOUND'
|
|
|
|
//config: message.
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_PROXY
|
2017-01-10 19:28:54 +05:30
|
|
|
//config: bool "Support reverse proxy"
|
2016-11-23 13:35:14 +05:30
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: This option allows you to define URLs that will be forwarded
|
|
|
|
//config: to another HTTP server. To setup add the following line to the
|
|
|
|
//config: configuration file
|
|
|
|
//config: P:/url/:http://hostname[:port]/new/path/
|
|
|
|
//config: Then a request to /url/myfile will be forwarded to
|
|
|
|
//config: http://hostname[:port]/new/path/myfile.
|
2016-11-23 13:35:14 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_GZIP
|
2017-01-10 19:28:54 +05:30
|
|
|
//config: bool "Support GZIP content encoding"
|
2016-11-23 13:35:14 +05:30
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Makes httpd send files using GZIP content encoding if the
|
|
|
|
//config: client supports it and a pre-compressed <file>.gz exists.
|
2020-08-09 03:53:32 +05:30
|
|
|
//config:
|
2020-08-16 03:22:48 +05:30
|
|
|
//config:config FEATURE_HTTPD_ETAG
|
|
|
|
//config: bool "Support caching via ETag header"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
|
|
|
//config: If server responds with ETag then next time client (browser)
|
|
|
|
//config: resend it via If-None-Match header.
|
|
|
|
//config: Then httpd will check if file wasn't modified and if not,
|
|
|
|
//config: return 304 Not Modified status code.
|
|
|
|
//config: The ETag value is constructed from last modification date
|
|
|
|
//config: in unix epoch, and size: "hex(last_mod)-hex(file_size)".
|
|
|
|
//config: It's not completely reliable as hash functions but fair enough.
|
|
|
|
//config:
|
2020-08-09 03:53:33 +05:30
|
|
|
//config:config FEATURE_HTTPD_LAST_MODIFIED
|
|
|
|
//config: bool "Add Last-Modified header to response"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
|
|
|
//config: The Last-Modified header is used for cache validation.
|
|
|
|
//config: The client sends last seen mtime to server in If-Modified-Since.
|
|
|
|
//config: Both headers MUST be an RFC 1123 formatted, which is hard to parse.
|
|
|
|
//config: Use ETag header instead.
|
|
|
|
//config:
|
2020-08-09 03:53:32 +05:30
|
|
|
//config:config FEATURE_HTTPD_DATE
|
|
|
|
//config: bool "Add Date header to response"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
|
|
|
//config: RFC2616 says that server MUST add Date header to response.
|
|
|
|
//config: But it is almost useless and can be omitted.
|
2020-08-16 18:28:31 +05:30
|
|
|
//config:
|
|
|
|
//config:config FEATURE_HTTPD_ACL_IP
|
|
|
|
//config: bool "ACL IP"
|
|
|
|
//config: default y
|
|
|
|
//config: depends on HTTPD
|
|
|
|
//config: help
|
|
|
|
//config: Support IP deny/allow rules
|
2016-11-23 13:35:14 +05:30
|
|
|
|
|
|
|
//applet:IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
|
|
|
|
|
|
|
|
//kbuild:lib-$(CONFIG_HTTPD) += httpd.o
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2011-04-11 06:59:49 +05:30
|
|
|
//usage:#define httpd_trivial_usage
|
|
|
|
//usage: "[-ifv[v]]"
|
|
|
|
//usage: " [-c CONFFILE]"
|
|
|
|
//usage: " [-p [IP:]PORT]"
|
|
|
|
//usage: IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]")
|
|
|
|
//usage: IF_FEATURE_HTTPD_BASIC_AUTH(" [-r REALM]")
|
|
|
|
//usage: " [-h HOME]\n"
|
|
|
|
//usage: "or httpd -d/-e" IF_FEATURE_HTTPD_AUTH_MD5("/-m") " STRING"
|
|
|
|
//usage:#define httpd_full_usage "\n\n"
|
|
|
|
//usage: "Listen for incoming HTTP requests\n"
|
|
|
|
//usage: "\n -i Inetd mode"
|
|
|
|
//usage: "\n -f Don't daemonize"
|
|
|
|
//usage: "\n -v[v] Verbose"
|
|
|
|
//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:80)"
|
|
|
|
//usage: IF_FEATURE_HTTPD_SETUID(
|
|
|
|
//usage: "\n -u USER[:GRP] Set uid/gid after binding to port")
|
|
|
|
//usage: IF_FEATURE_HTTPD_BASIC_AUTH(
|
|
|
|
//usage: "\n -r REALM Authentication Realm for Basic Authentication")
|
|
|
|
//usage: "\n -h HOME Home directory (default .)"
|
|
|
|
//usage: "\n -c FILE Configuration file (default {/etc,HOME}/httpd.conf)"
|
|
|
|
//usage: IF_FEATURE_HTTPD_AUTH_MD5(
|
|
|
|
//usage: "\n -m STRING MD5 crypt STRING")
|
|
|
|
//usage: "\n -e STRING HTML encode STRING"
|
|
|
|
//usage: "\n -d STRING URL decode STRING"
|
|
|
|
|
2019-04-14 23:27:13 +05:30
|
|
|
/* TODO: use TCP_CORK, parse_config() */
|
|
|
|
|
2007-05-27 00:30:18 +05:30
|
|
|
#include "libbb.h"
|
2016-04-21 19:56:30 +05:30
|
|
|
#include "common_bufsiz.h"
|
2011-11-29 18:21:11 +05:30
|
|
|
#if ENABLE_PAM
|
|
|
|
/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
|
|
|
|
# undef setlocale
|
|
|
|
/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
|
|
|
|
* Apparently they like to confuse people. */
|
|
|
|
# include <security/pam_appl.h>
|
|
|
|
# include <security/pam_misc.h>
|
|
|
|
#endif
|
2014-11-27 17:50:24 +05:30
|
|
|
#if ENABLE_FEATURE_USE_SENDFILE
|
2009-02-05 05:13:44 +05:30
|
|
|
# include <sys/sendfile.h>
|
2007-08-13 02:35:49 +05:30
|
|
|
#endif
|
2010-07-25 06:50:25 +05:30
|
|
|
|
2021-01-04 05:46:28 +05:30
|
|
|
/* see sys/netinet6/in6.h */
|
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
# define s6_addr32 __u6_addr.__u6_addr32
|
|
|
|
#endif
|
|
|
|
|
2010-07-25 06:50:25 +05:30
|
|
|
#define DEBUG 0
|
|
|
|
|
2021-05-05 18:30:09 +05:30
|
|
|
#if DEBUG
|
|
|
|
# define dbg(...) fprintf(stderr, __VA_ARGS__)
|
|
|
|
#else
|
|
|
|
# define dbg(...) ((void)0)
|
|
|
|
#endif
|
|
|
|
|
2010-07-25 06:50:25 +05:30
|
|
|
#define IOBUF_SIZE 8192
|
2019-04-19 17:49:41 +05:30
|
|
|
#define MAX_HTTP_HEADERS_SIZE (32*1024)
|
2004-10-08 13:33:29 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
#define HEADER_READ_TIMEOUT 60
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2009-04-22 19:19:16 +05:30
|
|
|
static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc";
|
|
|
|
static const char HTTPD_CONF[] ALIGN1 = "httpd.conf";
|
2020-08-09 03:53:31 +05:30
|
|
|
static const char HTTP_200[] ALIGN1 = "HTTP/1.1 200 OK\r\n";
|
2010-07-25 06:50:25 +05:30
|
|
|
static const char index_html[] ALIGN1 = "index.html";
|
2005-12-20 16:32:54 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
typedef struct has_next_ptr {
|
|
|
|
struct has_next_ptr *next;
|
|
|
|
} has_next_ptr;
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
/* Must have "next" as a first member */
|
2007-08-14 22:20:01 +05:30
|
|
|
typedef struct Htaccess {
|
|
|
|
struct Htaccess *next;
|
2007-08-18 00:50:07 +05:30
|
|
|
char *after_colon;
|
2007-08-14 22:20:01 +05:30
|
|
|
char before_colon[1]; /* really bigger, must be last */
|
2003-02-09 12:21:14 +05:30
|
|
|
} Htaccess;
|
|
|
|
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
2007-08-18 00:50:07 +05:30
|
|
|
/* Must have "next" as a first member */
|
2007-08-14 22:20:01 +05:30
|
|
|
typedef struct Htaccess_IP {
|
2007-08-18 00:50:07 +05:30
|
|
|
struct Htaccess_IP *next;
|
2007-06-10 05:19:05 +05:30
|
|
|
unsigned ip;
|
|
|
|
unsigned mask;
|
2003-09-08 16:29:27 +05:30
|
|
|
int allow_deny;
|
|
|
|
} Htaccess_IP;
|
2020-08-16 18:28:31 +05:30
|
|
|
#endif
|
2003-09-08 16:29:27 +05:30
|
|
|
|
2007-10-18 18:24:39 +05:30
|
|
|
/* Must have "next" as a first member */
|
|
|
|
typedef struct Htaccess_Proxy {
|
|
|
|
struct Htaccess_Proxy *next;
|
|
|
|
char *url_from;
|
|
|
|
char *host_port;
|
|
|
|
char *url_to;
|
|
|
|
} Htaccess_Proxy;
|
|
|
|
|
2007-08-21 15:56:55 +05:30
|
|
|
enum {
|
|
|
|
HTTP_OK = 200,
|
2007-09-22 04:05:18 +05:30
|
|
|
HTTP_PARTIAL_CONTENT = 206,
|
2007-08-21 15:56:55 +05:30
|
|
|
HTTP_MOVED_TEMPORARILY = 302,
|
2020-08-16 03:22:48 +05:30
|
|
|
HTTP_NOT_MODIFIED = 304,
|
2007-08-21 15:56:55 +05:30
|
|
|
HTTP_BAD_REQUEST = 400, /* malformed syntax */
|
|
|
|
HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
|
|
|
|
HTTP_NOT_FOUND = 404,
|
|
|
|
HTTP_FORBIDDEN = 403,
|
|
|
|
HTTP_REQUEST_TIMEOUT = 408,
|
|
|
|
HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
|
|
|
|
HTTP_INTERNAL_SERVER_ERROR = 500,
|
2019-04-16 15:07:02 +05:30
|
|
|
HTTP_ENTITY_TOO_LARGE = 413,
|
2007-08-21 15:56:55 +05:30
|
|
|
HTTP_CONTINUE = 100,
|
|
|
|
#if 0 /* future use */
|
|
|
|
HTTP_SWITCHING_PROTOCOLS = 101,
|
|
|
|
HTTP_CREATED = 201,
|
|
|
|
HTTP_ACCEPTED = 202,
|
|
|
|
HTTP_NON_AUTHORITATIVE_INFO = 203,
|
|
|
|
HTTP_NO_CONTENT = 204,
|
|
|
|
HTTP_MULTIPLE_CHOICES = 300,
|
|
|
|
HTTP_MOVED_PERMANENTLY = 301,
|
|
|
|
HTTP_PAYMENT_REQUIRED = 402,
|
|
|
|
HTTP_BAD_GATEWAY = 502,
|
|
|
|
HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2007-08-21 16:48:25 +05:30
|
|
|
static const uint16_t http_response_type[] ALIGN2 = {
|
2007-08-21 15:56:55 +05:30
|
|
|
HTTP_OK,
|
2007-09-22 04:05:18 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
|
|
|
HTTP_PARTIAL_CONTENT,
|
|
|
|
#endif
|
2007-08-21 15:56:55 +05:30
|
|
|
HTTP_MOVED_TEMPORARILY,
|
2020-08-16 03:22:48 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
HTTP_NOT_MODIFIED,
|
|
|
|
#endif
|
2007-08-21 15:56:55 +05:30
|
|
|
HTTP_REQUEST_TIMEOUT,
|
|
|
|
HTTP_NOT_IMPLEMENTED,
|
2007-10-14 10:25:59 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2007-08-21 15:56:55 +05:30
|
|
|
HTTP_UNAUTHORIZED,
|
|
|
|
#endif
|
|
|
|
HTTP_NOT_FOUND,
|
|
|
|
HTTP_BAD_REQUEST,
|
|
|
|
HTTP_FORBIDDEN,
|
|
|
|
HTTP_INTERNAL_SERVER_ERROR,
|
2019-04-16 15:07:02 +05:30
|
|
|
HTTP_ENTITY_TOO_LARGE,
|
2007-08-21 15:56:55 +05:30
|
|
|
#if 0 /* not implemented */
|
|
|
|
HTTP_CREATED,
|
|
|
|
HTTP_ACCEPTED,
|
|
|
|
HTTP_NO_CONTENT,
|
|
|
|
HTTP_MULTIPLE_CHOICES,
|
|
|
|
HTTP_MOVED_PERMANENTLY,
|
|
|
|
HTTP_BAD_GATEWAY,
|
|
|
|
HTTP_SERVICE_UNAVAILABLE,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char *name;
|
|
|
|
const char *info;
|
|
|
|
} http_response[ARRAY_SIZE(http_response_type)] = {
|
|
|
|
{ "OK", NULL },
|
2007-09-22 04:05:18 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
|
|
|
{ "Partial Content", NULL },
|
|
|
|
#endif
|
|
|
|
{ "Found", NULL },
|
2020-08-16 03:22:48 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
{ "Not Modified" },
|
|
|
|
#endif
|
2007-08-21 15:56:55 +05:30
|
|
|
{ "Request Timeout", "No request appeared within 60 seconds" },
|
|
|
|
{ "Not Implemented", "The requested method is not recognized" },
|
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
|
|
|
{ "Unauthorized", "" },
|
|
|
|
#endif
|
|
|
|
{ "Not Found", "The requested URL was not found" },
|
|
|
|
{ "Bad Request", "Unsupported method" },
|
|
|
|
{ "Forbidden", "" },
|
|
|
|
{ "Internal Server Error", "Internal Server Error" },
|
2019-04-16 15:07:02 +05:30
|
|
|
{ "Entity Too Large", "Entity Too Large" },
|
2007-08-21 15:56:55 +05:30
|
|
|
#if 0 /* not implemented */
|
|
|
|
{ "Created" },
|
|
|
|
{ "Accepted" },
|
|
|
|
{ "No Content" },
|
|
|
|
{ "Multiple Choices" },
|
|
|
|
{ "Moved Permanently" },
|
|
|
|
{ "Bad Gateway", "" },
|
|
|
|
{ "Service Unavailable", "" },
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2007-06-10 05:19:05 +05:30
|
|
|
struct globals {
|
2007-08-18 19:52:09 +05:30
|
|
|
int verbose; /* must be int (used by getopt32) */
|
2007-06-10 05:19:05 +05:30
|
|
|
smallint flg_deny_all;
|
2018-04-07 04:43:30 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_GZIP
|
|
|
|
/* client can handle gzip / we are going to send gzip */
|
|
|
|
smallint content_gzip;
|
|
|
|
#endif
|
2007-08-20 00:24:22 +05:30
|
|
|
time_t last_mod;
|
2020-08-16 03:22:48 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
char *if_none_match;
|
|
|
|
#endif
|
2007-08-20 00:24:22 +05:30
|
|
|
char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */
|
2007-08-14 22:25:01 +05:30
|
|
|
const char *bind_addr_or_port;
|
|
|
|
|
2019-04-16 17:05:56 +05:30
|
|
|
char *g_query;
|
2009-04-22 19:19:16 +05:30
|
|
|
const char *opt_c_configFile;
|
2007-06-10 05:19:05 +05:30
|
|
|
const char *home_httpd;
|
2007-12-29 07:46:23 +05:30
|
|
|
const char *index_page;
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2006-11-16 21:50:12 +05:30
|
|
|
const char *found_mime_type;
|
|
|
|
const char *found_moved_temporarily;
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
2007-08-18 19:52:09 +05:30
|
|
|
Htaccess_IP *ip_a_d; /* config allow/deny lines */
|
2020-08-16 18:28:31 +05:30
|
|
|
#endif
|
2007-06-10 05:19:05 +05:30
|
|
|
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
|
|
|
|
IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
|
2007-06-10 05:19:05 +05:30
|
|
|
|
2007-09-22 04:05:18 +05:30
|
|
|
off_t file_size; /* -1 - unknown */
|
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
|
|
|
off_t range_start;
|
|
|
|
off_t range_end;
|
|
|
|
off_t range_len;
|
|
|
|
#endif
|
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2007-08-18 19:52:09 +05:30
|
|
|
Htaccess *g_auth; /* config user:password lines */
|
2003-05-13 21:50:11 +05:30
|
|
|
#endif
|
2007-08-18 19:52:09 +05:30
|
|
|
Htaccess *mime_a; /* config mime types */
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
|
2007-08-18 19:52:09 +05:30
|
|
|
Htaccess *script_i; /* config script interpreters */
|
2006-01-31 19:23:30 +05:30
|
|
|
#endif
|
2010-10-29 15:16:52 +05:30
|
|
|
char *iobuf; /* [IOBUF_SIZE] */
|
2016-04-21 19:56:30 +05:30
|
|
|
#define hdr_buf bb_common_bufsiz1
|
|
|
|
#define sizeof_hdr_buf COMMON_BUFSIZE
|
2007-08-18 19:52:09 +05:30
|
|
|
char *hdr_ptr;
|
|
|
|
int hdr_cnt;
|
2020-08-16 03:22:48 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
char etag[sizeof("'%llx-%llx'") + 2 * sizeof(long long)*3];
|
|
|
|
#endif
|
2007-08-21 15:56:55 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
|
|
|
|
const char *http_error_page[ARRAY_SIZE(http_response_type)];
|
|
|
|
#endif
|
2007-10-18 18:24:39 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_PROXY
|
|
|
|
Htaccess_Proxy *proxy;
|
|
|
|
#endif
|
2007-06-10 05:19:05 +05:30
|
|
|
};
|
|
|
|
#define G (*ptr_to_globals)
|
2007-08-14 22:25:01 +05:30
|
|
|
#define verbose (G.verbose )
|
|
|
|
#define flg_deny_all (G.flg_deny_all )
|
2018-04-07 04:43:30 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_GZIP
|
|
|
|
# define content_gzip (G.content_gzip )
|
|
|
|
#else
|
|
|
|
# define content_gzip 0
|
|
|
|
#endif
|
2007-08-14 22:20:01 +05:30
|
|
|
#define bind_addr_or_port (G.bind_addr_or_port)
|
2007-08-14 22:25:01 +05:30
|
|
|
#define g_query (G.g_query )
|
2009-04-22 19:19:16 +05:30
|
|
|
#define opt_c_configFile (G.opt_c_configFile )
|
2007-08-14 22:25:01 +05:30
|
|
|
#define home_httpd (G.home_httpd )
|
2007-12-29 07:46:23 +05:30
|
|
|
#define index_page (G.index_page )
|
2007-08-14 22:25:01 +05:30
|
|
|
#define found_mime_type (G.found_mime_type )
|
|
|
|
#define found_moved_temporarily (G.found_moved_temporarily)
|
|
|
|
#define last_mod (G.last_mod )
|
|
|
|
#define g_realm (G.g_realm )
|
|
|
|
#define remoteuser (G.remoteuser )
|
2007-09-22 04:05:18 +05:30
|
|
|
#define file_size (G.file_size )
|
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
|
|
|
#define range_start (G.range_start )
|
|
|
|
#define range_end (G.range_end )
|
|
|
|
#define range_len (G.range_len )
|
2009-02-05 05:13:44 +05:30
|
|
|
#else
|
|
|
|
enum {
|
2012-02-19 21:48:45 +05:30
|
|
|
range_start = -1,
|
2009-02-05 05:13:44 +05:30
|
|
|
range_end = MAXINT(off_t) - 1,
|
|
|
|
range_len = MAXINT(off_t),
|
|
|
|
};
|
2007-09-22 04:05:18 +05:30
|
|
|
#endif
|
2007-08-14 22:25:01 +05:30
|
|
|
#define rmt_ip_str (G.rmt_ip_str )
|
|
|
|
#define g_auth (G.g_auth )
|
|
|
|
#define mime_a (G.mime_a )
|
|
|
|
#define script_i (G.script_i )
|
|
|
|
#define iobuf (G.iobuf )
|
2007-08-18 19:52:09 +05:30
|
|
|
#define hdr_ptr (G.hdr_ptr )
|
|
|
|
#define hdr_cnt (G.hdr_cnt )
|
2007-08-21 15:56:55 +05:30
|
|
|
#define http_error_page (G.http_error_page )
|
2007-10-18 18:24:39 +05:30
|
|
|
#define proxy (G.proxy )
|
2007-06-10 05:19:05 +05:30
|
|
|
#define INIT_G() do { \
|
2016-04-21 22:08:51 +05:30
|
|
|
setup_common_bufsiz(); \
|
2008-02-28 00:11:59 +05:30
|
|
|
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
|
2012-02-19 21:48:45 +05:30
|
|
|
IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
|
2007-08-14 22:20:01 +05:30
|
|
|
bind_addr_or_port = "80"; \
|
2009-09-09 00:47:49 +05:30
|
|
|
index_page = index_html; \
|
2007-09-22 04:05:18 +05:30
|
|
|
file_size = -1; \
|
2007-06-10 05:19:05 +05:30
|
|
|
} while (0)
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2007-08-18 19:52:09 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
|
2006-11-21 05:36:28 +05:30
|
|
|
|
2007-08-21 15:56:55 +05:30
|
|
|
/* Prototypes */
|
2008-02-21 05:42:07 +05:30
|
|
|
enum {
|
|
|
|
SEND_HEADERS = (1 << 0),
|
|
|
|
SEND_BODY = (1 << 1),
|
|
|
|
};
|
2008-07-05 14:48:54 +05:30
|
|
|
static void send_file_and_exit(const char *url, int what) NORETURN;
|
2007-08-21 15:56:55 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
static void free_llist(has_next_ptr **pptr)
|
|
|
|
{
|
|
|
|
has_next_ptr *cur = *pptr;
|
|
|
|
while (cur) {
|
|
|
|
has_next_ptr *t = cur;
|
|
|
|
cur = cur->next;
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
*pptr = NULL;
|
|
|
|
}
|
2006-11-21 05:36:28 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
|
|
|
|
{
|
|
|
|
free_llist((has_next_ptr**)pptr);
|
|
|
|
}
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
2007-08-18 00:50:07 +05:30
|
|
|
static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
|
|
|
|
{
|
|
|
|
free_llist((has_next_ptr**)pptr);
|
|
|
|
}
|
2020-08-16 18:28:31 +05:30
|
|
|
#endif
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
2007-08-20 02:45:42 +05:30
|
|
|
/* Returns presumed mask width in bits or < 0 on error.
|
|
|
|
* Updates strp, stores IP at provided pointer */
|
|
|
|
static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
|
2003-09-08 16:29:27 +05:30
|
|
|
{
|
2007-08-20 02:45:42 +05:30
|
|
|
const char *p = *strp;
|
2006-09-26 15:37:41 +05:30
|
|
|
int auto_mask = 8;
|
2007-08-20 02:45:42 +05:30
|
|
|
unsigned ip = 0;
|
2006-09-26 15:37:41 +05:30
|
|
|
int j;
|
|
|
|
|
2007-08-20 02:45:42 +05:30
|
|
|
if (*p == '/')
|
|
|
|
return -auto_mask;
|
|
|
|
|
2006-09-26 15:37:41 +05:30
|
|
|
for (j = 0; j < 4; j++) {
|
2007-06-10 05:19:05 +05:30
|
|
|
unsigned octet;
|
2006-09-26 15:37:41 +05:30
|
|
|
|
2007-08-20 02:45:42 +05:30
|
|
|
if ((*p < '0' || *p > '9') && *p != '/' && *p)
|
2006-09-26 15:37:41 +05:30
|
|
|
return -auto_mask;
|
|
|
|
octet = 0;
|
|
|
|
while (*p >= '0' && *p <= '9') {
|
|
|
|
octet *= 10;
|
|
|
|
octet += *p - '0';
|
|
|
|
if (octet > 255)
|
|
|
|
return -auto_mask;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (*p == '.')
|
|
|
|
p++;
|
2007-08-20 02:45:42 +05:30
|
|
|
if (*p != '/' && *p)
|
2006-09-26 15:37:41 +05:30
|
|
|
auto_mask += 8;
|
2007-08-20 02:45:42 +05:30
|
|
|
ip = (ip << 8) | octet;
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
2007-08-20 02:45:42 +05:30
|
|
|
if (*p) {
|
2006-09-26 15:37:41 +05:30
|
|
|
if (*p != endc)
|
|
|
|
return -auto_mask;
|
|
|
|
p++;
|
2007-08-18 00:50:39 +05:30
|
|
|
if (*p == '\0')
|
2006-09-26 15:37:41 +05:30
|
|
|
return -auto_mask;
|
|
|
|
}
|
2007-08-20 02:45:42 +05:30
|
|
|
*ipp = ip;
|
|
|
|
*strp = p;
|
2006-09-26 15:37:41 +05:30
|
|
|
return auto_mask;
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
|
|
|
|
2007-08-20 02:45:42 +05:30
|
|
|
/* Returns 0 on success. Stores IP and mask at provided pointers */
|
|
|
|
static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
|
2003-09-08 16:29:27 +05:30
|
|
|
{
|
2006-09-26 15:37:41 +05:30
|
|
|
int i;
|
2007-08-20 02:45:42 +05:30
|
|
|
unsigned mask;
|
|
|
|
char *p;
|
2006-09-26 15:37:41 +05:30
|
|
|
|
2007-08-20 02:45:42 +05:30
|
|
|
i = scan_ip(&str, ipp, '/');
|
2006-09-26 15:37:41 +05:30
|
|
|
if (i < 0)
|
|
|
|
return i;
|
2007-08-20 02:45:42 +05:30
|
|
|
|
|
|
|
if (*str) {
|
|
|
|
/* there is /xxx after dotted-IP address */
|
|
|
|
i = bb_strtou(str, &p, 10);
|
|
|
|
if (*p == '.') {
|
|
|
|
/* 'xxx' itself is dotted-IP mask, parse it */
|
|
|
|
/* (return 0 (success) only if it has N.N.N.N form) */
|
|
|
|
return scan_ip(&str, maskp, '\0') - 32;
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
2007-08-20 02:45:42 +05:30
|
|
|
if (*p)
|
|
|
|
return -1;
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
2007-08-20 02:45:42 +05:30
|
|
|
|
|
|
|
if (i > 32)
|
2006-10-04 01:26:34 +05:30
|
|
|
return -1;
|
2007-08-20 02:45:42 +05:30
|
|
|
|
|
|
|
if (sizeof(unsigned) == 4 && i == 32) {
|
|
|
|
/* mask >>= 32 below may not work */
|
|
|
|
mask = 0;
|
|
|
|
} else {
|
|
|
|
mask = 0xffffffff;
|
|
|
|
mask >>= i;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2007-08-20 02:45:42 +05:30
|
|
|
/* i == 0 -> *maskp = 0x00000000
|
|
|
|
* i == 1 -> *maskp = 0x80000000
|
|
|
|
* i == 4 -> *maskp = 0xf0000000
|
|
|
|
* i == 31 -> *maskp = 0xfffffffe
|
|
|
|
* i == 32 -> *maskp = 0xffffffff */
|
|
|
|
*maskp = (uint32_t)(~mask);
|
2006-09-26 15:37:41 +05:30
|
|
|
return 0;
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
2020-08-16 18:28:31 +05:30
|
|
|
#endif
|
2003-05-13 21:50:11 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
/*
|
|
|
|
* Parse configuration file into in-memory linked list.
|
2004-03-15 13:59:22 +05:30
|
|
|
*
|
2003-05-19 11:26:16 +05:30
|
|
|
* Any previous IP rules are discarded.
|
|
|
|
* If the flag argument is not SUBDIR_PARSE then all /path and mime rules
|
|
|
|
* are also discarded. That is, previous settings are retained if flag is
|
|
|
|
* SUBDIR_PARSE.
|
2007-08-21 15:56:55 +05:30
|
|
|
* Error pages are only parsed on the main config file.
|
2003-05-19 11:26:16 +05:30
|
|
|
*
|
2007-08-18 00:50:07 +05:30
|
|
|
* path Path where to look for httpd.conf (without filename).
|
|
|
|
* flag Type of the parse request.
|
|
|
|
*/
|
2009-04-22 19:19:16 +05:30
|
|
|
/* flag param: */
|
|
|
|
enum {
|
|
|
|
FIRST_PARSE = 0, /* path will be "/etc" */
|
|
|
|
SIGNALED_PARSE = 1, /* path will be "/etc" */
|
|
|
|
SUBDIR_PARSE = 2, /* path will be derived from URL */
|
|
|
|
};
|
2021-05-05 00:55:16 +05:30
|
|
|
static int parse_conf(const char *path, int flag)
|
2003-02-09 12:21:14 +05:30
|
|
|
{
|
2009-04-22 19:19:16 +05:30
|
|
|
/* internally used extra flag state */
|
|
|
|
enum { TRY_CURDIR_PARSE = 3 };
|
|
|
|
|
2006-09-26 15:37:41 +05:30
|
|
|
FILE *f;
|
2009-04-22 19:19:16 +05:30
|
|
|
const char *filename;
|
2006-09-26 15:37:41 +05:30
|
|
|
char buf[160];
|
2003-05-13 21:50:11 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
/* discard old rules */
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
|
|
|
free_Htaccess_IP_list(&G.ip_a_d);
|
|
|
|
#endif
|
2007-06-10 05:19:05 +05:30
|
|
|
flg_deny_all = 0;
|
2006-09-26 15:37:41 +05:30
|
|
|
/* retain previous auth and mime config only for subdir parse */
|
|
|
|
if (flag != SUBDIR_PARSE) {
|
2009-04-22 19:19:16 +05:30
|
|
|
free_Htaccess_list(&mime_a);
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2007-08-18 00:50:07 +05:30
|
|
|
free_Htaccess_list(&g_auth);
|
2003-05-13 21:50:11 +05:30
|
|
|
#endif
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
|
2007-08-18 00:50:07 +05:30
|
|
|
free_Htaccess_list(&script_i);
|
2003-05-13 21:50:11 +05:30
|
|
|
#endif
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2009-04-22 19:19:16 +05:30
|
|
|
filename = opt_c_configFile;
|
2008-06-13 15:23:06 +05:30
|
|
|
if (flag == SUBDIR_PARSE || filename == NULL) {
|
2009-04-22 19:19:16 +05:30
|
|
|
filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2);
|
|
|
|
sprintf((char *)filename, "%s/%s", path, HTTPD_CONF);
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|
|
|
|
|
2008-07-22 04:35:26 +05:30
|
|
|
while ((f = fopen_for_read(filename)) == NULL) {
|
2009-04-22 19:19:16 +05:30
|
|
|
if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */
|
2006-09-26 15:37:41 +05:30
|
|
|
/* config file not found, no changes to config */
|
2021-05-05 00:55:16 +05:30
|
|
|
return -1;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2009-04-22 19:19:16 +05:30
|
|
|
if (flag == FIRST_PARSE) {
|
|
|
|
/* -c CONFFILE given, but CONFFILE doesn't exist? */
|
|
|
|
if (opt_c_configFile)
|
|
|
|
bb_simple_perror_msg_and_die(opt_c_configFile);
|
|
|
|
/* else: no -c, thus we looked at /etc/httpd.conf,
|
|
|
|
* and it's not there. try ./httpd.conf: */
|
|
|
|
}
|
|
|
|
flag = TRY_CURDIR_PARSE;
|
|
|
|
filename = HTTPD_CONF;
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2009-04-22 19:19:16 +05:30
|
|
|
/* in "/file:user:pass" lines, we prepend path in subdirs */
|
|
|
|
if (flag != SUBDIR_PARSE)
|
|
|
|
path = "";
|
|
|
|
#endif
|
|
|
|
/* The lines can be:
|
|
|
|
*
|
|
|
|
* I:default_index_file
|
|
|
|
* H:http_home
|
|
|
|
* [AD]:IP[/mask] # allow/deny, * for wildcard
|
|
|
|
* Ennn:error.html # error page for status nnn
|
|
|
|
* P:/url:[http://]hostname[:port]/new/path # reverse proxy
|
|
|
|
* .ext:mime/type # mime type
|
|
|
|
* *.php:/path/php # run xxx.php through an interpreter
|
|
|
|
* /file:user:pass # username and password
|
|
|
|
*/
|
|
|
|
while (fgets(buf, sizeof(buf), f) != NULL) {
|
|
|
|
unsigned strlen_buf;
|
|
|
|
unsigned char ch;
|
2009-05-02 04:20:38 +05:30
|
|
|
char *after_colon;
|
2009-04-22 19:19:16 +05:30
|
|
|
|
|
|
|
{ /* remove all whitespace, and # comments */
|
|
|
|
char *p, *p0;
|
|
|
|
|
2009-05-02 04:20:38 +05:30
|
|
|
p0 = buf;
|
|
|
|
/* skip non-whitespace beginning. Often the whole line
|
|
|
|
* is non-whitespace. We want this case to work fast,
|
|
|
|
* without needless copying, therefore we don't merge
|
|
|
|
* this operation into next while loop. */
|
|
|
|
while ((ch = *p0) != '\0' && ch != '\n' && ch != '#'
|
|
|
|
&& ch != ' ' && ch != '\t'
|
|
|
|
) {
|
|
|
|
p0++;
|
|
|
|
}
|
|
|
|
p = p0;
|
|
|
|
/* if we enter this loop, we have some whitespace.
|
|
|
|
* discard it */
|
|
|
|
while (ch != '\0' && ch != '\n' && ch != '#') {
|
|
|
|
if (ch != ' ' && ch != '\t') {
|
2009-04-22 19:19:16 +05:30
|
|
|
*p++ = ch;
|
|
|
|
}
|
2009-05-02 04:20:38 +05:30
|
|
|
ch = *++p0;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2009-04-22 19:19:16 +05:30
|
|
|
*p = '\0';
|
|
|
|
strlen_buf = p - buf;
|
2009-04-22 19:22:22 +05:30
|
|
|
if (strlen_buf == 0)
|
2009-05-02 04:20:38 +05:30
|
|
|
continue; /* empty line */
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
|
|
|
|
2009-05-02 04:20:38 +05:30
|
|
|
after_colon = strchr(buf, ':');
|
2009-04-22 19:22:22 +05:30
|
|
|
/* strange line? */
|
2009-05-02 04:20:38 +05:30
|
|
|
if (after_colon == NULL || *++after_colon == '\0')
|
2009-04-22 19:19:16 +05:30
|
|
|
goto config_error;
|
|
|
|
|
|
|
|
ch = (buf[0] & ~0x20); /* toupper if it's a letter */
|
|
|
|
|
|
|
|
if (ch == 'I') {
|
2009-09-09 00:47:49 +05:30
|
|
|
if (index_page != index_html)
|
|
|
|
free((char*)index_page);
|
2009-04-22 19:19:16 +05:30
|
|
|
index_page = xstrdup(after_colon);
|
2006-09-26 15:37:41 +05:30
|
|
|
continue;
|
2009-04-22 19:19:16 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/* do not allow jumping around using H in subdir's configs */
|
|
|
|
if (flag == FIRST_PARSE && ch == 'H') {
|
|
|
|
home_httpd = xstrdup(after_colon);
|
|
|
|
xchdir(home_httpd);
|
2006-09-26 15:37:41 +05:30
|
|
|
continue;
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|
2003-05-26 19:37:50 +05:30
|
|
|
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
2009-04-22 19:19:16 +05:30
|
|
|
if (ch == 'A' || ch == 'D') {
|
|
|
|
Htaccess_IP *pip;
|
|
|
|
|
|
|
|
if (*after_colon == '*') {
|
|
|
|
if (ch == 'D') {
|
|
|
|
/* memorize "deny all" */
|
|
|
|
flg_deny_all = 1;
|
|
|
|
}
|
|
|
|
/* skip assumed "A:*", it is a default anyway */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* store "allow/deny IP/mask" line */
|
|
|
|
pip = xzalloc(sizeof(*pip));
|
|
|
|
if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) {
|
2008-06-13 15:23:06 +05:30
|
|
|
/* IP{/mask} syntax error detected, protect all */
|
2009-04-22 19:19:16 +05:30
|
|
|
ch = 'D';
|
2008-06-13 15:23:06 +05:30
|
|
|
pip->mask = 0;
|
|
|
|
}
|
2009-04-22 19:19:16 +05:30
|
|
|
pip->allow_deny = ch;
|
|
|
|
if (ch == 'D') {
|
2008-06-13 15:23:06 +05:30
|
|
|
/* Deny:from_IP - prepend */
|
2020-08-16 18:28:31 +05:30
|
|
|
pip->next = G.ip_a_d;
|
|
|
|
G.ip_a_d = pip;
|
2008-06-13 15:23:06 +05:30
|
|
|
} else {
|
2009-04-22 19:19:16 +05:30
|
|
|
/* A:from_IP - append (thus all D's precedes A's) */
|
2020-08-16 18:28:31 +05:30
|
|
|
Htaccess_IP *prev_IP = G.ip_a_d;
|
2008-06-13 15:23:06 +05:30
|
|
|
if (prev_IP == NULL) {
|
2020-08-16 18:28:31 +05:30
|
|
|
G.ip_a_d = pip;
|
2006-09-26 15:37:41 +05:30
|
|
|
} else {
|
2008-06-13 15:23:06 +05:30
|
|
|
while (prev_IP->next)
|
|
|
|
prev_IP = prev_IP->next;
|
|
|
|
prev_IP->next = pip;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
2006-09-26 15:37:41 +05:30
|
|
|
continue;
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
2020-08-16 18:28:31 +05:30
|
|
|
#endif
|
2007-08-21 15:56:55 +05:30
|
|
|
|
|
|
|
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
|
2009-04-22 19:19:16 +05:30
|
|
|
if (flag == FIRST_PARSE && ch == 'E') {
|
2008-05-16 03:00:45 +05:30
|
|
|
unsigned i;
|
2009-04-22 19:19:16 +05:30
|
|
|
int status = atoi(buf + 1); /* error status code */
|
|
|
|
|
2007-08-21 15:56:55 +05:30
|
|
|
if (status < HTTP_CONTINUE) {
|
2009-04-22 19:19:16 +05:30
|
|
|
goto config_error;
|
2007-08-21 15:56:55 +05:30
|
|
|
}
|
|
|
|
/* then error page; find matching status */
|
|
|
|
for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
|
|
|
|
if (http_response_type[i] == status) {
|
2008-06-13 15:23:06 +05:30
|
|
|
/* We chdir to home_httpd, thus no need to
|
|
|
|
* concat_path_file(home_httpd, after_colon)
|
|
|
|
* here */
|
|
|
|
http_error_page[i] = xstrdup(after_colon);
|
2007-08-21 15:56:55 +05:30
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-10-18 18:24:39 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_PROXY
|
2009-04-22 19:19:16 +05:30
|
|
|
if (flag == FIRST_PARSE && ch == 'P') {
|
2007-10-18 18:24:39 +05:30
|
|
|
/* P:/url:[http://]hostname[:port]/new/path */
|
|
|
|
char *url_from, *host_port, *url_to;
|
|
|
|
Htaccess_Proxy *proxy_entry;
|
|
|
|
|
2008-06-13 15:23:06 +05:30
|
|
|
url_from = after_colon;
|
|
|
|
host_port = strchr(after_colon, ':');
|
2007-10-18 18:24:39 +05:30
|
|
|
if (host_port == NULL) {
|
2009-04-22 19:19:16 +05:30
|
|
|
goto config_error;
|
2007-10-18 18:24:39 +05:30
|
|
|
}
|
|
|
|
*host_port++ = '\0';
|
2015-03-12 22:18:34 +05:30
|
|
|
if (is_prefixed_with(host_port, "http://"))
|
2007-10-22 04:54:42 +05:30
|
|
|
host_port += 7;
|
2007-10-18 18:24:39 +05:30
|
|
|
if (*host_port == '\0') {
|
2009-04-22 19:19:16 +05:30
|
|
|
goto config_error;
|
2007-10-18 18:24:39 +05:30
|
|
|
}
|
|
|
|
url_to = strchr(host_port, '/');
|
|
|
|
if (url_to == NULL) {
|
2009-04-22 19:19:16 +05:30
|
|
|
goto config_error;
|
2007-10-18 18:24:39 +05:30
|
|
|
}
|
|
|
|
*url_to = '\0';
|
2009-04-22 19:19:16 +05:30
|
|
|
proxy_entry = xzalloc(sizeof(*proxy_entry));
|
2007-10-18 18:24:39 +05:30
|
|
|
proxy_entry->url_from = xstrdup(url_from);
|
|
|
|
proxy_entry->host_port = xstrdup(host_port);
|
|
|
|
*url_to = '/';
|
|
|
|
proxy_entry->url_to = xstrdup(url_to);
|
|
|
|
proxy_entry->next = proxy;
|
|
|
|
proxy = proxy_entry;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2009-04-22 19:19:16 +05:30
|
|
|
/* the rest of directives are non-alphabetic,
|
|
|
|
* must avoid using "toupper'ed" ch */
|
|
|
|
ch = buf[0];
|
2007-10-18 18:24:39 +05:30
|
|
|
|
2009-04-22 19:19:16 +05:30
|
|
|
if (ch == '.' /* ".ext:mime/type" */
|
|
|
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
|
|
|
|
|| (ch == '*' && buf[1] == '.') /* "*.php:/path/php" */
|
|
|
|
#endif
|
|
|
|
) {
|
|
|
|
char *p;
|
|
|
|
Htaccess *cur;
|
2006-09-26 15:37:41 +05:30
|
|
|
|
2009-04-22 19:19:16 +05:30
|
|
|
cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + strlen_buf);
|
|
|
|
strcpy(cur->before_colon, buf);
|
|
|
|
p = cur->before_colon + (after_colon - buf);
|
|
|
|
p[-1] = '\0';
|
|
|
|
cur->after_colon = p;
|
|
|
|
if (ch == '.') {
|
|
|
|
/* .mime line: prepend to mime_a list */
|
|
|
|
cur->next = mime_a;
|
|
|
|
mime_a = cur;
|
|
|
|
}
|
|
|
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
|
|
|
|
else {
|
|
|
|
/* script interpreter line: prepend to script_i list */
|
|
|
|
cur->next = script_i;
|
|
|
|
script_i = cur;
|
2003-05-13 21:50:11 +05:30
|
|
|
}
|
|
|
|
#endif
|
2008-07-21 04:55:32 +05:30
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-10-18 18:24:39 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2009-04-22 19:19:16 +05:30
|
|
|
if (ch == '/') { /* "/file:user:pass" */
|
|
|
|
char *p;
|
|
|
|
Htaccess *cur;
|
|
|
|
unsigned file_len;
|
|
|
|
|
|
|
|
/* note: path is "" unless we are in SUBDIR parse,
|
2009-04-22 19:46:59 +05:30
|
|
|
* otherwise it does NOT start with "/" */
|
2009-04-22 19:19:16 +05:30
|
|
|
cur = xzalloc(sizeof(*cur) /* includes space for NUL */
|
2009-04-22 19:46:59 +05:30
|
|
|
+ 1 + strlen(path)
|
2009-04-22 19:19:16 +05:30
|
|
|
+ strlen_buf
|
|
|
|
);
|
|
|
|
/* form "/path/file" */
|
2009-04-22 19:46:59 +05:30
|
|
|
sprintf(cur->before_colon, "/%s%.*s",
|
2009-04-22 19:19:16 +05:30
|
|
|
path,
|
2009-06-10 02:31:24 +05:30
|
|
|
(int) (after_colon - buf - 1), /* includes "/", but not ":" */
|
2009-04-22 19:19:16 +05:30
|
|
|
buf);
|
|
|
|
/* canonicalize it */
|
|
|
|
p = bb_simplify_abs_path_inplace(cur->before_colon);
|
|
|
|
file_len = p - cur->before_colon;
|
|
|
|
/* add "user:pass" after NUL */
|
|
|
|
strcpy(++p, after_colon);
|
|
|
|
cur->after_colon = p;
|
|
|
|
|
|
|
|
/* insert cur into g_auth */
|
|
|
|
/* g_auth is sorted by decreased filename length */
|
|
|
|
{
|
|
|
|
Htaccess *auth, **authp;
|
|
|
|
|
|
|
|
authp = &g_auth;
|
|
|
|
while ((auth = *authp) != NULL) {
|
|
|
|
if (file_len >= strlen(auth->before_colon)) {
|
|
|
|
/* insert cur before auth */
|
|
|
|
cur->next = auth;
|
|
|
|
break;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2009-04-22 19:19:16 +05:30
|
|
|
authp = &auth->next;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2009-04-22 19:19:16 +05:30
|
|
|
*authp = cur;
|
2003-05-13 21:50:11 +05:30
|
|
|
}
|
2009-04-22 19:19:16 +05:30
|
|
|
continue;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2007-10-18 18:24:39 +05:30
|
|
|
#endif /* BASIC_AUTH */
|
2009-04-22 19:19:16 +05:30
|
|
|
|
|
|
|
/* the line is not recognized */
|
|
|
|
config_error:
|
|
|
|
bb_error_msg("config error '%s' in '%s'", buf, filename);
|
2013-01-14 06:04:48 +05:30
|
|
|
} /* while (fgets) */
|
2009-04-22 19:19:16 +05:30
|
|
|
|
2013-01-14 06:04:48 +05:30
|
|
|
fclose(f);
|
2021-05-05 00:55:16 +05:30
|
|
|
return 0;
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
|
2007-08-18 00:50:07 +05:30
|
|
|
/*
|
|
|
|
* Given a string, html-encode special characters.
|
|
|
|
* This is used for the -e command line option to provide an easy way
|
|
|
|
* for scripts to encode result data without confusing browsers. The
|
|
|
|
* returned string pointer is memory allocated by malloc().
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2007-08-18 00:50:07 +05:30
|
|
|
* Returns a pointer to the encoded string (malloced).
|
|
|
|
*/
|
2003-01-05 09:31:56 +05:30
|
|
|
static char *encodeString(const char *string)
|
|
|
|
{
|
2006-09-26 15:37:41 +05:30
|
|
|
/* take the simple route and encode everything */
|
|
|
|
/* could possibly scan once to get length. */
|
|
|
|
int len = strlen(string);
|
2007-01-30 04:21:58 +05:30
|
|
|
char *out = xmalloc(len * 6 + 1);
|
2006-09-26 15:37:41 +05:30
|
|
|
char *p = out;
|
|
|
|
char ch;
|
|
|
|
|
2010-07-25 06:50:25 +05:30
|
|
|
while ((ch = *string++) != '\0') {
|
2007-08-21 16:48:25 +05:30
|
|
|
/* very simple check for what to encode */
|
|
|
|
if (isalnum(ch))
|
|
|
|
*p++ = ch;
|
|
|
|
else
|
2018-04-07 04:43:30 +05:30
|
|
|
p += sprintf(p, "&#%u;", (unsigned char) ch);
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2007-01-30 04:21:58 +05:30
|
|
|
*p = '\0';
|
2006-09-26 15:37:41 +05:30
|
|
|
return out;
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
2010-07-25 06:50:25 +05:30
|
|
|
#endif
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2007-08-14 22:25:01 +05:30
|
|
|
/*
|
2007-08-18 00:50:07 +05:30
|
|
|
* Decode a base64 data stream as per rfc1521.
|
2007-08-14 22:25:01 +05:30
|
|
|
* Note that the rfc states that non base64 chars are to be ignored.
|
2007-08-18 00:50:07 +05:30
|
|
|
* Since the decode always results in a shorter size than the input,
|
|
|
|
* it is OK to pass the input arg as an output arg.
|
2007-08-14 22:25:01 +05:30
|
|
|
* Parameter: a pointer to a base64 encoded string.
|
|
|
|
* Decoded data is stored in-place.
|
|
|
|
*/
|
2020-11-28 17:56:44 +05:30
|
|
|
static void decodeBase64(char *data)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2020-11-28 17:56:44 +05:30
|
|
|
decode_base64(data, NULL)[0] = '\0';
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
/*
|
|
|
|
* Create a listen server socket on the designated port.
|
|
|
|
*/
|
2003-02-09 12:21:14 +05:30
|
|
|
static int openServer(void)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2007-10-18 18:24:39 +05:30
|
|
|
unsigned n = bb_strtou(bind_addr_or_port, NULL, 10);
|
2007-08-14 22:20:01 +05:30
|
|
|
if (!errno && n && n <= 0xffff)
|
|
|
|
n = create_and_bind_stream_or_die(NULL, n);
|
|
|
|
else
|
|
|
|
n = create_and_bind_stream_or_die(bind_addr_or_port, 80);
|
|
|
|
xlisten(n, 9);
|
|
|
|
return n;
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
|
|
|
|
2007-08-18 00:50:39 +05:30
|
|
|
/*
|
|
|
|
* Log the connection closure and exit.
|
|
|
|
*/
|
2008-07-05 14:48:54 +05:30
|
|
|
static void log_and_exit(void) NORETURN;
|
2007-08-18 00:50:39 +05:30
|
|
|
static void log_and_exit(void)
|
|
|
|
{
|
2007-08-20 00:58:09 +05:30
|
|
|
/* Paranoia. IE said to be buggy. It may send some extra data
|
|
|
|
* or be confused by us just exiting without SHUT_WR. Oh well. */
|
|
|
|
shutdown(1, SHUT_WR);
|
2008-05-18 20:44:36 +05:30
|
|
|
/* Why??
|
|
|
|
(this also messes up stdin when user runs httpd -i from terminal)
|
2007-08-20 00:58:09 +05:30
|
|
|
ndelay_on(0);
|
2008-05-19 15:18:17 +05:30
|
|
|
while (read(STDIN_FILENO, iobuf, IOBUF_SIZE) > 0)
|
2007-08-20 00:58:09 +05:30
|
|
|
continue;
|
2008-05-18 20:44:36 +05:30
|
|
|
*/
|
2007-08-20 00:58:09 +05:30
|
|
|
|
2007-08-18 00:50:39 +05:30
|
|
|
if (verbose > 2)
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_error_msg("closed");
|
2007-08-18 00:50:39 +05:30
|
|
|
_exit(xfunc_error_retval);
|
|
|
|
}
|
|
|
|
|
2007-08-14 22:20:01 +05:30
|
|
|
/*
|
|
|
|
* Create and send HTTP response headers.
|
|
|
|
* The arguments are combined and sent as one write operation. Note that
|
|
|
|
* IE will puke big-time if the headers are not sent in one packet and the
|
|
|
|
* second packet is delayed for any reason.
|
|
|
|
* responseNum - the result code to send.
|
|
|
|
*/
|
2018-04-07 04:43:30 +05:30
|
|
|
static void send_headers(unsigned responseNum)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2020-08-09 03:53:33 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_DATE || ENABLE_FEATURE_HTTPD_LAST_MODIFIED
|
2007-08-18 00:50:07 +05:30
|
|
|
static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
|
2016-11-22 06:53:35 +05:30
|
|
|
/* Fixed size 29-byte string. Example: Sun, 06 Nov 1994 08:49:37 GMT */
|
|
|
|
char date_str[40]; /* using a bit larger buffer to paranoia reasons */
|
2018-03-06 22:41:47 +05:30
|
|
|
struct tm tm;
|
2020-08-09 03:53:33 +05:30
|
|
|
#endif
|
2006-09-26 15:37:41 +05:30
|
|
|
const char *responseString = "";
|
2007-08-14 22:25:01 +05:30
|
|
|
const char *infoString = NULL;
|
2007-08-21 15:56:55 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
|
2008-02-21 05:42:07 +05:30
|
|
|
const char *error_page = NULL;
|
2007-08-21 15:56:55 +05:30
|
|
|
#endif
|
2018-04-07 04:43:30 +05:30
|
|
|
unsigned len;
|
2007-01-14 22:36:11 +05:30
|
|
|
unsigned i;
|
2006-09-26 15:37:41 +05:30
|
|
|
|
2007-08-21 15:56:55 +05:30
|
|
|
for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
|
|
|
|
if (http_response_type[i] == responseNum) {
|
|
|
|
responseString = http_response[i].name;
|
|
|
|
infoString = http_response[i].info;
|
|
|
|
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
|
|
|
|
error_page = http_error_page[i];
|
|
|
|
#endif
|
2003-02-09 12:21:14 +05:30
|
|
|
break;
|
|
|
|
}
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
|
|
|
|
2007-08-14 22:25:01 +05:30
|
|
|
if (verbose)
|
2007-08-18 00:48:47 +05:30
|
|
|
bb_error_msg("response:%u", responseNum);
|
2007-08-14 22:25:01 +05:30
|
|
|
|
2016-11-22 06:53:35 +05:30
|
|
|
/* We use sprintf, not snprintf (it's less code).
|
|
|
|
* iobuf[] is several kbytes long and all headers we generate
|
|
|
|
* always fit into those kbytes.
|
|
|
|
*/
|
|
|
|
|
2020-08-09 03:53:33 +05:30
|
|
|
{
|
2020-08-09 03:53:32 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_DATE
|
2020-08-09 03:53:33 +05:30
|
|
|
time_t timer = time(NULL);
|
|
|
|
strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&timer, &tm));
|
|
|
|
/* ^^^ using gmtime_r() instead of gmtime() to not use static data */
|
2020-08-09 03:53:32 +05:30
|
|
|
#endif
|
2020-08-09 03:53:33 +05:30
|
|
|
len = sprintf(iobuf,
|
2020-08-09 03:53:31 +05:30
|
|
|
"HTTP/1.1 %u %s\r\n"
|
2020-08-09 03:53:32 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_DATE
|
2016-11-22 06:53:35 +05:30
|
|
|
"Date: %s\r\n"
|
2020-08-09 03:53:32 +05:30
|
|
|
#endif
|
2016-11-22 06:53:35 +05:30
|
|
|
"Connection: close\r\n",
|
2020-08-09 03:53:32 +05:30
|
|
|
responseNum, responseString
|
|
|
|
#if ENABLE_FEATURE_HTTPD_DATE
|
|
|
|
,date_str
|
|
|
|
#endif
|
2020-08-09 03:53:33 +05:30
|
|
|
);
|
|
|
|
}
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2018-04-07 04:43:30 +05:30
|
|
|
if (responseNum != HTTP_OK || found_mime_type) {
|
|
|
|
len += sprintf(iobuf + len,
|
|
|
|
"Content-type: %s\r\n",
|
|
|
|
/* if it's error message, then it's HTML */
|
|
|
|
(responseNum != HTTP_OK ? "text/html" : found_mime_type)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2006-09-26 15:37:41 +05:30
|
|
|
if (responseNum == HTTP_UNAUTHORIZED) {
|
2007-08-14 22:25:01 +05:30
|
|
|
len += sprintf(iobuf + len,
|
2016-11-22 06:53:35 +05:30
|
|
|
"WWW-Authenticate: Basic realm=\"%.999s\"\r\n",
|
|
|
|
g_realm /* %.999s protects from overflowing iobuf[] */
|
|
|
|
);
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (responseNum == HTTP_MOVED_TEMPORARILY) {
|
2015-10-23 15:19:04 +05:30
|
|
|
/* Responding to "GET /dir" with
|
2020-08-09 03:53:31 +05:30
|
|
|
* "HTTP/1.1 302 Found" "Location: /dir/"
|
2015-10-23 15:19:04 +05:30
|
|
|
* - IOW, asking them to repeat with a slash.
|
|
|
|
* Here, overflow IS possible, can't use sprintf:
|
|
|
|
* mkdir test
|
|
|
|
* python -c 'print("get /test?" + ("x" * 8192))' | busybox httpd -i -h .
|
|
|
|
*/
|
|
|
|
len += snprintf(iobuf + len, IOBUF_SIZE-3 - len,
|
|
|
|
"Location: %s/%s%s\r\n",
|
2007-06-10 05:19:05 +05:30
|
|
|
found_moved_temporarily,
|
|
|
|
(g_query ? "?" : ""),
|
2016-11-22 06:53:35 +05:30
|
|
|
(g_query ? g_query : "")
|
|
|
|
);
|
2015-10-23 15:19:04 +05:30
|
|
|
if (len > IOBUF_SIZE-3)
|
|
|
|
len = IOBUF_SIZE-3;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
|
|
|
|
2007-08-21 15:56:55 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
|
2008-05-07 17:48:48 +05:30
|
|
|
if (error_page && access(error_page, R_OK) == 0) {
|
2015-10-23 15:19:04 +05:30
|
|
|
iobuf[len++] = '\r';
|
|
|
|
iobuf[len++] = '\n';
|
|
|
|
if (DEBUG) {
|
|
|
|
iobuf[len] = '\0';
|
2007-08-21 15:56:55 +05:30
|
|
|
fprintf(stderr, "headers: '%s'\n", iobuf);
|
2015-10-23 15:19:04 +05:30
|
|
|
}
|
2008-05-19 15:18:17 +05:30
|
|
|
full_write(STDOUT_FILENO, iobuf, len);
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("writing error page: '%s'\n", error_page);
|
2008-02-21 05:42:07 +05:30
|
|
|
return send_file_and_exit(error_page, SEND_BODY);
|
2007-08-21 15:56:55 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-09-22 04:05:18 +05:30
|
|
|
if (file_size != -1) { /* file */
|
2020-08-09 03:53:33 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
|
2018-03-06 22:41:47 +05:30
|
|
|
strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&last_mod, &tm));
|
2020-08-09 03:53:33 +05:30
|
|
|
#endif
|
2007-09-22 04:05:18 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
|
|
|
if (responseNum == HTTP_PARTIAL_CONTENT) {
|
2016-11-22 06:53:35 +05:30
|
|
|
len += sprintf(iobuf + len,
|
|
|
|
"Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n",
|
2007-09-22 04:05:18 +05:30
|
|
|
range_start,
|
|
|
|
range_end,
|
2016-11-22 06:53:35 +05:30
|
|
|
file_size
|
|
|
|
);
|
2007-09-22 04:05:18 +05:30
|
|
|
file_size = range_end - range_start + 1;
|
|
|
|
}
|
|
|
|
#endif
|
2019-04-17 15:04:21 +05:30
|
|
|
|
|
|
|
//RFC 2616 4.4 Message Length
|
|
|
|
// The transfer-length of a message is the length of the message-body as
|
|
|
|
// it appears in the message; that is, after any transfer-codings have
|
|
|
|
// been applied. When a message-body is included with a message, the
|
|
|
|
// transfer-length of that body is determined by one of the following
|
|
|
|
// (in order of precedence):
|
|
|
|
// 1.Any response message which "MUST NOT" include a message-body (such
|
|
|
|
// as the 1xx, 204, and 304 responses and any response to a HEAD
|
|
|
|
// request) is always terminated by the first empty line after the
|
|
|
|
// header fields, regardless of the entity-header fields present in
|
|
|
|
// the message.
|
|
|
|
// 2.If a Transfer-Encoding header field (section 14.41) is present and
|
|
|
|
// has any value other than "identity", then the transfer-length is
|
|
|
|
// defined by use of the "chunked" transfer-coding (section 3.6),
|
|
|
|
// unless the message is terminated by closing the connection.
|
|
|
|
// 3.If a Content-Length header field (section 14.13) is present, its
|
|
|
|
// decimal value in OCTETs represents both the entity-length and the
|
|
|
|
// transfer-length. The Content-Length header field MUST NOT be sent
|
|
|
|
// if these two lengths are different (i.e., if a Transfer-Encoding
|
|
|
|
// header field is present). If a message is received with both a
|
|
|
|
// Transfer-Encoding header field and a Content-Length header field,
|
|
|
|
// the latter MUST be ignored.
|
|
|
|
// 4.If the message uses the media type "multipart/byteranges" ...
|
|
|
|
// 5.By the server closing the connection.
|
|
|
|
//
|
|
|
|
// (NB: standards do not define "Transfer-Length:" _header_,
|
|
|
|
// transfer-length above is just a concept).
|
|
|
|
|
2007-09-22 04:05:18 +05:30
|
|
|
len += sprintf(iobuf + len,
|
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
|
|
|
"Accept-Ranges: bytes\r\n"
|
|
|
|
#endif
|
2020-08-09 03:53:33 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
|
2016-11-22 06:53:35 +05:30
|
|
|
"Last-Modified: %s\r\n"
|
2020-08-09 03:53:33 +05:30
|
|
|
#endif
|
2020-08-16 03:22:48 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
"ETag: %s\r\n"
|
|
|
|
#endif
|
|
|
|
|
2019-04-17 15:04:21 +05:30
|
|
|
/* Because of 4.4 (5), we can forgo sending of "Content-Length"
|
|
|
|
* since we close connection afterwards, but it helps clients
|
|
|
|
* to e.g. estimate download times, show progress bars etc.
|
|
|
|
* Theoretically we should not send it if page is compressed,
|
|
|
|
* but de-facto standard is to send it (see comment below).
|
|
|
|
*/
|
|
|
|
"Content-Length: %"OFF_FMT"u\r\n",
|
2020-08-09 03:53:33 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
|
2016-11-22 06:53:35 +05:30
|
|
|
date_str,
|
2020-08-16 03:22:48 +05:30
|
|
|
#endif
|
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
G.etag,
|
2020-08-09 03:53:33 +05:30
|
|
|
#endif
|
2007-09-22 04:05:18 +05:30
|
|
|
file_size
|
|
|
|
);
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2010-07-25 06:50:53 +05:30
|
|
|
|
2019-04-17 15:04:21 +05:30
|
|
|
/* This should be "Transfer-Encoding", not "Content-Encoding":
|
|
|
|
* "data is compressed for transfer", not "data is an archive".
|
|
|
|
* But many clients were not handling "Transfer-Encoding" correctly
|
|
|
|
* (they were not uncompressing gzipped pages, tried to show
|
|
|
|
* raw compressed data), and servers worked around it by using
|
|
|
|
* "Content-Encoding" instead... and this become de-facto standard.
|
|
|
|
* https://bugzilla.mozilla.org/show_bug.cgi?id=68517
|
|
|
|
* https://bugs.chromium.org/p/chromium/issues/detail?id=94730
|
|
|
|
*/
|
2010-07-25 06:50:53 +05:30
|
|
|
if (content_gzip)
|
|
|
|
len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n");
|
|
|
|
|
2007-08-18 00:50:39 +05:30
|
|
|
iobuf[len++] = '\r';
|
|
|
|
iobuf[len++] = '\n';
|
2006-09-26 15:37:41 +05:30
|
|
|
if (infoString) {
|
2007-08-14 22:25:01 +05:30
|
|
|
len += sprintf(iobuf + len,
|
2018-04-07 04:43:30 +05:30
|
|
|
"<HTML><HEAD><TITLE>%u %s</TITLE></HEAD>\n"
|
|
|
|
"<BODY><H1>%u %s</H1>\n"
|
2016-11-22 06:53:35 +05:30
|
|
|
"%s\n"
|
|
|
|
"</BODY></HTML>\n",
|
|
|
|
responseNum, responseString,
|
2006-09-26 15:37:41 +05:30
|
|
|
responseNum, responseString,
|
2016-11-22 06:53:35 +05:30
|
|
|
infoString
|
|
|
|
);
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2015-10-23 15:19:04 +05:30
|
|
|
if (DEBUG) {
|
|
|
|
iobuf[len] = '\0';
|
2007-08-14 22:25:01 +05:30
|
|
|
fprintf(stderr, "headers: '%s'\n", iobuf);
|
2015-10-23 15:19:04 +05:30
|
|
|
}
|
2008-05-19 15:18:17 +05:30
|
|
|
if (full_write(STDOUT_FILENO, iobuf, len) != len) {
|
2007-08-18 00:50:39 +05:30
|
|
|
if (verbose > 1)
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_perror_msg("error");
|
2007-08-18 00:50:39 +05:30
|
|
|
log_and_exit();
|
|
|
|
}
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
|
|
|
|
2008-07-05 14:48:54 +05:30
|
|
|
static void send_headers_and_exit(int responseNum) NORETURN;
|
2007-08-21 15:56:55 +05:30
|
|
|
static void send_headers_and_exit(int responseNum)
|
2007-08-18 00:49:15 +05:30
|
|
|
{
|
2011-03-25 18:08:52 +05:30
|
|
|
IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
|
2007-08-18 00:50:39 +05:30
|
|
|
send_headers(responseNum);
|
|
|
|
log_and_exit();
|
2007-08-18 00:49:15 +05:30
|
|
|
}
|
|
|
|
|
2007-08-14 22:20:01 +05:30
|
|
|
/*
|
2019-04-16 14:37:37 +05:30
|
|
|
* Read from the socket until '\n' or EOF.
|
|
|
|
* '\r' chars are removed.
|
2007-08-14 22:25:01 +05:30
|
|
|
* '\n' is replaced with NUL.
|
|
|
|
* Return number of characters read or 0 if nothing is read
|
|
|
|
* ('\r' and '\n' are not counted).
|
2007-08-14 22:20:01 +05:30
|
|
|
* Data is returned in iobuf.
|
|
|
|
*/
|
2019-04-16 16:48:12 +05:30
|
|
|
static unsigned get_line(void)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2019-04-16 16:48:12 +05:30
|
|
|
unsigned count;
|
2007-08-18 19:52:09 +05:30
|
|
|
char c;
|
|
|
|
|
2019-04-16 14:37:37 +05:30
|
|
|
count = 0;
|
2007-08-18 19:52:09 +05:30
|
|
|
while (1) {
|
|
|
|
if (hdr_cnt <= 0) {
|
2019-04-19 17:33:37 +05:30
|
|
|
alarm(HEADER_READ_TIMEOUT);
|
2016-04-21 19:56:30 +05:30
|
|
|
hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
|
2007-08-18 19:52:09 +05:30
|
|
|
if (hdr_cnt <= 0)
|
2019-04-16 14:37:37 +05:30
|
|
|
goto ret;
|
2007-08-18 19:52:09 +05:30
|
|
|
hdr_ptr = hdr_buf;
|
|
|
|
}
|
|
|
|
hdr_cnt--;
|
2019-04-16 14:37:37 +05:30
|
|
|
c = *hdr_ptr++;
|
2007-08-18 19:52:09 +05:30
|
|
|
if (c == '\r')
|
2007-08-14 22:20:01 +05:30
|
|
|
continue;
|
2019-04-16 14:37:37 +05:30
|
|
|
if (c == '\n')
|
2009-02-05 18:08:21 +05:30
|
|
|
break;
|
2019-04-16 14:37:37 +05:30
|
|
|
iobuf[count] = c;
|
2007-08-20 00:58:09 +05:30
|
|
|
if (count < (IOBUF_SIZE - 1)) /* check overflow */
|
2006-09-26 15:37:41 +05:30
|
|
|
count++;
|
|
|
|
}
|
2019-04-16 14:37:37 +05:30
|
|
|
ret:
|
|
|
|
iobuf[count] = '\0';
|
2007-08-14 22:25:01 +05:30
|
|
|
return count;
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
|
|
|
|
2007-10-18 18:24:39 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
|
2007-09-23 19:26:57 +05:30
|
|
|
|
|
|
|
/* gcc 4.2.1 fares better with NOINLINE */
|
2008-07-05 14:48:54 +05:30
|
|
|
static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) NORETURN;
|
2007-09-23 19:26:57 +05:30
|
|
|
static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len)
|
|
|
|
{
|
|
|
|
enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */
|
|
|
|
struct pollfd pfd[3];
|
|
|
|
int out_cnt; /* we buffer a bit of initial CGI output */
|
|
|
|
int count;
|
|
|
|
|
|
|
|
/* iobuf is used for CGI -> network data,
|
|
|
|
* hdr_buf is for network -> CGI data (POSTDATA) */
|
|
|
|
|
|
|
|
/* If CGI dies, we still want to correctly finish reading its output
|
|
|
|
* and send it to the peer. So please no SIGPIPEs! */
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
2007-10-14 08:04:20 +05:30
|
|
|
// We inconsistently handle a case when more POSTDATA from network
|
|
|
|
// is coming than we expected. We may give *some part* of that
|
|
|
|
// extra data to CGI.
|
|
|
|
|
|
|
|
//if (hdr_cnt > post_len) {
|
|
|
|
// /* We got more POSTDATA from network than we expected */
|
|
|
|
// hdr_cnt = post_len;
|
|
|
|
//}
|
|
|
|
post_len -= hdr_cnt;
|
|
|
|
/* post_len - number of POST bytes not yet read from network */
|
|
|
|
|
2007-09-23 19:26:57 +05:30
|
|
|
/* NB: breaking out of this loop jumps to log_and_exit() */
|
|
|
|
out_cnt = 0;
|
2013-09-18 15:38:41 +05:30
|
|
|
pfd[FROM_CGI].fd = fromCgi_rd;
|
|
|
|
pfd[FROM_CGI].events = POLLIN;
|
|
|
|
pfd[TO_CGI].fd = toCgi_wr;
|
2007-09-23 19:26:57 +05:30
|
|
|
while (1) {
|
2013-09-11 18:29:21 +05:30
|
|
|
/* Note: even pfd[0].events == 0 won't prevent
|
|
|
|
* revents == POLLHUP|POLLERR reports from closed stdin.
|
2013-09-18 15:38:41 +05:30
|
|
|
* Setting fd to -1 works: */
|
2013-09-11 18:29:21 +05:30
|
|
|
pfd[0].fd = -1;
|
2013-09-18 15:38:41 +05:30
|
|
|
pfd[0].events = POLLIN;
|
|
|
|
pfd[0].revents = 0; /* probably not needed, paranoia */
|
2007-09-23 19:26:57 +05:30
|
|
|
|
2013-09-18 15:38:41 +05:30
|
|
|
/* We always poll this fd, thus kernel always sets revents: */
|
|
|
|
/*pfd[FROM_CGI].events = POLLIN; - moved out of loop */
|
|
|
|
/*pfd[FROM_CGI].revents = 0; - not needed */
|
2007-09-23 19:26:57 +05:30
|
|
|
|
2013-09-18 15:38:41 +05:30
|
|
|
/* gcc-4.8.0 still doesnt fill two shorts with one insn :( */
|
|
|
|
/* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */
|
|
|
|
/* hopefully one day it will... */
|
2013-09-11 18:29:21 +05:30
|
|
|
pfd[TO_CGI].events = POLLOUT;
|
2013-09-18 15:38:41 +05:30
|
|
|
pfd[TO_CGI].revents = 0; /* needed! */
|
2013-09-11 18:29:21 +05:30
|
|
|
|
|
|
|
if (toCgi_wr && hdr_cnt <= 0) {
|
|
|
|
if (post_len > 0) {
|
|
|
|
/* Expect more POST data from network */
|
|
|
|
pfd[0].fd = 0;
|
2007-09-23 19:26:57 +05:30
|
|
|
} else {
|
|
|
|
/* post_len <= 0 && hdr_cnt <= 0:
|
|
|
|
* no more POST data to CGI,
|
|
|
|
* let CGI see EOF on CGI's stdin */
|
2010-04-02 14:10:58 +05:30
|
|
|
if (toCgi_wr != fromCgi_rd)
|
|
|
|
close(toCgi_wr);
|
2007-09-23 19:26:57 +05:30
|
|
|
toCgi_wr = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now wait on the set of sockets */
|
2013-09-11 18:29:21 +05:30
|
|
|
count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1);
|
2007-09-23 19:26:57 +05:30
|
|
|
if (count <= 0) {
|
|
|
|
#if 0
|
2008-01-03 01:25:04 +05:30
|
|
|
if (safe_waitpid(pid, &status, WNOHANG) <= 0) {
|
2007-09-23 19:26:57 +05:30
|
|
|
/* Weird. CGI didn't exit and no fd's
|
|
|
|
* are ready, yet poll returned?! */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (DEBUG && WIFEXITED(status))
|
2018-04-07 04:43:30 +05:30
|
|
|
bb_error_msg("CGI exited, status=%u", WEXITSTATUS(status));
|
2007-09-23 19:26:57 +05:30
|
|
|
if (DEBUG && WIFSIGNALED(status))
|
2018-04-07 04:43:30 +05:30
|
|
|
bb_error_msg("CGI killed, signal=%u", WTERMSIG(status));
|
2007-09-23 19:26:57 +05:30
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-03-02 19:32:45 +05:30
|
|
|
if (pfd[TO_CGI].revents) {
|
2013-09-11 18:29:21 +05:30
|
|
|
/* hdr_cnt > 0 here due to the way poll() called */
|
2007-09-23 19:26:57 +05:30
|
|
|
/* Have data from peer and can write to CGI */
|
|
|
|
count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt);
|
|
|
|
/* Doesn't happen, we dont use nonblocking IO here
|
|
|
|
*if (count < 0 && errno == EAGAIN) {
|
|
|
|
* ...
|
|
|
|
*} else */
|
|
|
|
if (count > 0) {
|
|
|
|
hdr_ptr += count;
|
|
|
|
hdr_cnt -= count;
|
|
|
|
} else {
|
|
|
|
/* EOF/broken pipe to CGI, stop piping POST data */
|
|
|
|
hdr_cnt = post_len = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-02 19:32:45 +05:30
|
|
|
if (pfd[0].revents) {
|
2007-09-23 19:26:57 +05:30
|
|
|
/* post_len > 0 && hdr_cnt == 0 here */
|
|
|
|
/* We expect data, prev data portion is eaten by CGI
|
|
|
|
* and there *is* data to read from the peer
|
|
|
|
* (POSTDATA) */
|
2016-04-21 19:56:30 +05:30
|
|
|
//count = post_len > (int)sizeof_hdr_buf ? (int)sizeof_hdr_buf : post_len;
|
2008-05-19 15:18:17 +05:30
|
|
|
//count = safe_read(STDIN_FILENO, hdr_buf, count);
|
2016-04-21 19:56:30 +05:30
|
|
|
count = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
|
2007-09-23 19:26:57 +05:30
|
|
|
if (count > 0) {
|
|
|
|
hdr_cnt = count;
|
|
|
|
hdr_ptr = hdr_buf;
|
|
|
|
post_len -= count;
|
|
|
|
} else {
|
|
|
|
/* no more POST data can be read */
|
|
|
|
post_len = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-02 19:32:45 +05:30
|
|
|
if (pfd[FROM_CGI].revents) {
|
2007-09-23 19:26:57 +05:30
|
|
|
/* There is something to read from CGI */
|
|
|
|
char *rbuf = iobuf;
|
|
|
|
|
|
|
|
/* Are we still buffering CGI output? */
|
|
|
|
if (out_cnt >= 0) {
|
|
|
|
/* HTTP_200[] has single "\r\n" at the end.
|
|
|
|
* According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
|
|
|
|
* CGI scripts MUST send their own header terminated by
|
|
|
|
* empty line, then data. That's why we have only one
|
|
|
|
* <cr><lf> pair here. We will output "200 OK" line
|
|
|
|
* if needed, but CGI still has to provide blank line
|
|
|
|
* between header and body */
|
|
|
|
|
|
|
|
/* Must use safe_read, not full_read, because
|
|
|
|
* CGI may output a few first bytes and then wait
|
|
|
|
* for POSTDATA without closing stdout.
|
|
|
|
* With full_read we may wait here forever. */
|
2019-04-19 17:49:41 +05:30
|
|
|
count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE - 8);
|
2007-09-23 19:26:57 +05:30
|
|
|
if (count <= 0) {
|
|
|
|
/* eof (or error) and there was no "HTTP",
|
2020-08-09 03:53:31 +05:30
|
|
|
* send "HTTP/1.1 200 OK\r\n", then send received data */
|
2007-09-23 19:26:57 +05:30
|
|
|
if (out_cnt) {
|
2008-05-19 15:18:17 +05:30
|
|
|
full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1);
|
|
|
|
full_write(STDOUT_FILENO, rbuf, out_cnt);
|
2007-09-23 19:26:57 +05:30
|
|
|
}
|
|
|
|
break; /* CGI stdout is closed, exiting */
|
|
|
|
}
|
|
|
|
out_cnt += count;
|
|
|
|
count = 0;
|
|
|
|
/* "Status" header format is: "Status: 302 Redirected\r\n" */
|
2017-09-01 20:36:12 +05:30
|
|
|
if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
|
2020-08-09 03:53:31 +05:30
|
|
|
/* send "HTTP/1.1 " */
|
2008-05-19 15:18:17 +05:30
|
|
|
if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
|
2007-09-23 19:26:57 +05:30
|
|
|
break;
|
2020-08-09 03:53:31 +05:30
|
|
|
/* skip "Status: " (including space, sending "HTTP/1.1 NNN" is wrong) */
|
2017-09-01 20:36:12 +05:30
|
|
|
rbuf += 8;
|
|
|
|
count = out_cnt - 8;
|
2007-09-23 19:26:57 +05:30
|
|
|
out_cnt = -1; /* buffering off */
|
|
|
|
} else if (out_cnt >= 4) {
|
|
|
|
/* Did CGI add "HTTP"? */
|
|
|
|
if (memcmp(rbuf, HTTP_200, 4) != 0) {
|
|
|
|
/* there is no "HTTP", do it ourself */
|
2008-05-19 15:18:17 +05:30
|
|
|
if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
|
2007-09-23 19:26:57 +05:30
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Commented out:
|
|
|
|
if (!strstr(rbuf, "ontent-")) {
|
|
|
|
full_write(s, "Content-type: text/plain\r\n\r\n", 28);
|
|
|
|
}
|
|
|
|
* Counter-example of valid CGI without Content-type:
|
2020-08-09 03:53:31 +05:30
|
|
|
* echo -en "HTTP/1.1 302 Found\r\n"
|
2007-09-23 19:26:57 +05:30
|
|
|
* echo -en "Location: http://www.busybox.net\r\n"
|
|
|
|
* echo -en "\r\n"
|
|
|
|
*/
|
|
|
|
count = out_cnt;
|
|
|
|
out_cnt = -1; /* buffering off */
|
|
|
|
}
|
|
|
|
} else {
|
2019-04-19 17:49:41 +05:30
|
|
|
count = safe_read(fromCgi_rd, rbuf, IOBUF_SIZE);
|
2007-09-23 19:26:57 +05:30
|
|
|
if (count <= 0)
|
|
|
|
break; /* eof (or error) */
|
|
|
|
}
|
2008-05-19 15:18:17 +05:30
|
|
|
if (full_write(STDOUT_FILENO, rbuf, count) != count)
|
2007-09-23 19:26:57 +05:30
|
|
|
break;
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("cgi read %d bytes: '%.*s'\n", count, count, rbuf);
|
2007-09-23 19:26:57 +05:30
|
|
|
} /* if (pfd[FROM_CGI].revents) */
|
|
|
|
} /* while (1) */
|
|
|
|
log_and_exit();
|
|
|
|
}
|
2007-10-18 18:24:39 +05:30
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
2007-09-23 19:26:57 +05:30
|
|
|
|
2007-08-20 00:58:09 +05:30
|
|
|
static void setenv1(const char *name, const char *value)
|
|
|
|
{
|
|
|
|
setenv(name, value ? value : "", 1);
|
|
|
|
}
|
|
|
|
|
2007-08-18 00:48:47 +05:30
|
|
|
/*
|
|
|
|
* Spawn CGI script, forward CGI's stdin/out <=> network
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2007-08-18 00:48:47 +05:30
|
|
|
* Environment variables are set up and the script is invoked with pipes
|
2007-09-23 19:26:57 +05:30
|
|
|
* for stdin/stdout. If a POST is being done the script is fed the POST
|
2007-08-18 00:48:47 +05:30
|
|
|
* data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2007-08-18 00:48:47 +05:30
|
|
|
* Parameters:
|
|
|
|
* const char *url The requested URL (with leading /).
|
2011-12-19 17:00:34 +05:30
|
|
|
* const char *orig_uri The original URI before rewriting (if any)
|
2007-09-23 19:26:57 +05:30
|
|
|
* int post_len Length of the POST body.
|
2007-08-18 00:48:47 +05:30
|
|
|
*/
|
|
|
|
static void send_cgi_and_exit(
|
|
|
|
const char *url,
|
2011-12-19 17:00:34 +05:30
|
|
|
const char *orig_uri,
|
2007-08-18 00:48:47 +05:30
|
|
|
const char *request,
|
2019-04-16 15:07:02 +05:30
|
|
|
int post_len) NORETURN;
|
2007-08-18 00:48:47 +05:30
|
|
|
static void send_cgi_and_exit(
|
|
|
|
const char *url,
|
2011-12-19 17:00:34 +05:30
|
|
|
const char *orig_uri,
|
2007-08-18 00:48:47 +05:30
|
|
|
const char *request,
|
2019-04-16 15:07:02 +05:30
|
|
|
int post_len)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2008-02-16 18:50:56 +05:30
|
|
|
struct fd_pair fromCgi; /* CGI -> httpd pipe */
|
|
|
|
struct fd_pair toCgi; /* httpd -> CGI pipe */
|
2011-12-18 07:52:36 +05:30
|
|
|
char *script, *last_slash;
|
2007-09-23 19:26:57 +05:30
|
|
|
int pid;
|
2007-08-18 00:51:12 +05:30
|
|
|
|
2008-05-07 17:48:48 +05:30
|
|
|
/* Make a copy. NB: caller guarantees:
|
|
|
|
* url[0] == '/', url[1] != '/' */
|
|
|
|
url = xstrdup(url);
|
|
|
|
|
2007-08-18 00:51:12 +05:30
|
|
|
/*
|
|
|
|
* We are mucking with environment _first_ and then vfork/exec,
|
2008-05-07 17:48:48 +05:30
|
|
|
* this allows us to use vfork safely. Parent doesn't care about
|
2007-08-18 00:51:12 +05:30
|
|
|
* these environment changes anyway.
|
|
|
|
*/
|
|
|
|
|
2008-05-07 17:48:48 +05:30
|
|
|
/* Check for [dirs/]script.cgi/PATH_INFO */
|
2011-12-18 07:52:36 +05:30
|
|
|
last_slash = script = (char*)url;
|
2007-08-18 00:51:12 +05:30
|
|
|
while ((script = strchr(script + 1, '/')) != NULL) {
|
2011-12-18 07:52:36 +05:30
|
|
|
int dir;
|
2007-08-18 00:51:12 +05:30
|
|
|
*script = '\0';
|
2011-12-18 07:57:46 +05:30
|
|
|
dir = is_directory(url + 1, /*followlinks:*/ 1);
|
2011-12-18 07:52:36 +05:30
|
|
|
*script = '/';
|
|
|
|
if (!dir) {
|
2007-08-18 00:51:12 +05:30
|
|
|
/* not directory, found script.cgi/PATH_INFO */
|
|
|
|
break;
|
|
|
|
}
|
2011-12-18 07:52:36 +05:30
|
|
|
/* is directory, find next '/' */
|
|
|
|
last_slash = script;
|
2007-08-18 00:51:12 +05:30
|
|
|
}
|
2008-05-07 17:48:48 +05:30
|
|
|
setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */
|
2007-08-18 00:51:12 +05:30
|
|
|
setenv1("REQUEST_METHOD", request);
|
|
|
|
if (g_query) {
|
2011-12-19 17:00:34 +05:30
|
|
|
putenv(xasprintf("%s=%s?%s", "REQUEST_URI", orig_uri, g_query));
|
2007-08-18 00:51:12 +05:30
|
|
|
} else {
|
2011-12-19 17:00:34 +05:30
|
|
|
setenv1("REQUEST_URI", orig_uri);
|
2007-08-18 00:51:12 +05:30
|
|
|
}
|
|
|
|
if (script != NULL)
|
|
|
|
*script = '\0'; /* cut off /PATH_INFO */
|
|
|
|
|
2008-05-07 17:48:48 +05:30
|
|
|
/* SCRIPT_FILENAME is required by PHP in CGI mode */
|
|
|
|
if (home_httpd[0] == '/') {
|
|
|
|
char *fullpath = concat_path_file(home_httpd, url);
|
|
|
|
setenv1("SCRIPT_FILENAME", fullpath);
|
|
|
|
}
|
2007-08-18 00:51:12 +05:30
|
|
|
/* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
|
2008-05-07 17:48:48 +05:30
|
|
|
setenv1("SCRIPT_NAME", url);
|
2007-08-18 00:51:12 +05:30
|
|
|
/* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
|
|
|
|
* QUERY_STRING: The information which follows the ? in the URL
|
|
|
|
* which referenced this script. This is the query information.
|
|
|
|
* It should not be decoded in any fashion. This variable
|
|
|
|
* should always be set when there is query information,
|
|
|
|
* regardless of command line decoding. */
|
|
|
|
/* (Older versions of bbox seem to do some decoding) */
|
|
|
|
setenv1("QUERY_STRING", g_query);
|
|
|
|
putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
|
2020-08-09 03:53:31 +05:30
|
|
|
putenv((char*)"SERVER_PROTOCOL=HTTP/1.1");
|
2007-08-18 00:51:12 +05:30
|
|
|
putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
|
|
|
|
/* Having _separate_ variables for IP and port defeats
|
|
|
|
* the purpose of having socket abstraction. Which "port"
|
|
|
|
* are you using on Unix domain socket?
|
|
|
|
* IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
|
|
|
|
* Oh well... */
|
|
|
|
{
|
|
|
|
char *p = rmt_ip_str ? rmt_ip_str : (char*)"";
|
|
|
|
char *cp = strrchr(p, ':');
|
|
|
|
if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
|
|
|
|
cp = NULL;
|
|
|
|
if (cp) *cp = '\0'; /* delete :PORT */
|
|
|
|
setenv1("REMOTE_ADDR", p);
|
2007-08-20 00:24:22 +05:30
|
|
|
if (cp) {
|
|
|
|
*cp = ':';
|
2007-08-18 00:51:12 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
|
2007-08-20 00:24:22 +05:30
|
|
|
setenv1("REMOTE_PORT", cp + 1);
|
2007-08-18 00:51:12 +05:30
|
|
|
#endif
|
2007-08-20 00:24:22 +05:30
|
|
|
}
|
|
|
|
}
|
2007-09-23 19:26:57 +05:30
|
|
|
if (post_len)
|
2018-04-07 04:43:30 +05:30
|
|
|
putenv(xasprintf("CONTENT_LENGTH=%u", post_len));
|
2007-08-18 00:51:12 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
|
|
|
if (remoteuser) {
|
|
|
|
setenv1("REMOTE_USER", remoteuser);
|
|
|
|
putenv((char*)"AUTH_TYPE=Basic");
|
|
|
|
}
|
|
|
|
#endif
|
2009-03-19 01:30:46 +05:30
|
|
|
/* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
|
|
|
|
* just run "env SERVER_NAME=xyz httpd ..." instead */
|
2006-09-26 15:37:41 +05:30
|
|
|
|
2008-02-16 18:50:56 +05:30
|
|
|
xpiped_pair(fromCgi);
|
|
|
|
xpiped_pair(toCgi);
|
2006-11-21 05:37:31 +05:30
|
|
|
|
2007-03-08 03:46:38 +05:30
|
|
|
pid = vfork();
|
2007-08-18 00:50:39 +05:30
|
|
|
if (pid < 0) {
|
|
|
|
/* TODO: log perror? */
|
|
|
|
log_and_exit();
|
|
|
|
}
|
2007-01-11 22:50:00 +05:30
|
|
|
|
2011-12-18 07:52:36 +05:30
|
|
|
if (pid == 0) {
|
2007-08-18 00:51:12 +05:30
|
|
|
/* Child process */
|
2008-05-07 17:48:48 +05:30
|
|
|
char *argv[3];
|
|
|
|
|
2007-08-18 00:48:47 +05:30
|
|
|
xfunc_error_retval = 242;
|
|
|
|
|
2008-02-16 18:48:17 +05:30
|
|
|
/* NB: close _first_, then move fds! */
|
|
|
|
close(toCgi.wr);
|
|
|
|
close(fromCgi.rd);
|
2007-08-12 01:50:02 +05:30
|
|
|
xmove_fd(toCgi.rd, 0); /* replace stdin with the pipe */
|
|
|
|
xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */
|
2007-08-18 00:51:12 +05:30
|
|
|
/* User seeing stderr output can be a security problem.
|
2007-08-18 00:48:47 +05:30
|
|
|
* If CGI really wants that, it can always do dup itself. */
|
2007-08-12 01:50:43 +05:30
|
|
|
/* dup2(1, 2); */
|
2006-11-21 05:37:31 +05:30
|
|
|
|
2008-05-07 17:48:48 +05:30
|
|
|
/* Chdiring to script's dir */
|
2011-12-18 07:52:36 +05:30
|
|
|
script = last_slash;
|
2008-05-07 17:48:48 +05:30
|
|
|
if (script != url) { /* paranoia */
|
|
|
|
*script = '\0';
|
|
|
|
if (chdir(url + 1) != 0) {
|
2012-06-12 16:51:02 +05:30
|
|
|
bb_perror_msg("can't change directory to '%s'", url + 1);
|
2008-05-07 17:48:48 +05:30
|
|
|
goto error_execing_cgi;
|
|
|
|
}
|
|
|
|
// not needed: *script = '/';
|
|
|
|
}
|
|
|
|
script++;
|
2008-02-11 21:56:22 +05:30
|
|
|
|
2008-05-07 17:48:48 +05:30
|
|
|
/* set argv[0] to name without path */
|
|
|
|
argv[0] = script;
|
|
|
|
argv[1] = NULL;
|
2008-02-11 21:56:22 +05:30
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
|
2008-05-07 17:48:48 +05:30
|
|
|
{
|
|
|
|
char *suffix = strrchr(script, '.');
|
|
|
|
|
|
|
|
if (suffix) {
|
|
|
|
Htaccess *cur;
|
|
|
|
for (cur = script_i; cur; cur = cur->next) {
|
|
|
|
if (strcmp(cur->before_colon + 1, suffix) == 0) {
|
|
|
|
/* found interpreter name */
|
|
|
|
argv[0] = cur->after_colon;
|
|
|
|
argv[1] = script;
|
|
|
|
argv[2] = NULL;
|
|
|
|
break;
|
2006-11-21 05:36:28 +05:30
|
|
|
}
|
|
|
|
}
|
2006-11-21 05:37:31 +05:30
|
|
|
}
|
|
|
|
}
|
2008-05-07 17:48:48 +05:30
|
|
|
#endif
|
|
|
|
/* restore default signal dispositions for CGI process */
|
|
|
|
bb_signals(0
|
|
|
|
| (1 << SIGCHLD)
|
|
|
|
| (1 << SIGPIPE)
|
|
|
|
| (1 << SIGHUP)
|
|
|
|
, SIG_DFL);
|
|
|
|
|
|
|
|
/* _NOT_ execvp. We do not search PATH. argv[0] is a filename
|
|
|
|
* without any dir components and will only match a file
|
|
|
|
* in the current directory */
|
|
|
|
execv(argv[0], argv);
|
|
|
|
if (verbose)
|
2010-06-27 20:27:55 +05:30
|
|
|
bb_perror_msg("can't execute '%s'", argv[0]);
|
2008-05-07 17:48:48 +05:30
|
|
|
error_execing_cgi:
|
2007-08-18 00:49:15 +05:30
|
|
|
/* send to stdout
|
|
|
|
* (we are CGI here, our stdout is pumped to the net) */
|
2007-08-18 00:50:39 +05:30
|
|
|
send_headers_and_exit(HTTP_NOT_FOUND);
|
2006-11-21 05:37:31 +05:30
|
|
|
} /* end child */
|
|
|
|
|
2007-08-18 00:51:12 +05:30
|
|
|
/* Parent process */
|
|
|
|
|
2007-09-23 19:26:57 +05:30
|
|
|
/* Restore variables possibly changed by child */
|
2007-08-18 00:51:12 +05:30
|
|
|
xfunc_error_retval = 0;
|
|
|
|
|
2007-09-23 19:26:57 +05:30
|
|
|
/* Pump data */
|
2007-08-12 01:50:02 +05:30
|
|
|
close(fromCgi.wr);
|
|
|
|
close(toCgi.rd);
|
2007-09-23 19:26:57 +05:30
|
|
|
cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len);
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
2007-09-23 19:26:57 +05:30
|
|
|
|
2006-11-16 23:34:43 +05:30
|
|
|
#endif /* FEATURE_HTTPD_CGI */
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2007-08-18 00:48:47 +05:30
|
|
|
/*
|
|
|
|
* Send a file response to a HTTP request, and exit
|
2007-10-14 10:25:59 +05:30
|
|
|
*
|
2007-08-21 15:56:55 +05:30
|
|
|
* Parameters:
|
2008-02-21 05:42:07 +05:30
|
|
|
* const char *url The requested URL (with leading /).
|
|
|
|
* what What to send (headers/body/both).
|
2007-08-18 00:48:47 +05:30
|
|
|
*/
|
2009-02-05 05:13:44 +05:30
|
|
|
static NOINLINE void send_file_and_exit(const char *url, int what)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2007-08-13 02:35:49 +05:30
|
|
|
char *suffix;
|
2009-02-05 05:13:44 +05:30
|
|
|
int fd;
|
2007-08-13 02:35:49 +05:30
|
|
|
ssize_t count;
|
2009-02-05 05:13:44 +05:30
|
|
|
|
2010-07-25 06:50:53 +05:30
|
|
|
if (content_gzip) {
|
|
|
|
/* does <url>.gz exist? Then use it instead */
|
|
|
|
char *gzurl = xasprintf("%s.gz", url);
|
|
|
|
fd = open(gzurl, O_RDONLY);
|
|
|
|
free(gzurl);
|
|
|
|
if (fd != -1) {
|
|
|
|
struct stat sb;
|
|
|
|
fstat(fd, &sb);
|
|
|
|
file_size = sb.st_size;
|
2011-01-11 22:29:45 +05:30
|
|
|
last_mod = sb.st_mtime;
|
2010-07-25 06:50:53 +05:30
|
|
|
} else {
|
|
|
|
IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
|
|
|
|
fd = open(url, O_RDONLY);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fd = open(url, O_RDONLY);
|
2020-08-16 03:22:48 +05:30
|
|
|
/* file_size and last_mod are already populated */
|
2010-07-25 06:50:53 +05:30
|
|
|
}
|
2009-02-05 05:13:44 +05:30
|
|
|
if (fd < 0) {
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("can't open '%s'\n", url);
|
2009-02-05 05:13:44 +05:30
|
|
|
/* Error pages are sent by using send_file_and_exit(SEND_BODY).
|
|
|
|
* IOW: it is unsafe to call send_headers_and_exit
|
|
|
|
* if what is SEND_BODY! Can recurse! */
|
|
|
|
if (what != SEND_BODY)
|
|
|
|
send_headers_and_exit(HTTP_NOT_FOUND);
|
|
|
|
log_and_exit();
|
|
|
|
}
|
2020-08-16 03:22:48 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
/* ETag is "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417" */
|
|
|
|
sprintf(G.etag, "\"%llx-%llx\"", (unsigned long long)last_mod, (unsigned long long)file_size);
|
|
|
|
|
|
|
|
if (G.if_none_match) {
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("If-None-Match:'%s' file's ETag:'%s'\n", G.if_none_match, G.etag);
|
2020-08-16 03:22:48 +05:30
|
|
|
/* Weak ETag comparision.
|
|
|
|
* If-None-Match may have many ETags but they are quoted so we can use simple substring search */
|
|
|
|
if (strstr(G.if_none_match, G.etag))
|
|
|
|
send_headers_and_exit(HTTP_NOT_MODIFIED);
|
|
|
|
}
|
|
|
|
#endif
|
2008-02-21 05:42:07 +05:30
|
|
|
/* If you want to know about EPIPE below
|
|
|
|
* (happens if you abort downloads from local httpd): */
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
2018-04-07 04:43:30 +05:30
|
|
|
/* If not found, default is to not send "Content-type:" */
|
|
|
|
/*found_mime_type = NULL; - already is */
|
2009-09-10 05:16:02 +05:30
|
|
|
suffix = strrchr(url, '.');
|
2007-08-18 00:50:07 +05:30
|
|
|
if (suffix) {
|
2009-09-10 05:16:02 +05:30
|
|
|
static const char suffixTable[] ALIGN1 =
|
|
|
|
/* Shorter suffix must be first:
|
|
|
|
* ".html.htm" will fail for ".htm"
|
|
|
|
*/
|
|
|
|
".txt.h.c.cc.cpp\0" "text/plain\0"
|
|
|
|
/* .htm line must be after .h line */
|
|
|
|
".htm.html\0" "text/html\0"
|
|
|
|
".jpg.jpeg\0" "image/jpeg\0"
|
|
|
|
".gif\0" "image/gif\0"
|
|
|
|
".png\0" "image/png\0"
|
2019-06-06 20:42:55 +05:30
|
|
|
".svg\0" "image/svg+xml\0"
|
2009-09-10 05:16:02 +05:30
|
|
|
/* .css line must be after .c line */
|
|
|
|
".css\0" "text/css\0"
|
2019-06-07 16:02:30 +05:30
|
|
|
".js\0" "application/javascript\0"
|
2009-09-10 05:16:02 +05:30
|
|
|
".wav\0" "audio/wav\0"
|
|
|
|
".avi\0" "video/x-msvideo\0"
|
|
|
|
".qt.mov\0" "video/quicktime\0"
|
|
|
|
".mpe.mpeg\0" "video/mpeg\0"
|
|
|
|
".mid.midi\0" "audio/midi\0"
|
|
|
|
".mp3\0" "audio/mpeg\0"
|
|
|
|
#if 0 /* unpopular */
|
|
|
|
".au\0" "audio/basic\0"
|
|
|
|
".pac\0" "application/x-ns-proxy-autoconfig\0"
|
|
|
|
".vrml.wrl\0" "model/vrml\0"
|
|
|
|
#endif
|
|
|
|
/* compiler adds another "\0" here */
|
|
|
|
;
|
2007-08-18 00:50:07 +05:30
|
|
|
Htaccess *cur;
|
2009-09-10 05:16:02 +05:30
|
|
|
|
|
|
|
/* Examine built-in table */
|
|
|
|
const char *table = suffixTable;
|
|
|
|
const char *table_next;
|
|
|
|
for (; *table; table = table_next) {
|
|
|
|
const char *try_suffix;
|
|
|
|
const char *mime_type;
|
|
|
|
mime_type = table + strlen(table) + 1;
|
|
|
|
table_next = mime_type + strlen(mime_type) + 1;
|
|
|
|
try_suffix = strstr(table, suffix);
|
|
|
|
if (!try_suffix)
|
|
|
|
continue;
|
|
|
|
try_suffix += strlen(suffix);
|
|
|
|
if (*try_suffix == '\0' || *try_suffix == '.') {
|
|
|
|
found_mime_type = mime_type;
|
|
|
|
break;
|
2007-08-18 00:50:07 +05:30
|
|
|
}
|
2009-09-10 05:16:02 +05:30
|
|
|
/* Example: strstr(table, ".av") != NULL, but it
|
|
|
|
* does not match ".avi" after all and we end up here.
|
|
|
|
* The table is arranged so that in this case we know
|
|
|
|
* that it can't match anything in the following lines,
|
|
|
|
* and we stop the search: */
|
|
|
|
break;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2009-09-10 05:16:02 +05:30
|
|
|
/* ...then user's table */
|
2007-06-10 05:19:05 +05:30
|
|
|
for (cur = mime_a; cur; cur = cur->next) {
|
2006-09-26 15:37:41 +05:30
|
|
|
if (strcmp(cur->before_colon, suffix) == 0) {
|
2007-06-10 05:19:05 +05:30
|
|
|
found_mime_type = cur->after_colon;
|
2006-09-26 15:37:41 +05:30
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-03 22:23:22 +05:30
|
|
|
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("sending file '%s' content-type:%s\n", url, found_mime_type);
|
2009-05-03 22:23:22 +05:30
|
|
|
|
2007-09-22 04:05:18 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
2010-07-25 06:50:53 +05:30
|
|
|
if (what == SEND_BODY /* err pages and ranges don't mix */
|
|
|
|
|| content_gzip /* we are sending compressed page: can't do ranges */ ///why?
|
|
|
|
) {
|
2012-02-19 21:48:45 +05:30
|
|
|
range_start = -1;
|
2010-07-25 06:50:53 +05:30
|
|
|
}
|
2007-09-22 04:05:18 +05:30
|
|
|
range_len = MAXINT(off_t);
|
2012-02-19 21:48:45 +05:30
|
|
|
if (range_start >= 0) {
|
2012-04-03 11:39:28 +05:30
|
|
|
if (!range_end || range_end > file_size - 1) {
|
2007-09-22 04:05:18 +05:30
|
|
|
range_end = file_size - 1;
|
|
|
|
}
|
|
|
|
if (range_end < range_start
|
2009-02-05 05:13:44 +05:30
|
|
|
|| lseek(fd, range_start, SEEK_SET) != range_start
|
2007-09-22 04:05:18 +05:30
|
|
|
) {
|
2009-02-05 05:13:44 +05:30
|
|
|
lseek(fd, 0, SEEK_SET);
|
2012-02-19 21:48:45 +05:30
|
|
|
range_start = -1;
|
2007-09-22 04:05:18 +05:30
|
|
|
} else {
|
|
|
|
range_len = range_end - range_start + 1;
|
|
|
|
send_headers(HTTP_PARTIAL_CONTENT);
|
2008-02-21 05:42:07 +05:30
|
|
|
what = SEND_BODY;
|
2007-09-22 04:05:18 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2008-02-21 05:42:07 +05:30
|
|
|
if (what & SEND_HEADERS)
|
2007-08-21 15:56:55 +05:30
|
|
|
send_headers(HTTP_OK);
|
2014-11-27 17:50:24 +05:30
|
|
|
#if ENABLE_FEATURE_USE_SENDFILE
|
2009-02-05 05:13:44 +05:30
|
|
|
{
|
2020-12-29 20:59:05 +05:30
|
|
|
off_t offset = (range_start < 0) ? 0 : range_start;
|
2009-02-05 05:13:44 +05:30
|
|
|
while (1) {
|
|
|
|
/* sz is rounded down to 64k */
|
|
|
|
ssize_t sz = MAXINT(ssize_t) - 0xffff;
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;)
|
2009-02-05 05:13:44 +05:30
|
|
|
count = sendfile(STDOUT_FILENO, fd, &offset, sz);
|
|
|
|
if (count < 0) {
|
|
|
|
if (offset == range_start)
|
|
|
|
break; /* fall back to read/write loop */
|
|
|
|
goto fin;
|
|
|
|
}
|
2012-02-05 02:07:17 +05:30
|
|
|
IF_FEATURE_HTTPD_RANGES(range_len -= count;)
|
2009-02-05 05:13:44 +05:30
|
|
|
if (count == 0 || range_len == 0)
|
|
|
|
log_and_exit();
|
2007-08-13 02:35:49 +05:30
|
|
|
}
|
2009-02-05 05:13:44 +05:30
|
|
|
}
|
2007-08-13 02:35:49 +05:30
|
|
|
#endif
|
2009-02-05 05:13:44 +05:30
|
|
|
while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) {
|
2007-09-22 04:05:18 +05:30
|
|
|
ssize_t n;
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;)
|
2008-05-19 15:18:17 +05:30
|
|
|
n = full_write(STDOUT_FILENO, iobuf, count);
|
2007-08-18 00:48:47 +05:30
|
|
|
if (count != n)
|
2007-08-13 02:35:49 +05:30
|
|
|
break;
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_RANGES(range_len -= count;)
|
2009-02-05 05:13:44 +05:30
|
|
|
if (range_len == 0)
|
2007-09-22 04:05:18 +05:30
|
|
|
break;
|
2007-08-13 02:35:49 +05:30
|
|
|
}
|
2009-02-05 05:13:44 +05:30
|
|
|
if (count < 0) {
|
2014-11-27 17:50:24 +05:30
|
|
|
IF_FEATURE_USE_SENDFILE(fin:)
|
2009-02-05 05:13:44 +05:30
|
|
|
if (verbose > 1)
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_perror_msg("error");
|
2009-02-05 05:13:44 +05:30
|
|
|
}
|
2007-08-18 00:50:39 +05:30
|
|
|
log_and_exit();
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
|
|
|
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
2019-04-16 16:48:12 +05:30
|
|
|
static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
|
2003-09-08 16:29:27 +05:30
|
|
|
{
|
2007-08-18 00:50:39 +05:30
|
|
|
Htaccess_IP *cur;
|
2003-09-08 16:29:27 +05:30
|
|
|
|
2020-08-16 18:28:31 +05:30
|
|
|
for (cur = G.ip_a_d; cur; cur = cur->next) {
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
|
2007-08-14 22:25:01 +05:30
|
|
|
rmt_ip_str,
|
2007-01-12 16:05:23 +05:30
|
|
|
(unsigned char)(cur->ip >> 24),
|
|
|
|
(unsigned char)(cur->ip >> 16),
|
|
|
|
(unsigned char)(cur->ip >> 8),
|
|
|
|
(unsigned char)(cur->ip),
|
|
|
|
(unsigned char)(cur->mask >> 24),
|
|
|
|
(unsigned char)(cur->mask >> 16),
|
|
|
|
(unsigned char)(cur->mask >> 8),
|
|
|
|
(unsigned char)(cur->mask)
|
|
|
|
);
|
2019-04-16 16:48:12 +05:30
|
|
|
if ((remote_ip & cur->mask) == cur->ip) {
|
2019-04-14 23:27:13 +05:30
|
|
|
if (cur->allow_deny == 'A')
|
|
|
|
return;
|
|
|
|
send_headers_and_exit(HTTP_FORBIDDEN);
|
2019-04-16 13:44:50 +05:30
|
|
|
}
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
|
|
|
|
2019-04-14 23:27:13 +05:30
|
|
|
if (flg_deny_all) /* depends on whether we saw "D:*" */
|
|
|
|
send_headers_and_exit(HTTP_FORBIDDEN);
|
2003-09-08 16:29:27 +05:30
|
|
|
}
|
2020-08-16 18:28:31 +05:30
|
|
|
#else
|
|
|
|
# define if_ip_denied_send_HTTP_FORBIDDEN_and_exit(arg) ((void)0)
|
|
|
|
#endif
|
2003-09-08 16:29:27 +05:30
|
|
|
|
2007-08-18 19:52:09 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2011-11-29 18:21:11 +05:30
|
|
|
|
2012-04-20 18:18:00 +05:30
|
|
|
# if ENABLE_PAM
|
2011-11-29 18:21:11 +05:30
|
|
|
struct pam_userinfo {
|
|
|
|
const char *name;
|
|
|
|
const char *pw;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int pam_talker(int num_msg,
|
|
|
|
const struct pam_message **msg,
|
|
|
|
struct pam_response **resp,
|
|
|
|
void *appdata_ptr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr;
|
|
|
|
struct pam_response *response;
|
|
|
|
|
|
|
|
if (!resp || !msg || !userinfo)
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
|
|
|
|
/* allocate memory to store response */
|
|
|
|
response = xzalloc(num_msg * sizeof(*response));
|
|
|
|
|
|
|
|
/* copy values */
|
|
|
|
for (i = 0; i < num_msg; i++) {
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
switch (msg[i]->msg_style) {
|
|
|
|
case PAM_PROMPT_ECHO_ON:
|
|
|
|
s = userinfo->name;
|
|
|
|
break;
|
|
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
|
|
s = userinfo->pw;
|
|
|
|
break;
|
2013-01-14 06:04:48 +05:30
|
|
|
case PAM_ERROR_MSG:
|
2013-07-30 15:22:58 +05:30
|
|
|
case PAM_TEXT_INFO:
|
|
|
|
s = "";
|
2011-11-29 18:21:11 +05:30
|
|
|
break;
|
|
|
|
default:
|
|
|
|
free(response);
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
}
|
|
|
|
response[i].resp = xstrdup(s);
|
|
|
|
if (PAM_SUCCESS != 0)
|
|
|
|
response[i].resp_retcode = PAM_SUCCESS;
|
|
|
|
}
|
|
|
|
*resp = response;
|
|
|
|
return PAM_SUCCESS;
|
|
|
|
}
|
|
|
|
# endif
|
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
/*
|
2008-06-13 15:23:06 +05:30
|
|
|
* Config file entries are of the form "/<path>:<user>:<passwd>".
|
|
|
|
* If config file has no prefix match for path, access is allowed.
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2008-06-13 15:23:06 +05:30
|
|
|
* path The file path
|
|
|
|
* user_and_passwd "user:passwd" to validate
|
2003-01-05 09:31:56 +05:30
|
|
|
*
|
2008-06-13 15:23:06 +05:30
|
|
|
* Returns 1 if user_and_passwd is OK.
|
2007-08-18 00:50:07 +05:30
|
|
|
*/
|
2011-11-29 18:21:11 +05:30
|
|
|
static int check_user_passwd(const char *path, char *user_and_passwd)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2007-08-18 00:50:07 +05:30
|
|
|
Htaccess *cur;
|
2006-09-26 15:37:41 +05:30
|
|
|
const char *prev = NULL;
|
|
|
|
|
2007-06-10 05:19:05 +05:30
|
|
|
for (cur = g_auth; cur; cur = cur->next) {
|
2008-06-13 15:25:13 +05:30
|
|
|
const char *dir_prefix;
|
|
|
|
size_t len;
|
2011-11-29 18:21:11 +05:30
|
|
|
int r;
|
2008-06-13 15:25:13 +05:30
|
|
|
|
|
|
|
dir_prefix = cur->before_colon;
|
|
|
|
|
|
|
|
/* WHY? */
|
|
|
|
/* If already saw a match, don't accept other different matches */
|
|
|
|
if (prev && strcmp(prev, dir_prefix) != 0)
|
|
|
|
continue;
|
2006-11-16 23:34:43 +05:30
|
|
|
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
|
2006-11-16 23:34:43 +05:30
|
|
|
|
2008-06-13 15:25:13 +05:30
|
|
|
/* If it's not a prefix match, continue searching */
|
|
|
|
len = strlen(dir_prefix);
|
|
|
|
if (len != 1 /* dir_prefix "/" matches all, don't need to check */
|
|
|
|
&& (strncmp(dir_prefix, path, len) != 0
|
2011-11-29 18:21:11 +05:30
|
|
|
|| (path[len] != '/' && path[len] != '\0')
|
|
|
|
)
|
2006-11-16 23:34:43 +05:30
|
|
|
) {
|
2008-06-13 15:25:13 +05:30
|
|
|
continue;
|
|
|
|
}
|
2003-07-28 13:10:39 +05:30
|
|
|
|
2008-06-13 15:25:13 +05:30
|
|
|
/* Path match found */
|
|
|
|
prev = dir_prefix;
|
2006-09-26 15:37:41 +05:30
|
|
|
|
2008-06-13 15:25:13 +05:30
|
|
|
if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
|
2011-11-29 18:21:11 +05:30
|
|
|
char *colon_after_user;
|
|
|
|
const char *passwd;
|
2011-11-30 01:24:30 +05:30
|
|
|
# if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM
|
|
|
|
char sp_buf[256];
|
|
|
|
# endif
|
2011-11-29 18:21:11 +05:30
|
|
|
|
|
|
|
colon_after_user = strchr(user_and_passwd, ':');
|
|
|
|
if (!colon_after_user)
|
|
|
|
goto bad_input;
|
2012-02-01 07:12:54 +05:30
|
|
|
|
|
|
|
/* compare "user:" */
|
|
|
|
if (cur->after_colon[0] != '*'
|
|
|
|
&& strncmp(cur->after_colon, user_and_passwd,
|
|
|
|
colon_after_user - user_and_passwd + 1) != 0
|
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* this cfg entry is '*' or matches username from peer */
|
|
|
|
|
2011-11-29 18:21:11 +05:30
|
|
|
passwd = strchr(cur->after_colon, ':');
|
|
|
|
if (!passwd)
|
|
|
|
goto bad_input;
|
|
|
|
passwd++;
|
|
|
|
if (passwd[0] == '*') {
|
|
|
|
# if ENABLE_PAM
|
|
|
|
struct pam_userinfo userinfo;
|
|
|
|
struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
|
|
|
|
pam_handle_t *pamh;
|
|
|
|
|
|
|
|
*colon_after_user = '\0';
|
|
|
|
userinfo.name = user_and_passwd;
|
|
|
|
userinfo.pw = colon_after_user + 1;
|
2011-11-30 01:24:30 +05:30
|
|
|
r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS;
|
|
|
|
if (r == 0) {
|
|
|
|
r = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
|
|
|
|
|| pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
|
|
|
|
;
|
|
|
|
pam_end(pamh, PAM_SUCCESS);
|
|
|
|
}
|
2011-11-29 18:21:11 +05:30
|
|
|
*colon_after_user = ':';
|
|
|
|
goto end_check_passwd;
|
|
|
|
# else
|
|
|
|
# if ENABLE_FEATURE_SHADOWPASSWDS
|
|
|
|
/* Using _r function to avoid pulling in static buffers */
|
|
|
|
struct spwd spw;
|
|
|
|
# endif
|
|
|
|
struct passwd *pw;
|
|
|
|
|
|
|
|
*colon_after_user = '\0';
|
|
|
|
pw = getpwnam(user_and_passwd);
|
|
|
|
*colon_after_user = ':';
|
|
|
|
if (!pw || !pw->pw_passwd)
|
2006-11-16 23:34:43 +05:30
|
|
|
continue;
|
2011-11-29 18:21:11 +05:30
|
|
|
passwd = pw->pw_passwd;
|
|
|
|
# if ENABLE_FEATURE_SHADOWPASSWDS
|
|
|
|
if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) {
|
|
|
|
/* getspnam_r may return 0 yet set result to NULL.
|
|
|
|
* At least glibc 2.4 does this. Be extra paranoid here. */
|
|
|
|
struct spwd *result = NULL;
|
2011-11-30 01:24:30 +05:30
|
|
|
r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result);
|
2011-11-29 18:21:11 +05:30
|
|
|
if (r == 0 && result)
|
|
|
|
passwd = result->sp_pwdp;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2011-11-29 18:21:11 +05:30
|
|
|
# endif
|
2012-02-01 07:12:54 +05:30
|
|
|
/* In this case, passwd is ALWAYS encrypted:
|
|
|
|
* it came from /etc/passwd or /etc/shadow!
|
|
|
|
*/
|
|
|
|
goto check_encrypted;
|
2011-11-29 18:21:11 +05:30
|
|
|
# endif /* ENABLE_PAM */
|
|
|
|
}
|
2012-02-01 07:12:54 +05:30
|
|
|
/* Else: passwd is from httpd.conf, it is either plaintext or encrypted */
|
|
|
|
|
|
|
|
if (passwd[0] == '$' && isdigit(passwd[1])) {
|
|
|
|
char *encrypted;
|
2012-04-20 18:18:00 +05:30
|
|
|
# if !ENABLE_PAM
|
2012-02-01 07:12:54 +05:30
|
|
|
check_encrypted:
|
2012-04-20 18:18:00 +05:30
|
|
|
# endif
|
2012-02-01 07:12:54 +05:30
|
|
|
/* encrypt pwd from peer and check match with local one */
|
|
|
|
encrypted = pw_encrypt(
|
|
|
|
/* pwd (from peer): */ colon_after_user + 1,
|
2011-11-29 18:21:11 +05:30
|
|
|
/* salt: */ passwd,
|
|
|
|
/* cleanup: */ 0
|
|
|
|
);
|
|
|
|
r = strcmp(encrypted, passwd);
|
|
|
|
free(encrypted);
|
2012-02-01 07:12:54 +05:30
|
|
|
} else {
|
|
|
|
/* local passwd is from httpd.conf and it's plaintext */
|
|
|
|
r = strcmp(colon_after_user + 1, passwd);
|
2011-11-29 18:21:11 +05:30
|
|
|
}
|
2012-02-01 07:12:54 +05:30
|
|
|
goto end_check_passwd;
|
2008-06-13 15:25:13 +05:30
|
|
|
}
|
2012-02-01 07:12:54 +05:30
|
|
|
bad_input:
|
2008-06-13 15:25:13 +05:30
|
|
|
/* Comparing plaintext "user:pass" in one go */
|
2011-11-29 18:21:11 +05:30
|
|
|
r = strcmp(cur->after_colon, user_and_passwd);
|
|
|
|
end_check_passwd:
|
|
|
|
if (r == 0) {
|
2008-06-13 15:25:13 +05:30
|
|
|
remoteuser = xstrndup(user_and_passwd,
|
2011-11-29 18:21:11 +05:30
|
|
|
strchrnul(user_and_passwd, ':') - user_and_passwd
|
|
|
|
);
|
2008-06-13 15:25:13 +05:30
|
|
|
return 1; /* Ok */
|
2003-10-03 16:20:56 +05:30
|
|
|
}
|
2007-08-18 00:49:42 +05:30
|
|
|
} /* for */
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2008-06-13 15:25:13 +05:30
|
|
|
/* 0(bad) if prev is set: matches were found but passwd was wrong */
|
|
|
|
return (prev == NULL);
|
2003-05-13 21:50:11 +05:30
|
|
|
}
|
2006-11-16 23:34:43 +05:30
|
|
|
#endif /* FEATURE_HTTPD_BASIC_AUTH */
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2007-10-18 18:24:39 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_PROXY
|
|
|
|
static Htaccess_Proxy *find_proxy_entry(const char *url)
|
|
|
|
{
|
|
|
|
Htaccess_Proxy *p;
|
|
|
|
for (p = proxy; p; p = p->next) {
|
2015-03-12 22:18:34 +05:30
|
|
|
if (is_prefixed_with(url, p->url_from))
|
2007-10-18 18:24:39 +05:30
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-08-14 22:20:01 +05:30
|
|
|
/*
|
|
|
|
* Handle timeouts
|
|
|
|
*/
|
2009-02-05 18:08:21 +05:30
|
|
|
static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
|
|
|
|
static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
|
2004-10-08 13:33:29 +05:30
|
|
|
{
|
2007-08-18 00:49:15 +05:30
|
|
|
send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
|
2004-10-08 13:33:29 +05:30
|
|
|
}
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2007-08-14 22:20:01 +05:30
|
|
|
/*
|
|
|
|
* Handle an incoming http request and exit.
|
|
|
|
*/
|
2008-07-05 14:48:54 +05:30
|
|
|
static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN;
|
2007-08-18 19:50:21 +05:30
|
|
|
static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2006-09-26 15:37:41 +05:30
|
|
|
struct stat sb;
|
2007-08-20 00:23:43 +05:30
|
|
|
char *urlcopy;
|
|
|
|
char *urlp;
|
|
|
|
char *tptr;
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
2019-04-16 16:48:12 +05:30
|
|
|
unsigned remote_ip;
|
2020-08-16 18:28:31 +05:30
|
|
|
#endif
|
2019-04-16 16:48:12 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
|
|
|
unsigned total_headers_len;
|
|
|
|
#endif
|
2021-05-04 23:45:24 +05:30
|
|
|
const char *prequest;
|
2021-05-04 13:11:50 +05:30
|
|
|
static const char request_GET[] ALIGN1 = "GET";
|
2008-02-21 05:42:07 +05:30
|
|
|
static const char request_HEAD[] ALIGN1 = "HEAD";
|
2021-05-04 23:45:24 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
2021-05-04 13:11:50 +05:30
|
|
|
static const char request_POST[] ALIGN1 = "POST";
|
|
|
|
unsigned long POST_length;
|
|
|
|
enum CGI_type {
|
|
|
|
CGI_NONE = 0,
|
|
|
|
CGI_NORMAL,
|
|
|
|
CGI_INDEX,
|
|
|
|
CGI_INTERPRETER,
|
|
|
|
} cgi_type = CGI_NONE;
|
2007-10-18 18:24:39 +05:30
|
|
|
#endif
|
2021-05-04 23:21:39 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_PROXY
|
|
|
|
Htaccess_Proxy *proxy_entry;
|
|
|
|
#endif
|
2008-06-13 15:23:06 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
|
|
|
smallint authorized = -1;
|
|
|
|
#endif
|
2019-04-16 16:15:26 +05:30
|
|
|
char *HTTP_slash;
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2007-08-18 19:52:09 +05:30
|
|
|
/* Allocation of iobuf is postponed until now
|
|
|
|
* (IOW, server process doesn't need to waste 8k) */
|
2007-08-20 00:58:09 +05:30
|
|
|
iobuf = xmalloc(IOBUF_SIZE);
|
2007-08-18 19:52:09 +05:30
|
|
|
|
2007-08-18 19:50:21 +05:30
|
|
|
if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
|
2008-05-18 20:44:36 +05:30
|
|
|
/* NB: can be NULL (user runs httpd -i by hand?) */
|
2008-01-29 16:03:34 +05:30
|
|
|
rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
|
2007-08-18 19:50:21 +05:30
|
|
|
}
|
|
|
|
if (verbose) {
|
|
|
|
/* this trick makes -v logging much simpler */
|
2008-05-18 20:44:36 +05:30
|
|
|
if (rmt_ip_str)
|
|
|
|
applet_name = rmt_ip_str;
|
2007-08-18 19:50:21 +05:30
|
|
|
if (verbose > 2)
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_error_msg("connected");
|
2007-08-18 19:50:21 +05:30
|
|
|
}
|
2020-08-16 18:28:31 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ACL_IP
|
|
|
|
remote_ip = 0;
|
|
|
|
if (fromAddr->u.sa.sa_family == AF_INET) {
|
|
|
|
remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
|
|
|
|
}
|
|
|
|
# if ENABLE_FEATURE_IPV6
|
|
|
|
if (fromAddr->u.sa.sa_family == AF_INET6
|
|
|
|
&& fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
|
|
|
|
&& fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
|
|
|
|
&& ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
|
|
|
|
remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
|
|
|
|
# endif
|
2019-04-16 16:48:12 +05:30
|
|
|
if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
|
2020-08-16 18:28:31 +05:30
|
|
|
#endif
|
2007-08-18 19:50:21 +05:30
|
|
|
|
2009-02-05 18:08:21 +05:30
|
|
|
/* Install timeout handler. get_line() needs it. */
|
|
|
|
signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
|
2004-10-08 13:33:29 +05:30
|
|
|
|
2021-05-05 18:30:09 +05:30
|
|
|
if (!get_line()) { /* EOF or error or empty line */
|
|
|
|
/* Observed Firefox to "speculatively" open
|
|
|
|
* extra connections to a new site on first access,
|
|
|
|
* they are closed in ~5 seconds with nothing
|
|
|
|
* being sent at all.
|
|
|
|
* (Presumably it's a method to decrease latency?)
|
|
|
|
*/
|
|
|
|
if (verbose > 2)
|
|
|
|
bb_simple_error_msg("eof on read, closing");
|
|
|
|
/* Don't bother generating error page in this case,
|
|
|
|
* just close the socket.
|
|
|
|
*/
|
|
|
|
//send_headers_and_exit(HTTP_BAD_REQUEST);
|
|
|
|
_exit(xfunc_error_retval);
|
|
|
|
}
|
|
|
|
dbg("Request:'%s'\n", iobuf);
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2021-05-04 23:21:39 +05:30
|
|
|
/* Find URL */
|
2013-03-26 03:57:00 +05:30
|
|
|
// rfc2616: method and URI is separated by exactly one space
|
|
|
|
//urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed
|
|
|
|
urlp = strchr(iobuf, ' ');
|
2007-08-20 00:23:43 +05:30
|
|
|
if (urlp == NULL)
|
2007-08-18 00:49:42 +05:30
|
|
|
send_headers_and_exit(HTTP_BAD_REQUEST);
|
2007-08-20 00:23:43 +05:30
|
|
|
*urlp++ = '\0';
|
2021-05-04 23:21:39 +05:30
|
|
|
//urlp = skip_whitespace(urlp); - should not be necessary
|
|
|
|
if (urlp[0] != '/')
|
|
|
|
send_headers_and_exit(HTTP_BAD_REQUEST);
|
|
|
|
/* Find end of URL */
|
|
|
|
HTTP_slash = strchr(urlp, ' ');
|
|
|
|
/* Is it " HTTP/"? */
|
|
|
|
if (!HTTP_slash || strncmp(HTTP_slash + 1, HTTP_200, 5) != 0)
|
|
|
|
send_headers_and_exit(HTTP_BAD_REQUEST);
|
|
|
|
*HTTP_slash++ = '\0';
|
|
|
|
|
|
|
|
#if ENABLE_FEATURE_HTTPD_PROXY
|
|
|
|
proxy_entry = find_proxy_entry(urlp);
|
|
|
|
if (proxy_entry) {
|
|
|
|
int proxy_fd;
|
|
|
|
len_and_sockaddr *lsa;
|
|
|
|
|
|
|
|
if (verbose > 1)
|
|
|
|
bb_error_msg("proxy:%s", urlp);
|
|
|
|
lsa = host2sockaddr(proxy_entry->host_port, 80);
|
|
|
|
if (!lsa)
|
|
|
|
send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
|
|
|
|
proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
|
|
|
|
if (proxy_fd < 0)
|
|
|
|
send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
|
|
|
|
if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
|
|
|
|
send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
|
|
|
|
/* Disable peer header reading timeout */
|
|
|
|
alarm(0);
|
|
|
|
/* Config directive was of the form:
|
|
|
|
* P:/url:[http://]hostname[:port]/new/path
|
|
|
|
* When /urlSFX is requested, reverse proxy it
|
|
|
|
* to http://hostname[:port]/new/pathSFX
|
|
|
|
*/
|
|
|
|
fdprintf(proxy_fd, "%s %s%s %s\r\n",
|
|
|
|
iobuf, /* "GET" / "POST" / etc */
|
|
|
|
proxy_entry->url_to, /* "/new/path" */
|
|
|
|
urlp + strlen(proxy_entry->url_from), /* "SFX" */
|
|
|
|
HTTP_slash /* "HTTP/xyz" */
|
|
|
|
);
|
|
|
|
cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Determine type of request (GET/POST/...) */
|
2007-08-18 00:50:07 +05:30
|
|
|
prequest = request_GET;
|
2021-05-04 13:11:50 +05:30
|
|
|
if (strcasecmp(iobuf, prequest) == 0)
|
|
|
|
goto found;
|
|
|
|
prequest = request_HEAD;
|
|
|
|
if (strcasecmp(iobuf, prequest) == 0)
|
|
|
|
goto found;
|
2021-05-04 23:45:24 +05:30
|
|
|
#if !ENABLE_FEATURE_HTTPD_CGI
|
|
|
|
send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
|
|
|
|
#else
|
2021-05-04 13:11:50 +05:30
|
|
|
prequest = request_POST;
|
|
|
|
if (strcasecmp(iobuf, prequest) == 0)
|
|
|
|
goto found;
|
2021-05-04 23:21:39 +05:30
|
|
|
/* For CGI, allow DELETE, PUT, OPTIONS, etc too */
|
2021-05-04 13:11:50 +05:30
|
|
|
prequest = alloca(16);
|
|
|
|
safe_strncpy((char*)prequest, iobuf, 16);
|
2006-09-26 15:37:41 +05:30
|
|
|
#endif
|
2021-05-04 23:45:24 +05:30
|
|
|
found:
|
2021-05-04 23:21:39 +05:30
|
|
|
/* Copy URL to stack-allocated char[] */
|
2019-04-16 16:15:26 +05:30
|
|
|
urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
|
2007-08-20 00:23:43 +05:30
|
|
|
strcpy(urlcopy, urlp);
|
|
|
|
/* NB: urlcopy ptr is never changed after this */
|
2007-08-18 00:50:07 +05:30
|
|
|
|
2019-04-16 17:05:56 +05:30
|
|
|
/* Extract url args if present */
|
|
|
|
g_query = strchr(urlcopy, '?');
|
|
|
|
if (g_query)
|
|
|
|
*g_query++ = '\0';
|
|
|
|
|
2019-04-16 16:15:26 +05:30
|
|
|
/* Decode URL escape sequences */
|
|
|
|
tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1);
|
|
|
|
if (tptr == NULL)
|
|
|
|
send_headers_and_exit(HTTP_BAD_REQUEST);
|
|
|
|
if (tptr == urlcopy + 1) {
|
|
|
|
/* '/' or NUL is encoded */
|
|
|
|
send_headers_and_exit(HTTP_NOT_FOUND);
|
2019-04-16 13:30:06 +05:30
|
|
|
}
|
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
/* Canonicalize path */
|
|
|
|
/* Algorithm stolen from libbb bb_simplify_path(),
|
2008-05-07 17:48:48 +05:30
|
|
|
* but don't strdup, retain trailing slash, protect root */
|
2007-08-20 00:23:43 +05:30
|
|
|
urlp = tptr = urlcopy;
|
2021-05-04 13:11:50 +05:30
|
|
|
while (1) {
|
2007-08-20 00:23:43 +05:30
|
|
|
if (*urlp == '/') {
|
2007-08-18 00:49:42 +05:30
|
|
|
/* skip duplicate (or initial) slash */
|
2007-08-20 00:23:43 +05:30
|
|
|
if (*tptr == '/') {
|
2011-12-16 06:07:02 +05:30
|
|
|
goto next_char;
|
2007-08-18 00:49:42 +05:30
|
|
|
}
|
2007-08-20 00:23:43 +05:30
|
|
|
if (*tptr == '.') {
|
2011-12-16 06:07:02 +05:30
|
|
|
if (tptr[1] == '.' && (tptr[2] == '/' || tptr[2] == '\0')) {
|
|
|
|
/* "..": be careful */
|
|
|
|
/* protect root */
|
|
|
|
if (urlp == urlcopy)
|
2007-08-18 00:49:42 +05:30
|
|
|
send_headers_and_exit(HTTP_BAD_REQUEST);
|
2011-12-16 06:07:02 +05:30
|
|
|
/* omit previous dir */
|
|
|
|
while (*--urlp != '/')
|
2007-08-18 00:49:42 +05:30
|
|
|
continue;
|
2011-12-16 06:07:02 +05:30
|
|
|
/* skip to "./" or ".<NUL>" */
|
|
|
|
tptr++;
|
|
|
|
}
|
|
|
|
if (tptr[1] == '/' || tptr[1] == '\0') {
|
|
|
|
/* skip extra "/./" */
|
|
|
|
goto next_char;
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-08-20 00:23:43 +05:30
|
|
|
*++urlp = *tptr;
|
2019-04-16 13:30:06 +05:30
|
|
|
if (*tptr == '\0')
|
2011-12-16 06:07:02 +05:30
|
|
|
break;
|
|
|
|
next_char:
|
|
|
|
tptr++;
|
|
|
|
}
|
2007-08-18 00:49:42 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
/* Log it */
|
2007-08-18 00:49:42 +05:30
|
|
|
if (verbose > 1)
|
2007-08-20 00:23:43 +05:30
|
|
|
bb_error_msg("url:%s", urlcopy);
|
2007-08-18 00:49:42 +05:30
|
|
|
|
2007-08-20 00:23:43 +05:30
|
|
|
tptr = urlcopy;
|
2019-04-14 23:27:13 +05:30
|
|
|
while ((tptr = strchr(tptr + 1, '/')) != NULL) {
|
2007-08-18 00:49:42 +05:30
|
|
|
/* have path1/path2 */
|
2007-08-20 00:23:43 +05:30
|
|
|
*tptr = '\0';
|
2021-05-05 00:55:16 +05:30
|
|
|
/* may have subdir config */
|
|
|
|
if (parse_conf(urlcopy + 1, SUBDIR_PARSE) == 0)
|
2019-04-16 16:48:12 +05:30
|
|
|
if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
|
2007-08-20 00:23:43 +05:30
|
|
|
*tptr = '/';
|
2007-08-18 00:49:42 +05:30
|
|
|
}
|
2007-10-18 18:24:39 +05:30
|
|
|
|
2019-04-16 15:07:02 +05:30
|
|
|
tptr = urlcopy + 1; /* skip first '/' */
|
|
|
|
|
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
|
|
|
if (is_prefixed_with(tptr, "cgi-bin/")) {
|
|
|
|
if (tptr[8] == '\0') {
|
|
|
|
/* protect listing "cgi-bin/" */
|
|
|
|
send_headers_and_exit(HTTP_FORBIDDEN);
|
|
|
|
}
|
|
|
|
cgi_type = CGI_NORMAL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (urlp[-1] == '/') {
|
|
|
|
/* When index_page string is appended to <dir>/ URL, it overwrites
|
|
|
|
* the query string. If we fall back to call /cgi-bin/index.cgi,
|
|
|
|
* query string would be lost and not available to the CGI.
|
|
|
|
* Work around it by making a deep copy.
|
|
|
|
*/
|
|
|
|
if (ENABLE_FEATURE_HTTPD_CGI)
|
|
|
|
g_query = xstrdup(g_query); /* ok for NULL too */
|
|
|
|
strcpy(urlp, index_page);
|
|
|
|
}
|
|
|
|
if (stat(tptr, &sb) == 0) {
|
2021-05-05 00:41:03 +05:30
|
|
|
/* If URL is a directory with no slash, set up
|
|
|
|
* "HTTP/1.1 302 Found" "Location: /dir/" reply */
|
|
|
|
if (urlp[-1] != '/' && S_ISDIR(sb.st_mode)) {
|
|
|
|
found_moved_temporarily = urlcopy;
|
|
|
|
} else {
|
2019-04-16 15:07:02 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
|
2021-05-05 00:41:03 +05:30
|
|
|
char *suffix = strrchr(tptr, '.');
|
|
|
|
if (suffix) {
|
|
|
|
Htaccess *cur;
|
|
|
|
for (cur = script_i; cur; cur = cur->next) {
|
|
|
|
if (strcmp(cur->before_colon + 1, suffix) == 0) {
|
|
|
|
cgi_type = CGI_INTERPRETER;
|
|
|
|
break;
|
|
|
|
}
|
2019-04-16 15:07:02 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
file_size = sb.st_size;
|
|
|
|
last_mod = sb.st_mtime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
|
|
|
else if (urlp[-1] == '/') {
|
2021-05-05 13:10:59 +05:30
|
|
|
/* It's a dir URL and there is no index.html */
|
|
|
|
/* Is there cgi-bin/index.cgi? */
|
2021-05-05 00:55:16 +05:30
|
|
|
if (access("/cgi-bin/index.cgi"+1, X_OK) != 0)
|
2021-05-05 13:10:59 +05:30
|
|
|
send_headers_and_exit(HTTP_NOT_FOUND); /* no */
|
2021-05-05 00:55:16 +05:30
|
|
|
cgi_type = CGI_INDEX;
|
2019-04-16 15:07:02 +05:30
|
|
|
}
|
|
|
|
#endif
|
2021-05-05 13:10:59 +05:30
|
|
|
|
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CGI
|
|
|
|
/* check_user_passwd() would be confused by added .../index.html, truncate it */
|
2019-04-16 15:07:02 +05:30
|
|
|
urlp[0] = '\0';
|
2021-05-05 13:10:59 +05:30
|
|
|
#endif
|
2019-04-16 15:07:02 +05:30
|
|
|
|
2019-04-16 16:48:12 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
|
|
|
total_headers_len = 0;
|
2021-05-04 13:11:50 +05:30
|
|
|
POST_length = 0;
|
2019-04-16 16:48:12 +05:30
|
|
|
#endif
|
2007-08-18 00:50:07 +05:30
|
|
|
|
2019-04-19 17:32:51 +05:30
|
|
|
/* Read until blank line */
|
|
|
|
while (1) {
|
|
|
|
unsigned iobuf_len = get_line();
|
|
|
|
if (!iobuf_len)
|
|
|
|
break; /* EOF or error or empty line */
|
2019-04-16 16:48:12 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
2019-04-19 17:32:51 +05:30
|
|
|
/* Prevent unlimited growth of HTTP_xyz envvars */
|
|
|
|
total_headers_len += iobuf_len;
|
|
|
|
if (total_headers_len >= MAX_HTTP_HEADERS_SIZE)
|
|
|
|
send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
|
2019-04-16 16:48:12 +05:30
|
|
|
#endif
|
2021-05-05 18:30:09 +05:30
|
|
|
dbg("header:'%s'\n", iobuf);
|
2021-05-04 13:11:50 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
|
|
|
/* Only POST needs to know POST_length */
|
|
|
|
if (prequest == request_POST && STRNCASECMP(iobuf, "Content-Length:") == 0) {
|
|
|
|
tptr = skip_whitespace(iobuf + sizeof("Content-Length:") - 1);
|
|
|
|
if (!tptr[0])
|
|
|
|
send_headers_and_exit(HTTP_BAD_REQUEST);
|
|
|
|
/* not using strtoul: it ignores leading minus! */
|
|
|
|
POST_length = bb_strtou(tptr, NULL, 10);
|
|
|
|
/* length is "ulong", but we need to pass it to int later */
|
|
|
|
if (errno || POST_length > INT_MAX)
|
|
|
|
send_headers_and_exit(HTTP_BAD_REQUEST);
|
2019-04-19 17:32:51 +05:30
|
|
|
continue;
|
|
|
|
}
|
2003-02-09 12:21:14 +05:30
|
|
|
#endif
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2019-04-19 17:32:51 +05:30
|
|
|
if (STRNCASECMP(iobuf, "Authorization:") == 0) {
|
|
|
|
/* We only allow Basic credentials.
|
|
|
|
* It shows up as "Authorization: Basic <user>:<passwd>" where
|
|
|
|
* "<user>:<passwd>" is base64 encoded.
|
|
|
|
*/
|
|
|
|
tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1);
|
|
|
|
if (STRNCASECMP(tptr, "Basic") == 0) {
|
|
|
|
tptr += sizeof("Basic")-1;
|
|
|
|
/* decodeBase64() skips whitespace itself */
|
|
|
|
decodeBase64(tptr);
|
|
|
|
authorized = check_user_passwd(urlcopy, tptr);
|
|
|
|
continue;
|
2007-08-18 00:49:42 +05:30
|
|
|
}
|
2019-04-19 17:32:51 +05:30
|
|
|
}
|
2008-06-13 15:23:06 +05:30
|
|
|
#endif
|
2007-09-22 04:05:18 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_RANGES
|
2019-04-19 17:32:51 +05:30
|
|
|
if (STRNCASECMP(iobuf, "Range:") == 0) {
|
|
|
|
/* We know only bytes=NNN-[MMM] */
|
|
|
|
char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
|
2020-12-29 20:59:05 +05:30
|
|
|
s = is_prefixed_with(s, "bytes=");
|
|
|
|
if (s) {
|
2019-04-19 17:32:51 +05:30
|
|
|
range_start = BB_STRTOOFF(s, &s, 10);
|
|
|
|
if (s[0] != '-' || range_start < 0) {
|
|
|
|
range_start = -1;
|
|
|
|
} else if (s[1]) {
|
|
|
|
range_end = BB_STRTOOFF(s+1, NULL, 10);
|
|
|
|
if (errno || range_end < range_start)
|
2012-02-19 21:48:45 +05:30
|
|
|
range_start = -1;
|
2007-09-22 04:05:18 +05:30
|
|
|
}
|
|
|
|
}
|
2019-04-19 17:32:51 +05:30
|
|
|
continue;
|
|
|
|
}
|
2010-07-25 06:50:53 +05:30
|
|
|
#endif
|
|
|
|
#if ENABLE_FEATURE_HTTPD_GZIP
|
2019-04-19 17:32:51 +05:30
|
|
|
if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) {
|
|
|
|
/* Note: we do not support "gzip;q=0"
|
|
|
|
* method of _disabling_ gzip
|
|
|
|
* delivery. No one uses that, though */
|
|
|
|
const char *s = strstr(iobuf, "gzip");
|
|
|
|
if (s) {
|
|
|
|
// want more thorough checks?
|
|
|
|
//if (s[-1] == ' '
|
|
|
|
// || s[-1] == ','
|
|
|
|
// || s[-1] == ':'
|
|
|
|
//) {
|
|
|
|
content_gzip = 1;
|
|
|
|
//}
|
2019-04-16 15:07:02 +05:30
|
|
|
}
|
2019-04-19 17:32:51 +05:30
|
|
|
continue;
|
|
|
|
}
|
2019-04-16 15:07:02 +05:30
|
|
|
#endif
|
2020-08-16 03:22:48 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ETAG
|
|
|
|
if (STRNCASECMP(iobuf, "If-None-Match:") == 0) {
|
|
|
|
free(G.if_none_match);
|
|
|
|
G.if_none_match = xstrdup(skip_whitespace(iobuf + sizeof("If-None-Match:") - 1));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2019-04-16 15:07:02 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
2019-04-19 17:32:51 +05:30
|
|
|
if (cgi_type != CGI_NONE) {
|
|
|
|
bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);
|
|
|
|
char *cp;
|
|
|
|
char *colon = strchr(iobuf, ':');
|
2019-04-16 15:07:02 +05:30
|
|
|
|
2019-04-19 17:32:51 +05:30
|
|
|
if (!colon)
|
|
|
|
continue;
|
|
|
|
cp = iobuf;
|
|
|
|
while (cp < colon) {
|
|
|
|
/* a-z => A-Z, not-alnum => _ */
|
|
|
|
char c = (*cp & ~0x20); /* toupper for A-Za-z, undef for others */
|
|
|
|
if ((unsigned)(c - 'A') <= ('Z' - 'A')) {
|
|
|
|
*cp++ = c;
|
2019-04-16 15:07:02 +05:30
|
|
|
continue;
|
|
|
|
}
|
2019-04-19 17:32:51 +05:30
|
|
|
if (!isdigit(*cp))
|
|
|
|
*cp = '_';
|
|
|
|
cp++;
|
2010-07-25 06:50:53 +05:30
|
|
|
}
|
2019-04-19 17:32:51 +05:30
|
|
|
/* "Content-Type:" gets no HTTP_ prefix, all others do */
|
|
|
|
cp = xasprintf(ct ? "HTTP_%.*s=%s" + 5 : "HTTP_%.*s=%s",
|
|
|
|
(int)(colon - iobuf), iobuf,
|
|
|
|
skip_whitespace(colon + 1)
|
|
|
|
);
|
|
|
|
putenv(cp);
|
|
|
|
}
|
2007-09-22 04:05:18 +05:30
|
|
|
#endif
|
2019-04-19 17:32:51 +05:30
|
|
|
} /* while extra header reading */
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2007-10-18 18:24:39 +05:30
|
|
|
/* We are done reading headers, disable peer timeout */
|
2007-08-18 00:49:42 +05:30
|
|
|
alarm(0);
|
|
|
|
|
2019-04-14 23:27:13 +05:30
|
|
|
if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0) {
|
2009-04-22 19:19:16 +05:30
|
|
|
/* protect listing [/path]/httpd.conf or IP deny */
|
2007-08-18 00:49:42 +05:30
|
|
|
send_headers_and_exit(HTTP_FORBIDDEN);
|
|
|
|
}
|
2003-02-09 12:21:14 +05:30
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
2011-11-29 18:21:11 +05:30
|
|
|
/* Case: no "Authorization:" was seen, but page might require passwd.
|
2008-06-13 15:23:06 +05:30
|
|
|
* Check that with dummy user:pass */
|
2008-06-13 18:50:38 +05:30
|
|
|
if (authorized < 0)
|
2011-11-29 18:21:11 +05:30
|
|
|
authorized = check_user_passwd(urlcopy, (char *) "");
|
2008-06-13 18:50:38 +05:30
|
|
|
if (!authorized)
|
2007-08-18 00:49:42 +05:30
|
|
|
send_headers_and_exit(HTTP_UNAUTHORIZED);
|
2003-02-09 12:21:14 +05:30
|
|
|
#endif
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2021-05-04 23:21:39 +05:30
|
|
|
if (found_moved_temporarily)
|
2007-08-18 00:49:42 +05:30
|
|
|
send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
|
2004-10-08 13:33:29 +05:30
|
|
|
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
2019-04-16 15:07:02 +05:30
|
|
|
if (cgi_type != CGI_NONE) {
|
|
|
|
send_cgi_and_exit(
|
|
|
|
(cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
|
|
|
|
/*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy,
|
2021-05-04 13:11:50 +05:30
|
|
|
urlcopy, prequest, POST_length
|
2019-04-16 15:07:02 +05:30
|
|
|
);
|
2007-08-18 00:49:42 +05:30
|
|
|
}
|
2009-09-09 00:47:49 +05:30
|
|
|
#endif
|
|
|
|
|
2019-04-16 15:07:02 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_CGI
|
2009-09-09 00:47:49 +05:30
|
|
|
if (prequest != request_GET && prequest != request_HEAD) {
|
2021-05-04 13:11:50 +05:30
|
|
|
/* POST / DELETE / PUT / OPTIONS for files do not make sense */
|
2009-09-09 00:47:49 +05:30
|
|
|
send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
|
|
|
|
}
|
2008-03-17 14:34:04 +05:30
|
|
|
#else
|
2021-05-04 23:45:24 +05:30
|
|
|
/* !CGI: it can be only GET or HEAD */
|
2008-03-17 14:34:04 +05:30
|
|
|
#endif
|
2021-05-05 13:10:59 +05:30
|
|
|
|
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
|
|
|
|
/* Restore truncated .../index.html */
|
|
|
|
if (urlp[-1] == '/')
|
|
|
|
urlp[0] = index_page[0];
|
|
|
|
#endif
|
|
|
|
send_file_and_exit(urlcopy + 1,
|
2021-05-04 23:45:24 +05:30
|
|
|
(prequest != request_HEAD ? (SEND_HEADERS + SEND_BODY) : SEND_HEADERS)
|
|
|
|
);
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
|
|
|
|
2007-08-14 22:20:01 +05:30
|
|
|
/*
|
|
|
|
* The main http server function.
|
2007-08-18 19:50:21 +05:30
|
|
|
* Given a socket, listen for new connections and farm out
|
|
|
|
* the processing as a [v]forked process.
|
2007-08-14 22:20:01 +05:30
|
|
|
* Never returns.
|
|
|
|
*/
|
2007-08-18 19:50:21 +05:30
|
|
|
#if BB_MMU
|
2008-07-05 14:48:54 +05:30
|
|
|
static void mini_httpd(int server_socket) NORETURN;
|
2007-08-18 19:48:43 +05:30
|
|
|
static void mini_httpd(int server_socket)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2007-08-18 00:50:07 +05:30
|
|
|
/* NB: it's best to not use xfuncs in this loop before fork().
|
|
|
|
* Otherwise server may die on transient errors (temporary
|
|
|
|
* out-of-memory condition, etc), which is Bad(tm).
|
|
|
|
* Try to do any dangerous calls after fork.
|
|
|
|
*/
|
2006-09-26 15:37:41 +05:30
|
|
|
while (1) {
|
2007-08-18 00:49:15 +05:30
|
|
|
int n;
|
|
|
|
len_and_sockaddr fromAddr;
|
2007-10-14 10:25:59 +05:30
|
|
|
|
2007-08-18 00:49:15 +05:30
|
|
|
/* Wait for connections... */
|
|
|
|
fromAddr.len = LSA_SIZEOF_SA;
|
2008-01-29 16:03:34 +05:30
|
|
|
n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
|
2007-08-18 00:49:15 +05:30
|
|
|
if (n < 0)
|
2006-11-11 04:58:57 +05:30
|
|
|
continue;
|
2021-05-05 19:01:18 +05:30
|
|
|
//TODO: we can reject connects from denied IPs right away;
|
|
|
|
//also, we might want to do one MSG_DONTWAIT'ed recv() here
|
|
|
|
//to detect immediate EOF,
|
|
|
|
//to avoid forking a whole new process for attackers
|
|
|
|
//who open and close lots of connections.
|
|
|
|
//(OTOH, the real mitigtion for this sort of thing is
|
|
|
|
//to ratelimit connects in iptables)
|
2010-07-25 06:50:25 +05:30
|
|
|
|
2007-08-18 00:49:15 +05:30
|
|
|
/* set the KEEPALIVE option to cull dead connections */
|
2015-08-24 23:18:03 +05:30
|
|
|
setsockopt_keepalive(n);
|
2006-11-21 15:45:25 +05:30
|
|
|
|
2007-08-14 22:20:01 +05:30
|
|
|
if (fork() == 0) {
|
2006-11-21 15:45:25 +05:30
|
|
|
/* child */
|
2007-08-14 22:20:01 +05:30
|
|
|
/* Do not reload config on HUP */
|
2006-11-11 04:58:57 +05:30
|
|
|
signal(SIGHUP, SIG_IGN);
|
2007-08-18 19:48:43 +05:30
|
|
|
close(server_socket);
|
|
|
|
xmove_fd(n, 0);
|
|
|
|
xdup2(0, 1);
|
|
|
|
|
2007-08-18 19:50:21 +05:30
|
|
|
handle_incoming_and_exit(&fromAddr);
|
|
|
|
}
|
|
|
|
/* parent, or fork failed */
|
|
|
|
close(n);
|
|
|
|
} /* while (1) */
|
|
|
|
/* never reached */
|
|
|
|
}
|
|
|
|
#else
|
2008-07-05 14:48:54 +05:30
|
|
|
static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN;
|
2007-08-18 19:50:21 +05:30
|
|
|
static void mini_httpd_nommu(int server_socket, int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *argv_copy[argc + 2];
|
|
|
|
|
|
|
|
argv_copy[0] = argv[0];
|
|
|
|
argv_copy[1] = (char*)"-i";
|
|
|
|
memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0]));
|
|
|
|
|
|
|
|
/* NB: it's best to not use xfuncs in this loop before vfork().
|
|
|
|
* Otherwise server may die on transient errors (temporary
|
|
|
|
* out-of-memory condition, etc), which is Bad(tm).
|
|
|
|
* Try to do any dangerous calls after fork.
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
int n;
|
2007-10-14 10:25:59 +05:30
|
|
|
|
2007-08-18 19:50:21 +05:30
|
|
|
/* Wait for connections... */
|
2018-04-07 04:43:30 +05:30
|
|
|
n = accept(server_socket, NULL, NULL);
|
2007-08-18 19:50:21 +05:30
|
|
|
if (n < 0)
|
|
|
|
continue;
|
2010-07-25 06:50:25 +05:30
|
|
|
|
2007-08-18 19:50:21 +05:30
|
|
|
/* set the KEEPALIVE option to cull dead connections */
|
2015-08-24 23:18:03 +05:30
|
|
|
setsockopt_keepalive(n);
|
2007-08-18 19:50:21 +05:30
|
|
|
|
|
|
|
if (vfork() == 0) {
|
|
|
|
/* child */
|
|
|
|
/* Do not reload config on HUP */
|
|
|
|
signal(SIGHUP, SIG_IGN);
|
|
|
|
close(server_socket);
|
|
|
|
xmove_fd(n, 0);
|
|
|
|
xdup2(0, 1);
|
|
|
|
|
|
|
|
/* Run a copy of ourself in inetd mode */
|
|
|
|
re_exec(argv_copy);
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2011-11-22 21:49:26 +05:30
|
|
|
argv_copy[0][0] &= 0x7f;
|
2007-08-20 00:58:09 +05:30
|
|
|
/* parent, or vfork failed */
|
2007-08-18 00:50:07 +05:30
|
|
|
close(n);
|
2006-11-21 15:45:25 +05:30
|
|
|
} /* while (1) */
|
2007-08-18 00:48:47 +05:30
|
|
|
/* never reached */
|
2003-01-05 09:31:56 +05:30
|
|
|
}
|
2007-06-24 04:44:02 +05:30
|
|
|
#endif
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2007-08-18 19:50:21 +05:30
|
|
|
/*
|
|
|
|
* Process a HTTP connection on stdin/out.
|
|
|
|
* Never returns.
|
|
|
|
*/
|
2008-07-05 14:48:54 +05:30
|
|
|
static void mini_httpd_inetd(void) NORETURN;
|
2007-08-14 22:20:01 +05:30
|
|
|
static void mini_httpd_inetd(void)
|
2003-02-09 12:21:14 +05:30
|
|
|
{
|
2007-08-18 00:49:15 +05:30
|
|
|
len_and_sockaddr fromAddr;
|
|
|
|
|
2008-05-18 20:44:36 +05:30
|
|
|
memset(&fromAddr, 0, sizeof(fromAddr));
|
2007-08-18 00:49:15 +05:30
|
|
|
fromAddr.len = LSA_SIZEOF_SA;
|
2008-05-18 20:44:36 +05:30
|
|
|
/* NB: can fail if user runs it by hand and types in http cmds */
|
2008-01-29 16:03:34 +05:30
|
|
|
getpeername(0, &fromAddr.u.sa, &fromAddr.len);
|
2007-08-18 19:50:21 +05:30
|
|
|
handle_incoming_and_exit(&fromAddr);
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|
|
|
|
|
2009-02-05 05:13:44 +05:30
|
|
|
static void sighup_handler(int sig UNUSED_PARAM)
|
2003-02-09 12:21:14 +05:30
|
|
|
{
|
2019-04-14 23:27:13 +05:30
|
|
|
int sv = errno;
|
2009-04-22 19:19:16 +05:30
|
|
|
parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE);
|
2019-04-14 23:27:13 +05:30
|
|
|
errno = sv;
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|
|
|
|
|
2006-11-06 01:17:33 +05:30
|
|
|
enum {
|
2006-02-15 18:57:18 +05:30
|
|
|
c_opt_config_file = 0,
|
|
|
|
d_opt_decode_url,
|
|
|
|
h_opt_home_httpd,
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
|
|
|
|
IF_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
|
|
|
|
IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
|
|
|
|
IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
|
2006-11-16 21:47:02 +05:30
|
|
|
p_opt_port ,
|
|
|
|
p_opt_inetd ,
|
|
|
|
p_opt_foreground,
|
2007-08-14 22:25:01 +05:30
|
|
|
p_opt_verbose ,
|
2006-11-06 01:17:33 +05:30
|
|
|
OPT_CONFIG_FILE = 1 << c_opt_config_file,
|
|
|
|
OPT_DECODE_URL = 1 << d_opt_decode_url,
|
|
|
|
OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
|
2009-04-21 16:39:40 +05:30
|
|
|
OPT_ENCODE_URL = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
|
|
|
|
OPT_REALM = IF_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
|
|
|
|
OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
|
|
|
|
OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
|
2006-11-16 21:47:02 +05:30
|
|
|
OPT_PORT = 1 << p_opt_port,
|
|
|
|
OPT_INETD = 1 << p_opt_inetd,
|
|
|
|
OPT_FOREGROUND = 1 << p_opt_foreground,
|
2007-08-14 22:25:01 +05:30
|
|
|
OPT_VERBOSE = 1 << p_opt_verbose,
|
2006-02-15 18:57:18 +05:30
|
|
|
};
|
2003-06-26 14:35:32 +05:30
|
|
|
|
|
|
|
|
2007-10-11 15:35:36 +05:30
|
|
|
int httpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
2008-07-05 14:48:54 +05:30
|
|
|
int httpd_main(int argc UNUSED_PARAM, char **argv)
|
2003-01-05 09:31:56 +05:30
|
|
|
{
|
2007-08-18 19:48:43 +05:30
|
|
|
int server_socket = server_socket; /* for gcc */
|
2006-10-04 02:30:06 +05:30
|
|
|
unsigned opt;
|
2006-09-26 15:37:41 +05:30
|
|
|
char *url_for_decode;
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
|
|
|
|
IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
|
|
|
|
IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
|
|
|
|
IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
|
2003-07-28 13:10:39 +05:30
|
|
|
|
2007-08-18 00:50:07 +05:30
|
|
|
INIT_G();
|
|
|
|
|
2006-11-21 05:39:37 +05:30
|
|
|
#if ENABLE_LOCALE_SUPPORT
|
|
|
|
/* Undo busybox.c: we want to speak English in http (dates etc) */
|
|
|
|
setlocale(LC_TIME, "C");
|
|
|
|
#endif
|
|
|
|
|
2007-06-10 05:19:05 +05:30
|
|
|
home_httpd = xrealloc_getcwd_or_warn(NULL);
|
|
|
|
/* We do not "absolutize" path given by -h (home) opt.
|
2008-05-07 17:48:48 +05:30
|
|
|
* If user gives relative path in -h,
|
|
|
|
* $SCRIPT_FILENAME will not be set. */
|
2017-08-09 01:25:02 +05:30
|
|
|
opt = getopt32(argv, "^"
|
|
|
|
"c:d:h:"
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_ENCODE_URL_STR("e:")
|
|
|
|
IF_FEATURE_HTTPD_BASIC_AUTH("r:")
|
|
|
|
IF_FEATURE_HTTPD_AUTH_MD5("m:")
|
|
|
|
IF_FEATURE_HTTPD_SETUID("u:")
|
2017-08-09 01:25:02 +05:30
|
|
|
"p:ifv"
|
|
|
|
"\0"
|
|
|
|
/* -v counts, -i implies -f */
|
|
|
|
"vv:if",
|
2009-04-22 19:19:16 +05:30
|
|
|
&opt_c_configFile, &url_for_decode, &home_httpd
|
2009-04-21 16:39:40 +05:30
|
|
|
IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
|
|
|
|
IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
|
|
|
|
IF_FEATURE_HTTPD_AUTH_MD5(, &pass)
|
|
|
|
IF_FEATURE_HTTPD_SETUID(, &s_ugid)
|
2007-08-14 22:20:01 +05:30
|
|
|
, &bind_addr_or_port
|
2007-08-14 22:25:01 +05:30
|
|
|
, &verbose
|
2006-09-26 15:37:41 +05:30
|
|
|
);
|
|
|
|
if (opt & OPT_DECODE_URL) {
|
2021-02-04 01:17:14 +05:30
|
|
|
fputs_stdout(percent_decode_in_place(url_for_decode, /*strict:*/ 0));
|
2006-09-26 15:37:41 +05:30
|
|
|
return 0;
|
|
|
|
}
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
|
2006-09-26 15:37:41 +05:30
|
|
|
if (opt & OPT_ENCODE_URL) {
|
2021-02-04 01:17:14 +05:30
|
|
|
fputs_stdout(encodeString(url_for_encode));
|
2006-09-26 15:37:41 +05:30
|
|
|
return 0;
|
|
|
|
}
|
2003-02-09 12:21:14 +05:30
|
|
|
#endif
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_AUTH_MD5
|
2006-09-26 15:37:41 +05:30
|
|
|
if (opt & OPT_MD5) {
|
2009-12-16 06:58:50 +05:30
|
|
|
char salt[sizeof("$1$XXXXXXXX")];
|
|
|
|
salt[0] = '$';
|
|
|
|
salt[1] = '1';
|
|
|
|
salt[2] = '$';
|
2011-05-13 06:49:01 +05:30
|
|
|
crypt_make_salt(salt + 3, 4);
|
2011-11-29 18:21:11 +05:30
|
|
|
puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
|
2006-09-26 15:37:41 +05:30
|
|
|
return 0;
|
|
|
|
}
|
2003-07-28 13:10:39 +05:30
|
|
|
#endif
|
2006-10-01 02:11:44 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_SETUID
|
2006-09-26 15:37:41 +05:30
|
|
|
if (opt & OPT_SETUID) {
|
2008-07-21 20:11:33 +05:30
|
|
|
xget_uidgid(&ugid, s_ugid);
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|
|
|
|
#endif
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2007-08-18 19:50:21 +05:30
|
|
|
#if !BB_MMU
|
|
|
|
if (!(opt & OPT_FOREGROUND)) {
|
|
|
|
bb_daemonize_or_rexec(0, argv); /* don't change current directory */
|
2020-06-09 21:08:21 +05:30
|
|
|
re_execed = 0; /* for the following chdir to work */
|
2007-08-18 19:50:21 +05:30
|
|
|
}
|
|
|
|
#endif
|
2020-06-09 21:08:21 +05:30
|
|
|
/* Chdir to home (unless we were re_exec()ed for NOMMU case
|
|
|
|
* in mini_httpd_nommu(): we are already in the home dir then).
|
2020-04-07 15:11:34 +05:30
|
|
|
*/
|
|
|
|
if (!re_execed)
|
|
|
|
xchdir(home_httpd);
|
|
|
|
|
2006-11-16 21:47:02 +05:30
|
|
|
if (!(opt & OPT_INETD)) {
|
2007-01-12 16:05:23 +05:30
|
|
|
signal(SIGCHLD, SIG_IGN);
|
2007-06-10 05:19:05 +05:30
|
|
|
server_socket = openServer();
|
2006-11-16 21:47:02 +05:30
|
|
|
#if ENABLE_FEATURE_HTTPD_SETUID
|
|
|
|
/* drop privileges */
|
|
|
|
if (opt & OPT_SETUID) {
|
|
|
|
if (ugid.gid != (gid_t)-1) {
|
|
|
|
if (setgroups(1, &ugid.gid) == -1)
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_perror_msg_and_die("setgroups");
|
2006-11-16 21:47:02 +05:30
|
|
|
xsetgid(ugid.gid);
|
|
|
|
}
|
|
|
|
xsetuid(ugid.uid);
|
2006-10-06 04:20:22 +05:30
|
|
|
}
|
2003-02-09 12:21:14 +05:30
|
|
|
#endif
|
2006-11-16 21:47:02 +05:30
|
|
|
}
|
2003-01-05 09:31:56 +05:30
|
|
|
|
2009-02-05 05:13:44 +05:30
|
|
|
#if 0
|
2007-09-15 18:58:30 +05:30
|
|
|
/* User can do it himself: 'env - PATH="$PATH" httpd'
|
|
|
|
* We don't do it because we don't want to screw users
|
|
|
|
* which want to do
|
2007-10-18 18:24:39 +05:30
|
|
|
* 'env - VAR1=val1 VAR2=val2 httpd'
|
2007-09-15 18:58:30 +05:30
|
|
|
* and have VAR1 and VAR2 values visible in their CGIs.
|
|
|
|
* Besides, it is also smaller. */
|
2006-09-26 15:37:41 +05:30
|
|
|
{
|
|
|
|
char *p = getenv("PATH");
|
2008-03-28 07:54:59 +05:30
|
|
|
/* env strings themself are not freed, no need to xstrdup(p): */
|
2006-09-26 15:37:41 +05:30
|
|
|
clearenv();
|
|
|
|
if (p)
|
2007-06-10 05:19:05 +05:30
|
|
|
putenv(p - 5);
|
2007-08-18 19:48:43 +05:30
|
|
|
// if (!(opt & OPT_INETD))
|
|
|
|
// setenv_long("SERVER_PORT", ???);
|
2006-09-26 15:37:41 +05:30
|
|
|
}
|
2003-09-11 05:05:45 +05:30
|
|
|
#endif
|
|
|
|
|
2009-04-22 19:19:16 +05:30
|
|
|
parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
|
2009-02-05 05:13:44 +05:30
|
|
|
if (!(opt & OPT_INETD))
|
|
|
|
signal(SIGHUP, sighup_handler);
|
2007-08-18 19:50:21 +05:30
|
|
|
|
2007-08-18 00:50:39 +05:30
|
|
|
xfunc_error_retval = 0;
|
2007-08-14 22:20:01 +05:30
|
|
|
if (opt & OPT_INETD)
|
2018-04-07 04:43:30 +05:30
|
|
|
mini_httpd_inetd(); /* never returns */
|
2007-08-18 19:50:21 +05:30
|
|
|
#if BB_MMU
|
2006-11-16 21:47:02 +05:30
|
|
|
if (!(opt & OPT_FOREGROUND))
|
2007-08-14 22:20:01 +05:30
|
|
|
bb_daemonize(0); /* don't change current directory */
|
|
|
|
mini_httpd(server_socket); /* never returns */
|
2007-06-24 04:44:02 +05:30
|
|
|
#else
|
2007-08-18 19:50:21 +05:30
|
|
|
mini_httpd_nommu(server_socket, argc, argv); /* never returns */
|
2007-06-24 04:44:02 +05:30
|
|
|
#endif
|
2007-08-18 19:50:21 +05:30
|
|
|
/* return 0; */
|
2003-02-09 12:21:14 +05:30
|
|
|
}
|