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
22static unsigned int s_children;
23static struct hash* s_p_ip_count_hash;
24static struct hash* s_p_pid_ip_hash;
25static unsigned int s_ipaddr_size;
26
27static void handle_sigchld(int duff);
28static void handle_sighup(int duff);
29static void prepare_child(int sockfd);
30static unsigned int handle_ip_count(void* p_raw_addr);
31static void drop_ip_count(void* p_raw_addr);
32
33static unsigned int hash_ip(unsigned int buckets, void* p_key);
34static unsigned int hash_pid(unsigned int buckets, void* p_key);
35
36struct vsf_client_launch
37vsf_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_close_failok(0);
57#ifndef DEBUG
58    vsf_sysutil_close_failok(1);
59    vsf_sysutil_close_failok(2);
60#endif
61    vsf_sysutil_make_session_leader();
62  }
63  if (tunable_listen)
64  {
65    listen_sock = vsf_sysutil_get_ipv4_sock();
66  }
67  else
68  {
69    listen_sock = vsf_sysutil_get_ipv6_sock();
70  }
71  vsf_sysutil_activate_reuseaddr(listen_sock);
72
73  s_p_ip_count_hash = hash_alloc(256, s_ipaddr_size,
74                                 sizeof(unsigned int), hash_ip);
75  s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
76                               s_ipaddr_size, hash_pid);
77  if (tunable_setproctitle_enable)
78  {
79    vsf_sysutil_setproctitle("LISTENER");
80  }
81  vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
82  vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup);
83  if (tunable_listen)
84  {
85    struct vsf_sysutil_sockaddr* p_sockaddr = 0;
86    vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
87    vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
88    if (!tunable_listen_address)
89    {
90      vsf_sysutil_sockaddr_set_any(p_sockaddr);
91    }
92    else
93    {
94      if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr))
95      {
96        die2("bad listen_address: ", tunable_listen_address);
97      }
98    }
99    retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
100    vsf_sysutil_free(p_sockaddr);
101    if (vsf_sysutil_retval_is_error(retval))
102    {
103      die("could not bind listening IPv4 socket");
104    }
105  }
106  else
107  {
108    struct vsf_sysutil_sockaddr* p_sockaddr = 0;
109    vsf_sysutil_sockaddr_alloc_ipv6(&p_sockaddr);
110    vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
111    if (!tunable_listen_address6)
112    {
113      vsf_sysutil_sockaddr_set_any(p_sockaddr);
114    }
115    else
116    {
117      struct mystr addr_str = INIT_MYSTR;
118      const unsigned char* p_raw_addr;
119      str_alloc_text(&addr_str, tunable_listen_address6);
120      p_raw_addr = vsf_sysutil_parse_ipv6(&addr_str);
121      str_free(&addr_str);
122      if (!p_raw_addr)
123      {
124        die2("bad listen_address6: ", tunable_listen_address6);
125      }
126      vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr);
127    }
128    retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
129    vsf_sysutil_free(p_sockaddr);
130    if (vsf_sysutil_retval_is_error(retval))
131    {
132      die("could not bind listening IPv6 socket");
133    }
134  }
135  vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
136  vsf_sysutil_sockaddr_alloc(&p_accept_addr);
137  while (1)
138  {
139    struct vsf_client_launch child_info;
140    void* p_raw_addr;
141    int new_child;
142    int new_client_sock;
143    vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
144    vsf_sysutil_unblock_sig(kVSFSysUtilSigHUP);
145    new_client_sock = vsf_sysutil_accept_timeout(
146        listen_sock, p_accept_addr, 0);
147    vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
148    vsf_sysutil_block_sig(kVSFSysUtilSigHUP);
149    if (vsf_sysutil_retval_is_error(new_client_sock))
150    {
151      continue;
152    }
153    ++s_children;
154    child_info.num_children = s_children;
155    child_info.num_this_ip = 0;
156    p_raw_addr = vsf_sysutil_sockaddr_get_raw_addr(p_accept_addr);
157    child_info.num_this_ip = handle_ip_count(p_raw_addr);
158    new_child = vsf_sysutil_fork_failok();
159    if (new_child != 0)
160    {
161      /* Parent context */
162      vsf_sysutil_close(new_client_sock);
163      if (new_child > 0)
164      {
165        hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr);
166      }
167      else
168      {
169        /* fork() failed, clear up! */
170        --s_children;
171        drop_ip_count(p_raw_addr);
172      }
173      /* Fall through to while() loop and accept() again */
174    }
175    else
176    {
177      /* Child context */
178      vsf_sysutil_close(listen_sock);
179      prepare_child(new_client_sock);
180      /* By returning here we "launch" the child process with the same
181       * contract as xinetd would provide.
182       */
183      return child_info;
184    }
185  }
186}
187
188static void
189prepare_child(int new_client_sock)
190{
191  /* We must satisfy the contract: command socket on fd 0, 1, 2 */
192  vsf_sysutil_dupfd2(new_client_sock, 0);
193#ifndef DEBUG
194  vsf_sysutil_dupfd2(new_client_sock, 1);
195  vsf_sysutil_dupfd2(new_client_sock, 2);
196#endif
197  if (new_client_sock > 2)
198  {
199    vsf_sysutil_close(new_client_sock);
200  }
201}
202
203static void
204drop_ip_count(void* p_raw_addr)
205{
206  unsigned int count;
207  unsigned int* p_count =
208    (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_raw_addr);
209  if (!p_count)
210  {
211    bug("IP address missing from hash");
212  }
213  count = *p_count;
214  if (!count)
215  {
216    bug("zero count for IP address");
217  }
218  count--;
219  *p_count = count;
220  if (!count)
221  {
222    hash_free_entry(s_p_ip_count_hash, p_raw_addr);
223  }
224}
225
226static void
227handle_sigchld(int duff)
228{
229  unsigned int reap_one = 1;
230  (void) duff;
231  while (reap_one)
232  {
233    reap_one = (unsigned int)vsf_sysutil_wait_reap_one();
234    if (reap_one)
235    {
236      struct vsf_sysutil_ipaddr* p_ip;
237      /* Account total number of instances */
238      --s_children;
239      /* Account per-IP limit */
240      p_ip = (struct vsf_sysutil_ipaddr*)
241        hash_lookup_entry(s_p_pid_ip_hash, (void*)&reap_one);
242      drop_ip_count(p_ip);
243      hash_free_entry(s_p_pid_ip_hash, (void*)&reap_one);
244    }
245  }
246}
247
248static void
249handle_sighup(int duff)
250{
251  (void) duff;
252  /* We don't crash the out the listener if an invalid config was added */
253  vsf_parseconf_load_file(0, 0);
254}
255
256static unsigned int
257hash_ip(unsigned int buckets, void* p_key)
258{
259  const unsigned char* p_raw_ip = (const unsigned char*)p_key;
260  unsigned int val = 0;
261  int shift = 24;
262  unsigned int i;
263  for (i = 0; i < s_ipaddr_size; ++i)
264  {
265    val ^= p_raw_ip[i] << shift;
266    shift -= 8;
267    if (shift < 0)
268    {
269      shift = 24;
270    }
271  }
272  return val % buckets;
273}
274
275static unsigned int
276hash_pid(unsigned int buckets, void* p_key)
277{
278  unsigned int* p_pid = (unsigned int*)p_key;
279  return (*p_pid) % buckets;
280}
281
282static unsigned int
283handle_ip_count(void* p_ipaddr)
284{
285  unsigned int* p_count =
286    (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_ipaddr);
287  unsigned int count;
288  if (!p_count)
289  {
290    count = 1;
291    hash_add_entry(s_p_ip_count_hash, p_ipaddr, (void*)&count);
292  }
293  else
294  {
295    count = *p_count;
296    count++;
297    *p_count = count;
298  }
299  return count;
300}
301
302