Exemple #1
0
        public void TestQuery()
        {
            var zone         = new DNSZone(start_of_authority, relays);
            var www_a_record = new DNSRecord(
                new Domain("www.example.com"),
                AddressClass.INTERNET,
                42,
                new AResource(IPv4Address.Parse("192.168.0.1")));

            var www_a_record_2 = new DNSRecord(
                new Domain("www.example.com"),
                AddressClass.INTERNET,
                42,
                new AResource(IPv4Address.Parse("192.168.0.2")));

            // Something that matches the class and record type, but not the domain
            var www2_a_record = new DNSRecord(
                new Domain("www2.example.com"),
                AddressClass.INTERNET,
                42,
                new AResource(IPv4Address.Parse("192.168.0.3")));

            // Something that matches the domain and class, but not the record type
            var www_cname_record = new DNSRecord(
                new Domain("www.example.com"),
                AddressClass.INTERNET,
                42,
                new CNAMEResource(new Domain("www2.example.com")));

            zone.Add(www_a_record);
            zone.Add(www_a_record_2);
            zone.Add(www2_a_record);
            zone.Add(www_cname_record);

            var query_result = zone.Query(
                new Domain("www.example.com"),
                ResourceRecordType.HOST_ADDRESS,
                AddressClass.INTERNET);

            var expected = new List <DNSRecord>();

            expected.Add(www_a_record);
            expected.Add(www_a_record_2);
            Assert.That(query_result, Is.EquivalentTo(expected));
        }
Exemple #2
0
        public void TestAuthorityWithDelegation()
        {
            // Ensures that we're not an authority for things that are subzones
            var zone    = new DNSZone(start_of_authority, relays);
            var subzone = new DNSRecord(
                new Domain("foo.example.com"),
                AddressClass.INTERNET,
                42,
                new NSResource(new Domain("ns.foo.example.com")));

            zone.Add(subzone);

            Assert.That(zone.IsAuthorityFor(new Domain("www.foo.example.com")), Is.False);
        }
Exemple #3
0
        public void TestAuthorityWhenPTRIsKnownV6()
        {
            // We should be an authority for a PTR record that we know about
            var zone = new DNSZone(start_of_authority, relays);
            var ptr  = new DNSRecord(
                new Domain("b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa"),
                AddressClass.INTERNET,
                42,
                new PTRResource(new Domain("www.example.com")));

            zone.Add(ptr);

            Assert.That(zone.IsAuthorityFor(new Domain("b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa")), Is.True);
        }
Exemple #4
0
        public void TestSubzoneWithDelegation()
        {
            // Ensure that we can find the subzone for a domain in that subzone
            var zone    = new DNSZone(start_of_authority, relays);
            var subzone = new DNSRecord(
                new Domain("foo.example.com"),
                AddressClass.INTERNET,
                42,
                new NSResource(new Domain("ns.foo.example.com")));

            zone.Add(subzone);

            Assert.That(zone.FindSubZone(new Domain("www.foo.example.com")),
                        Is.EqualTo(new Domain("foo.example.com")));
        }
Exemple #5
0
        public void TestAuthorityWhenEqualToDelegation()
        {
            // Ensure that we are an authority for the subzone's address itself (which can
            // occur if, say, there's a CNAME pointing to it)
            var zone    = new DNSZone(start_of_authority, relays);
            var subzone = new DNSRecord(
                new Domain("foo.example.com"),
                AddressClass.INTERNET,
                42,
                new NSResource(new Domain("ns.foo.example.com")));

            zone.Add(subzone);

            Assert.That(zone.IsAuthorityFor(new Domain("foo.example.com")), Is.True);
        }
