/*
Proxyscanner. Code 100% ripped from Operserv.
Fixed 22/08/03 by commx for asuka compatibility. (C) 2003 by chris "commx" jurk.
(C) Michael Meier 2000
-----------------------------------------------------------------------------
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
------------------------------------------------------------------------------

ToDo:
- Maybe some bugfixing? No Bugs known ATM.
*/
#undef sunossuxx

#define proxyscanversion "2.00"

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ctype.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>

#define NICKLEN 15     // Maximum nick length
#define USERLEN 10     // Maximum ident length
#define HOSTLEN 63     // Maximum hostname length
#define REALLEN 50     // Maximum realname length

#define MYHOST "192.168.1.1" // The IP we bind to
#define MYPORT 6660    // The port we are listening on
#define SCANTHREADS 25  // How many Scanners run simultanously
#define MAXSEARCH 20  // Maximum number of clients to search for an unscanned
#define RESCANTIME 24*3600 // Rescan after 20h
#define RESCANOPEN 5*60    // 5 minutes
#define GLINETIME 1800   // GLINEs last 1/2 hour.
#define CONTIMEOUT 45      // A connection attempt times out after 35 secs
#define TRANSTIMEOUT 45    // A transfer (read or write) times out after 45 secs
#define CHILDSLPTIME 5     // How long a child sleeps when it has nothing to do
#define MAXPARAMS 30       // out of operservice2... // commx
#define TMPSSIZE 1040      // out of operservice2... // commx

typedef struct {
  char nick[NICKLEN+1];
  int hopsaway;
  long numeric;
  char ident[USERLEN+1];
  char host[HOSTLEN+1];
  char umode[20];
  char realname[REALLEN+1];
  char authname[NICKLEN+1];
  long realip;
  long connectat;
  struct userdata *next;
} userdata;

typedef struct {
  char nick[200];
  int hopsaway;
  long numeric;
  long createdat;
  long connectat;
  long connectedto;
  long maxclients;
  struct serverdata *next;
} serverdata;

typedef struct {  // A host on the network
  int a, b, c, d; // His IP
  int n;          // How many are online from that IP
  long lastscan;  // Timestamp of last scan
} ahost;

typedef struct {   // A scanned host
  int a, b, c, d;  // IP
  long lastscan;   // Timestamp of last scan
} scannedhost;

typedef struct {
  int a, b, c, d;  // IP
  long lastscan;   // Timestamp of last scan
} opensocks;

typedef struct {
  int a, b, c, d;  // IP this child is currently scanning
  pid_t pid;
  int fdwri;
  int fdrea;
  long scanstart;
} scanthread;

