1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * standalone.c
6 *
7 * Code to listen on the network and launch children servants.
8 */
9
10 #include "standalone.h"
11
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"
21
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;
26
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);
32
33 static unsigned int hash_ip(unsigned int buckets, void* p_key);
34 static unsigned int hash_pid(unsigned int buckets, void* p_key);
35
36 struct vsf_client_launch
vsf_standalone_main(void)37 vsf_standalone_main(void)
38 {
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)
44 {
45 die("run two copies of vsftpd for IPv4 and IPv6");
46 }
47 if (tunable_background)
48 {
49 int forkret = vsf_sysutil_fork();
50 if (forkret > 0)
51 {
52 /* Parent, just exit */
53 vsf_sysutil_exit(0);
54 }
55 /* Son, close standard FDs to avoid SSH hang-on-exit */
56 vsf_sysutil_reopen_standard_fds();
57 vsf_sysutil_make_session_leader();
58 }
59 if (tunable_listen)
60 {
61 listen_sock = vsf_sysutil_get_ipv4_sock();
62 }
63 else
64 {
65 listen_sock = vsf_sysutil_get_ipv6_sock();
66 }
67 vsf_sysutil_activate_reuseaddr(listen_sock);
68
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)
74 {
75 vsf_sysutil_setproctitle("LISTENER");
76 }
77 vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
78 vsf_sysutil_install_sighandler(kVSFSysUtilSigHUP, handle_sighup, 0, 1);
79 if (tunable_listen)
80 {
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)
86 {
87 vsf_sysutil_sockaddr_set_any(p_sockaddr);
88 }
89 else
90 {
91 if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr))
92 {
93 die2("bad listen_address: ", tunable_listen_address);
94 }
95 }
96 retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
97 vsf_sysutil_free(p_sockaddr);
98 if (vsf_sysutil_retval_is_error(retval))
99 {
100 die("could not bind listening IPv4 socket");
101 }
102 }
103 else
104 {
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)
110 {
111 vsf_sysutil_sockaddr_set_any(p_sockaddr);
112 }
113 else
114 {
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)
121 {
122 die2("bad listen_address6: ", tunable_listen_address6);
123 }
124 vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr);
125 }
126 retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
127 vsf_sysutil_free(p_sockaddr);
128 if (vsf_sysutil_retval_is_error(retval))
129 {
130 die("could not bind listening IPv6 socket");
131 }
132 }
133 retval = vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
134 if (vsf_sysutil_retval_is_error(retval))
135 {
136 die("could not listen");
137 }
138 vsf_sysutil_sockaddr_alloc(&p_accept_addr);
139 while (1)
140 {
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))
148 {
149 continue;
150 }
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)
157 {
158 if (tunable_http_enable && tunable_isolate_network)
159 {
160 new_child = vsf_sysutil_fork_isolate_all_failok();
161 }
162 else
163 {
164 new_child = vsf_sysutil_fork_isolate_failok();
165 }
166 }
167 else
168 {
169 new_child = vsf_sysutil_fork_failok();
170 }
171 if (new_child != 0)
172 {
173 /* Parent context */
174 vsf_sysutil_close(new_client_sock);
175 if (new_child > 0)
176 {
177 hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr);
178 }
179 else
180 {
181 /* fork() failed, clear up! */
182 --s_children;
183 drop_ip_count(p_raw_addr);
184 }
185 /* Fall through to while() loop and accept() again */
186 }
187 else
188 {
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.
195 */
196 return child_info;
197 }
198 }
199 }
200
201 static void
prepare_child(int new_client_sock)202 prepare_child(int new_client_sock)
203 {
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)
209 {
210 vsf_sysutil_close(new_client_sock);
211 }
212 }
213
214 static void
drop_ip_count(void * p_raw_addr)215 drop_ip_count(void* p_raw_addr)
216 {
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)
221 {
222 bug("IP address missing from hash");
223 }
224 count = *p_count;
225 if (!count)
226 {
227 bug("zero count for IP address");
228 }
229 count--;
230 *p_count = count;
231 if (!count)
232 {
233 hash_free_entry(s_p_ip_count_hash, p_raw_addr);
234 }
235 }
236
237 static void
handle_sigchld(void * duff)238 handle_sigchld(void* duff)
239 {
240 unsigned int reap_one = 1;
241 (void) duff;
242 while (reap_one)
243 {
244 reap_one = (unsigned int)vsf_sysutil_wait_reap_one();
245 if (reap_one)
246 {
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);
255 }
256 }
257 }
258
259 static void
handle_sighup(void * duff)260 handle_sighup(void* duff)
261 {
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);
266 }
267
268 static unsigned int
hash_ip(unsigned int buckets,void * p_key)269 hash_ip(unsigned int buckets, void* p_key)
270 {
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)
276 {
277 val = val ^ (unsigned int) (p_raw_ip[i] << shift);
278 shift -= 8;
279 if (shift < 0)
280 {
281 shift = 24;
282 }
283 }
284 return val % buckets;
285 }
286
287 static unsigned int
hash_pid(unsigned int buckets,void * p_key)288 hash_pid(unsigned int buckets, void* p_key)
289 {
290 unsigned int* p_pid = (unsigned int*)p_key;
291 return (*p_pid) % buckets;
292 }
293
294 static unsigned int
handle_ip_count(void * p_ipaddr)295 handle_ip_count(void* p_ipaddr)
296 {
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)
301 {
302 count = 1;
303 hash_add_entry(s_p_ip_count_hash, p_ipaddr, (void*)&count);
304 }
305 else
306 {
307 count = *p_count;
308 count++;
309 *p_count = count;
310 }
311 return count;
312 }
313
314