/***************************************************************************
* Anastasios Monachos - anastasiosm@gmail.com
* $Id: DomainBF.java 15 2009-04-27 21:28:59Z tasos $ 
* Compile: javac DomainBF.java
*
* Usage: java DomainBF domain.tld filename
*
* In the past, with a zone transfer of the target domain we could get a 
* list of all public hosts, their respective IP addresses and the record
* type phase.  Since most of the server now (finally) are configured to
* prevent AXFR requests from non valid IPs (who are not setup as secondary
* DNS) host reconnaissance has become more obscure.
* 
* This program was written a few years ago to fill the gap left by commands 
* like:		(unix)	dig domain.tld ANY				and the obsolete trick
*			(unix)	dig @ns1.foo.bar domain.tld axfr
			(winz via nslookup)	ls -d domain.tld
*
* basically, it goes through a list of predefined subdomain prefixes (the list 
* of the subdomains is being passed as an argument, for your convenience we
* we have gathered a few in the subs.list file) and asks the OpenDNS servers
* if that subdomain with specific DNS records exists.
*
* ***Note***	This tool is supplementary and it should be used along with
*				other host reconnaissance tools (see dig, samspade etc) and
*				techniques (bounce emails, social engineering etc). 
***************************************************************************/
import java.io.*;
import java.lang.*;
import java.net.*;
import java.io.IOException;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;

class DomainBF extends Thread
{
	private String recordToDig[]= {"A", "NS", "MX", "PTR"};//DNS records to check
	private String fileUserInput = "";
	private String constructedSubDomain = "";
	private static String userDomain = "";
	private static final String OPEN_DNS_MASTER_SERVER = "208.67.222.222";
	private static final String OPEN_DNS_SECONDARY_SERVER = "208.67.220.220";//not used
	private static Vector dirty;
	private static Vector clean;
	private static String opendnsIpIsJunk = "";//some older values were "208.69.34.132" and "67.215.65.132"
	
