/*
 * 
 * This plugin is (c) Renaud Deraison and is licensed under the GPL
 * 
 * The biggest part of this plugin has been cut'n'pasted from the hostloop library,
 * which happens to be written by me
 *
 */
 
#include <includes.h>
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_IP_ICMP_H
#include <netinet/ip_icmp.h>
#endif
#include "icmp.h"

#define NAME "icmp netmask request"
#define DESCRIPTION "\
This plugin performs an ICMP_MASKREQ request,\n\
and may obtain the remote host netmask. This will\n\
help an intruder to guess the attacked network\n\
layout. This may also help him to find how the\n\
routing is done in the attacked network.\n\
Risk factor : low"

#define COPYRIGHT "licensed under the GPL"
#define SUMMARY "sends an ICMP_MASKREQ request"

#ifndef ICMP_MASKREQ
#define ICMP_MASKREQ            17
#endif

#ifndef ICMP_MASKREPLY
#define ICMP_MASKREPLY          18
#endif

#ifndef ICMP_MASKLEN
#define ICMP_MASKLEN    12 
#endif

char * icmp_maskreq(struct arglist *  env);

PlugExport int plugin_init(struct arglist *desc);
PlugExport int plugin_init(struct arglist *desc)
{
  plug_set_name(desc, NAME);
  plug_set_description(desc, DESCRIPTION);
  plug_set_summary(desc, SUMMARY);
  plug_set_copyright(desc, COPYRIGHT);
  plug_set_category(desc, ACT_GATHER_INFO);
  plug_set_family(desc, "Misc.");
  return(0);
}


PlugExport int plugin_run(struct arglist * env);
PlugExport int plugin_run(struct arglist * env)
{
 char * netmask;
  if((netmask = icmp_maskreq(env)))
   {
    char * report = emalloc(350);
    
    sprintf(report,"\
The remote hosts answered to an icmp ICMP_MASKREQ request.\n\
This told us that the attacked host netmask was %s.\n\
This may help an attacker to find out how your network is\n\
laid out.\n\
Solution : configure your firewalls/router and deny ICMP_MASKREQ requests",
 netmask);
    proto_post_info(env, 0, "icmp", report);
   }
}

/*
 * Checksum routine for Internet Protocol family headers (C Version)
 * From ping examples in W.Richard Stevens "UNIX NETWORK PROGRAMMING" book.
 */
int in_cksum(p,n)
u_short *p; int n;
{
  register u_short answer;
  register long sum = 0;
  u_short odd_byte = 0;

  while( n > 1 )  { sum += *p++; n -= 2; }

  /* mop up an odd byte, if necessary */
  if( n == 1 ) {
      *(u_char *)(&odd_byte) = *(u_char *)p;
      sum += odd_byte;
  }

  sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
  sum += (sum >> 16);			/* add carry */
  answer = (int)~sum;			/* ones-complement, truncate*/
  return (answer);
}

char * icmp_maskreq(struct arglist *  env)
{
 int soc;
 struct in_addr addr, *p_addr;
 char * buf;
 struct ip *ip;
 struct icmp *icmp;
 struct protoent * ip_proto;
 int len;
 struct timeval tv = {0,0};
 struct sockaddr_in saddr;
 unsigned char * c_addr;
 int count = 1;
 int max_to = 5;
 int ok = 0;
 char * a, * b = NULL;
 bzero(&saddr, sizeof(struct sockaddr_in));
 p_addr = plug_get_host_ip(env);
 addr = *p_addr;
 c_addr = (unsigned char *)&(addr.s_addr);
 c_addr[3]=255;
 ip_proto = getprotobyname("icmp");
 if(!ip_proto)return(NULL);
  
 soc = socket(AF_INET, SOCK_RAW, ip_proto->p_proto);
 if(soc < 0 )return(NULL);
 while(max_to && !ok)
 {
  int i;
  fd_set r,w;
  int n;

  buf = (char*)malloc(ICMP_MASKLEN);
  bzero(buf, ICMP_MASKLEN);
  icmp = (struct icmp *)buf;
  icmp->icmp_type = ICMP_MASKREQ;
  icmp->icmp_code = 0;
  icmp->icmp_cksum = 0;
  icmp->icmp_seq = 1;
  icmp->icmp_id = getpid() & 0xFFFF;
  len = ICMP_MASKLEN;
  saddr.sin_family = AF_INET;
  saddr.sin_addr = addr;
  saddr.sin_port = 0;
  icmp->icmp_cksum = in_cksum((u_short *)icmp,len);
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  free(buf);
  
  FD_ZERO(&r);
  FD_ZERO(&w);
  FD_SET(soc, &r);
  n = select(soc+1, &r, &w, NULL, &tv);
  if(n>0)
  {
   int slen = sizeof(struct sockaddr);
   max_to--;
   buf = malloc(4096);
   bzero(buf, 4096);
   ip  = (struct ip *)buf;
   bzero(&saddr, sizeof(struct sockaddr_in));
   recvfrom(soc, buf, 4096, 0, (struct sockaddr *)&saddr, &slen);
   icmp = (struct icmp *)(buf + (ip->ip_hl << 2));
   if((icmp->icmp_type == ICMP_MASKREPLY)&&
      (icmp->icmp_id == (getpid() & 0xFFFF)))
      {
      struct in_addr in;
      in.s_addr = icmp->icmp_mask,
      ok = 1;
      a = inet_ntoa(in);
      b = estrdup(a);
      }
   free(buf);
   }
   else max_to--;
  }
  shutdown(soc, 2);
  close(soc);
  return(b);
}
  

