/* * Sigh.... all of the whacked out Identd code... whoooooo * Much thanks to Simkin at harmless@h.imap.itd.umich.edu * Basically, start_auth is called once a person connects to * the port which the mud is running on, start_auth opens a connection * to port 113 at the host of the user which just connected * (port 113 is used as a identd daemon if the system has one) * then, next time through the unix_game_loop, it calls send_auth * to send the information which is being requested, then next * time through, read_auth gets whatever information was returned * and searches through it for the accountname */ /*$Id: id.c,v 1.11 2005/02/22 23:55:17 ahsile Exp $*/ #ifdef RUN_AS_WIN32SERVICE #include <winsock2.h> #endif #if defined( macintosh ) #include <types.h> #else #include <sys/types.h> #endif #include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <signal.h> #include <fcntl.h> #include <errno.h> #include "merc.h" #ifndef RUN_AS_WIN32SERVICE #include <netdb.h> #include <arpa/telnet.h> #include <netinet/in.h> #include <sys/socket.h> #define DUNNO_STRICMP #endif #define FLAG_WRAUTH 1 /* Auth unsent yet, send if able */ #define FLAG_AUTH 2 /* Authorization in progress */ extern int maxdesc; #ifdef RUN_AS_WIN32SERVICE extern int _write args( ( int fd, char *buf, int nbyte ) ); extern int _read args( ( int fd, char *buf, int nbyte ) ); #else extern int write args( ( int fd, char *buf, int nbyte ) ); extern int read args( ( int fd, char *buf, int nbyte ) ); #endif extern int close args( ( int fd ) ); void nonblock args( ( int s ) ); void start_auth args( ( struct descriptor_data *d ) ); void read_auth args( ( struct descriptor_data *d ) ); void send_auth args( ( struct descriptor_data *d ) ); void userl_update args( ( struct descriptor_data *d, bool login ) ); void userl_load args( ( void ) ); void userl_save args( ( void ) ); void ul_sort args( ( USERL_DATA *ul ) ); #if defined(DUNNO_STRICMP) int stricmp args( ( char *a, char *b ) ); #endif USERL_DATA * user_list; /* Start_auth - yay, TRI */ void start_auth( struct descriptor_data *d ) { struct sockaddr_in sock; int err; /* error & result stuffs */ int tlen; if ( !str_prefix( "130.63.236", d->host ) || !str_prefix( "130.63", d->host ) ) { free_string(d->user); d->user = str_dup( "(ncsa format)" ); return; } d->auth_fd = socket( AF_INET, SOCK_STREAM, 0 ); err = errno; if ( d->auth_fd < 0 && err == EAGAIN ) bug( "Can't allocate fd for authorization check", 0 ); nonblock( d->auth_fd ); /* Clone incoming host address */ tlen = sizeof( sock ); getpeername( d->descriptor, ( struct sockaddr * )&sock, &tlen ); sock.sin_port = htons( 113 ); sock.sin_family = AF_INET; #ifdef RUN_AS_WIN32SERVICE if ( ( connect( d->auth_fd, ( struct sockaddr *)&sock, sizeof(sock)) == -1 )) #else if ( ( connect( d->auth_fd, ( struct sockaddr *)&sock, sizeof(sock)) == -1 ) && ( errno != EINPROGRESS )) #endif { /* Identd Denied */ bug( "Unable to verify userid", 0 ); close( d->auth_fd ); free_string(d->user); d->user = str_dup( "(no verify)" ); d->auth_fd = -1; d->auth_state = 0; d->atimes = 70; return; } #if !defined(RUN_AS_WIN32SERVICE) if ( errno == ECONNREFUSED ) { close( d->auth_fd ); d->auth_fd = -1; free_string(d->user); d->user = str_dup( "(no identd)" ); d->auth_state = 0; d->atimes = 70; return; } #endif d->auth_state |= ( FLAG_WRAUTH|FLAG_AUTH ); /* Successful, but not sent */ if ( d->auth_fd > maxdesc ) maxdesc = d->auth_fd; return; } /* send_auth */ void send_auth( struct descriptor_data *d ) { struct sockaddr_in us, them; char authbuf[32]; int ulen, tlen, z; tlen = ulen = sizeof( us ); if ( getsockname( d->descriptor, ( struct sockaddr *)&us, &ulen ) || getpeername( d->descriptor, ( struct sockaddr *)&them, &tlen ) ) { bug( "auth getsockname error", 0 ); goto authsenderr; } /* compose request */ sprintf( authbuf, "%u , %u\r\n", (unsigned int)ntohs(them.sin_port), (unsigned int)ntohs(us.sin_port)); #ifdef RUN_AS_WIN32SERVICE z = send( d->auth_fd, authbuf, strlen( authbuf ), 0 ); #else z = write( d->auth_fd, authbuf, strlen( authbuf ) ); if ( errno == ECONNREFUSED ) { close( d->auth_fd ); d->auth_fd = -1; free_string(d->user); d->user = str_dup( "(no identd)" ); d->auth_state = 0; return; } #endif if ( z != (int) strlen( authbuf ) ) { if ( d->atimes >= 69 ) { sprintf( log_buf, "auth request, broken pipe [%d/%d]", z, errno ); bug( log_buf, 0 ); } authsenderr: close( d->auth_fd ); if ( d->auth_fd == maxdesc ) maxdesc--; d->auth_fd = -1; d->auth_state &= ~FLAG_AUTH; /* Failure/Continue */ d->auth_inc = 0; if ( d->atimes < 70 ) d->atimes++; } d->auth_state &= ~FLAG_WRAUTH; /* Successfully sent request */ return; } /* read_auth */ void read_auth( struct descriptor_data *d ) { char *s, *t; int len; /* length read */ char ruser[20], system[8]; /* remote userid */ u_short remp = 0, locp = 0; /* remote port, local port */ #ifdef RUN_AS_WIN32SERVICE struct sockaddr blah; int blahlen = sizeof(blah); #endif *system = *ruser = '\0'; /* * Can't allow any other reads from client fd while waiting on the * authfd to return a full valid string. Use the client's input buffer * to buffer the authd reply. May take more than one read. */ #ifdef RUN_AS_WIN32SERVICE if ( ( len = recvfrom( d->auth_fd, d->abuf + d->auth_inc, sizeof( d->abuf ) - 1 - d->auth_inc, 0, &blah, &blahlen ) ) >= 0 ) #else if ( ( len = read( d->auth_fd, d->abuf + d->auth_inc, sizeof( d->abuf ) - 1 - d->auth_inc ) ) >= 0 ) #endif { d->auth_inc += len; d->abuf[d->auth_inc] = '\0'; } if ( d->abuf[0] != '\0' ) bug( d->abuf, 0 ); if ( ( len > 0 ) && ( d->auth_inc != ( sizeof( d->abuf ) - 1 ) ) && (sscanf( d->abuf, "%hd , %hd : USERID : %*[^:]: %10s", &remp, &locp, ruser ) == 3 ) ) { #ifdef RUN_AS_WIN32SERVICE s = strrchr(d->abuf, ':'); #else s = rindex( d->abuf, ':'); #endif *s++ = '\0'; #ifdef RUN_AS_WIN32SERVICE for ( t = ( strrchr( d->abuf, ':' ) + 1 ); *t; t++ ) #else for ( t = ( rindex( d->abuf, ':' ) + 1 ); *t; t++ ) #endif if ( !isspace(*t) ) break; strncpy( system, t, sizeof( system ) ); if ( !str_prefix( "OTHER", system ) ) { close( d->auth_fd ); if ( d->auth_fd == maxdesc ) maxdesc--; d->auth_state = 0; d->auth_fd = -1; free_string(d->user); d->user = str_dup( "(invalid identd)" ); d->atimes = 70; return; } for ( t = ruser; *s && ( t < ruser + sizeof( ruser ) ); s++ ) if ( !isspace( *s ) && *s != ':' ) *t++ = *s; *t = '\0'; sprintf( log_buf, "auth reply ok, incoming user: [%s]", ruser ); bug( log_buf, 0 ); } else if ( len != 0 ) { #ifdef RUN_AS_WIN32SERVICE if (!strchr( d->abuf, '\n' ) && !strchr( d->abuf, '\r' ) ) return; #else if (!index( d->abuf, '\n' ) && !index( d->abuf, '\r' ) ) return; #endif sprintf( log_buf, "bad auth reply: %s", d->abuf ); bug( log_buf, 0 ); *ruser = '\0'; } close( d->auth_fd ); if ( d->auth_fd == maxdesc ) --maxdesc; d->auth_inc = 0; *d->abuf = '\0'; d->auth_fd = -1; d->auth_state = 0; if (ruser[0] == '\0') strcpy(ruser, "(no auth)" ); free_string(d->user); d->user = str_dup(ruser); userl_update( d, FALSE ); return; } void nonblock( int s ) { #if !defined(RUN_AS_WIN32SERVICE) if ( fcntl( s, F_SETFL, FNDELAY ) == -1 ) { perror( "Noblock" ); bug( "Noblock", 0 ); } #endif } void userl_load( ) { FILE *fp; extern FILE *fpArea; extern char strArea[MAX_INPUT_LENGTH]; char* buf; bug ( "Loading User_list...", 0 ); if ( !( fp = fopen( USERLIST_FILE, "r" ) ) ) return; for ( ; ; ) { char letter; USERL_DATA *ul; do { letter = getc(fp); if ( feof(fp) || letter == '$' ) { fclose(fp); bug("Done Loading User List.",0); return; } } while ( isspace(letter) ); ungetc(letter, fp); ul = new_userl(); if ( str_cmp( fread_word(fp), "name" ) ) break; ul->name = fread_string(fp); if ( str_cmp( fread_word(fp), "lvl" ) ) break; ul->level = fread_number(fp); if ( str_cmp( fread_word(fp), "user" ) ) break; ul->user = fread_string(fp); if ( str_cmp( fread_word(fp), "host" ) ) break; ul->host = fread_string(fp); if ( str_cmp( fread_word(fp), "last" ) ) break; ul->lastlogin = fread_string(fp); if ( str_cmp( fread_word(fp), "desc" ) ) break; ul->desc = fread_string(fp); buf = fread_word(fp); if ( !str_cmp( buf , "name" ) ) { // push the word back onto the stream ungetc(' ', fp); ungetc('e', fp); ungetc('m', fp); ungetc('a', fp); ungetc('n', fp); ul->class = 20; ul->multi = 20; } else { if(!str_cmp(buf,"$")) { ungetc('$', fp); } else { ul->class = fread_number(fp); fread_word(fp); ul->multi = fread_number(fp); } } if ( ul->level == 0 || ul->level == 1 ) { free_userl(ul); continue; } ul_sort(ul); } strcpy( strArea, USERLIST_FILE ); fpArea = fp; bug("Load_userl: bad key word.", 0 ); ___exit( 1 ); return; } void userl_update( struct descriptor_data *d, bool login ) { int found = 0; USERL_DATA * ul; if ( !d->character ) return; for ( ul = user_list; ul; ul = ul->next ) { if ( !str_cmp( d->character->name, ul->name ) ) found = 1; if ( ( found == 1 ) && ( !str_cmp( ul->user, "(unknown)" ) || !str_cmp( ul->user, "(no auth)" ) ) && str_cmp( d->user, "(unknown)" ) ) found = 2; if ( found > 0 ) break; } if ( found == 1 ) { if ( login ) { sprintf( log_buf, "On since %s", ctime( ¤t_time ) ); *(log_buf + strlen(log_buf) - 1) = '\0'; free_string(ul->lastlogin); ul->lastlogin = str_dup( log_buf ); } ul->class = d->character->class; ul->multi = d->character->multied; ul->level = d->character->level; free_string(ul->host); ul->host = str_dup( d->host ); free_string(ul->user); ul->user = str_dup( d->user ); return; } if ( found == 0 ) { ul = new_userl(); ul->name = str_dup( d->character->name ); ul->level = d->character->level; ul->user = str_dup( d->user ); ul->host = str_dup( d->host ); ul->class = d->character->class; ul->multi = d->character->multied; sprintf( log_buf, "On since %s", ctime( ¤t_time ) ); *(log_buf + strlen(log_buf) - 1) = '\0'; ul->lastlogin = str_dup( log_buf ); ul->desc = str_dup( "(none)" ); ul_sort(ul); return; } if ( found == 2 ) { if ( login ) { sprintf( log_buf, "On since %s", ctime( ¤t_time ) ); *(log_buf + strlen(log_buf) - 1) = '\0'; free_string(ul->lastlogin); ul->lastlogin = str_dup( log_buf ); } ul->class = d->character->class; ul->multi = d->character->multied; ul->level = d->character->level; free_string(ul->user); ul->user = str_dup( d->user ); free_string(ul->host); ul->host = str_dup( d->host ); return; } return; } void userl_save( ) { FILE *fp; USERL_DATA * ul; char filename[30]; sprintf( filename, "users.txt" ); fclose( fpReserve ); if ( !( fp = fopen( filename, "w" ) ) ) perror( filename ); else { ul = NULL; for ( ul = user_list; ul; ul = ul->next ) { fprintf( fp, "Name %s~\n", ul->name ); fprintf( fp, "Lvl %d\n", ul->level ); fprintf( fp, "User %s~\n", ul->user ); fprintf( fp, "Host %s~\n", ul->host ); fprintf( fp, "Last %s~\n", (ul->lastlogin ? ul->lastlogin : "(none)") ); fprintf( fp, "Desc %s~\n", (ul->desc ? ul->desc : "(none)") ); fprintf( fp, "Class %d\n", ul->class ); fprintf( fp, "Multi %d\n", ul->multi ); } fprintf( fp, "$\n" ); fclose( fp ); fpReserve = fopen( NULL_FILE, "r" ); return; } fpReserve = fopen( NULL_FILE, "r" ); return; } #if defined DUNNO_STRICMP /* * Note: only works for alpha strings. No punctuation or numbers, or things * might go messy.. :).. * returns: 1 if a is larger, -1 if b is larger, or 0 if equal * -- Altrag */ int stricmp( char *a, char *b ) { char *ap; char *bp; for ( ap = a, bp = b; ; ap++, bp++ ) { if ( (*ap == *bp) && *ap == '\0' ) return 0; if ( *ap == '\0' ) return -1; if ( *bp == '\0' ) return 1; if ( LOWER(*ap) < LOWER(*bp) ) return -1; if ( LOWER(*bp) < LOWER(*ap) ) return 1; } return 0; } #endif void ul_sort( USERL_DATA *ul ) { USERL_DATA *user; if ( !user_list ) { user_list = ul; return; } for ( user = user_list; user; user = user->next ) { if (!stricmp(ul->name, user->name) || ( stricmp(ul->name, user->name) > 0 && (!user->next || stricmp(ul->name, user->next->name) < 0) ) ) { ul->next = user->next; user->next = ul; return; } } ul->next = user_list; user_list = ul; return; }