/****************************************************************************
* ^ +----- | / ^ ^ | | +-\ *
* / \ | | / |\ /| | | | \ *
* / \ +--- |< | \ / | | | | | *
* /-----\ | | \ | v | | | | / *
* / \ | | \ | | +-----+ +-/ *
****************************************************************************
* AFKMud (c)1997-2002 Alsherok. Contributors: Samson, Dwip, Whir, *
* Cyberfox, Karangi, Rathian, Cam, Raine, and Tarl. *
* *
* Original SMAUG 1.4a written by Thoric (Derek Snider) with Altrag, *
* Blodkai, Haus, Narn, Scryn, Swordbearer, Tricops, Gorog, Rennard, *
* Grishnakh, Fireblade, and Nivek. *
* *
* Original MERC 2.1 code by Hatchet, Furey, and Kahn. *
* *
* Original DikuMUD code by: Hans Staerfeldt, Katja Nyboe, Tom Madsen, *
* Michael Seifert, and Sebastian Hammer. *
****************************************************************************
* DNS Resolver Module *
****************************************************************************/
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include "mud.h"
DNS_DATA *first_cache;
DNS_DATA *last_cache;
void save_dns( void );
void prune_dns( void )
{
DNS_DATA *cache, *cache_next;
for( cache = first_cache; cache; cache = cache_next )
{
cache_next = cache->next;
/*
* Stay in cache for 14 days
*/
if( current_time - cache->time >= 1209600 || !str_cmp( cache->ip, "Unknown??" ) || !str_cmp( cache->name, "Unknown??" ) )
{
STRFREE( cache->ip );
STRFREE( cache->name );
UNLINK( cache, first_cache, last_cache, next, prev );
DISPOSE( cache );
}
}
save_dns( );
return;
}
void check_dns( void )
{
if( current_time >= new_boot_time_t )
prune_dns( );
return;
}
void add_dns( char *dhost, char *address )
{
DNS_DATA *cache;
CREATE( cache, DNS_DATA, 1 );
cache->ip = STRALLOC( dhost );
cache->name = STRALLOC( address );
cache->time = current_time;
LINK( cache, first_cache, last_cache, next, prev );
save_dns( );
return;
}
char *in_dns_cache( char *ip )
{
DNS_DATA *cache;
static char dnsbuf[MSL];
dnsbuf[0] = '\0';
for( cache = first_cache; cache; cache = cache->next )
{
if( !str_cmp( ip, cache->ip ) )
{
strcpy( dnsbuf, cache->name );
break;
}
}
return dnsbuf;
}
#if defined(KEY)
#undef KEY
#endif
#define KEY( literal, field, value ) \
if ( !str_cmp( word, literal ) ) \
{ \
field = value; \
fMatch = TRUE; \
break; \
}
void fread_dns( DNS_DATA * cache, FILE * fp )
{
char *word;
bool fMatch;
for( ;; )
{
word = feof( fp ) ? "End" : fread_word( fp );
fMatch = FALSE;
switch ( UPPER( word[0] ) )
{
case '*':
fMatch = TRUE;
fread_to_eol( fp );
break;
case 'E':
if( !str_cmp( word, "End" ) )
{
if( !cache->ip )
cache->ip = STRALLOC( "Unknown??" );
if( !cache->name )
cache->name = STRALLOC( "Unknown??" );
return;
}
break;
case 'I':
KEY( "IP", cache->ip, fread_string( fp ) );
break;
case 'N':
KEY( "Name", cache->name, fread_string( fp ) );
break;
case 'T':
KEY( "Time", cache->time, fread_number( fp ) );
break;
}
if( !fMatch )
bug( "fread_dns: no match: %s", word );
}
}
void load_dns( void )
{
char filename[256];
DNS_DATA *cache;
FILE *fp;
first_cache = NULL;
last_cache = NULL;
sprintf( filename, "%s", DNS_FILE );
if( ( fp = fopen( filename, "r" ) ) != NULL )
{
for( ;; )
{
char letter;
char *word;
letter = fread_letter( fp );
if( letter == '*' )
{
fread_to_eol( fp );
continue;
}
if( letter != '#' )
{
bug( "load_dns: # not found.", 0 );
break;
}
word = fread_word( fp );
if( !str_cmp( word, "CACHE" ) )
{
CREATE( cache, DNS_DATA, 1 );
fread_dns( cache, fp );
LINK( cache, first_cache, last_cache, next, prev );
continue;
}
else if( !str_cmp( word, "END" ) )
break;
else
{
bug( "load_dns: bad section: %s.", word );
continue;
}
}
FCLOSE( fp );
}
prune_dns( ); /* Clean out entries beyond 14 days */
return;
}
void save_dns( void )
{
DNS_DATA *cache;
FILE *fp;
char filename[256];
sprintf( filename, "%s", DNS_FILE );
if( ( fp = fopen( filename, "w" ) ) == NULL )
{
bug( "save_dns: fopen", 0 );
perror( filename );
}
else
{
for( cache = first_cache; cache; cache = cache->next )
{
fprintf( fp, "#CACHE\n" );
fprintf( fp, "IP %s~\n", cache->ip );
fprintf( fp, "Name %s~\n", cache->name );
fprintf( fp, "Time %ld\n", cache->time );
fprintf( fp, "End\n\n" );
}
fprintf( fp, "#END\n" );
FCLOSE( fp );
}
return;
}
/* DNS Resolver code by Trax of Forever's End */
/*
* Almost the same as read_from_buffer...
*/
bool read_from_dns( int fd, char *buffer )
{
static char inbuf[MAX_STRING_LENGTH * 2];
int iStart, i, j, k;
/*
* Check for overflow.
*/
iStart = strlen( inbuf );
if( iStart >= sizeof( inbuf ) - 10 )
{
bug( "DNS input overflow!!!", 0 );
return FALSE;
}
/*
* Snarf input.
*/
for( ;; )
{
int nRead;
nRead = read( fd, inbuf + iStart, sizeof( inbuf ) - 10 - iStart );
if( nRead > 0 )
{
iStart += nRead;
if( inbuf[iStart - 2] == '\n' || inbuf[iStart - 2] == '\r' )
break;
}
else if( nRead == 0 )
{
return FALSE;
}
else if( errno == EWOULDBLOCK )
break;
else
{
perror( "Read_from_dns" );
return FALSE;
}
}
inbuf[iStart] = '\0';
/*
* Look for at least one new line.
*/
for( i = 0; inbuf[i] != '\n' && inbuf[i] != '\r'; i++ )
{
if( inbuf[i] == '\0' )
return FALSE;
}
/*
* Canonical input processing.
*/
for( i = 0, k = 0; inbuf[i] != '\n' && inbuf[i] != '\r'; i++ )
{
if( inbuf[i] == '\b' && k > 0 )
--k;
else if( isascii( inbuf[i] ) && isprint( inbuf[i] ) )
buffer[k++] = inbuf[i];
}
/*
* Finish off the line.
*/
if( k == 0 )
buffer[k++] = ' ';
buffer[k] = '\0';
/*
* Shift the input buffer.
*/
while( inbuf[i] == '\n' || inbuf[i] == '\r' )
i++;
for( j = 0; ( inbuf[j] = inbuf[i + j] ) != '\0'; j++ )
;
return TRUE;
}
/* DNS Resolver code by Trax of Forever's End */
/*
* Process input that we got from resolve_dns.
*/
void process_dns( DESCRIPTOR_DATA * d )
{
char address[MIL];
int status;
address[0] = '\0';
if( !read_from_dns( d->ifd, address ) || address[0] == '\0' )
return;
if( address[0] != '\0' )
{
add_dns( d->host, address ); /* Add entry to DNS cache */
STRFREE( d->host );
d->host = STRALLOC( address );
if( d->character )
{
STRFREE( d->character->pcdata->lasthost );
d->character->pcdata->lasthost = STRALLOC( address );
}
}
/*
* close descriptor and kill dns process
*/
if( d->ifd != -1 )
{
close( d->ifd );
d->ifd = -1;
}
/*
* we don't have to check here,
* cos the child is probably dead already. (but out of safety we do)
*
* (later) I found this not to be true. The call to waitpid( ) is
* necessary, because otherwise the child processes become zombie
* and keep lingering around... The waitpid( ) removes them.
*/
if( d->ipid != -1 )
{
waitpid( d->ipid, &status, 0 );
d->ipid = -1;
}
return;
}
/* DNS Resolver hook. Code written by Trax of Forever's End */
void resolve_dns( DESCRIPTOR_DATA * d, long ip )
{
int fds[2];
pid_t pid;
/*
* create pipe first
*/
if( pipe( fds ) != 0 )
{
perror( "resolve_dns: pipe: " );
return;
}
if( dup2( fds[1], STDOUT_FILENO ) != STDOUT_FILENO )
{
perror( "resolve_dns: dup2(stdout): " );
return;
}
if( ( pid = fork( ) ) > 0 )
{
/*
* parent process
*/
d->ifd = fds[0];
d->ipid = pid;
close( fds[1] );
}
else if( pid == 0 )
{
/*
* child process
*/
char str_ip[64];
int i;
d->ifd = fds[0];
d->ipid = pid;
for( i = 2; i < 255; ++i )
close( i );
sprintf( str_ip, "%ld", ip );
execl( "../src/resolver", "AFKMud Resolver", str_ip, NULL );
/*
* Still here --> hmm. An error.
*/
bug( "resolve_dns: Exec failed; Closing child.", 0 );
d->ifd = -1;
d->ipid = -1;
exit( 0 );
}
else
{
/*
* error
*/
perror( "resolve_dns: failed fork" );
close( fds[0] );
close( fds[1] );
}
}
void do_cache( CHAR_DATA * ch, char *argument )
{
DNS_DATA *cache;
int ip = 0;
send_to_pager( "&YCached DNS Information\n\r", ch );
send_to_pager( "IP | Address\n\r", ch );
send_to_pager( "------------------------------------------------------------------------------\n\r", ch );
for( cache = first_cache; cache; cache = cache->next )
{
pager_printf( ch, "&W%16.16s &Y%s\n\r", cache->ip, cache->name );
ip++;
}
pager_printf( ch, "\n\r&W%d IPs in the cache.\n\r", ip );
return;
}