背景:
阅读文章

MUD FTP服务器的修缮(上)

[日期:2007-05-10] 来源:  作者: [字体: ]

   毛病找到了,开始着手解决吧!先查找这些错误的格式是在哪里生成的。查找什么呢?想想:我们给 FTP 服务器发送了 ls 命令,那么 FTP 服务器必定有接收这
个命令的程序。这里需要一点 FTP 方面的知识,就是当我们对着服务器敲 ls 命
令时,我们的程序向服务器发送的实际是 nlst 命令,ls -l 实际就是 nlst -l
了,而敲 dir 命令实际发送的是 list 命令,所以,我们应该在 FTP 服务器的


源程序 /adm/daemons/ftpd.c 中查找字符串 "nlst" 或字符串 "list",找到接
收 ls 和 dir 命令的入口。
在 UltraEdit 编辑器里一找,就能找到如下的程序段:
……
     case "size": // return size of file
……
     case "nlst": // give name list of files in directory
           ^^^^对 ls 命令的处理就在这里了
        CHECK_LOGIN();
        /* Send name list */
        if ((i = sizeof(command)) > 1 && command[1] == "-l") {
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -l 参数就在这里了^^
          if (i == 2)
             command[1] = ".";
          else
             command = ({ command[0] }) + command[2..s-1];
          // and fall through to "list"
        } else {
          /* Used by commands like "dir", "mget", and "mput" */
          if ( i > 1 )
             tmp = get_path( fd, command[ 1 ] );
          else
             tmp = socket_info[ fd ][ CWD ];

          if ( check_valid_read( tmp, fd ) ) {
             tmp2 = ls( tmp, 1, fd );
             ^^^^^^^^^^^^^^^^^^^^^^^注意这一行,调用了 ls() 函数,没错,
             ^^^^^^ ls() 函数就是用来从磁盘读取目录列表的,跟进去看看吧
             if (tmp2 == "")
               socket_write( fd, "550 No files found.\n" );
             else
               data_conn( fd, tmp2, "ls", STRING );
          } else
             PERMISSION_DENIED550(command[ 1 ]);
          break;
        }
     case "list": // give list files in a directory
           ^^^^对 dir 命令的处理就在这里了
        CHECK_LOGIN();
        /* Send directory file list (like exec'ing "ls") */
        if ( sizeof( command ) > 1 )
          tmp = get_path( fd, command[ 1 ] );
        else
          tmp = socket_info[ fd ][ CWD ];
        if ( check_valid_read( tmp, fd ) ) {
          tmp2 = ls( tmp, 0, fd );
          ^^^^^^^^^^^^^^^^^^^^^^这一行也调用了 ls() 函数,与前面不同之处
          ^^^^^^^^^^^^ 是第二个参数,前面是 1 ,这里是 0 ,等会儿再研究它
          if (tmp2 == "")
             socket_write( fd, "550 No files found.\n");
          else
             data_conn( fd, tmp2, "ls", STRING );
        } else
          PERMISSION_DENIED550(command[ 1 ]);
        break;
     case "xpwd": // print the current working directory (deprecated)
……

看看 ls() 函数的内容:
string ls( string path, int column, int fd ) {
   string *files;
   int i, j, s;
   mixed *xfiles;
   mixed *stats;
   string tmp, tmp2, creator, domain;

   /* if path is a directory get contents */
   if ( directory_exists( path ) ) {
     if ( path[ strlen( path ) - 1 ] == '/' )
        path += "*";
     else
        path += "/*";
   } // 这个判断的意思是说如果 ls 的目标是一个目录,就在它的尾巴
     // 加上个记号,确保它以 "/*" 结尾,在后面的程序中好跟文件区别

   /* begin narrow columnar "nlst" */
   if (column) { // 前面不是说了么,调用 ls() 函数的两个地方参数
                 // 不同,这里是处理参数为 1 的地方
     files = get_dir( path );
     ^^^^^^^^^^^^^^^^^^^^^^^瞧,开始读磁盘目录列表了

     /* can only happen if permissions are messed up at account level */
     if (!files)
        return ""; // 如果目录里什么都没有,那当然传回一个空串喽

     files -= ({ ".", ".." }); // 去掉这两个多余的项

     if (!(i = sizeof( files )))
        return "";

     /* no wild cards...must have been the exact pathname to a file */
     if (strsrch(path, '*') == -1 && strsrch(path, '?') == -1) {
        return files[0] + "\n";
     } // 前面加的目录尾巴记号 "/*" 在这里起作用了

     /* remove globber at end of path, leave a trailing slash */
     j = strsrch(path, '/', -1);
     path = path[0..j];
     while ( i-- ) {
        /* scan next level down for files */
        tmp = sprintf("%s%s/", path, files[i]);
        if ( directory_exists( tmp ) ) {
          files[i] += "/"; // 瞧,这里判断如果一个目录项是目录的话,就
        }                  // 给它加上尾巴 "/" ,虽然好跟文件项区分,却
     }                     // 是多余的
     return implode( files, "\n" ) + "\n";
   }

以下就是处理 ls() 的第二个参数为 0 的地方了,也就是要返回详细目录列表的地方。
   /* begin long "list" */ // 详细列表的显示
   xfiles = get_dir( path, -1 );
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^读取磁盘目录列表
   if (!xfiles || !(s = sizeof( xfiles )))
     return "";
   files = allocate(s);
   // the Unix-like file permissions are mainly for effect...hopefully it
   // isn't too much, since anything more would likely be too cpu intensive
   // and cause it to max eval...
   creator = (string)MASTER_OB->creator_file(path);
   if (!creator)  creator = ROOT_UID;
   domain = (string)MASTER_OB->domain_file(path);
   if (!domain)  domain = ROOT_UID;
上面这一小段程序涉及 creater 和 domain ,指的是文件的所有权问题

   i = strsrch(path, '/', -1);
   if (i >= 0)
     path = path[0..i];
   for (i = 0; i < s; i++) {
     /* process timestamp */
     tmp2 = ctime((xfiles[i])[2]); /* get last modified timestamp */
     if ((xfiles[i])[2] + SECS_IN_YEAR < time()) {
        /* MMM DD  YYYY */
        tmp = sprintf("%s  %s", tmp2[4..9], tmp2[20..23]);
     } else {
        /* MMM DD hh:mm */
        tmp = tmp2[4..15];
     }
     j = (xfiles[i])[1];   /* get filesize */ // 获取目录项的字节数
     if (j == -2) { // 字节数为 -2 ,不是文件是目录
        files[i] = sprintf("drwxrwsr-x  %12s  %12s    <DIR>  %12s  %s/",
            creator, domain, tmp, (xfiles[i])[0]);
        !!^^^^^^^看看这一串目录格式定义,就是我们千辛万苦要找的东东了
     } else { // 除了目录就是文件了
        stats = stat(path + (xfiles[i])[0]);
        files[i] = sprintf("-rw%crw-r--  %12s  %12s  %8d  %12s  %s",
            stats[2] ? 'x' : '-', /* 'x' if loaded, else ' ' */
            creator, domain, j, tmp, (xfiles[i])[0]);
        !!^^^^^^^这一串文件格式定义也不合标准
     }
   }
   return sprintf( "%-#70s\n", implode( files, "\n" ) );
   ^^^^最后,把“自定义格式”的目录项传回调用它的地方,最终传回客户端
}
至此可以告一段落了,MUD FTP 服务器传回的目录项格式不正确的根源已经找到
了,将这些格式稍作修改,使之“基本”符合标准就OK了(说“基本”是因为
我没有翻过关于 FTP 协议一系列标准的典籍,自己改的是否真的符合标准我也
不清楚,:-) 另外,CuteFTP 喜欢发 list -a -l 命令,多了个 -a 参数,也
得考虑进去的喔!

有兴趣的你大可一试,有什么新发现请务必告诉我,先谢过了!

(未完待续)

尊重作者 转载请注明出处52mud.com

收藏 推荐 打印 | 录入:sbso | 阅读:
相关内容      
内容推送
52mud提供
一起回忆泥巴游戏QQ群68186072
52mud官方微信公众平台
热门评论