Learn F5 Technologies, Get Answers & Share Community Solutions Join DevCentral

Filter by:
  • Solution
  • Technology
Answers

SMTP STARTTLS iRule

 I need help with getting a STARTTLS iRule working for SMTP on our 1600's.  We are on version 10.2.1.  And to be clear it is working but there are a few tweaks I can't figure out.  The main issue is that when I telnet to the VIP I can't do an SMTP conversation.  It throws "530 Must issue a STARTTLS command first".  If I fire up Outlook Express I am able to use port 25/TLS just fine and the smtp.log with Outlook Express shows a normal conversation.  How to get the conversation to work?  Here is the code:

when CLIENT_ACCEPTED {
  SSL::disable
}

    when SERVER_CONNECTED {
    TCP::collect
}
  when CLIENT_DATA {
        set lcpayload [string tolower [TCP::payload]]
      if { $lcpayload starts_with "ehlo" } {
        TCP::respond "250-STARTTLS\r\n250 OK\r\n"
         TCP::payload replace 0 [TCP::payload length] ""
         TCP::release
        TCP::collect
     } elseif { $lcpayload starts_with "starttls" } {
         TCP::respond "220 Ready to start TLS\r\n"
           TCP::payload replace 0 [TCP::payload length] ""
            TCP::release
            SSL::enable
            } else {
          TCP::respond "530 Must issue a STARTTLS command first\r\n"
         TCP::payload replace 0 [TCP::payload length] ""
       TCP::release
     TCP::collect
   }
  }
     when SERVER_DATA {
   TCP::release
  clientside { TCP::collect }
}

0
Rate this Question

Answers to this Question

placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Below is a succesful STARTTLS transaction from the Outlook Express log. Thought this might provide some expert a clue. I am very weak on these iRules.  I added [SERVER] and [CLIENT]+ for easy identification. 

[SERVER] 220 mail.company.com Microsoft ESMTP MAIL Service ready at
[CLIENT]+EHLO
[SERVER] 250-STARTTLS
[SERVER] 250 OK
[CLIENT]+STARTTLS
[SERVER] 220 Ready to start TLS
[CLIENT]+EHLO
[SERVER] 250-mail.company.com Hello
[SERVER] 250-SIZE 36700160
[SERVER] 250-PIPELINING
[SERVER] 250-DSN
[SERVER] 250-ENHANCEDSTATUSCODES
[SERVER] 250-AUTH GSSAPI NTLM LOGIN
[SERVER] 250-8BITMIME
[SERVER] 250-BINARYMIME
[SERVER] 250 CHUNKING
[SERVER] AUTH NTLM
[SERVER] 334 NTLM supported
[CLIENT]+TXXXXXBXX4IXIogAAAAAAAAAAAAAAAAAAAAFAs4OAAAADw==
[SERVER] 334 TXXXXXBXX4IXIogAAAAAAAAAAAAAAAAAAFAs4OAAAADw==
[SERVER] 235 2.7.0 Authentication successful
[CLIENT]+MAIL FROM:
[SERVER] 250 2.1.0 Sender OK
[CLIENT]+RCPT TO:
[SERVER] 250 2.1.5 Recipient OK
[CLIENT]+DATA
[SERVER] 354 Start mail input; end with .
[CLIENT]+ .
[SERVER] 250 2.6.0 <3FDBD313125DE@company.com> [InternalId=322182] Queued mail for delivery
[CLIENT]+QUIT
[SERVER] 221 2.0.0 Service closing transmission channel
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
if I am not wrong telnet may send one or two character at a time, and that may not work with this iRule.
you may try using netcat and or openssl (s_client)

