Exemplo n.º 1
0
        /// <summary>
        /// Searches the network for UPnP devices.
        /// </summary>
        /// <param name="deviceType">UPnP device type. For example: "urn:schemas-upnp-org:device:InternetGatewayDevice:1".</param>
        /// <param name="timeout">Search wait timeout in milliseconds.</param>
        /// <returns>Returns matched UPnP devices.</returns>
        public UPnP_Device[] Search(string deviceType, int timeout)
        {
            if (timeout < 1)
            {
                timeout = 1;
            }

            /* www.unpnp.org. Search request with M-SEARCH.
             *      When a control point desires to search the network for devices, it MUST send a multicast request with method M-SEARCH in the
             *      following format. Control points that know the address of a specific device MAY also use a similar format to send unicast requests
             *      with method M-SEARCH.
             *
             *      For multicast M-SEARCH, the message format is defined below. Values in italics are placeholders for actual values.
             *
             *          M-SEARCH * HTTP/1.1
             *          HOST: 239.255.255.250:1900
             *          MAN: "ssdp:discover"
             *          MX: seconds to delay response
             *          ST: search target
             *          USER-AGENT: OS/version UPnP/1.1 product/version
             *
             *  Note: No body is present in requests with method M-SEARCH, but note that the message MUST have a blank line following the
             *  last header field.
             *  Note: The TTL for the IP packet SHOULD default to 2 and SHOULD be configurable.
             *
             *  Listed below are details for the request line and header fields appearing in the listing above. Field names are not case sensitive.
             *  All field values are case sensitive except where noted.
             *
             *  Request line
             *  Must be “M-SEARCH * HTTP/1.1”
             *
             *  M-SEARCH
             *      Method for search requests.
             *
             *      Request applies generally and not to a specific resource. MUST be *.
             *
             *  HTTP/1.1
             *      HTTP version.
             *
             * Header fields
             *
             * HOST
             *  REQUIRED. Field value contains the multicast address and port reserved for SSDP by Internet Assigned Numbers Authority
             *  (IANA). MUST be 239.255.255.250:1900.
             *
             * MAN
             *  REQUIRED by HTTP Extension Framework. Unlike the NTS and ST field values, the field value of the MAN header field is
             *  enclosed in double quotes; it defines the scope (namespace) of the extension. MUST be "ssdp:discover".
             *
             * MX
             *  REQUIRED. Field value contains maximum wait time in seconds. MUST be greater than or equal to 1 and SHOULD be less than
             *  5 inclusive. Device responses SHOULD be delayed a random duration between 0 and this many seconds to balance load for
             *  the control point when it processes responses. This value MAY be increased if a large number of devices are expected to
             *  respond. The MX field value SHOULD NOT be increased to accommodate network characteristics such as latency or
             *  propagation delay (for more details, see the explanation below). Specified by UPnP vendor. Integer.
             *
             * ST
             *  REQUIRED. Field value contains Search Target. MUST be one of the following. (See NT header field in NOTIFY with ssdp:alive
             *  above.) Single URI.
             *
             *      ssdp:all
             *          Search for all devices and services.
             *
             *      upnp:rootdevice
             *          Search for root devices only.
             *
             *      uuid:device-UUID
             *          Search for a particular device. device-UUID specified by UPnP vendor. See section 1.1.4, “UUID format and
             *          RECOMMENDED generation algorithms” for the MANDATORY UUID format.
             *
             *      urn:schemas-upnp-org:device:deviceType:ver
             *          Search for any device of this type where deviceType and ver are defined by the UPnP Forum working committee.
             *
             *      urn:schemas-upnp-org:service:serviceType:ver
             *          Search for any service of this type where serviceType and ver are defined by the UPnP Forum working committee.
             *
             *      urn:domain-name:device:deviceType:ver
             *          Search for any device of this typewhere domain-name (a Vendor Domain Name), deviceType and ver are defined
             *          by the UPnP vendor and ver specifies the highest specifies the highest supported version of the device type.
             *          Period characters in the Vendor Domain Name MUST be replaced with hyphens in accordance with RFC 2141.
             *
             *      urn:domain-name:service:serviceType:ver
             *          Search for any service of this type. Where domain-name (a Vendor Domain Name), serviceType and ver are
             *          defined by the UPnP vendor and ver specifies the highest specifies the highest supported version of the service
             *          type. Period characters in the Vendor Domain Name MUST be replaced with hyphens in accordance with RFC 2141.
             *
             * USER-AGENT
             *  OPTIONAL. Specified by UPnP vendor. String. Field value MUST begin with the following “product tokens” (defined by
             *  HTTP/1.1). The first product token identifes the operating system in the form OS name/OS version, the second token
             *  represents the UPnP version and MUST be UPnP/1.1, and the third token identifes the product using the form
             *  product name/product version. For example, “USER-AGENT: unix/5.1 UPnP/1.1 MyProduct/1.0”. Control points MUST be
             *  prepared to accept a higher minor version number of the UPnP version than the control point itself implements. For
             *  example, control points implementing UDA version 1.0 will be able to interoperate with devices implementing
             *  UDA version 1.1.
             */

            StringBuilder query = new StringBuilder();

            query.Append("M-SEARCH * HTTP/1.1\r\n");
            query.Append("HOST: 239.255.255.250:1900\r\n");
            query.Append("MAN: \"ssdp:discover\"\r\n");
            query.Append("MX: 1\r\n");
            query.Append("ST: " + deviceType + "\r\n");
            query.Append("\r\n");

            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
            {
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.IpTimeToLive, 2);

                socket.SendTo(Encoding.UTF8.GetBytes(query.ToString()), new IPEndPoint(IPAddress.Broadcast, 1900));

                List <string> deviceLocations = new List <string>();
                byte[]        buffer          = new byte[32000];
                DateTime      startTime       = DateTime.Now;
                // Receive responses while timeout reached.
                while (startTime.AddMilliseconds(timeout) > DateTime.Now)
                {
                    // We have response, read it.
                    if (socket.Poll(1, SelectMode.SelectRead))
                    {
                        int countReceived = socket.Receive(buffer);

                        string[] responseLines = Encoding.UTF8.GetString(buffer, 0, countReceived).Split('\n');
                        foreach (string responseLine in responseLines)
                        {
                            string[] name_value = responseLine.Split(new char[] { ':' }, 2);
                            if (String2.Equals(name_value[0], "location", StringComparison2.InvariantCultureIgnoreCase))
                            {
                                deviceLocations.Add(name_value[1].Trim());
                            }
                        }
                    }
                }

                // Create devices.
                List <UPnP_Device> devices = new List <UPnP_Device>();
                foreach (string location in deviceLocations)
                {
                    try
                    {
                        devices.Add(new UPnP_Device(location));
                    }
                    catch
                    {
                    }
                }

                return(devices.ToArray());
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Searches the network for UPnP devices.
        /// </summary>
        /// <param name="ip">IP address of UPnP device.</param>
        /// <param name="deviceType">UPnP device type. For example: "urn:schemas-upnp-org:device:InternetGatewayDevice:1".</param>
        /// <param name="timeout">Search wait timeout in milliseconds.</param>
        /// <returns>Returns matched UPnP devices.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>ip</b> is null reference.</exception>
        public UPnP_Device[] Search(IPAddress ip, string deviceType, int timeout)
        {
            if (ip == null)
            {
                throw new ArgumentNullException("ip");
            }
            if (timeout < 1)
            {
                timeout = 1;
            }

            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
            {
                socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.IpTimeToLive, 2);

                StringBuilder query = new StringBuilder();
                query.Append("M-SEARCH * HTTP/1.1\r\n");
                query.Append("MAN: \"ssdp:discover\"\r\n");
                query.Append("MX: 1\r\n");
                query.Append("ST: " + deviceType + "\r\n");
                query.Append("\r\n");

                socket.SendTo(Encoding.UTF8.GetBytes(query.ToString()), new IPEndPoint(ip, 1900));

                List <string> deviceLocations = new List <string>();
                byte[]        buffer          = new byte[32000];
                DateTime      startTime       = DateTime.Now;
                // Receive responses while timeout reached.
                while (startTime.AddMilliseconds(timeout) > DateTime.Now)
                {
                    // We have response, read it.
                    if (socket.Poll(1, SelectMode.SelectRead))
                    {
                        int countReceived = socket.Receive(buffer);

                        string[] responseLines = Encoding.UTF8.GetString(buffer, 0, countReceived).Split('\n');
                        foreach (string responseLine in responseLines)
                        {
                            string[] name_value = responseLine.Split(new char[] { ':' }, 2);
                            if (String2.Equals(name_value[0], "location", StringComparison2.InvariantCultureIgnoreCase))
                            {
                                deviceLocations.Add(name_value[1].Trim());
                            }
                        }
                    }
                }

                // Create devices.
                List <UPnP_Device> devices = new List <UPnP_Device>();
                foreach (string location in deviceLocations)
                {
                    try
                    {
                        devices.Add(new UPnP_Device(location));
                    }
                    catch
                    {
                    }
                }

                return(devices.ToArray());
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Parses authetication info from client digest response.
        /// </summary>
        /// <param name="digestResponse">Client returned digest response.</param>
        private void Parse(string digestResponse)
        {
            string[] parameters = TextUtils.SplitQuotedString(digestResponse, ',');
            foreach (string parameter in parameters)
            {
                string[] name_value = parameter.Split(new char[] { '=' }, 2);
                string   name       = name_value[0].Trim();

                if (name_value.Length == 2)
                {
                    if (String2.Equals(name, "realm", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Realm = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "nonce", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Nonce = TextUtils.UnQuoteString(name_value[1]);
                    }
                    // RFC bug ?: RFC 2831. digest-uri = "digest-uri" "=" <"> digest-uri-value <">
                    //            RFC 2617  digest-uri        = "uri" "=" digest-uri-value
                    else if (String2.Equals(name, "uri", StringComparison2.InvariantCultureIgnoreCase) || String2.Equals(name, "digest-uri", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Uri = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "qop", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Qop = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "nc", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_NonceCount = Convert.ToInt32(TextUtils.UnQuoteString(name_value[1]));
                    }
                    else if (String2.Equals(name, "cnonce", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Cnonce = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "response", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Response = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "opaque", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Opaque = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "username", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_UserName = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "algorithm", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Algorithm = TextUtils.UnQuoteString(name_value[1]);
                    }
                    else if (String2.Equals(name, "charset", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        m_Charset = TextUtils.UnQuoteString(name_value[1]);
                    }
                }
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Calculates response value.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <param name="password">User password.</param>
        /// <returns>Returns calculated rsponse value.</returns>
        public string CalculateResponse(string userName, string password)
        {
            /* RFC 2617.
             *
             *  3.2.2.1 Request-Digest
             *
             *      If the "qop" value is "auth" or "auth-int":
             *
             *          request-digest  = <"> < KD ( H(A1),unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) )> <">
             *
             *      If the "qop" directive is not present (this construction is for
             *      compatibility with RFC 2069):
             *
             *          request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
             *
             *  3.2.2.2 A1
             *
             *      If the "algorithm" directive's value is "MD5" or is unspecified, then A1 is:
             *
             *          A1 = unq(username-value) ":" unq(realm-value) ":" passwd
             *
             *      If the "algorithm" directive's value is "MD5-sess", then A1 is
             *      calculated only once - on the first request by the client following
             *      receipt of a WWW-Authenticate challenge from the server.  It uses the
             *      server nonce from that challenge, and the first client nonce value to
             *      construct A1 as follows:
             *
             *          A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) ":" unq(nonce-value) ":" unq(cnonce-value)
             *
             *      This creates a 'session key' for the authentication of subsequent
             *      requests and responses which is different for each "authentication
             *      session", thus limiting the amount of material hashed with any one
             *      key.  (Note: see further discussion of the authentication session in
             *      section 3.3.) Because the server need only use the hash of the user
             *      credentials in order to create the A1 value, this construction could
             *      be used in conjunction with a third party authentication service so
             *      that the web server would not need the actual password value.  The
             *      specification of such a protocol is beyond the scope of this
             *      specification.
             *
             *  3.2.2.3 A2
             *
             *      If the "qop" directive's value is "auth" or is unspecified, then A2 is:
             *
             *          A2 = Method ":" digest-uri-value
             *
             *      If the "qop" value is "auth-int", then A2 is:
             *
             *          A2 = Method ":" digest-uri-value ":" H(entity-body)
             *
             *
             *  H(data) = MD5(data)
             *  KD(secret, data) = H(concat(secret, ":", data))
             *  unc = unqoute string
             */

            string A1 = "";

            if (string.IsNullOrEmpty(this.Algorithm) || String2.Equals(this.Algorithm, "md5", StringComparison2.InvariantCultureIgnoreCase))
            {
                A1 = userName + ":" + this.Realm + ":" + password;
            }
            else if (String2.Equals(this.Algorithm, "md5-sess", StringComparison2.InvariantCultureIgnoreCase))
            {
                A1 = H(userName + ":" + this.Realm + ":" + password) + ":" + this.Nonce + ":" + this.CNonce;
            }
            else
            {
                throw new ArgumentException("Invalid 'algorithm' value '" + this.Algorithm + "'.");
            }

            string A2 = "";

            if (string.IsNullOrEmpty(this.Qop) || String2.Equals(this.Qop, "auth", StringComparison2.InvariantCultureIgnoreCase))
            {
                A2 = this.RequestMethod + ":" + this.Uri;
            }
            else
            {
                throw new ArgumentException("Invalid 'qop' value '" + this.Qop + "'.");
            }

            if (String2.Equals(this.Qop, "auth", StringComparison2.InvariantCultureIgnoreCase) || String2.Equals(this.Qop, "auth-int", StringComparison2.InvariantCultureIgnoreCase))
            {
                // request-digest  = <"> < KD ( H(A1),unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) )> <">
                // We don't add quoutes here.

                return(KD(H(A1), this.Nonce + ":" + this.NonceCount.ToString("x8") + ":" + this.CNonce + ":" + this.Qop + ":" + H(A2)));
            }
            else if (string.IsNullOrEmpty(this.Qop))
            {
                // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
                // We don't add quoutes here.

                return(KD(H(A1), this.Nonce + ":" + H(A2)));
            }
            else
            {
                throw new ArgumentException("Invalid 'qop' value '" + this.Qop + "'.");
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Calculates 'rspauth' value.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <param name="password">Password.</param>
        /// <returns>Returns 'rspauth' value.</returns>
        public string CalculateRspAuth(string userName, string password)
        {
            /* RFC 2617 3.2.3.
             *  The optional response digest in the "response-auth" directive
             *  supports mutual authentication -- the server proves that it knows the
             *  user's secret, and with qop=auth-int also provides limited integrity
             *  protection of the response. The "response-digest" value is calculated
             *  as for the "request-digest" in the Authorization header, except that
             *  if "qop=auth" or is not specified in the Authorization header for the
             *  request, A2 is
             *
             *      A2 = ":" digest-uri-value
             *
             *  and if "qop=auth-int", then A2 is
             *
             *      A2 = ":" digest-uri-value ":" H(entity-body)
             *
             *  where "digest-uri-value" is the value of the "uri" directive on the
             *  Authorization header in the request. The "cnonce-value" and "nc-
             *  value" MUST be the ones for the client request to which this message
             *  is the response. The "response-auth", "cnonce", and "nonce-count"
             *  directives MUST BE present if "qop=auth" or "qop=auth-int" is
             *  specified.
             */


            string a1 = "";
            string a2 = "";

            // Create A1
            if (string.IsNullOrEmpty(this.Algorithm) || String2.Equals(this.Algorithm, "md5", StringComparison2.InvariantCultureIgnoreCase))
            {
                a1 = userName + ":" + this.Realm + ":" + password;
            }
            else if (String2.Equals(this.Algorithm, "md5-sess", StringComparison2.InvariantCultureIgnoreCase))
            {
                a1 = Net_Utils.ComputeMd5(userName + ":" + this.Realm + ":" + password, false) + ":" + this.Nonce + ":" + this.CNonce;
            }
            else
            {
                throw new ArgumentException("Invalid Algorithm value '" + this.Algorithm + "' !");
            }
            // Create A2
            if (string.IsNullOrEmpty(this.Qop) || String2.Equals(this.Qop, "auth", StringComparison2.InvariantCultureIgnoreCase))
            {
                a2 = ":" + this.Uri;
            }
            else
            {
                throw new ArgumentException("Invalid qop value '" + this.Qop + "' !");
            }

            // Calculate response value.
            // qop present
            if (!string.IsNullOrEmpty(this.Qop))
            {
                return(Net_Utils.ComputeMd5(Net_Utils.ComputeMd5(a1, true) + ":" + this.Nonce + ":" + this.NonceCount.ToString("x8") + ":" + this.CNonce + ":" + this.Qop + ":" + Net_Utils.ComputeMd5(a2, true), true));
            }
            // qop not present
            else
            {
                return(Net_Utils.ComputeMd5(Net_Utils.ComputeMd5(a1, true) + ":" + this.Nonce + ":" + Net_Utils.ComputeMd5(a2, true), true));
            }
        }
        /// <summary>
        /// Decodes non-ascii text with MIME <b>encoded-word</b> method. Defined in RFC 2047 2.
        /// </summary>
        /// <param name="text">Text.</param>
        /// <returns>Returns decoded text.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>text</b> is null reference.</exception>
        public static string DecodeTextS(string text)
        {
            if (text == null)
            {
                throw new ArgumentNullException("word");
            }

            /* RFC 2047 2.
             *  encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
             *
             *  encoded-text = 1*<Any printable ASCII character other than "?" or SPACE>
             *                 ; (but see "Use of encoded-words in message
             *                 ; headers", section 5)
             *
             *  An 'encoded-word' may not be more than 75 characters long, including
             *  'charset', 'encoding', 'encoded-text', and delimiters.  If it is
             *  desirable to encode more text than will fit in an 'encoded-word' of
             *  75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may
             *  be used.
             *
             *  RFC 2231 updates.
             *      encoded-word := "=?" charset ["*" language] "?" encoded-text "?="
             */

            string retVal = text;

            retVal = encodedword_regex.Replace(retVal, delegate(Match m)
            {
                // We have encoded word, try to decode it.
                // Also if we have continuing encoded word, we need to skip all whitespaces between words.

                string encodedWord = m.Value;
                try
                {
                    if (String2.Equals(m.Groups["encoding"].Value, "Q", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        encodedWord = MIME_Utils.QDecode(Encoding.GetEncoding(m.Groups["charset"].Value), m.Groups["value"].Value);
                    }
                    else if (String2.Equals(m.Groups["encoding"].Value, "B", StringComparison2.InvariantCultureIgnoreCase))
                    {
                        encodedWord = Encoding.GetEncoding(m.Groups["charset"].Value).GetString(Net_Utils.FromBase64(Encoding2.Default.GetBytes(m.Groups["value"].Value)));
                    }
                    // Failed to parse encoded-word, leave it as is. RFC 2047 6.3.
                    // else{

                    // No continuing encoded-word, append whitespaces to retval.
                    Match mNext = encodedword_regex.Match(retVal, m.Index + m.Length);
                    if (!(mNext.Success && mNext.Index == (m.Index + m.Length)))
                    {
                        encodedWord += m.Groups["whitespaces"].Value;
                    }
                    // We have continuing encoded-word, so skip all whitespaces.
                    //else{

                    return(encodedWord);
                }
                catch
                {
                    // Failed to parse encoded-word, leave it as is. RFC 2047 6.3.
                    return(encodedWord);
                }
            });

            return(retVal);
        }