/*-------------------------------------------------------------------------- * Copyright (c) 2001-2004 Igor Daniloff. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at this directory. * *-------------------------------------------------------------------------- Dr.Web (R) Filter for Sendmail (http://www.sendmail.org) --------------------------------------------------------------------------*/ #ifndef lint static const char rcsid[] = "$Id: drweb_smf.c,v 1.87 2004/06/18 15:52:12 asv Exp $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(EMX_DW) #include #else /* EMX */ #include #include #endif /* EMX */ #include #include "libmilter/mfapi.h" #include "dwc_ipc.h" #include "dwc_lib.h" #include "dwc_scan.h" #include "dwc_macro.h" #include "dwc_err.h" #include "dwc_conf.h" #include "dw_options.h" #include "dw_files.h" #include "dw_mail.h" #include "dw_mailscan.h" #include "dw_util.h" typedef SMFICTX DWF_context_t; typedef sfsistat DWF_retcode_t; typedef struct { dwc_fd_t fd_tmp; /* fd for temp file */ dwc_mail_t mail; /* mail info */ long body_offset; /* offset from begin of file to data section */ int all_addrs; /* count of all addresses (senders and recipients) */ int uncheckable_addrs; /* count of all addresses */ dwc_bool_t denied_sender; /* to show was sender denied by addresses list */ dwc_bool_t wait_for_hash; /* wait dwf_msg_end() for deletion hash and pass it */ dwc_string_t helo_str; /* story helo info */ int num_cte_headers; /* number of main Content-Transfer-Encoding headers */ struct stat stat_tmp; /* for extended diagnostic */ } DWF_private_data_t; #if defined(SMFI_DRWEB_PATCHED) static DWF_retcode_t dwf_got_hup( void ); #endif /* SMFI_DRWEB_PATCHED */ static DWF_retcode_t dwf_connect ( DWF_context_t *context, char* peername, _SOCK_ADDR *peeraddr ); static DWF_retcode_t dwf_helo ( DWF_context_t *context, char* helo ); static DWF_retcode_t dwf_from ( DWF_context_t *context, char** from ); static DWF_retcode_t dwf_rcpt ( DWF_context_t *context, char** rcpt ); static DWF_retcode_t dwf_header ( DWF_context_t *context, char* hdr_name, char* hdr_value ); static DWF_retcode_t dwf_header_end ( DWF_context_t *context ); static DWF_retcode_t dwf_msg_chunk ( DWF_context_t *context, u_char* chunk, size_t length ); static DWF_retcode_t dwf_msg_end ( DWF_context_t *context ); static DWF_retcode_t dwf_abort ( DWF_context_t *context ); static DWF_retcode_t dwf_close ( DWF_context_t *context ); static DWF_retcode_t convert_action_to_retcode( dwc_action_t action ); static int reply_file( DWF_context_t *context, const char* path, long offset ); static dwc_bool_t skip_check( DWF_private_data_t *priv, dwc_string_t addr, dwc_bitset_t who ); static dwc_bool_t update_offset( DWF_private_data_t *priv ); #define DW_HOSTNAME_SIZE (512) #define DW_DATE_SIZE (64) #define DW_FILTER_VER "Dr.Web (R) Filter for sendmail ver."DW_VERSION #define DW_X_CHECKED "Dr.Web (R) for Mail Servers on %s host" dw_filter_settings_t dwsmf_conf; struct smfiDesc drweb_filter_desc = { DW_FILTER_VER, /* filter name */ SMFI_VERSION, /* version code -- do not change */ SMFIF_CHGHDRS | SMFIF_CHGBODY | SMFIF_ADDHDRS, /* flags */ dwf_connect, /* connection info filter */ dwf_helo, /* HELO command filter */ dwf_from, /* MAIL envelope sender filter */ dwf_rcpt, /* RCPT envelope recipient filter */ dwf_header, /* header filter */ dwf_header_end, /* end of header */ dwf_msg_chunk, /* body block filter */ dwf_msg_end, /* .\r\n end of message */ dwf_abort, /* message or connection aborted */ dwf_close /* connection cleanup */ }; /* ---------------------------------------------------------------------- */ #define set_custom_reply( ctx, action, reply )\ BEGIN_MACRO\ printLib( DW_LL_DEBUG, "dwlib[%d]: scan::setreply try to set '%s' as reply for action %d", getpid(), (reply), (action) );\ if( dwsmf_conf.postscan.actions.custom_reply\ && ((action) == DW_ACT_REJECT) )\ {\ printLib( DW_LL_VERBOSE, "dwlib[%d]: scan::setreply set '%s' as reply", getpid(), (reply) );\ if( MI_FAILURE == smfi_setreply( (ctx), "554", "5.7.0", (reply) ) )\ {\ printLib( DW_LL_ALERT, "dwlib[%d]: scan::setreply cannot set custom reply" );\ }\ }\ END_MACRO /* ---------------------------------------------------------------------- */ #define get_private_data( ctx ) ((DWF_private_data_t*) smfi_getpriv((ctx))) /* ---------------------------------------------------------------------- */ #define set_private_data( ctx, priv ) smfi_setpriv((ctx), (priv)) /* ---------------------------------------------------------------------- */ #define create_private_data( context, priv ) \ BEGIN_MACRO \ (priv) = DWC_Malloc( DWF_private_data_t*, sizeof(DWF_private_data_t) ); \ if( !(priv) ) \ { \ printLib( DW_LL_ERROR, "cannot create private data" ); \ C_FAIL_EXIT( (context), (priv) ); \ } \ (priv)->helo_str = NULL; \ init_msg_private_data( (context), (priv) ); \ END_MACRO /* ---------------------------------------------------------------------- */ #define init_msg_private_data( context, priv ) \ BEGIN_MACRO \ (priv)->fd_tmp = -1; \ (priv)->mail = NULL; \ (priv)->body_offset = 0; \ (priv)->all_addrs = 0; \ (priv)->uncheckable_addrs = 0; \ (priv)->wait_for_hash = FALSE; \ (priv)->num_cte_headers = 0; \ END_MACRO /* ---------------------------------------------------------------------- */ #define free_msg_private_data( context, priv ) \ BEGIN_MACRO \ if( (priv)->fd_tmp >= 0 ) \ { \ fsync( (priv)->fd_tmp ); \ close( (priv)->fd_tmp ); \ } \ if( (priv)->mail ) \ { \ if( !(priv)->wait_for_hash ) \ { \ dw_mail_delete( (priv)->mail, !dwsmf_conf.log.debug_spool );\ } \ else \ { \ dw_mail_delete( (priv)->mail, TRUE );\ } \ } \ init_msg_private_data( (context), (priv) ); \ END_MACRO /* ---------------------------------------------------------------------- */ #define free_conn_private_data( context, priv ) \ BEGIN_MACRO \ free_msg_private_data( (context), (priv) ); \ DWC_Free( (priv)->helo_str ); \ DWC_Free( (priv) ); \ END_MACRO /* ---------------------------------------------------------------------- */ #define C_FAIL_EXIT( context, priv ) \ BEGIN_MACRO \ if( (priv) ) \ { \ free_conn_private_data( (context), (priv) );\ } \ if( set_private_data( (context), NULL ) != MI_SUCCESS ) \ { \ printLib( DW_LL_ERROR, "%s(%d) - FATAL ERROR: cannot store connection private data", __FILE__, __LINE__ );\ } \ return SMFIS_REJECT; \ END_MACRO /* ---------------------------------------------------------------------- */ #define M_FAIL_EXIT( context, priv ) \ BEGIN_MACRO \ if( (priv) ) \ { \ free_msg_private_data( (context), (priv) );\ } \ if( set_private_data( (context), (priv) ) != MI_SUCCESS ) \ { \ printLib( DW_LL_ERROR, "%s(%d) - FATAL ERROR: cannot store message private data", __FILE__, __LINE__ );\ } \ set_custom_reply( (context), dwsmf_conf.postscan.actions.on_proc_error, dwsmf_conf.postscan.actions.reply_error );\ return convert_action_to_retcode(dwsmf_conf.postscan.actions.on_proc_error);\ END_MACRO /* ---------------------------------------------------------------------- */ #define PROLOGUE( context, priv ) \ BEGIN_MACRO \ (priv) = get_private_data( (context) ); \ if( !(priv) ) \ { \ printLib( DW_LL_ERROR, "%s(%d) - FATAL ERROR: cannot extract private data from context", __FILE__, __LINE__ ); \ M_FAIL_EXIT( (context), (priv) ); \ } \ END_MACRO /* ---------------------------------------------------------------------- */ #define WRITE_TO_TMPFILE( str ) \ BEGIN_MACRO\ if( !dw_write_str( priv->fd_tmp, (str) ) )\ {\ printLib( DW_LL_ERROR, "[pid=%d]: [%s]: cannot write(%s) to %d - (%d) %s", getpid(), dw_mail_get_property( priv->mail, DW_MP_MSGID ), (str), priv->fd_tmp, errno, stdlib_error(errno) );\ if( fstat( priv->fd_tmp, &priv->stat_tmp ) < 0 )\ {\ printLib( DW_LL_ERROR, "[pid=%d]: [%s]: cannot stat fd %d - (%d) %s", getpid(), dw_mail_get_property( priv->mail, DW_MP_MSGID ), priv->fd_tmp, errno, stdlib_error(errno) );\ } else {\ printLib( DW_LL_ERROR, "[pid=%d]: [%s]: fd %d -> { mode = %#o, uid = %d, size =%d }", getpid(), dw_mail_get_property( priv->mail, DW_MP_MSGID ), priv->fd_tmp, priv->stat_tmp.st_mode, priv->stat_tmp.st_uid, priv->stat_tmp.st_size );\ }\ M_FAIL_EXIT( context, priv );\ }\ END_MACRO /* ----------------------------------------------------------------------- */ int main( int argc, char *argv[] ) { int retcode = 0; struct stat sf_stat; dwc_string_t sf_name; DWC_ipc_addr_ptr_t parsed_addr; DWC_ipc_type_t addr_type; DWC_length_t len; #if !defined(EMX_DW) pid_t pid; #endif /* not EMX */ if( !dw_load_settings( argc, argv, &dwsmf_conf ) ) { return EX_USAGE; } dwsmf_conf.scan.hostname = DWC_Malloc( dwc_string_t, DW_HOSTNAME_SIZE ); if( !dwsmf_conf.scan.hostname || (-1 == gethostname( dwsmf_conf.scan.hostname, DW_HOSTNAME_SIZE )) ) { if( !dwsmf_conf.scan.hostname ) { printLib( DW_LL_ALERT, "set hostname to unknown because cannot allocate memory" ); } else { printLib( DW_LL_ALERT, "set hostname to unknown because cannot get hostname - %s", stdlib_error(errno) ); DWC_Free( dwsmf_conf.scan.hostname ); } dwsmf_conf.scan.hostname = DWC_Strdup( "unknown hostname" ); if( !dwsmf_conf.scan.hostname ) { printLib( DW_LL_ERROR, "cannot set hostname" ); return EX_OSERR; } } if( dwsmf_conf.scan.add_headers ) { len = strlen(DW_X_CHECKED) + strlen(dwsmf_conf.scan.hostname) + 1; dwsmf_conf.scan.x_header = DWC_Malloc( dwc_string_t, len ); if( !dwsmf_conf.scan.x_header ) { printLib( DW_LL_ERROR, "cannot preformat X-Antivirus header" ); return EX_OSERR; } --len; snprintf( dwsmf_conf.scan.x_header, len, DW_X_CHECKED, dwsmf_conf.scan.hostname ); dwsmf_conf.scan.x_header[len] = NULLCHAR; } if( !dw_set_user( dwsmf_conf.scan.account ) ) { return EX_OSERR; } #if !defined(EMX_DW) if( !dwsmf_conf.scan.stay_fg ) { pid = dw_daemonize( "/" ); if( pid > 0 ) { /* parent normally exiting */ return 0; } else if( pid < 0 ) { printLib(DW_LL_VERBOSE, "cannot daemonize"); return EX_OSERR; } } #endif /* not EMX */ if( !dw_init_lib(&dwsmf_conf.comm) ) { return EX_SOFTWARE; } /* next check access, so we should drop privilegies before execute it */ /* ASV:4.29 if( !dw_check_settings( &dwsmf_conf, TRUE ) ) { printLib( DW_LL_VERBOSE, "configuration is incorrect" ); retcode = EX_USAGE; goto error_exit; } */ if( !dw_init_agent(&dwsmf_conf.agent) ) { return EX_SOFTWARE; } #if defined(SMFI_DRWEB_PATCHED) printLib( DW_LL_INFO, "set SIGHUP handler" ); if( smfi_register_hup_handler( dwf_got_hup ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "cannot set SIGHUP handler" ); retcode = EX_SOFTWARE; goto error_exit; } #endif /* SMFI_DRWEB_PATCHED */ #if defined(DW_HAVE_BACKLOG) printLib( DW_LL_INFO, "set milter backlog = %d", dwsmf_conf.submit.milter.backlog ); if( smfi_setbacklog( dwsmf_conf.submit.milter.backlog ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "cannot set backlog for milter" ); retcode = EX_SOFTWARE; goto error_exit; } #endif /* DW_HAVE_BACKLOG */ printLib( DW_LL_INFO, "set milter timeout = %d", dwsmf_conf.submit.milter.timeout ); if( smfi_settimeout( dwsmf_conf.submit.milter.timeout ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "cannot set timeout for milter" ); retcode = EX_SOFTWARE; goto error_exit; } printLib( DW_LL_INFO, "set milter loglevel = %d", dwsmf_conf.submit.milter.dbg_level ); if( smfi_setdbg( dwsmf_conf.submit.milter.dbg_level ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "cannot set loglevel for milter" ); retcode = EX_SOFTWARE; goto error_exit; } if( NULL == (parsed_addr = create_ipc_addr( dwsmf_conf.submit.milter.addr, FALSE, &addr_type )) ) { printLib( DW_LL_ERROR, "malformed parameter MilterAddress = %s", dwsmf_conf.submit.milter.addr ); retcode = EX_SOFTWARE; goto error_exit; } if( DWC_IPC_UNIX_SOCKET == addr_type ) { sf_name = dwc_ipc_unix_filename( parsed_addr ); if( !sf_name ) { printLib( DW_LL_ERROR, "malformed parameter MilterAddress = %s", dwsmf_conf.submit.milter.addr ); DWC_Free( parsed_addr ); retcode = EX_SOFTWARE; goto error_exit; } if( 0 == stat( sf_name, &sf_stat ) ) { printLib( DW_LL_INFO, "stalled socket %s detected - try remove", sf_name ); unlink( sf_name ); } else if( errno != ENOENT ) { printLib( DW_LL_ERROR, "stat(%s) failed - %s", sf_name, stdlib_error(errno) ); DWC_Free( parsed_addr ); retcode = EX_SOFTWARE; goto error_exit; } } DWC_Free( parsed_addr ); printLib( DW_LL_INFO, "set milter connection %s", dwsmf_conf.submit.milter.addr ); if( smfi_setconn( dwsmf_conf.submit.milter.addr ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "cannot set address for milter" ); retcode = EX_SOFTWARE; goto error_exit; } if( smfi_register( drweb_filter_desc ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "cannot register filter in Milter API library (libmilter.a)" ); retcode = EX_SOFTWARE; goto error_exit; } printLib( DW_LL_INFO, DW_FILTER_VER" started ..." ); if( smfi_main() != MI_SUCCESS ) { printLib( DW_LL_ERROR, DW_FILTER_VER" stopped abnormally ..." ); retcode = EX_SOFTWARE; } error_exit: printLib( DW_LL_INFO, DW_FILTER_VER" shutdown in progress..." ); dw_fini_agent( &dwsmf_conf.agent ); if( cleanupLib() == DWL_NOERROR ) { printLib( DW_LL_INFO, DW_FILTER_VER" shutdown - OK." ); } else { printLib( DW_LL_INFO, DW_FILTER_VER" shutdown - fail library clean up" ); } return retcode; } /*-------------------------------------------------------------------------*/ static DWF_retcode_t convert_action_to_retcode( dwc_action_t action ) { switch( action ) { case DW_ACT_CONTINUE: return SMFIS_CONTINUE; case DW_ACT_PASS: return SMFIS_ACCEPT; case DW_ACT_REJECT: return SMFIS_REJECT; case DW_ACT_TEMPFAIL: return SMFIS_TEMPFAIL; case DW_ACT_QUARANTINE: /* fallthru */ case DW_ACT_REDIRECT: /* fallthru */ case DW_ACT_DISCARD: return SMFIS_DISCARD; default: return SMFIS_REJECT; } } /* ---------------------------------------------------------------------- */ static dwc_bool_t update_offset( DWF_private_data_t *priv ) { int *poff; poff = dw_mail_get_property( priv->mail, DW_MP_HDRLENGTH ); if( !poff ) { printLib( DW_LL_ERROR, "[%s]: cannot got length of cured message headers", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); return FALSE; } priv->body_offset = *poff; return TRUE; } /* ---------------------------------------------------------------------- */ static int reply_file( DWF_context_t *context, const char* path, long offset ) { char* buf; int len, max_len, readed, written, error, save_errno, src_fd; struct stat st; buf = NULL; src_fd = -1; save_errno = 0; if( stat( path, &st ) < 0 ) { save_errno = errno; printLib( DW_LL_VERBOSE, "cannot stat %s for trial reply", path ); error = -1; goto error_exit; } len = st.st_size - offset; buf = DWC_Malloc( char*, len ); if( buf == NULL ) { save_errno = errno; printLib( DW_LL_VERBOSE, "cannot allocate memory for trial reply" ); error = -1; goto error_exit; } src_fd = open_file( path, O_RDONLY ); if( src_fd < 0 ) { save_errno = errno; printLib( DW_LL_VERBOSE, "cannot open file for trial reply" ); error = -1; goto error_exit; } if( lseek( src_fd, offset, SEEK_SET ) < 0 ) { save_errno = errno; printLib( DW_LL_VERBOSE, "cannot seek to offset=%d file for trial reply", offset ); error = -1; goto error_exit; } written = 0; while( written < len ) { if( (len - written) > IO_BUF_LEN ) max_len = IO_BUF_LEN; else max_len = len - written; readed = read( src_fd, buf + written, max_len ); if( readed < 0 ) { save_errno = errno; printLib( DW_LL_VERBOSE, "cannot read file on trial reply" ); error = -1; goto error_exit; } if( readed == 0 ) { /* end-of-file */ break; } written += readed; } if( smfi_replacebody( context, buf, len ) != MI_SUCCESS ) { printLib( DW_LL_VERBOSE, "cannot replace body on trial reply" ); } error = 0; error_exit: DWC_Free( buf ); /* buf = NULL after that */ if( src_fd >= 0 ) { if( close( src_fd ) < 0 ) { save_errno = errno; printLib( DW_LL_VERBOSE, "cannot close message file after trial-msg" ); error = -1; } } errno = save_errno; return error; } /* ---------------------------------------------------------------------- */ static dwc_bool_t skip_check( DWF_private_data_t *priv, dwc_string_t addr, dwc_bitset_t who ) { dwc_bool_t denied; if( addr ) { denied = !is_checkable_user( dwsmf_conf.scan.users, addr, who, dwsmf_conf.scan.deny_by_default ); if( who == DW_WHO_FROM ) { if( denied && ( (dwsmf_conf.scan.deny_mode == DW_DM_SENDER) || (dwsmf_conf.scan.deny_mode == DW_DM_ONE) ) ) { printLib( DW_LL_INFO, "dwlib[%d]: mail: sender %s is uncheckable - skip check (by deny_mode)", getpid(), addr ); return TRUE; } priv->denied_sender = denied; } else /* RCPT */ { if( denied ) { if( (dwsmf_conf.scan.deny_mode == DW_DM_ONE) || (dwsmf_conf.scan.deny_mode == DW_DM_ONE_RCPT) || ( (dwsmf_conf.scan.deny_mode == DW_DM_SENDER_AND_ONE_RCPT) && (priv->denied_sender) ) ) { printLib( DW_LL_INFO, "dwlib[%d]: mail: rcpt %s is uncheckable - skip check (by deny_mode)", getpid(), addr ); return TRUE; } ++priv->uncheckable_addrs; } ++priv->all_addrs; } } else { if( (dwsmf_conf.scan.deny_mode == DW_DM_SENDER) || (dwsmf_conf.scan.deny_mode == DW_DM_ONE_RCPT) ) { return FALSE; } if( dwsmf_conf.scan.deny_mode == DW_DM_SENDER_AND_ONE_RCPT ) { if( (priv->uncheckable_addrs > 0) && (priv->denied_sender) ) return TRUE; else return FALSE; } if( dwsmf_conf.scan.deny_mode == DW_DM_ALL_RCPTS ) { return ( priv->all_addrs == priv->uncheckable_addrs ? TRUE : FALSE ); } if( dwsmf_conf.scan.deny_mode == DW_DM_ALL ) { if( (priv->all_addrs == priv->uncheckable_addrs) && (priv->denied_sender) ) return TRUE; else return FALSE; } } return FALSE; } #if defined(SMFI_DRWEB_PATCHED) /* ---------------------------------------------------------------------- */ DWF_retcode_t dwf_got_hup( void ) { dwc_bool_t reload_err = FALSE; printLib( DW_LL_ALERT, DW_FILTER_VER" receive SIGHUP, going to reloading ..." ); if( !reload_err && DW_HAVE_A_PATH(dwsmf_conf.scan.users_file) ) { reload_err = !reload_user_list( dwsmf_conf.scan.users, dwsmf_conf.scan.users_file ); } printLib( DW_LL_INFO, DW_FILTER_VER" DenyList reloaded ..." ); if( !reload_err && DW_HAVE_A_PATH(dwsmf_conf.postscan.actions.non_notified_viruses_file) ) { reload_err = !reload_virus_list( dwsmf_conf.postscan.actions.non_notified_viruses, dwsmf_conf.postscan.actions.non_notified_viruses_file ); } printLib( DW_LL_INFO, DW_FILTER_VER" NonNotifiedVirusesList reloaded ..." ); if( !reload_err && DW_HAVE_A_PATH(dwsmf_conf.postscan.actions.non_notified_addresses_file) ) { reload_err = !reload_maddr_list( dwsmf_conf.postscan.actions.non_notified_addresses, dwsmf_conf.postscan.actions.non_notified_addresses_file ); } printLib( DW_LL_INFO, DW_FILTER_VER" NonNotifiedAddressesList reloaded ..." ); if( !reload_err ) { print_user_list( dwsmf_conf.scan.users, dwsmf_conf.log.level ); print_maddr_list( dwsmf_conf.postscan.actions.non_notified_addresses, dwsmf_conf.log.level ); print_virus_list( dwsmf_conf.postscan.actions.non_notified_viruses, dwsmf_conf.log.level ); printLib( DW_LL_ALERT, DW_FILTER_VER" reloading - OK" ); return SMFIS_CONTINUE; } printLib( DW_LL_ALERT, DW_FILTER_VER" reloading failed ..." ); return SMFIS_REJECT; } #endif /* SMFI_DRWEB_PATCHED */ /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * IN: char *peername; * Host domain name, as determined by a reverse lookup on the host * address. * IN: _SOCK_ADDR *peeraddr; * Host address, as determined by a getpeername call on the SMTP * socket. * TYPE: Connection-oriented * CALLED: once at start of each smtp connection * NOTE: Invoked on each connection * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_connect ( DWF_context_t *context, char* peername, _SOCK_ADDR *peeraddr ) { DWF_private_data_t *priv; if( peername ) printLib( DW_LL_VERBOSE, "[%d]: connect with %s", getpid(), peername ); else printLib( DW_LL_VERBOSE, "[%d]: connect with unknown", getpid() ); create_private_data( context, priv ); /* macro - return if error */ if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%d]: %s(%d) - FATAL ERROR: cannot store private data", getpid(), __FILE__, __LINE__ ); C_FAIL_EXIT( context, priv ); } return SMFIS_CONTINUE; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * IN: char *helo; * Value passed to HELO/EHLO command, which should be the domain * name of the sending host (but is, in practice, anything the * sending host wants to send). * TYPE: Connection-oriented * CALLED: between zero and three times * NOTE: Invoked on SMTP HELO/EHLO command * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_helo ( DWF_context_t *context, char* helo ) { DWF_private_data_t *priv; priv = get_private_data( (context) ); if( !priv ) { printLib( DW_LL_ERROR, "%s(%d) - FATAL ERROR: cannot extract private data from context", __FILE__, __LINE__ ); C_FAIL_EXIT( (context), (priv) ); } if( helo && helo[0] && !priv->helo_str ) { printLib( DW_LL_VERBOSE, "[%d]: helo %s", getpid(), helo ); priv->helo_str = DWC_Strdup(helo); } else { printLib( DW_LL_VERBOSE, "[%d]: helo ", getpid() ); } if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%d]: %s(%d) - FATAL ERROR: cannot store private data", getpid(), __FILE__, __LINE__ ); C_FAIL_EXIT( context, priv ); } return SMFIS_CONTINUE; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * IN: char **from; * Null-terminated SMTP command arguments. from[0] is guaranteed * to be the sender address. Later arguments are the ESMTP * arguments. * TYPE: Message-oriented * CALLED: once for each message * NOTE: Invoked on envelope from * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_from ( DWF_context_t *context, char** from ) { DWF_private_data_t *priv; char *mid, *rfrom, *fname, *last_relay; dwc_off_t pos; time_t systime; char prntime[DW_DATE_SIZE]; if( from && from[0] ) printLib( DW_LL_VERBOSE, "envelope from %s", from[0] ); else printLib( DW_LL_VERBOSE, "envelope from <>" ); PROLOGUE( context, priv ); init_msg_private_data( context, priv ); mid = smfi_getsymval( context, "{i}" ); /* get message ID if available */ if( !mid ) { mid = "???"; } /* save originator of message */ if( from && from[0] && (from[0][0] != NULLCHAR) ) { rfrom = dw_addr_normalize(from[0]); } else { if( dwsmf_conf.postscan.actions.on_empty_from == DW_ACT_CONTINUE ) { printLib( DW_LL_VERBOSE, "[%d]: processing with from=<> is allowed - continue", getpid() ); rfrom = DWC_Strdup("<>"); } else { printLib( DW_LL_ALERT, "[%d]: processing from <> is blocked", getpid() ); set_custom_reply( context, dwsmf_conf.postscan.actions.on_empty_from, dwsmf_conf.postscan.actions.reply_empty_from ); free_msg_private_data( context, priv ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return convert_action_to_retcode(dwsmf_conf.postscan.actions.on_empty_from); } } printLib( DW_LL_VERBOSE, "[%d]: retrieve message-id[%d] = %s", getpid(), strlen(mid), mid ); last_relay = smfi_getsymval( context, "{_}" ); /* get last relay record */ if( !last_relay ) { last_relay = "unknown"; } printLib( DW_LL_VERBOSE, "[%s]: retrieve last relay[%d] = %s", mid, strlen(last_relay), last_relay ); printLib( DW_LL_VERBOSE, "[%s]: %d start processing from %s", mid, getpid(), rfrom ); if( skip_check( priv, rfrom, DW_WHO_FROM ) ) { printLib( DW_LL_ALERT, "[%s]: skip check this mail - sender=%s is ", mid, rfrom ); DWC_Free( rfrom ); free_msg_private_data( context, priv ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return SMFIS_ACCEPT; } else { DWC_Free( rfrom ); } priv->fd_tmp = dw_open_tmpfile( &dwsmf_conf.scan.spool, &fname ); if( (priv->fd_tmp < 0) || !fname ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - cannot create mail file", mid, __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } priv->mail = dw_mail_create( fname ); DWC_Free( fname ); if( !priv->mail ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - cannot create mail info", mid, __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } if( !dw_e_set_from( dw_mail_envelope( priv->mail ), from[0] ) ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - cannot save from", mid, __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } if( !dw_mail_set_property( priv->mail, DW_MP_MSGID, mid ) ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - cannot save message ID", mid, __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } if( !dw_e_get_from(dw_mail_envelope(priv->mail)) ) { printLib( DW_LL_ERROR, "[%s]: no memory for private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); M_FAIL_EXIT( context, priv ); } /* create fake headers to allow detect message body as MAIL */ WRITE_TO_TMPFILE( "Return-Path: " ); WRITE_TO_TMPFILE( dw_e_get_from(dw_mail_envelope(priv->mail)) ); WRITE_TO_TMPFILE( "\r\n" ); pos = lseek( priv->fd_tmp, 0, SEEK_CUR ); if( !dw_mail_set_property( priv->mail, DW_MP_HDROFFSET, &pos ) ) { printLib( DW_LL_ERROR, "[%s]: cannot save headers offset", mid ); M_FAIL_EXIT( context, priv ); } WRITE_TO_TMPFILE( "Received: from " ); if( priv->helo_str ) { WRITE_TO_TMPFILE( priv->helo_str ); WRITE_TO_TMPFILE( " ( " ); WRITE_TO_TMPFILE( last_relay ); WRITE_TO_TMPFILE( " )" ); } else { WRITE_TO_TMPFILE( last_relay ); } WRITE_TO_TMPFILE( "\r\n\tby " ); WRITE_TO_TMPFILE( dwsmf_conf.scan.hostname ); WRITE_TO_TMPFILE( " (" ); WRITE_TO_TMPFILE( DW_FILTER_VER ); WRITE_TO_TMPFILE( ")\r\n\tid " ); WRITE_TO_TMPFILE( mid ); systime = time(NULL); if( strftime( prntime, DW_DATE_SIZE, "%a, %d %b %Y %T %Z", localtime(&systime) ) > 0 ) { prntime[DW_DATE_SIZE-1] = NULLCHAR; WRITE_TO_TMPFILE( "; " ); WRITE_TO_TMPFILE( prntime ); } else { printLib( DW_LL_ALERT, "[%s]: cannot get current time", mid ); } WRITE_TO_TMPFILE( "\r\n" ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return SMFIS_CONTINUE; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * IN: char **rcpt; * Null-terminated SMTP command arguments. rcpt[0] is guaranteed * to be the recipient address. Later arguments are the ESMTP * arguments. * TYPE: Message-oriented * CALLED: one or more times for each message * NOTE: Invoked on each envelope recipient * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_rcpt ( DWF_context_t *context, char* rcpt[] ) { DWF_private_data_t *priv; dwc_string_t rrcpt; if( strlen(rcpt[0]) > 0 ) { PROLOGUE( context, priv ); rrcpt = dw_addr_normalize(rcpt[0]); printLib( DW_LL_VERBOSE, "[%s]: envelope to %s", dw_mail_get_property( priv->mail, DW_MP_MSGID ), rrcpt ); if( skip_check( priv, rrcpt, DW_WHO_TO ) ) { printLib( DW_LL_ALERT, "[%s]: skip check this mail - rcpt=%s is ", dw_mail_get_property( priv->mail, DW_MP_MSGID ), rrcpt ); DWC_Free( rrcpt ); free_msg_private_data( context, priv ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return SMFIS_ACCEPT; } else { DWC_Free( rrcpt ); } if( !dw_e_add_rcpt( dw_mail_envelope(priv->mail), rcpt[0] ) ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot add recipient to list %s", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__, rcpt[0] ); M_FAIL_EXIT( context, priv ); } if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } } return SMFIS_CONTINUE; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * IN: char *hdr_name; * Header field name * IN: char *hdr_value; * Header field value * TYPE: Message-oriented * CALLED: zero or more times for each message * NOTE: Invoked on each message header. The content of the header may * have folded white space (that is, multiple lines with * following white space) included. * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_header ( DWF_context_t *context, char* hdr_name, char* hdr_value ) { DWF_private_data_t *priv; if( !hdr_name ) { return SMFIS_CONTINUE; } PROLOGUE( context, priv ); printLib( DW_LL_DEBUG, "[%s]: header - %s:%s", dw_mail_get_property( priv->mail, DW_MP_MSGID ), hdr_name, hdr_value ); if( priv->wait_for_hash ) { return SMFIS_CONTINUE; } if( skip_check( priv, NULL, DW_WHO_ANY ) ) { printLib( DW_LL_ALERT, "[%s]: skip check this mail by deny mode", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); free_msg_private_data( context, priv ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return SMFIS_ACCEPT; } if( dwsmf_conf.postscan.fast_notify && (0 == DWC_strncmp( hdr_name, DRWEB_HASH_HEADER, strlen(DRWEB_HASH_HEADER) )) ) /* asv: -2 for ": " */ { if( 0 == DWC_strncmp( hdr_value, dwsmf_conf.postscan.secure_hash, strlen(dwsmf_conf.postscan.secure_hash) ) ) { printLib( DW_LL_VERBOSE, "[%s]: message contains secure hash", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); priv->wait_for_hash = TRUE; } else { printLib( DW_LL_ALERT, "[%s]: ALERT: anybody tries pass message with incorrect hash", dw_mail_get_property( priv->mail, DW_MP_MSGID )); /* asv future: possible send mail to admin */ } } WRITE_TO_TMPFILE( hdr_name ); WRITE_TO_TMPFILE( ": " ); if( hdr_value ) { if( 0 == DWC_strncmp( hdr_name, "Subject", strlen("Subject") ) ) { if( !dw_mail_set_property( priv->mail, DW_MP_SUBJECT, hdr_value ) ) { printLib( DW_LL_ERROR, "[%s]: no memory to story subject", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); M_FAIL_EXIT( context, priv ); } } else if( 0 == DWC_strncmp( hdr_name, "Content-Transfer-Encoding", strlen("Content-Transfer-Encoding") ) ) { ++priv->num_cte_headers; } WRITE_TO_TMPFILE( hdr_value ); } else { WRITE_TO_TMPFILE( "" ); } WRITE_TO_TMPFILE( "\r\n" ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return SMFIS_CONTINUE; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * TYPE: Message-oriented * CALLED: once for each message * NOTE: Invoked at end of header * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_header_end ( DWF_context_t *context ) { DWF_private_data_t *priv; dwc_off_t spos, epos; PROLOGUE( context, priv ); printLib( DW_LL_VERBOSE, "[%s]: end of headers", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); if( priv->wait_for_hash ) { return SMFIS_CONTINUE; } fsync( priv->fd_tmp ); epos = lseek( priv->fd_tmp, 0, SEEK_CUR ); spos = *((int*)dw_mail_get_property(priv->mail, DW_MP_HDROFFSET)); epos -= spos; printLib( DW_LL_VERBOSE, "[%s]: headers { offset=%d, length=%d }", dw_mail_get_property( priv->mail, DW_MP_MSGID ), spos, epos ); if( !dw_mail_set_property(priv->mail, DW_MP_HDRLENGTH, &epos) ) { printLib( DW_LL_ERROR, "[%s]: cannot save headers length", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); M_FAIL_EXIT( context, priv ); } WRITE_TO_TMPFILE( "\r\n" ); if( dwsmf_conf.scan.local ) { priv->body_offset = lseek( priv->fd_tmp, 0, SEEK_CUR ); } if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return SMFIS_CONTINUE; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * IN: u_char *bodyp; * Pointer to chuck of message body * IN: size_t bodylen; * Length of body data * TYPE: Message-oriented * CALLED: zero or more times for each message * NOTE: Invoked for each body chunk. There may be multiple body * chunks passed to the filter. End-of-line passed as received * from SMTP * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_msg_chunk ( DWF_context_t *context, u_char* chunk, size_t length ) { DWF_private_data_t *priv; PROLOGUE( context, priv ); printLib( DW_LL_DEBUG, "[%s]: message chunk - %d bytes", dw_mail_get_property( priv->mail, DW_MP_MSGID ), length ); if( priv->wait_for_hash ) { return SMFIS_CONTINUE; } if( !dw_write_buf( priv->fd_tmp, chunk, length ) ) { printLib( DW_LL_ERROR, "[%s]: cannot save message body to temporary file - (%d) %s", dw_mail_get_property( priv->mail, DW_MP_MSGID ), errno, stdlib_error(errno) ); M_FAIL_EXIT( context, priv ); } if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return SMFIS_CONTINUE; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * TYPE: Message-oriented * CALLED: once for each message * NOTE: Invoked at end of message. This routine can perform special * operations such as modifying the message header, body, or envelope. * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_msg_end ( DWF_context_t *context ) { DWF_private_data_t *priv; dwc_action_t ret; DWF_retcode_t exit_code; int status, cs_len; char code_str[24]; PROLOGUE( context, priv ); printLib( DW_LL_DEBUG, "[%s]: end of message chunks", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); if( priv->fd_tmp ) { fsync( priv->fd_tmp ); close( priv->fd_tmp ); priv->fd_tmp = -1; } if( priv->wait_for_hash ) { printLib( DW_LL_INFO, "[%s]: notification with valid secure-hash - pass without check", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); if( smfi_chgheader( context, DRWEB_HASH_HEADER, 1, "" ) != MI_SUCCESS ) { printLib( DW_LL_ALERT, "[%s]: cannot remove secure-hash from notification, so discard it", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); exit_code = SMFIS_DISCARD; } else { exit_code = SMFIS_ACCEPT; } goto final_exit; } printLib( DW_LL_VERBOSE, "[%s]: start scanning message from %s ...", dw_mail_get_property( priv->mail, DW_MP_MSGID ), dw_e_get_from(dw_mail_envelope(priv->mail)) ); ret = dw_mail_check( priv->mail, &dwsmf_conf.scan, &dwsmf_conf.postscan, &dwsmf_conf.submit, &dwsmf_conf.agent, &status ); if( ret == DW_ACT_INTERNAL_ERROR0 ) { M_FAIL_EXIT( context, priv ); } set_custom_reply( context, ret, dw_mail_get_property(priv->mail, DW_MP_REPLY_STRING) ); exit_code = convert_action_to_retcode( ret ); if( dwsmf_conf.scan.local && (exit_code == SMFIS_ACCEPT) ) { if( dw_bm_contain(status, DERR_HAVE_CURED | DERR_HAVE_DELETED) ) { printLib( DW_LL_VERBOSE, "[%s]: send cured message back to sendmail", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); if( !update_offset( priv ) ) { printLib( DW_LL_ALERT, "[%s]: cannot update message info", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); set_custom_reply( context, dwsmf_conf.postscan.actions.on_cure_fail, dwsmf_conf.postscan.actions.reply_error ); exit_code = convert_action_to_retcode( dwsmf_conf.postscan.actions.on_cure_fail ); goto final_exit; } if( dw_bm_contain( status, DERR_HAVE_DELETED ) && (priv->num_cte_headers > 0) ) { if( smfi_chgheader( context, "Content-Transfer-Encoding", priv->num_cte_headers, "7bit" ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: cannot change headers in cured message", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); set_custom_reply( context, dwsmf_conf.postscan.actions.on_cure_fail, dwsmf_conf.postscan.actions.reply_error ); exit_code = convert_action_to_retcode( dwsmf_conf.postscan.actions.on_cure_fail ); goto final_exit; } } if( reply_file( context, dw_mail_file(priv->mail), priv->body_offset ) == 0 ) { exit_code = SMFIS_ACCEPT; } else { printLib( DW_LL_ALERT, "[%s]: cannot send cured message back to sendmail - mark as incureable", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); set_custom_reply( context, dwsmf_conf.postscan.actions.on_cure_fail, dwsmf_conf.postscan.actions.reply_error ); exit_code = convert_action_to_retcode( dwsmf_conf.postscan.actions.on_cure_fail ); } } else if( dw_bm_contain(status, DERR_EVAL_KEY) && (priv->body_offset > 0) ) { printLib( DW_LL_VERBOSE, "[%s]: send message with evaluation banner back to sendmail", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); if( update_offset( priv ) ) { reply_file( context, dw_mail_file(priv->mail), priv->body_offset ); } } } if( dwsmf_conf.scan.add_headers ) { cs_len = sizeof(code_str) - 1; snprintf( code_str, cs_len, "%X", status ); code_str[cs_len] = NULLCHAR; if( (smfi_addheader( context, "X-Antivirus", dwsmf_conf.scan.x_header ) != MI_SUCCESS) || (smfi_addheader( context, "X-Antivirus-Code", code_str ) != MI_SUCCESS) ) { printLib( DW_LL_ALERT, "[%s]: cannot add X-Antivirus header to message", dw_mail_get_property( priv->mail, DW_MP_MSGID ) ); } } final_exit: printLib( DW_LL_INFO, "[%s]: processing message from %s is over", dw_mail_get_property( priv->mail, DW_MP_MSGID ), dw_e_get_from(dw_mail_envelope(priv->mail)) ); free_msg_private_data( context, priv ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "[%s]: %s(%d) - FATAL ERROR: cannot store private data", dw_mail_get_property( priv->mail, DW_MP_MSGID ), __FILE__, __LINE__ ); M_FAIL_EXIT( context, priv ); } return exit_code; } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * TYPE: Message-oriented * CALLED: at any time during message processing * NOTE: Invoked if message is aborted outside of the control of the * filter, for example, if the SMTP sender issues an RSET * command. If dwf_abort is called, msg_end will not be called * and vice versa. * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_abort ( DWF_context_t *context ) { DWF_private_data_t *priv; char *mid, *rfrom; priv = get_private_data( context ); if( priv ) { rfrom = dw_e_get_from(dw_mail_envelope(priv->mail)); if( !rfrom ) { rfrom = "unknown"; } mid = dw_mail_get_property( priv->mail, DW_MP_MSGID ); if( mid ) { printLib( DW_LL_ALERT, "[%s]: message from %s is aborted", mid, rfrom ); } else { printLib( DW_LL_ALERT, "message from %s is aborted", rfrom ); } free_msg_private_data( context, priv ); if( set_private_data( context, priv ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "%s(%d) - FATAL ERROR: cannot store private data", __FILE__, __LINE__ ); } } return convert_action_to_retcode(dwsmf_conf.postscan.actions.on_proc_error); } /* ----------------------------------------------------------------------- * IN: SMFICTX *context; * Context of current filtering session * TYPE: Connection-oriented * CALLED: Once at the end of connection * NOTE: Invoked at end of the connection. This is called on close * even if the previous mail transaction was aborted. * ----------------------------------------------------------------------- */ DWF_retcode_t dwf_close ( DWF_context_t *context ) { DWF_private_data_t *priv; printLib( DW_LL_VERBOSE, "[%d]: end of SMTP session", getpid() ); priv = get_private_data( context ); if( priv ) { free_conn_private_data( context, priv ); if( set_private_data( context, NULL ) != MI_SUCCESS ) { printLib( DW_LL_ERROR, "%s(%d) - FATAL ERROR: cannot store private data", __FILE__, __LINE__ ); set_custom_reply( context, dwsmf_conf.postscan.actions.on_proc_error, dwsmf_conf.postscan.actions.reply_error ); } } return SMFIS_CONTINUE; }