View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2006 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.services.tcpforwarder;
18  
19  import java.io.IOException;
20  import java.net.InetAddress;
21  import java.net.InetSocketAddress;
22  import java.net.ServerSocket;
23  import java.net.Socket;
24  import java.net.UnknownHostException;
25  import java.util.Collections;
26  import java.util.LinkedHashSet;
27  import java.util.Set;
28  
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * A TcpForwarder represents a service intended to forward TCP traffic
34   * directed to a specific port. Specifically, it permits to programmatically
35   * halt and resume network connectivity.
36   *
37   * <p>Since this is a Java class, it can not intercept traffic not destined for
38   * it. However, it can masquerade for a remote application by forwarding traffic
39   * to and from it achieving the same effect but for requiring the application to
40   * connect on the forwarder's port.
41   *
42   * @svnLink $Revision: 3884 $;$Date: 2009-08-04 15:48:31 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/tcp_forwarder/src/main/java/ch/elca/el4j/services/tcpforwarder/TcpForwarder.java $
43   *
44   * @author Adrian Moos (AMS)
45   * @author Alex Mathey (AMA)
46   * @author Martin Zeltner (MZE)
47   */
48  public class TcpForwarder implements Runnable {
49  	/**
50  	 * Private logger.
51  	 */
52  	private static Logger s_logger
53  		= LoggerFactory.getLogger(TcpForwarder.class);
54  	
55  	/**
56  	 * The listen port.
57  	 */
58  	protected int m_listenPort;
59  
60  	/**
61  	 * The internet socket address to forward traffic to.
62  	 */
63  	protected InetSocketAddress m_targetAddress;
64  
65  	/**
66  	 * The set of active links.
67  	 */
68  	protected Set<Link> m_activeLinks = Collections.synchronizedSet(
69  		new LinkedHashSet<Link>());
70  
71  	/**
72  	 * The used server socket.
73  	 */
74  	private ServerSocket m_serverSocket = null;
75  
76  	/**
77  	 * Forwarder to listen and forward to local ports.
78  	 *
79  	 * @param listenPort Is the input port.
80  	 * @param targetPort Is the output port.
81  	 */
82  	public TcpForwarder(int listenPort, int targetPort) {
83  		this(listenPort, getLocalSocketAddress(targetPort));
84  	}
85  
86  	/**
87  	 * Forwarder to listen on local port and forward to given target address.
88  	 *
89  	 * @param listenPort Is the input port.
90  	 * @param targetAddress Is the target internet socket address to forward to.
91  	 */
92  	public TcpForwarder(int listenPort, InetSocketAddress targetAddress) {
93  		m_listenPort = listenPort;
94  		m_targetAddress = targetAddress;
95  		Thread t = new Thread(this);
96  		t.setDaemon(true);
97  		t.start();
98  	}
99  	
100 	/**
101 	 * @param port Is the local port for the socket.
102 	 * @return Returns the InetSocketAddress with the given port number for
103 	 *         local host.
104 	 */
105 	public static InetSocketAddress getLocalSocketAddress(int port) {
106 		InetSocketAddress result = null;
107 		try {
108 			InetAddress localhost = InetAddress.getLocalHost();
109 			result = new InetSocketAddress(localhost, port);
110 		} catch (UnknownHostException e) {
111 			s_logger.debug("Unable to get the InetAddress of local host.", e);
112 		}
113 		if (result == null) {
114 			result = new InetSocketAddress("localhost", port);
115 		}
116 		return result;
117 	}
118 
119 	/**
120 	 * This method is not intended to be invoked from the outside.
121 	 */
122 	public void run() {
123 		try {
124 			while (true) {
125 				try {
126 					m_serverSocket = new ServerSocket(m_listenPort);
127 				} catch (IOException e) {
128 					s_logger.warn("Binding server socket to local port "
129 						+ m_listenPort + " failed. Aborting...", e);
130 					return;
131 				}
132 				s_logger.debug("Server socket successfully bound to address "
133 					+ m_serverSocket.getLocalSocketAddress());
134 
135 				while (true) {
136 					Socket listenSocket = null;
137 					Socket targetSocket = null;
138 					try {
139 						listenSocket = m_serverSocket.accept();
140 						s_logger.debug("Connection accepted; "
141 							+ listenSocket.toString());
142 						s_logger.debug("Trying to open the target socket at ["
143 							+ m_targetAddress + "].");
144 						targetSocket = new Socket(
145 							m_targetAddress.getAddress(),
146 							m_targetAddress.getPort());
147 					} catch (IOException e) {
148 						if (listenSocket != null) {
149 							try {
150 								listenSocket.close();
151 							} catch (IOException eInner) {
152 								s_logger.debug(
153 									"Due to an exception the listening "
154 									+ "socket should be closed, but there was "
155 									+ "an exception while closing it.", eInner);
156 							}
157 						}
158 						if (targetSocket != null) {
159 							try {
160 								targetSocket.close();
161 							} catch (IOException eInner) {
162 								s_logger.debug(
163 									"Due to an exception the target "
164 									+ "socket should be closed, but there was "
165 									+ "an exception while closing it.", eInner);
166 							}
167 						}
168 						
169 						if (m_serverSocket.isClosed()) {
170 							s_logger.warn(
171 								"Server socket on local port "
172 								+ m_listenPort + " closed.", e);
173 							m_serverSocket = null;
174 							break;
175 						}
176 						s_logger.error("Connection from local port "
177 							+ m_listenPort + " to target address "
178 							+ m_targetAddress + " failed. Aborting...", e);
179 						return;
180 					}
181 					new Link(this, listenSocket, targetSocket);
182 				}
183 				try {
184 					if (m_serverSocket != null) {
185 						m_serverSocket.close();
186 					}
187 				} catch (IOException e) {
188 					s_logger.warn("Closing server socket failed.", e);
189 				}
190 				m_serverSocket = null;
191 
192 				// wait until we are needed again
193 				synchronized (this) {
194 					try {
195 						wait();
196 						s_logger.debug(
197 							"Tcp forwarder awoken. Will continue work...");
198 					} catch (InterruptedException e) {
199 						s_logger.debug(
200 							"Tcp forwarder interrupted. Will continue work...");
201 					}
202 				}
203 			}
204 		} finally {
205 			unplug();
206 		}
207 	}
208 
209 	/**
210 	 * Starts forwarding tcp messages.
211 	 */
212 	public void plug() {
213 		synchronized (this) {
214 			notify();
215 		}
216 	}
217 
218 	/**
219 	 * Stops forwarding tcp messages.
220 	 */
221 	public void unplug() {
222 		try {
223 			if (m_serverSocket != null) {
224 				m_serverSocket.close();
225 			}
226 		} catch (IOException e) {
227 			s_logger.warn("Closing server socket failed.", e);
228 		}
229 		synchronized (m_activeLinks) {
230 			for (Link l : m_activeLinks) {
231 				l.cut();
232 			}
233 		}
234 	}
235 }