2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
57 package net.jxta.impl.endpoint;
60 import java.io.IOException;
61 import java.net.BindException;
62 import java.net.Inet6Address;
63 import java.net.InetAddress;
64 import java.net.InetSocketAddress;
65 import java.net.NetworkInterface;
66 import java.net.ServerSocket;
67 import java.net.Socket;
68 import java.net.SocketException;
69 import java.util.ArrayList;
70 import java.util.Collections;
71 import java.util.Enumeration;
72 import java.util.Iterator;
73 import java.util.List;
74 import java.util.Random;
75 import javax.net.ServerSocketFactory;
77 import java.util.logging.Level;
78 import net.jxta.logging.Logging;
79 import java.util.logging.Logger;
83 * Utility methods for use by IP based transports.
85 public final class IPUtils {
90 private final static Logger LOG = Logger.getLogger(IPUtils.class.getName());
92 final static Random random = new Random();
94 final static String IPV4ANYADDRESS = "0.0.0.0";
95 final static String IPV6ANYADDRESS = "::";
97 final static String IPV4LOOPBACK = "127.0.0.1";
98 final static String IPV6LOOPBACK = "::1";
101 * Constant which works as the IP "Any Address" value
103 public final static InetAddress ANYADDRESS;
104 public final static InetAddress ANYADDRESSV4;
105 public final static InetAddress ANYADDRESSV6;
108 * Constant which works as the IP "Local Loopback" value;
110 public final static InetAddress LOOPBACK;
111 public final static InetAddress LOOPBACKV4;
112 public final static InetAddress LOOPBACKV6;
115 * Socket factory to allow changing the way Sockets are created
116 * and connected. A null value is ok and results in the regular
117 * connectToFromNoFactory being used.
119 * <p/>Plugin in a different implementation via setSocketFactory().
121 private static SocketFactory socketFactory;
124 * Socket factory to allow changing the way Sockets are created
125 * and connected. A null value is ok and results in the regular
126 * connectToFromNoFactory being used.
128 * <p/>Plugin in a different implementation via setSocketFactory().
130 private static ServerSocketFactory serverSocketFactory;
133 InetAddress GET_ADDRESS = null;
136 GET_ADDRESS = InetAddress.getByName(IPV4ANYADDRESS);
137 } catch (Exception ignored) {
138 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
139 LOG.warning("failed to intialize ANYADDRESSV4. Not fatal");
143 ANYADDRESSV4 = GET_ADDRESS;
145 InetAddress GET_ANYADDRESSV6 = null;
149 GET_ADDRESS = InetAddress.getByName(IPV6ANYADDRESS);
150 } catch (Exception ignored) {
151 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
152 LOG.warning("failed to intialize IPV6ANYADDRESS. Not fatal");
156 ANYADDRESSV6 = GET_ADDRESS;
158 ANYADDRESS = (ANYADDRESSV4 == null) ? ANYADDRESSV6 : ANYADDRESSV4;
162 GET_ADDRESS = InetAddress.getByName(IPV4LOOPBACK);
163 } catch (Exception ignored) {
164 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
165 LOG.warning("failed to intialize IPV4LOOPBACK. Not fatal");
169 LOOPBACKV4 = GET_ADDRESS;
173 GET_ADDRESS = InetAddress.getByName(IPV6LOOPBACK);
174 } catch (Exception ignored) {
175 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
176 LOG.warning("failed to intialize ANYADDRESSV4. Not fatal");
180 LOOPBACKV6 = GET_ADDRESS;
182 LOOPBACK = (LOOPBACKV4 == null) ? LOOPBACKV6 : LOOPBACKV4;
184 if (LOOPBACK == null || ANYADDRESS == null) {
185 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
186 LOG.severe("failure initializing statics. Neither IPV4 nor IPV6 seem to work.");
189 throw new IllegalStateException("failure initializing statics. Neither IPV4 nor IPV6 seem to work.");
194 * This is a static utility class, you don't make instances.
199 * Provide an iterator which returns all of the local InetAddresses for this
202 * @return iterator of InetAddress which is all of the InetAddress for all
205 public static Iterator<InetAddress> getAllLocalAddresses() {
206 List<InetAddress> allAddr = new ArrayList<InetAddress>();
208 Enumeration<NetworkInterface> allInterfaces = null;
211 allInterfaces = NetworkInterface.getNetworkInterfaces();
212 } catch (SocketException caught) {
213 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
214 LOG.log(Level.SEVERE, "Could not get local interfaces list", caught);
218 if (null == allInterfaces) {
219 allInterfaces = Collections.enumeration(Collections.<NetworkInterface>emptyList());
222 while (allInterfaces.hasMoreElements()) {
223 NetworkInterface anInterface = allInterfaces.nextElement();
226 Enumeration<InetAddress> allIntfAddr = anInterface.getInetAddresses();
228 while (allIntfAddr.hasMoreElements()) {
229 InetAddress anAddr = allIntfAddr.nextElement();
231 if (anAddr.isLoopbackAddress() || anAddr.isAnyLocalAddress()) {
235 if (!allAddr.contains(anAddr)) {
239 } catch (Throwable caught) {
240 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
241 LOG.log(Level.SEVERE, "Could not get addresses for " + anInterface, caught);
246 // if nothing suitable was found then return loopback address.
247 if (allAddr.isEmpty() || Boolean.getBoolean("net.jxta.impl.IPUtils.localOnly")) {
248 if (null != LOOPBACKV4) {
249 allAddr.add(LOOPBACKV4);
252 if (null != LOOPBACKV6) {
253 allAddr.add(LOOPBACKV6);
256 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
257 LOG.fine("Adding loopback interfaces");
261 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
262 LOG.fine("Returning " + allAddr.size() + " addresses.");
265 return allAddr.iterator();
269 * Normalized version of {@link java.net.InetAddress#getHostAddress()} that
270 * handles IPv6 addresss formatting using the style of IETF RFC 2732 and
271 * also handles removal of IPv6 scoping identifiers.
273 * @see <a href="http://www.ietf.org/rfc/rfc2732.txt" target="_blank">IETF RFC 2732 <i>MIME : IPv6 Literal Addresses in URL's</i></a>
274 * @param anAddress The address to format as a <tt>String</tt>.
275 * @return The addresss formatted as a String.
277 public static String getHostAddress(InetAddress anAddress) {
280 if (anAddress instanceof Inet6Address) {
281 hostAddress = anAddress.getHostAddress();
282 int percentAt = hostAddress.indexOf('%');
284 if (-1 == percentAt) {
285 // no scoping identifier. Just add the brackets.
286 hostAddress = "[" + hostAddress + "]";
288 // Remove scoping identifier. They aren't relevant when published.
289 hostAddress = "[" + hostAddress.substring(0, percentAt) + "]";
292 hostAddress = anAddress.getHostAddress();
299 * Parses a String containing a SokectAddress formatted as either:
302 * <host> ":" <port>
304 * "[" <numeric_host> "]:" <port>
307 * @param anAddress The address string to be parsed.
308 * @return The parsed address.
310 public static InetSocketAddress parseSocketAddress(String anAddress) {
314 if (anAddress.startsWith("[")) {
315 int endBracketAt = anAddress.indexOf(']');
316 int portSeparatorAt = anAddress.lastIndexOf(':');
318 if (-1 == endBracketAt) {
319 throw new IllegalArgumentException("missing final ]");
322 if (-1 == portSeparatorAt) {
323 throw new IllegalArgumentException("missing port separator");
326 if (portSeparatorAt < endBracketAt) {
327 throw new IllegalArgumentException("missing port");
330 hostAddress = anAddress.substring(1, endBracketAt);
331 port = anAddress.substring(portSeparatorAt);
333 int portSeparatorAt = anAddress.lastIndexOf(':');
335 if (-1 == portSeparatorAt) {
336 throw new IllegalArgumentException("missing port separator");
339 hostAddress = anAddress.substring(0, portSeparatorAt);
340 port = anAddress.substring(portSeparatorAt + 1);
343 int portNum = Integer.parseInt(port);
345 return InetSocketAddress.createUnresolved(hostAddress, portNum);
349 * Create a client socket using the configured socketFactory or
350 * connectToFromNoFactory if none is available.
352 * @param inetAddress Destination address
353 * @param port Destination port
354 * @param usingInterface Interface to use
355 * @param localPort local port
356 * @param timeout timeout in millis
357 * @return a client socket with the JDK1.4 method connect().
358 * @throws IOException if an io error occurs
360 public static Socket connectToFrom(InetAddress inetAddress, int port, InetAddress usingInterface, int localPort, int timeout) throws IOException {
361 if (socketFactory != null) {
362 return socketFactory.createConnection(inetAddress, port, usingInterface, localPort, timeout);
364 return connectToFromNoFactory(inetAddress, port, usingInterface, localPort, timeout);
369 * Create a client socket with the JDK1.4 method connect().
371 * @param inetAddress Destination address
372 * @param port Destination port
373 * @param usingInterface Interface to use
374 * @param localPort local port
375 * @param timeout timeout in millis
376 * @return a client socket with the JDK1.4 method connect().
377 * @throws IOException if an io error occurs
379 public static Socket connectToFromNoFactory(InetAddress inetAddress, int port, InetAddress usingInterface, int localPort, int timeout) throws IOException {
381 Socket socket = new Socket();
382 InetSocketAddress src = new InetSocketAddress(usingInterface, localPort);
383 InetSocketAddress dst = new InetSocketAddress(inetAddress, port);
386 socket.connect(dst, timeout);
392 * makes connectToFrom create sockets with this factory.
394 * @param sf is the socket factory to use or null if you want the
395 * default behaviour provided by connectToFromNoFactory().
397 public static void setSocketFactory(SocketFactory sf) {
402 * returns the socketFactory used by connectToFrom() to create sockets, or
403 * null if connectToFromNoFactory() is being used.
405 * @return the socket factory used by connectToFrom() or null if
406 * the connectToFromNoFactory() method is used to create Sockets.
408 public static SocketFactory getSocketFactory() {
409 return socketFactory;
413 * makes connectToFrom create sockets with this factory.
415 * @param sf is the socket factory to use or null if you want the
416 * default behaviour provided by new SeverSocket().
418 public static void setServerSocketFactory(ServerSocketFactory sf) {
419 serverSocketFactory = sf;
423 * returns the ServerSocketFactory to create server sockets, or
424 * null if new SeverSocket() is being used.
426 * @return the socket factory used or null if
427 * the new SeverSocket() method is used to create ServerSockets.
429 public static ServerSocketFactory getServerSocketFactory() {
430 return serverSocketFactory;
434 * Size of port groups we will probe.
436 final static int rangesize = 200;
439 * Open a ServerSocket in the specified range.
442 * The method used is done so that the entire range is examined if
443 * needed while ensuring that the process eventually terminates if no port
446 * @param start The lowest numbered port to try.
447 * @param end The highest numbered port to try.
448 * @param backlog the allowed backlog of unaccepted connections.
449 * @param bindAddress the InetAddress to which to bind.
450 * @return a ServerSocket in the specified range.
451 * @throws IOException when the socket cannot be opened. (Lame, but that's what ServerSocket says).
453 public static ServerSocket openServerSocketInRange(int start, int end, int backlog, InetAddress bindAddress) throws IOException {
454 ServerSocketFactory factory = getServerSocketFactory();
456 if ((start < 1) || (start > 65535)) {
457 throw new IllegalArgumentException("Invalid start port");
460 if ((end < 1) || (end > 65535) || (end < start)) {
461 throw new IllegalArgumentException("Invalid end port");
464 // fill the inRange array.
465 List<Integer> inRange = new ArrayList<Integer>(rangesize);
467 for (int eachInRange = 0; eachInRange < rangesize; eachInRange++) {
468 inRange.add(eachInRange, eachInRange);
471 // fill the ranges array.
472 List<Integer> ranges = new ArrayList<Integer>();
475 while (starts <= end) {
480 // shuffle the ranges
481 Collections.shuffle(ranges);
482 while (!ranges.isEmpty()) {
483 int range = ranges.remove(0);
485 // reshuffle the inRange
486 Collections.shuffle(inRange);
488 for (int eachInRange = 0; eachInRange < rangesize; eachInRange++) {
489 int tryPort = range + inRange.get(eachInRange);
498 if (null == factory) {
499 result = new ServerSocket(tryPort, backlog, bindAddress);
501 result = factory.createServerSocket(tryPort, backlog, bindAddress);
505 } catch (BindException failed) {// this one is busy. try another.
509 throw new BindException("All ports in range are in use.");