Forum Discussion

Richard__Harlan's avatar
Richard__Harlan
Historic F5 Account
Nov 15, 2005

SMTP Sensitivity Tag

What we are looking at doing is make LB desision based on the SMTP Sensitivity Tag. If the Tag is marked Confidential then we will send the client to PGP mail gateways, other wise the BigIp will send to the main Gateways. At first this seemed rather stright foward. The problem I saw is the Tag will comin into the client has aready set up a connection to to the Mail Gateway, and at this poit it is two late to rip out the connection and push to a now node.

 

 

Now can I buffer up the SMTP connection and waite for the Confidential tag then pick a node and pass all information to the node. The main problem I see even with that is I think the mail client will want a response from the gateway. Any help of ideas will be great. Thanks
  • unRuleY_95363's avatar
    unRuleY_95363
    Historic F5 Account
    Yes, the iRule would need to respond and proxy the SMTP server. The real challenge with SMTP is the option negotation that occurs after the initial HELO/EHLO handshake. The trick is to a) have a list of options that the servers support so the iRule can proxy an appropriate response; and b) remember the options negotiated so that when the connection is made to the server, they can be replayed.

     

     

    Another approach might be to go ahead and load balance to a server, save all the client messages and then LB::detach when you discover the Confidential tag and then replay the initial exchange to the new server.

     

     

    Good luck and hopefully you'll share your result with us...
  • Richard__Harlan's avatar
    Richard__Harlan
    Historic F5 Account
    Is there a good way to replay the conversion to the server. THe way I was going to do it was to store the client response into a array then TCP::respond at the right time to the server. Is there a better way then this? Thanks
  • Richard__Harlan's avatar
    Richard__Harlan
    Historic F5 Account
    Ok I have writen the First part of my irule to load balance based on STMP Sensitivity. Right now the Rule is trying to act like a STMP server. Below is the RULE is there a simpler way of doing this? Also the varable ::tcpdata how will it act when this is rule is rule mutiple times at the same time?

    I will add the LD logic and the server connection latter, still testing the SMTP gateway part first. As a side tought I was thinking we could use this STMP rule as a honypot for SPAM and log information about the source and use it to build better spam filters. Thanks

    when RULE_INIT {
       set ::tcpdata ""
    }
    when CLIENT_ACCEPTED {
    set ::tcpdata ""
    TCP::respond "220 fdpnmailgw2.mailgws.com SMTP Relay Service ready\n\r"
    TCP::collect
    }
    when CLIENT_DATA {
    if { $::tcpdata starts_with "EHLO"} {
       TCP::respond "250-fdpnmailgw2.thehouseofspam.com Hello\r\n"
       TCP::respond "250-DSN\r\n"
       TCP::respond "250 SIZE 52428800\r\n"
       set servername $::tcpdata
       set ::tcpdata ""
       log "Servername = $servername"
     }
    if { $::tcpdata contains "MAIL FROM:" }{
       TCP::respond "250 OK\r\n"
       set mailfrom $::tcpdata
       set ::tcpdata ""
       set rec "1"
       log "Mail from = $mailfrom"
     }
    if { $::tcpdata contains "RCPT TO:" } {
        if Rec = 1 this is the mail to line
        if { $rec == "1" } {
          set mailto $::tcpdata
          set rec "2"
          set ::tcpdata ""
          log "mail to: $mailto"
        if rec = 2 this is the CC line
       } elseif { $rec == "2" } {
         set mailcc $::tcpdata
         set rec "3"
         set ::tcpdata ""
         log "mail cc: $mailcc"
        if mail =3 this is the BCC line
       } elseif { $rec == "3" } {
         set mailbcc $::tcpdata
         set rec "4"
         set ::tcpdata ""
         log "mail bcc: $mailbcc"
       }
       TCP::respond "250 OK\r\n"
    }
    if { $::tcpdata == "DATA" } {
       TCP::respond "354 Start mail input; end with .\r\n"
       set ::tcpdata ""
       log "ready for DATA"
       }
       
    if { $::tcpdata == ".\r\n" } {
       set data $::tcpdata
       TCP::respond "250 OK - Data received"
       set ::tcpdata ""
       log "data = $data"
    }
    if { $::tcpdata == "QUIT" } {
       TCP::respond "221 fdpnmailgw2.thehouseofspam.com Service closing connection"
       set ::tcpdata ""
       }
    if { [TCP::payload] == "\r\n" } { 
    } else {
    set temp_payload [TCP::payload]
    set ::tcpdata $::tcpdata$temp_payload
    }
    TCP::release
    TCP::collect
    }
  • unRuleY_95363's avatar
    unRuleY_95363
    Historic F5 Account
    Well, that's a good start. My two main suggestions are:

    A) You shouldn't need to use a global variable for ::tcpdata. Just remove the :: from the variable and remove your RULE_INIT event. It should work just fine with a local variable.

    B) You should either fix your flow to return after matching a command or change to using elseif's or even better, use a switch command to structure all the different command matches. EG:
    switch -glob $tcpdata {
    "EHLO*" {
          TCP::respond "250-fdpnmailgw2.thehouseofspam.com Hello\r\n"
          ...
       }
    "MAIL FROM:*" {
          TCP::respond "250 OK\r\n"
          set mailfrom $tcpdata
          ...
       }
    "RCPT TO:*" {
          ...
       }
    "DATA" {
          ...
       }
    ".\r\n" {
          ...
       }
    "QUIT" {
          ...
       }
    default {
          append tcpdata [TCP::payload]
       }
    }

  • Richard__Harlan's avatar
    Richard__Harlan
    Historic F5 Account
    I was testing out my rule it work fine for small messages but when a large message comes it it casue the TMM system to core. I have open a case with support. My first guess is somehow I am stepping on memory from another run of the rule or something like that. Any help would be great. Thanks

    
    when CLIENT_ACCEPTED {
       use pool mailhost1v
       set clienttcpdata ""
       set rec "1"
       TCP::respond "220\r\n"
       serverside {TCP::respond "/r/n"} 
       TCP::collect 
    }
    when SERVER_CONNECTED {
       set servertcpdata ""
       serverside {TCP::respond "/n"}
       serverside {TCP::collect}
       log "Server Connected"
    }
    when SERVER_DATA {
       set servertcpdata [TCP::payload]
       TCP::payload replace 0 [TCP::payload length] ""
       clientside {TCP::respond $servertcpdata}
       clientside {TCP::collect}
       clientside {TCP::release}
       serverside {TCP::respond $clienttcpdata}
       log "$servertcpdata"
    }
    when CLIENT_DATA {
       set clienttcpdata [TCP::payload]
       serverside {TCP::respond $clienttcpdata}
       log "USER_RESPONSE"
       log "Client Data = $clienttcpdata"
       clientside {TCP::payload replace 0 [TCP::payload length] ""}
       TCP::release
       if {$clienttcpdata starts_with "HELO"} {
            set smtphelo $clienttcpdata
            log "Helo = $smtphelo"
       } elseif {$clienttcpdata starts_with "MAIL FROM:"} {
            set smtpmailfrom $clienttcpdata
            log "Mail From = $smtpmailfrom"
       } elseif {$clienttcpdata starts_with "RCPT TO:"} {
            if { $rec == "1" } {
               set smtpmailto $clienttcpdata
               set rec "2"
               log "hi"
               log "Mail To = $smtpmailto"
            } elseif { $rec == "2" } {
               set smtpmailcc $clienttcpdata
               set rec "3"
               log "CC = $smtpmailcc"
            } elseif { $rec == "3" } {
               set smtpmailbcc $clienttcpdata
               set rec "4"
               log "BCC = $smtpmailbcc"
            }
        } elseif {$clienttcpdata == "DATA"} {
            set smtpdata $clienttcpdata
            log "data = $smtpdata"
        } elseif { $clienttcpdata != "." || $clienttcpdata != "QUIT"} {
               append smtpdata $clienttcpdata
               if {$clienttcpdata contains "Sensitivity: Company-Confidential\r\n" } {
                   log "encript" }
               log "DATA = $smtpdata"
               log "Client data = $clienttcpdata"
        } elseif { $clienttcpdata == "." } {
               log "end of data" 
        } elseif { $clienttcpdata == "QUIT" } {
               log "QUIT"
        }
     
    }
  • unRuleY_95363's avatar
    unRuleY_95363
    Historic F5 Account
    I believe you have gotten your answer to this through support, but I thought it would be good to follow up here as well.

     

     

    The allocator that Tcl uses can only give out 4MB of contiguous data. This unintentionally puts a limit of 4MB on the maximum size a Tcl variable can hold. This is why the above rule would not work when the message was larger than 4MB. The above rule could probably be changed to not save the entire e-mail message in a variable which would avoid this limitation.