📄 sftp-server.c
字号:
/* * Copyright (c) 2000, 2001, 2002 Markus Friedl. 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. */#include "includes.h"RCSID("$OpenBSD: sftp-server.c,v 1.35 2002/06/06 17:30:11 markus Exp $");#include "buffer.h"#include "bufaux.h"#include "getput.h"#include "log.h"#include "xmalloc.h"#include "sftp.h"#include "sftp-common.h"/* helper */#define get_int64() buffer_get_int64(&iqueue);#define get_int() buffer_get_int(&iqueue);#define get_string(lenp) buffer_get_string(&iqueue, lenp);#define TRACE debug#ifdef HAVE___PROGNAMEextern char *__progname;#elsechar *__progname;#endif/* input and output queue */Buffer iqueue;Buffer oqueue;/* Version of client */int version;/* portable attibutes, etc. */typedef struct Stat Stat;struct Stat { char *name; char *long_name; Attrib attrib;};static interrno_to_portable(int unixerrno){ int ret = 0; switch (unixerrno) { case 0: ret = SSH2_FX_OK; break; case ENOENT: case ENOTDIR: case EBADF: case ELOOP: ret = SSH2_FX_NO_SUCH_FILE; break; case EPERM: case EACCES: case EFAULT: ret = SSH2_FX_PERMISSION_DENIED; break; case ENAMETOOLONG: case EINVAL: ret = SSH2_FX_BAD_MESSAGE; break; default: ret = SSH2_FX_FAILURE; break; } return ret;}static intflags_from_portable(int pflags){ int flags = 0; if ((pflags & SSH2_FXF_READ) && (pflags & SSH2_FXF_WRITE)) { flags = O_RDWR; } else if (pflags & SSH2_FXF_READ) { flags = O_RDONLY; } else if (pflags & SSH2_FXF_WRITE) { flags = O_WRONLY; } if (pflags & SSH2_FXF_CREAT) flags |= O_CREAT; if (pflags & SSH2_FXF_TRUNC) flags |= O_TRUNC; if (pflags & SSH2_FXF_EXCL) flags |= O_EXCL; return flags;}static Attrib *get_attrib(void){ return decode_attrib(&iqueue);}/* handle handles */typedef struct Handle Handle;struct Handle { int use; DIR *dirp; int fd; char *name;};enum { HANDLE_UNUSED, HANDLE_DIR, HANDLE_FILE};Handle handles[100];static voidhandle_init(void){ int i; for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) handles[i].use = HANDLE_UNUSED;}static inthandle_new(int use, char *name, int fd, DIR *dirp){ int i; for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { if (handles[i].use == HANDLE_UNUSED) { handles[i].use = use; handles[i].dirp = dirp; handles[i].fd = fd; handles[i].name = name; return i; } } return -1;}static inthandle_is_ok(int i, int type){ return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type;}static inthandle_to_string(int handle, char **stringp, int *hlenp){ if (stringp == NULL || hlenp == NULL) return -1; *stringp = xmalloc(sizeof(int32_t)); PUT_32BIT(*stringp, handle); *hlenp = sizeof(int32_t); return 0;}static inthandle_from_string(char *handle, u_int hlen){ int val; if (hlen != sizeof(int32_t)) return -1; val = GET_32BIT(handle); if (handle_is_ok(val, HANDLE_FILE) || handle_is_ok(val, HANDLE_DIR)) return val; return -1;}static char *handle_to_name(int handle){ if (handle_is_ok(handle, HANDLE_DIR)|| handle_is_ok(handle, HANDLE_FILE)) return handles[handle].name; return NULL;}static DIR *handle_to_dir(int handle){ if (handle_is_ok(handle, HANDLE_DIR)) return handles[handle].dirp; return NULL;}static inthandle_to_fd(int handle){ if (handle_is_ok(handle, HANDLE_FILE)) return handles[handle].fd; return -1;}static inthandle_close(int handle){ int ret = -1; if (handle_is_ok(handle, HANDLE_FILE)) { ret = close(handles[handle].fd); handles[handle].use = HANDLE_UNUSED; } else if (handle_is_ok(handle, HANDLE_DIR)) { ret = closedir(handles[handle].dirp); handles[handle].use = HANDLE_UNUSED; } else { errno = ENOENT; } return ret;}static intget_handle(void){ char *handle; int val = -1; u_int hlen; handle = get_string(&hlen); if (hlen < 256) val = handle_from_string(handle, hlen); xfree(handle); return val;}/* send replies */static voidsend_msg(Buffer *m){ int mlen = buffer_len(m); buffer_put_int(&oqueue, mlen); buffer_append(&oqueue, buffer_ptr(m), mlen); buffer_consume(m, mlen);}static voidsend_status(u_int32_t id, u_int32_t error){ Buffer msg; const char *status_messages[] = { "Success", /* SSH_FX_OK */ "End of file", /* SSH_FX_EOF */ "No such file", /* SSH_FX_NO_SUCH_FILE */ "Permission denied", /* SSH_FX_PERMISSION_DENIED */ "Failure", /* SSH_FX_FAILURE */ "Bad message", /* SSH_FX_BAD_MESSAGE */ "No connection", /* SSH_FX_NO_CONNECTION */ "Connection lost", /* SSH_FX_CONNECTION_LOST */ "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ "Unknown error" /* Others */ }; TRACE("sent status id %d error %d", id, error); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_STATUS); buffer_put_int(&msg, id); buffer_put_int(&msg, error); if (version >= 3) { buffer_put_cstring(&msg, status_messages[MIN(error,SSH2_FX_MAX)]); buffer_put_cstring(&msg, ""); } send_msg(&msg); buffer_free(&msg);}static voidsend_data_or_handle(char type, u_int32_t id, char *data, int dlen){ Buffer msg; buffer_init(&msg); buffer_put_char(&msg, type); buffer_put_int(&msg, id); buffer_put_string(&msg, data, dlen); send_msg(&msg); buffer_free(&msg);}static voidsend_data(u_int32_t id, char *data, int dlen){ TRACE("sent data id %d len %d", id, dlen); send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);}static voidsend_handle(u_int32_t id, int handle){ char *string; int hlen; handle_to_string(handle, &string, &hlen); TRACE("sent handle id %d handle %d", id, handle); send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); xfree(string);}static voidsend_names(u_int32_t id, int count, Stat *stats){ Buffer msg; int i; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_NAME); buffer_put_int(&msg, id); buffer_put_int(&msg, count); TRACE("sent names id %d count %d", id, count); for (i = 0; i < count; i++) { buffer_put_cstring(&msg, stats[i].name); buffer_put_cstring(&msg, stats[i].long_name); encode_attrib(&msg, &stats[i].attrib); } send_msg(&msg); buffer_free(&msg);}static voidsend_attrib(u_int32_t id, Attrib *a){ Buffer msg; TRACE("sent attrib id %d have 0x%x", id, a->flags); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_ATTRS); buffer_put_int(&msg, id); encode_attrib(&msg, a); send_msg(&msg); buffer_free(&msg);}/* parse incoming */static voidprocess_init(void){ Buffer msg; version = get_int(); TRACE("client version %d", version); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_VERSION); buffer_put_int(&msg, SSH2_FILEXFER_VERSION); send_msg(&msg); buffer_free(&msg);}static voidprocess_open(void){ u_int32_t id, pflags; Attrib *a; char *name; int handle, fd, flags, mode, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); pflags = get_int(); /* portable flags */ a = get_attrib(); flags = flags_from_portable(pflags); mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode); fd = open(name, flags, mode); if (fd < 0) { status = errno_to_portable(errno); } else { handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL); if (handle < 0) { close(fd); } else { send_handle(id, handle); status = SSH2_FX_OK; } } if (status != SSH2_FX_OK) send_status(id, status); xfree(name);}static voidprocess_close(void){ u_int32_t id; int handle, ret, status = SSH2_FX_FAILURE; id = get_int(); handle = get_handle(); TRACE("close id %d handle %d", id, handle); ret = handle_close(handle); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status);}static voidprocess_read(void){ char buf[64*1024]; u_int32_t id, len; int handle, fd, ret, status = SSH2_FX_FAILURE; u_int64_t off; id = get_int(); handle = get_handle(); off = get_int64(); len = get_int(); TRACE("read id %d handle %d off %llu len %d", id, handle, (u_int64_t)off, len); if (len > sizeof buf) { len = sizeof buf; log("read change len %d", len); } fd = handle_to_fd(handle); if (fd >= 0) { if (lseek(fd, off, SEEK_SET) < 0) { error("process_read: seek failed"); status = errno_to_portable(errno); } else { ret = read(fd, buf, len); if (ret < 0) { status = errno_to_portable(errno); } else if (ret == 0) { status = SSH2_FX_EOF; } else { send_data(id, buf, ret); status = SSH2_FX_OK; } } } if (status != SSH2_FX_OK) send_status(id, status);}static voidprocess_write(void){ u_int32_t id; u_int64_t off; u_int len; int handle, fd, ret, status = SSH2_FX_FAILURE; char *data; id = get_int(); handle = get_handle(); off = get_int64(); data = get_string(&len); TRACE("write id %d handle %d off %llu len %d", id, handle, (u_int64_t)off, len); fd = handle_to_fd(handle); if (fd >= 0) { if (lseek(fd, off, SEEK_SET) < 0) { status = errno_to_portable(errno); error("process_write: seek failed"); } else {/* XXX ATOMICIO ? */ ret = write(fd, data, len); if (ret == -1) { error("process_write: write failed"); status = errno_to_portable(errno); } else if (ret == len) { status = SSH2_FX_OK; } else { log("nothing at all written"); } } } send_status(id, status); xfree(data);}static voidprocess_do_stat(int do_lstat){ Attrib a; struct stat st; u_int32_t id; char *name; int ret, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name); ret = do_lstat ? lstat(name, &st) : stat(name, &st); if (ret < 0) { status = errno_to_portable(errno); } else { stat_to_attrib(&st, &a); send_attrib(id, &a); status = SSH2_FX_OK; } if (status != SSH2_FX_OK) send_status(id, status); xfree(name);}static voidprocess_stat(void){ process_do_stat(0);}static voidprocess_lstat(void){ process_do_stat(1);}static voidprocess_fstat(void){ Attrib a; struct stat st; u_int32_t id; int fd, ret, handle, status = SSH2_FX_FAILURE; id = get_int(); handle = get_handle(); TRACE("fstat id %d handle %d", id, handle); fd = handle_to_fd(handle); if (fd >= 0) { ret = fstat(fd, &st); if (ret < 0) { status = errno_to_portable(errno); } else { stat_to_attrib(&st, &a); send_attrib(id, &a); status = SSH2_FX_OK; } } if (status != SSH2_FX_OK) send_status(id, status);}static struct timeval *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -