Example #1
0
        private static ByteString Encode(ByteString payload, int fieldOffset, int fieldLength, ByteOrder byteOrder)
        {
            var h      = new ByteStringBuilder().PutInt(payload.Count, byteOrder).Result();
            var header = byteOrder == ByteOrder.LittleEndian ? h.Take(fieldLength) : h.Drop(4 - fieldLength);

            return(ByteString.Create(new byte[fieldOffset]) + header + payload);
        }
Example #2
0
        public void TestParseLargeLiteral()
        {
            var literal = new ByteStringBuilder(1 * 1024 * 1024);

              for (var i = 0; i < literal.Capacity; i += 16) {
            literal.Append("0123456789abcdef");
              }

              WriteReceived(string.Format("{{{0}}}\r\n{1}\r\n", literal.Length, literal.ToString()));

              var data = receiver.Parse();

              Assert.IsNull(data);

              data = receiver.Parse();

              Assert.IsNotNull(data);
              Assert.AreEqual(1, data.Length);
              Assert.AreEqual(ImapDataFormat.Text, data[0].Format);

              var stream = data[0].GetTextAsStream();

              Assert.IsNotInstanceOfType(typeof(MemoryStream), stream);

              Assert.AreEqual(literal.ToByteArray(),
                      Smdn.IO.StreamExtensions.ReadToEnd(stream));
        }
Example #3
0
 private void HandleWrite(object message)
 {
     if (message is ByteString)
     {
         var bs      = message as ByteString;
         var buffer  = ByteString.Unsafe.GetBuffer(bs);
         var builder = new ByteStringBuilder();
         builder.PutInt(buffer.Length, ByteOrder.BigEndian);
         builder.PutBytes(buffer);
         _connection.Tell(Tcp.Write.Create(builder.Result()));
     }
     else
     {
         Unhandled(message);
     }
 }
Example #4
0
        protected override SaslExchangeStatus Exchange(ByteString serverChallenge, out ByteString clientResponse)
        {
            if (Credential == null)
            throw new SaslException("Credential property must be set");

              clientResponse = null;

              if (string.IsNullOrEmpty(Credential.UserName) || string.IsNullOrEmpty(Credential.Password))
            return SaslExchangeStatus.Failed;

              var responseBuilder = new ByteStringBuilder(0x300); // 255(authcid) + 1(NUL) + 255(authzid) + 1(NUL) + 255(passwd)

              responseBuilder.Append(Encoding.UTF8.GetBytes(Credential.Domain ?? string.Empty)); // authcid
              responseBuilder.Append(Smdn.Formats.Octets.NUL);
              responseBuilder.Append(Encoding.UTF8.GetBytes(Credential.UserName)); // authzid
              responseBuilder.Append(Smdn.Formats.Octets.NUL);
              responseBuilder.Append(Encoding.UTF8.GetBytes(Credential.Password)); // passwd

              clientResponse = responseBuilder.ToByteString();

              return SaslExchangeStatus.Succeeded;
        }
Example #5
0
        private static ImapSearchCriteria FromUri(Uri uri,
                                              bool convertLiteral,
                                              bool synchronizedLiteral,
                                              bool splitCharset,
                                              out bool containsLiteral,
                                              out string charset)
        {
            if (uri == null)
            throw new ArgumentNullException("uri");

              containsLiteral = false;
              charset = null;

              var q = uri.Query;

              if (q.Length == 0)
            return null;
              else if (q.Length == 1) // '?'
            return new ImapSearchCriteria(string.Empty);

              /*
               * http://tools.ietf.org/html/rfc5092
               * RFC 5092 - IMAP URL Scheme
               *
               *    Note that quoted strings and non-synchronizing literals [LITERAL+]
               *    are allowed in the <enc-search> content; however, synchronizing
               *    literals are not allowed, as their presence would effectively mean
               *    that the agent interpreting IMAP URLs needs to parse an <enc-search>
               *    content, find all synchronizing literals, and perform proper command
               *    continuation request handling (see Sections 4.3 and 7 of [IMAP4]).
               */
              var query = PercentEncoding.Decode(q.Substring(1), false);
              var len = query.Length;
              var convertedQuery = new ByteStringBuilder(len);

              if (splitCharset) {
            var queryString = new ByteString(query);

            if (queryString.StartsWithIgnoreCase(charsetSpecification)) {
              // CHARSET<SP>astring<SP>
              var posEndOfCharset = queryString.IndexOf(Octets.SP, charsetSpecification.Length);

              if (posEndOfCharset < 0) {
            throw new ArgumentException("search criteria contains invalid charset specification", "uri");
              }
              else {
            charset = queryString.Substring(charsetSpecification.Length,
                                            posEndOfCharset - charsetSpecification.Length).ToString();

            query = queryString.Substring(posEndOfCharset + 1).ByteArray;
            len = query.Length;
              }
            }
              }

              for (var i = 0; i < len;) {
            if (query[i] == ImapOctets.DQuote) {
              /*
               * quoted
               */
              var start = i;

              for (;;) {
            if (++i == len)
              throw new ArgumentException("search criteria contains unclosed quoted string", "uri");

            if (query[i] == ImapOctets.DQuote) {
              break;
            }
            else if (query[i] == ImapOctets.BackSlash) {
              if (++i == len || !(query[i] == ImapOctets.DQuote || query[i] == ImapOctets.BackSlash))
                throw new ArgumentException("search criteria contains invalid quoted string", "uri");
            }
              }

              i++;

              convertedQuery.Append(query, start, i - start);
            }
            else if (query[i] == ImapOctets.OpenBrace) {
              /*
               * literal
               */
              var start = i;
              var isLiteralSynchronizing = false;
              var literalLength = 0;

              for (;;) {
            if (++i == len)
              throw new ArgumentException("search criteria contains incomplete literal", "uri");

            if (Octets.IsDecimalNumber(query[i])) {
              literalLength = literalLength * 10 + (query[i] - 0x30 /* '0' */);
              // TODO: check length
            }
            else if (query[i] == ImapOctets.CloseBrace) {
              // {xxx}
              isLiteralSynchronizing = true;
              break;
            }
            else if (query[i] == ImapOctets.Plus) {
              // {xxx+}
              if (++i == len || query[i] != ImapOctets.CloseBrace)
                throw new ArgumentException("search criteria contains incomplete non-synchronized literal", "uri");
              isLiteralSynchronizing = false;
              break;
            }
            else {
              throw new ArgumentException("search criteria contains invalid literal", "uri");
            }
              }

              if (++i == len || query[i] != Octets.CR)
            throw new ArgumentException("search criteria contains incomplete literal (CR not found)", "uri");
              if (++i == len || query[i] != Octets.LF)
            throw new ArgumentException("search criteria contains incomplete literal (LF not found)", "uri");
              if (++i == len && 0 < literalLength)
            throw new ArgumentException("search criteria contains incomplete literal (unexpected EOL)", "uri");

              containsLiteral = true;

              if (convertLiteral) {
            if (synchronizedLiteral)
              convertedQuery.Append(string.Format("{{{0}}}\x0d\x0a", literalLength));
            else
              convertedQuery.Append(string.Format("{{{0}+}}\x0d\x0a", literalLength));

            convertedQuery.Append(query, i, literalLength);
              }
              else {
            if (synchronizedLiteral != isLiteralSynchronizing)
              throw new ArgumentException(synchronizedLiteral
                                          ? "search criteria contains non-synchronizing literal"
                                          : "search criteria contains synchronizing literal",
                                          "uri");

            convertedQuery.Append(query, start, i - start + literalLength);
              }

              i += literalLength;
            }
            else {
              convertedQuery.Append(query[i++]);
            }
              }

              return new ImapSearchCriteria(new ImapPreformattedString(convertedQuery.ToByteArray()));
        }
