首页 > 学技术 > 技术网文 > C/C++ > 正文

[原创] 【原创】一个简单的FTP服务器的源码(CuFTPD)


来源 chinaunix.net kuqin整理

[size=2]
这两天有点空闲,于是写了一个非常简单的FTP服务器练练手。包括浏览、删除、上传、下载、转换目录等最基本的功能,用传统的多进程的方式实现。可供初学Linux网络编程的朋友参考。后续有时间我会考虑进一步完善,例如加入配置文件、权限控制、多线程、对FTP协议更完善的支持、断点续传、ASCII传输模式、限速、日志、……太多了。有兴趣的朋友,在充分理解FTP协议的基础上,也可以自己对它做完善。关于FTP协议,请参见RFC 0959。
什么都不用说了,一切尽在代码中,注释还是比较丰富的。 :)

编译:
gcc cuftpd.c -o cuftpd

运行:
./cuftpd -d -p 2121
其中-d打开调试开关,会输出大量调试信息。
-p 2121指定监听端口,默认是21。
还可以./cuftpd -h看帮助信息。



cuftpd.h
[/size]

#ifndef _CUFTPD_H
#define _CUFTPD_H

#define CUFTPD_DEBUG(fmt, ...) cuftpd_debug(__FILE__, __LINE__, fmt, __VA_ARGS__)
#define CUFTPD_ARR_LEN(arr) (sizeof(arr)/sizeof(arr[0]))

#define CUFTPD_VER "1.0"
#define CUFTPD_DEF_SRV_PORT 21
#define CUFTPD_LISTEN_QU_LEN 8
#define CUFTPD_LINE_END "\r\n"

#define CUFTPD_OK 0
#define CUFTPD_ERR (-1)

#define CUFTPD_CHECK_LOGIN()  \
do { \
if (!cuftpd_cur_user) { \
cuftpd_send_resp(ctrlfd, 530, "first please"); \
return CUFTPD_ERR; \
} \
} while(0)

struct cuftpd_cmd_struct {
char *cmd_name;
int (*cmd_handler)(int ctrlfd, char *cmd_line);
};

struct cuftpd_user_struct {
char user[128];
char pass[128];
};


#endif





 mynets 回复于:2006-11-24 21:28:51

[size=2]
cuftpd.c
[/size]


/*
 * A very simple ftp server's source code for demonstration.
 * It supports PASV/PORT modes and following operations:
 *   ls,pwd,cwd,get,put,dele.
 * I have tested it using following ftp clients:
 * 1. Windows XP's command line ftp client,
 * 2. IE 6.0,
 * 3. Redhat 9.0's ftp client,
 * 4. CuteFTP 8,
 * I'll introduce more functions and improve its performance
 * if i have enough idle time afterwards.
 * Any issues pls paste to CU.
 *
 * Change History:
 * 11/24/2006 1.0 Initial version.
 */
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdarg.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>

#include "cuftpd.h"

/* local functions declarations */
static int cuftpd_do_user(int,char*);
static int cuftpd_do_pass(int,char*);
static int cuftpd_do_pwd(int, char*);
static int cuftpd_do_cwd(int, char*);
static int cuftpd_do_syst(int,char*);
static int cuftpd_do_list(int,char*);
static int cuftpd_do_size(int,char*);
static int cuftpd_do_dele(int,char*);
static int cuftpd_do_type(int,char*);
static int cuftpd_do_port(int,char*);
static int cuftpd_do_pasv(int,char*);
static int cuftpd_do_retr(int,char*);
static int cuftpd_do_stor(int,char*);
static int cuftpd_do_quit(int,char*);

/* global variables */
int cuftpd_debug_on;
int cuftpd_srv_port = CUFTPD_DEF_SRV_PORT;
int cuftpd_cur_child_num;
int cuftpd_quit_flag;
struct cuftpd_user_struct *cuftpd_cur_user;
int cuftpd_pasv_fd = -1;
int cuftpd_pasv_connfd = -1;
int cuftpd_port_connfd = -1;
char cuftpd_home_dir[PATH_MAX];

/*
 * Currently supported ftp commands.
 */
struct cuftpd_cmd_struct cuftpd_cmds[] = {
{"USER", cuftpd_do_user},
{"PASS", cuftpd_do_pass},
{"PWD",  cuftpd_do_pwd},
{"XPWD", cuftpd_do_pwd},
{"CWD",  cuftpd_do_cwd},
{"LIST", cuftpd_do_list},
{"NLST", cuftpd_do_list},
{"SYST", cuftpd_do_syst},
{"TYPE", cuftpd_do_type},
{"SIZE", cuftpd_do_size},
{"DELE", cuftpd_do_dele},
{"RMD",  cuftpd_do_dele},
{"PORT", cuftpd_do_port},
{"PASV", cuftpd_do_pasv},
{"RETR", cuftpd_do_retr},
{"STOR", cuftpd_do_stor},
{"QUIT", cuftpd_do_quit},
{ NULL,  NULL}
};

/*
 * Anonymous users, you can add more user/pass pairs here.
 */
struct cuftpd_user_struct cuftpd_users[] = {
{"anonymous", ""},
{"ftp", ""}
};

/*
 * Ftp server's responses, and are not complete.
 */
char cuftpd_srv_resps[][256] = {
"150 Begin transfer" CUFTPD_LINE_END,
"200 OK" CUFTPD_LINE_END,
"213 %d" CUFTPD_LINE_END,
"215 UNIX Type: L8" CUFTPD_LINE_END,
"220 CuFTPD " CUFTPD_VER " Server" CUFTPD_LINE_END,
"221 Goodbye" CUFTPD_LINE_END,
"226 Transfer complete" CUFTPD_LINE_END,
"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)" CUFTPD_LINE_END,
"230 User %s logged in" CUFTPD_LINE_END,
"250 CWD command successful" CUFTPD_LINE_END,
"257 \"%s\" is current directory" CUFTPD_LINE_END,
"331 Password required for %s" CUFTPD_LINE_END,
"500 Unsupport command %s" CUFTPD_LINE_END,
"530 Login %s" CUFTPD_LINE_END,
"550 Error" CUFTPD_LINE_END
};



static void
cuftpd_debug(const char *file, int line, const char *fmt, ...)
{
va_list ap;

if (!cuftpd_debug_on)
return;

fprintf(stderr, "(%s:%d:%d)", file, line, getpid());
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}

