001/*
002 * Copyright 2015-2024 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.util;
012
013import java.net.Inet4Address;
014import java.net.Inet6Address;
015import java.net.InetAddress;
016import java.net.UnknownHostException;
017import java.util.regex.Pattern;
018
019import org.minidns.dnsname.DnsName;
020
021public class InetAddressUtil {
022
023    public static Inet4Address ipv4From(CharSequence cs) {
024        InetAddress inetAddress;
025        try {
026            inetAddress = InetAddress.getByName(cs.toString());
027        } catch (UnknownHostException e) {
028            throw new IllegalArgumentException(e);
029        }
030        if (inetAddress instanceof Inet4Address) {
031            return (Inet4Address) inetAddress;
032        }
033        throw new IllegalArgumentException();
034    }
035
036    public static Inet6Address ipv6From(CharSequence cs) {
037        InetAddress inetAddress;
038        try {
039            inetAddress = InetAddress.getByName(cs.toString());
040        } catch (UnknownHostException e) {
041            throw new IllegalArgumentException(e);
042        }
043        if (inetAddress instanceof Inet6Address) {
044            return (Inet6Address) inetAddress;
045        }
046        throw new IllegalArgumentException();
047    }
048
049    // IPV4_REGEX from http://stackoverflow.com/a/46168/194894 by Kevin Wong (http://stackoverflow.com/users/4792/kevin-wong) licensed under
050    // CC BY-SA 3.0.
051    private static final Pattern IPV4_PATTERN = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
052
053    public static boolean isIpV4Address(CharSequence address) {
054        if (address == null) {
055            return false;
056        }
057        return IPV4_PATTERN.matcher(address).matches();
058    }
059
060    // IPv6 Regular Expression from http://stackoverflow.com/a/17871737/194894 by David M. Syzdek
061    // (http://stackoverflow.com/users/903194/david-m-syzdek) licensed under CC BY-SA 3.0.
062    private static final Pattern IPV6_PATTERN = Pattern.compile(
063            "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))");
064
065    public static boolean isIpV6Address(CharSequence address) {
066        if (address == null) {
067            return false;
068        }
069        return IPV6_PATTERN.matcher(address).matches();
070    }
071
072    public static boolean isIpAddress(CharSequence address) {
073        return isIpV6Address(address) || isIpV4Address(address);
074    }
075
076    public static InetAddress convertToInetAddressIfPossible(CharSequence address) {
077        if (!isIpAddress(address)) {
078            return null;
079        }
080
081        String addressString = address.toString();
082        try {
083            return InetAddress.getByName(addressString);
084        } catch (UnknownHostException e) {
085            // Should never happen.
086            throw new AssertionError(e);
087        }
088    }
089
090    public static DnsName reverseIpAddressOf(Inet6Address inet6Address) {
091        final String ipAddress = inet6Address.getHostAddress();
092        final String[] ipAddressParts = ipAddress.split(":");
093
094        String[] parts = new String[32];
095        int currentPartNum = 0;
096        for (int i = ipAddressParts.length - 1; i >= 0; i--) {
097            final String currentPart = ipAddressParts[i];
098            final int missingPlaces = 4 - currentPart.length();
099            for (int j = 0; j < missingPlaces; j++) {
100                parts[currentPartNum++] = "0";
101            }
102            for (int j = 0; j < currentPart.length(); j++) {
103                parts[currentPartNum++] = Character.toString(currentPart.charAt(j));
104            }
105        }
106
107        return DnsName.from(parts);
108    }
109
110    public static DnsName reverseIpAddressOf(Inet4Address inet4Address) {
111        final String[] ipAddressParts = inet4Address.getHostAddress().split("\\.");
112        assert ipAddressParts.length == 4;
113
114        return DnsName.from(ipAddressParts);
115    }
116}