354 lines
9.2 KiB
C
354 lines
9.2 KiB
C
/*
|
|
* Rangitaki Sync Library
|
|
*
|
|
* A program for downloading and uploading files over ssh.
|
|
* Written for the Rangitaki blogging engine.
|
|
*
|
|
* Proudly written in C and with use of libssh (libssh.org)
|
|
*
|
|
* Version: 0.1
|
|
*
|
|
* COPYRIGHT (c) 2015 - 2016 The Rangitaki Project
|
|
* COPYRIGHT (c) 2015 - 2016 Marcel Kapfer (mmk2410)
|
|
* <marcelmichaelkapfer@yahoo.co.nz>
|
|
*
|
|
* MIT License
|
|
*
|
|
*/
|
|
|
|
#include <libssh/libssh.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "rangitaki-sync.h"
|
|
#include "dbg.h"
|
|
|
|
#define MAX_FILE_LENGTH 255
|
|
|
|
char * combine(char *begin, char *end){
|
|
char *result;
|
|
|
|
check(begin != NULL, "Begin can't be NULL.");
|
|
check(end != NULL, "End can't be NULL.");
|
|
|
|
size_t begin_length = strnlen(begin, MAX_FILE_LENGTH - 1);
|
|
size_t end_length = strnlen(end, MAX_FILE_LENGTH - 1);
|
|
|
|
assert(begin_length < MAX_FILE_LENGTH && "Got the begin too long.");
|
|
assert(end_length < MAX_FILE_LENGTH && "Got the end to long.");
|
|
|
|
result = malloc(begin_length + end_length + 1);
|
|
check(result != NULL, "Failed to define result");
|
|
|
|
strncpy(result, begin, MAX_FILE_LENGTH);
|
|
check(result != NULL, "Failed to copy the string");
|
|
|
|
strncat(result, end, MAX_FILE_LENGTH * 2);
|
|
int rc = strncmp(result, begin, MAX_FILE_LENGTH * 2);
|
|
check(rc <= 0, "Failed to concat string.");
|
|
|
|
return result;
|
|
|
|
error:
|
|
if (result) free(result);
|
|
return NULL;
|
|
}
|
|
|
|
char * getFilename(char *input)
|
|
{
|
|
check(input != NULL, "Input is NULL.");
|
|
char* string;
|
|
|
|
string = strdup(input);
|
|
|
|
int buffer[100];
|
|
int i = 0;
|
|
int j = 0;
|
|
for(i = 0; i <= strlen(string); i++) {
|
|
if(string[i] == '/') {
|
|
buffer[j] = i;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
j--;
|
|
int size = strlen(string) - buffer[j]; // Includes the \0
|
|
char *result = malloc(size);
|
|
strcpy(result, &string[buffer[j]+1]);
|
|
result[size] = '\0';
|
|
free(string);
|
|
return result;
|
|
|
|
error:
|
|
return NULL;
|
|
}
|
|
|
|
void ssh_initialize(const char *host, const char *user, const char *password,
|
|
const char *remote_dir, const char *local_dir,
|
|
const unsigned int port, ssh_data *data)
|
|
{
|
|
data->host = strdup(host);
|
|
data->user = strdup(user);
|
|
data->password = strdup(password);
|
|
data->remote_dir = strdup(remote_dir);
|
|
data->local_dir = strdup(local_dir);
|
|
data->port = port;
|
|
data->verbosity = SSH_LOG_PROTOCOL;
|
|
}
|
|
|
|
int ssh_server_auth(ssh_session ssh)
|
|
{
|
|
char *hexa;
|
|
int state;
|
|
unsigned char *hash = NULL;
|
|
size_t hlen;
|
|
ssh_key srv_pubkey;
|
|
int rc;
|
|
|
|
state = ssh_is_server_known(ssh);
|
|
|
|
rc = ssh_get_publickey(ssh, &srv_pubkey);
|
|
check(rc >= 0, "Couldn't not authenticate the server.");
|
|
|
|
rc = ssh_get_publickey_hash(srv_pubkey,
|
|
SSH_PUBLICKEY_HASH_SHA1,
|
|
&hash,
|
|
&hlen);
|
|
ssh_key_free(srv_pubkey);
|
|
check(rc >= 0, "Couldn't not authenticate the server.");
|
|
|
|
switch (state)
|
|
{
|
|
case SSH_SERVER_KNOWN_OK:
|
|
log_info("Found host key");
|
|
break;
|
|
|
|
case SSH_SERVER_KNOWN_CHANGED:
|
|
log_err("The host key of the server changed. Aborting connection.");
|
|
ssh_print_hexa("Public key hash", hash, hlen);
|
|
ssh_clean_pubkey_hash(&hash);
|
|
return -1;
|
|
|
|
case SSH_SERVER_FOUND_OTHER:
|
|
log_err("The host key for this server was not found but an other. Aborting connection.");
|
|
ssh_clean_pubkey_hash(&hash);
|
|
return -1;
|
|
|
|
case SSH_SERVER_FILE_NOT_FOUND:
|
|
log_warn("Couldn't find known hosts file.");
|
|
|
|
case SSH_SERVER_NOT_KNOWN:
|
|
hexa = ssh_get_hexa(hash, hlen);
|
|
log_info("Adding %s to known hosts.", hexa);
|
|
free(hexa);
|
|
if (ssh_write_knownhost(ssh) < 0) {
|
|
log_err("Error adding server to known hosts file: %s", strerror(errno));
|
|
ssh_clean_pubkey_hash(&hash);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
log_err("%s", ssh_get_error(ssh));
|
|
ssh_clean_pubkey_hash(&hash);
|
|
return -1;
|
|
}
|
|
|
|
ssh_clean_pubkey_hash(&hash);
|
|
return 0;
|
|
|
|
error:
|
|
ssh_clean_pubkey_hash(&hash);
|
|
return -1;
|
|
}
|
|
|
|
ssh_session ssh_open(ssh_data *data)
|
|
{
|
|
int rc = 0;
|
|
ssh_session ssh = ssh_new();
|
|
|
|
if(ssh == NULL) {
|
|
log_err("Failed th create ssh session.");
|
|
return NULL;
|
|
}
|
|
|
|
if(ssh_options_set(ssh, SSH_OPTIONS_HOST, data->host) < 0) {
|
|
ssh_free(ssh);
|
|
log_err("Failed to set host.");
|
|
return NULL;
|
|
}
|
|
|
|
if(ssh_options_set(ssh, SSH_OPTIONS_USER, data->user) < 0) {
|
|
ssh_free(ssh);
|
|
log_err("Failed to set user.");
|
|
return NULL;
|
|
}
|
|
|
|
if(ssh_options_set(ssh, SSH_OPTIONS_PORT, &data->port)) {
|
|
ssh_free(ssh);
|
|
log_err("Failed to set port.");
|
|
return NULL;
|
|
}
|
|
|
|
if(ssh_options_set(ssh, SSH_OPTIONS_LOG_VERBOSITY, &data->verbosity)) {
|
|
ssh_free(ssh);
|
|
log_err("Failed to set verbosity.");
|
|
return NULL;
|
|
}
|
|
|
|
if(ssh_connect(ssh)) {
|
|
log_err("Connection failed: %s.", ssh_get_error(ssh));
|
|
ssh_disconnect(ssh);
|
|
ssh_free(ssh);
|
|
return NULL;
|
|
}
|
|
|
|
if(ssh_server_auth < 0) {
|
|
log_err("Couldn't authenticate the server.");
|
|
ssh_disconnect(ssh);
|
|
ssh_free(ssh);
|
|
return NULL;
|
|
}
|
|
|
|
rc = ssh_userauth_password(ssh, data->user, data->password);
|
|
if(rc == SSH_AUTH_SUCCESS) {
|
|
return ssh;
|
|
} else if(rc == SSH_AUTH_DENIED) {
|
|
log_err("Authentication failed.");
|
|
} else {
|
|
log_err("Error while authenticating");
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
int ssh_close(ssh_session ssh)
|
|
{
|
|
ssh_disconnect(ssh);
|
|
ssh_free(ssh);
|
|
return 0;
|
|
}
|
|
|
|
ssh_scp scp_open(ssh_session ssh, ssh_data *data, int mode)
|
|
{
|
|
int rc = 0;
|
|
ssh_scp scp = ssh_scp_new(ssh, mode, data->remote_dir);
|
|
check(scp != NULL, "Failed to create the scp session: %s", ssh_get_error(ssh));
|
|
|
|
rc = ssh_scp_init(scp);
|
|
if(rc == -1)
|
|
{
|
|
log_err("Failed to initialize the scp connection.");
|
|
goto error;
|
|
}
|
|
check(rc == SSH_OK, "Failed to initialize the scp session: %s", ssh_get_error(ssh));
|
|
|
|
return scp;
|
|
|
|
error:
|
|
if(scp) ssh_scp_free(scp);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
int scp_close(ssh_scp scp)
|
|
{
|
|
ssh_scp_close(scp);
|
|
ssh_scp_free(scp);
|
|
return 0;
|
|
}
|
|
|
|
int scp_download(ssh_session ssh, ssh_scp scp, ssh_data *data)
|
|
{
|
|
int rc;
|
|
int rv;
|
|
int size, mode;
|
|
char buffer[16384];
|
|
char *local_dir = strdup(data->local_dir);
|
|
|
|
do {
|
|
rc = ssh_scp_pull_request(scp);
|
|
|
|
switch (rc) {
|
|
case SSH_SCP_REQUEST_NEWFILE:
|
|
size = ssh_scp_request_get_size(scp);
|
|
mode = ssh_scp_request_get_permissions(scp);
|
|
char *filename = strdup(ssh_scp_request_get_filename(scp));
|
|
check(filename != NULL, "Couldn't get the filename.");
|
|
printf("DOWNLOAD: %s %d %d\n", filename, size, mode);
|
|
char *dir = combine(local_dir, filename);
|
|
FILE *output = fopen(dir, "w+");
|
|
check(output != NULL, "FILE couldn't be created");
|
|
ssh_scp_accept_request(scp);
|
|
rc = ssh_scp_read(scp, buffer, sizeof(buffer));
|
|
check(rc != SSH_ERROR, "Error downloading the file. %s",
|
|
ssh_get_error(ssh));
|
|
rv = fwrite(buffer, size, 1, output);
|
|
check(rv != 0, "Failed to write file");
|
|
rv = fclose(output);
|
|
free(dir);
|
|
free(filename);
|
|
check(rv == 0, "Failed to close FILE");
|
|
printf("Done.\n");
|
|
break;
|
|
|
|
case SSH_ERROR:
|
|
log_err("Error: %s", ssh_get_error(ssh));
|
|
goto error;
|
|
|
|
case SSH_SCP_REQUEST_WARNING:
|
|
log_warn("Warning: %s", ssh_scp_request_get_warning(scp));
|
|
break;
|
|
|
|
case SSH_SCP_REQUEST_NEWDIR:
|
|
filename = strdup(ssh_scp_request_get_filename(scp));
|
|
mode = ssh_scp_request_get_permissions(scp);
|
|
printf("DOWNLOAD: %s %d\n", filename, mode);
|
|
ssh_scp_accept_request(scp);
|
|
break;
|
|
|
|
case SSH_SCP_REQUEST_ENDDIR:
|
|
break;
|
|
|
|
case SSH_SCP_REQUEST_EOF:
|
|
log_info("DONE");
|
|
return 0;
|
|
|
|
}
|
|
} while (1);
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
int scp_upload(ssh_session ssh, ssh_scp scp, ssh_data *data)
|
|
{
|
|
int rc = 0;
|
|
FILE *fisz = fopen(data->local_dir, "rb");
|
|
fseek(fisz, 0L, SEEK_END);
|
|
int size = ftell(fisz);
|
|
fclose(fisz);
|
|
|
|
rc = ssh_scp_push_file64(scp, getFilename(data->local_dir), size,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
|
|
check(rc == SSH_OK, "Failed to create file: %s", ssh_get_error(ssh));
|
|
|
|
char *buffer = malloc(size + 100);
|
|
FILE *file = fopen(data->local_dir, "r");
|
|
check(file != NULL, "Error opening local file.");
|
|
rc = 0;
|
|
rc = fread(buffer, size, 1, file);
|
|
rc = ssh_scp_write(scp, buffer, size);
|
|
check(rc == SSH_OK, "Error while writing remote file: %s", ssh_get_error(ssh));
|
|
|
|
return 0;
|
|
|
|
error:
|
|
return -1;
|
|
}
|