public void TestDomainSlice() { var actual = new Domain("foo.example.com"); var expected = new Domain("example.com"); Assert.That(actual.Slice(1), Is.EqualTo(expected)); }
/** * Returns true if the given Domain is below the current domain (i.e * they share a common suffix) */ public bool IsSubdomain(Domain other) { var prefix_length = other.Length - this.Length; var other_tail = other.Slice(prefix_length); return(other_tail == this); }
/** * Converts a standard domain name into a series of bytes, possibly * compressed using domains seen so far. */ public byte[] SerializeDomain(Domain domain) { var stream = new MemoryStream(); if (domain[domain.Length - 1] != "") { throw new InvalidDataException("Domain name must end in 0 byte segment"); } var idx = 0; foreach (var segment in domain) { Domain current_subdomain = domain.Slice(idx); // Avoid compressing on an empty domain, since it wastes a byte // of space and makes the decoder work harder for no reason if (segment != "" && domains_seen.ContainsKey(current_subdomain)) { // RFC specifies that top-2 bits must be 1 UInt16 compress_bits = (1 << 15) + (1 << 14); UInt16 offset_bits = domains_seen[current_subdomain]; UInt16 compress_ptr = (UInt16)(compress_bits | offset_bits); stream.WriteByte((byte)((compress_ptr >> 8) & 0xff)); stream.WriteByte((byte)(compress_ptr & 0xff)); MoveForward(2); break; } else { // Record the current domain for later compression, and // then write it out (but only if it'll fit into the // 14 bits we have to store the pointer in) if (offset <= (1 << 14) - 1) { domains_seen[current_subdomain] = offset; } var segment_bytes = new byte[segment.Length]; for (int i = 0; i < segment.Length; i++) { segment_bytes[i] = (byte)segment[i]; } stream.WriteByte((byte)segment_bytes.Length); stream.Write(segment_bytes, 0, segment_bytes.Length); MoveForward((UInt16)(1 + segment_bytes.Length)); idx++; } } return(stream.ToArray()); }