/***************************************
This is part of frox: A simple transparent FTP proxy
Copyright (C) 2000 James Hollingshead
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
main.c
***************************************/
#include <sys/wait.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <netdb.h>
#include <time.h>
#include <stdlib.h>
#include "vscan.h"
#include "control.h"
#include "common.h"
#include "transdata.h"
#include "cache.h"
#include "os.h"
#include "ftp-cmds.h"
void run_daemon(void);
void run_with_inetd(void);
void new_connection(int fd, struct sockaddr_in client_address);
int accept_connection(struct sockaddr_in client);
void daemonify(void);
void write_pidfile(void);
int init_log(void);
static RETSIGTYPE signal_handle(int signo);
static int listen_sockfd, noforks = 0;
static int reread_flag = FALSE;
pid_t cmgrpid = 0, tdatapid = 0;
/*Limit connections from single IP stuff*/
static struct client_info {
u_int32_t ip;
pid_t pid;
} *clients = NULL;
void add_client(pid_t, struct sockaddr_in client);
void rm_client(pid_t pid);
#ifdef ENABLE_CHANGEPROC
int main(int argc, char *argv[], char *envp[])
#else
int main(int argc, char *argv[])
#endif
{
struct sockaddr_in listen_address;
struct linger linger_opt = { 1, 0 }; /*Linger active, timeout 0 */
int i;
sstr_setopts(sstrerr, 0);
info = NULL;
#ifdef ENABLE_CHANGEPROC
init_set_proc_title(argc, argv, envp);
#endif
process_cmdline(argc, argv);
if(read_config() != 0) {
fprintf(stderr, "Error reading configuration file\n");
exit(1);
}
#ifndef HAVE_NANOSLEEP
if(config.maxulrate || config.maxdlrate) {
write_log(ERROR, "Unable to limit transfer rate - "
"nanosleep() not availiable.");
config.maxulrate = config.maxdlrate = 0;
}
#endif
#ifndef HAVE_SETENV
#ifdef USE_CCP
if(config.oldccp) {
write_log(ERROR, "Unable to use old CCP method - "
"setenv() not availiable.");
config.ccpcmd = NULL;
}
#endif
#endif
if(config.resolvhack)
gethostbyname(config.resolvhack);
os_init();
if(!config.inetd && config.maxforks != 0 && !config.nodetach)
daemonify();
if(config.maxforksph)
clients =
malloc(sizeof(struct client_info) * config.maxforks);
init_log();
ftpcmds_init();
signal(SIGCHLD, signal_handle);
signal(SIGINT, signal_handle);
signal(SIGTERM, signal_handle);
signal(SIGHUP, signal_handle);
signal(SIGPIPE, SIG_IGN);
if(config.inetd) {
run_with_inetd();
return (0);
}
/*Not running from inetd */
write_pidfile();
/*Fork any other processes before we open the listen_sockfd */
transdata_setup();
do_chroot(); /*Do this before cache init to simplify local cache code */
cache_geninit();
listen_address = config.listen_address;
listen_sockfd = listen_on_socket(&listen_address, NULL);
if(listen_sockfd == -1)
exit(1);
i = 1;
setsockopt(listen_sockfd, SOL_SOCKET, SO_OOBINLINE, &i, sizeof(i));
setsockopt(listen_sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
setsockopt(listen_sockfd, SOL_SOCKET, SO_LINGER,
&linger_opt, sizeof(linger_opt));
write_log(IMPORT, "Listening on %s:%d",
sstr_buf(addr2name(listen_address.sin_addr)),
ntohs(listen_address.sin_port));
bindtodevice(listen_sockfd);
#ifdef ENABLE_CHANGEPROC
set_proc_title("frox: accepting connections");
#endif
droppriv();
run_daemon();
return (0);
}
void run_with_inetd(void)
{
struct sockaddr_in client_address;
int len = sizeof(client_address);
transdata_setup();
do_chroot();
droppriv();
cache_geninit();
vscan_init();
if(getpeername(0, (struct sockaddr *) &client_address, &len)) {
debug_perr("getpeername");
die(ERROR, "Unable to establish client address from inetd",
0, NULL, -1);
}
signal(SIGHUP, SIG_IGN);
init_session(0, client_address);
}
void run_daemon(void)
{
struct sockaddr_in client_address;
int len = sizeof(client_address);
int fd;
do {
fd = accept(listen_sockfd,
(struct sockaddr *) &client_address, &len);
if(fd < 0) {
if(errno == EINTR)
continue;
debug_perr("accept");
continue;
}
if(noforks == 0)
transdata_flush();
if(reread_flag) {
reread_flag = FALSE;
reread_config();
if(config.maxforksph)
clients =
realloc(clients,
sizeof(struct client_info)
* (config.maxforksph >
noforks ? config.
maxforksph : noforks));
}
if(!accept_connection(client_address))
close(fd);
else
new_connection(fd, client_address);
} while(TRUE);
}
void new_connection(int fd, struct sockaddr_in client_address)
{
pid_t pid;
if(config.maxforks == 0) {
close(listen_sockfd);
signal(SIGHUP, SIG_IGN);
signal(SIGALRM, signal_handle);
vscan_init();
alarm(config.timeout);
transdata_newsocketpair();
init_session(fd, client_address);
exit(0);
}
pid = fork();
switch (pid) {
case -1:
debug_perr("fork failed");
close(fd);
break;
case 0:
srand(time(NULL) + getpid());
signal(SIGHUP, SIG_IGN);
signal(SIGALRM, signal_handle);
tdatapid = cmgrpid = 0;
vscan_init();
alarm(config.timeout);
transdata_newsocketpair();
init_session(fd, client_address);
exit(0);
default:
close(fd);
if(config.maxforksph)
add_client(pid, client_address);
noforks++;
}
}
int accept_connection(struct sockaddr_in client)
{
if(noforks >= config.maxforks && config.maxforks != 0) {
write_log(ERROR,
"Connect from %s refused: Too many connections",
inet_ntoa(client.sin_addr));
return (FALSE);
}
if(config.bdefend && ntohs(client.sin_port) == 20) {
write_log(ATTACK,
"Connect from %s refused: Comes from ftp-data port",
inet_ntoa(client.sin_addr));
return (FALSE);
}
if(config.maxforksph) {
int i, x;
for(x = 0, i = 0; x < noforks && i < config.maxforksph; x++)
if(clients[x].ip == client.sin_addr.s_addr)
i++;
if(i >= config.maxforksph) {
write_log(ERROR, "Connect from %s refused: "
"too many connections from that host",
inet_ntoa(client.sin_addr));
return (FALSE);
}
}
return (TRUE);
}
int init_log(void)
{
time_t t;
if(config.logfile && strcasecmp(config.logfile, "stderr")) {
int tmpfd;
tmpfd = open(config.logfile, O_APPEND | O_CREAT | O_WRONLY,
S_IRUSR | S_IWUSR);
if(tmpfd == -1 || dup2(tmpfd, 2) == -1) {
fprintf(stderr, "Unable to open logfile %s\n",
config.logfile);
return (-1);
}
}
openlog("frox", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_DAEMON);
/* These are necessary so that syslog() and ctime() can do their
* initialisation (specifically timezone stuff) before we chroot()
* and they don't have access to /etc */
syslog(LOG_NOTICE | LOG_DAEMON, "Frox started\n");
t = time(NULL);
ctime(&t);
return (0);
}
void daemonify()
{
switch (fork()) {
case -1:
write_log(ERROR, "can't fork daemon");
exit(-1);
break;
case 0:
/******** child ********/
break;
default:
/******** parent ********/
exit(0);
}
write_log(VERBOSE, "Forked to background");
freopen("/dev/null", "w", stdin);
freopen("/dev/null", "w", stdout);
}
void write_pidfile(void)
{
FILE *fp;
pid_t pid;
pid = getpid();
if(config.pidfile) {
fp = fopen(config.pidfile, "w");
if(fp != NULL) {
fprintf(fp, "%d\n", pid);
fclose(fp);
}
}
}
void add_client(pid_t pid, struct sockaddr_in client)
{
clients[noforks].pid = pid;
clients[noforks].ip = client.sin_addr.s_addr;
}
void rm_client(pid_t pid)
{
int i;
for(i = 0; i < noforks && pid != clients[i].pid; i++);
if(i < noforks) {
clients[i].pid = clients[noforks].pid;
clients[i].ip = clients[noforks].ip;
}
}
static RETSIGTYPE signal_handle(int signo)
{
pid_t pid;
switch (signo) {
case SIGCHLD:
if(info)
return; /*Children do their own waitpid() */
while((pid = waitpid(-1, (int *) 0, WNOHANG)) > 0) {
noforks--;
if(config.maxforksph)
rm_client(pid);
}
signal(signo, signal_handle);
break;
case SIGINT:
case SIGTERM:
close(listen_sockfd);
kill_procs();
exit(0);
case SIGHUP: /*Reread config file. Not safe from signal handler. */
reread_flag = TRUE;
signal(signo, signal_handle);
break;
case SIGALRM:
die(ERROR, "Connection timed out.", 0, NULL, -1);
}
}
syntax highlighted by Code2HTML, v. 0.9.1