毛病找到了,开始着手解决吧!先查找这些错误的格式是在哪里生成的。查找什么呢?想想:我们给 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