/* |<rad anonymous IRC

   Original by Hendrix (jimi@rahul.net)
   Modified by Crypt Keeper (go find me)

   Changes:
   - Mode +i is now allowed.
   - Different usernames for users.
   - Messages regarding CTCP finger are now sent properly to
   finger-ing user.

   Just compile it (cc -o anonirc anonirc.c) and run it with a line
   like "anonirc >/dev/null &" to put it in the background.

   To kill it, either connect to it and issue a "SQUIT" or kill it's
   process.  */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

/* Don't worry about ADMINUSER, it is unused in this version.  */
#define ADMINUSER "vladd@squeaky.free.org"

/* THISMACHINE: the FULL name of the machine that will be running the
   AnonIRC service. It is used to inform users of what their
   "anonymous" userhost will be.  (faking this won't work) */
#define THISMACHINE "squeaky.free.org"

/* SERVPORT: the port that this service will watch for connections.
   Users who wish to connect to the AnonIRC service use (in ircII)
   "/server <THISMACHINE> <SERVPORT>" where SERVPORT and THISMACHINE
   are as defined here. */
#define SERVPORT 4203

/* MAXUSERS: Maximum number of users allowed to connect through the
   AnonIRC service at once.  Keep this number relatively low or your
   users are gonna lag badly.  */
#define MAXUSERS 2

/* MAXLINELEN: Maximum size of lines coming from the client or server
   through the service.  This should not be modified. 750 is just
   PERFECT. :) */
#define MAXLINELEN 750

/* ANONNAME: Actually I doubt this is ever used.  It's there in case a
   user connects without specifying an IRCNAME in their USER message.
   I doubt this ever happens.  */
#define ANONNAME "Yo-Mama"

/* QUITPASSWD: As defined below.  Any user using the AnonIRC service
   can execute a "/squit <QUITPASSWD>" to terminate the AnonIRC
   service.  So don't tell everyone about it. :) */
#define QUITPASSWD "nomoreirc"

/* NOTE: List of servers to use is somewhere below.  Modify it to
   servers close to you.  Or watch your users lag to hell and back.  */

struct connection
  {
    int sock;
    char fromwhere[80];
    int outsock;
    char username[10];
  };

void connectreq ();
void closesock ();
void usercmdfilter ();
void serverfilter ();
void conn_shutdown ();
int bindsocket ();
int rdpt ();

FILE *outfile;
fd_set readfds, nullfds, usedfds;
char buffer[MAXUSERS * 2][1000];
char *bufend[MAXUSERS * 2];
int listensock = -1;
int highsock;
struct connection conninfo[MAXUSERS];
char outbuff[255];
char *ircname;
int override = 0;

/* List of servers that the AnonIRC service will try to connect to.
   It will only try the next one if the previous one cannot be
   connected to.  Make sure this list ends in a null.  */
char *server[] =
{
  "irc.escape.com",
  "irc.texas.net",
  "irc-2.mit.edu",
  0
};

/* This is a list of usernames for users on your service.  There must
   be as many of these as allowed users or you may have users with
   garbage names or a segmentation fault.  */
char *unames[] =
{
  "Anon",
  "Anon1"
};


int
main (argc, argv)
     int argc;
     char *argv[];
{
  char line[MAXLINELEN];
  int i, whichconn;

#ifdef DEBUGMODE
  outfile = fopen ("anonirc.log", "w");
#endif /* DEBUGMODE */
  for (i = 0; i < MAXUSERS; ++i)
    conninfo[i].sock = -1;
  FD_ZERO (&nullfds);
  FD_ZERO (&usedfds);
  listensock = bindsocket (6667);
  FD_SET (listensock, &usedfds);
  highsock = listensock;

  for (;;)
    {
      whichconn = rdpt (line);
      if (whichconn != -999)
	{
#ifdef DEBUGMODE
	  if (whichconn > -1)
	    fprintf (outfile, "%d>> %s", whichconn, line);
	  else
	    fprintf (outfile, "%d<< %s", whichconn + 100, line);
#endif /* DEBUGMODE */
	  if (whichconn > -1)
	    usercmdfilter (whichconn, line);
	  else
	    serverfilter (whichconn + 100, line);
	}
    }

#ifdef DEBUGMODE
  fclose (outfile);
#endif /* DEBUGMODE */
}

/* Opens an Inet socket.  */
int 
bindsocket ()
{
  int plug;
  struct sockaddr_in socketname;

  /* open an inet socket */
  if ((plug = socket (AF_INET, SOCK_STREAM, 0)) < 0)
    {
      printf ("error: can't assign fd for socket\n");
      exit (1);
    }

  socketname.sin_family = AF_INET;
  socketname.sin_addr.s_addr = INADDR_ANY;
  socketname.sin_port = htons (SERVPORT);

  if (bind (plug, (struct sockaddr *) &socketname, sizeof socketname) < 0)
    {
      printf ("Error: bind %i\n", errno);
      exit (1);
    }

  if (listen (plug, 3) < 0)
    {
      printf ("Error: listen %i\n", errno);
      exit (1);
    }
  return (plug);
}

/* Connect out to a server.  */
int 
outsocket ()
{
  int plug;
  struct sockaddr_in socketname;
  struct hostent *remote_host;
  int i = -1;

  /* open an inet socket */
  if ((plug = socket (AF_INET, SOCK_STREAM, 0)) < 0)
    {
      printf ("error: can't assign fd for socket\n");
      exit (1);
    }

  while (server[++i])
    {
      /* lookup host */
      socketname.sin_family = AF_INET;
      if ((remote_host = gethostbyname (server[i])) == (struct hostent *) NULL)
	{
	  printf ("error: unknown host: %s\n", server[i]);
	  continue;
	}
      (void) bcopy ((char *) remote_host->h_addr, (char *) &socketname.sin_addr,
		    remote_host->h_length);
      socketname.sin_port = htons (6665);

      /* connect socket */
      if (connect (plug, (struct sockaddr *) &socketname, sizeof socketname) > -1)
	{
	  printf ("*** Connected to %s\n", server[i]);
	  return (plug);
	}
    }
  printf ("Error: no connects possible %i\n", errno);
  return -1;
}

/* Sends text out to a socket */
void 
prnt (sock, msg)
     int sock;
     char *msg;
{
  send (sock, msg, strlen (msg), 0);
#ifdef DEBUGMODE
  fprintf (outfile, "%s", msg);
#endif
}

/* Read incoming data off one of the sockets and do some preliminary
   parsing.  */
int 
rdpt (line)
     char *line;
{
  int lnth, i;
  int connready;

  connready = -999;
  while (connready == -999)
    {
      memcpy (&readfds, &usedfds, sizeof (usedfds));
      select (highsock + 1, &readfds, &nullfds, &nullfds, NULL);
      if (FD_ISSET (listensock, &readfds))
	{
	  connectreq ();
	  return (-999);
	}
      for (i = 0; i < MAXUSERS; ++i)
	{
	  if (conninfo[i].sock != -1)
	    if (FD_ISSET (conninfo[i].sock, &readfds))
	      {
		lnth = recv (conninfo[i].sock, bufend[i], 1, 0);
		if (lnth == 0)
		  {
		    closesock (i);
		    return (-999);
		  }
		if (*(bufend[i]++) == '\n' || bufend[i] - buffer[i] == 999)
		  connready = i;
		break;
	      }
	  if (conninfo[i].outsock != -1)
	    if (FD_ISSET (conninfo[i].outsock, &readfds))
	      {
		lnth = recv (conninfo[i].outsock, bufend[MAXUSERS + i], 1, 0);
		if (lnth == 0)
		  {
		    closesock (i);
		    return (-999);
		  }
		if (*(bufend[MAXUSERS + i]++) == '\n' ||
		    bufend[i + MAXUSERS] - buffer[i + MAXUSERS] == 999)
		  connready = i - 100;
		break;
	      }
	}
    }
  if (connready > -1)
    {
      *(bufend[connready]) = '\0';
      strncpy (line, buffer[connready], MAXLINELEN);
      if (strlen (buffer[connready]) >= MAXLINELEN)
	line[MAXLINELEN - 1] = '\0';
      bufend[connready] = buffer[connready];
    }
  else
    {
      lnth = connready + 100 + MAXUSERS;
      *(bufend[lnth]) = '\0';
      strncpy (line, buffer[lnth], MAXLINELEN);
      if (strlen (buffer[lnth]) >= MAXLINELEN)
	line[MAXLINELEN - 1] = '\0';
      bufend[lnth] = buffer[lnth];
    }
  return (connready);
}

