ftpd.c
来自「C实现的MUD,对大家基本入门网络游戏很有帮助!」· C语言 代码 · 共 1,581 行 · 第 1/3 页
C
1,581 行
#define FTPD_VERSION "5.8"
inherit F_DBASE;
mapping socket_info;
int our_socket;
mapping temp_map;
int number_of_users;
int rest_pos;
/*
* edit ftpdconf.h & ftpdsupp.h to configure ftpd for your mudlib
*/
#include <net/ftpd.h>
#include <net/ftpdconf.h>
#include <net/ftpdsupp.h>
void setup_ftp( int port );
void finish_lookup( string host, string number );
void resolve_callback( string address, string resolved, int key );
string get_path( int fd, string str );
int strncmp(string str, string sub, int i);
void lose_connection( int fd )
{
socket_close( fd );
map_delete( socket_info, fd );
}
void lose_user( int fd )
{
socket_close( fd );
map_delete( socket_info, fd );
number_of_users--;
}
int strncmp(string str, string sub, int i)
{
int j;
j = strlen(str);
return (i > j ? strcmp(str, sub) : strcmp(str[0..i-1], sub));
}
int check_valid_read( string fname, int fd )
{
int res;
seteuid( socket_info[ fd ][ USER_NAME ] );
res = (int) master() -> valid_read(fname, this_object(), "read_file");
seteuid( getuid() );
/*
* This rather complicated looking section of code handles the
* various read levels available. See ftpdconf.h for READ_LEVEL
* definitions of these flags.
*/
#ifdef READ_LEVEL
#if READ_LEVEL == VALID_READ
return res;
#elif READ_LEVEL == RESTRICTED_READ
if (strncmp(fname, HOME_DIR( UNAME ), strlen(HOME_DIR( UNAME ))) == 0
#ifdef PUB_DIR
|| strncmp(fname, PUB_DIR, strlen(PUB_DIR)) == 0
#endif
#ifdef FTP_DIR
|| strncmp(fname, FTP_DIR, strlen(FTP_DIR)) == 0
#endif
#ifdef TMP_DIR
|| strncmp(fname, TMP_DIR, strlen(TMP_DIR)) == 0
#endif
)
return res;
else
return 0;
#elif READ_LEVEL == WRITE_LIMIT_READ
return check_valid_write( fname, fd );
#else
// default
return res;
#endif
#else
// default
return res;
#endif
}
int check_valid_write( string fname, int fd )
{
int res;
#ifdef MESSAGE_FILES
// Prevent removing .message file from ftp connection.
if( fname[<8..<0] == "/.message") return 0;
#endif
seteuid( socket_info[ fd ][ USER_NAME ] );
res = (int) master() -> valid_write(fname, this_object(), "write_file");
seteuid( getuid() );
/*
* This rather complicated looking section of code handles the
* various write levels available. See ftpdconf.h for WRITE_LEVEL
* definitions of these flags.
*/
#ifdef WRITE_LEVEL
#if WRITE_LEVEL == VALID_WRITE
return res;
#elif WRITE_LEVEL == RESTRICTED_WRITE
if (strncmp(fname, HOME_DIR( UNAME ), strlen(HOME_DIR( UNAME ))) == 0
#ifdef PUB_DIR
|| strncmp(fname, PUB_DIR, strlen(PUB_DIR)) == 0
#endif
#ifdef FTP_DIR
|| strncmp(fname, FTP_DIR, strlen(FTP_DIR)) == 0
#endif
#ifdef TMP_DIR
|| strncmp(fname, TMP_DIR, strlen(TMP_DIR)) == 0
#endif
)
return res;
else
return 0;
#elif WRITE_LEVEL == READ_ONLY
return 0;
#else
// default
return res;
#endif
#else
// default
return res;
#endif
}
mapping query_sinfo() { return socket_info; }
/* query_connections returns an array of users connected to ftpd */
mapping *query_connections()
{
mixed *vals;
mapping *list;
int count, i;
return values(socket_info);
vals = values( socket_info );
list = allocate( sizeof( vals ) );
count = sizeof( vals );
while ( count-- )
if (!undefinedp( (vals[count])[USER_NAME] ))
list[i++] = ([
USER_NAME :
capitalize(( string )((vals[count])[USER_NAME])),
USER_SITE :
(string)((vals[count])[USER_SITE])
]);
return i ? list[0..i-1] : ({ });
}
void create()
{
set("channel_id", "传输精灵");
seteuid(ROOT_UID);
if ( !socket_info ) {
socket_info = ([ ]);
temp_map = ([ ]);
number_of_users = 0;
call_out( "setup_ftp", 2, FTPD_PORT );
call_out( "check_connections", 3 * 60 );
}
rest_pos = 0;
CHANNEL_D->do_channel(this_object(), "sys", "FTP服务已经启动。");
}
protected void setup_ftp( int port )
{
our_socket = socket_create( STREAM, "in_read_callback",
"in_close_callback" );
if ( our_socket < 0 ) {
TP( "Failed to create socket.\n" );
return;
}
if ( socket_bind( our_socket, port ) < 0 ) {
TP( "Failed to bind socket.\n" );
socket_close( our_socket );
return;
}
if ( socket_listen( our_socket, "in_listen_callback" ) < 0 ) {
TP( "Failed to listen to socket.\n" );
return;
}
} /* setup_ftp() */
void in_write_callback( int fd )
{
string address;
int res;
if ( undefinedp( socket_info[fd][CONNECTED] ) ) {
if ( number_of_users > FTPD_MAX_USERS ) {
res = socket_write( fd,
"530 Too many ftp connections active. Try again later.\n");
/*
* In these cases there isn't much more that can be done
*/
if (res != EECALLBACK)
lose_user( fd );
/*
* The following blocks the code in_read_callback &
* in_write_callback on this connection; we let the driver
* complete the send and let the daemon timeout the connection
*/
if (res == EECALLBACK) {
socket_info[fd][CONNECTED] = 0;
}
return;
}
res = socket_write( fd,
sprintf( "220- %s FTP server (Version %s (MudOS/LPC)) ready.\n"
#ifdef ANONYMOUS_FTP
"220 Please login as yourself or 'anonymous'.\n",
#else
"220 Please login as yourself.\n",
#endif
THE_MUD_NAME, FTPD_VERSION ) );
/*
* In these cases, assume user will receive above message
*/
if (res == EESUCCESS || res == EECALLBACK) {
socket_info[fd][CONNECTED] = 1;
if ((address = socket_address(fd)) &&
sscanf( address, "%s %*s", address )) {
socket_info[fd][USER_SITE] = address;
socket_info[fd][IDLE_SETTING] = FTPD_TIMEOUT;
temp_map += ([ resolve( address, "resolve_callback" ) : fd ]);
} else {
socket_write( fd,
"451 Error in server: socket_address() failed.\n"
"221 Closing connection due to server error.\n");
lose_user( fd );
}
} else if (res == EEWOULDBLOCK) {
call_out( "in_write_callback", 2, fd );
} else {
lose_user( fd );
}
}
}
/*
* called for each new ftp login connection
*/
void in_listen_callback( int fd )
{
int new_fd;
if ( ( new_fd =
socket_accept( fd, "in_read_callback", "in_write_callback" ) ) < 0 )
return;
number_of_users++;
socket_info[new_fd] = ([ ]);
socket_info[new_fd][LAST_DATA] = time();
in_write_callback( new_fd );
}
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()->creator_file(path);
if (!creator) creator = ROOT_UID;
creator = creator[0..11];
domain = (string)master()->domain_file(path);
if (!domain) domain = ROOT_UID;
domain = domain[0..11];
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 %3d %-8s %-8s 1024 %12s %s",
2, creator, domain, tmp, (xfiles[i])[0]);
} else {
/* file */
stats = stat(path + (xfiles[i])[0]);
files[i] = sprintf("-rw%crw-r-- 1 %-8s %-8s %8d %12s %s",
stats[2] ? 'x' : '-', /* 'x' if loaded, else ' ' */
creator, domain, j, tmp, (xfiles[i])[0]);
}
}
return sprintf( "%-#70s\n", implode( files, "\n" ) );
}
buffer to_stream(buffer buf)
{
string s;
buffer ret;
if (! sizeof(buf) )
return buf;
s = read_buffer(buf, 0, sizeof(buf));
s = replace_string(s, "\n", "\r\n");
ret = allocate_buffer(strlen(s));
write_buffer(ret, 0, s);
return ret;
}
void data_write_callback( int fd )
{
int pos, length, ret_val;
buffer tmp;
string s;
if ( socket_info[fd][TYPE] == DOWNLOAD ) return;
pos = socket_info[fd][POS];
length = ( (pos+BLOCK_SIZE) >= socket_info[fd][LEN] ) ?
( socket_info[fd][LEN] - pos ) : BLOCK_SIZE;
socket_info[ socket_info[fd][PARENT_FD] ][LAST_DATA] = time();
if ( socket_info[fd][TYPE] == STRING ) {
while ( ((ret_val = socket_write( fd, socket_info[fd][DATA][pos..(pos+BLOCK_SIZE-1)] )) >= 0)
|| (ret_val==EECALLBACK) ) {
pos += BLOCK_SIZE;
if ( pos > socket_info[ fd ][ LEN ] ) {
socket_write( socket_info[ fd ][ PARENT_FD ], "226 Transfer complete.\n" );
socket_close( fd );
map_delete( socket_info[ socket_info[ fd ][ PARENT_FD ] ], DATA_FD );
return;
}
if( ret_val==EECALLBACK ) break;
}
} else {
if (catch( tmp = read_buffer( socket_info[ fd ][ DATA ], pos, length ) )) {
socket_write( socket_info[ fd ][ PARENT_FD ], "551 Error on input file.\n" );
socket_close( fd );
map_delete( socket_info[ socket_info[ fd ][ PARENT_FD ] ], DATA_FD );
return;
}
if ( socket_info[ fd ][ TRANSFER_MODE ] == STREAM )
tmp = to_stream(tmp);
while ( ((ret_val = socket_write( fd, tmp )) >= 0)
|| (ret_val==EECALLBACK) ) {
pos += length;
if ( pos >= socket_info[ fd ][ LEN ] ) {
socket_write( socket_info[ fd ][ PARENT_FD ], "226 Transfer complete.\n" );
socket_close( fd );
map_delete( socket_info[ socket_info[ fd ][ PARENT_FD ] ], DATA_FD );
return;
}
if( ret_val==EECALLBACK ) break;
length = ( (pos+BLOCK_SIZE) >= socket_info[ fd ][ LEN ] ) ?
( socket_info[ fd ][ LEN ] - pos ) : BLOCK_SIZE;
tmp = read_buffer( socket_info[ fd ][ DATA ], pos, length );
if ( socket_info[ fd ][ TRANSFER_MODE ] == STREAM )
tmp = to_stream(tmp);
}
}
// Advance the position we're sending next time.
socket_info[ fd ][ POS ] = pos;
switch(ret_val) {
case EEWOULDBLOCK: // data transfer busy (MudOS -> OS)
call_out("data_write_callback", 2, fd);
case EECALLBACK: // data transfer busy (LPC -> MudOS)
return;
default:
#ifdef DEBUG_REPORT
CHANNEL_D->do_channel(this_object(), "sys",
"data_write_callback: error " + ret_val + ", wait until next callback.");
#endif
while ( remove_call_out( "data_write_callback" ) != -1 );
}
}
protected void data_conn( int fd, string mess, string name, int type )
{
int new_fd, ret, data_mode;
string data_mode_name;
mixed tmp;
if( type == STRING
|| ( type == FILE && UTYPE == STRING ) ) {
data_mode_name = "ASCII";
data_mode = STREAM;
tmp = "";
rest_pos = 0;
} else {
data_mode_name = "BINARY";
data_mode = STREAM_BINARY;
tmp = allocate_buffer( 0 );
}
if ( socket_info[ fd ][ DATA_FD ] ) {
socket_write( fd, "425 Can't open data connection.\n" );
return;
}
new_fd = socket_create( data_mode, "data_read_callback",
"data_close_callback" );
if ( new_fd < 0 ) {
socket_write( fd, "425 Can't create data socket.\n" );
return;
}
if ( ( ret = socket_connect( new_fd, sprintf("%s %d",
socket_info[ fd ][ DATA_ADDR ], socket_info[ fd ][ DATA_PORT ]),
"data_read_callback", "data_write_callback" ) ) < 0 ) {
socket_write( fd, "425 Can't build data connection.\n" );
socket_close( new_fd );
return;
}
socket_info[ new_fd ] = ([
DATA : (type == STRING ? replace_string(mess, "\n", "\r\n") : mess),
POS : rest_pos,
PARENT_FD : fd,
TYPE : type,
LEN : (type == STRING ? strlen(mess) : file_size(mess)),
TRANSFER_MODE : data_mode,
]);
rest_pos = 0;
socket_info[ fd ][ DATA_FD ] = new_fd;
if ( type == STRING )
{
socket_write( fd, sprintf("150 Opening %s mode data "
"connection for %s (%d bytes).\n",
data_mode_name, name,
socket_info[ new_fd ][ LEN ]));
} else
{
socket_write( fd, sprintf("150 Opening %s mode data "
"connection for %s start at %d (%d bytes).\n",
data_mode_name, name,
socket_info[ new_fd ][ POS ],
socket_info[ new_fd ][ LEN ]));
}
}
protected void read_connection( int fd, string path, int append )
{
int new_fd, ret, data_mode;
string data_mode_name;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?