# Copyright (c) 2010 Flowerfire, Inc. All Rights Reserved. anti_spam_smtpproxy = { plugin_version = "3.1.1" info.1.manfacturer = "ASSP" info.1.device = "Anti-spam SMTP Proxy" info.1.version.1 = "1.3.3.1" info.1.version.2 = "1.3.3.8" # 2007-09-20 - gas - 1.0.0 - Current plug-in # 2007-09-20 - gas - 2.1.0 - Added support for 1.3.3.1 logging (almost complete rewrite, # hence the jump in the plugin_version) # 2007-09-24 - gas - 2.1.1 - Added support for old logging style # 2007-12-11 - KBB - 2.1.2 - Fixed a bug where v.remainder was not being set. Also made m- optional # (see examples), having seen a variation where it isn't there. # 2008-01-18 - KBB - 2.2.0 - Added support for 1.3.3.8. by increasing flexibility in parsing the # common portion of the line and by supporting lines without "to:". Eliminated use of key when # collecting since there seemed to be no reason for it, though some entries in the log are related. # 2008-04-30 - KBB - 3.0.0 - Changed collect/accept approach completely. Instead of accepting on # every line, only accept when comment is "is disconnected". Added numeric fields for a spam count # and for immediate disconnects. New fields for queue_id and whitelist_addition. We now support # a 1.3.3.1 variant with queue IDs but no "is disconnected" messages, a 1.3.3.1 variant with # "is disconnected" messages but no queue-id (where it must be assumed that messages # do not interleave), and a 1.3.3.8 variant with both. # 2008-08-18 - KBB - 3.1.0 - Added support for a 1.3.3.8 variation with neither queue_id nor # "is disconnected". Since there was no queue_id, discard_expired_entries = "false" wasn't enough # to capture all the events. Now accepting when the sender changes for the same key, whether # empty or not. This has the side effect of handling repeat queue_ids. # 2010-10-11 - 3.1.1 - MSG - Edited info lines. # 2011-02-08 - 3.1.2 - MSG - Fixed typo in info.1.manfacturer line log.format.format_label = "Anti-Spam SMTP Proxy (ASSP) Log Format" log.miscellaneous.log_data_type = "mail_server" log.miscellaneous.log_format_type = "mail_server" log.format.autodetect_expression = ` matches_regular_expression(volatile.log_data_line, '[A-Z][a-z][a-z]-[0-9]+-[0-9][0-9] [0-9]+:[0-9][0-9]:[0-9][0-9] [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ <[^>]*> to:') or matches_regular_expression(volatile.log_data_line, '^[A-Za-z0-9-]+ [0-9:]+ \\[[^]]+\\] ') or matches_regular_expression(volatile.log_data_line, '^[A-Za-z0-9-]+ [0-9:]+ ASSP version') or matches_regular_expression(volatile.log_data_line, '^[A-Za-z0-9-]+ [0-9:]+ Admin connection from ') ` log.format.parse_only_with_filters = "true" # Where there is no "is disconnect", this is the way entries get accepted. log.format.discard_expired_entries = false log.format.date_format = "auto" log.format.time_format = "auto" # Log fields log.fields = { date = "" time = "" queue_id = "" resulttag = "" source_ip = "" sender = "" recipient = "" whitelist_addition = "" unchecked_recipient = "" comment = "" comment_trail = "" subject = "" #collection_filename = "" message_type = "" immediate_disconnects = "" spam_messages = "" } # log.fields # Log Parsing Filters log.parsing_filters.parse = ` v.subject = ''; # parse the common elements of the line # lines like this with a [ResultTag] and a [ResultTagExtension] # Sep-19-07 11:39:46 [URIBL][sl] 88.288.18.188 to: postmaster@realdomain.com spam determined to be safe, passing on to recipient xyz -> /assp/spam/xyz--374.eml # lines like these with field before IP # Jul-12-07 16:52:20 [White] m-1939c6449 81.173.245.62 to: someone@valid-domain.com local or whitelisted - (attachments unchecked) Liebe_Baboon_finden_Sie_den_Partner_der_Sie -> ./notspam/6449.eml # lines like this with no field before IP # Sep-19-07 12:07:56 [DNSBL] 122.222.222.122 to: someone@realdomain.co.uk failed DNSBL: combined.hello.org SOLD_OUT_LAST_CHANCE_Rolex_Rolex_Rolex_All_Rolexes -> /assp/spam/SOLD_OUT_LAST_CHANCE_Rolex_Rolex_Rolex_All_Rolexes_are_here_porx_--385.eml # lines like this with no resulttag # Dec-10-07 00:00:25 0025c8419 66.174.91.171 <6025385889@vzwpix.com> to: serena@serenity.com recipient accepted unchecked: serena@serenity.com if (matches_regular_expression(current_log_line(), '^([A-Za-z0-9-]+) ([0-9:]+) (\\[([^]]+)\\])?(\\[([^]]+)\\])? ?[a-z-]*([0-9]+[a-z][0-9]+)? ?([0-9.]+) <([^>]*)> (.*)$')) then ( v.key = $7; v.resulttag = $4; # might be empty v.resulttag2 = $6; # might be empty v.sender = $9; v.remainder = $10; # Handles case where there is no "is disconnected" and no queue_id. v.previous_sender = get_collected_field(v.key, 'sender'); if (v.sender ne v.previous_sender and v.previous_sender ne '' and v.previous_sender ne '(empty)') then ( accept_collected_entry(v.key, false); ); set_collected_field(v.key, 'date', $1); set_collected_field(v.key, 'time', $2); set_collected_field(v.key, 'queue_id', v.key); set_collected_field(v.key, 'source_ip', $8); set_collected_field(v.key, 'sender', v.sender); # Here are examples with and without a recipient. # Note that this format only lists one recipient, though multiple recipients may be # accepted unchecked or added to the whitelist. # Dec-10-07 00:00:25 0025c8419 66.174.91.171 <6025385889@vzwpix.com> to: serena@serenity.com recipient accepted unchecked: serena@serenity.com # Dec-10-07 00:00:09 [DNSBLcache] 0009c12612 88.48.28.98 - 88.48.28.98 rejected by dul.dnsbl.sorbs.net (07-12-09/23:52) if (matches_regular_expression(v.remainder, '^to: ([^ ]+) (.*)$')) then ( set_collected_field(v.key, 'recipient', $1); v.remainder = $2; ); v.comment = ''; # Get subject and isolate collection filename if present. # Jul-12-07 16:52:20 [White] m-1939c6449 81.173.245.62 to: someone@valid-domain.com local or whitelisted - (attachments unchecked) Liebe_Baboon_finden_Sie_den_Partner_der_Sie -> ./notspam/6449.eml # Sep-19-07 12:07:56 [DNSBL] 122.222.222.122 to: someone@realdomain.co.uk failed DNSBL: combined.hello.org SOLD_OUT_LAST_CHANCE_Rolex_Rolex_Rolex_All_Rolexes -> /assp/spam/SOLD_OUT_LAST_CHANCE_Rolex_Rolex_Rolex_All_Rolexes_are_here_porx_--385.eml # Dec-10-07 00:07:24 [MessageLimit][low] 0444c7229 66.206.166.226 to: hello@goodbye.com spam determined to be safe, passing on to recipient Bachelor_s_Degree_Review_ # If there is no collection file name after the subject, make sure there is at least one _. if (matches_regular_expression(v.remainder, '^(.*) +([0-9A-Za-z_]+) +-> (.*)$') or matches_regular_expression(v.remainder, '^(.*)( +-> (.*))$') or # no subject, just collection file matches_regular_expression(v.remainder, '^(.*) +([0-9A-Za-z_]*_[0-9A-Za-z_]*) *$')) then ( v.comment = $1; # this supports empty (or _ only) subjects v.subject = replace_all($2, '_', ' '); if (v.subject eq ' ') then v.subject = '(empty)'; set_collected_field(v.key, 'subject', v.subject); # not terribly interesting, un-comment to re-enable #set_collected_field(v.key, 'collection_filename', $4); ); # Jul-12-07 16:55:39 [Noprocessing] m-2139c7403 123.123.123.123 to: someone@valid-domain.com message proxied without processing - (attachments unchecked) else if (matches_regular_expression(v.remainder, '^[ -]*([^ -].*[^ ])[ ]*$')) then ( v.comment = $1; ); if (matches_regular_expression(v.comment, 'whitelist addition: (.*)')) then ( v.whitelisted = get_collected_field(v.key, 'whitelist_addition'); v.comment = ''; if (v.whitelisted ne '(empty)' and v.whitelisted ne '') then ( set_collected_field(v.key, 'whitelist_addition', v.whitelisted . '; ' . $1); ); else ( set_collected_field(v.key, 'whitelist_addition', $1); ); ); else if (matches_regular_expression(v.comment, 'recipient accepted unchecked: (.*)')) then ( v.unchecked = get_collected_field(v.key, 'unchecked_recipient'); v.comment = ''; if (v.unchecked ne '(empty)' and v.unchecked ne '') then ( set_collected_field(v.key, 'unchecked_recipient', v.unchecked . '; ' . $1); ); else ( set_collected_field(v.key, 'unchecked_recipient', $1); ); ); v.old_comment = get_collected_field(v.key, 'comment_trail'); if (v.comment ne '' and v.old_comment ne '(empty)' and v.old_comment ne '' and !contains(v.old_comment, v.comment)) then ( set_collected_field(v.key, 'comment_trail', v.old_comment . '; ' . v.comment); ); else if (v.comment ne '') then ( set_collected_field(v.key, 'comment_trail', v.comment); ); # It is OK to mark something falsely as spam; it is the final value that will be counted. if (matches_regular_expression(v.resulttag, '^(Local/White|White|Local|No[pP]rocessing)$')) then ( set_collected_field(v.key, 'message_type', 'NotSpam'); ); else if (v.resulttag ne '') then ( set_collected_field(v.key, 'message_type', 'Spam'); ); v.old_resulttag = get_collected_field(v.key, 'resulttag'); if (v.resulttag2 ne '') then ( v.resulttag = v.resulttag . ' (' . v.resulttag2 . ')'; ); if (v.resulttag ne '' and v.old_resulttag ne '(empty)' and v.old_resulttag ne '' and !contains(v.old_resulttag, v.resulttag)) then ( set_collected_field(v.key, 'resulttag', v.old_resulttag . '; ' . v.resulttag); ); else if (v.resulttag ne '') then ( set_collected_field(v.key, 'resulttag', v.resulttag); ); # The first three are immediate disconnects and the last is the end of a legitimate message. #Dec-10-07 21:46:24 8383c10416 222.222.222.222 <> is disconnected #Dec-10-07 21:54:03 222.222.222.222 <> is disconnected #Dec-10-07 00:00:09 0009c12612 222.222.222.222 is disconnected #Dec-10-07 00:00:26 0025c8419 222.222.222.222 <6025385889@vzwpix.com> to: lydia@raininspain.com is disconnected if (v.comment eq 'is disconnected') then ( v.recipient = get_collected_field(v.key, 'recipient'); if (v.recipient eq '' or v.recipient eq '(empty)') then ( set_collected_field(v.key, 'immediate_disconnects', 1); ); accept_collected_entry(v.key, false); ); # This way, the comment that is shown in the report is the one prior to "is disconnected" else if (v.comment ne '') then ( set_collected_field(v.key, 'comment', v.comment); ); ); # old logging style from original plug-in for backward compatibility else if (matches_regular_expression(current_log_line(), '^([A-Z][a-z][a-z]-[0-9]+-[0-9][0-9]) ([0-9:]*) ([0-9.]*) <([^>]*)> to: ([^ ]*) ([^$]*)$')) then ( set_collected_field('', 'date', $1); set_collected_field('', 'time', $2); set_collected_field('', 'source_ip', $3); set_collected_field('', 'sender', $4); set_collected_field('', 'recipient', $5); set_collected_field('', 'comment', $6); accept_collected_entry('', false); ); # old logging style from original plug-in for backward compatibility else if (matches_regular_expression(current_log_line(), '^([A-Z][a-z][a-z]-[0-9]+-[0-9][0-9]) ([0-9:]*) ([0-9.]*) <([^>]*)> (relay attempt blocked) for: ([^$]*)$')) then ( set_collected_field('', 'date', $1); set_collected_field('', 'time', $2); set_collected_field('', 'source_ip', $3); set_collected_field('', 'sender', $4); set_collected_field('', 'comment', $5); set_collected_field('', 'recipient', $6); accept_collected_entry('', false); ); ` # Database fields database.fields = { date_time = "" day_of_week = "" hour_of_day = "" queue_id = "" resulttag = "" source_ip = "" sender = "" recipient = "" whitelist_addition = "" unchecked_recipient = "" comment = "" comment_trail = "" subject = "" message_type = "" # not terribly interesting, un-comment to re-enable #collection_filename = "" } # database.fields # Log Filters log.filters = { mark_entry = { label = '$lang_admin.log_filters.mark_entry_label' comment = '$lang_admin.log_filters.mark_entry_comment' value = 'messages = 1;' } # mark_entry count_spam = { label = '$lang_admin.log_filters.count_spam_label' comment = '$lang_admin.log_filters.count_spam_comment' value = 'if (message_type eq "Spam") then spam_messages = 1;' } # count_spam } # log.filters database.numerical_fields = { messages = { default = true requires_log_field = false entries_field = true } # messages spam_messages = "" immediate_disconnects = "" } # database.numerical_fields create_profile_wizard_options = { # How the reports should be grouped in the report menu report_groups = { date_time_group = "" } # report_groups } # create_profile_wizard_options } # anti_spam_smtpproxy