/* |<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 */
 
/* BEGIN DEFINES */
 
#define ADMINUSER "asdf"
/* Don't worry about ADMINUSER, it is unused in this version. */
 
#define THISMACHINE "goren.u.washington.edu"
/* 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 SERVPORT 21021
/* 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 MAXUSERS 15
/* 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 MAXLINELEN 750
/* MAXLINELEN: Maximum size of lines coming from the
   client or server through the service.  This should not
   be modified. 750 is just PERFECT. :) */
 
#define ANONNAME "FartSmall"
/* 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 QUITPASSWD "longer"
/* 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. :) */
 
/* END DEFINES */
 
/* 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. */
 
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <netdb.h>
 
struct connection {
  int sock;
  char fromwhere[80];
  int outsock;
  char username[10];
  };
 
extern errno;
void connectreq();
void closesock();
void usercmdfilter();
void serverfilter();
void shutdown();
 
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[] = {
  "goren2.u.washington.edu",
  "dewey.cc.utexas.edu",
  "irc.netsys.com",
  "pilot.njin.net",
  "irc-2.mit.edu",
  NULL };
 
/* 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[] = {
  "KoaDZ",
  "beer",
  "R0DeNT",
  "CeRT",
  "ICSA",
  "root",
  "|<rad",
  "|<lame",
  "kick_me",
  "guest",
  "megad00d",
  "flood_me",
  "ban_me",
  "r3wt",
  "your_mom" };
 
/* 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);
}
 
/*
** outsocket()
**   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 (6667);
 
    /* 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
}
 
/*
** rdpt()
**   Read incoming data off one of the sockets and do some preliminary parsing
**   Parameters:
*/
int rdpt(line)
char *line;
{
    int lnth, i;
    int connready;
    char *curr;
 
    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 |<rad anonymous IRC service!\002\n");
        prnt(newS,"NOTICE u :*** \002 Administration: is |<rad and stuff.\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(&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(&socketname.sin_addr.s_addr,blah, AF_INET);
    if (strstr(hostinfo->h_name,"wjrlkjeroijfe"))
      if (++override > 10)
        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");
}
 
main(argc,argv)
int argc;
char *argv[];
{
    char line[MAXLINELEN];
    int i,whichconn;
 
#ifdef DEBUGMODE
    outfile = fopen("anonirc.log","w");
#endif
    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
        if (whichconn > -1)
          usercmdfilter(whichconn,line);
        else
          serverfilter(whichconn+100,line);
        }
    }
#ifdef DEBUGMODE
    fclose(outfile);
#endif
}
 
void 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)))
      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);
}