another alternative is using outlook express client and tcpdump/wireshark to see how it works after you put your tweak in the iRule
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Good point NAT. Telnet isn't a mail client and not behaving like one. The reality is the iRule works. TLS is working as supposed to but it was a matter of having an easy means of testing via telnet. I'll have to sniff the traffic. Thanks.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Did you get this working and is this essentially the iRule to offload the workload of TLS encryption to the F5 for SMTP->SMTPS over port 25
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Tried it and it seems like the logic is wrong. Because shouldn't this enable clear text pass through if starttls is never requested by client?
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Hi Damion,

As I remember, this iRule enforce the encryption. So it does not allow clear text.
if you want clear text pass through, the iRule may need to be modified.

you might try changing last "else" condition to be something like below.
I am not sure if it might help or not, I didn't test :)


    } else {
          TCP::respond "530 Must issue a STARTTLS command first\r\n"
         # comment below line (the below line swallow client data...)
         #TCP::payload replace 0 [TCP::payload length] ""
         TCP::release
         # release and never collect again
         #TCP::collect
   }


Thanks,
Nat
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
umm...
the "TCP::respond" should be commented out as well
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I'll test this out and let you know.. You are right Damion - clear text is not working.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Well it doesn't seem if even the original does the proper pass through of information let alone enabling ssl

telnet smtptestssl 25
Trying x.x.x.x...
Connected to smtptestssl.domain.com (x.x.x.x).
Escape character is '^]'.
220 pmx1.domain.com ESMTP Postfix
ehlo myhost-b.domain.com
250-STARTTLS
250 OK
starttls
220 Ready to start TLS
mail from: myemail@domain.com
503 5.5.1 Error: send HELO/EHLO first
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
This yielded a 503 5.5.2 error "Send Hello First"

} else {
#TCP::respond "530 Must issue a STARTTLS command first\r\n"
# comment below line (the below line swallow client data...)
#TCP::payload replace 0 [TCP::payload length] ""
TCP::release
# release and never collect again
#TCP::collect
}


I then tried to uncomment this #TCP::respond "530 Must issue a STARTTLS command first\r\n" with this:

TCP::respond "EHLO\r\n"

and got an error I forgot to write down.

SSL/TLS on port 25 does work with my original code but I get no telnet and as Damion pointed out no clear text.

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
What about something more along this:

when CLIENT_ACCEPTED {
SSL::disable
}

when SERVER_CONNECTED {
TCP::collect
}

when CLIENT_DATA {
set lcpayload [string tolower [TCP::payload]]
if { $lcpayload starts_with "ehlo" } {
TCP::release
TCP::collect
} elseif { $lcpayload starts_with "starttls" } {
TCP::respond "220 Ready to start TLS\r\n"
TCP::payload replace 0 [TCP::payload length] ""
TCP::release
SSL::enable
} else {
TCP::release
TCP::collect
}
}

when SERVER_DATA {
TCP::release
clientside { TCP::collect }
}
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

Clear text on port 25 worked with that code Damion but TLS/SSL failed, "250 CHUNKING" error. Says server does not support SSL.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I think we are getting closer. :)

I believe, we might change the ehlo block to one of either 2 options
if { $lcpayload starts_with "ehlo" } {
TCP::release
TCP::collect
}

1) use original method but include all features that server support example:


set response "250-mail.company.com Hello\r\n\" 
append response "250-PIPELINING\r\n"
append response "250-DNS\r\n"
append response "250-ENHANCEDSTATUSCODES\r\n"
append response "250-AUTH GSSAPI NTLM LOGIN\r\n"
append response "250-8BITMIME\r\n"
append response "250-BINARYMIME\r\n"
append response "250-CHUNKING\r\n"
append response "250 STARTTLS\r\n"
TCP::respond $response


2) do something like...

set ehlo 0
if { $lcpayload starts_with "ehlo" } {
TCP::release
serverside { TCP::collect}
set ehlo 1
TCP::collect
}

in when SERVER_DATA insert 250-STARTTLS\r\n to payload of server response

if { $ehlo == 1 } {
# insert 250-STARTTLS... at beginning of the payload
TCP::payload replace 0 0 "250-STARTTLS\r\n"
TCP::release
set ehlo 0
}

Nat
0
Comments on this Answer
Comment made 19-Jun-2018 by rgordon 01 92

Rule /Common/Exchange_2016_STARTTLS : ehlo ssl disabled Rule /Common/Exchange_2016_STARTTLS : server_connected collect Rule /Common/Exchange_2016_STARTTLS : server_data before STARTTLS Rule /Common/Exchange_2016_STARTTLS : TCP release clientside collect Rule /Common/Exchange_2016_STARTTLS : CLIENT_DATA set ehlo 1 Rule /Common/Exchange_2016_STARTTLS : server_data before STARTTLS Rule /Common/Exchange_2016_STARTTLS : TCP release clientside collect Rule /Common/Exchange_2016_STARTTLS : CLIENT_DATA 220 Ready to start TLS

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
But the server will respond with the default responses, I think the issue was not specifying the serverside or client side in the opposite respective when clauses. What about the following:

when CLIENT_ACCEPTED {
SSL::disable
}

when SERVER_CONNECTED {
TCP::collect
}

when CLIENT_DATA {
set lcpayload [string tolower [TCP::payload]]
if { $lcpayload starts_with "ehlo" } {
TCP::release
serverside { TCP::collect }
} elseif { $lcpayload starts_with "starttls" } {
TCP::respond "220 Ready to start TLS\r\n"
TCP::payload replace 0 [TCP::payload length] ""
TCP::release
SSL::enable
serverside { TCP::collect }
} else {
TCP::release
serverside { TCP::collect }
}
}

when SERVER_DATA {
TCP::release
clientside { TCP::collect }
}
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Damion,

Tried your code above and still got the "250 CHUNKING" error. Clear text worked. I guess the logic would go something like this:


Nat - I tried to work your suggestion into my irule and had no luck, too lousy at this. :(
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
try this. I did a quick test with thunderbird...


when CLIENT_ACCEPTED {
    set ehlo 0
    SSL::disable
}
when SERVER_CONNECTED {
    TCP::collect
}
when CLIENT_DATA {
    set lcpayload [string tolower [TCP::payload]]
    if { $lcpayload starts_with "ehlo" } {
        set ehlo 1
        serverside { TCP::collect }
        TCP::release
        TCP::collect
    } elseif { $lcpayload starts_with "starttls" } {
        TCP::respond "220 Ready to start TLS\r\n"
        TCP::payload replace 0 [TCP::payload length] ""
        TCP::release
        SSL::enable
    } else {
        TCP::release
    }
}
when SERVER_DATA {
    if { $ehlo == 1 and not([string tolower [TCP::payload]] contains "starttls") } {
        TCP::payload replace 0 0 "250-STARTTLS\r\n"
    }
    TCP::release
    clientside { TCP::collect }
}
0
Comments on this Answer
Comment made 19-Jun-2018 by rgordon 01 92

Hi Natty,

I know this is an old post but it's worth a shot. We are having this issue related to bug - K16312: The BIG-IP system may fail to appropriately respond to SMTPS commands after the STARTTLS handshake. we are on version 11.5.1 I figured I could write an irule to fix this problem. I've used the irule above but it's still not working. I added logging to see where the issue was. The problem is in the server_data-

when SERVER_DATA { log local0. "server_data before STARTTLS" if { $ehlo == 1 and not([string tolower [TCP::payload]] contains "starttls") } { TCP::payload replace 0 0 "250-STARTTLS\r\n" log local0. "server_data STARTTLS" } TCP::release clientside { TCP::collect } log local0. "TCP release clientside collect" }

I see in the logs "server_data before STARTTLS" but never gets to "server_data STARTTLS" so clearly it's an issue here? if { $ehlo == 1 and not([string tolower [TCP::payload]] contains "starttls")

but I know ehlo==1 b/c I added logging for that too so it has to do something with the payload containing starttls right? so just for the sake of testing, I removed the not in front b/c it should definitely match and still not working.

any ideas why this wouldn't work? here's the full irule with my logging added

when CLIENT_ACCEPTED { set ehlo 0 SSL::disable log local0. "ehlo ssl disabled"
} when SERVER_CONNECTED { log local0. "server_connected collect" TCP::collect } when CLIENT_DATA { set lcpayload [string tolower [TCP::payload]] if { $lcpayload starts_with "ehlo" } { set ehlo 1 serverside { TCP::collect } TCP::release TCP::collect log local0. "CLIENT_DATA set ehlo 1" } elseif { $lcpayload starts_with "starttls" } { TCP::respond "220 Ready to start TLS\r\n" TCP::payload replace 0 [TCP::payload length] "" TCP::release SSL::enable log local0. "CLIENT_DATA 220 Ready to start TLS\r\n" } else { TCP::release log local0. "CLIENT_DATA else release" } } when SERVER_DATA { log local0. "server_data before STARTTLS" log local0. [TCP::payload] if { $ehlo == 1 and not([string tolower [TCP::payload]] contains "starttls") } { TCP::payload replace 0 0 "250-STARTTLS\r\n" log local0. "server_data STARTTLS" } TCP::release clientside { TCP::collect } log local0. "TCP release clientside collect" }

0
Comment made 19-Jun-2018 by rgordon 01 92

Rule /Common/Exchange_2016_STARTTLS : ehlo ssl disabled Rule /Common/Exchange_2016_STARTTLS : server_connected collect Rule /Common/Exchange_2016_STARTTLS : server_data before STARTTLS Rule /Common/Exchange_2016_STARTTLS : TCP release clientside collect Rule /Common/Exchange_2016_STARTTLS : CLIENT_DATA set ehlo 1 Rule /Common/Exchange_2016_STARTTLS : server_data before STARTTLS Rule /Common/Exchange_2016_STARTTLS : TCP release clientside collect Rule /Common/Exchange_2016_STARTTLS : CLIENT_DATA 220 Ready to start TLS

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Well I'll be damned. You did it Nat. Both SSL and clear text work. And I can telnet in for a lovely SMTP conversation. You guys are the best. Your code worked as it stands, no changes. This should be put in the codeshare, would no doubt be useful to others.

Thanks again!

-kraig
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Ask and ye shall receive. CodeShare entry created:

http://devcentral.f5.com/wiki/default.aspx/iRules/SMTPStartTLS.html

Awesome work Nat, I want to be like you when I grow up. ;)

#Colin
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

Thanks Colin. If I ever grow up.. agreed.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Sure thing, thanks for the awesome feedback to help get this one wrapped up. I love forum threads like this.

#Colin
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
One question: if we get activate the Message Security Module, will we still be able to use this iRule?
http://www.f5.com/solutions/security/anti-spam/

Thank you kraigk for the original question and Nat for working to solve it.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I don't see why MSM would interfere with this. MSM is dealing with the rating of inbound senders, this is dealing with people connecting directly to send/retrieve mail. The two should play nicely together, but I suppose I'd have to test them to be sure.

#Colin
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
found neat command to test ssl connection using openssl

openssl s_client -connect smtptestssl:25 -starttls smtp
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Are any of you using MSM? Just curious how well it works. I have borrowed code that we use to maintain datagroup lists of rate controlled and blocked IP's. Works great with all our virtual servers (SMTP, POP, IMAP, OWA). Better done in a firewall but a different group manages those so it isn't easy. FW -> LB -> spam FW -> to mail servers. Anywho I ssuspect MSM works well but at a price?
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I have the same question as kraigk about MSM.

Another question, I just wanted to verify that under the Profile -> SSL -> Client -> Properties that "Non-SSL Connections" is Enabled?

Are there any other smtp specific settings under the Virtual Server or SSL profile that would help this iRule?

