Ask Ben: Checking Domain Name Availability Using ColdFusion

Posted November 25, 2008 at 10:06 AM

Tags: ColdFusion, Ask Ben

Do you know of any ColdFusion code to check to see if a domain is available. I want to add a domain search to my site, but can't find any code for it in CF. I have found PHP code, but really would like ColdFusion version. Any ideas... thanks.

When it comes to checking domain name availability, it seems the challenge is not so much the ColdFusion code but rather finding a web service that makes this sort of information publicly available. After a good amount of Googling, I couldn't find anything. I did find some Registrar APIs, but those required having an account with the given registrar in order to use. The only thing I could come up with was to screen scrape the public Whois search form on Internic.net (I believe this is how others are doing it as well).

When I found this form, the first thing I wanted to do was encapsulate the interaction with the Whois form in a way that would be easy to use. So, I created a Whois.cfc ColdFusion component that has a method for scaping the Internic.net Whois form as well as a method for leveraging this scraper to determine domain availability. Here is my Whois.cfc ColdFusion component:

 Launch code in new window » Download code as text file »

  • <cfcomponent
  • output="false"
  • hint="I have utility methods for checking domain availability.">
  •  
  •  
  • <cffunction
  • name="Init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I return the intialized object.">
  •  
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetDomainAvailability"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I check to see if the given domain is avilable.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Domain"
  • type="string"
  • required="true"
  • hint="I am the top level domain whose availability is being checked."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!---
  • Try to get the domain information. This may throw
  • various types of exceptions.
  • --->
  • <cftry>
  •  
  • <!--- Get the domain information. --->
  • <cfset LOCAL.DomainInformation = THIS.GetDomainInformation(
  • ARGUMENTS.Domain
  • ) />
  •  
  • <!---
  • If we made it this far, then the domain came
  • back successfully. This means that the domain is
  • NOT available (already registered).
  • --->
  • <cfreturn false />
  •  
  • <!---
  • Catch the invalid domain error. This error means
  • that Whois could not find the domain (it is NOT
  • yet registered).
  • --->
  • <cfcatch type="Whois.InvalidDomainName">
  •  
  • <!--- Domain is still available. --->
  • <cfreturn true />
  •  
  • </cfcatch>
  •  
  • <!---
  • Let any other errors bubble up. We caught the
  • only error that has meaning to our initiative;
  • any other errors that get thrown need to be
  • caught by the calling code.
  • --->
  •  
  • </cftry>
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetDomainInformation"
  • access="public"
  • returntype="struct"
  • output="false"
  • hint="I look up domain information. If the given domain could not be found, I throw an exception.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Domain"
  • type="string"
  • required="true"
  • hint="I am the top level domain being searched."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  •  
  • <!--- Create the default domain information struct. --->
  • <cfset LOCAL.DomainInformation = {
  • DomainName = "",
  • Registrar = "",
  • WhoisServer = "",
  • ReferralURL = "",
  • NameServer = [],
  • Status = [],
  • UpdatedDate = "",
  • CreationDate = "",
  • ExpirationDate = ""
  • } />
  •  
  •  
  • <!---
  • Get the domain information from the WHOIS server by
  • mocking the public form submission.
  • --->
  • <cfhttp
  • method="get"
  • url="http://reports.internic.net/cgi/whois"
  • useragent="whois.cfc"
  • result="LOCAL.HttpGet">
  •  
  • <!--- Domain to be searched. --->
  • <cfhttpparam
  • type="url"
  • name="whois_nic"
  • value="#ARGUMENTS.Domain#"
  • />
  •  
  • <!---
  • The type of search that we are performing - the
  • domain name search.
  • --->
  • <cfhttpparam
  • type="url"
  • name="type"
  • value="domain"
  • />
  •  
  • </cfhttp>
  •  
  •  
  • <!--- Check to see if the request was good. --->
  • <cfif NOT FindNoCase( "200", LOCAL.HttpGet.StatusCode )>
  •  
  • <!--- Throw error. --->
  • <cfthrow
  • type="Whois.CouldNotConnect"
  • message="Could not connect to Whois server."
  • detail="Could not connect to Whois server - came back with status: #LOCAL.HttpGet.StatusCode#"
  • />
  •  
  • </cfif>
  •  
  •  
  • <!--- Grab the PRE text in the response text. --->
  • <cfset LOCAL.PreMatches = REMatchNoCase(
  • "<pre>[\w\W]+?</pre>",
  • LOCAL.HttpGet.FileContent
  • ) />
  •  
  • <!--- Grab the Whois text out of the PRE tag. --->
  • <cfset LOCAL.WhoisDomainText = REReplaceNoCase(
  • LOCAL.PreMatches[ 1 ],
  • "^<pre>|</pre>$",
  • "",
  • "all"
  • ) />
  •  
  •  
  • <!---
  • Check to see if the domain was found. If it was, then
  • there will be certain markers in the Whois data text.
  • --->
  • <cfif FindNoCase( "no match", LOCAL.WhoisDomainText )>
  •  
  • <!---
  • The domain name was not found in the Whois
  • server. Throw exception to alert calling
  • method that domain name was not valid.
  • --->
  • <cfthrow
  • type="Whois.InvalidDomainName"
  • message="Domain name is invalid."
  • detail="The domain name, #ARGUMENTS.Domain#, could not be found on the Whois server."
  • />
  •  
  • </cfif>
  •  
  •  
  • <!---
  • ASSERT: At this point, we know that we succesfully
  • pinged the Whois server and have received valid
  • domain data.
  • --->
  •  
  •  
  • <!--- Pull out the data for the domain properties. --->
  • <cfset LOCAL.Properties = REMatchNoCase(
  • "(?m)^\s*(Domain Name|Registrar|Whois Server|Referral URL|Name Server|Status|Updated Date|Creation Date|Expiration Date):[^\r\n]+$",
  • LOCAL.WhoisDomainText
  • ) />
  •  
  • <!---
  • Loop over the properties to populate the return
  • domain data values.
  • --->
  • <cfloop
  • index="LOCAL.Proprety"
  • array="#LOCAL.Properties#">
  •  
  • <!--- Parse the property name. --->
  • <cfset LOCAL.PropertyName = Trim(
  • ListFirst( LOCAL.Proprety, ":" )
  • ) />
  •  
  • <!--- Parse the property value. --->
  • <cfset LOCAL.PropertyValue = Trim(
  • ListRest( LOCAL.Proprety, ":" )
  • ) />
  •  
  •  
  • <!---
  • Update the return value property based on the
  • current property.
  • --->
  • <cfswitch expression="#LOCAL.PropertyName#">
  • <cfcase value="Domain Name">
  • <cfset LOCAL.DomainInformation.DomainName = LOCAL.PropertyValue />
  • </cfcase>
  • <cfcase value="Registrar">
  • <cfset LOCAL.DomainInformation.Registrar = LOCAL.PropertyValue />
  • </cfcase>
  • <cfcase value="Whois Server">
  • <cfset LOCAL.DomainInformation.WhoisServer = LOCAL.PropertyValue />
  • </cfcase>
  • <cfcase value="Referral URL">
  • <cfset LOCAL.DomainInformation.ReferralURL = LOCAL.PropertyValue />
  • </cfcase>
  • <cfcase value="Name Server">
  • <cfset ArrayAppend(
  • LOCAL.DomainInformation.NameServer,
  • LOCAL.PropertyValue
  • ) />
  • </cfcase>
  • <cfcase value="Status">
  • <cfset ArrayAppend(
  • LOCAL.DomainInformation.Status,
  • LOCAL.PropertyValue
  • ) />
  • </cfcase>
  • <cfcase value="Updated Date">
  • <cfset LOCAL.DomainInformation.UpdatedDate = LOCAL.PropertyValue />
  • </cfcase>
  • <cfcase value="Creation Date">
  • <cfset LOCAL.DomainInformation.CreationDate = LOCAL.PropertyValue />
  • </cfcase>
  • <cfcase value="Expiration Date">
  • <cfset LOCAL.DomainInformation.ExpirationDate = LOCAL.PropertyValue />
  • </cfcase>
  • </cfswitch>
  •  
  • </cfloop>
  •  
  •  
  • <!--- Return the domain information. --->
  • <cfreturn LOCAL.DomainInformation />
  • </cffunction>
  •  
  • </cfcomponent>

