/******************************************************************************* * Copyright (c) 2014 Zend Technologies Ltd. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.zend.php.zendserver.deployment.core.tunnel; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; /** * Port forwarding for SSH tunneling. It supports both local and remote side * port forwarding. * * @author Wojciech Galanciak, 2014 * */ public class PortForwarding { private static final String COLON = ":"; //$NON-NLS-1$ /** * Port forwarding sides. Possible values are local or remote. */ public enum Side { LOCAL("-L", "local"), //$NON-NLS-1$ //$NON-NLS-2$ REMOTE("-R", "remote"); //$NON-NLS-1$ //$NON-NLS-2$ private String sideSwitch; private String name; private Side(String sideSwitch, String name) { this.sideSwitch = sideSwitch; this.name = name; } public String getSideSwitch() { return sideSwitch; } public String getName() { return name; } public static Side bySwitch(String name) { if (name != null) { Side[] sides = values(); for (Side side : sides) { if (side.sideSwitch.equals(name)) { return side; } } } return null; } public static Side byName(String name) { if (name != null) { Side[] sides = values(); for (Side side : sides) { if (side.name.equals(name)) { return side; } } } return null; } } private Side side; private String localAddress; private int localPort; private String remoteAddress; private int remotePort; private PortForwarding(Side side, String localAddress, int localPort, String remoteAddress, int remotePort) { super(); this.side = side; this.localAddress = localAddress; this.localPort = localPort; this.remoteAddress = remoteAddress; this.remotePort = remotePort; } /** * Factory method to create a local side port forwarding. * * @param localAddress * the network interface we should be listening on * @param localPort * the local port to listen on * @param remoteAddress * the remote host (i.e. at the server-side) to forward the * connections to * @param remotePort * the port at the remote host to forward the connections to * @return */ public static PortForwarding createLocal(String localAddress, int localPort, String remoteAddress, int remotePort) { return new PortForwarding(Side.LOCAL, localAddress, localPort, remoteAddress, remotePort); } /** * Factory method to create a local side port forwarding. * * @param localPort * the local port to listen on * @param remoteAddress * the remote host (i.e. at the server-side) to forward the * connections to * @param remotePort * the port at the remote host to forward the connections to * @return */ public static PortForwarding createLocal(int localPort, String remoteAddress, int remotePort) { return createLocal(null, localPort, remoteAddress, remotePort); } /** * Factory method to create a remote side port forwarding. * * @param remoteAddress * the network interface to bind on on the remote side * @param remotePort * the port to listen on on the remote side * @param localAddress * the host on the local side to forward connections to * @param localPort * the port at host to forward connections to * @return */ public static PortForwarding createRemote(String remoteAddress, int remotePort, String localAddress, int localPort) { return new PortForwarding(Side.REMOTE, localAddress, localPort, remoteAddress, remotePort); } /** * Factory method to create a remote side port forwarding. * * @param remotePort * the port to listen on on the remote side * @param localAddress * the host on the local side to forward connections to * @param localPort * the port at host to forward connections to * @return */ public static PortForwarding createRemote(int remotePort, String localAddress, int localPort) { return createRemote(null, remotePort, localAddress, localPort); } /** * Parse port forwarding from its string representation. Supported input is * a full command line switch for creating particular type of port * forwarding: * <ul> * <li>local-side: <code>-L [bind_address:]port:host:hostport]</code></li> * <li>remote-side: <code>-R [bind_address:]port:host:hostport]</code></li> * </ul> * * @param input * string representation of port forwarding * @return {@link PortForwarding} instance */ public static PortForwarding deserialize(String input) { String[] segments = input.split(" "); //$NON-NLS-1$ if (segments.length == 2) { String[] parts = segments[1].split(COLON); switch (Side.bySwitch(segments[0])) { case LOCAL: if (parts.length == 4) { // -L bind_address:port:host:hostport return createLocal(parts[0], Integer.valueOf(parts[1]), parts[2], Integer.valueOf(parts[3])); } else if (parts.length == 3) { // -L port:host:hostport return createLocal(null, Integer.valueOf(parts[0]), parts[1], Integer.valueOf(parts[2])); } break; case REMOTE: if (parts.length == 4) { // -R bind_address:port:host:hostport return createRemote(parts[0], Integer.valueOf(parts[1]), parts[2], Integer.valueOf(parts[3])); } else if (parts.length == 3) { // -R port:host:hostport return createRemote(null, Integer.valueOf(parts[0]), parts[1], Integer.valueOf(parts[2])); } break; default: break; } } return null; } /** * Serialize {@link PortForwarding} instance to its string representation. * Possible outputs are following: * <ul> * <li>for local-side: <code>-L [bind_address:]port:host:hostport]</code></li> * <li>for remote-side: <code>-R [bind_address:]port:host:hostport]</code></li> * </ul> * * @return string representation of port forwarding */ public String serialize() { StringBuilder result = new StringBuilder(); result.append(side.getSideSwitch()); result.append(" "); //$NON-NLS-1$ switch (side) { case LOCAL: if (localAddress != null) { result.append(localAddress); result.append(COLON); } result.append(localPort); result.append(COLON); result.append(remoteAddress); result.append(COLON); result.append(remotePort); break; case REMOTE: if (remoteAddress != null) { result.append(remoteAddress); result.append(COLON); } result.append(remotePort); result.append(COLON); result.append(localAddress); result.append(COLON); result.append(localPort); break; default: break; } return result.toString(); } /** * Setup particular port forwarding for specified SSH session. * * @param session * {@link Session} instance * @throws JSchException */ public void setup(Session session) throws JSchException { switch (side) { case LOCAL: if (localAddress != null) { session.setPortForwardingL(localAddress, localPort, remoteAddress, remotePort); } else { session.setPortForwardingL(localPort, remoteAddress, remotePort); } break; case REMOTE: if (remoteAddress != null) { session.setPortForwardingR(remoteAddress, remotePort, localAddress, localPort); } else { session.setPortForwardingR(remotePort, localAddress, localPort); } break; default: break; } } /** * Delete particular port forwarding for specified SSH session. * * @param session * {@link Session} instance * @throws JSchException */ public void delete(Session session) throws JSchException { switch (side) { case LOCAL: if (localAddress != null) { session.delPortForwardingL(localAddress, localPort); } else { session.delPortForwardingL(localPort); } break; case REMOTE: if (remoteAddress != null) { session.delPortForwardingR(remoteAddress, remotePort); } else { session.delPortForwardingR(remotePort); } break; default: break; } } /** * @return local address */ public String getLocalAddress() { return localAddress; } /** * @return local port */ public int getLocalPort() { return localPort; } /** * @return remote address */ public String getRemoteAddress() { return remoteAddress; } /** * @return remote port */ public int getRemotePort() { return remotePort; } /** * @return port forwarding side * @see Side */ public Side getSide() { return side; } }