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