What I really like here is that the GetDomainInformation() method will throw an error if the domain could not be found. I decided to do this because I felt that it created a nice separation of concerns; the GetDomainInformation() is meant to get domain data - if the domain doesn't exist, then the method cannot do its job and should throw an error. How this error gets handled should be and is a concern of the calling code.

If the given domain name is found, the screen data is scraped (parsed) and stored in a standardized domain information struct that looks like this:

 
 
 
 
 
 
Google Whois Data As Gotten From Whois.cfc A ColdFusion Domain Information Component. 
 
 
 

Whois.cfc has an additional method, GetDomainAvailability(), that returns a boolean as to whether the domain is available for registration. As you can see in the above code, it is built to catch the "invalid domain" error that GetDomainInformation() might throw. Something about this just feels very clean.

Once I had this coded, I set up a simple test page:

 Launch code in new window » Download code as text file »

  • <!--- Create out Whois utility object. --->
  • <cfset objWhois = CreateObject( "component", "Whois" ).Init() />
  •  
  •  
  • <!--- Check to see if some domains are available. --->
  • <cfif objWhois.GetDomainAvailability( "Google.com" )>
  •  
  • Google IS available.<br />
  •  
  • <cfelse>
  •  
  • Google is NOT available.<br />
  •  
  • </cfif>
  •  
  •  
  • <!--- Check to see if some domains are available. --->
  • <cfif objWhois.GetDomainAvailability( "GoogleRocksHardCore.com" )>
  •  
  • GoogleRocksHardCore IS available.<br />
  •  
  • <cfelse>
  •  
  • GoogleRocksHardCore is NOT available.<br />
  •  
  • </cfif>

