Example #1
0
        /// <summary>
        /// Create HTTP headers for a SOAP message.
        /// </summary>
        /// <param name="size">SOAP message size.</param>
        /// <param name="path">Address for the first line.</param>
        /// <param name="address">Hostname.</param>
        /// <param name="packet"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="testingSettings"></param>
        /// <returns>UTF-8 encoded string.</returns>
        public static byte[] CreateHttpHeaders(long size,
                                               string path,
                                               string address,
                                               HttpPacket packet,
                                               string username,
                                               string password,
                                               DigestTestingSettings testingSettings,
                                               ref string nonceBack,
                                               ref int nonceCounter)
        {
            // Create HTTP headers and add content
            StringBuilder httpRequest = new StringBuilder();

            httpRequest.AppendFormat(STATUSLINEPATTERN, path);
            httpRequest.AppendFormat(HOSTLINEPATTERN, address);
            httpRequest.AppendFormat("{0}: {1}; {2}={3}\r\n", CONTENTTYPE, APPLICATIONSOAPXML, CHARSET, "utf-8");
            if (packet != null)
            {
                HttpDigest.DigestAuthenticationParameters parameters = new HttpDigest.DigestAuthenticationParameters();
                parameters.Address   = path;
                parameters.Challenge = packet;
                parameters.UserName  = username;
                parameters.Password  = password;
                httpRequest.AppendFormat("{0}\r\n", HttpDigest.CreateDigestAuthenticationHeader(parameters, testingSettings, ref nonceBack, ref nonceCounter));
            }
            httpRequest.Append(CONTENTLENGTH + ": " + (size).ToString() + "\r\n");
            httpRequest.Append("\r\n");

            // Convert HTTP request to byte array to send
            return(Encoding.UTF8.GetBytes(httpRequest.ToString()));
        }
Example #2
0
        /// <summary>
        /// Performs basic headers validation.
        /// </summary>
        /// <param name="packet">HTTP packet information.</param>
        /// <returns></returns>
        /// <remarks>The following headers are validated:
        /// * Content-Type, as it it mandatory for SOAP 1.2 (presence + value)
        ///
        /// </remarks>
        public static bool ValidateHttpHeaders(HttpPacket packet)
        {
            if (!packet.Headers.ContainsKey(CONTENTTYPE))
            {
                throw new HttpProtocolException("Mandatory Content-Type header not found.");
            }

            string contentTypeHeader = packet.Headers[CONTENTTYPE];

            string[] contentTypeDefinitions = contentTypeHeader.Split(';');
            if (contentTypeDefinitions.Length == 0)
            {
                throw new HttpProtocolException("Content-Type not defined");
            }
            else
            {
                string contentType = contentTypeDefinitions[0];
                //if (StringComparer.InvariantCultureIgnoreCase.Compare(contentType, APPLICATIONSOAPXML) != 0)
                //{
                //    throw new HttpProtocolException(string.Format("Content-Type mismatch; expected: application/soap+xml, actual: {0}", contentType));
                //}
                packet.ContentType = contentType;
                for (int i = 1; i < contentTypeDefinitions.Length; i++)
                {
                    string[] parameters = contentTypeDefinitions[i].Trim().Split('=');
                    if (parameters.Length > 1)
                    {
                        if (StringComparer.InvariantCultureIgnoreCase.Compare(parameters[0], "charset") == 0)
                        {
                            packet.Encoding = parameters[1].Trim();

                            if (StringComparer.InvariantCultureIgnoreCase.Compare(packet.Encoding, UTF8) != 0)
                            {
                                throw new HttpProtocolException(string.Format("Charset mismatch. Expected: utf-8, actual: {0}", packet.Encoding));
                            }
                        }
                    }
                }
            }

            return(true);
        }
Example #3
0
        /// <summary>
        /// Create HTTP headers for a SOAP message.
        /// </summary>
        /// <param name="size">SOAP message size.</param>
        /// <param name="path">Address for the first line.</param>
        /// <param name="address">Hostname.</param>
        /// <returns>UTF-8 encoded string.</returns>
        public static byte[] CreateHttpHeaders(long size,
                                               string path,
                                               string address,
                                               HttpPacket packet, string username, string password)
        {
            // Create HTTP headers and add content
            StringBuilder httpRequest = new StringBuilder();

            httpRequest.AppendFormat(STATUSLINEPATTERN, path);
            httpRequest.AppendFormat(HOSTLINEPATTERN, address);
            httpRequest.AppendFormat("{0}: {1}; {2}={3}\r\n", CONTENTTYPE, APPLICATIONSOAPXML, CHARSET, "utf-8");
            if (packet != null)
            {
                httpRequest.AppendFormat("{0}\r\n", HttpDigest.CreateDigestAuthenticationHeader(packet, username, password, path));
            }
            httpRequest.Append(CONTENTLENGTH + ": " + (size).ToString() + "\r\n");
            httpRequest.Append("\r\n");

            // Convert HTTP request to byte array to send
            return(Encoding.UTF8.GetBytes(httpRequest.ToString()));
        }
