/// <summary>
        /// Encode a PDU to a binary stream. Then send the stream.
        /// </summary>
        /// <param name="pdu">A specified type of a PDU. This argument cannot be null.
        /// If it is null, ArgumentNullException will be thrown.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception>
        public void SendPdu(KerberosPdu pdu)
        {
            if (pdu == null)
            {
                throw new ArgumentNullException("pdu");
            }

            if (UseProxy)
            {
                Debug.Assert(ProxyClient != null, "Proxy Client should be set when using proxy.");

                //temporarily change the tranpsort type to TCP, since all proxy messages are TCP format.
                var oriTransportType = Context.TransportType;
                Context.TransportType = TransportType.TCP;
                KDCProxyMessage message = ProxyClient.MakeProxyMessage(pdu);
                //send proxy message
                ProxyClient.SendProxyRequest(message);
                //restore the original transport type
                Context.TransportType = oriTransportType;
            }
            else
            {
                this.Connect();
                kdcTransport.SendPacket(pdu);
            }
        }
        /// <summary>
        /// Expect to receive a PDU of any type from the remote host.
        /// </summary>
        /// <param name="timeout">Timeout of receiving PDU.</param>
        /// <returns>The expected PDU.</returns>
        /// <exception cref="System.TimeoutException">Thrown when the timeout parameter is negative.</exception>
        public KerberosPdu ExpectPdu(TimeSpan timeout, Type pduType = null)
        {
            this.expectedPduType = pduType;
            KerberosPdu pdu = null;

            if (UseProxy)
            {
                Debug.Assert(ProxyClient != null, "Proxy Client should be set when using proxy.");
                int consumedLength = 0;
                int expectedLength = 0;
                if (ProxyClient.Error == KKDCPError.STATUS_SUCCESS)
                {
                    KDCProxyMessage message = ProxyClient.GetProxyResponse();
                    //temporarily change the tranpsort type to TCP, since all proxy messages are TCP format.
                    var oriTransportType = Context.TransportType;
                    Context.TransportType = TransportType.TCP;
                    //get proxy message
                    pdu = getExpectedPduFromBytes(message.Message.kerb_message.ByteArrayValue, out consumedLength, out expectedLength);
                    //restore the original transport type
                    Context.TransportType = oriTransportType;
                }
            }
            else
            {
                if (timeout.TotalMilliseconds < 0)
                {
                    throw new TimeoutException(KerberosConstValue.TIMEOUT_EXCEPTION);
                }
                TransportEvent eventPacket = kdcTransport.ExpectTransportEvent(timeout);
                pdu = (KerberosPdu)eventPacket.EventObject;
                this.DisConnect();
            }
            return(pdu);
        }
        /// <summary>
        /// Get the proxy message replied from the server.
        /// NULL will be returned if not response is received.
        /// </summary>
        /// <returns>The responded proxy message</returns>
        public KDCProxyMessage GetProxyResponse()
        {
            if (responseBytes == null)
            {
                return(null);
            }
            KDCProxyMessage message = new KDCProxyMessage();

            message.FromBytes(responseBytes);
            return(message);
        }
        /// <summary>
        /// Send the specified proxy message using https.
        /// </summary>
        /// <param name="message"></param>
        public void SendProxyRequest(KDCProxyMessage message)
        {
            try
            {
                //create web request
                HttpRequestMessage request = new HttpRequestMessage()
                {
                    RequestUri = new Uri(config.KKDCPServerURL),
                    Method     = HttpMethod.Post,
                    Version    = HttpVersion.Version11,
                    Content    = new ByteArrayContent(message.ToBytes())
                };
                request.Headers.Connection.Add("keep-alive");
                request.Headers.UserAgent.ParseAdd("Kerberos/1.0");

                var clientHandler = new HttpClientHandler();
                if (config.TlsClientCertificate != null)
                {
                    clientHandler.ClientCertificates.Add(config.TlsClientCertificate);
                }

                HttpClient client = new HttpClient(clientHandler);

                //send message
                HttpResponseMessage response = client.SendAsync(request).Result;

                //get response
                if (response.StatusCode == HttpStatusCode.Forbidden)
                {
                    //HTTP 403 error received, set ERROR to STATUS_AUTHENTICATION_FIREWALL_FAILED.
                    Error = KKDCPError.STATUS_AUTHENTICATION_FIREWALL_FAILED;
                    return;
                }
                responseBytes = response.Content.ReadAsByteArrayAsync().Result;
            }
            catch (AggregateException ex)
            {
                foreach (var e in ex.Flatten().InnerExceptions)
                {
                    if (e is HttpRequestException)
                    {
                        //server dropped the TCP connection
                        //set Error to STATUS_NO_LOGON_SERVERS
                        Error = KKDCPError.STATUS_NO_LOGON_SERVERS;
                    }
                    else
                    {
                        throw;
                    }
                }
            }
        }
        /// <summary>
        /// Send the specified proxy message using https.
        /// </summary>
        /// <param name="message"></param>
        public void SendProxyRequest(KDCProxyMessage message)
        {
            //create web request
            HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(new Uri(config.KKDCPServerURL));

            webRequest.KeepAlive       = config.HttpKeepAlive;
            webRequest.Method          = "POST";
            webRequest.ProtocolVersion = config.HttpVersion;
            webRequest.CachePolicy     = config.HttpCachePolicy;
            webRequest.UserAgent       = config.HttpUserAgent;
            webRequest.Timeout         = config.HttpRequestTimeout;

            if (config.TlsClientCertificate != null)
            {
                webRequest.ClientCertificates.Add(config.TlsClientCertificate);
            }
            //send message
            byte[] data = message.ToBytes();
            webRequest.ContentLength = data.Length;
            Stream postData = webRequest.GetRequestStream();

            postData.Write(data, 0, data.Length);
            postData.Close();

            //get response
            try
            {
                HttpWebResponse response = (System.Net.HttpWebResponse)webRequest.GetResponse();
                if (response.StatusCode == HttpStatusCode.Forbidden)
                {
                    //HTTP 403 error received, set ERROR to STATUS_AUTHENTICATION_FIREWALL_FAILED.
                    Error = KKDCPError.STATUS_AUTHENTICATION_FIREWALL_FAILED;
                    return;
                }
                using (Stream responseDataSteam = response.GetResponseStream())
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        responseDataSteam.CopyTo(ms);
                        responseBytes = ms.ToArray();
                    }
                }
                response.Close();
            }
            catch (WebException)
            {
                //server dropped the TCP connection
                //set Error to STATUS_NO_LOGON_SERVERS
                Error = KKDCPError.STATUS_NO_LOGON_SERVERS;
            }
        }
        /// <summary>
        /// Wrap the KerberosPdu provided into proxy message.
        /// </summary>
        /// <param name="pdu">the specified KerberosPdu to be wrapped</param>
        public KDCProxyMessage MakeProxyMessage(KerberosPdu pdu)
        {
            //prepare proxy message
            KDCProxyMessage message = new KDCProxyMessage(pdu);

            if (!string.IsNullOrEmpty(TargetDomain))
            {
                message.TargetDomain = TargetDomain;
            }
            if (DCLocatorHint.HasValue)
            {
                message.DCLocatorHint = DCLocatorHint.Value;
            }
            return(message);
        }