/* * Copyright 2011 Thomas Bocek * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package net.tomp2p.connection2; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * Gathers information about interface bindings. Here a user can set the * preferences to which addresses to bind the socket. This class contains two * types of information: 1.) the interface/address to listen for incoming * connections and 2.) how other peers see us. The default is to listen to all * interfaces and our outside address is set to the first interface it finds. If * more than one search hint is used, then the combination operation will be * "and" * * @author Thomas Bocek */ public class Bindings { /** * Types of protocols. This was a boolean, but there needs to be the type * "Any". */ public enum Protocol { /** * 32 bit IP version 4. */ IPv4, /** * 128 bit IP version 6. */ IPv6, /** * This type indicates that the address to bind to can be either IPv4 or * IPv6. */ Any }; /** * The number of maximum ports, 2^16. */ public static final int MAX_PORT = 65535; /** * IANA recommends to use ports higher than 49152. */ public static final int MIN_DYN_PORT = 49152; /** * The default port of TomP2P. */ public static final int DEFAULT_PORT = 7700; private static final Random RND = new Random(); // IANA recommends to use ports higher than 49152 private static final int RANGE = MAX_PORT - MIN_DYN_PORT; // This can be set by the user. The discover process will not use this field // to store anything private final List<InetAddress> listenAddresses = new ArrayList<InetAddress>(1); private final List<String> listenInterfaceHints = new ArrayList<String>(1); private final Protocol listenProtocolHint; // provide this information if you know your mapping beforehand, i.e. manual // port-forwarding private final InetAddress externalAddress; private final int externalTCPPort; private final int externalUDPPort; private final boolean setExternalPortsManually; // here are the found address stored. This is not set by the user private final List<InetAddress> foundBroadcastAddresses = new ArrayList<InetAddress>(1); // here are the found address stored. This is not set by the user private final List<Inet4Address> foundAddresses4 = new ArrayList<Inet4Address>(1); // here are the found address stored. This is not set by the user private final List<Inet6Address> foundAddresses6 = new ArrayList<Inet6Address>(1); /** * Creates a binding class that binds to everything. */ public Bindings() { this(Protocol.Any, null, 0, 0); } /** * Creates a binding class that binds to a specified address. * * @param bind * the address to bind to */ public Bindings(final InetAddress bind) { this(Protocol.Any, null, 0, 0); addAddress(bind); } /** * Creates a Binding class that binds to a specified interface. * * @param iface * The interface to bind to */ public Bindings(final String iface) { this(Protocol.Any, null, 0, 0); addInterface(iface); } /** * Creates a Binding class that binds to a specified protocol. * * @param protocol * The protocol to bind to */ public Bindings(final Protocol protocol) { this(protocol, null, 0, 0); } /** * Creates a Binding class that binds to a specified protocol and interface. * * @param protocol * The protocol to bind to * @param iface * The interface to bind to */ public Bindings(final Protocol protocol, final String iface) { this(protocol, null, 0, 0); addInterface(iface); } /** * Creates a Binding class that binds to a specified protocol and interface * and address. * * @param protocol * The protocol to bind to * @param iface * The interface to bind to * @param bind * The address to bind to */ public Bindings(final Protocol protocol, final String iface, final InetAddress bind) { this(protocol, null, 0, 0); addInterface(iface); } /** * Creates a Binding class that binds to everything and provides information * about manual port forwarding. * * @param externalAddress * The external address, how other peers will see us * @param externalTCPPort * The external port, how other peers will see us * @param externalUDPPort * The external port, how other peers will see us */ public Bindings(final InetAddress externalAddress, final int externalTCPPort, final int externalUDPPort) { this(Protocol.Any, externalAddress, externalTCPPort, externalUDPPort); } /** * Creates a Binding class that binds to a specified protocol and provides * information about manual port forwarding. * * @param protocol * The protocol to bind to * @param externalAddress * The external address, how other peers will see us. Use null if * you don't want to use external address * @param externalTCPPort * The external port, how other peers will see us, if 0 is * provided, a random port will be used * @param externalUDPPort * The external port, how other peers will see us, if 0 is * provided, a random port will be used */ public Bindings(final Protocol protocol, final InetAddress externalAddress, final int externalTCPPort, final int externalUDPPort) { if (externalTCPPort < 0 || externalUDPPort < 0) { throw new IllegalArgumentException("port needs to be >= 0"); } this.externalAddress = externalAddress; this.externalTCPPort = externalTCPPort == 0 ? (RND.nextInt(RANGE) + MIN_DYN_PORT) : externalTCPPort; this.externalUDPPort = externalUDPPort == 0 ? (RND.nextInt(RANGE) + MIN_DYN_PORT) : externalUDPPort; // set setExternalPortsManually to true if the user specified both ports // in advance. This tells us that the user knows about the ports and did // a manual port-forwarding. this.setExternalPortsManually = externalUDPPort != 0 && externalTCPPort != 0; this.listenProtocolHint = protocol; } /** * Adds an address that we want to listen to. If the address is not found, * it will be ignored * * @param address * The current class * @return The bindings (this) */ Bindings addFoundAddress(final InetAddress address) { if (address == null) { throw new IllegalArgumentException("Cannot add null"); } if (address instanceof Inet4Address) { foundAddresses4.add((Inet4Address) address); } else if (address instanceof Inet6Address) { foundAddresses6.add((Inet6Address) address); } else { throw new IllegalArgumentException("Unknown address family " + address.getClass()); } return this; } /** * Returns a list of InetAddresses to listen to. First Inet4Addresses, then * Inet6Addresses are present in the list. This returns the matching * addresses from DiscoverNetworks. * * @return A list of InetAddresses to listen to */ public List<InetAddress> getFoundAddresses() { // first return ipv4, then ipv6 List<InetAddress> listenAddresses2 = new ArrayList<InetAddress>(); listenAddresses2.addAll(foundAddresses4); listenAddresses2.addAll(foundAddresses6); return listenAddresses2; } /** * Adds an address that we want to listen to. If the address is not found, * it will be ignored * * @param address * The current class * @return this instance */ public Bindings addAddress(final InetAddress address) { listenAddresses.add(address); return this; } /** * @return A list of the addresses provided by the user */ public List<InetAddress> getAddresses() { return listenAddresses; } /** * @return A list of broadcast addresses. */ public List<InetAddress> getBroadcastAddresses() { return foundBroadcastAddresses; } /** * Adds an interface that will be searched for. If the interface is not * found, it will be ignored * * @param interfaceHint * The interface, e.g. eth0 * @return The same instance */ public Bindings addInterface(final String interfaceHint) { if (interfaceHint == null) { throw new IllegalArgumentException("Cannot add null"); } listenInterfaceHints.add(interfaceHint); return this; } /** * @return A list of interfaces to listen to */ public List<String> getInterfaces() { return listenInterfaceHints; } /** * @return The protocol to listen to */ public Protocol getProtocol() { return listenProtocolHint; } /** * Clears all lists: listenInterfaceHints, listenAddresses, * broadcastAddresses. */ public void clear() { listenInterfaceHints.clear(); listenAddresses.clear(); foundAddresses4.clear(); foundAddresses6.clear(); foundBroadcastAddresses.clear(); } /** * @return Checks if the user sets any addresses */ public boolean isAllAddresses() { return listenAddresses.size() == 0; } /** * @return Checks if the user sets any interfaces */ public boolean isAllInterfaces() { return listenInterfaceHints.size() == 0; } /** * @return Checks if the user sets any protocols */ public boolean isAllProtocols() { return listenProtocolHint == Protocol.Any; } /** * @return Checks if the user sets protocol to anything or IPv4 */ public boolean isIPv4() { return isAllProtocols() || listenProtocolHint == Protocol.IPv4; } /** * @return Checks if the user sets protocol to anything or IPv6 */ public boolean isIPv6() { return isAllProtocols() || listenProtocolHint == Protocol.IPv6; } /** * @return Checks if the user sets anything at all */ public boolean isListenAll() { return isAllProtocols() && isAllInterfaces() && isAllAddresses(); } /** * Checks if the user provided an interface hint. * * @param name * The name of the interface reported by the system * @return True if the user added the interface */ public boolean containsInterface(final String name) { return listenInterfaceHints.contains(name); } /** * @return Checks if the user set the external address */ public boolean isExternalAddress() { return externalAddress != null && externalTCPPort != 0 && externalUDPPort != 0; } /** * @return Returns the externalAddress, how other peers see us */ public InetAddress getExternalAddress() { return externalAddress; } /** * @return Returns the external port, how other peers see us */ public int getOutsideTCPPort() { return externalTCPPort; } /** * @return Returns the external port, how other peers see us */ public int getOutsideUDPPort() { return externalUDPPort; } /** * Adds the results from an other binding. This is useful because you can * add within one Bindings hints only with "and", with add() you have the * option "or" as well. E.g., Bindings b1 = new Bindings(IPv4, eth0); * Bindings b2 = new Bindings(IPv6, eth1); b2.add(b1) -> this will bind to * all IPv4 addresses on eth0 and all IPv6 addresses on eth1 * * @param other * The other instance to get the results from * @return The same instance */ public Bindings add(final Bindings other) { this.foundAddresses4.addAll(other.foundAddresses4); this.foundAddresses6.addAll(other.foundAddresses6); this.foundBroadcastAddresses.addAll(other.foundBroadcastAddresses); return this; } /** * @return True if the user specified both ports in advance. This tells us * that the user knows about the ports and did a manual * port-forwarding. */ public boolean isSetExternalPortsManually() { return setExternalPortsManually; } }