| Chunk |
|---|
| Conflicting content |
|---|
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java package com.trilead.ssh2; import java.io.CharArrayWriter; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; import java.io.InputStream; import java.net.InetSocketAddress; import java.net.SocketTimeoutException; import java.security.SecureRandom; import java.util.Vector; ======= package com.trilead.ssh2; import com.trilead.ssh2.auth.AgentProxy; >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java import com.trilead.ssh2.auth.AuthenticationManager; import com.trilead.ssh2.channel.ChannelManager; import com.trilead.ssh2.crypto.CryptoWishList; |
| Solution content |
|---|
package com.trilead.ssh2; import com.trilead.ssh2.auth.AgentProxy; import com.trilead.ssh2.auth.AuthenticationManager; import com.trilead.ssh2.channel.ChannelManager; import com.trilead.ssh2.crypto.CryptoWishList; |
| File |
|---|
| Connection.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
import com.trilead.ssh2.util.TimeoutService;
import com.trilead.ssh2.util.TimeoutService.TimeoutToken;
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
=======
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.util.Vector;
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
/**
* A |
| Solution content |
|---|
import com.trilead.ssh2.util.TimeoutService;
import com.trilead.ssh2.util.TimeoutService.TimeoutToken;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.util.Vector;
/**
* A |
| File |
|---|
| Connection.java |
| Developer's decision |
|---|
| Manual |
| Kind of conflict |
|---|
| Import |
| Chunk |
|---|
| Conflicting content |
|---|
return authenticateWithKeyboardInteractive(user, null, cb);
}
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
=======
public synchronized boolean authenticateWithAgent(String user, AgentProxy proxy) throws IOException {
if (tm == null)
throw new IllegalStateException("Connection is not established!");
if (authenticated)
throw new IllegalStateException("Connection is already authenticated!");
if (am == null)
am = new AuthenticationManager(tm);
if (cm == null)
cm = new ChannelManager(tm);
if (user == null)
throw new IllegalArgumentException("user argument is null");
authenticated = am.authenticatePublicKey(user, proxy);
return authenticated;
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
/**
* After a successful connect, one has to authenticate oneself. This method
* is based on "keyboard-interactive", specified in |
| Solution content |
|---|
return authenticateWithKeyboardInteractive(user, null, cb);
}
public synchronized boolean authenticateWithAgent(String user, AgentProxy proxy) throws IOException {
if (tm == null)
throw new IllegalStateException("Connection is not established!");
if (authenticated)
throw new IllegalStateException("Connection is already authenticated!");
if (am == null)
am = new AuthenticationManager(tm);
if (cm == null)
cm = new ChannelManager(tm);
if (user == null)
throw new IllegalArgumentException("user argument is null");
authenticated = am.authenticatePublicKey(user, proxy);
return authenticated;
}
/**
* After a successful connect, one has to authenticate oneself. This method
* is based on "keyboard-interactive", specified in |
| File |
|---|
| Connection.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Method declaration |
| Chunk |
|---|
| Conflicting content |
|---|
* proxy is buggy and does not return a proper HTTP response,
* then a normal IOException is thrown instead.
*/
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
throws IOException
{
=======
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout) throws IOException {
return connect(verifier, connectTimeout, 0, kexTimeout);
}
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int readTimeout, int kexTimeout)
throws IOException {
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
final class TimeoutState
{
boolean isCancelled = false; |
| Solution content |
|---|
* proxy is buggy and does not return a proper HTTP response,
* then a normal IOException is thrown instead.
*/
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout) throws IOException {
return connect(verifier, connectTimeout, 0, kexTimeout);
}
public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int readTimeout, int kexTimeout)
throws IOException {
final class TimeoutState
{
boolean isCancelled = false; |
| File |
|---|
| Connection.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Method declaration |
| Method signature |
| Chunk |
|---|
| Conflicting content |
|---|
final TimeoutState state = new TimeoutState(); tm = new TransportManager(hostname, port); <<<<<<< HEAD:src/com/trilead/ssh2/Connection.java ======= >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java tm.setConnectionMonitors(connectionMonitors); /* |
| Solution content |
|---|
final TimeoutState state = new TimeoutState(); tm = new TransportManager(hostname, port); tm.setConnectionMonitors(connectionMonitors); /* |
| File |
|---|
| Connection.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Blank |
| Chunk |
|---|
| Conflicting content |
|---|
try
{
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
=======
tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, readTimeout, getOrCreateSecureRND(), proxyData);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
}
catch (SocketTimeoutException se)
{ |
| Solution content |
|---|
try
{
tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, readTimeout, getOrCreateSecureRND(), proxyData);
}
catch (SocketTimeoutException se)
{ |
| File |
|---|
| Connection.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Method invocation |
| Chunk |
|---|
| Conflicting content |
|---|
cm.requestGlobalTrileadPing();
}
<<<<<<< HEAD:src/com/trilead/ssh2/Connection.java
/**
* Executes a process remotely and blocks until its completion.
*
* @param output
* The stdout/stderr will be sent to this stream.
*/
public int exec(String command, OutputStream output) throws IOException, InterruptedException {
Session session = openSession();
try {
session.execCommand(command);
PumpThread t1 = new PumpThread(session.getStdout(), output);
t1.start();
PumpThread t2 = new PumpThread(session.getStderr(), output);
t2.start();
session.getStdin().close();
t1.join();
t2.join();
// wait for some time since the delivery of the exit status often gets delayed
session.waitForCondition(ChannelCondition.EXIT_STATUS,3000);
Integer r = session.getExitStatus();
if(r!=null) return r.intValue();
return -1;
} finally {
session.close();
}
}
/**
* Pumps {@link InputStream} to {@link OutputStream}.
*
* @author Kohsuke Kawaguchi
*/
private static final class PumpThread extends Thread {
private final InputStream in;
private final OutputStream out;
public PumpThread(InputStream in, OutputStream out) {
super("pump thread");
this.in = in;
this.out = out;
}
public void run() {
byte[] buf = new byte[1024];
try {
while(true) {
int len = in.read(buf);
if(len<0) {
in.close();
return;
}
out.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/Connection.java
} |
| Solution content |
|---|
cm.requestGlobalTrileadPing();
}
/**
* Executes a process remotely and blocks until its completion.
*
* @param output
* The stdout/stderr will be sent to this stream.
*/
public int exec(String command, OutputStream output) throws IOException, InterruptedException {
Session session = openSession();
try {
session.execCommand(command);
PumpThread t1 = new PumpThread(session.getStdout(), output);
t1.start();
PumpThread t2 = new PumpThread(session.getStderr(), output);
t2.start();
session.getStdin().close();
t1.join();
t2.join();
// wait for some time since the delivery of the exit status often gets delayed
session.waitForCondition(ChannelCondition.EXIT_STATUS,3000);
Integer r = session.getExitStatus();
if(r!=null) return r.intValue();
return -1;
} finally {
session.close();
}
}
/**
* Pumps {@link InputStream} to {@link OutputStream}.
*
* @author Kohsuke Kawaguchi
*/
private static final class PumpThread extends Thread {
private final InputStream in;
private final OutputStream out;
public PumpThread(InputStream in, OutputStream out) {
super("pump thread");
this.in = in;
this.out = out;
}
public void run() {
byte[] buf = new byte[1024];
try {
while(true) {
int len = in.read(buf);
if(len<0) {
in.close();
return;
}
out.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} |
| File |
|---|
| Connection.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Method declaration |
| Chunk |
|---|
| Conflicting content |
|---|
import java.io.FileReader; import java.io.IOException; import java.io.RandomAccessFile; <<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java ======= import java.io.Reader; >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.UnknownHostException; |
| Solution content |
|---|
import java.io.FileReader; import java.io.IOException; import java.io.RandomAccessFile; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.UnknownHostException; |
| File |
|---|
| KnownHosts.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Import |
| Chunk |
|---|
| Conflicting content |
|---|
import com.trilead.ssh2.crypto.digest.HMAC; import com.trilead.ssh2.crypto.digest.MD5; import com.trilead.ssh2.crypto.digest.SHA1; <<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java ======= import com.trilead.ssh2.log.Logger; >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java import com.trilead.ssh2.signature.DSAPublicKey; import com.trilead.ssh2.signature.DSASHA1Verify; import com.trilead.ssh2.signature.RSAPublicKey; |
| Solution content |
|---|
import com.trilead.ssh2.crypto.digest.HMAC; import com.trilead.ssh2.crypto.digest.MD5; import com.trilead.ssh2.crypto.digest.SHA1; import com.trilead.ssh2.log.Logger; import com.trilead.ssh2.signature.DSAPublicKey; import com.trilead.ssh2.signature.DSASHA1Verify; import com.trilead.ssh2.signature.RSAPublicKey; |
| File |
|---|
| KnownHosts.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Import |
| Chunk |
|---|
| Conflicting content |
|---|
public class KnownHosts
{
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
=======
private static final Logger LOGGER = Logger.getLogger(KnownHosts.class);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
public static final int HOSTKEY_IS_OK = 0;
public static final int HOSTKEY_IS_NEW = 1;
public static final int HOSTKEY_HAS_CHANGED = 2; |
| Solution content |
|---|
public class KnownHosts
{
private static final Logger LOGGER = Logger.getLogger(KnownHosts.class);
public static final int HOSTKEY_IS_OK = 0;
public static final int HOSTKEY_IS_NEW = 1;
public static final int HOSTKEY_HAS_CHANGED = 2; |
| File |
|---|
| KnownHosts.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Attribute |
| Method invocation |
| Chunk |
|---|
| Conflicting content |
|---|
* @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
* @throws IOException
*/
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
{
if (hostnames == null)
throw new IllegalArgumentException("hostnames may not be null");
if ("ssh-rsa".equals(serverHostKeyAlgorithm))
{
RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
synchronized (publicKeys)
{
publicKeys.add(new KnownHostsEntry(hostnames, rpk));
}
}
else if ("ssh-dss".equals(serverHostKeyAlgorithm))
{
DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
synchronized (publicKeys)
{
publicKeys.add(new KnownHostsEntry(hostnames, dpk));
}
}
else
throw new IOException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
=======
public void addHostkey(String[] hostnames, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
if (hostnames == null) {
throw new IllegalArgumentException("hostnames may not be null");
}
if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
final RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
synchronized (publicKeys) {
publicKeys.add(new KnownHostsEntry(hostnames, rpk));
}
}
else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
final DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
synchronized (publicKeys) {
publicKeys.add(new KnownHostsEntry(hostnames, dpk));
}
}
else {
throw new IOWarningException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
}
/** |
| Solution content |
|---|
* @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
* @throws IOException
*/
public void addHostkey(String[] hostnames, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
if (hostnames == null) {
throw new IllegalArgumentException("hostnames may not be null");
}
if ("ssh-rsa".equals(serverHostKeyAlgorithm)) {
final RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
synchronized (publicKeys) {
publicKeys.add(new KnownHostsEntry(hostnames, rpk));
}
}
else if ("ssh-dss".equals(serverHostKeyAlgorithm)) {
final DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
synchronized (publicKeys) {
publicKeys.add(new KnownHostsEntry(hostnames, dpk));
}
}
else {
throw new IOWarningException("Unknwon host key type (" + serverHostKeyAlgorithm + ")");
}
}
/** |
| File |
|---|
| KnownHosts.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| If statement |
| Method signature |
| Chunk |
|---|
| Conflicting content |
|---|
return false;
isMatch = true;
}
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
=======
else
{
final int indexColon = pattern.indexOf(':');
final int indexLastColon = pattern.indexOf(':');
if (indexColon > 0 && indexColon < pattern.length() - 2 && indexColon == indexLastColon)
{
final String bracketizedHost = '[' + hostname + ']';
if (pattern.startsWith(bracketizedHost))
{
if (negate)
return false;
isMatch = true;
}
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
}
}
|
| Solution content |
|---|
return false;
isMatch = true;
}
else
{
final int indexColon = pattern.indexOf(':');
final int indexLastColon = pattern.indexOf(':');
if (indexColon > 0 && indexColon < pattern.length() - 2 && indexColon == indexLastColon)
{
final String bracketizedHost = '[' + hostname + ']';
if (pattern.startsWith(bracketizedHost))
{
if (negate)
return false;
isMatch = true;
}
}
}
}
}
|
| File |
|---|
| KnownHosts.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| If statement |
| Method invocation |
| Variable |
| Chunk |
|---|
| Conflicting content |
|---|
if (arr.length >= 3)
{
return isMatch;
}
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
private void initialize(char[] knownHostsData) throws IOException
{
BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
while (true)
{
String line = br.readLine();
if (line == null)
break;
line = line.trim();
if (line.startsWith("#"))
continue;
String[] arr = line.split(" ");
if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0))
{
String[] hostnames = arr[0].split(",");
byte[] msg = Base64.decode(arr[2].toCharArray());
addHostkey(hostnames, arr[1], msg);
}
=======
private void initialize(char[] knownHostsData) throws IOException {
final BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
for (String line = br.readLine(); line != null; line = br.readLine()) {
line = line.trim();
if (line.startsWith("#")) {
continue;
}
final String[] arr = line.split(" ");
if (arr.length < 3) {
continue;
}
final String serverHostKeyAlgorithm = arr[1];
if (!"ssh-rsa".equals(serverHostKeyAlgorithm) && !"ssh-dss".equals(serverHostKeyAlgorithm)) {
continue;
}
final String[] hostnames = arr[0].split(",");
final byte[] msg = Base64.decode(arr[2].toCharArray());
try {
addHostkey(hostnames, serverHostKeyAlgorithm, msg);
}
catch (IOWarningException ex) {
LOGGER.log(20, "Ignored invalid line '" + line + "'");
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
}
}
} |
| Solution content |
|---|
return isMatch;
}
private void initialize(char[] knownHostsData) throws IOException {
final BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
for (String line = br.readLine(); line != null; line = br.readLine()) {
line = line.trim();
if (line.startsWith("#")) {
continue;
}
final String[] arr = line.split(" ");
if (arr.length < 3) {
continue;
}
final String serverHostKeyAlgorithm = arr[1];
if (!"ssh-rsa".equals(serverHostKeyAlgorithm) && !"ssh-dss".equals(serverHostKeyAlgorithm)) {
continue;
}
final String[] hostnames = arr[0].split(",");
final byte[] msg = Base64.decode(arr[2].toCharArray());
try {
addHostkey(hostnames, serverHostKeyAlgorithm, msg);
}
catch (IOWarningException ex) {
LOGGER.log(20, "Ignored invalid line '" + line + "'");
}
}
} |
| File |
|---|
| KnownHosts.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Array access |
| Catch clause |
| For statement |
| If statement |
| Method invocation |
| Method signature |
| Try statement |
| Variable |
| While statement |
| Chunk |
|---|
| Conflicting content |
|---|
}
}
<<<<<<< HEAD:src/com/trilead/ssh2/KnownHosts.java
private void initialize(File knownHosts) throws IOException
{
char[] buff = new char[512];
CharArrayWriter cw = new CharArrayWriter();
knownHosts.createNewFile();
FileReader fr = new FileReader(knownHosts);
while (true)
{
int len = fr.read(buff);
if (len < 0)
break;
cw.write(buff, 0, len);
}
fr.close();
initialize(cw.toCharArray());
=======
private void initialize(File knownHosts) throws IOException {
final char[] buffer = new char[512];
final CharArrayWriter charWriter = new CharArrayWriter();
knownHosts.createNewFile();
final Reader reader = new FileReader(knownHosts);
try {
while (true) {
final int readCharCount = reader.read(buffer);
if (readCharCount < 0) {
break;
}
charWriter.write(buffer, 0, readCharCount);
}
}
finally {
reader.close();
}
initialize(charWriter.toCharArray());
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/KnownHosts.java
}
private final boolean matchKeys(Object key1, Object key2) |
| Solution content |
|---|
}
}
private void initialize(File knownHosts) throws IOException {
final char[] buffer = new char[512];
final CharArrayWriter charWriter = new CharArrayWriter();
knownHosts.createNewFile();
final Reader reader = new FileReader(knownHosts);
try {
while (true) {
final int readCharCount = reader.read(buffer);
if (readCharCount < 0) {
break;
}
charWriter.write(buffer, 0, readCharCount);
}
}
finally {
reader.close();
}
initialize(charWriter.toCharArray());
}
private final boolean matchKeys(Object key1, Object key2) |
| File |
|---|
| KnownHosts.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Method invocation |
| Method signature |
| Try statement |
| Variable |
| While statement |
| Chunk |
|---|
| Conflicting content |
|---|
<<<<<<< HEAD:src/com/trilead/ssh2/SFTPv3Client.java ======= >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/SFTPv3Client.java package com.trilead.ssh2; import java.io.BufferedOutputStream; |
| Solution content |
|---|
return charsetName; } package com.trilead.ssh2; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Vector; import com.trilead.ssh2.packets.TypesReader; import com.trilead.ssh2.packets.TypesWriter; import com.trilead.ssh2.sftp.AttribFlags; import com.trilead.ssh2.sftp.ErrorCodes; import com.trilead.ssh2.sftp.Packet; /** * A
* If you are still not tired then please go on and read the comment for * {@link #setCharset(String)}. * * @author Christian Plattner, plattner@trilead.com * @version $Id: SFTPv3Client.java,v 1.3 2008/04/01 12:38:09 cplattne Exp $ */ public class SFTPv3Client { final Connection conn; final Session sess; final PrintStream debug; boolean flag_closed = false; InputStream is; OutputStream os; int protocol_version = 0; HashMap server_extensions = new HashMap(); int next_request_id = 1000; String charsetName = null; /** * Create a SFTP v3 client. * * @param conn The underlying SSH-2 connection to be used. * @param debug * @throws IOException * * @deprecated this constructor (debug version) will disappear in the future, * use {@link #SFTPv3Client(Connection)} instead. */ public SFTPv3Client(Connection conn, PrintStream debug) throws IOException { if (conn == null) throw new IllegalArgumentException("Cannot accept null argument!"); this.conn = conn; this.debug = debug; if (debug != null) debug.println("Opening session and starting SFTP subsystem."); sess = conn.openSession(); sess.startSubSystem("sftp"); is = sess.getStdout(); os = new BufferedOutputStream(sess.getStdin(), 2048); if ((is == null) || (os == null)) throw new IOException("There is a problem with the streams of the underlying channel."); init(); } /** * Create a SFTP v3 client. * * @param conn The underlying SSH-2 connection to be used. * @throws IOException */ public SFTPv3Client(Connection conn) throws IOException { this(conn, null); } /** * Set the charset used to convert between Java Unicode Strings and byte encodings * used by the server for paths and file names. Unfortunately, the SFTP v3 draft * says NOTHING about such conversions (well, with the exception of error messages * which have to be in UTF-8). Newer drafts specify to use UTF-8 for file names * (if I remember correctly). However, a quick test using OpenSSH serving a EXT-3 * filesystem has shown that UTF-8 seems to be a bad choice for SFTP v3 (tested with * filenames containing german umlauts). "windows-1252" seems to work better for Europe. * Luckily, "windows-1252" is the platform default in my case =). *
* If you don't set anything, then the platform default will be used (this is the default
* behavior).
*
* @see #getCharset()
*
* @param charset the name of the charset to be used or
* Note: receiveMessage(34000) actually means that the message may be up to 34004
* bytes (the length attribute preceeding the contents is 4 bytes).
*
* @param maxlen
* @return the message contents
* @throws IOException
*/
private final byte[] receiveMessage(int maxlen) throws IOException
{
byte[] msglen = new byte[4];
readBytes(msglen, 0, 4);
int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff));
if ((len > maxlen) || (len <= 0))
throw new IOException("Illegal sftp packet len: " + len);
byte[] msg = new byte[len];
readBytes(msg, 0, len);
return msg;
}
private final int generateNextRequestID()
{
synchronized (this)
{
return next_request_id++;
}
}
private final void closeHandle(byte[] handle) throws IOException
{
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(handle, 0, handle.length);
sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes());
expectStatusOKMessage(req_id);
}
private SFTPv3FileAttributes readAttrs(TypesReader tr) throws IOException
{
/*
* uint32 flags
* uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
* uint32 uid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
}
* uint32 gid present only if flag SSH_FILEXFER_ATTR_V3_UIDGID
* uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
* uint32 atime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
* uint32 mtime present only if flag SSH_FILEXFER_ATTR_V3_ACMODTIME
* uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
* string extended_type
* string extended_data
* ... more extended data (extended_type - extended_data pairs),
* so that number of pairs equals extended_count
*/
SFTPv3FileAttributes fa = new SFTPv3FileAttributes();
int flags = tr.readUINT32();
if ((flags & AttribFlags.SSH_FILEXFER_ATTR_SIZE) != 0)
{
if (debug != null)
debug.println("SSH_FILEXFER_ATTR_SIZE");
fa.size = new Long(tr.readUINT64());
}
if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_UIDGID) != 0)
{
if (debug != null)
debug.println("SSH_FILEXFER_ATTR_V3_UIDGID");
fa.uid = new Integer(tr.readUINT32());
fa.gid = new Integer(tr.readUINT32());
}
if ((flags & AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS) != 0)
{
if (debug != null)
debug.println("SSH_FILEXFER_ATTR_PERMISSIONS");
fa.permissions = new Integer(tr.readUINT32());
}
if ((flags & AttribFlags.SSH_FILEXFER_ATTR_V3_ACMODTIME) != 0)
{
if (debug != null)
debug.println("SSH_FILEXFER_ATTR_V3_ACMODTIME");
fa.atime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
fa.mtime = new Long(((long)tr.readUINT32()) & 0xffffffffl);
}
if ((flags & AttribFlags.SSH_FILEXFER_ATTR_EXTENDED) != 0)
{
int count = tr.readUINT32();
if (debug != null)
debug.println("SSH_FILEXFER_ATTR_EXTENDED (" + count + ")");
/* Read it anyway to detect corrupt packets */
while (count > 0)
{
tr.readByteString();
tr.readByteString();
count--;
}
}
return fa;
}
/**
* Retrieve the file attributes of an open file.
*
* @param handle a SFTPv3FileHandle handle.
* @return a SFTPv3FileAttributes object.
* @throws IOException
*/
public SFTPv3FileAttributes fstat(SFTPv3FileHandle handle) throws IOException
{
checkHandleValidAndOpen(handle);
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
if (debug != null)
{
debug.println("Sending SSH_FXP_FSTAT...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_FSTAT, req_id, tw.getBytes());
byte[] resp = receiveMessage(34000);
if (debug != null)
{
debug.println("Got REPLY.");
debug.flush();
}
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t == Packet.SSH_FXP_ATTRS)
{
return readAttrs(tr);
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
throw new SFTPException(tr.readString(), errorCode);
}
private SFTPv3FileAttributes statBoth(String path, int statMethod) throws IOException
{
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(path, charsetName);
if (debug != null)
{
debug.println("Sending SSH_FXP_STAT/SSH_FXP_LSTAT...");
debug.flush();
}
sendMessage(statMethod, req_id, tw.getBytes());
byte[] resp = receiveMessage(34000);
if (debug != null)
{
debug.println("Got REPLY.");
debug.flush();
}
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t == Packet.SSH_FXP_ATTRS)
{
return readAttrs(tr);
}
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
throw new SFTPException(tr.readString(), errorCode);
}
/**
* Retrieve the file attributes of a file. This method
/**
* follows symbolic links on the server.
*
* @see #lstat(String)
*
* @param path See the {@link SFTPv3Client comment} for the class for more details.
* @return a SFTPv3FileAttributes object.
* @throws IOException
*/
public SFTPv3FileAttributes stat(String path) throws IOException
{
return statBoth(path, Packet.SSH_FXP_STAT);
}
/**
* Retrieve the file attributes of a file. This method
* does NOT follow symbolic links on the server.
*
* @see #stat(String)
*
* @param path See the {@link SFTPv3Client comment} for the class for more details.
* @return a SFTPv3FileAttributes object.
* @throws IOException
*/
public SFTPv3FileAttributes lstat(String path) throws IOException
{
return statBoth(path, Packet.SSH_FXP_LSTAT);
}
/**
* Read the target of a symbolic link.
*
* @param path See the {@link SFTPv3Client comment} for the class for more details.
* @return The target of the link.
* @throws IOException
*/
public String readLink(String path) throws IOException
{
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(path, charsetName);
if (debug != null)
{
debug.println("Sending SSH_FXP_READLINK...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes());
byte[] resp = receiveMessage(34000);
if (debug != null)
{
debug.println("Got REPLY.");
debug.flush();
}
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t == Packet.SSH_FXP_NAME)
{
int count = tr.readUINT32();
if (count != 1)
throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
return tr.readString(charsetName);
}
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
throw new SFTPException(tr.readString(), errorCode);
}
private void expectStatusOKMessage(int id) throws IOException
{
byte[] resp = receiveMessage(34000);
if (debug != null)
{
debug.println("Got REPLY.");
debug.flush();
}
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != id)
throw new IOException("The server sent an invalid id field.");
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
if (errorCode == ErrorCodes.SSH_FX_OK)
return;
throw new SFTPException(tr.readString(), errorCode);
}
* Modify the attributes of a file. Used for operations such as changing
* the ownership, permissions or access times, as well as for truncating a file.
*
* @param path See the {@link SFTPv3Client comment} for the class for more details.
* @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
* made to the attributes of the file. Empty fields will be ignored.
* @throws IOException
*/
public void setstat(String path, SFTPv3FileAttributes attr) throws IOException
{
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(path, charsetName);
tw.writeBytes(createAttrs(attr));
if (debug != null)
{
debug.println("Sending SSH_FXP_SETSTAT...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes());
expectStatusOKMessage(req_id);
}
/**
* Modify the attributes of a file. Used for operations such as changing
* the ownership, permissions or access times, as well as for truncating a file.
*
* @param handle a SFTPv3FileHandle handle
* @param attr A SFTPv3FileAttributes object. Specifies the modifications to be
* made to the attributes of the file. Empty fields will be ignored.
* @throws IOException
*/
public void fsetstat(SFTPv3FileHandle handle, SFTPv3FileAttributes attr) throws IOException
{
checkHandleValidAndOpen(handle);
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
tw.writeBytes(createAttrs(attr));
if (debug != null)
{
debug.println("Sending SSH_FXP_FSETSTAT...");
debug.flush();
}
}
sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes());
expectStatusOKMessage(req_id);
}
/**
* Create a symbolic link on the server. Creates a link "src" that points
* to "target".
*
* @param src See the {@link SFTPv3Client comment} for the class for more details.
* @param target See the {@link SFTPv3Client comment} for the class for more details.
* @throws IOException
*/
public void createSymlink(String src, String target) throws IOException
{
int req_id = generateNextRequestID();
/* Either I am too stupid to understand the SFTP draft
* or the OpenSSH guys changed the semantics of src and target.
*/
TypesWriter tw = new TypesWriter();
tw.writeString(target, charsetName);
tw.writeString(src, charsetName);
if (debug != null)
{
debug.println("Sending SSH_FXP_SYMLINK...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes());
expectStatusOKMessage(req_id);
}
/**
* Have the server canonicalize any given path name to an absolute path.
* This is useful for converting path names containing ".." components or
* relative pathnames without a leading slash into absolute paths.
*
* @param path See the {@link SFTPv3Client comment} for the class for more details.
* @return An absolute path.
* @throws IOException
*/
public String canonicalPath(String path) throws IOException
{
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(path, charsetName);
if (debug != null)
{
debug.println("Sending SSH_FXP_REALPATH...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes());
byte[] resp = receiveMessage(34000);
if (debug != null)
{
debug.println("Got REPLY.");
debug.flush();
}
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t == Packet.SSH_FXP_NAME)
{
int count = tr.readUINT32();
if (count != 1)
throw new IOException("The server sent an invalid SSH_FXP_NAME packet.");
return tr.readString(charsetName);
}
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
throw new SFTPException(tr.readString(), errorCode);
}
private final Vector scanDirectory(byte[] handle) throws IOException
{
Vector files = new Vector();
while (true)
{
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(handle, 0, handle.length);
if (debug != null)
{
debug.println("Sending SSH_FXP_READDIR...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_READDIR, req_id, tw.getBytes());
/* Some servers send here a packet with size > 34000 */
/* To whom it may concern: please learn to read the specs. */
byte[] resp = receiveMessage(65536);
if (debug != null)
{
debug.println("Got REPLY.");
debug.flush();
}
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t == Packet.SSH_FXP_NAME)
{
int count = tr.readUINT32();
if (debug != null)
debug.println("Parsing " + count + " name entries...");
while (count > 0)
{
SFTPv3DirectoryEntry dirEnt = new SFTPv3DirectoryEntry();
dirEnt.filename = tr.readString(charsetName);
dirEnt.longEntry = tr.readString(charsetName);
dirEnt.attributes = readAttrs(tr);
files.addElement(dirEnt);
if (debug != null)
debug.println("File: '" + dirEnt.filename + "'");
count--;
}
continue;
}
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
if (errorCode == ErrorCodes.SSH_FX_EOF)
return files;
throw new SFTPException(tr.readString(), errorCode);
}
}
private final byte[] openDirectory(String path) throws IOException
{
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(path, charsetName);
if (debug != null)
{
debug.println("Sending SSH_FXP_OPENDIR...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_OPENDIR, req_id, tw.getBytes());
byte[] resp = receiveMessage(34000);
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t == Packet.SSH_FXP_HANDLE)
{
if (debug != null)
{
debug.println("Got SSH_FXP_HANDLE.");
debug.flush();
}
byte[] handle = tr.readByteString();
return handle;
}
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
String errorMessage = tr.readString();
throw new SFTPException(errorMessage, errorCode);
}
private final String expandString(byte[] b, int off, int len)
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i++)
{
int c = b[off + i] & 0xff;
if ((c >= 32) && (c <= 126))
{
sb.append((char) c);
else
{
sb.append("{0x" + Integer.toHexString(c) + "}");
}
}
return sb.toString();
}
private void init() throws IOException
{
/* Send SSH_FXP_INIT (version 3) */
final int client_version = 3;
if (debug != null)
debug.println("Sending SSH_FXP_INIT (" + client_version + ")...");
TypesWriter tw = new TypesWriter();
tw.writeUINT32(client_version);
sendMessage(Packet.SSH_FXP_INIT, 0, tw.getBytes());
/* Receive SSH_FXP_VERSION */
if (debug != null)
debug.println("Waiting for SSH_FXP_VERSION...");
TypesReader tr = new TypesReader(receiveMessage(34000)); /* Should be enough for any reasonable server */
int type = tr.readByte();
if (type != Packet.SSH_FXP_VERSION)
{
throw new IOException("The server did not send a SSH_FXP_VERSION packet (got " + type + ")");
}
protocol_version = tr.readUINT32();
if (debug != null)
debug.println("SSH_FXP_VERSION: protocol_version = " + protocol_version);
if (protocol_version != 3)
throw new IOException("Server version " + protocol_version + " is currently not supported");
/* Read and save extensions (if any) for later use */
while (tr.remain() != 0)
{
String name = tr.readString();
byte[] value = tr.readByteString();
server_extensions.put(name, value);
if (debug != null)
debug.println("SSH_FXP_VERSION: extension: " + name + " = '" + expandString(value, 0, value.length)
+ "'");
}
}
/**
* Returns the negotiated SFTP protocol version between the client and the server.
*
* @return SFTP protocol version, i.e., "3".
*
*/
public int getProtocolVersion()
{
return protocol_version;
}
/**
* Close this SFTP session. NEVER forget to call this method to free up
* resources - even if you got an exception from one of the other methods.
* Sometimes these other methods may throw an exception, saying that the
* underlying channel is closed (this can happen, e.g., if the other server
* sent a close message.) However, as long as you have not called the
* *
EOF
* @throws IOException
*/
public int read(SFTPv3FileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException
{
checkHandleValidAndOpen(handle);
if ((len > 32768) || (len <= 0))
throw new IllegalArgumentException("invalid len argument");
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
tw.writeUINT64(fileOffset);
tw.writeUINT32(len);
if (debug != null)
{
debug.println("Sending SSH_FXP_READ...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_READ, req_id, tw.getBytes());
byte[] resp = receiveMessage(34000);
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t == Packet.SSH_FXP_DATA)
{
if (debug != null)
{
debug.println("Got SSH_FXP_DATA...");
debug.flush();
}
int readLen = tr.readUINT32();
if ((readLen < 0) || (readLen > len))
throw new IOException("The server sent an invalid length field.");
tr.readBytes(dst, dstoff, readLen);
return readLen;
}
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
if (errorCode == ErrorCodes.SSH_FX_EOF)
{
if (debug != null)
{
debug.println("Got SSH_FX_EOF.");
debug.flush();
}
return -1;
}
String errorMessage = tr.readString();
throw new SFTPException(errorMessage, errorCode);
}
/**
* Write bytes to a file. If len > 32768, then the write operation will
* be split into multiple writes.
*
* @param handle a SFTPv3FileHandle handle.
* @param fileOffset offset (in bytes) in the file.
* @param src the source byte array.
* @param srcoff offset in the source byte array.
* @param len how many bytes to write.
* @throws IOException
*/
public void write(SFTPv3FileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException
{
checkHandleValidAndOpen(handle);
while (len > 0)
{
int writeRequestLen = len;
if (writeRequestLen > 32768)
writeRequestLen = 32768;
int req_id = generateNextRequestID();
TypesWriter tw = new TypesWriter();
tw.writeString(handle.fileHandle, 0, handle.fileHandle.length);
tw.writeUINT64(fileOffset);
tw.writeString(src, srcoff, writeRequestLen);
if (debug != null)
{
debug.println("Sending SSH_FXP_WRITE...");
debug.flush();
}
sendMessage(Packet.SSH_FXP_WRITE, req_id, tw.getBytes());
fileOffset += writeRequestLen;
srcoff += writeRequestLen;
len -= writeRequestLen;
byte[] resp = receiveMessage(34000);
TypesReader tr = new TypesReader(resp);
int t = tr.readByte();
int rep_id = tr.readUINT32();
if (rep_id != req_id)
throw new IOException("The server sent an invalid id field.");
if (t != Packet.SSH_FXP_STATUS)
throw new IOException("The SFTP server sent an unexpected packet type (" + t + ")");
int errorCode = tr.readUINT32();
if (errorCode == ErrorCodes.SSH_FX_OK)
continue;
String errorMessage = tr.readString();
throw new SFTPException(errorMessage, errorCode);
}
}
/**
* Close a file.
*
* @param handle a SFTPv3FileHandle handle
* @throws IOException
*/
public void closeFile(SFTPv3FileHandle handle) throws IOException
{
if (handle == null)
throw new IllegalArgumentException("the handle argument may not be null");
try
{
if (handle.isClosed == false)
{
closeHandle(handle.fileHandle);
}
}
finally
{
handle.isClosed = true;
}
}
/**
* Checks if the given path exists.
*/
public boolean exists(String path) throws IOException {
return _stat(path)!=null;
}
/**
* Graceful {@link #stat(String)} that returns null if the path doesn't exist.
*/
public SFTPv3FileAttributes _stat(String path) throws IOException {
try {
return stat(path);
} catch (SFTPException e) {
int c = e.getServerErrorCode();
if (c== ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH)
return null;
else
throw e;
}
}
/**
* Makes sure that the directory exists, by creating it if necessary.
*/
public void mkdirs(String path, int posixPermission) throws IOException {
SFTPv3FileAttributes atts = _stat(path);
if (atts!=null && atts.isDirectory())
return;
int idx = path.lastIndexOf("/");
if (idx>0)
mkdirs(path.substring(0,idx), posixPermission);
try {
mkdir(path, posixPermission);
} catch (IOException e) {
throw (IOException)new IOException("Failed to mkdir "+path).initCause(e);
}
}
/**
* Creates a new file and writes to it.
*/
public OutputStream writeToFile(String path) throws IOException {
final SFTPv3FileHandle h = createFile(path);
return new OutputStream() {
private long offset = 0;
public void write(int b) throws IOException {
write(new byte[]{(byte)b});
}
public void write(byte[] b, int off, int len) throws IOException {
SFTPv3Client.this.write(h,offset,b,off,len);
offset += len;
}
public void close() throws IOException {
closeFile(h);
}
};
}
public InputStream read(String file) throws IOException {
final SFTPv3FileHandle h = openFileRO(file);
return new InputStream() {
private long offset = 0;
public int read() throws IOException {
byte[] b = new byte[1];
if(read(b)<0)
return -1;
return b[0];
}
public int read(byte[] b, int off, int len) throws IOException {
int r = SFTPv3Client.this.read(h,offset,b,off,len);
if (r<0) return -1;
offset += r;
return r;
}
public long skip(long n) throws IOException {
offset += n;
return n;
}
public void close() throws IOException {
closeFile(h);
}
};
}
public void chmod(String path, int permissions) throws IOException {
SFTPv3FileAttributes atts = new SFTPv3FileAttributes();
atts.permissions = Integer.valueOf(permissions);
setstat(path, atts);
}
} |
| File |
|---|
| SFTPv3Client.java |
| Developer's decision |
|---|
| Manual |
| Kind of conflict |
|---|
| Blank |
| Chunk |
|---|
| Conflicting content |
|---|
handle.isClosed = true;
}
}
<<<<<<< HEAD:src/com/trilead/ssh2/SFTPv3Client.java
/**
* Checks if the given path exists.
*/
public boolean exists(String path) throws IOException {
return _stat(path)!=null;
}
/**
* Graceful {@link #stat(String)} that returns null if the path doesn't exist.
*/
public SFTPv3FileAttributes _stat(String path) throws IOException {
try {
return stat(path);
} catch (SFTPException e) {
int c = e.getServerErrorCode();
if (c== ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH)
return null;
else
throw e;
}
}
/**
* Makes sure that the directory exists, by creating it if necessary.
*/
public void mkdirs(String path, int posixPermission) throws IOException {
SFTPv3FileAttributes atts = _stat(path);
if (atts!=null && atts.isDirectory())
return;
int idx = path.lastIndexOf("/");
if (idx>0)
mkdirs(path.substring(0,idx), posixPermission);
try {
mkdir(path, posixPermission);
} catch (IOException e) {
throw (IOException)new IOException("Failed to mkdir "+path).initCause(e);
}
}
/**
* Creates a new file and writes to it.
*/
public OutputStream writeToFile(String path) throws IOException {
final SFTPv3FileHandle h = createFile(path);
return new OutputStream() {
private long offset = 0;
public void write(int b) throws IOException {
write(new byte[]{(byte)b});
}
public void write(byte[] b, int off, int len) throws IOException {
SFTPv3Client.this.write(h,offset,b,off,len);
offset += len;
}
public void close() throws IOException {
closeFile(h);
}
};
}
public InputStream read(String file) throws IOException {
final SFTPv3FileHandle h = openFileRO(file);
return new InputStream() {
private long offset = 0;
public int read() throws IOException {
byte[] b = new byte[1];
if(read(b)<0)
return -1;
return b[0];
}
public int read(byte[] b, int off, int len) throws IOException {
int r = SFTPv3Client.this.read(h,offset,b,off,len);
if (r<0) return -1;
offset += r;
return r;
}
public long skip(long n) throws IOException {
offset += n;
return n;
}
public void close() throws IOException {
closeFile(h);
}
};
}
public void chmod(String path, int permissions) throws IOException {
SFTPv3FileAttributes atts = new SFTPv3FileAttributes();
atts.permissions = Integer.valueOf(permissions);
setstat(path, atts);
}
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/SFTPv3Client.java
} |
| Solution content |
|---|
/**
* Checks if the given path exists.
*/
public boolean exists(String path) throws IOException {
return _stat(path)!=null;
}
/**
* Graceful {@link #stat(String)} that returns null if the path doesn't exist.
*/
public SFTPv3FileAttributes _stat(String path) throws IOException {
try {
return stat(path);
} catch (SFTPException e) {
int c = e.getServerErrorCode();
if (c== ErrorCodes.SSH_FX_NO_SUCH_FILE || c==ErrorCodes.SSH_FX_NO_SUCH_PATH)
return null;
else
throw e;
}
}
/**
* Makes sure that the directory exists, by creating it if necessary.
*/
public void mkdirs(String path, int posixPermission) throws IOException {
SFTPv3FileAttributes atts = _stat(path);
if (atts!=null && atts.isDirectory())
return;
int idx = path.lastIndexOf("/");
if (idx>0)
mkdirs(path.substring(0,idx), posixPermission);
try {
mkdir(path, posixPermission);
} catch (IOException e) {
throw (IOException)new IOException("Failed to mkdir "+path).initCause(e);
}
}
/**
* Creates a new file and writes to it.
*/
public OutputStream writeToFile(String path) throws IOException {
final SFTPv3FileHandle h = createFile(path);
return new OutputStream() {
private long offset = 0;
public void write(int b) throws IOException {
write(new byte[]{(byte)b});
}
public void write(byte[] b, int off, int len) throws IOException {
SFTPv3Client.this.write(h,offset,b,off,len);
offset += len;
}
public void close() throws IOException {
closeFile(h);
}
};
}
public InputStream read(String file) throws IOException {
final SFTPv3FileHandle h = openFileRO(file);
return new InputStream() {
private long offset = 0;
public int read() throws IOException {
byte[] b = new byte[1];
if(read(b)<0)
return -1;
return b[0];
}
public int read(byte[] b, int off, int len) throws IOException {
int r = SFTPv3Client.this.read(h,offset,b,off,len);
if (r<0) return -1;
offset += r;
return r;
}
public long skip(long n) throws IOException {
offset += n;
return n;
}
public void close() throws IOException {
closeFile(h);
}
};
}
public void chmod(String path, int permissions) throws IOException {
SFTPv3FileAttributes atts = new SFTPv3FileAttributes();
atts.permissions = Integer.valueOf(permissions);
setstat(path, atts);
}
} |
| File |
|---|
| SFTPv3Client.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Comment |
| Method declaration |
| Chunk |
|---|
| Conflicting content |
|---|
} <<<<<<< HEAD:src/com/trilead/ssh2/Session.java package com.trilead.ssh2; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.security.SecureRandom; import com.trilead.ssh2.channel.Channel; import com.trilead.ssh2.channel.ChannelManager; import com.trilead.ssh2.channel.X11ServerData; /** * A
terminal_modes) is described in RFC4254.
*
* @param term
* The TERM environment variable value (e.g., vt100)
* @param term_width_characters
* terminal width, characters (e.g., 80)
* @param term_height_characters
* terminal height, rows (e.g., 24)
* @param term_width_pixels
* terminal width, pixels (e.g., 640)
* @param term_height_pixels
* terminal height, pixels (e.g., 480)
* @param terminal_modes
* encoded terminal modes (may be null)
* @throws IOException
*/
public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
int term_height_pixels, byte[] terminal_modes) throws IOException
{
if (term == null)
throw new IllegalArgumentException("TERM cannot be null.");
if ((terminal_modes != null) && (terminal_modes.length > 0))
{
if (terminal_modes[terminal_modes.length - 1] != 0)
throw new IOException("Illegal terminal modes description, does not end in zero byte");
else
terminal_modes = new byte[] { 0 };
synchronized (this)
{
/* The following is just a nicer error, we would catch it anyway later in the channel code */
if (flag_closed)
throw new IOException("This session is closed.");
if (flag_pty_requested)
throw new IOException("A PTY was already requested.");
if (flag_execution_started)
throw new IOException(
"Cannot request PTY at this stage anymore, a remote execution has already started.");
flag_pty_requested = true;
}
cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
terminal_modes);
}
/**
* Request X11 forwarding for the current session.
* * You have to supply the name and port of your X-server. * * This method may only be called before a program or shell is started in * this session. * * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) * @param port the port of the real (target) X11 server (e.g., 6010) * @param cookie if non-null, then present this cookie to the real X11 server * @param singleConnection if true, then the server is instructed to only forward one single * connection, no more connections shall be forwarded after first, or after the session * channel has been closed * @throws IOException */ public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) throws IOException { if (hostname == null) throw new IllegalArgumentException("hostname argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_x11_requested) throw new IOException("X11 forwarding was already requested."); if (flag_execution_started) throw new IOException( "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); flag_x11_requested = true; } /* X11ServerData - used to store data about the target X11 server */ X11ServerData x11data = new X11ServerData(); x11data.hostname = hostname; x11data.port = port; x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ /* Generate fake cookie - this one is used between remote clients and our proxy */ byte[] fakeCookie = new byte[16]; String hexEncodedFakeCookie; /* Make sure that this fake cookie is unique for this connection */ while (true) { rnd.nextBytes(fakeCookie); /* Generate also hex representation of fake cookie */ StringBuffer tmp = new StringBuffer(32); for (int i = 0; i < fakeCookie.length; i++) { String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); } hexEncodedFakeCookie = tmp.toString(); /* Well, yes, chances are low, but we want to be on the safe side */ if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) break; } /* Ask for X11 forwarding */ cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); /* OK, that went fine, get ready to accept X11 connections... */ /* ... but only if the user has not called close() in the meantime =) */ synchronized (this) { if (flag_closed == false) { this.x11FakeCookie = hexEncodedFakeCookie; cm.registerX11Cookie(hexEncodedFakeCookie, x11data); } } /* Now it is safe to start remote X11 programs */ } /** * Execute a command on the remote machine. * * @param cmd * The command to execute on the remote host. * @throws IOException */ public void execCommand(String cmd) throws IOException { if (cmd == null) throw new IllegalArgumentException("cmd argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestExecCommand(cn, cmd); } /** * Start a shell on the remote machine. * * @throws IOException */ public void startShell() throws IOException { synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestShell(cn); } /** * Start a subsystem on the remote machine. * Unless you know what you are doing, you will never need this. * * @param name the name of the subsystem. * @throws IOException */ public void startSubSystem(String name) throws IOException { if (name == null) throw new IllegalArgumentException("name argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestSubSystem(cn, name); } /** * This method can be used to perform end-to-end session (i.e., SSH channel) * testing. It sends a 'ping' message to the server and waits for the 'pong' * from the server. * * Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply * packet. * * @throws IOException in case of any problem or when the session is closed */ public void ping() throws IOException { synchronized (this) { /* * The following is just a nicer error, we would catch it anyway * later in the channel code */ if (flag_closed) return 1; throw new IOException("This session is closed."); } cm.requestChannelTrileadPing(cn); } public InputStream getStdout() { return cn.getStdoutStream(); } public InputStream getStderr() { return cn.getStderrStream(); } public OutputStream getStdin() { return cn.getStdinStream(); } /** * Write stdout received from the other side to the specified {@link OutputStream}. * * * By default, when data arrives from the other side, trilead buffers them and lets * you read it at your convenience from {@link #getStdout()}. This is normally convenient, * but if all you are doing is to send the data to another {@link OutputStream} by * copying a stream, then you'll be end up wasting a thread just for this. * In such a situation, you can call this method and let the I/O handling thread of trilead * directly pass the received data to the output stream. This also eliminates the internal * buffer used for spooling. * * * When you do this, beware of a blocking write! If a write blocks, it'll affect * all the other channels and sessions that are sharing the same SSH connection, * as there's only one I/O thread. For example, this can happen if you are writing to * {@link Socket}. * * * If any data has already been received and spooled before calling this method, * the data will be sent to the given stream immediately. * *
* To signal the end of the stream, when the other side notifies us of EOF or when
* the channel closes, the output stream gets closed. If this is not desirable,
* you must wrap the output stream and ignore the {@link OutputStream#close()} call.
*/
public void pipeStdout(OutputStream os) throws IOException {
cn.pipeStdoutStream(os);
}
/**
* The same as {@link #pipeStdout(OutputStream)} except for stderr, not for stdout.
*/
public void pipeStderr(OutputStream os) throws IOException {
cn.pipeStderrStream(os);
}
/**
* This method blocks until there is more data available on either the
* stdout or stderr InputStream of this
* This method returns as soon as one of the following happens: *
* In any case, the result value contains ALL current conditions, which may be more * than the specified condition set (i.e., never use the "==" operator to test for conditions * in the bitmask, see also comments in {@link ChannelCondition}). *
* Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
* there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
* InputStreams of this
* This method may only be called before a program or shell is started in
* this session.
*
* Different aspects can be specified:
*
*
* You have to supply the name and port of your X-server.
*
* This method may only be called before a program or shell is started in
* this session.
*
* @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1)
* @param port the port of the real (target) X11 server (e.g., 6010)
* @param cookie if non-null, then present this cookie to the real X11 server
* @param singleConnection if true, then the server is instructed to only forward one single
* connection, no more connections shall be forwarded after first, or after the session
* channel has been closed
* @throws IOException
*/
public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection)
throws IOException
{
if (hostname == null)
throw new IllegalArgumentException("hostname argument may not be null");
synchronized (this)
{
/* The following is just a nicer error, we would catch it anyway later in the channel code */
if (flag_closed)
throw new IOException("This session is closed.");
if (flag_x11_requested)
throw new IOException("X11 forwarding was already requested.");
if (flag_execution_started)
throw new IOException(
"Cannot request X11 forwarding at this stage anymore, a remote execution has already started.");
flag_x11_requested = true;
}
/* X11ServerData - used to store data about the target X11 server */
X11ServerData x11data = new X11ServerData();
x11data.hostname = hostname;
x11data.port = port;
x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */
/* Generate fake cookie - this one is used between remote clients and our proxy */
byte[] fakeCookie = new byte[16];
String hexEncodedFakeCookie;
/* Make sure that this fake cookie is unique for this connection */
while (true)
{
rnd.nextBytes(fakeCookie);
/* Generate also hex representation of fake cookie */
StringBuffer tmp = new StringBuffer(32);
for (int i = 0; i < fakeCookie.length; i++)
{
String digit2 = Integer.toHexString(fakeCookie[i] & 0xff);
tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
}
hexEncodedFakeCookie = tmp.toString();
/* Well, yes, chances are low, but we want to be on the safe side */
if (cm.checkX11Cookie(hexEncodedFakeCookie) == null)
break;
}
/* Ask for X11 forwarding */
cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0);
/* OK, that went fine, get ready to accept X11 connections... */
/* ... but only if the user has not called close() in the meantime =) */
synchronized (this)
{
if (flag_closed == false)
{
this.x11FakeCookie = hexEncodedFakeCookie;
cm.registerX11Cookie(hexEncodedFakeCookie, x11data);
}
}
/* Now it is safe to start remote X11 programs */
}
/**
* Execute a command on the remote machine.
*
* @param cmd
* The command to execute on the remote host.
* @throws IOException
*/
public void execCommand(String cmd) throws IOException
{
if (cmd == null)
throw new IllegalArgumentException("cmd argument may not be null");
synchronized (this)
{
/* The following is just a nicer error, we would catch it anyway later in the channel code */
if (flag_closed)
throw new IOException("This session is closed.");
if (flag_execution_started)
throw new IOException("A remote execution has already started.");
flag_execution_started = true;
}
cm.requestExecCommand(cn, cmd);
}
/**
* Start a shell on the remote machine.
*
* @throws IOException
*/
public void startShell() throws IOException
{
synchronized (this)
{
/* The following is just a nicer error, we would catch it anyway later in the channel code */
if (flag_closed)
throw new IOException("This session is closed.");
if (flag_execution_started)
throw new IOException("A remote execution has already started.");
flag_execution_started = true;
}
cm.requestShell(cn);
}
/**
* Start a subsystem on the remote machine.
* Unless you know what you are doing, you will never need this.
*
* @param name the name of the subsystem.
* @throws IOException
*/
public void startSubSystem(String name) throws IOException
{
if (name == null)
throw new IllegalArgumentException("name argument may not be null");
synchronized (this)
{
/* The following is just a nicer error, we would catch it anyway later in the channel code */
if (flag_closed)
throw new IOException("This session is closed.");
if (flag_execution_started)
throw new IOException("A remote execution has already started.");
flag_execution_started = true;
}
cm.requestSubSystem(cn, name);
}
/**
* This method can be used to perform end-to-end session (i.e., SSH channel)
* testing. It sends a 'ping' message to the server and waits for the 'pong'
* from the server.
*
* Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request
* ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply
* packet.
*
* @throws IOException in case of any problem or when the session is closed
*/
public void ping() throws IOException
{
synchronized (this)
{
/*
* The following is just a nicer error, we would catch it anyway
* later in the channel code
*/
if (flag_closed)
throw new IOException("This session is closed.");
}
cm.requestChannelTrileadPing(cn);
public InputStream getStdout()
{
return cn.getStdoutStream();
}
public InputStream getStderr()
{
return cn.getStderrStream();
}
public OutputStream getStdin()
{
return cn.getStdinStream();
}
/**
* This method blocks until there is more data available on either the
* stdout or stderr InputStream of this
* This method returns as soon as one of the following happens:
*
* In any case, the result value contains ALL current conditions, which may be more
* than the specified condition set (i.e., never use the "==" operator to test for conditions
* in the bitmask, see also comments in {@link ChannelCondition}).
*
* Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
* there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
* InputStreams of this |
| Solution content |
|---|
package com.trilead.ssh2; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.security.SecureRandom; import com.trilead.ssh2.channel.Channel; import com.trilead.ssh2.channel.ChannelManager; import com.trilead.ssh2.channel.X11ServerData; /** * A
terminal_modes) is described in RFC4254.
*
* @param term
* The TERM environment variable value (e.g., vt100)
* @param term_width_characters
* terminal width, characters (e.g., 80)
* @param term_height_characters
* terminal height, rows (e.g., 24)
* @param term_width_pixels
* terminal width, pixels (e.g., 640)
* @param term_height_pixels
* terminal height, pixels (e.g., 480)
* @param terminal_modes
* encoded terminal modes (may be null)
* @throws IOException
*/
public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
int term_height_pixels, byte[] terminal_modes) throws IOException
{
if (term == null)
throw new IllegalArgumentException("TERM cannot be null.");
if ((terminal_modes != null) && (terminal_modes.length > 0))
{
if (terminal_modes[terminal_modes.length - 1] != 0)
throw new IOException("Illegal terminal modes description, does not end in zero byte");
}
else
terminal_modes = new byte[] { 0 };
synchronized (this)
{
/* The following is just a nicer error, we would catch it anyway later in the channel code */
if (flag_closed)
throw new IOException("This session is closed.");
if (flag_pty_requested)
throw new IOException("A PTY was already requested.");
if (flag_execution_started)
throw new IOException(
"Cannot request PTY at this stage anymore, a remote execution has already started.");
flag_pty_requested = true;
}
cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
terminal_modes);
}
/**
* Request X11 forwarding for the current session.
* * You have to supply the name and port of your X-server. * * This method may only be called before a program or shell is started in * this session. * * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) * @param port the port of the real (target) X11 server (e.g., 6010) * @param cookie if non-null, then present this cookie to the real X11 server * @param singleConnection if true, then the server is instructed to only forward one single * connection, no more connections shall be forwarded after first, or after the session * channel has been closed * @throws IOException */ public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) throws IOException { if (hostname == null) throw new IllegalArgumentException("hostname argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_x11_requested) throw new IOException("X11 forwarding was already requested."); if (flag_execution_started) throw new IOException( "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); flag_x11_requested = true; } /* X11ServerData - used to store data about the target X11 server */ X11ServerData x11data = new X11ServerData(); x11data.hostname = hostname; x11data.port = port; x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ /* Generate fake cookie - this one is used between remote clients and our proxy */ byte[] fakeCookie = new byte[16]; String hexEncodedFakeCookie; /* Make sure that this fake cookie is unique for this connection */ while (true) { rnd.nextBytes(fakeCookie); /* Generate also hex representation of fake cookie */ StringBuffer tmp = new StringBuffer(32); for (int i = 0; i < fakeCookie.length; i++) { String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); } hexEncodedFakeCookie = tmp.toString(); /* Well, yes, chances are low, but we want to be on the safe side */ if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) break; } /* Ask for X11 forwarding */ cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); /* OK, that went fine, get ready to accept X11 connections... */ /* ... but only if the user has not called close() in the meantime =) */ synchronized (this) { if (flag_closed == false) { this.x11FakeCookie = hexEncodedFakeCookie; cm.registerX11Cookie(hexEncodedFakeCookie, x11data); } } /* Now it is safe to start remote X11 programs */ } /** * Execute a command on the remote machine. * * @param cmd * The command to execute on the remote host. * @throws IOException */ public void execCommand(String cmd) throws IOException { if (cmd == null) throw new IllegalArgumentException("cmd argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestExecCommand(cn, cmd); } /** * Start a shell on the remote machine. * * @throws IOException */ public void startShell() throws IOException { synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestShell(cn); } /** * Start a subsystem on the remote machine. * Unless you know what you are doing, you will never need this. * * @param name the name of the subsystem. * @throws IOException */ public void startSubSystem(String name) throws IOException { if (name == null) throw new IllegalArgumentException("name argument may not be null"); synchronized (this) { /* The following is just a nicer error, we would catch it anyway later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); if (flag_execution_started) throw new IOException("A remote execution has already started."); flag_execution_started = true; } cm.requestSubSystem(cn, name); } /** * This method can be used to perform end-to-end session (i.e., SSH channel) * testing. It sends a 'ping' message to the server and waits for the 'pong' * from the server. * * Implementation details: this method sends a SSH_MSG_CHANNEL_REQUEST request * ('trilead-ping') to the server and waits for the SSH_MSG_CHANNEL_FAILURE reply * packet. * * @throws IOException in case of any problem or when the session is closed */ public void ping() throws IOException { synchronized (this) { /* * The following is just a nicer error, we would catch it anyway * later in the channel code */ if (flag_closed) throw new IOException("This session is closed."); } cm.requestChannelTrileadPing(cn); } public InputStream getStdout() { return cn.getStdoutStream(); } public InputStream getStderr() { return cn.getStderrStream(); } public OutputStream getStdin() { return cn.getStdinStream(); } /** * Write stdout received from the other side to the specified {@link OutputStream}. * * * By default, when data arrives from the other side, trilead buffers them and lets * you read it at your convenience from {@link #getStdout()}. This is normally convenient, * but if all you are doing is to send the data to another {@link OutputStream} by * copying a stream, then you'll be end up wasting a thread just for this. * In such a situation, you can call this method and let the I/O handling thread of trilead * directly pass the received data to the output stream. This also eliminates the internal * buffer used for spooling. * * * When you do this, beware of a blocking write! If a write blocks, it'll affect * all the other channels and sessions that are sharing the same SSH connection, * as there's only one I/O thread. For example, this can happen if you are writing to * {@link Socket}. * * * If any data has already been received and spooled before calling this method, * the data will be sent to the given stream immediately. * *
* To signal the end of the stream, when the other side notifies us of EOF or when
* the channel closes, the output stream gets closed. If this is not desirable,
* you must wrap the output stream and ignore the {@link OutputStream#close()} call.
*/
public void pipeStdout(OutputStream os) throws IOException {
cn.pipeStdoutStream(os);
}
/**
* The same as {@link #pipeStdout(OutputStream)} except for stderr, not for stdout.
*/
public void pipeStderr(OutputStream os) throws IOException {
cn.pipeStderrStream(os);
}
/**
* This method blocks until there is more data available on either the
* stdout or stderr InputStream of this
* This method returns as soon as one of the following happens: *
* In any case, the result value contains ALL current conditions, which may be more * than the specified condition set (i.e., never use the "==" operator to test for conditions * in the bitmask, see also comments in {@link ChannelCondition}). *
* Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
* there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
* InputStreams of this |
| File |
|---|
| Session.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
*/ <<<<<<< HEAD:src/com/trilead/ssh2/StreamGobbler.java package com.trilead.ssh2; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; /** * A |
| Solution content |
|---|
package com.trilead.ssh2; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; /** * A |
| File |
|---|
| StreamGobbler.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
*
}
<<<<<<< HEAD:src/com/trilead/ssh2/auth/AuthenticationManager.java
package com.trilead.ssh2.auth;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;
import java.util.Vector;
import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.PacketServiceAccept;
import com.trilead.ssh2.packets.PacketServiceRequest;
import com.trilead.ssh2.packets.PacketUserauthBanner;
import com.trilead.ssh2.packets.PacketUserauthFailure;
import com.trilead.ssh2.packets.PacketUserauthInfoRequest;
import com.trilead.ssh2.packets.PacketUserauthInfoResponse;
import com.trilead.ssh2.packets.PacketUserauthRequestInteractive;
import com.trilead.ssh2.packets.PacketUserauthRequestNone;
import com.trilead.ssh2.packets.PacketUserauthRequestPassword;
import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesWriter;
import com.trilead.ssh2.signature.DSAPrivateKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPrivateKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;
/**
* AuthenticationManager.
* @author Christian Plattner, plattner@trilead.com
* @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class AuthenticationManager implements MessageHandler
{
TransportManager tm;
Vector packets = new Vector();
boolean connectionClosed = false;
String banner;
String[] remainingMethods = new String[0];
boolean isPartialSuccess = false;
boolean authenticated = false;
boolean initDone = false;
public AuthenticationManager(TransportManager tm)
{
this.tm = tm;
}
boolean methodPossible(String methName)
{
if (remainingMethods == null)
return false;
for (int i = 0; i < remainingMethods.length; i++)
{
if (remainingMethods[i].compareTo(methName) == 0)
return true;
}
return false;
}
byte[] deQueue() throws IOException
{
synchronized (packets)
{
while (packets.size() == 0)
{
if (connectionClosed)
throw (IOException) new IOException("The connection is closed.").initCause(tm
.getReasonClosedCause());
try
{
packets.wait();
}
catch (InterruptedException ign)
{
throw new InterruptedIOException();
}
}
/* This sequence works with J2ME */
byte[] res = (byte[]) packets.firstElement();
packets.removeElementAt(0);
return res;
}
}
byte[] getNextMessage() throws IOException
{
while (true)
{
byte[] msg = deQueue();
if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
return msg;
PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
banner = sb.getBanner();
}
}
public String[] getRemainingMethods(String user) throws IOException
{
initialize(user);
return remainingMethods;
}
public boolean getPartialSuccess()
{
return isPartialSuccess;
}
private boolean initialize(String user) throws IOException
{
if (initDone == false)
{
tm.registerMessageHandler(this, 0, 255);
PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
tm.sendMessage(sr.getPayload());
PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
tm.sendMessage(urn.getPayload());
byte[] msg = getNextMessage();
new PacketServiceAccept(msg, 0, msg.length);
msg = getNextMessage();
initDone = true;
if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
}
return authenticated;
}
public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
throws IOException
{
try
{
initialize(user);
if (methodPossible("publickey") == false)
throw new IOException("Authentication method publickey not supported by the server at this stage.");
Object key = PEMDecoder.decode(PEMPrivateKey, password);
if (key instanceof DSAPrivateKey)
{
DSAPrivateKey pk = (DSAPrivateKey) key;
byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
TypesWriter tw = new TypesWriter();
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString("ssh-dss");
tw.writeString(pk_enc, 0, pk_enc.length);
byte[] msg = tw.getBytes();
DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
"ssh-dss", pk_enc, ds_enc);
tm.sendMessage(ua.getPayload());
}
else if (key instanceof RSAPrivateKey)
{
RSAPrivateKey pk = (RSAPrivateKey) key;
byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());
TypesWriter tw = new TypesWriter();
{
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString("ssh-rsa");
tw.writeString(pk_enc, 0, pk_enc.length);
byte[] msg = tw.getBytes();
RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);
byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
"ssh-rsa", pk_enc, rsa_sig_enc);
tm.sendMessage(ua.getPayload());
}
else
{
throw new IOException("Unknown private key type returned by the PEM decoder.");
}
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
}
}
public boolean authenticateNone(String user) throws IOException
{
try
{
initialize(user);
return authenticated;
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("None authentication failed.").initCause(e);
}
}
public boolean authenticatePassword(String user, String pass) throws IOException
{
try
{
initialize(user);
if (methodPossible("password") == false)
throw new IOException("Authentication method password not supported by the server at this stage.");
PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
tm.sendMessage(ua.getPayload());
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Password authentication failed.").initCause(e);
}
}
public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
{
try
{
initialize(user);
if (methodPossible("keyboard-interactive") == false)
throw new IOException(
"Authentication method keyboard-interactive not supported by the server at this stage.");
if (submethods == null)
submethods = new String[0];
PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
submethods);
tm.sendMessage(ua.getPayload());
while (true)
{
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
{
PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
String[] responses;
try
{
responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
.getPrompt(), pui.getEcho());
}
catch (Exception e)
{
throw (IOException) new IOException("Exception in callback.").initCause(e);
}
if (responses == null)
throw new IOException("Your callback may not return NULL!");
PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
tm.sendMessage(puir.getPayload());
continue;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
}
}
public void handleMessage(byte[] msg, int msglen) throws IOException
{
synchronized (packets)
{
if (msg == null)
{
connectionClosed = true;
}
else
{
byte[] tmp = new byte[msglen];
System.arraycopy(msg, 0, tmp, 0, msglen);
packets.addElement(tmp);
}
packets.notifyAll();
if (packets.size() > 5)
{
connectionClosed = true;
throw new IOException("Error, peer is flooding us with authentication packets.");
}
}
}
}
=======
package com.trilead.ssh2.auth;
import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.*;
import com.trilead.ssh2.signature.*;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Vector;
/**
* AuthenticationManager.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class AuthenticationManager implements MessageHandler
{
TransportManager tm;
Vector packets = new Vector();
boolean connectionClosed = false;
String banner;
String[] remainingMethods = new String[0];
boolean isPartialSuccess = false;
boolean authenticated = false;
boolean initDone = false;
public AuthenticationManager(TransportManager tm)
{
this.tm = tm;
}
boolean methodPossible(String methName)
{
if (remainingMethods == null)
return false;
for (int i = 0; i < remainingMethods.length; i++)
{
if (remainingMethods[i].compareTo(methName) == 0)
return true;
}
return false;
}
byte[] deQueue() throws IOException
{
synchronized (packets)
{
while (packets.size() == 0)
{
if (connectionClosed)
throw (IOException) new IOException("The connection is closed.").initCause(tm
.getReasonClosedCause());
try
{
packets.wait();
}
catch (InterruptedException ign)
{
}
}
/* This sequence works with J2ME */
byte[] res = (byte[]) packets.firstElement();
packets.removeElementAt(0);
return res;
}
}
byte[] getNextMessage() throws IOException
{
while (true)
{
byte[] msg = deQueue();
if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
return msg;
PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
banner = sb.getBanner();
}
}
public String[] getRemainingMethods(String user) throws IOException
{
initialize(user);
return remainingMethods;
}
public boolean getPartialSuccess()
{
return isPartialSuccess;
}
private boolean initialize(String user) throws IOException
{
if (initDone == false)
{
tm.registerMessageHandler(this, 0, 255);
PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
tm.sendMessage(sr.getPayload());
PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
tm.sendMessage(urn.getPayload());
byte[] msg = getNextMessage();
new PacketServiceAccept(msg, 0, msg.length);
msg = getNextMessage();
initDone = true;
if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
}
return authenticated;
}
public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException {
initialize(user);
boolean success = false;
Iterator agentIdentities = proxy.getIdentities().iterator();
while(agentIdentities.hasNext()) {
AgentIdentity identity = (AgentIdentity)agentIdentities.next();
success = authenticatePublicKey(user, proxy, identity);
if(success) {
return true;
}
}
return false;
}
boolean authenticatePublicKey(String user, AgentProxy proxy, AgentIdentity identity) throws IOException {
if (methodPossible("publickey") == false)
throw new IOException("Authentication method publickey not supported by the server at this stage.");
byte[] pubKeyBlob = identity.getPublicKeyBlob();
if(pubKeyBlob == null) {
return false;
}
TypesWriter tw = new TypesWriter();
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString(identity.getAlgName());
tw.writeString(pubKeyBlob, 0, pubKeyBlob.length);
byte[] msg = tw.getBytes();
byte[] response = identity.sign(msg);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey(
"ssh-connection", user, identity.getAlgName(), pubKeyBlob, response);
tm.sendMessage(ua.getPayload());
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
throws IOException
{
try
{
initialize(user);
if (methodPossible("publickey") == false)
throw new IOException("Authentication method publickey not supported by the server at this stage.");
Object key = PEMDecoder.decode(PEMPrivateKey, password);
if (key instanceof DSAPrivateKey)
{
DSAPrivateKey pk = (DSAPrivateKey) key;
byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
TypesWriter tw = new TypesWriter();
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString("ssh-dss");
tw.writeString(pk_enc, 0, pk_enc.length);
byte[] msg = tw.getBytes();
DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
"ssh-dss", pk_enc, ds_enc);
tm.sendMessage(ua.getPayload());
}
else if (key instanceof RSAPrivateKey)
{
RSAPrivateKey pk = (RSAPrivateKey) key;
byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());
TypesWriter tw = new TypesWriter();
{
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString("ssh-rsa");
tw.writeString(pk_enc, 0, pk_enc.length);
}
byte[] msg = tw.getBytes();
RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);
byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
"ssh-rsa", pk_enc, rsa_sig_enc);
tm.sendMessage(ua.getPayload());
}
else
{
throw new IOException("Unknown private key type returned by the PEM decoder.");
}
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
}
}
public boolean authenticateNone(String user) throws IOException
{
try
{
initialize(user);
return authenticated;
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("None authentication failed.").initCause(e);
}
}
public boolean authenticatePassword(String user, String pass) throws IOException
{
try
{
initialize(user);
if (methodPossible("password") == false)
throw new IOException("Authentication method password not supported by the server at this stage.");
PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
tm.sendMessage(ua.getPayload());
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Password authentication failed.").initCause(e);
}
}
public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
{
try
{
initialize(user);
if (methodPossible("keyboard-interactive") == false)
throw new IOException(
"Authentication method keyboard-interactive not supported by the server at this stage.");
if (submethods == null)
submethods = new String[0];
PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
submethods);
tm.sendMessage(ua.getPayload());
while (true)
{
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
{
PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
String[] responses;
try
{
responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
.getPrompt(), pui.getEcho());
}
catch (Exception e)
{
throw (IOException) new IOException("Exception in callback.").initCause(e);
}
if (responses == null)
throw new IOException("Your callback may not return NULL!");
PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
tm.sendMessage(puir.getPayload());
continue;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
}
}
public void handleMessage(byte[] msg, int msglen) throws IOException
{
synchronized (packets)
{
if (msg == null)
{
connectionClosed = true;
}
else
{
byte[] tmp = new byte[msglen];
System.arraycopy(msg, 0, tmp, 0, msglen);
packets.addElement(tmp);
}
packets.notifyAll();
if (packets.size() > 5)
{
connectionClosed = true;
throw new IOException("Error, peer is flooding us with authentication packets.");
}
}
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java |
| Solution content |
|---|
package com.trilead.ssh2.auth;
import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.*;
import com.trilead.ssh2.signature.*;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Vector;
/**
* AuthenticationManager.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class AuthenticationManager implements MessageHandler
{
TransportManager tm;
Vector packets = new Vector();
boolean connectionClosed = false;
String banner;
String[] remainingMethods = new String[0];
boolean isPartialSuccess = false;
boolean authenticated = false;
boolean initDone = false;
public AuthenticationManager(TransportManager tm)
{
this.tm = tm;
}
boolean methodPossible(String methName)
{
if (remainingMethods == null)
return false;
for (int i = 0; i < remainingMethods.length; i++)
{
if (remainingMethods[i].compareTo(methName) == 0)
return true;
}
return false;
}
byte[] deQueue() throws IOException
{
synchronized (packets)
{
while (packets.size() == 0)
{
if (connectionClosed)
throw (IOException) new IOException("The connection is closed.").initCause(tm
.getReasonClosedCause());
try
{
packets.wait();
}
catch (InterruptedException ign)
{
throw new InterruptedIOException();
}
}
/* This sequence works with J2ME */
byte[] res = (byte[]) packets.firstElement();
packets.removeElementAt(0);
return res;
}
}
byte[] getNextMessage() throws IOException
{
while (true)
{
byte[] msg = deQueue();
if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
return msg;
PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
banner = sb.getBanner();
}
}
public String[] getRemainingMethods(String user) throws IOException
{
initialize(user);
return remainingMethods;
}
public boolean getPartialSuccess()
{
return isPartialSuccess;
}
private boolean initialize(String user) throws IOException
{
if (initDone == false)
{
tm.registerMessageHandler(this, 0, 255);
PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
tm.sendMessage(sr.getPayload());
PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
tm.sendMessage(urn.getPayload());
byte[] msg = getNextMessage();
new PacketServiceAccept(msg, 0, msg.length);
msg = getNextMessage();
initDone = true;
if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
}
return authenticated;
}
public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException {
initialize(user);
boolean success = false;
Iterator agentIdentities = proxy.getIdentities().iterator();
while(agentIdentities.hasNext()) {
AgentIdentity identity = (AgentIdentity)agentIdentities.next();
success = authenticatePublicKey(user, proxy, identity);
if(success) {
return true;
}
}
return false;
}
boolean authenticatePublicKey(String user, AgentProxy proxy, AgentIdentity identity) throws IOException {
if (methodPossible("publickey") == false)
throw new IOException("Authentication method publickey not supported by the server at this stage.");
byte[] pubKeyBlob = identity.getPublicKeyBlob();
if(pubKeyBlob == null) {
return false;
}
TypesWriter tw = new TypesWriter();
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString(identity.getAlgName());
tw.writeString(pubKeyBlob, 0, pubKeyBlob.length);
byte[] msg = tw.getBytes();
byte[] response = identity.sign(msg);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey(
"ssh-connection", user, identity.getAlgName(), pubKeyBlob, response);
tm.sendMessage(ua.getPayload());
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
throws IOException
{
try
{
initialize(user);
if (methodPossible("publickey") == false)
throw new IOException("Authentication method publickey not supported by the server at this stage.");
Object key = PEMDecoder.decode(PEMPrivateKey, password);
if (key instanceof DSAPrivateKey)
{
DSAPrivateKey pk = (DSAPrivateKey) key;
byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
TypesWriter tw = new TypesWriter();
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString("ssh-dss");
tw.writeString(pk_enc, 0, pk_enc.length);
byte[] msg = tw.getBytes();
DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
"ssh-dss", pk_enc, ds_enc);
tm.sendMessage(ua.getPayload());
}
else if (key instanceof RSAPrivateKey)
{
RSAPrivateKey pk = (RSAPrivateKey) key;
byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());
TypesWriter tw = new TypesWriter();
{
byte[] H = tm.getSessionIdentifier();
tw.writeString(H, 0, H.length);
tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
tw.writeString(user);
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
tw.writeString("ssh-rsa");
tw.writeString(pk_enc, 0, pk_enc.length);
}
byte[] msg = tw.getBytes();
RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);
byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
"ssh-rsa", pk_enc, rsa_sig_enc);
tm.sendMessage(ua.getPayload());
}
else
{
throw new IOException("Unknown private key type returned by the PEM decoder.");
}
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
}
}
public boolean authenticateNone(String user) throws IOException
{
try
{
initialize(user);
return authenticated;
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("None authentication failed.").initCause(e);
}
}
public boolean authenticatePassword(String user, String pass) throws IOException
{
try
{
initialize(user);
if (methodPossible("password") == false)
throw new IOException("Authentication method password not supported by the server at this stage.");
PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
tm.sendMessage(ua.getPayload());
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Password authentication failed.").initCause(e);
}
}
public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
{
try
{
initialize(user);
if (methodPossible("keyboard-interactive") == false)
throw new IOException(
"Authentication method keyboard-interactive not supported by the server at this stage.");
if (submethods == null)
submethods = new String[0];
PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
submethods);
tm.sendMessage(ua.getPayload());
while (true)
{
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
{
PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
String[] responses;
try
{
responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
.getPrompt(), pui.getEcho());
}
catch (Exception e)
{
throw (IOException) new IOException("Exception in callback.").initCause(e);
}
if (responses == null)
throw new IOException("Your callback may not return NULL!");
PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
tm.sendMessage(puir.getPayload());
continue;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
}
}
public void handleMessage(byte[] msg, int msglen) throws IOException
{
synchronized (packets)
{
if (msg == null)
{
connectionClosed = true;
}
else
{
byte[] tmp = new byte[msglen];
System.arraycopy(msg, 0, tmp, 0, msglen);
packets.addElement(tmp);
}
packets.notifyAll();
if (packets.size() > 5)
{
connectionClosed = true;
throw new IOException("Error, peer is flooding us with authentication packets.");
}
}
}
} |
| File |
|---|
| AuthenticationManager.java |
| Developer's decision |
|---|
| Combination |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
{
<<<<<<< HEAD:src/com/trilead/ssh2/channel/Channel.java
package com.trilead.ssh2.channel;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.transport.TransportManager;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import static com.trilead.ssh2.util.IOUtils.closeQuietly;
/**
* Channel.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class Channel
{
/*
* OK. Here is an important part of the JVM Specification:
* (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
*
* Any association between locks and variables is purely conventional.
* Locking any lock conceptually flushes all variables from a thread's
* working memory, and unlocking any lock forces the writing out to main
* memory of all variables that the thread has assigned. That a lock may be
* associated with a particular object or a class is purely a convention.
* (...)
*
* If a thread uses a particular shared variable only after locking a
* particular lock and before the corresponding unlocking of that same lock,
* then the thread will read the shared value of that variable from main
* memory after the lock operation, if necessary, and will copy back to main
* memory the value most recently assigned to that variable before the
* unlock operation.
*
* This, in conjunction with the mutual exclusion rules for locks, suffices
* to guarantee that values are correctly transmitted from one thread to
* another through shared variables.
*
* ====> Always keep that in mind when modifying the Channel/ChannelManger
* code.
*
*/
static final int STATE_OPENING = 1;
static final int STATE_OPEN = 2;
static final int STATE_CLOSED = 4;
private static final int CHANNEL_BUFFER_SIZE = Integer.getInteger(
Channel.class.getName()+".bufferSize",
1024*1024 + 16*1024).intValue();
/**
* This channel's session size.
*/
// @GuarydedBy("this")
int channelBufferSize = CHANNEL_BUFFER_SIZE;
/*
* To achieve correctness, the following rules have to be respected when
* accessing this object:
*/
// These fields can always be read
final ChannelManager cm;
final ChannelOutputStream stdinStream;
/**
* One stream.
*
* Either {@link #stream} and {@link #buffer} is set, or the {@link #sink} is set, but those
* are mutually exclusive. The former is used when we are buffering data and let the application
* read it via {@link InputStream}, and the latter is used when we are passing through the data
* to another {@link OutputStream}.
*
* The synchronization is done by {@link Channel}
*/
class Output {
ChannelInputStream stream;
FifoBuffer buffer = new FifoBuffer(Channel.this, 2048, channelBufferSize);
OutputStream sink;
public void write(byte[] buf, int start, int len) throws IOException {
if (buffer!=null) {
try {
buffer.write(buf,start,len);
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
} else {
sink.write(buf,start,len);
freeupWindow(len);
}
}
/**
* How many bytes can be read from the buffer?
*/
public int readable() {
if (buffer!=null) return buffer.readable();
else return 0;
}
/**
* See {@link InputStream#available()}
*/
public int available() {
if (buffer==null)
throw new IllegalStateException("Output is being piped to "+sink);
int sz = buffer.readable();
if (sz>0) return sz;
return isEOF() ? -1 : 0;
}
/**
* Read from the buffer.
*/
public int read(byte[] buf, int start, int len) throws InterruptedException {
return buffer.read(buf,start,len);
}
/**
* Called when there will be no more data arriving to this output any more.
* Not that buffer might still have some more data that needs to be drained.
*/
public void eof() {
if (buffer!=null)
buffer.close();
else
closeQuietly(sink);
}
/**
* Instead of spooling data, let our I/O thread write to the given {@link OutputStream}.
*/
public void pipeTo(OutputStream os) throws IOException {
sink = os;
if (buffer.readable()!=0) {
freeupWindow(buffer.writeTo(os));
}
buffer = null;
stream = null;
}
}
final Output stdout = new Output();
final Output stderr = new Output();
// These two fields will only be written while the Channel is in state
// STATE_OPENING.
// The code makes sure that the two fields are written out when the state is
// changing to STATE_OPEN.
// Therefore, if you know that the Channel is in state STATE_OPEN, then you
// can read these two fields without synchronizing on the Channel. However, make
// sure that you get the latest values (e.g., flush caches by synchronizing on any
// object). However, to be on the safe side, you can lock the channel.
int localID = -1;
int remoteID = -1;
/*
* Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
* msg.
*
* This is a little bit complicated, but we have to do it in that way, since
* we cannot keep a lock on the Channel during the send operation (this
* would block sometimes the receiver thread, and, in extreme cases, can
* lead to a deadlock on both sides of the connection (senders are blocked
* since the receive buffers on the other side are full, and receiver
* threads wait for the senders to finish). It all depends on the
* implementation on the other side. But we cannot make any assumptions, we
* have to assume the worst case. Confused? Just believe me.
*/
/*
* If you send a message on a channel, then you have to aquire the
* "channelSendLock" and check the "closeMessageSent" flag (this variable
* may only be accessed while holding the "channelSendLock" !!!
*
* BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
* above.
*/
final Object channelSendLock = new Object();
boolean closeMessageSent = false;
/*
* Stop memory fragmentation by allocating this often used buffer.
* May only be used while holding the channelSendLock
*/
final byte[] msgWindowAdjust = new byte[9];
// If you access (read or write) any of the following fields, then you have
// to synchronize on the channel.
int state = STATE_OPENING;
boolean closeMessageRecv = false;
/* This is a stupid implementation. At the moment we can only wait
* for one pending request per channel.
*/
int successCounter = 0;
int failedCounter = 0;
int localWindow = 0; /* locally, we use a small window, < 2^31 */
long remoteWindow = 0; /* long for readable 2^32 - 1 window support */
int localMaxPacketSize = -1;
int remoteMaxPacketSize = -1;
private boolean eof = false;
synchronized void eof() {
stdout.eof();
stderr.eof();
eof = true;
}
boolean isEOF() {
return eof;
}
Integer exit_status;
String exit_signal;
// we keep the x11 cookie so that this channel can be closed when this
// specific x11 forwarding gets stopped
String hexX11FakeCookie;
// reasonClosed is special, since we sometimes need to access it
// while holding the channelSendLock.
// We protect it with a private short term lock.
private final Object reasonClosedLock = new Object();
private String reasonClosed = null;
public Channel(ChannelManager cm)
{
this.cm = cm;
this.localWindow = channelBufferSize;
this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE - 1024; // leave enough slack
this.stdinStream = new ChannelOutputStream(this);
this.stdout.stream = new ChannelInputStream(this, false);
this.stderr.stream = new ChannelInputStream(this, true);
}
/* Methods to allow access from classes outside of this package */
public synchronized void setWindowSize(int newSize) {
if (newSize<=0) throw new IllegalArgumentException("Invalid value: "+newSize);
this.channelBufferSize = newSize;
// next time when the other side sends us something, we'll issue SSH_MSG_CHANNEL_WINDOW_ADJUST
}
public ChannelInputStream getStderrStream()
{
return stderr.stream;
}
public ChannelOutputStream getStdinStream()
{
return stdinStream;
}
public ChannelInputStream getStdoutStream()
{
return stdout.stream;
}
public synchronized void pipeStdoutStream(OutputStream os) throws IOException {
stdout.pipeTo(os);
}
public synchronized void pipeStderrStream(OutputStream os) throws IOException {
stderr.pipeTo(os);
}
public String getExitSignal()
{
synchronized (this)
{
return exit_signal;
}
}
public Integer getExitStatus()
{
synchronized (this)
return exit_status;
}
}
public String getReasonClosed()
{
synchronized (reasonClosedLock)
{
return reasonClosed;
}
}
public void setReasonClosed(String reasonClosed)
{
synchronized (reasonClosedLock)
{
if (this.reasonClosed == null)
this.reasonClosed = reasonClosed;
}
}
/**
* Update the flow control couner and if necessary, sends ACK to the other end to
* let it send more data.
*/
void freeupWindow(int copylen) throws IOException {
if (copylen <= 0) return;
int increment = 0;
int remoteID;
int localID;
synchronized (this) {
if (localWindow <= ((channelBufferSize * 3) / 4)) {
// have enough local window been consumed? if so, we'll send Ack
// the window control is on the combined bytes of stdout & stderr
int space = channelBufferSize - stdout.readable() - stderr.readable();
increment = space - localWindow;
if (increment > 0) // increment<0 can't happen, but be defensive
localWindow += increment;
}
remoteID = this.remoteID; /* read while holding the lock */
localID = this.localID; /* read while holding the lock */
}
/*
* If a consumer reads stdout and stdin in parallel, we may end up with
* sending two msgWindowAdjust messages. Luckily, it
* does not matter in which order they arrive at the server.
*/
if (increment > 0)
{
if (log.isEnabled())
log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
synchronized (channelSendLock)
{
byte[] msg = msgWindowAdjust;
msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
msg[1] = (byte) (remoteID >> 24);
msg[2] = (byte) (remoteID >> 16);
msg[3] = (byte) (remoteID >> 8);
msg[4] = (byte) (remoteID);
msg[5] = (byte) (increment >> 24);
msg[6] = (byte) (increment >> 16);
msg[7] = (byte) (increment >> 8);
msg[8] = (byte) (increment);
if (closeMessageSent == false)
cm.tm.sendMessage(msg);
}
}
}
private static final Logger log = Logger.getLogger(Channel.class);
}
=======
package com.trilead.ssh2.channel;
/**
* Channel.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class Channel
{
/*
* OK. Here is an important part of the JVM Specification:
* (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
*
* Any association between locks and variables is purely conventional.
* Locking any lock conceptually flushes all variables from a thread's
* working memory, and unlocking any lock forces the writing out to main
* memory of all variables that the thread has assigned. That a lock may be
* associated with a particular object or a class is purely a convention.
* (...)
*
* If a thread uses a particular shared variable only after locking a
* particular lock and before the corresponding unlocking of that same lock,
* then the thread will read the shared value of that variable from main
* memory after the lock operation, if necessary, and will copy back to main
* memory the value most recently assigned to that variable before the
* unlock operation.
*
* This, in conjunction with the mutual exclusion rules for locks, suffices
* to guarantee that values are correctly transmitted from one thread to
* another through shared variables.
*
* ====> Always keep that in mind when modifying the Channel/ChannelManger
* code.
*
*/
static final int STATE_OPENING = 1;
static final int STATE_OPEN = 2;
static final int STATE_CLOSED = 4;
static final int CHANNEL_BUFFER_SIZE = 30000;
/*
* To achieve correctness, the following rules have to be respected when
* accessing this object:
*/
// These fields can always be read
final ChannelManager cm;
final ChannelOutputStream stdinStream;
final ChannelInputStream stdoutStream;
final ChannelInputStream stderrStream;
// These two fields will only be written while the Channel is in state
// STATE_OPENING.
// The code makes sure that the two fields are written out when the state is
// changing to STATE_OPEN.
// Therefore, if you know that the Channel is in state STATE_OPEN, then you
// can read these two fields without synchronizing on the Channel. However, make
// sure that you get the latest values (e.g., flush caches by synchronizing on any
// object). However, to be on the safe side, you can lock the channel.
int localID = -1;
int remoteID = -1;
/*
* Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
* msg.
*
* This is a little bit complicated, but we have to do it in that way, since
* we cannot keep a lock on the Channel during the send operation (this
* would block sometimes the receiver thread, and, in extreme cases, can
* lead to a deadlock on both sides of the connection (senders are blocked
* since the receive buffers on the other side are full, and receiver
* threads wait for the senders to finish). It all depends on the
* implementation on the other side. But we cannot make any assumptions, we
* have to assume the worst case. Confused? Just believe me.
*/
/*
* If you send a message on a channel, then you have to aquire the
* "channelSendLock" and check the "closeMessageSent" flag (this variable
* may only be accessed while holding the "channelSendLock" !!!
*
* BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
* above.
*/
final Object channelSendLock = new Object();
boolean closeMessageSent = false;
/*
* Stop memory fragmentation by allocating this often used buffer.
* May only be used while holding the channelSendLock
*/
final byte[] msgWindowAdjust = new byte[9];
// If you access (read or write) any of the following fields, then you have
// to synchronize on the channel.
int state = STATE_OPENING;
boolean closeMessageRecv = false;
/* This is a stupid implementation. At the moment we can only wait
* for one pending request per channel.
*/
int successCounter = 0;
int failedCounter = 0;
int localWindow = 0; /* locally, we use a small window, < 2^31 */
long remoteWindow = 0; /* long for readable 2^32 - 1 window support */
int localMaxPacketSize = -1;
int remoteMaxPacketSize = -1;
final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
int stdoutReadpos = 0;
int stdoutWritepos = 0;
int stderrReadpos = 0;
int stderrWritepos = 0;
boolean EOF = false;
Integer exit_status;
String exit_signal;
// we keep the x11 cookie so that this channel can be closed when this
// specific x11 forwarding gets stopped
String hexX11FakeCookie;
// reasonClosed is special, since we sometimes need to access it
// while holding the channelSendLock.
// We protect it with a private short term lock.
private final Object reasonClosedLock = new Object();
private String reasonClosed = null;
public Channel(ChannelManager cm)
{
this.cm = cm;
this.localWindow = CHANNEL_BUFFER_SIZE;
this.localMaxPacketSize = 35000 - 1024; // leave enough slack
this.stdinStream = new ChannelOutputStream(this);
this.stdoutStream = new ChannelInputStream(this, false);
this.stderrStream = new ChannelInputStream(this, true);
}
/* Methods to allow access from classes outside of this package */
public ChannelInputStream getStderrStream()
{
return stderrStream;
}
public ChannelOutputStream getStdinStream()
{
return stdinStream;
}
public ChannelInputStream getStdoutStream()
{
return stdoutStream;
}
public String getExitSignal()
{
synchronized (this)
{
return exit_signal;
}
}
public Integer getExitStatus()
{
synchronized (this)
{
return exit_status;
}
}
public String getReasonClosed()
{
synchronized (reasonClosedLock)
{
return reasonClosed;
}
}
public void setReasonClosed(String reasonClosed)
{
synchronized (reasonClosedLock)
{
if (this.reasonClosed == null)
this.reasonClosed = reasonClosed;
}
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/Channel.java |
| Solution content |
|---|
* to another {@link OutputStream}.
package com.trilead.ssh2.channel;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.transport.TransportManager;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import static com.trilead.ssh2.util.IOUtils.closeQuietly;
/**
* Channel.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class Channel
{
/*
* OK. Here is an important part of the JVM Specification:
* (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
*
* Any association between locks and variables is purely conventional.
* Locking any lock conceptually flushes all variables from a thread's
* working memory, and unlocking any lock forces the writing out to main
* memory of all variables that the thread has assigned. That a lock may be
* associated with a particular object or a class is purely a convention.
* (...)
*
* If a thread uses a particular shared variable only after locking a
* particular lock and before the corresponding unlocking of that same lock,
* then the thread will read the shared value of that variable from main
* memory after the lock operation, if necessary, and will copy back to main
* memory the value most recently assigned to that variable before the
* unlock operation.
*
* This, in conjunction with the mutual exclusion rules for locks, suffices
* to guarantee that values are correctly transmitted from one thread to
* another through shared variables.
*
* ====> Always keep that in mind when modifying the Channel/ChannelManger
* code.
*
*/
static final int STATE_OPENING = 1;
static final int STATE_OPEN = 2;
static final int STATE_CLOSED = 4;
private static final int CHANNEL_BUFFER_SIZE = Integer.getInteger(
Channel.class.getName()+".bufferSize",
1024*1024 + 16*1024).intValue();
/**
* This channel's session size.
*/
// @GuarydedBy("this")
int channelBufferSize = CHANNEL_BUFFER_SIZE;
/*
* To achieve correctness, the following rules have to be respected when
* accessing this object:
*/
// These fields can always be read
final ChannelManager cm;
final ChannelOutputStream stdinStream;
/**
* One stream.
*
* Either {@link #stream} and {@link #buffer} is set, or the {@link #sink} is set, but those
* are mutually exclusive. The former is used when we are buffering data and let the application
* read it via {@link InputStream}, and the latter is used when we are passing through the data
*
* The synchronization is done by {@link Channel}
*/
class Output {
ChannelInputStream stream;
FifoBuffer buffer = new FifoBuffer(Channel.this, 2048, channelBufferSize);
OutputStream sink;
public void write(byte[] buf, int start, int len) throws IOException {
if (buffer!=null) {
try {
buffer.write(buf,start,len);
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
} else {
sink.write(buf,start,len);
freeupWindow(len);
}
}
/**
* How many bytes can be read from the buffer?
*/
public int readable() {
if (buffer!=null) return buffer.readable();
else return 0;
}
/**
* See {@link InputStream#available()}
*/
public int available() {
if (buffer==null)
throw new IllegalStateException("Output is being piped to "+sink);
int sz = buffer.readable();
if (sz>0) return sz;
return isEOF() ? -1 : 0;
}
/**
* Read from the buffer.
*/
public int read(byte[] buf, int start, int len) throws InterruptedException {
return buffer.read(buf,start,len);
}
/**
* Called when there will be no more data arriving to this output any more.
* Not that buffer might still have some more data that needs to be drained.
*/
public void eof() {
if (buffer!=null)
buffer.close();
else
closeQuietly(sink);
}
/**
* Instead of spooling data, let our I/O thread write to the given {@link OutputStream}.
*/
public void pipeTo(OutputStream os) throws IOException {
sink = os;
if (buffer.readable()!=0) {
freeupWindow(buffer.writeTo(os));
}
buffer = null;
stream = null;
}
}
final Output stdout = new Output();
final Output stderr = new Output();
// These two fields will only be written while the Channel is in state
// STATE_OPENING.
// The code makes sure that the two fields are written out when the state is
// changing to STATE_OPEN.
// Therefore, if you know that the Channel is in state STATE_OPEN, then you
// can read these two fields without synchronizing on the Channel. However, make
// sure that you get the latest values (e.g., flush caches by synchronizing on any
// object). However, to be on the safe side, you can lock the channel.
int localID = -1;
int remoteID = -1;
/*
* Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
* msg.
*
* This is a little bit complicated, but we have to do it in that way, since
* we cannot keep a lock on the Channel during the send operation (this
* would block sometimes the receiver thread, and, in extreme cases, can
* lead to a deadlock on both sides of the connection (senders are blocked
* since the receive buffers on the other side are full, and receiver
* threads wait for the senders to finish). It all depends on the
}
* implementation on the other side. But we cannot make any assumptions, we
* have to assume the worst case. Confused? Just believe me.
*/
/*
* If you send a message on a channel, then you have to aquire the
* "channelSendLock" and check the "closeMessageSent" flag (this variable
* may only be accessed while holding the "channelSendLock" !!!
*
* BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
* above.
*/
final Object channelSendLock = new Object();
boolean closeMessageSent = false;
/*
* Stop memory fragmentation by allocating this often used buffer.
* May only be used while holding the channelSendLock
*/
final byte[] msgWindowAdjust = new byte[9];
// If you access (read or write) any of the following fields, then you have
// to synchronize on the channel.
int state = STATE_OPENING;
boolean closeMessageRecv = false;
/* This is a stupid implementation. At the moment we can only wait
* for one pending request per channel.
*/
int successCounter = 0;
int failedCounter = 0;
int localWindow = 0; /* locally, we use a small window, < 2^31 */
long remoteWindow = 0; /* long for readable 2^32 - 1 window support */
int localMaxPacketSize = -1;
int remoteMaxPacketSize = -1;
private boolean eof = false;
synchronized void eof() {
stdout.eof();
stderr.eof();
eof = true;
}
boolean isEOF() {
return eof;
}
Integer exit_status;
String exit_signal;
// we keep the x11 cookie so that this channel can be closed when this
// specific x11 forwarding gets stopped
String hexX11FakeCookie;
// reasonClosed is special, since we sometimes need to access it
// while holding the channelSendLock.
// We protect it with a private short term lock.
private final Object reasonClosedLock = new Object();
private String reasonClosed = null;
public Channel(ChannelManager cm)
{
this.cm = cm;
this.localWindow = channelBufferSize;
this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE - 1024; // leave enough slack
this.stdinStream = new ChannelOutputStream(this);
this.stdout.stream = new ChannelInputStream(this, false);
this.stderr.stream = new ChannelInputStream(this, true);
}
/* Methods to allow access from classes outside of this package */
public synchronized void setWindowSize(int newSize) {
if (newSize<=0) throw new IllegalArgumentException("Invalid value: "+newSize);
this.channelBufferSize = newSize;
// next time when the other side sends us something, we'll issue SSH_MSG_CHANNEL_WINDOW_ADJUST
}
public ChannelInputStream getStderrStream()
{
return stderr.stream;
}
public ChannelOutputStream getStdinStream()
{
return stdinStream;
}
public ChannelInputStream getStdoutStream()
{
return stdout.stream;
}
public synchronized void pipeStdoutStream(OutputStream os) throws IOException {
stdout.pipeTo(os);
public synchronized void pipeStderrStream(OutputStream os) throws IOException {
stderr.pipeTo(os);
}
public String getExitSignal()
{
synchronized (this)
{
return exit_signal;
}
}
public Integer getExitStatus()
{
synchronized (this)
{
return exit_status;
}
}
public String getReasonClosed()
{
synchronized (reasonClosedLock)
{
return reasonClosed;
}
}
public void setReasonClosed(String reasonClosed)
{
synchronized (reasonClosedLock)
{
if (this.reasonClosed == null)
this.reasonClosed = reasonClosed;
}
}
/**
* Update the flow control couner and if necessary, sends ACK to the other end to
* let it send more data.
*/
void freeupWindow(int copylen) throws IOException {
if (copylen <= 0) return;
int increment = 0;
int remoteID;
int localID;
synchronized (this) {
if (localWindow <= ((channelBufferSize * 3) / 4)) {
// have enough local window been consumed? if so, we'll send Ack
// the window control is on the combined bytes of stdout & stderr
int space = channelBufferSize - stdout.readable() - stderr.readable();
increment = space - localWindow;
if (increment > 0) // increment<0 can't happen, but be defensive
localWindow += increment;
}
remoteID = this.remoteID; /* read while holding the lock */
localID = this.localID; /* read while holding the lock */
}
/*
* If a consumer reads stdout and stdin in parallel, we may end up with
* sending two msgWindowAdjust messages. Luckily, it
* does not matter in which order they arrive at the server.
*/
if (increment > 0)
{
if (log.isEnabled())
log.log(80, "Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
synchronized (channelSendLock)
{
byte[] msg = msgWindowAdjust;
msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
msg[1] = (byte) (remoteID >> 24);
msg[2] = (byte) (remoteID >> 16);
msg[3] = (byte) (remoteID >> 8);
msg[4] = (byte) (remoteID);
msg[5] = (byte) (increment >> 24);
msg[6] = (byte) (increment >> 16);
msg[7] = (byte) (increment >> 8);
msg[8] = (byte) (increment);
if (closeMessageSent == false)
cm.tm.sendMessage(msg);
}
}
}
private static final Logger log = Logger.getLogger(Channel.class);
} |
| File |
|---|
| Channel.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
{
<<<<<<< HEAD:src/com/trilead/ssh2/channel/ChannelManager.java
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.HashMap;
import java.util.Vector;
import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketChannelOpenConfirmation;
import com.trilead.ssh2.packets.PacketChannelOpenFailure;
import com.trilead.ssh2.packets.PacketChannelTrileadPing;
import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalForwardRequest;
import com.trilead.ssh2.packets.PacketGlobalTrileadPing;
import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel;
import com.trilead.ssh2.packets.PacketOpenSessionChannel;
}
import com.trilead.ssh2.packets.PacketSessionExecCommand;
import com.trilead.ssh2.packets.PacketSessionPtyRequest;
import com.trilead.ssh2.packets.PacketSessionStartShell;
import com.trilead.ssh2.packets.PacketSessionSubsystemRequest;
import com.trilead.ssh2.packets.PacketSessionX11Request;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesReader;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;
/**
* ChannelManager. Please read the comments in Channel.java.
* |
| Solution content |
|---|
package com.trilead.ssh2.channel; import java.io.IOException; import java.io.InterruptedIOException; import java.util.HashMap; import java.util.Vector; import com.trilead.ssh2.ChannelCondition; import com.trilead.ssh2.log.Logger; import com.trilead.ssh2.packets.PacketChannelOpenConfirmation; import com.trilead.ssh2.packets.PacketChannelOpenFailure; import com.trilead.ssh2.packets.PacketChannelTrileadPing; import com.trilead.ssh2.packets.PacketGlobalCancelForwardRequest; import com.trilead.ssh2.packets.PacketGlobalForwardRequest; import com.trilead.ssh2.packets.PacketGlobalTrileadPing; import com.trilead.ssh2.packets.PacketOpenDirectTCPIPChannel; import com.trilead.ssh2.packets.PacketOpenSessionChannel; import com.trilead.ssh2.packets.PacketSessionExecCommand; import com.trilead.ssh2.packets.PacketSessionPtyRequest; import com.trilead.ssh2.packets.PacketSessionStartShell; import com.trilead.ssh2.packets.PacketSessionSubsystemRequest; import com.trilead.ssh2.packets.PacketSessionX11Request; import com.trilead.ssh2.packets.Packets; import com.trilead.ssh2.packets.TypesReader; import com.trilead.ssh2.transport.MessageHandler; import com.trilead.ssh2.transport.TransportManager; /** * ChannelManager. Please read the comments in Channel.java. * |
| File |
|---|
| ChannelManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
<<<<<<< HEAD:src/com/trilead/ssh2/channel/LocalAcceptThread.java
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* LocalAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class LocalAcceptThread extends Thread implements IChannelWorkerThread
{
ChannelManager cm;
String host_to_connect;
int port_to_connect;
final ServerSocket ss;
public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
throws IOException
{
this.cm = cm;
this.host_to_connect = host_to_connect;
this.port_to_connect = port_to_connect;
ss = new ServerSocket(local_port);
}
public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
int port_to_connect) throws IOException
{
this.cm = cm;
this.host_to_connect = host_to_connect;
this.port_to_connect = port_to_connect;
ss = new ServerSocket();
ss.bind(localAddress);
}
public void run()
{
try
{
cm.registerThread(this);
}
catch (IOException e)
{
stopWorking();
return;
}
while (true)
{
Socket s = null;
try
{
s = ss.accept();
}
catch (IOException e)
{
stopWorking();
return;
}
Channel cn = null;
StreamForwarder r2l = null;
StreamForwarder l2r = null;
try
{
/* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
.getPort());
}
catch (IOException e)
{
/* Simply close the local socket and wait for the next incoming connection */
try
{
s.close();
}
catch (IOException ignore)
{
}
continue;
}
try
{
r2l = new StreamForwarder(cn, null, null, cn.getStdoutStream(), s.getOutputStream(), "RemoteToLocal");
l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
}
catch (IOException e)
{
try
{
/* This message is only visible during debugging, since we discard the channel immediatelly */
cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
true);
}
catch (IOException ignore)
{
}
continue;
}
r2l.setDaemon(true);
l2r.setDaemon(true);
r2l.start();
l2r.start();
}
}
public void stopWorking()
{
try
{
/* This will lead to an IOException in the ss.accept() call */
ss.close();
}
catch (IOException e)
{
}
}
}
=======
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* LocalAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class LocalAcceptThread extends Thread implements IChannelWorkerThread
{
ChannelManager cm;
String host_to_connect;
int port_to_connect;
final ServerSocket ss;
public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
throws IOException
{
this.cm = cm;
this.host_to_connect = host_to_connect;
this.port_to_connect = port_to_connect;
ss = new ServerSocket(local_port);
}
public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
int port_to_connect) throws IOException
{
this.cm = cm;
this.host_to_connect = host_to_connect;
this.port_to_connect = port_to_connect;
ss = new ServerSocket();
ss.bind(localAddress);
}
public void run()
{
try
{
cm.registerThread(this);
}
catch (IOException e)
{
stopWorking();
return;
}
while (true)
{
Socket s = null;
try
{
s = ss.accept();
}
catch (IOException e)
{
stopWorking();
return;
}
Channel cn = null;
StreamForwarder r2l = null;
StreamForwarder l2r = null;
try
{
/* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
.getPort());
}
catch (IOException e)
{
/* Simply close the local socket and wait for the next incoming connection */
try
{
s.close();
}
catch (IOException ignore)
{
}
continue;
}
try
{
r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
}
catch (IOException e)
{
try
{
/* This message is only visible during debugging, since we discard the channel immediatelly */
cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
true);
}
catch (IOException ignore)
{
}
continue;
}
r2l.setDaemon(true);
l2r.setDaemon(true);
r2l.start();
l2r.start();
}
}
public void stopWorking()
{
try
{
/* This will lead to an IOException in the ss.accept() call */
ss.close();
}
catch (IOException e)
{
}
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/LocalAcceptThread.java |
| Solution content |
|---|
try
{
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* LocalAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class LocalAcceptThread extends Thread implements IChannelWorkerThread
{
ChannelManager cm;
String host_to_connect;
int port_to_connect;
final ServerSocket ss;
public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
throws IOException
{
this.cm = cm;
this.host_to_connect = host_to_connect;
this.port_to_connect = port_to_connect;
ss = new ServerSocket(local_port);
}
public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
int port_to_connect) throws IOException
{
this.cm = cm;
this.host_to_connect = host_to_connect;
this.port_to_connect = port_to_connect;
ss = new ServerSocket();
ss.bind(localAddress);
}
public void run()
{
cm.registerThread(this);
}
catch (IOException e)
{
stopWorking();
return;
}
while (true)
{
Socket s = null;
try
{
s = ss.accept();
}
catch (IOException e)
{
stopWorking();
return;
}
Channel cn = null;
StreamForwarder r2l = null;
StreamForwarder l2r = null;
try
{
/* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
.getPort());
}
catch (IOException e)
{
/* Simply close the local socket and wait for the next incoming connection */
try
{
s.close();
}
catch (IOException ignore)
{
}
continue;
}
try
{
r2l = new StreamForwarder(cn, null, null, cn.getStdoutStream(), s.getOutputStream(), "RemoteToLocal");
l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
}
catch (IOException e)
{
try
{
/* This message is only visible during debugging, since we discard the channel immediatelly */
cn.cm.closeChannel(cn, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
true);
}
catch (IOException ignore)
{
}
continue;
}
r2l.setDaemon(true);
l2r.setDaemon(true);
r2l.start();
l2r.start();
}
}
public void stopWorking()
{
try
{
/* This will lead to an IOException in the ss.accept() call */
ss.close();
}
catch (IOException e)
{
}
}
} |
| File |
|---|
| LocalAcceptThread.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
<<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteAcceptThread.java
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.Socket;
import com.trilead.ssh2.log.Logger;
/**
* RemoteAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class RemoteAcceptThread extends Thread
{
private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
Channel c;
String remoteConnectedAddress;
int remoteConnectedPort;
String remoteOriginatorAddress;
int remoteOriginatorPort;
String targetAddress;
int targetPort;
Socket s;
public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
{
this.c = c;
this.remoteConnectedAddress = remoteConnectedAddress;
this.remoteConnectedPort = remoteConnectedPort;
this.remoteOriginatorAddress = remoteOriginatorAddress;
this.remoteOriginatorPort = remoteOriginatorPort;
this.targetAddress = targetAddress;
this.targetPort = targetPort;
if (log.isEnabled())
log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
+ remoteOriginatorAddress + "/" + remoteOriginatorPort);
}
public void run()
{
try
{
c.cm.sendOpenConfirmation(c);
s = new Socket(targetAddress, targetPort);
StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
"RemoteToLocal");
StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
"LocalToRemote");
/* No need to start two threads, one can be executed in the current thread */
r2l.setDaemon(true);
r2l.start();
l2r.run();
while (r2l.isAlive())
{
try
{
r2l.join();
}
catch (InterruptedException e)
{
throw new InterruptedIOException();
}
}
/* If the channel is already closed, then this is a no-op */
c.cm.closeChannel(c, "EOF on both streams reached.", true);
s.close();
}
catch (IOException e)
{
log.log(50, "IOException in proxy code: " + e.getMessage());
try
{
c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
}
catch (IOException e1)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}
=======
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.net.Socket;
import com.trilead.ssh2.log.Logger;
/**
* RemoteAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class RemoteAcceptThread extends Thread
{
private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
Channel c;
String remoteConnectedAddress;
int remoteConnectedPort;
String remoteOriginatorAddress;
int remoteOriginatorPort;
String targetAddress;
int targetPort;
Socket s;
public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
{
this.c = c;
this.remoteConnectedAddress = remoteConnectedAddress;
this.remoteConnectedPort = remoteConnectedPort;
this.remoteOriginatorAddress = remoteOriginatorAddress;
this.remoteOriginatorPort = remoteOriginatorPort;
this.targetAddress = targetAddress;
this.targetPort = targetPort;
if (log.isEnabled())
log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
+ remoteOriginatorAddress + "/" + remoteOriginatorPort);
}
public void run()
{
try
{
c.cm.sendOpenConfirmation(c);
s = new Socket(targetAddress, targetPort);
StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
"RemoteToLocal");
StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
"LocalToRemote");
/* No need to start two threads, one can be executed in the current thread */
r2l.setDaemon(true);
r2l.start();
l2r.run();
while (r2l.isAlive())
{
try
{
r2l.join();
}
catch (InterruptedException e)
{
}
}
/* If the channel is already closed, then this is a no-op */
c.cm.closeChannel(c, "EOF on both streams reached.", true);
s.close();
}
catch (IOException e)
{
log.log(50, "IOException in proxy code: " + e.getMessage());
try
{
c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
}
catch (IOException e1)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteAcceptThread.java |
| Solution content |
|---|
catch (IOException e)
{
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.Socket;
import com.trilead.ssh2.log.Logger;
/**
* RemoteAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class RemoteAcceptThread extends Thread
{
private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
Channel c;
String remoteConnectedAddress;
int remoteConnectedPort;
String remoteOriginatorAddress;
int remoteOriginatorPort;
String targetAddress;
int targetPort;
Socket s;
public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
{
this.c = c;
this.remoteConnectedAddress = remoteConnectedAddress;
this.remoteConnectedPort = remoteConnectedPort;
this.remoteOriginatorAddress = remoteOriginatorAddress;
this.remoteOriginatorPort = remoteOriginatorPort;
this.targetAddress = targetAddress;
this.targetPort = targetPort;
if (log.isEnabled())
log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
+ remoteOriginatorAddress + "/" + remoteOriginatorPort);
}
public void run()
{
try
{
c.cm.sendOpenConfirmation(c);
s = new Socket(targetAddress, targetPort);
StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
"RemoteToLocal");
StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
"LocalToRemote");
/* No need to start two threads, one can be executed in the current thread */
r2l.setDaemon(true);
r2l.start();
l2r.run();
while (r2l.isAlive())
{
try
{
r2l.join();
}
catch (InterruptedException e)
{
throw new InterruptedIOException();
}
}
/* If the channel is already closed, then this is a no-op */
c.cm.closeChannel(c, "EOF on both streams reached.", true);
s.close();
}
log.log(50, "IOException in proxy code: " + e.getMessage());
try
{
c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
}
catch (IOException e1)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
} |
| File |
|---|
| RemoteAcceptThread.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
<<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java ======= >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java package com.trilead.ssh2.channel; import java.io.IOException; |
| Solution content |
|---|
package com.trilead.ssh2.channel; import java.io.IOException; |
| File |
|---|
| RemoteX11AcceptThread.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Blank |
| Chunk |
|---|
| Conflicting content |
|---|
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; <<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java import java.io.InterruptedIOException; ======= >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java import java.net.Socket; import com.trilead.ssh2.log.Logger; |
| Solution content |
|---|
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.InterruptedIOException; import java.net.Socket; import com.trilead.ssh2.log.Logger; |
| File |
|---|
| RemoteX11AcceptThread.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Import |
| Chunk |
|---|
| Conflicting content |
|---|
}
catch (InterruptedException e)
{
<<<<<<< HEAD:src/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
throw new InterruptedIOException();
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/RemoteX11AcceptThread.java
}
}
|
| Solution content |
|---|
}
catch (InterruptedException e)
{
throw new InterruptedIOException();
}
}
|
| File |
|---|
| RemoteX11AcceptThread.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Throw statement |
| Chunk |
|---|
| Conflicting content |
|---|
}
if (sibling != null)
<<<<<<< HEAD:src/com/trilead/ssh2/channel/StreamForwarder.java
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* A StreamForwarder forwards data between two given streams.
* If two StreamForwarder threads are used (one for each direction)
* then one can be configured to shutdown the underlying channel/socket
* if both threads have finished forwarding (EOF).
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class StreamForwarder extends Thread
{
OutputStream os;
InputStream is;
byte[] buffer;
Channel c;
StreamForwarder sibling;
Socket s;
String mode;
StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
throws IOException
{
this.is = is;
this.os = os;
this.mode = mode;
this.c = c;
this.sibling = sibling;
this.s = s;
// window size is for the other side of the network with some latency.
// we don't need such a big buffer for a copy stream tight loop
this.buffer = new byte[8192/*c.channelBufferSize*/];
}
public void run()
{
try
{
while (true)
{
int len = is.read(buffer);
if (len <= 0)
break;
os.write(buffer, 0, len);
os.flush();
}
}
catch (IOException ignore)
{
try
{
c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
+ ignore.getMessage(), true);
}
catch (IOException e)
{
}
}
finally
{
try
{
os.close();
}
catch (IOException e1)
{
}
try
{
is.close();
}
catch (IOException e2)
{
{
while (sibling.isAlive())
{
try
{
sibling.join();
}
catch (InterruptedException e)
{
}
}
try
{
c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
}
catch (IOException e3)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}
=======
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* A StreamForwarder forwards data between two given streams.
* If two StreamForwarder threads are used (one for each direction)
* then one can be configured to shutdown the underlying channel/socket
* if both threads have finished forwarding (EOF).
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class StreamForwarder extends Thread
{
OutputStream os;
InputStream is;
byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
Channel c;
StreamForwarder sibling;
Socket s;
String mode;
StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
throws IOException
{
this.is = is;
this.os = os;
this.mode = mode;
this.c = c;
this.sibling = sibling;
this.s = s;
}
public void run()
{
try
{
while (true)
{
int len = is.read(buffer);
if (len <= 0)
break;
os.write(buffer, 0, len);
os.flush();
}
}
catch (IOException ignore)
{
try
{
c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
+ ignore.getMessage(), true);
}
catch (IOException e)
{
}
}
finally
{
try
{
os.close();
}
catch (IOException e1)
{
}
try
{
is.close();
}
catch (IOException e2)
{
}
if (sibling != null)
{
while (sibling.isAlive())
{
try
{
sibling.join();
}
catch (InterruptedException e)
{
}
}
try
{
c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
}
catch (IOException e3)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/channel/StreamForwarder.java
} |
| Solution content |
|---|
StreamForwarder sibling;
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* A StreamForwarder forwards data between two given streams.
* If two StreamForwarder threads are used (one for each direction)
* then one can be configured to shutdown the underlying channel/socket
* if both threads have finished forwarding (EOF).
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class StreamForwarder extends Thread
{
OutputStream os;
InputStream is;
byte[] buffer;
Channel c;
Socket s;
String mode;
StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
throws IOException
{
this.is = is;
this.os = os;
this.mode = mode;
this.c = c;
this.sibling = sibling;
this.s = s;
// window size is for the other side of the network with some latency.
// we don't need such a big buffer for a copy stream tight loop
this.buffer = new byte[8192/*c.channelBufferSize*/];
}
public void run()
{
try
{
while (true)
{
int len = is.read(buffer);
if (len <= 0)
break;
os.write(buffer, 0, len);
os.flush();
}
}
catch (IOException ignore)
{
try
{
c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
+ ignore.getMessage(), true);
}
catch (IOException e)
{
}
}
finally
{
try
{
os.close();
}
catch (IOException e1)
{
}
try
{
is.close();
}
catch (IOException e2)
{
}
if (sibling != null)
{
while (sibling.isAlive())
{
try
{
sibling.join();
}
catch (InterruptedException e)
{
}
}
try
{
c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
}
catch (IOException e3)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}
} |
| File |
|---|
| StreamForwarder.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Attribute |
| Comment |
| Import |
| Method declaration |
| Method invocation |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
import java.math.BigInteger; import java.security.SecureRandom; <<<<<<< HEAD:src/com/trilead/ssh2/signature/DSASHA1Verify.java ======= import com.trilead.ssh2.IOWarningException; >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java import com.trilead.ssh2.crypto.digest.SHA1; import com.trilead.ssh2.log.Logger; import com.trilead.ssh2.packets.TypesReader; |
| Solution content |
|---|
import java.math.BigInteger; import java.security.SecureRandom; import com.trilead.ssh2.IOWarningException; import com.trilead.ssh2.crypto.digest.SHA1; import com.trilead.ssh2.log.Logger; import com.trilead.ssh2.packets.TypesReader; |
| File |
|---|
| DSASHA1Verify.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Import |
| Chunk |
|---|
| Conflicting content |
|---|
{
private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
<<<<<<< HEAD:src/com/trilead/ssh2/signature/DSASHA1Verify.java
public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException
{
TypesReader tr = new TypesReader(key);
String key_format = tr.readString();
if (key_format.equals("ssh-dss") == false)
throw new IllegalArgumentException("This is not a ssh-dss public key!");
BigInteger p = tr.readMPINT();
BigInteger q = tr.readMPINT();
BigInteger g = tr.readMPINT();
BigInteger y = tr.readMPINT();
if (tr.remain() != 0)
throw new IOException("Padding in DSA public key!");
=======
public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException {
final TypesReader tr = new TypesReader(key);
final String key_format = tr.readString();
if (!key_format.equals("ssh-dss")) {
throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-dss");
}
final BigInteger p = tr.readMPINT();
final BigInteger q = tr.readMPINT();
final BigInteger g = tr.readMPINT();
final BigInteger y = tr.readMPINT();
if (tr.remain() != 0) {
throw new IOException("Padding in DSA public key!");
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/signature/DSASHA1Verify.java
return new DSAPublicKey(p, q, g, y);
} |
| Solution content |
|---|
{
private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException {
final TypesReader tr = new TypesReader(key);
final String key_format = tr.readString();
if (!key_format.equals("ssh-dss")) {
throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting ssh-dss");
}
final BigInteger p = tr.readMPINT();
final BigInteger q = tr.readMPINT();
final BigInteger g = tr.readMPINT();
final BigInteger y = tr.readMPINT();
if (tr.remain() != 0) {
throw new IOException("Padding in DSA public key!");
}
return new DSAPublicKey(p, q, g, y);
} |
| File |
|---|
| DSASHA1Verify.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| If statement |
| Method invocation |
| Method signature |
| Variable |
| Chunk |
|---|
| Conflicting content |
|---|
{
<<<<<<< HEAD:src/com/trilead/ssh2/signature/RSAPrivateKey.java
package com.trilead.ssh2.signature;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.GeneralSecurityException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
/**
* RSAPrivateKey.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class RSAPrivateKey
{
private BigInteger d;
private BigInteger e;
private BigInteger n;
public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
{
this.d = d;
this.e = e;
this.n = n;
}
public BigInteger getD()
{
return d;
}
public BigInteger getE()
return e;
}
public BigInteger getN()
{
return n;
}
public RSAPublicKey getPublicKey()
{
return new RSAPublicKey(e, n);
}
/**
* Converts this to a JCE API representation of the RSA key pair.
*/
public KeyPair toJCEKeyPair() throws GeneralSecurityException {
KeyFactory kf = KeyFactory.getInstance("RSA");
return new KeyPair(
kf.generatePublic(new RSAPublicKeySpec(getN(), getE())),
kf.generatePrivate(new RSAPrivateKeySpec(getN(), getD())));
}
=======
package com.trilead.ssh2.signature;
import java.math.BigInteger;
/**
* RSAPrivateKey.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class RSAPrivateKey
{
private BigInteger d;
private BigInteger e;
private BigInteger n;
public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
{
this.d = d;
this.e = e;
this.n = n;
}
public BigInteger getD()
{
return d;
}
public BigInteger getE()
{
return e;
}
public BigInteger getN()
{
return n;
}
public RSAPublicKey getPublicKey()
{
return new RSAPublicKey(e, n);
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/signature/RSAPrivateKey.java
} |
| Solution content |
|---|
package com.trilead.ssh2.signature;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.GeneralSecurityException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
/**
* RSAPrivateKey.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class RSAPrivateKey
{
private BigInteger d;
private BigInteger e;
private BigInteger n;
public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
{
this.d = d;
this.e = e;
this.n = n;
}
public BigInteger getD()
{
return d;
}
public BigInteger getE()
{
return e;
}
public BigInteger getN()
{
return n;
}
public RSAPublicKey getPublicKey()
{
return new RSAPublicKey(e, n);
}
/**
* Converts this to a JCE API representation of the RSA key pair.
*/
public KeyPair toJCEKeyPair() throws GeneralSecurityException {
KeyFactory kf = KeyFactory.getInstance("RSA");
return new KeyPair(
kf.generatePublic(new RSAPublicKeySpec(getN(), getE())),
kf.generatePrivate(new RSAPrivateKeySpec(getN(), getD())));
}
} |
| File |
|---|
| RSAPrivateKey.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Attribute |
| Comment |
| Import |
| Method declaration |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
len++;
if (pos >= buffer.length)
<<<<<<< HEAD:src/com/trilead/ssh2/transport/ClientServerHello.java
throw new IOException("The server sent a too long line: "+new String(buffer, "ISO-8859-1"));
=======
throw new IOException("The server sent a too long line.");
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/ClientServerHello.java
}
return len; |
| Solution content |
|---|
len++;
if (pos >= buffer.length)
throw new IOException("The server sent a too long line: "+new String(buffer, "ISO-8859-1"));
}
return len; |
| File |
|---|
| ClientServerHello.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Throw statement |
| Chunk |
|---|
| Conflicting content |
|---|
<<<<<<< HEAD:src/com/trilead/ssh2/transport/KexManager.java
package com.trilead.ssh2.transport;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;
import com.trilead.ssh2.ConnectionInfo;
import com.trilead.ssh2.DHGexParameters;
import com.trilead.ssh2.ServerHostKeyVerifier;
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.crypto.KeyMaterial;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketKexDHInit;
import com.trilead.ssh2.packets.PacketKexDHReply;
import com.trilead.ssh2.packets.PacketKexDhGexGroup;
import com.trilead.ssh2.packets.PacketKexDhGexInit;
import com.trilead.ssh2.packets.PacketKexDhGexReply;
import com.trilead.ssh2.packets.PacketKexDhGexRequest;
import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
import com.trilead.ssh2.packets.PacketKexInit;
import com.trilead.ssh2.packets.PacketNewKeys;
import com.trilead.ssh2.packets.Packets;
}
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;
/**
* KexManager.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class KexManager
{
private static final Logger log = Logger.getLogger(KexManager.class);
KexState kxs;
int kexCount = 0;
KeyMaterial km;
byte[] sessionId;
ClientServerHello csh;
final Object accessLock = new Object();
ConnectionInfo lastConnInfo = null;
boolean connectionClosed = false;
boolean ignore_next_kex_packet = false;
final TransportManager tm;
CryptoWishList nextKEXcryptoWishList;
DHGexParameters nextKEXdhgexParameters;
ServerHostKeyVerifier verifier;
final String hostname;
final int port;
final SecureRandom rnd;
public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
{
this.tm = tm;
this.csh = csh;
this.nextKEXcryptoWishList = initialCwl;
this.nextKEXdhgexParameters = new DHGexParameters();
this.hostname = hostname;
this.port = port;
this.verifier = keyVerifier;
this.rnd = rnd;
}
public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
{
synchronized (accessLock)
{
while (true)
{
if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
return lastConnInfo;
if (connectionClosed)
throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
.initCause(tm.getReasonClosedCause());
try
{
accessLock.wait();
}
catch (InterruptedException e)
{
throw new InterruptedIOException();
}
}
}
}
private String getFirstMatch(String[] client, String[] server) throws NegotiateException
{
if (client == null || server == null)
throw new IllegalArgumentException();
if (client.length == 0)
return null;
for (int i = 0; i < client.length; i++)
{
for (int j = 0; j < server.length; j++)
{
if (client[i].equals(server[j]))
return client[i];
}
}
throw new NegotiateException();
}
private boolean compareFirstOfNameList(String[] a, String[] b)
{
if (a == null || b == null)
throw new IllegalArgumentException();
if ((a.length == 0) && (b.length == 0))
return true;
if ((a.length == 0) || (b.length == 0))
return false;
return (a[0].equals(b[0]));
}
private boolean isGuessOK(KexParameters cpar, KexParameters spar)
{
if (cpar == null || spar == null)
throw new IllegalArgumentException();
if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
{
return false;
}
if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
{
return false;
}
/*
* We do NOT check here if the other algorithms can be agreed on, this
* is just a check if kex_algorithms and server_host_key_algorithms were
* guessed right!
*/
return true;
}
private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
{
NegotiatedParameters np = new NegotiatedParameters();
try
{
np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
log.log(20, "kex_algo=" + np.kex_algo);
np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
server.server_host_key_algorithms);
log.log(20, "server_host_key_algo=" + np.server_host_key_algo);
np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
server.encryption_algorithms_client_to_server);
np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
server.encryption_algorithms_server_to_client);
log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);
np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
server.mac_algorithms_client_to_server);
np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
server.mac_algorithms_server_to_client);
log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);
np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
server.compression_algorithms_client_to_server);
np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
server.compression_algorithms_server_to_client);
log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);
catch (NegotiateException e)
{
return null;
}
try
{
np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
server.languages_client_to_server);
}
catch (NegotiateException e1)
{
np.lang_client_to_server = null;
}
try
{
np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
server.languages_server_to_client);
}
catch (NegotiateException e2)
{
np.lang_server_to_client = null;
}
if (isGuessOK(client, server))
np.guessOK = true;
return np;
}
public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
{
nextKEXcryptoWishList = cwl;
nextKEXdhgexParameters = dhgex;
if (kxs == null)
{
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
kxs.localKEX = kp;
tm.sendKexMessage(kp.getPayload());
}
}
private boolean establishKeyMaterial()
{
try
{
int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
}
catch (IllegalArgumentException e)
{
return false;
}
return true;
}
private void finishKex() throws IOException
{
if (sessionId == null)
sessionId = kxs.H;
establishKeyMaterial();
/* Tell the other side that we start using the new material */
PacketNewKeys ign = new PacketNewKeys();
tm.sendKexMessage(ign.getPayload());
BlockCipher cbc;
MAC mac;
try
{
cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
km.initial_iv_client_to_server);
mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
}
catch (IllegalArgumentException e1)
{
throw new IOException("Fatal error during MAC startup!");
}
tm.changeSendCipher(cbc, mac);
tm.kexFinished();
}
public static final String[] getDefaultServerHostkeyAlgorithmList()
{
return new String[] { "ssh-rsa", "ssh-dss" };
}
public static final void checkServerHostkeyAlgorithmsList(String[] algos)
{
for (int i = 0; i < algos.length; i++)
{
if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
}
}
public static final String[] getDefaultKexAlgorithmList()
{
return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
"diffie-hellman-group1-sha1" };
}
public static final void checkKexAlgorithmList(String[] algos)
{
for (int i = 0; i < algos.length; i++)
{
if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
continue;
if ("diffie-hellman-group14-sha1".equals(algos[i]))
continue;
if ("diffie-hellman-group1-sha1".equals(algos[i]))
continue;
throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
}
}
private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
{
if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
{
RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
log.log(50, "Verifying ssh-rsa signature");
return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
}
if (kxs.np.server_host_key_algo.equals("ssh-dss"))
{
DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
log.log(50, "Verifying ssh-dss signature");
return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
}
throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
}
public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
{
PacketKexInit kip;
if (msg == null)
{
synchronized (accessLock)
{
connectionClosed = true;
accessLock.notifyAll();
return;
}
}
if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
throw new IOException("Unexpected KEX message (type " + msg[0] + ")");
if (ignore_next_kex_packet)
{
ignore_next_kex_packet = false;
return;
}
if (msg[0] == Packets.SSH_MSG_KEXINIT)
{
if ((kxs != null) && (kxs.state != 0))
throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");
if (kxs == null)
{
/*
* Ah, OK, peer wants to do KEX. Let's be nice and play
* together.
*/
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
kxs.localKEX = kip;
tm.sendKexMessage(kip.getPayload());
}
kip = new PacketKexInit(msg, 0, msglen);
kxs.remoteKEX = kip;
kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
if (kxs.np == null)
throw new IOException("Cannot negotiate, proposals do not match.");
if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
{
/*
* Guess was wrong, we need to ignore the next kex packet.
*/
ignore_next_kex_packet = true;
}
if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
{
if (kxs.dhgexParameters.getMin_group_len() == 0)
{
PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
else
{
PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
kxs.state = 1;
return;
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
{
kxs.dhx = new DhExchange();
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
kxs.dhx.init(1, rnd);
else
kxs.dhx.init(14, rnd);
PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
tm.sendKexMessage(kp.getPayload());
kxs.state = 1;
return;
}
throw new IllegalStateException("Unkown KEX method!");
}
if (msg[0] == Packets.SSH_MSG_NEWKEYS)
{
if (km == null)
throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
BlockCipher cbc;
MAC mac;
try
{
cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
km.enc_key_server_to_client, km.initial_iv_server_to_client);
mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
}
catch (IllegalArgumentException e1)
{
throw new IOException("Fatal error during MAC startup!");
}
tm.changeRecvCipher(cbc, mac);
ConnectionInfo sci = new ConnectionInfo();
kexCount++;
sci.keyExchangeAlgorithm = kxs.np.kex_algo;
sci.keyExchangeCounter = kexCount;
sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
sci.serverHostKey = kxs.hostkey;
synchronized (accessLock)
{
lastConnInfo = sci;
accessLock.notifyAll();
}
kxs = null;
return;
}
if ((kxs == null) || (kxs.state == 0))
throw new IOException("Unexpected Kex submessage!");
if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
{
if (kxs.state == 1)
{
PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
kxs.dhgx.init(rnd);
PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
tm.sendKexMessage(dhgexinit.getPayload());
kxs.state = 2;
return;
}
if (kxs.state == 2)
{
PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);
kxs.hostkey = dhgexrpl.getHostKey();
if (verifier != null)
{
boolean vres = false;
try
{
vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
}
catch (Exception e)
{
throw (IOException) new IOException(
"The server hostkey was not accepted by the verifier callback.").initCause(e);
}
if (vres == false)
throw new IOException("The server hostkey was not accepted by the verifier callback");
}
kxs.dhgx.setF(dhgexrpl.getF());
try
{
kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
kxs.dhgexParameters);
}
catch (IllegalArgumentException e)
{
throw (IOException) new IOException("KEX error.").initCause(e);
}
boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);
if (res == false)
throw new IOException("Hostkey signature sent by remote is wrong!");
kxs.K = kxs.dhgx.getK();
finishKex();
kxs.state = -1;
return;
}
throw new IllegalStateException("Illegal State in KEX Exchange!");
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
{
if (kxs.state == 1)
{
PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);
kxs.hostkey = dhr.getHostKey();
if (verifier != null)
{
boolean vres = false;
try
{
vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
}
catch (Exception e)
{
throw (IOException) new IOException(
"The server hostkey was not accepted by the verifier callback.").initCause(e);
}
if (vres == false)
throw new IOException("The server hostkey was not accepted by the verifier callback");
}
kxs.dhx.setF(dhr.getF());
try
{
kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
kxs.remoteKEX.getPayload(), dhr.getHostKey());
}
catch (IllegalArgumentException e)
{
throw (IOException) new IOException("KEX error.").initCause(e);
}
boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);
if (res == false)
throw new IOException("Hostkey signature sent by remote is wrong!");
kxs.K = kxs.dhx.getK();
finishKex();
kxs.state = -1;
return;
}
}
throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
}
}
=======
package com.trilead.ssh2.transport;
import java.io.IOException;
import java.security.SecureRandom;
import com.trilead.ssh2.ConnectionInfo;
import com.trilead.ssh2.DHGexParameters;
import com.trilead.ssh2.ServerHostKeyVerifier;
{
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.crypto.KeyMaterial;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketKexDHInit;
import com.trilead.ssh2.packets.PacketKexDHReply;
import com.trilead.ssh2.packets.PacketKexDhGexGroup;
import com.trilead.ssh2.packets.PacketKexDhGexInit;
import com.trilead.ssh2.packets.PacketKexDhGexReply;
import com.trilead.ssh2.packets.PacketKexDhGexRequest;
import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
import com.trilead.ssh2.packets.PacketKexInit;
import com.trilead.ssh2.packets.PacketNewKeys;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;
/**
* KexManager.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class KexManager
{
private static final Logger log = Logger.getLogger(KexManager.class);
KexState kxs;
int kexCount = 0;
KeyMaterial km;
byte[] sessionId;
ClientServerHello csh;
final Object accessLock = new Object();
ConnectionInfo lastConnInfo = null;
boolean connectionClosed = false;
boolean ignore_next_kex_packet = false;
final TransportManager tm;
CryptoWishList nextKEXcryptoWishList;
DHGexParameters nextKEXdhgexParameters;
ServerHostKeyVerifier verifier;
final String hostname;
final int port;
final SecureRandom rnd;
public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
{
this.tm = tm;
this.csh = csh;
this.nextKEXcryptoWishList = initialCwl;
this.nextKEXdhgexParameters = new DHGexParameters();
this.hostname = hostname;
this.port = port;
this.verifier = keyVerifier;
this.rnd = rnd;
}
public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
{
synchronized (accessLock)
{
while (true)
{
if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
return lastConnInfo;
if (connectionClosed)
throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
.initCause(tm.getReasonClosedCause());
try
{
accessLock.wait();
}
catch (InterruptedException e)
{
}
}
}
}
private String getFirstMatch(String[] client, String[] server) throws NegotiateException
{
if (client == null || server == null)
throw new IllegalArgumentException();
if (client.length == 0)
return null;
for (int i = 0; i < client.length; i++)
for (int j = 0; j < server.length; j++)
{
if (client[i].equals(server[j]))
return client[i];
}
}
throw new NegotiateException();
}
private boolean compareFirstOfNameList(String[] a, String[] b)
{
if (a == null || b == null)
throw new IllegalArgumentException();
if ((a.length == 0) && (b.length == 0))
return true;
if ((a.length == 0) || (b.length == 0))
return false;
return (a[0].equals(b[0]));
}
private boolean isGuessOK(KexParameters cpar, KexParameters spar)
{
if (cpar == null || spar == null)
throw new IllegalArgumentException();
if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
{
return false;
}
if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
{
return false;
}
/*
* We do NOT check here if the other algorithms can be agreed on, this
* is just a check if kex_algorithms and server_host_key_algorithms were
* guessed right!
*/
return true;
}
private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
{
NegotiatedParameters np = new NegotiatedParameters();
try
{
np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
log.log(20, "kex_algo=" + np.kex_algo);
np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
server.server_host_key_algorithms);
log.log(20, "server_host_key_algo=" + np.server_host_key_algo);
np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
server.encryption_algorithms_client_to_server);
np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
server.encryption_algorithms_server_to_client);
log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);
np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
server.mac_algorithms_client_to_server);
np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
server.mac_algorithms_server_to_client);
log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);
np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
server.compression_algorithms_client_to_server);
np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
server.compression_algorithms_server_to_client);
log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);
}
catch (NegotiateException e)
{
return null;
}
try
{
np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
server.languages_client_to_server);
}
catch (NegotiateException e1)
{
np.lang_client_to_server = null;
}
try
{
np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
server.languages_server_to_client);
}
catch (NegotiateException e2)
{
np.lang_server_to_client = null;
}
if (isGuessOK(client, server))
np.guessOK = true;
return np;
}
public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
{
nextKEXcryptoWishList = cwl;
nextKEXdhgexParameters = dhgex;
if (kxs == null)
{
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
kxs.localKEX = kp;
tm.sendKexMessage(kp.getPayload());
}
}
private boolean establishKeyMaterial()
{
try
{
int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
}
int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
}
catch (IllegalArgumentException e)
{
return false;
}
return true;
}
private void finishKex() throws IOException
{
if (sessionId == null)
sessionId = kxs.H;
establishKeyMaterial();
/* Tell the other side that we start using the new material */
PacketNewKeys ign = new PacketNewKeys();
tm.sendKexMessage(ign.getPayload());
BlockCipher cbc;
MAC mac;
try
{
cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
km.initial_iv_client_to_server);
mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
}
catch (IllegalArgumentException e1)
{
throw new IOException("Fatal error during MAC startup!");
}
tm.changeSendCipher(cbc, mac);
tm.kexFinished();
}
public static final String[] getDefaultServerHostkeyAlgorithmList()
{
return new String[] { "ssh-rsa", "ssh-dss" };
}
public static final void checkServerHostkeyAlgorithmsList(String[] algos)
{
for (int i = 0; i < algos.length; i++)
{
if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
}
}
public static final String[] getDefaultKexAlgorithmList()
{
return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
"diffie-hellman-group1-sha1" };
}
public static final void checkKexAlgorithmList(String[] algos)
{
for (int i = 0; i < algos.length; i++)
{
if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
continue;
if ("diffie-hellman-group14-sha1".equals(algos[i]))
continue;
if ("diffie-hellman-group1-sha1".equals(algos[i]))
continue;
throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
}
}
private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
{
if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
{
RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
log.log(50, "Verifying ssh-rsa signature");
return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
}
if (kxs.np.server_host_key_algo.equals("ssh-dss"))
{
DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
log.log(50, "Verifying ssh-dss signature");
return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
}
public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
{
PacketKexInit kip;
if (msg == null)
{
synchronized (accessLock)
{
connectionClosed = true;
accessLock.notifyAll();
return;
}
}
if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
throw new IOException("Unexpected KEX message (type " + msg[0] + ")");
if (ignore_next_kex_packet)
{
ignore_next_kex_packet = false;
return;
}
if (msg[0] == Packets.SSH_MSG_KEXINIT)
{
if ((kxs != null) && (kxs.state != 0))
throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");
if (kxs == null)
{
/*
* Ah, OK, peer wants to do KEX. Let's be nice and play
* together.
*/
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
kxs.localKEX = kip;
tm.sendKexMessage(kip.getPayload());
}
kip = new PacketKexInit(msg, 0, msglen);
kxs.remoteKEX = kip;
kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
if (kxs.np == null)
throw new IOException("Cannot negotiate, proposals do not match.");
if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
{
/*
* Guess was wrong, we need to ignore the next kex packet.
*/
ignore_next_kex_packet = true;
}
if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
{
if (kxs.dhgexParameters.getMin_group_len() == 0)
{
PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
else
{
PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
kxs.state = 1;
return;
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
{
kxs.dhx = new DhExchange();
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
kxs.dhx.init(1, rnd);
else
kxs.dhx.init(14, rnd);
PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
tm.sendKexMessage(kp.getPayload());
kxs.state = 1;
return;
}
throw new IllegalStateException("Unkown KEX method!");
}
if (msg[0] == Packets.SSH_MSG_NEWKEYS)
{
if (km == null)
throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
BlockCipher cbc;
MAC mac;
try
{
cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
km.enc_key_server_to_client, km.initial_iv_server_to_client);
mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
}
catch (IllegalArgumentException e1)
{
throw new IOException("Fatal error during MAC startup!");
}
tm.changeRecvCipher(cbc, mac);
ConnectionInfo sci = new ConnectionInfo();
kexCount++;
sci.keyExchangeAlgorithm = kxs.np.kex_algo;
sci.keyExchangeCounter = kexCount;
sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
sci.serverHostKey = kxs.hostkey;
synchronized (accessLock)
{
lastConnInfo = sci;
accessLock.notifyAll();
}
kxs = null;
return;
}
if ((kxs == null) || (kxs.state == 0))
throw new IOException("Unexpected Kex submessage!");
if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
{
if (kxs.state == 1)
{
PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
kxs.dhgx.init(rnd);
PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
tm.sendKexMessage(dhgexinit.getPayload());
kxs.state = 2;
return;
}
if (kxs.state == 2)
{
PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);
kxs.hostkey = dhgexrpl.getHostKey();
if (verifier != null)
{
boolean vres = false;
try
{
vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
}
catch (Exception e)
{
throw (IOException) new IOException(
"The server hostkey was not accepted by the verifier callback.").initCause(e);
}
if (vres == false)
throw new IOException("The server hostkey was not accepted by the verifier callback");
}
kxs.dhgx.setF(dhgexrpl.getF());
try
{
kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
kxs.dhgexParameters);
}
catch (IllegalArgumentException e)
{
throw (IOException) new IOException("KEX error.").initCause(e);
}
boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);
if (res == false)
throw new IOException("Hostkey signature sent by remote is wrong!");
kxs.K = kxs.dhgx.getK();
finishKex();
kxs.state = -1;
return;
}
throw new IllegalStateException("Illegal State in KEX Exchange!");
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
{
if (kxs.state == 1)
{
PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);
kxs.hostkey = dhr.getHostKey();
if (verifier != null)
{
boolean vres = false;
try
{
vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
}
catch (Exception e)
{
throw (IOException) new IOException(
"The server hostkey was not accepted by the verifier callback.").initCause(e);
}
if (vres == false)
throw new IOException("The server hostkey was not accepted by the verifier callback");
}
kxs.dhx.setF(dhr.getF());
try
{
kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
kxs.remoteKEX.getPayload(), dhr.getHostKey());
}
catch (IllegalArgumentException e)
{
throw (IOException) new IOException("KEX error.").initCause(e);
}
boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);
if (res == false)
throw new IOException("Hostkey signature sent by remote is wrong!");
kxs.K = kxs.dhx.getK();
finishKex();
kxs.state = -1;
return;
}
}
throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/KexManager.java |
| Solution content |
|---|
}
package com.trilead.ssh2.transport;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.security.SecureRandom;
import com.trilead.ssh2.ConnectionInfo;
import com.trilead.ssh2.DHGexParameters;
import com.trilead.ssh2.ServerHostKeyVerifier;
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.crypto.KeyMaterial;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketKexDHInit;
import com.trilead.ssh2.packets.PacketKexDHReply;
import com.trilead.ssh2.packets.PacketKexDhGexGroup;
import com.trilead.ssh2.packets.PacketKexDhGexInit;
import com.trilead.ssh2.packets.PacketKexDhGexReply;
import com.trilead.ssh2.packets.PacketKexDhGexRequest;
import com.trilead.ssh2.packets.PacketKexDhGexRequestOld;
import com.trilead.ssh2.packets.PacketKexInit;
import com.trilead.ssh2.packets.PacketNewKeys;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;
/**
* KexManager.
*
* @author Christian Plattner, plattner@trilead.com
return false;
* @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class KexManager
{
private static final Logger log = Logger.getLogger(KexManager.class);
KexState kxs;
int kexCount = 0;
KeyMaterial km;
byte[] sessionId;
ClientServerHello csh;
final Object accessLock = new Object();
ConnectionInfo lastConnInfo = null;
boolean connectionClosed = false;
boolean ignore_next_kex_packet = false;
final TransportManager tm;
CryptoWishList nextKEXcryptoWishList;
DHGexParameters nextKEXdhgexParameters;
ServerHostKeyVerifier verifier;
final String hostname;
final int port;
final SecureRandom rnd;
public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
ServerHostKeyVerifier keyVerifier, SecureRandom rnd)
{
this.tm = tm;
this.csh = csh;
this.nextKEXcryptoWishList = initialCwl;
this.nextKEXdhgexParameters = new DHGexParameters();
this.hostname = hostname;
this.port = port;
this.verifier = keyVerifier;
this.rnd = rnd;
}
public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException
{
synchronized (accessLock)
{
while (true)
{
if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount))
return lastConnInfo;
if (connectionClosed)
throw (IOException) new IOException("Key exchange was not finished, connection is closed.")
.initCause(tm.getReasonClosedCause());
try
{
accessLock.wait();
}
catch (InterruptedException e)
{
throw new InterruptedIOException();
}
}
}
}
private String getFirstMatch(String[] client, String[] server) throws NegotiateException
{
if (client == null || server == null)
throw new IllegalArgumentException();
if (client.length == 0)
return null;
for (int i = 0; i < client.length; i++)
{
for (int j = 0; j < server.length; j++)
{
if (client[i].equals(server[j]))
return client[i];
}
}
throw new NegotiateException();
}
private boolean compareFirstOfNameList(String[] a, String[] b)
{
if (a == null || b == null)
throw new IllegalArgumentException();
if ((a.length == 0) && (b.length == 0))
return true;
if ((a.length == 0) || (b.length == 0))
return false;
return (a[0].equals(b[0]));
}
private boolean isGuessOK(KexParameters cpar, KexParameters spar)
{
if (cpar == null || spar == null)
throw new IllegalArgumentException();
if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false)
{
return false;
}
if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false)
{
/*
* We do NOT check here if the other algorithms can be agreed on, this
* is just a check if kex_algorithms and server_host_key_algorithms were
* guessed right!
*/
return true;
}
private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
{
NegotiatedParameters np = new NegotiatedParameters();
try
{
np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
log.log(20, "kex_algo=" + np.kex_algo);
np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
server.server_host_key_algorithms);
log.log(20, "server_host_key_algo=" + np.server_host_key_algo);
np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
server.encryption_algorithms_client_to_server);
np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
server.encryption_algorithms_server_to_client);
log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server);
log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client);
np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
server.mac_algorithms_client_to_server);
np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
server.mac_algorithms_server_to_client);
log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server);
log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client);
np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
server.compression_algorithms_client_to_server);
np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
server.compression_algorithms_server_to_client);
log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server);
log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client);
}
catch (NegotiateException e)
{
return null;
}
try
{
np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
server.languages_client_to_server);
}
catch (NegotiateException e1)
{
np.lang_client_to_server = null;
}
try
{
np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
server.languages_server_to_client);
}
catch (NegotiateException e2)
{
np.lang_server_to_client = null;
}
if (isGuessOK(client, server))
np.guessOK = true;
return np;
}
public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException
{
nextKEXcryptoWishList = cwl;
nextKEXdhgexParameters = dhgex;
if (kxs == null)
{
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
kxs.localKEX = kp;
tm.sendKexMessage(kp.getPayload());
}
}
private boolean establishKeyMaterial()
{
try
{
int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
}
int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
}
catch (IllegalArgumentException e)
{
return false;
}
return true;
}
private void finishKex() throws IOException
{
if (sessionId == null)
sessionId = kxs.H;
establishKeyMaterial();
/* Tell the other side that we start using the new material */
PacketNewKeys ign = new PacketNewKeys();
tm.sendKexMessage(ign.getPayload());
BlockCipher cbc;
MAC mac;
try
{
cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server,
km.initial_iv_client_to_server);
mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
}
catch (IllegalArgumentException e1)
{
throw new IOException("Fatal error during MAC startup!");
}
tm.changeSendCipher(cbc, mac);
tm.kexFinished();
}
public static final String[] getDefaultServerHostkeyAlgorithmList()
{
return new String[] { "ssh-rsa", "ssh-dss" };
}
public static final void checkServerHostkeyAlgorithmsList(String[] algos)
{
for (int i = 0; i < algos.length; i++)
{
if (("ssh-rsa".equals(algos[i]) == false) && ("ssh-dss".equals(algos[i]) == false))
throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'");
}
}
public static final String[] getDefaultKexAlgorithmList()
{
return new String[] { "diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
"diffie-hellman-group1-sha1" };
}
public static final void checkKexAlgorithmList(String[] algos)
{
for (int i = 0; i < algos.length; i++)
{
if ("diffie-hellman-group-exchange-sha1".equals(algos[i]))
continue;
if ("diffie-hellman-group14-sha1".equals(algos[i]))
continue;
if ("diffie-hellman-group1-sha1".equals(algos[i]))
continue;
throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'");
}
}
private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException
{
if (kxs.np.server_host_key_algo.equals("ssh-rsa"))
{
RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
log.log(50, "Verifying ssh-rsa signature");
return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
}
if (kxs.np.server_host_key_algo.equals("ssh-dss"))
{
DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
log.log(50, "Verifying ssh-dss signature");
return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
}
public synchronized void handleMessage(byte[] msg, int msglen) throws IOException
{
PacketKexInit kip;
if (msg == null)
{
synchronized (accessLock)
{
connectionClosed = true;
accessLock.notifyAll();
return;
}
}
if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT))
throw new IOException("Unexpected KEX message (type " + msg[0] + ")");
if (ignore_next_kex_packet)
{
ignore_next_kex_packet = false;
return;
}
if (msg[0] == Packets.SSH_MSG_KEXINIT)
{
if ((kxs != null) && (kxs.state != 0))
throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!");
if (kxs == null)
{
/*
* Ah, OK, peer wants to do KEX. Let's be nice and play
* together.
*/
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
kxs.localKEX = kip;
tm.sendKexMessage(kip.getPayload());
}
kip = new PacketKexInit(msg, 0, msglen);
kxs.remoteKEX = kip;
kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
if (kxs.np == null)
throw new IOException("Cannot negotiate, proposals do not match.");
if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false))
{
/*
* Guess was wrong, we need to ignore the next kex packet.
*/
ignore_next_kex_packet = true;
}
if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
{
if (kxs.dhgexParameters.getMin_group_len() == 0)
{
PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
else
{
PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
kxs.state = 1;
return;
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
{
kxs.dhx = new DhExchange();
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
kxs.dhx.init(1, rnd);
else
kxs.dhx.init(14, rnd);
PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
tm.sendKexMessage(kp.getPayload());
kxs.state = 1;
return;
}
throw new IllegalStateException("Unkown KEX method!");
}
if (msg[0] == Packets.SSH_MSG_NEWKEYS)
{
if (km == null)
throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
BlockCipher cbc;
MAC mac;
try
{
cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
km.enc_key_server_to_client, km.initial_iv_server_to_client);
mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
}
catch (IllegalArgumentException e1)
{
throw new IOException("Fatal error during MAC startup!");
}
tm.changeRecvCipher(cbc, mac);
ConnectionInfo sci = new ConnectionInfo();
kexCount++;
sci.keyExchangeAlgorithm = kxs.np.kex_algo;
sci.keyExchangeCounter = kexCount;
sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
sci.serverHostKey = kxs.hostkey;
synchronized (accessLock)
{
lastConnInfo = sci;
accessLock.notifyAll();
}
kxs = null;
return;
}
if ((kxs == null) || (kxs.state == 0))
throw new IOException("Unexpected Kex submessage!");
if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1"))
{
if (kxs.state == 1)
{
PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen);
kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
kxs.dhgx.init(rnd);
PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
tm.sendKexMessage(dhgexinit.getPayload());
kxs.state = 2;
return;
}
if (kxs.state == 2)
{
PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen);
kxs.hostkey = dhgexrpl.getHostKey();
if (verifier != null)
{
boolean vres = false;
try
{
vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
}
catch (Exception e)
{
throw (IOException) new IOException(
"The server hostkey was not accepted by the verifier callback.").initCause(e);
}
if (vres == false)
throw new IOException("The server hostkey was not accepted by the verifier callback");
}
kxs.dhgx.setF(dhgexrpl.getF());
try
{
kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
kxs.dhgexParameters);
}
catch (IllegalArgumentException e)
{
throw (IOException) new IOException("KEX error.").initCause(e);
}
boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey);
if (res == false)
throw new IOException("Hostkey signature sent by remote is wrong!");
kxs.K = kxs.dhgx.getK();
finishKex();
kxs.state = -1;
return;
}
throw new IllegalStateException("Illegal State in KEX Exchange!");
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
|| kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
{
if (kxs.state == 1)
{
PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen);
kxs.hostkey = dhr.getHostKey();
if (verifier != null)
{
boolean vres = false;
try
{
vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey);
}
catch (Exception e)
{
throw (IOException) new IOException(
"The server hostkey was not accepted by the verifier callback.").initCause(e);
}
if (vres == false)
throw new IOException("The server hostkey was not accepted by the verifier callback");
}
kxs.dhx.setF(dhr.getF());
try
{
kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
kxs.remoteKEX.getPayload(), dhr.getHostKey());
}
catch (IllegalArgumentException e)
{
throw (IOException) new IOException("KEX error.").initCause(e);
}
boolean res = verifySignature(dhr.getSignature(), kxs.hostkey);
if (res == false)
throw new IOException("Hostkey signature sent by remote is wrong!");
kxs.K = kxs.dhx.getK();
finishKex();
kxs.state = -1;
return;
}
}
throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")");
}
} |
| File |
|---|
| KexManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
* TransportConnection.
*
}
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportConnection.java
package com.trilead.ssh2.transport;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CipherInputStream;
import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
import com.trilead.ssh2.crypto.cipher.NullCipher;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;
/**
* @author Christian Plattner, plattner@trilead.com
* @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class TransportConnection
{
private static final Logger log = Logger.getLogger(TransportConnection.class);
int send_seq_number = 0;
int recv_seq_number = 0;
CipherInputStream cis;
CipherOutputStream cos;
boolean useRandomPadding = false;
/* Depends on current MAC and CIPHER */
MAC send_mac;
byte[] send_mac_buffer;
int send_padd_blocksize = 8;
MAC recv_mac;
byte[] recv_mac_buffer;
byte[] recv_mac_buffer_cmp;
int recv_padd_blocksize = 8;
/* won't change */
final byte[] send_padding_buffer = new byte[256];
final byte[] send_packet_header_buffer = new byte[5];
final byte[] recv_padding_buffer = new byte[256];
final byte[] recv_packet_header_buffer = new byte[5];
boolean recv_packet_header_present = false;
ClientServerHello csh;
final SecureRandom rnd;
public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
{
this.cis = new CipherInputStream(new NullCipher(), is);
this.cos = new CipherOutputStream(new NullCipher(), os);
this.rnd = rnd;
}
public void changeRecvCipher(BlockCipher bc, MAC mac)
{
cis.changeCipher(bc);
recv_mac = mac;
recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
recv_padd_blocksize = bc.getBlockSize();
if (recv_padd_blocksize < 8)
recv_padd_blocksize = 8;
}
public void changeSendCipher(BlockCipher bc, MAC mac)
{
if ((bc instanceof NullCipher) == false)
{
/* Only use zero byte padding for the first few packets */
useRandomPadding = true;
/* Once we start encrypting, there is no way back */
}
cos.changeCipher(bc);
send_mac = mac;
send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
send_padd_blocksize = bc.getBlockSize();
if (send_padd_blocksize < 8)
send_padd_blocksize = 8;
}
public void sendMessage(byte[] message) throws IOException
{
sendMessage(message, 0, message.length, 0);
}
public void sendMessage(byte[] message, int off, int len) throws IOException
{
sendMessage(message, off, len, 0);
}
public int getPacketOverheadEstimate()
{
// return an estimate for the paket overhead (for send operations)
return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
}
public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
{
if (padd < 4)
padd = 4;
else if (padd > 64)
padd = 64;
int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
int slack = packet_len % send_padd_blocksize;
if (slack != 0)
{
packet_len += (send_padd_blocksize - slack);
}
if (packet_len < 16)
packet_len = 16;
int padd_len = packet_len - (5 + len);
if (useRandomPadding)
{
for (int i = 0; i < padd_len; i = i + 4)
{
/*
* don't waste calls to rnd.nextInt() (by using only 8bit of the
* output). just believe me: even though we may write here up to 3
* bytes which won't be used, there is no "buffer overflow" (i.e.,
* arrayindexoutofbounds). the padding buffer is big enough =) (256
* bytes, and that is bigger than any current cipher block size + 64).
*/
int r = rnd.nextInt();
send_padding_buffer[i] = (byte) r;
send_padding_buffer[i + 1] = (byte) (r >> 8);
send_padding_buffer[i + 2] = (byte) (r >> 16);
send_padding_buffer[i + 3] = (byte) (r >> 24);
}
}
else
{
/* use zero padding for unencrypted traffic */
for (int i = 0; i < padd_len; i++)
send_padding_buffer[i] = 0;
/* Actually this code is paranoid: we never filled any
* bytes into the padding buffer so far, therefore it should
* consist of zeros only.
*/
}
send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
send_packet_header_buffer[3] = (byte) ((packet_len - 4));
send_packet_header_buffer[4] = (byte) padd_len;
cos.write(send_packet_header_buffer, 0, 5);
cos.write(message, off, len);
cos.write(send_padding_buffer, 0, padd_len);
if (send_mac != null)
{
send_mac.initMac(send_seq_number);
send_mac.update(send_packet_header_buffer, 0, 5);
send_mac.update(message, off, len);
send_mac.update(send_padding_buffer, 0, padd_len);
send_mac.getMac(send_mac_buffer, 0);
cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
}
cos.flush();
if (log.isEnabled())
{
log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
}
send_seq_number++;
}
public int peekNextMessageLength() throws IOException
{
if (recv_packet_header_present == false)
{
cis.read(recv_packet_header_buffer, 0, 5);
recv_packet_header_present = true;
}
int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
| ((recv_packet_header_buffer[3] & 0xff));
int padding_length = recv_packet_header_buffer[4] & 0xff;
if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
throw new IOException("Illegal packet size! (" + packet_length + ")");
int payload_length = packet_length - padding_length - 1;
if (payload_length < 0)
throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
return payload_length;
}
public int receiveMessage(byte buffer[], int off, int len) throws IOException
{
if (recv_packet_header_present == false)
{
cis.read(recv_packet_header_buffer, 0, 5);
}
else
recv_packet_header_present = false;
int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
| ((recv_packet_header_buffer[3] & 0xff));
int padding_length = recv_packet_header_buffer[4] & 0xff;
if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
throw new IOException("Illegal packet size! (" + packet_length + ")");
int payload_length = packet_length - padding_length - 1;
if (payload_length < 0)
throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
if (payload_length >= len)
throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
cis.read(buffer, off, payload_length);
cis.read(recv_padding_buffer, 0, padding_length);
if (recv_mac != null)
{
cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
recv_mac.initMac(recv_seq_number);
recv_mac.update(recv_packet_header_buffer, 0, 5);
recv_mac.update(buffer, off, payload_length);
recv_mac.update(recv_padding_buffer, 0, padding_length);
recv_mac.getMac(recv_mac_buffer_cmp, 0);
for (int i = 0; i < recv_mac_buffer.length; i++)
{
if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
throw new IOException("Remote sent corrupt MAC.");
}
}
recv_seq_number++;
if (log.isEnabled())
{
log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
+ " bytes payload");
}
return payload_length;
}
}
=======
package com.trilead.ssh2.transport;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CipherInputStream;
import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
import com.trilead.ssh2.crypto.cipher.NullCipher;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;
/**
* TransportConnection.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class TransportConnection
{
private static final Logger log = Logger.getLogger(TransportConnection.class);
int send_seq_number = 0;
int recv_seq_number = 0;
CipherInputStream cis;
CipherOutputStream cos;
boolean useRandomPadding = false;
/* Depends on current MAC and CIPHER */
MAC send_mac;
byte[] send_mac_buffer;
int send_padd_blocksize = 8;
MAC recv_mac;
byte[] recv_mac_buffer;
byte[] recv_mac_buffer_cmp;
int recv_padd_blocksize = 8;
/* won't change */
final byte[] send_padding_buffer = new byte[256];
final byte[] send_packet_header_buffer = new byte[5];
final byte[] recv_padding_buffer = new byte[256];
final byte[] recv_packet_header_buffer = new byte[5];
boolean recv_packet_header_present = false;
ClientServerHello csh;
final SecureRandom rnd;
public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
{
this.cis = new CipherInputStream(new NullCipher(), is);
this.cos = new CipherOutputStream(new NullCipher(), os);
this.rnd = rnd;
}
public void changeRecvCipher(BlockCipher bc, MAC mac)
{
cis.changeCipher(bc);
recv_mac = mac;
recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
recv_padd_blocksize = bc.getBlockSize();
if (recv_padd_blocksize < 8)
recv_padd_blocksize = 8;
}
public void changeSendCipher(BlockCipher bc, MAC mac)
{
if ((bc instanceof NullCipher) == false)
{
/* Only use zero byte padding for the first few packets */
useRandomPadding = true;
/* Once we start encrypting, there is no way back */
}
cos.changeCipher(bc);
send_mac = mac;
send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
send_padd_blocksize = bc.getBlockSize();
if (send_padd_blocksize < 8)
send_padd_blocksize = 8;
}
public void sendMessage(byte[] message) throws IOException
{
sendMessage(message, 0, message.length, 0);
}
public void sendMessage(byte[] message, int off, int len) throws IOException
{
sendMessage(message, off, len, 0);
}
public int getPacketOverheadEstimate()
{
// return an estimate for the paket overhead (for send operations)
return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
}
public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
{
if (padd < 4)
padd = 4;
else if (padd > 64)
padd = 64;
int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
int slack = packet_len % send_padd_blocksize;
if (slack != 0)
{
packet_len += (send_padd_blocksize - slack);
}
if (packet_len < 16)
packet_len = 16;
int padd_len = packet_len - (5 + len);
if (useRandomPadding)
{
for (int i = 0; i < padd_len; i = i + 4)
{
/*
* don't waste calls to rnd.nextInt() (by using only 8bit of the
* output). just believe me: even though we may write here up to 3
* bytes which won't be used, there is no "buffer overflow" (i.e.,
* arrayindexoutofbounds). the padding buffer is big enough =) (256
* bytes, and that is bigger than any current cipher block size + 64).
*/
int r = rnd.nextInt();
send_padding_buffer[i] = (byte) r;
send_padding_buffer[i + 1] = (byte) (r >> 8);
send_padding_buffer[i + 2] = (byte) (r >> 16);
send_padding_buffer[i + 3] = (byte) (r >> 24);
}
}
else
{
/* use zero padding for unencrypted traffic */
for (int i = 0; i < padd_len; i++)
send_padding_buffer[i] = 0;
/* Actually this code is paranoid: we never filled any
* bytes into the padding buffer so far, therefore it should
* consist of zeros only.
*/
}
send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
send_packet_header_buffer[3] = (byte) ((packet_len - 4));
send_packet_header_buffer[4] = (byte) padd_len;
cos.write(send_packet_header_buffer, 0, 5);
cos.write(message, off, len);
cos.write(send_padding_buffer, 0, padd_len);
if (send_mac != null)
{
send_mac.initMac(send_seq_number);
send_mac.update(send_packet_header_buffer, 0, 5);
send_mac.update(message, off, len);
send_mac.update(send_padding_buffer, 0, padd_len);
send_mac.getMac(send_mac_buffer, 0);
cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
}
cos.flush();
if (log.isEnabled())
{
log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
}
send_seq_number++;
public int peekNextMessageLength() throws IOException
{
if (recv_packet_header_present == false)
{
cis.read(recv_packet_header_buffer, 0, 5);
recv_packet_header_present = true;
}
int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
| ((recv_packet_header_buffer[3] & 0xff));
int padding_length = recv_packet_header_buffer[4] & 0xff;
if (packet_length > 35000 || packet_length < 12)
throw new IOException("Illegal packet size! (" + packet_length + ")");
int payload_length = packet_length - padding_length - 1;
if (payload_length < 0)
throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
return payload_length;
}
public int receiveMessage(byte buffer[], int off, int len) throws IOException
{
if (recv_packet_header_present == false)
{
cis.read(recv_packet_header_buffer, 0, 5);
}
else
recv_packet_header_present = false;
int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
| ((recv_packet_header_buffer[3] & 0xff));
int padding_length = recv_packet_header_buffer[4] & 0xff;
if (packet_length > 35000 || packet_length < 12)
throw new IOException("Illegal packet size! (" + packet_length + ")");
int payload_length = packet_length - padding_length - 1;
if (payload_length < 0)
throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
if (payload_length >= len)
throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
cis.read(buffer, off, payload_length);
cis.read(recv_padding_buffer, 0, padding_length);
if (recv_mac != null)
{
cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
recv_mac.initMac(recv_seq_number);
recv_mac.update(recv_packet_header_buffer, 0, 5);
recv_mac.update(buffer, off, payload_length);
recv_mac.update(recv_padding_buffer, 0, padding_length);
recv_mac.getMac(recv_mac_buffer_cmp, 0);
for (int i = 0; i < recv_mac_buffer.length; i++)
{
if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
throw new IOException("Remote sent corrupt MAC.");
}
}
recv_seq_number++;
if (log.isEnabled())
{
log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
+ " bytes payload");
}
return payload_length;
}
}
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportConnection.java |
| Solution content |
|---|
import java.io.InputStream;
}
package com.trilead.ssh2.transport;
import java.io.IOException;
import java.io.OutputStream;
import java.security.SecureRandom;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CipherInputStream;
import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
import com.trilead.ssh2.crypto.cipher.NullCipher;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.Packets;
/**
* TransportConnection.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class TransportConnection
{
private static final Logger log = Logger.getLogger(TransportConnection.class);
int send_seq_number = 0;
int recv_seq_number = 0;
CipherInputStream cis;
CipherOutputStream cos;
boolean useRandomPadding = false;
/* Depends on current MAC and CIPHER */
MAC send_mac;
byte[] send_mac_buffer;
int send_padd_blocksize = 8;
MAC recv_mac;
byte[] recv_mac_buffer;
byte[] recv_mac_buffer_cmp;
int recv_padd_blocksize = 8;
/* won't change */
final byte[] send_padding_buffer = new byte[256];
final byte[] send_packet_header_buffer = new byte[5];
final byte[] recv_padding_buffer = new byte[256];
final byte[] recv_packet_header_buffer = new byte[5];
boolean recv_packet_header_present = false;
ClientServerHello csh;
final SecureRandom rnd;
public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd)
{
this.cis = new CipherInputStream(new NullCipher(), is);
this.cos = new CipherOutputStream(new NullCipher(), os);
this.rnd = rnd;
}
public void changeRecvCipher(BlockCipher bc, MAC mac)
{
cis.changeCipher(bc);
recv_mac = mac;
recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
recv_padd_blocksize = bc.getBlockSize();
if (recv_padd_blocksize < 8)
recv_padd_blocksize = 8;
}
public void changeSendCipher(BlockCipher bc, MAC mac)
{
if ((bc instanceof NullCipher) == false)
{
/* Only use zero byte padding for the first few packets */
useRandomPadding = true;
/* Once we start encrypting, there is no way back */
}
cos.changeCipher(bc);
send_mac = mac;
send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
send_padd_blocksize = bc.getBlockSize();
if (send_padd_blocksize < 8)
send_padd_blocksize = 8;
}
public void sendMessage(byte[] message) throws IOException
{
sendMessage(message, 0, message.length, 0);
}
public void sendMessage(byte[] message, int off, int len) throws IOException
{
sendMessage(message, off, len, 0);
}
public int getPacketOverheadEstimate()
{
// return an estimate for the paket overhead (for send operations)
return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
}
public void sendMessage(byte[] message, int off, int len, int padd) throws IOException
{
if (padd < 4)
padd = 4;
else if (padd > 64)
padd = 64;
int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
int slack = packet_len % send_padd_blocksize;
if (slack != 0)
{
packet_len += (send_padd_blocksize - slack);
}
if (packet_len < 16)
packet_len = 16;
int padd_len = packet_len - (5 + len);
if (useRandomPadding)
{
for (int i = 0; i < padd_len; i = i + 4)
{
/*
* don't waste calls to rnd.nextInt() (by using only 8bit of the
* output). just believe me: even though we may write here up to 3
* bytes which won't be used, there is no "buffer overflow" (i.e.,
* arrayindexoutofbounds). the padding buffer is big enough =) (256
* bytes, and that is bigger than any current cipher block size + 64).
*/
int r = rnd.nextInt();
send_padding_buffer[i] = (byte) r;
send_padding_buffer[i + 1] = (byte) (r >> 8);
send_padding_buffer[i + 2] = (byte) (r >> 16);
send_padding_buffer[i + 3] = (byte) (r >> 24);
}
}
else
{
/* use zero padding for unencrypted traffic */
for (int i = 0; i < padd_len; i++)
send_padding_buffer[i] = 0;
/* Actually this code is paranoid: we never filled any
* bytes into the padding buffer so far, therefore it should
* consist of zeros only.
*/
}
send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
send_packet_header_buffer[3] = (byte) ((packet_len - 4));
send_packet_header_buffer[4] = (byte) padd_len;
cos.write(send_packet_header_buffer, 0, 5);
cos.write(message, off, len);
cos.write(send_padding_buffer, 0, padd_len);
if (send_mac != null)
{
send_mac.initMac(send_seq_number);
send_mac.update(send_packet_header_buffer, 0, 5);
send_mac.update(message, off, len);
send_mac.update(send_padding_buffer, 0, padd_len);
send_mac.getMac(send_mac_buffer, 0);
cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
}
cos.flush();
if (log.isEnabled())
{
log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
}
send_seq_number++;
}
public int peekNextMessageLength() throws IOException
{
if (recv_packet_header_present == false)
{
cis.read(recv_packet_header_buffer, 0, 5);
recv_packet_header_present = true;
}
int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
| ((recv_packet_header_buffer[3] & 0xff));
int padding_length = recv_packet_header_buffer[4] & 0xff;
if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
throw new IOException("Illegal packet size! (" + packet_length + ")");
int payload_length = packet_length - padding_length - 1;
if (payload_length < 0)
throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
return payload_length;
}
public int receiveMessage(byte buffer[], int off, int len) throws IOException
{
if (recv_packet_header_present == false)
{
cis.read(recv_packet_header_buffer, 0, 5);
}
else
recv_packet_header_present = false;
int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
| ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
| ((recv_packet_header_buffer[3] & 0xff));
int padding_length = recv_packet_header_buffer[4] & 0xff;
if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12)
throw new IOException("Illegal packet size! (" + packet_length + ")");
int payload_length = packet_length - padding_length - 1;
if (payload_length < 0)
throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
if (payload_length >= len)
throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
cis.read(buffer, off, payload_length);
cis.read(recv_padding_buffer, 0, padding_length);
if (recv_mac != null)
{
cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
recv_mac.initMac(recv_seq_number);
recv_mac.update(recv_packet_header_buffer, 0, 5);
recv_mac.update(buffer, off, payload_length);
recv_mac.update(recv_padding_buffer, 0, padding_length);
recv_mac.getMac(recv_mac_buffer_cmp, 0);
for (int i = 0; i < recv_mac_buffer.length; i++)
{
if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
throw new IOException("Remote sent corrupt MAC.");
}
recv_seq_number++;
if (log.isEnabled())
{
log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
+ " bytes payload");
}
return payload_length;
}
} |
| File |
|---|
| TransportConnection.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Class declaration |
| Comment |
| Import |
| Package declaration |
| Chunk |
|---|
| Conflicting content |
|---|
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java ======= >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java package com.trilead.ssh2.transport; import java.io.IOException; |
| Solution content |
|---|
package com.trilead.ssh2.transport; import java.io.IOException; |
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Blank |
| Chunk |
|---|
| Conflicting content |
|---|
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; <<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java import java.io.InterruptedIOException; ======= >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; |
| Solution content |
|---|
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.InterruptedIOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; |
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Import |
| Chunk |
|---|
| Conflicting content |
|---|
*/
public class TransportManager
{
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
private static final Logger log = Logger.getLogger(TransportManager.class);
class HandlerEntry
=======
private static final Logger log = Logger.getLogger(TransportManager.class);
class HandlerEntry
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
{
MessageHandler mh;
int low; |
| Solution content |
|---|
*/
public class TransportManager
{
private static final Logger log = Logger.getLogger(TransportManager.class);
class HandlerEntry
{
MessageHandler mh;
int low; |
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Attribute |
| Class signature |
| Method invocation |
| Chunk |
|---|
| Conflicting content |
|---|
}
}
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException
=======
private void establishConnection(ProxyData proxyData, int connectTimeout, int readTimeout) throws IOException
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
{
/* See the comment for createInetAddress() */
|
| Solution content |
|---|
}
}
private void establishConnection(ProxyData proxyData, int connectTimeout, int readTimeout) throws IOException
{
/* See the comment for createInetAddress() */
|
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Method signature |
| Chunk |
|---|
| Conflicting content |
|---|
{
InetAddress addr = createInetAddress(hostname);
sock.connect(new InetSocketAddress(addr, port), connectTimeout);
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
sock.setSoTimeout(0);
=======
sock.setSoTimeout(readTimeout);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
return;
}
|
| Solution content |
|---|
{
InetAddress addr = createInetAddress(hostname);
sock.connect(new InetSocketAddress(addr, port), connectTimeout);
sock.setSoTimeout(readTimeout);
return;
}
|
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Method invocation |
| Chunk |
|---|
| Conflicting content |
|---|
InetAddress addr = createInetAddress(pd.proxyHost); sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout); <<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java sock.setSoTimeout(0); ======= sock.setSoTimeout(readTimeout); >>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java /* OK, now tell the proxy where we actually want to connect to */ |
| Solution content |
|---|
InetAddress addr = createInetAddress(pd.proxyHost); sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout); sock.setSoTimeout(readTimeout); /* OK, now tell the proxy where we actually want to connect to */ |
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Method invocation |
| Chunk |
|---|
| Conflicting content |
|---|
throw new IOException("Unsupported ProxyData");
}
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
{
/* First, establish the TCP connection to the SSH-2 server */
establishConnection(proxyData, connectTimeout);
=======
public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException {
initialize(cwl, verifier, dhgex, connectTimeout, 0, rnd, proxyData);
}
public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
int connectTimeout, int readTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
{
/* First, establish the TCP connection to the SSH-2 server */
establishConnection(proxyData, connectTimeout, readTimeout);
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
/* Parse the server line and say hello - important: this information is later needed for the
* key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object |
| Solution content |
|---|
throw new IOException("Unsupported ProxyData");
}
public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException {
initialize(cwl, verifier, dhgex, connectTimeout, 0, rnd, proxyData);
}
public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex,
int connectTimeout, int readTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException
{
/* First, establish the TCP connection to the SSH-2 server */
establishConnection(proxyData, connectTimeout, readTimeout);
/* Parse the server line and say hello - important: this information is later needed for the
* key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object |
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 2 |
| Kind of conflict |
|---|
| Comment |
| Method declaration |
| Method invocation |
| Method signature |
| Chunk |
|---|
| Conflicting content |
|---|
}
catch (InterruptedException e)
{
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
throw new InterruptedIOException();
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
}
}
|
| Solution content |
|---|
}
catch (InterruptedException e)
{
throw new InterruptedIOException();
}
}
|
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Throw statement |
| Chunk |
|---|
| Conflicting content |
|---|
public void receiveLoop() throws IOException
{
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
byte[] msg = new byte[MAX_PACKET_SIZE];
=======
byte[] msg = new byte[35000];
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
while (true)
{ |
| Solution content |
|---|
public void receiveLoop() throws IOException
{
byte[] msg = new byte[MAX_PACKET_SIZE];
while (true)
{ |
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Method invocation |
| Variable |
| Chunk |
|---|
| Conflicting content |
|---|
mh.handleMessage(msg, msglen);
}
}
<<<<<<< HEAD:src/com/trilead/ssh2/transport/TransportManager.java
/**
* Advertised maximum SSH packet size that the other side can send to us.
*/
public static final int MAX_PACKET_SIZE = Integer.getInteger(
TransportManager.class.getName()+".maxPacketSize",
64*1024);
=======
>>>>>>> 1c88b087347422c786592188737e8cf0ac8a1b24:src/main/java/com/trilead/ssh2/transport/TransportManager.java
} |
| Solution content |
|---|
mh.handleMessage(msg, msglen);
}
}
/**
* Advertised maximum SSH packet size that the other side can send to us.
*/
public static final int MAX_PACKET_SIZE = Integer.getInteger(
TransportManager.class.getName()+".maxPacketSize",
64*1024);
} |
| File |
|---|
| TransportManager.java |
| Developer's decision |
|---|
| Version 1 |
| Kind of conflict |
|---|
| Attribute |
| Comment |
| Method invocation |