/*
 * Copyright (c) 2001-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

/*
 * OpenFWTK project generation 2 API misc functions
 *
 * (C) Copyright 2001 ArkanoiD <ark@eltex.ru>
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <libgen.h>
#include <net/if.h>
#include <time.h>
#include <sysexits.h>
#include "magic.h"
#include "firewall.h"
#include "firewall2.h"
#include "fwfunc.h"

static char* moduleId ATTR_UNUSED = "$Id: gen2api.c,v 1.28 2007/09/10 02:49:13 arkenoi Exp $";

#if defined(linux) || defined(SOLARIS)
extern void initsetproctitle(int,char**);
#endif

#ifndef INT_MAX
#define INT_MAX         2147483647
#endif

#define DAEMON		1
#define FASTDAEMON	2
#define SAFEDAEMON	3

void proxy_update_status();
extern time_t sotimeout(int);
extern void setproctitle(const char*,...);
extern magic_t proxy_magic;

Cfg* proxy_init(int ac,char *av[])
{
	int 	daemon = 0;
	int 	i;
	int 	port;

	memset(proxy_chroot,0,sizeof(proxy_chroot));
	strlcpy(proxy_name,basename(av[0]),sizeof(proxy_name));

	for (i = 1; i < ac; i++) {
		if (!strcmp(av[i], "-daemon")) {
			daemon = DAEMON;
			if (++i >= ac) {
				fprintf(stderr,"-daemon requires port number");
				exit(EX_USAGE);
			}
			port = str_to_port(av[i]);
		}
		if (!strcmp(av[i], "-fastdaemon")) {
			daemon = FASTDAEMON;
			if (++i >= ac) {
				fprintf(stderr,"-fastdaemon requires port number");
				exit(EX_USAGE);
			}
			port = str_to_port(av[i]);
		}
		if (!strcmp(av[i], "-safedaemon")) {
			daemon = SAFEDAEMON;
			if (++i >= ac) {
				fprintf(stderr,"-safedaemon requires port number");
				exit(EX_USAGE);
			}
			port = str_to_port(av[i]);
		}
		if (!strcmp(av[i], "-as")) {
			if (++i >= ac) {
				fprintf(stderr,"-as requires parameter");
				exit(EX_USAGE);
			}
			strlcpy(proxy_name,av[i],sizeof(proxy_name));
		}
	}


#ifndef LOG_DAEMON
	openlog(proxy_name,LOG_PID);
#else
	openlog(proxy_name,LOG_PID|LOG_NDELAY,LFAC);
#endif
#if defined(linux) || defined(SOLARIS)
	initsetproctitle(ac, av);
#endif
	proxy_stats.inbytes = 0;
	proxy_stats.outbytes = 0;

	if ((proxy_confp = cfg_read(proxy_name)) == (Cfg *)-1) {
		syslog(LLEV,"fwtkcfgerr: no netperm-table records for %.128s",proxy_name);
		exit(1);
	}
	proxy_timeout = proxy_conf_int(proxy_confp,"timeout",1,INT_MAX,PROXY_TIMEOUT);
	proxy_nodns = (proxy_conf_string(proxy_confp,"nodns") != NULL);
	proxy_stats.child_limit = proxy_conf_int(proxy_confp,"maxchildren",1,4096,0);

	proxy_magic = magic_open(MAGIC_MIME);
	magic_load(proxy_magic,NULL);

	if (daemon) {
		syslog(LLEV,"Starting daemon mode on port %d",port);
		do_daemon(port);
	}

	if (daemon != FASTDAEMON) {

/*
 *	Not reloading magic here, too slow. Send -HUP if you really need to.
 */
		cfg_free(proxy_confp);
		if ((proxy_confp = cfg_read(proxy_name)) == (Cfg *)-1) {
			syslog(LLEV,"fwtkcfgerr: no netperm-table records for %.128s",proxy_name);
			exit(1);
		}
		proxy_timeout = proxy_conf_int(proxy_confp,"timeout",1,INT_MAX,PROXY_TIMEOUT);
		proxy_stats.child_limit = proxy_conf_int(proxy_confp,"maxchildren",1,4096,0);
	}

	proxy_conf_groupid(proxy_confp);
	proxy_conf_userid(proxy_confp);
	proxy_conf_chroot(proxy_confp);
	sotimeout(proxy_timeout);

	time(&proxy_stats.start_time);

	if (peername(0,proxy_stats.rladdr,proxy_stats.riaddr,sizeof(proxy_stats.rladdr))) {
		if (errno==ENOTCONN)
			syslog (LLEV, "lost connection");
		else
			syslog (LLEV, "fwtksyserr: cannot get peer name: %s", strerror(errno));
		exit(1);
	}
	return(proxy_confp);

}

void
proxy_setugid()
{
	if(proxy_gid != -1 && setgid(proxy_gid)) {
		syslog(LLEV,"fwtksyserr: cannot set gid to %d: %s",proxy_gid, strerror(errno));
		exit(1);
	}

	if(proxy_uid != -1 && setuid(proxy_uid)) {
		syslog(LLEV,"fwtksyserr: cannot set uid to %d: %s",proxy_uid, strerror(errno));
		exit(1);
	}
}


void
proxy_chroot_setugid()
{
	if (proxy_chroot[0]) {
		chdir("/");
		if(chdir(proxy_chroot) || chroot(proxy_chroot)) {
			syslog(LLEV,"fwtksyserr: cannot chroot %.128s %s",proxy_chroot, strerror(errno));
			exit(1);
		}
		chdir("/");
	}
	proxy_setugid();
}

void
proxy_exit()
{
	time_t	offtime;

	time(&offtime);
	syslog(LLEV,"exit host=%.512s/%.20s dest=%.128s in=%u out=%u user=%.64s duration=%u",
		    proxy_stats.rladdr,proxy_stats.riaddr,proxy_stats.dst,
		    proxy_stats.inbytes,proxy_stats.outbytes,
		    proxy_stats.authuser,
		    (unsigned int)(offtime-proxy_stats.start_time));
	exit(0);
}

void
proxy_update_status()
{
	setproctitle("%.20s->%.20s %.64s in=%u out=%u %.32s",proxy_stats.rladdr,
		     proxy_stats.dst,proxy_stats.operation,
		     proxy_stats.inbytes,proxy_stats.outbytes,proxy_stats.authuser);
}

void
proxy_update_operation(char *op)
{
	strlcpy(proxy_stats.operation,op,sizeof(proxy_stats.operation));
	proxy_update_status();
}

void
proxy_set_dscp(int sock, int dscp)
{
	if (setsockopt(sock,IPPROTO_IP, IP_TOS, (void *) & dscp, sizeof(dscp)) < 0) {
		syslog(LLEV,"fwtksyserr: cannot set DSCP 0x%X on socket %d",dscp,sock);
		exit(1);
	}
}

int
net_write(s, buf, len, flags)
int s;
char *buf;
int len;
int flags;
{
	int nbytes = 0;
	int j;

	while (nbytes < len) {
		j = send(s, buf, len-nbytes, flags);
		if (j <= 0)
			return (nbytes ? nbytes : j);
		buf += j;
		nbytes += j;
	}
	return nbytes;
}


