Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/ShellFactoryImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/ShellFactoryImpl.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/ShellFactoryImpl.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/ShellFactoryImpl.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,277 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.felix.gogo.jline.ssh; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Map; + +import org.apache.felix.gogo.jline.Shell; +import org.apache.felix.gogo.jline.Shell.Context; +import org.apache.felix.service.command.CommandProcessor; +import org.apache.felix.service.command.CommandSession; +import org.apache.sshd.common.Factory; +import org.apache.sshd.common.channel.PtyMode; +import org.apache.sshd.server.Command; +import org.apache.sshd.server.Environment; +import org.apache.sshd.server.ExitCallback; +import org.apache.sshd.server.SessionAware; +import org.apache.sshd.server.Signal; +import org.apache.sshd.server.SignalListener; +import org.apache.sshd.server.session.ServerSession; +import org.jline.terminal.Attributes; +import org.jline.terminal.Attributes.ControlChar; +import org.jline.terminal.Attributes.InputFlag; +import org.jline.terminal.Attributes.LocalFlag; +import org.jline.terminal.Attributes.OutputFlag; +import org.jline.terminal.Size; +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; + +/** + * SSHD {@link org.apache.sshd.server.Command} factory which provides access to + * Shell. + */ +public class ShellFactoryImpl implements Factory<Command> { + private final CommandProcessor processor; + + public ShellFactoryImpl(CommandProcessor processor) { + this.processor = processor; + } + + private static void flush(OutputStream... streams) { + for (OutputStream s : streams) { + try { + s.flush(); + } catch (IOException e) { + // Ignore + } + } + } + + static void close(Closeable... closeables) { + for (Closeable c : closeables) { + try { + c.close(); + } catch (IOException e) { + // Ignore + } + } + } + + public Command create() { + return new ShellImpl(); + } + + public class ShellImpl implements Command, SessionAware { + private InputStream in; + + private OutputStream out; + + private OutputStream err; + + private ExitCallback callback; + + private ServerSession session; + + private boolean closed; + + public void setInputStream(final InputStream in) { + this.in = in; + } + + public void setOutputStream(final OutputStream out) { + this.out = out; + } + + public void setErrorStream(final OutputStream err) { + this.err = err; + } + + public void setExitCallback(ExitCallback callback) { + this.callback = callback; + } + + public void setSession(ServerSession session) { + this.session = session; + } + + public void start(final Environment env) throws IOException { + try { + new Thread() { + public void run() { + try { + ShellImpl.this.run(env); + } catch (Throwable t) { + t.printStackTrace(); + } + } + }.start(); + } catch (Exception e) { + throw (IOException) new IOException("Unable to start shell").initCause(e); + } + } + + public void run(Environment env) throws Exception { + try { + Terminal terminal = TerminalBuilder.builder() + .name("gogo") + .type(env.getEnv().get("TERM")) + .system(false) + .streams(in, out) + .build(); + terminal.setSize(new Size(Integer.parseInt(env.getEnv().get("COLUMNS")), + Integer.parseInt(env.getEnv().get("LINES")))); + Attributes attr = terminal.getAttributes(); + for (Map.Entry<PtyMode, Integer> e : env.getPtyModes().entrySet()) { + switch (e.getKey()) { + case VINTR: + attr.setControlChar(ControlChar.VINTR, e.getValue()); + break; + case VQUIT: + attr.setControlChar(ControlChar.VQUIT, e.getValue()); + break; + case VERASE: + attr.setControlChar(ControlChar.VERASE, e.getValue()); + break; + case VKILL: + attr.setControlChar(ControlChar.VKILL, e.getValue()); + break; + case VEOF: + attr.setControlChar(ControlChar.VEOF, e.getValue()); + break; + case VEOL: + attr.setControlChar(ControlChar.VEOL, e.getValue()); + break; + case VEOL2: + attr.setControlChar(ControlChar.VEOL2, e.getValue()); + break; + case VSTART: + attr.setControlChar(ControlChar.VSTART, e.getValue()); + break; + case VSTOP: + attr.setControlChar(ControlChar.VSTOP, e.getValue()); + break; + case VSUSP: + attr.setControlChar(ControlChar.VSUSP, e.getValue()); + break; + case VDSUSP: + attr.setControlChar(ControlChar.VDSUSP, e.getValue()); + break; + case VREPRINT: + attr.setControlChar(ControlChar.VREPRINT, e.getValue()); + break; + case VWERASE: + attr.setControlChar(ControlChar.VWERASE, e.getValue()); + break; + case VLNEXT: + attr.setControlChar(ControlChar.VLNEXT, e.getValue()); + break; + /* + case VFLUSH: + attr.setControlChar(ControlChar.VMIN, e.getValue()); + break; + case VSWTCH: + attr.setControlChar(ControlChar.VTIME, e.getValue()); + break; + */ + case VSTATUS: + attr.setControlChar(ControlChar.VSTATUS, e.getValue()); + break; + case VDISCARD: + attr.setControlChar(ControlChar.VDISCARD, e.getValue()); + break; + case ECHO: + attr.setLocalFlag(LocalFlag.ECHO, e.getValue() != 0); + break; + case ICANON: + attr.setLocalFlag(LocalFlag.ICANON, e.getValue() != 0); + break; + case ISIG: + attr.setLocalFlag(LocalFlag.ISIG, e.getValue() != 0); + break; + case ICRNL: + attr.setInputFlag(InputFlag.ICRNL, e.getValue() != 0); + break; + case INLCR: + attr.setInputFlag(InputFlag.INLCR, e.getValue() != 0); + break; + case IGNCR: + attr.setInputFlag(InputFlag.IGNCR, e.getValue() != 0); + break; + case OCRNL: + attr.setOutputFlag(OutputFlag.OCRNL, e.getValue() != 0); + break; + case ONLCR: + attr.setOutputFlag(OutputFlag.ONLCR, e.getValue() != 0); + break; + case ONLRET: + attr.setOutputFlag(OutputFlag.ONLRET, e.getValue() != 0); + break; + case OPOST: + attr.setOutputFlag(OutputFlag.OPOST, e.getValue() != 0); + break; + } + } + terminal.setAttributes(attr); + PrintStream pout = new PrintStream(terminal.output()); + final CommandSession session = processor.createSession(terminal.input(), pout, pout); + for (Map.Entry<String, String> e : env.getEnv().entrySet()) { + session.put(e.getKey(), e.getValue()); + } + env.addSignalListener(new SignalListener() { + @Override + public void signal(Signal signal) { + terminal.setSize(new Size(Integer.parseInt(env.getEnv().get("COLUMNS")), + Integer.parseInt(env.getEnv().get("LINES")))); + terminal.raise(Terminal.Signal.WINCH); + } + }, Signal.WINCH); + Context context = new Context() { + @Override + public String getProperty(String name) { + return System.getProperty(name); + } + + @Override + public void exit() throws Exception { + destroy(); + } + }; + new Shell(context, processor, terminal).gosh(session, new String[]{"--login"}); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public void destroy() { + if (!closed) { + closed = true; + ShellFactoryImpl.flush(out, err); + ShellFactoryImpl.close(in, out, err); + callback.onExit(0); + } + } + + } + +}
Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/Ssh.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/Ssh.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/Ssh.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ssh/Ssh.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.felix.gogo.jline.ssh; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.apache.felix.service.command.CommandProcessor; +import org.apache.felix.service.command.CommandSession; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.server.Command; +import org.apache.sshd.server.ServerBuilder; +import org.apache.sshd.server.SshServer; +import org.apache.sshd.server.command.ScpCommandFactory; +import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; +import org.jline.builtins.Options; + +public class Ssh { + + public static final String[] functions = {"sshd"}; + + private static final int defaultPort = 2022; + + private final CommandProcessor processor; + private SshServer server; + private Object context; + private int port; + private String ip; + + public Ssh(CommandProcessor processor) { + this.processor = processor; + } + + public void sshd(CommandSession session, String[] argv) throws IOException { + final String[] usage = {"sshd - start an ssh server", + "Usage: sshd [-i ip] [-p port] start | stop | status", + " -i --ip=INTERFACE listen interface (default=127.0.0.1)", + " -p --port=PORT listen port (default=" + defaultPort + ")", + " -? --help show help"}; + + Options opt = Options.compile(usage).parse(argv); + List<String> args = opt.args(); + + if (opt.isSet("help") || args.isEmpty()) { + opt.usage(System.err); + return; + } + + String command = args.get(0); + + if ("start".equals(command)) { + if (server != null) { + throw new IllegalStateException("sshd is already running on port " + port); + } + ip = opt.get("ip"); + port = opt.getNumber("port"); + context = session.get(org.apache.felix.gogo.runtime.activator.Activator.CONTEXT); + start(); + status(); + } else if ("stop".equals(command)) { + if (server == null) { + throw new IllegalStateException("sshd is not running."); + } + stop(); + } else if ("status".equals(command)) { + status(); + } else { + throw opt.usageError("bad command: " + command); + } + + } + + private void status() { + if (server != null) { + System.out.println("sshd is running on " + ip + ":" + port); + } else { + System.out.println("sshd is not running."); + } + } + + private void start() throws IOException { + server = ServerBuilder.builder().build(); + server.setPort(port); + server.setHost(ip); + server.setShellFactory(new ShellFactoryImpl(processor)); + server.setCommandFactory(new ScpCommandFactory.Builder().withDelegate(new ShellCommandFactory(processor)).build()); + server.setSubsystemFactories(Collections.<NamedFactory<Command>>singletonList( + new SftpSubsystemFactory.Builder().build() + )); + server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider()); + server.start(); + } + + private void stop() throws IOException { + server.stop(); + } +} Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/BootException.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/BootException.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/BootException.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/BootException.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + +/** + * Class that implements a BootException.<br> + * This exception will flag a broken boot process, + * which expresses startup failure and unavailabilty + * of telnet service for the container application. + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + */ +public class BootException extends Exception { + + /** + * Constructor method for a BootException.<br> + * + * @param msg String that contains an understandable failure message. + */ + public BootException(String msg) { + super(msg); + }//constructor + +}//class BootException \ No newline at end of file Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Connection.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Connection.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Connection.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/Connection.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,257 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Class that implements a connection with this telnet daemon.<br> + * It is derived from java.lang.Thread, which reflects the architecture + * constraint of one thread per connection. This might seem a waste of + * resources, but as a matter of fact sharing threads would require a + * far more complex imlementation, due to the fact that telnet is not a + * stateless protocol (i.e. alive throughout a session of multiple requests + * and responses).<br> + * Each Connection instance is created by the listeners ConnectionManager + * instance, making it part of a threadgroup and passing in an associated + * ConnectionData instance, that holds vital information about the connection. + * Be sure to take a look at their documention.<br> + * <p/> + * Once the thread has started and is running, it will get a login + * shell instance from the ShellManager and run passing its own reference. + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + * @see ConnectionManager + * @see ConnectionData + */ +public abstract class Connection + extends Thread { + + private static final Logger LOG = Logger.getLogger(Connection.class.getName()); + private static int number; //unique number for a thread in the thread group + private boolean dead; + private List<ConnectionListener> listeners; + + //Associations + private ConnectionData connectionData; //associated information + + /** + * Constructs a TelnetConnection by invoking its parent constructor + * and setting of various members.<br> + * Subsequently instantiates the whole i/o subsystem, negotiating + * telnet protocol level options etc.<br> + * + * @param tcg ThreadGroup that this instance is running in. + * @param cd ConnectionData instance containing all vital information + * of this connection. + * @see ConnectionData + */ + public Connection(ThreadGroup tcg, ConnectionData cd) { + super(tcg, ("Connection" + (++number))); + + connectionData = cd; + //init the connection listeners for events + //(there should actually be only one or two) + listeners = new CopyOnWriteArrayList<ConnectionListener>(); + dead = false; + }//constructor + + /** + * Method overloaded to implement following behaviour: + * <ol> + * <li> On first entry, retrieve an instance of the configured + * login shell from the ShellManager and run it. + * <li> Handle a shell switch or close down disgracefully when + * problems (i.e. unhandled unchecked exceptions) occur in the + * running shell. + * </ol> + */ + public void run() { + try { + doRun(); + + } catch (Exception ex) { + LOG.log(Level.SEVERE, "run()", ex); //Handle properly + } finally { + //call close if not dead already + if (!dead) { + close(); + } + } + LOG.log(Level.FINE, "run():: Returning from " + this.toString()); + }//run + + protected abstract void doRun() throws Exception; + + protected abstract void doClose() throws Exception; + + /** + * Method to access the associated connection data. + * + * @return ConnectionData associated with the Connection instance. + * @see ConnectionData + */ + public ConnectionData getConnectionData() { + return connectionData; + }//getConnectionData + + /** + * Closes the connection and its underlying i/o and network + * resources.<br> + */ + public synchronized void close() { + if (dead) { + return; + } else { + try { + //connection dead + dead = true; + //close i/o + doClose(); + } catch (Exception ex) { + LOG.log(Level.SEVERE, "close()", ex); + //handle + } + try { + //close socket + connectionData.getSocket().close(); + } catch (Exception ex) { + LOG.log(Level.SEVERE, "close()", ex); + //handle + } + try { + //register closed connection in ConnectionManager + connectionData.getManager().registerClosedConnection(this); + } catch (Exception ex) { + LOG.log(Level.SEVERE, "close()", ex); + //handle + } + try { + //try to interrupt it + interrupt(); + } catch (Exception ex) { + LOG.log(Level.SEVERE, "close()", ex); + //handle + } + + + LOG.log(Level.FINE, "Closed " + this.toString() + " and inactive."); + } + }//close + + /** + * Returns if a connection has been closed.<br> + * + * @return the state of the connection. + */ + public boolean isActive() { + return !dead; + }//isClosed + + /****** Event handling ****************/ + + /** + * Method that registers a ConnectionListener with the + * Connection instance. + * + * @param cl ConnectionListener to be registered. + * @see ConnectionListener + */ + public void addConnectionListener(ConnectionListener cl) { + listeners.add(cl); + }//addConnectionListener + + /** + * Method that removes a ConnectionListener from the + * Connection instance. + * + * @param cl ConnectionListener to be removed. + * @see ConnectionListener + */ + public void removeConnectionListener(ConnectionListener cl) { + listeners.remove(cl); + }//removeConnectionListener + + + /** + * Method called by the io subsystem to pass on a + * "low-level" event. It will be properly delegated to + * all registered listeners. + * + * @param ce ConnectionEvent to be processed. + * @see ConnectionEvent + */ + public void processConnectionEvent(ConnectionEvent ce) { + for (ConnectionListener cl : listeners) { + switch (ce.getType()) { + case CONNECTION_IDLE: + cl.connectionIdle(ce); + break; + case CONNECTION_TIMEDOUT: + cl.connectionTimedOut(ce); + break; + case CONNECTION_LOGOUTREQUEST: + cl.connectionLogoutRequest(ce); + break; + case CONNECTION_BREAK: + cl.connectionSentBreak(ce); + break; + case CONNECTION_TERMINAL_GEOMETRY_CHANGED: + cl.connectionTerminalGeometryChanged(ce); + } + } + }//processConnectionEvent + +}//class Connection Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionData.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionData.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionData.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionData.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,449 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + +import java.net.InetAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * An utility class that is used to store and allow retrieval + * of all data associated with a connection. + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + * @see Connection + */ +public class ConnectionData { + + //Associations + private ConnectionManager connectionManager; //the connection's ConnectionManager + private Socket socket; //the connection's socket + private InetAddress address; //the connection's IP Address Object + private Map<String, String> environment; //the environment + + //Members + private String hostName; //cache for the hostname + private String hostAddress; //cache for the host ip + private int port; //port of the connection + private Locale locale; //locale of the connection + private long lastActivity; //timestamp for the last activity + private boolean warned; //warned flag + private String negotiatedTerminalType; //negotiated TerminalType as String + private int[] terminalGeometry; //negotiated terminal geometry + private boolean terminalGeometryChanged = true; //flag for changes in the terminal geometry + private String loginShell; //the login shell + private boolean lineMode = false; + + /** + * Constructs a ConnectionData instance storing vital + * information about a connection. + * + * @param sock Socket of the inbound connection. + */ + public ConnectionData(Socket sock, ConnectionManager cm) { + socket = sock; + connectionManager = cm; + address = sock.getInetAddress(); + setHostName(); + setHostAddress(); + setLocale(); + port = sock.getPort(); + //this will set a default geometry and terminal type for the terminal + terminalGeometry = new int[2]; + terminalGeometry[0] = 80; //width + terminalGeometry[1] = 25; //height + negotiatedTerminalType = "default"; + environment = new HashMap<String, String>(20); + //this will stamp the first activity for validity :) + activity(); + }//ConnectionData + + + /** + * Returns a reference to the ConnectionManager the + * connection is associated with. + * + * @return Reference to the associated ConnectionManager. + * @see ConnectionManager + */ + public ConnectionManager getManager() { + return connectionManager; + }//getManager + + /** + * Returns a reference to the socket the Connection + * is associated with. + * + * @return Reference to the associated Socket. + * @see java.net.Socket + */ + public Socket getSocket() { + return socket; + }//getSocket + + /** + * Returns the remote port to which the socket is connected. + * + * @return String that contains the remote port number to which the socket is connected. + */ + public int getPort() { + return port; + }//getPort + + /** + * Returns the fully qualified host name for the connection's IP address.<br> + * The name is cached on creation for performance reasons. Subsequent calls + * will not result in resolve queries. + * + * @return String that contains the fully qualified host name for this address. + */ + public String getHostName() { + return hostName; + }//getHostName + + /** + * Returns the IP address of the connection. + * + * @return String that contains the connection's IP address.<br> + * The format "%d.%d.%d.%d" is well known, where %d goes from zero to 255. + */ + public String getHostAddress() { + return hostAddress; + }//getHostAddress + + /** + * Returns the InetAddress object associated with the connection. + * + * @return InetAddress associated with the connection. + */ + public InetAddress getInetAddress() { + return address; + }//getInetAddress + + /** + * Returns the Locale object associated with the connection + * by carrying out a simple domain match. <br> + * This can either be effective, if your users are really + * home in the country they are connecting from, + * or ineffective if they are on the move getting connected + * from anywhere in the world.<br> + * <br> + * Yet this gives the chance of capturing a default locale + * and starting from some point. On application context + * this can be by far better handled, so be aware that + * it makes sense to spend some thoughts on that thing when you + * build your application. + * + * @return the Locale object "guessed" for the connection based + * on its host name. + */ + public Locale getLocale() { + return locale; + }//getLocale + + + /** + * Returns a timestamp of the last activity that happened on + * the associated connection. + * + * @return the timestamp as a long representing the difference, + * measured in milliseconds, between the current time and + * midnight, January 1, 1970 UTC. + */ + public long getLastActivity() { + return lastActivity; + }//getLastActivity + + + /** + * Sets a new timestamp to the actual time in millis + * retrieved from the System. This will remove an idle warning + * flag if it has been set. Note that you can use this behaviour + * to implement your own complex idle timespan policies within + * the context of your application.<br> + * The check frequency of the ConnectionManager should just be set + * according to the lowest time to warning and time to disconnect + * requirements. + */ + public void activity() { + warned = false; + lastActivity = System.currentTimeMillis(); + }//setLastActivity + + /** + * Returns the state of the idle warning flag, which + * will be true if a warning has been issued, and false + * if not. + * + * @return the state of the idle warning flag. + */ + public boolean isWarned() { + return warned; + }//isWarned + + /** + * Sets the state of the idle warning flag.<br> + * Note that this method will also update the + * the timestamp if the idle warning flag is removed, + * which means its kind of a second way to achieve the + * same thing as with the activity method. + * + * @param bool true if a warning is to be issued, + * false if to be removed. + * @see #activity() + */ + public void setWarned(boolean bool) { + warned = bool; + if (!bool) { + lastActivity = System.currentTimeMillis(); + } + }//setWarned + + /** + * Sets the terminal geometry data.<br> + * <em>This method should not be called explicitly + * by the application (i.e. the its here for the io subsystem).</em><br> + * A call will set the terminal geometry changed flag. + * + * @param width of the terminal in columns. + * @param height of the terminal in rows. + */ + public void setTerminalGeometry(int width, int height) { + terminalGeometry[0] = width; + terminalGeometry[1] = height; + terminalGeometryChanged = true; + }//setTerminalGeometry + + /** + * Returns the terminal geometry in an array of two integers. + * <ul> + * <li>index 0: Width in columns. + * <li>index 1: Height in rows. + * </ul> + * A call will reset the terminal geometry changed flag. + * + * @return integer array containing width and height. + */ + public int[] getTerminalGeometry() { + //we toggle the flag because the change should now be known + if (terminalGeometryChanged) terminalGeometryChanged = false; + return terminalGeometry; + }//getTerminalGeometry + + /** + * Returns the width of the terminal in columns for convenience. + * + * @return the number of columns. + */ + public int getTerminalColumns() { + return terminalGeometry[0]; + }//getTerminalColumns + + /** + * Returns the height of the terminal in rows for convenience. + * + * @return the number of rows. + */ + public int getTerminalRows() { + return terminalGeometry[1]; + }//getTerminalRows + + /** + * Returns the state of the terminal geometry changed flag, + * which will be true if it has been set, and false + * if not. + * + * @return the state of the terminal geometry changed flag. + */ + public boolean isTerminalGeometryChanged() { + return terminalGeometryChanged; + }//isTerminalGeometryChanged + + /** + * Returns the terminal type that has been negotiated + * between the telnet client and the telnet server, in + * of a String.<br> + * + * @return the negotiated terminal type as String. + */ + public String getNegotiatedTerminalType() { + return negotiatedTerminalType; + }//getNegotiatedTerminalType + + /** + * Sets the terminal type that has been negotiated + * between telnet client and telnet server, in form of + * a String.<br> + * <p/> + * <em>This method should not be called explicitly + * by the application (i.e. the its here for the io subsystem).</em><br> + * + * @param termtype the negotiated terminal type as String. + */ + public void setNegotiatedTerminalType(String termtype) { + negotiatedTerminalType = termtype; + }//setNegotiatedTerminalType + + /** + * Returns the hashmap for storing and + * retrieving environment variables to be passed + * between shells. + * + * @return a <tt>HashMap</tt> instance. + */ + public Map<String, String> getEnvironment() { + return environment; + }//getEnvironment + + /** + * Returns the login shell name. + * + * @return the shell name as string. + */ + public String getLoginShell() { + return loginShell; + }//getLoginShell + + /** + * Sets the login shell name. + * + * @param s the shell name as string. + */ + public void setLoginShell(String s) { + loginShell = s; + }//setLoginShell + + /** + * Tests if in line mode. + * + * @return true if in line mode, false otherwise + */ + public boolean isLineMode() { + return lineMode; + }//isLineMode + + /** + * Sets the line mode flag for the connection. + * Note that the setting will only be used at + * startup at the moment. + * + * @param b true if to be initialized in linemode, + * false otherwise. + */ + public void setLineMode(boolean b) { + lineMode = b; + }//setLineMode + + /** + * Mutator for HostName cache + */ + private void setHostName() { + hostName = address.getHostName(); + }//setHostName + + /** + * Mutator for HostAddress cache + */ + private void setHostAddress() { + hostAddress = address.getHostAddress(); + }//setHostAddress + + /** + * Mutator for Locale + * Sets a Locale derived from the hostname, + * or the default which is Locale.ENGLISH if something + * goes wrong. + * The localhost represents a problem for example :) + */ + private void setLocale() { + String country = getHostName(); + try { + country = country.substring(country.lastIndexOf(".") + 1); + if (country.equals("at")) { + locale = new Locale("de", "AT"); + } else if (country.equals("de")) { + locale = new Locale("de", "DE"); + } else if (country.equals("mx")) { + locale = new Locale("es", "MX"); + } else if (country.equals("es")) { + locale = new Locale("es", "ES"); + } else if (country.equals("it")) { + locale = Locale.ITALY; + } else if (country.equals("fr")) { + locale = Locale.FRANCE; + } else if (country.equals("uk")) { + locale = new Locale("en", "GB"); + } else if (country.equals("arpa")) { + locale = Locale.US; + } else if (country.equals("com")) { + locale = Locale.US; + } else if (country.equals("edu")) { + locale = Locale.US; + } else if (country.equals("gov")) { + locale = Locale.US; + } else if (country.equals("org")) { + locale = Locale.US; + } else if (country.equals("mil")) { + locale = Locale.US; + } else { + //default to english + locale = Locale.ENGLISH; + } + } catch (Exception ex) { + //default to english + locale = Locale.ENGLISH; + } + }//setLocale + +}//class ConnectionData Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionEvent.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionEvent.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionEvent.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionEvent.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + +/** + * Class implmenting a ConnectionEvent.<br> + * These events are used to communicate things that are + * supposed to be handled within the application context. + * These events are processed by the Connection instance + * calling upon its registered listeners. + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + * @see Connection + * @see ConnectionListener + */ +public class ConnectionEvent { + + private final Connection source; + private final Type type; + /** + * Constructs a new instance of a ConnectionEvent + * with a given source (Connection) and a given type. + * + * @param source Connection that represents the source of this event. + * @param type int that contains one of the defined event types. + */ + public ConnectionEvent(Connection source, Type type) { + this.type = type; + this.source = source; + }//constructor + + /** + * Accessor method returning the source of the + * ConnectionEvent instance. + * + * @return Connection representing the source. + */ + public Connection getSource() { + return source; + }//getSource + + /** + * Method that helps identifying the type. + * + * @return Event type. + */ + public Type getType() { + return type; + }//getType + + public enum Type { + /** + * Defines the connection idle event type.<br> + * It occurs if a connection has been idle exceeding + * the configured time to warning. + */ + CONNECTION_IDLE, + + /** + * Defines the connection timed out event type.<br> + * It occurs if a connection has been idle exceeding + * the configured time to warning and the configured time + * to timedout. + */ + CONNECTION_TIMEDOUT, + + /** + * Defines the connection requested logout event type.<br> + * It occurs if a connection requested disgraceful logout by + * sending a <Ctrl>-<D> key combination. + */ + CONNECTION_LOGOUTREQUEST, + + /** + * Defines the connection sent break event type.<br> + * It occurs when the connection sent a NVT BREAK. + */ + CONNECTION_BREAK, + + /** + * Defines the connection geometry event type. + * It occurs when the connection sent a NAWS. + */ + CONNECTION_TERMINAL_GEOMETRY_CHANGED; + } + + +}//class ConnectionEvent \ No newline at end of file Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionFilter.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionFilter.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionFilter.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionFilter.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + +import java.net.InetAddress; + +/** + * Interface defining a generic IP level connection + * filter.<br> + * Due to the fact that this task depends heavily on + * application context, I chose a very generic way + * of applying IP level connection filtering. + * <br><br> + * Implementations should consider following issues: + * <ul> + * <li>performance + * <li>administration (maybe via an admin shell) + * <li>logging denials + * </ul> + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + */ +public interface ConnectionFilter { + + /** + * Tests if a given ip address is allowed to connect. + * + * @param ip the address to be tested. + * @return true if allowed to connect, false otherwise. + */ + boolean isAllowed(InetAddress ip); + +}//interface ConnectionFilter \ No newline at end of file Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionListener.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionListener.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionListener.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionListener.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + + +/** + * Interface to be implemented if a class wants to + * qualify as a ConnectionListener.<br> + * Note that a Shell is per contract also forced to + * implement this interface. + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + * @see ConnectionEvent + */ +public interface ConnectionListener { + + /** + * Called when a CONNECTION_IDLE event occured. + * + * @param ce ConnectionEvent instance. + * @see ConnectionEvent.Type#CONNECTION_IDLE + */ + void connectionIdle(ConnectionEvent ce); + + /** + * Called when a CONNECTION_TIMEDOUT event occured. + * + * @param ce ConnectionEvent instance. + * @see ConnectionEvent.Type#CONNECTION_TIMEDOUT + */ + void connectionTimedOut(ConnectionEvent ce); + + /** + * Called when a CONNECTION_LOGOUTREQUEST occured. + * + * @param ce ConnectionEvent instance. + * @see ConnectionEvent.Type#CONNECTION_LOGOUTREQUEST + */ + void connectionLogoutRequest(ConnectionEvent ce); + + /** + * Called when a CONNECTION_BREAK event occured. + * + * @param ce ConnectionEvent instance. + * @see ConnectionEvent.Type#CONNECTION_BREAK + */ + void connectionSentBreak(ConnectionEvent ce); + + /** + * Called when a CONNECTION_TERMINAL_GEOMETRY_CHANGED event occured. + * + * @param ce ConnectionEvent instance. + * @see ConnectionEvent.Type#CONNECTION_TERMINAL_GEOMETRY_CHANGED + */ + void connectionTerminalGeometryChanged(ConnectionEvent ce); + +}//interface ConnectionListener \ No newline at end of file Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionManager.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionManager.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionManager.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/ConnectionManager.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,393 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Class that takes care for active and queued connection. + * Housekeeping is done also for connections that were just broken + * off, or exceeded their timeout. + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + */ +public abstract class ConnectionManager implements Runnable { + + private static Logger LOG = Logger.getLogger(ConnectionManager.class.getName()); + private final List<Connection> openConnections; + private Thread thread; + private ThreadGroup threadGroup; //ThreadGroup all connections run in + private Stack<Connection> closedConnections; + private ConnectionFilter connectionFilter; //reference to the connection filter + private int maxConnections; //maximum allowed connections stored from the properties + private int warningTimeout; //time to idle warning + private int disconnectTimeout; //time to idle diconnection + private int housekeepingInterval; //interval for managing cleanups + private String loginShell; + private boolean lineMode = false; + private boolean stopping = false; + + public ConnectionManager() { + threadGroup = new ThreadGroup(toString() + "Connections"); + closedConnections = new Stack<Connection>(); + openConnections = Collections.synchronizedList(new ArrayList<Connection>(100)); + } + + public ConnectionManager(int con, int timew, int timedis, int hoke, ConnectionFilter filter, String lsh, boolean lm) { + this(); + connectionFilter = filter; + loginShell = lsh; + lineMode = lm; + maxConnections = con; + warningTimeout = timew; + disconnectTimeout = timedis; + housekeepingInterval = hoke; + }//constructor + + /** + * Gets the active ConnectionFilter instance or + * returns null if no filter is set. + * + * @return the managers ConnectionFilter. + */ + public ConnectionFilter getConnectionFilter() { + return connectionFilter; + }//getConnectionFilter + + /** + * Set a connection filter for this + * ConnectionManager instance. The filter is used to handle + * IP level allow/deny of incoming connections. + * + * @param filter ConnectionFilter instance. + */ + public void setConnectionFilter(ConnectionFilter filter) { + connectionFilter = filter; + }//setConnectionFilter + + /** + * Returns the number of open connections. + * @return the number of open connections as <tt>int</tt>. + */ + public int openConnectionCount() { + return openConnections.size(); + }//openConnectionCount + + /** + * Returns the {@link Connection} at the given index. + * @param idx + * @return + */ + public Connection getConnection(int idx) { + synchronized (openConnections) { + return openConnections.get(idx); + } + }//getConnection + + /** + * Get all {@link Connection} instances with the given + * <tt>InetAddress</tt>. + * + * @return all {@link Connection} instances with the given + * <tt>InetAddress</tt>. + */ + public Connection[] getConnectionsByAdddress(InetAddress addr) { + ArrayList<Connection> l = new ArrayList<Connection>(); + synchronized (openConnections) { + for (Connection connection : openConnections) { + if (connection.getConnectionData().getInetAddress().equals(addr)) { + l.add(connection); + } + } + } + Connection[] conns = new Connection[l.size()]; + return l.toArray(conns); + }//getConnectionsByAddress + + /** + * Starts this <tt>ConnectionManager</tt>. + */ + public void start() { + thread = new Thread(this); + thread.start(); + }//start + + /** + * Stops this <tt>ConnectionManager</tt>. + */ + public void stop() { + LOG.log(Level.FINE, "stop()::" + this.toString()); + stopping = true; + //wait for thread to die + try { + if (thread != null) { + thread.join(); + } + } catch (InterruptedException iex) { + LOG.log(Level.SEVERE, "stop()", iex); + } + synchronized (openConnections) { + for (Connection tc : openConnections) { + try { + //maybe write a disgrace to the socket? + tc.close(); + } catch (Exception exc) { + LOG.log(Level.SEVERE, "stop()", exc); + } + } + openConnections.clear(); + } + LOG.log(Level.FINE, "stop():: Stopped " + this.toString()); + }//stop + + /** + * Method that that tries to connect an incoming request. + * Properly queueing. + * + * @param insock Socket thats representing the incoming connection. + */ + public void makeConnection(Socket insock) { + LOG.log(Level.FINE, "makeConnection()::" + insock.toString()); + if (connectionFilter == null || connectionFilter.isAllowed(insock.getInetAddress())) { + //we create the connection data object at this point to + //store certain information there. + ConnectionData newCD = new ConnectionData(insock, this); + newCD.setLoginShell(loginShell); + newCD.setLineMode(lineMode); + if (openConnections.size() < maxConnections) { + //create a new Connection instance + Connection con = createConnection(threadGroup, newCD); + //log the newly created connection + Object[] args = {openConnections.size() + 1}; + LOG.info(MessageFormat.format("connection #{0,number,integer} made.", args)); + //register it for being managed + synchronized (openConnections) { + openConnections.add(con); + } + //start it + con.start(); + } + } else { + LOG.info("makeConnection():: Active Filter blocked incoming connection."); + try { + insock.close(); + } catch (IOException ex) { + //do nothing or log. + } + } + }//makeConnection + + protected abstract Connection createConnection(ThreadGroup threadGroup, ConnectionData newCD); + + /** + * Periodically does following work: + * <ul> + * <li> cleaning up died connections. + * <li> checking managed connections if they are working properly. + * <li> checking the open connections. + * </ul> + */ + public void run() { + //housekeep connections + try { + do { + //clean up and close all broken connections + //cleanupBroken(); + //clean up closed connections + cleanupClosed(); + //check all active connections + checkOpenConnections(); + //sleep interval + Thread.sleep(housekeepingInterval); + } while (!stopping); + + } catch (Exception e) { + LOG.log(Level.SEVERE, "run()", e); + } + LOG.log(Level.FINE, "run():: Ran out " + this.toString()); + }//run + + /* + private void cleanupBroken() { + //cleanup loop + while (!m_BrokenConnections.isEmpty()) { + Connection nextOne = (Connection) m_BrokenConnections.pop(); + log.info("cleanupBroken():: Closing broken connection " + nextOne.toString()); + //fire logoff event for shell site cleanup , beware could hog the daemon thread + nextOne.processConnectionEvent(new ConnectionEvent(nextOne, ConnectionEvent.CONNECTION_BROKEN)); + //close the connection, will be automatically registered as closed + nextOne.close(); + } + }//cleanupBroken + */ + private void cleanupClosed() { + if (stopping) { + return; + } + //cleanup loop + while (!closedConnections.isEmpty()) { + Connection nextOne = closedConnections.pop(); + LOG.info("cleanupClosed():: Removing closed connection " + nextOne.toString()); + synchronized (openConnections) { + openConnections.remove(nextOne); + } + } + }//cleanupBroken + + private void checkOpenConnections() { + if (stopping) { + return; + } + //do routine checks on active connections + synchronized (openConnections) { + for (Connection conn : openConnections) { + ConnectionData cd = conn.getConnectionData(); + //check if it is dead and remove it. + if (!conn.isActive()) { + registerClosedConnection(conn); + continue; + } + /* Timeouts check */ + //first we caculate the inactivity time + long inactivity = System.currentTimeMillis() - cd.getLastActivity(); + //now we check for warning and disconnection + if (inactivity > warningTimeout) { + //..and for disconnect + if (inactivity > (disconnectTimeout + warningTimeout)) { + //this connection needs to be disconnected :) + LOG.log(Level.FINE, "checkOpenConnections():" + conn.toString() + " exceeded total timeout."); + //fire logoff event for shell site cleanup , beware could hog the daemon thread + conn.processConnectionEvent(new ConnectionEvent(conn, ConnectionEvent.Type.CONNECTION_TIMEDOUT)); + //conn.close(); + } else { + //this connection needs to be warned :) + if (!cd.isWarned()) { + LOG.log(Level.FINE, "checkOpenConnections():" + conn.toString() + " exceeded warning timeout."); + cd.setWarned(true); + //warning event is fired but beware this could hog the daemon thread!! + conn.processConnectionEvent(new ConnectionEvent(conn, ConnectionEvent.Type.CONNECTION_IDLE)); + } + } + } + } + /* end Timeouts check */ + } + }//checkConnections + + public void registerClosedConnection(Connection con) { + if (stopping) { + return; + } + if (!closedConnections.contains(con)) { + LOG.log(Level.FINE, "registerClosedConnection()::" + con.toString()); + closedConnections.push(con); + } + }//unregister + + public int getDisconnectTimeout() { + return disconnectTimeout; + } + + public void setDisconnectTimeout(int disconnectTimeout) { + this.disconnectTimeout = disconnectTimeout; + } + + public int getHousekeepingInterval() { + return housekeepingInterval; + } + + public void setHousekeepingInterval(int housekeepingInterval) { + this.housekeepingInterval = housekeepingInterval; + } + + public boolean isLineMode() { + return lineMode; + } + + public void setLineMode(boolean lineMode) { + this.lineMode = lineMode; + } + + public String getLoginShell() { + return loginShell; + } + + public void setLoginShell(String loginShell) { + this.loginShell = loginShell; + } + + public int getMaxConnections() { + return maxConnections; + } + + public void setMaxConnections(int maxConnections) { + this.maxConnections = maxConnections; + } + + public int getWarningTimeout() { + return warningTimeout; + } + + public void setWarningTimeout(int warningTimeout) { + this.warningTimeout = warningTimeout; + } + +}//class ConnectionManager Added: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/PortListener.java URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/PortListener.java?rev=1735995&view=auto ============================================================================== --- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/PortListener.java (added) +++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/telnet/PortListener.java Mon Mar 21 16:53:06 2016 @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/*** + * Java TelnetD library (embeddable telnet daemon) + * Copyright (c) 2000-2005 Dieter Wimberger + * All rights reserved. + * <p/> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * <p/> + * Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * <p/> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ***/ + +package org.apache.felix.gogo.jline.telnet; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.text.MessageFormat; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Class that implements a <tt>PortListener</tt>.<br> + * If available, it accepts incoming connections and passes them + * to an associated <tt>ConnectionManager</tt>. + * + * @author Dieter Wimberger + * @version 2.0 (16/07/2006) + * @see ConnectionManager + */ +public class PortListener + implements Runnable { + + private static final Logger LOG = Logger.getLogger(PortListener.class.getName()); + private static final String logmsg = + "Listening to Port {0,number,integer} with a connectivity queue size of {1,number,integer}."; + private String name; + private int port; //port number running on + private int floodProtection; //flooding protection + private ServerSocket serverSocket = null; //server socket + private Thread thread; + private ConnectionManager connectionManager; //connection management thread + private boolean stopping = false; + private boolean available; //Flag for availability + + /** + * Constructs a PortListener instance.<br> + * + * @param port int that specifies the port number of the server socket. + * @param floodprot that specifies the server socket queue size. + */ + public PortListener(String name, int port, int floodprot) { + this.name = name; + available = false; + this.port = port; + floodProtection = floodprot; + }//constructor + + /** + * Returns the name of this <tt>PortListener</tt>. + * + * @return the name as <tt>String</tt>. + */ + public String getName() { + return name; + }//getName + + /** + * Tests if this <tt>PortListener</tt> is available. + * + * @return true if available, false otherwise. + */ + public boolean isAvailable() { + return available; + }//isAvailable + + /** + * Sets the availability flag of this <tt>PortListener</tt>. + * + * @param b true if to be available, false otherwise. + */ + public void setAvailable(boolean b) { + available = b; + }//setAvailable + + /** + * Starts this <tt>PortListener</tt>. + */ + public void start() { + LOG.log(Level.FINE, "start()"); + thread = new Thread(this); + thread.start(); + available = true; + }//start + + /** + * Stops this <tt>PortListener</tt>, and returns + * when everything was stopped successfully. + */ + public void stop() { + LOG.log(Level.FINE, "stop()::" + this.toString()); + //flag stop + stopping = true; + available = false; + //take down all connections + connectionManager.stop(); + + //close server socket + try { + serverSocket.close(); + } catch (IOException ex) { + LOG.log(Level.SEVERE, "stop()", ex); + } + + //wait for thread to die + try { + thread.join(); + } catch (InterruptedException iex) { + LOG.log(Level.SEVERE, "stop()", iex); + } + + LOG.info("stop()::Stopped " + this.toString()); + }//stop + + /** + * Listen constantly to a server socket and handles incoming connections + * through the associated {a:link ConnectionManager}. + * + * @see ConnectionManager + */ + public void run() { + try { + /* + A server socket is opened with a connectivity queue of a size specified + in int floodProtection. Concurrent login handling under normal circumstances + should be handled properly, but denial of service attacks via massive parallel + program logins should be prevented with this. + */ + serverSocket = new ServerSocket(port, floodProtection); + + //log entry + LOG.info(MessageFormat.format(logmsg, port, floodProtection)); + + do { + try { + Socket s = serverSocket.accept(); + if (available) { + connectionManager.makeConnection(s); + } else { + //just shut down the socket + s.close(); + } + } catch (SocketException ex) { + if (stopping) { + //server socket was closed blocked in accept + LOG.log(Level.FINE, "run(): ServerSocket closed by stop()"); + } else { + LOG.log(Level.SEVERE, "run()", ex); + } + } + } while (!stopping); + + } catch (IOException e) { + LOG.log(Level.SEVERE, "run()", e); + } + LOG.log(Level.FINE, "run(): returning."); + }//run + + /** + * Returns reference to ConnectionManager instance associated + * with the PortListener. + * + * @return the associated ConnectionManager. + */ + public ConnectionManager getConnectionManager() { + return connectionManager; + }//getConnectionManager + + public void setConnectionManager(ConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + +}//class PortListener
