#!/usr/bin/python

# dnsbf.py
# script written by t0ka7a
# http://infond.blogspot.com
# licence new BSD Licence
# 02/14/2010 v0.3

# log modifications
# 02/12/2010 v0.1 start
# 02/13/2010 v0.2 threading
# 02/14/2010 v0.3 semaphore to deal with nb max of threads


from socket import gethostbyaddr
import threading
import sys
import time

NB_OF_THREADS_MAX = 200

################################################################
# print error message
def use (msg):
  sys.stderr.write ("%s\n\n" % msg)
  sys.stderr.write("exemple: %s 192.168.1.0/24\n\n" % sys.argv[0] )
  sys.exit (1)

################################################################
# print intro message
def intro ():
  sys.stdout.write ("\n")
  sys.stdout.write ("*****************************************\n")
  sys.stdout.write ("* program created by t0ka7a             *\n")
  sys.stdout.write ("* http://infond.blogspot.com            *\n")
  sys.stdout.write ("* under GNU 3.0 licence                 *\n")
  sys.stdout.write ("* v0.2 02/13/2010                       *\n")
  sys.stdout.write ("* using dns, find hostnames in a subnet *\n")
  sys.stdout.write ("*****************************************\n\n")
  sys.stdout.write ("begin search...\n\n")


#################################################################
# list_ip
# network = string ex: '192.168.0.1/31'
# returns list of IP in network ex: ['192.168.0.1','192.168.0.2',192.168.0.3']
def list_ip(network):

  output = []
  # ip_list = 4-uplet of the ip. ex: ['192','168','0','1']
  ip_list=[]
  netmask = 0
  # ip in hexa. ex 192.168.0.1 = C0A80001 = 3232235521
  ip = 0

  # test format of network
  if len( network.split('/') ) != 2:
    use("syntax error (/)")
  netmask = network.split('/')[1]
  try:
    netmask = int(netmask)
  except:
    use("syntax error (netmask not digit)")
  if netmask > 32:
    use("syntax error (netmask > 32)")
  ip_list = network.split('/')[0].split('.')
  if len(ip_list) != 4:
    use("syntax error (ip not x.x.x.x)")
  for i in range(4):
    try:
      if int(ip_list[i]) > 0xFF:
        use ("syntax error (numbers upper than 255 in IP)")
    except:
      use ("syntax error (not digit characters in IP)")
  ip = (int(ip_list[0]) << 24) + (int(ip_list[1]) << 16) + (int(ip_list[2]) << 8) + int(ip_list[3])

  # calculate the host mask. Ex: 24 -> 32 - 24 = 8 -> 8 bits 1, the others 0 -> 000000FF
  mask_ip = 0
  for i in range (32-netmask+1):
    mask_ip |= 1 << i

  # creates list of IP
  for i in range(ip, min (ip + mask_ip, 0xFFFFFFFF)):
    output.append( "%d.%d.%d.%d" % ( (i >> 24)&0xFF,(i >> 16)&0xFF,(i >> 8)&0xFF,i&0xFF ) )

  return output   

################################################################
# print name associated with an IP
# ip = IP which name we want. Ex: '192.168.0.1'
def show_name(ip):
  global nb_of_names_found
  global lock
  try:
    sys.stdout.write( "%s %s\n" % (ip,gethostbyaddr(ip)[0]) )
    lock.acquire()
    nb_of_names_found += 1
    lock.release()
  except:
    ""
  # release semaphore. Main() can then create a new thread
  semaphore.release()


################################################################
# main function
def main ():
  # date of start
  begin = time.time()
  # lock used by threads
  global lock
  lock = threading.Lock()
  global nb_of_names_found
  nb_of_names_found = 0
  global semaphore
  semaphore = threading.BoundedSemaphore(value = NB_OF_THREADS_MAX)

  intro()

  # test nb of arguments given in command line
  if len (sys.argv) == 2:
    net = list_ip(sys.argv[1])
  else:
    use("wrong number of arguments")
   
  for ip_to_test in net:
    # start NB_OF_THREADS_MAX threads
    # stop main thread if more than NB_OF_THREADS_MAX threads are running.
    semaphore.acquire()
    current_thread = threading.Thread(None,show_name,None,(ip_to_test,),None)
    try:
      current_thread.start()
    except:
      use("can't start so many threads.")

  # wait for the end of all threads
  while threading.activeCount() != 1:
    time.sleep(2)

  sys.stdout.write("\nend of search\n")
  sys.stdout.write("%i ip tested, %i names found, in %i s\n\n" % ( len(net), nb_of_names_found, int(time.time()-begin ) ))

main()

