aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn L McGrath <bug1@ihug.co.nz>2003-02-09 06:51:14 +0000
committerGlenn L McGrath <bug1@ihug.co.nz>2003-02-09 06:51:14 +0000
commit06e9565b6c365668dafeef1fdc0e60c9a1154623 (patch)
treec75619aad6d3ca6f4c32cd8d1dffbe4f6c53367b
parent877d418b39421361800afe4e1d4ca69b80edc121 (diff)
downloadbusybox-w32-06e9565b6c365668dafeef1fdc0e60c9a1154623.tar.gz
busybox-w32-06e9565b6c365668dafeef1fdc0e60c9a1154623.tar.bz2
busybox-w32-06e9565b6c365668dafeef1fdc0e60c9a1154623.zip
New applet, inetd, make httpd features more configurable, update authors, last_patch_80
from Vladimir N. Oleynik
-rw-r--r--AUTHORS7
-rw-r--r--docs/busybox_footer.pod12
-rw-r--r--docs/busybox_header.pod19
-rw-r--r--include/applets.h3
-rw-r--r--include/usage.h8
-rw-r--r--networking/Config.in113
-rw-r--r--networking/Makefile.in1
-rw-r--r--networking/httpd.c1948
-rw-r--r--networking/inetd.c1280
9 files changed, 2636 insertions, 755 deletions
diff --git a/AUTHORS b/AUTHORS
index 1da11bbae..c965d3c72 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -44,6 +44,9 @@ Magnus Damm <damm@opensource.se>
44Larry Doolittle <ldoolitt@recycle.lbl.gov> 44Larry Doolittle <ldoolitt@recycle.lbl.gov>
45 pristine source directory compilation, lots of patches and fixes. 45 pristine source directory compilation, lots of patches and fixes.
46 46
47Glenn Engel <glenne@engel.org>
48 httpd
49
47Gennady Feldman <gfeldman@gena01.com> 50Gennady Feldman <gfeldman@gena01.com>
48 Sysklogd (single threaded syslogd, IPC Circular buffer support, 51 Sysklogd (single threaded syslogd, IPC Circular buffer support,
49 logread), various fixes. 52 logread), various fixes.
@@ -67,8 +70,8 @@ Glenn McGrath <bug1@optushome.com.au>
67 ar, dpkg, dpkg-deb 70 ar, dpkg, dpkg-deb
68 71
69Vladimir Oleynik <dzo@simtreas.ru> 72Vladimir Oleynik <dzo@simtreas.ru>
70 cmdedit; xargs(current); 73 cmdedit; xargs(current), httpd(current);
71 ports: ash, crond, fdisk, stty, traceroute, telnetd, top; 74 ports: ash, crond, fdisk, inetd, stty, traceroute, telnetd, top;
72 locale, various fixes 75 locale, various fixes
73 and irreconcilable critic of everything not perfect. 76 and irreconcilable critic of everything not perfect.
74 77
diff --git a/docs/busybox_footer.pod b/docs/busybox_footer.pod
index 7c53cced5..f72bd217a 100644
--- a/docs/busybox_footer.pod
+++ b/docs/busybox_footer.pod
@@ -113,8 +113,8 @@ Glenn McGrath <bug1@netconnect.com.au>
113 113
114Vladimir Oleynik <dzo@simtreas.ru> 114Vladimir Oleynik <dzo@simtreas.ru>
115 115
116 cmdedit, xargs(current); 116 cmdedit, xargs(current), httpd(current);
117 ports: ash, crond, fdisk, stty, traceroute, telnetd, top; 117 ports: ash, crond, fdisk, inetd, stty, traceroute, telnetd, top;
118 locale, various fixes 118 locale, various fixes
119 and irreconcilable critic of everything not perfect. 119 and irreconcilable critic of everything not perfect.
120 120
@@ -166,6 +166,12 @@ Enrique Zanardi <ezanardi@ull.es>
166 166
167 tarcat (since removed), loadkmap, various fixes, Debian maintenance 167 tarcat (since removed), loadkmap, various fixes, Debian maintenance
168 168
169=for html <br>
170
171Glenn Engel <glenne@engel.org>
172
173 httpd
174
169=cut 175=cut
170 176
171# $Id: busybox_footer.pod,v 1.9 2002/11/26 22:00:19 bug1 Exp $ 177# $Id: busybox_footer.pod,v 1.10 2003/02/09 06:51:12 bug1 Exp $
diff --git a/docs/busybox_header.pod b/docs/busybox_header.pod
index a684cc194..af37b5a59 100644
--- a/docs/busybox_header.pod
+++ b/docs/busybox_header.pod
@@ -60,15 +60,16 @@ chmod, chown, chroot, chvt, clear, cmp, cp, cpio, crond, crontab, cut,
60date, dc, dd, deallocvt, deluser, df, dirname, dmesg, dos2unix, dpkg, 60date, dc, dd, deallocvt, deluser, df, dirname, dmesg, dos2unix, dpkg,
61dpkg-deb, du, dumpkmap, dutmp, echo, expr, false, fbset, fdflush, fdisk, 61dpkg-deb, du, dumpkmap, dutmp, echo, expr, false, fbset, fdflush, fdisk,
62find, free, freeramdisk, fsck.minix, getopt, getty, grep, gunzip, gzip, 62find, free, freeramdisk, fsck.minix, getopt, getty, grep, gunzip, gzip,
63halt, head, hostid, hostname, id, ifconfig, init, insmod, kill, killall, 63halt, head, hostid, hostname, httpd, id, ifconfig, inetd, init, insmod,
64klogd, length, ln, loadacm, loadfont, loadkmap, logger, logname, ls, lsmod, 64kill, killall, klogd, length, ln, loadacm, loadfont, loadkmap, logger,
65makedevs, md5sum, mkdir, mkfifo, mkfs.minix, mknod, mkswap, mktemp, more, 65logname, ls, lsmod, makedevs, md5sum, mkdir, mkfifo, mkfs.minix, mknod,
66mount, mt, mv, nc, netstat, nslookup, ping, pivot_root, poweroff, printf, 66mkswap, mktemp, more, mount, mt, mv, nc, netstat, nslookup, ping,
67ps, pwd, rdate, readlink, reboot, renice, reset, rm, rmdir, rmmod, route, 67pivot_root, poweroff, printf, ps, pwd, rdate, readlink, reboot, renice,
68rpm2cpio, sed, setkeycodes, sh, sleep, sort, stty, swapoff, swapon, sync, 68reset, rm, rmdir, rmmod, route, rpm2cpio, sed, setkeycodes, sh, sleep,
69syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top, touch, tr, 69sort, stty, swapoff, swapon, sync, syslogd, tail, tar, tee, telnet,
70true, tty, umount, uname, uniq, unix2dos, update, uptime, usleep, uudecode, 70telnetd, test, tftp, time, top, touch, tr, true, tty, umount, uname, uniq,
71uuencode, watchdog, wc, wget, which, whoami, xargs, yes, zcat, [ 71unix2dos, update, uptime, usleep, uudecode, uuencode, watchdog, wc, wget,
72which, whoami, xargs, yes, zcat, [
72 73
73=over 4 74=over 4
74 75
diff --git a/include/applets.h b/include/applets.h
index 9e88f5044..5474de581 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -265,6 +265,9 @@
265#ifdef CONFIG_IFUPDOWN 265#ifdef CONFIG_IFUPDOWN
266 APPLET(ifup, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER) 266 APPLET(ifup, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
267#endif 267#endif
268#ifdef CONFIG_INETD
269 APPLET(inetd, inetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
270#endif
268#ifdef CONFIG_INIT 271#ifdef CONFIG_INIT
269 APPLET(init, init_main, _BB_DIR_SBIN, _BB_SUID_NEVER) 272 APPLET(init, init_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
270#endif 273#endif
diff --git a/include/usage.h b/include/usage.h
index d2fb19372..55693c61e 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -915,6 +915,14 @@
915 "\t-m\tdon't run any mappings\n" \ 915 "\t-m\tdon't run any mappings\n" \
916 "\t-f\tforce de/configuration\n" 916 "\t-f\tforce de/configuration\n"
917 917
918#define inetd_trivial_usage \
919 "[-q len] [conf]"
920#define inetd_full_usage \
921 "Usage: [-q len] [conf]\n\n" \
922 "Option:\n" \
923 "\t-q\tSets the size of the socket listen queue to\n" \
924 "the specified value. Default is 128."
925
918#define init_trivial_usage \ 926#define init_trivial_usage \
919 "" 927 ""
920#define init_full_usage \ 928#define init_full_usage \
diff --git a/networking/Config.in b/networking/Config.in
index 22676c085..527aebbac 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -41,6 +41,15 @@ config CONFIG_HTTPD
41 help 41 help
42 Serve web pages via an HTTP server. 42 Serve web pages via an HTTP server.
43 43
44config CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
45 bool " Support call from inetd only"
46 default n
47 depends on CONFIG_HTTPD
48 help
49 This option disabling uid and port options for httpd applet
50 and more others reducing size moments, but require
51 internet superserver daemon for usaging.
52
44config CONFIG_FEATURE_HTTPD_BASIC_AUTH 53config CONFIG_FEATURE_HTTPD_BASIC_AUTH
45 bool " Enable Basic Authentication and IP address checking" 54 bool " Enable Basic Authentication and IP address checking"
46 default n 55 default n
@@ -49,6 +58,66 @@ config CONFIG_FEATURE_HTTPD_BASIC_AUTH
49 Utilizes /etc/httpd.conf for security settings allowing 58 Utilizes /etc/httpd.conf for security settings allowing
50 ip address filtering and basic authentication on a per url basis. 59 ip address filtering and basic authentication on a per url basis.
51 60
61config CONFIG_FEATURE_HTTPD_CGI
62 bool " Enable support Common Gateway Interface"
63 default n
64 depends on CONFIG_HTTPD
65 help
66 Disable this for do very small module
67
68config CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
69 bool " Enable support reload global config file after hup signaled"
70 default n
71 depends on CONFIG_HTTPD
72 help
73 Disable this for do very small module
74
75config CONFIG_FEATURE_HTTPD_SETUID
76 bool " Enable support -u user option"
77 default n
78 depends on CONFIG_HTTPD
79 help
80 Require for drop privilegies after bind() to privilegies port
81
82config CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
83 bool " Enable support load from config file mime types"
84 default n
85 depends on CONFIG_HTTPD
86 help
87 After set this you can adding or change mime types from file
88 suffixes in config files
89
90config CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
91 bool " Enable support set eviroment REMOTE_PORT"
92 default n
93 depends on CONFIG_FEATURE_HTTPD_CGI
94 help
95 After set this your CGI script can know own remote port connecting
96
97config CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
98 bool " Enable support nonstandart httpd feature set CGI_var=value"
99 default n
100 depends on CONFIG_FEATURE_HTTPD_CGI
101 help
102 After set this your CGI script can have trivial parse getted vars
103
104config CONFIG_FEATURE_HTTPD_DECODE_URL_STR
105 bool " Support nonstandart httpd feature decode URL to stdout"
106 default n
107 depends on CONFIG_HTTPD
108 help
109 After set this your can decode URL from -d argument to stdout,
110 example -d "Hello%20World" as "Hello World"
111
112config CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
113 bool " Support nonstandart httpd feature encode argument to URL"
114 default n
115 depends on CONFIG_HTTPD
116 help
117 After set this your can encode from -d argument to stdout as URL,
118 example -e "<Hello World>" as "%3CHello%20World%3E"
119
120
52config CONFIG_IFCONFIG 121config CONFIG_IFCONFIG
53 bool "ifconfig" 122 bool "ifconfig"
54 default n 123 default n
@@ -132,6 +201,48 @@ config CONFIG_FEATURE_IFUPDOWN_MAPPING
132 This enables support for the "mapping" stanza, unless you have 201 This enables support for the "mapping" stanza, unless you have
133 a weird network setup you dont need it. 202 a weird network setup you dont need it.
134 203
204config CONFIG_INETD
205 bool "inetd"
206 default n
207 help
208 Internet superserver daemon
209
210config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
211 bool " Support echo service"
212 default y
213 depends on CONFIG_INETD
214 help
215 Echo received data internal inetd service
216
217config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
218 bool " Support discard service"
219 default y
220 depends on CONFIG_INETD
221 help
222 Internet /dev/null internal inetd service
223
224config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
225 bool " Support time service"
226 default y
227 depends on CONFIG_INETD
228 help
229 Return 32 bit time since 1900 internal inetd service
230
231config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
232 bool " Support daytime service"
233 default y
234 depends on CONFIG_INETD
235 help
236 Return human-readable time internal inetd service
237
238config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
239 bool " Support chargen service"
240 default y
241 depends on CONFIG_INETD
242 help
243 Familiar character generator internal inetd service
244
245
135config CONFIG_IP 246config CONFIG_IP
136 bool "ip" 247 bool "ip"
137 default n 248 default n
@@ -316,7 +427,7 @@ config CONFIG_TELNETD
316 Please submit a patch to add help text for this item. 427 Please submit a patch to add help text for this item.
317 428
318config CONFIG_FEATURE_TELNETD_INETD 429config CONFIG_FEATURE_TELNETD_INETD
319 bool " Use inetd" 430 bool " Support call from inetd only"
320 default n 431 default n
321 depends on CONFIG_TELNETD 432 depends on CONFIG_TELNETD
322 help 433 help
diff --git a/networking/Makefile.in b/networking/Makefile.in
index 12fc8ddbd..95709ee42 100644
--- a/networking/Makefile.in
+++ b/networking/Makefile.in
@@ -30,6 +30,7 @@ NETWORKING-$(CONFIG_HOSTNAME) += hostname.o
30NETWORKING-$(CONFIG_HTTPD) += httpd.o 30NETWORKING-$(CONFIG_HTTPD) += httpd.o
31NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o 31NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o
32NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o 32NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o
33NETWORKING-$(CONFIG_INETD) += inetd.o
33NETWORKING-$(CONFIG_IP) += ip.o 34NETWORKING-$(CONFIG_IP) += ip.o
34NETWORKING-$(CONFIG_IPCALC) += ipcalc.o 35NETWORKING-$(CONFIG_IPCALC) += ipcalc.o
35NETWORKING-$(CONFIG_IPADDR) += ipaddr.o 36NETWORKING-$(CONFIG_IPADDR) += ipaddr.o
diff --git a/networking/httpd.c b/networking/httpd.c
index bceb89ba3..e61518c20 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -1,8 +1,10 @@
1/* 1/*
2 * httpd implementation for busybox 2 * httpd implementation for busybox
3 * 3 *
4 * Copyright (C) 2002 Glenn Engel <glenne@engel.org> 4 * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
5 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
5 * 6 *
7 * simplify patch stolen from libbb without using strdup
6 * 8 *
7 * This program is free software; you can redistribute it and/or modify 9 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by 10 * it under the terms of the GNU General Public License as published by
@@ -20,100 +22,252 @@
20 * 22 *
21 ***************************************************************************** 23 *****************************************************************************
22 * 24 *
23 * Typical usage: 25 * Typical usage:
24 * cd /var/www 26 * for non root user
25 * httpd 27 * httpd -p 8080 -h $HOME/public_html
26 * This is equivalent to 28 * or for daemon start from rc script with uid=0:
27 * cd /var/www 29 * httpd -u www
28 * httpd -p 80 -c /etc/httpd.conf -r "Web Server Authentication" 30 * This is equivalent if www user have uid=80 to
31 * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
32 *
29 * 33 *
30 * When a url contains "cgi-bin" it is assumed to be a cgi script. The 34 * When a url contains "cgi-bin" it is assumed to be a cgi script. The
31 * server changes directory to the location of the script and executes it 35 * server changes directory to the location of the script and executes it
32 * after setting QUERY_STRING and other environment variables. If url args 36 * after setting QUERY_STRING and other environment variables. If url args
33 * are included in the url or as a post, the args are placed into decoded 37 * are included in the url or as a post, the args are placed into decoded
34 * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set 38 * environment variables. e.g. /cgi-bin/setup?foo=Hello%20World will set
35 * the $CGI_foo environment variable to "Hello World". 39 * the $CGI_foo environment variable to "Hello World" while
40 * CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV enabled.
36 * 41 *
37 * The server can also be invoked as a url arg decoder and html text encoder 42 * The server can also be invoked as a url arg decoder and html text encoder
38 * as follows: 43 * as follows:
39 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" 44 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
40 * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62" 45 * bar=`httpd -e "<Hello World>"` # encode as "%3CHello%20World%3E"
41 * 46 *
42 * httpd.conf has the following format: 47 * httpd.conf has the following format:
43
44ip:10.10. # Allow any address that begins with 10.10.
45ip:172.20. # Allow 172.20.x.x
46ip:127.0.0.1 # Allow local loopback connections
47/cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin
48/:admin:setup # Require user admin, pwd setup on urls starting with /
49 48
50 * 49A:172.20. # Allow any address that begins with 172.20
51 * To open up the server: 50A:10.10. # Allow any address that begins with 10.10.
52 * ip:* # Allow any IP address 51A:10.10 # Allow any address that previous set and 10.100-109.X.X
53 * /:* # no password required for urls starting with / (all) 52A:127.0.0.1 # Allow local loopback connections
54 * 53D:* # Deny from other IP connections
55 * Processing of the file stops on the first sucessful match. If the file 54/cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
56 * is not found, the server is assumed to be wide open. 55/adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
57 * 56/adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
58 ***************************************************************************** 57.au:audio/basic # additional mime type for audio.au files
59 * 58
60 * Desired enhancements: 59A shortes path and D:from[^*] automaticaly sorting to top.
61 * cache httpd.conf 60All longest path can`t reset user:password if shorted protect setted.
62 * support tinylogin 61
63 * 62A/D may be as a/d or allow/deny - first char case unsensitive parsed only.
63
64Each subdir can have config file.
65For protect as user:pass current subdir and subpathes set from subdir config:
66/:user:pass
67if not, other subpathes for give effect must have path from httpd root
68/current_subdir_path_from_httpd_root/subpath:user:pass
69
70The Deny/Allow IP logic:
71
72 1. Allow all:
73The config don`t set D: lines
74
75 2. Allow from setted only:
76see the begin format example
77
78 3. Set deny, allow from other:
79D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
80D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
81A:* # allow from other, this line not strongly require
82
83 A global and subdirs config merging logic:
84allow rules reducing, deny lines strongled.
85 The algorithm combinations:
86
87 4. If current config have
88A:from
89D:*
90 subdir config A: lines skiping, D:from - moving top
91
92 5. If current config have
93D:from
94A:*
95 result config:
96D:from current
97D:from subdir
98A:from subdir
99 and seting D:* if subdir config have this
100
101 If -c don`t setted, used httpd root config, else httpd root config skiped.
102 Exited with fault if can`t open start config.
103 For set wide open server, use -c /dev/null ;=)
64 */ 104 */
105
65#include <stdio.h> 106#include <stdio.h>
66#include <ctype.h> /* for isspace */ 107#include <ctype.h> /* for isspace */
67#include <stdarg.h> /* for varargs */ 108#include <string.h>
68#include <string.h> /* for strerror */
69#include <stdlib.h> /* for malloc */ 109#include <stdlib.h> /* for malloc */
70#include <time.h> 110#include <time.h>
71#include <errno.h>
72#include <unistd.h> /* for close */ 111#include <unistd.h> /* for close */
73#include <signal.h> 112#include <signal.h>
74#include <sys/types.h> 113#include <sys/types.h>
75#include <sys/socket.h> /* for connect and socket*/ 114#include <sys/socket.h> /* for connect and socket*/
76#include <netinet/in.h> /* for sockaddr_in */ 115#include <netinet/in.h> /* for sockaddr_in */
77#include <sys/types.h> 116#include <sys/time.h>
78#include <sys/stat.h> 117#include <sys/stat.h>
79#include <sys/wait.h> 118#include <sys/wait.h>
80#include <fcntl.h> 119#include <fcntl.h> /* for open modes */
120#include "busybox.h"
121
81 122
82static const char httpdVersion[] = "busybox httpd/1.13 3-Jan-2003"; 123static const char httpdVersion[] = "busybox httpd/1.20 31-Jan-2003";
124static const char default_patch_httpd_conf[] = "/etc";
125static const char httpd_conf[] = "httpd.conf";
126static const char home[] = "/www";
83 127
84// #define DEBUG 1
85#ifndef HTTPD_STANDALONE
86#include <config.h>
87#include <busybox.h>
88// Note: xfuncs are not used because we want the server to keep running 128// Note: xfuncs are not used because we want the server to keep running
89// if something bad happens due to a malformed user request. 129// if something bad happens due to a malformed user request.
90// As a result, all memory allocation is checked rigorously 130// As a result, all memory allocation after daemonize
91#else 131// is checked rigorously
92/* standalone */ 132
133//#define DEBUG 1
134
135/* Configure options, disabled by default as nonstandart httpd feature */
136//#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
137//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
138//#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
139
140/* disabled as not necessary feature */
141//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
142//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
143//#define CONFIG_FEATURE_HTTPD_SETUID
144//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
145
146/* If seted this you can use this server from internet superserver only */
147//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
148
149/* You can use this server as standalone, require libbb.a for linking */
150//#define HTTPD_STANDALONE
151
152/* Config options, disable this for do very small module */
153//#define CONFIG_FEATURE_HTTPD_CGI
154//#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
155
156#ifdef HTTPD_STANDALONE
157/* standalone, enable all features */
158#undef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
159/* unset config option for remove warning as redefined */
160#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
161#undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
162#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
163#undef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
164#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
165#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
166#undef CONFIG_FEATURE_HTTPD_CGI
167#undef CONFIG_FEATURE_HTTPD_SETUID
168#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
169/* enable all features now */
93#define CONFIG_FEATURE_HTTPD_BASIC_AUTH 170#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
94void show_usage() 171#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
172#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
173#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
174#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
175#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
176#define CONFIG_FEATURE_HTTPD_CGI
177#define CONFIG_FEATURE_HTTPD_SETUID
178#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
179
180/* require from libbb.a for linking */
181const char *applet_name = "httpd";
182
183void show_usage(void)
95{ 184{
96 fprintf(stderr,"Usage: httpd [-p <port>] [-c configFile] [-d/-e <string>] [-r realm]\n"); 185 fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
186 "[-r realm] [-u user]\n", applet_name);
187 exit(1);
97} 188}
98#endif 189#endif
99 190
100/* minimal global vars for busybox */ 191#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
101#ifndef ENVSIZE 192#undef CONFIG_FEATURE_HTTPD_SETUID /* use inetd user.group config settings */
102#define ENVSIZE 50 193#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP /* so is not daemon */
194/* inetd set stderr to accepted socket and we can`t true see debug messages */
195#undef DEBUG
196#endif
197
198/* CGI environ size */
199#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
200#define ENVSIZE 50 /* set max 35 CGI_variable */
201#else
202#define ENVSIZE 15 /* minimal requires */
103#endif 203#endif
104int debugHttpd; 204
105static char **envp; 205#define MAX_POST_SIZE (64*1024) /* 64k. Its Small? May be ;) */
106static int envCount; 206
107static char *realm = "Web Server Authentication"; 207#define MAX_MEMORY_BUFF 8192 /* IO buffer */
108static char *configFile; 208
209typedef struct HT_ACCESS {
210 char *after_colon;
211 struct HT_ACCESS *next;
212 char before_colon[1]; /* really bigger, must last */
213} Htaccess;
214
215typedef struct
216{
217#ifdef CONFIG_FEATURE_HTTPD_CGI
218 char *envp[ENVSIZE+1];
219 int envCount;
220#endif
221 char buf[MAX_MEMORY_BUFF];
222
223#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
224 const char *realm;
225#endif
226 const char *configFile;
227
228 char rmt_ip[16]; /* for set env REMOTE_ADDR */
229 unsigned port; /* server initial port and for
230 set env REMOTE_PORT */
231
232 const char *found_mime_type;
233 off_t ContentLength; /* -1 - unknown */
234 time_t last_mod;
235#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
236 int accepted_socket;
237#define a_c_r config->accepted_socket
238#define a_c_w config->accepted_socket
239 int debugHttpd; /* if seted, don`t stay daemon */
240#else
241#define a_c_r 0
242#define a_c_w 1
243#endif
244 Htaccess *Httpd_conf_parsed;
245} HttpdConfig;
246
247static HttpdConfig *config;
248
249static const char request_GET[] = "GET"; /* size algorithic optimize */
109 250
110static const char* const suffixTable [] = { 251static const char* const suffixTable [] = {
252/* Warning: shorted equalent suffix in one line must be first */
111 ".htm.html", "text/html", 253 ".htm.html", "text/html",
112 ".jpg.jpeg", "image/jpeg", 254 ".jpg.jpeg", "image/jpeg",
113 ".gif", "image/gif", 255 ".gif", "image/gif",
114 ".png", "image/png", 256 ".png", "image/png",
115 ".txt.h.c.cc.cpp", "text/plain", 257 ".txt.h.c.cc.cpp", "text/plain",
116 0,0 258 ".css", "text/css",
259 ".wav", "audio/wav",
260 ".avi", "video/x-msvideo",
261 ".qt.mov", "video/quicktime",
262 ".mpe.mpeg", "video/mpeg",
263 ".mid.midi", "audio/midi",
264 ".mp3", "audio/mpeg",
265#if 0 /* unpopular */
266 ".au", "audio/basic",
267 ".pac", "application/x-ns-proxy-autoconfig",
268 ".vrml.wrl", "model/vrml",
269#endif
270 0, "application/octet-stream" /* default */
117 }; 271 };
118 272
119typedef enum 273typedef enum
@@ -121,9 +275,10 @@ typedef enum
121 HTTP_OK = 200, 275 HTTP_OK = 200,
122 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ 276 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
123 HTTP_NOT_FOUND = 404, 277 HTTP_NOT_FOUND = 404,
278 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
279 HTTP_BAD_REQUEST = 400, /* malformed syntax */
280 HTTP_FORBIDDEN = 403,
124 HTTP_INTERNAL_SERVER_ERROR = 500, 281 HTTP_INTERNAL_SERVER_ERROR = 500,
125 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
126 HTTP_BAD_REQUEST = 400, /* malformed syntax */
127#if 0 /* future use */ 282#if 0 /* future use */
128 HTTP_CONTINUE = 100, 283 HTTP_CONTINUE = 100,
129 HTTP_SWITCHING_PROTOCOLS = 101, 284 HTTP_SWITCHING_PROTOCOLS = 101,
@@ -136,7 +291,6 @@ typedef enum
136 HTTP_MOVED_TEMPORARILY = 302, 291 HTTP_MOVED_TEMPORARILY = 302,
137 HTTP_NOT_MODIFIED = 304, 292 HTTP_NOT_MODIFIED = 304,
138 HTTP_PAYMENT_REQUIRED = 402, 293 HTTP_PAYMENT_REQUIRED = 402,
139 HTTP_FORBIDDEN = 403,
140 HTTP_BAD_GATEWAY = 502, 294 HTTP_BAD_GATEWAY = 502,
141 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ 295 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
142 HTTP_RESPONSE_SETSIZE=0xffffffff 296 HTTP_RESPONSE_SETSIZE=0xffffffff
@@ -152,15 +306,16 @@ typedef struct
152 306
153static const HttpEnumString httpResponseNames[] = { 307static const HttpEnumString httpResponseNames[] = {
154 { HTTP_OK, "OK" }, 308 { HTTP_OK, "OK" },
155 { HTTP_NOT_IMPLEMENTED, "Not Implemented", 309 { HTTP_NOT_IMPLEMENTED, "Not Implemented",
156 "The requested method is not recognized by this server." }, 310 "The requested method is not recognized by this server." },
157 { HTTP_UNAUTHORIZED, "Unauthorized", "" }, 311 { HTTP_UNAUTHORIZED, "Unauthorized", "" },
158 { HTTP_NOT_FOUND, "Not Found", 312 { HTTP_NOT_FOUND, "Not Found",
159 "The requested URL was not found on this server." }, 313 "The requested URL was not found on this server." },
314 { HTTP_BAD_REQUEST, "Bad Request" ,
315 "Unsupported method." },
316 { HTTP_FORBIDDEN, "Forbidden", "" },
160 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error" 317 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error"
161 "Internal Server Error" }, 318 "Internal Server Error" },
162 { HTTP_BAD_REQUEST, "Bad Request" ,
163 "Unsupported method.\n" },
164#if 0 319#if 0
165 { HTTP_CREATED, "Created" }, 320 { HTTP_CREATED, "Created" },
166 { HTTP_ACCEPTED, "Accepted" }, 321 { HTTP_ACCEPTED, "Accepted" },
@@ -169,12 +324,247 @@ static const HttpEnumString httpResponseNames[] = {
169 { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, 324 { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
170 { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" }, 325 { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" },
171 { HTTP_NOT_MODIFIED, "Not Modified" }, 326 { HTTP_NOT_MODIFIED, "Not Modified" },
172 { HTTP_FORBIDDEN, "Forbidden", "" },
173 { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, 327 { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
174 { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, 328 { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
175#endif 329#endif
176}; 330};
177 331
332
333static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
334static const char Content_length[] = "Content-length:";
335
336
337/*
338 * sotring to:
339 * .ext:mime/type
340 * /path:user:pass
341 * /path/subdir:user:pass
342 * D:from
343 * A:from
344 * D:*
345 */
346static int conf_sort(const void *p1, const void *p2)
347{
348 const Htaccess *cl1 = *(const Htaccess **)p1;
349 const Htaccess *cl2 = *(const Htaccess **)p2;
350 char c1 = cl1->before_colon[0];
351 char c2 = cl2->before_colon[0];
352 int test;
353
354#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
355 /* .ext line up before other lines for simlify algorithm */
356 test = c2 == '.';
357 if(c1 == '.')
358 return -(!test);
359 if(test)
360 return test;
361#endif
362
363#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
364 test = c1 == '/';
365 /* /path line up before A/D lines for simlify algorithm */
366 if(test) {
367 if(c2 != '/')
368 return -test;
369 /* a shortes path with user:pass must be first */
370 return strlen(cl1->before_colon) - strlen(cl2->before_colon);
371 } else if(c2 == '/')
372 return !test;
373#endif
374
375 /* D:from must move top */
376 test = c2 == 'D' && cl2->after_colon[0] != 0;
377 if(c1 == 'D' && cl1->after_colon[0] != 0) {
378 return -(!test);
379 }
380 if(test)
381 return test;
382
383 /* next lines - A:from */
384 test = c2 == 'A' && cl2->after_colon[0] != 0;
385 if(c1 == 'A' && cl1->after_colon[0] != 0) {
386 return -(!test);
387 }
388 if(test)
389 return test;
390
391 /* end lines - D:* */
392 test = c2 == 'D' && cl2->after_colon[0] == 0;
393 if(c1 == 'D' && cl1->after_colon[0] == 0) {
394 return -(!test);
395 }
396#ifdef DEBUG
397 if(!test)
398 error_msg_and_die("sort: can`t found compares!");
399#endif
400 return test;
401}
402
403
404/* flag */
405#define FIRST_PARSE 0
406#define SUBDIR_PARSE 1
407#define SIGNALED_PARSE 2
408
409static void parse_conf(const char *path, int flag)
410{
411#define bc cur->before_colon[0]
412#define ac cur->after_colon[0]
413 FILE *f;
414 Htaccess *prev;
415 Htaccess *cur;
416 const char *cf = config->configFile;
417 char buf[80];
418 char *p0 = NULL;
419 int deny_all = 0; /* default A:* */
420 int n = 0; /* count config lines */
421
422 if(flag == SUBDIR_PARSE || cf == NULL) {
423 cf = p0 = alloca(strlen(path) + sizeof(httpd_conf) + 2);
424 if(p0 == NULL) {
425 if(flag == FIRST_PARSE)
426 error_msg_and_die(memory_exhausted);
427 return;
428 }
429 sprintf(p0, "%s/%s", path, httpd_conf);
430 }
431
432 while((f = fopen(cf, "r")) == NULL) {
433 if(flag != FIRST_PARSE)
434 return; /* subdir config not found */
435 if(p0 == NULL) /* if -c option gived */
436 perror_msg_and_die("%s", cf);
437 p0 = NULL;
438 cf = httpd_conf; /* set -c ./httpd_conf */
439 }
440
441 prev = config->Httpd_conf_parsed;
442 if(flag != SUBDIR_PARSE) {
443 /* free previous setuped */
444 while( prev ) {
445 cur = prev;
446 prev = cur->next;
447 free(cur);
448 }
449 config->Httpd_conf_parsed = prev; /* eq NULL */
450 } else {
451 /* parse previous IP logic for merge */
452 for(cur = prev; cur; cur = cur->next) {
453 if(bc == 'D' && ac == 0)
454 deny_all++;
455 n++;
456 /* find last for set prev->next of merging */
457 if(cur != prev)
458 prev = prev->next;
459 }
460 }
461
462 /* This could stand some work */
463 while ( (p0 = fgets(buf, 80, f)) != NULL) {
464 char *p;
465 char *colon;
466
467 for(p = colon = p0; *p; p++) {
468 if(*p == '#') {
469 *p = 0;
470 break;
471 }
472 if(isspace(*p)) {
473 if(p != p0) {
474 *p = 0;
475 break;
476 }
477 p0++;
478 }
479 else if(*p == ':' && colon <= p0)
480 colon = p;
481 }
482
483 /* test for empty or strange line */
484 if (colon <= p0 || colon[1] == 0)
485 continue;
486 if(colon[1] == '*')
487 colon[1] = 0; /* Allow all */
488 if(*p0 == 'a')
489 *p0 = 'A';
490 if(*p0 == 'd')
491 *p0 = 'D';
492 if(*p0 != 'A' && *p0 != 'D'
493#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
494 && *p0 != '/'
495#endif
496#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
497 && *p0 != '.'
498#endif
499 )
500 continue;
501#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
502 if(*p0 == '/' && colon[1] == 0) {
503 /* skip /path:* */
504 continue;
505 }
506#endif
507
508 if(*p0 == 'A' || *p0 == 'D') {
509 if(colon[1] == 0) {
510 if(*p0 == 'A' || deny_all != 0)
511 continue; /* skip default A:* or double D:* */
512 }
513 if(deny_all != 0 && *p0 == 'A')
514 continue; // if previous setted rule D:* skip all subdir A:
515 }
516
517 /* storing current config line */
518 cur = calloc(1, sizeof(Htaccess) + (p-p0));
519 if(cur) {
520 if(*(colon-1) == '/' && (colon-1) != p0)
521 colon[-1] = 0; // remove last / from /path/
522 cur->after_colon = strcpy(cur->before_colon, p0);
523 cur->after_colon += (colon-p0);
524 *cur->after_colon++ = 0;
525
526 if(prev == NULL) {
527 /* first line */
528 config->Httpd_conf_parsed = prev = cur;
529 } else {
530 prev->next = cur;
531 prev = cur;
532 }
533 n++;
534 }
535 }
536 fclose(f);
537
538 if(n > 1) {
539 /* sorting conf lines */
540 Htaccess ** pcur; /* array for qsort */
541
542 prev = config->Httpd_conf_parsed;
543 pcur = alloca((n + 1) * sizeof(Htaccess *));
544 if(pcur == NULL) {
545 if(flag == FIRST_PARSE)
546 error_msg_and_die(memory_exhausted);
547 return;
548 }
549 n = 0;
550 for(cur = prev; cur; cur = cur->next)
551 pcur[n++] = cur;
552 pcur[n] = NULL;
553
554 qsort(pcur, n, sizeof(Htaccess *), conf_sort);
555
556 /* storing sorted config */
557 config->Httpd_conf_parsed = *pcur;
558 for(cur = *pcur; cur; cur = cur->next) {
559#ifdef DEBUG
560 error_msg("%s: %s:%s", cf, cur->before_colon, cur->after_colon);
561#endif
562 cur->next = *++pcur;
563 }
564 }
565}
566
567#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
178/**************************************************************************** 568/****************************************************************************
179 * 569 *
180 > $Function: encodeString() 570 > $Function: encodeString()
@@ -197,20 +587,22 @@ static char *encodeString(const char *string)
197 /* take the simple route and encode everything */ 587 /* take the simple route and encode everything */
198 /* could possibly scan once to get length. */ 588 /* could possibly scan once to get length. */
199 int len = strlen(string); 589 int len = strlen(string);
200 char *out = (char*)malloc(len*5 +1); 590 char *out = malloc(len*5 +1);
201 char *p=out; 591 char *p=out;
202 char ch; 592 char ch;
593
203 if (!out) return ""; 594 if (!out) return "";
204 while ((ch = *string++)) 595 while ((ch = *string++)) {
205 {
206 // very simple check for what to encode 596 // very simple check for what to encode
207 if (isalnum(ch)) *p++ = ch; 597 if (isalnum(ch)) *p++ = ch;
208 else p += sprintf(p,"&#%d", (unsigned char) ch); 598 else p += sprintf(p, "&#%d", (unsigned char) ch);
209 } 599 }
210 *p=0; 600 *p=0;
211 return out; 601 return out;
212} 602}
603#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
213 604
605#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
214/**************************************************************************** 606/****************************************************************************
215 * 607 *
216 > $Function: decodeString() 608 > $Function: decodeString()
@@ -236,12 +628,11 @@ static char *decodeString(char *string)
236 char *ptr = string; 628 char *ptr = string;
237 while (*ptr) 629 while (*ptr)
238 { 630 {
239 if (*ptr == '+') { *string++ = ' '; ptr++; } 631 if (*ptr == '+') { *string++ = ' '; ptr++; }
240 else if (*ptr != '%') *string++ = *ptr++; 632 else if (*ptr != '%') *string++ = *ptr++;
241 else 633 else {
242 {
243 unsigned int value; 634 unsigned int value;
244 sscanf(ptr+1,"%2X",&value); 635 sscanf(ptr+1, "%2X", &value);
245 *string++ = value; 636 *string++ = value;
246 ptr += 3; 637 ptr += 3;
247 } 638 }
@@ -249,8 +640,10 @@ static char *decodeString(char *string)
249 *string = '\0'; 640 *string = '\0';
250 return orig; 641 return orig;
251} 642}
643#endif /* CONFIG_FEATURE_HTTPD_DECODE_URL_STR */
252 644
253 645
646#ifdef CONFIG_FEATURE_HTTPD_CGI
254/**************************************************************************** 647/****************************************************************************
255 * 648 *
256 > $Function: addEnv() 649 > $Function: addEnv()
@@ -260,28 +653,47 @@ static char *decodeString(char *string)
260 * environment settings passed to the cgi execution script. 653 * environment settings passed to the cgi execution script.
261 * 654 *
262 * $Parameters: 655 * $Parameters:
263 * (char *) name . . . The environment variable name. 656 * (char *) name_before_underline - The first part environment variable name.
264 * (char *) value . . The value to which the env variable is set. 657 * (char *) name_after_underline - The second part environment variable name.
658 * (char *) value . . The value to which the env variable is set.
265 * 659 *
266 * $Return: (void) 660 * $Return: (void)
267 * 661 *
268 * $Errors: Silently returns if the env runs out of space to hold the new item 662 * $Errors: Silently returns if the env runs out of space to hold the new item
269 * 663 *
270 ****************************************************************************/ 664 ****************************************************************************/
271static void addEnv(const char *name, const char *value) 665static void addEnv(const char *name_before_underline,
666 const char *name_after_underline, const char *value)
272{ 667{
273 char *s; 668 char *s;
274 if (envCount >= ENVSIZE) return; 669
275 if (!value) value = ""; 670 if (config->envCount >= ENVSIZE)
276 s=(char*)malloc(strlen(name)+strlen(value)+2); 671 return;
277 if (s) 672 if (!value)
278 { 673 value = "";
279 sprintf(s,"%s=%s",name, value); 674 s = malloc(strlen(name_before_underline) + strlen(name_after_underline) +
280 envp[envCount++]=s; 675 strlen(value) + 3);
281 envp[envCount]=0; 676 if (s) {
677 const char *underline = *name_after_underline ? "_" : "";
678
679 sprintf(s,"%s%s%s=%s", name_before_underline, underline,
680 name_after_underline, value);
681 config->envp[config->envCount++] = s;
682 config->envp[config->envCount] = 0;
282 } 683 }
283} 684}
284 685
686/* set environs SERVER_PORT and REMOTE_PORT */
687static void addEnvPort(const char *port_name)
688{
689 char buf[16];
690
691 sprintf(buf, "%u", config->port);
692 addEnv(port_name, "PORT", buf);
693}
694#endif /* CONFIG_FEATURE_HTTPD_CGI */
695
696#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
285/**************************************************************************** 697/****************************************************************************
286 * 698 *
287 > $Function: addEnvCgi 699 > $Function: addEnvCgi
@@ -289,7 +701,8 @@ static void addEnv(const char *name, const char *value)
289 * $Description: Create environment variables given a URL encoded arg list. 701 * $Description: Create environment variables given a URL encoded arg list.
290 * For each variable setting the URL encoded arg list, create a corresponding 702 * For each variable setting the URL encoded arg list, create a corresponding
291 * environment variable. URL encoded arguments have the form 703 * environment variable. URL encoded arguments have the form
292 * name1=value1&name2=value2&name3=value3 704 * name1=value1&name2=value2&name3=&ignores
705 * from this example, name3 set empty value, tail without '=' skiping
293 * 706 *
294 * $Parameters: 707 * $Parameters:
295 * (char *) pargs . . . . A pointer to the URL encoded arguments. 708 * (char *) pargs . . . . A pointer to the URL encoded arguments.
@@ -302,65 +715,29 @@ static void addEnv(const char *name, const char *value)
302static void addEnvCgi(const char *pargs) 715static void addEnvCgi(const char *pargs)
303{ 716{
304 char *args; 717 char *args;
718 char *memargs;
305 if (pargs==0) return; 719 if (pargs==0) return;
306 720
307 /* args are a list of name=value&name2=value2 sequences */ 721 /* args are a list of name=value&name2=value2 sequences */
308 args = strdup(pargs); 722 memargs = args = strdup(pargs);
309 while (args && *args) 723 while (args && *args) {
310 { 724 const char *name = args;
311 char *sep; 725 char *value = strchr(args, '=');
312 char *name=args; 726
313 char *value=strchr(args,'='); 727 if (!value) /* &XXX without '=' */
314 char *cginame; 728 break;
315 if (!value) break; 729 *value++ = 0;
316 *value++=0; 730 args = strchr(value, '&');
317 sep=strchr(value,'&'); 731 if (args)
318 if (sep) 732 *args++ = 0;
319 { 733 addEnv("CGI", name, decodeString(value));
320 *sep=0;
321 args=sep+1;
322 }
323 else
324 {
325 sep = value + strlen(value);
326 args = 0; /* no more */
327 }
328 cginame=(char*)malloc(strlen(decodeString(name))+5);
329 if (!cginame) break;
330 sprintf(cginame,"CGI_%s",name);
331 addEnv(cginame,decodeString(value));
332 free(cginame);
333 } 734 }
735 free(memargs);
334} 736}
737#endif /* CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV */
335 738
336#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
337static const unsigned char base64ToBin[] = {
338 255 /* ' ' */, 255 /* '!' */, 255 /* '"' */, 255 /* '#' */,
339 1 /* '$' */, 255 /* '%' */, 255 /* '&' */, 255 /* ''' */,
340 255 /* '(' */, 255 /* ')' */, 255 /* '*' */, 62 /* '+' */,
341 255 /* ',' */, 255 /* '-' */, 255 /* '.' */, 63 /* '/' */,
342 52 /* '0' */, 53 /* '1' */, 54 /* '2' */, 55 /* '3' */,
343 56 /* '4' */, 57 /* '5' */, 58 /* '6' */, 59 /* '7' */,
344 60 /* '8' */, 61 /* '9' */, 255 /* ':' */, 255 /* ';' */,
345 255 /* '<' */, 00 /* '=' */, 255 /* '>' */, 255 /* '?' */,
346 255 /* '@' */, 00 /* 'A' */, 01 /* 'B' */, 02 /* 'C' */,
347 03 /* 'D' */, 04 /* 'E' */, 05 /* 'F' */, 06 /* 'G' */,
348 7 /* 'H' */, 8 /* 'I' */, 9 /* 'J' */, 10 /* 'K' */,
349 11 /* 'L' */, 12 /* 'M' */, 13 /* 'N' */, 14 /* 'O' */,
350 15 /* 'P' */, 16 /* 'Q' */, 17 /* 'R' */, 18 /* 'S' */,
351 19 /* 'T' */, 20 /* 'U' */, 21 /* 'V' */, 22 /* 'W' */,
352 23 /* 'X' */, 24 /* 'Y' */, 25 /* 'Z' */, 255 /* '[' */,
353 255 /* '\' */, 255 /* ']' */, 255 /* '^' */, 255 /* '_' */,
354 255 /* '`' */, 26 /* 'a' */, 27 /* 'b' */, 28 /* 'c' */,
355 29 /* 'd' */, 30 /* 'e' */, 31 /* 'f' */, 32 /* 'g' */,
356 33 /* 'h' */, 34 /* 'i' */, 35 /* 'j' */, 36 /* 'k' */,
357 37 /* 'l' */, 38 /* 'm' */, 39 /* 'n' */, 40 /* 'o' */,
358 41 /* 'p' */, 42 /* 'q' */, 43 /* 'r' */, 44 /* 's' */,
359 45 /* 't' */, 46 /* 'u' */, 47 /* 'v' */, 48 /* 'w' */,
360 49 /* 'x' */, 50 /* 'y' */, 51 /* 'z' */, 255 /* '{' */,
361 255 /* '|' */, 255 /* '}' */, 255 /* '~' */, 255 /* '' */
362};
363 739
740#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
364/**************************************************************************** 741/****************************************************************************
365 * 742 *
366 > $Function: decodeBase64() 743 > $Function: decodeBase64()
@@ -370,186 +747,96 @@ static const unsigned char base64ToBin[] = {
370 * Since the decode always results in a shorter size than the input, it is 747 * Since the decode always results in a shorter size than the input, it is
371 * OK to pass the input arg as an output arg. 748 * OK to pass the input arg as an output arg.
372 * 749 *
373 * $Parameters: 750 * $Parameter:
374 * (void *) outData. . . Where to place the decoded data. 751 * (char *) Data . . . . A pointer to a base64 encoded string.
375 * (size_t) outDataLen . The length of the output data string. 752 * Where to place the decoded data.
376 * (void *) inData . . . A pointer to a base64 encoded string.
377 * (size_t) inDataLen . The length of the input data string.
378 * 753 *
379 * $Return: (char *) . . . . A pointer to the decoded string (same as input). 754 * $Return: void
380 * 755 *
381 * $Errors: None 756 * $Errors: None
382 * 757 *
383 ****************************************************************************/ 758 ****************************************************************************/
384static size_t decodeBase64(void *outData, size_t outDataLen, 759static void decodeBase64(char *Data)
385 void *inData, size_t inDataLen)
386{ 760{
387 int i = 0; 761 int i = 0;
388 unsigned char *in = inData; 762 static const char base64ToBin[] =
389 unsigned char *out = outData; 763 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
764
765 const unsigned char *in = Data;
766 // The decoded size will be at most 3/4 the size of the encoded
390 unsigned long ch = 0; 767 unsigned long ch = 0;
391 while (inDataLen && outDataLen) 768
392 { 769 while (*in) {
393 unsigned char conv = 0; 770 unsigned char conv = 0;
394 unsigned char newch; 771
395 772 while (*in) {
396 while (inDataLen) 773 const char *p64;
397 { 774
398 inDataLen--; 775 p64 = strchr(base64ToBin, *in++);
399 newch = *in++; 776 if(p64 == NULL)
400 if ((newch < '0') || (newch > 'z')) continue; 777 continue;
401 conv = base64ToBin[newch - 32]; 778 conv = (p64 - base64ToBin);
402 if (conv == 255) continue;
403 break; 779 break;
404 } 780 }
405 ch = (ch << 6) | conv; 781 ch = (ch << 6) | conv;
406 i++; 782 i++;
407 if (i== 4) 783 if (i== 4) {
408 { 784 *Data++ = (char) (ch >> 16);
409 if (outDataLen >= 3) 785 *Data++ = (char) (ch >> 8);
410 { 786 *Data++ = (char) ch;
411 *(out++) = (unsigned char) (ch >> 16);
412 *(out++) = (unsigned char) (ch >> 8);
413 *(out++) = (unsigned char) ch;
414 outDataLen-=3;
415 }
416
417 i = 0; 787 i = 0;
418 } 788 }
419
420 if ((inDataLen == 0) && (i != 0))
421 {
422 /* error - non multiple of 4 chars on input */
423 break;
424 }
425
426 } 789 }
427 790 *Data = 0;
428 /* return the actual number of chars in output array */
429 return out-(unsigned char*) outData;
430} 791}
431#endif 792#endif
432 793
433/****************************************************************************
434 *
435 > $Function: perror_and_exit()
436 *
437 > $Description: A helper function to print an error and exit.
438 *
439 * $Parameters:
440 * (const char *) msg . . . A 'context' message to include.
441 *
442 * $Return: None
443 *
444 * $Errors: None
445 *
446 ****************************************************************************/
447static void perror_exit(const char *msg)
448{
449 perror(msg);
450 exit(1);
451}
452
453
454/****************************************************************************
455 *
456 > $Function: strncmpi()
457 *
458 * $Description: compare two strings without regard to case.
459 *
460 * $Parameters:
461 * (char *) a . . . . . The first string.
462 * (char *) b . . . . . The second string.
463 * (int) n . . . . . . The number of chars to compare.
464 *
465 * $Return: (int) . . . . . . 0 if strings equal. 1 if a>b, -1 if b < a.
466 *
467 * $Errors: None
468 *
469 ****************************************************************************/
470#define __toupper(c) ((('a' <= (c))&&((c) <= 'z')) ? ((c) - 'a' + 'A') : (c))
471#define __tolower(c) ((('A' <= (c))&&((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
472static int strncmpi(const char *a, const char *b,int n)
473{
474 char a1,b1;
475 a1 = b1 = 0;
476
477 while(n-- && ((a1 = *a++) != '\0') && ((b1 = *b++) != '\0'))
478 {
479 if(a1 == b1) continue; /* No need to convert */
480 a1 = __tolower(a1);
481 b1 = __tolower(b1);
482 if(a1 != b1) break; /* No match, abort */
483 }
484 if (n>=0)
485 {
486 if(a1 > b1) return 1;
487 if(a1 < b1) return -1;
488 }
489 return 0;
490}
491 794
795#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
492/**************************************************************************** 796/****************************************************************************
493 * 797 *
494 > $Function: openServer() 798 > $Function: openServer()
495 * 799 *
496 * $Description: create a listen server socket on the designated port. 800 * $Description: create a listen server socket on the designated port.
497 * 801 *
498 * $Parameters:
499 * (int) port . . . The port to listen on for connections.
500 *
501 * $Return: (int) . . . A connection socket. -1 for errors. 802 * $Return: (int) . . . A connection socket. -1 for errors.
502 * 803 *
503 * $Errors: None 804 * $Errors: None
504 * 805 *
505 ****************************************************************************/ 806 ****************************************************************************/
506static int openServer(int port) 807static int openServer(void)
507{ 808{
508 struct sockaddr_in lsocket; 809 struct sockaddr_in lsocket;
509 int fd; 810 int fd;
510 811
511 /* create the socket right now */ 812 /* create the socket right now */
512 /* inet_addr() returns a value that is already in network order */ 813 /* inet_addr() returns a value that is already in network order */
513 memset(&lsocket, 0, sizeof(lsocket)); 814 memset(&lsocket, 0, sizeof(lsocket));
514 lsocket.sin_family = AF_INET; 815 lsocket.sin_family = AF_INET;
515 lsocket.sin_addr.s_addr = INADDR_ANY; 816 lsocket.sin_addr.s_addr = INADDR_ANY;
516 lsocket.sin_port = htons(port) ; 817 lsocket.sin_port = htons(config->port) ;
517 fd = socket(AF_INET, SOCK_STREAM, 0); 818 fd = socket(AF_INET, SOCK_STREAM, 0);
518 if (fd >= 0) 819 if (fd >= 0) {
519 {
520 /* tell the OS it's OK to reuse a previous address even though */ 820 /* tell the OS it's OK to reuse a previous address even though */
521 /* it may still be in a close down state. Allows bind to succeed. */ 821 /* it may still be in a close down state. Allows bind to succeed. */
522 int one = 1; 822 int on = 1;
523#ifdef SO_REUSEPORT 823#ifdef SO_REUSEPORT
524 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char*)&one, sizeof(one)) ; 824 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
525#else 825#else
526 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) ; 826 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
527#endif 827#endif
528 if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) 828 if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) {
529 {
530 listen(fd, 9); 829 listen(fd, 9);
531 signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */ 830 signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */
831 } else {
832 perror_msg_and_die("bind");
532 } 833 }
533 else 834 } else {
534 { 835 perror_msg_and_die("create socket");
535 perror("failure to bind to server port"); 836 }
536 shutdown(fd,0);
537 close(fd);
538 fd = -1;
539 }
540 }
541 else
542 {
543 fprintf(stderr,"httpd: unable to create socket \n");
544 }
545 return fd; 837 return fd;
546} 838}
547 839#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
548static int sendBuf(int s, char *buf, int len)
549{
550 if (len == -1) len = strlen(buf);
551 return send(s, buf, len, 0);
552}
553 840
554/**************************************************************************** 841/****************************************************************************
555 * 842 *
@@ -558,84 +845,65 @@ static int sendBuf(int s, char *buf, int len)
558 * $Description: Create and send HTTP response headers. 845 * $Description: Create and send HTTP response headers.
559 * The arguments are combined and sent as one write operation. Note that 846 * The arguments are combined and sent as one write operation. Note that
560 * IE will puke big-time if the headers are not sent in one packet and the 847 * IE will puke big-time if the headers are not sent in one packet and the
561 * second packet is delayed for any reason. If contentType is null the 848 * second packet is delayed for any reason.
562 * content type is assumed to be text/html
563 * 849 *
564 * $Parameters: 850 * $Parameter:
565 * (int) s . . . The http socket.
566 * (HttpResponseNum) responseNum . . . The result code to send. 851 * (HttpResponseNum) responseNum . . . The result code to send.
567 * (const char *) contentType . . . . A string indicating the type.
568 * (int) contentLength . . . . . . . . Content length. -1 if unknown.
569 * (time_t) expire . . . . . . . . . . Expiration time (secs since 1970)
570 * 852 *
571 * $Return: (int) . . . . Always 0 853 * $Return: (int) . . . . writing errors
572 *
573 * $Errors: None
574 * 854 *
575 ****************************************************************************/ 855 ****************************************************************************/
576static int sendHeaders(int s, HttpResponseNum responseNum , 856static int sendHeaders(HttpResponseNum responseNum)
577 const char *contentType,
578 int contentLength, time_t expire)
579{ 857{
580 char buf[1200]; 858 char *buf = config->buf;
581 const char *responseString = ""; 859 const char *responseString = "";
582 const char *infoString = 0; 860 const char *infoString = 0;
583 unsigned int i; 861 unsigned int i;
584 time_t timer = time(0); 862 time_t timer = time(0);
585 char timeStr[80]; 863 char timeStr[80];
586 for (i=0; 864 int len;
587 i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) 865
588 { 866 for (i = 0;
589 if (httpResponseNames[i].type != responseNum) continue; 867 i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) {
590 responseString = httpResponseNames[i].name; 868 if (httpResponseNames[i].type == responseNum) {
591 infoString = httpResponseNames[i].info; 869 responseString = httpResponseNames[i].name;
592 break; 870 infoString = httpResponseNames[i].info;
871 break;
872 }
593 } 873 }
594 if (infoString || !contentType) 874 if (responseNum != HTTP_OK) {
595 { 875 config->found_mime_type = "text/html"; // error message is HTML
596 contentType = "text/html";
597 } 876 }
598
599 sprintf(buf, "HTTP/1.0 %d %s\nContent-type: %s\r\n",
600 responseNum, responseString, contentType);
601 877
602 /* emit the current date */ 878 /* emit the current date */
603 strftime(timeStr, sizeof(timeStr), 879 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
604 "%a, %d %b %Y %H:%M:%S GMT",gmtime(&timer)); 880 len = sprintf(buf,
605 sprintf(buf+strlen(buf), "Date: %s\r\n", timeStr); 881 "HTTP/1.0 %d %s\nContent-type: %s\r\n"
606 sprintf(buf+strlen(buf), "Connection: close\r\n"); 882 "Date: %s\r\nConnection: close\r\n",
607 if (expire) 883 responseNum, responseString, config->found_mime_type, timeStr);
608 {
609 strftime(timeStr, sizeof(timeStr),
610 "%a, %d %b %Y %H:%M:%S GMT",gmtime(&expire));
611 sprintf(buf+strlen(buf), "Expire: %s\r\n", timeStr);
612 }
613 884
614 if (responseNum == HTTP_UNAUTHORIZED) 885 if (responseNum == HTTP_UNAUTHORIZED) {
615 { 886 len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
616 sprintf(buf+strlen(buf), 887 config->realm);
617 "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm);
618 } 888 }
619 if (contentLength != -1) 889 if (config->ContentLength != -1) { /* file */
620 { 890 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
621 int len = strlen(buf); 891 len += sprintf(buf+len, "Last-Modified: %s\r\n%s %ld\r\n",
622 sprintf(buf+len,"Content-length: %d\r\n", contentLength); 892 timeStr, Content_length, config->ContentLength);
623 } 893 }
624 strcat(buf,"\r\n"); 894 strcat(buf, "\r\n");
625 if (infoString) 895 len += 2;
626 { 896 if (infoString) {
627 sprintf(buf+strlen(buf), 897 len += sprintf(buf+len,
628 "<HEAD><TITLE>%d %s</TITLE></HEAD>\n" 898 "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
629 "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n", 899 "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
630 responseNum, responseString, 900 responseNum, responseString,
631 responseNum, responseString, 901 responseNum, responseString, infoString);
632 infoString);
633 } 902 }
634#ifdef DEBUG 903#ifdef DEBUG
635 if (debugHttpd) fprintf(stderr,"Headers:'%s'", buf); 904 if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf);
636#endif 905#endif
637 sendBuf(s, buf,-1); 906 return full_write(a_c_w, buf, len);
638 return 0;
639} 907}
640 908
641/**************************************************************************** 909/****************************************************************************
@@ -647,30 +915,29 @@ static int sendHeaders(int s, HttpResponseNum responseNum ,
647 * Characters are read one at a time until an eol sequence is found. 915 * Characters are read one at a time until an eol sequence is found.
648 * 916 *
649 * $Parameters: 917 * $Parameters:
650 * (int) s . . . . . The socket fildes.
651 * (char *) buf . . Where to place the read result. 918 * (char *) buf . . Where to place the read result.
652 * (int) maxBuf . . Maximum number of chars to fit in buf.
653 * 919 *
654 * $Return: (int) . . . . number of characters read. -1 if error. 920 * $Return: (int) . . . . number of characters read. -1 if error.
655 * 921 *
656 ****************************************************************************/ 922 ****************************************************************************/
657static int getLine(int s, char *buf, int maxBuf) 923static int getLine(char *buf)
658{ 924{
659 int count = 0; 925 int count = 0;
660 while (recv(s, buf+count, 1, 0) == 1) 926
661 { 927 while (read(a_c_r, buf + count, 1) == 1) {
662 if (buf[count] == '\r') continue; 928 if (buf[count] == '\r') continue;
663 if (buf[count] == '\n') 929 if (buf[count] == '\n') {
664 {
665 buf[count] = 0; 930 buf[count] = 0;
666 return count; 931 return count;
667 } 932 }
668 count++; 933 if(count < (MAX_MEMORY_BUFF-1)) /* check owerflow */
934 count++;
669 } 935 }
670 if (count) return count; 936 if (count) return count;
671 else return -1; 937 else return -1;
672} 938}
673 939
940#ifdef CONFIG_FEATURE_HTTPD_CGI
674/**************************************************************************** 941/****************************************************************************
675 * 942 *
676 > $Function: sendCgi() 943 > $Function: sendCgi()
@@ -682,193 +949,215 @@ static int getLine(int s, char *buf, int maxBuf)
682 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). 949 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
683 * 950 *
684 * $Parameters: 951 * $Parameters:
685 * (int ) s . . . . . . . . The session socket.
686 * (const char *) url . . . The requested URL (with leading /). 952 * (const char *) url . . . The requested URL (with leading /).
687 * (const char *urlArgs). . Any URL arguments. 953 * (const char *urlArgs). . Any URL arguments.
688 * (const char *body) . . . POST body contents. 954 * (const char *body) . . . POST body contents.
689 * (int bodyLen) . . . . . Length of the post body. 955 * (int bodyLen) . . . . . Length of the post body.
690 956 * (const char *cookie) . . For set HTTP_COOKIE.
957
691 * 958 *
692 * $Return: (char *) . . . . A pointer to the decoded string (same as input). 959 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
693 * 960 *
694 * $Errors: None 961 * $Errors: None
695 * 962 *
696 ****************************************************************************/ 963 ****************************************************************************/
697static int sendCgi(int s, const char *url, 964static int sendCgi(const char *url,
698 const char *request, const char *urlArgs, 965 const char *request, const char *urlArgs,
699 const char *body, int bodyLen) 966 const char *body, int bodyLen, const char *cookie)
700{ 967{
701 int fromCgi[2]; /* pipe for reading data from CGI */ 968 int fromCgi[2]; /* pipe for reading data from CGI */
702 int toCgi[2]; /* pipe for sending data to CGI */ 969 int toCgi[2]; /* pipe for sending data to CGI */
703 970
704 char *argp[] = { 0, 0 }; 971 static char * argp[] = { 0, 0 };
705 int pid=0; 972 int pid = 0;
706 int inFd=inFd; 973 int inFd;
707 int outFd; 974 int outFd;
708 int firstLine=1; 975 int firstLine = 1;
709 976
710 do 977 do {
711 { 978 if (pipe(fromCgi) != 0) {
712 if (pipe(fromCgi) != 0)
713 {
714 break; 979 break;
715 } 980 }
716 if (pipe(toCgi) != 0) 981 if (pipe(toCgi) != 0) {
717 {
718 break; 982 break;
719 } 983 }
720 984
721 pid = fork(); 985 pid = fork();
722 if (pid < 0) 986 if (pid < 0) {
723 {
724 pid = 0; 987 pid = 0;
725 break;; 988 break;
726 } 989 }
727 990
728 if (!pid) 991 if (!pid) {
729 {
730 /* child process */ 992 /* child process */
731 char *script; 993 char *script;
732 char *directory; 994 char *purl = strdup( url );
733 inFd=toCgi[0]; 995 char realpath_buff[MAXPATHLEN];
734 outFd=fromCgi[1]; 996
997 if(purl == NULL)
998 _exit(242);
999
1000 inFd = toCgi[0];
1001 outFd = fromCgi[1];
735 1002
736 dup2(inFd, 0); // replace stdin with the pipe 1003 dup2(inFd, 0); // replace stdin with the pipe
737 dup2(outFd, 1); // replace stdout with the pipe 1004 dup2(outFd, 1); // replace stdout with the pipe
738 if (!debugHttpd) dup2(outFd, 2); // replace stderr with the pipe 1005
1006#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1007 if (!config->debugHttpd)
1008#endif
1009 dup2(outFd, 2); // replace stderr with the pipe
1010
739 close(toCgi[0]); 1011 close(toCgi[0]);
740 close(toCgi[1]); 1012 close(toCgi[1]);
741 close(fromCgi[0]); 1013 close(fromCgi[0]);
742 close(fromCgi[1]); 1014 close(fromCgi[1]);
743 1015
744#if 0
745 fcntl(0,F_SETFD, 1);
746 fcntl(1,F_SETFD, 1);
747 fcntl(2,F_SETFD, 1);
748#endif
749
750 script = (char*) malloc(strlen(url)+2);
751 if (!script) _exit(242);
752 sprintf(script,".%s",url);
753
754 envCount=0;
755 addEnv("SCRIPT_NAME",script);
756 addEnv("REQUEST_METHOD",request);
757 addEnv("QUERY_STRING",urlArgs);
758 addEnv("SERVER_SOFTWARE",httpdVersion);
759 if (strncmpi(request,"POST",4)==0) addEnvCgi(body);
760 else addEnvCgi(urlArgs);
761
762 /* 1016 /*
763 * Most HTTP servers chdir to the cgi directory. 1017 * Find PATH_INFO.
764 */ 1018 */
765 while (*url == '/') url++; // skip leading slash(s) 1019 script = purl;
766 directory = strdup( url ); 1020 while((script = strchr( script + 1, '/' )) != NULL) {
767 if ( directory == (char*) 0 ) 1021 /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
768 script = (char*) (url); /* ignore errors */ 1022 struct stat sb;
769 else 1023
770 { 1024 *script = '\0';
771 script = strrchr( directory, '/' ); 1025 if(is_directory(purl + 1, 1, &sb) == 0) {
772 if ( script == (char*) 0 ) 1026 /* not directory, found script.cgi/PATH_INFO */
773 script = directory; 1027 *script = '/';
774 else 1028 break;
775 {
776 *script++ = '\0';
777 (void) chdir( directory ); /* ignore errors */
778 } 1029 }
1030 *script = '/'; /* is directory, find next '/' */
1031 }
1032 addEnv("PATH", "INFO", script); /* set /PATH_INFO or NULL */
1033 addEnv("PATH", "", getenv("PATH"));
1034 addEnv("REQUEST", "METHOD", request);
1035 if(urlArgs) {
1036 char *uri = alloca(strlen(purl) + 2 + strlen(urlArgs));
1037 if(uri)
1038 sprintf(uri, "%s?%s", purl, urlArgs);
1039 addEnv("REQUEST", "URI", uri);
1040 } else {
1041 addEnv("REQUEST", "URI", purl);
1042 }
1043 if(script != NULL)
1044 *script = '\0'; /* reduce /PATH_INFO */
1045 /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
1046 addEnv("SCRIPT_NAME", "", purl);
1047 addEnv("QUERY_STRING", "", urlArgs);
1048 addEnv("SERVER", "SOFTWARE", httpdVersion);
1049 addEnv("SERVER", "PROTOCOL", "HTTP/1.0");
1050 addEnv("GATEWAY_INTERFACE", "", "CGI/1.1");
1051#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
1052 addEnv("REMOTE", "ADDR", config->rmt_ip);
1053 addEnvPort("REMOTE");
1054#else
1055 addEnv("REMOTE_ADDR", "", config->rmt_ip);
1056#endif
1057 if(bodyLen) {
1058 char sbl[32];
1059
1060 sprintf(sbl, "%d", bodyLen);
1061 addEnv("CONTENT_LENGTH", "", sbl);
1062 }
1063 if(cookie)
1064 addEnv("HTTP_COOKIE", "", cookie);
1065
1066#ifdef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
1067 if (request != request_GET) {
1068 addEnvCgi(body);
1069 } else {
1070 addEnvCgi(urlArgs);
779 } 1071 }
1072#endif
1073
1074 /* set execve argp[0] without path */
1075 argp[0] = strrchr( purl, '/' ) + 1;
1076 /* but script argp[0] must have absolute path and chdiring to this */
1077 if(realpath(purl + 1, realpath_buff) != NULL) {
1078 script = strrchr(realpath_buff, '/');
1079 if(script) {
1080 *script = '\0';
1081 if(chdir(realpath_buff) == 0) {
1082 *script = '/';
780 // now run the program. If it fails, use _exit() so no destructors 1083 // now run the program. If it fails, use _exit() so no destructors
781 // get called and make a mess. 1084 // get called and make a mess.
782 execve(script, argp, envp); 1085 execve(realpath_buff, argp, config->envp);
783 1086 }
784#ifdef DEBUG 1087 }
785 fprintf(stderr, "exec failed\n"); 1088 }
1089#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1090 config->accepted_socket = 1; /* send to stdout */
786#endif 1091#endif
787 close(2); 1092 sendHeaders(HTTP_NOT_FOUND);
788 close(1);
789 close(0);
790 _exit(242); 1093 _exit(242);
791 } /* end child */ 1094 } /* end child */
792 1095
1096 } while (0);
1097
1098 if (pid) {
793 /* parent process */ 1099 /* parent process */
794 inFd=fromCgi[0]; 1100 int status;
795 outFd=toCgi[1]; 1101
1102 inFd = fromCgi[0];
1103 outFd = toCgi[1];
796 close(fromCgi[1]); 1104 close(fromCgi[1]);
797 close(toCgi[0]); 1105 close(toCgi[0]);
798 if (body) write(outFd, body, bodyLen); 1106 if (body) full_write(outFd, body, bodyLen);
799 close(outFd); 1107 close(outFd);
800 1108
801 } while (0); 1109 while (1) {
802
803 if (pid)
804 {
805 int status;
806 pid_t dead_pid;
807
808 while (1)
809 {
810 struct timeval timeout; 1110 struct timeval timeout;
811 fd_set readSet; 1111 fd_set readSet;
812 char buf[160]; 1112 char buf[160];
813 int nfound; 1113 int nfound;
814 int count; 1114 int count;
815 1115
816 FD_ZERO(&readSet); 1116 FD_ZERO(&readSet);
817 FD_SET(inFd, &readSet); 1117 FD_SET(inFd, &readSet);
818 1118
819 /* Now wait on the set of sockets! */ 1119 /* Now wait on the set of sockets! */
820 timeout.tv_sec = 0; 1120 timeout.tv_sec = 0;
821 timeout.tv_usec = 10000; 1121 timeout.tv_usec = 10000;
822 nfound = select(inFd+1, &readSet, 0, 0, &timeout); 1122 nfound = select(inFd + 1, &readSet, 0, 0, &timeout);
823 1123
824 if (nfound <= 0) 1124 if (nfound <= 0) {
825 { 1125 if (waitpid(pid, &status, WNOHANG) > 0) {
826 dead_pid = waitpid(pid, &status, WNOHANG); 1126 close(inFd);
827 if (dead_pid != 0)
828 {
829 close(fromCgi[0]);
830 close(fromCgi[1]);
831 close(toCgi[0]);
832 close(toCgi[1]);
833#ifdef DEBUG 1127#ifdef DEBUG
834 if (debugHttpd) 1128 if (config->debugHttpd) {
835 {
836 if (WIFEXITED(status)) 1129 if (WIFEXITED(status))
837 fprintf(stderr,"piped has exited with status=%d\n", WEXITSTATUS(status)); 1130 error_msg("piped has exited with status=%d", WEXITSTATUS(status));
838 if (WIFSIGNALED(status)) 1131 if (WIFSIGNALED(status))
839 fprintf(stderr,"piped has exited with signal=%d\n", WTERMSIG(status)); 1132 error_msg("piped has exited with signal=%d", WTERMSIG(status));
840 } 1133 }
841#endif 1134#endif
842 pid = -1; 1135 pid = -1;
843 break; 1136 break;
844 } 1137 }
845 } 1138 } else {
846 else 1139 int s = a_c_w;
847 { 1140
848 // There is something to read 1141 // There is something to read
849 count = read(inFd,buf,sizeof(buf)-1); 1142 count = full_read(inFd, buf, sizeof(buf)-1);
850 // If a read returns 0 at this point then some type of error has 1143 // If a read returns 0 at this point then some type of error has
851 // occurred. Bail now. 1144 // occurred. Bail now.
852 if (count == 0) break; 1145 if (count == 0) break;
853 if (count > 0) 1146 if (count > 0) {
854 { 1147 if (firstLine) {
855 if (firstLine)
856 {
857 /* check to see if the user script added headers */ 1148 /* check to see if the user script added headers */
858 if (strcmp(buf,"HTTP")!= 0) 1149 if (strncmp(buf, "HTTP/1.0 200 OK\n", 4) != 0) {
859 { 1150 full_write(s, "HTTP/1.0 200 OK\n", 16);
860 write(s,"HTTP/1.0 200 OK\n", 16);
861 } 1151 }
862 if (strstr(buf,"ontent-") == 0) 1152 if (strstr(buf, "ontent-") == 0) {
863 { 1153 full_write(s, "Content-type: text/plain\n\n", 26);
864 write(s,"Content-type: text/plain\n\n", 26);
865 } 1154 }
866
867 firstLine=0; 1155 firstLine=0;
868 } 1156 }
869 write(s,buf,count); 1157 full_write(s, buf, count);
870#ifdef DEBUG 1158#ifdef DEBUG
871 if (debugHttpd) fprintf(stderr,"cgi read %d bytes\n", count); 1159 if (config->debugHttpd)
1160 fprintf(stderr, "cgi read %d bytes\n", count);
872#endif 1161#endif
873 } 1162 }
874 } 1163 }
@@ -876,6 +1165,7 @@ static int sendCgi(int s, const char *url,
876 } 1165 }
877 return 0; 1166 return 0;
878} 1167}
1168#endif /* CONFIG_FEATURE_HTTPD_CGI */
879 1169
880/**************************************************************************** 1170/****************************************************************************
881 * 1171 *
@@ -883,55 +1173,66 @@ static int sendCgi(int s, const char *url,
883 * 1173 *
884 * $Description: Send a file response to an HTTP request 1174 * $Description: Send a file response to an HTTP request
885 * 1175 *
886 * $Parameters: 1176 * $Parameter:
887 * (int) s . . . . . . . The http session socket.
888 * (const char *) url . . The URL requested. 1177 * (const char *) url . . The URL requested.
1178 * (char *) buf . . . . . The stack buffer.
889 * 1179 *
890 * $Return: (int) . . . . . . Always 0. 1180 * $Return: (int) . . . . . . Always 0.
891 * 1181 *
892 ****************************************************************************/ 1182 ****************************************************************************/
893static int sendFile(int s, const char *url) 1183static int sendFile(const char *url, char *buf)
894{ 1184{
895 char *suffix = strrchr(url,'.'); 1185 char * suffix;
896 const char *content = "application/octet-stream";
897 int f; 1186 int f;
1187 const char * const * table;
1188 const char * try_suffix;
898 1189
899 if (suffix) 1190 suffix = strrchr(url, '.');
900 { 1191
901 const char ** table; 1192 for (table = suffixTable; *table; table += 2)
902 for (table = (const char **) &suffixTable[0]; 1193 if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
903 *table && (strstr(*table, suffix) == 0); table+=2); 1194 try_suffix += strlen(suffix);
904 if (table) content = *(table+1); 1195 if(*try_suffix == 0 || *try_suffix == '.')
1196 break;
1197 }
1198 /* also, if not found, set default as "application/octet-stream"; */
1199 config->found_mime_type = *(table+1);
1200#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
1201 if (suffix) {
1202 Htaccess * cur;
1203
1204 for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) {
1205 if(strcmp(cur->before_colon, suffix) == 0) {
1206 config->found_mime_type = cur->after_colon;
1207 break;
1208 }
1209 }
905 } 1210 }
906 1211#endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
907 if (*url == '/') url++;
908 suffix = strchr(url,'?');
909 if (suffix) *suffix = 0;
910 1212
911#ifdef DEBUG 1213#ifdef DEBUG
912 fprintf(stderr,"Sending file '%s'\n", url); 1214 if (config->debugHttpd)
1215 fprintf(stderr, "Sending file '%s' Content-type: %s\n",
1216 url, config->found_mime_type);
913#endif 1217#endif
914 1218
915 f = open(url,O_RDONLY, 0444); 1219 f = open(url, O_RDONLY);
916 if (f >= 0) 1220 if (f >= 0) {
917 { 1221 int count;
918 char buf[1450]; 1222
919 int count; 1223 sendHeaders(HTTP_OK);
920 sendHeaders(s, HTTP_OK, content, -1, 0 ); 1224 while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
921 while ((count = read(f, buf, sizeof(buf)))) 1225 full_write(a_c_w, buf, count);
922 { 1226 }
923 sendBuf(s, buf, count); 1227 close(f);
924 } 1228 } else {
925 close(f);
926 }
927 else
928 {
929#ifdef DEBUG 1229#ifdef DEBUG
930 fprintf(stderr,"Unable to open '%s'\n", url); 1230 if (config->debugHttpd)
1231 perror_msg("Unable to open '%s'", url);
931#endif 1232#endif
932 sendHeaders(s, HTTP_NOT_FOUND, "text/html", -1, 0); 1233 sendHeaders(HTTP_NOT_FOUND);
933 } 1234 }
934 1235
935 return 0; 1236 return 0;
936} 1237}
937 1238
@@ -941,93 +1242,85 @@ static int sendFile(int s, const char *url)
941 * 1242 *
942 * $Description: Check the permission file for access. 1243 * $Description: Check the permission file for access.
943 * 1244 *
944 * Both IP addresses as well as url pathnames can be specified. If an IP
945 * address check is desired, the 'path' should be specified as "ip" and the
946 * dotted decimal IP address placed in request.
947 *
948 * For url pathnames, place the url (with leading /) in 'path' and any
949 * authentication information in request. e.g. "user:pass"
950 *
951 *******
952 *
953 * Keep the algorithm simple.
954 * If config file isn't present, everything is allowed. 1245 * If config file isn't present, everything is allowed.
955 * Run down /etc/httpd.hosts a line at a time. 1246 * Entries are of the form you can see example from header source
956 * Stop if match is found.
957 * Entries are of the form:
958 * ip:10.10 # any address that begins with 10.10
959 * dir:user:pass # dir security for dirs that start with 'dir'
960 *
961 * httpd.conf has the following format:
962 * ip:10.10. # Allow any address that begins with 10.10.
963 * ip:172.20. # Allow 172.20.x.x
964 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin
965 * /:foo:bar # Require user foo, pwd bar on urls starting with /
966 *
967 * To open up the server:
968 * ip:* # Allow any IP address
969 * /:* # no password required for urls starting with / (all)
970 * 1247 *
971 * $Parameters: 1248 * $Parameters:
972 * (const char *) path . . . . The file path or "ip" for ip addresses. 1249 * (const char *) path . . . . The file path or NULL for ip addresses.
973 * (const char *) request . . . User information to validate. 1250 * (const char *) request . . . User information to validate.
974 * 1251 *
975 * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise. 1252 * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
976 * 1253 *
977 ****************************************************************************/ 1254 ****************************************************************************/
1255
1256
978static int checkPerm(const char *path, const char *request) 1257static int checkPerm(const char *path, const char *request)
979{ 1258{
980 FILE *f=NULL; 1259 Htaccess * cur;
981 int rval; 1260 const char *p;
982 char buf[80];
983 char *p;
984 int ipaddr=0;
985
986 /* If httpd.conf not there assume anyone can get in */
987 if (configFile) f = fopen(configFile,"r");
988 if(f == NULL) f = fopen("/etc/httpd.conf","r");
989 if(f == NULL) f = fopen("httpd.conf","r");
990 if(f == NULL) {
991 return(1);
992 }
993 if (strcmp("ip",path) == 0) ipaddr=1;
994 1261
995 rval=0; 1262#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1263 int ipaddr = path == NULL;
1264 const char *prev = NULL;
1265#else
1266# define ipaddr 1
1267#endif
996 1268
997 /* This could stand some work */ 1269 /* This could stand some work */
998 while ( fgets(buf, 80, f) != NULL) 1270 for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) {
999 { 1271 const char *p0 = cur->before_colon;
1000 if(buf[0] == '#') continue; 1272
1001 if(buf[0] == '\0') continue; 1273 if(*p0 == 'A' || *p0 == 'D') {
1002 for(p = buf + (strlen(buf) - 1); p >= buf; p--) 1274 if(!ipaddr)
1003 { 1275 continue;
1004 if(isspace(*p)) *p = 0; 1276 } else {
1005 } 1277 if(ipaddr)
1006 1278 continue;
1007 p = strchr(buf,':'); 1279#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
1008 if (!p) continue; 1280 if(*p0 == '.')
1009 *p++=0; 1281 continue;
1282#endif
1283#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1284 if(prev != NULL && strcmp(prev, p0) != 0)
1285 continue; /* find next identical */
1286#endif
1287 }
1288
1289 p = cur->after_colon;
1010#ifdef DEBUG 1290#ifdef DEBUG
1011 fprintf(stderr,"checkPerm: '%s' ? '%s'\n",buf,path); 1291 if (config->debugHttpd)
1292 fprintf(stderr,"checkPerm: '%s' ? '%s'\n",
1293 (ipaddr ? p : p0), request);
1012#endif 1294#endif
1013 if((ipaddr ? strcmp(buf,path) : strncmp(buf, path, strlen(buf))) == 0) 1295 if(ipaddr) {
1014 { 1296 if(strncmp(p, request, strlen(p)) != 0)
1015 /* match found. Check request */ 1297 continue;
1016 if ((strcmp("*",p) == 0) || 1298 return *p0 == 'A'; /* Allow/Deny */
1017 (strcmp(p, request) == 0) ||
1018 (ipaddr && (strncmp(p, request, strlen(p)) == 0)))
1019 {
1020 rval = 1;
1021 break;
1022 }
1023 1299
1024 /* reject on first failure for non ipaddresses */
1025 if (!ipaddr) break;
1026 } 1300 }
1027 }; 1301#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1028 fclose(f); 1302 else {
1029 return(rval); 1303 int l = strlen(p0);
1030}; 1304
1305 if(strncmp(p0, path, l) == 0 &&
1306 (l == 1 || path[l] == '/' || path[l] == 0)) {
1307 /* path match found. Check request */
1308 if (strcmp(p, request) == 0)
1309 return 1; /* Ok */
1310 /* unauthorized, but check next /path:user:password */
1311 prev = p0;
1312 }
1313 }
1314#endif
1315 } /* for */
1316
1317#ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1318 /* if uncofigured, return 1 - access from all */
1319 return 1;
1320#else
1321 return prev == NULL;
1322#endif
1323}
1031 1324
1032 1325
1033/**************************************************************************** 1326/****************************************************************************
@@ -1036,156 +1329,242 @@ static int checkPerm(const char *path, const char *request)
1036 * 1329 *
1037 * $Description: Handle an incoming http request. 1330 * $Description: Handle an incoming http request.
1038 * 1331 *
1039 * $Parameters:
1040 * (s) s . . . . . The http request socket.
1041 *
1042 * $Return: (int) . . . Always 0.
1043 *
1044 ****************************************************************************/ 1332 ****************************************************************************/
1045static int handleIncoming(int s) 1333static void handleIncoming(void)
1046{ 1334{
1047 char buf[8192]; 1335 char *buf = config->buf;
1048 char url[8192]; /* hold args too initially */ 1336 char *url;
1049 char credentials[80]; 1337 char *purl;
1050 char request[20]; 1338 int blank = -1;
1051 long length=0;
1052 int major;
1053 int minor;
1054 char *urlArgs; 1339 char *urlArgs;
1055 char *body=0; 1340#ifdef CONFIG_FEATURE_HTTPD_CGI
1341 const char *prequest = request_GET;
1342 char *body = 0;
1343 long length=0;
1344 char *cookie = 0;
1345#endif
1346 char *test;
1347 struct stat sb;
1056 1348
1057 credentials[0] = 0; 1349#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1058 do 1350 int credentials = -1; /* if not requred this is Ok */
1059 {
1060 int count = getLine(s, buf, sizeof(buf));
1061 int blank;
1062 if (count <= 0) break;
1063 count = sscanf(buf, "%9s %1000s HTTP/%d.%d", request,
1064 url, &major, &minor);
1065
1066 if (count < 2)
1067 {
1068 /* Garbled request/URL */
1069#if 0
1070 genHttpHeader(&requestInfo,
1071 HTTP_BAD_REQUEST, requestInfo.dataType,
1072 HTTP_LENGTH_UNKNOWN);
1073#endif 1351#endif
1352
1353 do {
1354 int count;
1355
1356 if (getLine(buf) <= 0)
1357 break; /* closed */
1358
1359 purl = strpbrk(buf, " \t");
1360 if(purl == NULL) {
1361BAD_REQUEST:
1362 sendHeaders(HTTP_BAD_REQUEST);
1074 break; 1363 break;
1075 } 1364 }
1076 1365 *purl = 0;
1077 /* If no version info, assume 0.9 */ 1366#ifdef CONFIG_FEATURE_HTTPD_CGI
1078 if (count != 4) 1367 if(strcasecmp(buf, prequest) != 0) {
1079 { 1368 prequest = "POST";
1080 major = 0; 1369 if(strcasecmp(buf, prequest) != 0) {
1081 minor = 9; 1370 sendHeaders(HTTP_NOT_IMPLEMENTED);
1371 break;
1372 }
1373 }
1374#else
1375 if(strcasecmp(buf, request_GET) != 0) {
1376 sendHeaders(HTTP_NOT_IMPLEMENTED);
1377 break;
1082 } 1378 }
1379#endif
1380 *purl = ' ';
1381 count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
1083 1382
1383 if (count < 1 || buf[0] != '/') {
1384 /* Garbled request/URL */
1385 goto BAD_REQUEST;
1386 }
1387 url = alloca(strlen(buf) + 12); /* + sizeof("/index.html\0") */
1388 if(url == NULL) {
1389 sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
1390 break;
1391 }
1392 strcpy(url, buf);
1084 /* extract url args if present */ 1393 /* extract url args if present */
1085 urlArgs = strchr(url,'?'); 1394 urlArgs = strchr(url, '?');
1086 if (urlArgs) 1395 if (urlArgs) {
1087 { 1396 *urlArgs++ = 0; /* next code can set '/' to this pointer,
1088 *urlArgs=0; 1397 but CGI script can`t be a directory */
1089 urlArgs++; 1398 }
1399
1400 /* algorithm stolen from libbb simplify_path(),
1401 but don`t strdup and reducing trailing slash */
1402 purl = test = url;
1403
1404 do {
1405 if (*purl == '/') {
1406 if (*test == '/') { /* skip duplicate (or initial) slash */
1407 continue;
1408 } else if (*test == '.') {
1409 if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */
1410 continue;
1411 } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) {
1412 ++test;
1413 if (purl == url) {
1414 /* protect out root */
1415 goto BAD_REQUEST;
1416 }
1417 while (*--purl != '/'); /* omit previous dir */
1418 continue;
1419 }
1420 }
1421 }
1422 *++purl = *test;
1423 } while (*++test);
1424
1425 *++purl = 0; /* so keep last character */
1426 test = purl; /* end ptr */
1427
1428 /* If URL is directory, adding '/' */
1429 if(test[-1] != '/') {
1430 if ( is_directory(url + 1, 1, &sb) ) {
1431 *test++ = '/';
1432 *test = 0;
1433 purl = test; /* end ptr */
1434 }
1090 } 1435 }
1091
1092#ifdef DEBUG 1436#ifdef DEBUG
1093 if (debugHttpd) fprintf(stderr,"url='%s', args=%s\n", url, urlArgs); 1437 if (config->debugHttpd)
1094#endif 1438 fprintf(stderr, "url='%s', args=%s\n", url, urlArgs);
1095 1439#endif
1096 // read until blank line(s) 1440
1097 blank = 0; 1441 test = url;
1098 while ((count = getLine(s, buf, sizeof(buf))) >= 0) 1442 while((test = strchr( test + 1, '/' )) != NULL) {
1099 { 1443 /* have path1/path2 */
1100 if (count == 0) 1444 *test = '\0';
1101 { 1445 if( is_directory(url + 1, 1, &sb) ) {
1102 if (major > 0) break; 1446 /* may be having subdir config */
1103 blank++; 1447 parse_conf(url + 1, SUBDIR_PARSE);
1104 if (blank == 2) break; 1448 }
1105 } 1449 *test = '/';
1450 }
1451
1452 // read until blank line for HTTP version specified, else parse immediate
1453 while (blank >= 0 && (count = getLine(buf)) > 0) {
1454
1106#ifdef DEBUG 1455#ifdef DEBUG
1107 if (debugHttpd) fprintf(stderr,"Header: '%s'\n", buf); 1456 if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf);
1108#endif 1457#endif
1109 1458
1459#ifdef CONFIG_FEATURE_HTTPD_CGI
1110 /* try and do our best to parse more lines */ 1460 /* try and do our best to parse more lines */
1111 if ((strncmpi(buf, "Content-length:", 15) == 0)) 1461 if ((strncasecmp(buf, Content_length, 15) == 0)) {
1112 { 1462 if(prequest != request_GET)
1113 sscanf(buf, "%*s %ld", &length); 1463 length = strtol(buf + 15, 0, 0); // extra read only for POST
1114 } 1464 } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
1465 for(test = buf + 7; isspace(*test); test++)
1466 ;
1467 cookie = strdup(test);
1468 }
1469#endif
1470
1115#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1471#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1116 else if (strncmpi(buf, "Authorization:", 14) == 0) 1472 if (strncasecmp(buf, "Authorization:", 14) == 0) {
1117 {
1118 /* We only allow Basic credentials. 1473 /* We only allow Basic credentials.
1119 * It shows up as "Authorization: Basic <userid:password>" where 1474 * It shows up as "Authorization: Basic <userid:password>" where
1120 * the userid:password is base64 encoded. 1475 * the userid:password is base64 encoded.
1121 */ 1476 */
1122 char *ptr = buf+14; 1477 for(test = buf + 14; isspace(*test); test++)
1123 while (*ptr == ' ') ptr++; 1478 ;
1124 if (strncmpi(ptr, "Basic", 5) != 0) break; 1479 if (strncasecmp(test, "Basic", 5) != 0)
1125 ptr += 5; 1480 continue;
1126 while (*ptr == ' ') ptr++; 1481
1127 memset(credentials, 0, sizeof(credentials)); 1482 test += 5; /* decodeBase64() skiping space self */
1128 decodeBase64(credentials, 1483 decodeBase64(test);
1129 sizeof(credentials)-1, 1484 credentials = checkPerm(url, test);
1130 ptr,
1131 strlen(ptr) );
1132
1133 } 1485 }
1486#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
1487
1488 } /* while extra header reading */
1489
1490
1491 if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 ||
1492 checkPerm(NULL, config->rmt_ip) == 0) {
1493 /* protect listing [/path]/httpd_conf or IP deny */
1494#ifdef CONFIG_FEATURE_HTTPD_CGI
1495FORBIDDEN: /* protect listing /cgi-bin */
1496#endif
1497 sendHeaders(HTTP_FORBIDDEN);
1498 break;
1134 } 1499 }
1135 if (!checkPerm(url, credentials)) 1500
1136 { 1501#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1137 sendHeaders(s, HTTP_UNAUTHORIZED, 0, -1, 0); 1502 if (credentials <= 0 && checkPerm(url, ":") == 0) {
1138 length=-1; 1503 sendHeaders(HTTP_UNAUTHORIZED);
1139 break; /* no more processing */ 1504 break;
1140 }
1141#else
1142 } 1505 }
1143#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */ 1506#endif
1144 1507
1145 /* we are done if an error occurred */ 1508 test = url + 1; /* skip first '/' */
1146 if (length == -1) break;
1147 1509
1148 if (strcmp(url,"/") == 0) strcpy(url,"/index.html"); 1510#ifdef CONFIG_FEATURE_HTTPD_CGI
1511 /* if strange Content-Length */
1512 if (length < 0 || length > MAX_POST_SIZE)
1513 break;
1149 1514
1150 if (length>0) 1515 if (length > 0) {
1151 { 1516 body = malloc(length + 1);
1152 body=(char*) malloc(length+1); 1517 if (body) {
1153 if (body) 1518 length = full_read(a_c_r, body, length);
1154 { 1519 if(length < 0) // closed
1155 length = read(s,body,length); 1520 length = 0;
1156 body[length]=0; // always null terminate for safety 1521 body[length] = 0; // always null terminate for safety
1157 urlArgs=body; 1522 urlArgs = body;
1158 } 1523 }
1159 } 1524 }
1160 1525
1161 if (strstr(url,"..") || strstr(url, "httpd.conf")) 1526 if (strncmp(test, "cgi-bin", 7) == 0) {
1162 { 1527 if(test[7] == 0 || (test[7] == '/' && test[8] == 0))
1163 /* protect from .. path creep */ 1528 goto FORBIDDEN; // protect listing cgi-bin
1164 sendHeaders(s, HTTP_NOT_FOUND, "text/html", -1, 0); 1529 sendCgi(url, prequest, urlArgs, body, length, cookie);
1165 } 1530 } else {
1166 else if (strstr(url,"cgi-bin")) 1531 if (prequest != request_GET)
1167 { 1532 sendHeaders(HTTP_NOT_IMPLEMENTED);
1168 sendCgi(s, url, request, urlArgs, body, length); 1533 else {
1169 } 1534#endif /* CONFIG_FEATURE_HTTPD_CGI */
1170 else if (strncmpi(request,"GET",3) == 0) 1535 if(purl[-1] == '/')
1171 { 1536 strcpy(purl, "index.html");
1172 sendFile(s, url); 1537 if ( stat(test, &sb ) == 0 ) {
1173 } 1538 config->ContentLength = sb.st_size;
1174 else 1539 config->last_mod = sb.st_mtime;
1175 { 1540 }
1176 sendHeaders(s, HTTP_NOT_IMPLEMENTED, 0, -1, 0); 1541 sendFile(test, buf);
1542#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1543 /* unset if non inetd looped */
1544 config->ContentLength = -1;
1545#endif
1546
1547#ifdef CONFIG_FEATURE_HTTPD_CGI
1548 }
1177 } 1549 }
1550#endif
1551
1178 } while (0); 1552 } while (0);
1179 1553
1180#ifdef DEBUG 1554
1181 if (debugHttpd) fprintf(stderr,"closing socket\n"); 1555#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1182#endif 1556/* from inetd don`t looping: freeing, closing automatic from exit always */
1183 if (body) free(body); 1557# ifdef DEBUG
1184 shutdown(s,SHUT_WR); 1558 if (config->debugHttpd) fprintf(stderr, "closing socket\n");
1185 shutdown(s,SHUT_RD); 1559# endif
1186 close(s); 1560# ifdef CONFIG_FEATURE_HTTPD_CGI
1187 1561 free(body);
1188 return 0; 1562 free(cookie);
1563# endif
1564 shutdown(a_c_w, SHUT_WR);
1565 shutdown(a_c_r, SHUT_RD);
1566 close(config->accepted_socket);
1567#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
1189} 1568}
1190 1569
1191/**************************************************************************** 1570/****************************************************************************
@@ -1203,24 +1582,23 @@ static int handleIncoming(int s)
1203 * $Return: (int) . . . . Always 0. 1582 * $Return: (int) . . . . Always 0.
1204 * 1583 *
1205 ****************************************************************************/ 1584 ****************************************************************************/
1585#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1206static int miniHttpd(int server) 1586static int miniHttpd(int server)
1207{ 1587{
1208 fd_set readfd, portfd; 1588 fd_set readfd, portfd;
1209 int nfound; 1589 int nfound;
1210 1590
1211 FD_ZERO(&portfd); 1591 FD_ZERO(&portfd);
1212 FD_SET(server, &portfd); 1592 FD_SET(server, &portfd);
1213 1593
1214 /* copy the ports we are watching to the readfd set */ 1594 /* copy the ports we are watching to the readfd set */
1215 while (1) 1595 while (1) {
1216 { 1596 readfd = portfd;
1217 readfd = portfd ; 1597
1218
1219 /* Now wait INDEFINATELY on the set of sockets! */ 1598 /* Now wait INDEFINATELY on the set of sockets! */
1220 nfound = select(server+1, &readfd, 0, 0, 0); 1599 nfound = select(server + 1, &readfd, 0, 0, 0);
1221 1600
1222 switch (nfound) 1601 switch (nfound) {
1223 {
1224 case 0: 1602 case 0:
1225 /* select timeout error! */ 1603 /* select timeout error! */
1226 break ; 1604 break ;
@@ -1228,48 +1606,41 @@ static int miniHttpd(int server)
1228 /* select error */ 1606 /* select error */
1229 break; 1607 break;
1230 default: 1608 default:
1231 if (FD_ISSET(server, &readfd)) 1609 if (FD_ISSET(server, &readfd)) {
1232 { 1610 int on;
1233 char on;
1234 struct sockaddr_in fromAddr; 1611 struct sockaddr_in fromAddr;
1235 char rmt_ip[20]; 1612
1236 int addr; 1613 unsigned int addr;
1237 socklen_t fromAddrLen = sizeof(fromAddr); 1614 socklen_t fromAddrLen = sizeof(fromAddr);
1238 int s = accept(server, 1615 int s = accept(server,
1239 (struct sockaddr *)&fromAddr, &fromAddrLen) ; 1616 (struct sockaddr *)&fromAddr, &fromAddrLen);
1240 if (s < 0) 1617
1241 { 1618 if (s < 0) {
1242 continue; 1619 continue;
1243 } 1620 }
1621 config->accepted_socket = s;
1244 addr = ntohl(fromAddr.sin_addr.s_addr); 1622 addr = ntohl(fromAddr.sin_addr.s_addr);
1245 sprintf(rmt_ip,"%u.%u.%u.%u", 1623 sprintf(config->rmt_ip, "%u.%u.%u.%u",
1246 (unsigned char)(addr >> 24), 1624 (unsigned char)(addr >> 24),
1247 (unsigned char)(addr >> 16), 1625 (unsigned char)(addr >> 16),
1248 (unsigned char)(addr >> 8), 1626 (unsigned char)(addr >> 8),
1249 (unsigned char)(addr >> 0)); 1627 addr & 0xff);
1628 config->port = ntohs(fromAddr.sin_port);
1250#ifdef DEBUG 1629#ifdef DEBUG
1251 if (debugHttpd) 1630 if (config->debugHttpd) {
1252 { 1631 error_msg("connection from IP=%s, port %u\n",
1253 fprintf(stderr,"httpMini.cpp: connection from IP=%s, port %d\n", 1632 config->rmt_ip, config->port);
1254 rmt_ip, ntohs(fromAddr.sin_port));
1255 } 1633 }
1256#endif 1634#endif
1257 if(checkPerm("ip", rmt_ip) == 0)
1258 {
1259 close(s);
1260 continue;
1261 }
1262
1263 /* set the KEEPALIVE option to cull dead connections */ 1635 /* set the KEEPALIVE option to cull dead connections */
1264 on = 1; 1636 on = 1;
1265 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, 1637 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
1266 sizeof (on));
1267 1638
1268 if (fork() == 0) 1639 if (config->debugHttpd || fork() == 0) {
1269 {
1270 /* This is the spawned thread */ 1640 /* This is the spawned thread */
1271 handleIncoming(s); 1641 handleIncoming();
1272 exit(0); 1642 if(!config->debugHttpd)
1643 exit(0);
1273 } 1644 }
1274 close(s); 1645 close(s);
1275 } 1646 }
@@ -1278,72 +1649,169 @@ static int miniHttpd(int server)
1278 return 0; 1649 return 0;
1279} 1650}
1280 1651
1652#else
1653 /* from inetd */
1654
1655static int miniHttpd(void)
1656{
1657 struct sockaddr_in fromAddrLen;
1658 socklen_t sinlen = sizeof (struct sockaddr_in);
1659 unsigned int addr;
1660
1661 getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
1662 addr = ntohl(fromAddrLen.sin_addr.s_addr);
1663 sprintf(config->rmt_ip, "%u.%u.%u.%u",
1664 (unsigned char)(addr >> 24),
1665 (unsigned char)(addr >> 16),
1666 (unsigned char)(addr >> 8),
1667 addr & 0xff);
1668 config->port = ntohs(fromAddrLen.sin_port);
1669 handleIncoming();
1670 return 0;
1671}
1672#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
1673
1674#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
1675static void sighup_handler(int sig)
1676{
1677 /* set and reset */
1678 struct sigaction sa;
1679
1680 sa.sa_handler = sighup_handler;
1681 sigemptyset(&sa.sa_mask);
1682 sa.sa_flags = SA_RESTART;
1683 sigaction(SIGHUP, &sa, NULL);
1684 parse_conf(default_patch_httpd_conf,
1685 sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
1686}
1687#endif
1688
1689#ifdef HTTPD_STANDALONE
1690int main(int argc, char *argv[])
1691#else
1281int httpd_main(int argc, char *argv[]) 1692int httpd_main(int argc, char *argv[])
1693#endif
1282{ 1694{
1695 const char *home_httpd = home;
1696
1697#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1283 int server; 1698 int server;
1284 int port = 80; 1699#endif
1285 int c; 1700
1286 1701#ifdef CONFIG_FEATURE_HTTPD_SETUID
1702 long uid = -1;
1703#endif
1704
1705 config = xcalloc(1, sizeof(*config));
1706#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1707 config->realm = "Web Server Authentication";
1708#endif
1709
1710#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1711 config->port = 80;
1712#endif
1713
1714 config->ContentLength = -1;
1715
1287 /* check if user supplied a port number */ 1716 /* check if user supplied a port number */
1288 for (;;) { 1717 for (;;) {
1289 c = getopt( argc, argv, "p:ve:d:" 1718 int c = getopt( argc, argv, "c:h:"
1719#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1720 "p:v"
1721#endif
1722#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
1723 "e:"
1724#endif
1725#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
1726 "d:"
1727#endif
1290#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1728#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1291 "r:c:" 1729 "r:"
1730#endif
1731#ifdef CONFIG_FEATURE_HTTPD_SETUID
1732 "u:"
1292#endif 1733#endif
1293 ); 1734 );
1294 if (c == EOF) break; 1735 if (c == EOF) break;
1295 switch (c) { 1736 switch (c) {
1737 case 'c':
1738 config->configFile = optarg;
1739 break;
1740 case 'h':
1741 home_httpd = optarg;
1742 break;
1743#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1296 case 'v': 1744 case 'v':
1297 debugHttpd=1; 1745 config->debugHttpd = 1;
1298 break; 1746 break;
1299 case 'p': 1747 case 'p':
1300 port = atoi(optarg); 1748 config->port = atoi(optarg);
1749 if(config->port <= 0 || config->port > 0xffff)
1750 error_msg_and_die("invalid %s for -p", optarg);
1301 break; 1751 break;
1752#endif
1753#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
1302 case 'd': 1754 case 'd':
1303 printf("%s",decodeString(optarg)); 1755 printf("%s", decodeString(optarg));
1304 return 0; 1756 return 0;
1757#endif
1758#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
1305 case 'e': 1759 case 'e':
1306 printf("%s",encodeString(optarg)); 1760 printf("%s", encodeString(optarg));
1307 return 0; 1761 return 0;
1762#endif
1763#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1308 case 'r': 1764 case 'r':
1309 realm = optarg; 1765 config->realm = optarg;
1310 break; 1766 break;
1311 case 'c': 1767#endif
1312 configFile = optarg; 1768#ifdef CONFIG_FEATURE_HTTPD_SETUID
1769 case 'u':
1770 {
1771 char *e;
1772
1773 uid = strtol(optarg, &e, 0);
1774 if(*e != '\0') {
1775 /* not integer */
1776 uid = my_getpwnam(optarg);
1777 }
1778 }
1313 break; 1779 break;
1780#endif
1314 default: 1781 default:
1315 fprintf(stderr,"%s\n", httpdVersion); 1782 error_msg("%s", httpdVersion);
1316 show_usage(); 1783 show_usage();
1317 exit(1);
1318 } 1784 }
1319 } 1785 }
1320 1786
1321 envp = (char**) malloc((ENVSIZE+1)*sizeof(char*)); 1787 if(chdir(home_httpd)) {
1322 if (envp == 0) perror_exit("envp alloc"); 1788 perror_msg_and_die("can`t chdir to %s", home_httpd);
1323
1324 server = openServer(port);
1325 if (server < 0) exit(1);
1326
1327 if (!debugHttpd)
1328 {
1329 /* remember our current pwd, daemonize, chdir back */
1330 char *dir = (char *) malloc(256);
1331 if (dir == 0) perror_exit("out of memory for getpwd");
1332 if (getcwd(dir, 256) == 0) perror_exit("getcwd failed");
1333 if (daemon(0, 1) < 0) perror_exit("daemon");
1334 chdir(dir);
1335 free(dir);
1336 } 1789 }
1790#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1791 server = openServer();
1792# ifdef CONFIG_FEATURE_HTTPD_SETUID
1793 /* drop privilegies */
1794 if(uid > 0)
1795 setuid(uid);
1796# endif
1797# ifdef CONFIG_FEATURE_HTTPD_CGI
1798 addEnvPort("SERVER");
1799# endif
1800#endif
1337 1801
1338 miniHttpd(server); 1802#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
1339 1803 sighup_handler(0);
1340 return 0; 1804#else
1341} 1805 parse_conf(default_patch_httpd_conf, FIRST_PARSE);
1342 1806#endif
1343#ifdef HTTPD_STANDALONE
1344int main(int argc, char *argv[])
1345{
1346 return httpd_main(argc, argv);
1347}
1348 1807
1808#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1809 if (!config->debugHttpd) {
1810 if (daemon(1, 0) < 0) /* don`t change curent directory */
1811 perror_msg_and_die("daemon");
1812 }
1813 return miniHttpd(server);
1814#else
1815 return miniHttpd();
1349#endif 1816#endif
1817}
diff --git a/networking/inetd.c b/networking/inetd.c
new file mode 100644
index 000000000..047358c90
--- /dev/null
+++ b/networking/inetd.c
@@ -0,0 +1,1280 @@
1/*
2 * Copyright (c) 1983,1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * David A. Holland.
7 *
8 * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26/*
27 * Inetd - Internet super-server
28 *
29 * This program invokes all internet services as needed.
30 * connection-oriented services are invoked each time a
31 * connection is made, by creating a process. This process
32 * is passed the connection as file descriptor 0 and is
33 * expected to do a getpeername to find out the source host
34 * and port.
35 *
36 * Datagram oriented services are invoked when a datagram
37 * arrives; a process is created and passed a pending message
38 * on file descriptor 0. Datagram servers may either connect
39 * to their peer, freeing up the original socket for inetd
40 * to receive further messages on, or ``take over the socket'',
41 * processing all arriving datagrams and, eventually, timing
42 * out. The first type of server is said to be ``multi-threaded'';
43 * the second type of server ``single-threaded''.
44 *
45 * Inetd uses a configuration file which is read at startup
46 * and, possibly, at some later time in response to a hangup signal.
47 * The configuration file is ``free format'' with fields given in the
48 * order shown below. Continuation lines for an entry must being with
49 * a space or tab. All fields must be present in each entry.
50 *
51 * service name must be in /etc/services
52 * socket type stream/dgram/raw/rdm/seqpacket
53 * protocol must be in /etc/protocols
54 * wait/nowait[.max] single-threaded/multi-threaded, max #
55 * user[.group] user/group to run daemon as
56 * server program full path name
57 * server program arguments maximum of MAXARGS (20)
58 *
59 * RPC services unsuported
60 *
61 * Comment lines are indicated by a `#' in column 1.
62 */
63
64/*
65 * Here's the scoop concerning the user.group feature:
66 *
67 * 1) No group listed.
68 *
69 * a) for root: NO setuid() or setgid() is done
70 *
71 * b) nonroot: setuid()
72 * setgid(primary group as found in passwd)
73 * initgroups(name, primary group)
74 *
75 * 2) set-group-option on.
76 *
77 * a) for root: NO setuid()
78 * setgid(specified group)
79 * setgroups(1, specified group)
80 *
81 * b) nonroot: setuid()
82 * setgid(specified group)
83 * initgroups(name, specified group)
84 *
85 * All supplementary groups are discarded at startup in case inetd was
86 * run manually.
87 */
88
89#define __USE_BSD_SIGNAL
90
91#include "busybox.h"
92
93#include <sys/param.h>
94#include <sys/stat.h>
95#include <sys/ioctl.h>
96#include <sys/socket.h>
97#include <sys/un.h>
98#include <sys/file.h>
99#include <sys/wait.h>
100#include <sys/time.h>
101#include <sys/resource.h>
102
103#ifndef __linux__
104#ifndef RLIMIT_NOFILE
105#define RLIMIT_NOFILE RLIMIT_OFILE
106#endif
107#endif
108
109#include <sys/param.h>
110#include <sys/stat.h>
111#include <sys/ioctl.h>
112#include <sys/socket.h>
113#include <sys/file.h>
114#include <sys/wait.h>
115#include <sys/time.h>
116#include <sys/resource.h>
117
118#include <netinet/in.h>
119#include <netinet/ip.h>
120#include <arpa/inet.h>
121
122#include <errno.h>
123#include <signal.h>
124#include <netdb.h>
125#include <syslog.h>
126#include <pwd.h>
127#include <grp.h>
128#include <stdio.h>
129#include <stdlib.h>
130#include <string.h>
131#include <getopt.h>
132#include <unistd.h>
133#include <stdarg.h>
134
135#define _PATH_INETDCONF "/etc/inetd.conf"
136#define _PATH_INETDPID "/var/run/inetd.pid"
137
138#ifndef MIN
139#define MIN(a, b) ((a) < (b) ? (a) : (b))
140#endif
141
142#define TOOMANY 40 /* don't start more than TOOMANY */
143#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
144#define RETRYTIME (60*10) /* retry after bind or server fail */
145
146#ifndef OPEN_MAX
147#define OPEN_MAX 64
148#endif
149
150
151/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
152#define FD_MARGIN (8)
153static int rlim_ofile_cur = OPEN_MAX;
154
155#ifdef RLIMIT_NOFILE
156static struct rlimit rlim_ofile;
157#endif
158
159
160static struct servtab {
161 char *se_service; /* name of service */
162 int se_socktype; /* type of socket to use */
163 int se_family; /* address family */
164 char *se_proto; /* protocol used */
165 short se_wait; /* single threaded server */
166 short se_checked; /* looked at during merge */
167 char *se_user; /* user name to run as */
168 char *se_group; /* group name to run as */
169#ifndef INETD_UNSUPPORT_BILTIN
170 const struct biltin *se_bi; /* if built-in, description */
171#endif
172 char *se_server; /* server program */
173#define MAXARGV 20
174 char *se_argv[MAXARGV+1]; /* program arguments */
175 int se_fd; /* open descriptor */
176 union {
177 struct sockaddr se_un_ctrladdr;
178 struct sockaddr_in se_un_ctrladdr_in;
179 struct sockaddr_un se_un_ctrladdr_un;
180 } se_un; /* bound address */
181#define se_ctrladdr se_un.se_un_ctrladdr
182#define se_ctrladdr_in se_un.se_un_ctrladdr_in
183#define se_ctrladdr_un se_un.se_un_ctrladdr_un
184 int se_ctrladdr_size;
185 int se_max; /* max # of instances of this service */
186 int se_count; /* number started since se_time */
187 struct timeval se_time; /* start of se_count */
188 struct servtab *se_next;
189} *servtab;
190
191/* Length of socket listen queue. Should be per-service probably. */
192static int global_queuelen = 128;
193
194static int nsock, maxsock;
195static fd_set allsock;
196static int timingout;
197static sigset_t blockmask, emptymask;
198
199
200#define INETD_UNSUPPORT_BILTIN 1
201
202 /* Echo received data */
203#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
204#undef INETD_UNSUPPORT_BILTIN
205static void echo_stream(int, struct servtab *);
206static void echo_dg(int, struct servtab *);
207#endif
208 /* Internet /dev/null */
209#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
210#undef INETD_UNSUPPORT_BILTIN
211static void discard_stream(int, struct servtab *);
212static void discard_dg(int, struct servtab *);
213#endif
214 /* Return 32 bit time since 1900 */
215#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
216#undef INETD_UNSUPPORT_BILTIN
217static void machtime_stream(int, struct servtab *);
218static void machtime_dg(int, struct servtab *);
219#endif
220 /* Return human-readable time */
221#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
222#undef INETD_UNSUPPORT_BILTIN
223static void daytime_stream(int, struct servtab *);
224static void daytime_dg(int, struct servtab *);
225#endif
226 /* Familiar character generator */
227#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
228#undef INETD_UNSUPPORT_BILTIN
229static void chargen_stream(int, struct servtab *);
230static void chargen_dg(int, struct servtab *);
231#endif
232
233#ifndef INETD_UNSUPPORT_BILTIN
234struct biltin {
235 const char *bi_service; /* internally provided service name */
236 int bi_socktype; /* type of socket supported */
237 short bi_fork; /* 1 if should fork before call */
238 short bi_wait; /* 1 if should wait for child */
239 void (*bi_fn)(int, struct servtab *); /* fn which performs it */
240};
241
242static const struct biltin biltins[] = {
243#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
244 /* Echo received data */
245 { "echo", SOCK_STREAM, 1, 0, echo_stream, },
246 { "echo", SOCK_DGRAM, 0, 0, echo_dg, },
247#endif
248#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
249 /* Internet /dev/null */
250 { "discard", SOCK_STREAM, 1, 0, discard_stream, },
251 { "discard", SOCK_DGRAM, 0, 0, discard_dg, },
252#endif
253#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
254 /* Return 32 bit time since 1900 */
255 { "time", SOCK_STREAM, 0, 0, machtime_stream, },
256 { "time", SOCK_DGRAM, 0, 0, machtime_dg, },
257#endif
258#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
259 /* Return human-readable time */
260 { "daytime", SOCK_STREAM, 0, 0, daytime_stream, },
261 { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, },
262#endif
263#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
264 /* Familiar character generator */
265 { "chargen", SOCK_STREAM, 1, 0, chargen_stream, },
266 { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, },
267#endif
268 { NULL, 0, 0, 0, NULL }
269};
270#endif /* INETD_UNSUPPORT_BILTIN */
271
272#define NUMINT (sizeof(intab) / sizeof(struct inent))
273static const char *CONFIG = _PATH_INETDCONF;
274
275#define BCOPY(s, d, z) memcpy(d, s, z)
276
277static void
278syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
279 __attribute__ ((noreturn, format (printf, 2, 3)));
280
281static void
282syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
283{
284 char buf[50];
285 va_list p;
286
287 va_start(p, msg);
288 vsyslog(LOG_ERR, msg, p);
289 if (se_socktype != SOCK_STREAM)
290 recv(0, buf, sizeof (buf), 0);
291 _exit(1);
292}
293
294static FILE *fconfig;
295static char line[256];
296
297static FILE *
298setconfig(void)
299{
300 FILE *f = fconfig;
301
302 if (f != NULL) {
303 fseek(f, 0L, L_SET);
304 } else {
305 f = fconfig = fopen(CONFIG, "r");
306 if(f == NULL)
307 syslog(LOG_ERR, "%s: %m", CONFIG);
308 }
309 return f;
310}
311
312static char *
313nextline(void)
314{
315 char *cp;
316 FILE *fd = fconfig;
317
318 if (fgets(line, sizeof (line), fd) == NULL)
319 return ((char *)0);
320 cp = strchr(line, '\n');
321 if (cp)
322 *cp = '\0';
323 return (line);
324}
325
326static char *
327skip(char **cpp)
328{
329 char *cp = *cpp;
330 char *start;
331
332 if (*cpp == NULL)
333 return ((char *)0);
334
335again:
336 while (*cp == ' ' || *cp == '\t')
337 cp++;
338 if (*cp == '\0') {
339 int c;
340
341 c = getc(fconfig);
342 (void) ungetc(c, fconfig);
343 if (c == ' ' || c == '\t')
344 if ((cp = nextline()) != NULL)
345 goto again;
346 *cpp = NULL;
347 return NULL;
348 }
349 start = cp;
350 while (*cp && *cp != ' ' && *cp != '\t')
351 cp++;
352 if (*cp != '\0')
353 *cp++ = '\0';
354 *cpp = cp;
355 return (start);
356}
357
358static char *
359newstr(char *cp)
360{
361 cp = strdup(cp ? cp : "");
362 if (cp)
363 return(cp);
364
365 syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m");
366}
367
368
369static struct servtab *
370getconfigent(void)
371{
372 static struct servtab serv;
373 struct servtab *sep = &serv;
374 int argc;
375 char *cp, *arg;
376
377more:
378 while ((cp = nextline()) && *cp == '#')
379 ;
380 if (cp == NULL)
381 return ((struct servtab *)0);
382 memset((char *)sep, 0, sizeof *sep);
383 sep->se_service = newstr(skip(&cp));
384 arg = skip(&cp);
385 if (arg == NULL)
386 goto more;
387
388 if (strcmp(arg, "stream") == 0)
389 sep->se_socktype = SOCK_STREAM;
390 else if (strcmp(arg, "dgram") == 0)
391 sep->se_socktype = SOCK_DGRAM;
392 else if (strcmp(arg, "rdm") == 0)
393 sep->se_socktype = SOCK_RDM;
394 else if (strcmp(arg, "seqpacket") == 0)
395 sep->se_socktype = SOCK_SEQPACKET;
396 else if (strcmp(arg, "raw") == 0)
397 sep->se_socktype = SOCK_RAW;
398 else
399 sep->se_socktype = -1;
400
401 sep->se_proto = newstr(skip(&cp));
402 if (strcmp(sep->se_proto, "unix") == 0) {
403 sep->se_family = AF_UNIX;
404 } else {
405 sep->se_family = AF_INET;
406 if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
407 syslog(LOG_ERR, "%s: rpc services not suported",
408 sep->se_service);
409 goto more;
410 }
411 }
412 arg = skip(&cp);
413 if (arg == NULL)
414 goto more;
415 {
416 char *s = strchr(arg, '.');
417 if (s) {
418 *s++ = '\0';
419 sep->se_max = atoi(s);
420 } else
421 sep->se_max = TOOMANY;
422 }
423 sep->se_wait = strcmp(arg, "wait") == 0;
424 sep->se_user = newstr(skip(&cp));
425 sep->se_group = strchr(sep->se_user, '.');
426 if (sep->se_group) {
427 *sep->se_group++ = '\0';
428 }
429 sep->se_server = newstr(skip(&cp));
430 if (strcmp(sep->se_server, "internal") == 0) {
431#ifndef INETD_UNSUPPORT_BILTIN
432 const struct biltin *bi;
433
434 for (bi = biltins; bi->bi_service; bi++)
435 if (bi->bi_socktype == sep->se_socktype &&
436 strcmp(bi->bi_service, sep->se_service) == 0)
437 break;
438 if (bi->bi_service == 0) {
439 syslog(LOG_ERR, "internal service %s unknown",
440 sep->se_service);
441 goto more;
442 }
443 sep->se_bi = bi;
444 sep->se_wait = bi->bi_wait;
445#else
446 syslog(LOG_ERR, "internal service %s unknown",
447 sep->se_service);
448 goto more;
449#endif
450 } else
451#ifndef INETD_UNSUPPORT_BILTIN
452 sep->se_bi = NULL
453#endif
454 ;
455 argc = 0;
456 for (arg = skip(&cp); cp; arg = skip(&cp)) {
457 if (argc < MAXARGV)
458 sep->se_argv[argc++] = newstr(arg);
459 }
460 while (argc <= MAXARGV)
461 sep->se_argv[argc++] = NULL;
462 return (sep);
463}
464
465static void
466freeconfig(struct servtab *cp)
467{
468 int i;
469
470 free(cp->se_service);
471 free(cp->se_proto);
472 free(cp->se_user);
473 /* Note: se_group is part of the newstr'ed se_user */
474 free(cp->se_server);
475 for (i = 0; i < MAXARGV; i++)
476 free(cp->se_argv[i]);
477}
478
479#ifndef INETD_UNSUPPORT_BILTIN
480static char **Argv;
481static char *LastArg;
482
483static void
484setproctitle(char *a, int s)
485{
486 size_t size;
487 char *cp;
488 struct sockaddr_in sn;
489 char buf[80];
490
491 cp = Argv[0];
492 size = sizeof(sn);
493 if (getpeername(s, (struct sockaddr *)&sn, &size) == 0)
494 (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr));
495 else
496 (void) sprintf(buf, "-%s", a);
497 strncpy(cp, buf, LastArg - cp);
498 cp += strlen(cp);
499 while (cp < LastArg)
500 *cp++ = ' ';
501}
502#endif /* INETD_UNSUPPORT_BILTIN */
503
504static struct servtab *
505enter(struct servtab *cp)
506{
507 struct servtab *sep;
508 sigset_t oldmask;
509
510 sep = (struct servtab *)malloc(sizeof (*sep));
511 if (sep == NULL) {
512 syslog_err_and_discard_dg(SOCK_STREAM, memory_exhausted);
513 }
514 *sep = *cp;
515 sep->se_fd = -1;
516 sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
517 sep->se_next = servtab;
518 servtab = sep;
519 sigprocmask(SIG_SETMASK, &oldmask, NULL);
520 return (sep);
521}
522
523static int
524bump_nofile(void)
525{
526#ifdef RLIMIT_NOFILE
527
528#define FD_CHUNK 32
529
530 struct rlimit rl;
531
532 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
533 syslog(LOG_ERR, "getrlimit: %m");
534 return -1;
535 }
536 rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
537 if (rl.rlim_cur <= rlim_ofile_cur) {
538 syslog(LOG_ERR,
539 "bump_nofile: cannot extend file limit, max = %d",
540 rl.rlim_cur);
541 return -1;
542 }
543
544 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
545 syslog(LOG_ERR, "setrlimit: %m");
546 return -1;
547 }
548
549 rlim_ofile_cur = rl.rlim_cur;
550 return 0;
551
552#else
553 syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
554 return -1;
555#endif
556}
557
558
559static void
560setup(struct servtab *sep)
561{
562 int on = 1;
563
564 if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
565 syslog(LOG_ERR, "%s/%s: socket: %m",
566 sep->se_service, sep->se_proto);
567 return;
568 }
569 if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
570 sizeof(on)) < 0)
571 syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
572 if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
573 syslog(LOG_ERR, "%s/%s: bind: %m",
574 sep->se_service, sep->se_proto);
575 (void) close(sep->se_fd);
576 sep->se_fd = -1;
577 if (!timingout) {
578 timingout = 1;
579 alarm(RETRYTIME);
580 }
581 return;
582 }
583 if (sep->se_socktype == SOCK_STREAM)
584 listen(sep->se_fd, global_queuelen);
585
586 FD_SET(sep->se_fd, &allsock);
587 nsock++;
588 if (sep->se_fd > maxsock) {
589 maxsock = sep->se_fd;
590 if (maxsock > rlim_ofile_cur - FD_MARGIN)
591 bump_nofile();
592 }
593}
594
595static void
596config(int signum)
597{
598 struct servtab *sep, *cp, **sepp;
599 sigset_t oldmask;
600 unsigned n;
601
602 (void)signum;
603 if (setconfig() == NULL)
604 return;
605
606 for (sep = servtab; sep; sep = sep->se_next)
607 sep->se_checked = 0;
608 while ((cp = getconfigent()) != NULL) {
609 for (sep = servtab; sep; sep = sep->se_next)
610 if (strcmp(sep->se_service, cp->se_service) == 0 &&
611 strcmp(sep->se_proto, cp->se_proto) == 0)
612 break;
613 if (sep != 0) {
614 int i;
615
616#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
617
618 sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
619 /*
620 * sep->se_wait may be holding the pid of a daemon
621 * that we're waiting for. If so, don't overwrite
622 * it unless the config file explicitly says don't
623 * wait.
624 */
625 if (
626#ifndef INETD_UNSUPPORT_BILTIN
627 cp->se_bi == 0 &&
628#endif
629 (sep->se_wait == 1 || cp->se_wait == 0))
630 sep->se_wait = cp->se_wait;
631 if (cp->se_max != sep->se_max)
632 SWAP(int, cp->se_max, sep->se_max);
633 if (cp->se_user)
634 SWAP(char *, sep->se_user, cp->se_user);
635 if (cp->se_group)
636 SWAP(char *, sep->se_group, cp->se_group);
637 if (cp->se_server)
638 SWAP(char *, sep->se_server, cp->se_server);
639 for (i = 0; i < MAXARGV; i++)
640 SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
641#undef SWAP
642 sigprocmask(SIG_SETMASK, &oldmask, NULL);
643 freeconfig(cp);
644 } else {
645 sep = enter(cp);
646 }
647 sep->se_checked = 1;
648
649 switch (sep->se_family) {
650 case AF_UNIX:
651 if (sep->se_fd != -1)
652 break;
653 (void)unlink(sep->se_service);
654 n = strlen(sep->se_service);
655 if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1)
656 n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
657 strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n);
658 sep->se_ctrladdr_un.sun_family = AF_UNIX;
659 sep->se_ctrladdr_size = n +
660 sizeof sep->se_ctrladdr_un.sun_family;
661 setup(sep);
662 break;
663 case AF_INET:
664 sep->se_ctrladdr_in.sin_family = AF_INET;
665 sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
666 {
667 u_short port = htons(atoi(sep->se_service));
668
669 if (!port) {
670 struct servent *sp;
671 sp = getservbyname(sep->se_service,
672 sep->se_proto);
673 if (sp == 0) {
674 syslog(LOG_ERR,
675 "%s/%s: unknown service",
676 sep->se_service, sep->se_proto);
677 continue;
678 }
679 port = sp->s_port;
680 }
681 if (port != sep->se_ctrladdr_in.sin_port) {
682 sep->se_ctrladdr_in.sin_port = port;
683 if (sep->se_fd != -1) {
684 FD_CLR(sep->se_fd, &allsock);
685 nsock--;
686 (void) close(sep->se_fd);
687 }
688 sep->se_fd = -1;
689 }
690 if (sep->se_fd == -1)
691 setup(sep);
692 }
693 }
694 }
695 if (fconfig) {
696 (void) fclose(fconfig);
697 fconfig = NULL;
698 }
699 /*
700 * Purge anything not looked at above.
701 */
702 sigprocmask(SIG_SETMASK, &blockmask, &oldmask);
703 sepp = &servtab;
704 while ((sep = *sepp) != NULL) {
705 if (sep->se_checked) {
706 sepp = &sep->se_next;
707 continue;
708 }
709 *sepp = sep->se_next;
710 if (sep->se_fd != -1) {
711 FD_CLR(sep->se_fd, &allsock);
712 nsock--;
713 (void) close(sep->se_fd);
714 }
715 if (sep->se_family == AF_UNIX)
716 (void)unlink(sep->se_service);
717 freeconfig(sep);
718 free((char *)sep);
719 }
720 sigprocmask(SIG_SETMASK, &oldmask, NULL);
721}
722
723
724
725static void
726reapchild(int signum)
727{
728 int status;
729 int pid;
730 struct servtab *sep;
731
732 (void)signum;
733 for (;;) {
734 pid = wait3(&status, WNOHANG, (struct rusage *)0);
735 if (pid <= 0)
736 break;
737 for (sep = servtab; sep; sep = sep->se_next)
738 if (sep->se_wait == pid) {
739 if (WIFEXITED(status) && WEXITSTATUS(status))
740 syslog(LOG_WARNING,
741 "%s: exit status 0x%x",
742 sep->se_server, WEXITSTATUS(status));
743 else if (WIFSIGNALED(status))
744 syslog(LOG_WARNING,
745 "%s: exit signal 0x%x",
746 sep->se_server, WTERMSIG(status));
747 sep->se_wait = 1;
748 FD_SET(sep->se_fd, &allsock);
749 nsock++;
750 }
751 }
752}
753
754static void
755retry(int signum)
756{
757 struct servtab *sep;
758
759 (void)signum;
760 timingout = 0;
761 for (sep = servtab; sep; sep = sep->se_next) {
762 if (sep->se_fd == -1) {
763 switch (sep->se_family) {
764 case AF_UNIX:
765 case AF_INET:
766 setup(sep);
767 break;
768 }
769 }
770 }
771}
772
773static void
774goaway(int signum)
775{
776 struct servtab *sep;
777
778 (void)signum;
779 for (sep = servtab; sep; sep = sep->se_next)
780 if (sep->se_fd != -1 && sep->se_family == AF_UNIX)
781 (void)unlink(sep->se_service);
782 (void)unlink(_PATH_INETDPID);
783 exit(0);
784}
785
786
787
788extern int
789inetd_main(int argc, char *argv[])
790{
791 struct servtab *sep;
792 struct passwd *pwd;
793 struct group *grp = NULL;
794 struct sigaction sa;
795 int ch, pid;
796 gid_t gid;
797
798#ifdef INETD_UNSUPPORT_BILTIN
799# define dofork 1
800#else
801 int dofork;
802 extern char **environ;
803#endif
804
805 gid = getgid();
806 setgroups(1, &gid);
807
808#ifndef INETD_UNSUPPORT_BILTIN
809 Argv = argv;
810 if (environ == 0 || *environ == 0)
811 environ = argv;
812 while (*environ)
813 environ++;
814 LastArg = environ[-1] + strlen(environ[-1]);
815#endif
816
817 while ((ch = getopt(argc, argv, "q:")) != EOF)
818 switch(ch) {
819 case 'q':
820 global_queuelen = atoi(optarg);
821 if (global_queuelen < 8) global_queuelen=8;
822 break;
823 default:
824 show_usage(); // "[-q len] [conf]"
825 }
826 argc -= optind;
827 argv += optind;
828
829 if (argc > 0)
830 CONFIG = argv[0];
831
832 daemon(0, 0);
833 openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
834 {
835 FILE *fp;
836
837 if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
838 fprintf(fp, "%u\n", getpid());
839 (void)fclose(fp);
840 }
841 }
842
843#ifdef RLIMIT_NOFILE
844 if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
845 syslog(LOG_ERR, "getrlimit: %m");
846 } else {
847 rlim_ofile_cur = rlim_ofile.rlim_cur;
848 if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
849 rlim_ofile_cur = OPEN_MAX;
850 }
851#endif
852
853 config(0);
854
855 sigemptyset(&emptymask);
856 sigemptyset(&blockmask);
857 sigaddset(&blockmask, SIGCHLD);
858 sigaddset(&blockmask, SIGHUP);
859 sigaddset(&blockmask, SIGALRM);
860
861 memset(&sa, 0, sizeof(sa));
862 sa.sa_mask = blockmask;
863 sa.sa_handler = retry;
864 sigaction(SIGALRM, &sa, NULL);
865 sa.sa_handler = config;
866 sigaction(SIGHUP, &sa, NULL);
867 sa.sa_handler = reapchild;
868 sigaction(SIGCHLD, &sa, NULL);
869 sa.sa_handler = goaway;
870 sigaction(SIGTERM, &sa, NULL);
871 sa.sa_handler = goaway;
872 sigaction(SIGINT, &sa, NULL);
873 sa.sa_handler = SIG_IGN;
874 sigaction(SIGPIPE, &sa, NULL);
875
876 {
877 /* space for daemons to overwrite environment for ps */
878#define DUMMYSIZE 100
879 char dummy[DUMMYSIZE];
880
881 (void)memset(dummy, 'x', DUMMYSIZE - 1);
882 dummy[DUMMYSIZE - 1] = '\0';
883
884 (void)setenv("inetd_dummy", dummy, 1);
885 }
886
887 for (;;) {
888 int n, ctrl;
889 fd_set readable;
890
891 if (nsock == 0) {
892 sigprocmask(SIG_BLOCK, &blockmask, NULL);
893 while (nsock == 0)
894 sigsuspend(&emptymask);
895 sigprocmask(SIG_SETMASK, &emptymask, NULL);
896 }
897 readable = allsock;
898 if ((n = select(maxsock + 1, &readable, (fd_set *)0,
899 (fd_set *)0, (struct timeval *)0)) <= 0) {
900 if (n < 0 && errno != EINTR)
901 syslog(LOG_WARNING, "select: %m");
902 sleep(1);
903 continue;
904 }
905 for (sep = servtab; n && sep; sep = sep->se_next)
906 if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
907 n--;
908 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
909 /* Fixed AGC */
910 fcntl(sep->se_fd, F_SETFL, O_NDELAY);
911 /* --------- */
912 ctrl = accept(sep->se_fd, NULL, NULL);
913 fcntl(sep->se_fd, F_SETFL, 0);
914 if (ctrl < 0) {
915 if (errno == EINTR || errno == EWOULDBLOCK)
916 continue;
917 syslog(LOG_WARNING, "accept (for %s): %m",
918 sep->se_service);
919 continue;
920 }
921 } else
922 ctrl = sep->se_fd;
923 sigprocmask(SIG_BLOCK, &blockmask, NULL);
924 pid = 0;
925#ifndef INETD_UNSUPPORT_BILTIN
926 dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
927#endif
928 if (dofork) {
929 if (sep->se_count++ == 0)
930 (void)gettimeofday(&sep->se_time,
931 (struct timezone *)0);
932 else if (sep->se_count >= sep->se_max) {
933 struct timeval now;
934
935 (void)gettimeofday(&now, (struct timezone *)0);
936 if (now.tv_sec - sep->se_time.tv_sec >
937 CNT_INTVL) {
938 sep->se_time = now;
939 sep->se_count = 1;
940 } else {
941 syslog(LOG_ERR,
942 "%s/%s server failing (looping), service terminated",
943 sep->se_service, sep->se_proto);
944 FD_CLR(sep->se_fd, &allsock);
945 (void) close(sep->se_fd);
946 sep->se_fd = -1;
947 sep->se_count = 0;
948 nsock--;
949 sigprocmask(SIG_SETMASK, &emptymask,
950 NULL);
951 if (!timingout) {
952 timingout = 1;
953 alarm(RETRYTIME);
954 }
955 continue;
956 }
957 }
958 pid = fork();
959 }
960 if (pid < 0) {
961 syslog(LOG_ERR, "fork: %m");
962 if (sep->se_socktype == SOCK_STREAM)
963 close(ctrl);
964 sigprocmask(SIG_SETMASK, &emptymask, NULL);
965 sleep(1);
966 continue;
967 }
968 if (pid && sep->se_wait) {
969 sep->se_wait = pid;
970 FD_CLR(sep->se_fd, &allsock);
971 nsock--;
972 }
973 sigprocmask(SIG_SETMASK, &emptymask, NULL);
974 if (pid == 0) {
975#ifndef INETD_UNSUPPORT_BILTIN
976 if (sep->se_bi)
977 (*sep->se_bi->bi_fn)(ctrl, sep);
978 else
979#endif
980 {
981 if ((pwd = getpwnam(sep->se_user)) == NULL) {
982 syslog_err_and_discard_dg(
983 sep->se_socktype,
984 "getpwnam: %s: No such user",
985 sep->se_user);
986 }
987 if (sep->se_group &&
988 (grp = getgrnam(sep->se_group)) == NULL) {
989 syslog_err_and_discard_dg(
990 sep->se_socktype,
991 "getgrnam: %s: No such group",
992 sep->se_group);
993 }
994 /*
995 * Ok. There are four cases here:
996 * 1. nonroot user, no group specified
997 * 2. nonroot user, some group specified
998 * 3. root user, no group specified
999 * 4. root user, some group specified
1000 * In cases 2 and 4 we setgid to the specified
1001 * group. In cases 1 and 2 we run initgroups
1002 * to run with the groups of the given user.
1003 * In case 4 we do setgroups to run with the
1004 * given group. In case 3 we do nothing.
1005 */
1006 if (pwd->pw_uid) {
1007 if (sep->se_group)
1008 pwd->pw_gid = grp->gr_gid;
1009 setgid((gid_t)pwd->pw_gid);
1010 initgroups(pwd->pw_name, pwd->pw_gid);
1011 setuid((uid_t)pwd->pw_uid);
1012 } else if (sep->se_group) {
1013 setgid((gid_t)grp->gr_gid);
1014 setgroups(1, &grp->gr_gid);
1015 }
1016 dup2(ctrl, 0);
1017 close(ctrl);
1018 dup2(0, 1);
1019 dup2(0, 2);
1020#ifdef RLIMIT_NOFILE
1021 if (rlim_ofile.rlim_cur != rlim_ofile_cur) {
1022 if (setrlimit(RLIMIT_NOFILE,
1023 &rlim_ofile) < 0)
1024 syslog(LOG_ERR,"setrlimit: %m");
1025 }
1026#endif
1027 for (ctrl = rlim_ofile_cur-1; --ctrl > 2; )
1028 (void)close(ctrl);
1029
1030 memset(&sa, 0, sizeof(sa));
1031 sa.sa_handler = SIG_DFL;
1032 sigaction(SIGPIPE, &sa, NULL);
1033
1034 execv(sep->se_server, sep->se_argv);
1035 syslog_err_and_discard_dg(sep->se_socktype,
1036 "execv %s: %m", sep->se_server);
1037 }
1038 }
1039 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
1040 close(ctrl);
1041 }
1042 }
1043}
1044
1045
1046/*
1047 * Internet services provided internally by inetd:
1048 */
1049#define BUFSIZE 4096
1050
1051#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
1052/* Echo service -- echo data back */
1053static void
1054echo_stream(int s, struct servtab *sep)
1055{
1056 char buffer[BUFSIZE];
1057 int i;
1058
1059 setproctitle(sep->se_service, s);
1060 while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1061 write(s, buffer, i) > 0)
1062 ;
1063 exit(0);
1064}
1065
1066/* Echo service -- echo data back */
1067static void
1068echo_dg(int s, struct servtab *sep)
1069{
1070 char buffer[BUFSIZE];
1071 int i;
1072 size_t size;
1073 struct sockaddr sa;
1074
1075 (void)sep;
1076
1077 size = sizeof(sa);
1078 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
1079 return;
1080 (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
1081}
1082#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */
1083
1084
1085#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
1086/* Discard service -- ignore data */
1087static void
1088discard_stream(int s, struct servtab *sep)
1089{
1090 char buffer[BUFSIZE];
1091
1092 setproctitle(sep->se_service, s);
1093 while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1094 errno == EINTR)
1095 ;
1096 exit(0);
1097}
1098
1099/* Discard service -- ignore data */
1100static void
1101discard_dg(int s, struct servtab *sep)
1102{
1103 char buffer[BUFSIZE];
1104 (void)sep;
1105 read(s, buffer, sizeof(buffer));
1106}
1107#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */
1108
1109
1110#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
1111#include <ctype.h>
1112#define LINESIZ 72
1113static char ring[128];
1114static char *endring;
1115
1116static void
1117initring(void)
1118{
1119 int i;
1120
1121 endring = ring;
1122
1123 for (i = 0; i <= 128; ++i)
1124 if (isprint(i))
1125 *endring++ = i;
1126}
1127
1128/* Character generator */
1129static void
1130chargen_stream(int s, struct servtab *sep)
1131{
1132 char *rs;
1133 int len;
1134 char text[LINESIZ+2];
1135
1136 setproctitle(sep->se_service, s);
1137
1138 if (!endring) {
1139 initring();
1140 rs = ring;
1141 }
1142
1143 text[LINESIZ] = '\r';
1144 text[LINESIZ + 1] = '\n';
1145 for (rs = ring;;) {
1146 if ((len = endring - rs) >= LINESIZ)
1147 BCOPY(rs, text, LINESIZ);
1148 else {
1149 BCOPY(rs, text, len);
1150 BCOPY(ring, text + len, LINESIZ - len);
1151 }
1152 if (++rs == endring)
1153 rs = ring;
1154 if (write(s, text, sizeof(text)) != sizeof(text))
1155 break;
1156 }
1157 exit(0);
1158}
1159
1160/* Character generator */
1161static void
1162chargen_dg(int s, struct servtab *sep)
1163{
1164 struct sockaddr sa;
1165 static char *rs;
1166 size_t len, size;
1167 char text[LINESIZ+2];
1168
1169 (void)sep;
1170
1171 if (endring == 0) {
1172 initring();
1173 rs = ring;
1174 }
1175
1176 size = sizeof(sa);
1177 if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
1178 return;
1179
1180 if ((len = endring - rs) >= LINESIZ)
1181 BCOPY(rs, text, LINESIZ);
1182 else {
1183 BCOPY(rs, text, len);
1184 BCOPY(ring, text + len, LINESIZ - len);
1185 }
1186 if (++rs == endring)
1187 rs = ring;
1188 text[LINESIZ] = '\r';
1189 text[LINESIZ + 1] = '\n';
1190 (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
1191}
1192#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */
1193
1194
1195#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
1196/*
1197 * Return a machine readable date and time, in the form of the
1198 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1199 * returns the number of seconds since midnight, Jan 1, 1970,
1200 * we must add 2208988800 seconds to this figure to make up for
1201 * some seventy years Bell Labs was asleep.
1202 */
1203
1204static long
1205machtime(void)
1206{
1207 struct timeval tv;
1208
1209 if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1210 fprintf(stderr, "Unable to get time of day\n");
1211 return (0L);
1212 }
1213 return (htonl((long)tv.tv_sec + 2208988800UL));
1214}
1215
1216static void
1217machtime_stream(int s, struct servtab *sep)
1218{
1219 long result;
1220 (void)sep;
1221
1222 result = machtime();
1223 write(s, (char *) &result, sizeof(result));
1224}
1225
1226static void
1227machtime_dg(int s, struct servtab *sep)
1228{
1229 long result;
1230 struct sockaddr sa;
1231 size_t size;
1232 (void)sep;
1233
1234 size = sizeof(sa);
1235 if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
1236 return;
1237 result = machtime();
1238 (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
1239}
1240#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */
1241
1242
1243#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
1244/* Return human-readable time of day */
1245static int
1246human_readable_time_sprintf(char *buffer)
1247{
1248 time_t clocc = time(NULL);
1249
1250 return sprintf(buffer, "%.24s\r\n", ctime(&clocc));
1251}
1252
1253static void
1254daytime_stream(int s, struct servtab *sep)
1255{
1256 char buffer[256];
1257 size_t st = human_readable_time_sprintf(buffer);
1258
1259 (void)sep;
1260
1261 write(s, buffer, st);
1262}
1263
1264/* Return human-readable time of day */
1265static void
1266daytime_dg(int s, struct servtab *sep)
1267{
1268 char buffer[256];
1269 struct sockaddr sa;
1270 size_t size;
1271
1272 (void)sep;
1273
1274 size = sizeof(sa);
1275 if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
1276 return;
1277 size = human_readable_time_sprintf(buffer);
1278 sendto(s, buffer, size, 0, &sa, sizeof(sa));
1279}
1280#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */