resolved conflict before merge
[tomato/tomato-dir865l.git] / release / src / router / vsftpd / standalone.c
blobba01ab16bbde5c169aedd26f1917e957d1ddcbac
1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * standalone.c
7 * Code to listen on the network and launch children servants.
8 */
10 #include "standalone.h"
12 #include "parseconf.h"
13 #include "tunables.h"
14 #include "sysutil.h"
15 #include "sysdeputil.h"
16 #include "utility.h"
17 #include "defs.h"
18 #include "hash.h"
19 #include "str.h"
20 #include "ipaddrparse.h"
22 static unsigned int s_children;
23 static struct hash* s_p_ip_count_hash;
24 static struct hash* s_p_pid_ip_hash;
25 static unsigned int s_ipaddr_size;
27 static void handle_sigchld(void* duff);
28 static void handle_sighup(void* duff);
29 static void prepare_child(int sockfd);
30 static unsigned int handle_ip_count(void* p_raw_addr);
31 static void drop_ip_count(void* p_raw_addr);
33 static unsigned int hash_ip(unsigned int buckets, void* p_key);
34 static unsigned int hash_pid(unsigned int buckets, void* p_key);
36 struct vsf_client_launch
37 vsf_standalone_main(void)
39 struct vsf_sysutil_sockaddr* p_accept_addr = 0;
40 int listen_sock = -1;
41 int retval;
42 s_ipaddr_size = vsf_sysutil_get_ipaddr_size();
43 if (tunable_listen && tunable_listen_ipv6)
45 die("run two copies of vsftpd for IPv4 and IPv6");
47 if (tunable_background)
49 int forkret = vsf_sysutil_fork();
50 if (forkret > 0)
52 /* Parent, just exit */
53 vsf_sysutil_exit(0);
55 /* Son, close standard FDs to avoid SSH hang-on-exit */
56 vsf_sysutil_reopen_standard_fds();
57 vsf_sysutil_make_session_leader();
59 if (tunable_listen)
61 listen_sock = vsf_sysutil_get_ipv4_sock();
63 else
65 listen_sock = vsf_sysutil_get_ipv6_sock();
67 vsf_sysutil_activate_reuseaddr(listen_sock);
69 s_p_ip_count_hash = hash_alloc(256, s_ipaddr_size,
70 sizeof(unsigned int), hash_ip);
71 s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
72 s_ipaddr_size, hash_pid);
73 if (tunable_setproctitle_enable)
75 vsf_sysutil_setproctitle("LISTENER");
77 vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
78 vsf_sysutil_install_sighandler(kVSFSysUtilSigHUP, handle_sighup, 0, 1);
79 if (tunable_listen)
81 struct vsf_sysutil_sockaddr* p_sockaddr = 0;
82 vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
83 vsf_sysutil_sockaddr_set_port(p_sockaddr,
84 (unsigned short) tunable_listen_port);
85 if (!tunable_listen_address)
87 vsf_sysutil_sockaddr_set_any(p_sockaddr);
89 else
91 if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr))
93 die2("bad listen_address: ", tunable_listen_address);
96 retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
97 vsf_sysutil_free(p_sockaddr);
98 if (vsf_sysutil_retval_is_error(retval))
100 die("could not bind listening IPv4 socket");
103 else
105 struct vsf_sysutil_sockaddr* p_sockaddr = 0;
106 vsf_sysutil_sockaddr_alloc_ipv6(&p_sockaddr);
107 vsf_sysutil_sockaddr_set_port(p_sockaddr,
108 (unsigned short) tunable_listen_port);
109 if (!tunable_listen_address6)
111 vsf_sysutil_sockaddr_set_any(p_sockaddr);
113 else
115 struct mystr addr_str = INIT_MYSTR;
116 const unsigned char* p_raw_addr;
117 str_alloc_text(&addr_str, tunable_listen_address6);
118 p_raw_addr = vsf_sysutil_parse_ipv6(&addr_str);
119 str_free(&addr_str);
120 if (!p_raw_addr)
122 die2("bad listen_address6: ", tunable_listen_address6);
124 vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr);
126 retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
127 vsf_sysutil_free(p_sockaddr);
128 if (vsf_sysutil_retval_is_error(retval))
130 die("could not bind listening IPv6 socket");
133 retval = vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
134 if (vsf_sysutil_retval_is_error(retval))
136 die("could not listen");
138 vsf_sysutil_sockaddr_alloc(&p_accept_addr);
139 while (1)
141 struct vsf_client_launch child_info;
142 void* p_raw_addr;
143 int new_child;
144 int new_client_sock;
145 new_client_sock = vsf_sysutil_accept_timeout(
146 listen_sock, p_accept_addr, 0);
147 if (vsf_sysutil_retval_is_error(new_client_sock))
149 continue;
151 ++s_children;
152 child_info.num_children = s_children;
153 child_info.num_this_ip = 0;
154 p_raw_addr = vsf_sysutil_sockaddr_get_raw_addr(p_accept_addr);
155 child_info.num_this_ip = handle_ip_count(p_raw_addr);
156 if (tunable_isolate)
158 if (tunable_http_enable && tunable_isolate_network)
160 new_child = vsf_sysutil_fork_isolate_all_failok();
162 else
164 new_child = vsf_sysutil_fork_isolate_failok();
167 else
169 new_child = vsf_sysutil_fork_failok();
171 if (new_child != 0)
173 /* Parent context */
174 vsf_sysutil_close(new_client_sock);
175 if (new_child > 0)
177 hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr);
179 else
181 /* fork() failed, clear up! */
182 --s_children;
183 drop_ip_count(p_raw_addr);
185 /* Fall through to while() loop and accept() again */
187 else
189 /* Child context */
190 vsf_set_die_if_parent_dies();
191 vsf_sysutil_close(listen_sock);
192 prepare_child(new_client_sock);
193 /* By returning here we "launch" the child process with the same
194 * contract as xinetd would provide.
196 return child_info;
201 static void
202 prepare_child(int new_client_sock)
204 /* We must satisfy the contract: command socket on fd 0, 1, 2 */
205 vsf_sysutil_dupfd2(new_client_sock, 0);
206 vsf_sysutil_dupfd2(new_client_sock, 1);
207 vsf_sysutil_dupfd2(new_client_sock, 2);
208 if (new_client_sock > 2)
210 vsf_sysutil_close(new_client_sock);
214 static void
215 drop_ip_count(void* p_raw_addr)
217 unsigned int count;
218 unsigned int* p_count =
219 (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_raw_addr);
220 if (!p_count)
222 bug("IP address missing from hash");
224 count = *p_count;
225 if (!count)
227 bug("zero count for IP address");
229 count--;
230 *p_count = count;
231 if (!count)
233 hash_free_entry(s_p_ip_count_hash, p_raw_addr);
237 static void
238 handle_sigchld(void* duff)
240 unsigned int reap_one = 1;
241 (void) duff;
242 while (reap_one)
244 reap_one = (unsigned int)vsf_sysutil_wait_reap_one();
245 if (reap_one)
247 struct vsf_sysutil_ipaddr* p_ip;
248 /* Account total number of instances */
249 --s_children;
250 /* Account per-IP limit */
251 p_ip = (struct vsf_sysutil_ipaddr*)
252 hash_lookup_entry(s_p_pid_ip_hash, (void*)&reap_one);
253 drop_ip_count(p_ip);
254 hash_free_entry(s_p_pid_ip_hash, (void*)&reap_one);
259 static void
260 handle_sighup(void* duff)
262 (void) duff;
263 /* We don't crash the out the listener if an invalid config was added */
264 tunables_load_defaults();
265 vsf_parseconf_load_file(0, 0);
268 static unsigned int
269 hash_ip(unsigned int buckets, void* p_key)
271 const unsigned char* p_raw_ip = (const unsigned char*)p_key;
272 unsigned int val = 0;
273 int shift = 24;
274 unsigned int i;
275 for (i = 0; i < s_ipaddr_size; ++i)
277 val = val ^ (unsigned int) (p_raw_ip[i] << shift);
278 shift -= 8;
279 if (shift < 0)
281 shift = 24;
284 return val % buckets;
287 static unsigned int
288 hash_pid(unsigned int buckets, void* p_key)
290 unsigned int* p_pid = (unsigned int*)p_key;
291 return (*p_pid) % buckets;
294 static unsigned int
295 handle_ip_count(void* p_ipaddr)
297 unsigned int* p_count =
298 (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_ipaddr);
299 unsigned int count;
300 if (!p_count)
302 count = 1;
303 hash_add_entry(s_p_ip_count_hash, p_ipaddr, (void*)&count);
305 else
307 count = *p_count;
308 count++;
309 *p_count = count;
311 return count;