001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import java.net.Inet4Address;
021import java.net.Inet6Address;
022import java.net.InetAddress;
023import java.net.InetSocketAddress;
024import java.net.NetworkInterface;
025import java.net.SocketException;
026import java.util.Enumeration;
027import org.apache.yetus.audience.InterfaceAudience;
028
029/**
030 * Utility for network addresses, resolving and naming.
031 */
032@InterfaceAudience.Private
033public class Addressing {
034  public static final String VALID_PORT_REGEX = "[\\d]+";
035  public static final String HOSTNAME_PORT_SEPARATOR = ":";
036
037  /**
038   * Create a socket address
039   * @param hostAndPort Formatted as <code>&lt;hostname&gt; ':' &lt;port&gt;</code>
040   * @return An InetSocketInstance
041   */
042  public static InetSocketAddress
043    createInetSocketAddressFromHostAndPortStr(final String hostAndPort) {
044    return new InetSocketAddress(parseHostname(hostAndPort), parsePort(hostAndPort));
045  }
046
047  /**
048   * Create a host-and-port string
049   * @param hostname Server hostname
050   * @param port     Server port
051   * @return Returns a concatenation of <code>hostname</code> and <code>port</code> in following
052   *         form: <code>&lt;hostname&gt; ':' &lt;port&gt;</code>. For example, if hostname is
053   *         <code>example.org</code> and port is 1234, this method will return
054   *         <code>example.org:1234</code>
055   */
056  public static String createHostAndPortStr(final String hostname, final int port) {
057    return hostname + HOSTNAME_PORT_SEPARATOR + port;
058  }
059
060  /**
061   * Parse the hostname portion of a host-and-port string
062   * @param hostAndPort Formatted as <code>&lt;hostname&gt; ':' &lt;port&gt;</code>
063   * @return The hostname portion of <code>hostAndPort</code>
064   */
065  public static String parseHostname(final String hostAndPort) {
066    int colonIndex = hostAndPort.lastIndexOf(HOSTNAME_PORT_SEPARATOR);
067    if (colonIndex < 0) {
068      throw new IllegalArgumentException("Not a host:port pair: " + hostAndPort);
069    }
070    return hostAndPort.substring(0, colonIndex);
071  }
072
073  /**
074   * Parse the port portion of a host-and-port string
075   * @param hostAndPort Formatted as <code>&lt;hostname&gt; ':' &lt;port&gt;</code>
076   * @return The port portion of <code>hostAndPort</code>
077   */
078  public static int parsePort(final String hostAndPort) {
079    int colonIndex = hostAndPort.lastIndexOf(HOSTNAME_PORT_SEPARATOR);
080    if (colonIndex < 0) {
081      throw new IllegalArgumentException("Not a host:port pair: " + hostAndPort);
082    }
083    return Integer.parseInt(hostAndPort.substring(colonIndex + 1));
084  }
085
086  public static InetAddress getIpAddress() throws SocketException {
087    return getIpAddress(new AddressSelectionCondition() {
088      @Override
089      public boolean isAcceptableAddress(InetAddress addr) {
090        return addr instanceof Inet4Address || addr instanceof Inet6Address;
091      }
092    });
093  }
094
095  public static InetAddress getIp4Address() throws SocketException {
096    return getIpAddress(new AddressSelectionCondition() {
097      @Override
098      public boolean isAcceptableAddress(InetAddress addr) {
099        return addr instanceof Inet4Address;
100      }
101    });
102  }
103
104  public static InetAddress getIp6Address() throws SocketException {
105    return getIpAddress(new AddressSelectionCondition() {
106      @Override
107      public boolean isAcceptableAddress(InetAddress addr) {
108        return addr instanceof Inet6Address;
109      }
110    });
111  }
112
113  private static InetAddress getIpAddress(AddressSelectionCondition condition)
114    throws SocketException {
115    // Before we connect somewhere, we cannot be sure about what we'd be bound to; however,
116    // we only connect when the message where client ID is, is long constructed. Thus,
117    // just use whichever IP address we can find.
118    Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
119    while (interfaces.hasMoreElements()) {
120      NetworkInterface current = interfaces.nextElement();
121      if (!current.isUp() || current.isLoopback() || current.isVirtual()) continue;
122      Enumeration<InetAddress> addresses = current.getInetAddresses();
123      while (addresses.hasMoreElements()) {
124        InetAddress addr = addresses.nextElement();
125        if (addr.isLoopbackAddress()) continue;
126        if (condition.isAcceptableAddress(addr)) {
127          return addr;
128        }
129      }
130    }
131
132    throw new SocketException("Can't get our ip address, interfaces are: " + interfaces);
133  }
134
135  /**
136   * Given an InetAddress, checks to see if the address is a local address, by comparing the address
137   * with all the interfaces on the node.
138   * @param addr address to check if it is local node's address
139   * @return true if the address corresponds to the local node
140   */
141  public static boolean isLocalAddress(InetAddress addr) {
142    // Check if the address is any local or loop back
143    boolean local = addr.isAnyLocalAddress() || addr.isLoopbackAddress();
144
145    // Check if the address is defined on any interface
146    if (!local) {
147      try {
148        local = NetworkInterface.getByInetAddress(addr) != null;
149      } catch (SocketException e) {
150        local = false;
151      }
152    }
153    return local;
154  }
155
156  /**
157   * Given an InetSocketAddress object returns a String represent of it. This is a util method for
158   * Java 17. The toString() function of InetSocketAddress will flag the unresolved address with a
159   * substring in the string, which will result in unexpected problem. We should use this util
160   * function to get the string when we not sure whether the input address is resolved or not.
161   * @param address address to convert to a "host:port" String.
162   * @return the String represent of the given address, like "foo:1234".
163   */
164  public static String inetSocketAddress2String(InetSocketAddress address) {
165    return address.isUnresolved()
166      ? address.toString().replace("/<unresolved>", "")
167      : address.toString();
168  }
169
170  /** Interface for AddressSelectionCondition to check if address is acceptable */
171  public interface AddressSelectionCondition {
172    /**
173     * Condition on which to accept inet address
174     * @param address to check
175     * @return true to accept this address
176     */
177    public boolean isAcceptableAddress(InetAddress address);
178  }
179}