Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Getting a 'base' Domain from a Domain


:P
On this page:

Here's a simple one: How do you reliably get the base domain from full domain name or URI? Specifically I've run into this scenario in a few recent applications when creating the Forms Auth Cookie in my ASP.NET applications where I explicitly need to force the domain name to the common base domain. So, www.west-wind.com, store.west-wind.com, west-wind.com, dev.west-wind.com all should return west-wind.com.

Here's the code where I need to use this type of logic for issuing an AuthTicket explicitly:

private void IssueAuthTicket(UserState userState, bool rememberMe)
{
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, userState.UserId,
                                                         DateTime.Now, DateTime.Now.AddDays(10),
                                                         rememberMe, userState.ToString());

    string ticketString = FormsAuthentication.Encrypt(ticket);
    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticketString);
    cookie.HttpOnly = true;
    
    if (rememberMe)
        cookie.Expires = DateTime.Now.AddDays(10);

    // write out a domain cookie
    cookie.Domain = Request.Url.GetBaseDomain();

    HttpContext.Response.Cookies.Add(cookie);
}

Now unfortunately there's no Uri.GetBaseDomain() method unfortunately, as I was surprised to find out. So I ended up creating one:

public static class NetworkUtils
{

    /// <summary>
    /// Retrieves a base domain name from a full domain name.
    /// For example: www.west-wind.com produces west-wind.com
    /// </summary>
    /// <param name="domainName">Dns Domain name as a string</param>
    /// <returns></returns>
    public static string GetBaseDomain(string domainName)
    {
            var tokens = domainName.Split('.');

            // only split 3 segments like www.west-wind.com
            if (tokens == null || tokens.Length != 3)
                return domainName;

            var tok  = new List<string>(tokens);
            var remove = tokens.Length - 2;
            tok.RemoveRange(0, remove);

            return tok[0] + "." + tok[1]; ;                                
    }

    /// <summary>
    /// Returns the base domain from a domain name
    /// Example: http://www.west-wind.com returns west-wind.com
    /// </summary>
    /// <param name="uri"></param>
    /// <returns></returns>
    public static string GetBaseDomain(this Uri uri)
    {
        if (uri.HostNameType == UriHostNameType.Dns)                        
            return GetBaseDomain(uri.DnsSafeHost);
        
        return uri.Host;
    }
 
}

I've had a need for this so frequently it warranted a couple of helpers. The second Uri helper is an Extension method to the Uri class, which is what's used the in the first code sample. This is the preferred way to call this since the URI class can differentiate between Dns names and IP Addresses. If you use the first string based version there's a little more guessing going on if a URL is an IP Address.

There are a couple of small twists in dealing with 'domain names'. When passing a string only there's a possibility to not actually pass domain name, but end up passing an IP address, so the code explicitly checks for three domain segments (can there be more than 3?). IP4 Addresses have 4 and IP6 have none so they'll fall through. Then there are things like localhost or a NetBios machine name which also come back on URL strings, but also shouldn't be handled.

Anyway, small thing but maybe somebody else will find this useful.

Posted in ASP.NET  Networking  

The Voices of Reason


 

Prescott
April 25, 2012

# re: Getting a 'base' Domain from a Domain

There could be more than 3 - I logged into my hotmail account: http://col108.mail.live.com

Rick Strahl
April 25, 2012

# re: Getting a 'base' Domain from a Domain

Yup - several people commented on that on twitter as well. There are also things like myDomain.co.uk which would be valid.

For now this will work for me, since I'm working with a known set of domains. Heck, maybe it's even easier to just put a CookieDomain config flag and make it configurable.

But I wonder if there would be some 'safe' way to figure out the base domain?

Filip W
April 25, 2012

# re: Getting a 'base' Domain from a Domain

Rick, now all you need to do is extended these helpers to facilitate this list http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1

:)

James Hart
April 25, 2012

# re: Getting a 'base' Domain from a Domain

