summaryrefslogtreecommitdiffstats
path: root/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs')
-rw-r--r--src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs b/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs
new file mode 100644
index 0000000..a9961d2
--- /dev/null
+++ b/src/main.lib/Plugins/ValidationPlugins/Dns/DnsValidation.cs
@@ -0,0 +1,117 @@
+using ACMESharp.Authorizations;
+using PKISharp.WACS.Clients.DNS;
+using PKISharp.WACS.Services;
+using Serilog.Context;
+using System;
+using System.Linq;
+using System.Net;
+using System.Threading;
+
+namespace PKISharp.WACS.Plugins.ValidationPlugins
+{
+ /// <summary>
+ /// Base implementation for DNS-01 validation plugins
+ /// </summary>
+ public abstract class DnsValidation<TOptions, TPlugin> : Validation<TOptions, Dns01ChallengeValidationDetails>
+ {
+ protected LookupClientProvider _dnsClientProvider { get; private set; }
+
+ protected DnsValidation(LookupClientProvider dnsClient, ILogService logService, TOptions options, string identifier) :
+ base(logService, options, identifier)
+ {
+ _dnsClientProvider = dnsClient;
+ }
+
+ public override void PrepareChallenge()
+ {
+ CreateRecord(_challenge.DnsRecordName, _challenge.DnsRecordValue);
+ _log.Information("Answer should now be available at {answerUri}", _challenge.DnsRecordName);
+
+ // Verify that the record was created succesfully and wait for possible
+ // propagation/caching/TTL issues to resolve themselves naturally
+ var retry = 0;
+ var maxRetries = 5;
+ var retrySeconds = 30;
+ while (true)
+ {
+ if (PreValidate(retry))
+ {
+ break;
+ }
+ else
+ {
+ retry += 1;
+ if (retry > maxRetries)
+ {
+ _log.Information("It looks like validation is going to fail, but we will try now anyway...");
+ break;
+ }
+ else
+ {
+ _log.Information("Will retry in {s} seconds (retry {i}/{j})...", retrySeconds, retry, maxRetries);
+ Thread.Sleep(retrySeconds * 1000);
+ }
+ }
+ }
+ }
+
+ protected bool PreValidate(int attempt)
+ {
+ try
+ {
+ var dnsClient = _dnsClientProvider.GetClient(_challenge.DnsRecordName, attempt);
+ if (dnsClient.LookupClient.UseRandomNameServer)
+ {
+ using (LogContext.PushProperty("NameServerIpAddresses", dnsClient.LookupClient.NameServers.Select(ns => ns.Endpoint.Address.ToString()), true))
+ {
+ _log.Debug("Using random name server");
+ }
+ }
+ var tokens = dnsClient.GetTextRecordValues(_challenge.DnsRecordName, attempt).ToList();
+ if (tokens.Contains(_challenge.DnsRecordValue))
+ {
+ _log.Information("Preliminary validation succeeded: {ExpectedTxtRecord} found in {TxtRecords}", _challenge.DnsRecordValue, String.Join(", ", tokens));
+ return true;
+ }
+ else if (!tokens.Any())
+ {
+ _log.Warning("Preliminary validation failed: no TXT records found");
+ }
+ else
+ {
+ _log.Warning("Preliminary validation failed: {ExpectedTxtRecord} not found in {TxtRecords}", _challenge.DnsRecordValue, String.Join(", ", tokens));
+ }
+ }
+ catch (Exception ex)
+ {
+ _log.Error(ex, "Preliminary validation failed");
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Delete record when we're done
+ /// </summary>
+ public override void CleanUp()
+ {
+ if (_challenge != null)
+ {
+ DeleteRecord(_challenge.DnsRecordName, _challenge.DnsRecordValue);
+ }
+ }
+
+ /// <summary>
+ /// Delete validation record
+ /// </summary>
+ /// <param name="recordName">Name of the record</param>
+ public abstract void DeleteRecord(string recordName, string token);
+
+ /// <summary>
+ /// Create validation record
+ /// </summary>
+ /// <param name="recordName">Name of the record</param>
+ /// <param name="token">Contents of the record</param>
+ public abstract void CreateRecord(string recordName, string token);
+
+ }
+}