001: /*
002: * SSHTools - Java SSH2 API
003: *
004: * Copyright (C) 2002-2003 Lee David Painter and Contributors.
005: *
006: * Contributions made by:
007: *
008: * Brett Smith
009: * Richard Pernavas
010: * Erwin Bolwidt
011: *
012: * This program is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU General Public License
014: * as published by the Free Software Foundation; either version 2
015: * of the License, or (at your option) any later version.
016: *
017: * This program is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: * GNU General Public License for more details.
021: *
022: * You should have received a copy of the GNU General Public License
023: * along with this program; if not, write to the Free Software
024: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
025: */
026: package com.sshtools.daemon;
027:
028: import com.sshtools.daemon.authentication.*;
029: import com.sshtools.daemon.configuration.*;
030: import com.sshtools.daemon.transport.*;
031:
032: import com.sshtools.j2ssh.*;
033: import com.sshtools.j2ssh.configuration.*;
034: import com.sshtools.j2ssh.connection.*;
035: import com.sshtools.j2ssh.net.*;
036: import com.sshtools.j2ssh.transport.*;
037: import com.sshtools.j2ssh.util.*;
038:
039: import org.apache.commons.logging.*;
040:
041: import java.io.*;
042:
043: import java.net.*;
044:
045: import java.util.*;
046:
047: /**
048: *
049: *
050: * @author $author$
051: * @version $Revision: 1.18 $
052: */
053: public abstract class SshServer {
054: private static Log log = LogFactory.getLog(SshServer.class);
055: private ConnectionListener listener = null;
056: private ServerSocket server = null;
057: private boolean shutdown = false;
058: private ServerSocket commandServerSocket;
059:
060: /** */
061: protected List activeConnections = new Vector();
062: Thread thread;
063:
064: /**
065: * Creates a new SshServer object.
066: *
067: * @throws IOException
068: * @throws SshException
069: */
070: public SshServer() throws IOException {
071: String serverId = System.getProperty("sshtools.serverid");
072:
073: if (serverId != null) {
074: TransportProtocolServer.SOFTWARE_VERSION_COMMENTS = serverId;
075: }
076:
077: if (!ConfigurationLoader
078: .isConfigurationAvailable(ServerConfiguration.class)) {
079: throw new SshException(
080: "Server configuration not available!");
081: }
082:
083: if (!ConfigurationLoader
084: .isConfigurationAvailable(PlatformConfiguration.class)) {
085: throw new SshException(
086: "Platform configuration not available");
087: }
088:
089: if (((ServerConfiguration) ConfigurationLoader
090: .getConfiguration(ServerConfiguration.class))
091: .getServerHostKeys().size() <= 0) {
092: throw new SshException(
093: "Server cannot start because there are no server host keys available");
094: }
095: }
096:
097: /**
098: *
099: *
100: * @throws IOException
101: */
102: public void startServer() throws IOException {
103: log.info("Starting server");
104: shutdown = false;
105: startServerSocket();
106: thread = new Thread(new Runnable() {
107: public void run() {
108: try {
109: startCommandSocket();
110: } catch (IOException ex) {
111: log.info("Failed to start command socket", ex);
112:
113: try {
114: stopServer("The command socket failed to start");
115: } catch (IOException ex1) {
116: }
117: }
118: }
119: });
120: thread.start();
121:
122: try {
123: thread.join();
124: } catch (InterruptedException e) {
125: // TODO Auto-generated catch block
126: e.printStackTrace();
127: }
128: }
129:
130: /**
131: *
132: *
133: * @param command
134: * @param client
135: *
136: * @throws IOException
137: */
138: protected void processCommand(int command, Socket client)
139: throws IOException {
140: if (command == 0x3a) {
141: int len = client.getInputStream().read();
142: byte[] msg = new byte[len];
143: client.getInputStream().read(msg);
144: stopServer(new String(msg));
145: }
146: }
147:
148: /**
149: *
150: *
151: * @throws IOException
152: */
153: protected void startCommandSocket() throws IOException {
154: try {
155: commandServerSocket = new ServerSocket(
156: ((ServerConfiguration) ConfigurationLoader
157: .getConfiguration(ServerConfiguration.class))
158: .getCommandPort(), 50, InetAddress
159: .getLocalHost());
160:
161: Socket client;
162:
163: while ((client = commandServerSocket.accept()) != null) {
164: log.info("Command request received");
165:
166: // Read and process the command
167: processCommand(client.getInputStream().read(), client);
168: client.close();
169:
170: if (shutdown) {
171: break;
172: }
173: }
174:
175: commandServerSocket.close();
176: } catch (Exception e) {
177: if (!shutdown) {
178: log.fatal("The command socket failed", e);
179: }
180: }
181: }
182:
183: /**
184: *
185: *
186: * @throws IOException
187: */
188: protected void startServerSocket() throws IOException {
189: listener = new ConnectionListener(
190: ((ServerConfiguration) ConfigurationLoader
191: .getConfiguration(ServerConfiguration.class))
192: .getListenAddress(),
193: ((ServerConfiguration) ConfigurationLoader
194: .getConfiguration(ServerConfiguration.class))
195: .getPort());
196: listener.start();
197: }
198:
199: /**
200: *
201: *
202: * @param msg
203: *
204: * @throws IOException
205: */
206: public void stopServer(String msg) throws IOException {
207: log.info("Shutting down server");
208: shutdown = true;
209: log.debug(msg);
210: shutdown(msg);
211: listener.stop();
212: log.debug("Stopping command server");
213:
214: try {
215: if (commandServerSocket != null) {
216: commandServerSocket.close();
217: }
218: } catch (IOException ioe) {
219: log.error(ioe);
220: }
221: }
222:
223: /**
224: *
225: *
226: * @param msg
227: */
228: protected abstract void shutdown(String msg);
229:
230: /**
231: *
232: *
233: * @param connection
234: *
235: * @throws IOException
236: */
237: protected abstract void configureServices(
238: ConnectionProtocol connection) throws IOException;
239:
240: /**
241: *
242: *
243: * @param socket
244: *
245: * @throws IOException
246: */
247: protected void refuseSession(Socket socket) throws IOException {
248: TransportProtocolServer transport = new TransportProtocolServer(
249: true);
250: transport.startTransportProtocol(
251: new ConnectedSocketTransportProvider(socket),
252: new SshConnectionProperties());
253: }
254:
255: /**
256: *
257: *
258: * @param socket
259: *
260: * @return
261: *
262: * @throws IOException
263: */
264: protected TransportProtocolServer createSession(Socket socket)
265: throws IOException {
266: log.debug("Initializing connection");
267:
268: InetAddress address = socket.getInetAddress();
269:
270: /*( (InetSocketAddress) socket
271: .getRemoteSocketAddress()).getAddress();*/
272: log.debug("Remote Hostname: " + address.getHostName());
273: log.debug("Remote IP: " + address.getHostAddress());
274:
275: TransportProtocolServer transport = new TransportProtocolServer();
276:
277: // Create the Authentication Protocol
278: AuthenticationProtocolServer authentication = new AuthenticationProtocolServer();
279:
280: // Create the Connection Protocol
281: ConnectionProtocol connection = new ConnectionProtocol();
282:
283: // Configure the connections services
284: configureServices(connection);
285:
286: // Allow the Connection Protocol to be accepted by the Authentication Protocol
287: authentication.acceptService(connection);
288:
289: // Allow the Authentication Protocol to be accepted by the Transport Protocol
290: transport.acceptService(authentication);
291: transport.startTransportProtocol(
292: new ConnectedSocketTransportProvider(socket),
293: new SshConnectionProperties());
294:
295: return transport;
296: }
297:
298: class ConnectionListener implements Runnable {
299: private Log log = LogFactory.getLog(ConnectionListener.class);
300: private ServerSocket server;
301: private String listenAddress;
302: private Thread thread;
303: private int maxConnections;
304: private int port;
305: private StartStopState state = new StartStopState(
306: StartStopState.STOPPED);
307:
308: public ConnectionListener(String listenAddress, int port) {
309: this .port = port;
310: this .listenAddress = listenAddress;
311: }
312:
313: public void run() {
314: try {
315: log.debug("Starting connection listener thread");
316: state.setValue(StartStopState.STARTED);
317: server = new ServerSocket(port);
318:
319: Socket socket;
320: maxConnections = ((ServerConfiguration) ConfigurationLoader
321: .getConfiguration(ServerConfiguration.class))
322: .getMaxConnections();
323:
324: boolean refuse = false;
325: TransportProtocolEventHandler eventHandler = new TransportProtocolEventAdapter() {
326: public void onDisconnect(TransportProtocol transport) {
327: // Remove from our active channels list only if
328: // were still connected (the thread cleans up
329: // when were exiting so this is to avoid any concurrent
330: // modification problems
331: if (state.getValue() != StartStopState.STOPPED) {
332: synchronized (activeConnections) {
333: log.info(transport
334: .getUnderlyingProviderDetail()
335: + " connection closed");
336: activeConnections.remove(transport);
337: }
338: }
339: }
340: };
341:
342: try {
343: while (((socket = server.accept()) != null)
344: && (state.getValue() == StartStopState.STARTED)) {
345: log.debug("New connection requested");
346:
347: if (maxConnections < activeConnections.size()) {
348: refuseSession(socket);
349: } else {
350: TransportProtocolServer transport = createSession(socket);
351: log.info("Monitoring active session from "
352: + socket.getInetAddress()
353: .getHostName());
354:
355: synchronized (activeConnections) {
356: activeConnections.add(transport);
357: }
358:
359: transport.addEventHandler(eventHandler);
360: }
361: }
362: } catch (IOException ex) {
363: if (state.getValue() != StartStopState.STOPPED) {
364: log.info(
365: "The server was shutdown unexpectedly",
366: ex);
367: }
368: }
369:
370: state.setValue(StartStopState.STOPPED);
371:
372: // Closing all connections
373: log.info("Disconnecting active sessions");
374:
375: for (Iterator it = activeConnections.iterator(); it
376: .hasNext();) {
377: ((TransportProtocolServer) it.next())
378: .disconnect("The server is shuting down");
379: }
380:
381: listener = null;
382: log.info("Exiting connection listener thread");
383: } catch (IOException ex) {
384: log.info("The server thread failed", ex);
385: } finally {
386: thread = null;
387: }
388:
389: // brett
390: // System.exit(0);
391: }
392:
393: public void start() {
394: thread = new SshThread(this , "Connection listener", true);
395: thread.start();
396: }
397:
398: public void stop() {
399: try {
400: state.setValue(StartStopState.STOPPED);
401: server.close();
402:
403: if (thread != null) {
404: thread.interrupt();
405: }
406: } catch (IOException ioe) {
407: log
408: .warn(
409: "The listening socket reported an error during shutdown",
410: ioe);
411: }
412: }
413: }
414: }
|