   /*
    * This code was written by:
    * Joshua James Drake (jdrake@pulsar.net)
    *
    * Published 6/9/98 @ 12:02 AM
    *
    * The following lines of code are, in a nutshell, written to pry
    * some information from a nameserver.  The information it gives
    * you may or may not be useful.  That is for you to decide.
    *
    * However, it will tell you if the server supports IQUERY and, if
    * possible, what version of bind (by Paul Vixie) it is running.
    *
    */
   /* built into synscan by psychoid */
   /* local type includes */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <sys/errno.h>
   /* network type includes */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netdb.h>
   /* bulky shit for printing dnspkts need to link dnspkt.o from dnspkt.c
      too */
#ifdef DEBUG
#include "dnspkt.h"
#endif
   /* prototypes */
int lookup_host (struct sockaddr_in *ra, char *hn, unsigned short rp);
int probe_bind (struct sockaddr_in ra);
int talk (int sd, char *pkt, int pktl, char opc);
int make_keypkt (char *pktbuf, char opc);
int print_ver (char *host, int vul, char *buf);
void handle_alarm (int signum);
extern char bndver[200];
   /*
    * here we simply check arguments, resolve the hostname given and
    * if all is well, initialize the radom seed and probe away.
    */
int
pbind (char *host)
{
  struct sockaddr_in ra;
  memset (bndver, 0x0, 199);
  if (!lookup_host (&ra, host, NAMESERVER_PORT))
    return 0x0;
  return probe_bind (ra);
}

   /*
    * resolve a hostname to a sockaddr_in struct.
    * we first try treating it like an ip address in a.b.c.d notation
    * then, if that fails, we try to resolve using DNS ways
    * if all fails, we return 0. (failed)
    * if we get the sockaddr_in struct all filled out, we return 1.
    */
int
lookup_host (ra, hn, rp)
     struct sockaddr_in *ra;
     char *hn;
     unsigned short rp;
{
  struct hostent *he;
  ra->sin_family = AF_INET;
  ra->sin_port = htons (rp);
  if ((ra->sin_addr.s_addr = inet_addr (hn)) != -1)
    return 1;
  if ((he = gethostbyname (hn)) != (struct hostent *) NULL)
    {
      memcpy (&ra->sin_addr.s_addr, he->h_addr, 4);
      return 1;
    }
  return 0;
}

   /*
    * here we allocate some space for our packets and make sure it's
    * "fullanull".  then we attempt to allocate and setup our socket.
    * if failure occurs, we shall report error and return.
    * the we attempt to reverse our address in the sockaddr_in structure
    * passed as the only argument into a dns name, if that fails, we go
    * with the ascii ip address representation.  then we attempt to
    * communicate the remote server, if failure, we return.  after we
    * have successfully got our packets back, we close the socket and
    * print out a summary of what we got.
    */
int
probe_bind (ra)
     struct sockaddr_in ra;
{
  int sd;
  char iquery[512], vquery[512], rname[256];
  struct hostent *he;
  HEADER *dh = (HEADER *) iquery;
  memset (vquery, 0, sizeof (vquery));
  memset (iquery, 0, sizeof (iquery));
  if (((sd = socket (AF_INET, SOCK_DGRAM, 0)) == -1) ||
      (connect (sd, (struct sockaddr *) &ra, sizeof (ra)) == -1))
    {
      if (sd != -1)
	close (sd);
      return 0x0;
    }
  if ((he = gethostbyaddr ((char *) &ra.sin_addr, sizeof (ra.sin_addr),
			   AF_INET)) == (struct hostent *) NULL)
    sprintf (rname, "%s", inet_ntoa (ra.sin_addr));
  else
    strncpy (rname, he->h_name, sizeof (rname));
  if (!talk (sd, iquery, sizeof (iquery), IQUERY))
    return 0x0;
  if (!talk (sd, vquery, sizeof (vquery), QUERY))
    return 0x0;
  close (sd);
  /* if dh->rcode == 0, then our iquery request was answered and the
     remote server
     supports iquery */
  return print_ver (rname, dh->rcode == 0, vquery);
}

   /*
    * write our packet from pkt, wait for a response and put it in pkt.
    * if the alarm goes off or the read fails, we print error
    * and return 0.  otherwise, our response packet is in pkt and we
    return 1.
    */
int
talk (sd, pkt, pktl, opc)
     int sd, pktl;
     char *pkt, opc;
{
  int pktlen;
  pktlen = make_keypkt (pkt, opc);
  if (!write (sd, pkt, pktlen))
    {
      close (sd);
      return 0;
    }
#ifdef DEBUG
  printf ("write() success\n");
#endif
  siginterrupt (SIGALRM, 1);
  signal (SIGALRM, handle_alarm);
  alarm (3);
  pktlen = read (sd, pkt, pktl);
  if (pktlen <= 0)
    {
      close (sd);
      return 0;
    }
#ifdef DEBUG
  printf ("read success\n");
#endif
  alarm (0);
  return 1;
}

   /*
    * this forms a valid dns packet based on the op code given by opc.
    * only two opcodes are supported because that's all we need to
    support.
    * the packet ends up in pktbuf and the length of the packet is
    returned.
    */
