You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2009/11/19 14:05:19 UTC

svn commit: r882134 - in /mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd: ./ common/channel/ common/util/ server/channel/ server/shell/

Author: gnodet
Date: Thu Nov 19 13:05:18 2009
New Revision: 882134

URL: http://svn.apache.org/viewvc?rev=882134&view=rev
Log:
SSHD-63: Better support for controlling the console when launching external processes for shells

Removed:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java
Modified:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java Thu Nov 19 13:05:18 2009
@@ -23,6 +23,7 @@
 import java.security.InvalidKeyException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -31,10 +32,16 @@
 import org.apache.mina.core.service.IoAcceptor;
 import org.apache.mina.core.session.IoSession;
 import org.apache.mina.core.session.IoSessionConfig;
-import org.apache.mina.transport.socket.DefaultSocketSessionConfig;
-import org.apache.mina.transport.socket.SocketSessionConfig;
 import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
-import org.apache.sshd.common.*;
+import org.apache.sshd.common.AbstractFactoryManager;
+import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.Compression;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.KeyExchange;
+import org.apache.sshd.common.Mac;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
 import org.apache.sshd.common.cipher.AES128CBC;
 import org.apache.sshd.common.cipher.AES192CBC;
 import org.apache.sshd.common.cipher.AES256CBC;
@@ -43,7 +50,6 @@
 import org.apache.sshd.common.compression.CompressionNone;
 import org.apache.sshd.common.future.CloseFuture;
 import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.mac.HMACMD5;
 import org.apache.sshd.common.mac.HMACMD596;
 import org.apache.sshd.common.mac.HMACSHA1;
@@ -55,13 +61,21 @@
 import org.apache.sshd.common.signature.SignatureDSA;
 import org.apache.sshd.common.signature.SignatureRSA;
 import org.apache.sshd.common.util.SecurityUtils;
-import org.apache.sshd.server.*;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.TcpIpForwardFilter;
+import org.apache.sshd.server.UserAuth;
 import org.apache.sshd.server.auth.UserAuthPassword;
 import org.apache.sshd.server.auth.UserAuthPublicKey;
 import org.apache.sshd.server.channel.ChannelDirectTcpip;
 import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.kex.DHG1;
 import org.apache.sshd.server.kex.DHG14;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.session.SessionFactory;
 import org.apache.sshd.server.shell.ProcessShellFactory;
@@ -266,6 +280,10 @@
      * Stop the SSH server.  This method will block until all resources are actually disposed.
      */
     public void stop() throws InterruptedException {
+        stop(false);
+    }
+
+    public void stop(boolean immediately) throws InterruptedException {
         acceptor.setCloseOnDeactivation(false);
         acceptor.unbind();
         List<AbstractSession> sessions = new ArrayList<AbstractSession>();
@@ -282,9 +300,11 @@
             }
         };
         for (AbstractSession session : sessions) {
-            session.close(false).addListener(listener);
+            session.close(immediately).addListener(listener);
+        }
+        if (!immediately) {
+            latch.await();
         }
-        latch.await();
         acceptor.dispose();
         acceptor = null;
     }
@@ -399,12 +419,22 @@
             System.exit(-1);
         }
 
+        System.err.println("Starting SSHD on port " + port);
+
         SshServer sshd = SshServer.setUpDefaultServer();
         sshd.setPort(port);
-        sshd.setKeyPairProvider(new FileKeyPairProvider(new String[] { "/etc/ssh_host_rsa_key", "/etc/ssh_host_dsa_key" }));
-        //sshd.setShellFactory(new ProcessShellFactory(new String[] { "/usr/bin/login", "-f", "-h", "localhost", "$USER", "/bin/sh", "-i" }));
-        sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", "-l" }));
-        //sshd.setPasswordAuthenticator(new PAMPasswordAuthenticator());
+        if (SecurityUtils.isBouncyCastleRegistered()) {
+            sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider("key.pem"));
+        } else {
+            sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser"));
+        }
+        if (System.getProperty("os.name").toLowerCase().indexOf("windows") < 0) {
+            sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", "-l" },
+                                 EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr)));
+        } else {
+            sshd.setShellFactory(new ProcessShellFactory(new String[] { "cmd.exe "},
+                                 EnumSet.of(ProcessShellFactory.TtyOptions.Echo, ProcessShellFactory.TtyOptions.ICrNl, ProcessShellFactory.TtyOptions.ONlCr)));
+        }
         sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
             public boolean authenticate(String username, String password, ServerSession session) {
                 return username != null && username.equals(password);

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java Thu Nov 19 13:05:18 2009
@@ -46,6 +46,13 @@
         this.localWindow = localWindow;
     }
 
+    @Override
+    public int available() throws IOException {
+        synchronized (buffer) {
+            return buffer.available();
+        }
+    }
+
     public int read() throws IOException {
         synchronized (b) {
             int l = read(b, 0, 1);

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java Thu Nov 19 13:05:18 2009
@@ -40,10 +40,13 @@
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.IoUtils;
-import org.apache.sshd.common.util.LfToCrLfFilterOutputStream;
 import org.apache.sshd.common.util.LoggingFilterOutputStream;
-import org.apache.sshd.server.*;
+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;
 
 /**
@@ -424,10 +427,6 @@
         // Wrap in logging filters
         out = new LoggingFilterOutputStream(out, "OUT:", log);
         err = new LoggingFilterOutputStream(err, "ERR:", log);
-        if (getPtyModeValue(PtyMode.ONLCR) != 0) {
-            out = new LfToCrLfFilterOutputStream(out);
-            err = new LfToCrLfFilterOutputStream(err);
-        }
         in = new ChannelPipedInputStream(localWindow);
         shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
         shellIn = new LoggingFilterOutputStream(shellIn, "IN: ", log);

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java Thu Nov 19 13:05:18 2009
@@ -18,33 +18,51 @@
  */
 package org.apache.sshd.server.shell;
 
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.EnumSet;
 import java.util.Map;
 
 import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.server.Command;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * A {@link ShellFactory} that will create a new process and bridge
+ * A {@link Factory} of {@link Command} that will create a new process and bridge
  * the streams.
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public class ProcessShellFactory implements Factory<Command> {
 
+    public enum TtyOptions {
+        Echo,
+        INlCr,
+        ICrNl,
+        ONlCr,
+        OCrNl
+    }
+
     private static final Logger LOG = LoggerFactory.getLogger(ProcessShellFactory.class);
 
     private String[] command;
+    private EnumSet<TtyOptions> ttyOptions;
 
     public ProcessShellFactory() {
     }
 
     public ProcessShellFactory(String[] command) {
+        this(command, EnumSet.noneOf(TtyOptions.class));
+    }
+
+    public ProcessShellFactory(String[] command, EnumSet<TtyOptions> ttyOptions) {
         this.command = command;
+        this.ttyOptions = ttyOptions;
     }
 
     public String[] getCommand() {
@@ -56,17 +74,15 @@
     }
 
     public Command create() {
-        return new InvertedShellWrapper(new ProcessShell(command));
+        return new InvertedShellWrapper(new ProcessShell());
     }
 
-    public static class ProcessShell implements InvertedShell {
+    public class ProcessShell implements InvertedShell {
 
-        private String[] command;
         private Process process;
-
-        public ProcessShell(String[] command) {
-            this.command = command;
-        }
+        private TtyFilterOutputStream in;
+        private TtyFilterInputStream out;
+        private TtyFilterInputStream err;
 
         public void start(Map<String,String> env) throws IOException {
             String[] cmds = new String[command.length];
@@ -83,18 +99,21 @@
             }
             LOG.info("Starting shell with command: '{}' and env: {}", builder.command(), builder.environment());
             process = builder.start();
+            out = new TtyFilterInputStream(process.getInputStream());
+            err = new TtyFilterInputStream(process.getErrorStream());
+            in = new TtyFilterOutputStream(process.getOutputStream(), err);
         }
 
         public OutputStream getInputStream() {
-            return process.getOutputStream();
+            return in;
         }
 
         public InputStream getOutputStream() {
-            return process.getInputStream();
+            return out;
         }
 
         public InputStream getErrorStream() {
-            return process.getErrorStream();
+            return err;
         }
 
         public boolean isAlive() {
@@ -113,6 +132,84 @@
         public void destroy() {
             process.destroy();
         }
+
+        protected class TtyFilterInputStream extends FilterInputStream {
+            private Buffer buffer;
+            private int lastChar;
+            public TtyFilterInputStream(InputStream in) {
+                super(in);
+                buffer = new Buffer(32);
+            }
+            synchronized void write(int c) {
+                buffer.putByte((byte) c);
+            }
+            synchronized void write(byte[] buf, int off, int len) {
+                buffer.putBytes(buf, off, len);
+            }
+            @Override
+            public int available() throws IOException {
+                return super.available() + buffer.available();
+            }
+            @Override
+            public synchronized int read() throws IOException {
+                int c;
+                if (buffer.available() > 0) {
+                    c = buffer.getByte();
+                    buffer.compact();
+                } else {
+                    c = super.read();
+                }
+                if (c == '\n' && ttyOptions.contains(TtyOptions.ONlCr) && lastChar != '\r') {
+                    c = '\r';
+                    Buffer buf = new Buffer();
+                    buf.putByte((byte) '\n');
+                    buf.putBuffer(buffer);
+                    buffer = buf;
+                } else if (c == '\r' && ttyOptions.contains(TtyOptions.OCrNl)) {
+                    c = '\n';
+                }
+                lastChar = c;
+                return c;
+            }
+            @Override
+            public synchronized int read(byte[] b, int off, int len) throws IOException {
+                if (buffer.available() == 0) {
+                    int nb = super.read(b, off, len);
+                    buffer.putRawBytes(b, off, nb);
+                }
+                int nb = 0;
+                while (nb < len && buffer.available() > 0) {
+                    b[off + nb++] = (byte) read();
+                }
+                return nb;
+            }
+        }
+
+        protected class TtyFilterOutputStream extends FilterOutputStream {
+            private TtyFilterInputStream echo;
+            public TtyFilterOutputStream(OutputStream out, TtyFilterInputStream echo) {
+                super(out);
+                this.echo = echo;
+            }
+            @Override
+            public void write(int c) throws IOException {
+                if (c == '\n' && ttyOptions.contains(TtyOptions.INlCr)) {
+                    c = '\r';
+                } else if (c == '\r' && ttyOptions.contains(TtyOptions.ICrNl)) {
+                    c = '\n';
+                }
+                super.write(c);
+                if (ttyOptions.contains(TtyOptions.Echo)) {
+                    echo.write(c);
+                }
+            }
+            @Override
+            public void write(byte[] b, int off, int len) throws IOException {
+                for (int i = off; i < len; i++) {
+                    write(b[i]);
+                }
+            }
+        }
     }
 
 }