summaryrefslogtreecommitdiffhomepage
path: root/tunnel/src/main/java/com/wireguard/config/InetAddresses.java
blob: c1305e843a6382a588f84b83636813737ccac653 (plain)
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
/*
 * Copyright © 2017-2021 WireGuard LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package com.wireguard.config;

import com.wireguard.util.NonNullForAll;

import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Pattern;

import androidx.annotation.Nullable;

/**
 * Utility methods for creating instances of {@link InetAddress}.
 */
@NonNullForAll
public final class InetAddresses {
    @Nullable private static final Method PARSER_METHOD;
    private static final Pattern WONT_TOUCH_RESOLVER = Pattern.compile("^(((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?)|((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$");
    private static final Pattern VALID_HOSTNAME = Pattern.compile("^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\\.?$");

    static {
        Method m = null;
        try {
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q)
                // noinspection JavaReflectionMemberAccess
                m = InetAddress.class.getMethod("parseNumericAddress", String.class);
        } catch (final Exception ignored) {
        }
        PARSER_METHOD = m;
    }

    private InetAddresses() {
    }

    /**
     * Determines whether input is a valid DNS hostname.
     *
     * @param maybeHostname a string that is possibly a DNS hostname
     * @return whether or not maybeHostname is a valid DNS hostname
     */
    public static boolean isHostname(final CharSequence maybeHostname) {
        return VALID_HOSTNAME.matcher(maybeHostname).matches();
    }

    /**
     * Parses a numeric IPv4 or IPv6 address without performing any DNS lookups.
     *
     * @param address a string representing the IP address
     * @return an instance of {@link Inet4Address} or {@link Inet6Address}, as appropriate
     */
    public static InetAddress parse(final String address) throws ParseException {
        if (address.isEmpty())
            throw new ParseException(InetAddress.class, address, "Empty address");
        try {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q)
                return android.net.InetAddresses.parseNumericAddress(address);
            else if (PARSER_METHOD != null)
                return (InetAddress) PARSER_METHOD.invoke(null, address);
            else
                throw new NoSuchMethodException("parseNumericAddress");
        } catch (final IllegalArgumentException e) {
            throw new ParseException(InetAddress.class, address, e);
        } catch (final Exception e) {
            final Throwable cause = e.getCause();
            // Re-throw parsing exceptions with the original type, as callers might try to catch
            // them. On the other hand, callers cannot be expected to handle reflection failures.
            if (cause instanceof IllegalArgumentException)
                throw new ParseException(InetAddress.class, address, cause);
            try {
                if (WONT_TOUCH_RESOLVER.matcher(address).matches())
                    return InetAddress.getByName(address);
                else
                    throw new ParseException(InetAddress.class, address, "Not an IP address");
            } catch (final UnknownHostException f) {
                throw new ParseException(InetAddress.class, address, f);
            }
        }
    }
}