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 2008/12/04 19:59:36 UTC
svn commit: r723396 [10/12] - in /mina/sshd/trunk: ./ src/ src/docs/
src/main/ src/main/filtered-resources/ src/main/filtered-resources/org/
src/main/filtered-resources/org/apache/
src/main/filtered-resources/org/apache/sshd/ src/main/java/ src/main/ja...
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,102 @@
+/*
+ * 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.common.signature;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+
+public class SignatureDSA extends AbstractSignature {
+
+ public static class Factory implements NamedFactory<Signature> {
+
+ public String getName() {
+ return KeyPairProvider.SSH_DSS;
+ }
+
+ public Signature create() {
+ return new SignatureDSA();
+ }
+
+ }
+
+ public SignatureDSA() {
+ super("SHA1withDSA");
+ }
+
+ public byte[] sign() throws Exception {
+ byte[] sig = signature.sign();
+
+ // sig is in ASN.1
+ // SEQUENCE::={ r INTEGER, s INTEGER }
+ int len = 0;
+ int index = 3;
+ len = sig[index++] & 0xff;
+ byte[] r = new byte[len];
+ System.arraycopy(sig, index, r, 0, r.length);
+ index = index + len + 1;
+ len = sig[index++] & 0xff;
+ byte[] s = new byte[len];
+ System.arraycopy(sig, index, s, 0, s.length);
+
+ byte[] result = new byte[40];
+
+ // result must be 40 bytes, but length of r and s may not be 20 bytes
+
+ System.arraycopy(r,
+ (r.length > 20) ? 1 : 0,
+ result,
+ (r.length > 20) ? 0 : 20 - r.length,
+ (r.length > 20) ? 20 : r.length);
+ System.arraycopy(s,
+ (s.length > 20) ? 1 : 0,
+ result,
+ (s.length > 20) ? 20 : 40 - s.length,
+ (s.length > 20) ? 20 : s.length);
+
+ return result;
+ }
+
+ public boolean verify(byte[] sig) throws Exception {
+ sig = extractSig(sig);
+
+ // ASN.1
+ int frst = ((sig[0] & 0x80) != 0 ? 1 : 0);
+ int scnd = ((sig[20] & 0x80) != 0 ? 1 : 0);
+
+ int length = sig.length + 6 + frst + scnd;
+ byte[] tmp = new byte[length];
+ tmp[0] = (byte) 0x30;
+ tmp[1] = (byte) 0x2c;
+ tmp[1] += frst;
+ tmp[1] += scnd;
+ tmp[2] = (byte) 0x02;
+ tmp[3] = (byte) 0x14;
+ tmp[3] += frst;
+ System.arraycopy(sig, 0, tmp, 4 + frst, 20);
+ tmp[4 + tmp[3]] = (byte) 0x02;
+ tmp[5 + tmp[3]] = (byte) 0x14;
+ tmp[5 + tmp[3]] += scnd;
+ System.arraycopy(sig, 20, tmp, 6 + tmp[3] + scnd, 20);
+ sig = tmp;
+
+ return signature.verify(sig);
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,52 @@
+/*
+ * 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.common.signature;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+
+public class SignatureRSA extends AbstractSignature {
+
+ public static class Factory implements NamedFactory<Signature> {
+
+ public String getName() {
+ return KeyPairProvider.SSH_RSA;
+ }
+
+ public Signature create() {
+ return new SignatureRSA();
+ }
+
+ }
+
+ public SignatureRSA() {
+ super("SHA1withRSA");
+ }
+
+ public byte[] sign() throws Exception {
+ return signature.sign();
+ }
+
+ public boolean verify(byte[] sig) throws Exception {
+ sig = extractSig(sig);
+ return signature.verify(sig);
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/Buffer.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,349 @@
+/*
+ * 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.common.util;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.SshConstants;
+import org.apache.mina.common.ByteBuffer;
+
+public final class Buffer {
+
+ public static final int DEFAULT_SIZE = 256;
+
+ private byte[] data;
+ private int rpos;
+ private int wpos;
+
+ public Buffer() {
+ this(DEFAULT_SIZE);
+ }
+
+ public Buffer(int size) {
+ this(new byte[getNextPowerOf2(size)], false);
+ }
+
+ public Buffer(byte[] data) {
+ this(data, true);
+ }
+
+ public Buffer(byte[] data, boolean read) {
+ this.data = data;
+ this.rpos = 0;
+ this.wpos = read ? data.length : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Buffer [rpos=" + rpos + ", wpos=" + wpos + ", size=" + data.length + "]";
+ }
+
+ /*======================
+ Global methods
+ ======================*/
+
+ public int rpos() {
+ return rpos;
+ }
+
+ public void rpos(int rpos) {
+ this.rpos = rpos;
+ }
+
+ public int wpos() {
+ return wpos;
+ }
+
+ public void wpos(int wpos) {
+ ensureCapacity(wpos - this.wpos);
+ this.wpos = wpos;
+ }
+
+ public int available() {
+ return wpos - rpos;
+ }
+
+ public byte[] array() {
+ return data;
+ }
+
+ public void compact() {
+ if (available() > 0) {
+ System.arraycopy(data, rpos, data, 0, wpos - rpos);
+ }
+ wpos -= rpos;
+ rpos = 0;
+ }
+
+ public byte[] getCompactData() {
+ int l = available();
+ if (l > 0) {
+ byte[] b = new byte[l];
+ System.arraycopy(data, rpos, b, 0, l);
+ return b;
+ } else {
+ return new byte[0];
+ }
+ }
+
+ public void clear() {
+ rpos = 0;
+ wpos = 0;
+ }
+
+ public String printHex() {
+ return BufferUtils.printHex(array(), rpos(), available());
+ }
+
+ /*======================
+ Read methods
+ ======================*/
+
+ public byte getByte() {
+ ensureAvailable(1);
+ return data[rpos++];
+ }
+
+ public int getInt() {
+ ensureAvailable(4);
+ int i = ((data[rpos++] << 24) & 0xff000000)|
+ ((data[rpos++] << 16) & 0x00ff0000)|
+ ((data[rpos++] << 8) & 0x0000ff00)|
+ ((data[rpos++] ) & 0x000000ff);
+ return i;
+ }
+
+ public boolean getBoolean() {
+ return getByte() != 0;
+ }
+
+ public String getString() {
+ int len = getInt();
+ if (len < 0 || len > 32768) {
+ throw new IllegalStateException("Bad item length: " + len);
+ }
+ ensureAvailable(len);
+ String s = new String(data, rpos, len);
+ rpos += len;
+ return s;
+ }
+
+ public byte[] getStringAsBytes() {
+ return getBytes();
+ }
+
+ public BigInteger getMPInt() {
+ return new BigInteger(getMPIntAsBytes());
+ }
+
+ public byte[] getMPIntAsBytes() {
+ return getBytes();
+ }
+
+ public byte[] getBytes() {
+ int len = getInt();
+ if (len < 0 || len > 32768) {
+ throw new IllegalStateException("Bad item length: " + len);
+ }
+ byte[] b = new byte[len];
+ getRawBytes(b);
+ return b;
+ }
+
+ public void getRawBytes(byte[] buf) {
+ getRawBytes(buf, 0, buf.length);
+ }
+
+ public void getRawBytes(byte[] buf, int off, int len) {
+ ensureAvailable(len);
+ System.arraycopy(data, rpos, buf, off, len);
+ rpos += len;
+ }
+
+ public PublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
+ PublicKey key;
+ String keyAlg = getString();
+ if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
+ BigInteger e = getMPInt();
+ BigInteger n = getMPInt();
+ KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
+ key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
+ } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
+ BigInteger p = getMPInt();
+ BigInteger q = getMPInt();
+ BigInteger g = getMPInt();
+ BigInteger y = getMPInt();
+ KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
+ key = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+ } else {
+ throw new IllegalStateException("Unsupported algorithm: " + keyAlg);
+ }
+ return key;
+ }
+
+ public SshConstants.Message getCommand() {
+ byte b = getByte();
+ SshConstants.Message cmd = SshConstants.Message.fromByte(b);
+ if (cmd == null) {
+ throw new IllegalStateException("Unknown command code: " + b);
+ }
+ return cmd;
+ }
+
+ private void ensureAvailable(int a) {
+ if (available() < a) {
+ throw new BufferException("Underflow");
+ }
+ }
+
+ /*======================
+ Write methods
+ ======================*/
+
+ public void putByte(byte b) {
+ ensureCapacity(1);
+ data[wpos++] = b;
+ }
+
+ public void putBuffer(Buffer buffer) {
+ int r = buffer.available();
+ ensureCapacity(r);
+ System.arraycopy(buffer.data, buffer.rpos, data, wpos, r);
+ wpos += r;
+ }
+
+ public void putBuffer(ByteBuffer buffer) {
+ int r = buffer.remaining();
+ ensureCapacity(r);
+ buffer.get(data, wpos, r);
+ wpos += r;
+ }
+
+ public void putInt(int i) {
+ ensureCapacity(4);
+ data[wpos++] = (byte) (i >> 24);
+ data[wpos++] = (byte) (i >> 16);
+ data[wpos++] = (byte) (i >> 8);
+ data[wpos++] = (byte) (i );
+ }
+
+ public void putBoolean(boolean b) {
+ putByte(b ? (byte) 1 : (byte) 0);
+ }
+
+ public void putBytes(byte[] b) {
+ putBytes(b, 0, b.length);
+ }
+
+ public void putBytes(byte[] b, int off, int len) {
+ putInt(len);
+ ensureCapacity(len);
+ System.arraycopy(b, off, data, wpos, len);
+ wpos += len;
+ }
+
+ public void putString(String string) {
+ putString(string.getBytes());
+ }
+
+ public void putString(byte[] str) {
+ putInt(str.length);
+ putRawBytes(str);
+ }
+
+ public void putMPInt(BigInteger bi) {
+ putMPInt(bi.toByteArray());
+ }
+
+ public void putMPInt(byte[] foo) {
+ int i = foo.length;
+ if ((foo[0] & 0x80) != 0) {
+ i++;
+ putInt(i);
+ putByte((byte)0);
+ } else {
+ putInt(i);
+ }
+ putRawBytes(foo);
+ }
+
+ public void putRawBytes(byte[] d) {
+ putRawBytes(d, 0, d.length);
+ }
+
+ public void putRawBytes(byte[] d, int off, int len) {
+ ensureCapacity(len);
+ System.arraycopy(d, off, data, wpos, len);
+ wpos += len;
+ }
+
+ public void putPublicKey(PublicKey key) {
+ if (key instanceof RSAPublicKey) {
+ putString(KeyPairProvider.SSH_RSA);
+ putMPInt(((RSAPublicKey) key).getPublicExponent());
+ putMPInt(((RSAPublicKey) key).getModulus());
+ } else if (key instanceof DSAPublicKey) {
+ putString(KeyPairProvider.SSH_DSS);
+ putMPInt(((DSAPublicKey) key).getParams().getP());
+ putMPInt(((DSAPublicKey) key).getParams().getQ());
+ putMPInt(((DSAPublicKey) key).getParams().getG());
+ putMPInt(((DSAPublicKey) key).getY());
+ } else {
+ throw new IllegalStateException("Unsupported algorithm: " + key.getAlgorithm());
+ }
+ }
+
+ public void putCommand(SshConstants.Message cmd) {
+ putByte(cmd.toByte());
+ }
+
+ private void ensureCapacity(int capacity) {
+ if (data.length - wpos < capacity) {
+ int cw = wpos + capacity;
+ byte[] tmp = new byte[getNextPowerOf2(cw)];
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ data = tmp;
+ }
+ }
+
+ public static class BufferException extends RuntimeException {
+ public BufferException(String message) {
+ super(message);
+ }
+ }
+
+ private static final int getNextPowerOf2(int i) {
+ int j = 1;
+ while (j < i) {
+ j <<= 1;
+ }
+ return j;
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/BufferUtils.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,64 @@
+/*
+ * 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.common.util;
+
+public class BufferUtils {
+
+ public static String printHex(byte[] array) {
+ return printHex(array, 0, array.length);
+ }
+
+ public static String printHex(byte[] array, int offset, int len) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < len; i++) {
+ byte b = array[offset + i];
+ if (sb.length() > 0) {
+ sb.append(' ');
+ }
+ sb.append(digits[(b >> 4) & 0x0F]);
+ sb.append(digits[b & 0x0F]);
+ }
+ return sb.toString();
+ }
+
+ public static boolean equals(byte[] a1, byte[] a2) {
+ if (a1.length != a2.length) {
+ return false;
+ }
+ return equals(a1, 0, a2, 0, a1.length);
+ }
+
+ public static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) {
+ if (a1.length < a1Offset + length || a2.length < a2Offset + length) {
+ return false;
+ }
+ while (length-- > 0) {
+ if (a1[a1Offset++] != a2[a2Offset++]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ final static char[] digits = {
+ '0' , '1' , '2' , '3' , '4' , '5' ,
+ '6' , '7' , '8' , '9' , 'a' , 'b' ,
+ 'c' , 'd' , 'e' , 'f'
+ };
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/IoUtils.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,38 @@
+/*
+ * 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.common.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public class IoUtils {
+
+ public static void closeQuietly(Closeable... closeables) {
+ for (Closeable c : closeables) {
+ try {
+ if (c != null) {
+ c.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,61 @@
+/*
+ * 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.common.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class LfToCrLfFilterOutputStream extends FilterOutputStream {
+
+ public LfToCrLfFilterOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ byte[] d = new byte[1];
+ d[0] = (byte) b;
+ write(d, 0, 1);
+ }
+
+ @Override
+ public void write(byte b[], int off, int len) throws IOException {
+ int nb = 0;
+ for (int i = off; i < off + len; i++) {
+ if (b[i] == '\n') {
+ nb++;
+ }
+ }
+ if (nb > 0) {
+ byte[] bb = new byte[len + nb];
+ for (int i = 0, j = 0; i < len; i++) {
+ if (b[off + i] == '\n') {
+ bb[j++] = '\r';
+ }
+ bb[j++] = b[off + i];
+ }
+ b = bb;
+ off = 0;
+ len = len + nb;
+ }
+ out.write(b, off, len);
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/LoggingFilterOutputStream.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,52 @@
+/*
+ * 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.common.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.slf4j.Logger;
+
+public class LoggingFilterOutputStream extends FilterOutputStream {
+
+ private final String msg;
+ private final Logger log;
+
+ public LoggingFilterOutputStream(OutputStream out, String msg, Logger log) {
+ super(out);
+ this.msg = msg;
+ this.log = log;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ byte[] d = new byte[1];
+ d[0] = (byte) b;
+ write(d, 0, 1);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (log != null && log.isTraceEnabled()) {
+ log.trace("{} {}", msg, BufferUtils.printHex(b, off, len));
+ }
+ out.write(b, off, len);
+ }
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseInputStream.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,34 @@
+/*
+ * 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.common.util;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+public class NoCloseInputStream extends FilterInputStream {
+
+ public NoCloseInputStream(InputStream in) {
+ super(in);
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/NoCloseOutputStream.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,36 @@
+/*
+ * 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.common.util;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class NoCloseOutputStream extends FilterOutputStream {
+
+ public NoCloseOutputStream(OutputStream out) {
+ super(out);
+ }
+
+
+ @Override
+ public void close() throws IOException {
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/common/util/SecurityUtils.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,164 @@
+/*
+ * 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.common.util;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SecurityUtils {
+
+ public static final String BOUNCY_CASTLE = "BC";
+
+ private static final Logger LOG = LoggerFactory.getLogger(SecurityUtils.class);
+
+ private static String securityProvider = null;
+ private static Boolean registerBouncyCastle;
+ private static boolean registrationDone;
+
+ public static synchronized void setSecurityProvider(String securityProvider) {
+ SecurityUtils.securityProvider = securityProvider;
+ registrationDone = false;
+ }
+
+ public static synchronized void setRegisterBouncyCastle(boolean registerBouncyCastle) {
+ SecurityUtils.registerBouncyCastle = registerBouncyCastle;
+ registrationDone = false;
+ }
+
+ public static synchronized String getSecurityProvider() {
+ register();
+ return securityProvider;
+ }
+
+ public static synchronized boolean isBouncyCastleRegistered() {
+ register();
+ return BOUNCY_CASTLE.equals(securityProvider);
+ }
+
+ private static void register() {
+ if (!registrationDone) {
+ if (securityProvider == null && (registerBouncyCastle == null || registerBouncyCastle)) {
+ // Use an inner class to avoid a strong dependency from SshServer on BouncyCastle
+ try {
+ new BouncyCastleRegistration().run();
+ } catch (Throwable t) {
+ if (registerBouncyCastle == null) {
+ LOG.info("BouncyCastle not registered, using the default JCE provider");
+ } else {
+ LOG.error("Failed to register BouncyCastle as the defaut JCE provider");
+ throw new RuntimeException("Failed to register BouncyCastle as the defaut JCE provider", t);
+ }
+ }
+ }
+ registrationDone = true;
+ }
+ }
+
+ private static class BouncyCastleRegistration {
+ public void run() throws Exception {
+ if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {
+ LOG.info("Trying to register BouncyCastle as a JCE provider");
+ java.security.Security.addProvider(new BouncyCastleProvider());
+ MessageDigest.getInstance("MD5", BOUNCY_CASTLE);
+ KeyAgreement.getInstance("DH", BOUNCY_CASTLE);
+ LOG.info("Registration succeeded");
+ } else {
+ LOG.info("BouncyCastle already registered as a JCE provider");
+ }
+ securityProvider = BOUNCY_CASTLE;
+ }
+ }
+
+ public static synchronized KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+ register();
+ if (getSecurityProvider() == null) {
+ return KeyFactory.getInstance(algorithm);
+ } else {
+ return KeyFactory.getInstance(algorithm, getSecurityProvider());
+ }
+ }
+
+ public static synchronized Cipher getCipher(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
+ register();
+ if (getSecurityProvider() == null) {
+ return Cipher.getInstance(transformation);
+ } else {
+ return Cipher.getInstance(transformation, getSecurityProvider());
+ }
+ }
+
+ public static synchronized MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+ register();
+ if (getSecurityProvider() == null) {
+ return MessageDigest.getInstance(algorithm);
+ } else {
+ return MessageDigest.getInstance(algorithm, getSecurityProvider());
+ }
+ }
+
+ public static synchronized KeyPairGenerator getKeyPairGenerator(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+ register();
+ if (getSecurityProvider() == null) {
+ return KeyPairGenerator.getInstance(algorithm);
+ } else {
+ return KeyPairGenerator.getInstance(algorithm, getSecurityProvider());
+ }
+ }
+
+ public static synchronized KeyAgreement getKeyAgreement(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+ register();
+ if (getSecurityProvider() == null) {
+ return KeyAgreement.getInstance(algorithm);
+ } else {
+ return KeyAgreement.getInstance(algorithm, getSecurityProvider());
+ }
+ }
+
+ public static synchronized Mac getMac(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+ register();
+ if (getSecurityProvider() == null) {
+ return Mac.getInstance(algorithm);
+ } else {
+ return Mac.getInstance(algorithm, getSecurityProvider());
+ }
+ }
+
+ public static synchronized Signature getSignature(String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException {
+ register();
+ if (getSecurityProvider() == null) {
+ return Signature.getInstance(algorithm);
+ } else {
+ return Signature.getInstance(algorithm, getSecurityProvider());
+ }
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/CommandFactory.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public interface CommandFactory {
+
+ Command createCommand(String command);
+
+ public interface Command {
+
+ /**
+ * Set the input stream that can be used by the shell to read input.
+ * @param in
+ */
+ void setInputStream(InputStream in);
+
+ /**
+ * Set the output stream that can be used by the shell to write its output.
+ * @param out
+ */
+ void setOutputStream(OutputStream out);
+
+ /**
+ * Set the error stream that can be used by the shell to write its errors.
+ * @param err
+ */
+ void setErrorStream(OutputStream err);
+
+ /**
+ * Set the callback that the shell has to call when it is closed.
+ */
+ void setExitCallback(ExitCallback callback);
+
+ /**
+ * Execute the command and return its status
+ *
+ * @return
+ * @throws java.io.IOException
+ */
+ void start() throws IOException;
+ }
+
+
+ /**
+ * Callback used by the shell to notify the SSH server is has exited
+ */
+ public interface ExitCallback {
+
+ /**
+ * Informs the SSH server that the shell has exited
+ *
+ * @param exitValue the exit value
+ */
+ void onExit(int exitValue);
+
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/PasswordAuthenticator.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+public interface PasswordAuthenticator {
+
+ /**
+ * Check the validity of a password.
+ * This method should return null if the authentication fails.
+ *
+ * @param username the username
+ * @param password the password
+ * @return a non null identity object or <code>null</code if authentication fail
+ */
+ Object authenticate(String username, String password);
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/PublickeyAuthenticator.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import java.security.PublicKey;
+
+public interface PublickeyAuthenticator {
+
+ boolean hasKey(String username, PublicKey key);
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerChannel.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.channel.Window;
+import org.apache.sshd.server.session.ServerSession;
+
+public interface ServerChannel extends Channel {
+
+ void init(ServerSession session, int id, int recipient, int rwsize, int rmpsize);
+
+ void handleRequest(Buffer buffer) throws IOException;
+
+ Window getLocalWindow();
+
+}
\ No newline at end of file
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/ServerFactoryManager.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.NamedFactory;
+
+public interface ServerFactoryManager extends FactoryManager {
+
+ PublickeyAuthenticator getPublickeyAuthenticator();
+
+ List<NamedFactory<UserAuth>> getUserAuthFactories();
+
+ List<NamedFactory<ServerChannel>> getChannelFactories();
+
+ ShellFactory getShellFactory();
+
+ CommandFactory getCommandFactory();
+
+ PasswordAuthenticator getPasswordAuthenticator();
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,164 @@
+/*
+ * 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;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ *
+ * TODO: remove InvertedShell and create a wrapper instead to keep the api cleaner
+ *
+ */
+public interface ShellFactory {
+
+ /**
+ * Create a shell.
+ *
+ * @return
+ * @throws Exception
+ */
+ Shell createShell();
+
+ /**
+ * Represents a shell that can be used in an interactive mode to send command.
+ * Shell implementations must implement one of the two sub-interfaces {@link InvertedShell}
+ * or {@link DirectShell} which have different ways to configure the input and output streams.
+ */
+ public interface Shell {
+
+ /**
+ * Destroy the shell.
+ * This method can be called by the SSH server to destroy the shell because
+ * the client has disconnected somehow.
+ */
+ void destroy();
+
+ }
+
+ /**
+ * This shell have inverted streams, such as the one obtained when launching a
+ * new {@link Process} from java.
+ */
+ public interface InvertedShell extends Shell {
+
+ /**
+ * Starts the shell and will make the streams available for
+ * the ssh server to retrieve and use.
+ *
+ * @param env
+ * @throws Exception
+ */
+ void start(Map<String,String> env) throws IOException;
+
+ /**
+ * Returns the output stream used to feed the shell.
+ * This method is called after the shell has been started.
+ *
+ * @return
+ */
+ OutputStream getInputStream();
+
+ /**
+ * Return an InputStream representing the output stream of the shell.
+ * @return
+ */
+ InputStream getOutputStream();
+
+ /**
+ * Return an InputStream representing the error stream of the shell.
+ * @return
+ */
+ InputStream getErrorStream();
+
+ /**
+ * Check if the underlying shell is still alive
+ * @return
+ */
+ boolean isAlive();
+
+ /**
+ * Retrieve the exit value of the shell.
+ * This method must only be called when the shell is not alive anymore.
+ *
+ * @return the exit value of the shell
+ */
+ int exitValue();
+
+ }
+
+ /**
+ * This shell have direct streams, meaning those streams will be provided by the ssh server
+ * for the shell to use directy.
+ * This interface is suitable for implementing shells in java, rather than using an external
+ * process.
+ */
+ public interface DirectShell extends Shell {
+
+ /**
+ * Set the input stream that can be used by the shell to read input.
+ * @param in
+ */
+ void setInputStream(InputStream in);
+
+ /**
+ * Set the output stream that can be used by the shell to write its output.
+ * @param out
+ */
+ void setOutputStream(OutputStream out);
+
+ /**
+ * Set the error stream that can be used by the shell to write its errors.
+ * @param err
+ */
+ void setErrorStream(OutputStream err);
+
+ /**
+ * Set the callback that the shell has to call when it is closed.
+ */
+ void setExitCallback(ExitCallback callback);
+
+ /**
+ * Starts the shell.
+ * All streams must have been set before calling this method.
+ *
+ * @param env
+ * @throws Exception
+ */
+ void start(Map<String,String> env) throws IOException;
+
+ }
+
+ /**
+ * Callback used by the shell to notify the SSH server is has exited
+ */
+ public interface ExitCallback {
+
+ /**
+ * Informs the SSH server that the shell has exited
+ *
+ * @param exitValue the exit value
+ */
+ void onExit(int exitValue);
+
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/UserAuth.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.session.ServerSession;
+
+public interface UserAuth {
+
+ /**
+ * Try to authenticate the user.
+ * This methods should return a non null object which is the user identity if
+ * the authentication succeeded. If the authentication failed, this method should
+ * throw an exception. If the authentication is still ongoing, a null value should
+ * be returned.
+ *
+ * @param session the current ssh session
+ * @param username the user trying to log in
+ * @param buffer the request buffer containing parameters specific to this request
+ *
+ * @return the identity of the user or null if the authentication fails
+ */
+ Object auth(ServerSession session, String username, Buffer buffer) throws Exception;
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,41 @@
+/*
+ * 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.auth;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.UserAuth;
+
+public class UserAuthNone implements UserAuth {
+
+ public static class Factory implements NamedFactory<UserAuth> {
+ public String getName() {
+ return "none";
+ }
+ public UserAuth create() {
+ return new UserAuthNone();
+ }
+ }
+
+ public Object auth(ServerSession session, String username, Buffer buffer) {
+ return username;
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,60 @@
+/*
+ * 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.auth;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.UserAuth;
+
+public class UserAuthPassword implements UserAuth {
+
+ public static class Factory implements NamedFactory<UserAuth> {
+ public String getName() {
+ return "password";
+ }
+ public UserAuth create() {
+ return new UserAuthPassword();
+ }
+ }
+
+ public Object auth(ServerSession session, String username, Buffer buffer) throws Exception {
+ boolean newPassword = buffer.getBoolean();
+ if (newPassword) {
+ throw new IllegalStateException("Password changes are not supported");
+ }
+ String password = buffer.getString();
+ return checkPassword(session, username, password);
+ }
+
+ private Object checkPassword(ServerSession session, String username, String password) throws Exception {
+ PasswordAuthenticator auth = session.getServerFactoryManager().getPasswordAuthenticator();
+ if (auth != null) {
+ Object identity = auth.authenticate(username, password);
+ if (identity != null) {
+ return identity;
+ } else {
+ throw new Exception("Authentication failed: bad username or password supplied");
+ }
+ }
+ throw new Exception("No PasswordAuthenticator configured");
+ }
+
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,99 @@
+/*
+ * 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.auth;
+
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.server.UserAuth;
+
+public class UserAuthPublicKey implements UserAuth {
+
+ public static class Factory implements NamedFactory<UserAuth> {
+ public String getName() {
+ return "publickey";
+ }
+ public UserAuth create() {
+ return new UserAuthPublicKey();
+ }
+ }
+
+ public Object auth(ServerSession session, String username, Buffer buffer) throws Exception {
+ boolean hasSig = buffer.getBoolean();
+ String alg = buffer.getString();
+
+ int oldLim = buffer.wpos();
+ int oldPos = buffer.rpos();
+ int len = buffer.getInt();
+ buffer.wpos(buffer.rpos() + len);
+ PublicKey key = buffer.getPublicKey();
+ String keyAlg = (key instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS;
+
+ Signature verif = NamedFactory.Utils.create(session.getFactoryManager().getSignatureFactories(), keyAlg);
+ verif.init(key, null);
+ buffer.wpos(oldLim);
+
+ byte[] sig = hasSig ? buffer.getBytes() : null;
+
+ PublickeyAuthenticator authenticator = session.getServerFactoryManager().getPublickeyAuthenticator();
+ if (authenticator == null) {
+ throw new Exception("No PublickeyAuthenticator configured");
+ }
+
+ if (!hasSig) {
+ if (authenticator.hasKey(username, key)) {
+ Buffer buf = session.createBuffer(SshConstants.Message.SSH_MSG_USERAUTH_PK_OK);
+ buf.putString(alg);
+ buf.putRawBytes(buffer.array(), oldPos, 4 + len);
+ session.writePacket(buf);
+ return null;
+ } else {
+ throw new Exception("Unsupported key for user");
+ }
+ } else {
+ if (!authenticator.hasKey(username, key)) {
+ throw new Exception("Unsupported key for user");
+ }
+ Buffer buf = new Buffer();
+ buf.putString(session.getKex().getH());
+ buf.putCommand(SshConstants.Message.SSH_MSG_USERAUTH_REQUEST);
+ buf.putString(username);
+ buf.putString("ssh-connection");
+ buf.putString("publickey");
+ buf.putByte((byte) 1);
+ buf.putString(keyAlg);
+ buffer.rpos(oldPos);
+ buffer.wpos(oldPos + 4 + len);
+ buf.putBuffer(buffer);
+ verif.update(buf.array(), buf.rpos(), buf.available());
+ if (verif.verify(sig)) {
+ return username;
+ } else {
+ throw new Exception("Key verification failed");
+ }
+ }
+ }
+}
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/AbstractServerChannel.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,54 @@
+/*
+ * 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.channel;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.AbstractChannel;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.ServerChannel;
+import org.apache.sshd.server.session.ServerSession;
+
+public abstract class AbstractServerChannel extends AbstractChannel implements ServerChannel {
+
+ protected boolean exitStatusSent;
+
+ public void init(ServerSession session, int id, int recipient, int rwsize, int rmpsize) {
+ this.session = session;
+ this.id = id;
+ this.recipient = recipient;
+ this.remoteWindow.init(rwsize, rmpsize);
+ configureWindow();
+ }
+
+ protected void sendExitStatus(int v) throws IOException {
+ if (!exitStatusSent) {
+ exitStatusSent = true;
+ log.info("Send SSH_MSG_CHANNEL_REQUEST exit-status on channel {}", id);
+ Buffer buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_REQUEST);
+ buffer.putInt(recipient);
+ buffer.putString("exit-status");
+ buffer.putByte((byte) 0);
+ buffer.putInt(v);
+ session.writePacket(buffer);
+ }
+ }
+
+}
\ No newline at end of file
Added: mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java?rev=723396&view=auto
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java (added)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java Thu Dec 4 10:59:31 2008
@@ -0,0 +1,494 @@
+/*
+ * 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.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.channel.ChannelOutputStream;
+import org.apache.sshd.common.channel.ChannelPipedOutputStream;
+import org.apache.sshd.common.channel.ChannelPipedInputStream;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.common.util.LfToCrLfFilterOutputStream;
+import org.apache.sshd.common.util.LoggingFilterOutputStream;
+import org.apache.sshd.common.util.IoUtils;
+import org.apache.sshd.server.ServerChannel;
+import org.apache.sshd.server.ShellFactory;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ *
+ */
+public class ChannelSession extends AbstractServerChannel {
+
+ public static class Factory implements NamedFactory<ServerChannel> {
+
+ public String getName() {
+ return "session";
+ }
+
+ public ServerChannel create() {
+ return new ChannelSession();
+ }
+ }
+
+ protected static enum PtyMode {
+ // Chars
+ VINTR(1), VQUIT(2), VERASE(3), VKILL(4), VEOF(5), VEOL(6), VEOL2(7), VSTART(8), VSTOP(9),
+ VSUSP(10), VDSUSP(11), VREPRINT(12), VWERASE(13), VLNEXT(14), VFLUSH(15), VSWTCH(16), VSTATUS(17), VDISCARD(18),
+ // I flags
+ IGNPAR(30), PARMRK(31), INPCK(32), ISTRIP(33), INCLR(34), IGNCR(35), ICRNL(36), IUCLC(37), IXON(38), IXANY(39),
+ IXOFF(40), IMAXBEL(41),
+ // L flags
+ ISIG(50), ICANON(51), XCASE(52), ECHO(53), ECHOE(54), ECHOK(55), ECHONL(56), NOFLSH(57), TOSTOP(58), IEXTEN(59),
+ ECHOCTL(60), ECHOKE(61), PENDIN(62),
+ // O flags
+ OPOST(70), OLCUC(71), ONLCR(72), OCRNL(73), ONOCR(74), ONLRET(75),
+ // C flags
+ CS7(90), CS8(91), PARENB(92), PARODD(93),
+ // Speeed
+ TTY_OP_ISPEED(128), TTY_OP_OSPEED(129);
+
+ private int v;
+ private PtyMode(int v) {
+ this.v = v;
+ }
+
+ public int toInt() {
+ return v;
+ }
+
+ static Map<Integer,PtyMode> commands;
+ static {
+ commands = new HashMap<Integer, PtyMode>();
+ for (PtyMode c : PtyMode.values()) {
+ commands.put(c.toInt(), c);
+ }
+ }
+ public static PtyMode fromInt(int b) {
+ return commands.get(b);
+ }
+ }
+
+ protected static class PtyModeValue {
+ public final PtyMode mode;
+ public final int value;
+
+ public PtyModeValue(PtyMode mode, int value) {
+ this.mode = mode;
+ this.value = value;
+ }
+
+ public String toString() {
+ return mode + "(" + mode.toInt() + ") =" + value;
+ }
+ }
+
+ protected String type;
+ protected PtyModeValue[] ptyModes;
+ protected InputStream in;
+ protected OutputStream out;
+ protected OutputStream err;
+ protected ShellFactory.Shell shell;
+ protected OutputStream shellIn;
+ protected InputStream shellOut;
+ protected InputStream shellErr;
+ protected Map<String,String> env;
+ protected ExecutorService executor;
+ protected boolean usePipedChannelStreams;
+
+ public ChannelSession() {
+ executor = Executors.newSingleThreadExecutor();
+ }
+
+ public void close() throws IOException {
+ super.close();
+ if (shell != null) {
+ shell.destroy();
+ }
+ IoUtils.closeQuietly(in, out, err, shellIn, shellOut, shellErr);
+ executor.shutdown();
+ }
+
+ public void handleRequest(Buffer buffer) throws IOException {
+ log.info("Received SSH_MSG_CHANNEL_REQUEST on channel {}", id);
+ String type = buffer.getString();
+ log.info("Received channel request: {}", type);
+ if (!handleRequest(type, buffer)) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_FAILURE);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+ }
+
+ protected void doWriteData(byte[] data, int off, int len) throws IOException {
+ if (usePipedChannelStreams) {
+ shellIn.write(data, off, len);
+ shellIn.flush();
+ } else {
+ final byte[] buf = new byte[len];
+ System.arraycopy(data, off, buf, 0, len);
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ if (shellIn != null) {
+ shellIn.write(buf);
+ shellIn.flush();
+ }
+ } catch (IOException e) {
+ try {
+ internalClose();
+ } catch (IOException e2) {
+ // Ignore
+ }
+ }
+ }
+ });
+ localWindow.consumeAndCheck(len);
+ }
+ }
+
+ protected void doWriteExtendedData(byte[] data, int off, int len) throws IOException {
+ throw new UnsupportedOperationException("Server channel does not support extended data");
+ }
+
+ protected boolean handleRequest(String type, Buffer buffer) throws IOException {
+ if ("env".equals(type)) {
+ return handleEnv(buffer);
+ }
+ if ("pty-req".equals(type)) {
+ return handlePtyReq(buffer);
+ }
+ if ("window-change".equals(type)) {
+ return handleWindowChange(buffer);
+ }
+ if ("signal".equals(type)) {
+ return handleSignal(buffer);
+ }
+ if ("shell".equals(type)) {
+ if (checkType(type)) {
+ return handleShell(buffer);
+ } else {
+ return false;
+ }
+ }
+ if ("exec".equals(type)) {
+ if (checkType(type)) {
+ return handleExec(buffer);
+ } else {
+ return false;
+ }
+ }
+ if ("subsystem".equals(type)) {
+ if (checkType(type)) {
+ return handleSubsystem(buffer);
+ } else {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Only one of "shell", "exec" or "subsystem" command
+ * is permitted for a given channel.
+ *
+ * @param type
+ * @return
+ */
+ private boolean checkType(String type) {
+ if (this.type == null) {
+ this.type = type;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean handleEnv(Buffer buffer) throws IOException {
+ boolean wantReply = buffer.getBoolean();
+ String name = buffer.getString();
+ String value = buffer.getString();
+ addEnvVariable(name, value);
+ log.debug("env for channel {}: {} = {}", new Object[] { id, name, value });
+ if (wantReply) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+ return true;
+ }
+
+ protected boolean handlePtyReq(Buffer buffer) throws IOException {
+ boolean wantReply = buffer.getBoolean();
+ String term = buffer.getString();
+ int tColumns = buffer.getInt();
+ int tRows = buffer.getInt();
+ int tWidth = buffer.getInt();
+ int tHeight = buffer.getInt();
+ byte[] modes = buffer.getBytes();
+ List<PtyModeValue> modeList = new ArrayList<PtyModeValue>();
+ for (int i = 0; i < modes.length && modes[i] != 0;) {
+ PtyMode mode = PtyMode.fromInt(modes[i++]);
+ int val = ((modes[i++] << 24) & 0xff000000) |
+ ((modes[i++] << 16) & 0x00ff0000) |
+ ((modes[i++] << 8) & 0x0000ff00) |
+ ((modes[i++] ) & 0x000000ff);
+ PtyModeValue m = new PtyModeValue(mode, val);
+ modeList.add(m);
+ }
+ ptyModes = modeList.toArray(new PtyModeValue[0]);
+ if (log.isDebugEnabled()) {
+ StringBuffer strModes = new StringBuffer();
+ for (PtyModeValue m : ptyModes) {
+ if (strModes.length() > 0) {
+ strModes.append(", ");
+ }
+ strModes.append(m);
+ }
+ log.debug("pty for channel {}: term={}, size=({} - {}), pixels=({}, {}), modes=[{}]", new Object[] { id, term, tColumns, tRows, tWidth, tHeight, strModes.toString() });
+ }
+ addEnvVariable("TERM", term);
+ // TODO: handle pty request correctly
+ if (wantReply) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+ return true;
+ }
+
+ protected boolean handleWindowChange(Buffer buffer) throws IOException {
+ boolean wantReply = buffer.getBoolean();
+ int tColumns = buffer.getInt();
+ int tRows = buffer.getInt();
+ int tWidth = buffer.getInt();
+ int tHeight = buffer.getInt();
+ log.debug("window-change for channel {}: ({} - {}), ({}, {})", new Object[] { id, tColumns, tRows, tWidth, tHeight });
+ // TODO: handle window-change request correctly
+ if (wantReply) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+ return true;
+ }
+
+ protected boolean handleSignal(Buffer buffer) throws IOException {
+ boolean wantReply = buffer.getBoolean();
+ String name = buffer.getString();
+ log.debug("Signal received on channel {}: {}", id, name);
+ // TODO: handle signal request correctly
+ if (wantReply) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+ return true;
+ }
+
+ protected boolean handleShell(Buffer buffer) throws IOException {
+ boolean wantReply = buffer.getBoolean();
+
+ if (((ServerSession) session).getServerFactoryManager().getShellFactory() == null) {
+ return false;
+ }
+ addEnvVariable("USER", ((ServerSession) session).getUsername());
+ shell = ((ServerSession) session).getServerFactoryManager().getShellFactory().createShell();
+ out = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
+ err = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_EXTENDED_DATA);
+ // 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);
+ }
+ if (shell instanceof ShellFactory.InvertedShell) {
+ ((ShellFactory.InvertedShell) shell).start(env);
+ shellIn = ((ShellFactory.InvertedShell) shell).getInputStream();
+ shellOut = ((ShellFactory.InvertedShell) shell).getOutputStream();
+ shellErr = ((ShellFactory.InvertedShell) shell).getErrorStream();
+ new Thread() {
+ @Override
+ public void run() {
+ pumpStreams();
+ }
+ }.start();
+ // TODO: set this thread as daemon?
+ } else if (shell instanceof ShellFactory.DirectShell) {
+ in = new ChannelPipedInputStream(localWindow);
+ shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
+ usePipedChannelStreams = true;
+ ((ShellFactory.DirectShell) shell).setInputStream(in);
+ ((ShellFactory.DirectShell) shell).setOutputStream(out);
+ ((ShellFactory.DirectShell) shell).setErrorStream(err);
+ ((ShellFactory.DirectShell) shell).setExitCallback(new ShellFactory.ExitCallback() {
+ public void onExit(int exitValue) {
+ try {
+ closeShell(exitValue);
+ } catch (IOException e) {
+ log.info("Error closing shell", e);
+ }
+ }
+ });
+ ((ShellFactory.DirectShell) shell).start(env);
+ } else {
+ throw new IllegalStateException("Unknown shell type");
+ }
+ shellIn = new LoggingFilterOutputStream(shellIn, "IN: ", log);
+
+ if (wantReply) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+ return true;
+ }
+
+ protected int getPtyModeValue(PtyMode mode) {
+ if (ptyModes != null) {
+ for (PtyModeValue m : ptyModes) {
+ if (m.mode == mode) {
+ return m.value;
+ }
+ }
+ }
+ return 0;
+ }
+
+ protected boolean handleExec(Buffer buffer) throws IOException {
+ boolean wantReply = buffer.getBoolean();
+ String commandLine = buffer.getString();
+
+ if (((ServerSession) session).getServerFactoryManager().getCommandFactory() == null) {
+ return false;
+ }
+
+ CommandFactory.Command command = ((ServerSession) session).getServerFactoryManager().getCommandFactory().createCommand(commandLine);
+ out = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_DATA);
+ err = new ChannelOutputStream(this, remoteWindow, log, SshConstants.Message.SSH_MSG_CHANNEL_EXTENDED_DATA);
+ // Wrap in logging filters
+ out = new LoggingFilterOutputStream(out, "OUT:", log);
+ err = new LoggingFilterOutputStream(err, "ERR:", log);
+ in = new ChannelPipedInputStream(localWindow);
+ shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
+ usePipedChannelStreams = true;
+ command.setInputStream(in);
+ command.setOutputStream(out);
+ command.setErrorStream(err);
+ command.setExitCallback(new CommandFactory.ExitCallback() {
+ public void onExit(int exitValue) {
+ try {
+ closeShell(exitValue);
+ } catch (IOException e) {
+ log.info("Error closing shell", e);
+ }
+ }
+ });
+ command.start();
+
+ if (wantReply) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+
+ return true;
+ }
+
+ protected boolean handleSubsystem(Buffer buffer) throws IOException {
+ boolean wantReply = buffer.getBoolean();
+ // TODO: start subsystem
+ if (wantReply) {
+ buffer = session.createBuffer(SshConstants.Message.SSH_MSG_CHANNEL_SUCCESS);
+ buffer.putInt(id);
+ session.writePacket(buffer);
+ }
+ return true;
+ }
+
+ protected synchronized void addEnvVariable(String name, String value) {
+ if (env == null) {
+ env = new HashMap<String,String>();
+ }
+ env.put(name, value);
+ }
+
+ protected void pumpStreams() {
+ try {
+ // Use a single thread to correctly sequence the output and error streams.
+ // If any bytes are available from the output stream, send them first, then
+ // check the error stream, or wait until more data is available.
+ ShellFactory.InvertedShell is = (ShellFactory.InvertedShell) shell;
+ byte[] buffer = new byte[512];
+ for (;;) {
+ if (!is.isAlive()) {
+ closeShell(is.exitValue());
+ return;
+ }
+ if (pumpStream(shellOut, out, buffer)) {
+ continue;
+ }
+ if (pumpStream(shellErr, err, buffer)) {
+ continue;
+ }
+ // Sleep a bit. This is not very good, as it consumes CPU, but the
+ // input streams are not selectable for nio, and any other blocking
+ // method would consume at least two threads
+ Thread.sleep(1);
+ }
+ } catch (Exception e) {
+ if (!closed && !closing) {
+ // TODO: send a disconnect if there is a problem?
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private boolean pumpStream(InputStream in, OutputStream out, byte[] buffer) throws IOException {
+ if (in.available() > 0) {
+ int len = in.read(buffer);
+ if (len > 0) {
+ out.write(buffer, 0, len);
+ out.flush();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void closeShell(int exitValue) throws IOException {
+ sendEof();
+ sendExitStatus(exitValue);
+ // TODO: We should wait for all streams to be consumed before closing the channel
+ internalClose();
+ }
+
+}
\ No newline at end of file