Thanks in advance
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Damion - Non-SSL Connections" is not enabled on my profile. The only other items I have defined are pretty basic - Parent profile = clientssl (and it is default), Certificate, Key, Chain and Trusted Certificate Authority. That's it.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Damion - Non-SSL Connections" is not enabled on my profile. The only other items I have defined are pretty basic - Parent profile = clientssl (and it is default), Certificate, Key, Chain and Trusted Certificate Authority. That's it.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

Thanks to all in this thread I got SMTP TLS offloaded to our BIGIP! Works great.

I was frustrated for a bit though, the "stock" iRule in the codeshare did not work for me, best I could tell is that it would never match the payload on "starttls". Forgive me if my terminology is off, I am just a Windows guy ;-)

I had this issue testing from telnet, which I figured is the way that telnet submits every character which would cause the match not to happen on the "s" and the rule would then release TCP. I was merely testing to try a "ehlo" and then "starttls" to see if I got a "not implemented" (i.e. it didn't work) or a "220 Ready to start TLS" response which meant it was working.

I then tried using "checktls.com"receiver test to rule out the telnet issues, but that did not work either, which led me to believe there may be some SMTP sending servers out there that will not work with the stock iRule.

I ended up rearchitecting as such:

				
Good lord if someone can tell me how to post the code without the editor removing the spaces and carriage returns? I
tried pasting from iRule editor, notepad, wordpad...I even watched the video but he didn't paste in any code he just typed it in...
If you want to see the iRule just let me know how to do it and I can post it...

Anyways I had also written a rule to mask SMTP server names, so that one SMTP server can be multiple greeting names through the BIGIP, and I had the same issue, I needed a "if { not ( $lcpayload contains "\r\n" ) } { return }" in order to get the BIGIP to "hold off" until there is a carriage return to do the string matching. At least that is how I think it is working. It wouldn't work without in telnet or testing real SMTP servers with it.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Well for crying out loud it looks like the "quick reply" works, so here it is:
when CLIENT_ACCEPTED {
    set ehlo 0
    SSL::disable
}
when SERVER_CONNECTED {
    TCP::collect
}
when CLIENT_DATA {
    set lcpayload [string tolower [TCP::payload]]
	if { not ( $lcpayload contains "\r\n" ) } {
    return
	}
    if { $lcpayload starts_with "ehlo" } {
        set ehlo 1
        serverside { TCP::collect }
        TCP::release
        TCP::collect
   } 
	if { $lcpayload starts_with "starttls" } {
        TCP::respond "220 Ready to start TLS\r\n"
        TCP::payload replace 0 [TCP::payload length] ""
        TCP::release
        SSL::enable
    } else {
        TCP::release
    }
}
when SERVER_DATA {
    if { $ehlo == 1 and not([string tolower [TCP::payload]] contains "starttls") } {
        TCP::payload replace 0 0 "250-STARTTLS\r\n"
    }
    TCP::release
    clientside { TCP::collect }
} 
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Sorry for the troubles with the editor...glad you got it working finally. And thanks for the submission!

#Colin
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
No worries Colin, just glad I am getting good enough with the iRules I might actually be able to help someone else out!
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I'll say ;) Keep up the good work.

#Colin
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Hey all, revisiting this one. I got real close to production with this rule, until I found that some hosts cannot send (namely messagelabs cannot not negotiate TLS with this iRule), they are spitting back an error that TLS was not supported.

In my own testing, using various POP clients or checkTLS.com, everything works, SSL is negotiated properly.

So then I tried out OpenSSL as suggested here, and I seem to be getting the dreaded "250 Chunking" error the OP got earlier. SSL appears to be negotiated, but every subsequent command in the session returns an "unrecognized command" from the SMTP server in the pool. I can see in the SMTP logs what the server sees, it is getting unprintable characters added on to the end of the commands I input. So say I connect open ssl, I negotiate TLS, and I type "ehlo" the server log will show that I connected and typed ehlo(Open SSL does this), TLS nego is then done unknown to the SMTP server, then I type "ehlo" but the SMTP server sees "ehlo█" in the log .

I am not quite sure what is going on here. I am guessing the "250 chunking" is a bit of a red herring, as in the problem is not the "chunking" but it is telling that that is the last line of the server response to the client "ehlo" greeting, the one the iRule inserts the "250 Starttls" into. So I am thinking some sort of collection buffer issue with the BIGIP, due to the way the rule is formatted. Why that makes every command after TLS negotiation corrupt is beyond me! It is getting way over my head to udnerstand what is going on.

I also tried the exact rule from this thread and the repository, as it was when it "worked" for the OP here, but it still has this "250 Chunking" issue.

There was a discussion of fixing this issue from the earlier rules, but neither Nat nor Damion explained why this probem happened? If I knew why it happened with the earlier versions of this iRule I might have a better idea of what is wrong with this one.

I am about give up on TLS offloading with the BIGIP, too bad because it would have save much time and effort being able to run multiple mailhosts with TLS off the same server pool.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Hi Asharicz,

> There was a discussion of fixing this issue from the earlier rules, but neither Nat nor Damion explained why this probem happened?
actually, I never see that error myself. The issue I remember was we tried to support both client that support and the one that doesn't

you see the error on server? may I ask what server are you using?

can you post the packet trace? (please make sure it does not contain any sensitive information)

Nat
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

Nat,

Thanks for the reply. I could get the packet trace, but thinking about it, I am not sure which side I should trace? The outside interface is just recieving standard traffic and TLS negotiation, though maybe I would want to compare what a "working" client like checktls or Outlook does compared to non-working OpenSSL client.

If I capture on the inside, I would just capture the odd traffic that gets sent to the SMTP server, which is already in my SMTP logs, which doesn't really help either.

If you look back, the OP was talking about a "250 chunking" return when trying to get TLS negotiation working, do you see those posts in this thread? I am wondering what the actual problem with the iRule was there, because I seem to be having it with some SMTP clients even with the latest version posted here.

It seems the problem is inside the logic of the iRule, I'm not sure how to get to what the problem is there, other than logically working through the iRule in my mind, which hasn't worked out well so far ;-)

