diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2003-02-09 06:51:14 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2003-02-09 06:51:14 +0000 |
commit | 06e9565b6c365668dafeef1fdc0e60c9a1154623 (patch) | |
tree | c75619aad6d3ca6f4c32cd8d1dffbe4f6c53367b | |
parent | 877d418b39421361800afe4e1d4ca69b80edc121 (diff) | |
download | busybox-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-- | AUTHORS | 7 | ||||
-rw-r--r-- | docs/busybox_footer.pod | 12 | ||||
-rw-r--r-- | docs/busybox_header.pod | 19 | ||||
-rw-r--r-- | include/applets.h | 3 | ||||
-rw-r--r-- | include/usage.h | 8 | ||||
-rw-r--r-- | networking/Config.in | 113 | ||||
-rw-r--r-- | networking/Makefile.in | 1 | ||||
-rw-r--r-- | networking/httpd.c | 1948 | ||||
-rw-r--r-- | networking/inetd.c | 1280 |
9 files changed, 2636 insertions, 755 deletions
@@ -44,6 +44,9 @@ Magnus Damm <damm@opensource.se> | |||
44 | Larry Doolittle <ldoolitt@recycle.lbl.gov> | 44 | Larry 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 | ||
47 | Glenn Engel <glenne@engel.org> | ||
48 | httpd | ||
49 | |||
47 | Gennady Feldman <gfeldman@gena01.com> | 50 | Gennady 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 | ||
69 | Vladimir Oleynik <dzo@simtreas.ru> | 72 | Vladimir 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 | ||
114 | Vladimir Oleynik <dzo@simtreas.ru> | 114 | Vladimir 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 | |||
171 | Glenn 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, | |||
60 | date, dc, dd, deallocvt, deluser, df, dirname, dmesg, dos2unix, dpkg, | 60 | date, dc, dd, deallocvt, deluser, df, dirname, dmesg, dos2unix, dpkg, |
61 | dpkg-deb, du, dumpkmap, dutmp, echo, expr, false, fbset, fdflush, fdisk, | 61 | dpkg-deb, du, dumpkmap, dutmp, echo, expr, false, fbset, fdflush, fdisk, |
62 | find, free, freeramdisk, fsck.minix, getopt, getty, grep, gunzip, gzip, | 62 | find, free, freeramdisk, fsck.minix, getopt, getty, grep, gunzip, gzip, |
63 | halt, head, hostid, hostname, id, ifconfig, init, insmod, kill, killall, | 63 | halt, head, hostid, hostname, httpd, id, ifconfig, inetd, init, insmod, |
64 | klogd, length, ln, loadacm, loadfont, loadkmap, logger, logname, ls, lsmod, | 64 | kill, killall, klogd, length, ln, loadacm, loadfont, loadkmap, logger, |
65 | makedevs, md5sum, mkdir, mkfifo, mkfs.minix, mknod, mkswap, mktemp, more, | 65 | logname, ls, lsmod, makedevs, md5sum, mkdir, mkfifo, mkfs.minix, mknod, |
66 | mount, mt, mv, nc, netstat, nslookup, ping, pivot_root, poweroff, printf, | 66 | mkswap, mktemp, more, mount, mt, mv, nc, netstat, nslookup, ping, |
67 | ps, pwd, rdate, readlink, reboot, renice, reset, rm, rmdir, rmmod, route, | 67 | pivot_root, poweroff, printf, ps, pwd, rdate, readlink, reboot, renice, |
68 | rpm2cpio, sed, setkeycodes, sh, sleep, sort, stty, swapoff, swapon, sync, | 68 | reset, rm, rmdir, rmmod, route, rpm2cpio, sed, setkeycodes, sh, sleep, |
69 | syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top, touch, tr, | 69 | sort, stty, swapoff, swapon, sync, syslogd, tail, tar, tee, telnet, |
70 | true, tty, umount, uname, uniq, unix2dos, update, uptime, usleep, uudecode, | 70 | telnetd, test, tftp, time, top, touch, tr, true, tty, umount, uname, uniq, |
71 | uuencode, watchdog, wc, wget, which, whoami, xargs, yes, zcat, [ | 71 | unix2dos, update, uptime, usleep, uudecode, uuencode, watchdog, wc, wget, |
72 | which, 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 | ||
44 | config 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 | |||
44 | config CONFIG_FEATURE_HTTPD_BASIC_AUTH | 53 | config 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 | ||
61 | config 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 | |||
68 | config 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 | |||
75 | config 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 | |||
82 | config 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 | |||
90 | config 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 | |||
97 | config 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 | |||
104 | config 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 | |||
112 | config 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 | |||
52 | config CONFIG_IFCONFIG | 121 | config 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 | ||
204 | config CONFIG_INETD | ||
205 | bool "inetd" | ||
206 | default n | ||
207 | help | ||
208 | Internet superserver daemon | ||
209 | |||
210 | config 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 | |||
217 | config 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 | |||
224 | config 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 | |||
231 | config 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 | |||
238 | config 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 | |||
135 | config CONFIG_IP | 246 | config 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 | ||
318 | config CONFIG_FEATURE_TELNETD_INETD | 429 | config 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 | |||
30 | NETWORKING-$(CONFIG_HTTPD) += httpd.o | 30 | NETWORKING-$(CONFIG_HTTPD) += httpd.o |
31 | NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o | 31 | NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o |
32 | NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o | 32 | NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o |
33 | NETWORKING-$(CONFIG_INETD) += inetd.o | ||
33 | NETWORKING-$(CONFIG_IP) += ip.o | 34 | NETWORKING-$(CONFIG_IP) += ip.o |
34 | NETWORKING-$(CONFIG_IPCALC) += ipcalc.o | 35 | NETWORKING-$(CONFIG_IPCALC) += ipcalc.o |
35 | NETWORKING-$(CONFIG_IPADDR) += ipaddr.o | 36 | NETWORKING-$(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 "<Hello World>" | 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 | |||
44 | ip:10.10. # Allow any address that begins with 10.10. | ||
45 | ip:172.20. # Allow 172.20.x.x | ||
46 | ip: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 | * | 49 | A:172.20. # Allow any address that begins with 172.20 |
51 | * To open up the server: | 50 | A:10.10. # Allow any address that begins with 10.10. |
52 | * ip:* # Allow any IP address | 51 | A:10.10 # Allow any address that previous set and 10.100-109.X.X |
53 | * /:* # no password required for urls starting with / (all) | 52 | A:127.0.0.1 # Allow local loopback connections |
54 | * | 53 | D:* # 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: | 59 | A shortes path and D:from[^*] automaticaly sorting to top. |
61 | * cache httpd.conf | 60 | All longest path can`t reset user:password if shorted protect setted. |
62 | * support tinylogin | 61 | |
63 | * | 62 | A/D may be as a/d or allow/deny - first char case unsensitive parsed only. |
63 | |||
64 | Each subdir can have config file. | ||
65 | For protect as user:pass current subdir and subpathes set from subdir config: | ||
66 | /:user:pass | ||
67 | if not, other subpathes for give effect must have path from httpd root | ||
68 | /current_subdir_path_from_httpd_root/subpath:user:pass | ||
69 | |||
70 | The Deny/Allow IP logic: | ||
71 | |||
72 | 1. Allow all: | ||
73 | The config don`t set D: lines | ||
74 | |||
75 | 2. Allow from setted only: | ||
76 | see the begin format example | ||
77 | |||
78 | 3. Set deny, allow from other: | ||
79 | D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255 | ||
80 | D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255 | ||
81 | A:* # allow from other, this line not strongly require | ||
82 | |||
83 | A global and subdirs config merging logic: | ||
84 | allow rules reducing, deny lines strongled. | ||
85 | The algorithm combinations: | ||
86 | |||
87 | 4. If current config have | ||
88 | A:from | ||
89 | D:* | ||
90 | subdir config A: lines skiping, D:from - moving top | ||
91 | |||
92 | 5. If current config have | ||
93 | D:from | ||
94 | A:* | ||
95 | result config: | ||
96 | D:from current | ||
97 | D:from subdir | ||
98 | A: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 | ||
82 | static const char httpdVersion[] = "busybox httpd/1.13 3-Jan-2003"; | 123 | static const char httpdVersion[] = "busybox httpd/1.20 31-Jan-2003"; |
124 | static const char default_patch_httpd_conf[] = "/etc"; | ||
125 | static const char httpd_conf[] = "httpd.conf"; | ||
126 | static 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 |
94 | void 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 */ | ||
181 | const char *applet_name = "httpd"; | ||
182 | |||
183 | void 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 |
104 | int debugHttpd; | 204 | |
105 | static char **envp; | 205 | #define MAX_POST_SIZE (64*1024) /* 64k. Its Small? May be ;) */ |
106 | static int envCount; | 206 | |
107 | static char *realm = "Web Server Authentication"; | 207 | #define MAX_MEMORY_BUFF 8192 /* IO buffer */ |
108 | static char *configFile; | 208 | |
209 | typedef struct HT_ACCESS { | ||
210 | char *after_colon; | ||
211 | struct HT_ACCESS *next; | ||
212 | char before_colon[1]; /* really bigger, must last */ | ||
213 | } Htaccess; | ||
214 | |||
215 | typedef 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 | |||
247 | static HttpdConfig *config; | ||
248 | |||
249 | static const char request_GET[] = "GET"; /* size algorithic optimize */ | ||
109 | 250 | ||
110 | static const char* const suffixTable [] = { | 251 | static 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 | ||
119 | typedef enum | 273 | typedef 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 | ||
153 | static const HttpEnumString httpResponseNames[] = { | 307 | static 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 | |||
333 | static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT"; | ||
334 | static 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 | */ | ||
346 | static 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 | |||
409 | static 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 | ****************************************************************************/ |
271 | static void addEnv(const char *name, const char *value) | 665 | static 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 */ | ||
687 | static 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) | |||
302 | static void addEnvCgi(const char *pargs) | 715 | static 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 | ||
337 | static 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 | ****************************************************************************/ |
384 | static size_t decodeBase64(void *outData, size_t outDataLen, | 759 | static 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 | ****************************************************************************/ | ||
447 | static 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)) | ||
472 | static 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 | ****************************************************************************/ |
506 | static int openServer(int port) | 807 | static 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 */ | |
548 | static 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 | ****************************************************************************/ |
576 | static int sendHeaders(int s, HttpResponseNum responseNum , | 856 | static 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 | ****************************************************************************/ |
657 | static int getLine(int s, char *buf, int maxBuf) | 923 | static 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 | ****************************************************************************/ |
697 | static int sendCgi(int s, const char *url, | 964 | static 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 | ****************************************************************************/ |
893 | static int sendFile(int s, const char *url) | 1183 | static 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 | |||
978 | static int checkPerm(const char *path, const char *request) | 1257 | static 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 | ****************************************************************************/ |
1045 | static int handleIncoming(int s) | 1333 | static 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) { | ||
1361 | BAD_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 | ||
1495 | FORBIDDEN: /* 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 | ||
1206 | static int miniHttpd(int server) | 1586 | static 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 | |||
1655 | static 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 | ||
1675 | static 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 | ||
1690 | int main(int argc, char *argv[]) | ||
1691 | #else | ||
1281 | int httpd_main(int argc, char *argv[]) | 1692 | int 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 | ||
1344 | int 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) | ||
153 | static int rlim_ofile_cur = OPEN_MAX; | ||
154 | |||
155 | #ifdef RLIMIT_NOFILE | ||
156 | static struct rlimit rlim_ofile; | ||
157 | #endif | ||
158 | |||
159 | |||
160 | static 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. */ | ||
192 | static int global_queuelen = 128; | ||
193 | |||
194 | static int nsock, maxsock; | ||
195 | static fd_set allsock; | ||
196 | static int timingout; | ||
197 | static 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 | ||
205 | static void echo_stream(int, struct servtab *); | ||
206 | static 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 | ||
211 | static void discard_stream(int, struct servtab *); | ||
212 | static 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 | ||
217 | static void machtime_stream(int, struct servtab *); | ||
218 | static 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 | ||
223 | static void daytime_stream(int, struct servtab *); | ||
224 | static 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 | ||
229 | static void chargen_stream(int, struct servtab *); | ||
230 | static void chargen_dg(int, struct servtab *); | ||
231 | #endif | ||
232 | |||
233 | #ifndef INETD_UNSUPPORT_BILTIN | ||
234 | struct 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 | |||
242 | static 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)) | ||
273 | static const char *CONFIG = _PATH_INETDCONF; | ||
274 | |||
275 | #define BCOPY(s, d, z) memcpy(d, s, z) | ||
276 | |||
277 | static void | ||
278 | syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) | ||
279 | __attribute__ ((noreturn, format (printf, 2, 3))); | ||
280 | |||
281 | static void | ||
282 | syslog_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 | |||
294 | static FILE *fconfig; | ||
295 | static char line[256]; | ||
296 | |||
297 | static FILE * | ||
298 | setconfig(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 | |||
312 | static char * | ||
313 | nextline(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 | |||
326 | static char * | ||
327 | skip(char **cpp) | ||
328 | { | ||
329 | char *cp = *cpp; | ||
330 | char *start; | ||
331 | |||
332 | if (*cpp == NULL) | ||
333 | return ((char *)0); | ||
334 | |||
335 | again: | ||
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 | |||
358 | static char * | ||
359 | newstr(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 | |||
369 | static struct servtab * | ||
370 | getconfigent(void) | ||
371 | { | ||
372 | static struct servtab serv; | ||
373 | struct servtab *sep = &serv; | ||
374 | int argc; | ||
375 | char *cp, *arg; | ||
376 | |||
377 | more: | ||
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 | |||
465 | static void | ||
466 | freeconfig(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 | ||
480 | static char **Argv; | ||
481 | static char *LastArg; | ||
482 | |||
483 | static void | ||
484 | setproctitle(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 | |||
504 | static struct servtab * | ||
505 | enter(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 | |||
523 | static int | ||
524 | bump_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 | |||
559 | static void | ||
560 | setup(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 | |||
595 | static void | ||
596 | config(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 | |||
725 | static void | ||
726 | reapchild(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 | |||
754 | static void | ||
755 | retry(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 | |||
773 | static void | ||
774 | goaway(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 | |||
788 | extern int | ||
789 | inetd_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 */ | ||
1053 | static void | ||
1054 | echo_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 */ | ||
1067 | static void | ||
1068 | echo_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 */ | ||
1087 | static void | ||
1088 | discard_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 */ | ||
1100 | static void | ||
1101 | discard_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 | ||
1113 | static char ring[128]; | ||
1114 | static char *endring; | ||
1115 | |||
1116 | static void | ||
1117 | initring(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 */ | ||
1129 | static void | ||
1130 | chargen_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 */ | ||
1161 | static void | ||
1162 | chargen_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 | |||
1204 | static long | ||
1205 | machtime(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 | |||
1216 | static void | ||
1217 | machtime_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 | |||
1226 | static void | ||
1227 | machtime_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 */ | ||
1245 | static int | ||
1246 | human_readable_time_sprintf(char *buffer) | ||
1247 | { | ||
1248 | time_t clocc = time(NULL); | ||
1249 | |||
1250 | return sprintf(buffer, "%.24s\r\n", ctime(&clocc)); | ||
1251 | } | ||
1252 | |||
1253 | static void | ||
1254 | daytime_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 */ | ||
1265 | static void | ||
1266 | daytime_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 */ | ||