Author: gnodet
Date: Wed Mar 31 14:06:44 2010
New Revision: 929545

URL: http://svn.apache.org/viewvc?rev=929545&view=rev
Log:
SSHD-81: Support for X11 forwarding on the server side

Added:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/x11/
    
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java
Modified:
    
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/session/ServerSession.java

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=929545&r1=929544&r2=929545&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
 Wed Mar 31 14:06:44 2010
@@ -50,6 +50,7 @@ import org.apache.sshd.server.SessionAwa
 import org.apache.sshd.server.Signal;
 import org.apache.sshd.server.SignalListener;
 import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.x11.X11ForwardSupport;
 
 /**
  * TODO Add javadoc
@@ -498,7 +499,19 @@ public class ChannelSession extends Abst
             return true;
         }
 
-        // TODO: start x11 forwarding
+        String display = ((ServerSession) 
session).createX11Display(buffer.getBoolean(), buffer.getString(),
+                                                                    
buffer.getString(), buffer.getInt());
+        if (display == null) {
+            if (wantReply) {
+                buffer = 
session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE, 0);
+                buffer.putInt(recipient);
+                session.writePacket(buffer);
+            }
+            return true;
+        }
+
+        addEnvVariable(X11ForwardSupport.ENV_DISPLAY, display);
+
         if (wantReply) {
             buffer = 
session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS, 0);
             buffer.putInt(recipient);

Modified: 
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java?rev=929545&r1=929544&r2=929545&view=diff
==============================================================================
--- 
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
 (original)
+++ 
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
 Wed Mar 31 14:06:44 2010
@@ -41,6 +41,7 @@ import org.apache.sshd.common.util.Buffe
 import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.UserAuth;
 import org.apache.sshd.server.channel.OpenChannelException;
+import org.apache.sshd.server.x11.X11ForwardSupport;
 
 /**
  *
@@ -68,6 +69,7 @@ public class ServerSession extends Abstr
     private boolean allowMoreSessions = true;
     private final TcpipForwardSupport tcpipForward;
     private final AgentForwardSupport agentForward;
+    private final X11ForwardSupport x11Forward;
 
     private List<NamedFactory<UserAuth>> userAuthFactories;
 
@@ -81,6 +83,7 @@ public class ServerSession extends Abstr
         authTimeout = getIntProperty(FactoryManager.AUTH_TIMEOUT, authTimeout);
         tcpipForward = new TcpipForwardSupport(this);
         agentForward = new AgentForwardSupport(this);
+        x11Forward = new X11ForwardSupport (this);
         log.info("Session created...");
         sendServerIdentification();
         sendKexInit();
@@ -91,6 +94,7 @@ public class ServerSession extends Abstr
         unscheduleAuthTimer();
         tcpipForward.close();
         agentForward.close();
+        x11Forward.close ();
         return super.close(immediately);
     }
 
@@ -458,5 +462,8 @@ public class ServerSession extends Abstr
         return agentForward.initialize();
     }
 
+    public String createX11Display(boolean singleConnection, String 
authenticationProtocol, String authenticationCookie, int screen) throws 
IOException {
+        return x11Forward.createDisplay(singleConnection, 
authenticationProtocol, authenticationCookie, screen);
+    }
 
 }

Added: 
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java?rev=929545&view=auto
==============================================================================
--- 
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java
 (added)
+++ 
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/x11/X11ForwardSupport.java
 Wed Mar 31 14:06:44 2010
@@ -0,0 +1,228 @@
+/*
+ * 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.sshd.server.x11;
+
+import java.io.IOException;
+import java.net.BindException;
+import java.net.InetSocketAddress;
+import java.util.EnumSet;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoEventType;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.executor.ExecutorFilter;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.sshd.client.channel.AbstractClientChannel;
+import org.apache.sshd.client.future.DefaultOpenFuture;
+import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.session.ServerSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class X11ForwardSupport extends IoHandlerAdapter {
+
+    private static String xauthCommand = 
System.getProperty("sshd.xauthCommand", "xauth");
+
+    public static final int X11_DISPLAY_OFFSET = 10;
+    public static final int MAX_DISPLAYS = 1000;
+
+    /**
+     * Key for the user DISPLAY variable
+     */
+    public static final String ENV_DISPLAY = "DISPLAY";
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final ServerSession session;
+    private IoAcceptor acceptor;
+
+    public X11ForwardSupport(ServerSession session) {
+        super();
+        this.session = session;
+    }
+
+    public synchronized void initialize() {
+        if (this.acceptor == null) {
+            NioSocketAcceptor acceptor = new NioSocketAcceptor();
+            acceptor.setHandler(this);
+            acceptor.setReuseAddress(true);
+            acceptor.getFilterChain().addLast(
+                    "executor",
+                    new ExecutorFilter(EnumSet.complementOf(
+                            EnumSet.of(IoEventType.SESSION_CREATED)).toArray(
+                            new IoEventType[0])));
+            this.acceptor = acceptor;
+        }
+    }
+
+    public synchronized void close() {
+        if (acceptor != null) {
+            acceptor.dispose();
+            acceptor = null;
+        }
+    }
+
+    public synchronized String createDisplay(boolean singleConnection,
+                                             String authenticationProtocol, 
String authenticationCookie,
+                                             int screen) throws IOException {
+
+        initialize();
+
+        int displayNumber, port;
+        InetSocketAddress addr = null;
+
+        for (displayNumber = X11_DISPLAY_OFFSET; displayNumber < MAX_DISPLAYS; 
displayNumber++) {
+            port = 6000 + displayNumber;
+            try {
+                addr = new InetSocketAddress("127.0.0.1", port);
+                acceptor.bind(addr);
+                break;
+            } catch (BindException bindErr) {
+                // try until bind succesful or max is reached
+            }
+        }
+
+        if (displayNumber >= MAX_DISPLAYS) {
+            log.error("Failed to allocate internet-domain X11 display 
socket.");
+            if (acceptor.getLocalAddresses().isEmpty()) {
+                close();
+            }
+            return null;
+        }
+
+        // only support non windows systems
+        String os = System.getProperty("os.name").toLowerCase();
+        if (os.indexOf("windows") < 0) {
+            try {
+                String authDisplay = "unix:" + displayNumber + "." + screen;
+                Process p = new ProcessBuilder(xauthCommand, "remove", 
authDisplay).start();
+                int result = p.waitFor();
+                if (result == 0) {
+                    p = new ProcessBuilder(xauthCommand, "add", authDisplay, 
authenticationProtocol, authenticationCookie).start();
+                    result = p.waitFor();
+                }
+            } catch (Exception e) {
+                log.error("Could not run xauth", e);
+                return null;
+            }
+            return "localhost:" + displayNumber + "." + screen;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void sessionCreated(IoSession session) throws Exception {
+        ChannelForwardedX11 channel = new ChannelForwardedX11(session);
+        session.setAttribute(ChannelForwardedX11.class, channel);
+        this.session.registerChannel(channel);
+        OpenFuture future = channel.open().await();
+        Throwable t = future.getException();
+        if (t instanceof Exception) {
+            throw (Exception) t;
+        } else if (t != null) {
+            throw new Exception(t);
+        }
+    }
+
+    @Override
+    public void sessionClosed(IoSession session) throws Exception {
+        ChannelForwardedX11 channel = (ChannelForwardedX11) 
session.getAttribute(ChannelForwardedX11.class);
+        channel.close(false);
+    }
+
+    @Override
+    public void messageReceived(IoSession session, Object message) throws 
Exception {
+        ChannelForwardedX11 channel = (ChannelForwardedX11) 
session.getAttribute(ChannelForwardedX11.class);
+        IoBuffer ioBuffer = (IoBuffer) message;
+        int r = ioBuffer.remaining();
+        byte[] b = new byte[r];
+        ioBuffer.get(b, 0, r);
+        channel.getOut().write(b, 0, r);
+        channel.getOut().flush();
+    }
+
+    @Override
+    public void exceptionCaught(IoSession session, Throwable cause) throws 
Exception {
+        cause.printStackTrace();
+        session.close(false);
+    }
+
+    public static class ChannelForwardedX11 extends AbstractClientChannel {
+        private final IoSession serverSession;
+
+        public ChannelForwardedX11(IoSession serverSession) {
+            super("x11");
+            this.serverSession = serverSession;
+        }
+
+        public synchronized OpenFuture open() throws Exception {
+            InetSocketAddress remote = (InetSocketAddress) 
serverSession.getRemoteAddress();
+            if (closeFuture.isClosed()) {
+                throw new SshException("Session has been closed");
+            }
+            openFuture = new DefaultOpenFuture(lock);
+            log.info("Send SSH_MSG_CHANNEL_OPEN on channel {}", id);
+            Buffer buffer = 
session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_OPEN, 0);
+            buffer.putString(type);
+            buffer.putInt(id);
+            buffer.putInt(localWindow.getSize());
+            buffer.putInt(localWindow.getPacketSize());
+            buffer.putString(remote.getHostName());
+            buffer.putInt(remote.getPort());
+            session.writePacket(buffer);
+            return openFuture;
+        }
+
+        @Override
+        protected synchronized void doOpen() throws Exception {
+            out = new ChannelOutputStream(this, remoteWindow, log, 
SshConstants.Message.SSH_MSG_CHANNEL_DATA);
+        }
+
+        @Override
+        protected synchronized void doClose() {
+            serverSession.close(false);
+            super.doClose();
+        }
+
+        protected synchronized void doWriteData(byte[] data, int off, int len) 
throws IOException {
+            IoBuffer buf = IoBuffer.allocate(len);
+            buf.put(data, off, len);
+            buf.flip();
+            localWindow.consumeAndCheck(len);
+            serverSession.write(buf);
+        }
+
+        @Override
+        public void handleEof() throws IOException {
+            super.handleEof();
+            serverSession.close(false);
+        }
+    }
+
+}


Reply via email to