mirror of
https://github.com/libssh2/libssh2.git
synced 2025-04-19 02:24:04 +03:00
- kex: drop unused assigment. - knownhost: error when salt is NULL. - mbedtls: avoid unnecessary inline assigments, that were ignored for the second block and replaceable with a `ret = 0` initialization for the first one. - mbedtls: fix ignoring an API failure and ending up calling `mbedtls_rsa_check_privkey()` unconditionally. - misc: initialize datalen on error in `_libssh2_base64_decode()`. - openssl: drop unused assigments. - openssl: fix unused static function. - packet: avoid NULL deref. - packet: avoid NULL in `memcpy` src. - publickey: optimize struct layout to avoid padding. - sftp: replace ignored `rc` error assigment with `_libssh2_error()` call. - transport: fix potential NULL ptr dereferences. - transport: silence uninitialized value warnings. - userauth: drop unused assigment. - userauth: possible use of unitialized pointer. - userauth: replace `rewind()` with `fseek()`. `rewind()` returns an error condition in `errno`. `errno` is problematic and reduces portability. Use `fseek()` to avoid it. - userauth: replace potential NULL deref by returning error from `sign_frommemory()`. Possible false positive. `rc` should be set upstream if the callback is NULL. - userauth: replace potential NULL deref by returning error from `sign_fromfile()`. clang-tidy did not warn about this one, but let's match `sign_frommemory()` anyway. - wincng: fix potentially unused macros. - wincng: make sure bignum is not NULL before use. tests: - openssh_fixture: drop unused assignment. - session_fixture: exit if `username` not set, to avoid `strlen(NULL)`. - session_fixture: replace `rewind()` with `fseek()`. `rewind()` returns an error condition in `errno`. `errno` is problematic and reduces portability. Use `fseek()` to avoid it. - test_read: exit if `username` not set, to avoid `strlen(NULL)`. examples: - scp_write_nonblock: fix file handle leak. - sftp_write_nonblock: file handle leak on error. - sftp_write_sliding: file handle leak on error. - ssh2_agent_forwarding: fix unused error codes. Details in the subcommits under the PR. Thanks-to: Michael Buckley Thanks-to: Will Cosgrove Closes #1561
300 lines
7.9 KiB
C
300 lines
7.9 KiB
C
/* Copyright (C) The libssh2 project and its contributors.
|
|
*
|
|
* Sample showing how to do an SCP non-blocking upload transfer.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "libssh2_setup.h"
|
|
#include <libssh2.h>
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <time.h> /* for time() */
|
|
|
|
static const char *pubkey = "/home/username/.ssh/id_rsa.pub";
|
|
static const char *privkey = "/home/username/.ssh/id_rsa";
|
|
static const char *username = "username";
|
|
static const char *password = "password";
|
|
static const char *loclfile = "scp_write.c";
|
|
static const char *scppath = "/tmp/TEST";
|
|
|
|
static int waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session)
|
|
{
|
|
struct timeval timeout;
|
|
int rc;
|
|
fd_set fd;
|
|
fd_set *writefd = NULL;
|
|
fd_set *readfd = NULL;
|
|
int dir;
|
|
|
|
timeout.tv_sec = 10;
|
|
timeout.tv_usec = 0;
|
|
|
|
FD_ZERO(&fd);
|
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
|
#endif
|
|
FD_SET(socket_fd, &fd);
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
/* now make sure we wait in the correct direction */
|
|
dir = libssh2_session_block_directions(session);
|
|
|
|
if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
|
|
readfd = &fd;
|
|
|
|
if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
|
|
writefd = &fd;
|
|
|
|
rc = select((int)(socket_fd + 1), readfd, writefd, NULL, &timeout);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
uint32_t hostaddr;
|
|
libssh2_socket_t sock;
|
|
int i, auth_pw = 1;
|
|
struct sockaddr_in sin;
|
|
const char *fingerprint;
|
|
int rc;
|
|
LIBSSH2_SESSION *session = NULL;
|
|
LIBSSH2_CHANNEL *channel;
|
|
FILE *local;
|
|
char mem[1024 * 100];
|
|
size_t nread;
|
|
char *ptr;
|
|
struct stat fileinfo;
|
|
time_t start;
|
|
libssh2_struct_stat_size total = 0;
|
|
int duration;
|
|
size_t prev;
|
|
|
|
#ifdef _WIN32
|
|
WSADATA wsadata;
|
|
|
|
rc = WSAStartup(MAKEWORD(2, 0), &wsadata);
|
|
if(rc) {
|
|
fprintf(stderr, "WSAStartup failed with error: %d\n", rc);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
if(argc > 1) {
|
|
hostaddr = inet_addr(argv[1]);
|
|
}
|
|
else {
|
|
hostaddr = htonl(0x7F000001);
|
|
}
|
|
if(argc > 2) {
|
|
username = argv[2];
|
|
}
|
|
if(argc > 3) {
|
|
password = argv[3];
|
|
}
|
|
if(argc > 4) {
|
|
loclfile = argv[4];
|
|
}
|
|
if(argc > 5) {
|
|
scppath = argv[5];
|
|
}
|
|
|
|
rc = libssh2_init(0);
|
|
if(rc) {
|
|
fprintf(stderr, "libssh2 initialization failed (%d)\n", rc);
|
|
return 1;
|
|
}
|
|
|
|
local = fopen(loclfile, "rb");
|
|
if(!local) {
|
|
fprintf(stderr, "Cannot open local file %s\n", loclfile);
|
|
return 1;
|
|
}
|
|
|
|
stat(loclfile, &fileinfo);
|
|
|
|
/* Ultra basic "connect to port 22 on localhost". Your code is
|
|
* responsible for creating the socket establishing the connection
|
|
*/
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if(sock == LIBSSH2_INVALID_SOCKET) {
|
|
fprintf(stderr, "failed to create socket.\n");
|
|
goto shutdown;
|
|
}
|
|
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = htons(22);
|
|
sin.sin_addr.s_addr = hostaddr;
|
|
if(connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in))) {
|
|
fprintf(stderr, "failed to connect.\n");
|
|
goto shutdown;
|
|
}
|
|
|
|
/* Create a session instance */
|
|
session = libssh2_session_init();
|
|
if(!session) {
|
|
fprintf(stderr, "Could not initialize SSH session.\n");
|
|
goto shutdown;
|
|
}
|
|
|
|
/* Since we have set non-blocking, tell libssh2 we are non-blocking */
|
|
libssh2_session_set_blocking(session, 0);
|
|
|
|
/* ... start it up. This will trade welcome banners, exchange keys,
|
|
* and setup crypto, compression, and MAC layers
|
|
*/
|
|
while((rc = libssh2_session_handshake(session, sock)) ==
|
|
LIBSSH2_ERROR_EAGAIN);
|
|
if(rc) {
|
|
fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
|
|
goto shutdown;
|
|
}
|
|
|
|
/* At this point we have not yet authenticated. The first thing to do
|
|
* is check the hostkey's fingerprint against our known hosts Your app
|
|
* may have it hard coded, may go to a file, may present it to the
|
|
* user, that's your call
|
|
*/
|
|
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
|
fprintf(stderr, "Fingerprint: ");
|
|
for(i = 0; i < 20; i++) {
|
|
fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
if(auth_pw) {
|
|
/* We could authenticate via password */
|
|
while((rc = libssh2_userauth_password(session, username, password)) ==
|
|
LIBSSH2_ERROR_EAGAIN);
|
|
if(rc) {
|
|
fprintf(stderr, "Authentication by password failed.\n");
|
|
goto shutdown;
|
|
}
|
|
}
|
|
else {
|
|
/* Or by public key */
|
|
while((rc = libssh2_userauth_publickey_fromfile(session, username,
|
|
pubkey, privkey,
|
|
password)) ==
|
|
LIBSSH2_ERROR_EAGAIN);
|
|
if(rc) {
|
|
fprintf(stderr, "Authentication by public key failed.\n");
|
|
goto shutdown;
|
|
}
|
|
}
|
|
|
|
/* Send a file via scp. The mode parameter must only have permissions! */
|
|
do {
|
|
channel = libssh2_scp_send64(session, scppath, fileinfo.st_mode & 0777,
|
|
fileinfo.st_size, 0, 0);
|
|
|
|
if(!channel &&
|
|
libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) {
|
|
char *err_msg;
|
|
|
|
libssh2_session_last_error(session, &err_msg, NULL, 0);
|
|
fprintf(stderr, "%s\n", err_msg);
|
|
goto shutdown;
|
|
}
|
|
} while(!channel);
|
|
|
|
fprintf(stderr, "SCP session waiting to send file\n");
|
|
start = time(NULL);
|
|
do {
|
|
nread = fread(mem, 1, sizeof(mem), local);
|
|
if(nread <= 0) {
|
|
/* end of file */
|
|
break;
|
|
}
|
|
ptr = mem;
|
|
|
|
total += (libssh2_struct_stat_size)nread;
|
|
|
|
prev = 0;
|
|
do {
|
|
ssize_t nwritten;
|
|
while((nwritten = libssh2_channel_write(channel, ptr, nread)) ==
|
|
LIBSSH2_ERROR_EAGAIN) {
|
|
waitsocket(sock, session);
|
|
prev = 0;
|
|
}
|
|
if(nwritten < 0) {
|
|
fprintf(stderr, "ERROR %ld total %ld / %lu prev %lu\n",
|
|
(long)nwritten, (long)total,
|
|
(unsigned long)nread, (unsigned long)prev);
|
|
break;
|
|
}
|
|
else {
|
|
prev = nread;
|
|
|
|
/* nwritten indicates how many bytes were written this time */
|
|
ptr += nwritten;
|
|
nread -= (size_t)nwritten;
|
|
}
|
|
} while(nread);
|
|
} while(!nread); /* only continue if nread was drained */
|
|
|
|
duration = (int)(time(NULL) - start);
|
|
|
|
fprintf(stderr, "%ld bytes in %d seconds makes %.1f bytes/sec\n",
|
|
(long)total, duration, (double)total / duration);
|
|
|
|
fprintf(stderr, "Sending EOF\n");
|
|
while(libssh2_channel_send_eof(channel) == LIBSSH2_ERROR_EAGAIN);
|
|
|
|
fprintf(stderr, "Waiting for EOF\n");
|
|
while(libssh2_channel_wait_eof(channel) == LIBSSH2_ERROR_EAGAIN);
|
|
|
|
fprintf(stderr, "Waiting for channel to close\n");
|
|
while(libssh2_channel_wait_closed(channel) == LIBSSH2_ERROR_EAGAIN);
|
|
|
|
libssh2_channel_free(channel);
|
|
channel = NULL;
|
|
|
|
shutdown:
|
|
|
|
fclose(local);
|
|
|
|
if(session) {
|
|
while(libssh2_session_disconnect(session, "Normal Shutdown") ==
|
|
LIBSSH2_ERROR_EAGAIN);
|
|
libssh2_session_free(session);
|
|
}
|
|
|
|
if(sock != LIBSSH2_INVALID_SOCKET) {
|
|
shutdown(sock, 2);
|
|
LIBSSH2_SOCKET_CLOSE(sock);
|
|
}
|
|
|
|
fprintf(stderr, "all done\n");
|
|
|
|
libssh2_exit();
|
|
|
|
#ifdef _WIN32
|
|
WSACleanup();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|