Example #6
0
        protected override SaslExchangeStatus Exchange(ByteString serverChallenge, out ByteString clientResponse)
        {
            if (md5 == null)
            throw new ObjectDisposedException(GetType().FullName);
              if (Credential == null)
            throw new SaslException("Credential property must be set");
              if (string.IsNullOrEmpty(ServiceName))
            throw new SaslException("ServiceName property must be set");

              var pairs = ParseChallenge(serverChallenge);

              if (step == 3) {
            /*
             * 2.1.3 Step Three
             */
            if (pairs.ContainsKey("rspauth")) {
              clientResponse = ByteString.CreateEmpty();
              return SaslExchangeStatus.Succeeded;
            }
            else {
              clientResponse = null;
              return SaslExchangeStatus.Failed;
            }
              }

              /*
               * 2.1.1 Step One
               */
              step = 1;
              clientResponse = null;

              if (string.IsNullOrEmpty(Credential.UserName) || string.IsNullOrEmpty(Credential.Password))
            return SaslExchangeStatus.Failed;

              //    algorithm
              //       This directive is required for backwards compatibility with HTTP
              //       Digest., which supports other algorithms. . This directive is
              //       required and MUST appear exactly once; if not present, or if
              //       multiple instances are present, the client should abort the
              //       authentication exchange.
              //
              //         algorithm         = "algorithm" "=" "md5-sess"
              if (!(pairs.ContainsKey("algorithm") && pairs["algorithm"].Equals("md5-sess")))
            return SaslExchangeStatus.Failed; // "algorithm" is missing or unsupported algorithm

              //    charset
              //       This directive, if present, specifies that the server supports
              //       UTF-8 encoding for the username and password. If not present, the
              //       username and password must be encoded in ISO 8859-1 (of which
              //       US-ASCII is a subset). The directive is needed for backwards
              //       compatibility with HTTP Digest, which only supports ISO 8859-1.
              //       This directive may appear at most once; if multiple instances are
              //       present, the client should abort the authentication exchange.
              //
              //         charset           = "charset" "=" "utf-8"
              var charset = (pairs.ContainsKey("charset") && pairs["charset"].Equals("utf-8"))
            ? Encoding.UTF8
            : Encoding.GetEncoding("iso-8859-1");

              ByteString realm, nonce, qop;

              if (!pairs.TryGetValue("realm", out realm))
            return SaslExchangeStatus.Failed;
              if (!pairs.TryGetValue("nonce", out nonce))
            return SaslExchangeStatus.Failed;
              if (!pairs.TryGetValue("qop", out qop))
            return SaslExchangeStatus.Failed;

              qop = new ByteString("auth"); // ignore qop-options

              /*
               * 2.1.2 Step Two
               */
              step = 2;

              //    cnonce
              //       It is RECOMMENDED that it contain at least 64 bits of entropy.
              var cnonce = new ByteString(Cnonce ?? Nonce.Generate(64, true));

              //    nonce-count
              //       The nc-value is the hexadecimal count of the number of requests
              //       (including the current request) that the client has sent with the
              //       nonce value in this request.  For example, in the first request
              //       sent in response to a given nonce value, the client sends
              //       "nc=00000001"
              var nc = new ByteString((1).ToString("X8"));

              //    digest-uri
              //       Indicates the principal name of the service with which the client
              //       wishes to connect, formed from the serv-type, host, and serv-name.
              //       For example, the FTP service on "ftp.example.com" would have a
              //       "digest-uri" value of "ftp/ftp.example.com"; the SMTP server from
              //       the example above would have a "digest-uri" value of
              //       "smtp/mail3.example.com/example.com".
              var digestUri = new ByteString(string.Format("{0}/{1}", ServiceName, Credential.Domain ?? string.Empty));

              //       response-value  =
              //          HEX( KD ( HEX(H(A1)),
              //                  { nonce-value, ":" nc-value, ":",
              //                    cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
              var responseValue = HexKD(md5,
                                HexHA1(md5, charset, Credential.UserName, realm, Credential.Password, nonce, cnonce, null),
                                nonce,
                                nc,
                                cnonce,
                                qop,
                                HexHA2(md5, qop, digestUri));

              var responseBuilder = new ByteStringBuilder(0x200);

              if (charset == Encoding.UTF8)
            responseBuilder.Append("charset=utf-8,");

              responseBuilder.Append("username=\"");
              responseBuilder.Append(Credential.UserName);
              responseBuilder.Append("\",realm=\"");
              responseBuilder.Append(realm);
              responseBuilder.Append("\",nonce=\"");
              responseBuilder.Append(nonce);
              responseBuilder.Append("\",nc=");
              responseBuilder.Append(nc);
              responseBuilder.Append(",cnonce=\"");
              responseBuilder.Append(cnonce);
              responseBuilder.Append("\",digest-uri=\"");
              responseBuilder.Append(digestUri);
              responseBuilder.Append("\",response=");
              responseBuilder.Append(responseValue);
              responseBuilder.Append(",qop=");
              responseBuilder.Append(qop);

              step = 3;

              clientResponse = responseBuilder.ToByteString();

              return SaslExchangeStatus.Continuing;
        }
Example #7
0
        private static ByteString HexKD(MD5 h, ByteString hexHA1, ByteString nonce, ByteString nc, ByteString cnonce, ByteString qop, ByteString hexHA2)
        {
            //       response-value  =
              //          HEX( KD ( HEX(H(A1)),
              //                  { nonce-value, ":" nc-value, ":",
              //                    cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))

              //    Let KD(k, s) be H({k, ":", s}), i.e., the 16 octet hash of the string
              //    k, a colon and the string s.
              var tmp = new ByteStringBuilder(0x100);

              tmp.Append(hexHA1);
              tmp.Append(0x3a);
              tmp.Append(nonce);
              tmp.Append(0x3a);
              tmp.Append(nc);
              tmp.Append(0x3a);
              tmp.Append(cnonce);
              tmp.Append(0x3a);
              tmp.Append(qop);
              tmp.Append(0x3a);
              tmp.Append(hexHA2);

              return HexH(h, tmp.ToByteString());
        }
Example #8
0
        private static ByteString HexHA2(MD5 h, ByteString qop, ByteString digestUri)
        {
            //    If the "qop" directive's value is "auth", then A2 is:
              //
              //       A2       = { "AUTHENTICATE:", digest-uri-value }
              //
              //    If the "qop" value is "auth-int" or "auth-conf" then A2 is:
              //
              //       A2       = { "AUTHENTICATE:", digest-uri-value,
              //                ":00000000000000000000000000000000" }
              var a2 = new ByteStringBuilder(0x80);

              a2.Append("AUTHENTICATE:");
              a2.Append(digestUri);

              if (qop.EqualsIgnoreCase("auth-int") || qop.EqualsIgnoreCase("auth-conf"))
            a2.Append(":00000000000000000000000000000000");

              return HexH(h, a2.ToByteString());
        }
Example #9
0
        private static ByteString HexHA1(MD5 h, Encoding charset, string username, ByteString realm, string password, ByteString nonce, ByteString cnonce, ByteString authzid)
        {
            //    If authzid is specified, then A1 is
              //
              //       A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
              //            ":", nonce-value, ":", cnonce-value, ":", authzid-value }
              //
              //    If authzid is not specified, then A1 is
              //
              //       A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
              //            ":", nonce-value, ":", cnonce-value }
              var a1 = new ByteStringBuilder(0x100);

              a1.Append(h.ComputeHash(ArrayExtensions.Concat(charset.GetBytes(username),
                                                     new byte[] {0x3a},
                                                     realm.ByteArray,
                                                     new byte[] {0x3a},
                                                     charset.GetBytes(password))));
              a1.Append(0x3a);
              a1.Append(nonce);
              a1.Append(0x3a);
              a1.Append(cnonce);

              if (authzid != null) {
            a1.Append(0x3a);
            a1.Append(authzid);
              }

              return HexH(h, a1.ToByteString());
        }