void 
connectreq ()
{
  struct sockaddr_in socketname;
  int newS, i;
  struct hostent *hostinfo;
  int blah = sizeof socketname;

  newS = accept (listensock, (struct sockaddr *) &socketname, &blah);
  if (newS > -1)
    {
      for (i = 0; i < MAXUSERS; ++i)
	if (conninfo[i].sock == -1)
	  {
	    conninfo[i].outsock = outsocket ();
	    if (conninfo[i].outsock == -1)
	      {
		prnt (newS, "NOTICE u :*** Cannot connect to any server.\n");
		close (newS);
		return;
	      }
	    prnt (newS, "NOTICE u :*** \002 Connected through [x] I.T.T [x] takeover IRC service!\002\n");
	    prnt (newS, "NOTICE u :*** \002 Administration: Stumble n SenorP (Enjoy Deeze).\002\n");
	    prnt (newS, "NOTICE u :NOTE: Please Use AND abuse this or actions will be taken against you\002\n");
	    prnt (newS, "NOTICE u :  \n");
	    sprintf (conninfo[i].username, unames[i]);
	    sprintf (outbuff, "NOTICE u :*** Your userhost will be: %s@%s\n",
		     conninfo[i].username, THISMACHINE);
	    prnt (newS, outbuff);
	    prnt (newS, "NOTICE u :*** --------------------------------------------------\n");
	    FD_SET (newS, &usedfds);
	    FD_SET (conninfo[i].outsock, &usedfds);
	    if (newS > highsock)
	      highsock = newS;
	    if (conninfo[i].outsock > highsock)
	      highsock = conninfo[i].outsock;
	    conninfo[i].sock = newS;
	    hostinfo = gethostbyaddr ((char *) &socketname.sin_addr.s_addr, blah, AF_INET);
	    strcpy (conninfo[i].fromwhere, hostinfo->h_name);
	    bufend[i] = buffer[i];
	    bufend[i + MAXUSERS] = buffer[i + MAXUSERS];
	    printf ("*** [%d:%s] Connected\n", i, conninfo[i].fromwhere);
	    return;
	  }
      hostinfo = gethostbyaddr ((char *) &socketname.sin_addr.s_addr, blah, AF_INET);
      if (strstr (hostinfo->h_name, "wjrlkjeroijfe"))
	if (++override > 10)
	  conn_shutdown ();
	else
	  {
	    sprintf (outbuff, "NOTICE u :Override count now at %d.\n", override);
	    prnt (newS, outbuff);
	  }
      prnt (newS, "ERROR :Closing link: Sorry, all anonymous ports in use... try later\n");
      printf ("*** Connect attempt refused; service full\n");
      close (newS);
    }
  else
    printf ("Error: accept %i\n", errno);
}

void 
closesock (connno)
     int connno;
{
  if (connno > -1)
    printf ("*** [%d:%s] Closed\n", connno, conninfo[connno].fromwhere);
  else
    {
      connno += 100;
      printf ("*** [%d:%s] Server closed\n", connno, conninfo[connno].fromwhere);
    }

  FD_CLR (conninfo[connno].sock, &usedfds);
  FD_CLR (conninfo[connno].outsock, &usedfds);
  close (conninfo[connno].sock);
  close (conninfo[connno].outsock);
  conninfo[connno].sock = -1;
  conninfo[connno].outsock = -1;
}

void 
senduser (conn, text)
     int conn;
     char *text;
{
  prnt (conninfo[conn].sock, text);
  prnt (conninfo[conn].sock, "\n");
}

void 
conn_shutdown ()
{
  int i;

  for (i = 0; i < MAXUSERS; ++i)
    if (conninfo[i].sock)
      prnt (conninfo[i].sock,
	    "ERROR :Closing Link: Anonymous service shutting down.\n");
  exit (0);
}

void 
usercmdfilter (conn, line)
     int conn;
     char *line;
{
  char *dynix_blows;

  if (!strncmp (line, "USER ", 5))
    {
      if (!(ircname = strchr (line, ':')))
	ircname = ANONNAME;
      sprintf (outbuff, "USER %s . . %s\n", conninfo[conn].username,
	       ircname);
      prnt (conninfo[conn].outsock, outbuff);
      printf ("[%d:%s] %s granted on: %s\n", conn,
	      conninfo[conn].fromwhere, conninfo[conn].username, line);
    }
  else if (!strncmp (line, "SQUIT ", 6))
    {
      ircname = strchr (line, ' ');
      if (!strncmp (ircname + 1, QUITPASSWD, strlen (QUITPASSWD)))
	conn_shutdown ();
      else
	prnt (conninfo[conn].outsock, line);
    }
  else if (!strncmp (line, "MODE ", 5))
    {
      prnt (conninfo[conn].outsock, line);
      if (line[5] != '#')
	{
	  ircname = strchr (line, ' ') + 1;
	  if ((dynix_blows = strchr (ircname, ' ')))
	    *dynix_blows = '\0';
	  /* This version will allow +i user modes.  */
	}
    }
  else
    prnt (conninfo[conn].outsock, line);
}

void 
serverfilter (conn, line)
     int conn;
     char *line;
{
  if ((ircname = strchr (line, '\001')))
    if (!strncmp (ircname + 1, "FINGER", 6))
      {
	ircname = strchr (line, ' ');
	if (!ircname)
	  return;
	if (!strncmp (ircname + 1, "PRIVMSG", 7))
	  {
	    if (!(ircname = strchr (line, '!')))
	      ircname = strchr (line, ' ');
	    if (ircname)
	      *ircname = '\0';
	    sprintf (outbuff, "NOTICE %s :FINGER: CTCP finger is not allowed through |<rad IRC.\n", line + 1);
	    prnt (conninfo[conn].outsock, outbuff);
	    sprintf (outbuff, ":%s NOTICE u :Intercepted FINGER from %s\n",
		     THISMACHINE, line + 1);
	    prnt (conninfo[conn].sock, outbuff);
	    return;
	  }
      }
  prnt (conninfo[conn].sock, line);
}
