/*****************************************************************
 * PTlink IRCd is (C) CopyRight PTlink IRC Software 1999-2000     *
 *                http://software.pt-link.net                     *
 * This program is distributed under GNU Public License          *
 * Please read the file COPYING for copyright information.       *
 *****************************************************************
 *   $Id: spoof.c,v 1.3 2005/08/27 16:23:50 jpinto Exp $ 
 
  File: spoof.c
  Desc: host spoofing routines
  Author: Lamego@PTlink.net
*/
#include <string.h>
#include <stdio.h>

#include "m_commands.h"
#include "client.h"
#include "ircd.h"
#include "numeric.h"
#include "s_log.h"
#include "s_serv.h"
#include "send.h"
#include "whowas.h"
#include "irc_string.h"
#include "spoof.h"
#include "s_user.h"
#include "crypt.h"
#include "hash.h"
#include "m_svsinfo.h"
#include "s_conf.h"
#include "dconf_vars.h"

int tagcount = 0;
#define MAXVIRTSIZE     (3 + 5 + 1)
#define HASHVAL_TOTAL   30011
#define HASHVAL_PARTIAL 211


void truncstring(char *stringvar, int firstlast, int amount);
int Maskchecksum (char *data, int len);
unsigned z(char *s);
void remove_dots_and_slashes(char* msg);
int str2array(char **pparv, char *string, char *delim);

/*
 * spoofed
 *
 * inputs       - client to be spoofed
 * output       - spoofed hostname
 * side effects - 
 */

char *spoofed(struct Client *acptr)
{
  switch(SpoofMethod)
   {
	  case 0:
	   return maskme(acptr->realhost);
    
	  case 1:
#ifdef IPV6
	   return maskme_myCrypt(acptr->realhost,(unsigned long)acptr->ip.s6_addr);
#else	   
	   return maskme_myCrypt(acptr->realhost, acptr->ip.s_addr);
#endif	   
   }
   return maskme(acptr->realhost);
}

#define B_BASE                  0x1000
void truncstring(char *stringvar, int firstlast, int amount){
   if (firstlast)
   {
    stringvar+=amount;
    *stringvar=0;
    stringvar-=amount;
   }
    else
   {
    stringvar+=strlen(stringvar);
    stringvar-=amount;
   }
}

int Maskchecksum (char *data, int len)
{
        int                     i;
        int                     j;

        j=0;
        for (i=0 ; i<len ; i++)
        {
          j += *data++ * (i < 16 ? (i+1)*(i+1) : i*(i-15));

        }

        return (j+1000)%0xffff;
}

unsigned z(char *s)
{
    unsigned int i;
        i = Maskchecksum (s, strlen(s));
    return i;
}

int str2array(char **pparv, char *string, char *delim)
{
    char *tok;
    int pparc = 0;

    tok = strtok(string, delim);
    while (tok != NULL) {
	pparv[pparc++] = tok;
	tok = strtok(NULL, delim);
    }

    return pparc;
}


char *maskme(char *curr)
{
    char crcstring[HOSTLEN+34];
    static char mask[HOSTLEN + 1];
    unsigned hash_total, hash_partial;
    char     destroy[HOSTLEN+1], *parv[HOSTLEN+1], *ptr, charmask[2];
    
    int      parc=0, len=0, overflow=0;

	if(!HostPrefix)
	  {
		strncpy(mask, curr, HOSTLEN);
		return mask;
	  }
    if(CryptKey)
    {
	ircsprintf(crcstring, "%s%s", CryptKey, curr);
    }
    else
    {
	if(curr[0] % 1)
	    ircsprintf(crcstring, "%s%s", my_svsinfo_ts_S, curr);
	else
	    ircsprintf(crcstring, "%s%s", curr, my_svsinfo_ts_S);
    }
      
    hash_total =  z(crcstring) % HASHVAL_TOTAL;
    strncpy(destroy, curr, HOSTLEN);

    len = strlen(destroy);

    if ((len + MAXVIRTSIZE) > HOSTLEN)
    {
        overflow = (len + MAXVIRTSIZE) - HOSTLEN;

        ptr = &destroy[overflow];
    }
    else
        ptr = &destroy[0];
    if(strchr(curr,'.'))
	charmask[0]='.';
    else if(strchr(curr,':'))
	charmask[0]=':';
    else {
    	 strncpy(mask, curr, HOSTLEN);
    	 return mask;
    }
    charmask[1]='\0';
    parc = str2array(parv, ptr, charmask);
    if (parc == 4)
    {
        len = strlen(parv[3]);

        if (IsDigit(parv[3][len-1]))
        {
            hash_partial = (z(parv[0])+z(parv[1])+z(parv[2])) % HASHVAL_PARTIAL;
            ircsprintf(mask, "%s%c%s%c%u%c%s-%u",
                parv[0], charmask[0], parv[1], charmask[0], hash_partial,
		charmask[0], HostPrefix, hash_total);
            return mask;
        }
    }
    if (parc == 2)
    {
        ircsprintf(mask, "%s-%u%c%s%c%s",
            HostPrefix, hash_total, charmask[0], parv[parc-2], charmask[0], parv[parc-1]);
        return mask;
    }
    len = strlen(parv[parc-1]);
    if (len == 2)
    {
        ircsprintf(mask, "%s-%u%c%s%c%s", /* Fixes spoofing - Lamego 1999*/
            HostPrefix, hash_total, charmask[0], parv[parc-2], charmask[0], parv[parc-1]);	
        return mask;
    }
    ircsprintf(mask, "%s-%u%c%s%c%s", HostPrefix, hash_total, charmask[0], 
    			parv[parc-2], charmask[0], parv[parc-1]);
    return mask;    
}

/* BCRYPT */
void remove_dots_and_slashes(char* msg) {
  char temp[HOSTLEN+1];
  int i,j;
  strcpy(temp, msg);
  for(i = j = 0; i < strlen(temp); i++,j++) {
    if(temp[i] == '.') {
      msg[j++] = 'p'; msg[j] = 'p';
   } else if (temp[i] == '/') {
      msg[j++] = 'b'; msg[j] = 'b';
    } else
      msg[j] = temp[i];
  }
  msg[j] = 0;
}   

/*
 * Adapted from Brasnet IRCd - Lamego
 */
char* maskme_myCrypt(char* host, long IP) 
{
#ifndef OWN_CRYPT
  extern  char *crypt();
#endif
  static char hexa[HOSTLEN+1];
  static char ret[HOSTLEN+1];
  static char salt[2] = "br";
  static char pass[8] = "87654312";
  register int i, res, hostlen;

  ret[0] = 0;

  if(HostPrefix) {
        salt[0] = HostPrefix[0];
        salt[1] = HostPrefix[1];
        salt[2] = 0;
        strncpy(pass, HostPrefix+2, 8);
        pass[8] = 0;
  }
  ircsprintf(hexa, "%X", ntohl(IP));
  for(i = 0; i < 8 && pass[i]; i++)
    hexa[i] = (pass[i] == hexa[i])?hexa[i]:pass[i]^hexa[i];
#ifdef OWN_CRYPT
  strcpy(hexa, (char *)crypt_des(hexa, salt));
#else
  strcpy(hexa, (char *)crypt(hexa, salt));
#endif
  remove_dots_and_slashes(hexa);
  hostlen = strlen(host);
  
  if(!IsDigit(host[hostlen-1]))
	{
  	  for(i=0; host[i] && host[i] != '.'; i++);
  		strcpy(ret, hexa+2);
  		if(host[i]) 
  		  {
                    res=HOSTLEN-( hostlen - i )-strlen(ret);
  		    if(res<0) 
  		    	i-=res;
		  strcat(ret, host+i);
	} 
	} 
  else 
	{
  	  strcpy(ret, hexa+2);
  	  strcat(ret, ".");
  	  strcat(ret, host);
  	  for(i = hostlen+1; i && (host[i] != '.'); i--);
  	  ret[strlen(hexa+2)+i+1] = 0;
  	  strcat(ret, ".O");
	}
	return ret;
}
 

