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:[email protected]">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);
+ }
+ }
+
+}