Skip to main content
Ben Nadel at FirstMark Tech Summit (New York, NY) with: Atticus White
Ben Nadel at FirstMark Tech Summit (New York, NY) with: Atticus White

Ask Ben: Checking Domain Name Availability Using ColdFusion

By
Published in , Comments (34)

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:

<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:

<!--- 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.

Want to use code from this post? Check out the license.

Reader Comments

12 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. :)

15,880 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.

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

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.

132 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. :)

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?

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

17 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.

15,880 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.

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

15,880 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 = "" />

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

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.

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.

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.

15,880 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.

6 Comments

I've tried making this thing work but I seem to be getting the same error. I switched the

<cfthrow
type="Whois.InvalidDomainName"
message="Domain name is invalid."
detail="The domain name, #ARGUMENTS.Domain#, could not be found on the Whois server."
/>

to a cfcatch type any. The output I'm getting is "The element at position 1 cannot be found."

It seems that this portion is what is causing the error output above.

<cfset LOCAL.WhoisDomainText = REReplaceNoCase(
LOCAL.PreMatches[ 1 ],
"^<pre>|</pre>$",
"",
"all"
) />

Any thoughts on why this might be?

15,880 Comments

@Ty,

It's possible that the target page changed the way they implemented the code on the HTTP page. If that is the case, then our pattern won't match and the reMatchNoCase() won't return any values.

Try access the page manually and seeing how the source code works now.

6 Comments

Okay man, I've went through tons of search's, reviewed source code from the whois site and even talked to someone else about the code you wrote here, but I can't seem to get it to work.

Here's the code I've been working with on my form. and the code you provided for the component is the same. I'm trying to use my own form to request the data and the output the results of the cfhttp.filecontent onto the same page. All help is appreciated. I've just redesigned my site and this is a key part to the site and my clients. I've commented out the google.com code you try because the end result was always true no matter what url I place in it.

15,880 Comments

@Ty,

I just tried running this blog post again on my dev server and it appears to work correctly still. I get the following output:

Google is NOT available.
GoogleRocksHardCore IS available.

... which is what worked in the original post. There must be something going wrong somewhere else in your code.

6 Comments

Okay Ben I got the page to work finally. I eliminated the position error because I finally figured out how it was working. My resulting code now checks for domain nam availability from the input a user provides by writing the value to the obj instead of using the hard coded value. If you would like to see my final code that I've tested let me know.

I want to thank you for your time and input and your component has allowed me to complete this part of my site. There are several things on here that have been invaluable to me and I'm sure plenty of others. By the way I just redesigned my web site and I'm very glad that I'm now able to implement this new domain name availability check

THX

15,880 Comments

@Ty,

Awesome - glad you got it working. I'm always happy to be able to help out. Sounds like you're up and running nicely over tehre.

1 Comments

Ben,

I found this article the other day, and it didn't quite fit my needs. I then found a whois.cfc on riaforge that returned the results pretty well, but limited on the whois server etc.

Ive modified it (but my code is poor)... that will query the correct whois server dependant on the domain...

Would you like me to send it - perhaps you could tidy it up for people to use?

J

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel