Fix password md5 checking.
[xylftp.git] / server / src / do_cmd.c
blob3a232cb532b3e8f3e93d9e05db0e18c51c197731
1 /*
2 * Copyright (c) 2007,西安邮电学院Linux兴趣小组
3 * All rights reserved.
4 *
5 * 文件名称:命令处理
6 * 摘 要:处理MKD RMD DELE PASV STOR命令
7 *
8 * 当前版本:1.2
10 * 修 改 者:刘洋
11 * 修改日期:2007年12月8日
12 * 修改原因:LIST与STAT中的日期格式不符合要求,在很多图形界面客户端中出现问题,故更改之。
14 * 作 者:聂海海 王亚刚 郭拓 贾孟树
15 * 完成日期:2007年5月30日
16 * 修 改 者:刘洋 林峰 王聪 聂海海
17 * 修改日期:2007年6月16日
18 * 取代版本:1.1
19 * 摘 要:原来增加的命令不很符合要求,重建此文件。全部根据要求,现将命令单独在test目录中通过的逐条加入。
20 * 此次加入命令STAT。
22 *14日加入USER。
23 *6.15: 修正英文语法错误,排版错误。
24 *6.16: 添加do_mkd,do_rmd, do_dele, do_retr
25 *6.16:添加do_pass
28 #define _GNU_SOURCE
29 #include <wordexp.h>
30 #include "xylftp.h"
31 #include "debug.h"
33 #ifndef MAX_PATH
34 #define MAX_PATH 4096
35 #endif
37 extern struct user_env user_env;
38 extern struct run_env run_env;
40 static void _stat_fail_450(void);
41 static void _stat_error_421(void);
42 static void _stat_error1_501(void);
43 static void _stat_error2_501(void);
44 static void _stat_error3_501(void);
45 static void _stat_fail_550(void);
46 static void _stat_success_150(void);
47 static void _stat_success_226(void);
48 static void _stat_fail_501(void);
49 static void _path_tail(char *, char *);
50 static int _get_local_ip_address(int sock, char* ip_addr);
51 static int _stat_mkd(const char *path);
52 static int _stat_rmd(const char *path);
53 static int _stat_dele(const char *path);
54 static int _stat_retr(const char *);
55 static int _ignore_sigpipe(void);
56 static int _analysis_ipaddr(char *, char **, int *);
57 static int _chdir(const char*);
58 static int _response(const char*);
59 int do_quit(void);
60 int failed(const char *s);
62 int do_user(char username[])
64 const char anonymous[] = "anonymous";
65 const char inf_buf[] = "331 Please send you password.\r\n";
66 const char no_anonymous[] = "555 Anonymous not allowed on this server.\r\n";
67 const char noname[] = "500 USER: command requires a parameter.\r\n";
68 const char logged[] = "503 You have already logged in.\r\n";
70 if (username[0] == '\0') {
71 _response(noname);
72 debug_printf("%s\n", noname);
73 return -1;
76 if (user_env.login_in == TRUE) {
77 _response(logged);
78 debug_printf("%s\n", logged);
79 return -1;
82 if (strcmp(username, anonymous) == 0){
83 if(run_env.anonymous_enable){
84 strcpy(user_env.user_name, username);
85 _response(inf_buf);
86 debug_printf("%s\n", inf_buf);
87 } else {
88 _response(no_anonymous);
89 debug_printf("%s\n", no_anonymous);
92 /*anonymous client authentication*/
93 else {
94 strcpy(user_env.user_name, username);
95 _response(inf_buf);
96 debug_printf("%s\n", inf_buf);
99 return 0;
101 /*implement of USER*/
103 static char get_hex(const char *buf)
105 char tmp[3];
106 strncpy(tmp, buf, 2);
107 return (char) strtol(tmp, NULL, 16);
110 /*implement of PASS*/
111 int do_pass(char *pass)
113 char mess[50];
114 char password[16];
115 char md[16];
116 char name[16];
117 const char log_error[] = "Login failed!\r\n";
118 const char logged[] = "503 You have already logged in.\r\n";
119 FILE *fp;
120 size_t len = 0;
121 ssize_t k;
122 char *line = NULL, *tmp;
123 int i, j, pass_len, id;
125 if (user_env.login_in == TRUE) {
126 _response(logged);
127 return -1;
130 if (strcmp(user_env.user_name, "anonymous") == 0) {
131 snprintf(mess, 50, "230 User anonymous logged in.\r\n");
132 user_env.login_in = TRUE;
133 user_env.enable_upload = FALSE;
134 _response(mess);
135 return 0;
138 if ((fp = fopen(run_env.user_pass_file, "r")) == NULL) {
139 write_log("open user_pass_file error",0);
140 _response(log_error);
141 return -1;
144 while ((k = getline(&line, &len, fp)) != -1) {
145 fscanf(fp, "%d", &id);
146 tmp = line;
147 j = 0;
148 for (i = 0; ; i++) {
149 if (tmp[i] == ':') {
150 i = i + 2;
151 break;
154 for( ; ; i++) {
155 if(tmp[i] == ':') {
156 name[j] = '\0';
157 break;
158 } else {
159 name[j++] = tmp[i];
163 if (strcmp(name, user_env.user_name) == 0)
164 break;
167 if (k == -1) {
168 snprintf(mess, 50, "530 Login incorrect.\r\n");
169 _response(mess);
170 free(line);
171 return -1;
174 user_env.user_id = id;
175 tmp = line;
176 for (i = 0, j = 0; tmp[i] != '\n' && j < 6; i++) {
177 if (tmp[i] == ':') {
178 j++;
180 if (j == 4) {
181 i = i + 2;
182 if (tmp[i] == 'w') {
183 user_env.enable_upload = TRUE;
188 for (j = 0; j < 16; ) {
189 password[j++] = get_hex(tmp+i);
190 i += 2;
192 free(line);
194 pass_len=strlen(pass);
195 MD5((const unsigned char *)pass, pass_len, (unsigned char *)md);
197 fclose(fp);
199 if (!memcmp(password, md, 16) ) {
200 snprintf(mess, 50, "230 User %s logged in.\r\n", user_env.user_name);
201 user_env.login_in = TRUE;
202 _response(mess);
203 return 0;
204 } else {
205 snprintf(mess, 50, "530 Login incorrect.\r\n");
206 _response(mess);
207 return -1;
212 /*implement of STAT*/
214 static char *_format_time(struct stat *stat_buf, char *str)
216 const char month[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
217 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
218 struct tm tmp;
220 gmtime_r(&(stat_buf->st_mtime), &tmp);
222 if (S_ISDIR(stat_buf->st_mode)) {
223 snprintf(str, strlen(str), "%s %2d %4d", month[tmp.tm_mon], tmp.tm_mday, tmp.tm_year + 1900);
225 else {
226 snprintf(str, strlen(str), "%s %2d %2d:%2d", month[tmp.tm_mon], tmp.tm_mday, tmp.tm_hour, tmp.tm_min);
229 return str;
232 static char *_get_line_info(struct stat *stat_buf, char *buf, int *width)
234 char att[11] = "----------", tm[26];
235 char time_str_buff[] = "Dec 12, 18:00";
236 unsigned int t = S_IRUSR;
237 int i;
239 if (S_ISREG(stat_buf->st_mode)) {
240 att[0] = '-';
242 else if (S_ISDIR(stat_buf->st_mode)) {
243 att[0] = 'd';
245 else if (S_ISCHR(stat_buf->st_mode)) {
246 att[0] = 'c';
248 else if (S_ISBLK(stat_buf->st_mode)) {
249 att[0] = 'b';
251 else if (S_ISFIFO(stat_buf->st_mode)) {
252 att[0] = 'p';
254 else if (S_ISLNK(stat_buf->st_mode)) {
255 att[0] = 'l';
257 else if (S_ISSOCK(stat_buf->st_mode)) {
258 att[0] = 's';
260 for (i = 1;i < 10;i++,t >>= 1) {
261 if (stat_buf->st_mode & t) {
262 switch (i % 3) {
263 case 1: att[i] = 'r';
264 break;
265 case 2: att[i] = 'w';
266 break;
267 case 0: att[i] = 'x';
271 if (snprintf(tm, 25, "%s", _format_time(stat_buf, time_str_buff)) == -1) {
272 write_log("Read system time error!", 0);
274 if (snprintf(buf, MAX_PATH, "%s% *d %*s %*s% *d %s", att, width[2], (size_t)stat_buf->st_nlink, width[0],
275 run_env.visible_user_name,width[1], run_env.visible_group_name,width[3],
276 (size_t)stat_buf->st_size, tm) == -1){
277 write_log("The path string is overflow!", 0);
279 return buf;
282 static int _get_int_len(int n)
284 int i;
285 for (i = 1;n /= 10;i++) {
288 return i;
291 static int _stat_no_arg(void)
293 char msg[MAX_MSG_LEN]={0};
294 if (snprintf(msg, MAX_MSG_LEN, "211-Status for user %s from %s:\r\n"
295 "211-Stored %d files,%d KB\r\n211-Retrieved %d files,%d KB\r\n211 End of Status.\r\n",
296 user_env.user_name, user_env.client_ip, user_env.upload_files,
297 user_env.upload_kbytes, user_env.download_files, user_env.download_kbytes) == -1) {
298 write_log("The message is overflow.",0);
300 return _response(msg);
303 static int _stat_with_arg(const char *cmd_arg)
305 char buf[MAX_PATH], full_path[MAX_PATH], tmp[MAX_PATH];
306 const char not_found[] = "450 Target path doesn't exist.\r\n";
307 struct dirent *direntp;
308 DIR *target_dir;
309 struct stat stat_buf;
310 int max_width[4] = {0}; /*max length of user name, group name,link number and the file length number*/
311 int t;
313 if (*cmd_arg == '/') {
314 if (snprintf(buf, MAX_PATH, "%s", cmd_arg) == -1) {
315 write_log("Path string overflows.", 0);
318 else {
319 if (snprintf(buf, MAX_PATH, "%s%s", user_env.current_path, cmd_arg) == -1) {
320 write_log("Path string overflows.", 0);
324 strcpy(full_path, buf); /*the length of buf won't greater than the full_path so it's safe.*/
326 if (!(target_dir = opendir(buf))) {
327 _response(not_found);
328 return closedir(target_dir);
331 max_width[0] = (t = strlen(run_env.visible_user_name)) > max_width[0]?t:max_width[0];
332 max_width[1] = (t = strlen(run_env.visible_group_name)) > max_width[1]?t:max_width[1];
334 while ((direntp = readdir(target_dir)) != NULL) {
335 if (snprintf(buf, MAX_PATH, "%s/%s", full_path, direntp->d_name) == -1) {
336 write_log("Path string overflows.", 0);
339 if (stat(buf, &stat_buf) == -1) {
340 write_log("Read local file status error", 0);
341 closedir(target_dir);
342 return -1;
345 max_width[2] = (t = _get_int_len(stat_buf.st_nlink)) > max_width[2]?t:max_width[2];
346 max_width[3] = (t = _get_int_len(stat_buf.st_size)) > max_width[3]?t:max_width[3];
348 rewinddir(target_dir);
350 if (snprintf(buf, MAX_PATH, "211-Status of %s\r\n", cmd_arg) == -1) {
351 write_log("Path string overflows.",0);
354 _response(buf);
356 while ((direntp = readdir(target_dir)) != NULL) {
357 if (*(direntp->d_name) == '.') {
358 continue;
360 if (snprintf(buf, MAX_PATH,"%s/%s", full_path, direntp->d_name) == -1) {
361 write_log("Path string overflows.", 0);
364 if (stat(buf, &stat_buf) == -1) {
365 write_log("Read local file status error", 0);
366 closedir(target_dir);
367 return -1;
369 if (snprintf(buf, MAX_PATH, "211-%s %s\r\n", _get_line_info(&stat_buf, tmp, max_width),
370 direntp->d_name) == -1) {
371 write_log("Path string overflows.", 0);
373 _response(buf);
376 if (snprintf(buf, MAX_PATH, "211 End of Status.\r\n") == -1) {
377 write_log("Path string overflows.", 0);
379 _response(buf);
380 return closedir(target_dir);
383 int do_stat(const char *cmd_arg)
385 if (!strlen(cmd_arg)) {
386 return _stat_no_arg();
389 return _stat_with_arg(cmd_arg);
391 /*end of the implement of STAT*/
393 int do_list(char *filename)
395 int m_width[4]={3,10,10,10};
396 int ret = 0;
397 size_t i;
398 char each_dir_inf[BUF_LEN] = {0};
399 char buf[BUF_LEN] = {0};
400 const char finished[] = "226 Transfer complete.\r\n";
401 const char success[] = "150 Opening ASCII mode data connection for file list.\r\n";
402 const char fail[] = "450 No such file or directory.\r\n";
403 DIR *dir_inf_str;
404 struct dirent *dirp;
405 struct stat file_inf;
406 char user_path[PATH_NAME_LEN] ={""};
407 char inte_dir_inf[BUF_LEN]={0};
408 char **w;
409 wordexp_t wxp;
411 _response(success);
412 if(filename[0]!='/') {
413 if (strcmp(user_env.current_path, "/") != 0) {
414 snprintf(buf, BUF_LEN, "%s/%s", user_env.current_path, filename);
415 } else {
416 snprintf(buf, BUF_LEN, "%s%s", user_env.current_path, filename);
419 else {
420 snprintf(buf, BUF_LEN, "%s", filename);
423 debug_printf("buf=%s\n", buf);
425 wordexp(buf, &wxp, 0);
426 w = wxp.we_wordv;
427 for (i=0; i < wxp.we_wordc; i++){
428 if (*w[i] == '.') {
429 continue;
432 if (stat(w[i], &file_inf) == 0) {
433 if (!S_ISDIR(file_inf.st_mode)
434 || (wxp.we_wordc!=1 || strcmp(w[0], buf) != 0)) {
435 snprintf(inte_dir_inf, BUF_LEN, "%s %s\r\n",
436 _get_line_info(&file_inf, each_dir_inf, m_width),
437 w[i]);
438 write(user_env.data_fd, inte_dir_inf,
439 strlen(inte_dir_inf));
440 continue;
442 } else {
444 debug_printf("w[i]=%s\n", w[i]);
446 ret = -1;
447 break;
450 debug_printf("Get here with %s\n", w[i]);
452 if((dir_inf_str = opendir(w[i])) != NULL){
453 while((dirp = readdir(dir_inf_str)) != NULL){
454 if (*(dirp->d_name) == '.') {
455 continue;
458 memset(each_dir_inf, 0, BUF_LEN);
459 memset(inte_dir_inf, 0, BUF_LEN);
460 snprintf(user_path, PATH_NAME_LEN, "%s/%s", w[i], dirp->d_name);
461 if(stat(user_path, &file_inf) != -1){
462 snprintf(inte_dir_inf, BUF_LEN, "%s %s\r\n",
463 _get_line_info(&file_inf, each_dir_inf, m_width),
464 dirp->d_name);
465 write(user_env.data_fd, inte_dir_inf,
466 strlen(inte_dir_inf));
469 } else {
470 ret = -1;
471 break;
473 closedir(dir_inf_str);
474 }/*for*/
476 if (ret == 0) {
477 _response(finished);
478 } else {
479 _response(fail);
481 close(user_env.data_fd);
482 wordfree(&wxp);
483 return ret;
486 /* 命令MKD的处理 */
487 int do_mkd(const char *path) /*处理命令MKD的入口*/
489 char str[PATH_NAME_LEN] = {""};
490 if (user_env.enable_upload == 1) { /*判断权限*/
491 if (path[0] == '/') { /*参数是路径名?*/
492 path = strcat(str, path);
493 } else { /*参数是目录名?*/
494 strcpy(str, user_env.current_path);
495 if(str[strlen(str)-1] != '/')
496 strcat(str, "/");
497 path = strcat(str, path);
499 if(_stat_mkd(path) == 0) {
500 return 0;
501 } else {
502 return -1;
504 } else {
505 _stat_error_421();
506 return -1;
510 static int _stat_mkd(const char *path)
512 char buf[50+PATH_NAME_LEN] = {};
514 debug_printf("mkd: path=%s \r\n", path);
515 if (mkdir(path, S_IRWXU) == 0) { /*创建目录成功*/
516 snprintf(buf, 50+PATH_NAME_LEN, "257 Directory successfully created:%s.\r\n", path);
517 _response(buf);
518 return 0;
519 } else {
520 if (errno == EEXIST) {
521 _stat_error1_501();
523 else if (errno == ENAMETOOLONG) {
524 _stat_error2_501();
525 } else {
526 _stat_fail_450();
528 return -1;
533 *命令RMD的处理
535 int do_rmd(const char *path) /*处理命令RMD的入口*/
537 char str[PATH_NAME_LEN]={""};
538 if (user_env.enable_upload == 1) { /*判断权限*/
539 if (path[0] == '/') { /*参数是路径名?*/
540 path = strcat(str, path);
542 else { /*参数是目录名?*/
543 strcpy(str, user_env.current_path);
544 if(str[strlen(str)-1] != '/') {
545 strcat(str, "/");
547 path = strcat(str,path);
549 if (_stat_rmd(path) == 0) {
550 return 0;
551 } else {
552 return -1;
554 } else {
555 _stat_error_421();
556 return -1;
560 static int _stat_rmd(const char *path)
562 const char buf[] = "250 RMD command successful.\r\n";
564 debug_printf("rmdir at %s \r\n", path);
565 if (rmdir(path) == 0) { /*删除目录成功*/
566 _response(buf);
567 return 0;
568 } else {
569 if(errno == ENOENT) {
570 _stat_error3_501();
571 } else if (errno == ENAMETOOLONG) {
572 _stat_error2_501();
573 } else {
574 _stat_fail_450();
576 return -1;
581 *命令DELE的处理
583 int do_dele(const char *path) /*处理命令DELE的入口*/
585 char str[PATH_NAME_LEN] = {""};
586 if (user_env.enable_upload == 1) { /*判断权限*/
587 if (path[0] == '/') { /*参数是路径名?*/
588 path = strcat(str, path);
589 } else { /*参数是目录名?*/
590 strcpy(str, user_env.current_path);
591 if(str[strlen(str)-1] != '/') {
592 strcat(str, "/");
594 path = strcat(str,path);
596 if (_stat_dele(path) == 0) {
597 return 0;
598 } else {
599 return -1;
601 } else {
602 _stat_fail_550();
603 return -1;
607 int _stat_dele(const char *path)
609 const char msg[] = "250 File sucessfully deleted.\r\n";
611 debug_printf("dele at %s \r\n",path);
612 if (unlink(path) == 0) { /*删除文件成功*/
613 _response(msg);
614 return 0;
615 } else {
616 if (errno == ENOENT) {
617 _stat_error3_501();
618 } else if (errno == ENAMETOOLONG) {
619 _stat_error2_501();
620 } else {
621 _stat_fail_450();
623 return -1;
628 *命令RETR的处理
630 static int _stat_retr(const char *path)
632 int fd;
633 ssize_t i = 0;
634 char buf[BUF_LEN]={""};
636 debug_printf("retr from %s\n",path);
638 fd = open(path, O_RDONLY);
639 if(fd != -1) {
640 if((off_t)-1 == lseek(fd, user_env.restartat, SEEK_SET)){
641 _stat_fail_501();
642 r_close(fd);
643 LOG_IT("lseek error.");
644 return -errno;
646 _stat_success_150();
647 while((i = read(fd, buf, BUF_LEN)) != 0) {
648 if (i == -1) {
649 r_close(user_env.data_fd);
650 _stat_fail_501();
651 return -1;
652 } else {
653 write(user_env.data_fd, buf, i);
654 user_env.download_kbytes += i/1000;
657 user_env.download_files++;
658 r_close(fd);
659 r_close(user_env.data_fd);
660 user_env.restartat = 0;
661 _stat_success_226();
662 return 0;
663 } else {
664 r_close(user_env.data_fd);
665 _stat_fail_501();
666 return -1;
670 int do_retr(const char *path)
672 char str[PATH_NAME_LEN]={""};
673 if (strlen(path) == 0) {
674 close(user_env.data_fd);
675 return failed("RETR");
677 if (path[0] == '/') { /*参数是路径名?*/
678 path = strcat(str, path);
679 } else { /*参数是文件名?*/
680 strcpy(str, user_env.current_path);
681 if(str[strlen(str)-1] != '/') {
682 strcat(str,"/");
684 path = strcat(str,path);
686 return _stat_retr(path);
689 /*命令执行失败*/
690 static void _stat_fail_450(void)
692 const char msg[] = "450 File operation failed.\r\n";
693 _response(msg);
696 /*出现错误*/
697 static void _stat_error_421(void)
699 const char msg[] = "421 Service not available, closing control connection.\r\n";
700 _response(msg);
701 do_quit();
704 /*命令执行失败*/
705 static void _stat_fail_550(void)
707 const char msg[] = "550 Requested action not taken. File unavailable.\r\n";
708 _response(msg);
711 /*出现错误*/
712 static void _stat_error1_501(void)
714 const char msg[] = "501 Wrong arguments, the filename exists.\r\n";
715 _response(msg);
718 static void _stat_error2_501(void)
720 const char msg[] =
721 "501 Diretory or file name is too long.\r\n";
722 _response(msg);
725 static void _stat_error3_501(void)
727 const char msg[] =
728 "501 Arguments wrong,the file or directory does not exists!\r\n";
729 _response(msg);
732 static void _stat_success_150(void)
734 const char msg[] = "150 File status okay; about to open data connection.\r\n";
735 _response(msg);
738 static void _stat_success_226(void)
740 const char msg[] = "226 Closing data connection."
741 "Requested file-action succeed.\r\n";
742 _response(msg);
745 static void _stat_fail_501(void)
747 const char msg[] = "501 Syntax error in parameters or arguments.\r\n";
748 _response(msg);
751 int do_mode(const char *arg)
753 const char succ[] = "200 MODE S OK.\r\n";
754 const char fail[] = "504 Command not implemented for that parameter.\r\n";
755 if ((strlen(arg) == 1) && ((*arg == 's') || (*arg == 'S'))) {
756 _response(succ);
757 return 0;
758 } else {
759 _response(fail);
760 return 1;
764 static int _get_local_ip_address(int sock, char* ip_addr)
766 char *ip = NULL;
767 int fd = sock, intrface;
768 struct ifreq buf[MAX_INTERFACES];
769 struct ifconf ifc;
771 ifc.ifc_len = sizeof buf;
772 ifc.ifc_buf = (caddr_t) buf;
773 if (!ioctl(fd, SIOCGIFCONF, (char*)&ifc)) {
774 intrface = ifc.ifc_len / sizeof(struct ifreq);
775 while (intrface-- > 0) {
776 if (!(ioctl(fd, SIOCGIFADDR, (char*)&buf[intrface]))) {
777 ip = (inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
778 strcpy(ip_addr, ip);
779 break;
783 return 0;
786 int do_pasv(void)
788 char ip_addr[16] = {0}; /*I think 16 is just enough.*/
789 char port_buf[64] = {0};
791 const char pasv_fail[] = "425 Can't open data connection.\r\n";
792 int sock, ret;
793 int data_port;
794 int opt = SO_REUSEADDR;
795 socklen_t i;
796 struct sockaddr_in data, cliaddr;
797 unsigned int port, port1, port2;
798 struct timeval tv;
799 char *tmp;
801 tv.tv_sec = run_env.data_connection_timeout;
802 tv.tv_usec = 0;
803 bzero(&data, sizeof(data));
804 data_port = 0;
805 data.sin_family = AF_INET; /*建立数据连接*/
806 data.sin_port = htons(data_port);
807 data.sin_addr.s_addr = htonl(INADDR_ANY);
809 i = sizeof(cliaddr);
811 sock = socket(AF_INET, SOCK_STREAM, 0);
812 if (sock < 0) {
813 LOG_IT("socket error in do_pasv().");
814 return -errno;
816 _get_local_ip_address(sock, ip_addr);
817 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));
818 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
820 if (bind(sock, (struct sockaddr *)&data, i) != 0) {
821 LOG_IT("bind error in do_pasv().");
822 return -errno;
825 getsockname(sock, (struct sockaddr *)&data, (socklen_t*) &i);
827 debug_printf("pasv: ip=%s\n", ip_addr);
828 debug_printf("pasv: port=%d\n", ntohs(data.sin_port));
830 port = ntohs(data.sin_port);
831 port1 = port / 256;
832 port2 = port % 256;
833 while ((tmp = strchr(ip_addr, '.')) != NULL) {
834 *tmp = ',';
835 tmp++;
838 if (listen(sock, 1) != 0){
839 LOG_IT("listen error in do_pasv().");
840 return -errno;
843 snprintf(port_buf, 64, "227 Entering Passive Mode (%s,%d,%d).\r\n",
844 ip_addr, port1, port2);
845 _response(port_buf);
847 ret = accept(sock, (struct sockaddr *)&cliaddr, &i);
848 if (ret!=-1) {
849 user_env.data_fd = ret;
850 //write(user_env.connect_fd, pasv_ready, strlen(pasv_ready));
851 close(sock);
852 return 0;
853 } else {
854 LOG_IT("accept error in do_pasv().");
855 _response(pasv_fail);
856 close(sock);
857 return -errno;
861 int do_syst(void)
863 const char wel[] = "215 UNIX Type: L8\r\n";
865 if (_response(wel) == -1) {
866 LOG_IT("write error in do_syst()");
867 return -1;
869 return 0;
872 int do_noop(void)
874 const char mess[] = "200 NOOP command successful.\r\n";
875 if (_response(mess) == -1) {
876 LOG_IT("write error in do_noop()");
877 return -1;
879 return 0;
882 int do_type(char *arg)
884 if (strcasecmp(arg, "I") == 0) {
885 user_env.ascii_on = FALSE;
886 _response("200 Type set to I.\r\n");
887 } else if (strcasecmp(arg, "A") == 0) {
888 user_env.ascii_on = TRUE;
889 _response("200 Type set to A.\r\n");
890 } else {
891 _response("500 Type not understood.\r\n");
892 return -1;
895 return 0;
898 int do_stru(char *arg)
900 const char fail[] = "501 'STRU' not understood.\r\n";
901 const char succ[] = "200 Structure set to F.\r\n";
902 const char unsupported[] = "504 Unsupported structure type.\r\n";
903 const char unknown[] = "501 Unrecognized structure type.\r\n";
905 if (strcmp(arg, "") == 0) {
906 _response(fail);
907 return -1;
908 } else if (strcasecmp(arg, "F") == 0) {
909 _response(succ);
910 } else if (strcasecmp(arg, "P") == 0
911 || strcasecmp(arg, "R") == 0) {
912 _response(unsupported);
913 } else {
914 _response(unknown);
917 return 0;
921 *Note! When this is called in parse_cmd, it means
922 *we received an 'ABOR' when no data connection exists.
923 *So we just reply a 226 and do nothing actually.
925 int do_abor(char *arg)
927 const char fail[] = "501 Can not understood.\r\n";
928 const char succ[] = "226 Abort successful.\r\n";
930 if (strcmp(arg, "") != 0) {
931 _response(fail);
932 return -1;
933 } else {
934 _response(succ);
937 return 0;
940 int do_cwd(const char *dir)
942 if (_chdir(dir) < 0) {
943 return -1;
944 } else {
945 _response("250 Command ok.\r\n");
946 return 0;
950 int do_cdup(void)
952 if (_chdir("..") < 0) {
953 return -1;
954 } else {
955 _response("250 CDUP Command ok.\r\n");
956 return 0;
960 int do_pwd(void)
962 char mess[PATH_NAME_LEN+6] = {0};
963 snprintf(mess, 4096, "257 \"%s\"\r\n", user_env.current_path);
964 _response(mess);
965 return 0;
968 int do_rnfr(void)
970 if (_response("350 Please send the RNTO command.\r\n") < 0) {
971 return -1;
972 } else {
973 return 0;
977 int do_rnto(const char *old_path, const char *new_path)
979 const char *mess;
981 debug_printf("%s,%s\n", old_path, new_path);
983 if (user_env.enable_upload != 1) {
984 _response("550 Permission denied!\r\n");
985 return -1;
988 if (rename(old_path, new_path) < 0) {
989 switch (errno) {
990 case EISDIR:
991 mess = "553 New path is a directory.\r\n";
992 break;
993 case EBUSY:
994 mess = "502 The files are in use now.\r\n";
995 break;
996 default:
997 mess = "501 Can't rename this file.\r\n";
999 _response(mess) ;
1000 return -1;
1002 _response("250 Command succeed.\r\n") ;
1003 return 0;
1006 int do_port(char *arg)
1008 struct sockaddr_in client;
1009 int error;
1010 int retval;
1011 int sock;
1012 int port;
1013 char *addr;
1015 if (_analysis_ipaddr(arg, &addr, &port) < 0) {
1016 return -1;
1019 debug_printf("addr=%s, port=%d\n", addr, port);
1021 bzero((char *)&client, sizeof(client)) ;
1022 client.sin_port = htons(port);
1023 if (inet_pton(AF_INET, addr, &client.sin_addr) == 0) {
1024 _response("501 Incorrect IP address.\r\n");
1025 return -1;
1027 if (strcmp(user_env.port_ip, addr) != 0){
1028 strcpy(user_env.port_ip, addr);
1029 user_env.port_connections = 0;
1031 if (strcmp(user_env.port_ip, user_env.client_ip) != 0){
1032 if (user_env.port_connections >= run_env.max_port_connections){
1033 _response("421 Failed to create data connection.\r\n");
1034 return -1;
1035 } else {
1036 user_env.port_connections++;
1038 debug_printf("user_env.port_connections=%d\n", user_env.port_connections);
1042 client.sin_family = AF_INET;
1043 if ((_ignore_sigpipe() == -1) || ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)) {
1044 _response("421 Failed to create data connection.\r\n");
1045 return -1;
1047 if (((retval = connect(sock, (struct sockaddr *)&client, sizeof(client))) == -1)
1048 && ((errno == EINTR)||(errno == EALREADY))) {
1049 /*create data connection*/;
1051 if (retval == -1) {
1052 error = errno;
1053 while ((close(sock) == -1) && (errno == EINTR))
1054 /*do nothing*/;
1055 errno = error;
1056 _response("421 Failed to create data connection.\r\n");
1057 return -1;
1059 _response("200 Succeed to create data connection.\r\n");
1060 user_env.data_fd = sock;
1061 return 0;
1064 static int _ignore_sigpipe(void)
1066 struct sigaction act;
1067 if (sigaction(SIGPIPE, (struct sigaction *)NULL, &act) == -1) {
1068 return -1;
1070 if (act.sa_handler == SIG_DFL) {
1071 act.sa_handler = SIG_IGN;
1072 if (sigaction(SIGPIPE, &act, (struct sigaction *)NULL) == -1) {
1073 return -1;
1076 return 0;
1079 static int _analysis_ipaddr(char *str, char **re_addr, int *re_port)
1081 static char addr[16];
1082 int ip[4];
1083 int port[2];
1084 int m;
1085 int i = 0;
1086 sscanf(str, "%d,%d,%d,%d,%d,%d", &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]);
1087 while (i < 4) {
1088 if ((ip[i] > 255) || (ip[i] < 0)) {
1089 return -1;
1091 i++;
1093 m = port[0]*256 + port[1];
1094 if (m < 0) {
1095 return -1;
1097 snprintf(addr, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1098 *re_port = m;
1099 *re_addr = addr;
1100 return 0;
1103 #define RESPONSE(msg) write(user_env.connect_fd, msg, strlen(msg))
1105 static int _response(const char *buf)
1107 ssize_t byteswrite;
1109 while (((byteswrite = RESPONSE(buf)) == -1)
1110 && (errno == EINTR))
1111 /*do nothing here*/;
1112 if (byteswrite < 0) { /*If cannot write to socket,close all connections and kill the process*/
1113 r_close(user_env.connect_fd);
1114 /*?是否要关闭数据连接?*/
1115 write_log("remote host did not responsed.", 1);
1117 return (int)byteswrite;
1120 static int _chdir(const char *cdir)
1122 char *buffer = NULL;
1123 int size = 128;
1125 if (strlen(cdir) == 0) {
1126 strcpy(user_env.current_path,"/");
1127 if (chdir("/") < 0) {
1128 write_log("chdir failed", 0);
1129 return -1;
1131 return 0;
1134 if ((chdir(cdir)) < 0) {
1135 LOG_IT("chdir error in _chdir().");
1136 _response("501 Can't change directory.\r\n");
1137 return -1;
1139 /*检查是否合法,即是否超过根目录*/
1140 if ((buffer = malloc(size)) == NULL) {
1141 _response("550 Can't change directory.\r\n");
1142 LOG_IT("malloc failed");
1143 return -1;
1145 while ((getcwd(buffer, size) == NULL) && (errno == ERANGE)){
1146 size *= 2;
1147 if((buffer = realloc(buffer, size)) == NULL){
1148 _response("550 Can't change directory.\r\n");
1149 LOG_IT("realloc failed");
1150 return -1;
1154 strcpy(user_env.current_path, buffer);
1155 free(buffer);
1156 return 0;
1159 int do_quit(void)
1161 const char wel[] = "221 Goodbye.\r\n";
1163 while ((write(user_env.connect_fd, wel, strlen(wel))) == -1) {
1164 LOG_IT("write error in do_quit().");
1166 close(user_env.connect_fd);
1167 free_sources();
1168 kill(getpid(), SIGTERM);
1169 return 0;
1172 int do_stor(char *arg)
1174 char pathname[MAX_PATH]={""};
1175 const char stor_ok[] = "226 Transfer complete.\r\n";
1176 int sd, fd;
1177 ssize_t rsize;
1178 char buff[BUF_LEN];
1181 debug_printf("****STOR; arg = %s\n", arg);
1182 debug_printf("user_env.current_path=%s\n", user_env.current_path);
1184 if (!user_env.enable_upload) {
1185 _response("550 Permission denied.\r\n");
1186 close(user_env.data_fd);
1187 write_log("Attempt to write.", 1);
1188 return -1;
1190 if (arg[0]=='/'){
1191 #if 0
1192 strcpy(pathname, run_env.ftp_dir);
1193 if(pathname[strlen(pathname)-1] != '/') {
1194 strcat(pathname, "/");
1196 if(arg[0]=='/') {
1197 arg++;
1199 #endif
1200 strncpy(pathname, arg, MAX_PATH);
1202 else {
1203 strcpy(pathname, user_env.current_path);
1204 if(pathname[strlen(pathname)-1] != '/')
1205 strcat(pathname, "/");
1206 strcat(pathname, arg);
1209 debug_printf("pathname=%s.\n", pathname);
1211 fd = open(pathname, O_RDWR|O_CREAT);
1212 if (fd < 0) {
1213 _response("550 Permission denied.\r\n");
1214 close(user_env.data_fd);
1215 LOG_IT("Open error in do_stor().");
1216 return -errno;
1218 if ((off_t)-1 == lseek(fd, user_env.restartat, SEEK_SET)){
1219 r_close(fd);
1220 LOG_IT("lseek error in do_stor().");
1221 return -errno;
1223 _stat_success_150();
1225 debug_printf("%s create OK! \n", pathname);
1227 sd = user_env.data_fd;
1228 for (;;) {
1229 rsize = read(sd, buff, BUF_LEN);
1230 if(rsize == 0) {
1231 break;
1233 if (rsize < 0) {
1234 LOG_IT("Read socket error in do_stor().");
1235 break;
1237 write(fd, buff, rsize);
1238 user_env.upload_kbytes += rsize/1024;
1240 r_close(fd);
1241 r_close(sd);
1242 user_env.restartat = 0;
1243 _response(stor_ok);
1244 user_env.upload_files++;
1246 debug_printf("%d files, %d KB.\n", user_env.upload_files, user_env.upload_kbytes);
1248 return 0;
1251 int do_rest(const char *arg)
1253 char *endptr;
1254 const char failed_msg[] = "501 REST needs a numeric parameter\r\n";
1255 const char succ_msg[] = "350 Restarting successfully."
1256 " Send STORE or RETRIEVE to initiate transfer\r\n";
1257 const char restrict_msg[] = "501 REST: Resuming transfers not"
1258 " allowed in ASCII mode\r\n";
1260 user_env.restartat = (off_t) strtoull(arg, &endptr, 10);
1261 if (*endptr != 0 || user_env.restartat < (off_t) 0) {
1262 user_env.restartat = 0;
1263 _response(failed_msg);
1264 return -1;
1265 } else {
1266 if (user_env.ascii_on && user_env.restartat != 0) {
1267 _response(restrict_msg);
1268 return 0;
1269 } else {
1270 _response(succ_msg);
1271 return 0;
1276 int do_size(const char *name)
1278 const char fail_msg[] = "550 Could not get file size.\r\n";
1279 const char fail_msg2[] = "550 I can only retrieve regular files.\r\n";
1280 char buf[MAX_MSG_LEN] = {0,};
1281 struct stat st;
1283 if (!*name) {
1284 _response(fail_msg);
1285 return -1;
1286 } else if (stat(name, &st)) {
1287 _response(fail_msg);
1288 write_log("stat failed.", 0);
1289 return -errno;
1290 } else if (!S_ISREG(st.st_mode)) {
1291 _response(fail_msg2);
1292 return -1;
1293 } else {
1294 snprintf(buf, MAX_MSG_LEN, "%d %llu\r\n", 213,
1295 (unsigned long long)st.st_size);
1296 _response(buf);
1297 return 0;
1301 int do_site(const char *args)
1303 const char ok_msg[] = "200 Command okay.\r\n";
1304 const char bad_msg[] = "550 Bad file.\r\n";
1305 const char fail_msg[] = "550 Can't chmod.\r\n";
1306 const char no_msg[] = "502 Command not implemented.\r\n";
1308 if (!strncasecmp("CHMOD ", args, 6)) {
1309 mode_t mode;
1310 const char *filename;
1311 char buf[256] = {0,};
1313 if (!user_env.enable_upload) {
1314 _response("550 Permission denied.\r\n");
1315 LOG_IT("Attempt to write.");
1316 return -3;
1318 args += 5;
1319 mode = strtol(args, (char**)&args, 8);
1320 while (*args && isspace(*args))
1321 args++;
1322 filename = args;
1324 debug_printf("mode=%d, filename=%s\n", mode, args);
1326 if (!getcwd(buf, 128) ||
1327 strlen(buf) + strlen(filename) + 1 > 256) {
1328 _response(bad_msg);
1329 return -1;
1332 strcat(buf, "/");
1333 strcat(buf, filename);
1334 if (-1 == chmod(filename, mode)) {
1335 _response(fail_msg);
1336 LOG_IT("chmod failed");
1337 return -errno;
1339 _response(ok_msg);
1340 return 0;
1341 } else {
1342 _response(no_msg);
1343 return -2;
1347 int do_help(const char *args)
1349 const char help[] = "214-The following commands are implemented.\r\n"
1350 "214-USER QUIT PASS SYST HELP PORT PASV LIST\r\n"
1351 "214-NLST RETR STOR TYPE MKD RMD DELE PWD\r\n"
1352 "214-CWD SITE CDUP RNFR RNTO NOOP NLST\r\n"
1353 "214 End of list.\r\n";
1354 const char helpsite[] = "214-The following SITE commands are implemented.\r\n"
1355 "214-CHMOD HELP\r\n"
1356 "214 End of list.\r\n";
1357 const char nohelp[] = "214 There is no help for that command.\r\n";
1359 if (!strlen(args)) {
1360 _response(help);
1361 } else if (!strcasecmp(args, "SITE")) {
1362 _response(helpsite);
1363 } else {
1364 _response(nohelp);
1366 return 0;
1369 /*reply to wrong or unsupported commands*/
1370 int failed(const char *s)
1372 char msg500[MAX_PATH] = {0};
1373 int m_len;
1375 if ((m_len = snprintf(msg500, MAX_PATH,
1376 "500 \'%s\' command is not supported.\r\n", s)) >= MAX_PATH || m_len == -1) {
1377 write_log("Too long command got.", 0);
1378 return -1;
1380 _response(msg500);
1381 return 0;
1383 /*implement of NLST*/
1384 int do_nlst(const char *path)
1386 int i = 0;
1387 const char finish[] = "226 Transfer complete.\r\n";
1388 const char success[] = "150 Opening ASCII mode data connection for file list.\r\n";
1389 const char fail[] = "450 No such file or directory.\r\n";
1390 struct stat stat_buf;
1391 DIR *target_dir;
1392 struct dirent *direntp;
1393 char buf[BUF_LEN] = {0};
1394 char **words;
1395 wordexp_t wxp;
1397 if (path[0]!='/') {
1398 if ((user_env.current_path[strlen(user_env.current_path) - 1]) == '/') {
1399 snprintf(buf, BUF_LEN, "%s%s", user_env.current_path, path);
1400 } else {
1401 snprintf(buf, BUF_LEN, "%s/%s", user_env.current_path, path);
1404 else {
1405 snprintf(buf, BUF_LEN, "%s", path);
1408 debug_printf("buf=\'%s\'\n", buf);
1410 wordexp(buf, &wxp, 0);
1411 words = wxp.we_wordv;
1413 if (wxp.we_wordc < 1) {
1414 _response(fail);
1415 goto end;
1418 _response(success);
1420 if (wxp.we_wordc == 1) {
1421 if (stat(words[0], &stat_buf) == -1) {
1422 _response(fail);
1423 goto end;
1425 else {
1426 if (S_ISDIR(stat_buf.st_mode)) {
1427 _path_tail(buf, words[0]);
1428 if((target_dir = opendir(buf)) == NULL) {
1429 goto end;
1431 while ((direntp = readdir(target_dir)) != NULL && direntp->d_name[0] != '.') {
1432 write(user_env.data_fd, direntp->d_name, strlen(direntp->d_name));
1433 write(user_env.data_fd, "\n", 1);
1435 goto suc;
1440 for (i = 0; i < (int)wxp.we_wordc; i++) {
1441 if (words[i][0] != '.') {
1442 write(user_env.data_fd, words[i], strlen(words[i]));
1443 write(user_env.data_fd, "\n", 1);
1447 suc: _response(finish);
1449 end: close(user_env.data_fd);
1450 wordfree(&wxp);
1451 return 0;
1454 static void _path_tail(char *buf, char *name)
1456 int flag = 1;
1457 int i = 0;
1459 if ((strlen(buf) + strlen(name)) >= BUF_LEN) {
1460 flag = 0;
1463 for (i = strlen(buf); i >= 0; i--) {
1464 if (buf[i] == '/') {
1465 buf[i] = '\0';
1466 break;
1470 if (flag) {
1471 strcat(buf, name); /*It's safe*/
1474 /*end of implement NLST*/