/** * Class constructor. * * @param dnsDefaultATtl TTL used for any created resource records * @param dnsDefaultNsTtl TTL used for any created nameserver records * @param dnsDefaultDsTtl TTL used for any created DS records * @param transport the transport used to send/receive the UPDATE messages * @param clock a source of time */ @Inject public DnsUpdateWriter( @DnsWriterZone String zoneName, @Config("dnsDefaultATtl") Duration dnsDefaultATtl, @Config("dnsDefaultNsTtl") Duration dnsDefaultNsTtl, @Config("dnsDefaultDsTtl") Duration dnsDefaultDsTtl, DnsMessageTransport transport, Clock clock) { this.zoneName = zoneName; this.update = new Update(toAbsoluteName(zoneName)); this.dnsDefaultATtl = dnsDefaultATtl; this.dnsDefaultNsTtl = dnsDefaultNsTtl; this.dnsDefaultDsTtl = dnsDefaultDsTtl; this.transport = transport; this.clock = clock; }
@Test public void testPublishDomainCreate_publishesNameServers() throws Exception { HostResource host1 = persistActiveHost("ns1.example.tld"); HostResource host2 = persistActiveHost("ns2.example.tld"); DomainResource domain = persistActiveDomain("example.tld") .asBuilder() .setNameservers(ImmutableSet.of(Key.create(host1), Key.create(host2))) .build(); persistResource(domain); writer.publishDomain("example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatUpdateAdds(update, "example.tld.", Type.NS, "ns1.example.tld.", "ns2.example.tld."); assertThatTotalUpdateSetsIs(update, 2); // The delete and NS sets }
@Test public void testPublishAtomic_oneUpdate() throws Exception { HostResource host1 = persistActiveHost("ns.example1.tld"); DomainResource domain1 = persistActiveDomain("example1.tld") .asBuilder() .setNameservers(ImmutableSet.of(Key.create(host1))) .build(); persistResource(domain1); HostResource host2 = persistActiveHost("ns.example2.tld"); DomainResource domain2 = persistActiveDomain("example2.tld") .asBuilder() .setNameservers(ImmutableSet.of(Key.create(host2))) .build(); persistResource(domain2); writer.publishDomain("example1.tld"); writer.publishDomain("example2.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example1.tld.", Type.ANY); assertThatUpdateDeletes(update, "example2.tld.", Type.ANY); assertThatUpdateAdds(update, "example1.tld.", Type.NS, "ns.example1.tld."); assertThatUpdateAdds(update, "example2.tld.", Type.NS, "ns.example2.tld."); assertThatTotalUpdateSetsIs(update, 4); // The delete and NS sets for each TLD }
@Test public void testPublishDomainCreate_publishesDelegationSigner() throws Exception { DomainResource domain = persistActiveDomain("example.tld") .asBuilder() .setNameservers(ImmutableSet.of(Key.create(persistActiveHost("ns1.example.tld")))) .setDsData( ImmutableSet.of( DelegationSignerData.create(1, 3, 1, base16().decode("0123456789ABCDEF")))) .build(); persistResource(domain); writer.publishDomain("example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatUpdateAdds(update, "example.tld.", Type.NS, "ns1.example.tld."); assertThatUpdateAdds(update, "example.tld.", Type.DS, "1 3 1 0123456789ABCDEF"); assertThatTotalUpdateSetsIs(update, 3); // The delete, the NS, and DS sets }
@Test public void testPublishDomainWhenNotActive_removesDnsRecords() throws Exception { DomainResource domain = persistActiveDomain("example.tld") .asBuilder() .addStatusValue(StatusValue.SERVER_HOLD) .setNameservers(ImmutableSet.of(Key.create(persistActiveHost("ns1.example.tld")))) .build(); persistResource(domain); writer.publishDomain("example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatTotalUpdateSetsIs(update, 1); // Just the delete set }
@Test public void testPublishHostDelete_removesGlueRecords() throws Exception { persistDeletedHost("ns1.example.tld", clock.nowUtc().minusDays(1)); persistResource( persistActiveDomain("example.tld") .asBuilder() .setNameservers(ImmutableSet.of(Key.create(persistActiveHost("ns1.example.com")))) .build()); writer.publishHost("ns1.example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatUpdateDeletes(update, "ns1.example.tld.", Type.ANY); assertThatUpdateAdds(update, "example.tld.", Type.NS, "ns1.example.com."); assertThatTotalUpdateSetsIs(update, 3); }
@Test public void testSentMessageTooLongThrowsException() throws Exception { Update oversize = new Update(Name.fromString("tld", Name.root)); for (int i = 0; i < 2000; i++) { oversize.add( ARecord.newRecord( Name.fromString("test-extremely-long-name-" + i + ".tld", Name.root), Type.A, DClass.IN)); } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); when(mockSocket.getOutputStream()).thenReturn(outputStream); IllegalArgumentException thrown = expectThrows(IllegalArgumentException.class, () -> resolver.send(oversize)); assertThat(thrown).hasMessageThat().contains("message larger than maximum"); }
private void doUpdate(AdapterOperation op) { OnmsNode node = null; log().debug("doUpdate: operation: " + op.getType().name()); try { node = m_nodeDao.get(op.getNodeId()); DnsRecord record = new DnsRecord(node); log().debug("doUpdate: DnsRecord: hostname: " + record.getHostname() + " zone: " + record.getZone() + " ip address " + record.getIp().getHostAddress()); DnsRecord oldRecord = m_nodeDnsRecordMap.get(Integer.valueOf(node.getId())); Update update = new Update(Name.fromString(record.getZone())); if (oldRecord != null && oldRecord.getHostname() != record.getHostname()) { update.delete(Name.fromString(oldRecord.getHostname()), Type.A); } update.replace(Name.fromString(record.getHostname()), Type.A, 3600, record.getIp().getHostAddress()); m_resolver.send(update); m_nodeDnsRecordMap.put(Integer.valueOf(op.getNodeId()), record); } catch (Throwable e) { log().error("addNode: Error handling node added event.", e); sendAndThrow(op.getNodeId(), e); } }
private void doDelete(AdapterOperation op) { try { DnsRecord record = m_nodeDnsRecordMap.get(Integer.valueOf(op.getNodeId())); if (record != null) { Update update = new Update(Name.fromString(record.getZone())); update.delete(Name.fromString(record.getHostname()), Type.A); m_resolver.send(update); m_nodeDnsRecordMap.remove(Integer.valueOf(op.getNodeId())); } } catch (Throwable e) { log().error("deleteNode: Error handling node deleted event.", e); sendAndThrow(op.getNodeId(), e); } }
private void doUpdate(AdapterOperation op) { log().debug("doUpdate: operation: " + op.getType().name()); for (ReverseDnsRecord record : m_reverseDnsProvisioningAdapterService.get(op.getNodeId()) ) { log().debug("doUpdate: ReverseDnsRecord: hostname: " + record.getHostname() + " zone: " + record.getZone() + " ip address: " + record.getIp().getHostAddress()); try { Update update = new Update(Name.fromString(record.getZone())); Name ptrRecord=ReverseMap.fromAddress(record.getIp()); update.replace(ptrRecord, Type.PTR, 3600, record.getHostname()); m_resolver.send(update); m_reverseDnsProvisioningAdapterService.update(op.getNodeId(),record); } catch (Exception e) { log().error("updateNode: Error handling updated event.", e); sendAndThrow(op.getNodeId(), e); } } }
private void deleteSubordinateHostAddressSet( DomainResource domain, String additionalHost, Update update) { for (String hostName : union( domain.getSubordinateHosts(), (additionalHost == null ? ImmutableSet.of() : ImmutableSet.of(additionalHost)))) { update.delete(toAbsoluteName(hostName), Type.ANY); } }
private void addInBailiwickNameServerSet(DomainResource domain, Update update) { for (String hostName : intersection( domain.loadNameserverFullyQualifiedHostNames(), domain.getSubordinateHosts())) { HostResource host = loadByForeignKey(HostResource.class, hostName, clock.nowUtc()); update.add(makeAddressSet(host)); update.add(makeV6AddressSet(host)); } }
@Before public void setUp() throws Exception { inject.setStaticField(Ofy.class, "clock", clock); createTld("tld"); when(mockResolver.send(any(Update.class))).thenReturn(messageWithResponseCode(Rcode.NOERROR)); writer = new DnsUpdateWriter( "tld", Duration.ZERO, Duration.ZERO, Duration.ZERO, mockResolver, clock); }
@Test public void testPublishDomainDelete_removesDnsRecords() throws Exception { persistDeletedDomain("example.tld", clock.nowUtc().minusDays(1)); writer.publishDomain("example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatTotalUpdateSetsIs(update, 1); // Just the delete set }
@Test public void testPublishHostCreate_publishesAddressRecords() throws Exception { HostResource host = persistResource( newHostResource("ns1.example.tld") .asBuilder() .setInetAddresses( ImmutableSet.of( InetAddresses.forString("10.0.0.1"), InetAddresses.forString("10.1.0.1"), InetAddresses.forString("fd0e:a5c8:6dfb:6a5e:0:0:0:1"))) .build()); persistResource( newDomainResource("example.tld") .asBuilder() .addSubordinateHost("ns1.example.tld") .addNameservers(ImmutableSet.of(Key.create(host))) .build()); writer.publishHost("ns1.example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatUpdateDeletes(update, "ns1.example.tld.", Type.ANY); assertThatUpdateAdds(update, "ns1.example.tld.", Type.A, "10.0.0.1", "10.1.0.1"); assertThatUpdateAdds(update, "ns1.example.tld.", Type.AAAA, "fd0e:a5c8:6dfb:6a5e:0:0:0:1"); assertThatUpdateAdds(update, "example.tld.", Type.NS, "ns1.example.tld."); assertThatTotalUpdateSetsIs(update, 5); }
@Test public void testPublishHostDelete_removesDnsRecords() throws Exception { persistDeletedHost("ns1.example.tld", clock.nowUtc().minusDays(1)); persistActiveDomain("example.tld"); writer.publishHost("ns1.example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatUpdateDeletes(update, "ns1.example.tld.", Type.ANY); assertThatTotalUpdateSetsIs(update, 2); // Just the delete set }
@Test public void testPublishDomainExternalAndInBailiwickNameServer() throws Exception { HostResource externalNameserver = persistResource(newHostResource("ns1.example.com")); HostResource inBailiwickNameserver = persistResource( newHostResource("ns1.example.tld") .asBuilder() .setInetAddresses( ImmutableSet.of( InetAddresses.forString("10.0.0.1"), InetAddresses.forString("10.1.0.1"), InetAddresses.forString("fd0e:a5c8:6dfb:6a5e:0:0:0:1"))) .build()); persistResource( newDomainResource("example.tld") .asBuilder() .addSubordinateHost("ns1.example.tld") .addNameservers( ImmutableSet.of(Key.create(externalNameserver), Key.create(inBailiwickNameserver))) .build()); writer.publishDomain("example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatUpdateDeletes(update, "ns1.example.tld.", Type.ANY); assertThatUpdateAdds(update, "example.tld.", Type.NS, "ns1.example.com.", "ns1.example.tld."); assertThatUpdateAdds(update, "ns1.example.tld.", Type.A, "10.0.0.1", "10.1.0.1"); assertThatUpdateAdds(update, "ns1.example.tld.", Type.AAAA, "fd0e:a5c8:6dfb:6a5e:0:0:0:1"); assertThatTotalUpdateSetsIs(update, 5); }
@Test public void testPublishDomainDeleteOrphanGlues() throws Exception { HostResource inBailiwickNameserver = persistResource( newHostResource("ns1.example.tld") .asBuilder() .setInetAddresses( ImmutableSet.of( InetAddresses.forString("10.0.0.1"), InetAddresses.forString("10.1.0.1"), InetAddresses.forString("fd0e:a5c8:6dfb:6a5e:0:0:0:1"))) .build()); persistResource( newDomainResource("example.tld") .asBuilder() .addSubordinateHost("ns1.example.tld") .addSubordinateHost("foo.example.tld") .addNameservers(ImmutableSet.of(Key.create(inBailiwickNameserver))) .build()); writer.publishDomain("example.tld"); writer.commit(); verify(mockResolver).send(updateCaptor.capture()); Update update = updateCaptor.getValue(); assertThatUpdatedZoneIs(update, "tld."); assertThatUpdateDeletes(update, "example.tld.", Type.ANY); assertThatUpdateDeletes(update, "ns1.example.tld.", Type.ANY); assertThatUpdateDeletes(update, "foo.example.tld.", Type.ANY); assertThatUpdateAdds(update, "example.tld.", Type.NS, "ns1.example.tld."); assertThatUpdateAdds(update, "ns1.example.tld.", Type.A, "10.0.0.1", "10.1.0.1"); assertThatUpdateAdds(update, "ns1.example.tld.", Type.AAAA, "fd0e:a5c8:6dfb:6a5e:0:0:0:1"); assertThatTotalUpdateSetsIs(update, 6); }
private void assertThatUpdateAdds( Update update, String resourceName, int recordType, String... resourceData) { ArrayList<String> expectedData = new ArrayList<>(); Collections.addAll(expectedData, resourceData); ArrayList<String> actualData = new ArrayList<>(); for (Record record : findUpdateRecords(update, resourceName, recordType)) { actualData.add(record.rdataToString()); } assertThat(actualData).containsExactlyElementsIn(expectedData); }
private ImmutableList<Record> findUpdateRecords( Update update, String resourceName, int recordType) { for (RRset set : update.getSectionRRsets(Section.UPDATE)) { if (set.getName().toString().equals(resourceName) && set.getType() == recordType) { return fixIterator(Record.class, set.rrs()); } } assert_().fail( "No record set found for resource '%s' type '%s'", resourceName, Type.string(recordType)); throw new AssertionError(); }
public boolean sendAdd() throws TextParseException, IOException { boolean rc = false; Resolver res = createResolver(); String revIp = buildReverseIpString(); Name owner = new Name(revIp.toString()); PTRRecord ptr = new PTRRecord(owner, DClass.IN, ttl, new Name(fqdn)); Name _zone = buildZoneName(revIp); Update update = new Update(_zone); update.delete(owner); update.add(ptr); if (log.isDebugEnabled()) { log.debug("Sending reverse DDNS update (replace) to server=" + server + ":\n" + update.toString()); } else if (log.isInfoEnabled()) { log.info("Sending reverse DDNS update (replace): " + ptr.toString()); } Message response = res.send(update); if (response.getRcode() == Rcode.NOERROR) { log.info("Reverse DDNS update (replace) succeeded: " + ptr.toString()); rc = true; } else { log.error("Reverse DDNS update (replace) failed (rcode=" + Rcode.string(response.getRcode()) + "): " + ptr.toString()); } return rc; }
public boolean sendDelete() throws TextParseException, IOException { boolean rc = false; Resolver res = createResolver(); String revIp = buildReverseIpString(); Name owner = new Name(revIp); PTRRecord ptr = new PTRRecord(owner, DClass.IN, 0, new Name(fqdn)); Name _zone = buildZoneName(revIp); Update update = new Update(_zone); update.delete(ptr); if (log.isDebugEnabled()) { log.debug("Sending reverse DDNS update (delete) to server=" + server + ":\n" + update.toString()); } else if (log.isInfoEnabled()) { log.info("Sending reverse DDNS update (delete): " + ptr.toString()); } Message response = res.send(update); if (response.getRcode() == Rcode.NOERROR) { log.info("Reverse DDNS update (delete) succeeded: " + ptr.toString()); rc = true; } else { log.error("Reverse DDNS update (delete) failed (rcode=" + Rcode.string(response.getRcode()) + "): " + ptr.toString()); } return rc; }
private void assertThatUpdatedZoneIs(Update update, String zoneName) { Record[] zoneRecords = update.getSectionArray(Section.ZONE); assertThat(zoneRecords[0].getName().toString()).isEqualTo(zoneName); }
private void assertThatTotalUpdateSetsIs(Update update, int count) { assertThat(update.getSectionRRsets(Section.UPDATE)).hasLength(count); }
private void assertThatUpdateDeletes(Update update, String resourceName, int recordType) { ImmutableList<Record> deleted = findUpdateRecords(update, resourceName, recordType); // There's only an empty (i.e. "delete") record. assertThat(deleted.get(0).rdataToString()).hasLength(0); assertThat(deleted).hasSize(1); }