📄 sftp-client.c
字号:
/* * 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 * documentation and/or other materials provided with the distribution. * * 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.32 2002/06/09 13:32:01 markus Exp $");#include "openbsd-compat/fake-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 512struct sftp_conn { int fd_in; int fd_out; u_int transfer_buflen; u_int num_requests; u_int version; u_int msg_id;};static voidsend_msg(int fd, Buffer *m){ int mlen = buffer_len(m); int len; Buffer oqueue; buffer_init(&oqueue); buffer_put_int(&oqueue, mlen); buffer_append(&oqueue, buffer_ptr(m), mlen); buffer_consume(m, mlen); len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue)); if (len <= 0) fatal("Couldn't send packet: %s", strerror(errno)); buffer_free(&oqueue);}static voidget_msg(int fd, Buffer *m){ u_int len, msg_len; unsigned char buf[4096]; len = atomicio(read, fd, buf, 4); if (len == 0) fatal("Connection closed"); else if (len == -1) fatal("Couldn't read packet: %s", strerror(errno)); msg_len = GET_32BIT(buf); if (msg_len > 256 * 1024) fatal("Received message too long %d", msg_len); while (msg_len) { len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf))); if (len == 0) fatal("Connection closed"); else if (len == -1) fatal("Couldn't read packet: %s", strerror(errno)); msg_len -= len; buffer_append(m, buf, len); }}static voidsend_string_request(int fd, u_int id, u_int code, char *s, u_int len){ Buffer msg; buffer_init(&msg); buffer_put_char(&msg, code); buffer_put_int(&msg, id); buffer_put_string(&msg, s, len); send_msg(fd, &msg); debug3("Sent message fd %d T:%d I:%d", fd, code, id); buffer_free(&msg);}static voidsend_string_attrs_request(int fd, u_int id, u_int code, char *s, u_int len, Attrib *a){ Buffer msg; buffer_init(&msg); buffer_put_char(&msg, code); buffer_put_int(&msg, id); buffer_put_string(&msg, s, len); encode_attrib(&msg, a); send_msg(fd, &msg); debug3("Sent message fd %d T:%d I:%d", fd, code, id); buffer_free(&msg);}static u_intget_status(int fd, int expected_id){ Buffer msg; u_int type, id, status; buffer_init(&msg); get_msg(fd, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); if (id != expected_id) fatal("ID mismatch (%d != %d)", id, expected_id); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d", SSH2_FXP_STATUS, type); status = buffer_get_int(&msg); buffer_free(&msg); debug3("SSH2_FXP_STATUS %d", status); return(status);}static char *get_handle(int fd, u_int expected_id, u_int *len){ Buffer msg; u_int type, id; char *handle; buffer_init(&msg); get_msg(fd, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); if (id != expected_id) fatal("ID mismatch (%d != %d)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); error("Couldn't get handle: %s", fx2txt(status)); return(NULL); } else if (type != SSH2_FXP_HANDLE) fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d", SSH2_FXP_HANDLE, type); handle = buffer_get_string(&msg, len); buffer_free(&msg); return(handle);}static Attrib *get_decode_stat(int fd, u_int expected_id, int quiet){ Buffer msg; u_int type, id; Attrib *a; buffer_init(&msg); get_msg(fd, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); debug3("Received stat reply T:%d I:%d", type, id); if (id != expected_id) fatal("ID mismatch (%d != %d)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); if (quiet) debug("Couldn't stat remote file: %s", fx2txt(status)); else error("Couldn't stat remote file: %s", fx2txt(status)); return(NULL); } else if (type != SSH2_FXP_ATTRS) { fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d", SSH2_FXP_ATTRS, type); } a = decode_attrib(&msg); buffer_free(&msg); return(a);}struct sftp_conn *do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests){ int type, version; Buffer msg; struct sftp_conn *ret; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_INIT); buffer_put_int(&msg, SSH2_FILEXFER_VERSION); send_msg(fd_out, &msg); buffer_clear(&msg); get_msg(fd_in, &msg); /* Expecting a VERSION reply */ if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { error("Invalid packet back from SSH2_FXP_INIT (type %d)", type); buffer_free(&msg); return(NULL); } version = buffer_get_int(&msg); debug2("Remote version: %d", version); /* Check for extensions */ while (buffer_len(&msg) > 0) { char *name = buffer_get_string(&msg, NULL); char *value = buffer_get_string(&msg, NULL); debug2("Init extension: \"%s\"", name); xfree(name); xfree(value); } buffer_free(&msg); ret = xmalloc(sizeof(*ret)); ret->fd_in = fd_in; ret->fd_out = fd_out; ret->transfer_buflen = transfer_buflen; ret->num_requests = num_requests; ret->version = version; ret->msg_id = 1; /* Some filexfer v.0 servers don't support large packets */ if (version == 0) ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); return(ret);}u_intsftp_proto_version(struct sftp_conn *conn){ return(conn->version);}intdo_close(struct sftp_conn *conn, char *handle, u_int handle_len){ u_int id, status; Buffer msg; buffer_init(&msg); id = conn->msg_id++; buffer_put_char(&msg, SSH2_FXP_CLOSE); buffer_put_int(&msg, id); buffer_put_string(&msg, handle, handle_len); send_msg(conn->fd_out, &msg); debug3("Sent message SSH2_FXP_CLOSE I:%d", id); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't close file: %s", fx2txt(status)); buffer_free(&msg); return(status);}static intdo_lsreaddir(struct sftp_conn *conn, char *path, int printflag, SFTP_DIRENT ***dir){ Buffer msg; u_int type, id, handle_len, i, expected_id, ents = 0; char *handle; id = conn->msg_id++; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_OPENDIR); buffer_put_int(&msg, id); buffer_put_cstring(&msg, path); send_msg(conn->fd_out, &msg); buffer_clear(&msg); handle = get_handle(conn->fd_in, id, &handle_len); if (handle == NULL) return(-1); if (dir) { ents = 0; *dir = xmalloc(sizeof(**dir)); (*dir)[0] = NULL; } for (;;) { int count; id = expected_id = conn->msg_id++; debug3("Sending SSH2_FXP_READDIR I:%d", id); buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_READDIR); buffer_put_int(&msg, id); buffer_put_string(&msg, handle, handle_len); send_msg(conn->fd_out, &msg); buffer_clear(&msg); get_msg(conn->fd_in, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); debug3("Received reply T:%d I:%d", type, id); if (id != expected_id) fatal("ID mismatch (%d != %d)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); debug3("Received SSH2_FXP_STATUS %d", status); if (status == SSH2_FX_EOF) { break; } else { error("Couldn't read directory: %s", fx2txt(status)); do_close(conn, handle, handle_len); return(status); } } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); if (count == 0) break; debug3("Received %d SSH2_FXP_NAME responses", count); for (i = 0; i < count; i++) { char *filename, *longname; Attrib *a; filename = buffer_get_string(&msg, NULL); longname = buffer_get_string(&msg, NULL); a = decode_attrib(&msg); if (printflag) printf("%s\n", longname); if (dir) { *dir = xrealloc(*dir, sizeof(**dir) * (ents + 2)); (*dir)[ents] = xmalloc(sizeof(***dir)); (*dir)[ents]->filename = xstrdup(filename); (*dir)[ents]->longname = xstrdup(longname); memcpy(&(*dir)[ents]->a, a, sizeof(*a)); (*dir)[++ents] = NULL; } xfree(filename); xfree(longname); } } buffer_free(&msg); do_close(conn, handle, handle_len); xfree(handle); return(0);}intdo_ls(struct sftp_conn *conn, char *path){ return(do_lsreaddir(conn, path, 1, NULL));}intdo_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir){ return(do_lsreaddir(conn, path, 0, dir));}void free_sftp_dirents(SFTP_DIRENT **s){ int i; for (i = 0; s[i]; i++) { xfree(s[i]->filename); xfree(s[i]->longname); xfree(s[i]); } xfree(s);}intdo_rm(struct sftp_conn *conn, char *path){ u_int status, id; debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path, strlen(path)); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't delete file: %s", fx2txt(status)); return(status);}intdo_mkdir(struct sftp_conn *conn, char *path, Attrib *a){ u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path, strlen(path), a); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't create directory: %s", fx2txt(status)); return(status);}intdo_rmdir(struct sftp_conn *conn, char *path){ u_int status, id; id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path, strlen(path)); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't remove directory: %s", fx2txt(status)); return(status);}Attrib *do_stat(struct sftp_conn *conn, char *path, int quiet){ u_int id; id = conn->msg_id++; send_string_request(conn->fd_out, id, conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, path, strlen(path)); return(get_decode_stat(conn->fd_in, id, quiet));}Attrib *do_lstat(struct sftp_conn *conn, char *path, int quiet){ u_int id; if (conn->version == 0) { if (quiet) debug("Server version does not support lstat operation"); else log("Server version does not support lstat operation"); return(do_stat(conn, path, quiet)); } id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path, strlen(path)); return(get_decode_stat(conn->fd_in, id, quiet));}Attrib *do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet){ u_int id; id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle, handle_len); return(get_decode_stat(conn->fd_in, id, quiet));}intdo_setstat(struct sftp_conn *conn, char *path, Attrib *a){ u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path, strlen(path), a); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't setstat on \"%s\": %s", path, fx2txt(status)); return(status);}intdo_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len, Attrib *a){ u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle, handle_len, a); status = get_status(conn->fd_in, id);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -