/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.network;
import org.jboss.msc.service.ServiceName;
import org.wildfly.common.Assert;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
/**
* An outbound socket binding represents the client end of a socket. It represents binding from a local "host"
* to a remote "host". In some special cases the remote host can itself be the same local host.
* Unlike the {@link SocketBinding} which represents a {@link java.net.ServerSocket} that opens a socket for "listening",
* the {@link OutboundSocketBinding} represents a {@link Socket} which "connects" to a remote/local host.
*
* @author Jaikiran Pai
*/
public class OutboundSocketBinding {
/** @deprecated use capability based injection */
@Deprecated
public static final ServiceName OUTBOUND_SOCKET_BINDING_BASE_SERVICE_NAME = ServiceName.JBOSS.append("outbound-socket-binding");
private final String name;
private final SocketBindingManager socketBindingManager;
private final boolean fixedSourcePort;
private final NetworkInterfaceBinding sourceNetworkInterface;
private final Integer sourcePort;
private final String unresolvedDestinationAddress;
private final int destinationPort;
/**
* The destination address is lazily resolved whenever a request is made {@link #getResolvedDestinationAddress()}
* or for {@link #connect()}.
*/
private InetAddress resolvedDestinationAddress;
/**
* Creates an outbound socket binding
*
* @param name Name of the outbound socket binding
* @param socketBindingManager The socket binding manager
* @param destinationAddress The destination address to which this socket will be "connected". Cannot be null or empty string.
* @param destinationPort The destination port. Cannot be < 0.
* @param sourceNetworkInterface (Optional) source network interface which will be used as the "source" of the socket binding
* @param sourcePort (Optional) source port. Cannot be null or < 0
* @param fixedSourcePort True if the <code>sourcePort</code> has to be used as a fixed port number. False if the <code>sourcePort</code>
* will be added to the port offset while determining the absolute source port.
*/
public OutboundSocketBinding(final String name, final SocketBindingManager socketBindingManager,
final String destinationAddress, final int destinationPort,
final NetworkInterfaceBinding sourceNetworkInterface, final Integer sourcePort,
final boolean fixedSourcePort) {
Assert.checkNotNullParam("name", name);
Assert.checkNotEmptyParam("name", name);
Assert.checkNotNullParam("socketBindingManager", socketBindingManager);
Assert.checkNotNullParam("destinationAddress", destinationAddress);
Assert.checkMinimumParameter("destinationPort", 0, destinationPort);
this.name = name;
this.socketBindingManager = socketBindingManager;
this.unresolvedDestinationAddress = destinationAddress;
this.destinationPort = destinationPort;
this.sourceNetworkInterface = sourceNetworkInterface;
this.sourcePort = sourcePort;
this.fixedSourcePort = fixedSourcePort;
}
/**
* Creates an outbound socket binding.
*
* @param name Name of the outbound socket binding
* @param socketBindingManager The socket binding manager
* @param destinationAddress The destination address to which this socket will be "connected". Cannot be null.
* @param destinationPort The destination port. Cannot be < 0.
* @param sourceNetworkInterface (Optional) source network interface which will be used as the "source" of the socket binding
* @param sourcePort (Optional) source port. Cannot be null or < 0
* @param fixedSourcePort True if the <code>sourcePort</code> has to be used as a fixed port number. False if the <code>sourcePort</code>
* will be added to the port offset while determining the absolute source port.
*/
public OutboundSocketBinding(final String name, final SocketBindingManager socketBindingManager,
final InetAddress destinationAddress, final int destinationPort,
final NetworkInterfaceBinding sourceNetworkInterface, final Integer sourcePort,
final boolean fixedSourcePort) {
this(name, socketBindingManager, destinationAddress.getHostAddress(), destinationPort, sourceNetworkInterface, sourcePort, fixedSourcePort);
this.resolvedDestinationAddress = destinationAddress;
}
/**
* Creates a {@link Socket} represented by this {@link OutboundSocketBinding} and connects to the
* destination.
*
* @return the created and connected socket
* @throws IOException
*/
public Socket connect() throws IOException {
final Socket socket = this.createSocket();
final InetAddress destinationAddress = this.getResolvedDestinationAddress();
final int destinationPort = this.getDestinationPort();
final SocketAddress destination = new InetSocketAddress(destinationAddress, destinationPort);
socket.connect(destination);
return socket;
}
/**
* Returns the name of this outbound socket binding. Can be used in log statements to make the log statement usable.
*
* @return the name of this outbound socket binding
*/
public String getName() {
return name;
}
/**
* Returns the <em>unresolved</em> destination address of this outbound socket binding.
*
* @return the unresolved destination address
*/
public String getUnresolvedDestinationAddress() {
return this.unresolvedDestinationAddress;
}
/**
* Returns the <em>resolved</em> destination address of this outbound socket binding. If the destination address
* is already resolved then this method return that address or else it tries to resolve the
* address before return.
*
* @throws UnknownHostException If the destination address cannot be resolved
*/
public synchronized InetAddress getResolvedDestinationAddress() throws UnknownHostException {
if (this.resolvedDestinationAddress != null) {
return this.resolvedDestinationAddress;
}
return InetAddress.getByName(this.unresolvedDestinationAddress);
}
/**
* @deprecated Use {@link #getResolvedDestinationAddress()} instead to get the resolved destination address
* or {@link #getUnresolvedDestinationAddress()} to get the unresolved destination address.
*/
@Deprecated
public synchronized InetAddress getDestinationAddress() throws UnknownHostException {
return getResolvedDestinationAddress();
}
/**
* Returns the destination port number.
*
* @return destination port number
*/
public int getDestinationPort() {
return this.destinationPort;
}
/**
* Returns whether the source port is fixed, i.e. not accounting for port offset.
*
* @return true if the port number is fixed, false otherwise
*/
public boolean isFixedSourcePort() {
return this.fixedSourcePort;
}
/**
* Returns the source address of this outbound socket binding. If no explicit source address is specified
* for this binding, then this method returns the address of the default interface that's configured
* for the socket binding group.
*
* @return source address of this outbound socket binding if specified,
* default interface of the socket binding manager otherwise
*/
public InetAddress getSourceAddress() {
return this.sourceNetworkInterface != null ? this.sourceNetworkInterface.getAddress() : this.socketBindingManager.getDefaultInterfaceAddress();
}
/**
* Returns the source address of this outbound socket binding if one is configured. If no explicit source address
* is specified for this binding, then this method returns {@code null}. Use {@link #getSourceAddress()}
* instead to obtain the default interface of the socket binding manager if none is specified for this binding.
*
* @return source address of this outbound socket binding if specified,
* {@code null} otherwise
*/
public InetAddress getOptionalSourceAddress() {
return sourceNetworkInterface != null ? sourceNetworkInterface.getAddress() : null;
}
/**
* Returns the source port for this outbound socket binding. Note that this isn't the "absolute" port if the
* this outbound socket binding has a port offset. To get the absolute source port, use the {@link #getAbsoluteSourcePort()}
* method.
*
* @return the source port for this outbound socket binding not accounting for port offset/fixation; {@code null} if an ephemeral port should be used
*/
public Integer getSourcePort() {
return (this.sourcePort == null || this.sourcePort == 0) ? null : this.sourcePort;
}
/**
* Returns the absolute source port for this outbound socket binding. The absolute source port is the same as {@link #getSourcePort()}
* if the outbound socket binding is marked for "fixed source port". Else, it is the sum of {@link #getSourcePort()}
* and the port offset configured on the {@link SocketBindingManager}.
*
* @return the absolute source port accounting for port offset/fixation; {@code null} if an ephemeral port should be used
*/
public Integer getAbsoluteSourcePort() {
if (this.getSourcePort() == null) {
return null;
}
if (this.fixedSourcePort) {
return this.sourcePort;
}
final int portOffset = this.socketBindingManager.getPortOffset();
return this.sourcePort + portOffset;
}
/**
* Closes the outbound socket binding connection.
*
* @throws IOException
*/
public void close() throws IOException {
final ManagedBinding binding = this.socketBindingManager.getNamedRegistry().getManagedBinding(this.name);
if (binding == null) {
return;
}
binding.close();
}
/**
* Returns true if a socket connection has been established by this outbound socket binding, false otherwise.
*
* @return true if a socket connection has been established by this outbound socket binding, false otherwise
*/
public boolean isConnected() {
return this.socketBindingManager.getNamedRegistry().getManagedBinding(this.name) != null;
}
// At this point, don't really expose this createSocket() method and let's just expose
// the connect() method, since the caller can actually misuse the returned Socket
// to connect any random destination address/port.
private Socket createSocket() throws IOException {
final ManagedSocketFactory socketFactory = this.socketBindingManager.getSocketFactory();
final Socket socket = socketFactory.createSocket(this.name);
// if the outbound binding specifies the source to use, then bind this socket to the
// appropriate source
final SocketAddress sourceSocketAddress = this.getOptionalSourceSocketAddress();
if (sourceSocketAddress != null) {
socket.bind(sourceSocketAddress);
}
return socket;
}
private SocketAddress getOptionalSourceSocketAddress() {
final InetAddress sourceAddress = this.getOptionalSourceAddress();
final Integer absoluteSourcePort = this.getAbsoluteSourcePort();
if (sourceAddress == null && absoluteSourcePort == null) {
return null;
}
if (sourceAddress == null) {
return new InetSocketAddress(absoluteSourcePort);
}
return new InetSocketAddress(sourceAddress, absoluteSourcePort);
}
}