add -o SSH_OPTION option

This commit is contained in:
Ryo Nakamura
2024-04-12 23:08:22 +09:00
parent bf7e2c3ae3
commit a1b9afefe5
6 changed files with 89 additions and 15 deletions

View File

@@ -47,7 +47,10 @@ mscp \- copy files over multiple SSH connections
.BI \-P \ PORT\c .BI \-P \ PORT\c
] ]
[\c [\c
.BI \-F \ CONFIG\c .BI \-F \ SSH_CONFIG\c
]
[\c
.BI \-o \ SSH_OPTION\c
] ]
[\c [\c
.BI \-i \ IDENTITY\c .BI \-i \ IDENTITY\c
@@ -247,12 +250,19 @@ Specifies the port number to connect to on the remote machine as with
ssh(1) and scp(1). ssh(1) and scp(1).
.TP .TP
.B \-F \fICONFIG\fR .B \-F \fISSH_CONFIG\fR
Specifies an alternative per-user ssh configuration file. Note that Specifies an alternative per-user ssh configuration file. Note that
acceptable options in the configuration file are what acceptable options in the configuration file are what
.I libssh .I libssh
supports. supports.
.TP
.B \-o \fISSH_OPTION\fR
Specifies ssh options in the format used in ssh_config. Note that
acceptable options are what
.I libssh
supports.
.TP .TP
.B \-i \fIIDENTITY\fR .B \-i \fIIDENTITY\fR
Specifies the identity file for public key authentication. Specifies the identity file for public key authentication.

View File

@@ -2,7 +2,7 @@
MSCP MSCP
==== ====
:Date: v0.1.5-10-g00fa2c7 :Date: v0.1.5-13-gf2f0dab
NAME NAME
==== ====
@@ -17,9 +17,9 @@ SYNOPSIS
*CHECKPOINT* ] [ **-R** *CHECKPOINT* ] [ **-s** *MIN_CHUNK_SIZE* ] [ *CHECKPOINT* ] [ **-R** *CHECKPOINT* ] [ **-s** *MIN_CHUNK_SIZE* ] [
**-S** *MAX_CHUNK_SIZE* ] [ **-a** *NR_AHEAD* ] [ **-b** *BUF_SIZE* ] [ **-S** *MAX_CHUNK_SIZE* ] [ **-a** *NR_AHEAD* ] [ **-b** *BUF_SIZE* ] [
**-L** *LIMIT_BITRATE* ] [ **-l** *LOGIN_NAME* ] [ **-P** *PORT* ] [ **-L** *LIMIT_BITRATE* ] [ **-l** *LOGIN_NAME* ] [ **-P** *PORT* ] [
**-F** *CONFIG* ] [ **-i** *IDENTITY* ] [ **-c** *CIPHER* ] [ **-M** **-F** *SSH_CONFIG* ] [ **-o** *SSH_OPTION* ] [ **-i** *IDENTITY* ] [
*HMAC* ] [ **-C** *COMPRESS* ] [ **-g** *CONGESTION* ] *source ... **-c** *CIPHER* ] [ **-M** *HMAC* ] [ **-C** *COMPRESS* ] [ **-g**
target* *CONGESTION* ] *source ... target*
DESCRIPTION DESCRIPTION
=========== ===========
@@ -144,11 +144,15 @@ OPTIONS
Specifies the port number to connect to on the remote machine as with Specifies the port number to connect to on the remote machine as with
ssh(1) and scp(1). ssh(1) and scp(1).
**-F CONFIG** **-F SSH_CONFIG**
Specifies an alternative per-user ssh configuration file. Note that Specifies an alternative per-user ssh configuration file. Note that
acceptable options in the configuration file are what *libssh* acceptable options in the configuration file are what *libssh*
supports. supports.
**-o SSH_OPTION**
Specifies ssh options in the format used in ssh_config. Note that
acceptable options are what *libssh* supports.
**-i IDENTITY** **-i IDENTITY**
Specifies the identity file for public key authentication. Specifies the identity file for public key authentication.

View File

@@ -61,6 +61,7 @@ struct mscp_ssh_opts {
char *port; /** ssh port */ char *port; /** ssh port */
int ai_family; /** address family */ int ai_family; /** address family */
char *config; /** path to ssh_config, default ~/.ssh/config*/ char *config; /** path to ssh_config, default ~/.ssh/config*/
char **options; /** array of ssh_config options, terminated by NULL */
char *identity; /** path to private key */ char *identity; /** path to private key */
char *cipher; /** cipher spec */ char *cipher; /** cipher spec */
char *hmac; /** hmacp spec */ char *hmac; /** hmacp spec */

View File

@@ -28,8 +28,9 @@ void usage(bool print_help)
" [-u max_startups] [-I interval] [-W checkpoint] [-R checkpoint]\n" " [-u max_startups] [-I interval] [-W checkpoint] [-R checkpoint]\n"
" [-s min_chunk_sz] [-S max_chunk_sz] [-a nr_ahead]\n" " [-s min_chunk_sz] [-S max_chunk_sz] [-a nr_ahead]\n"
" [-b buf_sz] [-L limit_bitrate]\n" " [-b buf_sz] [-L limit_bitrate]\n"
" [-l login_name] [-P port] [-F ssh_config] [-i identity_file]\n" " [-l login_name] [-P port] [-F ssh_config] [-o ssh_option]\n"
" [-c cipher_spec] [-M hmac_spec] [-C compress] [-g congestion]\n" " [-i identity_file] [-c cipher_spec] [-M hmac_spec]\n"
" [-C compress] [-g congestion]\n"
" source ... target\n" " source ... target\n"
"\n"); "\n");
@@ -60,7 +61,8 @@ void usage(bool print_help)
"\n" "\n"
" -l LOGIN_NAME login name\n" " -l LOGIN_NAME login name\n"
" -P PORT port number\n" " -P PORT port number\n"
" -F CONFIG path to user ssh config (default ~/.ssh/config)\n" " -F SSH_CONFIG path to user ssh config (default ~/.ssh/config)\n"
" -o SSH_OPTION ssh_config option\n"
" -i IDENTITY identity file for public key authentication\n" " -i IDENTITY identity file for public key authentication\n"
" -c CIPHER cipher spec\n" " -c CIPHER cipher spec\n"
" -M HMAC hmac spec\n" " -M HMAC hmac spec\n"
@@ -267,6 +269,7 @@ int main(int argc, char **argv)
int direction = 0; int direction = 0;
char *remote = NULL, *checkpoint_save = NULL, *checkpoint_load = NULL; char *remote = NULL, *checkpoint_save = NULL, *checkpoint_load = NULL;
bool dryrun = false, resume = false; bool dryrun = false, resume = false;
int nr_options = 0;
size_t factor = 1; size_t factor = 1;
char *unit; char *unit;
@@ -274,7 +277,7 @@ int main(int argc, char **argv)
memset(&o, 0, sizeof(o)); memset(&o, 0, sizeof(o));
o.severity = MSCP_SEVERITY_WARN; o.severity = MSCP_SEVERITY_WARN;
#define mscpopts "n:m:u:I:W:R:s:S:a:b:L:46vqDrl:P:i:F:c:M:C:g:pdNh" #define mscpopts "n:m:u:I:W:R:s:S:a:b:L:46vqDrl:P:i:F:o:c:M:C:g:pdNh"
while ((ch = getopt(argc, argv, mscpopts)) != -1) { while ((ch = getopt(argc, argv, mscpopts)) != -1) {
switch (ch) { switch (ch) {
case 'n': case 'n':
@@ -359,6 +362,16 @@ int main(int argc, char **argv)
case 'F': case 'F':
s.config = optarg; s.config = optarg;
break; break;
case 'o':
nr_options++;
s.options = realloc(s.options, sizeof(char *) * (nr_options + 1));
if (!s.options) {
pr_err("realloc: %s", strerrno());
return 1;
}
s.options[nr_options - 1] = optarg;
s.options[nr_options] = NULL;
break;
case 'i': case 'i':
s.identity = optarg; s.identity = optarg;
break; break;

View File

@@ -4,12 +4,13 @@
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include "libssh/callbacks.h"
#include <ssh.h> #include <ssh.h>
#include <mscp.h> #include <mscp.h>
#include <strerrno.h> #include <strerrno.h>
#include "libssh/callbacks.h"
#include "libssh/options.h"
static int ssh_verify_known_hosts(ssh_session session); static int ssh_verify_known_hosts(ssh_session session);
static int ssh_authenticate_kbdint(ssh_session session); static int ssh_authenticate_kbdint(ssh_session session);
@@ -87,6 +88,17 @@ static int ssh_set_opts(ssh_session ssh, struct mscp_ssh_opts *opts)
return -1; return -1;
} }
if (opts->options) {
int n;
for (n = 0; opts->options[n]; n++) {
if (ssh_config_parse_string(ssh, opts->options[n]) != SSH_OK) {
priv_set_errv("failed to set ssh option %s: %s",
opts->options[n]);
return -1;
}
}
}
return 0; return 0;
} }

View File

@@ -496,6 +496,40 @@ def test_config_ng(mscp, src_prefix, dst_prefix):
src.cleanup() src.cleanup()
dst.cleanup() dst.cleanup()
param_valid_option_ok = [
[ "-o", "Port=8022" ],
[ "-o", "Port=8022", "-o", "User=root" ],
[ "-o", "unknown-option-is-silently-ignored" ],
]
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
@pytest.mark.parametrize("option", param_valid_option_ok)
def test_inline_option_ok(mscp, src_prefix, dst_prefix, option):
""" change port number with -o option. it should be ok. """
src = File("src", size = 1024 * 1024).make()
dst = File("dst")
run2ok([mscp, "-vvv"] + option +
[src_prefix + src.path, dst_prefix + dst.path])
assert check_same_md5sum(src, dst)
src.cleanup()
dst.cleanup()
param_valid_option_ng = [
[ "-o", "Port=8023" ],
[ "-o", "User=invaliduser" ],
]
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
@pytest.mark.parametrize("option", param_valid_option_ng)
def test_inline_option_ng(mscp, src_prefix, dst_prefix, option):
""" change port number with -o option. it should be ng. """
src = File("src", size = 1024 * 1024).make()
dst = File("dst")
run2ng([mscp, "-vvv"] + option +
[src_prefix + src.path, dst_prefix + dst.path])
src.cleanup()
# username test assumes that this test runs inside a container, see Dockerfiles # username test assumes that this test runs inside a container, see Dockerfiles
def test_specify_passphrase_via_env(mscp): def test_specify_passphrase_via_env(mscp):
src = File(os.getcwd() + "/src", size = 1024).make() src = File(os.getcwd() + "/src", size = 1024).make()
@@ -552,8 +586,8 @@ def test_10k_files(mscp, src_prefix, dst_prefix):
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix) @pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
def test_checkpoint_dump_and_resume(mscp, src_prefix, dst_prefix): def test_checkpoint_dump_and_resume(mscp, src_prefix, dst_prefix):
src1 = File("src1", size = 512 * 1024 * 1024).make() src1 = File("src1", size = 64 * 1024 * 1024).make()
src2 = File("src2", size = 512 * 1024 * 1024).make() src2 = File("src2", size = 64 * 1024 * 1024).make()
dst1 = File("dst/src1") dst1 = File("dst/src1")
dst2 = File("dst/src2") dst2 = File("dst/src2")
run2ok([mscp, "-vvv", "-W", "checkpoint", "-D", run2ok([mscp, "-vvv", "-W", "checkpoint", "-D",