Exemple #6
0
        /**
         * Produces a new zone from an input XML file.
         */
        public static DNSZone Unserialize(XmlDocument config)
        {
            DNSRecord start_of_authority = null;
            var       records            = new List <DNSRecord>();
            var       relays             = new List <EndPoint>();

            if (config.DocumentElement.Name != "zone")
            {
                throw new InvalidDataException("Root element must be called zone");
            }

            foreach (var entry in config.DocumentElement.ChildNodes.OfType <XmlNode>())
            {
                if (entry.NodeType != XmlNodeType.Element)
                {
                    logger.Trace("Ignoring node of type {0}", entry.NodeType);
                    continue;
                }

                bool is_record =
                    entry.Name == "A" ||
                    entry.Name == "NS" ||
                    entry.Name == "CNAME" ||
                    entry.Name == "MX" ||
                    entry.Name == "SOA" ||
                    entry.Name == "PTR" ||
                    entry.Name == "AAAA";

                if (is_record)
                {
                    if (entry.Attributes["name"] == null ||
                        entry.Attributes["class"] == null ||
                        entry.Attributes["ttl"] == null)
                    {
                        throw new InvalidDataException("Resource records must have 'name', 'class' and 'ttl' attributes");
                    }

                    var record_name = new Domain(entry.Attributes["name"].Value);

                    AddressClass record_class;
                    switch (entry.Attributes["class"].Value)
                    {
                    case "IN":
                        record_class = AddressClass.INTERNET;
                        break;

                    default:
                        throw new InvalidDataException("Only address class 'IN' is supported");
                    }

                    UInt32 record_ttl = 0;
                    try
                    {
                        record_ttl = UInt32.Parse(entry.Attributes["ttl"].Value);
                    }
                    catch (Exception err)
                    {
                        if (err is OverflowException || err is FormatException)
                        {
                            throw new InvalidDataException(entry.Attributes["ttl"].Value + " is not a valid TTL");
                        }
                        else
                        {
                            throw;
                        }
                    }

                    IDNSResource resource = null;
                    switch (entry.Name)
                    {
                    case "A":
                        if (entry.Attributes["address"] == null)
                        {
                            throw new InvalidDataException("A record must have address");
                        }


                        IPv4Address address;
                        try
                        {
                            address = IPv4Address.Parse(entry.Attributes["address"].Value);
                        }
                        catch (FormatException)
                        {
                            throw new InvalidDataException(entry.Attributes["address"].Value + " is not a valid IPv4 address");
                        }

                        resource = new AResource(address);
                        logger.Trace("A record: address={0}", ((AResource)resource).Address);
                        break;

                    case "NS":
                        if (entry.Attributes["nameserver"] == null)
                        {
                            throw new InvalidDataException("NS record must have a nameserver");
                        }

                        resource = new NSResource(new Domain(entry.Attributes["nameserver"].Value));
                        logger.Trace("NS record: nameserver={0}", ((NSResource)resource).Nameserver);
                        break;

                    case "CNAME":
                        if (entry.Attributes["alias"] == null)
                        {
                            throw new InvalidDataException("CNAME record must have an alias");
                        }

                        resource = new CNAMEResource(new Domain(entry.Attributes["alias"].Value));
                        logger.Trace("CNAME record: alias={0}", ((CNAMEResource)resource).Alias);
                        break;

                    case "MX":
                        if (entry.Attributes["priority"] == null ||
                            entry.Attributes["mailserver"] == null)
                        {
                            throw new InvalidDataException("MX record must have priority and mailserver");
                        }

                        var mailserver = new Domain(entry.Attributes["mailserver"].Value);

                        UInt16 preference = 0;
                        try
                        {
                            preference = UInt16.Parse(entry.Attributes["priority"].Value);
                        }
                        catch (Exception err)
                        {
                            if (err is OverflowException || err is FormatException)
                            {
                                throw new InvalidDataException(entry.Attributes["priority"].Value + " is not a valid priority value");
                            }
                            else
                            {
                                throw;
                            }
                        }

                        resource = new MXResource(preference, mailserver);

                        logger.Trace("MX record: priority={0} mailserver={1}",
                                     ((MXResource)resource).Preference,
                                     ((MXResource)resource).Mailserver);
                        break;

                    case "PTR":
                        if (entry.Attributes["pointer"] == null)
                        {
                            throw new InvalidDataException("PTR record must have pointer");
                        }

                        if (!reverse_zone_v4.IsSubdomain(record_name) && !reverse_zone_v6.IsSubdomain(record_name))
                        {
                            throw new InvalidDataException("PTR record be in the in-addr.arpa or ip6.arpa zone");
                        }

                        resource = new PTRResource(new Domain(entry.Attributes["pointer"].Value));
                        logger.Trace("PTR record: pointer={0}", ((PTRResource)resource).Pointer);
                        break;

                    case "AAAA":
                        if (entry.Attributes["address"] == null)
                        {
                            throw new InvalidDataException("AAAA record must have address");
                        }


                        IPv6Address v6address;
                        try
                        {
                            v6address = IPv6Address.Parse(entry.Attributes["address"].Value);
                        }
                        catch (FormatException)
                        {
                            throw new InvalidDataException(entry.Attributes["address"].Value + " is not a valid IPv4 address");
                        }

                        resource = new AAAAResource(v6address);
                        logger.Trace("AAAA record: address={0}", ((AAAAResource)resource).Address);
                        break;


                    case "SOA":
                        if (entry.Attributes["primary-ns"] == null ||
                            entry.Attributes["hostmaster"] == null ||
                            entry.Attributes["serial"] == null ||
                            entry.Attributes["refresh"] == null ||
                            entry.Attributes["retry"] == null ||
                            entry.Attributes["expire"] == null ||
                            entry.Attributes["min-ttl"] == null)
                        {
                            throw new InvalidDataException("SOA record missing one of: primary-ns, hostmaster, serial, refresh, retry, expire and min-ttl");
                        }

                        var primary_ns = new Domain(entry.Attributes["primary-ns"].Value);
                        var hostmaster = new Domain(entry.Attributes["hostmaster"].Value);

                        UInt32 serial = 0;
                        try
                        {
                            serial = UInt32.Parse(entry.Attributes["serial"].Value);
                        }
                        catch (Exception err)
                        {
                            if (err is OverflowException || err is FormatException)
                            {
                                throw new InvalidDataException(entry.Attributes["serial"].Value + " is not a valid serial number");
                            }
                            else
                            {
                                throw;
                            }
                        }

                        UInt32 refresh = 0;
                        try
                        {
                            refresh = UInt32.Parse(entry.Attributes["refresh"].Value);
                        }
                        catch (Exception err)
                        {
                            if (err is OverflowException || err is FormatException)
                            {
                                throw new InvalidDataException(entry.Attributes["refresh"].Value + " is not a valid refresh value");
                            }
                            else
                            {
                                throw;
                            }
                        }

                        UInt32 retry = 0;
                        try
                        {
                            retry = UInt32.Parse(entry.Attributes["retry"].Value);
                        }
                        catch (Exception err)
                        {
                            if (err is OverflowException || err is FormatException)
                            {
                                throw new InvalidDataException(entry.Attributes["retry"].Value + " is not a valid retry value");
                            }
                            else
                            {
                                throw;
                            }
                        }

                        UInt32 expire = 0;
                        try
                        {
                            expire = UInt32.Parse(entry.Attributes["expire"].Value);
                        }
                        catch (Exception err)
                        {
                            if (err is OverflowException || err is FormatException)
                            {
                                throw new InvalidDataException(entry.Attributes["expire"].Value + " is not a valid expire value");
                            }
                            else
                            {
                                throw;
                            }
                        }

                        UInt32 minttl = 0;
                        try
                        {
                            minttl = UInt32.Parse(entry.Attributes["min-ttl"].Value);
                        }
                        catch (Exception err)
                        {
                            if (err is OverflowException || err is FormatException)
                            {
                                throw new InvalidDataException(entry.Attributes["min-ttl"].Value + " is not a valid expire value");
                            }
                            else
                            {
                                throw;
                            }
                        }

                        resource = new SOAResource(primary_ns, hostmaster, serial, refresh, retry, expire, minttl);

                        logger.Trace("SOA record: primary-ns={0} hostmaster={1} serial={2} refresh={3} retry={4} expire={5} min-ttl={6}",
                                     ((SOAResource)resource).PrimaryNameServer,
                                     ((SOAResource)resource).Hostmaster,
                                     ((SOAResource)resource).Serial,
                                     ((SOAResource)resource).RefreshSeconds,
                                     ((SOAResource)resource).RetrySeconds,
                                     ((SOAResource)resource).ExpireSeconds,
                                     ((SOAResource)resource).MinimumTTL);

                        break;
                    }

                    var record = new DNSRecord(record_name, record_class, record_ttl, resource);
                    if (record.Resource.Type == ResourceRecordType.START_OF_AUTHORITY)
                    {
                        if (start_of_authority == null)
                        {
                            logger.Trace("Found SOA: {0}", record);
                            start_of_authority = record;
                        }
                        else
                        {
                            throw new InvalidDataException("Cannot have more than one SOA record in zone");
                        }
                    }
                    else
                    {
                        logger.Trace("Found other record: {0}", record);
                        records.Add(record);
                    }
                }
                else if (entry.Name == "relay")
                {
                    if (entry.Attributes["address"] == null ||
                        entry.Attributes["port"] == null)
                    {
                        throw new InvalidDataException("relay record must have address and port");
                    }

                    IPAddress address;
                    int       port;

                    try
                    {
                        address = IPAddress.Parse(entry.Attributes["address"].Value);
                    }
                    catch (FormatException)
                    {
                        throw new InvalidDataException(entry.Attributes["address"].Value + " is not a valid IPv4 address");
                    }

                    try
                    {
                        port = int.Parse(entry.Attributes["port"].Value);
                    }
                    catch (FormatException)
                    {
                        throw new InvalidDataException(entry.Attributes["port"].Value + " is not a valid port");
                    }

                    try
                    {
                        relays.Add(new IPEndPoint(address, port));
                    }
                    catch (ArgumentOutOfRangeException)
                    {
                        throw new InvalidDataException(entry.Attributes["port"].Value + " is not a valid port");
                    }

                    logger.Trace("Found relay: {0}:{1}", address, port);
                }
                else
                {
                    throw new InvalidDataException(entry.Name + " is not a valid zone entry");
                }
            }

            if (start_of_authority == null)
            {
                throw new InvalidDataException("Zone does not have SOA record");
            }

            var zone = new DNSZone(start_of_authority, relays.ToArray());

            foreach (var record in records)
            {
                if (record.TimeToLive < ((SOAResource)start_of_authority.Resource).MinimumTTL)
                {
                    logger.Trace("Correcting TTL: Record {0} has smaller TTL than SOA MinTTL {1}",
                                 record, start_of_authority);
                    record.TimeToLive = ((SOAResource)start_of_authority.Resource).MinimumTTL;
                }

                zone.Add(record);
            }

            return(zone);
        }