📄 sftp-server.c
字号:
attrib_to_tv(Attrib *a){ static struct timeval tv[2]; tv[0].tv_sec = a->atime; tv[0].tv_usec = 0; tv[1].tv_sec = a->mtime; tv[1].tv_usec = 0; return tv;}static voidprocess_setstat(void){ Attrib *a; u_int32_t id; char *name; int ret; int status = SSH2_FX_OK; id = get_int(); name = get_string(NULL); a = get_attrib(); TRACE("setstat id %d name %s", id, name); if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { ret = truncate(name, a->size); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { ret = chmod(name, a->perm & 0777); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { ret = utimes(name, attrib_to_tv(a)); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { ret = chown(name, a->uid, a->gid); if (ret == -1) status = errno_to_portable(errno); } send_status(id, status); xfree(name);}static voidprocess_fsetstat(void){ Attrib *a; u_int32_t id; int handle, fd, ret; int status = SSH2_FX_OK; char *name; id = get_int(); handle = get_handle(); a = get_attrib(); TRACE("fsetstat id %d handle %d", id, handle); fd = handle_to_fd(handle); name = handle_to_name(handle); if (fd < 0 || name == NULL) { status = SSH2_FX_FAILURE; } else { if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { ret = ftruncate(fd, a->size); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {#ifdef HAVE_FCHMOD ret = fchmod(fd, a->perm & 0777);#else ret = chmod(name, a->perm & 0777);#endif if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {#ifdef HAVE_FUTIMES ret = futimes(fd, attrib_to_tv(a));#else ret = utimes(name, attrib_to_tv(a));#endif if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {#ifdef HAVE_FCHOWN ret = fchown(fd, a->uid, a->gid);#else ret = chown(name, a->uid, a->gid);#endif if (ret == -1) status = errno_to_portable(errno); } } send_status(id, status);}static voidprocess_opendir(void){ DIR *dirp = NULL; char *path; int handle, status = SSH2_FX_FAILURE; u_int32_t id; id = get_int(); path = get_string(NULL); TRACE("opendir id %d path %s", id, path); dirp = opendir(path); if (dirp == NULL) { status = errno_to_portable(errno); } else { handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp); if (handle < 0) { closedir(dirp); } else { send_handle(id, handle); status = SSH2_FX_OK; } } if (status != SSH2_FX_OK) send_status(id, status); xfree(path);}/* * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh */static char *ls_file(char *name, struct stat *st){ int ulen, glen, sz = 0; struct passwd *pw; struct group *gr; struct tm *ltime = localtime(&st->st_mtime); char *user, *group; char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; strmode(st->st_mode, mode); if ((pw = getpwuid(st->st_uid)) != NULL) { user = pw->pw_name; } else { snprintf(ubuf, sizeof ubuf, "%d", st->st_uid); user = ubuf; } if ((gr = getgrgid(st->st_gid)) != NULL) { group = gr->gr_name; } else { snprintf(gbuf, sizeof gbuf, "%d", st->st_gid); group = gbuf; } if (ltime != NULL) { if (time(NULL) - st->st_mtime < (365*24*60*60)/2) sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); else sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); } if (sz == 0) tbuf[0] = '\0'; ulen = MAX(strlen(user), 8); glen = MAX(strlen(group), 8); snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode, st->st_nlink, ulen, user, glen, group, (u_int64_t)st->st_size, tbuf, name); return xstrdup(buf);}static voidprocess_readdir(void){ DIR *dirp; struct dirent *dp; char *path; int handle; u_int32_t id; id = get_int(); handle = get_handle(); TRACE("readdir id %d handle %d", id, handle); dirp = handle_to_dir(handle); path = handle_to_name(handle); if (dirp == NULL || path == NULL) { send_status(id, SSH2_FX_FAILURE); } else { struct stat st; char pathname[1024]; Stat *stats; int nstats = 10, count = 0, i; stats = xmalloc(nstats * sizeof(Stat)); while ((dp = readdir(dirp)) != NULL) { if (count >= nstats) { nstats *= 2; stats = xrealloc(stats, nstats * sizeof(Stat)); }/* XXX OVERFLOW ? */ snprintf(pathname, sizeof pathname, "%s%s%s", path, strcmp(path, "/") ? "/" : "", dp->d_name); if (lstat(pathname, &st) < 0) continue; stat_to_attrib(&st, &(stats[count].attrib)); stats[count].name = xstrdup(dp->d_name); stats[count].long_name = ls_file(dp->d_name, &st); count++; /* send up to 100 entries in one message */ /* XXX check packet size instead */ if (count == 100) break; } if (count > 0) { send_names(id, count, stats); for (i = 0; i < count; i++) { xfree(stats[i].name); xfree(stats[i].long_name); } } else { send_status(id, SSH2_FX_EOF); } xfree(stats); }}static voidprocess_remove(void){ char *name; u_int32_t id; int status = SSH2_FX_FAILURE; int ret; id = get_int(); name = get_string(NULL); TRACE("remove id %d name %s", id, name); ret = unlink(name); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); xfree(name);}static voidprocess_mkdir(void){ Attrib *a; u_int32_t id; char *name; int ret, mode, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); a = get_attrib(); mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm & 0777 : 0777; TRACE("mkdir id %d name %s mode 0%o", id, name, mode); ret = mkdir(name, mode); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); xfree(name);}static voidprocess_rmdir(void){ u_int32_t id; char *name; int ret, status; id = get_int(); name = get_string(NULL); TRACE("rmdir id %d name %s", id, name); ret = rmdir(name); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); xfree(name);}static voidprocess_realpath(void){ char resolvedname[MAXPATHLEN]; u_int32_t id; char *path; id = get_int(); path = get_string(NULL); if (path[0] == '\0') { xfree(path); path = xstrdup("."); } TRACE("realpath id %d path %s", id, path); if (realpath(path, resolvedname) == NULL) { send_status(id, errno_to_portable(errno)); } else { Stat s; attrib_clear(&s.attrib); s.name = s.long_name = resolvedname; send_names(id, 1, &s); } xfree(path);}static voidprocess_rename(void){ u_int32_t id; struct stat st; char *oldpath, *newpath; int ret, status = SSH2_FX_FAILURE; id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); TRACE("rename id %d old %s new %s", id, oldpath, newpath); /* fail if 'newpath' exists */ if (stat(newpath, &st) == -1) { ret = rename(oldpath, newpath); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; } send_status(id, status); xfree(oldpath); xfree(newpath);}static voidprocess_readlink(void){ u_int32_t id; int len; char link[MAXPATHLEN]; char *path; id = get_int(); path = get_string(NULL); TRACE("readlink id %d path %s", id, path); if ((len = readlink(path, link, sizeof(link) - 1)) == -1) send_status(id, errno_to_portable(errno)); else { Stat s; link[len] = '\0'; attrib_clear(&s.attrib); s.name = s.long_name = link; send_names(id, 1, &s); } xfree(path);}static voidprocess_symlink(void){ u_int32_t id; struct stat st; char *oldpath, *newpath; int ret, status = SSH2_FX_FAILURE; id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); TRACE("symlink id %d old %s new %s", id, oldpath, newpath); /* fail if 'newpath' exists */ if (stat(newpath, &st) == -1) { ret = symlink(oldpath, newpath); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; } send_status(id, status); xfree(oldpath); xfree(newpath);}static voidprocess_extended(void){ u_int32_t id; char *request; id = get_int(); request = get_string(NULL); send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ xfree(request);}/* stolen from ssh-agent */static voidprocess(void){ u_int msg_len; u_int buf_len; u_int consumed; u_int type; u_char *cp; buf_len = buffer_len(&iqueue); if (buf_len < 5) return; /* Incomplete message. */ cp = buffer_ptr(&iqueue); msg_len = GET_32BIT(cp); if (msg_len > 256 * 1024) { error("bad message "); exit(11); } if (buf_len < msg_len + 4) return; buffer_consume(&iqueue, 4); buf_len -= 4; type = buffer_get_char(&iqueue); switch (type) { case SSH2_FXP_INIT: process_init(); break; case SSH2_FXP_OPEN: process_open(); break; case SSH2_FXP_CLOSE: process_close(); break; case SSH2_FXP_READ: process_read(); break; case SSH2_FXP_WRITE: process_write(); break; case SSH2_FXP_LSTAT: process_lstat(); break; case SSH2_FXP_FSTAT: process_fstat(); break; case SSH2_FXP_SETSTAT: process_setstat(); break; case SSH2_FXP_FSETSTAT: process_fsetstat(); break; case SSH2_FXP_OPENDIR: process_opendir(); break; case SSH2_FXP_READDIR: process_readdir(); break; case SSH2_FXP_REMOVE: process_remove(); break; case SSH2_FXP_MKDIR: process_mkdir(); break; case SSH2_FXP_RMDIR: process_rmdir(); break; case SSH2_FXP_REALPATH: process_realpath(); break; case SSH2_FXP_STAT: process_stat(); break; case SSH2_FXP_RENAME: process_rename(); break; case SSH2_FXP_READLINK: process_readlink(); break; case SSH2_FXP_SYMLINK: process_symlink(); break; case SSH2_FXP_EXTENDED: process_extended(); break; default: error("Unknown message %d", type); break; } /* discard the remaining bytes from the current packet */ if (buf_len < buffer_len(&iqueue)) fatal("iqueue grows"); consumed = buf_len - buffer_len(&iqueue); if (msg_len < consumed) fatal("msg_len %d < consumed %d", msg_len, consumed); if (msg_len > consumed) buffer_consume(&iqueue, msg_len - consumed);}intmain(int ac, char **av){ fd_set *rset, *wset; int in, out, max; ssize_t len, olen, set_size; /* XXX should use getopt */ __progname = get_progname(av[0]); handle_init();#ifdef DEBUG_SFTP_SERVER log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);#endif in = dup(STDIN_FILENO); out = dup(STDOUT_FILENO);#ifdef HAVE_CYGWIN setmode(in, O_BINARY); setmode(out, O_BINARY);#endif max = 0; if (in > max) max = in; if (out > max) max = out; buffer_init(&iqueue); buffer_init(&oqueue); set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); rset = (fd_set *)xmalloc(set_size); wset = (fd_set *)xmalloc(set_size); for (;;) { memset(rset, 0, set_size); memset(wset, 0, set_size); FD_SET(in, rset); olen = buffer_len(&oqueue); if (olen > 0) FD_SET(out, wset); if (select(max+1, rset, wset, NULL, NULL) < 0) { if (errno == EINTR) continue; exit(2); } /* copy stdin to iqueue */ if (FD_ISSET(in, rset)) { char buf[4*4096]; len = read(in, buf, sizeof buf); if (len == 0) { debug("read eof"); exit(0); } else if (len < 0) { error("read error"); exit(1); } else { buffer_append(&iqueue, buf, len); } } /* send oqueue to stdout */ if (FD_ISSET(out, wset)) { len = write(out, buffer_ptr(&oqueue), olen); if (len < 0) { error("write error"); exit(1); } else { buffer_consume(&oqueue, len); } } /* process requests from client */ process(); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -