2009-07-25 19:03:01 +04:00
|
|
|
/* libssh_scp.c
|
|
|
|
* Sample implementation of a SCP client
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Copyright 2009 Aris Adamantiadis
|
|
|
|
|
|
|
|
This file is part of the SSH Library
|
|
|
|
|
|
|
|
You are free to copy this file, modify it in any way, consider it being public
|
2009-09-04 19:26:57 +04:00
|
|
|
domain. This does not apply to the rest of the library though, but it is
|
2009-07-25 19:03:01 +04:00
|
|
|
allowed to cut-and-paste working code from this file to any license of
|
|
|
|
program.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
2009-07-26 00:29:12 +04:00
|
|
|
#include <sys/stat.h>
|
2009-07-25 19:03:01 +04:00
|
|
|
|
|
|
|
#include <libssh/libssh.h>
|
|
|
|
#include "examples_common.h"
|
|
|
|
|
2021-05-09 21:00:24 +03:00
|
|
|
#ifndef BUF_SIZE
|
|
|
|
#define BUF_SIZE 16384
|
|
|
|
#endif
|
|
|
|
|
2012-02-18 15:26:23 +04:00
|
|
|
static char **sources;
|
|
|
|
static int nsources;
|
|
|
|
static char *destination;
|
2018-09-25 17:31:52 +03:00
|
|
|
static int verbosity = 0;
|
2009-07-26 00:29:12 +04:00
|
|
|
|
|
|
|
struct location {
|
2018-09-25 17:31:52 +03:00
|
|
|
int is_ssh;
|
|
|
|
char *user;
|
|
|
|
char *host;
|
|
|
|
char *path;
|
|
|
|
ssh_session session;
|
|
|
|
ssh_scp scp;
|
|
|
|
FILE *file;
|
2009-07-26 00:29:12 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
2018-09-25 17:31:52 +03:00
|
|
|
READ,
|
|
|
|
WRITE
|
2009-07-26 00:29:12 +04:00
|
|
|
};
|
2009-07-25 19:03:01 +04:00
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
static void usage(const char *argv0) {
|
|
|
|
fprintf(stderr, "Usage : %s [options] [[user@]host1:]file1 ... \n"
|
|
|
|
" [[user@]host2:]destination\n"
|
|
|
|
"sample scp client - libssh-%s\n",
|
|
|
|
// "Options :\n",
|
|
|
|
// " -r : use RSA to verify host public key\n",
|
|
|
|
argv0,
|
|
|
|
ssh_version(0));
|
|
|
|
exit(0);
|
2009-07-25 19:03:01 +04:00
|
|
|
}
|
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
static int opts(int argc, char **argv) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
while((i = getopt(argc, argv, "v")) != -1) {
|
|
|
|
switch(i) {
|
|
|
|
case 'v':
|
|
|
|
verbosity++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "unknown option %c\n", optopt);
|
|
|
|
usage(argv[0]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsources = argc - optind - 1;
|
|
|
|
if (nsources < 1) {
|
2009-07-25 19:03:01 +04:00
|
|
|
usage(argv[0]);
|
2009-07-26 00:29:12 +04:00
|
|
|
return -1;
|
2009-07-25 19:03:01 +04:00
|
|
|
}
|
2018-09-25 17:31:52 +03:00
|
|
|
|
|
|
|
sources = malloc((nsources + 1) * sizeof(char *));
|
|
|
|
if (sources == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < nsources; ++i) {
|
|
|
|
sources[i] = argv[optind];
|
|
|
|
optind++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sources[i] = NULL;
|
|
|
|
destination = argv[optind];
|
|
|
|
return 0;
|
2009-07-25 19:03:01 +04:00
|
|
|
}
|
|
|
|
|
2018-09-25 17:08:30 +03:00
|
|
|
static void location_free(struct location *loc)
|
|
|
|
{
|
|
|
|
if (loc) {
|
|
|
|
if (loc->path) {
|
|
|
|
free(loc->path);
|
|
|
|
}
|
|
|
|
loc->path = NULL;
|
|
|
|
if (loc->is_ssh) {
|
|
|
|
if (loc->host) {
|
|
|
|
free(loc->host);
|
|
|
|
}
|
|
|
|
loc->host = NULL;
|
|
|
|
if (loc->user) {
|
|
|
|
free(loc->user);
|
|
|
|
}
|
|
|
|
loc->user = NULL;
|
|
|
|
}
|
|
|
|
free(loc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
static struct location *parse_location(char *loc) {
|
|
|
|
struct location *location;
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
location = malloc(sizeof(struct location));
|
|
|
|
if (location == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(location, 0, sizeof(struct location));
|
|
|
|
|
|
|
|
location->host = location->user = NULL;
|
|
|
|
ptr = strchr(loc, ':');
|
|
|
|
|
|
|
|
if (ptr != NULL) {
|
|
|
|
location->is_ssh = 1;
|
|
|
|
location->path = strdup(ptr+1);
|
|
|
|
*ptr = '\0';
|
|
|
|
ptr = strchr(loc, '@');
|
|
|
|
|
|
|
|
if (ptr != NULL) {
|
|
|
|
location->host = strdup(ptr+1);
|
|
|
|
*ptr = '\0';
|
|
|
|
location->user = strdup(loc);
|
|
|
|
} else {
|
|
|
|
location->host = strdup(loc);
|
|
|
|
}
|
2009-07-26 00:29:12 +04:00
|
|
|
} else {
|
2018-09-25 17:31:52 +03:00
|
|
|
location->is_ssh = 0;
|
|
|
|
location->path = strdup(loc);
|
2009-07-26 00:29:12 +04:00
|
|
|
}
|
2018-09-25 17:31:52 +03:00
|
|
|
return location;
|
2009-07-26 00:29:12 +04:00
|
|
|
}
|
2009-07-25 19:03:01 +04:00
|
|
|
|
2018-09-25 17:08:30 +03:00
|
|
|
static void close_location(struct location *loc) {
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (loc) {
|
|
|
|
if (loc->is_ssh) {
|
|
|
|
if (loc->scp) {
|
|
|
|
rc = ssh_scp_close(loc->scp);
|
|
|
|
if (rc == SSH_ERROR) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Error closing scp: %s\n",
|
|
|
|
ssh_get_error(loc->session));
|
|
|
|
}
|
|
|
|
ssh_scp_free(loc->scp);
|
|
|
|
loc->scp = NULL;
|
|
|
|
}
|
|
|
|
if (loc->session) {
|
|
|
|
ssh_disconnect(loc->session);
|
|
|
|
ssh_free(loc->session);
|
|
|
|
loc->session = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (loc->file) {
|
|
|
|
fclose(loc->file);
|
|
|
|
loc->file = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
static int open_location(struct location *loc, int flag) {
|
|
|
|
if (loc->is_ssh && flag == WRITE) {
|
|
|
|
loc->session = connect_ssh(loc->host, loc->user, verbosity);
|
|
|
|
if (!loc->session) {
|
|
|
|
fprintf(stderr, "Couldn't connect to %s\n", loc->host);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
loc->scp = ssh_scp_new(loc->session, SSH_SCP_WRITE, loc->path);
|
|
|
|
if (!loc->scp) {
|
|
|
|
fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
|
|
|
|
ssh_disconnect(loc->session);
|
|
|
|
ssh_free(loc->session);
|
|
|
|
loc->session = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ssh_scp_init(loc->scp) == SSH_ERROR) {
|
|
|
|
fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
|
|
|
|
ssh_scp_free(loc->scp);
|
|
|
|
loc->scp = NULL;
|
|
|
|
ssh_disconnect(loc->session);
|
|
|
|
ssh_free(loc->session);
|
|
|
|
loc->session = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
} else if (loc->is_ssh && flag == READ) {
|
|
|
|
loc->session = connect_ssh(loc->host, loc->user, verbosity);
|
|
|
|
if (!loc->session) {
|
|
|
|
fprintf(stderr, "Couldn't connect to %s\n", loc->host);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
loc->scp = ssh_scp_new(loc->session, SSH_SCP_READ, loc->path);
|
|
|
|
if (!loc->scp) {
|
|
|
|
fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
|
|
|
|
ssh_disconnect(loc->session);
|
|
|
|
ssh_free(loc->session);
|
|
|
|
loc->session = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ssh_scp_init(loc->scp) == SSH_ERROR) {
|
|
|
|
fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
|
|
|
|
ssh_scp_free(loc->scp);
|
|
|
|
loc->scp = NULL;
|
|
|
|
ssh_disconnect(loc->session);
|
|
|
|
ssh_free(loc->session);
|
|
|
|
loc->session = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
loc->file = fopen(loc->path, flag == READ ? "r":"w");
|
|
|
|
if (!loc->file) {
|
|
|
|
if (errno == EISDIR) {
|
2021-08-04 10:22:08 +03:00
|
|
|
if (loc->path != NULL && chdir(loc->path)) {
|
2018-09-25 17:31:52 +03:00
|
|
|
fprintf(stderr,
|
|
|
|
"Error changing directory to %s: %s\n",
|
|
|
|
loc->path, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
fprintf(stderr,
|
|
|
|
"Error opening %s: %s\n",
|
|
|
|
loc->path, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2009-07-26 00:29:12 +04:00
|
|
|
}
|
2018-09-25 17:31:52 +03:00
|
|
|
return -1;
|
2009-07-26 00:29:12 +04:00
|
|
|
}
|
|
|
|
|
2009-09-04 19:26:57 +04:00
|
|
|
/** @brief copies files from source location to destination
|
|
|
|
* @param src source location
|
|
|
|
* @param dest destination location
|
|
|
|
* @param recursive Copy also directories
|
|
|
|
*/
|
2018-09-25 17:31:52 +03:00
|
|
|
static int do_copy(struct location *src, struct location *dest, int recursive) {
|
2019-10-31 12:28:22 +03:00
|
|
|
size_t size;
|
2018-09-25 17:31:52 +03:00
|
|
|
socket_t fd;
|
|
|
|
struct stat s;
|
|
|
|
int w, r;
|
2021-05-09 21:00:24 +03:00
|
|
|
char buffer[BUF_SIZE];
|
2019-10-31 12:28:22 +03:00
|
|
|
size_t total = 0;
|
|
|
|
mode_t mode;
|
2018-09-25 17:31:52 +03:00
|
|
|
char *filename = NULL;
|
2019-10-31 12:28:22 +03:00
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
/* recursive mode doesn't work yet */
|
|
|
|
(void)recursive;
|
|
|
|
/* Get the file name and size*/
|
|
|
|
if (!src->is_ssh) {
|
|
|
|
fd = fileno(src->file);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Invalid file pointer, error: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
r = fstat(fd, &s);
|
|
|
|
if (r < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
size = s.st_size;
|
|
|
|
mode = s.st_mode & ~S_IFMT;
|
|
|
|
filename = ssh_basename(src->path);
|
|
|
|
} else {
|
|
|
|
size = 0;
|
|
|
|
do {
|
|
|
|
r = ssh_scp_pull_request(src->scp);
|
|
|
|
if (r == SSH_SCP_REQUEST_NEWDIR) {
|
|
|
|
ssh_scp_deny_request(src->scp, "Not in recursive mode");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (r == SSH_SCP_REQUEST_NEWFILE) {
|
|
|
|
size = ssh_scp_request_get_size(src->scp);
|
|
|
|
filename = strdup(ssh_scp_request_get_filename(src->scp));
|
|
|
|
mode = ssh_scp_request_get_permissions(src->scp);
|
|
|
|
//ssh_scp_accept_request(src->scp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (r == SSH_ERROR) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Error: %s\n",
|
|
|
|
ssh_get_error(src->session));
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2018-09-25 17:31:52 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} while(r != SSH_SCP_REQUEST_NEWFILE);
|
2013-06-19 14:32:10 +04:00
|
|
|
}
|
2018-09-25 17:31:52 +03:00
|
|
|
|
|
|
|
if (dest->is_ssh) {
|
|
|
|
r = ssh_scp_push_file(dest->scp, src->path, size, mode);
|
|
|
|
// snprintf(buffer, sizeof(buffer), "C0644 %d %s\n", size, src->path);
|
|
|
|
if (r == SSH_ERROR) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"error: %s\n",
|
|
|
|
ssh_get_error(dest->session));
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2018-09-25 17:31:52 +03:00
|
|
|
ssh_scp_free(dest->scp);
|
|
|
|
dest->scp = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!dest->file) {
|
|
|
|
dest->file = fopen(filename, "w");
|
|
|
|
if (!dest->file) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Cannot open %s for writing: %s\n",
|
|
|
|
filename, strerror(errno));
|
|
|
|
if (src->is_ssh) {
|
|
|
|
ssh_scp_deny_request(src->scp, "Cannot open local file");
|
|
|
|
}
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2018-09-25 17:31:52 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (src->is_ssh) {
|
|
|
|
ssh_scp_accept_request(src->scp);
|
|
|
|
}
|
2013-07-14 17:01:06 +04:00
|
|
|
}
|
2018-09-25 17:31:52 +03:00
|
|
|
|
2009-09-04 19:26:57 +04:00
|
|
|
do {
|
2018-09-25 17:31:52 +03:00
|
|
|
if (src->is_ssh) {
|
|
|
|
r = ssh_scp_read(src->scp, buffer, sizeof(buffer));
|
|
|
|
if (r == SSH_ERROR) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Error reading scp: %s\n",
|
|
|
|
ssh_get_error(src->session));
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2018-09-25 17:31:52 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r = fread(buffer, 1, sizeof(buffer), src->file);
|
|
|
|
if (r == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Error reading file: %s\n",
|
|
|
|
strerror(errno));
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2018-09-25 17:31:52 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dest->is_ssh) {
|
|
|
|
w = ssh_scp_write(dest->scp, buffer, r);
|
|
|
|
if (w == SSH_ERROR) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Error writing in scp: %s\n",
|
|
|
|
ssh_get_error(dest->session));
|
|
|
|
ssh_scp_free(dest->scp);
|
|
|
|
dest->scp = NULL;
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2018-09-25 17:31:52 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
w = fwrite(buffer, r, 1, dest->file);
|
|
|
|
if (w <= 0) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Error writing in local file: %s\n",
|
|
|
|
strerror(errno));
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2018-09-25 17:31:52 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
total += r;
|
|
|
|
|
|
|
|
} while(total < size);
|
|
|
|
|
2019-11-05 15:14:12 +03:00
|
|
|
SSH_STRING_FREE_CHAR(filename);
|
2019-10-31 12:28:22 +03:00
|
|
|
printf("wrote %zu bytes\n", total);
|
2018-09-25 17:31:52 +03:00
|
|
|
return 0;
|
2009-07-26 00:29:12 +04:00
|
|
|
}
|
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
struct location *dest, *src;
|
|
|
|
int i;
|
|
|
|
int r;
|
|
|
|
if (opts(argc, argv) < 0) {
|
2021-05-07 08:46:03 +03:00
|
|
|
return EXIT_FAILURE;
|
2018-09-25 17:31:52 +03:00
|
|
|
}
|
|
|
|
|
2021-05-07 08:46:03 +03:00
|
|
|
ssh_init();
|
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
dest = parse_location(destination);
|
2018-09-25 17:44:06 +03:00
|
|
|
if (dest == NULL) {
|
|
|
|
r = EXIT_FAILURE;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
if (open_location(dest, WRITE) < 0) {
|
|
|
|
location_free(dest);
|
|
|
|
r = EXIT_FAILURE;
|
|
|
|
goto end;
|
2009-07-26 00:29:12 +04:00
|
|
|
}
|
2018-09-25 17:31:52 +03:00
|
|
|
|
|
|
|
for (i = 0; i < nsources; ++i) {
|
|
|
|
src = parse_location(sources[i]);
|
2018-09-25 17:44:06 +03:00
|
|
|
if (src == NULL) {
|
|
|
|
r = EXIT_FAILURE;
|
|
|
|
goto close_dest;
|
|
|
|
}
|
|
|
|
|
2018-09-25 17:31:52 +03:00
|
|
|
if (open_location(src, READ) < 0) {
|
|
|
|
location_free(src);
|
|
|
|
r = EXIT_FAILURE;
|
|
|
|
goto close_dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_copy(src, dest, 0) < 0) {
|
|
|
|
close_location(src);
|
|
|
|
location_free(src);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-09-25 17:08:30 +03:00
|
|
|
close_location(src);
|
|
|
|
location_free(src);
|
2009-09-06 13:56:04 +04:00
|
|
|
}
|
2018-09-25 17:31:52 +03:00
|
|
|
|
|
|
|
r = 0;
|
2018-09-25 17:08:30 +03:00
|
|
|
|
|
|
|
close_dest:
|
2018-09-25 17:31:52 +03:00
|
|
|
close_location(dest);
|
|
|
|
location_free(dest);
|
2018-09-25 17:08:30 +03:00
|
|
|
end:
|
2021-05-07 08:46:03 +03:00
|
|
|
ssh_finalize();
|
2021-05-07 08:54:10 +03:00
|
|
|
free(sources);
|
2018-09-25 17:31:52 +03:00
|
|
|
return r;
|
2009-07-25 19:03:01 +04:00
|
|
|
}
|