aboutsummaryrefslogtreecommitdiffstats
path: root/node-maintainer/src/main/java/com/yahoo/vespa/hosted/node/verification/commons/noderepo/IPAddressVerifier.java
blob: 4d62fe88c1ced5a07ae19ed4429d7180bb69740e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.verification.commons.noderepo;

import com.yahoo.vespa.hosted.node.verification.commons.report.SpecVerificationReport;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by olaa on 14/07/2017.
 * Verifies that the IP addresses of a node points to the correct hostname
 * 
 * @author olaaun
 * @author sgrostad
 */
public class IPAddressVerifier {

    private static final Logger logger = Logger.getLogger(IPAddressVerifier.class.getName());

    public void reportFaultyIpAddresses(NodeSpec nodeSpec, SpecVerificationReport specVerificationReport) {
        String[] faultyIpAddresses = getFaultyIpAddresses(nodeSpec);
        if (faultyIpAddresses.length > 0) {
            specVerificationReport.setFaultyIpAddresses(faultyIpAddresses);
        }
    }

    public String[] getFaultyIpAddresses(NodeSpec nodeSpec) {
        String expectedHostname = nodeSpec.getHostname();
        List<String> faultyIpAddresses = new ArrayList<>();
        if (expectedHostname == null || expectedHostname.equals(""))
            return new String[0];
        if (!isValidIpv4(nodeSpec.getIpv4Address(), expectedHostname)) {
            faultyIpAddresses.add(nodeSpec.getIpv4Address());
        }
        if (!isValidIpv6(nodeSpec.getIpv6Address(), expectedHostname)) {
            faultyIpAddresses.add(nodeSpec.getIpv6Address());
        }
        return faultyIpAddresses.stream().toArray(String[]::new);
    }

    private boolean isValidIpv4(String ipv4Address, String expectedHostname) {
        if (ipv4Address == null) {
            return true;
        }
        String ipv4LookupFormat = convertIpv4ToLookupFormat(ipv4Address);
        try {
            String ipv4Hostname = reverseLookUp(ipv4LookupFormat);
            return ipv4Hostname.equals(expectedHostname);
        } catch (NamingException e) {
            logger.log(Level.WARNING, "Could not get IPv4 hostname", e);
        }
        return false;
    }

    private boolean isValidIpv6(String ipv6Address, String expectedHostname) {
        if (ipv6Address == null) {
            return true;
        }
        String ipv6LookupFormat = convertIpv6ToLookupFormat(ipv6Address);
        try {
            String ipv6Hostname = reverseLookUp(ipv6LookupFormat);
            return ipv6Hostname.equals(expectedHostname);
        } catch (NamingException e) {
            logger.log(Level.WARNING, "Could not get IPv6 hostname", e);
        }
        return false;
    }

    protected String reverseLookUp(String ipAddress) throws NamingException {
        Hashtable<String, String> env = new Hashtable<>();
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        String attributeName = ipAddress;
        DirContext ctx = new InitialDirContext(env);
        Attributes attrs = ctx.getAttributes(attributeName, new String[]{"PTR"});
        for (NamingEnumeration<? extends Attribute> ae = attrs.getAll(); ae.hasMoreElements(); ) {
            Attribute attr = ae.next();
            Enumeration<?> vals = attr.getAll();
            if (vals.hasMoreElements()) {
                String hostname = vals.nextElement().toString();
                ctx.close();
                return hostname.substring(0, hostname.length() - 1);
            }
        }
        ctx.close();
        return "";
    }

    protected String convertIpv6ToLookupFormat(String ipAddress) {
        StringBuilder newIpAddress = new StringBuilder();
        String doubleColonReplacement = "0.0.0.0.0.0.0.0.0.0.0.0.";
        String domain = "ip6.arpa";
        String[] hextets = ipAddress.split(":");
        for (int i = hextets.length - 1; i >= 0; i--) {
            String reversedHextet = new StringBuilder(hextets[i]).reverse().toString();
            if (reversedHextet.equals("")) {
                newIpAddress.append(doubleColonReplacement);
                continue;
            }
            String trailingZeroes = "0000";
            String paddedHextet = (reversedHextet + trailingZeroes).substring(0, trailingZeroes.length());
            String punctuatedHextet = paddedHextet.replaceAll(".(?=)", "$0.");
            newIpAddress.append(punctuatedHextet);
        }
        newIpAddress.append(domain);
        return newIpAddress.toString();
    }

    protected String convertIpv4ToLookupFormat(String ipAddress) {
        String domain = "in-addr.arpa";
        String[] octets = ipAddress.split("\\.");
        StringBuilder convertedIpAddress = new StringBuilder();
        for (int i = octets.length - 1; i >= 0; i--) {
            convertedIpAddress.append(octets[i]).append(".");
        }
        convertedIpAddress.append(domain);
        return convertedIpAddress.toString();
    }

}