#!/usr/bin/perl
=begin comment info
+--------------------------------------------------------------------
+------+
| spam-learn
| $Id: getcrr.pl,v 1.8 2006-11-26 17:00:55 eric Exp $
| Description: Downloads email addresses and creates postfix recipien
+t lists
+--------------------------------------------------------------------
+------+
=end comment info
=cut
use strict;
use warnings;
use Benchmark;
use Config::ApacheFormat;
use Fcntl qw(:flock);
use Getopt::Std qw(getopts);
use Log::Log4perl qw(get_logger :levels);
use Mail::Sender;
use Net::hostent;
use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant ( "LDAP_CONTROL_PAGED" );
use Parallel::ForkManager;
use POSIX qw(strftime);
use Socket;
use Sys::Hostname;
use vars qw
(
%STATS %opt
@Server @addresses @ldap_args @mynetworks @_t
$LOG $_appender $_layout $_pm
$DATE $EMAIL $HEAD $HOST
$config $domains $ldap $relay_domains
);
######################################################################
+##########
# DO NOT EDIT BELOW HERE!!!
# Unless you are hacking up the script :)
######################################################################
+##########
# Just in case we get through the whole script
Bench_Begin();
# Get the options from the command line
getopts('c:dhl:ns:V',\%opt);
# Parse the options and do Usage/Version when necessary
Usage() if $opt{'h'};
Version() if $opt{'V'};
$config = Config::ApacheFormat->new(
expand_vars => 1,
hash_directives => [qw( PostfixBase )],
valid_blocks => [qw( Server )]
);
# Use the new config file if its specified
if ($opt{'c'}) { $config->read($opt{'c'}); }
else { $config->read("/etc/getcrr.conf")
or die "Config File: $!";
}
Setup_Log();
Create_PID();
Setup_ForkManager($config->get("MaxProc"));
Setup_Defaults();
# Log completed initialization
@Server = $config->get("Server");
$LOG->info("Loaded ". ($#Server + 1) ." Total Servers");
$EMAIL .= "Total Servers: ". ($#Server + 1). "\n";
# Iterate over our Server hash
foreach my $sid (@Server) {
my $host = $config->block($sid);
# Skip it if the record's ACTIVE boolean is < 1
if ($host->get("Active") < 0) {
$LOG->info("Skipping Record: ". join(" ",$host->get("Company")) ."
+ - ". $host->get("Host"));
$EMAIL .= join(" ",$host->get("Company")) ." - ". $host->get("Host
+") ." Skipped\n";
next;
} elsif ($host->get("Active") == 1) {
push @mynetworks,
"/^". inet_ntoa(inet_aton($host->get("Host"))) ."\\/32\$/
+ OK\n";
}
# Fork off the children and get going on the queries
my $pid = $_pm->start($host) and next;
if ($host->get("Type") =~ /Exchange/) { GetExchange($host); }
elsif ($host->get("Type") =~ /Nitix/) { GetNitix($host); }
else {
$LOG->error(join(" ",$host->get("Company")). ": '". $host->get("Ty
+pe") ."' is unknown server type.");
$EMAIL .= join(" ",$host->get("Company")). ": '". $host->get("Type
+") ."' is unknown server type.\n";
$_pm->finish;
}
# Closing the forked process
$_pm->finish;
}
# Ensure all the child processes finish
$_pm->wait_all_children;
# Generate all the Postfix Files
Write_Networks($config->get("MyNetworks"));
Write_Relay_Domains($config->get("RelayDomains"));
Write_Transport($config->get("TransportMaps"));
Write_Relay_Recipients($config->get("RelayRecipients"));
Write_SenderScores($config->get("SenderScoresFile"))
if ($config->get("SenderScoresFile"));
# We're done, so remove the PID file.
unlink $config->get("PIDFile");
$LOG->debug("Removed PID file:". $config->get("PIDFile"));
my $runtime = Bench_End();
do {
Send_Email($EMAIL, $runtime)
if defined($config->get("AdminEmail"));
} unless ($opt{'n'});
######################################################################
+##########
# DESCRIPTION: Does the Benchmark open and logs it to the $LOG
# PARAMETERS: None
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Bench_Begin
{
$_t[1] = new Benchmark;
}
######################################################################
+##########
# DESCRIPTION: Does the Benchmark diff and writes it to $LOG
# PARAMETERS: None
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Bench_End
{
$_t[2] = new Benchmark;
$_t[0] = timediff($_t[2], $_t[1]);
$LOG->info("$0 took: ". timestr($_t[0]));
return timestr($_t[0]);
}
######################################################################
+##########
# DESCRIPTION: Creates the PID file if it doesn't alreay exist
# PARAMETERS: None
# RETURN: Nothing
# NOTES: Dies if PIDFile exists
######################################################################
+##########
sub Create_PID
{
Die_Clean("$0 is already running or ended improperly",
"$0 is already running or ended improperly.\n".
"Either delete ". $config->get("PIDFile") ."or wait for
+ the process to end.\n")
if (-e $config->get("PIDFile"));
open (PID, ">".$config->get("PIDFile"));
print PID $$;
close (PID);
$LOG->debug("Created PID file: ". $config->get("PIDFile"));
}
######################################################################
+##########
# DESCRIPTION: Removes the PID file and dies with @param text
# PARAMETERS: Text of $LOG->fatal(), Text of die()
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Die_Clean
{
my ($fatal, $die) = @_;
$LOG->fatal($fatal);
unlink $config->get("PIDFile");
$LOG->debug("Removed PID file: ". $config->get("PIDFile"));
Bench_End();
die($die);
}
######################################################################
+##########
# DESCRIPTION: Queries Exchange Server for email addresses
# PARAMETERS: $host hash
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub GetExchange
{
my $host = shift;
# Connecting to Active Directory domain controllers
$LOG->debug("Beginning ". join(" ",$host->get("Company")));
unless ($ldap = Net::LDAP->new($host->get("IP"))) {
$LOG->warn($host->get("Host"). ": Error connecting to specified do
+main controllers $@ \n");
$_pm->finish;
next;
}
my $mesg = $ldap->bind ( dn => $host->get("User"),
password => $host->get("Pass"));
if ( $mesg->code()) {
$LOG->warn ($host->get("Host"). ":\n". "error:", $mesg->code(),"
+\n","error name: ",$mesg->error_name(),
"\n", "error text: ",$mesg->error_text(),"\n");
$_pm->finish;
next;
}
# How many LDAP query results to grab for each paged round
# Set to under 1000 for Active Directory
my $page = Net::LDAP::Control::Paged->new( size => 990 );
@ldap_args = (
base => $host->get("Base"),
# Change to grab various objects (Contacts, Public Folders, etc.)
# A minimal filter for just users with email would be:
# filter => "(&(sAMAccountName=*)(mail=*))"
filter => "(& (mailnickname=*) (| (&(objectCategory=person)
(objectClass=user)(!(homeMDB=*))(!(msExchHomeSer
+verName=*)))
(&(objectCategory=person)(objectClass=user)(|(ho
+meMDB=*)
(msExchHomeServerName=*)))(&(objectCategory=pers
+on)(objectClass=contact))
(objectCategory=group)(objectCategory=publicFold
+er) ))",
control => [ $page ],
attrs => "proxyAddresses",
);
my $cookie;
while(1) {
# Perform search
$LOG->debug(join(" ",$host->get("Company")). ": Performming Search
+");
my $mesg = $ldap->search( @ldap_args );
$LOG->debug(join(" ",$host->get("Company")). ": Search Completed")
+;
# Setup filenames and open the files
my $DC_DOMAIN = $config->get("Libdir"). "/". $host->get("Host") ."
+_domain";
my $DC_RECIPIENT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_recipients";
my $DC_TRANSPORT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_transport";
open DC_DOMAIN, ">$DC_DOMAIN";
open DC_RECIPIENT, ">$DC_RECIPIENT";
open DC_TRANSPORT, ">$DC_TRANSPORT";
$LOG->debug(join(" ",$host->get("Company")). ": Opened Lib Files")
+;
# Print the headers for each file
print DC_DOMAIN $HEAD; print DC_RECIPIENT $HEAD; print DC_TRANSPOR
+T $HEAD;
# Filtering results for proxyAddresses attributes
$LOG->debug(join(" ",$host->get("Company")). ": Filtering Results"
+);
foreach my $entry ( $mesg->entries ) {
my $name = $entry->get_value( "cn" );
# LDAP Attributes are multi-valued, so we have to print each one
+.
foreach my $mail ( $entry->get_value( "proxyAddresses" ) ) {
# Test if the Line starts with one of the following lines:
# proxyAddresses: [smtp|SMTP]:
# and also discard this starting string, so that $mail is only
+ the
# address without any other characters...
if ( $mail =~ s/^(smtp|SMTP)://gs ) {
# Escape '/' in addresses
$mail =~ s/\//\\\//g;
# Split the address into domains to build the relay_domains file
my ($u, $domain) = split ('@', $mail);
$relay_domains->{$domain} = $host->get("Host");
# Address the address to the array
print DC_RECIPIENT "/^". $mail ."\$/ OK\n";
}
}
}
$LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_RECI
+PIENT");
# Create the $DC_DOMAIN file
foreach my $dom ( keys %{$relay_domains} ) {
print DC_DOMAIN
"/$dom\$/ OK\n".
"/\\.?$dom\$/ OK\n";
print DC_TRANSPORT
"/$dom\$/ smtp:[". $host->get("IP") ."]\n".
"/\\.?$dom\$/ smtp:[". $host->get("IP") ."]\n";
}
undef $relay_domains;
$LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_DOMA
+IN");
$LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_TRAN
+SPORT");
close DC_DOMAIN; close DC_RECIPIENT; close DC_TRANSPORT;
$LOG->debug(join(" ",$host->get("Company")). ": Closed Lib Files")
+;
# Only continue on LDAP_SUCCESS
$mesg->code and last;
# Get cookie from paged control
my($resp) = $mesg->control( LDAP_CONTROL_PAGED ) or last;
$cookie = $resp->cookie or last;
# Set cookie in paged control
$page->cookie($cookie);
$LOG->debug(join(" ",$host->get("Company")). ": Completed");
}
if ($cookie) {
# We had an abnormal exit, so let the server know we do not want a
+ny more
$page->cookie($cookie);
$page->size(0);
$ldap->search( @ldap_args );
$LOG->warn(join(" ",$host->get("Company")). ": LDAP query unsucces
+sful");
$_pm->finish;
}
# Completed a server
$LOG->debug(join(" ",$host->get("Company")). ": Completed");
}
######################################################################
+##########
# DESCRIPTION: Queries Nitix Server for email addresses
# PARAMETERS: $host hash
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub GetNitix
{
my $host = shift;
# Connecting to Active Directory domain controllers
$LOG->debug("Beginning ". join(" ",$host->get("Company")));
unless ($ldap = Net::LDAP->new($host->get("IP"))) {
$LOG->warn($host->get("Host"). ": Error connecting to specified do
+main controllers $@ \n");
$_pm->finish;
next;
}
my $mesg = $ldap->bind ( dn => $host->get("Base"));
if ( $mesg->code()) {
$LOG->warn ($host->get("Host"). ":\n". "error:", $mesg->code(),"
+\n","error name: ",$mesg->error_name(),
"\n", "error text: ",$mesg->error_text(),"\n");
$_pm->finish;
next;
}
# How many LDAP query results to grab for each paged round
my $page = Net::LDAP::Control::Paged->new( size => 990 );
@ldap_args = (
base => $host->get("Base"),
filter => "(&(mail=*))",
control => [ $page ],
attrs => "mail",
);
my $cookie;
while(1) {
# Perform search
$LOG->debug(join(" ",$host->get("Company")). ": Performming Search
+");
my $mesg = $ldap->search( @ldap_args );
$LOG->debug(join(" ",$host->get("Company")). ": Search Completed")
+;
# Setup filenames and open the files
my $DC_DOMAIN = $config->get("Libdir"). "/". $host->get("Host") ."
+_domain";
my $DC_RECIPIENT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_recipients";
my $DC_TRANSPORT = $config->get("Libdir"). "/". $host->get("Host")
+ ."_transport";
open DC_DOMAIN, ">$DC_DOMAIN";
open DC_RECIPIENT, ">$DC_RECIPIENT";
open DC_TRANSPORT, ">$DC_TRANSPORT";
$LOG->debug(join(" ",$host->get("Company")). ": Opened Lib Files")
+;
# Print the headers for each file
print DC_DOMAIN $HEAD; print DC_RECIPIENT $HEAD; print DC_TRANSPOR
+T $HEAD;
# Filtering results for proxyAddresses attributes
$LOG->debug(join(" ",$host->get("Company")). ": Filtering Results"
+);
foreach my $entry ( $mesg->entries ) {
my $mail = $entry->get_value( "mail" );
# Split the address into domains to build the relay_domains file
my ($u, $domain) = split ('@', $mail);
$relay_domains->{$domain} = $host->get("Host");
# Address the address to the array
print DC_RECIPIENT "/^". $mail ."\$/ OK\n";
}
$LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_RECI
+PIENT");
# Create the $DC_DOMAIN file
foreach my $dom ( keys %{$relay_domains} ) {
print DC_DOMAIN
"/$dom\$/ OK\n".
"/\\.?$dom\$/ OK\n";
print DC_TRANSPORT
"/$dom\$/ smtp:[". $host->get("IP") ."]\n".
"/\\.?$dom\$/ smtp:[". $host->get("IP") ."]\n";
}
undef $relay_domains;
$LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_DOMA
+IN");
$LOG->debug(join(" ",$host->get("Company")). ": Wrote out $DC_TRAN
+SPORT");
close DC_DOMAIN; close DC_RECIPIENT; close DC_TRANSPORT;
$LOG->debug(join(" ",$host->get("Company")). ": Closed Lib Files")
+;
# Only continue on LDAP_SUCCESS
$mesg->code and last;
# Get cookie from paged control
my($resp) = $mesg->control( LDAP_CONTROL_PAGED ) or last;
$cookie = $resp->cookie or last;
# Set cookie in paged control
$page->cookie($cookie);
$LOG->debug(join(" ",$host->get("Company")). ": Completed");
}
if ($cookie) {
# We had an abnormal exit, so let the server know we do not want a
+ny more
$page->cookie($cookie);
$page->size(0);
$ldap->search( @ldap_args );
$LOG->warn(join(" ",$host->get("Company")). ": LDAP query unsucces
+sful");
$_pm->finish;
}
# Completed a server
$LOG->debug(join(" ",$host->get("Company")). ": Completed");
return;
}
######################################################################
+##########
# DESCRIPTION: Sends the stats email
# PARAMETERS: $EMAIL
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Send_Email
{
my ($body,$runtime) = @_;
$body .= "Runtime: $runtime\n";
# Create the host portions of the message
foreach my $host (sort keys %STATS) {
my $length = length $host;
$body .= "\n\n+-". "-" x $length ."-+\n".
"| $host |\n".
"+-". "-" x $length ."-+\n\n".
"Total Domains: ". $STATS{$host}->{"RelayDoma
+ins"}. "\n".
"Total Recipients: ". $STATS{$host}->{"RelayR
+ecipients"}. "\n".
"Total Runtime: ". $STATS{$host}->{"Diff"} ."\n";
}
# Fix this ugly module addition
$Mail::Sender::NO_X_MAILER = 1;
eval {
(new Mail::Sender)->MailMsg(
{
smtp => "hood.tforge.com",
from => "\"Postmaster\" <postmaster\@tforge.com>",
to => scalar join(" ", $config->get("AdminEmail")),
cc => scalar join(" ", $config->get("AdminEmailCC")),
subject => "CRR Statistics ($HOST on $DATE)",
msg => "$body"
})
or die "Error Sending Mail: $Mail::Sender::Error";
};
$LOG->info("Sent email to ". scalar join(" ", $config->get("AdminEma
+il")));
$LOG->info("Sent email to ". scalar join(" ", $config->get("AdminEma
+ilCC")))
if (defined($config->get("AdminEmailCC")));
return;
}
######################################################################
+##########
# DESCRIPTION: Sets up global program defaults
# PARAMETERS: None
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Setup_Defaults
{
$DATE = strftime("%Y-%b-%d", localtime(time));
$HOST = Sys::Hostname::hostname();
$EMAIL = <<EMAIL;
+---------------------------------+
| Get Concurrent Relay Recipients |
| $DATE on $HOST |
+---------------------------------+
+--------+
| Totals |
+--------+
EMAIL
# Prepare and write the file header
my ($now) = strftime("%Y-%b-%d %H:%M:%S ", localtime(time));
$HEAD = <<HEAD;
# File automatically generated
# DO NOT EDIT BY HAND. YOUR CHANGES WILL BE LOST.
# Last created on: $now
HEAD
# Add the localhost class to @mynetworks
push @mynetworks,
"/^127.0.0.0\\/8\$/ OK\n";
return;
}
######################################################################
+##########
# DESCRIPTION: Sets up everything for $LOG
# PARAMETERS: None
# RETURN: Help text to STDOUT
# NOTES:
######################################################################
+##########
sub Setup_Log
{
# Setup the logging functionality
# Logging Levels: WARN,DEBUG,ERROR,INFO,FATAL
$LOG = get_logger();
$LOG->level($INFO);
$LOG->level($DEBUG)
if ($config->get("Debug") or $opt{'d'});
$_appender = Log::Log4perl::Appender->new(
"Log::Dispatch::File",
filename => $config->get("LogFile"),
mode => "append",
);
# Create the log formatting
$_layout = Log::Log4perl::Layout::PatternLayout->new(
"%d{MMM dd HH:mm:ss} %p> %F{1}[%P]: %L: %m%n");
$LOG->add_appender($_appender);
$_appender->layout($_layout);
return;
}
######################################################################
+##########
# DESCRIPTION: Sets up everything for the Parallel Forkmanager
# PARAMETERS: None
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Setup_ForkManager
{
# Begin ForkManager
my $_max_procs = shift;
$_pm = new Parallel::ForkManager($_max_procs);
# Log at process fork
$_pm ->run_on_start(
sub { my ($pid, $host) = @_;
$LOG->debug("Forking process PID: $pid\n");
$STATS{$host->get("Host")}->{"Start"} = new Benchmark;
}
);
# Log at process copmletion
$_pm ->run_on_finish(
sub { my ($pid, $exit_code, $host) = @_;
$LOG->debug("Finishing up process PID: $pid\n");
$STATS{$host->get("Host")}->{"End"} = new Benchmark;
$STATS{$host->get("Host")}->{"Diff"} = timestr(timediff($STATS{$h
+ost->get("Host")}->{"End"}, $STATS{$host->get("Host")}->{"Start"}));
}
);
$_pm->run_on_wait(
sub {
$LOG->debug("Waiting for children to finish")
},
5.0
);
$LOG->debug("ForkManger initialized");
return;
}
######################################################################
+##########
# DESCRIPTION: Prints the Usage Menu to STDOUT
# PARAMETERS: None
# RETURN: Help text to STDOUT
# NOTES:
######################################################################
+##########
sub Usage
{
print <<USAGE;
$0 -dhnV -c <log_file>
-c Use this config file (default: /etc/config/getcrr.conf)
-d Debugging mode (via logfile)
-h Prints out this help menu
-n Do not send any email
-V Prints out version information
USAGE
exit(0);
}
######################################################################
+##########
# DESCRIPTION: Prints the Version to STDOUT
# PARAMETERS: None
# RETURN: Version text to STDOUT
# NOTES:
######################################################################
+##########
sub Version
{
my $VERSION = do { my @r = (q$Revision: 1.8 $ =~ /\d+/g); sprintf "%
+d."."%03d" x $#r, @r };
print <<VERSION;
Get Concurrent Relay Recipients v$VERSION
VERSION
exit(0);
}
######################################################################
+##########
# DESCRIPTION: Write the NETWORKS file
# PARAMETERS: Location of MyNetworks map
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Write_Networks
{
my $NETWORKS = shift;
open NETWORKS, ">$NETWORKS" or
Die_Clean("Error Opening $NETWORKS: $!", "Error Opening $NETWORKS:
+ $!");
# Lock the file
flock NETWORKS, LOCK_EX;
print NETWORKS $HEAD;
print NETWORKS @mynetworks;
flock NETWORKS, LOCK_UN;
close NETWORKS;
$EMAIL .= "Total Networks: ". ($#mynetworks + 1) ."\n";
$LOG->info("Wrote $NETWORKS");
return;
}
######################################################################
+##########
# DESCRIPTION: Write the Relay_Domains file
# PARAMETERS: Location of the RelayDomains map
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Write_Relay_Domains
{
my $DOMAINS = shift;
my (@exclude_domains, $total_domains);
# Begin iterating over the domain files in the directory
$LOG->debug("Creating $DOMAINS file");
# Only write the file once all the queries are successful
open DOMAINS, ">$DOMAINS" or
Die_Clean("Cannot Open $DOMAINS: $!", "Cannot Open $DOMAINS: $!");
# Lock the file
flock DOMAINS, LOCK_EX;
print DOMAINS $HEAD;
foreach my $sid (@Server) {
my $host = $config->block($sid);
@exclude_domains = $host->get("ExcludeDomain");
next if $host->get("Active") == -1;
$LOG->debug("Processing ". $host->get("Host") ."_domain file");
open DC_DOMAIN, $config->get("Libdir") ."/". $host->get("Host") ."
+_domain"
or do {
$LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_domain");
next;
};
while (my $line = <DC_DOMAIN>) {
next unless $line =~ /^\/.*$/;
my $domain = $1 if ($line =~ /^\/\.?(.*)\$\/.*$/);
do {
$STATS{$host->get("Host")}->{"RelayDomains"}++;
$total_domains++;
print DOMAINS $line;
} unless (grep { m/$domain/ } @exclude_domains );
}
close DC_DOMAIN;
}
flock DOMAINS,LOCK_UN;
close DOMAINS;
$LOG->info("Wrote $DOMAINS");
$EMAIL .= "Total Relay Domains: ". ($total_domains / 2) ."\n"
+;
return;
}
######################################################################
+##########
# DESCRIPTION: Write the Relay_Recipients file
# PARAMETERS: None
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Write_Relay_Recipients
{
my $RECIP = shift;
my (@exclude_domains,$reject_domains,$total_recipients);
$LOG->debug("Creating $RECIP file");
# Only write the file once all the queries are successful
open RECIP, ">$RECIP" or
Die_Clean("Cannot Open $RECIP: $!", "Cannot Open $RECIP: $!");
# Lock the file
flock RECIP, LOCK_EX;
print RECIP $HEAD;
foreach my $sid (@Server) {
my $host = $config->block($sid);
@exclude_domains = $host->get("ExcludeDomain");
next if $host->get("Active") == -1;
$LOG->debug("Processing ". $host->get("Host") ."_recipients file")
+;
open DC_RECIP, $config->get("Libdir") ."/". $host->get("Host") ."_
+recipients"
or do {
$LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_recipients");
next;
};
while (my $line = <DC_RECIP>) {
next unless $line =~ /^\/.*$/;
my ($user,$domain) = ($1,$2) if ($line =~ /^\/\^(.*?)\@(.*?)\$\/
+.*OK$/);
do {
$STATS{$host->get("Host")}->{"RelayRecipients"}++;
$total_recipients++;
print RECIP $line;
$domains->{$domain} = 1;
} unless (grep { m/$domain/ } @exclude_domains );
}
close DC_RECIP;
}
print RECIP "\n\n".
"# REJECT all other emails not destined for a user in our do
+mains.\n".
"\n";
foreach my $dom ( keys %{$domains} ) {
$reject_domains++;
$dom =~ s/OK/REJECT/;
print RECIP "/^.*\@$dom\$/ REJECT\n";
}
flock RECIP, LOCK_UN;
close RECIP;
$LOG->info("Wrote $RECIP");
$EMAIL .= "Total Relay Recipients: $total_recipients\n".
"Total Reject Domains: $reject_domains\n";
return;
}
######################################################################
+##########
# DESCRIPTION: Prints the SPAM recipients to the SPAM hash file
# PARAMETERS: Hash of domains to be added
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Write_SenderScores
{
my $SCORES = shift;
$LOG->debug("Creating $SCORES file");
# Only write the file once all the queries are successful
open SCORES, ">$SCORES" or
Die_Clean("Cannot Open $SCORES: $!", "Cannot Open $SCORES: $!");
# Lock the file
flock SCORES, LOCK_EX;
print SCORES $HEAD;
foreach my $sid (@Server) {
my $host = $config->block($sid);
next if $host->get("Active") == -1;
$LOG->debug("Processing ". $host->get("Host") ."_recipients file")
+;
open DC_SCORES, $config->get("Libdir") ."/". $host->get("Host") ."
+_recipients"
or do {
$LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_recipients");
next;
};
while (my $line = <DC_SCORES>) {
next unless $line =~ /^\/.*$/;
my ($user,$domain) = ($1,$2) if ($line =~ /^\/\^(.*?)\@(.*?)\$\/
+.*OK$/);
# The format of the SPAM_SITES file
# .example.com -4.0
print SCORES "$user\@$domain ". $config->get("WLSende
+rScore") ."\n";
$domains->{$domain} = 1;
}
close DC_SCORES;
}
# Add the whitelist and blacklist sender specified in the config fil
+e
my (@wl) = $config->get("WLSenders");
my (@bl) = $config->get("BLSenders");
$EMAIL .= "Total Whitelist Senders: ". ($#wl + 1) ."\n".
"Total Blacklist Senders: ". ($#bl + 1) ."\n";
do {
print SCORES "\n\n".
"# Additional Whitelist senders\n";
foreach my $sender (@wl) {
print SCORES "$sender ". $config->get("WLSenderScore"
+) ."\n";
}
} if ($config->get("WLSenderScore"));
do {
print SCORES "\n\n".
"# Additional Blacklist senders\n";
foreach my $sender (@bl) {
print SCORES "$sender ". $config->get("BLSenderScore"
+) ."\n";
}
} if ($config->get("BLSenderScore"));
print SCORES "\n\n".
"# SCORES all other emails seemingly from a user in one of o
+ur domains.\n".
"\n";
foreach my $dom ( keys %{$domains} ) {
print SCORES ".$dom ". $config->get("BLLocalSenderScore
+") ."\n"
unless ($dom =~ /\$\//);
}
flock SCORES, LOCK_UN;
close SCORES;
$LOG->info("Wrote $SCORES");
return;
}
######################################################################
+##########
# DESCRIPTION: Write the TRANSPORT file
# PARAMETERS: Location of the Transport map
# RETURN: Nothing
# NOTES:
######################################################################
+##########
sub Write_Transport
{
my $TRANSPORT = shift;
my (@exclude_domains,$total_transports);
$LOG->debug("Creating $TRANSPORT file");
# Only write the file once all the queries are successful
open TRANSPORT, ">$TRANSPORT" or
Die_Clean("Cannot Open $TRANSPORT: $!", "Cannot Open $TRANSPORT: $
+!");
# Lock the file
flock TRANSPORT, LOCK_EX;
print TRANSPORT $HEAD;
foreach my $sid (@Server) {
my $host = $config->block($sid);
@exclude_domains = $host->get("ExcludeDomain");
next if $host->get("Active") == -1;
$total_transports++;
$LOG->debug("Processing ". $host->get("Host") ."_transport file");
open DC_TRANSPORT, $config->get("Libdir") ."/". $host->get("Host")
+ ."_transport"
or do {
$LOG->warn("Error opening file: ". $config->get("Libdir") ."/"
+. $host->get("Host") ."_transport");
next;
};
while (my $line = <DC_TRANSPORT>) {
next unless $line =~ /^\/.*$/;
my $domain = $1 if ($line =~ /^\/\.?(.*)\$\/.*$/);
print TRANSPORT $line
unless (grep { m/$domain/ } @exclude_domains );
}
close DC_TRANSPORT;
}
my (@bmx) = $config->get("BackupMX");
do {
print TRANSPORT "\n\n".
"# Secondary MX Records\n\n";
foreach my $server (@bmx) {
print TRANSPORT "/^.*\$/ relay:[$server]\n";
}
$EMAIL .= "Total Backup MX Servers: ". ($#bmx + 1) ."\n";
} if ($config->get("BackupMX"));
flock TRANSPORT, LOCK_UN;
close TRANSPORT;
$LOG->info("Wrote $TRANSPORT");
$EMAIL .= "Total Transports: $total_transports\n";
return;
}
######################################################################
+##########
=head1 NAME
GetCRR - Get Company Relay Recipients
=head1 SYNOPSIS
Usage: getcrr.pl && postfix reload
=cut
=head1 VERSION
$Revision: 1.8 $
=cut
=head1 DESCRIPTION
This script will pull all users SMTP addresses from your Active Direc
+tory
(including primary and secondary email addresses) and list them in th
+e
format "user@example.com OK" which Postfix uses with relay_recipient_
+maps.
It will also automatically create a mynetworks file, transport file,
+and
relay_domains file to ensure all information is properly included.
Additionally, if you have amavisd installed, you can specify whitelis
+t
and blacklist information for the information retrieved. You can als
+o
include senders and recipients not on either list and assign them a s
+core.
Be sure to double-check the path to perl above.
Don't forget to restart postfix after running this script.
=cut
=head1 TODO
Need to modify to do the following:
+ Add the Usage() output to SYNOPSIS
+ Incorporate reloading postfix and amavisd into the script
+ Add Perl formatting for the output files so everything looks pret
+ty
+ Add option in config to accept email for *@domain.tld
- May need to modify type
+ Add to statistics
- Probable email address count (less the stupid Exchange email ad
+dy's)
=cut
=head1 CAVEATS
It is possible for an email account on an Exchange server to the same
+ on
another Exchange server. For instance, if you are querying ex01 and
+ex02
and tom@ex.com is an admin and has an account on both, then this scri
+pt
will create the postfix transport map for whichever address is proces
+sed
first. To fix this you have to make a "force" entry in the config fi
+le.
=cut
=head1 CONFIGURATION
The default configuration file is /etc/getcrr.conf. All variables
listed in the config file are explained in the config file.
=cut
######################################################################
+##########
1;
|