Tim
Tim

Reputation: 2911

Connecting to SSL using POP3

I have an application that scans an email account for returned mail. It uses POP3, and works successfully on several clients' systems. However, with one client, when we try to connect, we are getting a SocketException - No such host is known.

My first thought was that either the address or port were not accessible, but they came back saying that it is an SSL port, and I figured that my code may not be able to handle SSL. However, the error is happening when I call tcpClient = new TcpClient(Host, Port);, so I am back to my previous hypothesis. Is there a special way TcpClient needs to connect to an SSL port?

My second question is whether there is an easy way to convert code to use SSL without basically creating a regular POP3 connection class and an SSL POP3 connection class? I believe I need to use SslStream instead of StreamReader, which means I would have to modify any code that accesses the POP3 server, since SslStream does not have a ReadLine() method.

I've added my initial connection code below (or the important bits).

try
{
    tcpClient = new TcpClient(Host, Port);
}
catch (SocketException e)
{
    logger.Log(...);
    throw (e);
}
String response = "";

try
{
    streamReader = new StreamReader(tcpClient.GetStream());

    //  Log in to the account
    response = streamReader.ReadLine();
    if (response.StartsWith("+OK"))
    {
        response = SendReceive("USER ", UserName.Trim() + "@" + Domain.Trim());
        if (response.StartsWith("+OK"))
        {
            response = SendReceive("PASS ", Password);
        }
    }

    if (response.StartsWith("+OK"))
        result = true;
}
catch (Exception e)
{
    result = false;
}

The SendReceive method is pretty simple:

private String SendReceive(String command, String parameter)
{
    String result = null;
    try
    {
        String myCommand = command.ToUpper().Trim() + " " + parameter.Trim() + Environment.NewLine;
        byte[] data = System.Text.Encoding.ASCII.GetBytes(myCommand.ToCharArray());
        tcpClient.GetStream().Write(data, 0, data.Length);
        result = streamReader.ReadLine();
    }
    catch { }   //  Not logged in...
    return result;
}

It seems to be mainly the ReadLine() method that does not work, but reading up on that suggests that it is difficult to read a line with a stream since you don't know whether it is finished sending or not. Is this the case, or do I just need to write a quick method to read until I hit a \r or \n?

Upvotes: 0

Views: 1600

Answers (1)

jstedfast
jstedfast

Reputation: 38528

To answer your first question, there is no different way to connect to an SSL port, it works exactly the same way.

As far as your second question, a StreamReader wraps a System.IO.Stream and an SslStream is just an implementation of System.IO.Stream, so you can create a StreamReader around it just fine.

What you will need to do is something like this:

var stream = tcpClient.GetStream ();

if (useSsl) {
    var ssl = new SslStream (stream);
    ssl.AuthenticateAsClient (Host, null, SslProtocols.Tls12, true);
    stream = ssl;
}

streamReader = new StreamReader (stream);

Of course, you'll need to fix your SendReceive() method to not use tcpClient.GetStream() again, because you'll need to use the SslStream and not the NetworkStream that tcpClient.GetStream() will return.

The easiest way to do that is probably to just pass the stream variable to SendReceive(), or, I suppose, add a Stream member to your class like you presumably did for streamReader and tcpClient.

Of course, a better solution would be to use a library for this such as my MailKit library which handles all of this for you in a much more robust way than this code does :)

Upvotes: 2

Related Questions