/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | \\._.// *
* -----------------------------------------------------------| (0...0) *
* SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by Derek Snider | ).:.( *
* -----------------------------------------------------------| {o o} *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, | / ' ' \ *
* Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek, |~'~.VxvxV.~'~*
* Tricops and Fireblade | *
*------------------------------------------------------------------------ *
* Ident *
****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#ifdef WIN32
#include <io.h>
#define EWOULDBLOCK WSAEWOULDBLOCK
#else
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#define closesocket close
#endif
#include "mud.h"
#ifndef EAGAIN
#define EAGAIN EWOULDBLOCK
#endif
typedef enum
{ AS_TOOPEN, AS_TOSEND, AS_TOREAD } AUTH_STATE;
typedef struct auth_data
{
struct auth_data *next, *prev;
DESCRIPTOR_DATA *d;
int afd;
int state;
int times;
struct sockaddr_in us, them;
} AUTH_DATA;
AUTH_DATA *first_auth, *last_auth;
bool auth_read( AUTH_DATA * a, CHAR_DATA * ch );
bool auth_write( AUTH_DATA * a, CHAR_DATA * ch );
bool auth_open( AUTH_DATA * a, CHAR_DATA * ch );
void auth_maxdesc( int *md, fd_set * ins, fd_set * outs, fd_set * excs )
{
AUTH_DATA *a;
for( a = first_auth; a; a = a->next )
if( a->state != AS_TOOPEN )
{
*md = UMAX( *md, a->afd );
FD_SET( a->afd, ins );
FD_SET( a->afd, outs );
FD_SET( a->afd, excs );
}
return;
}
void auth_check( fd_set * ins, fd_set * outs, fd_set * excs )
{
AUTH_DATA *a, *a_next;
CHAR_DATA *ch;
bool ferr;
for( a = first_auth; a; a = a_next )
{
a_next = a->next;
ferr = FALSE;
ch = ( a->d->original ? a->d->original : a->d->character );
if( a->state == AS_TOOPEN )
{
if( !auth_open( a, ch ) )
ferr = TRUE;
}
else if( FD_ISSET( a->afd, excs ) )
{
FD_CLR( a->afd, ins );
FD_CLR( a->afd, outs );
bug( "Auth_check: exception found for %s@%s.", ( ch ? ch->name : "(unknown)" ), a->d->host );
STRFREE( a->d->user );
a->d->user = STRALLOC( "Exception" );
ferr = TRUE;
}
else if( FD_ISSET( a->afd, ins ) && a->state == AS_TOREAD )
{
if( !auth_read( a, ch ) )
{
FD_CLR( a->afd, outs );
ferr = TRUE;
}
}
else if( FD_ISSET( a->afd, outs ) && a->state == AS_TOSEND )
{
if( !auth_write( a, ch ) )
ferr = TRUE;
}
if( ferr )
{
if( a->state != AS_TOOPEN )
closesocket( a->afd );
UNLINK( a, first_auth, last_auth, next, prev );
DISPOSE( a );
}
}
return;
}
char *break_arg( char **s, char end )
{
char *ret, *ws;
while( isspace( **s ) )
++ * s;
ret = *s;
while( **s && **s != end )
++ * s;
for( ws = ( *s ) - 1; isspace( *ws ); --ws )
*ws = '\0';
if( **s != '\0' )
{
**s = '\0';
++*s;
}
/* bug("break_arg: broke '%s', left '%s'", ret, *s);*/
return ret;
}
#define KILLRET(bs, us) \
do { \
bug(bs, (ch ? ch->name : "(unknown)"), a->d->host); \
STRFREE(a->d->user); \
a->d->user = STRALLOC(us); \
return FALSE; \
} while(0)
bool auth_read( AUTH_DATA * a, CHAR_DATA * ch )
{
char readbuf[MAX_STRING_LENGTH];
char system[MAX_INPUT_LENGTH], user[MAX_INPUT_LENGTH];
char *s = readbuf;
int n;
n = recv( a->afd, readbuf, sizeof( readbuf ) - 10, 0 );
if( n < 0 )
{
perror( "auth_read: read" );
KILLRET( "Auth_read: Error on read for %s@%s.", "(Error_on_read)" );
}
if( !n )
return TRUE;
/* KILLRET("Auth_read: EOF on read for %s@%s.", "(EOF on read)");*/
readbuf[n] = '\0';
while( isspace( *s ) )
++s;
if( !*s )
KILLRET( "Auth_read: blank auth for %s@%s.", "(blank_auth)" );
/*
* That 1024 check should actually be against the port the user is
* logged into, but in SMAUG theres like 4 possible, and I don't
* feel like checking every single one.. -- Alty
*/
/* bug("ident = '%s'.", s);*/
if( !atoi( break_arg( &s, ',' ) ) || atoi( break_arg( &s, ':' ) ) < 1024 )
KILLRET( "Auth_read: Invalid ident reply for %s@%s.", "(invalid_ident)" );
break_arg( &s, ':' );
sprintf( system, "%.*s", ( int )sizeof( system ) - 1, break_arg( &s, ':' ) );
/* if (!str_cmp(system, "OTHER"))
KILLRET("Auth_read: invalid system for %s@%s.", "(invalid system)");*/
sprintf( user, "%.*s", ( int )sizeof( user ) - 1, break_arg( &s, ' ' ) );
if( !*user )
KILLRET( "Auth_read: no username for %s@%s.", "(no_username)" );
sprintf( log_buf, "Auth reply ok. Incoming user [%s@%s] for %s.", user, a->d->host, ( ch ? ch->name : "(unknown)" ) );
STRFREE( a->d->user );
a->d->user = STRALLOC( user );
return FALSE; /* FALSE actually removes the AUTH_DATA, which is good. */
}
bool auth_write( AUTH_DATA * a, CHAR_DATA * ch )
{
char authbuf[32];
int n;
sprintf( authbuf, "%u , %u\r\n", ( unsigned int )ntohs( a->them.sin_port ), ( unsigned int )ntohs( a->us.sin_port ) );
n = send( a->afd, authbuf, strlen( authbuf ), 0 );
if( n != strlen( authbuf ) )
{
if( !--a->times )
{
/*
* This used to be the KILLRET you see below, but too many imms complained
* about log being spammed.. yeah.. like it aint spammed anyways..
* -- Alty
*/
STRFREE( a->d->user );
a->d->user = STRALLOC( "(broken_pipe)" );
return FALSE;
}
/* KILLRET("Auth_write: broken pipe for %s@%s.", "(broken pipe)");*/
closesocket( a->afd );
a->state = AS_TOOPEN;
if( a->times == sysdata.ident_retries - 1 )
{
STRFREE( a->d->user );
a->d->user = STRALLOC( "(pipe_breaking)" );
}
return TRUE; /* Dont kill AUTH_DATA */
}
a->state = AS_TOREAD;
return TRUE;
}
bool auth_open( AUTH_DATA * a, CHAR_DATA * ch )
{
struct sockaddr_in sock;
struct servent *serv;
int err;
#ifdef WIN32
unsigned int arg = 1;
#endif
a->afd = socket( AF_INET, SOCK_STREAM, 0 );
err = errno;
if( a->afd < 0 )
{
perror( "auth_open: socket" );
if( err == EAGAIN )
{
bug( "Auth_open: can't allocate filedesc for %s@%s.", ( ch ? ch->name : "(unknown)" ), a->d->host );
--a->times;
return TRUE;
}
KILLRET( "Auth_open: unknown socket error", "(socket_error)" );
}
#ifndef FNDELAY
#define FNDELAY O_NDELAY
#endif
#ifdef WIN32
if( ioctlsocket( a->afd, FIONBIO, &arg ) == -1 )
#else
if( fcntl( a->afd, F_SETFL, FNDELAY ) < 0 )
#endif
{
perror( "auth_open: fcntl" );
KILLRET( "Auth_open: unknown fcntl error", "(fcntl_error)" );
}
sock = a->them;
serv = getservbyname( "ident", "tcp" );
if( !serv )
sock.sin_port = htons( 113 );
else
sock.sin_port = serv->s_port;
sock.sin_family = AF_INET;
if( connect( a->afd, ( struct sockaddr * )&sock, sizeof( sock ) ) < 0
#ifdef WIN32
&& errno != WSAEINPROGRESS )
#else
&& errno != EINPROGRESS )
#endif
{
#ifndef WIN32
if( errno != ECONNREFUSED )
{
perror( "auth_open: connect" );
KILLRET( "Auth_open: unknown connect error", "(connect_error)" );
}
#endif
KILLRET( "Auth_open: connection refused", "(connect_refused)" );
}
a->state = AS_TOSEND;
return TRUE;
}
#undef KILLRET
#define ENDRET(bs, us) \
do { \
bug(bs, (ch ? ch->name : "(unknown)"), d->host); \
STRFREE(d->user); \
d->user = STRALLOC(us); \
return; \
} while(0)
void set_auth( DESCRIPTOR_DATA * d )
{
CHAR_DATA *ch = ( d->original ? d->original : d->character );
AUTH_DATA *a;
struct sockaddr_in us, them;
int ulen = sizeof( us ), tlen = sizeof( them );
/*
* To stop an uninitialized memory read --Shaddai
*/
us.sin_port = 0;
them.sin_port = 0;
if( sysdata.ident_retries <= 0 )
{
STRFREE( d->user );
d->user = STRALLOC( "(ident-not-active)" );
return;
}
if( getsockname( d->descriptor, ( struct sockaddr * )&us, &ulen ) < 0 )
{
perror( "set_auth: getsockname" );
ENDRET( "Set_auth: getsockname error for %s@%s.", "(getsockname_error)" );
}
if( getpeername( d->descriptor, ( struct sockaddr * )&them, &tlen ) < 0 )
{
perror( "set_auth: getpeername" );
ENDRET( "Set_auth: getpeername error for %s@%s.", "(getpeername_error)" );
}
CREATE( a, AUTH_DATA, 1 );
a->d = d;
a->state = AS_TOOPEN;
a->times = sysdata.ident_retries;
a->us = us;
a->them = them;
LINK( a, first_auth, last_auth, next, prev );
STRFREE( d->user );
d->user = STRALLOC( "(in_progress)" );
return;
}
void kill_auth( DESCRIPTOR_DATA * d )
{
AUTH_DATA *a;
for( a = first_auth; a; a = a->next )
if( a->d == d )
{
if( a->state != AS_TOOPEN )
closesocket( a->afd );
UNLINK( a, first_auth, last_auth, next, prev );
DISPOSE( a );
STRFREE( d->user );
d->user = STRALLOC( "(killed)" );
return;
}
return;
}