1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/*
18 * Part of Very Secure FTPd
19 * Licence: GPL v2
20 * Author: Chris Evans
21 * standalone.c
22 *
23 * Code to listen on the network and launch children servants.
24 */
25
26#include "standalone.h"
27
28#include "parseconf.h"
29#include "tunables.h"
30#include "sysutil.h"
31#include "sysdeputil.h"
32#include "utility.h"
33#include "defs.h"
34#include "hash.h"
35#include "str.h"
36#include "ipaddrparse.h"
37
38static unsigned int s_children;
39static struct hash* s_p_ip_count_hash;
40static struct hash* s_p_pid_ip_hash;
41static unsigned int s_ipaddr_size;
42
43static void handle_sigchld(int duff);
44static void handle_sighup(int duff);
45static void prepare_child(int sockfd);
46static unsigned int handle_ip_count(void* p_raw_addr);
47static void drop_ip_count(void* p_raw_addr);
48
49static unsigned int hash_ip(unsigned int buckets, void* p_key);
50static unsigned int hash_pid(unsigned int buckets, void* p_key);
51
52struct vsf_client_launch
53vsf_standalone_main(void)
54{
55  struct vsf_sysutil_sockaddr* p_accept_addr = 0;
56  int listen_sock = -1;
57  int retval;
58  s_ipaddr_size = vsf_sysutil_get_ipaddr_size();
59  if (tunable_listen && tunable_listen_ipv6)
60  {
61    die("run two copies of vsftpd for IPv4 and IPv6");
62  }
63  if (tunable_background)
64  {
65    int forkret = vsf_sysutil_fork();
66    if (forkret > 0)
67    {
68      /* Parent, just exit */
69      vsf_sysutil_exit(0);
70    }
71    /* Son, close standard FDs to avoid SSH hang-on-exit */
72    vsf_sysutil_close_failok(0);
73#ifndef DEBUG	// disable for tmp
74    vsf_sysutil_close_failok(1);
75    vsf_sysutil_close_failok(2);
76#endif
77    vsf_sysutil_make_session_leader();
78  }
79  if (tunable_listen)
80  {
81    listen_sock = vsf_sysutil_get_ipv4_sock();
82  }
83  else
84  {
85    listen_sock = vsf_sysutil_get_ipv6_sock();
86  }
87  vsf_sysutil_activate_reuseaddr(listen_sock);
88
89  s_p_ip_count_hash = hash_alloc(256, s_ipaddr_size,
90                                 sizeof(unsigned int), hash_ip);
91  s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
92                               s_ipaddr_size, hash_pid);
93  if (tunable_setproctitle_enable)
94  {
95    vsf_sysutil_setproctitle("LISTENER");
96  }
97  vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
98  vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup);
99  if (tunable_listen)
100  {
101    struct vsf_sysutil_sockaddr* p_sockaddr = 0;
102    vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
103    vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
104    if (!tunable_listen_address)
105    {
106      vsf_sysutil_sockaddr_set_any(p_sockaddr);
107    }
108    else
109    {
110      if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr))
111      {
112        die2("bad listen_address: ", tunable_listen_address);
113      }
114    }
115    retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
116    vsf_sysutil_free(p_sockaddr);
117    if (vsf_sysutil_retval_is_error(retval))
118    {
119      die("could not bind listening IPv4 socket");
120    }
121  }
122  else
123  {
124    struct vsf_sysutil_sockaddr* p_sockaddr = 0;
125    vsf_sysutil_sockaddr_alloc_ipv6(&p_sockaddr);
126    vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
127    if (!tunable_listen_address6)
128    {
129      vsf_sysutil_sockaddr_set_any(p_sockaddr);
130    }
131    else
132    {
133      struct mystr addr_str = INIT_MYSTR;
134      const unsigned char* p_raw_addr;
135      str_alloc_text(&addr_str, tunable_listen_address6);
136      p_raw_addr = vsf_sysutil_parse_ipv6(&addr_str);
137      str_free(&addr_str);
138      if (!p_raw_addr)
139      {
140        die2("bad listen_address6: ", tunable_listen_address6);
141      }
142      vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr);
143    }
144    retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
145    vsf_sysutil_free(p_sockaddr);
146    if (vsf_sysutil_retval_is_error(retval))
147    {
148      die("could not bind listening IPv6 socket");
149    }
150  }
151  vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
152  vsf_sysutil_sockaddr_alloc(&p_accept_addr);
153  while (1)
154  {
155    struct vsf_client_launch child_info;
156    void* p_raw_addr;
157    int new_child;
158    int new_client_sock;
159    vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
160    vsf_sysutil_unblock_sig(kVSFSysUtilSigHUP);
161    new_client_sock = vsf_sysutil_accept_timeout(
162        listen_sock, p_accept_addr, 0);
163    vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
164    vsf_sysutil_block_sig(kVSFSysUtilSigHUP);
165    if (vsf_sysutil_retval_is_error(new_client_sock))
166    {
167      continue;
168    }
169    ++s_children;
170    child_info.num_children = s_children;
171    child_info.num_this_ip = 0;
172    p_raw_addr = vsf_sysutil_sockaddr_get_raw_addr(p_accept_addr);
173    child_info.num_this_ip = handle_ip_count(p_raw_addr);
174    new_child = vsf_sysutil_fork_failok();
175    if (new_child != 0)
176    {
177      /* Parent context */
178      vsf_sysutil_close(new_client_sock);
179      if (new_child > 0)
180      {
181        hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr);
182      }
183      else
184      {
185        /* fork() failed, clear up! */
186        --s_children;
187        drop_ip_count(p_raw_addr);
188      }
189      /* Fall through to while() loop and accept() again */
190    }
191    else
192    {
193      /* Child context */
194      vsf_sysutil_close(listen_sock);
195      prepare_child(new_client_sock);
196      /* By returning here we "launch" the child process with the same
197       * contract as xinetd would provide.
198       */
199      return child_info;
200    }
201  }
202}
203
204static void
205prepare_child(int new_client_sock)
206{
207  /* We must satisfy the contract: command socket on fd 0, 1, 2 */
208  vsf_sysutil_dupfd2(new_client_sock, 0);
209#ifndef DEBUG	// disable for tmp
210  vsf_sysutil_dupfd2(new_client_sock, 1);
211  vsf_sysutil_dupfd2(new_client_sock, 2);
212#endif
213  if (new_client_sock > 2)
214  {
215    vsf_sysutil_close(new_client_sock);
216  }
217}
218
219static void
220drop_ip_count(void* p_raw_addr)
221{
222  unsigned int count;
223  unsigned int* p_count =
224    (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_raw_addr);
225  if (!p_count)
226  {
227    bug("IP address missing from hash");
228  }
229  count = *p_count;
230  if (!count)
231  {
232    bug("zero count for IP address");
233  }
234  count--;
235  *p_count = count;
236  if (!count)
237  {
238    hash_free_entry(s_p_ip_count_hash, p_raw_addr);
239  }
240}
241
242static void
243handle_sigchld(int duff)
244{
245  unsigned int reap_one = 1;
246  (void) duff;
247  while (reap_one)
248  {
249    reap_one = (unsigned int)vsf_sysutil_wait_reap_one();
250    if (reap_one)
251    {
252      struct vsf_sysutil_ipaddr* p_ip;
253      /* Account total number of instances */
254      --s_children;
255      /* Account per-IP limit */
256      p_ip = (struct vsf_sysutil_ipaddr*)
257        hash_lookup_entry(s_p_pid_ip_hash, (void*)&reap_one);
258      drop_ip_count(p_ip);
259      hash_free_entry(s_p_pid_ip_hash, (void*)&reap_one);
260    }
261  }
262}
263
264static void
265handle_sighup(int duff)
266{
267  (void) duff;
268  /* We don't crash the out the listener if an invalid config was added */
269  vsf_parseconf_load_file(0, 0);
270}
271
272static unsigned int
273hash_ip(unsigned int buckets, void* p_key)
274{
275  const unsigned char* p_raw_ip = (const unsigned char*)p_key;
276  unsigned int val = 0;
277  int shift = 24;
278  unsigned int i;
279  for (i = 0; i < s_ipaddr_size; ++i)
280  {
281    val ^= p_raw_ip[i] << shift;
282    shift -= 8;
283    if (shift < 0)
284    {
285      shift = 24;
286    }
287  }
288  return val % buckets;
289}
290
291static unsigned int
292hash_pid(unsigned int buckets, void* p_key)
293{
294  unsigned int* p_pid = (unsigned int*)p_key;
295  return (*p_pid) % buckets;
296}
297
298static unsigned int
299handle_ip_count(void* p_ipaddr)
300{
301  unsigned int* p_count =
302    (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_ipaddr);
303  unsigned int count;
304  if (!p_count)
305  {
306    count = 1;
307    hash_add_entry(s_p_ip_count_hash, p_ipaddr, (void*)&count);
308  }
309  else
310  {
311    count = *p_count;
312    count++;
313    *p_count = count;
314  }
315  return count;
316}
317
318