自己动手写ftp服务器2(更进一步)
Tofloor
poster avatar
zhqh100
deepin
2014-04-30 08:29
Author
前面基本把框架搭建好,后面的逻辑处理就比较简单了,一步一步对功能进行完善。
为了简化代码,对于一些合法性校验都是尽量简化,只处理核心功能。

封装是编程的最基本思想,不过我这里还是尽量减少封装,因为较多的封装的话在看代码时会不得不停顿下来找调用到的地方,增加看代码的难度。

开发语言:C语言
操作系统:LinuxDeepin 2013

增加了后面的一点交互,代码如下:
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #define bool unsigned short
  9. typedef struct cmd_opts {
  10.         bool daemonize;
  11.         bool listen_any;
  12.         char *listen_addr;
  13.         int port;
  14.         int max_conn;
  15.         int userid;
  16.         char *chrootdir;
  17. }cmd_opts;
  18. //判断字符是否是以某指令开始
  19. int strBeginWith(char *str,char *substr){
  20.         while(*str && *substr && *str==*substr){
  21.                 str++;
  22.                 substr++;
  23.         }
  24.         if(*substr=='\0')
  25.                 return 1;
  26.         else
  27.                 return 0;
  28. }
  29. //字符串转int
  30. int toint(char *str) {
  31.         if (strlen(str) < 1)
  32.         {
  33.                 return -1;
  34.         }
  35.         int retVal = str[0] - '0';
  36.         int i = 1;
  37.         while(str[i] >= '0' && str[i] <= '9')
  38.         {
  39.                 retVal = retVal * 10 + str[i] - '0';
  40.                 i++;
  41.         }
  42.         return retVal;
  43. }
  44. //向客户端发送消息
  45. int send_repl(int send_sock,char *msg) {
  46.         printf("Send Message:%s\n", msg);
  47.         if (send(send_sock, msg, strlen(msg),0) < 0) {
  48.                 perror("send");
  49.                 close(send_sock);
  50.                 return -1;
  51.         }
  52.         return 0;
  53. }
  54. //创建数据传输的连接,用于处理"LIST","STOR","RETR"等请求
  55. int make_client_connection(int client_port,const char* client_addr) {
  56.         int sock=-1;
  57.         struct sockaddr_in servaddr;
  58.         servaddr.sin_family = AF_INET;
  59.         servaddr.sin_addr.s_addr = inet_addr(client_addr);
  60.         servaddr.sin_port = htons (client_port);
  61.         if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  62.                 send_repl(sock,"425 Can't open data connection.\r\n");
  63.                 perror("socket");
  64.                 return -1;
  65.         }
  66.         int status = connect (sock, (struct sockaddr *)&servaddr, sizeof (servaddr));
  67.         if(status!=0) {
  68.                 send_repl(sock,"425 Can't open data connection.\r\n");
  69.                 perror("connect");
  70.                 return -1;
  71.         }
  72.         return sock;
  73. }
  74. enum {
  75.         TRUE=1,
  76.         FALSE=0
  77. };
  78. int main(int argc,char *argv[])
  79. {
  80.         struct cmd_opts *opts= malloc(sizeof(struct cmd_opts));
  81.         //设置一些初始配置
  82.         opts->daemonize    = FALSE;
  83.         opts->listen_any   = TRUE;
  84.         opts->port         = 21;
  85.         opts->userid       = 0;
  86.         opts->chrootdir    = "./";
  87.         opts->max_conn     = 5;
  88.         opts->listen_addr  = NULL;
  89.         struct sockaddr_in servaddr;
  90.         memset((char *)&servaddr, 0, sizeof(servaddr));
  91.         //TCP协议
  92.         servaddr.sin_family = PF_INET;
  93.         servaddr.sin_addr.s_addr =  htonl(INADDR_ANY);
  94.         servaddr.sin_port = htons (opts->port);
  95.         int servaddr_len = sizeof(servaddr);
  96.         int sock;
  97.         if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  98.                 perror("socket");
  99.                 return -1;
  100.         }
  101.         int flag = 1;
  102.         setsockopt(sock, SOL_SOCKET,SO_REUSEADDR,(char *) &flag, sizeof(int));
  103.        
  104.         setsockopt(sock, IPPROTO_TCP,TCP_NODELAY,(char *) &flag, sizeof(int));
  105.         //套接字绑定端口和地址
  106.         if(bind (sock, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
  107.                 perror("bind");
  108.                 return -1;
  109.         }
  110.         if(listen(sock,opts->max_conn) <0) {
  111.                 perror("listen");
  112.                 return -1;
  113.         }
  114.         int connection;
  115.         //死循环等待客户端连接
  116.         while(1){
  117.                 printf("Begin to accept\n");
  118.                 if ((connection = accept(sock, (struct sockaddr *) &servaddr, &servaddr_len)) < 0) {
  119.                         perror("accept");
  120.                         return -1;
  121.                 }
  122.                 int pid = fork();
  123.                 char msg[]= "220 Service ready for new user.\r\n";
  124.                 char send_buff[1000];
  125.                 char read_buff[1000];
  126.                 char current_dir[1000];
  127.                 //客户端IP地址和端口号
  128.                 char client_addr[16];
  129.                 int client_port;
  130.                 //临时指针
  131.                 char *temp;
  132.                 int i;
  133.                 int client_fd;
  134.                 strcpy(current_dir,opts->chrootdir);
  135.                 //获取当前绝对路径
  136.                 getcwd(current_dir,sizeof(current_dir));
  137.                 int len = strlen(msg);
  138.                 if(pid==0) {
  139.                         //客户端连接后,服务器先主动发送200 Service ready
  140.                         if (send(connection, msg, len,0) < 0) {
  141.                                 perror("send");
  142.                                 close(connection);
  143.                                 return -1;
  144.                         }
  145.                         while(1){
  146.                                 //初始化读取buffer和发送buffer
  147.                                 memset(send_buff,0,sizeof(send_buff));
  148.                                 memset(read_buff,0,sizeof(read_buff));
  149.                                 //等待接收客户端指令
  150.                                 int recvbuff = recv(connection,read_buff,sizeof(read_buff),0);
  151.                                 if(recvbuff<1) {
  152.                                         return -1;
  153.                                 }
  154.                                 if(recvbuff==sizeof(read_buff)) {
  155.                                         return -2;
  156.                                 }
  157.                                 printf("Received:%s",read_buff);
  158.                                 if (strBeginWith(read_buff,"USER")){
  159.                                         //收到客户端发送的用户名,这里不处理用户名,均答复登陆成功
  160.                                         //注释代码也可用,只是这里修改后,发送的消息更精简了
  161.                                         //后面的人性化提示可以省略,但\r\n不可省略,经Filezilla和 CuteFTP客户端验证,必须把\r\n带上才可正常识别
  162.                                         // if (send_repl(connection, "331 Anonymous login okay, send your complete email as your password.\r\n") < 0)
  163.                                         if(send_repl(connection,"331 Anonymous\r\n") < 0)
  164.                                                 return -1;
  165.                                 }else if(strBeginWith(read_buff,"PASS")){
  166.                                         //可以看到ftp协议发送的密码均为明文发送
  167.                                         //这里不做密码校验,均答复已登录
  168.                                         // if (send_repl(connection, "230 User logged in, proceed.\r\n") < 0)
  169.                                         if (send_repl(connection, "230 User\r\n") < 0)
  170.                                                 return -1;
  171.                                 }else if (strBeginWith(read_buff,"SYST")){
  172.                                         //问操作系统类型
  173.                                         //下面UNIX的可替代选项参考 http://www.iana.org/assignments/operating-system-names/operating-system-names.xhtml
  174.                                         if (send_repl(connection, "215 UNIX\r\n") < 0)
  175.                                                 return -1;
  176.                                 }else if (strBeginWith(read_buff,"PWD")){
  177.                                         //当前路径
  178.                                         sprintf(send_buff,"257 %s\r\n",current_dir);
  179.                                         if (send_repl(connection, send_buff) < 0)
  180.                                                 return -1;
  181.                                 }else if (strBeginWith(read_buff,"PORT")){
  182.                                         //客户端发来的IP和端口号
  183.                                         char *temp = strtok(read_buff + 5,",");
  184.                                         strcpy(client_addr, temp);
  185.                                         for (i = 0; i < 3; ++i)
  186.                                         {
  187.                                                 strcat(client_addr, ".");
  188.                                                 temp = strtok(NULL, ",");
  189.                                                 strcat(client_addr, temp);
  190.                                         }
  191.                                         printf("client_addr = %s\n", client_addr);
  192.                                         temp = strtok(NULL, ",");
  193.                                         client_port = toint(temp);
  194.                                         temp = strtok(NULL, ",");
  195.                                         client_port = client_port * 256 + toint(temp);
  196.                                         printf("client_port = %d\n", client_port);
  197.                                         //回复传说中的200 OK
  198.                                         if (send_repl(connection, "200 \r\n") < 0)
  199.                                                 return -1;
  200.                                 }else if (strBeginWith(read_buff,"LIST")){
  201.                                         //当前路径
  202.                                         client_fd = make_client_connection(client_port, client_addr);
  203.                                         if (client_fd != -1){
  204.                                         }
  205.                                 }else{
  206.                                         //其他一律回复500,I don't know
  207.                                         if (send_repl(connection, "500 \r\n") < 0)
  208.                                                 return -1;
  209.                                 }
  210.                         }
  211.                 } else if(pid>0) {
  212.                         close(connection);
  213.                 }
  214.         }
  215.         return 0;
  216.        
  217. }
Copy the Code

用CuteFTP客户端登陆,打印信息如下:
  1.                 *** CuteFTP 9.0 - build Jun 25 2013 ***
  2. STATUS:>          [2014/4/30 0:28:13] Getting listing ""...
  3. STATUS:>          [2014/4/30 0:28:13] Connecting to FTP server... 192.168.11.29:21 (ip = 192.168.11.29)...
  4. STATUS:>          [2014/4/30 0:28:13] Socket connected. Waiting for welcome message...
  5.                 [2014/4/30 0:28:13] 220 Service ready for new user.
  6. STATUS:>          [2014/4/30 0:28:13] Connected. Authenticating...
  7. COMMAND:>        [2014/4/30 0:28:13] USER anonymous
  8.                 [2014/4/30 0:28:13] 331 Anonymous
  9. COMMAND:>        [2014/4/30 0:28:13] PASS *****
  10.                 [2014/4/30 0:28:13] 230 User
  11. STATUS:>          [2014/4/30 0:28:13] Login successful.
  12. COMMAND:>        [2014/4/30 0:28:13] SYST
  13.                 [2014/4/30 0:28:13] 215 UNIX
  14. STATUS:>          [2014/4/30 0:28:13] Host type detected: Unix.
  15. COMMAND:>        [2014/4/30 0:28:13] PWD
  16.                 [2014/4/30 0:28:13] 257 /home/taiji/桌面
  17. STATUS:>          [2014/4/30 0:28:13] Home directory: /home/taiji/桌面
  18. COMMAND:>        [2014/4/30 0:28:13] FEAT
  19.                 [2014/4/30 0:28:13] 500
  20. STATUS:>          [2014/4/30 0:28:13] This site doesn't support the 'features' command.
  21. STATUS:>          [2014/4/30 0:28:13] Setting up character encoding.
  22. COMMAND:>        [2014/4/30 0:28:13] OPTS UTF8 on
  23.                 [2014/4/30 0:28:13] 500
  24. STATUS:>          [2014/4/30 0:28:13] Using local encoding.
  25. COMMAND:>        [2014/4/30 0:28:13] REST 100
  26.                 [2014/4/30 0:28:13] 500
  27. STATUS:>          [2014/4/30 0:28:13] This site cannot resume broken downloads.
  28. COMMAND:>        [2014/4/30 0:28:13] PASV
  29.                 [2014/4/30 0:28:13] 500
  30. STATUS:>          [2014/4/30 0:28:13] PASV failed, trying PORT.
  31. COMMAND:>        [2014/4/30 0:28:13] PORT 192,168,11,16,209,127
  32.                 [2014/4/30 0:28:13] 200
  33. COMMAND:>        [2014/4/30 0:28:13] LIST
Copy the Code
Reply Favorite View the author
All Replies

No replies yet