/* Dosmud hack */
#include "std.h"
/*
* The wholely non useful inetd written by pinkfish@discworld.
* Blue.
*/
#include "socket.h"
/*
* inetd type means it all runs through ident's bound socket.
* It does all sort of useful and hairy things this way.
*/
#define VALID_TYPE ({ "mud", "tcp", "udp", "inetd", "inetd_udp" })
#define INETD_TYPE 3
#define INETD_UDP_TYPE 3
#define CONFIG "/net/config/"
#define NAMESERVER "/net/nameserver"
#undef DEBUG
#ifdef DEBUG
#define TP(STR) tell_object(find_player("pinkfish"), STR)
#else
#define TP(STR)
#endif
/* This is indexed by file descriptor */
mapping services, to_go, my_valid, close_it;
int my_socket;
void load_config( string str );
void close_callback( int fd );
void create()
{
seteuid( getuid() );
services = ([ ]);
to_go = ([ ]);
my_valid = ([ ]);
close_it = ([ ]);
load_config( CONFIG + "inetd" );
} /* create() */
/* Make sure our inetd server is running */
void connect_inetd()
{
if( my_socket ) /* Already up */
return;
my_socket = socket_create( STREAM, "inetd_read", "close_callback" );
if( my_socket < 0 )
{
write_file( "/log/INETD", "Failed to find service create socket " +
socket_error( my_socket ) + ".\n" );
return;
}
NAMESERVER->lookup_service( "inetd", MUD_NAME, "finish_lookup" );
} /* connect_inetd() */
void finish_lookup( string name, string host, int port )
{
int ret;
if( !port )
{
log_file( "INETD", "Failed to lookup service " + name + "\n" );
return;
}
ret = socket_bind( my_socket, port );
if( ret < 0 )
{
socket_close( my_socket );
my_socket = 0;
write_file( "/log/INETD", "Failed to bind socket for " + name +
"-" + socket_error( my_socket ) + ".\n" );
return;
}
ret = socket_listen( my_socket, "inetd_connect" );
if( ret < 0 )
{
socket_close( my_socket );
my_socket = 0;
write_file( "/log/INETD", "Failed to listen socket for " + name +
"-" + socket_error( my_socket ) + ".\n" );
return;
}
} /* finish_lookup() */
void load_config( string name )
{
string data, str, file, *lines;
int i, type;
data = read_file( name );
if( !data )
return;
data = implode( explode( data, " " ) - ({ "" }), " " );
lines = explode( data, "\n" );
for( i = 0; i < sizeof( lines ); i++ )
{
while( strlen( lines[ i ] ) && lines[ i ][ 0 ] == ' ' )
lines[ i ] = lines[ i ][ 1..1000 ];
if( !strlen( lines[ i ] ) || lines[ i ][ 0 ] == '#' )
continue;
TP( "Checking line " + lines[ i ] + "\n" );
if( sscanf( lines[ i ], "%s %s %s", name, str, file ) == 3 )
{
if( (type = member_array( str, VALID_TYPE )) == -1 )
continue;
if( type == INETD_TYPE )
{
my_valid[ name ] = file;
connect_inetd();
}
else
{
TP( "Looking up " + name + ".\n" );
NAMESERVER->lookup_service( name, MUD_NAME, "found_service",
({ type, file }) );
}
}
else
log_file( "Invalid line in " + name + ", " + lines[ i ] + "\n" );
}
} /* load_config() */
void found_service( string name, string host, int port, mixed args )
{
int fd, ret;
TP( sprintf( "found_service called with %O, %O, %O.\n", name, host, port ) );
/* Failed :( */
if( !port )
{
write_file( "/log/INETD", "Failed to find service " + name + ".\n" );
return;
}
/* Ok... we have a service... so set ourselves up a look thingy */
fd = socket_create( args[ 0 ], "read_callback", "close_callback" );
if( fd < 0 )
{
write_file( "/log/INETD", "Failed to create socket for " + name +
"-" + socket_error( fd ) + ".\n" );
return;
}
ret = socket_bind( fd, port );
if( ret < 0 )
{
socket_close( fd );
write_file( "/log/INETD", "Failed to bind socket for " + name +
"-" + socket_error( fd ) + ".\n" );
return;
}
if( args[ 0 ] != DATAGRAM )
{
ret = socket_listen( fd, "listen_callback" );
if( ret < 0 )
{
socket_close( fd );
write_file( "/log/INETD", "Failed to listen socket for " + name +
"-" + socket_error( fd ) + ".\n" );
return;
}
}
/* Ok... tis setup. Bing! */
/* Do we need to remember what port we are on? Nahhh... */
services[ fd ] = args[ 1 ];
} /* found_service() */
void read_callback( int fd, string str, string addr )
{
string file;
file = services[ fd ];
if( !file )
{
socket_close( fd );
return;
}
file->read_callback( fd, str, addr );
} /* read_callback() */
void write_callback( int fd )
{
int last, i;
if( !to_go[ fd ] )
to_go[ fd ] = 1;
if( !pointerp( to_go[ fd ] ) )
{
services[ fd ]->write_callback( fd );
return;
}
while( sizeof( to_go[ fd ] ) && (last = socket_write( fd, to_go[ fd ][ 0 ] )) >= 0 )
to_go[ fd ] = to_go[ fd ][ 1..100 ];
if( !sizeof( to_go[ fd ] ) )
{
to_go[ fd ] = (last >= 0);
if( to_go[ fd ] )
{
if( close_it[ fd ] )
{
close_callback( fd );
map_delete( close_it, fd );
}
else
services[ fd ]->write_callback( fd );
}
}
} /* write_callback() */
void write_fd( int fd, mixed mess )
{
int bing;
/* Security violation... */
if( previous_object() != this_object() &&
previous_object() != services[ fd ] &&
file_name( previous_object() ) != services[ fd ] )
{
event( users(), "inform", "Security violation in /net/inetd.c (" +
file_name( previous_object() ) + " : " + services[ fd ] + ")",
"security" );
return;
}
if( !pointerp( to_go[ fd ] ) )
{
/*
* If in here there is nothing in the queue already. so we assume we can
* send down the link.
*/
to_go[ fd ] = ({ mess });
bing = 1;
}
else
to_go[ fd ] += ({ mess });
if( bing )
write_callback( fd );
} /* write_fd() */
/*
* This sets the close_it flag or closes it immediately if there is
* nothing to go and we have the correct flags.
*/
void close_fd( int fd )
{
int bing;
if( previous_object() != this_object() &&
previous_object() != services[ fd ] &&
file_name( previous_object() ) != services[ fd ] )
{
event( users(), "inform", "Security violation in /net/inetd.c (" +
file_name( previous_object() ) + " : " + services[ fd ] + ")",
"security" );
return;
}
bing = intp( to_go[ fd ] ) && to_go[ fd ];
if( bing )
{
close_callback( fd );
}
else
close_it[ fd ] = 1;
} /* close_fd() */
void close_callback( int fd )
{
if( !services[ fd ] )
return;
/* Altered by Newstyle, 26/01/94 */
if( services[ fd ] == "waiting" )
my_valid[ fd ][ 1 ]->close_callback( fd );
else
if( catch( services[ fd ]->close_callback( fd ) ) )
{
TP( "Error " + fd + sprintf( "%O\n", my_valid[ fd ] ) );
}
map_delete( services, fd );
map_delete( my_valid, fd );
/* Just to make sure... */
socket_close( fd );
} /* close_callback() */
void listen_callback( int fd )
{
int new_fd;
if( (new_fd = socket_accept( fd, "read_callback", "write_callback" )) < 0 )
{
write_file( "/log/INETD", "Failed to accept a connection " +
"-" + socket_error( new_fd ) + ".\n" );
return;
}
services[ new_fd ] = services[ fd ];
/* whats all this then? */
call_out( "close_callback", 5 * 60 * 60, new_fd );
to_go[ new_fd ] = 1;
} /* listen_callback() */
void inetd_connect( int fd )
{
int new_fd;
if( (new_fd = socket_accept( fd, "inetd_read", "inetd_write" )) < 0 )
{
write_file( "/log/INETD", "Failed to accept a connection " +
"-" + socket_error( new_fd ) + ".\n" );
return;
}
services[ new_fd ] = "request";
socket_write( new_fd, "SERVICE?\n" );
to_go[ new_fd ] = 1;
call_out( "close_callback", 5 * 60 * 60, new_fd );
} /* inetd_connect() */
void inetd_read( int fd, string mess )
{
string *bits;
if( !services[ fd ] )
{
socket_close( fd );
return;
}
switch( services[ fd ] )
{
case "waiting":
/* Should be asking us for a service... */
if( !my_valid[ fd ] )
{
socket_close( fd );
return;
}
if( mess == "SERVICE?\n" )
{
/* They are... */
/*
* The old one doesnt use a response code.... so... We won't
* either for now.
services[fd] = "response";
*/
if( my_valid[ fd ][ 2 ] )
socket_write( fd, my_valid[ fd ][ 0 ] + " " + my_valid[ fd ][ 2 ] + "\n" );
else
socket_write( fd, my_valid[ fd ][ 0 ] + "\n" );
services[ fd ] = my_valid[ fd ][ 1 ];
map_delete( my_valid, fd );
services[ fd ]->connected( fd );
}
return;
case "response":
if( mess == "YES!\n" )
{
/* Ok! Bings are us */
if( !my_valid[ fd ] )
{
socket_close( fd );
return;
}
services[ fd ] = my_valid[ fd ][ 1 ];
map_delete( my_valid, fd );
services[ fd ]->connected( fd );
}
else
{
socket_close( fd );
if( my_valid[ fd ] )
my_valid[ fd ][ 1 ]->failed( "unknow service" );
map_delete( my_valid, fd );
return;
}
return;
case "request":
bits = explode( mess, "\n" );
if( sscanf( bits[ 0 ], "%s %s", bits[ 0 ], mess ) == 2 )
bits = ({ bits[ 0 ], mess }) +bits[ 1..1000 ];
bits = explode( bits[ 0 ], " " ) + bits[ 1..1000 ];
/*
mess = mess[0..strlen(mess)-2]; /* Strip the \n */
mess = bits[ 0 ];
if( !my_valid[ mess ] )
{
/* Invalid service... */
socket_write( fd, "UNKNOWN SERVICE\n" );
socket_close( fd );
return;
}
/* Don't need the yes string.
socket_write(fd, "YES!\n");
*/
services[ fd ] = my_valid[ mess ];
services[ fd ]->connected( fd );
if( sizeof( bits ) > 1 )
{
read_callback( fd, implode( bits[ 1..1000 ], "\n" ), 0 );
}
return;
default:
read_callback( fd, mess, 0 );
return;
}
} /* inetd_read() */
void inetd_write( int fd )
{
if( !services[ fd ] )
{
socket_close( fd );
return;
}
switch( services[ fd ] )
{
case "response":
case "waiting":
case "request":
to_go[ fd ] = 1;
return;
default:
write_callback( fd );
return;
}
} /* inetd_write() */
/*
* Sends a message off to the destination service, with closeing
* and opening of sockets etc done all for you.
* Maybe useful, dubious.
*/
void datagram_message( string name, string dest, string mess )
{
NAMESERVER->lookup_service( name, dest, "finish_datagram_open",
({ previous_object(), name, mess }) );
} /* datagram_message() */
/*
* The last paramater bing is used for compatability with an older
* version of inetd. Please do not use this in any new daemons you
* create. I consider it somewhat of a hack. This is also only
* used in the INETD_TYPE case, not for streams or mud connections.
*/
void open_to_service( string name, int type, string dest, string bing )
{
if( type == DATAGRAM )
{
/*
* What we do here is resolv a named [port thingy and return that to
* the calling object...
*/
previous_object()->failed( "unsupported type", dest, name, bing );
return;
}
if( type == INETD_TYPE )
{
NAMESERVER->lookup_service( "inetd", dest, "finish_inet_open",
({ previous_object(), name, bing }) );
return;
}
/* Else.... */
NAMESERVER->lookup_service( name, dest, "finish_open",
({ previous_object(), type }) );
} /* open_to_service() */
void finish_open( string name, string host, int port, mixed *args )
{
int new_fd, ret;
if( !args[ 0 ] )
return;
if( !port )
{
args[ 0 ]->failed( "lookup", host, name, args[ 1 ] );
return;
}
new_fd = socket_create( args[ 1 ], "read_callback", "close_callback" );
if( new_fd < 0 )
{
args[ 0 ]->failed( "socket_create", host, name, args[ 1 ], new_fd );
return;
}
ret = socket_connect( new_fd, host + " " + port, "read_callback",
"write_callback" );
if( ret < 0 )
{
socket_close( new_fd );
args[ 0 ]->failed( "socket_connect", host, name, args[ 1 ], new_fd );
return;
}
call_out( "close_callback", 5 * 60 * 60, new_fd );
services[ new_fd ] = file_name( args[ 0 ] );
to_go[ new_fd ] = 1;
args[ 0 ]->connected( new_fd, host );
} /* finish_open() */
void finish_inet_open( string name, string host, int port, mixed *args )
{
int new_fd, ret;
if( !args[ 0 ] )
return;
if( !port )
{
args[ 0 ]->failed( "lookup", name, args[ 1 ], host );
return;
}
new_fd = socket_create( STREAM, "inetd_read", "close_callback" );
if( new_fd < 0 )
{
args[ 0 ]->failed( "socket_create", name, args[ 1 ], host, new_fd );
return;
}
ret = socket_connect( new_fd, host + " " + port, "inetd_read",
"inetd_write" );
if( ret < 0 )
{
socket_close( new_fd );
args[ 0 ]->failed( "socket_connect", name, args[ 1 ], host, new_fd );
return;
}
call_out( "close_callback", 5 * 60 * 60, new_fd );
my_valid[ new_fd ] = ({ args[ 1 ], file_name( args[ 0 ] ), args[ 2 ] });
services[ new_fd ] = "waiting";
TP( "Opening " + new_fd + "\n" );
} /* finish_inet_open() */
void finish_datagram_open( string name, string host, int port, mixed *args )
{
int s;
TP( sprintf( "Called datagram with: %O, %O, %O\n", name, host, port ) );
if( !port )
{
if( args[ 0 ] )
args[ 0 ]->failed( "lookup", name, args[ 1 ], host );
return;
}
s = socket_create( DATAGRAM, "blurble" );
if( s < 0 )
{
if( args[ 0 ] )
{
args[ 0 ]->failed( "socket_create", name, args[ 1 ], host );
}
return;
}
if( socket_write( s, args[ 2 ], host + " " + port ) < 0 )
{
/* Tarnation... */
if( args[ 0 ] )
args[ 0 ]->failed( "socket_write", name, args[ 1 ], host );
TP( "Failed.\n" );
}
socket_close( s );
} /* finish_datagram_open() */