title: C# LDAP performance
body: <p>Where I work, we have two modes of authentication:</p>
  
  <ul>
  <li>CAS (<a href="http://www.jasig.org/cas" rel="nofollow noreferrer">http://www.jasig.org/cas</a>)</li>
  <li>LDAP</li>
  </ul>
  
  <p>CAS is the primary method, but it is often unreliable at peak traffic times and so we have been using LDAP as a fallback mode for when we notice that CAS is down. Previously, we were using PHP for doing our LDAP fallback and got reasonable performance. There wasn't a noticeable delay during login other than the expected network lag times. A login took probably ~250-500ms to complete using LDAP.</p>
  
  <p>Now, we are making a new system and have chosen ASP.NET MVC4 as the platform rather than PHP and I am tasked with trying to get this fallback working again. I have been pulling my hair out for about 6 hours now trying different things over and over again, getting the same result (perhaps I am insane). I have finally managed to connect to LDAP, authenticate the user, and get their attributes from LDAP. However, the query consistently takes 4.5 seconds to complete no matter what method I try.</p>
  
  <p>This is <em>very surprising</em> to me seeing as the PHP version was able to do nearly the same thing in 1/8th the time and it would seem that the .NET framework has excellent support for LDAP/ActiveDirectory. <strong>Am I doing something incredibly horribly wrong?</strong></p>
  
  <p>Here are the guts of my function as it stands now (this one is the latest iteration that manages to do everything in one 4.5 second query):</p>
  
  <pre><code>public Models.CASAttributes Authenticate(string username, string pwd)
  {
      string uid = string.Format("uid={0},ou=People,o=byu.edu", username);
  
      LdapDirectoryIdentifier identifier = new LdapDirectoryIdentifier("ldap.byu.edu", 636, false, false);
  
      try
      {
          using (LdapConnection connection = new LdapConnection(identifier))
          {
              connection.Credential = new NetworkCredential(uid, pwd);
              connection.AuthType = AuthType.Basic;
              connection.SessionOptions.SecureSocketLayer = true;
              connection.SessionOptions.ProtocolVersion = 3;
  
              string filter = "(uid=" + username + ")";
              SearchRequest request = new SearchRequest("ou=People,o=byu.edu", filter, SearchScope.Subtree);
              Stopwatch sw = Stopwatch.StartNew();
              SearchResponse response = connection.SendRequest(request) as SearchResponse;
              sw.Stop();
              Debug.WriteLine(sw.ElapsedMilliseconds);
              foreach (SearchResultEntry entry in response.Entries)
              {
                  Debug.WriteLine(entry.DistinguishedName);
                  foreach (System.Collections.DictionaryEntry attribute in entry.Attributes)
                  {
                      Debug.WriteLine(attribute.Key + " " + attribute.Value.GetType().ToString());
                  }
                  Debug.WriteLine("");
              }
          }
      }
      catch
      {
          Debugger.Break();
      }
  
      Debugger.Break();
      return null; //debug
  }
  </code></pre>
  
  <p>The PHP version of this follows this sequence:</p>
  
  <ol>
  <li>Bind anonymously and look up the user information using a basedn and cn</li>
  <li>Bind again using the username and password of the user to see if they are authentic</li>
  </ol>
  
  <p>It does two binds (connects?) in 1/8th the time it takes the .NET version to do one! Its this sort of thing that makes me thing I am missing something.</p>
  
  <p>I have tried methods based on the following sites:</p>
  
  <ul>
  <li><a href="http://roadha.us/2013/04/ldap-authentication-with-c-sharp/" rel="nofollow noreferrer">http://roadha.us/2013/04/ldap-authentication-with-c-sharp/</a> - Required 2 queries to do what I wanted and was too slow. I went through probably 6 different tries of doing it differently (varying the authentication &amp; connection settings, etc).</li>
  <li><a href="http://web.byu.edu/docs/ldap-authentication-0" rel="nofollow noreferrer">http://web.byu.edu/docs/ldap-authentication-0</a> - One PHP version, but has a small snippet about .NET at the bottom. I needed to get the profile as well and they weren't exactly descriptive.</li>
  <li><a href="https://stackoverflow.com/questions/1846436/system-directoryservices-is-slow">System.DirectoryServices is slow?</a> - Current version</li>
  </ul>
  
  <p><strong>EDIT:</strong></p>
  
  <p>Using wireshark, I saw that the following requests are made:</p>
  
  <ol>
  <li>bindRequest passing along my uid (delta 0.7ms)</li>
  <li>bindResponse success (delta 2ms)</li>
  <li>searchRequest "ou=People,o=byu.edu" wholdSubtree (delta 0.2ms)</li>
  <li>searchResEntry "uid=<em>my uid</em>,ou=People,o=byu.edu" | searchResDone success 1 result (delta 10.8ms)</li>
  <li>unbindRequest (delta 55.7ms)</li>
  </ol>
  
  <p>Clearly, the overhead is coming from .NET and not from the requests. These don't add up to 4.5 seconds in any way, shape, or form.</p>