Example #4
0
        /// <summary>
        /// Checks if whole packet has been received (accordingly to Content-Length).
        /// </summary>
        /// <param name="responseStream">Response stream with data.</param>
        /// <param name="header">Structure with header information to be filled.</param>
        /// <returns>True, if we still have to wait for answer; false otherwise.</returns>
        public static bool ContinueReading(MemoryStream responseStream, out HttpPacket header)
        {
            //System.Diagnostics.Debug.WriteLine("check if we still have to wait for message.");
            MemoryStream streamCopy = new MemoryStream(responseStream.GetBuffer());
            StreamReader rdr        = new StreamReader(streamCopy);

            header = new HttpPacket();

            int contentLength = 0;
            int bodyOffset    = 0;

            bool bFound = false;
            bool bFirst = true;

            bool noBodySupposed = false;

            while (!rdr.EndOfStream)
            {
                string nextLine = rdr.ReadLine();

                bodyOffset += (2 + nextLine.Length);

                if (nextLine == null)
                {
                    break;
                }

                if (!string.IsNullOrEmpty(nextLine) && string.IsNullOrEmpty(nextLine.Trim('\0')))
                {
                    break;
                }

                if (bFirst)
                {
                    // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF

                    string[] statusParts = nextLine.Split(' ');

                    if (statusParts.Length > 2)
                    {
                        string code = statusParts[1].Trim();
                        header.HttpVersion = statusParts[0];
                        header.StatusCode  = int.Parse(code);
                        int descriptionStart = statusParts[0].Length + statusParts[1].Length + 2;
                        header.StatusDescription = nextLine.Substring(descriptionStart);

                        if (code == "204" || code == "304" || code.StartsWith("1"))
                        {
                            noBodySupposed = true;
                        }
                    }
                    else
                    {
                        throw new HttpProtocolException(string.Format("The first line of a Response message is incorrect: {0}", nextLine));
                    }
                    bFirst = false;
                }
                else
                {
                    // header:        header-field   = field-name ":" OWS [ field-value ] OWS
                    // field-name     = token
                    // field-value    = *( field-content / OWS )
                    // field-content  = *( WSP / VCHAR / obs-text )

                    int colonPos = nextLine.IndexOf(':');

                    if (colonPos > 0)
                    {
                        string headerName  = nextLine.Substring(0, colonPos).Trim();
                        string headerValue = nextLine.Substring(colonPos + 1).Trim();

                        bool add = true;

                        if (StringComparer.CurrentCultureIgnoreCase.Compare(HttpDigest.WWWAUTHENTICATEHEADER, headerName) == 0)
                        {
                            if (!headerValue.ToLower().StartsWith("digest"))
                            {
                                add = false;
                            }
                        }
                        if (StringComparer.CurrentCultureIgnoreCase.Compare(HttpHelper.CONNECTION, headerName) == 0)
                        {
                            header.Connection.Add(headerValue);
                            add = false;
                        }

                        if (StringComparer.CurrentCultureIgnoreCase.Compare("Proxy-Support", headerName) == 0)
                        {
                            add = false;
                        }

                        if (add)
                        {
                            header.Headers.Add(headerName, headerValue);
                        }

                        if (StringComparer.InvariantCultureIgnoreCase.Compare(headerName, CONTENTLENGTH) == 0)
                        {
                            if (!int.TryParse(headerValue, out contentLength))
                            {
                                throw new HttpProtocolException(string.Format("Content-Length incorrect: {0}, integer expected", headerValue));
                            }

                            header.ContentLength = contentLength;
                        }
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(nextLine))
                        {
                            bFound = true;
                            break;
                        }
                        else
                        {
                            throw new HttpProtocolException(string.Format("Unexpected header format: {0}", nextLine));
                        }
                    }
                }
            }

            header.BodyOffset     = bodyOffset;
            header.NoBodySupposed = noBodySupposed;

            // empty line found - body can be checked
            if (bFound)
            {
                // message must not have a body;
                if (noBodySupposed)
                {
                    return(false);
                }
                else
                {
                    if (contentLength <= responseStream.Length - bodyOffset)
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
Example #5
0
        /// <summary>
        /// Performs basic headers validation.
        /// </summary>
        /// <param name="packet">HTTP packet information.</param>
        /// <returns></returns>
        /// <remarks>The following headers are validated:
        /// * Content-Type, as it it mandatory for SOAP 1.2 (presence + value)
        ///
        /// </remarks>
        public static bool ValidateHttpHeaders(HttpPacket packet)
        {
            if (!packet.Headers.ContainsKey(CONTENTTYPE))
            {
                throw new HttpProtocolException("Mandatory Content-Type header not found.");
            }

            string        contentTypeHeader      = packet.Headers[CONTENTTYPE].Trim();
            List <string> contentTypeDefinitions = new List <string>();

            bool constant = false;
            int  start    = 0;

            for (int i = 0; i < contentTypeHeader.Length; i++)
            {
                char ch = contentTypeHeader[i];
                if (ch == ';')
                {
                    if (!constant)
                    {
                        string nextDef = contentTypeHeader.Substring(start, i - start).Trim();
                        start = i + 1;
                        contentTypeDefinitions.Add(nextDef);
                    }
                }
                if (ch == '"')
                {
                    constant = !constant;
                }
            }

            if (start != contentTypeHeader.Length)
            {
                string nextDef = contentTypeHeader.Substring(start, contentTypeHeader.Length - start).Trim();
                contentTypeDefinitions.Add(nextDef);
            }

            if (contentTypeDefinitions.Count == 0)
            {
                throw new HttpProtocolException("Content-Type not defined");
            }
            else
            {
                string contentType = contentTypeDefinitions[0];
                //if (StringComparer.InvariantCultureIgnoreCase.Compare(contentType, APPLICATIONSOAPXML) != 0)
                //{
                //    throw new HttpProtocolException(string.Format("Content-Type mismatch; expected: application/soap+xml, actual: {0}", contentType));
                //}
                packet.ContentType = contentType;
                for (int i = 1; i < contentTypeDefinitions.Count; i++)
                {
                    string[] parameters = contentTypeDefinitions[i].Trim().Split('=');
                    if (parameters.Length > 1)
                    {
                        if (StringComparer.InvariantCultureIgnoreCase.Compare(parameters[0], "charset") == 0)
                        {
                            packet.Encoding = parameters[1].Trim();

                            if (StringComparer.InvariantCultureIgnoreCase.Compare(packet.Encoding, UTF8) != 0)
                            {
                                throw new HttpProtocolException(string.Format("Charset mismatch. Expected: utf-8, actual: {0}", packet.Encoding));
                            }
                        }
                    }
                }
            }

            return(true);
        }
Example #6
0
        /// <summary>
        /// Creates header using challenge packet.
        /// </summary>
        /// <param name="packet">Server's challenge packet</param>
        /// <param name="username">Username.</param>
        /// <param name="password">Password.</param>
        /// <param name="address">HTTP-address</param>
        /// <returns></returns>
        public static string CreateDigestAuthenticationHeader(HttpPacket packet,
                                                              string username,
                                                              string password, string address)
        {
            string header = string.Empty;

            if (packet.Headers.ContainsKey(WWWAUTHENTICATEHEADER))
            {
                string challenge = packet.Headers[WWWAUTHENTICATEHEADER];

                int schemeEnd = challenge.IndexOf(' ');

                // scheme - should be "Digest"
                string scheme = challenge.Substring(0, schemeEnd).Trim();

                string authParams = challenge.Substring(schemeEnd).Trim();

                string[] challengeFields = authParams.Split(',');

                Dictionary <string, string> parameters = new Dictionary <string, string>(StringComparer.CurrentCultureIgnoreCase);

                foreach (string field in challengeFields)
                {
                    string[] pair = field.Split('=');
                    if (pair.Length == 2)
                    {
                        parameters.Add(Unquote(pair[0]), pair[1].Trim());
                    }
                    else
                    {
                        throw new ApplicationException(string.Format("Invalid authentication header: {0}", challenge));
                    }
                }

                // parameters are parsed
                // quotes are not trimmed!

                StringBuilder sb = new StringBuilder("Authorization: Digest ");
                sb.AppendFormat("username=\"{0}\", ", username);

                // realm should contain quotes;
                sb.AppendFormat("{0}={1}, ", REALM, parameters[REALM]);

                string alg = MD5;
                if (parameters.ContainsKey(ALGORITHM))
                {
                    alg = parameters[ALGORITHM];
                }

                sb.AppendFormat("qop=\"auth\", algorithm=\"{0}\", ", alg);
                sb.AppendFormat("uri=\"{0}\", ", address);
                sb.AppendFormat("{0}={1}, nc=00000001, ", NONCE, parameters[NONCE]);

                // cnonce
                string cnonce = GetNonce();
                sb.AppendFormat("cnonce=\"{0}\", ", cnonce);

                // opaque
                if (parameters.ContainsKey(OPAQUE))
                {
                    sb.AppendFormat("{0}={1}, ", OPAQUE, parameters[OPAQUE]);
                }

                // response

                string realm = Unquote(parameters[REALM]);
                string nonce = Unquote(parameters[NONCE]);

                /*
                 * request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
                 *                        ":" nc-value
                 *                        ":" unq(cnonce-value)
                 *                        ":" unq(qop-value)
                 *                        ":" H(A2)
                 *                ) <">
                 *
                 * A1       = unq(username-value) ":" unq(realm-value) ":" passwd
                 *
                 * A2       = Method ":" digest-uri-value
                 *
                 */

                string a1;
                a1 = string.Format("{0}:{1}:{2}", username, realm, password);

                System.Diagnostics.Debug.WriteLine(string.Format("A1: {0}", a1));

                if (alg == MD5SESS)
                {
                    if (string.IsNullOrEmpty(_a1))
                    {
                        _a1 = GetMD5HashBinHex(a1);
                    }
                    a1 = string.Format("{0}:{1}:{2}", _a1, nonce, cnonce);
                }
                string a2 = string.Format("POST:{0}", address);

                string ha1 = GetMD5HashBinHex(a1);
                string ha2 = GetMD5HashBinHex(a2);

                System.Diagnostics.Debug.WriteLine(string.Format("HA1: {0}", ha1));
                System.Diagnostics.Debug.WriteLine(string.Format("A2: {0}", a2));
                System.Diagnostics.Debug.WriteLine(string.Format("HA2: {0}", ha2));

                string a = string.Format("{0}:{1}:00000001:{2}:auth:{3}", ha1, nonce, cnonce, ha2);

                System.Diagnostics.Debug.WriteLine(string.Format("unhashedDigest: {0}", a));

                string response = GetMD5HashBinHex(a);
                System.Diagnostics.Debug.WriteLine(string.Format("hashedDigest: {0}", response));

                sb.AppendFormat("response=\"{0}\"", response);
                header = sb.ToString();
            }

            return(header);
        }
Example #7
0
        /// <summary>
        /// Creates header using challenge packet.
        /// </summary>
        /// <returns></returns>
        public static string CreateDigestAuthenticationHeader(
            DigestAuthenticationParameters authenticationParameters,
            DigestTestingSettings testingSettings,
            ref string nonceBack,
            ref int nonceCounter)
        {
            string header = string.Empty;

            string     username = authenticationParameters.UserName;
            string     password = authenticationParameters.Password;
            string     address  = authenticationParameters.Address;
            HttpPacket packet   = authenticationParameters.Challenge;

            if (packet.Headers.ContainsKey(WWWAUTHENTICATEHEADER))
            {
                string challenge = packet.Headers[WWWAUTHENTICATEHEADER];

                int schemeEnd = challenge.IndexOf(' ');

                // scheme - should be "Digest"
                string scheme = challenge.Substring(0, schemeEnd).Trim();

                string authParams = challenge.Substring(schemeEnd).Trim();

                List <int> commas  = new List <int>();
                bool       inQuote = false;
                for (int i = 0; i < authParams.Length; i++)
                {
                    char nextChar = authParams[i];
                    if (nextChar == '"')
                    {
                        inQuote = !inQuote;
                    }
                    else
                    {
                        if (nextChar == ',' && !inQuote)
                        {
                            commas.Add(i);
                        }
                    }
                }
                commas.Add(authParams.Length);

                Dictionary <string, string> parameters = new Dictionary <string, string>(StringComparer.CurrentCultureIgnoreCase);

                int lastStart = 0;
                foreach (int comma in commas)
                {
                    string   field      = authParams.Substring(lastStart, (comma - lastStart));
                    string[] pair       = field.Split('=');
                    var      equalPos   = field.IndexOf('=');
                    var      paramName  = field.Substring(0, equalPos);
                    var      paramValue = field.Substring(equalPos + 1);
                    if (!string.IsNullOrEmpty(paramName) && !string.IsNullOrEmpty(paramValue))
                    {
                        parameters.Add(Unquote(paramName), paramValue.Trim());
                    }
                    else
                    {
                        throw new ApplicationException(string.Format("Invalid authentication header: {0}", challenge));
                    }

                    lastStart = comma + 1;
                }


                // parameters are parsed
                // quotes are not trimmed!

                if (parameters[NONCE] != nonceBack)
                {
                    nonceBack    = parameters[NONCE];
                    nonceCounter = 1;
                }
                else
                {
                    nonceCounter++;
                }
                string nonceCounterString = string.Format("{0:x08}", nonceCounter);

                StringBuilder sb = new StringBuilder("Authorization: Digest ");
                if (testingSettings == null || !testingSettings.UserNameMissing)
                {
                    sb.AppendFormat("username=\"{0}\", ", username);
                }

                if (testingSettings == null || !testingSettings.RealmMissing)
                {
                    // realm should contain quotes;
                    sb.AppendFormat("{0}={1}, ", REALM, parameters[REALM]);
                }

                string alg = MD5;
                if (parameters.ContainsKey(ALGORITHM))
                {
                    alg = parameters[ALGORITHM];
                }

                sb.AppendFormat("qop=\"auth\", algorithm=\"{0}\", ", alg);
                if (testingSettings == null || !testingSettings.UriMissing)
                {
                    sb.AppendFormat("uri=\"{0}\", ", address);
                }
                if (testingSettings == null || !testingSettings.NonceMissing)
                {
                    sb.AppendFormat("{0}={1}, nc={2}, ", NONCE, parameters[NONCE], nonceCounterString);
                }

                // cnonce
                string cnonce = GetNonce();
                sb.AppendFormat("cnonce=\"{0}\", ", cnonce);

                // opaque
                if (parameters.ContainsKey(OPAQUE))
                {
                    sb.AppendFormat("{0}={1}, ", OPAQUE, parameters[OPAQUE]);
                }

                // response
                if (testingSettings == null || !testingSettings.ResponseMissing)
                {
                    string realm = Unquote(parameters[REALM]);
                    string nonce = Unquote(parameters[NONCE]);

                    /*
                     * request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
                     *                        ":" nc-value
                     *                        ":" unq(cnonce-value)
                     *                        ":" unq(qop-value)
                     *                        ":" H(A2)
                     *                ) <">
                     *
                     * A1       = unq(username-value) ":" unq(realm-value) ":" passwd
                     *
                     * A2       = Method ":" digest-uri-value
                     *
                     */

                    string a1;
                    a1 = string.Format("{0}:{1}:{2}", username, realm, password);

                    System.Diagnostics.Debug.WriteLine(string.Format("A1: {0}", a1));

                    if (alg == MD5SESS)
                    {
                        if (string.IsNullOrEmpty(_a1))
                        {
                            _a1 = GetMD5HashBinHex(a1);
                        }
                        a1 = string.Format("{0}:{1}:{2}", _a1, nonce, cnonce);
                    }
                    string a2 = string.Format("POST:{0}", address);

                    string ha1 = GetMD5HashBinHex(a1);
                    string ha2 = GetMD5HashBinHex(a2);

                    System.Diagnostics.Debug.WriteLine(string.Format("HA1: {0}", ha1));
                    System.Diagnostics.Debug.WriteLine(string.Format("A2: {0}", a2));
                    System.Diagnostics.Debug.WriteLine(string.Format("HA2: {0}", ha2));

                    string a = string.Format("{0}:{1}:{4}:{2}:auth:{3}", ha1, nonce, cnonce, ha2, nonceCounterString);

                    System.Diagnostics.Debug.WriteLine(string.Format("unhashedDigest: {0}", a));

                    string response = GetMD5HashBinHex(a);
                    System.Diagnostics.Debug.WriteLine(string.Format("hashedDigest: {0}", response));

                    sb.AppendFormat("response=\"{0}\"", response);
                }
                header = sb.ToString();
            }

            return(header);
        }