char dummy[20];
serverdata *sls;
serverdata *sle;
#define SIZEOFUL 4096
userdata * uls[SIZEOFUL];
char lastline[520];
char tokens[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789[]";
char tmps1[2000];
char part1[2000];
char command[2000];
char conpass[100]="yeahsure";  // These are compile-time defaults only, you
char mynick[NICKLEN+1]="O2";          // should not change them, use a configfile
char myname[100]="someservice.somenet.org";  // instead.
char conto[100]="someserver.somenet.org";
char conport[10]="4400";
char scandatfile[FILENAME_MAX]="proxyscan.dat";
char servernumeric[3];
char uplnum[3]="";
char lastburstchan[2000]="";
int lastburstflags=0;
long starttime;  // Timestamp, contains starttime of the service
int burstcomplete=0;  // Is set to 1 once the netburst completed
char logline[2500]="";
char logfile[FILENAME_MAX]="proxyscan.log";
long timestdif=0;   // Timestamp-difference
long lasthourly=0;
scanthread scanchilds[SCANTHREADS];
pid_t serverspid=0;
char myip[4];
ahost *hon;   // Hosts On the Net
long honcu, honma;
scannedhost * scho[256]; // SCanned HOsts
long schocu[256], schoma[256];
opensocks *opso; // Hosts that were found to be open
long opsocu, opsoma;
char inputbuffer[10000];
char outpb[2000];
int whatever;
int lastscanpos=0;
int terminateme=0;
char chantojoin[200+1]="#twilightzone";
char authusername[50]="username";
char authpassword[50]="pass";
char authline[500]="";
char osnum[10]="";
char params[MAXPARAMS][TMPSSIZE]; // The last line received from the
                                         // server, split at the spaces.
int paramcount;                   // How many slots of params[] are used
  
/* End of global variable definitions */

/* Define important functions */
time_t getnettime();
void writeoutp();
long getlastscan(int a, int b, int c, int d);
int match2strings(char *patrn, char *strng);
int opensql(char *host, char *username, char *password, char *database, int port);
int alldigit(char *s);
int tcpopen(char *host, char *service);
long gettokenv(char tok);
long tokentolong(char* token);
void longtotoken(long what, char* result, int dig);
void msgtouser(long unum, char *txt);
long iptolong(int a, int b, int c, int d);
void toLowerCase(char* a);
void normnum(char* a);
int numericinuse(long num);
long getsrvnum(char *a);
long countusershit(char *pattern);
void noticeallircops(char *thetext);
void delalluserson(long num);
void delsrv(char *a);
void newserver(char *nick, int hopsaway, long numeric, long createdat, long connectat, long connectedto, long maxclients);
void putlog();
void splitip(unsigned long IP, unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d);
void saveall();
long countusersonsrv(long num);
long countusersonthenet();
long nicktonum(char *a);
void numtohostmask(long num, char *res);
void numtonick(long num, char *res);
int isircop(long num);
long durationtolong(char *dur);
void longtoduration(char *dur, long what);
int loadconfig(char *filename);
void delchar(char *a, char b);
void appchar(char *a, char b);
void addnicktoul(userdata *a);
/* End of important functions defining */

void killallchilds() {
  int i;
  if (serverspid!=0) { kill(serverspid,SIGTERM); }
  for (i=0;i<SCANTHREADS;i++) {
    kill(scanchilds[i].pid,SIGTERM);
  }
}

void splitip(unsigned long IP, unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d) {
  unsigned long t;
  t=IP;
  *d=t%256; t/=256; *c=t%256; t/=256; *b=t%256; t/=256; *a=t;
}

void honadd(long IP) {
  int a,b,c,d; long i; userdata *x;
  splitip(IP,&a,&b,&c,&d);
  if (IP==0) { return; }
  if ((honcu+1)>=honma) {
    // Resize the list
    honma*=2;
    hon=(ahost *)realloc((void *)hon,honma*sizeof(ahost));
    if (hon==NULL) {
      sprintf(logline,"!!! Resizing of Hostlist failed. Terminating. !!!"); putlog();
      exit(1);
    }
  }
  for (i=0;i<honcu;i++) {
    if ((hon[i].a==a) && (hon[i].b==b) && (hon[i].c==c) && (hon[i].d==d)) {
      // increase number of clients from that host
      hon[i].n++;
      return;
    }
  }
  i=honcu;
  hon[i].a=a; hon[i].b=b; hon[i].c=c; hon[i].d=d; hon[i].n=1;
  hon[i].lastscan=getlastscan(a,b,c,d);
  if (hon[i].lastscan+RESCANTIME>getnettime()) { return; }
  for (i=0;i<SIZEOFUL;i++) {
    x=uls[i];
    while (x!=NULL) {
      if (x->realip==IP) { hon[honcu].n++; }
      x=(void *)x->next;
    }
  }
  honcu++;
}

void hondel(long IP) {
  int a,b,c,d; long i;
  splitip(IP,&a,&b,&c,&d);
  for (i=0;i<honcu;i++) {
    if ((hon[i].a==a) && (hon[i].b==b) && (hon[i].c==c) && (hon[i].d==d)) {
      // Decrease number of clients from that host
      hon[i].n--;
      // No clients remaining? => Delete it from the list
      if (hon[i].n==0) {
        honcu--;
        if (i!=honcu) {
          hon[i]=hon[honcu];
        }
      }
      return;
    }
  }
/*  sprintf(logline,"!!! WARNING: hondel failed for %d.%d.%d.%d !!!",a,b,c,d); putlog(); */
}

void hondel2(long IP) {
  int a,b,c,d; long i;
  splitip(IP,&a,&b,&c,&d);
  for (i=0;i<honcu;i++) {
    if ((hon[i].a==a) && (hon[i].b==b) && (hon[i].c==c) && (hon[i].d==d)) {
      honcu--;
      if (i!=honcu) {
        hon[i]=hon[honcu];
      }
      return;
    }
  }
  sprintf(logline,"!!! WARNING: hondel2 failed for %d.%d.%d.%d !!!",a,b,c,d); putlog();
}

void schoadd(long IP, long scantime) {
  int a,b,c,d; int i;
  splitip(IP,&a,&b,&c,&d);
  if ((schocu[a]+1)>=schoma[a]) {
    // Resize the list
    schoma[a]+=100;
    scho[a]=(scannedhost *)realloc((void *)scho[a],schoma[a]*sizeof(scannedhost));
    if (scho[a]==NULL) {
      sprintf(logline,"!!! Resizing of Hostlist failed. Terminating. !!!"); putlog();
      exit(1);
    }
  }
  for (i=0;i<schocu[a];i++) {
    if ((scho[a][i].a==a) && (scho[a][i].b==b) && (scho[a][i].c==c) && (scho[a][i].d==d)) {
      // Enter data
      scho[a][i].lastscan=scantime;
      return;
    }
  }
  i=schocu[a];
  scho[a][i].a=a; scho[a][i].b=b; scho[a][i].c=c; scho[a][i].d=d;
  scho[a][i].lastscan=scantime;
  schocu[a]++;
}

void schoaddwithoutcheck(long IP, long scantime) {
  int a,b,c,d; int i;
  splitip(IP,&a,&b,&c,&d);
  if ((schocu[a]+1)>=schoma[a]) {
    // Resize the list
    schoma[a]+=100;
    scho[a]=(scannedhost *)realloc((void *)scho[a],schoma[a]*sizeof(scannedhost));
    if (scho[a]==NULL) {
      sprintf(logline,"!!! Resizing of Hostlist failed. Terminating. !!!"); putlog();
      exit(1);
    }
  }
  i=schocu[a];
  scho[a][i].a=a; scho[a][i].b=b; scho[a][i].c=c; scho[a][i].d=d;
  scho[a][i].lastscan=scantime;
  schocu[a]++;
}

void schopurge(long scantime) {
  int i, j;
  for (j=0;j<256;j++) {
    for (i=0;i<schocu[j];i++) {
      if (scho[j][i].lastscan<scantime) {
        // Delete it from the list
        schocu[j]--;
        if (i!=schocu[j]) {
          scho[j][i]=scho[j][schocu[j]];
        }
        i--;
      }
    }
  }
}

void opsoadd(long IP, long scantime) {
  int a,b,c,d; int i;
  splitip(IP,&a,&b,&c,&d);
  if ((opsocu+1)>=opsoma) {
    // Resize the list
    opsoma*=2;
    opso=(opensocks *)realloc((void *)opso,opsoma*sizeof(opensocks));
    if (opso==NULL) {
      sprintf(logline,"!!! Resizing of Open host list failed. Terminating. !!!"); putlog();
      exit(1);
    }
  }
  for (i=0;i<opsocu;i++) {
    if ((opso[i].a==a) && (opso[i].b==b) && (opso[i].c==c) && (opso[i].d==d)) {
      // Enter data
      opso[i].lastscan=scantime;
      return;
    }
  }
  i=opsocu;
  opso[i].a=a; opso[i].b=b; opso[i].c=c; opso[i].d=d;
  opso[i].lastscan=scantime;
  opsocu++;
}

void opsopurge(long scantime) {
  int i;
  for (i=0;i<opsocu;i++) {
    if (opso[i].lastscan<scantime) {
      // Delete it from the list
      opsocu--;
      if (i!=opsocu) { memcpy(&opso[i],&opso[i+1],(opsocu-i)*sizeof(scannedhost)); }
    }
  }
}

void schosave(char *filename) {
  int i, j, k;
  j=open(filename,O_WRONLY | O_CREAT | O_TRUNC,S_IRWXU);
  if (j<0) {
    sprintf(logline,"!!! Could not write datafile %s !!!",filename); putlog();
    return;
  }
  for (k=0;k<256;k++) {
    for (i=0;i<schocu[k];i++) {
      write(j,&scho[k][i],sizeof(scannedhost));
    }
  }
  close(j);
}

void schoload(char *filename) {
  int i, j; scannedhost tmph; long ip;
  j=open(filename,O_RDONLY);
  if (j<0) {
    sprintf(logline,"!!! Could not read datafile %s !!!",filename); putlog();
    return;
  }
  do {
    i=read(j,&tmph,sizeof(scannedhost));
    ip=((tmph.a*256+tmph.b)*256+tmph.c)*256+tmph.d;
    schoaddwithoutcheck(ip,tmph.lastscan);
  } while (i!=0);
  close(j);
}

void putlog() {
  time_t whatever; int h, m; FILE *lf;
  whatever=time(NULL);
  h=localtime(&whatever)->tm_hour;
  m=localtime(&whatever)->tm_min;
  printf("[%02d:%02d] %s\n",h,m,logline);
  if (strcmp(logfile,"")!=0) {
    lf=fopen(logfile,"a");
    fprintf(lf,"[%02d:%02d] %s\n",h,m,logline);
    fclose(lf);
  }
}

time_t getnettime() {
  return time(NULL)+timestdif;
}

/* Next proc is stolen from some library
   http://avrc.city.ac.uk/nethack/nethack-cxref-3.3.1/src/hacklib.c.src.html */
int match2strings(char *patrn, char *strng) {
  char s, p;
  /*
   :  Simple pattern matcher:  '*' matches 0 or more characters, '?' matches
   :  any single character.  Returns TRUE if 'strng' matches 'patrn'.
   */
 pmatch_top:
     s = *strng++;  p = *patrn++; /* get next chars and pre-advance */
     if (!p)                      /* end of pattern */
  return((s == '\0'));           /* matches iff end of string too */
     else if (p == '*')           /* wildcard reached */
  return(((!*patrn || match2strings(patrn, strng-1)) ? 1 : s ? match2strings(patrn-1, strng) : 0));
     else if (p != s && (p != '?' || !s))  /* check single character */
  return 0;           /* doesn't match */
     else                         /* return pmatch(patrn, strng); */
  goto pmatch_top;        /* optimize tail recursion */
}

int alldigit(char *s) {
  for (;*s;s++) if (!isdigit(*s)) return(0);
  return(1);
}

int tcpopen(char *host, char *service) {
  int unit;
  static struct sockaddr_in sin;
  static struct servent *sp;
  static struct hostent *hp;
  if ((hp=gethostbyname(host)) == NULL) {
    fprintf(stderr,"Oops ... unknown host \"%s\"\n",host);
    return(-1);
  }
  memset((char *)&sin, sizeof(sin),0);
  memcpy((char *)&sin.sin_addr,hp->h_addr,hp->h_length);
  sin.sin_family=hp->h_addrtype;
  if (alldigit(service)) {
    sin.sin_port=htons(atoi(service));
  } else {
    if ((sp=getservbyname(service,"tcp")) != NULL) {
      sin.sin_port=sp->s_port;
    } else {
      fprintf(stderr,"Oops ... no such service \"%s\"\n",service);
      return(-1);
    }
  }
  if ((unit=socket(AF_INET,SOCK_STREAM,0)) < 0) {
    fprintf(stderr,"Oops ... tcp/ip cannot open socket\n");
    return(-1);
  }
  if (connect(unit,(struct sockaddr *)&sin,sizeof(sin)) < 0) {
    fprintf(stderr,"Oops ... tcp/ip cannot connect to server\n");
    return(-1);
  }
  return(unit);
}

long gettokenv(char tok) {
  long i;
  for (i=0;i<64;i++) {
    if (tok==tokens[i]) { return i; }
  }
  return -1;
}

long tokentolong(char* token) {
  long res=0;
  int i=0;
  while (token[i]!='\0') {
    res=res << 6;
    res+=gettokenv(token[i]);
    i++;
  }
  return res;
}

void longtotoken(long what, char* result, int dig) {
  int i;
  for (i=0;i<dig;i++) {
    result[dig-1-i]=tokens[what%64];
    what/=64;
  }
  result[dig]='\0';
}

void msgtouser(long unum, char *txt) {
  char target[100];
  longtotoken(unum,target,5);
  sprintf(outpb,"%sAAB O %s :%s\n",servernumeric,target,txt); writeoutp();
}

long iptolong(int a, int b, int c, int d) {
  return (((((a*256)+b)*256)+c)*256)+d;
}

void toLowerCase(char* a) {
  int i, j;
  j=('a'-'A');
  i=0;
  while (a[i]!='\0') {
    if ((a[i]>='A') && (a[i]<='Z')) {
      a[i]=a[i]+j;
    }
    if (a[i]=='{') { a[i]='['; }
    if (a[i]=='}') { a[i]=']'; }
    i++;
  }
}

void normnum(char* a) {
  int b;
  b=strlen(a);
  if (b==3) {
    a[4]=a[2];
    a[3]=a[1];
    a[2]='A';
    a[1]=a[0];
    a[0]='A';
    a[5]='\0';
    return;
  }
  if (b==1) {
    a[1]=a[0];
    a[0]='A';
    a[2]='\0';
    return;
  }
  if ((b!=5) && (b!=2)) { sprintf(logline,"Couldn't norm numeric %s",a); putlog(); }
}

long getsrvnum(char *a) {
  serverdata *b;
  b=sls;
  while (b!=NULL) {
    if (strcmp(b->nick,a)==0) { return b->numeric; }
    b=(void *)b->next;
  }
  return -1;
}

long countusershit(char *pattern) {
  userdata *a; long howmany=0; char tmps2[2000], tmps3[2000]; int i;
  for (i=0;i<SIZEOFUL;i++) {
    a=uls[i];
    strcpy(tmps2,pattern);
    toLowerCase(tmps2);
    while (a!=NULL) {
      sprintf(tmps3,"%s@%s",a->ident,a->host);
      toLowerCase(tmps3);
      if (match2strings(tmps2,tmps3)) {
        howmany++;
      }
      a=(void *)a->next;
    }
  }
  return howmany;
}

void noticeallircops(char *thetext) {
  userdata *a; char target[10]; int i;
  for (i=0;i<SIZEOFUL;i++) {
    a=uls[i];
    while (a!=NULL) {
      if (strcspn(a->umode,"o")!=strlen(a->umode)) {
        longtotoken(a->numeric,target,5);
        sprintf(outpb,"%sAAB O %s :%s\n",servernumeric,target,thetext); writeoutp();
      }
      a=(void *)a->next;
    }
  }
}

void delalluserson(long num) {
  userdata *a, *b, *c;
  a=uls[num];
  uls[num]=NULL;
  while (a!=NULL) {
//      deluserfromallchans(a->numeric);
//      printf("Killuser: %s\n",a->nick);
    hondel(a->realip);
    b=(void *)a->next;
    free(a);
    a=b;
  }
}

void delsrv(char *a) {
  long num; char tmps2[100];
  serverdata *b, *c;
  num=getsrvnum(a);
  longtotoken(num,tmps2,2);
  sprintf(logline,"Lost server: %s (Numeric %s)",a,tmps2); putlog();
  delalluserson(num);
  b=sls; c=NULL;
  while (b!=NULL) {
    if (b->numeric==num) {
      if (c==NULL) {
        sls=(void *)b->next;
      } else {
        c->next=b->next;
      }
      if (b==sle) { sle=c; }
      free(b);
      b=sls; c=NULL;
      while (b!=NULL) {
        if (b->connectedto==num) {
          delsrv(b->nick);
          b=sls; c=NULL;
        } else {
          c=b;
          b=(void *)b->next;
        }
      }
      return;
    }
    c=b;
    b=(void *)b->next;
  }
}

void addnicktoul(userdata *a) {
  userdata *b;
  b=uls[a->numeric/262144];
  if (b==NULL) {
    uls[a->numeric/262144]=a;
  } else {
    while (b->next!=NULL) {
      b=(void *)b->next;
    }
    b->next=(void *)a;
  }
}

void handlenickmsg() {
  userdata *a;
  int res=0; int rnstart;
  char nick[2000]=""; int hopsaway=0;
  long connectat=0; char ident[2000]="";
  char host[2000]="";
  char umode[2000]=""; char realname[2000]="";
  char realiptmp[2000]=""; char numtmp[2000]="";
  char tmps2[2000]="";
  char lecken1[2000]=""; char lecken2[2000]="";
  int hass; long tmpl; int ipa, ipb, ipc, ipd;
  if (strlen(part1)>=3) {
    // Nickchange
    res=sscanf(lastline,"%s %s %s %d",lecken1,lecken2,nick,&hass);
    if (res!=4) {
      sprintf(logline,"!!! Parsing Nickchange failed: %s !!!",lastline); putlog();
      return;
    }
    normnum(lecken1);
    connectat=hass;
    tmpl=tokentolong(lecken1);
    toLowerCase(nick);
    a=uls[tmpl/262144];
    while (a!=NULL) {
      if (a->numeric==tmpl) {
        strcpy(a->nick,nick);
        a->connectat=connectat;
        return;
      }
      a=(void *)a->next;
    }
    sprintf(logline,"!!! Nickchange failed: %s !!!",lastline); putlog();
  } else {
    if (strlen(part1)<10) {
      putlog("!!! Nick introduction failed (wrong # args): %s !!!",lastline);
      return;
    }
    rnstart=(paramcount-1);
    if (paramcount==10) {
    	strcpy(umode,"+");
    } else {
    	int ti1, ti2;
    	strcpy(umode,params[7]);
    	ti1=0; ti2=0;
    	for (ti1=0;umode[ti1]!=0;ti1++) {
    		if (umode[ti1]=='h') { ti2++; rnstart++; }
    		if (umode[ti1]=='r') { ti2++; rnstart++; }
    	}
    }
    strcpy(lecken1,params[rnstart-1]);
    strcpy(realiptmp,params[rnstart-2]);
    normnum(lecken1);
    connectat=hass;
    a=(userdata *) malloc(sizeof(userdata));
    a->next=NULL;
    a->hopsaway=hopsaway;
    a->connectat=connectat;
    a->realip=tokentolong(realiptmp);
    honadd(a->realip);
    a->numeric=tokentolong(lecken1);
    toLowerCase(nick);
    strcpy(a->nick,nick);
    strcpy(a->ident,ident);
    strcpy(a->host,host);
    strcpy(a->umode,&umode[1]);
    strcpy(a->realname,params[rnstart]);
    addnicktoul(a);
   // sprintf(logline,"New User: %s (Numeric %s - %s) (Mode %s)",a->nick,a->numeric,lecken1,a->umode); putlog();
  }
}

void handlequitmsg() {
  userdata *a, *b; char tmps2[2000]; long tmpl; int i;
  if (part1[0]==':') {
    // We have some QUIT :nick
    strcpy(tmps2,&part1[1]);
    toLowerCase(tmps2);
    for (i=0;i<SIZEOFUL;i++) {
      a=uls[i]; b=NULL;
      while (a!=NULL) {
        if (strcmp(tmps2,a->nick)==0) {
          hondel(a->realip);
          if (b==NULL) {
            uls[i]=(void *)a->next;
          } else {
            b->next=a->next;
          }
          free(a);
          return;
        }
        b=a;
        a=(void *)a->next;
      }
    }
  } else {
    // We have a QUIT numeric
    normnum(part1);
    tmpl=tokentolong(part1);
    a=uls[tmpl/262144]; b=NULL;
    while (a!=NULL) {
      if (tmpl==a->numeric) {
        hondel(a->realip);
        if (b==NULL) {
          uls[tmpl/262144]=(void *)a->next;
        } else {
          b->next=a->next;
        }
        free(a);
        return;
      }
      b=a;
      a=(void *)a->next;
    }
  }
  sprintf(logline,"!!! Deluser failed: %s !!!",lastline); putlog();
}

void newserver(char *nick, int hopsaway, long numeric, long createdat, long connectat, long connectedto, long maxclients) {
  serverdata *a; char tmps2[100];
  longtotoken(numeric,tmps2,2);
  sprintf(logline,"New Server: %s (Numeric %s)",nick,tmps2); putlog();
  a=(serverdata *) malloc(sizeof(serverdata));
  a->next=NULL;
  a->hopsaway=hopsaway;
  a->numeric=numeric;
  a->createdat=createdat; a->connectat=connectat;
  a->connectedto=connectedto; a->maxclients=maxclients;
  strcpy(a->nick,nick);
  if (sls==NULL) {
    sls=a; sle=a;
  } else {
    sle->next=(void *)a;
    sle=a;
  }
}

void appchar(char *a, char b) {
  int c;
  c=strlen(a);
  a[c]=b;
  a[c+1]='\0';
}

void delchar(char *a, char b) {
  int c, d;
  for (c=0;c<strlen(a);c++) {
    if (a[c]==b) {
      for (d=c;d<strlen(a)-1;d++) {
        a[d]=a[d+1];
      }
      a[strlen(a)-1]='\0';
      c--;
    }
  }
}

int loadconfig(char *filename) {
  FILE *myf; char myline[1024]; char *res;
  char tmps2[2000], tmps3[2000]; int r2;
  myf=fopen(filename,"r");
  if (myf==NULL) { return (-1); }
  do {
    res=fgets(myline,1024,myf);
    delchar(myline,'\n');
    delchar(myline,'\r');
    if ((myline[0]!='\0') && (myline[0]!='#')) {
      // This line is not a comment, we'll have to parse it
      r2=sscanf(myline,"%[^=]%[^\n]",tmps2,tmps3);
      if (r2!=2) {
        printf("Illegal line format in configfile: %s\n",myline);
      } else {
        delchar(tmps3,'=');
//        printf("%s // %s\n",tmps2,tmps3);
        if (strcmp(tmps2,"mynick")==0) { strcpy(mynick,tmps3); }
        if (strcmp(tmps2,"myname")==0) { strcpy(myname,tmps3); }
        if (strcmp(tmps2,"conto")==0) { strcpy(conto,tmps3); }
        if (strcmp(tmps2,"conport")==0) { strcpy(conport,tmps3); }
        if (strcmp(tmps2,"conpass")==0) { strcpy(conpass,tmps3); }
        if (strcmp(tmps2,"numeric")==0) { longtotoken(atoi(tmps3),servernumeric,2); }
        if (strcmp(tmps2,"logfile")==0) { strcpy(logfile,tmps3); }
        if (strcmp(tmps2,"authline")==0) { strcpy(authline,tmps3); }
        if (strcmp(tmps2,"osnum")==0) { longtotoken(atoi(tmps3),osnum,2); strcat(osnum,"AAB"); }
        if (strcmp(tmps2,"chantojoin")==0) { strcpy(chantojoin,tmps3); }
        if (strcmp(tmps2,"authusername")==0) { strcpy(authusername,tmps3); }
        if (strcmp(tmps2,"authpassword")==0) { strcpy(authpassword,tmps3); }
      }
    } else {
//      printf("Line ignored: %s\n",myline);
    }
  } while (res!=NULL);
  sprintf(authline,"P %s :auth %s %s",osnum,authusername,authpassword);
  return 0;
}

void longtoduration(char *dur, long what) {
  long d, h, m, s, tmpl;
  tmpl=what;
  d=(tmpl/86400); tmpl=tmpl%86400;
  h=(tmpl/3600); tmpl=tmpl%3600;
  m=(tmpl/60); tmpl=tmpl%60;
  s=tmpl;
  sprintf(dur,"%4dd %2dh %2dm %2ds",d,h,m,s);
}

long durationtolong(char *dur) {
  long res; char tmps2[2000]; char tmpc;
  strcpy(tmps2,dur);
  tmps2[strlen(tmps2)-1]='\0';
  res=atol(tmps2);
  tmpc=dur[strlen(dur)-1];
  if (tmpc=='s') { return res; }
  if (tmpc=='m') { return (res*60); }
  if (tmpc=='h') { return (res*3600); }
  if (tmpc=='d') { return (res*86400); }
  if (tmpc=='w') { return (res*604800); }
  if (tmpc=='M') { return (res*2592000); }
  if (tmpc=='y') { return (res*31104000); }
  return -2;
}

int ischarinstr(char a, char *b) {
  int i;
  for (i=0;i<strlen(b);i++) {
    if (b[i]==a) { return 1; }
  }
  return 0;
}

int isircop(long num) {
  userdata *a;
  a=uls[num/262144];
  while (a!=NULL) {
    if (a->numeric==num) {
      // printf("Umodes: %s ; strcspn: %d\n",a->umode,strcspn(a->umode,"o"));
      if (ischarinstr('o',a->umode)) { return 1; } else { return 0; }
    }
    a=(void *)a->next;
  }
  return 1;
}

void numtonick(long num, char *res) {
  userdata *a;
  a=uls[num/262144];
  while (a!=NULL) {
    if (num==a->numeric) {
      strcpy(res,a->nick);
      return;
    }
    a=(void *)a->next;
  }
  strcpy(res,"");
  sprintf(logline,"!!! Failed to convert numeric %d to nick !!!",num); putlog();
}

void numtohostmask(long num, char *res) {
  userdata *a;
  a=uls[num/262144];
  while (a!=NULL) {
    if (num==a->numeric) {
      sprintf(res,"%s!%s@%s",a->nick,a->ident,a->host);
      return;
    }
    a=(void *)a->next;
  }
  strcpy(res,"");
  sprintf(logline,"!!! Failed to convert numeric %d to hostmask !!!",num); putlog();
}

void handleservermsg(int firsts) {
  char tmps2[2000]; char tmps3[2000];
  int hopsaway; int createdat; int res; int connectat;
  char tmpnum[2000]; char tmpconnectedto[2000];
  char nick[2000];
  if (firsts==1) {
    res=sscanf(lastline,"%s %s %d %d %d %s %s",tmps2,nick,&hopsaway,&createdat,&connectat,tmps3,tmpnum);
    strcpy(tmpconnectedto,servernumeric);
    if (res!=7) { sprintf(logline,"!!! Failed to parse Servermessage: %s !!!",lastline); putlog(); return; }
    timestdif=connectat-getnettime();
    sprintf(logline,"Timestamp-difference: %d",timestdif); putlog();
  } else {
    res=sscanf(lastline,"%s %s %s %d %d %d %s %s",tmpconnectedto,tmps2,nick,&hopsaway,&createdat,&connectat,tmps3,tmpnum);
    if (res!=8) { sprintf(logline,"!!! Failed to parse Servermessage: %s !!!",lastline); putlog(); return; }
  }
  normnum(tmpnum);
  normnum(tmpconnectedto);
  strcpy(tmps2,tmpnum);
  tmps2[2]='\0';
  if (firsts==1) { strcpy(uplnum,tmps2); }
  newserver(nick,hopsaway,tokentolong(tmps2),createdat,connectat,tokentolong(tmpconnectedto),tokentolong(&tmpnum[2]));
  sprintf(outpb,"%sAAB %s\n",servernumeric,authline); writeoutp();
}

void handleping() {
  char tmps[2000]="";
  sscanf(lastline,"%s",tmps);
  sprintf(outpb,"%s Z %s\n",myname,tmps); writeoutp();
  sprintf(logline,"Ping? Pong!"); putlog();
}

void handlekillmsg() {
  userdata *a, *b; int res; long num;
  char tmps2[2000]; char tmps3[2000]; char tmps4[2000];
  res=sscanf(lastline,"%s %s %s",tmps2,tmps3,tmps4);
  if (res!=3) {
    sprintf(logline,"!!! Couldnt parse killmessage: %s !!!",lastline); putlog();
    return;
  }
  normnum(tmps4);
  num=tokentolong(tmps4);
  a=uls[num/262144]; b=NULL;
  while (a!=NULL) {
    if (a->numeric==num) {
      hondel(a->realip);
      if (b==NULL) {
        uls[num/262144]=(void *)a->next;
      } else {
        b->next=a->next;
      }
      free(a);
      return;
    }
    b=a;
    a=(void *)a->next;
  }
  sprintf(logline,"!!! KILL failed: %s !!!",lastline); putlog();
}

void handlesquit() {
  char tmps2[2000]; char tmps3[2000]; char tmps4[2000];
  int res;
  res=sscanf(lastline,"%s %s %s",tmps2,tmps3,tmps4);
  if (res!=3) {
    sprintf(logline,"!!! Failed to parse SQUIT: %s !!!",lastline); putlog();
    return;
  }
  delsrv(tmps4);
}

void handlemodemsg() {
  char tmps2[2000]=""; char tmps3[2000]; char tmps4[2000]=""; char tmps5[2000]="";
  int res; userdata *a; int t1, t2; char *parspos; int i2;
  res=sscanf(lastline,"%s %s %s %s",tmps2,tmps3,tmps4,tmps5);
  if (res!=4) {
    printf("!!! Failed to parse MODE: %s !!!\n",lastline);
    return;
  }
  if (tmps2[0]!=':') {
    normnum(tmps2);
    if (strlen(tmps2)<3) {
      /* We got a servernumeric! */
      strcpy(tmps2,":some.ircserver.quakenet.eu.org");
      toLowerCase(tmps4);
    } else {
      /* We got a clientnumeric! */
      numtonick(tokentolong(tmps2),tmps3);
      tmps2[0]=':';
      strcpy(&tmps2[1],tmps3);
//      strcpy(tmps4,tmps3);
      toLowerCase(tmps4);
    }
  } else {
    toLowerCase(tmps2); toLowerCase(tmps4);
  }
  if (strcmp(&tmps2[1],tmps4)==0) {
    // User changes Usermodes
    for (i2=0;i2<SIZEOFUL;i2++) {
      a=uls[i2];
      while(a!=NULL) {
        if (strcmp(tmps4,a->nick)==0) {
          t1=0; t2=1;
          while (tmps5[t2]!='\0') {
            if ((tmps5[t2]=='+') || (tmps5[t2]=='-')) {
              if (tmps5[t2]=='+') { t1=1; } else { t1=0; }
            } else {
              if (t1==1) {
                appchar(a->umode,tmps5[t2]);
              } else {
                delchar(a->umode,tmps5[t2]);
              }
            }
            t2++;
          }
  //        printf("Modes now: %s \n",a->umode);
          return;
        }
        a=(void *)a->next;
      }
    }
    sprintf(logline,"!!! Usermodechange failed: %s !!!",lastline); putlog();
  } else {
    // Mode on a channel
  }
}

long nicktonu2(char *a) {
  userdata *b; int i;
  for (i=0;i<SIZEOFUL;i++) {
    b=uls[i];
    while (b!=NULL) {
      if (strcmp(b->nick,a)==0) {
        return b->numeric;
      }
      b=(void *)b->next;
    }
  }
  return -1;
}

long nicktonum(char *a) {
  long c;
  c=nicktonu2(a);
  if (c==-1) {
    sprintf(logline,"!!! Couldnt translate nick %s to numeric !!!",a); putlog();
  }
  return c;
}

long countusersonthenet() {
  userdata *a; long tmpl; int i;
  tmpl=0;
  for (i=0;i<SIZEOFUL;i++) {
    a=uls[i];
    while (a!=NULL) {
      tmpl++;
      a=(void *)a->next;
    }
  }
  return tmpl;
}

long countusersonsrv(long num) {
  userdata *a; long n=0;
  a=uls[num];
  while (a!=NULL) {
    n++;
    a=(void *)a->next;
  }
  return n;
}

/* Thestats needs to be an array of longs.
   [0]: hosts total
   [1]: hosts scanned
   [2]: hosts waiting for rescan
   [3]: hosts not yet scanned
   [4]: length of scanned host list
*/
void getsomestats(long *thestats) {
  int i; long b; long cnt;
  thestats[4]=0;
  for (i=0;i<256;i++) {
    thestats[4]+=schocu[i];
  }
  thestats[0]=honcu;
  thestats[1]=0; thestats[2]=0; thestats[3]=0;
  cnt=getnettime();
  for (i=0;i<honcu;i++) {
    b=hon[i].lastscan;
    if (b==0) { thestats[3]++; } else {
      if (b+RESCANTIME<cnt) { thestats[2]++; } else { thestats[1]++; }
    }
  }
  
}

void dostatus(long unum, char *tail) {
  int usersonnet; char tmps2[2000], tmps3[2000];
  long somestats[5]; int i;
  usersonnet=countusersonthenet();
  sprintf(tmps2,"There are currently %d users on the network.",usersonnet);
  msgtouser(unum,tmps2);
  sprintf(tmps2,"Current network time: %d",getnettime());
  msgtouser(unum,tmps2);
  longtoduration(tmps3,(time(NULL)-starttime));
  sprintf(tmps2,"I am running for %s",tmps3);
  msgtouser(unum,tmps2);
  getsomestats(somestats);
  sprintf(tmps2,"%6d hosts need to be scanned.",somestats[0]);
  msgtouser(unum,tmps2);
  sprintf(tmps2,"%6d were scanned and found to be open, waiting for GLINE to take effect or rescan",somestats[1]);
  msgtouser(unum,tmps2);
  sprintf(tmps2,"%6d need rescan because last scan was more than %d seconds ago",somestats[2],RESCANTIME);
  msgtouser(unum,tmps2);
  sprintf(tmps2,"%6d not yet scanned.",somestats[3]);
  msgtouser(unum,tmps2);
  sprintf(tmps2,"List of scanned hosts has %d entries, entries expire after about %d hours.",somestats[4],RESCANTIME/3600);
  msgtouser(unum,tmps2);
  for (i=0;i<SCANTHREADS;i++) {
    if (scanchilds[i].a==0) {
      sprintf(tmps2,"%2d  %5ds  idle",i,getnettime()-scanchilds[i].scanstart);
    } else {
      sprintf(tmps2,"%2d  %5ds  %d.%d.%d.%d",
        i,getnettime()-scanchilds[i].scanstart,scanchilds[i].a,scanchilds[i].b,scanchilds[i].c,scanchilds[i].d);
    }
    msgtouser(unum,tmps2);
  }
}

void dooperhelp(long unum, char *tail) {
  int res; char tmps2[2000], tmps3[2000], tmps4[2000];
  res=sscanf(tail,"%s %s %s",tmps2,tmps3,tmps4);
  if (res>1) {
    msgtouser(unum,"Sorry, help command is not yet implemented.");
    return;
  }
  msgtouser(unum,"Additional commands for opers:");
  msgtouser(unum,"-----------------------------------------------------------------------");
  msgtouser(unum,"help [command]            Help for a specific command");
  msgtouser(unum,"status                    Prints some status information");
  msgtouser(unum,"listopen                  Lists hosts that were found to be open");
  msgtouser(unum,"listhon                   Shows the list of hosts to scan");
  msgtouser(unum,"save                      Take a wild guess");
}

void saveall() {
  schosave(scandatfile);
}

void dosave(long unum, char *tail) {
  schopurge(getnettime()-RESCANTIME);
  saveall();
  msgtouser(unum,"Done. Tried to save everything.");
}

void dolistopen(long unum, char *tail) {
  int i; char tmps2[2000], tmps3[2000], tmps4[2000]; struct tm *myti; long timestamp;
  msgtouser(unum,"List of open SOCKs/Wingates:");
  opsopurge(getnettime()-RESCANTIME);
  sprintf(tmps2,"%-20s %s","IP","Found at");
  msgtouser(unum,tmps2);
  for (i=0;i<opsocu;i++) {
    timestamp=opso[i].lastscan;
    myti=localtime(&timestamp); strcpy(tmps4,"[strftime failed]");
    strftime(tmps4,200,"%d.%m.%Y %H:%M:%S",myti);
    sprintf(tmps2,"%d.%d.%d.%d",opso[i].a,opso[i].b,opso[i].c,opso[i].d);
    sprintf(tmps3,"%-20s %s",tmps2,tmps4);
    msgtouser(unum,tmps3);
  }
  msgtouser(unum,"--- End of list ---");
}

void dolisthon(long unum, char *tail) {
  int i; char tmps2[2000], tmps3[2000], tmps4[2000]; struct tm *myti;
  for (i=0;i<honcu;i++) {
    sprintf(tmps2,"%d.%d.%d.%d",hon[i].a,hon[i].b,hon[i].c,hon[i].d);
    myti=localtime(&hon[i].lastscan); strcpy(tmps4,"[strftime failed]");
    strftime(tmps4,200,"%d.%m.%Y %H:%M:%S",myti);
    sprintf(tmps3,"%-20s %s",tmps2,tmps4);
    msgtouser(unum,tmps3);
  }
  sprintf(tmps3,"--- End of list - %d listed ---",i);
  msgtouser(unum,tmps3);
}

void handleprivmsg() {
  char tmps2[2000], tmps3[2000], tmps4[2000], ucmd[2000], lotmp[2000];
  int res; long unum; char tail[2000]; int iio;
  res=sscanf(lastline,"%s %s %s %s",tmps2,tmps3,tmps4,ucmd);
  if (res!=4) {
    sprintf(logline,"!!! Failed to parse privmsg: %s !!!",lastline); putlog();
    return;
  }
  if (tmps2[0]==':') {
    toLowerCase(tmps2);
    unum=nicktonum(&tmps2[1]);
  } else {
    normnum(tmps2);
    unum=tokentolong(tmps2);
  }
  toLowerCase(ucmd);
  strcpy(tmps3,&ucmd[1]);
  strcpy(ucmd,tmps3);
//  printf("%d :: %s\n",&unum,ucmd);
  res=strcspn(&lastline[1],":");
  strcpy(tail,&lastline[res+2]);
  iio=isircop(unum);
  if (!iio) {
    tail[250]='\0';  // Truncate to max. 250 characters for non-ircops
  } else {
    tail[450]='\0';  // And 450 for opers to prevent too long lines
  }
//  printf("Tail: %s ;\n",tail);
  numtohostmask(unum,lotmp);
  if (unum==tokentolong(osnum)) { return; }
  sprintf(logline,"%s !%s!",lotmp,tail); putlog();
  if (iio) {
// Commands only available for ircops
    if (strcmp(ucmd,"help")==0) { dooperhelp(unum,tail); return; }
    if (strcmp(ucmd,"status")==0) { dostatus(unum,tail); return; }
    if (strcmp(ucmd,"save")==0) { dosave(unum,tail); return; }
    if (strcmp(ucmd,"listopen")==0) { dolistopen(unum,tail); return; }
    if (strcmp(ucmd,"listhon")==0) { dolisthon(unum,tail); return; }
    msgtouser(unum,"Unknown command.");
  } else {
    msgtouser(unum,"Sorry, my features are only available to opers.");
  }
}

void openlisteningport() {
  /* Opens a port that listens for connections and just sends "OPEN PROXY!"
     to everyone that connects. */
  int listsock; struct sockaddr_in sa; int rrr;
  int incomingconnection; char sockstxt[200];
	memcpy(&sa.sin_addr.s_addr,myip,4);
  listsock=socket(AF_INET,SOCK_STREAM,0);
  sa.sin_family=AF_INET;
  sa.sin_port=htons(MYPORT);
  rrr=bind(listsock,(struct sockaddr *)&sa,sizeof(sa));
  if (rrr<0) {
    printf("bind failed. This is probably because the requested port (%d) is already in use, or it is a priviledged port (<1024) and you are not root. Or you are trying to bind to a nonexistant interface.\n",MYPORT);
    exit(1);
  }
  rrr=listen(listsock,5);
  if (rrr<0) {
    perror("listen() failed");
    exit(1);
  }
  strcpy(sockstxt,"Open SOCKs!\r\n");
  while (1) {
    do {
      errno=0;
      incomingconnection=accept(listsock,NULL,0);
    } while (errno==EINTR);
    if (errno!=0) {
      perror("Notice: Error on accept.");
      close(incomingconnection);
#ifdef sunossuxx
      sleep(1);
      printf("Slowlaris-Bug-Workaround: Trying to reopen socket\n");
      close(listsock);
    	memcpy(&sa.sin_addr.s_addr,myip,4);
      listsock=socket(AF_INET,SOCK_STREAM,0);
      sa.sin_family=AF_INET;
      sa.sin_port=htons(MYPORT);
      rrr=bind(listsock,(struct sockaddr *)&sa,sizeof(sa));
      if (rrr<0) {
        perror("bind() failed. Could not reopen socket");
        kill(getppid(),SIGINT);
        exit(1);
      }
      rrr=listen(listsock,5);
      if (rrr<0) {
        perror("listen() failed. Could not reopen socket");
        kill(getppid(),SIGINT);
        exit(1);
      }
#endif
    } else {
      write(incomingconnection,sockstxt,strlen(sockstxt));
      shutdown(incomingconnection,2);
    }
  }
}

void sigalhandler(int bla) {
  return;
}

int tcpopen2(char *ia, int port) {
	struct sockaddr_in sa; int oursock;
  struct sockaddr_in sa2; int rrr;
	oursock=socket(AF_INET,SOCK_STREAM,0);
	if (oursock<0) {
		return -1;
	}
	memcpy(&sa2.sin_addr.s_addr,myip,4);
  sa2.sin_family=AF_INET;
  sa2.sin_port=htons(0);
  rrr=bind(oursock,(struct sockaddr *)&sa2,sizeof(sa2));
  if (rrr<0) {
    printf("Bind failed for outgoing connection.\n");
    exit(1);
  }
	sa.sin_family=AF_INET;
	sa.sin_port=htons(port);
	memcpy(&sa.sin_addr.s_addr,ia,4);
  alarm(CONTIMEOUT);
  errno=0;
	connect(oursock,(struct sockaddr *)&sa,sizeof(sa));
	alarm(0);
	if (errno!=0) { close(oursock); return -2; }
	return oursock;
}

int socks4scan(int *IP) {
  char adr[4]; int ourcon; char ourreq[9]; int res; unsigned short int poo;
  adr[0]=IP[0];
  adr[1]=IP[1];
  adr[2]=IP[2];
  adr[3]=IP[3];
  memcpy(&ourreq[4],myip,4);
  poo=htons(MYPORT);
  memcpy(&ourreq[2],&poo,2);
  ourreq[0]=4;
  ourreq[1]=1; ourreq[8]=0;
  ourcon=tcpopen2(adr,1080);
  if (ourcon<=0) { return -1; }
  alarm(TRANSTIMEOUT);
  res=write(ourcon,ourreq,9);
  alarm(0);
  if (res<9) { close(ourcon); return 0; }
  ourreq[0]=0; ourreq[1]=0;
  alarm(TRANSTIMEOUT);
  res=read(ourcon,ourreq,2);
  alarm(0);
  close(ourcon);
  if (res<2) { return 0; }
/*  if (ourreq[0]!=4) { return 0; } */ /* This is now commented out because many
                                          socks implementations set that wrong */
  if (ourreq[1]!=90) { return 0; }
  return 1;
}

int socks5scan(int *IP) {
  char adr[4]; int ourcon; int res; unsigned short int poo;
  char req1[3]; char reply[5]; char req2[10];
  adr[0]=IP[0];
  adr[1]=IP[1];
  adr[2]=IP[2];
  adr[3]=IP[3];
  ourcon=tcpopen2(adr,1080);
  if (ourcon<=0) { return 0; }
  req1[0]=5; req1[1]=1; req1[2]=0;
  alarm(TRANSTIMEOUT);
  res=write(ourcon,req1,3);
  alarm(0);
  if (res<3) { close(ourcon); return 0; }
  alarm(TRANSTIMEOUT);
  res=read(ourcon,reply,2);
  alarm(0);
  if (res<2) { close(ourcon); return 0; }
  if (reply[0]!=5) { close(ourcon); return 0; }
  if (reply[1]!=0) { close(ourcon); return 0; }
  req2[0]=5; req2[1]=1; req2[2]=0; req2[3]=1;
  memcpy(&req2[4],myip,4);
  poo=htons(MYPORT);
  memcpy(&req2[8],&poo,2);
  alarm(TRANSTIMEOUT);
  res=write(ourcon,req2,10);
  alarm(0);
  if (res<10) { close(ourcon); return 0; }
  alarm(TRANSTIMEOUT);
  res=read(ourcon,reply,2);
  alarm(0);
  close(ourcon);
  if (res<2) { return 0; }
  if (reply[0]!=5) { return 0; }
  if (reply[1]!=0) { return 0; }
  return 1;
}

int wgatescan(int *IP) {
  char adr[4]; int ourcon; int res; char reply[15];
  adr[0]=IP[0];
  adr[1]=IP[1];
  adr[2]=IP[2];
  adr[3]=IP[3];
  ourcon=tcpopen2(adr,23);
  if (ourcon<=0) { return 0; }
  alarm(TRANSTIMEOUT);
  res=read(ourcon,reply,8);
  close(ourcon);
  alarm(0);
  if (res<8) { return 0; }
  reply[8]='\0';
  if (strcmp(&reply[1],"ingate>")==0) { return 1; }
  return 0;
}

void doscans(int fdrea, int fdwri) {
  /* Does the socksscanning */
  int laststat=0; int nextip[4]; struct sigaction sa; int bla;
  sa.sa_handler=sigalhandler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags=0;
  sigaction(SIGALRM,&sa,NULL);
  sa.sa_handler=sigalhandler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags=0;
  sigaction(SIGPIPE,&sa,NULL);
  do {
    write(fdwri,&laststat,sizeof(int));
    read(fdrea,nextip,sizeof(int)*4);
    if (nextip[0]==0) {
      sleep(CHILDSLPTIME);
      laststat=0;
    } else {
      laststat=1;
      bla=socks4scan(nextip);
      if (bla==1) { laststat=laststat | 2; }
      if (bla!=-1) {
        if (socks5scan(nextip)) { laststat=laststat | 4; }
      }
/*      if (wgatescan(nextip)) { laststat=laststat | 8; } */ /* Needs further testing */
    }
  } while (1);
}

long getlastscan(int a, int b, int c, int d) {
  int i;
  for (i=0;i<schocu[a];i++) {
    if ((a==scho[a][i].a) && (b==scho[a][i].b) && (c==scho[a][i].c) && (d==scho[a][i].d)) {
      return scho[a][i].lastscan;
    }
  }
  return 0;
}

int otherchildscanning(int a, int b, int c, int d) {
  int i;
  for (i=0;i<SCANTHREADS;i++) {
    if ((scanchilds[i].a==a) && (scanchilds[i].b==b) && (scanchilds[i].c==c) && (scanchilds[i].d==d)) {
      return 1;
    }
  }
/* Comment this out after debugging */
  for (i=0;i<opsocu;i++) {
    if ((opso[i].a==a) && (opso[i].b==b) && (opso[i].c==c) && (opso[i].d==d)) {
      if ((opso[i].lastscan+RESCANOPEN)>getnettime()) {
        return 1;
      } else {
        opsopurge(getnettime()-RESCANTIME);
        return 0;
      }
    }
  }
/* -------------------------------- */
  return 0;
}

void resolvehost(int a, int b, int c, int d, char *res) {
  int i; userdata *x; long IP;
  strcpy(res,"unresolved");
  IP=((a*256+b)*256+c)*256+d;
  for (i=0;i<SIZEOFUL;i++) {
    x=uls[i];
    while (x!=NULL) {
      if (x->realip==IP) {
        strcpy(res,x->host);
        i=SIZEOFUL;
      }
      x=(void *)x->next;
    }
  }
  for (i=0;i<SIZEOFUL;i++) {
    x=uls[i];
    while (x!=NULL) {
      if (x->realip==IP) {
        if (strlen(res)<200) { 
          strcat(res," ");
          strcat(res,x->nick);
          strcat(res,"!");
          strcat(res,x->ident);
          strcat(res,"@...");
        }
      }
      x=(void *)x->next;
    }
  }
}

void getresultandsendnewhost(int n) {
  int IP[4]; int res, r2; long a, cuneti; char tmps2[2000], tmps3[2000], tmps4[2000];
  res=read(scanchilds[n].fdrea,&r2,sizeof(int));
  if (res<=0) {
    sprintf(logline,"!!! READING FROM PIPE FAILED!!!!"); putlog(); exit(1);
  }
  sprintf(logline,"Thread %d is done with scanning of %d.%d.%d.%d - Result: %d",
    n,scanchilds[n].a,scanchilds[n].b,scanchilds[n].c,scanchilds[n].d,r2);
  if (scanchilds[n].a!=0) { putlog(); }
  if (scanchilds[n].a!=0) {
    if (r2==1) {
      schoadd(((scanchilds[n].a*256+scanchilds[n].b)*256+scanchilds[n].c)*256+scanchilds[n].d,getnettime());
/*      for (a=0;a<honcu;a++) {
        if ((hon[a].a==scanchilds[n].a) && (hon[a].b==scanchilds[n].b) && (hon[a].c==scanchilds[n].c) && (hon[a].d==scanchilds[n].d)) {
          hon[a].lastscan=getnettime();
          a=honcu;
        }
      } */
      hondel2(((scanchilds[n].a*256+scanchilds[n].b)*256+scanchilds[n].c)*256+scanchilds[n].d);
    }
    if (r2>1) {
      resolvehost(scanchilds[n].a,scanchilds[n].b,scanchilds[n].c,scanchilds[n].d,tmps4);
      strcpy(tmps2,"");
      if ((r2 & 2)==2) { strcat(tmps2,"SOCKS4 "); }
      if ((r2 & 4)==4) { strcat(tmps2,"SOCKS5 "); }
      if ((r2 & 8)==8) { strcat(tmps2,"Wingate "); }
      sprintf(tmps3,"Open %son %d.%d.%d.%d [%s]",tmps2,scanchilds[n].a,scanchilds[n].b,scanchilds[n].c,scanchilds[n].d,tmps4);
      strcpy(logline,tmps3); putlog();
      noticeallircops(tmps3);
      opsoadd(((scanchilds[n].a*256+scanchilds[n].b)*256+scanchilds[n].c)*256+scanchilds[n].d,getnettime());
      sprintf(outpb,"%sAAB P %s :gline *@%d.%d.%d.%d %ds Open SOCKs/Wingate, see http://www.quakenet.eu.org/openproxies.html\n",
        servernumeric,osnum,scanchilds[n].a,scanchilds[n].b,scanchilds[n].c,scanchilds[n].d,GLINETIME);
      writeoutp();
      hondel2(((scanchilds[n].a*256+scanchilds[n].b)*256+scanchilds[n].c)*256+scanchilds[n].d);
    }
  }
  res=0;
  IP[0]=0; IP[1]=0; IP[2]=0; IP[3]=0;
  cuneti=getnettime();
  if (burstcomplete) {
    do {
      res++;
      lastscanpos++;
      if (lastscanpos>=honcu) { lastscanpos=0; }
      a=hon[lastscanpos].lastscan;
      if ((a+RESCANTIME<cuneti) && (lastscanpos<honcu)) {
        // This host has not been scanned yet or too long ago
        if (hon[lastscanpos].a!=0) {
          if (!otherchildscanning(hon[lastscanpos].a,hon[lastscanpos].b,hon[lastscanpos].c,hon[lastscanpos].d)) {
            // No other child is scanning this - VOILA we have a winner!
            IP[0]=hon[lastscanpos].a;
            IP[1]=hon[lastscanpos].b;
            IP[2]=hon[lastscanpos].c;
            IP[3]=hon[lastscanpos].d;
            res=MAXSEARCH;
          }
        }
      }
    } while (res<MAXSEARCH);
  }
  scanchilds[n].a=IP[0];
  scanchilds[n].b=IP[1];
  scanchilds[n].c=IP[2];
  scanchilds[n].d=IP[3];
  scanchilds[n].scanstart=getnettime();
//  sprintf(logline,"Sending Thread %d to scan %d.%d.%d.%d",n,IP[0],IP[1],IP[2],IP[3]); putlog();
  write(scanchilds[n].fdwri,IP,sizeof(int)*4);
}

int readtobuffer(int whatever) {
  char tmp[514]; int res;
  if (strlen(inputbuffer)>9000) { return 0; }
  res=read(whatever,tmp,512);
  if (res<=0) { printf("Read failed!\n"); return -1; }
  tmp[res]='\0';
  strcat(inputbuffer,tmp);
//  printf("[] %s []\n",inputbuffer);
  return 0;
}

int lineinbuffer() {
  int i;
  if (inputbuffer[0]=='\0') { return 0; }
  for (i=0;i<strlen(inputbuffer);i++) {
    if (inputbuffer[i]=='\n') { return 1; }
  }
  return 0;
}

void myfgets(char *targ) {
  int i;
  for (i=0;i<strlen(inputbuffer);i++) {
    if (inputbuffer[i]=='\n') { break; }
  }
  if (i>512) { i=512; }
  memcpy(targ,inputbuffer,i+1);
  targ[i+1]='\0';
  memcpy(inputbuffer,&inputbuffer[i+1],strlen(inputbuffer)-i+2);
}

void writeoutp() {
  write(whatever,outpb,strlen(outpb));
}

void siginthandler(int bla) {
  terminateme=1;
}

void installsiginthandler() {
  struct sigaction sa;
  sa.sa_handler=siginthandler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags=0;
  sigaction(SIGINT,&sa,NULL);
}

int main(int argc, char **argv) {
  char prevline[2000]; int iii; struct hostent *he; fd_set myfds;
  struct timeval tv;
  burstcomplete=0; sls=NULL; sle=NULL;
  for (iii=0;iii<SIZEOFUL;iii++) {
    uls[iii]=NULL;
  }
  inputbuffer[0]='\0';
  longtotoken(62,servernumeric,2);
  srand(time(NULL)%24167);
  if (argc>1) {
    sprintf(logline,"Trying to load config: %s...",argv[1]); putlog();
    if (loadconfig(argv[1])<0) {
      sprintf(logline,"FAILED"); putlog();
    } else {
      sprintf(logline,"done."); putlog();
    }
  } else {
    sprintf(logline,"Trying to load default config proxyscan.conf..."); putlog();
    if (loadconfig("proxyscan.conf")<0) {
      sprintf(logline,"FAILED"); putlog();
    } else {
      sprintf(logline,"done."); putlog();
    }
  }
  sprintf(logline,"Trying to resolve my IP..."); putlog();
 	he=gethostbyname(MYHOST);
	if (he==NULL) {
		printf("Could not resolve the IP to bind to... Terminating.\n");
		exit(1);
	}
	if (he->h_length!=4) {
		printf("Ouups? Addresslength is %d, not 4! This is not an IPv4-Address! I cannot bind to this!\n",he->h_length);
		exit(1);
	}
	memcpy(myip,he->h_addr,he->h_length);
  sprintf(logline,"Forking connectionhandler..."); putlog();
  iii=fork();
  if (iii<0) {
    perror("fork() failed."); exit(1);
  }
  if (iii==0) {
    openlisteningport();
    exit(0);
  }
  serverspid=iii;
  sprintf(logline,"Forking %d Scan-Children...",SCANTHREADS); putlog();
  for (iii=0;iii<SCANTHREADS;iii++) {
    pid_t res; int p1[2]; int p2[2];
    if (pipe(p1)!=0) {
      sprintf(logline,"Creation of pipe 1 failed... Terminating..."); putlog(); exit(1);
    }
    if (pipe(p2)!=0) {
      sprintf(logline,"Creation of pipe 2 failed... Terminating..."); putlog(); exit(1);
    }
    res=fork();
    if (res<0) {
      perror("Fork failed."); exit(1);
    }
    if (res==0) {
      doscans(p1[0],p2[1]);
      exit(0);
    }
    scanchilds[iii].pid=res;
    scanchilds[iii].fdwri=p1[1];
    scanchilds[iii].fdrea=p2[0];
    scanchilds[iii].a=0;
    scanchilds[iii].b=0;
    scanchilds[iii].c=0;
    scanchilds[iii].d=0;
    scanchilds[iii].scanstart=getnettime();
    fcntl(p2[0],F_SETFD,fcntl(p2[0],F_GETFD,0) | O_NONBLOCK);
    printf("%d... ",iii); fflush(stdout);
  }
  sprintf(logline,"Done."); putlog();
  honcu=0; honma=100;
  hon=(ahost *)malloc(sizeof(ahost)*honma);
  if (hon==NULL) {
    sprintf(logline,"Failed to get memory for Hostlist."); putlog();
    exit(1);
  }
  for (iii=0;iii<256;iii++) {
    schocu[iii]=0; schoma[iii]=100;
    scho[iii]=(scannedhost *)malloc(sizeof(scannedhost)*schoma[iii]);
    if (scho[iii]==NULL) {
      sprintf(logline,"Failed to get memory for Scannedhostlist."); putlog();
      exit(1);
    }
  }
  opsocu=0; opsoma=100;
  opso=(opensocks *)malloc(sizeof(opensocks)*opsoma);
  if (opso==NULL) {
    sprintf(logline,"Failed to get memory for open host list."); putlog();
    exit(1);
  }
  sprintf(logline,"Loading list of already scanned hosts..."); putlog();
  schoload(scandatfile);
  sprintf(logline,"Connecting to %s Port %s",conto,conport); putlog();
  whatever=tcpopen(conto,conport);
  if (whatever<0) {
    sprintf(logline,"Connection failed."); putlog();
    killallchilds();
    return -1;
  }
  sprintf(logline,"Connection accepted."); putlog();
  installsiginthandler();
  sprintf(outpb,"PASS %s\n",conpass); writeoutp();
  starttime=time(NULL);
  lasthourly=starttime;
  longtotoken(iptolong(127,0,0,1),tmps1,6);
  sprintf(outpb,"SERVER %s 1 %d %d J10 %sA]] 0 :This is Proxyscan %s\n",myname,starttime,starttime,servernumeric,proxyscanversion); writeoutp();
  /* adding some stuff like network auth. // commx */
  sprintf(outpb,"%s N %s 1 %d proxyscan %s +owskr %s %s %sAAB :This is Proxyscan %s\n",servernumeric,mynick,starttime,myname,mynick,tmps1,servernumeric,proxyscanversion); writeoutp();
  sprintf(outpb,"%s EB\n",servernumeric); writeoutp();
  sprintf(outpb,"%sAAB J %s\n",servernumeric,chantojoin); writeoutp();
  /* proxyscan should op self. // commx */
  sprintf(outpb,"%s M %s +o %sAAB\n",servernumeric,chantojoin,servernumeric); writeoutp();
  sprintf(outpb,"%sAAB P %s :\002Proxyscan 1.00b.fixed\002 ready. Started \002(\002 25 \002)\002 scan-threads.\n",servernumeric,chantojoin); writeoutp();
  sprintf(outpb,"%sAAB %s\n",servernumeric,authline); writeoutp();
  iii=fcntl(whatever,F_GETFD,0);
  fcntl(whatever,F_SETFD,iii | O_NONBLOCK);
  do {
    strcpy(prevline,lastline);
    lastline[0]='\0'; part1[0]='\0'; command[0]='\0';
    do {
      if (lineinbuffer()) { break; }
      errno=0;
      FD_ZERO(&myfds);
      FD_SET(whatever,&myfds);
      for (iii=0;iii<SCANTHREADS;iii++) {
        FD_SET(scanchilds[iii].fdrea,&myfds);
      }
      tv.tv_sec=0; tv.tv_usec=300;
      select(FD_SETSIZE,&myfds,NULL,NULL,&tv);
      if (errno==0) {
        if (FD_ISSET(whatever,&myfds)) {
          if (readtobuffer(whatever)==-1) {
            sprintf(logline,"Read from socket returned error - exiting"); putlog();
            errno=0; terminateme=1; strcat(inputbuffer,"--- END ---\r\n");
          }
        }
        for (iii=0;iii<SCANTHREADS;iii++) {
          if (FD_ISSET(scanchilds[iii].fdrea,&myfds)) {
            // This child is done with scanning one host. Get result and send
            // it a new host to scan.
            getresultandsendnewhost(iii);
          }
        }
      }
    } while (((errno==EINTR) || (lineinbuffer()==0)) && (!terminateme));
    if (!terminateme) { myfgets(lastline); } else { lastline[0]='\0'; }
    if (lastline[0]=='\0') { terminateme=1; }
    /* Remove the trailing linefeed */
    if (strlen(lastline)>0) {
      delchar(lastline,'\r');
      delchar(lastline,'\n');
    }
    if (lastline[0]!='\0') {
      if ((lasthourly+3600)<getnettime()) {
        sprintf(logline,"Starting hourly duties..."); putlog();
        schopurge(getnettime()-RESCANTIME);
        saveall();
        lasthourly=getnettime();
        sprintf(logline,"Hourly duties done."); putlog();
      }
      sscanf(lastline,"%s %s",part1,command);
//      sprintf(logline,"[%d] %s",strlen(lastline),lastline); putlog();
      if (strcmp(part1,"SERVER")==0) {
        handleservermsg(1);
        sprintf(logline,"I am connected to numeric: %s",uplnum); putlog();
      } else {
        if (strcmp(part1,"PASS")==0) {
          sprintf(logline,"Got password (and dont give a fuck about it)"); putlog();
        } else {
          if ((strcmp(command,"END_OF_BURST")==0) || (strcmp(command,"EB")==0)) {
            normnum(part1);
            if (strcmp(part1,uplnum)==0) {
              sprintf(logline,"Sending EOB_ACK"); putlog();
              sprintf(outpb,"%s EA\n",servernumeric); writeoutp();
              burstcomplete=1;
              sprintf(logline,"Merge took %d seconds.",(time(NULL)-starttime)); putlog();
            }
          }
          if ((strcmp(command,"NICK")==0) || (strcmp(command,"N")==0)) { handlenickmsg(); }
          if ((strcmp(command,"QUIT")==0) || (strcmp(command,"Q")==0)) { handlequitmsg(); }
          if ((strcmp(command,"KILL")==0) || (strcmp(command,"D")==0)) { handlekillmsg(); }
          if ((strcmp(command,"SERVER")==0) || (strcmp(command,"S")==0)) { handleservermsg(0); }
          if ((strcmp(command,"PING")==0) || (strcmp(command,"G")==0)) { handleping(); }
          if ((strcmp(command,"SQUIT")==0) || (strcmp(command,"SQ")==0)) { handlesquit(); }
          if ((strcmp(command,"MODE")==0) || (strcmp(command,"M")==0)) { handlemodemsg(); }
          if ((strcmp(command,"PRIVMSG")==0) || (strcmp(command,"P")==0)) { handleprivmsg(); }
        }
      }
    }
  } while (terminateme!=1);
  close(whatever);
  sprintf(logline,"Last line received was: %s",prevline); putlog();
  sprintf(logline,"Saving status info..."); putlog();
  schosave(scandatfile);
  sprintf(logline,"Killing all my children..."); putlog();
  killallchilds();
  sprintf(logline,"Done."); putlog();
  return 0;
}