	//we will try to get a record for this noexisted domain. For every 
	//non-existing domain OpenDNS return an IP that belongs to its NetRange. 
	//That's an easy way to tell which subdomains do not exist, so to remove 
	//them later from our dirty vector, and save them into the clean vector
	private static String thisIsADummyDomain = "th1s1sadummysubdoma1n.google.com";

public static void main(String[] args) throws Exception
{
	try
	{
   		if (args.length==2)
		{
			userDomain = args[1];
			dirty = new Vector();
			clean = new Vector();
			//get the IP for the non-existing domain
			getDummySubDomainRecordIP(thisIsADummyDomain);
			
			DomainBF bf = new DomainBF(args[0], args[1]);
		}
		else
		{
			usage();
			System.exit(0);
		}
    }
    catch (Exception e)
    {}
}

static void usage()
{
	System.err.print(
	"\nUsage: java DomainBF [domain] [file-subdomain-list]\n\n"
        + "        1. where domain is the domain to be investigated, in the form\n"
        + "           domain.tld eg google.com\n"
        + "        2. where file-subdomain-list is the name of the file which will contain\n"
        + "           the list of subdomains that will be checked against.\n\n"
		+ "		Info: The program makes use of the free OpenDNS servers.\n"
          			);
}

static void getDummySubDomainRecordIP(String str)
{
	try
	{
		Hashtable temp = new Hashtable();
		temp.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        temp.put("java.naming.provider.url", "dns://" + OPEN_DNS_MASTER_SERVER + "/");

		DirContext ictx = new InitialDirContext(temp);
		Attributes attrsForJunk = ictx.getAttributes(str, new String[] {"A"});

        NamingEnumeration enumeration = attrsForJunk.getAll();
		Attribute attr = (Attribute)enumeration.next();
		NamingEnumeration values = attr.getAll();

		opendnsIpIsJunk = (String) values.next();
		System.out.println("Default IP returned by OpenDNS is: " + opendnsIpIsJunk+" do not worry I will clear the junk for you.");

	}
	catch(Exception e){}
}

public DomainBF(String userInputDomain, String fileUserInput)
{	
	try{
	//Open the file that is the 2nd command line parameter, the file should contain the domain prefixes eg. www
	FileInputStream fstream = new FileInputStream(fileUserInput);
	// Get the object of DataInputStream
	DataInputStream in = new DataInputStream(fstream);
    BufferedReader br = new BufferedReader(new InputStreamReader(in));
	String strLine;
	//Read file line-by-line
	while ((strLine = br.readLine()) != null)
	{
		//Print the content on the console for each line construct the new subdomain
		constructedSubDomain = strLine+"."+userInputDomain;
		//System.out.println ("The constructed domain is: "+constructedSubDomain);
	/*****************************/
		//now call accessSocketSubDomain
		accessSocketSubDomain(constructedSubDomain);// samplefromfile.domain 
		//the above method will then call getRecord
		processDirtyData();
		callNoDuplicateDomains();
	/*****************************/
	}
	in.close();//Close the input stream
	}
	catch (Exception e)//Catch exception if any
	{
		System.err.println("\nError: " + e.getMessage());
		usage();
	}
		displayCleanData();
}//end of public SubDomain(String userInput)


public boolean accessSocketSubDomain(String subdomainToTry)
{
	if (subdomainToTry.charAt(0) != '.')
	{
		for (int i=0; i<recordToDig.length;i++)
		{
			//uncomment the following line to check what we are passing to getRecord
			//System.out.println("TRY: "+subdomainToTry+"::::::"+recordToDig[i]);
			getRecord(subdomainToTry, recordToDig[i]);
		}
	}//end of if
	else
	{
		//System.out.println("Ingoring: "+subdomainToTry);
	}
	return true;
}


/*
	Queries openDNS name server for each domainName, for each recordType
	and stores the results in the vector dirty.
	The vector is called "dirty" as results like:
	pop.domain.com		A		208.69.34.132
	and 
	smtp.domain.com		MX		0 .
	may be returned, which they are always wrong (OpenDNS returns those).
	Such results will be satinised later on by another method.
*/
public static void getRecord(String domainName, String recordType) 
{
	try 
        {  
		Hashtable env = new Hashtable(); 
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        env.put("java.naming.provider.url", "dns://" + OPEN_DNS_MASTER_SERVER + "/");
        DirContext ictx = new InitialDirContext(env);
        Attributes a = ictx.getAttributes(domainName, new String[] { recordType });
        NamingEnumeration all = a.getAll();
        while (all.hasMore())
        {
			Attribute attr = (Attribute)all.next();
                NamingEnumeration values = attr.getAll();
		        while (values.hasMore())
		        {
					dirty.add(new String (domainName+"\t\t"+ attr.getID()+"\t\t" +values.next()));
		        }
         } 
        }
        catch (Exception e) 
	{
	//System.out.println("Lookup.getRecord() ERROR "+ e.getMessage() + " while getting information for: "+domainName+" "+recordType);
	}
}//end of getRecord

/*
	Vector "dirty" is being checked for entries as
	pop.domain.com		A		208.69.34.132
	and 
	smtp.domain.com		MX		0 .
	Any returned result that does not comply to the above
	cases ("208.69.34.132" and "0 .") will be stored to 
	the "clean" vector.
*/
public static void processDirtyData()
{	
	for(int i=0;i<dirty.size();i++)
	{
		String fromVector = dirty.elementAt(i).toString();
		System.out.println("\t"+fromVector);
       	String zeroInMX = "0 .";

        
        int match1 = fromVector.indexOf(opendnsIpIsJunk);
        int match2 = fromVector.indexOf(zeroInMX);
        
        if (match1 != -1 || match2 != -1)
		{
			i++;
		}        	
		else
		{
            //System.out.println("Will be added to  " + match1);
			clean.addElement(dirty.elementAt(i));
			i++;
		}
	}
}//end processDirtyData

/*
	The "clean" vector now needs to be checked for duplicate entries.
*/
public void callNoDuplicateDomains()
{
	try
	{
		Collections.sort(clean);//Sorts elements in the Vector alphabetically
		for(int i=0;i<clean.size();i++)
		{
			for (int j=1;j<clean.size()-1;j++)//size()-1 to avoid ArrrayOutOfBounds Exception
			{
				if (clean.elementAt(i).equals(clean.elementAt(j)) && i!=j)
				{
					///System.out.println("remove: "+ domains.get(j));
					clean.removeElementAt(j);
				}//end of if
			}//end of for
		}//end of for
	}//end of try
	catch(ArrayIndexOutOfBoundsException e){}
}//end of callNoDuplicateDomains()


public static void displayCleanData()
{
	System.out.println("\n\nCleared Data: \t"+clean.size()+"\n**************************************************************************\n");
	for(int i=0;i<clean.size();i++)
	{
		System.out.println(clean.elementAt(i));
	}	
}

}//End public class DomainBF