/*-
* Copyright (c) 2002 Granch Ltd. All rigts reserved.
*
* All or some portions of this file are derived from material licensed
* to the Grahcn Ltd. and are reproduced herein with the permission of
* Granch Ltd.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE GRANCH LTD. AND THEIR CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Author : Rashid N. Achilov E-Mail shelton@granch.ru
*
* @(#)kavmilter.c 0.91.3 (Granch Ltd.) 23/01/03
*/
/*------------------------------------------------------------------------
Kaspersky Anti-Virus mail filter, based on libmilter API
UNIX FreeBSD 4.6 version
Main program.
This program based on:
LibMilter API sample program (sample.c)
KAV Sample program (AvpDaemonClient.cpp)
Also in general develompent were used sources of:
avpmilter.c by Vadim Zotov (general idea about milter API)
avp-smf.c by Andrey Shidenko (mailing to postmaster)
This program REQUIRED a GNU getopt package. You can install it from
/usr/ports/devel/libgnugetopt.
--------------------------------------------------------------------------*/
#include "kavmilter.h"
#include "externals.h"
#include "functions.h"
// Main program section
int main(int argc, char *argv[])
{
extern char *optarg;
char c,*p;
int retcode;
struct stat st,sb; // Stat file struct
FILE *pidf; // PID file to save
const char *options = "hvdc:s:D:p:t:m:T:P:C:";
const char *mustbe = "must be specified";
static const struct option long_options[] =
{
{"help", 0, 0, 'h' },
{"version", 0, 0, 'v' },
{"daemon", 0, 0, 'd' },
{"sendmail", 1, 0, 'c' },
{"KAV-socket", 1, 0, 's' },
{"debug", 1, 0, 'D' },
{"tmpdir", 1, 0, 'p' },
{"sendmail-timeout", 1, 0, 't' },
{"mode", 1, 0, 'm' },
{"KAV-timeout", 1, 0, 'T' },
{"pidfile", 1, 0, 'P' },
{"config-file", 1, 0, 'C' },
{ NULL, 0, 0, 0 }
};
// Catch-up signal handling
signal(SIGINT,_KAV_milter_close);
signal(SIGTERM,_KAV_milter_close);
signal(SIGHUP,SIG_IGN);
// Store pointer to program name
p = strrchr(argv[0],'/' );
if (p == NULL)
progname = argv[0];
else
progname = p + 1;
// Set up default values to flags and parameters
*((int *) (&_KAV_milter_config._idflags)) = 0;
// Process short or long options
while ((c = getopt_long(argc, argv, options, long_options, NULL)) != EOF)
{
switch (c)
{
case 'c': // Sendmail communication socket
if (*optarg == '\0')
{
fprintf(stderr,"sendmail communication socket %s\n",mustbe);
exit(EX_USAGE);
}
mailsocket = optarg;
break;
case 's': // KAV control socket
if (*optarg == '\0')
{
fprintf(stderr,"KAV control socket %s\n",mustbe);
exit(EX_USAGE);
}
kavconnect = optarg;
break;
case 't': // Sendmail connect timeout
if (*optarg == '\0')
{
fprintf(stderr,"sendmail connect timeout %s\n",mustbe);
exit(EX_USAGE);
}
_timeout = atoi(optarg);
break;
case 'T': // KAV connect timeout
if (*optarg == '\0')
{
fprintf(stderr,"KAV connect timeout %s\n",mustbe);
exit(EX_USAGE);
}
_kavtimeout = atoi(optarg);
break;
case 'p': // Temp directory
if (*optarg == '\0')
{
fprintf(stderr,"temp directory %s\n",mustbe);
exit(EX_USAGE);
}
tmpdir = optarg;
break;
case 'P': // PID file specification
if (*optarg == '\0')
{
fprintf(stderr,"PID file directory and name %s\n",mustbe);
exit(EX_USAGE);
}
pidfile = optarg;
break;
case 'C': // Config file specification
if (*optarg == '\0')
{
fprintf(stderr,"Config file name %s\n",mustbe);
exit(EX_USAGE);
}
config = optarg;
break;
case 'd': // Daemon mode
_flags._isdaemon = OK;
break;
case 'D': // Debug level
_debug_level = atoi(optarg);
break;
case 'm': // Reject/discard mode
if (*optarg == '\0' )
{
fprintf(stderr,"mode must be specified as \"reject\" or \"discard\"\n");
exit(EX_USAGE);
}
else
if (!(strcmp(optarg,"reject")))
{
_flags._mode_reject = OK;
}
else
_flags._mode_discard = OK;
break;
case 'v': // Version info requested
fprintf(stderr,"version: %s\n%s, version %1d.%2d.%1d\n",
KAV_MILTER,KAV_DATE,VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH);
exit(EX_USAGE);
case 'h': // Help requested or unknown parameter was given
default:
_KAV_milter_usage();
exit(EX_USAGE);
}
}
// At first, process configuration file. Options from command-line should
// overlap configfile options
if (!config)
config = CONFIG;
_KAV_milter_parse_config(config);
// Check command-line specified parameters and values. When checknig
// sendmail timeout, call appropriate procedure to set it. When timeout in command-line
// greather than zero, store it in parameters structure, else use config value
if (_timeout)
_KAV_milter_config.sendmail_timeout = _timeout;
if (_KAV_milter_config.sendmail_timeout < 0)
{
fputs("Sendmail connect timeout must be greather than zero",stderr);
_KAV_milter_close(EX_USAGE);
}
if (smfi_settimeout(_KAV_milter_config.sendmail_timeout) == MI_FAILURE)
{
fputs("Set timeout to sendmail connect (smfi_settimeout) failed",stderr);
_KAV_milter_close(EX_USAGE);
}
// Check KAV timeout, when set, store it, else use config value
if (_kavtimeout)
_KAV_milter_config.KAV_timeout = _kavtimeout;
if (_KAV_milter_config.KAV_timeout < 0)
{
fputs("KAV connect timeout must be greather than zero",stderr);
_KAV_milter_close(EX_USAGE);
}
// Check specified temporary directory against exist, directory and
// permissions.
if (tmpdir)
_KAV_milter_config.temp_directory = tmpdir;
if (stat(_KAV_milter_config.temp_directory,&st) == ERR)
{
fprintf(stderr,"cannot stat tempopary directory: %s\n",strerror(errno));
_KAV_milter_close(EX_TEMPFAIL);
}
if (!S_ISDIR(st.st_mode))
{
fprintf(stderr,"%s is not a directory\n",_KAV_milter_config.temp_directory);
_KAV_milter_close(EX_TEMPFAIL);
}
if (access(_KAV_milter_config.temp_directory, R_OK | W_OK | X_OK) == ERR)
{
fprintf(stderr,"wrong permissions on the %s : %s\n",
_KAV_milter_config.temp_directory,strerror(errno));
_KAV_milter_close(EX_TEMPFAIL);
}
// Store PID file pathname, when requested
if (pidfile)
_KAV_milter_config.pidfile = pidfile;
// Check and store debug level value in config structure
if (_debug_level < 0)
{
fprintf(stderr,
"Debug level %d incorrect, fallback to config value\n",_debug_level);
_debug_level = 0;
}
if (_debug_level)
_KAV_milter_config.debug_level = _debug_level;
// Check on presence -P (--pidfile) parameter
if (pidfile)
_KAV_milter_config.pidfile = pidfile;
// Check on presence -m (--mode) parameter and store it in config structure
if (_flags._mode_reject)
{
_KAV_milter_config._idflags._mode_reject = OK;
_KAV_milter_config._idflags._mode_discard = NO;
}
else
if (_flags._mode_discard)
{
_KAV_milter_config._idflags._mode_reject = NO;
_KAV_milter_config._idflags._mode_discard = OK;
}
// Check on presence -d (--daemon) parameter and store it in config structure
if (_flags._isdaemon)
_KAV_milter_config._idflags._isdaemon = OK;
// Check on presence -c (--sendmail) parameter and store it in config structure
if (mailsocket)
_KAV_milter_config.sendmail_pipe = mailsocket;
// Check on presence -s (--KAV-socket) paramater and store it in config structure
if (kavconnect)
_KAV_milter_config.KAV_pipe = kavconnect;
// When requested, go into daemon mode
if (_KAV_milter_config._idflags._isdaemon)
_KAV_milter_daemon_mode();
// Open system log and send start identification line
if (!_KAV_milter_config._idflags._isdaemon)
openlog(progname, LOG_PID | LOG_PERROR, LOG_MAIL);
else
openlog(progname, LOG_PID, LOG_MAIL);
_flags._openlog = OK;
syslog(LOG_INFO,"starting sendmail filter '%s' version %s",
_KAV_milter_config.progname,_KAV_milter_config.progversion);
// Store PID number of daemonized process
if (stat(_KAV_milter_config.pidfile,&sb) > NULL)
{
unlink(_KAV_milter_config.pidfile);
syslog(LOG_INFO,"Old PID file was detected - unclean shutdown?");
}
if ((pidf = (fopen(_KAV_milter_config.pidfile,"w"))) == NULL)
{
syslog(LOG_ERR,"PID file %s cannot open : %m",_KAV_milter_config.pidfile);
_KAV_milter_close(EX_TEMPFAIL);
}
else
{
_flags._pidfile = OK;
if (_KAV_milter_config.debug_level > 30)
syslog(LOG_ERR,"PID file open, name %s, flag %d",
_KAV_milter_config.pidfile,_flags._pidfile);
fprintf(pidf,"%d",(int) getpid());
fclose(pidf);
}
// Set debug level and print startup parameters
smfi_setdbg(_KAV_milter_config.debug_level);
if (_KAV_milter_config._idflags._mode_reject)
syslog(LOG_INFO,"action with infected - REJECT");
else
syslog(LOG_INFO,"action with infected - DISCARD");
if (_KAV_milter_config.debug_level > 10)
syslog(LOG_INFO,"debug level: %d, sendmail timeout: %d, KAV timeout: %d",
_KAV_milter_config.debug_level, _KAV_milter_config.sendmail_timeout,
_KAV_milter_config.KAV_timeout);
// Set reasonable umask
umask(0027);
// Check and remove, when need old local communication socket file
if (stat(_KAV_milter_config.sendmail_pipe,&st) != ERR)
unlink(_KAV_milter_config.sendmail_pipe);
// Set connection port
if (smfi_setconn(_KAV_milter_config.sendmail_pipe) == MI_FAILURE)
{
syslog(LOG_ERR,"Set connection port (smfi_setconn) failed, port %s",_KAV_milter_config.sendmail_pipe);
_KAV_milter_close(EX_SOFTWARE);
}
// Register this filter in sendmail space
if (smfi_register(kavfilter) == MI_FAILURE)
{
syslog(LOG_ERR,"kavfilter registration (smfi_register) failed");
_KAV_milter_close(EX_UNAVAILABLE);
}
// Main SMFI_ call
if ((retcode = smfi_main()) != MI_SUCCESS)
syslog(LOG_ERR,"Main SMFI_ call (smfi_main) failed, return code %d",retcode);
return retcode;
}
syntax highlighted by Code2HTML, v. 0.9.1