Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An RFC 2253 Compliant Distinguished Name Parser

0.00/5 (No votes)
27 Mar 2009 1  
A set of classes to parse and manipulate LDAP distinguished names
Sample Image - DNParser.png

Introduction

Every object in an LDAP directory is uniquely identified by a distinguished name (DN). A distinguished name specifies the name of the object itself, along with the names of all its parent objects. Thus, a DN identifies the object itself as well as its position in the tree.

The rules for representing distinguished names as strings are laid out in RFC 2253, and they actually get a little complex in places. Hence, this parser.

Background

I was rewriting some terrible ADSI code (that I wrote myself a few years ago, but I'm going to gloss over that little detail), and I noticed that the original code navigated the LDAP tree by getting an ADSI object, reading its distinguishedName property, then getting its parent object and reading that object's distinguishedName property recursively. That procedure made me pretty uncomfortable because it involved a lot of back-and-forth with the server that wasn't really necessary. I figured it would be a simple thing to parse a distinguished name in code, thereby avoiding all the server-side processing. Well, it turns out that it was less simple than I expected, but it came out pretty well, so I thought I'd share the results with you folks.

Using the code

There are three main classes in this code:

  • DN, which represents a full distinguished name
  • RDN, which represents a relative distinguished name
  • RDNComponent, which represents the individual components of a multivalued RDN

I don't think that multivalued RDNs are even supported by Active Directory, but they're supported by the RFC, so they're supported by my parser.

You construct a DN object by feeding it a distinguished name string, like so:

DN myDN = new DN(@"CN=Pete Everett\, esq.,OU=People,DC=example,DC=com");

To print out a DN object, you use its ToString() method, as you'd expect.

Console.WriteLine(myDN.ToString());
// prints out:
// CN=Pete Everett\, esq.,OU=People,DC=example,DC=com

But if you'd like more control over the formatting, you can specify categories of characters to escape.

Console.WriteLine(myDN.ToString(EscapeChars.None));
// prints out:
// CN=Pete Everett, esq.,OU=People,DC=example,DC=com
// (Note that this is an incorrect DN format, and will not parse correctly.)

To get the parent object of a given DN object, you can use its Parent property.

DN myParentDN = myDN.Parent;
Console.WriteLine(myParentDN.ToString());
// prints out:
// OU=People,DC=example,DC=com

And to get a child of a given DN object, you can use its GetChild() method.

DN myChildDN = myParentDN.GetChild("CN=Mike");
Console.WriteLine(myChildDN.ToString());
// prints out:
// CN=Mike,OU=Poeple,DC=example,DC=com

You can also access the individual RDNs of a given DN object, like so:

Console.WriteLine(myChildDN.RDNs[2].ToString());
// prints out:
// DC=example

And you can get the type or value of a component of an RDN, if you're inclined.

Console.WriteLine(myChildDN.RDNs[1].Components[0].ComponentValue);
// prints out:
// People

Design Considerations

I wanted to make sure that each DN (and its component parts) was immutable. This allowed me to do a couple of cute things up front, like calculate an object's hash code upon object creation (and use the hash code as a quick test for inequality) and pass references to the same underlying objects between different DN objects. For example:

// referenceDN contains references to 4 RDN objects
DN referenceDN = new DN("OU=Marketing,OU=People,DC=example,DC=com");
// parentDN contains references to 3 of the same RDN objects from referenceDN
DN parentDN = referenceDN.Parent;

It's worth noting, though, that this only does the job halfway, because:

// childDN is now equal to referenceDN, but its first RDN points to a
// different object than referenceDN's first RDN does.
DN childDN = parentDN.GetChild("OU=Marketing");

Limitations

  • This is my first crack at coding something to match an RFC. I think I've done a pretty good job of it, but it's possible that I've misinterpreted something in the spec. Send criticisms my way. (But be gentle. I'm a little sensitive.)
  • Working with multibyte characters is tricky. The RFC specifies that multibyte characters should be allowed to be escaped as their individual bytes. So for example, Unicode character 0x266B (a musical note) should be allowed to be represented as \E2\99\AB (its three-byte UTF-8 encoding). The problem is that ADSI's GetObject() method (and anything built on top of it, like .NET's DirectoryServices stuff) doesn't support escaped multibyte characters. So if you feed it an escaped character, it'll choke. But if you feed an unescaped multibyte character to something that expects ASCII, it'll also choke. My workaround to this problem has been to let you choose which categories of characters to escape. (There's an example above.)
  • I thought up as many strange test cases as I could and wrote a bunch of NUnit tests which kept me company throughout the development process. I've included the tests along with the code, so you can verify that any changes you might want to make won't break existing functionality. I'm sure, though, that I've done a half-hearted job of thinking of tests, and if you see any glaring omissions, please send them my way.

History

  • March 7, 2005 - Initial posting
  • March 26, 2009 - Fixed some null-reference bugs

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here