Here, we are testing the availability of two different domains - one that is obviously taken and one that is likely to be available. When we run this code, we get the following output:

Google is NOT available.

GoogleRocksHardCore IS available.

Works nicely. Of course, the problem with any ColdFusion "screen scraper" solution is that you become highly coupled to the output methodology of a third party server. This means that your functionality might break at any time if the third party server changes the way in which it outputs its information. As such, it is important to proceed with extreme caution when proceeding with a screen-scape-based solution.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page



Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Ed
Nov 25, 2008 at 2:05 PM // reply »
8 Comments

Nice work!

I made something similar long ago using http://whois.domaintools.com/yourdomain.com but probably it would not work now because they seem to be changed the way they build the source. This site have more information, but it would be a bit more difficult to parse their source. Well... maybe not more "complicated" but surely more time consuming than the one you used. :)


Nov 25, 2008 at 2:35 PM // reply »
6,516 Comments

@Ed,

Thanks. That is the complication with screen scraping - the moment the output gets changed, it all becomes garbage :( I am surprised there is not more of an API for this type of work. Maybe I am just not Googling for the right stuff.... or maybe it's behind some sort of membership or something.


Jim
Nov 25, 2008 at 7:13 PM // reply »
16 Comments

Hi Guys,

You could always try the free tool from zone edit.

link:
http://www.zoneedit.com/whois.html

source:
http://www.zoneedit.com/doc/code/zwhois.cpp

Parsing the cfexecute works quite nicely. It's it looks a like a fairly simple task to re-write into CF. If you read the source, you can see they're pulling just pulling data from whois.arin.net, whois.apnic.net and whois.ripe.net.

Anyone want cool side project to post to Riaforge?


Nov 25, 2008 at 7:42 PM // reply »
6 Comments

"they're pulling just pulling data from whois.arin.net, whois.apnic.net and whois.ripe.net."

Isn't the data they're pulling similar to a screen scrape as far as being subject to change? Are you saying this method is better because it is less subject to a change in output? I took a couple Java classes, but that code is over my head, so I want to be clear as to any benefit.

Thanks, Ben, for answering my question and going the extra mile and writing a CFC. I'm going to try your code tonight and see how it goes. I want to put it on my new website as a little extra gizmo. I'm sure we're going to see many more comments regarding this because so many people are looking for a little tool like this.

I would think an API would be publicly available for something like this, and maybe soon it will be...maybe something using XML. For now, I love your code, and I will give it a go.

Thanks again


Jim
Nov 25, 2008 at 8:08 PM // reply »
16 Comments

"Isn't the data they're pulling similar to a screen scrape as far as being subject to change?"

The standard output is static, and the registrars are less likely to change the output as say, a website.

If you want to shortcut this, and if you're using windows, try downloading the EXE, and parse the output from the that.


Nov 25, 2008 at 8:18 PM // reply »
125 Comments

@Stork

Provided you query for the A record contents then no, it will never change.

If you have access to a *nix machine try "dig google.com" and what you'll get back is the DNS master file records.

That's the actual DNS format that will never change until they replace DNS with something better. :)


Nov 26, 2008 at 10:59 AM // reply »
10 Comments

@Elliot: That's what I thought might make sense too, but I'm not sure all domain names are necessarily diggable, even if they're registered.

Does anyone know for sure?


Rob
Nov 26, 2008 at 12:41 PM // reply »
1 Comments

Hi Ben;
Let say I want to do the same thing for Craigslist.org and display a list of items from their categories. Should I'll do a screen scrape or pull the data from their RSS. I googled, and Craigslist does not have an API to tap into and do this. What would be your recommendation.
Thanks


Gov
Nov 26, 2008 at 3:29 PM // reply »
8 Comments

Hi all,

This (nice!) solution only works for .com names, right?
Does anyone know which 'database' should be 'queried' for to get results for all kind of top levels?

Let say I want to be able to check for domain names like .be domain names for example besside .com, .org, .fr, .nl and so on...
I'm way to lazy to build cases with cfswitch depending on the entered top level to query the appropriate server (dns.be in the case of a .be domain name,...)

So what I'm looking for, is such a whois service that can retrive information for all top levels.


Nov 28, 2008 at 5:10 PM // reply »
6,516 Comments

@Rob,

I would say definitely go with the CL RSS feeds if possible. I would always opt for a data-driven solution that one that relies on screen scraping. At least with RSS, there is a standard way to grab and parse the code that is completely separate from the display.


Nov 29, 2008 at 11:38 AM // reply »
6 Comments

Ben,

I tried your code and I get an invalid construct. CF is expecting {.
It occurs here:
<cfset LOCAL.DomainInformation = {
DomainName = "",
Registrar = "",
WhoisServer = "",
ReferralURL = "",
NameServer = [],
Status = [],
UpdatedDate = "",
CreationDate = "",
ExpirationDate = ""
} />

It looks right to me. Any ideas?

Thanks


Nov 29, 2008 at 4:34 PM // reply »
6,516 Comments

@Stork,

That is a CF7 / CF8 issue. I am using implicit struct and array creation, which is the {} and [] notation. To convert that to a pre-ColdFusion 8 code, you'd have to do something like this:

<cfset LOCAL.DomainInformation = StructNew() />
<cfset LOCAL.DomainInformation.DomainName = "" />
<cfset LOCAL.DomainInformation.Registrar = "" />
<cfset LOCAL.DomainInformation.WhoisServer = "" />
<cfset LOCAL.DomainInformation.ReferralURL = "" />
<cfset LOCAL.DomainInformation.NameServer = ArrayNew( 1 ) />
<cfset LOCAL.DomainInformation.Status = ArrayNew( 1 ) />
<cfset LOCAL.DomainInformation.UpdatedDate = "" />
<cfset LOCAL.DomainInformation.CreationDate = "" />
<cfset LOCAL.DomainInformation.ExpirationDate = "" />


Nov 29, 2008 at 5:14 PM // reply »
8 Comments

Also, don't forget it is possible to telnet into whois.arin.net on port 43 and query a whois request that way


Nov 30, 2008 at 12:12 PM // reply »
9 Comments

domaintools.com has a free API access for domain searches, up to 100 queries a day. If you have more, you can get a paid subscription.

http://xml-api.domaintools.com/index.php?cmd=showDescription


Gov
Nov 30, 2008 at 2:33 PM // reply »
8 Comments

quote:
domaintools.com has a free API access for domain searches, up to 100 queries a day. If you have more, you can get a paid subscription.

http://xml-api.domaintools.com/index.php?cmd=showDescription

Now that *is* interesting! And easy to implement!


Nov 30, 2008 at 9:14 PM // reply »
6,516 Comments

@Paul,

That looks pretty cool. Thanks!


Dec 1, 2008 at 12:02 AM // reply »
1 Comments

very nice approach, I have developed one just using php/javascript and it works just fine.

http://www.livedomainsearch.com check out cheap domains finder


Dec 1, 2008 at 2:01 PM // reply »
1 Comments

A few years back, I built a quick and dirty Domain lookup for personal use, and it's proven quite useful. It's built on DIG, available here:
http://members.shaw.ca/nicholas.fong/dig/

then the code to call the lookup is simply:
<cfexecute name="C:\dig\whois.exe" arguments="#domain#" variable="whois" timeout="10"></cfexecute>

Now obviously, the response is raw text, but it does get you there, and quickly.


Dec 1, 2008 at 2:08 PM // reply »
10 Comments

Since you can make Coldfusion a telnet client (http://www.forta.com/blog/index.cfm?mode=entry&entry=A61BC7FF-3048-80A9-EFBD57CB2B673C94), it seems you should be able to do this very easily without necessarily going to any outside service other than the whois service mentioned above.

There may also be Java libraries for this kind of thing, as well...


Dec 1, 2008 at 7:05 PM // reply »
6 Comments

I ended up changing Ben's code to a CF7 friendly version using a structure. Someone here mentioned that was a CF7-CF8 issue, and it made me realize I was on a CF7 server.
Thanks to all who post on this blog for information on other methodologies, and I will have to try them too.


Dec 2, 2008 at 7:50 AM // reply »
6,516 Comments

@Stork,

Glad you are the right track. Please keep us updated with what seems to be working for you.


Dec 14, 2008 at 4:34 PM // reply »
5 Comments

I remeber that for over a year ago I come across this website: http://www.cfdns.org. It is a CFC wrapper for the dnsjava library, and it is also released under the BSD License!


Dec 14, 2008 at 8:00 PM // reply »
5 Comments

A whois cfc: http://snippets.dzone.com/posts/show/1610


Oct 15, 2009 at 2:03 PM // reply »
1 Comments

I tried your code and it worked great for .com domains. Unfortunately it didn't work for .co.za domains (South Africa) which is where I am from.

I found this post (http://www.phillnacelli.net/blog/index.cfm/2007/2/21/DNS-Lookup-in-ColdFusion) that explains how to use the java.net.InetAddress class to do a dns lookup. Either it finds the ip address for a given domain name or it throws an error.

I was thinking of modifying to code to include a try...catch. Either it would return true if the domain was found successfully or false if an error occurred. The only error that seems to occur (I haven't done heeps of testing yet) when I specify domains that I KNOW are not in use is java.net.UnknownHostException.

I would like it if you could give me some guidance here. Do you think this would be a sufficient method for determining whether a domain is available or do you know if other errors could be thrown and why or what they could be?

The code would look something like this:

<cfcomponent>
<cffunction name="getDomainNameStatus" access="public" returntype="boolean">
<cfargument name="domain" required="true" hint="The domain name being queried">
<cfset javaInet = createObject("java","java.net.InetAddress")>
<cftry>
<cfset dnsLookup = javaInet.getByName("#ARGUMENTS.domain#")>
<cfreturn true/>
<cfcatch>
<cfdump var="#cfcatch#">
</cfcatch>
</cftry>
</cffunction>
</cfcomponent>

Thanks in advance.

P.S. Thanks for all the valuable input. Your site has helped me out in many situations.


Oct 31, 2009 at 5:12 PM // reply »
6,516 Comments

@Andrew,

As long as Phill's Java stuff works, then it should be sufficient. Other than that, I don't know much about this type of thing. I literally Googled for a WhoIS API method to find the one I played with; the whole DNS world is a bit fuzzy for me.


Nov 3, 2009 at 1:08 PM // reply »
6 Comments

I tried that link that Andrew left and got an error.


Nov 3, 2009 at 1:12 PM // reply »
6,516 Comments

@Mark,

I just tried it and it seems to work? Make sure you are getting the whole URL in there.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »
Nov 20, 2009 at 11:00 PM
Five Months Without Hungarian Notation And I'm Loving It
@Marcel, Yeah, I always err on the side of longer but more readable variable names. As for the camel casing of CF methods and the headless camel casing of custom items, I get around this by always ... read »
Nov 20, 2009 at 10:56 PM
Five Months Without Hungarian Notation And I'm Loving It
I use the following and love it: my.namespace.MyComponents.functionMethodsOrUDF() CONSTANT_VALUES_OR_PROPERTIES One thing I always try is to CamelCaseBuiltInColdFusionFunctions() so others can tell ... read »
Nov 20, 2009 at 5:38 PM
Learning ColdFusion 8: CFImage Part I - Reading And Writing Images
Hi Ben, Great article. I've been looking around to see if ColdFusion image engine can programatically create the following "wrap around" effect: http://www.creativepro.com/article/photoshop-s-she ... read »
Nov 20, 2009 at 5:35 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Dave: I talked to Gert he suggested: <cfhttp method="get" url="http://{some cf website}" result="stuff" addtoken="yes" /> Note the addition of cfhttp attribute addtoken. That should persist y ... read »
Nov 20, 2009 at 5:23 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Todd, Ahh, gotcha, yeah that makes sense. ... read »
Nov 20, 2009 at 5:17 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
Ben, sorry if I didn't make this clear. You can make it work like that if you want, just put <cfset session.foo = 1> (and <cfset application.foo = 1>) in your OnRequestStart() and it reve ... read »