View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.net;
29  
30  import java.net.InetAddress;
31  import java.net.InetSocketAddress;
32  import java.net.SocketAddress;
33  import java.net.UnknownHostException;
34  import java.util.regex.Pattern;
35  
36  import org.apache.hc.core5.util.Args;
37  
38  /**
39   * A collection of utilities relating to InetAddresses.
40   *
41   * @since 4.0
42   */
43  public class InetAddressUtils {
44  
45      private InetAddressUtils() {
46      }
47  
48      private static final String IPV4_BASIC_PATTERN_STRING =
49              "(([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){1}" + // initial first field, 1-255
50              "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){2}" + // following 2 fields, 0-255 followed by .
51               "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255
52  
53      private static final Pattern IPV4_PATTERN =
54          Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING + "$");
55  
56      private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros
57              Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING + "$");
58  
59      private static final Pattern IPV6_STD_PATTERN =
60          Pattern.compile(
61                  "^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$");
62  
63      private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
64          Pattern.compile(
65                  "^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" + // 0-6 hex fields
66                   "::" +
67                   "(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields
68  
69      /*
70       *  The above pattern is not totally rigorous as it allows for more than 7 hex fields in total
71       */
72      private static final char COLON_CHAR = ':';
73  
74      // Must not have more than 7 colons (i.e. 8 fields)
75      private static final int MAX_COLON_COUNT = 7;
76  
77      /**
78       * Checks whether the parameter is a valid IPv4 address
79       *
80       * @param input the address string to check for validity
81       * @return true if the input parameter is a valid IPv4 address
82       */
83      public static boolean isIPv4Address(final String input) {
84          return IPV4_PATTERN.matcher(input).matches();
85      }
86  
87      public static boolean isIPv4MappedIPv64Address(final String input) {
88          return IPV4_MAPPED_IPV6_PATTERN.matcher(input).matches();
89      }
90  
91      /**
92       * Checks whether the parameter is a valid standard (non-compressed) IPv6 address
93       *
94       * @param input the address string to check for validity
95       * @return true if the input parameter is a valid standard (non-compressed) IPv6 address
96       */
97      public static boolean isIPv6StdAddress(final String input) {
98          return IPV6_STD_PATTERN.matcher(input).matches();
99      }
100 
101     /**
102      * Checks whether the parameter is a valid compressed IPv6 address
103      *
104      * @param input the address string to check for validity
105      * @return true if the input parameter is a valid compressed IPv6 address
106      */
107     public static boolean isIPv6HexCompressedAddress(final String input) {
108         int colonCount = 0;
109         for(int i = 0; i < input.length(); i++) {
110             if (input.charAt(i) == COLON_CHAR) {
111                 colonCount++;
112             }
113         }
114         return  colonCount <= MAX_COLON_COUNT && IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
115     }
116 
117     /**
118      * Checks whether the parameter is a valid IPv6 address (including compressed).
119      *
120      * @param input the address string to check for validity
121      * @return true if the input parameter is a valid standard or compressed IPv6 address
122      */
123     public static boolean isIPv6Address(final String input) {
124         return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
125     }
126 
127     /**
128      * Formats {@link SocketAddress} as text.
129      *
130      * @since 5.0
131      */
132     public static void formatAddress(
133             final StringBuilder buffer,
134             final SocketAddress socketAddress) {
135         Args.notNull(buffer, "buffer");
136         if (socketAddress instanceof InetSocketAddress) {
137             final InetSocketAddress socketaddr = (InetSocketAddress) socketAddress;
138             final InetAddress inetaddr = socketaddr.getAddress();
139             if (inetaddr != null) {
140                 buffer.append(inetaddr.getHostAddress()).append(':').append(socketaddr.getPort());
141             } else {
142                 buffer.append(socketAddress);
143             }
144         } else {
145             buffer.append(socketAddress);
146         }
147     }
148 
149     /**
150      * Returns canonical name (fully qualified domain name) of the localhost.
151      *
152      * @since 5.0
153      */
154     public static String getCanonicalLocalHostName() {
155         try {
156             final InetAddress localHost = InetAddress.getLocalHost();
157             return localHost.getCanonicalHostName();
158         } catch (final UnknownHostException ex) {
159             return "localhost";
160         }
161     }
162 
163 }