static void
cuftpd_usage(void)
{
printf("cuftpd " CUFTPD_VER " usage:\n");
printf("cuftpd -p port -d\n");
}

static void
cuftpd_parse_args(int argc, char *argv[])
{
int opt;
int err = 0;

while ((opt = getopt(argc, argv, "p:dh")) != -1) {
switch (opt) {
case 'p':
    cuftpd_srv_port = atoi(optarg);
err = (cuftpd_srv_port < 0 || cuftpd_srv_port > 65535);    
    break;
case 'd':
    cuftpd_debug_on = 1;
    break;
default:
    err = 1;
    break;
}

if (err) {
cuftpd_usage();
exit(-1);
}
}

CUFTPD_DEBUG("srv port:%d\n", cuftpd_srv_port);
}

static void
cuftpd_sigchild(int signo)
{
int status;

if (signo != SIGCHLD)
return;

while (waitpid(-1, &status, WNOHANG) > 0) 
cuftpd_cur_child_num--;

CUFTPD_DEBUG("caught a SIGCHLD, cuftpd_cur_child_num=%d\n", cuftpd_cur_child_num);
}

static int
cuftpd_init(void)
{
/* add all init statements here */

signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, cuftpd_sigchild);
getcwd(cuftpd_home_dir, sizeof(cuftpd_home_dir));

return CUFTPD_OK;
}

/*
 * Create ftp server's listening socket.
 */
static int
cuftpd_create_srv(void)
{
int fd;
int on = 1;
int err;
struct sockaddr_in srvaddr;

if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
CUFTPD_DEBUG("socket() failed: %s\n", strerror(errno));
return fd;
}

err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 
if (err < 0) {
CUFTPD_DEBUG("setsockopt() failed: %s\n", strerror(errno));
close(fd);
return CUFTPD_ERR;
}

memset(&srvaddr, 0, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(cuftpd_srv_port);
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
err = bind(fd, (struct sockaddr*)&srvaddr, sizeof(srvaddr));
if (err < 0) {
CUFTPD_DEBUG("bind() failed: %s\n", strerror(errno));
close(fd);
return CUFTPD_ERR;
}

err = listen(fd, CUFTPD_LISTEN_QU_LEN);
if (err < 0) {
CUFTPD_DEBUG("listen() failed: %s\n", strerror(errno));
close(fd);
return CUFTPD_ERR;
}

if (cuftpd_debug_on) {
int len = sizeof(srvaddr);
getsockname(fd, (struct sockaddr*)&srvaddr, &len);
CUFTPD_DEBUG("create srv listen socket OK: %s:%d\n",
   inet_ntoa(srvaddr.sin_addr), ntohs(srvaddr.sin_port));
}

return fd;

}

/*
 * Map server response's number to the msg pointer.
 */
static char *
cuftpd_srv_resp_num2msg(int num)
{
int i;
char buf[8];

snprintf(buf, sizeof(buf), "%d", num);
if (strlen(buf) != 3)
return NULL;

for (i = 0; i < CUFTPD_ARR_LEN(cuftpd_srv_resps); i++)
if (strncmp(buf, cuftpd_srv_resps, strlen(buf)) == 0)
return cuftpd_srv_resps;

return NULL;
}

static int
cuftpd_send_msg(int fd, char *msg, int len)
{
int n, off = 0, left = len;

while (1) {
n = write(fd, msg + off, left);
if (n < 0) {
if (errno == EINTR)
continue;
return n;
}
if (n < left) {
left -= n;
off += n;
continue;
}
return len;
}
}

static int
cuftpd_recv_msg(int fd, char buf[], int len)
{
int n;

while (1) {
n = read(fd, buf, len);
if (n < 0) {
if (errno == EINTR)
continue;
return n;
}
return n;
}
}

static int
cuftpd_send_resp(int fd, int num, ...)
{
char *cp = cuftpd_srv_resp_num2msg(num);
va_list ap;
char buf[BUFSIZ];

if (!cp) {
CUFTPD_DEBUG("cuftpd_srv_resp_num2msg(%d) failed\n", num);
return CUFTPD_ERR;
}

va_start(ap, num);
vsnprintf(buf, sizeof(buf), cp, ap);
va_end(ap);

CUFTPD_DEBUG("send resp:%s\n", buf);
if (cuftpd_send_msg(fd, buf, strlen(buf)) != strlen(buf)) {
CUFTPD_DEBUG("cuftpd_send_msg() failed: %s\n", strerror(errno));
return CUFTPD_ERR;
}

return CUFTPD_OK;
}

static void
cuftpd_trim_lineend(char *cp)
{
if (cp && strlen(cp) > 0) {
char *cp2 = &cp[strlen(cp) - 1];

while (*cp2 == '\r' || *cp2 == '\n')
if (--cp2 < cp)
break;
cp2[1] = '\0';
}
}

static int
cuftpd_get_connfd(void)
{
int fd;

if (cuftpd_pasv_fd >= 0) {
fd = accept(cuftpd_pasv_fd, NULL, NULL);
if (fd >= 0) {
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
cuftpd_pasv_connfd = fd;
return fd;
}
else
CUFTPD_DEBUG("accept() failed:%s\n", strerror(errno));
}
else if (cuftpd_port_connfd >= 0)
return cuftpd_port_connfd;

return (-1);
}

static int
cuftpd_close_all_fd(void)
{
if (cuftpd_pasv_fd >= 0) {
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
}
if (cuftpd_pasv_connfd >= 0) {
close(cuftpd_pasv_connfd);
cuftpd_pasv_connfd = -1;
}
if (cuftpd_port_connfd >= 0) {
close(cuftpd_port_connfd);
cuftpd_port_connfd = -1;
}
return CUFTPD_OK;
}




 mynets 回复于:2006-11-24 21:30:15



static int
cuftpd_do_user(int ctrlfd, char *cmdline)
{
char *cp = strchr(cmdline, ' ');

if (cp) {
int i;

for (i = 0; i < CUFTPD_ARR_LEN(cuftpd_users); i++)
if (strcmp(cp + 1, cuftpd_users.user) == 0) {
CUFTPD_DEBUG("user(%s) is found\n", cp + 1);
cuftpd_cur_user = &cuftpd_users;
break;
}

if (!cuftpd_cur_user)
CUFTPD_DEBUG("user(%s) not found\n", cp + 1);

/*
 * If user name is bad, we still don't close the connection 
 * and send back the 331 response to ask for password.
 */
return cuftpd_send_resp(ctrlfd, 331, cp + 1);
}

return cuftpd_send_resp(ctrlfd, 550);
}

static int
cuftpd_do_pass(int ctrlfd, char *cmdline)
{
char *space = strchr(cmdline, ' ');

if (cuftpd_cur_user && space) {
if (strlen(cuftpd_cur_user->pass) == 0 ||
strcmp(space + 1, cuftpd_cur_user->pass) == 0) {
CUFTPD_DEBUG("password for %s OK\n", cuftpd_cur_user->user);
return cuftpd_send_resp(ctrlfd, 230, cuftpd_cur_user->user);
}
CUFTPD_DEBUG("password for %s ERR\n", cuftpd_cur_user->user);
}

/*
 * User and pass don't match or 
 * cmd line does not contain a space character.
 */
cuftpd_cur_user = NULL;
return cuftpd_send_resp(ctrlfd, 530, "incorrect");
}

static int
cuftpd_do_pwd(int ctrlfd, char *cmdline)
{
char curdir[PATH_MAX];
char *cp;

CUFTPD_CHECK_LOGIN();

getcwd(curdir, sizeof(curdir));
cp = &curdir[strlen(cuftpd_home_dir)];
return cuftpd_send_resp(ctrlfd, 257, (*cp == '\0') ? "/" : cp);
}

static int
cuftpd_do_cwd(int ctrlfd, char *cmdline)
{
char *space = strchr(cmdline, ' ');
char curdir[PATH_MAX];

CUFTPD_CHECK_LOGIN();

if (!space)
return cuftpd_send_resp(ctrlfd, 550);

getcwd(curdir, sizeof(curdir));
if (strcmp(curdir, cuftpd_home_dir) == 0 &&
space[1] == '.' &&
space[2] == '.')
return cuftpd_send_resp(ctrlfd, 550);

/* Absolute path */
if (space[1] == '/') {
if (chdir(cuftpd_home_dir) == 0) {
if (space[2] == '\0' || chdir(space+2) == 0)
return cuftpd_send_resp(ctrlfd, 250);
}
chdir(curdir);
return cuftpd_send_resp(ctrlfd, 550);
}

/* Relative path */
if (chdir(space+1) == 0)
return cuftpd_send_resp(ctrlfd, 250);

chdir(curdir);
return cuftpd_send_resp(ctrlfd, 550);
}

/*
 * This function acts as a implementation like 'ls -l' shell command.
 */
static int
cuftpd_get_list(char buf[], int len)
{
DIR *dir;
struct dirent *ent;
int off = 0;

if ((dir = opendir(".")) < 0) {
CUFTPD_DEBUG("opendir() failed:%s\n", strerror(errno));
return CUFTPD_ERR;
}

buf[0] = '\0';

while ((ent = readdir(dir)) != NULL) {
char *filename = ent->d_name;
struct stat st;
char mode[] = "----------";
struct passwd *pwd;
struct group *grp;
struct tm *ptm;
char timebuf[BUFSIZ]; 
int timelen;

if (strcmp(filename, ".") == 0 ||
strcmp(filename, "..") == 0)
continue;

if (stat(filename, &st) < 0) {
closedir(dir);
CUFTPD_DEBUG("stat() failed:%s\n", strerror(errno));
return CUFTPD_ERR;
}

if (S_ISDIR(st.st_mode))
mode[0] = 'd';
if (st.st_mode & S_IRUSR)
mode[1] = 'r';
if (st.st_mode & S_IWUSR)
mode[2] = 'w';
if (st.st_mode & S_IXUSR)
mode[3] = 'x';
if (st.st_mode & S_IRGRP)
mode[4] = 'r';
if (st.st_mode & S_IWGRP)
mode[5] = 'w';
if (st.st_mode & S_IXGRP)
mode[6] = 'x';
if (st.st_mode & S_IROTH)
mode[7] = 'r';
if (st.st_mode & S_IWOTH)
mode[8] = 'w';
if (st.st_mode & S_IXOTH)
mode[9] = 'x';
mode[10] = '\0';
off += snprintf(buf + off, len - off, "%s ", mode);

/* hard link number, this field is nonsense for ftp */
off += snprintf(buf + off, len - off, "%d ", 1);

/* user */
if ((pwd = getpwuid(st.st_uid)) == NULL) {
closedir(dir);
return CUFTPD_ERR;
}
off += snprintf(buf + off, len - off, "%s ", pwd->pw_name);

/* group */
if ((grp = getgrgid(st.st_gid)) == NULL) {
closedir(dir);
return CUFTPD_ERR;
}
off += snprintf(buf + off, len - off, "%s ", grp->gr_name);

/* size */
off += snprintf(buf + off, len - off, "%*d ", 10, st.st_size);

/* mtime */
ptm = localtime(&st.st_mtime);
if (ptm && (timelen = strftime(timebuf, sizeof(timebuf), "%b %d %H:%S", ptm)) > 0) {
timebuf[timelen] = '\0';
off += snprintf(buf + off, len - off, "%s ", timebuf);
}
else {
closedir(dir);
return CUFTPD_ERR;
}

off += snprintf(buf + off, len - off, "%s\r\n", filename);

}

return off;
}

static int
cuftpd_do_list(int ctrlfd, char *cmdline)
{
char buf[BUFSIZ];
int n;
int fd;

CUFTPD_CHECK_LOGIN();

if ((fd = cuftpd_get_connfd()) < 0) {
CUFTPD_DEBUG("LIST cmd:no available fd%s", "\n");
goto err_label;
}

cuftpd_send_resp(ctrlfd, 150);

/* 
 * Get the 'ls -l'-like result and send it to client.
 */
n = cuftpd_get_list(buf, sizeof(buf));
if (n >= 0) {
if (cuftpd_send_msg(fd, buf, n) != n) {
CUFTPD_DEBUG("cuftpd_send_msg() failed: %s\n", strerror(errno));
goto err_label;
}
}
else {
CUFTPD_DEBUG("cuftpd_get_list() failed %s", "\n");
goto err_label;
}

cuftpd_close_all_fd();
return cuftpd_send_resp(ctrlfd, 226);

err_label:
cuftpd_close_all_fd();
return cuftpd_send_resp(ctrlfd, 550);
}

static int
cuftpd_do_syst(int ctrlfd, char *cmdline)
{
CUFTPD_CHECK_LOGIN();
return cuftpd_send_resp(ctrlfd, 215);
}

static int 
cuftpd_do_size(int ctrlfd, char *cmdline)
{
char *space = strchr(cmdline, ' ');
struct stat st;

CUFTPD_CHECK_LOGIN();

if (!space || lstat(space + 1, &st) < 0) {
CUFTPD_DEBUG("SIZE cmd err: %s\n", cmdline);
return cuftpd_send_resp(ctrlfd, 550);
}

return cuftpd_send_resp(ctrlfd, 213, st.st_size);
}

static int
cuftpd_do_dele(int ctrlfd, char *cmdline)
{
char *space = strchr(cmdline, ' ');
struct stat st;

CUFTPD_CHECK_LOGIN();

if (!space || lstat(space+1, &st) < 0 ||
remove(space+1) < 0) {
CUFTPD_DEBUG("DELE cmd err: %s\n", cmdline);
return cuftpd_send_resp(ctrlfd, 550);
}

return cuftpd_send_resp(ctrlfd, 200);
}

static int
cuftpd_do_type(int ctrlfd, char *cmdline)
{
CUFTPD_CHECK_LOGIN();

/*
 * Just send back 200 response and do nothing
 */
return cuftpd_send_resp(ctrlfd, 200);
}

/*
 * Parse PORT cmd and fetch the ip and port, 
 * and both in network byte order.
 */
static int
cuftpd_get_port_mode_ipport(char *cmdline, unsigned int *ip, unsigned short *port)
{
char *cp = strchr(cmdline, ' ');
int i;
unsigned char buf[6];

if (!cp)
return CUFTPD_ERR;

for (cp++, i = 0; i < CUFTPD_ARR_LEN(buf); i++) {
buf = atoi(cp);
cp = strchr(cp, ',');
if (!cp && i < CUFTPD_ARR_LEN(buf) - 1)
return CUFTPD_ERR;
cp++;
}

if (ip) 
*ip = *(unsigned int*)&buf[0];

if (port) 
*port = *(unsigned short*)&buf[4];

return CUFTPD_OK;
}

/*
 * Ftp client shipped with Windows XP uses PORT
 * mode as default to connect ftp server.
 */
