Well, it's not a trivial rule, what it does is that it extracts RCPT TO fields and checks them via en external sideband connection and if the user doesn't exist it fakes the server response and doesn't pass it to the server. We are using it for the intermediate time of migration of mail systems and the incoming mail is already going trough the new system, which doesn't know which users are valid and which not. And some other fixups (like deleting STARTLS from options)..
The error lines show the error at
TCP::respond $static::DiePerm
but in less than 1% of cases, so it doesn't seem as an error as such (I think)..
Here goes:
when RULE_INIT {
set static::debugSCRT 0
set static::ConnTimeout 1000
set static::ConnIdle 30
set static::VServer "LdapCheck"
set static::DieTemp "450 Temporary unavailable\r\n"
set static::DiePerm "550 Mailbox unavailable\r\n"
set static::STLS "250-STARTTLS\r\n"
set static::RCPTBAD "550 Invalid recipient: "
set static::EOT "\r\n"
set static::HSLpool "mail.syslog"
set static::collectBuffer 40000
}
when CLIENT_ACCEPTED {
set host [IP::client_addr]
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>" }
set hsl [HSL::open -proto UDP -pool $static::HSLpool]
set dataon 0
set ischecked 0
}
when CLIENT_DATA {
all the commands
set cmds [split [TCP::payload] $static::EOT];
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: List>$cmds<" }
skip DATA section
if { [expr {$dataon==1}] }{
serverside { TCP::collect }
TCP::release
}
this we process
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: Tpay>[TCP::payload]<" }
catch HELO section, reset ischecked
if { ([lsearch $cmds "HELO*"] > -1) ||
([lsearch $cmds "EHLO*"] > -1) ||
([lsearch $cmds "RSET*"] > -1) } {
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: resetting params" }
set ischecked 0
set dataon 0
}
catch STARTLS section
if { [lsearch $cmds "STARTTLS*"] > -1 } {
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: block STARTLS" }
TCP::respond "502 TLS not supported\r\n"
TCP::payload replace 0 [TCP::payload length] ""
TCP::release
serverside { TCP::collect }
}
foreach cdata [lsearch -inline -glob -all $cmds "RCPT *"] {
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: RCPT TO...>$cdata<" }
extract email
set email [split [lindex [lsearch -inline -glob [split $cdata <>] *@*] 0] @]
set fullmail [join $email @]
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>em: >$email<>$fullmail<" }
if { [llength $email] } { got the email, check the email
get domain
set domain [lindex $email 1]
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>dom: >$domain<" }
check if it is in backupMX list
if { [class match $domain equals MailPassDomains] }{
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>:WL pass trough $fullmail" }
pass directly
set ischecked 1
} else {
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: sideband check email" }
check in LDAP, open a connection
set conn [connect -timeout $static::ConnTimeout -idle $static::ConnIdle -status conn_status $static::VServer]
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: connGet: <$conn> connStatus: <$conn_status>" }
connection ok?
if {$conn eq ""}{
if { $static::debugSCRT >= 0 }{ log local0.debug "C<$host>: failed opening sideband connection" }
it failed, die temp
TCP::respond $static::DieTemp
TCP::close
}
conn ok, send&recieve
set send_info [send -timeout $static::ConnTimeout -status send_status $conn "$fullmail$static::EOT"]
set recv_data [recv -status recv_status -timeout $static::ConnTimeout $conn]
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>sband-rcv: <$recv_data>" }
if { [string length $recv_data] } {
make actions, response 0-user ok,1-no user,2-err
switch -glob $recv_data {
0* {
allow trough
if { $static::debugSCRT > 0 }{ log local0.debug "C<$host>: OK >$fullmail<" }
HSL::send $hsl "<134> LDAPCHECK: <$host>: OK >$fullmail<"
set ischecked 1
}
1* {
drop permanently
if { $static::debugSCRT > 0 }{ log local0.debug "C<$host>: DROP >$fullmail<" }
HSL::send $hsl "<134> LDAPCHECK: <$host>: BAD >$fullmail<"
TCP::respond "$static::RCPTBAD <$fullmail>\r\n"
TCP::payload replace 0 [TCP::payload length] ""
TCP::release
serverside { TCP::collect }
}
2* {
drop temporary
if { $static::debugSCRT > 0 }{ log local0.debug "C<$host>: TEMP FAIL >$fullmail<" }
HSL::send $hsl "<134> LDAPCHECK: <$host>: TEMP FAIL >$fullmail<"
TCP::respond $static::DieTemp
TCP::close
}
}
} else {
drop temporary
if { $static::debugSCRT > 0 }{ log local0.debug "C<$host>: TEMP FAIL/NO CONN" }
HSL::send $hsl "<134> LDAPCHECK: <$host>: TEMP FAIL/NO CONN"
TCP::respond $static::DieTemp
TCP::close
}
}
} else { wrong email format
drop perm
if { $static::debugSCRT > 0 }{ log local0.debug "C<$host>: DROP/BAD EMAIL FORMAT" }
HSL::send $hsl "<134> LDAPCHECK: <$host>: DROP/BAD EMAIL FORMAT"
TCP::respond $static::DiePerm
TCP::close
}
end check the email
}
catch DATA section
if { [lsearch $cmds "DATA*"] > -1 } {
don't buffer DATA
if { $static::debugSCRT > 1 }{ log local0.debug "C<$host>: DATA, skip processing" }
if { [expr {$ischecked != 1}] }{
this one got trough checking somehow
if { $static::debugSCRT > 0 }{ log local0.debug "C<$host>: DROP/NOT PROPERLY CHECKED" }
HSL::send $hsl "<134> LDAPCHECK: <$host>: DROP/NOT PROPERLY CHECKED"
TCP::respond $static::DieTemp
TCP::close
}
set dataon 1
}
nothing else to do
serverside { TCP::collect }
TCP::release
TCP::collect
}
when SERVER_CONNECTED {
TCP::collect
}
when SERVER_DATA {
if { $static::debugSCRT > 1 }{ log local0.debug "S<$host>: [TCP::payload]" }
check end of DATA
if { [expr {$dataon==1}] &&
([string toupper [TCP::payload]] starts_with 250) }{
if { $static::debugSCRT > 1 }{ log local0.debug "S<$host>: end of DATA" }
set dataon 0
}
remove STARTTLS from response
if { [string toupper [TCP::payload]] contains "STARTTLS" } {
if { $static::debugSCRT > 1 }{ log local0.debug "S<$host>: hiding TLS" }
TCP::payload replace [string first $static::STLS [TCP::payload]] [string length $static::STLS] ""
}
TCP::release
clientside { TCP::collect }
}