2011-12-03 | #1 |
高级会员
注册: 08年04月11日
来自: 盘丝洞
帖子: 311
声望力: 20
声望:
50
现金:29两梁山币
资产:1245两梁山币
致谢数: 0
获感谢文章数:0
获会员感谢数:0 |
MUD FTP服务器的修缮
发信人: fof (格斗之迷~思考中), 信区: wiz 标 题: MUD FTP 服务器的修缮 发信站: 交通大学思源BBS (Thu Nov 16 00:24:33 2000), 转信 写这篇文章的目的一是介绍修改 MUD 的 FTP 功能使 CuteFTP 能正常显示 FTP 服务器的目录的方法(不一定是最好的喔),二是希望这种方法能为有 兴趣者发现问题和解决问题提供一个参照。 一切都是因为要对运行中的 MUD 的部分程序进行修改而起的。 当运行中的 MUD 需要作比较小的改动,小到不值得将 MUD 服务器重新 启动一次时,有两种方法可以实现:一是使用 MUDOS 的 ed 命令(MUDOS 最先是在 UNIX 系统下开发出来的,因此行编辑的命令叫 ed 而不是 edit, 当然我们不用管这个,只管敲 edit 就行了,edit motd、edit here...... 随叫随到,但不大好用,尤其是编辑大文件 :-( );另一个方法就是通过 MUD 的 FTP 功能下载——编辑——上传,先载入 MUD 的 FTP 服务程序, 通常是 /adm/daemons/ftpd.c ,在 /adm/etc/preload 文件中增加一行: /adm/daemons/ftpd 就可以在 MUD 启动的时候连带启动 FTP 服务器,也可以在 MUD 运行的过程 中让有权限的巫师使 update /adm/daemons/ftpd.c 来加载 FTP 服务器。接 着使用 CuteFTP 之类的软件连接到 MUD 的 FTP 服务器,下载源程序到自己 的机器上,用 UltraEdit 等功能较强、使用较方便的编辑器来修改,随你怎 么改都行,但别忘了存盘 :-) 改好了再用 CuteFTP 传回 MUD 服务器,最后 update 使改动生效。 CuteFTP 是很优秀的软件,但不是万能的,它的作者没有专门为 MUD 的 FTP 服务器考虑过(其实应该是 MUD 的 FTP 程序的作者没有认真考虑过啦), 便导致了用 CuteFTP 连接 MUD 的 FTP 服务器时,默认的目录显示方式不能 正常的显示服务器端的目录结构,以 FTPD V5.8 为例,一个目录都显示不出 来,真……把 CuteFTP的目录显示方式设为简单方式(编辑连接的 Advanced 属性,把 Simple directory listing 勾上)只显示文件、目录名,忽略文件 大小、创建日期等)稍微好些,但切换目录还是很不方便,习惯的鼠标双击在 这里不能用了。 CuteFTP 不能正常显示目录,是因为 FTP 服务器传回的数据格式不正确, 解决之道就是使 FTP 服务器传给 CuteFTP 的数据格式正确化,别无他法。别 把这个格式想的太难太深奥,只要细心些,它其实是很简单的——无非就是些 字符串罢了,说到字符串的处理,有信心了吧! 接下来就得弄到这些字符串的一个样本。从哪里弄呢?从 CuteFTP 是不 行的,得依靠一个比较原始的 FTP 软件——就是吻都死自带的 ftp.exe 。 打开一个 DOS 命令窗口,敲入 ftp ,回车,就会出现提示符:ftp> 接着敲 open 连接到 MUD 的 FTP 服务器,输入帐号 和密码(如果 MUD 的管理员为你开了个使用权限,FTP 的帐号和密码就是你 在 MUD 里的帐号和密码)登录。比如: ftp> open localhost 8900 Connected to User.ChinaTone.com. 220- 西游记 FTP server (Version 5.8 (MudOS/LPC)) ready. 220 Please login as yourself. User (User.ChinaTone.com:(none)):fof 331 Password required for fof. Password:******** 230 No directory! Logging in with home=/ ftp> 至此已登录成功。 敲一个 ls (列目录)命令: ftp> ls 200 PORT command successful. 150 Opening ASCII mode data connection for ls (220 bytes). adm/ cmds/ config.xiyou d/ daemon/ data/ doc/ feature/ include/ log/ mudos.exe mudos.log obj/ std/ 226 Transfer complete. ftp: 237 bytes received in 0.00Seconds 237000.00Kbytes/sec. ftp> 看啦,MUD 的根目录就是上面这些东东。带"/"尾巴的是目录,不带的是文件。 这只是简单列表而已,就相当于 CuteFTP 里把 Simple directory listing 那个选项勾上。看详细列表,得加个 -l 参数或者使 dir 命令: ftp> ls -l (或 dir ) 200 PORT command successful. 150 Opening ASCII mode data connection for ls (1316 bytes). drwxrwsr-x MudOS Root < DIR > Oct 29 22:11 ./ drwxrwsr-x MudOS Root < DIR > Jan 01 1980 ../ drwxrwsr-x MudOS Root < DIR > Oct 29 22:11 adm/ drwxrwsr-x MudOS Root < DIR > Oct 29 22:11 cmds/ -rw-rw-r-- MudOS Root 5504 Nov 15 00:10 config drwxrwsr-x MudOS Root < DIR > Oct 29 22:11 d/ drwxrwsr-x MudOS Root < DIR > Oct 29 22:12 daemon/ drwxrwsr-x MudOS Root < DIR > Oct 29 22:12 data/ drwxrwsr-x MudOS Root < DIR > Oct 29 22:12 doc/ drwxrwsr-x MudOS Root < DIR > Oct 29 22:13 feature drwxrwsr-x MudOS Root < DIR > Oct 29 22:13 include drwxrwsr-x MudOS Root < DIR > Oct 29 22:13 log/ -rw-rw-r-- MudOS Root 700416 Jun 29 13:09 mudos. -rw-rw-r-- MudOS Root 5185 Nov 15 12:06 mudos. drwxrwsr-x MudOS Root < DIR > Oct 29 22:13 obj/ drwxrwsr-x MudOS Root < DIR > Oct 29 22:13 std/ 226 Transfer complete. ftp: 1335 bytes received in 0.22Seconds 6.07Kbytes/sec. ftp> 以上看到的是 v5.8 版的 MUD FTP 服务器,下面来看看标准的 FTP 服务器: (就拿北大的 FTP 服务器做例子,这个服务器应该是蛮标准的吧) ftp> open ftp.pku.edu.cn …… ftp> ls -l 200 PORT command successful. 150 Opening ASCII mode data connection for /bin/ls. total 55880 -rw-r--r-- 1 ftpadmin ftpadmin 984 Nov 19 1999 .message ---------- 1 ftpadmin ftpadmin 0 Sep 6 1999 .notar -rw-r--r-- 1 ftpadmin ftpadmin 534 Nov 19 1999 WELCOME d--x--x--x 2 ftpadmin ftpadmin 512 Nov 11 1999 bin drwxrwxr-x 2 ftpadmin ftpadmin 512 Jul 5 1999 dev d--x--x--x 2 ftpadmin ftpadmin 512 Nov 15 1999 etc drwxrws-wt 2 ftpadmin ftpadmin 40960 Nov 15 10:10 incoming -rw-r--r-- 1 ftpadmin ftpadmin 25941142 Nov 10 00:06 ls-lR -rw-r--r-- 1 ftpadmin ftpadmin 2593008 Nov 10 00:06 ls-lR.gz -rw-r--r-- 1 ftpadmin ftpadmin 0 Nov 15 00:00 ls-lR.new drwxr-xr-x 25 ftpadmin ftpadmin 1024 Oct 12 18:28 pub drwxr-xr-x 3 ftpadmin ftpadmin 512 Jul 5 1999 usr 226 Transfer complete. ftp: 752 bytes received in 0.00Seconds 752000.00Kbytes/sec. ftp> OK!标准的目录列表格式已经拿到了。接下来, 将 MUD FTP 和标准 FTP 各抽出一个文件项来比较: -rw-rw-r-- MudOS Root 700416 Jun 29 13:09 mudos. -rw-r--r-- 1 ftpadmin ftpadmin 534 Nov 19 1999 WELCOME 将 MUD FTP 和标准 FTP 各抽出一个目录项来比较: drwxrwsr-x MudOS Root < DIR > Oct 29 22:13 std/ drwxr-xr-x 3 ftpadmin ftpadmin 512 Jul 5 1999 usr 怎么样,很不相同吧?文件和目录都各有千秋,MUD FTP 的文件名不完整,目录 名后面的"/"竟成了多余的 :-( ,"< DIR >"也是多余的! 毛病找到了,开始着手解决吧!先查找这些错误的格式是在哪里生成的。查找什么 呢?想想:我们给 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 参数,也 得考虑进去的喔! 有兴趣的你大可一试,有什么新发现请务必告诉我,先谢过了! 我修改过的那两段程序如下: …… case "size": // return size of file …… case "nlst": // give name list of files in directory CHECK_LOGIN(); /* Send name list */ if ((i = sizeof(command)) > 1 && command[1] == "-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 ); 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 CHECK_LOGIN(); /* Send directory file list (like exec'ing "ls") */ if ((i = sizeof(command)) > 1 && (command[1] == "-L" || command[1] == "-a" && command[2] == "-L")) { if (i == 2) command[1] = "."; else command = ({ command[0] }) + command[2..s-1]; } 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 ); 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 += "/*"; } /* begin narrow columnar "nlst" */ if (column) { 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"; } /* 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; 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) { /* directory */ // files[i] = sprintf("drwxrwsr-x %12s %12s < DIR > %12s %s/", files[i] = sprintf("drwxrwsr-x 1 %-8s %-8s 0 %12s %s", creator, domain, tmp, (xfiles[i])[0]); } else { /* file */ stats = stat(path + (xfiles[i])[0]); // files[i] = sprintf("-rw%crw-r-- %12s %12s %8d %12s %s", files[i] = sprintf("-rw%crw-r-- 1 %-8s %-8s %12d %12s %s", stats[2] ? 'x' : '-', /* 'x' if loaded, else ' ' */ creator, domain, j, tmp, (xfiles[i])[0]); } } return sprintf( "%s\n", implode( files, "\n" ) ); } Well, are you clear ? 后记: 在西游记2000版中,西游记巫师组对 FTP 服务器程序作了一些修缮,不过因为 没有去掉 "< DIR >" 这个不合标准的东东,CuteFTP 还是不能识别它传回的目录。 把 "< DIR >" 换成目录的字节 "0" 就行了,整个 ftpd.c 就改这一个地方就可以 适应 CuteFTP 了,弄一个试试吧! (完) |
|