/
LIB3/
LIB3/D/ADMIN/
LIB3/D/ADMIN/OBJ/
LIB3/D/ADMIN/ROOM/W/
LIB3/D/HOME/
LIB3/D/HOME/CITY/ARENA/
LIB3/D/HOME/CITY/ITEMS/
LIB3/D/HOME/CITY/POSTOFFI/
LIB3/DOC/
LIB3/GLOBAL/SPECIAL/
LIB3/GLOBAL/VIRTUAL/
LIB3/NET/
LIB3/NET/CONFIG/
LIB3/NET/DAEMON/CHARS/
LIB3/NET/GOPHER/
LIB3/NET/INHERIT/
LIB3/NET/OBJ/
LIB3/NET/SAVE/
LIB3/NET/VIRTUAL/
LIB3/OBJ/B_DAY/
LIB3/OBJ/HANDLERS/TERM_TYP/
LIB3/PLAYERS/B/
LIB3/PLAYERS/N/
LIB3/ROOM/
LIB3/SAVE/
LIB3/SAVE/BOARDS/
LIB3/SAVE/ENVIRON/
LIB3/SAVE/POST/
LIB3/STD/COMMANDS/SHADOWS/
LIB3/STD/CREATOR/
LIB3/STD/DOM/
LIB3/STD/EFFECTS/
LIB3/STD/EFFECTS/HEALING/
LIB3/STD/EFFECTS/OTHER/
LIB3/STD/EFFECTS/POISONS/
LIB3/STD/ENVIRON/
LIB3/STD/GUILDS/
LIB3/STD/LIQUIDS/
LIB3/STD/ROOM/
LIB3/STD/TRIGGER/SHADOW/
LIB3/W/
LIB3/W/BANNOR/
LIB3/W/NEWSTYLE/
/* 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() */