A few months ago I built a Windows screensaver using WPF and an RSS feed. I blogged about it over at and thought I should mention it here…

While poking at a recent project with a large stick my script resource handler began coughing and gave me a nasty timeout error. After a little prodding of google I came up with this fix. It works, assuming you don't actually have something wrong with your code that is timing out.

In your master page or page where your script manager lives add the AsyncPostBackTimeout value. The default is 90 seconds in case you're wondering.

<asp:ScriptManager ID="ScriptManager1" runat="server" AsyncPostBackTimeout ="360000"></asp:ScriptManager>

In your web.config file, add this to your system.web section.

<httpRuntime executionTimeout="360000"/>

That's it. Your pages shouldn't timeout now unless you've broken something else. :)

Wayne posted an interesting little SMTP snippet recently and I had been poking around at that, so I thought I'd share this... It is an adaptation of several articles scattered around through the years, his code, and several other things I wrote a while ago. Enjoy...

namespace SMTP
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Net.Mail;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading;

    /// <summary>
    /// provides methods to send email via smtp direct to mail server
    /// </summary>
    public class SmtpDirect
    {
        /// <summary>
        /// Determines whether the specified input email is email.
        /// </summary>
        /// <param name="inputEmail">The input email.</param>
        /// <param name="fromEmail">From email.</param>
        /// <param name="tryLookup">if set to <c>true</c> [try lookup].</param>
        /// <param name="tryConnect">if set to <c>true</c> [try connect].</param>
        /// <returns>
        ///     <c>true</c> if the specified input email is email; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsEmail(string inputEmail, string fromEmail, bool tryLookup, bool tryConnect)
        {
            // WLF: Check for null or empty input

            if (string.IsNullOrEmpty(inputEmail))
            {
                return false;
            }

            // WLF: RegEx check

            const string strRegex = @"^(([a-zA-Z0-9_\-\+/\^]+)([\.]?)([a-zA-Z0-9_\-\+/\^]+))+@((\[[0-9]{1,3}" +
                                    @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                                    @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
            var re = new Regex(strRegex);

            if (!re.IsMatch(inputEmail))
            {
                return false;
            }

            // WWB: Check To Make Sure The Email Address Parses

            MailAddress mailAddress;
            try
            {
                mailAddress = new MailAddress(inputEmail);
            }
            catch
            {
                return false;
            }

            if (tryLookup)
            {
                // WWB: Check To Make Sure There Is a SMTP Server To Recieve The Email Address

                if (DnsMx.GetMXRecords(mailAddress.Host).Length == 0)
                {
                    return false;
                }
            }

            if (tryConnect)
            {
                // WLF: Try to connect to the SMTP server

                try
                {
                    var mxRecords = DnsMx.GetMXRecords(mailAddress.Host);
                    if (mxRecords.Length == 0)
                    {
                        return false;
                    }

                    var ipHostEntry = Dns.GetHostEntry(mxRecords[0]);
                    var endPoint = new IPEndPoint(ipHostEntry.AddressList[0], 25);
                    var s = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    s.Connect(endPoint);

                    // WLF: Attempting to connect

                    if (!CheckResponse(s, SmtpResponse.ConnectSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    // WLF: HELO server

                    Senddata(s, string.Format("HELO {0}\r\n", Dns.GetHostName()));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    // WLF: Say who it is from 

                    Senddata(s, string.Format("MAIL From: {0}\r\n", fromEmail));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    // WLF: Try to send 

                    Senddata(s, string.Format("RCPT TO: {0}\r\n", inputEmail));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }
                }
                catch
                {
                    return false;
                }
            }

            // WWB: Success

            return true;
        }

        /// <summary>
        /// Sends the mail directly to the receiver's server.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="errors">The errors.</param>
        /// <returns></returns>
        public static bool SendDirect(MailMessage message, out List<string> errors)
        {
            errors = new List<string>();
            var deliveryAddresses = new List<MailAddress>();

            // WLF: Add To's
            foreach (var address in message.To)
            {
                deliveryAddresses.Add(address);
            }

            // WLF: Add CC's
            foreach (var address in message.CC)
            {
                deliveryAddresses.Add(address);
            }

            foreach (var to in deliveryAddresses)
            {
                var mxRecords = DnsMx.GetMXRecords(to.Host);
                if (mxRecords.Length == 0)
                {
                    errors.Add(to.Host);
                }
                else
                {
                    var ipHostEntry = Dns.GetHostEntry(mxRecords[0]);
                    var endPoint = new IPEndPoint(ipHostEntry.AddressList[0], 25);
                    var s = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    s.Connect(endPoint);

                    if (!CheckResponse(s, SmtpResponse.ConnectSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, string.Format("HELO {0}\r\n", Dns.GetHostName()));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, string.Format("MAIL From: {0}\r\n", message.From));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, string.Format("RCPT TO: {0}\r\n", to.Address));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    #region Build message body

                    var header = GetHeader(message);

                    var msgBody = message.Body;
                    if (!msgBody.EndsWith("\r\n"))
                        msgBody += "\r\n";

                    #region Handle attachments

                    if (message.Attachments.Count > 0)
                    {
                        header.Append("MIME-Version: 1.0\r\n");
                        header.Append("Content-Type: multipart/mixed; boundary=unique-boundary-1\r\n");
                        header.Append("\r\n");
                        header.Append("This is a multi-part message in MIME format.\r\n");
                        var sb = new StringBuilder();
                        sb.Append("--unique-boundary-1\r\n");
                        sb.Append("Content-Type: text/plain\r\n");
                        sb.Append("Content-Transfer-Encoding: 7Bit\r\n");
                        sb.Append("\r\n");
                        sb.Append(msgBody + "\r\n");
                        sb.Append("\r\n");

                        foreach (var a in message.Attachments)
                        {
                            if (a == null) continue;

                            var f = new FileInfo(a.Name);
                            sb.Append("--unique-boundary-1\r\n");
                            sb.Append("Content-Type: application/octet-stream; file=" + f.Name + "\r\n");
                            sb.Append("Content-Transfer-Encoding: base64\r\n");
                            sb.Append("Content-Disposition: attachment; filename=" + f.Name + "\r\n");
                            sb.Append("\r\n");
                            var fs = f.OpenRead();
                            var binaryData = new Byte[fs.Length];
                            long bytesRead = fs.Read(binaryData, 0, (int) fs.Length);
                            Trace.WriteLine(string.Format("Bytes read: {0}", bytesRead));
                            fs.Close();
                            var base64String = Convert.ToBase64String(binaryData, 0, binaryData.Length);

                            for (var i = 0; i < base64String.Length;)
                            {
                                var nextchunk = 100;
                                if (base64String.Length - (i + nextchunk) < 0)
                                    nextchunk = base64String.Length - i;
                                sb.Append(base64String.Substring(i, nextchunk));
                                sb.Append("\r\n");
                                i += nextchunk;
                            }
                            sb.Append("\r\n");
                        }

                        msgBody = sb.ToString();
                    }

                    #endregion

                    #endregion

                    #region Send message body

                    Senddata(s, ("DATA\r\n"));
                    if (!CheckResponse(s, SmtpResponse.DataSuccess))
                    {
                        s.Close();
                        return false;
                    }
                    header.Append("\r\n");
                    header.Append(msgBody);
                    header.Append(".\r\n");
                    header.Append("\r\n");
                    header.Append("\r\n");
                    Senddata(s, header.ToString());
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, "QUIT\r\n");
                    CheckResponse(s, SmtpResponse.QuitSuccess);
                    s.Close();

                    #endregion
                }
            }

            return errors.Count == 0;
        }

        /// <summary>
        /// Sends the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="outboundSmtpServer">The outbound SMTP server.</param>
        /// <returns></returns>
        public static bool Send(MailMessage message, string outboundSmtpServer)
        {
            var ipHostEntry = Dns.GetHostEntry(outboundSmtpServer);
            var endPoint = new IPEndPoint(ipHostEntry.AddressList[0], 25);
            var s = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            s.Connect(endPoint);

            if (!CheckResponse(s, SmtpResponse.ConnectSuccess))
            {
                s.Close();
                return false;
            }

            Senddata(s, string.Format("HELO {0}\r\n", Dns.GetHostName()));
            if (!CheckResponse(s, SmtpResponse.GenericSuccess))
            {
                s.Close();
                return false;
            }

            Senddata(s, string.Format("MAIL From: {0}\r\n", message.From));
            if (!CheckResponse(s, SmtpResponse.GenericSuccess))
            {
                s.Close();
                return false;
            }

            foreach (var to in message.To)
            {
                Senddata(s, string.Format("RCPT TO: {0}\r\n", to.Address));
                if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                {
                    s.Close();
                    return false;
                }
            }

            foreach (var cc in message.CC)
            {
                Senddata(s, string.Format("RCPT TO: {0}\r\n", cc.Address));
                if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                {
                    s.Close();
                    return false;
                }
            }

            var header = GetHeader(message);

            var msgBody = message.Body;
            if (!msgBody.EndsWith("\r\n"))
                msgBody += "\r\n";

            #region Handle attachments

            if (message.Attachments.Count > 0)
            {
                header.Append("MIME-Version: 1.0\r\n");
                header.Append("Content-Type: multipart/mixed; boundary=unique-boundary-1\r\n");
                header.Append("\r\n");
                header.Append("This is a multi-part message in MIME format.\r\n");
                var sb = new StringBuilder();
                sb.Append("--unique-boundary-1\r\n");
                sb.Append("Content-Type: text/plain\r\n");
                sb.Append("Content-Transfer-Encoding: 7Bit\r\n");
                sb.Append("\r\n");
                sb.Append(msgBody + "\r\n");
                sb.Append("\r\n");

                foreach (var a in message.Attachments)
                {
                    if (a == null) continue;

                    var f = new FileInfo(a.Name);
                    sb.Append("--unique-boundary-1\r\n");
                    sb.Append("Content-Type: application/octet-stream; file=" + f.Name + "\r\n");
                    sb.Append("Content-Transfer-Encoding: base64\r\n");
                    sb.Append("Content-Disposition: attachment; filename=" + f.Name + "\r\n");
                    sb.Append("\r\n");
                    var fs = f.OpenRead();
                    var binaryData = new Byte[fs.Length];
                    long bytesRead = fs.Read(binaryData, 0, (int) fs.Length);
                    Trace.WriteLine(string.Format("Bytes read: {0}", bytesRead));
                    fs.Close();
                    var base64String = Convert.ToBase64String(binaryData, 0, binaryData.Length);

                    for (var i = 0; i < base64String.Length;)
                    {
                        var nextchunk = 100;
                        if (base64String.Length - (i + nextchunk) < 0)
                            nextchunk = base64String.Length - i;
                        sb.Append(base64String.Substring(i, nextchunk));
                        sb.Append("\r\n");
                        i += nextchunk;
                    }
                    sb.Append("\r\n");
                }

                msgBody = sb.ToString();
            }

            #endregion

            Senddata(s, ("DATA\r\n"));
            if (!CheckResponse(s, SmtpResponse.DataSuccess))
            {
                s.Close();
                return false;
            }
            header.Append("\r\n");
            header.Append(msgBody);
            header.Append(".\r\n");
            header.Append("\r\n");
            header.Append("\r\n");
            Senddata(s, header.ToString());
            if (!CheckResponse(s, SmtpResponse.GenericSuccess))
            {
                s.Close();
                return false;
            }

            Senddata(s, "QUIT\r\n");
            CheckResponse(s, SmtpResponse.QuitSuccess);
            s.Close();
            return true;
        }

        /// <summary>
        /// Gets the header.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns></returns>
        private static StringBuilder GetHeader(MailMessage message)
        {
            var header = new StringBuilder();

            header.Append("From: " + message.From + "\r\n");

            var tos = string.Empty;
            foreach (var to in message.To)
            {
                tos = to.Address + ",";
            }
            header.AppendFormat("To: {0}\r\n", tos.TrimEnd(','));

            if (message.CC.Count > 0)
            {
                var ccs = string.Empty;
                foreach (var cc in message.CC)
                {
                    ccs = cc.Address + ",";
                }
                header.AppendFormat("Cc: {0}\r\n", ccs.TrimEnd(','));
            }

            header.AppendFormat("Date: {0}\r\n", DateTime.Now.ToString("ddd, d M y H:m:s z"));
            header.AppendFormat("Subject: {0}\r\n", message.Subject);
            header.Append("X-Mailer: SMTPDirect v1\r\n");
            return header;
        }

        #region Socket Operations

        /// <summary>
        /// Sends data to the specified socket.
        /// </summary>
        /// <param name="socket">The socket.</param>
        /// <param name="message">The message.</param>
        private static void Senddata(Socket socket, string message)
        {
            var messageBytes = Encoding.ASCII.GetBytes(message);
            socket.Send(messageBytes, 0, messageBytes.Length, SocketFlags.None);
        }

        /// <summary>
        /// Checks the response.
        /// </summary>
        /// <param name="socket">The socket.</param>
        /// <param name="responseExpected">The response expected.</param>
        /// <returns></returns>
        private static bool CheckResponse(Socket socket, SmtpResponse responseExpected)
        {
            var bytes = new byte[1024];
            while (socket.Available == 0)
            {
                Thread.Sleep(100);
            }

            socket.Receive(bytes, 0, socket.Available, SocketFlags.None);
            var sResponse = Encoding.ASCII.GetString(bytes);
            var response = Convert.ToInt32(sResponse.Substring(0, 3));
            return response == (int) responseExpected;
        }

        #endregion

        #region DNS

        /// <summary>
        /// DNS MX Lookup
        /// </summary>
        public class DnsMx
        {
            /// <summary>
            /// Executes a DNS Query
            /// </summary>
            /// <param name="pszName">Name of the PSZ.</param>
            /// <param name="wType">Type of the query.</param>
            /// <param name="options">The query options.</param>
            /// <param name="aipServers">The aip servers.</param>
            /// <param name="ppQueryResults">The pp query results.</param>
            /// <param name="pReserved">The p reserved.</param>
            /// <returns></returns>
            [DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true,
                ExactSpelling = true)]
            private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName,
                                               QueryTypes wType, QueryOptions options, int aipServers,
                                               ref IntPtr ppQueryResults, int pReserved);

            /// <summary>
            /// Grabs the DNS Record List
            /// </summary>
            /// <param name="pRecordList">The p record list.</param>
            /// <param name="FreeType">Type of the free.</param>
            [DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);

            /// <summary>
            /// Gets the MX records.
            /// </summary>
            /// <param name="domain">The domain.</param>
            /// <returns></returns>
            public static string[] GetMXRecords(string domain)
            {
                var ptr1 = IntPtr.Zero;
                IntPtr ptr2;
                MXRecord recMx;

                if (Environment.OSVersion.Platform != PlatformID.Win32NT)
                {
                    throw new NotSupportedException();
                }

                var list1 = new List<string>();
                var num1 = DnsQuery(ref domain, QueryTypes.DnsTypeMX, QueryOptions.DnsQueryBypassCache, 0, ref ptr1, 0);

                if (num1 != 0)
                {
                    throw new Win32Exception(num1);
                }

                for (ptr2 = ptr1; !ptr2.Equals(IntPtr.Zero); ptr2 = recMx.PNext)
                {
                    recMx = (MXRecord) Marshal.PtrToStructure(ptr2, typeof (MXRecord));
                    if (recMx.WType != 15) continue;

                    var text1 = Marshal.PtrToStringAuto(recMx.PNameExchange);
                    list1.Add(text1);
                }

                DnsRecordListFree(ptr1, 0);
                return list1.ToArray();
            }

            #region Nested type: MXRecord

            [StructLayout(LayoutKind.Sequential)]
            private struct MXRecord
            {
                public IntPtr PNext;
                public string PName;
                public short WType;
                public short WDataLength;
                public int Flags;
                public int DwTtl;
                public int DwReserved;
                public IntPtr PNameExchange;
                public short WPreference;
                public short Pad;
            }

            #endregion

            #region Nested type: QueryOptions

            /// <summary>
            /// Query Options
            /// </summary>
            private enum QueryOptions
            {
                //DnsQueryAcceptTruncatedResponse = 1,
                /// <summary>
                /// DNS Query Bypass Cache
                /// </summary>
                DnsQueryBypassCache = 8,
                //DnsQueryDontResetTtlValues = 0x100000,
                //DnsQueryNoHostsFile = 0x40,
                //DnsQueryNoLocalName = 0x20,
                //DnsQueryNoNetbt = 0x80,
                //DnsQueryNoRecursion = 4,
                //DnsQueryNoWireQuery = 0x10,
                //DnsQueryReserved = -16777216,
                //DnsQueryReturnMessage = 0x200,
                //DnsQueryStandard = 0,
                //DnsQueryTreatAsFqdn = 0x1000,
                //DnsQueryUseTcpOnly = 2,
                //DnsQueryWireOnly = 0x100
            }

            #endregion

            #region Nested type: QueryTypes

            /// <summary>
            /// Query Types
            /// </summary>
            private enum QueryTypes
            {
                //DnsTypeA = 1,
                //DnsTypeNs = 2,
                //DnsTypeCname = 5,
                //DnsTypeSoa = 6,
                //DnsTypePtr = 12,
                //DnsTypeHinfo = 13,
                /// <summary>
                /// DNS Type MX
                /// </summary>
                DnsTypeMX = 15,
                //DnsTypeTxt = 16,
                //DnsTypeAaaa = 28
            }

            #endregion
        }

        #endregion

        #region Nested type: SmtpResponse

        /// <summary>
        /// Smtp Response
        /// </summary>
        private enum SmtpResponse
        {
            /// <summary>
            /// Connect Success
            /// </summary>
            ConnectSuccess = 220,
            /// <summary>
            /// Generic Success
            /// </summary>
            GenericSuccess = 250,
            /// <summary>
            /// Data Success
            /// </summary>
            DataSuccess = 354,
            /// <summary>
            /// Quit Success
            /// </summary>
            QuitSuccess = 221
        }

        #endregion
    }
}