I think the closest concept DNS has to the 'base domain' that you seem to be after is probably the 'start of authority', or SOA record. You can query a DNS server for the SOA record for a hostname, and it should return a record indicating at what level in the name hierarchy that hostname is defined. If you query SOA for www.west-wind.com, for example, it tells you that west-wind.com is the SOA. For Prescott's col108.mail.live.com, the SOA record is at live.com. For www.somedomain.co.uk it would be somedomain.co.uk. For www.boston.k12.ma.us (a school district website) it returns boston.k12.ma.us.

You certainly can't reliably figure this out using only string manipulation - you have to ask the DNS records.

Carsten Petersen
April 25, 2012

# re: Getting a 'base' Domain from a Domain

It might be better to use a Regex to find the most general "formats". It would be something like:

var domainString = "www.mirror.co.uk";
var RE_RootDomain = new Regex(@"([\w\d\-]{2,}\.(?:co.uk|[\w]{2,}))$", RegexOptions.IgnoreCase);
if (RE_RootDomain.IsMatch(domainString)){
    var rootDomain = RE_RootDomain.Match(domainString).Groups[1].Value;
}


Matched "co.uk" as TLD.
TLD must be at least 2 chr. or "co.uk".
Domain without TLD must be at least 2 chr.

Sander
April 26, 2012

# re: Getting a 'base' Domain from a Domain

needed something similar, ended up with this:

        internal static string GetBaseDomain( this Uri uri )
        {
            //see for more: http://data.iana.org/TLD/tlds-alpha-by-domain.txt
            var tld = new[] { ".nl" , ".be" , ".de" , ".eu" , ".fr" , ".com" , ".net" , ".info" , ".org" , ".biz" , ".co.uk" , ".nu" , ".tv" } ;
 
            for ( int i = 0 ; i < tld.Length ; i++ )
            {
                if ( uri.Host.EndsWith( tld[ i ] ) )
                {
                    int dot = uri.Host.Substring( 0 , uri.Host.Length - tld[ i ].Length ).LastIndexOf( '.' ) ;
                    //if dot equals -1 (when '.' is not found) +1 will bump to 0 (for example if host equals test.com)
                    //if dot greater than or equal 0 (when '.' is found) +1 will exclude the '.' (for eaxmple if host equals www.test.com)
                    return uri.Host.Substring( dot + 1 ) ;
                }
            }
            return uri.Host;
        } 
.

Rick Strahl
April 26, 2012

# re: Getting a 'base' Domain from a Domain

@Sander - yes, that looks good. I was thinking if I need more than the simple specific domains I'm dealing with then a white list of domain extensions would do the trick. Thanks!

Steven Quick
April 26, 2012

# re: Getting a 'base' Domain from a Domain

Why not just make "BaseDomain" a web.config setting with a transformation for dev (localhost), beta (beta.west-wind.com) and live (west-wind.com)?

Rick Strahl
April 26, 2012

# re: Getting a 'base' Domain from a Domain

@Steven - essentially that's what I did except I used the FormsAuthentication domain key for it since that's what the domain is used for.

Richard Lawley
August 15, 2012

# re: Getting a 'base' Domain from a Domain

I appreciate this is a bit late, but I've had to do something similar recently. The solution you have here works fine for a restricted set of domains, but gets completely broken by some other international domains. For example, domain.com and domain.uk.com are both valid "base" domains, but "uk.com" is not (it's a TLD).

There's a list maintained at http://publicsuffix.org/ which contains a lot of rules for domains (Filip W pointed to a copy of this within some source code from Mozilla in an earlier comment). There used to be a rule engine implementation on a google code site which was taken down (though is still available at www.softpedia.com/progDownload/DomainNameParser-Download-137099.html). Not sure why the original implementation was taken down, but there are another couple of implementations on github: https://github.com/interlinkone/interlinkONE.PublicSuffix (I've not tested this).

steve
April 10, 2014

# re: Getting a 'base' Domain from a Domain

you can get domain name like the following:

Uri myUri = new Uri("http://forums.asp.net/t/1110512.aspx?");
string host = myUri.Host;

more methods to get domain name...

http://net-informations.com/faq/asp/domain.htm

steve

tino
November 02, 2016

# re: Getting a 'base' Domain from a Domain

Here is a up-to-date library of publicsuffix (https://github.com/tinohager/Nager.PublicSuffix) a nuget package is available

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024