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