diff --git a/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BdxlLocator.java b/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BdxlLocator.java index 915143f3..07f7b10d 100644 --- a/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BdxlLocator.java +++ b/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BdxlLocator.java @@ -26,14 +26,14 @@ import network.oxalis.vefa.peppol.lookup.util.DynamicHostnameGenerator; import network.oxalis.vefa.peppol.lookup.util.EncodingUtils; import network.oxalis.vefa.peppol.mode.Mode; -import org.apache.commons.lang3.StringUtils; import org.xbill.DNS.*; -import org.xbill.DNS.SimpleResolver; -import org.xbill.DNS.Record; +import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,11 +44,18 @@ */ public class BdxlLocator extends AbstractLocator { - private long timeout = 30L; + private long timeout = 20L; private int maxRetries = 3; - private DynamicHostnameGenerator hostnameGenerator; + private static final List customDNSServers = new ArrayList<>(); + //Google DNS: faster, supported by multiple data centers all around the world + public static InetAddress GOOGLE_PRIMARY_DNS; + public static InetAddress GOOGLE_SECONDARY_DNS; + //Cloudflare DNS: internet’s fastest DNS directory + public static InetAddress CLOUDFLARE_PRIMARY_DNS; + public static InetAddress CLOUDFLARE_SECONDARY_DNS; + private final DynamicHostnameGenerator hostnameGenerator; public BdxlLocator(Mode mode) { this( @@ -59,6 +66,21 @@ public BdxlLocator(Mode mode) { ); maxRetries = Integer.parseInt(mode.getString("lookup.locator.bdxl.maxRetries")); timeout = Long.parseLong(mode.getString("lookup.locator.bdxl.timeout")); + + try { + GOOGLE_PRIMARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (8 & 0xff), (byte) (8 & 0xff), (byte) (8 & 0xff), (byte) (8 & 0xff)})); + GOOGLE_SECONDARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (8 & 0xff), (byte) (8 & 0xff), (byte) (4 & 0xff), (byte) (4 & 0xff)})); + + CLOUDFLARE_PRIMARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (1 & 0xff), (byte) (1 & 0xff), (byte) (1 & 0xff), (byte) (1 & 0xff)})); + CLOUDFLARE_SECONDARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (1 & 0xff), (byte) (0 & 0xff), (byte) (0 & 0xff), (byte) (1 & 0xff)})); + } catch (UnknownHostException e) { + //Unable to initialize Custom DNS server Primary DNS lookup fail, now try with default resolver + } + + customDNSServers.add(GOOGLE_PRIMARY_DNS); + customDNSServers.add(GOOGLE_SECONDARY_DNS); + customDNSServers.add(CLOUDFLARE_PRIMARY_DNS); + customDNSServers.add(CLOUDFLARE_SECONDARY_DNS); } /** @@ -110,53 +132,41 @@ public URI lookup(ParticipantIdentifier participantIdentifier) throws LookupExce String hostname = hostnameGenerator.generate(participantIdentifier).replaceAll("=*", ""); try { - // Fetch all records of type NAPTR registered on hostname. - final Lookup lookup = new Lookup(hostname, Type.NAPTR); - Record[] records; + ExtendedResolver extendedResolver = CustomExtendedDNSResolver.createExtendedResolver(customDNSServers, timeout, maxRetries); + extendedResolver.setRetries(maxRetries); + extendedResolver.setTimeout(Duration.ofSeconds(timeout)); - ExtendedResolver extendedResolver = new ExtendedResolver(); - try { - if (StringUtils.isNotBlank(hostname)) { - extendedResolver.addResolver(new SimpleResolver(hostname)); - } - } catch (final UnknownHostException ex) { - //Primary DNS lookup fail, now try with default resolver - } - extendedResolver.addResolver (Lookup.getDefaultResolver ()); - extendedResolver.setRetries (maxRetries); - extendedResolver.setTimeout (Duration.ofSeconds (timeout)); - lookup.setResolver (extendedResolver); + // Fetch all records of type NAPTR registered on hostname. + final Lookup naptrLookup = new Lookup(hostname, Type.NAPTR); + naptrLookup.setResolver(extendedResolver); + Record[] records; int retryCountLeft = maxRetries; - // Retry = The lookup may fail due to a network error. Repeating the lookup might be helpful + // Retry, the NAPTR lookup may fail due to a network error. Repeating the lookup might be helpful do { - records = lookup.run(); + records = naptrLookup.run(); --retryCountLeft; - } while (lookup.getResult () == Lookup.TRY_AGAIN && retryCountLeft >= 0); + } while (naptrLookup.getResult() == Lookup.TRY_AGAIN && retryCountLeft >= 0); // Retry with TCP as well - if (lookup.getResult () == Lookup.TRY_AGAIN) { - extendedResolver.setTCP (true); + if (naptrLookup.getResult() == Lookup.TRY_AGAIN) { + extendedResolver.setTCP(true); retryCountLeft = maxRetries; do { - records = lookup.run(); + records = naptrLookup.run(); --retryCountLeft; - } while (lookup.getResult () == Lookup.TRY_AGAIN && retryCountLeft >= 0); + } while (naptrLookup.getResult() == Lookup.TRY_AGAIN && retryCountLeft >= 0); } - if (lookup.getResult () != Lookup.SUCCESSFUL) { - // HOST_NOT_FOUND = The host does not exist - // TYPE_NOT_FOUND = The host exists, but has no records associated with the queried type - // Since we already tried couple of times with TRY_AGAIN for TCP and UDP, now giving up ... - if(lookup.getResult() == Lookup.HOST_NOT_FOUND || lookup.getResult() == Lookup.TRY_AGAIN - || lookup.getResult() == Lookup.TYPE_NOT_FOUND) { - throw new NotFoundException( - String.format("Identifier '%s' is not registered in SML.", participantIdentifier.toString())); - } else { - // Attribute to UNRECOVERABLE error, repeating the lookup would not be helpful - throw new LookupException( - String.format("Error when looking up identifier '%s' in SML.", participantIdentifier.toString())); + if (naptrLookup.getResult() != Lookup.SUCCESSFUL) { + switch (naptrLookup.getResult()) { + case Lookup.HOST_NOT_FOUND: // HOST_NOT_FOUND = The host does not exist + case Lookup.TYPE_NOT_FOUND: // TYPE_NOT_FOUND = The host exists, but has no records associated with the queried type + throw new NotFoundException(String.format("Identifier '%s' is not registered in SML.", participantIdentifier.getIdentifier())); + case Lookup.TRY_AGAIN: // Since we already tried a couple of times with TRY_AGAIN for TCP and UDP, now giving up ... + default: + throw new LookupException(String.format("Error when looking up identifier '%s' in SML. DNS-Lookup-Err: %s", participantIdentifier.getIdentifier(), naptrLookup.getErrorString())); } } diff --git a/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BusdoxLocator.java b/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BusdoxLocator.java index ddd45262..22716c5e 100644 --- a/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BusdoxLocator.java +++ b/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/BusdoxLocator.java @@ -24,22 +24,31 @@ import network.oxalis.vefa.peppol.lookup.api.NotFoundException; import network.oxalis.vefa.peppol.lookup.util.DynamicHostnameGenerator; import network.oxalis.vefa.peppol.mode.Mode; -import org.apache.commons.lang3.StringUtils; import org.xbill.DNS.ExtendedResolver; import org.xbill.DNS.Lookup; -import org.xbill.DNS.SimpleResolver; import org.xbill.DNS.TextParseException; +import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; public class BusdoxLocator extends AbstractLocator { - private long timeout = 30L; + private long timeout = 20L; private int maxRetries = 3; - private DynamicHostnameGenerator hostnameGenerator; + private static final List customDNSServers = new ArrayList<>(); + //Google DNS: faster, supported by multiple data centers all around the world + public static InetAddress GOOGLE_PRIMARY_DNS; + public static InetAddress GOOGLE_SECONDARY_DNS; + //Cloudflare DNS: internet’s fastest DNS directory + public static InetAddress CLOUDFLARE_PRIMARY_DNS; + public static InetAddress CLOUDFLARE_SECONDARY_DNS; + + private final DynamicHostnameGenerator hostnameGenerator; public BusdoxLocator(Mode mode) { this( @@ -49,6 +58,22 @@ public BusdoxLocator(Mode mode) { ); maxRetries = Integer.parseInt(mode.getString("lookup.locator.busdox.maxRetries")); timeout = Long.parseLong(mode.getString("lookup.locator.busdox.timeout")); + + try { + GOOGLE_PRIMARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (8 & 0xff), (byte) (8 & 0xff), (byte) (8 & 0xff), (byte) (8 & 0xff)})); + GOOGLE_SECONDARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (8 & 0xff), (byte) (8 & 0xff), (byte) (4 & 0xff), (byte) (4 & 0xff)})); + + CLOUDFLARE_PRIMARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (1 & 0xff), (byte) (1 & 0xff), (byte) (1 & 0xff), (byte) (1 & 0xff)})); + CLOUDFLARE_SECONDARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (1 & 0xff), (byte) (0 & 0xff), (byte) (0 & 0xff), (byte) (1 & 0xff)})); +// CLOUDFLARE_SECONDARY_DNS = InetAddress.getByAddress((new byte[]{(byte) (1 & 0xff), (byte) (0), (byte) (0), (byte) (1 & 0xff)})); + } catch (UnknownHostException e) { + //Unable to initialize Custom DNS server Primary DNS lookup fail, now try with default resolver + } + + customDNSServers.add(GOOGLE_PRIMARY_DNS); + customDNSServers.add(GOOGLE_SECONDARY_DNS); + customDNSServers.add(CLOUDFLARE_PRIMARY_DNS); + customDNSServers.add(CLOUDFLARE_SECONDARY_DNS); } @SuppressWarnings("unused") @@ -66,51 +91,39 @@ public URI lookup(ParticipantIdentifier participantIdentifier) throws LookupExce String hostname = hostnameGenerator.generate(participantIdentifier); try { - final Lookup lookup = new Lookup(hostname); + ExtendedResolver extendedResolver = CustomExtendedDNSResolver.createExtendedResolver(customDNSServers, timeout, maxRetries); + extendedResolver.setRetries(maxRetries); + extendedResolver.setTimeout(Duration.ofSeconds(timeout)); - ExtendedResolver extendedResolver = new ExtendedResolver(); - try { - if (StringUtils.isNotBlank(hostname)) { - extendedResolver.addResolver(new SimpleResolver(hostname)); - } - } catch (final UnknownHostException ex) { - //Primary DNS lookup fail, now try with default resolver - } - extendedResolver.addResolver (Lookup.getDefaultResolver ()); - extendedResolver.setRetries (maxRetries); - extendedResolver.setTimeout (Duration.ofSeconds (timeout)); - lookup.setResolver (extendedResolver); + final Lookup lookup = new Lookup(hostname); + lookup.setResolver(extendedResolver); int retryCountLeft = maxRetries; // Retry = The lookup may fail due to a network error. Repeating the lookup might be helpful do { lookup.run(); --retryCountLeft; - } while (lookup.getResult () == Lookup.TRY_AGAIN && retryCountLeft >= 0); + } while (lookup.getResult() == Lookup.TRY_AGAIN && retryCountLeft >= 0); // Retry with TCP as well - if (lookup.getResult () == Lookup.TRY_AGAIN) { - extendedResolver.setTCP (true); + if (lookup.getResult() == Lookup.TRY_AGAIN) { + extendedResolver.setTCP(true); retryCountLeft = maxRetries; do { lookup.run(); --retryCountLeft; - } while (lookup.getResult () == Lookup.TRY_AGAIN && retryCountLeft >= 0); + } while (lookup.getResult() == Lookup.TRY_AGAIN && retryCountLeft >= 0); } - if (lookup.getResult () != Lookup.SUCCESSFUL) { - // HOST_NOT_FOUND = The host does not exist - // TYPE_NOT_FOUND = The host exists, but has no records associated with the queried type - // Since we already tried couple of times with TRY_AGAIN for TCP and UDP, now giving up ... - if(lookup.getResult() == Lookup.HOST_NOT_FOUND || lookup.getResult() == Lookup.TRY_AGAIN - || lookup.getResult() == Lookup.TYPE_NOT_FOUND) { - throw new NotFoundException( - String.format("Identifier '%s' is not registered in SML.", participantIdentifier.getIdentifier())); - } else { - // Attribute to UNRECOVERABLE error, repeating the lookup would not be helpful - throw new LookupException( - String.format("Error when looking up identifier '%s' in SML.", participantIdentifier.getIdentifier())); + if (lookup.getResult() != Lookup.SUCCESSFUL) { + switch (lookup.getResult()) { + case Lookup.HOST_NOT_FOUND: // HOST_NOT_FOUND = The host does not exist + case Lookup.TYPE_NOT_FOUND: // TYPE_NOT_FOUND = The host exists, but has no records associated with the queried type + throw new NotFoundException(String.format("Identifier '%s' is not registered in SML.", participantIdentifier.getIdentifier())); + case Lookup.TRY_AGAIN: // Since we already tried a couple of times with TRY_AGAIN for TCP and UDP, now giving up ... + default: + throw new LookupException(String.format("Error when looking up identifier '%s' in SML. DNS-Lookup-Err: %s", participantIdentifier.getIdentifier(), lookup.getErrorString())); } } } catch (TextParseException e) { diff --git a/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/CustomExtendedDNSResolver.java b/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/CustomExtendedDNSResolver.java new file mode 100644 index 00000000..1990ec1b --- /dev/null +++ b/peppol-lookup/src/main/java/network/oxalis/vefa/peppol/lookup/locator/CustomExtendedDNSResolver.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015-2017 Direktoratet for forvaltning og IKT + * + * This source code is subject to dual licensing: + * + * + * Licensed under the EUPL, Version 1.1 or – as soon they + * will be approved by the European Commission - subsequent + * versions of the EUPL (the "Licence"); + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * + * See the Licence for the specific language governing + * permissions and limitations under the Licence. + */ + +package network.oxalis.vefa.peppol.lookup.locator; + +import org.xbill.DNS.ExtendedResolver; +import org.xbill.DNS.Resolver; +import org.xbill.DNS.ResolverConfig; +import org.xbill.DNS.SimpleResolver; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class CustomExtendedDNSResolver { + + private CustomExtendedDNSResolver() { + } + + public static void setCustomizeResolverTimeout(Resolver resolver, long timeout) { + // Set query timeout + resolver.setTimeout(Duration.ofSeconds(timeout)); + } + + public static void setCustomizeResolverTimeoutAndMaxRetries(ExtendedResolver extendedResolver, long timeout, int maxRetries) { + setCustomizeResolverTimeout(extendedResolver, timeout); + // Set the default retries + extendedResolver.setRetries(maxRetries); + } + + public static void iterateEachDefaultResolverAndConfigure(Consumer consumer, long timeout) { + for (final InetSocketAddress inetSocketAddress : ResolverConfig.getCurrentConfig().servers()) + if (inetSocketAddress != null) { + SimpleResolver simpleResolver = new SimpleResolver(inetSocketAddress); + setCustomizeResolverTimeout(simpleResolver, timeout); + consumer.accept(simpleResolver); + } + } + + public static void iterateEachResolverAndConfigure(Iterable customDNSServersAddress, + Consumer aConsumer, long timeout) { + if (customDNSServersAddress != null) + for (final InetAddress inetAddress : customDNSServersAddress) + if (inetAddress != null) { + // Use the default port + final SimpleResolver simpleResolver = new SimpleResolver(inetAddress); + setCustomizeResolverTimeout(simpleResolver, timeout); + aConsumer.accept(simpleResolver); + } + } + + private static boolean isResolverAlreadyAvailable(List resolverList, SimpleResolver simpleResolver) { + final InetSocketAddress aSearchAddr = simpleResolver.getAddress(); + + // Use Stream API to check if any resolver has the same address + return resolverList.stream() + .filter(x -> x instanceof SimpleResolver) + .map(x -> (SimpleResolver) x) + .anyMatch(x -> x.getAddress().equals(aSearchAddr)); + } + + public static ExtendedResolver createExtendedResolver(final Iterable customDNSServersAddress, long timeout, int maxRetries) { + List resolverList = new ArrayList<>(); + + iterateEachResolverAndConfigure(customDNSServersAddress, x -> { + if (!isResolverAlreadyAvailable(resolverList, x)) + resolverList.add(x); + }, timeout); + + iterateEachDefaultResolverAndConfigure(x -> { + if (!isResolverAlreadyAvailable(resolverList, x)) + resolverList.add(x); + }, timeout); + + ExtendedResolver extendedResolver = new ExtendedResolver(resolverList); + setCustomizeResolverTimeoutAndMaxRetries(extendedResolver, timeout, maxRetries); + return extendedResolver; + } +} + +