int
make_keypkt (pktbuf, opc)
     char *pktbuf;
     char opc;
{
  HEADER *dnsh;
  char *ptr = pktbuf;
  int pktlen = 0;
  dnsh = (HEADER *) ptr;
  /* fill out the parts of the DNS header that aren't 0 */
  dnsh->id = htons (rand () % 65535);
  dnsh->opcode = opc;
  dnsh->rd = 1;
  dnsh->ra = 1;
  /* one answer for IQUERY, one question for QUERY */
  if (opc == IQUERY)
    dnsh->ancount = htons (1);
  else if (opc == QUERY)
    dnsh->qdcount = htons (1);
  pktlen += sizeof (HEADER);
  ptr += sizeof (HEADER);
  /* we have to make a QUERY, fill out the question section */
  if (opc == QUERY)
    {
      /* version.bind. == elite */
      char qstr[] = "\007version\004bind\000";
      int qlen = strlen (qstr) + 1;
      memcpy (ptr, qstr, qlen);
      ptr += qlen;
      pktlen += qlen;
      PUTSHORT (T_TXT, ptr);
      PUTSHORT (C_CHAOS, ptr);
      pktlen += sizeof (short) * 2;
    }
  /* add a resource record for the inverse query */
  else if (opc == IQUERY)
    {
      unsigned long addr = inet_addr ("1.2.3.4");
      unsigned long ttl = 31337;
      unsigned short addrlen = 4;
      *(ptr++) = '\0';
      pktlen++;
      PUTSHORT (T_A, ptr);
      PUTSHORT (C_IN, ptr);
      PUTLONG (ttl, ptr);
      PUTSHORT (addrlen, ptr);
      PUTLONG (addr, ptr);
      pktlen += (sizeof (short) * 3) + (sizeof (long) * 2);
    }
  /* if we're debugging, show what we just made */
#ifdef DEBUG
  print_dnspkt (pktbuf, pktbuf + pktlen);
#endif
  return pktlen;
}

   /*
    * This function takes a DNS packet in buf, and whether or not it
    reponds to IQUERY in vul.
    * We cast the packet and extract the response as long as there is
    one.
    * If there isn't one, then we assume that the remote server is an old
    version of bind.
    * this is the end of the line.
    */
int
print_ver (host, vul, buf)
     char *host, *buf;
     int vul;
{
  HEADER *dnsh = (HEADER *) buf;
  char *ptr, *verstr;
  int len;
  if (dnsh->rcode != 0)
    {
      printf
	("%s's named that %s iquery does not respond to   version.bind.\n",
	 host, vul ? "supports" : "errors on");
      return 0x0;
    }
  /* So we actually have a response.  Lets skip the crap, starting
     with the header */
  ptr = (buf + sizeof (HEADER));
  /* then the question section domain name. */
  while (*ptr != '\0')
    ptr++;
  /* then the trailing null and the type/class of the question */
  ptr += 1 + (sizeof (short) * 2);
  /* now we skip the answer section domain name. (should be the same
     as the question) */
  while (*ptr != '\0')
    ptr++;
  /* don't forget the trailing null, type, class, and time to live.
   */
  ptr += 1 + (sizeof (long) + (sizeof (short) * 2));
  /* Here we are at the resource record data length, extract it */
  GETSHORT (len, ptr);
  /* avoid the need to decompress the string (treat it as one) */
  ptr++;
  /* allocate space for and copy the version response txt */
  verstr = (char *) malloc (len);
  memset (verstr, 0, len);
  memcpy (verstr, ptr, len - 1);
  /* run through the vesion string and replace non-printable and
     non-whitespace characters
     with a '.' */
  for (ptr = verstr; ptr - verstr != len - 1; ptr++)
    if (!isprint (*ptr) && !isspace (*ptr))
      *ptr = '.';
  /* print the version and iquery support status, woo hoo */
  snprintf (bndver, sizeof (bndver),
	    "%s's named that %s iquery is version: %s\n", host,
	    vul ? "supports" : "errors on", verstr);
  printf ("%s", bndver);
  return 0x1;
}

   /*
    * handle the alarm signal by resetting the alarm timer and
    * the signal handler for SIGALRM.  This stuff probably isn't needed,
    * but I did it anyway.  It's good for debugging, ran into some
    problems with
    * alarm() not doing its job.
    */
void
handle_alarm (signum)
     int signum;
{
  alarm (0);
  signal (SIGALRM, SIG_DFL);
#ifdef DEBUG
  printf ("recieved alarm\n");
#endif
}