The SMTP server is an Exchange 2007 HUB server. I could easily put another type of SMTP server behind that pool to test if it is an interaction specific to the Exchange 2007 server.

0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
We used the iRule in this thread and it didn't work for us. We use Postini, and their custom SMTP MTA software was not seeing the STARTTLS advertisement, because it was being placed above the banner from the exchange server instead of below it with the other Extended SMTP options (see screenshot of packet capture below). To place it at the bottom, but before the "250 ok/r/n" I updated the rule to that shown at the bootom, which got it working for us.



when CLIENT_ACCEPTED {
    set ehlo 0
    SSL::disable
}
when SERVER_CONNECTED {
    TCP::collect
}
when CLIENT_DATA {
    set lcpayload [string tolower [TCP::payload]]
    if { $lcpayload starts_with "ehlo" } {
        set ehlo 1
        TCP::release
        serverside { TCP::collect }
    } elseif { $lcpayload starts_with "starttls" } {
        TCP::respond "220 Ready to start TLS\r\n"
        TCP::payload replace 0 [TCP::payload length] ""
        TCP::release
        SSL::enable
    } else {
        TCP::release
    }
}
when SERVER_DATA {
    set lspayload [string tolower [TCP::payload]]
    if { $ehlo == 1 and not ( $lspayload contains "starttls" ) } {
        if { $lspayload contains "250 ok\r\n" } {
            TCP::payload replace [expr [TCP::payload length] - 8] 0 "250-STARTTLS\r\n"
        }
    }
    TCP::release
    clientside { TCP::collect }
}
0