static int
cuftpd_do_port(int ctrlfd, char *cmdline)
{
unsigned int ip;
unsigned short port;
struct sockaddr_in sin;

CUFTPD_CHECK_LOGIN();

if (cuftpd_get_port_mode_ipport(cmdline, &ip, &port) != CUFTPD_OK) {
CUFTPD_DEBUG("cuftpd_get_port_mode_ipport() failed%s", "\n");
goto err_label;
}

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ip;
sin.sin_port = port;

CUFTPD_DEBUG("PORT cmd:%s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));

if (cuftpd_port_connfd >= 0) {
close(cuftpd_port_connfd);
cuftpd_port_connfd = -1;
}

cuftpd_port_connfd = socket(AF_INET, SOCK_STREAM, 0);
if (cuftpd_port_connfd < 0) {
CUFTPD_DEBUG("socket() failed:%s\n", strerror(errno));
goto err_label;
}

if (connect(cuftpd_port_connfd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
CUFTPD_DEBUG("bind() failed:%s\n", strerror(errno));
goto err_label;
}

CUFTPD_DEBUG("PORT mode connect OK%s", "\n");
return cuftpd_send_resp(ctrlfd, 200);

err_label:
if (cuftpd_port_connfd >= 0) {
close(cuftpd_port_connfd);
cuftpd_port_connfd = -1;
}
return cuftpd_send_resp(ctrlfd, 550);

}

static int
cuftpd_do_pasv(int ctrlfd, char *cmdline)
{
struct sockaddr_in pasvaddr;
int len;
unsigned int ip;
unsigned short port;

CUFTPD_CHECK_LOGIN();

if (cuftpd_pasv_fd >= 0) {
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
}

cuftpd_pasv_fd = socket(AF_INET, SOCK_STREAM, 0);
if (cuftpd_pasv_fd < 0) {
CUFTPD_DEBUG("socket() failed: %s\n", strerror(errno));
return cuftpd_send_resp(ctrlfd, 550);
}

/* 
 * must bind to the same interface as ctrl connectin came from.
 */
len = sizeof(pasvaddr);
getsockname(ctrlfd, (struct sockaddr*)&pasvaddr, &len);
pasvaddr.sin_port = 0;

if (bind(cuftpd_pasv_fd, (struct sockaddr*)&pasvaddr, sizeof(pasvaddr)) < 0) {
CUFTPD_DEBUG("bind() failed: %s\n", strerror(errno));
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
return cuftpd_send_resp(ctrlfd, 550);
}

if (listen(cuftpd_pasv_fd, CUFTPD_LISTEN_QU_LEN) < 0) {
CUFTPD_DEBUG("listen() failed: %s\n", strerror(errno));
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
return cuftpd_send_resp(ctrlfd, 550);
}

len = sizeof(pasvaddr);
getsockname(cuftpd_pasv_fd, (struct sockaddr*)&pasvaddr, &len);
ip = ntohl(pasvaddr.sin_addr.s_addr);
port = ntohs(pasvaddr.sin_port);
CUFTPD_DEBUG("local bind: %s:%d\n", inet_ntoa(pasvaddr.sin_addr), port);

/* 
 * write local ip/port into response msg
 * and send to client.
 */
return cuftpd_send_resp(ctrlfd, 227, (ip>>24)&0xff, (ip>>16)&0xff,
(ip>>8)&0xff, ip&0xff, (port>>8)&0xff, port&0xff);

}

static int
cuftpd_do_retr(int ctrlfd, char *cmdline)
{
char buf[BUFSIZ];
char *space = strchr(cmdline, ' ');
struct stat st;
int fd = -1, n;
int connfd;

CUFTPD_CHECK_LOGIN();

if (!space || lstat(space + 1, &st) < 0) {
CUFTPD_DEBUG("RETR cmd err: %s\n", cmdline);
goto err_label;
}

if ((connfd = cuftpd_get_connfd()) < 0) {
CUFTPD_DEBUG("cuftpd_get_connfd() failed%s", "\n");
goto err_label;
}

cuftpd_send_resp(ctrlfd, 150);

/* begin to read file and write it to conn socket */
if ((fd = open(space + 1, O_RDONLY)) < 0) {
CUFTPD_DEBUG("open() failed: %s\n", strerror(errno));
goto err_label;
}

while (1) {
if ((n = read(fd, buf, sizeof(buf))) < 0) {
if (errno == EINTR)
continue;
CUFTPD_DEBUG("read() failed: %s\n", strerror(errno));
goto err_label;
}

if (n == 0)
break;

if (cuftpd_send_msg(connfd, buf, n) != n) {
CUFTPD_DEBUG("cuftpd_send_msg() failed: %s\n", strerror(errno));
goto err_label;
}
}

CUFTPD_DEBUG("RETR(%s) OK\n", space + 1);
if (fd >= 0)
close(fd);
cuftpd_close_all_fd();
return cuftpd_send_resp(ctrlfd, 226);

err_label:
if (fd >= 0)
close(fd);
cuftpd_close_all_fd();
return cuftpd_send_resp(ctrlfd, 550);
}

static int
cuftpd_do_stor(int ctrlfd, char *cmdline)
{
char buf[BUFSIZ];
char *space = strchr(cmdline, ' ');
struct stat st;
int fd = -1, n;
int left, off;
int connfd;

CUFTPD_CHECK_LOGIN();

/*
 * Should add some permission control mechanism here.
 */
if (!space || lstat(space + 1, &st) == 0) {
CUFTPD_DEBUG("STOR cmd err: %s\n", cmdline);
goto err_label;
}

if ((connfd = cuftpd_get_connfd()) < 0) {
CUFTPD_DEBUG("cuftpd_get_connfd() failed%s", "\n");
goto err_label;
}

cuftpd_send_resp(ctrlfd, 150);

if ((fd = open(space + 1, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
CUFTPD_DEBUG("open() failed: %s\n", strerror(errno));
goto err_label;
}

/* begin to read data from socket and wirte to disk file */
while (1) {
if ((n = cuftpd_recv_msg(connfd, buf, sizeof(buf))) < 0) {
CUFTPD_DEBUG("cuftpd_recv_msg() failed: %s\n", strerror(errno));
goto err_label;
}

if (n == 0)
break;

left = n;
off = 0;
while (left > 0) {
int nwrite;

if ((nwrite = write(fd, buf + off, left)) < 0) {
if (errno == EINTR)
continue;
CUFTPD_DEBUG("write() failed:%s\n", strerror(errno));
goto err_label;
}
off += nwrite;
left -= nwrite;
}
}

CUFTPD_DEBUG("STOR(%s) OK\n", space+1);
if (fd >= 0)
close(fd);
cuftpd_close_all_fd();
sync();
return cuftpd_send_resp(ctrlfd, 226);

err_label:
if (fd >= 0) {
close(fd);
unlink(space+1);
}
cuftpd_close_all_fd();
return cuftpd_send_resp(ctrlfd, 550);
}

static int
cuftpd_do_quit(int ctrlfd, char *cmdline)
{
cuftpd_send_resp(ctrlfd, 221);
cuftpd_quit_flag = 1;
return CUFTPD_OK;
}

static int
cuftpd_do_request(int ctrlfd, char buf[])
{
char *end = &buf[strlen(buf) - 1];
char *space = strchr(buf, ' ');
int i;
char save;
int err;

if (*end == '\n' || *end == '\r') {
/* 
 * this is a valid ftp request.
 */
cuftpd_trim_lineend(buf);

if (!space) 
space = &buf[strlen(buf)];

save = *space;
*space = '\0';
for (i = 0; cuftpd_cmds.cmd_name; i++) {
if (strcmp(buf, cuftpd_cmds.cmd_name) == 0) {
*space = save;
CUFTPD_DEBUG("recved a valid ftp cmd:%s\n", buf);
return cuftpd_cmds.cmd_handler(ctrlfd, buf);
}
}

/*
 * unrecognized cmd
 */
*space = save;
CUFTPD_DEBUG("recved a unsupported ftp cmd:%s\n", buf);

*space = '\0';
err = cuftpd_send_resp(ctrlfd, 500, buf);
*space = save;

return err;
}

CUFTPD_DEBUG("recved a invalid ftp cmd:%s\n", buf);

/* 
 * Even if it's a invalid cmd, we should also send  
 * back a response to prevent client from blocking.
 */
return cuftpd_send_resp(ctrlfd, 550);
}

static int 
cuftpd_ctrl_conn_handler(int connfd)
{
char buf[BUFSIZ];
int buflen;
int err = CUFTPD_OK;

/* 
 * Control connection has set up,
 * we can send out the first ftp msg.
 */
if (cuftpd_send_resp(connfd, 220) != CUFTPD_OK) {
close(connfd);
CUFTPD_DEBUG("Close the ctrl connection OK%s", "\n");
return CUFTPD_ERR;
}

/*
 * Begin to interact with client and one should implement
 * a state machine here for full compatibility. But we only
 * show a demonstration ftp server and i do my best to 
 * simplify it. Base on this skeleton, you can write a
 * full-funtional ftp server if you like. ;-)
 */
while (1) {
buflen = cuftpd_recv_msg(connfd, buf, sizeof(buf));
if (buflen < 0) {
CUFTPD_DEBUG("cuftpd_recv_msg() failed: %s\n", strerror(errno));
err = CUFTPD_ERR;
break;
}

if (buflen == 0) 
/* this means client launch a active close */
break;

buf[buflen] = '\0';
cuftpd_do_request(connfd, buf);

/* 
 * The negative return value from cuftpd_do_request 
 * should not cause the breaking of ctrl connection.
 * Only when the client send QUIT cmd, we exit and
 * launch a active close.
 */

if (cuftpd_quit_flag)
break;
}

close(connfd);
CUFTPD_DEBUG("Close the ctrl connection OK%s", "\n");
return err;
}

static int
cuftpd_do_loop(int listenfd)
{
int connfd;
int pid;

while (1) {
CUFTPD_DEBUG("Server ready, wait client's connection...%s", "\n");

connfd = accept(listenfd, NULL, NULL);
if (connfd < 0) {
CUFTPD_DEBUG("accept() failed: %s\n", strerror(errno));
continue;
}

if (cuftpd_debug_on) {
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
getpeername(connfd, (struct sockaddr*)&cliaddr, &len);
CUFTPD_DEBUG("accept a conn from %s:%d\n",
inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
}

if ((pid = fork()) < 0) {
CUFTPD_DEBUG("fork() failed: %s\n", strerror(errno));
close(connfd);
continue;
}
if (pid > 0) {
/* parent */
close(connfd);
cuftpd_cur_child_num++;
continue;
}

/* child */
close(listenfd);
signal(SIGCHLD, SIG_IGN);
if (cuftpd_ctrl_conn_handler(connfd) != CUFTPD_OK)
exit(-1);
exit(0);
}

return CUFTPD_OK;
}

int
main(int argc, char *argv[])
{
int listenfd;

cuftpd_parse_args(argc, argv);
cuftpd_init();

listenfd = cuftpd_create_srv();
if (listenfd >= 0)
cuftpd_do_loop(listenfd);

exit(0);
}




 lknh17 回复于:2006-11-24 21:51:25

支持原创!


 pingmm 回复于:2006-11-24 22:26:42

支持原创!收藏了慢慢看。


 mik 回复于:2006-11-24 22:47:03

有一些CU里新手貌似挺牛,前段有发布OS的,有发布线程池的,有发布下载器的
后生可畏!有前途


 醉卧水云间 回复于:2006-11-25 00:46:38

框架出来了,距离完善估计还要写1-5万行,++++OIL。


 lovesaka 回复于:2006-11-25 00:53:51

不错支持一下


 flw 回复于:2006-11-25 07:44:54

昨晚我第一眼看见这个帖子的时候,就知道有个人会回帖打击一下,也知道这个帖子一定又会被 C* 锁掉。
偷笑一把~
:m01::m01::m01::m01::m01:


 gvim 回复于:2006-11-25 08:11:24

引用:原帖由 mik 于 2006-11-24 22:47 发表
有一些CU里新手貌似挺牛,前段有发布OS的,有发布线程池的,有发布下载器的
后生可畏!有前途 



"新手":em06: 如何判断的? 
单从注册时间来看MS LZ的比你长一些

[ 本帖最后由 gvim 于 2006-11-25 08:12 编辑 ]


 大大狗 回复于:2006-11-25 08:53:29

原创的 学习


 converse 回复于:2006-11-25 10:01:12

引用:原帖由 flw 于 2006-11-25 07:44 发表
昨晚我第一眼看见这个帖子的时候,就知道有个人会回帖打击一下,也知道这个帖子一定又会被 C* 锁掉。
偷笑一把~
:m01::m01::m01::m01::m01: 




:em14::em14:


 langue 回复于:2006-11-25 10:19:23

能把代码控制到这样短,不错


 x.jc 回复于:2006-11-25 10:56:26

支持!!!!!!!!


 mynets 回复于:2006-11-25 11:07:19

引用:原帖由 mik 于 2006-11-24 22:47 发表
有一些CU里新手貌似挺牛,前段有发布OS的,有发布线程池的,有发布下载器的
后生可畏!有前途 



1、“新手”?我不知道你评判新手的标准是什么?你不认识人家,人家也不认识你,也不知道你是如何断定这些人一定属于你的标准界定之内的?
2、退一步说,就算人家是新手,对于你来说,这几个领域你都是老手?我看也未必吧。
3、估计你就是那个“欢迎加入a64汇编译器的开发工作”帖子的作者吧,是不是在那里被别人打击惯了,来这里寻求一下安慰呢。


 converse 回复于:2006-11-25 11:09:43

引用:原帖由 mynets 于 2006-11-25 11:07 发表


1、“新手”?我不知道你评判新手的标准是什么?你不认识人家,人家也不认识你,也不知道你是如何断定这些人一定属于你的标准界定之内的?
2、退一步说,就算人家是新手,对于你来说,这几个领域你都是老手? ... 



打住,谢谢!:mrgreen::mrgreen:


 langue 回复于:2006-11-25 11:15:42

引用:原帖由 converse 于 2006-11-25 11:09 发表


打住,谢谢!:mrgreen::mrgreen: 



是啊,配合一下版主的工作。八成是误解了啊


 infernor 回复于:2006-11-25 11:43:13

收藏,正需要这个呢


 chjcpu1 回复于:2006-11-26 11:44:44

我也写过一个ftp server,但和楼主的模块化比起来可逊色好多了,支持楼主!


 old-cow 回复于:2006-11-26 13:03:47

支持阿。


 sntianren 回复于:2006-11-26 13:25:10

厉害~~~~又是大牛!!!
又学到一些新东东了~~~~呵呵


 unixsc 回复于:2007-02-23 17:18:19

支持.


 G65535 回复于:2007-02-23 17:37:41

收下研究。


 1jjk 回复于:2007-02-23 21:09:35

新手不新手无所谓,学到东西就是好手.留个记号,方便学习


 lyjjr 回复于:2007-02-23 21:14:26

我不懂程序,但我还是要顶一下。


 zhujiang73 回复于:2007-02-23 21:29:41

支持原创。

[ 本帖最后由 zhujiang73 于 2007-2-23 21:31 编辑 ]


 iesc 回复于:2007-02-24 10:40:51

很经典的代码。虽然编译的时候有一些错误。


 fnems 回复于:2007-02-24 22:38:22

....何止佩服


 mingyanguo 回复于:2007-02-25 08:18:06

代码格式我喜欢,KNF
BSDer?


 soul_of_moon 回复于:2007-02-25 11:11:05

弱弱地问:是原创吗?


 unix_and_me 回复于:2007-02-25 11:24:53

谢谢


 bitmilong 回复于:2007-02-25 13:35:20

代码很精练,风格不错,收藏了学习下


 chinaljj 回复于:2007-02-26 18:07:42

老潜水,顶一下,辛苦!


 zw2002 回复于:2007-02-27 09:27:50

下了,学习


 iwolcbao 回复于:2007-03-06 16:54:25

羡慕,今天在机子上调试了一下,很不错,
在丰富一下可以搞个开源项目,
呵呵,建议一下!


 netkiller 回复于:2007-03-06 18:00:10

加油,再完善完善:
再加一个支持pam 认证模块.
再加一个可以取ftp client banner的功能.这样可以封 flashget,net ant...
流量分配
线程管理
空间/容量限制
权限访问例表.

去申请一个Subverion空间吧...


 boxpei 回复于:2007-03-07 15:02:05

用来学习还是很不错的,支持一下。对开源有兴趣的可以看看sourceforge,上面的vsftp功能很全。

[ 本帖最后由 boxpei 于 2007-3-7 15:21 编辑 ]


 kanwairen 回复于:2007-03-08 16:09:47

我前几天看vsftpd的代码,看不懂... ,看这个应该更容易些,多谢LZ哈


 shanan 回复于:2007-03-13 15:25:28

不错啊呵呵


 LF_532 回复于:2007-03-21 17:25:39

写的相当好!


 RockAndRoll 回复于:2007-04-07 20:08:49

好!我顶!


 tianxiaogang12 回复于:2007-04-07 20:55:29

小弟是 新手  但要支持原创


 b_liu 回复于:2007-04-08 11:06:47

支持原创..


 hdygw 回复于:2007-04-09 17:23:30

不错!支持!


 RockAndRoll 回复于:2007-04-09 22:54:47

好东西,楼猪应该再做一个简单的FTP客户端就更好了,楼猪+U, 支持你!


 nahua 回复于:2007-04-10 09:33:18

先收了再说


 suntoltti 回复于:2007-04-10 14:15:38

exclent code style,you should be a talent.:em18::em18::em18:


 ivhb 回复于:2007-04-10 14:47:40

引用:原帖由 mynets 于 2006-11-24 21:30 发表


static int
cuftpd_do_user(int ctrlfd, char *cmdline)
{
char *cp = strchr(cmdline, ' ');

if (cp) {
int i;

for (i = 0; i < CUFTPD_ARR_LEN(cuftpd_users); i++)
if (strc ... 






哥们,再来个附件不是更好?


 suntoltti 回复于:2007-04-10 14:55:45

cuftpd_debug(const char *file, int line, const char *fmt, ...)
哥们能解释一下吗,函数定义部分,最后的那...表示什么呢?:em14::em14:


 abcde645 回复于:2007-04-10 17:37:29

支持下


 kangji 回复于:2007-04-10 22:09:13

支持,学习


 xcmissyou 回复于:2007-04-10 22:41:59

这也叫新手啊,呵呵楼主冤枉了!


 a511125 回复于:2007-04-14 14:36:27

不错,收藏了。


 asdf511 回复于:2007-06-22 11:15:05

在cuftpd_do_pasv函数中
static int
cuftpd_do_pasv(int ctrlfd, char *cmdline)
{
struct sockaddr_in pasvaddr;
int len;
unsigned int ip;
unsigned short port;

CUFTPD_CHECK_LOGIN();

if (cuftpd_pasv_fd >= 0) {
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
}

cuftpd_pasv_fd = socket(AF_INET, SOCK_STREAM, 0);
if (cuftpd_pasv_fd < 0) {
CUFTPD_DEBUG("socket() failed: %s\n", strerror(errno));
return cuftpd_send_resp(ctrlfd, 550);
}

/* 
 * must bind to the same interface as ctrl connectin came from.
 */
len = sizeof(pasvaddr);

getsockname(ctrlfd, (struct sockaddr*)&pasvaddr, &len);
pasvaddr.sin_port = 0;//为什么端口为0???
if (bind(cuftpd_pasv_fd, (struct sockaddr*)&pasvaddr, sizeof(pasvaddr)) < 0) {
CUFTPD_DEBUG("bind() failed: %s\n", strerror(errno));
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
return cuftpd_send_resp(ctrlfd, 550);
}

if (listen(cuftpd_pasv_fd, CUFTPD_LISTEN_QU_LEN) < 0) {
CUFTPD_DEBUG("listen() failed: %s\n", strerror(errno));
close(cuftpd_pasv_fd);
cuftpd_pasv_fd = -1;
return cuftpd_send_resp(ctrlfd, 550);
}

len = sizeof(pasvaddr);
getsockname(cuftpd_pasv_fd, (struct sockaddr*)&pasvaddr, &len);
ip = ntohl(pasvaddr.sin_addr.s_addr);
port = ntohs(pasvaddr.sin_port);
CUFTPD_DEBUG("local bind: %s:%d\n", inet_ntoa(pasvaddr.sin_addr), port);

/* 
 * write local ip/port into response msg
 * and send to client.
 */
return cuftpd_send_resp(ctrlfd, 227, (ip>>24)&0xff, (ip>>16)&0xff,
(ip>>8)&0xff, ip&0xff, (port>>8)&0xff, port&0xff);

}
获取绑定的地址后,为什么pasvaddr.sin_port = 0;有什么用?请高手指点??


 jaffaz 回复于:2007-06-23 01:04:31

编码风格不错!
刚才看了下,找出了个bug:


static int
cuftpd_do_cwd(int ctrlfd, char *cmdline)
{
        char *space = strchr(cmdline, ' ');
        char curdir[PATH_MAX];

        CUFTPD_CHECK_LOGIN();

        if (!space)
                return cuftpd_send_resp(ctrlfd, 550);

        getcwd(curdir, sizeof(curdir));
        if (strcmp(curdir, cuftpd_home_dir) == 0 &&
                space[1] == '.' &&
                space[2] == '.')
                return cuftpd_send_resp(ctrlfd, 550);

        /* Absolute path */       
        if (space[1] == '/') {
                if (chdir(cuftpd_home_dir) == 0) {
                        if (space[2] == '\0' || chdir(space+2) == 0)
                                return cuftpd_send_resp(ctrlfd, 250);       
                }
                chdir(curdir);
                return cuftpd_send_resp(ctrlfd, 550);
        }

        /* Relative path */
        if (chdir(space+1) == 0)
                return cuftpd_send_resp(ctrlfd, 250);

        chdir(curdir);
        return cuftpd_send_resp(ctrlfd, 550);
}

作者在cwd=/时再往父目录走做了判断,防止用户“走出”根目录树。
但是这个判断不够严谨,只是不能在cwd=/的时候执行 cd ..
若用户cwd=/的时候执行 cd /.. 或 cd ./.. 就可以访问根目录树以外的数据了。

下面是客户端运行情况:

jaffaz@msh:~$ ftp
ftp> open 
(to) 127.0.0.1
Connected to 127.0.0.1.
220 CuFTPD 1.0 Server
Name (127.0.0.1:jaffaz): ftp
331 Password required for ftp
Password:
230 User ftp logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd /
250 CWD command successful
ftp> ls
200 OK
150 Begin transfer
-rw-r--r-- 1 jaffaz jaffaz      32914 Jun 22 22:21 cuftpd.c
-rw-r--r-- 1 jaffaz jaffaz       9277 Jun 22 22:55 Makefile
-rw-r--r-- 1 jaffaz jaffaz       8410 Jun 22 22:47 Makefile.in
-rw-r--r-- 1 jaffaz jaffaz        901 Jun 22 22:00 cuftpd.h
-rw-r--r-- 1 jaffaz jaffaz      45560 Jun 22 22:58 cuftpd.o
-rw-r--r-- 1 jaffaz jaffaz         47 Jun 22 22:19 Makefile.am
-rwxr-xr-x 1 jaffaz jaffaz      45707 Jun 22 22:58 cuftpd
drwxr-xr-x 1 jaffaz jaffaz       4096 Jun 22 22:58 .deps
226 Transfer complete
ftp> cd ..
550 Error
ftp> cd /..
250 CWD command successful
ftp> ls
200 OK
150 Begin transfer
drwxr-xr-x 1 jaffaz jaffaz       4096 Jun 22 22:58 src
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:49 autoscan.log
-rw-r--r-- 1 jaffaz jaffaz         14 Jun 22 22:11 Makefile.am
-rw-r--r-- 1 jaffaz jaffaz        893 Jun 22 22:35 configure.in
-rwxr-xr-x 1 jaffaz jaffaz       6406 Jun 22 22:46 install-sh
-rwxr-xr-x 1 jaffaz jaffaz        722 Jun 22 22:46 mkinstalldirs
-rwxr-xr-x 1 jaffaz jaffaz       6480 Jun 22 22:46 missing
-rw-r--r-- 1 jaffaz jaffaz       7831 Jun 22 22:46 INSTALL
-rw-r--r-- 1 jaffaz jaffaz      18001 Jun 22 22:46 COPYING
-rw-r--r-- 1 jaffaz jaffaz      10041 Jun 22 22:47 Makefile.in
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 NEWS
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 README
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 AUTHORS
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 ChangeLog
-rw-r--r-- 1 jaffaz jaffaz      41102 Jun 22 22:47 aclocal.m4
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:43 config.guess
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:43 config.sub
drwxr-xr-x 1 jaffaz jaffaz       4096 Jun 22 22:49 autom4te.cache
-rw-r--r-- 1 jaffaz jaffaz      30682 Jun 22 22:55 config.log
-rwxr-xr-x 1 jaffaz jaffaz     199547 Jun 22 22:49 configure
-rwxr-xr-x 1 jaffaz jaffaz      21885 Jun 22 22:55 config.status
-rw-r--r-- 1 jaffaz jaffaz      10029 Jun 22 22:55 Makefile
226 Transfer complete
ftp> cd /
250 CWD command successful
ftp> cd ./..
250 CWD command successful
ftp> ls
200 OK
150 Begin transfer
drwxr-xr-x 1 jaffaz jaffaz       4096 Jun 22 22:58 src
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:49 autoscan.log
-rw-r--r-- 1 jaffaz jaffaz         14 Jun 22 22:11 Makefile.am
-rw-r--r-- 1 jaffaz jaffaz        893 Jun 22 22:35 configure.in
-rwxr-xr-x 1 jaffaz jaffaz       6406 Jun 22 22:46 install-sh
-rwxr-xr-x 1 jaffaz jaffaz        722 Jun 22 22:46 mkinstalldirs
-rwxr-xr-x 1 jaffaz jaffaz       6480 Jun 22 22:46 missing
-rw-r--r-- 1 jaffaz jaffaz       7831 Jun 22 22:46 INSTALL
-rw-r--r-- 1 jaffaz jaffaz      18001 Jun 22 22:46 COPYING
-rw-r--r-- 1 jaffaz jaffaz      10041 Jun 22 22:47 Makefile.in
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 NEWS
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 README
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 AUTHORS
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:17 ChangeLog
-rw-r--r-- 1 jaffaz jaffaz      41102 Jun 22 22:47 aclocal.m4
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:43 config.guess
-rw-r--r-- 1 jaffaz jaffaz          0 Jun 22 22:43 config.sub
drwxr-xr-x 1 jaffaz jaffaz       4096 Jun 22 22:49 autom4te.cache
-rw-r--r-- 1 jaffaz jaffaz      30682 Jun 22 22:55 config.log
-rwxr-xr-x 1 jaffaz jaffaz     199547 Jun 22 22:49 configure
-rwxr-xr-x 1 jaffaz jaffaz      21885 Jun 22 22:55 config.status
-rw-r--r-- 1 jaffaz jaffaz      10029 Jun 22 22:55 Makefile
226 Transfer complete
ftp> 



[ 本帖最后由 jaffaz 于 2007-6-23 01:16 编辑 ]


 gaocheng 回复于:2007-06-23 02:35:44

好东东:em02::em02::em02:


 woai97 回复于:2007-06-23 09:52:24

有时间学习一下!:D:)


 ffangmm 回复于:2007-06-24 09:41:27

楼主高手啊!


 sepmemory 回复于:2007-06-24 11:35:11

做个记号

学习ing


 weigongwan 回复于:2007-06-25 17:24:00

支持原创!


 cccccc 回复于:2007-06-26 10:11:53

我编译后,运行时不能ls
为了定位错误,我对程序加了几条代码
在函数cuftpd_get_list(char buf[], int len)中
                /* user */
                if ((pwd = getpwuid(st.st_uid)) == NULL) {
                        closedir(dir);
                CUFTPD_DEBUG("getpwuid fail %s", "\n"); [color=Red]//这一行是我加的[/color]
                        return CUFTPD_ERR;
                }
                off += snprintf(buf + off, len - off, "%s ", pwd->pw_name);


服务器端提示
(cuftpd.c:550:2464)getpwuid fail 
(cuftpd.c:612:2464)cuftpd_get_list() failed 
(cuftpd.c:323:2464)send resp:550 Error


客户端提示
ftp  127.0.0.1 2100
Connected to 127.0.0.1 (127.0.0.1).
220 CuFTPD 1.0 Server
Name (127.0.0.1:root): ftp
331 Password required for ftp
Password:
230 User ftp logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
227 Entering Passive Mode (127,0,0,1,128,58)
150 Begin transfer
550 Error
ftp>




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=860935
转载请注明作者名及原文出处



收藏本页到: