#!/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;

In reply to Exchange to Postfix Firewall Configuration by madbombX

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.