X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=src%2Fclub%2Fwpia%2Fgigi%2Fping%2FPingerDaemon.java;h=dee4e90c8d47f18278e7cbfde4c219647fa2c173;hp=51cc985838b23cd318274e4dcdddbbefcf13ca86;hb=39c7c20aa234235e4285e013e59164ab093a1de1;hpb=8543d1c7ae576fdbf13c6926be26cf323f3f75ea diff --git a/src/club/wpia/gigi/ping/PingerDaemon.java b/src/club/wpia/gigi/ping/PingerDaemon.java index 51cc9858..dee4e90c 100644 --- a/src/club/wpia/gigi/ping/PingerDaemon.java +++ b/src/club/wpia/gigi/ping/PingerDaemon.java @@ -1,18 +1,38 @@ package club.wpia.gigi.ping; +import java.io.IOException; +import java.security.GeneralSecurityException; import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.sql.Timestamp; +import java.util.Date; import java.util.HashMap; import java.util.LinkedList; +import java.util.Locale; +import java.util.Map; import java.util.Queue; +import club.wpia.gigi.GigiApiException; import club.wpia.gigi.database.DatabaseConnection; import club.wpia.gigi.database.DatabaseConnection.Link; import club.wpia.gigi.database.GigiPreparedStatement; import club.wpia.gigi.database.GigiResultSet; +import club.wpia.gigi.dbObjects.Certificate; +import club.wpia.gigi.dbObjects.Certificate.RevocationType; +import club.wpia.gigi.dbObjects.CertificateOwner; import club.wpia.gigi.dbObjects.Domain; import club.wpia.gigi.dbObjects.DomainPingConfiguration; +import club.wpia.gigi.dbObjects.DomainPingExecution; import club.wpia.gigi.dbObjects.DomainPingType; -import club.wpia.gigi.util.RandomToken; +import club.wpia.gigi.dbObjects.Organisation; +import club.wpia.gigi.dbObjects.User; +import club.wpia.gigi.localisation.Language; +import club.wpia.gigi.output.ArrayIterable; +import club.wpia.gigi.output.template.MailTemplate; +import club.wpia.gigi.pages.account.domain.EditDomain; +import club.wpia.gigi.ping.DomainPinger.PingState; +import club.wpia.gigi.util.ServerConstants; +import club.wpia.gigi.util.ServerConstants.Host; public class PingerDaemon extends Thread { @@ -22,6 +42,8 @@ public class PingerDaemon extends Thread { private Queue toExecute = new LinkedList<>(); + private final MailTemplate pingFailedMail = new MailTemplate(PingerDaemon.class.getResource("PingFailedWithActiveCertificates.templ")); + public PingerDaemon(KeyStore truststore) { this.truststore = truststore; } @@ -37,10 +59,7 @@ public class PingerDaemon extends Thread { public void runWithConnection() { - pingers.put(DomainPingType.EMAIL, new EmailPinger()); - pingers.put(DomainPingType.SSL, new SSLPinger(truststore)); - pingers.put(DomainPingType.HTTP, new HTTPFetch()); - pingers.put(DomainPingType.DNS, new DNSPinger()); + initializeConnectionUsage(); while (true) { try { @@ -54,22 +73,8 @@ public class PingerDaemon extends Thread { } notifyAll(); } - try (GigiPreparedStatement searchNeededPings = new GigiPreparedStatement("SELECT `pc`.`id`" // - + " FROM `pingconfig` AS `pc`" // - + " INNER JOIN `domains` AS `d` ON `d`.`id` = `pc`.`domainid`" // - + " WHERE `d`.`deleted` IS NULL" // - + " AND `pc`.`deleted` IS NULL" // - + " AND NOT EXISTS (" // - + " SELECT 1 FROM `domainPinglog` AS `dpl`" // - + " WHERE `dpl`.`configId` = `pc`.`id`" // - + " AND `dpl`.`when` >= CURRENT_TIMESTAMP - interval '6 mons')")) { - - GigiResultSet rs = searchNeededPings.executeQuery(); - while (rs.next()) { - worked = true; - handle(DomainPingConfiguration.getById(rs.getInt("id"))); - } - } + long time = System.currentTimeMillis(); + worked |= executeNeededPings(new Date(time)); try { if ( !worked) { Thread.sleep(5000); @@ -86,23 +91,144 @@ public class PingerDaemon extends Thread { } } - private void handle(DomainPingConfiguration conf) { + protected void initializeConnectionUsage() { + pingers.put(DomainPingType.EMAIL, new EmailPinger()); + pingers.put(DomainPingType.SSL, new SSLPinger(truststore)); + pingers.put(DomainPingType.HTTP, new HTTPFetch()); + pingers.put(DomainPingType.DNS, new DNSPinger()); + } + + public synchronized boolean executeNeededPings(Date time) { + boolean worked = false; + try (GigiPreparedStatement searchNeededPings = new GigiPreparedStatement("SELECT `d`.`id`, `dpl`.`configId`, `dpl`.`when`," // + // .. for all found pings we want to know, if we do not have a more + // recent successful ping + + " NOT EXISTS (" // + + " SELECT 1 FROM `domainPinglog` AS `dpl2`" // + + " WHERE `dpl`.`configId` = `dpl2`.`configId`" // + + " AND `dpl2`.state = 'success' AND `dpl2`.`when` > `dpl`.`when`) AS `resucceeded`" // + + " FROM `domainPinglog` AS `dpl`" // + // We search valid pings + + " INNER JOIN `pingconfig` AS `pc` ON `pc`.`id` = `dpl`.`configId` AND `pc`.`deleted` IS NULL" // + + " INNER JOIN `domains` AS `d` ON `d`.`id` = `pc`.`domainid` AND `d`.`deleted` IS NULL" // + // .. that failed, .. + + " WHERE `dpl`.`state` = 'failed'" // + // .. are older than 2 weeks + + " AND `dpl`.`when` <= ?::timestamp - interval '2 weeks'" // + // .. and are flagged for corrective action + + " AND `dpl`.`needsAction`" // + )) { + searchNeededPings.setTimestamp(1, new Timestamp(time.getTime())); + GigiResultSet rs = searchNeededPings.executeQuery(); + try (GigiPreparedStatement updateDone = new GigiPreparedStatement("UPDATE `domainPinglog` SET `needsAction`=false WHERE `configId`=? AND `when`=?")) { + while (rs.next()) { + worked = true; + // Give this ping a last chance to succeed. + handle(DomainPingConfiguration.getById(rs.getInt(2))); + // We only consider revoking if this ping has not been + // superseded by a following successful ping. + if (rs.getBoolean(4)) { + Domain d = Domain.getById(rs.getInt(1)); + int ct = 0; + boolean[] used = new boolean[DomainPingType.values().length]; + // We only revoke, there are not 2 pings that are not + // 'strictly invalid' + for (DomainPingConfiguration cfg : d.getConfiguredPings()) { + if ( !cfg.isStrictlyInvalid(time) && !used[cfg.getType().ordinal()]) { + ct++; + used[cfg.getType().ordinal()] = true; + } + if (ct >= 2) { + break; + } + } + if (ct < 2) { + for (Certificate c : d.fetchActiveCertificates()) { + // TODO notify user + c.revoke(RevocationType.PING_TIMEOUT); + } + } + } + updateDone.setInt(1, rs.getInt(2)); + updateDone.setTimestamp(2, rs.getTimestamp(3)); + updateDone.executeUpdate(); + } + } + } + try (GigiPreparedStatement searchNeededPings = new GigiPreparedStatement("SELECT `pc`.`id`" // + + " FROM `pingconfig` AS `pc`" // + + " INNER JOIN `domains` AS `d` ON `d`.`id` = `pc`.`domainid`" // + + " WHERE `d`.`deleted` IS NULL" // + + " AND `pc`.`deleted` IS NULL" // + + " AND NOT EXISTS (" // + + " SELECT 1 FROM `domainPinglog` AS `dpl`" // + + " WHERE `dpl`.`configId` = `pc`.`id`" // + + " AND `dpl`.`when` >= ?::timestamp - interval '6 mons')")) { + searchNeededPings.setTimestamp(1, new Timestamp(time.getTime())); + GigiResultSet rs = searchNeededPings.executeQuery(); + while (rs.next()) { + worked = true; + handle(DomainPingConfiguration.getById(rs.getInt("id"))); + } + } + return worked; + } + + protected void handle(DomainPingConfiguration conf) { DomainPingType type = conf.getType(); String config = conf.getInfo(); DomainPinger dp = pingers.get(type); if (dp != null) { - if (dp instanceof EmailPinger) { - String token = null; - token = RandomToken.generateToken(16); - config = config + ":" + token; - } Domain target = conf.getTarget(); System.err.println("Executing " + dp + " on " + target + " (" + System.currentTimeMillis() + ")"); try { - dp.ping(target, config, target.getOwner(), conf.getId()); + DomainPingExecution x = dp.ping(target, config, target.getOwner(), conf); + if (x.getState() == PingState.FAILED) { + Certificate[] cs = target.fetchActiveCertificates(); + if (cs.length != 0) { + CertificateOwner o = target.getOwner(); + Locale l = Locale.ENGLISH; + String contact; + if (o instanceof User) { + l = ((User) o).getPreferredLocale(); + contact = ((User) o).getEmail(); + } else if (o instanceof Organisation) { + contact = ((Organisation) o).getContactEmail(); + + } else { + throw new Error(); + } + HashMap vars = new HashMap<>(); + vars.put("valid", target.isVerified()); + vars.put("domain", target.getSuffix()); + vars.put("domainLink", "https://" + ServerConstants.getHostNamePortSecure(Host.WWW) + EditDomain.PATH + target.getId()); + vars.put("certs", new ArrayIterable(cs) { + + @Override + public void apply(Certificate t, Language l, Map vars) { + vars.put("serial", t.getSerial()); + vars.put("ca", t.getParent().getKeyname()); + try { + X509Certificate c = t.cert(); + vars.put("from", c.getNotBefore()); + vars.put("to", c.getNotAfter()); + } catch (IOException e) { + e.printStackTrace(); + } catch (GeneralSecurityException e) { + e.printStackTrace(); + } catch (GigiApiException e) { + e.printStackTrace(); + } + } + + }); + pingFailedMail.sendMail(Language.getInstance(l), vars, contact); + System.out.println("Ping failed with active certificates"); + } + } } catch (Throwable t) { t.printStackTrace(); - DomainPinger.enterPingResult(conf.getId(), "error", "exception", null); + DomainPinger.enterPingResult(conf, "error", "exception", null); } System.err.println("done (" + System.currentTimeMillis() + ")"); }