/*
** m_newmask - Ported to PTlink6 
**      parv[0] = sender prefix
**      parv[1] = new mask (if no @ host is assumed)
**      parv[2] = target nick (optional, restricted to services)
*/
int	m_newmask(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  char    *newhost = NULL, *newuser = NULL;	
  struct Client *acptr = sptr;
  char *mename = me.name;
  static char paramhost[HOSTLEN];

  if(acptr->user && acptr->user->vlink)
    mename = acptr->user->vlink->name;
	
  if (MyClient(sptr)) 
    {
      if (!IsAnOper(sptr) && !IsAdmin(sptr) && cptr!=&me)
        {
    	  sendto_one(sptr, form_str(ERR_NOPRIVILEGES), mename, parv[0]);
    	  return -1; 
	}		
      if(!OperCanUseNewMask && cptr!=&me)
	{
	  sendto_one(sptr, form_str(ERR_NOPRIVILEGES), mename, parv[0]);
	  return -1;
	}
    } 
  else
    {
	if(IsService(sptr) && parc>2)
	  {
	    if((acptr = find_client(parv[2],NULL)) == NULL)
	      return -1;
          }
    }
    
  if (parc<2 || EmptyString(parv[1]))
    {
      if(MyClient(sptr))
        sendto_one(sptr, ":%s NOTICE %s :*** Syntax: /NEWMASK <user@host|user@|host>", mename, parv[0]);
      return 0;
    }
      
  newhost = strchr(parv[1],'@');
	  
  if(newhost)
    {
      (*newhost++) = '\0';
      newuser = parv[1];
    } 
  else 
    newhost = parv[1];
  		
  if (!EmptyString(newhost)) 
    {
/*
 parametric host spoof patch by dp:
 if the user supplies a host in the form of
   +staff.network.org
 prepend "nick." to the host.
 works also with the dconfig entries.
 if you have any questions, i can be
 reached at ptlink.10.dpx@spamgourmet.com
*/
		/* prepend "nick."? */
		if( *newhost == '+' )
		{
			if( (strlen(newhost) + strlen(parv[0])) > HOSTLEN )
			{
				sendto_one(sptr, ":%s NOTICE %s :*** Error: Hostname too long", 
					mename, parv[0]);
				return -1;
			}
			else
			{
				snprintf(paramhost, HOSTLEN, "%s.%s", parv[0],
					newhost + 1);
				
				newhost = paramhost;
			}
		}

      if (strlen(newhost) > (HOSTLEN)) 
        {
  	  sendto_one(sptr, ":%s NOTICE %s :*** Error: Hostname too long", 
	    mename, parv[0]);
    	  return -1;
      	}
  
      if (!valid_hostname(newhost)) 
	{
      	  sendto_one(sptr, ":%s NOTICE %s :*** Error: Invalid hostname", 
	    mename, parv[0]);
            return -1;
      	}
		
      strcpy(acptr->host, newhost);
    }

  if (!EmptyString(newuser)) 
    {

      if (strlen(newuser) > (USERLEN)) 
        {
  	  sendto_one(sptr, ":%s NOTICE %s :*** Error: username too long", mename, parv[0]);
    	  return -1;
      	}
      if (!valid_username(newuser)) 
	{
      	  sendto_one(sptr, ":%s NOTICE %s :*** Error: Invalid username",
	    mename, parv[0]);
            return -1;
      	}		  
       strcpy(acptr->username, newuser);
    }
    
  if(IsService(sptr) && parc>2) /* this was a remote newmask */
    sendto_serv_butone(cptr, ":%s NEWMASK %s@%s %s", sptr->name, 
      newuser ? newuser :"", newhost ? newhost :"", acptr->name);
  else
    sendto_serv_butone(cptr, ":%s NEWMASK %s@%s", parv[0], 
       newuser ? newuser :"", newhost ? newhost :"");
	  
  if (MyClient(acptr)) /* We will only send new mask notices if its a local user */
    sendto_one(acptr, ":%s NOTICE %s :*** New mask: \2%s (%s@%s)\2", mename, acptr->name,
      acptr->name, acptr->username, acptr->host);
  return 0;	    
}
  


syntax highlighted by Code2HTML, v. 0.9.1