sftp-client.c revision 4bc0a2ef2b7ba50a7a717e7ddbf31472ad28e358
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2001,2002 Damien Miller. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* XXX: memleaks */
/* XXX: signed vs unsigned */
/* XXX: remove all logging, only return status codes */
/* XXX: copy between two remote sites */
#include "includes.h"
RCSID("$OpenBSD: sftp-client.c,v 1.35 2002/09/11 22:41:49 djm Exp $");
#pragma ident "%Z%%M% %I% %E% SMI"
#include "sys-queue.h"
#include "buffer.h"
#include "bufaux.h"
#include "getput.h"
#include "xmalloc.h"
#include "log.h"
#include "atomicio.h"
#include "sftp.h"
#include "sftp-common.h"
#include "sftp-client.h"
/* Minimum amount of data to read at at time */
#define MIN_READ_SIZE 512
struct sftp_conn {
int fd_in;
int fd_out;
};
static void
{
int mlen = buffer_len(m);
int len;
buffer_consume(m, mlen);
if (len <= 0)
}
static void
{
unsigned char buf[4096];
if (len == 0)
fatal("Connection closed");
while (msg_len) {
if (len == 0)
fatal("Connection closed");
}
}
static void
{
buffer_init(&msg);
buffer_free(&msg);
}
static void
{
buffer_init(&msg);
encode_attrib(&msg, a);
buffer_free(&msg);
}
static u_int
{
buffer_init(&msg);
if (id != expected_id)
if (type != SSH2_FXP_STATUS)
fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
buffer_free(&msg);
return(status);
}
static char *
{
char *handle;
buffer_init(&msg);
if (id != expected_id)
if (type == SSH2_FXP_STATUS) {
return(NULL);
} else if (type != SSH2_FXP_HANDLE)
fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
buffer_free(&msg);
return(handle);
}
static Attrib *
{
Attrib *a;
buffer_init(&msg);
if (id != expected_id)
if (type == SSH2_FXP_STATUS) {
if (quiet)
else
return(NULL);
} else if (type != SSH2_FXP_ATTRS) {
fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
}
a = decode_attrib(&msg);
buffer_free(&msg);
return(a);
}
struct sftp_conn *
{
int version;
buffer_init(&msg);
buffer_clear(&msg);
/* Expecting a VERSION reply */
error("Invalid packet back from SSH2_FXP_INIT (type %u)",
type);
buffer_free(&msg);
return(NULL);
}
/* Check for extensions */
while (buffer_len(&msg) > 0) {
}
buffer_free(&msg);
/* Some filexfer v.0 servers don't support large packets */
if (version == 0)
return(ret);
}
{
}
int
{
buffer_init(&msg);
if (status != SSH2_FX_OK)
buffer_free(&msg);
return(status);
}
static int
SFTP_DIRENT ***dir)
{
char *handle;
buffer_init(&msg);
buffer_clear(&msg);
return(-1);
if (dir) {
ents = 0;
}
for (;;) {
int count;
buffer_clear(&msg);
buffer_clear(&msg);
if (id != expected_id)
if (type == SSH2_FXP_STATUS) {
if (status == SSH2_FX_EOF) {
break;
} else {
error("Couldn't read directory: %s",
return(status);
}
} else if (type != SSH2_FXP_NAME)
fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
if (count == 0)
break;
for (i = 0; i < count; i++) {
Attrib *a;
a = decode_attrib(&msg);
if (printflag)
if (dir) {
(ents + 2));
}
}
}
buffer_free(&msg);
return(0);
}
int
{
}
void free_sftp_dirents(SFTP_DIRENT **s)
{
int i;
for (i = 0; s[i]; i++) {
xfree(s[i]);
}
xfree(s);
}
int
{
if (status != SSH2_FX_OK)
return(status);
}
int
{
if (status != SSH2_FX_OK)
return(status);
}
int
{
if (status != SSH2_FX_OK)
return(status);
}
Attrib *
{
}
Attrib *
{
if (quiet)
debug("Server version does not support lstat operation");
else
log("Server version does not support lstat operation");
}
}
#if 0
Attrib *
{
}
#endif
int
{
if (status != SSH2_FX_OK)
return(status);
}
int
Attrib *a)
{
handle_len, a);
if (status != SSH2_FX_OK)
return(status);
}
char *
{
buffer_init(&msg);
if (id != expected_id)
if (type == SSH2_FXP_STATUS) {
return(NULL);
} else if (type != SSH2_FXP_NAME)
fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
if (count != 1)
(void) decode_attrib(&msg);
buffer_free(&msg);
return(filename);
}
int
{
buffer_init(&msg);
/* Send rename request */
newpath);
buffer_free(&msg);
if (status != SSH2_FX_OK)
return(status);
}
int
{
error("This server does not support the symlink operation");
return(SSH2_FX_OP_UNSUPPORTED);
}
buffer_init(&msg);
/* Send rename request */
newpath);
buffer_free(&msg);
if (status != SSH2_FX_OK)
return(status);
}
#if 0
char *
{
buffer_init(&msg);
if (id != expected_id)
if (type == SSH2_FXP_STATUS) {
return(NULL);
} else if (type != SSH2_FXP_NAME)
fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
if (count != 1)
(void) decode_attrib(&msg);
buffer_free(&msg);
return(filename);
}
#endif
static void
{
buffer_init(&msg);
buffer_clear(&msg);
buffer_free(&msg);
}
int
int pflag)
{
char *handle;
int read_error, write_errno;
struct request {
};
if (a == NULL)
return(-1);
/* XXX: should we preserve set[ug]id? */
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
else
mode = 0666;
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
return(-1);
}
if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
else
size = 0;
buffer_init(&msg);
/* Send open request */
buffer_free(&msg);
return(-1);
}
if (local_fd == -1) {
error("Couldn't open local file \"%s\" for writing: %s",
buffer_free(&msg);
return(-1);
}
/* Read from remote and write to local */
max_req = 1;
char *data;
/* Send some more requests */
debug3("Request range %llu -> %llu (%d/%d)",
(unsigned long long)offset,
num_req++;
}
buffer_clear(&msg);
/* Find the request in our queue */
;
switch (type) {
case SSH2_FXP_STATUS:
if (status != SSH2_FX_EOF)
read_error = 1;
max_req = 0;
num_req--;
break;
case SSH2_FXP_DATA:
debug3("Received data %llu -> %llu",
fatal("Received more data than asked for "
!write_error) {
write_errno = errno;
write_error = 1;
max_req = 0;
}
num_req--;
} else {
/* Resend the request for the missing data */
debug3("Short data block, re-requesting "
"%llu -> %llu (%2d)",
/* Reduce the request size */
}
if (max_req > 0) { /* max_req = 0 iff EOF received */
/* Only one request at a time
* after the expected EOF */
debug3("Finish at %llu (%2d)",
(unsigned long long)offset,
num_req);
max_req = 1;
}
++max_req;
}
}
break;
default:
fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
}
}
/* Sanity check */
fatal("Transfer complete, but requests still in queue");
if (read_error) {
error("Couldn't read from remote file \"%s\" : %s",
} else if (write_error) {
status = -1;
} else {
/* Override umask and utimes if asked */
#ifdef HAVE_FCHMOD
#else
#endif /* HAVE_FCHMOD */
error("Can't set times on \"%s\": %s",
}
}
buffer_free(&msg);
return(status);
}
int
int pflag)
{
Attrib a;
struct outstanding_ack {
};
struct outstanding_ack *ack;
TAILQ_INIT(&acks);
error("Couldn't open local file \"%s\" for reading: %s",
return(-1);
}
error("Couldn't fstat local file \"%s\": %s",
return(-1);
}
stat_to_attrib(&sb, &a);
a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
a.perm &= 0777;
if (!pflag)
buffer_init(&msg);
/* Send open request */
encode_attrib(&msg, &a);
buffer_clear(&msg);
buffer_free(&msg);
return(-1);
}
/* Read from local and write to remote */
offset = 0;
for (;;) {
int len;
/*
* Can't use atomicio here because it returns 0 on EOF, thus losing
* the last block of the file
*/
do
if (len == -1)
if (len != 0) {
buffer_clear(&msg);
debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
break;
buffer_clear(&msg);
if (type != SSH2_FXP_STATUS)
fatal("Expected SSH2_FXP_STATUS(%d) packet, "
/* Find the request in our queue */
;
if (status != SSH2_FX_OK) {
error("Couldn't write to remote file \"%s\": %s",
goto done;
}
debug3("In write loop, ack for %u %u bytes at %llu",
++ackid;
}
}
status = -1;
goto done;
}
/* Override umask and utimes if asked */
if (pflag)
done:
buffer_free(&msg);
return(status);
}