Skip to content

Commit

Permalink
Merge branch 'master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
dkocher committed Sep 16, 2024
2 parents 0eb5d6b + 5601f39 commit 79ebef2
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 64 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
name: Build with Java 11
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Java 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 11
Expand All @@ -31,9 +31,9 @@ jobs:
name: Integration test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 11
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ jobs:
name: Build with Java 12
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 12
uses: actions/setup-java@v2
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 12
Expand All @@ -30,10 +30,10 @@ jobs:
needs: [java12]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v2
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 12
Expand Down
15 changes: 7 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ compileJava {

configurations.implementation.transitive = false

def bouncycastleVersion = "1.78"
def sshdVersion = "2.10.0"
def bouncycastleVersion = "1.78.1"
def sshdVersion = "2.12.1"

dependencies {
implementation "org.slf4j:slf4j-api:2.0.7"
implementation "org.slf4j:slf4j-api:2.0.13"
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "com.hierynomus:asn-one:0.6.0"
Expand All @@ -59,7 +59,6 @@ license {
java = 'SLASHSTAR_STYLE'
}
excludes([
'**/sshj/common/Base64.java',
'**/com/hierynomus/sshj/userauth/keyprovider/bcrypt/*.java',
'**/files/test_file_*.txt',
])
Expand All @@ -86,15 +85,15 @@ testing {
configureEach {
useJUnitJupiter()
dependencies {
implementation "org.slf4j:slf4j-api:2.0.7"
implementation "org.slf4j:slf4j-api:2.0.13"
implementation 'org.spockframework:spock-core:2.3-groovy-3.0'
implementation "org.mockito:mockito-core:4.11.0"
implementation "org.assertj:assertj-core:3.24.2"
implementation "ru.vyarus:spock-junit5:1.2.0"
implementation "org.apache.sshd:sshd-core:$sshdVersion"
implementation "org.apache.sshd:sshd-sftp:$sshdVersion"
implementation "org.apache.sshd:sshd-scp:$sshdVersion"
implementation "ch.qos.logback:logback-classic:1.3.8"
implementation "ch.qos.logback:logback-classic:1.3.14"
implementation 'org.glassfish.grizzly:grizzly-http-server:3.0.1'
}

Expand Down Expand Up @@ -130,8 +129,8 @@ testing {
integrationTest(JvmTestSuite) {
dependencies {
implementation project()
implementation 'org.testcontainers:testcontainers:1.18.3'
implementation 'org.testcontainers:junit-jupiter:1.18.3'
implementation 'org.testcontainers:testcontainers:1.19.8'
implementation 'org.testcontainers:junit-jupiter:1.19.8'
}

sources {
Expand Down
47 changes: 47 additions & 0 deletions src/itest/java/com/hierynomus/sshj/sftp/SftpIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.sftp;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import com.hierynomus.sshj.SshdContainer;

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.FileAttributes;
import net.schmizz.sshj.sftp.SFTPClient;

@Testcontainers
public class SftpIntegrationTest {
@Container
private static SshdContainer sshd = new SshdContainer();

@Test
public void shouldCheckFileExistsForNonExistingFile_GH894() throws Throwable {
try (SSHClient client = sshd.getConnectedClient()) {
client.authPublickey("sshj", "src/test/resources/id_rsa");
try (SFTPClient sftp = client.newSFTPClient()) {
String file = "/home/sshj/i_do_not_exist.txt";
FileAttributes exists = sftp.statExistence(file);
assertNull(exists);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ protected void gotExtendedData(SSHPacket buf)

@Override
public void notifyError(SSHException error) {
err.notifyError(error);
if (err != null) {
err.notifyError(error);
}
super.notifyError(error);
}

Expand Down
36 changes: 19 additions & 17 deletions src/main/java/net/schmizz/sshj/transport/kex/Curve25519DH.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ public class Curve25519DH extends DHBase {

private static final String ALGORITHM = "X25519";

private static final int ENCODED_ALGORITHM_ID_KEY_LENGTH = 44;
private static final int KEY_LENGTH = 32;

private static final int ALGORITHM_ID_LENGTH = 12;
private int encodedKeyLength;

private static final int KEY_LENGTH = 32;
private int algorithmIdLength;

private final byte[] algorithmId = new byte[ALGORITHM_ID_LENGTH];
// Algorithm Identifier is set on Key Agreement Initialization
private byte[] algorithmId = new byte[KEY_LENGTH];

public Curve25519DH() {
super(ALGORITHM, ALGORITHM);
Expand Down Expand Up @@ -81,23 +82,24 @@ public void init(final AlgorithmParameterSpec params, final Factory<Random> rand
private void setPublicKey(final PublicKey publicKey) {
final byte[] encoded = publicKey.getEncoded();

// Set key and algorithm identifier lengths based on initialized Public Key
encodedKeyLength = encoded.length;
algorithmIdLength = encodedKeyLength - KEY_LENGTH;
algorithmId = new byte[algorithmIdLength];

// Encoded public key consists of the algorithm identifier and public key
if (encoded.length == ENCODED_ALGORITHM_ID_KEY_LENGTH) {
final byte[] publicKeyEncoded = new byte[KEY_LENGTH];
System.arraycopy(encoded, ALGORITHM_ID_LENGTH, publicKeyEncoded, 0, KEY_LENGTH);
setE(publicKeyEncoded);

// Save Algorithm Identifier byte array
System.arraycopy(encoded, 0, algorithmId, 0, ALGORITHM_ID_LENGTH);
} else {
throw new IllegalArgumentException(String.format("X25519 unsupported public key length [%d]", encoded.length));
}
final byte[] publicKeyEncoded = new byte[KEY_LENGTH];
System.arraycopy(encoded, algorithmIdLength, publicKeyEncoded, 0, KEY_LENGTH);
setE(publicKeyEncoded);

// Save Algorithm Identifier byte array
System.arraycopy(encoded, 0, algorithmId, 0, algorithmIdLength);
}

private KeySpec getPeerPublicKeySpec(final byte[] peerPublicKey) {
final byte[] encodedKeySpec = new byte[ENCODED_ALGORITHM_ID_KEY_LENGTH];
System.arraycopy(algorithmId, 0, encodedKeySpec, 0, ALGORITHM_ID_LENGTH);
System.arraycopy(peerPublicKey, 0, encodedKeySpec, ALGORITHM_ID_LENGTH, KEY_LENGTH);
final byte[] encodedKeySpec = new byte[encodedKeyLength];
System.arraycopy(algorithmId, 0, encodedKeySpec, 0, algorithmIdLength);
System.arraycopy(peerPublicKey, 0, encodedKeySpec, algorithmIdLength, KEY_LENGTH);
return new X509EncodedKeySpec(encodedKeySpec);
}
}
22 changes: 13 additions & 9 deletions src/main/java/net/schmizz/sshj/xfer/scp/SCPDownloadClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,20 @@ public synchronized int copy(String sourcePath, LocalDestFile targetFile) throws

public synchronized int copy(String sourcePath, LocalDestFile targetFile, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SOURCE)
.and(ScpCommandLine.Arg.QUIET)
.and(ScpCommandLine.Arg.PRESERVE_TIMES)
.and(ScpCommandLine.Arg.RECURSIVE, recursiveMode)
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
return copy(sourcePath, targetFile, escapeMode, commandLine);
}

public synchronized int copy(String sourcePath, LocalDestFile targetFile, ScpCommandLine.EscapeMode escapeMode, ScpCommandLine commandLine)
throws IOException {
engine.cleanSlate();
try {
startCopy(sourcePath, targetFile, escapeMode);
commandLine.withPath(sourcePath, escapeMode);
startCopy(targetFile, commandLine);
} finally {
engine.exit();
}
Expand All @@ -62,14 +73,7 @@ public void setRecursiveMode(boolean recursive) {
this.recursiveMode = recursive;
}

private void startCopy(String sourcePath, LocalDestFile targetFile, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SOURCE)
.and(ScpCommandLine.Arg.QUIET)
.and(ScpCommandLine.Arg.PRESERVE_TIMES)
.and(ScpCommandLine.Arg.RECURSIVE, recursiveMode)
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
commandLine.withPath(sourcePath, escapeMode);
private void startCopy(LocalDestFile targetFile, ScpCommandLine commandLine) throws IOException {
engine.execSCPWith(commandLine);

engine.signal("Start status OK");
Expand Down
32 changes: 20 additions & 12 deletions src/main/java/net/schmizz/sshj/xfer/scp/SCPUploadClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import net.schmizz.sshj.xfer.LocalFileFilter;
import net.schmizz.sshj.xfer.LocalSourceFile;
import net.schmizz.sshj.xfer.TransferListener;
import net.schmizz.sshj.xfer.scp.ScpCommandLine.Arg;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -43,37 +44,44 @@ public synchronized int copy(LocalSourceFile sourceFile, String remotePath)
return copy(sourceFile, remotePath, ScpCommandLine.EscapeMode.SingleQuote);
}

public synchronized int copy (LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode) throws IOException {
public synchronized int copy(LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode)
throws IOException {
return copy(sourceFile, remotePath, escapeMode, true);
}

public synchronized int copy(LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode, boolean preserveTimes)
throws IOException {
throws IOException {
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SINK)
.and(ScpCommandLine.Arg.RECURSIVE)
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
if (preserveTimes) {
commandLine.and(ScpCommandLine.Arg.PRESERVE_TIMES, sourceFile.providesAtimeMtime());
}
return copy(sourceFile, remotePath, escapeMode, commandLine);
}

public synchronized int copy(LocalSourceFile sourceFile, String remotePath, ScpCommandLine.EscapeMode escapeMode, ScpCommandLine commandLine)
throws IOException {
engine.cleanSlate();
try {
startCopy(sourceFile, remotePath, escapeMode, preserveTimes);
commandLine.withPath(remotePath, escapeMode);
startCopy(sourceFile, commandLine);
} finally {
engine.exit();
}
return engine.getExitStatus();
}


public void setUploadFilter(LocalFileFilter uploadFilter) {
this.uploadFilter = uploadFilter;
}

private void startCopy(LocalSourceFile sourceFile, String targetPath, ScpCommandLine.EscapeMode escapeMode, boolean preserveTimes)
private void startCopy(LocalSourceFile sourceFile, ScpCommandLine commandLine)
throws IOException {
ScpCommandLine commandLine = ScpCommandLine.with(ScpCommandLine.Arg.SINK)
.and(ScpCommandLine.Arg.RECURSIVE)
.and(ScpCommandLine.Arg.LIMIT, String.valueOf(bandwidthLimit), (bandwidthLimit > 0));
if (preserveTimes) {
commandLine.and(ScpCommandLine.Arg.PRESERVE_TIMES, sourceFile.providesAtimeMtime());
}
commandLine.withPath(targetPath, escapeMode);
engine.execSCPWith(commandLine);
engine.check("Start status OK");
process(engine.getTransferListener(), sourceFile, preserveTimes);
process(engine.getTransferListener(), sourceFile, commandLine.has(Arg.PRESERVE_TIMES));
}

private void process(TransferListener listener, LocalSourceFile f, boolean preserveTimes)
Expand Down
22 changes: 13 additions & 9 deletions src/main/java/net/schmizz/sshj/xfer/scp/ScpCommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class ScpCommandLine {
private static final String SCP_COMMAND = "scp";
private EscapeMode mode;

enum Arg {
public enum Arg {
SOURCE('f'),
SINK('t'),
RECURSIVE('r'),
Expand Down Expand Up @@ -77,19 +77,19 @@ String escapedPath(String path) {
ScpCommandLine() {
}

static ScpCommandLine with(Arg name) {
public static ScpCommandLine with(Arg name) {
return with(name, null, true);
}

static ScpCommandLine with(Arg name, String value) {
public static ScpCommandLine with(Arg name, String value) {
return with(name, value, true);
}

static ScpCommandLine with(Arg name, boolean accept) {
public static ScpCommandLine with(Arg name, boolean accept) {
return with(name, null, accept);
}

static ScpCommandLine with(Arg name, String value, boolean accept) {
public static ScpCommandLine with(Arg name, String value, boolean accept) {
ScpCommandLine commandLine = new ScpCommandLine();
commandLine.addArgument(name, value, accept);
return commandLine;
Expand All @@ -101,22 +101,22 @@ private void addArgument(Arg name, String value, boolean accept) {
}
}

ScpCommandLine and(Arg name) {
public ScpCommandLine and(Arg name) {
addArgument(name, null, true);
return this;
}

ScpCommandLine and(Arg name, String value) {
public ScpCommandLine and(Arg name, String value) {
addArgument(name, value, true);
return this;
}

ScpCommandLine and(Arg name, boolean accept) {
public ScpCommandLine and(Arg name, boolean accept) {
addArgument(name, null, accept);
return this;
}

ScpCommandLine and(Arg name, String value, boolean accept) {
public ScpCommandLine and(Arg name, String value, boolean accept) {
addArgument(name, value, accept);
return this;
}
Expand All @@ -127,6 +127,10 @@ ScpCommandLine withPath(String path, EscapeMode mode) {
return this;
}

boolean has(Arg arg) {
return arguments.containsKey(arg);
}

String toCommandLine() {
final StringBuilder cmd = new StringBuilder(SCP_COMMAND);
for (Arg arg : arguments.keySet()) {
Expand Down
Loading

0 comments on commit 79ebef2

Please sign in to comment.