Forum Discussion

Josh_Hildebran1's avatar
Josh_Hildebran1
Icon for Nimbostratus rankNimbostratus
Jul 11, 2006

SMTP EHLO response

I'd like to make an iRule that looks for a "EHLO " command and simply responds w/ a particular error message.

I've tried to piece together something, but I've come up short, apparently. Can anyone help?

rule FixUP-SMTP {
   when CLIENT_ACCEPTED {
     TCP::collect
     TCP::release
   }
   when CLIENT_DATA {
    set sdata [TCP::payload]
    if { $sdata starts_with "EHLO " } {
        TCP::respond "500 5.3.3 Unrecognized command\r\n"
        reject
    } else {
      TCP::release
      TCP::collect
    }
  }
}

8 Replies

  • Hmm.. Thanks for the pointers. Unfortunately, it's still not doing what I need.

    The goal is to hijack the TCP stream between a webserver that is behind the F5 (internal) as it communicates to an SMTP server that is external to the F5's.

    The SMTP server is reached through a VIP on the F5:

    virtual smtptest_from_internal {
       destination 172.20.140.26:smtp
       ip protocol tcp
       pool smtp_testpool
       rule FixUP-SMTP
       vlans internal enable
    }

    which uses the rule of

    
    rule FixUP-SMTP {
      when CLIENT_ACCEPTED {
        TCP::collect}
      when CLIENT_DATA {
        if { [TCP::payload] contains "EHLO " } {
          TCP::respond "500 5.3.3 Unrecognized command\r\n"
          reject
        }
        TCP::release
      }
    }

    But when I connect to 172.20.140.26 on port 25, and issue the EHLO command this is what I get:

    220 *********************************
    EHLO EXDEMO
    502 Error: command not implemented

    Any other ideas?
  • Colin_Walker_12's avatar
    Colin_Walker_12
    Historic F5 Account
    Have you tried moving the status code outside of the quotes on the TCP::respond line?

     

     

    I.E.

     

     

    
    when CLIENT_ACCEPTED {
      TCP::collect
    }
    when CLIENT_DATA {
      if { [TCP::payload] contains "EHLO " } {
        TCP::respond 500 "5.3.3 Unrecognized command\r\n"
        reject
      }
      TCP::release
    }

     

     

    Colin
  • rapmaster_c_127's avatar
    rapmaster_c_127
    Historic F5 Account
    Try this:

    
    rule FixUP-SMTP {
       when SERVER_CONNECTED {
            peer { TCP::collect }
        }
        when CLIENT_DATA {
            if { [TCP::payload] starts_with "EHLO" } {
                TCP::respond "500 5.3.3 Unrecognized command\r\n"
                set len [TCP::payload length]
                TCP::payload replace 0 $len ""
                TCP::release
            } else {
               TCP::release
               TCP::collect
            }
        }
    }

    You might want to disable the rule after it has fired the first time, or after you see the DATA method in order to prevent parsing payload. Up to you, but you get the general idea.
  • rapmaster_c_127's avatar
    rapmaster_c_127
    Historic F5 Account
    You also would get generally better performance (at the cost of missing an EHLO if it's not the 1st command from the client) by doing this:

    
    rule FixUP-SMTP {
       when SERVER_CONNECTED {
            peer { TCP::collect 4 }
        }
        when CLIENT_DATA {
            if { [TCP::payload] starts_with "EHLO" } {
                TCP::respond "500 5.3.3 Unrecognized command\r\n"
                TCP::payload replace 0 [TCP::payload length] ""
            }
            TCP::release
        }
    }

    This is probably the common case you're trying to resolve.

    Comments:

    1. We use SERVER_CONNECTED instead of CLIENT_ACCEPTED because SMTP is a banner protocol. Many clients will not send their first HELO/EHLO until they see the banner from the server. So we wait for the server connection prior to starting our client side collection.

    2. Since the event is on the server side, we need to collect on the client side. Thus the use of the peer command.

    3. We want to make sure we have at least 4 bytes of payload in our collect.

    4. starts_with is more efficient than contains.

    5. reject/discard/etc result in operations on the connection. You don't want this; you simply want the payload in that particular segment to be ignored. So we replace it with nothing and continue.

    Hope this helps.
  • rapmaster_c,

     

     

    That sure looks like it should work, but it isn't doing anything differently for some reason. I'm beginning to wonder if I have something misconfigured now.

     

     

    I don't suppose you've actually tested that iRule, have you?
  • rapmaster_c_127's avatar
    rapmaster_c_127
    Historic F5 Account
    Yes, I tested it. It's working here.

    Here is the virtual server:

    
    virtual vmail {
       destination 172.27.138.7:smtp
       snat automap
       ip protocol tcp
       pool mail
       rule muppet
    }

    Here is the rule:

    
    rule muppet {
       when SERVER_CONNECTED {
            peer { TCP::collect 4 }
        }
        when CLIENT_DATA {
            if { [TCP::payload] starts_with "EHLO" } {
                TCP::respond "500 5.3.3 Unrecognized command\r\n"
                TCP::payload replace 0 [TCP::payload length] ""
            }
            TCP::release
        }
    }

    And here is the dry run:

    
    Connected to acheron.pdsea.f5net.com (172.27.138.7).
    Escape character is '^]'.
    220 mail.internal Microsoft ESMTP MAIL Service, Version: 6.0.3790.1830 ready at  Thu, 13 Jul 2006 11:29:26 -0700
    EHLO foobar
    500 5.3.3 Unrecognized command
    HELO foobar
    250 mail.internal Hello [172.27.138.6]
    ^]c
                                                                                         
    Connection closed.

    Have you associated the rule with the right virtual server?
  • OMG! If I would use the VIP's IP to test with, it might help! hah.. It works now.

     

     

    It does reply w/ the response as soon as I type EHLO, even before I hit space and the hostname and hit enter.. sort of not what I was looking for, but it actually works w/ .Net SmtpMail function properly, so that's really all I care about.

     

     

    Thanks a bunch!
  • rapmaster_c_127's avatar
    rapmaster_c_127
    Historic F5 Account
    Not that it matters all that much since it's working for your application, but I think the reason it's responding straight away to your "EHLO" command from telnet is because the Microsoft Windows telnet client doesn't enter linemode by default, violating the following section from RFC854 page 4:

     

     

     

    TRANSMISSION OF DATA

     

     

    Although a TELNET connection through the network is intrinsically

     

    full duplex, the NVT is to be viewed as a half-duplex device

     

    operating in a line-buffered mode. That is, unless and until

     

     

    RFC 854 May 1983

     

     

    options are negotiated to the contrary, the following default

     

    conditions pertain to the transmission of data over the TELNET

     

    connection:

     

     

    1) Insofar as the availability of local buffer space permits,

     

    data should be accumulated in the host where it is generated

     

    until a complete line of data is ready for transmission,

     

     

     

     

    Looking at traces from a Windows telnet client, it's clear that the MS client sends "E", "H", "L", and "O" in their own individual segments despite not having negotiated non-linemode operation, triggering the rule right after the "O" is transmitted.

     

     

    If you tested from a Unix derivative or a Linux box whose telnet client entered linemode by default unless it was negotiated to the contrary it should work fine.

     

     

    As I mentioned, this is probably largely academic at this point though.