I've had a few customers lately getting hangs using Exchange's MAPI to talk to Exchange. Their specific symptoms and repro steps varied from case to case, but in every case, the stack showed the client waiting on an NSPI call to return. NSPI is the interface the Exchange Address Book Provider uses to talk to the Global Catalog. In every case, the hung NSPI call was to the Exchange server and not the GC. The fix in each case was to enable referral so the client talked to GC directly. In this article, I want to discuss NSPI, DSProxy, the reasons for the hang, referral, and introduce an updated GCReconnect sample that demonstrates how this all works. I've also tried to consolidate all the relevant information into this one article so you can get your apps working quickly.
Back in the day (AKA Exchange 5.5 and earlier), Exchange had its own directory. When a MAPI client wanted to look something up in the directory, it would make a call using the Name Service Provider (NSPI) interface. Exchange registered to listen on this interface, and when it received an NSPI request, it would perform the lookup in the directory and return the results. When Exchange and Windows 2000 came along, Exchange stopped handling the directory - that's the GC's job. The GC implemented NSPI, so any MAPI client that wanted to talk to them instead could do so. But many MAPI clients out there still wanted to talk to the Exchange server, so Exchange still registered to listen for NSPI requests. Instead of handling the requests though, Exchange would look up an appropriate GC and send the packets over there. Any response it got back from the GC would be sent straight on to the client that originated the call. The process that handles this is called DSProxy. This whole process is documented at the bottom of this article: How MAPI Clients Access Active Directory.
Of course, this double hop of every directory lookup isn't terribly efficient. It would be better if the client could ask the Exchange server what the best GC is, and then send it's requests there instead. So we developed the Referral (RFR) Interface. A client that wishes to avoid using DSProxy can use the RFR interface to get the name of a GC, then connect directly to that GC and avoid all the extra network traffic and load on the Exchange server. As How MAPI Clients Access Active Directory notes, Outlook 2000 was the first client to implement this logic. At the time that article was written, Exchange's MAPI couldn't ask for a referral, so anyone using Exchange's MAPI always used DSProxy.
Then along came Jason's article: Getting Exchange MAPI's GC-Reconnect stuff to work. Jason found that we had ported the RFR logic into Exchange's MAPI, and that it was possible to turn it on in a brand new profile. All you had to do was set a couple extra properties before logging on. If Jason's properties are set, the Exchange Address Book Provider will ask for a referral, and assuming it gets one, will always talk directly to the GC.
Recently, we found that Jason's settings weren't working when connecting to an Exchange 2007 server. Due to some changes we made in Exchange 2007, getting Exchange's implementation of MAPI to request a referral required setting a few more properties.
Exchange's implementation of the NSPI proxy in DSProxy is somewhat simplestic. It literally reads packets from one socket and writes them to the other. A side effect of this simple implementation is a number of error scenarios don't get handled well. Particularly if the GC being proxied to goes down - DSProxy doesn't always send the error back to the client. So we can end up with a client that's waiting on the server to respond, and a server that's convinced the connection has been torn down. This particular hang can only happen if the MAPI client isn't using referral, which is why it's so important to ensure referral is working.
I've updated the sequence of steps performed in requesting a referral, assuming the RFR settings have been made:
- We read in various settings from the pbGlobalProfileSectionGuid profile section, including PR_PROFILE_HOME_SERVER, which is the name of the Exchange server.
- We look for the "DS Server" key - if we find it, we bind to it with NSPI. If we fail to bind here, then we fail overall (hence the cautions against ever setting this key).
- Next, we read in various settings from the MUIDEMSAB profile section, including PR_PROFILE_HOME_SERVER, which, if set, should be the name of a directory server.
- If PR_PROFILE_HOME_SERVER was set on the MUIDEMSAB section, we bind to it with NSPI. If we succeed, we're done. If we fail, we continue to the next step.
- We ask the Exchange server for a referral. If that succeeds, and we can bind to the referred server, we write the referred server name to PR_PROFILE_HOME_SERVER in the MUIDEMSAB section, and we're done.
- If the referral request fails, we try to bind directly to the Exchange server. The Exchange server will proxy any of our address book requests on to a GC.
- If binding directly to the Exchange server fails, we try to ask for a referral one more time. If that fails, we fail overall.
Always set the following:
In the MUIDEMSAB profile section (dca740c8c042101ab4b908002b2fe182):
In the global profile section (13DBB0C8AA05101A9BB000AA002FC45A):
Set the following if your application connects to Exchange 2007 and does not run locally on the Exchange 2007 server:
In the global profile section (13DBB0C8AA05101A9BB000AA002FC45A):
If your application connects to Exchange 2007 and runs locally on the Exchange 2007 server, then requesting referral is not possible. In fact, setting the Exchange 2007 referral settings in this scenario will cause logon to fail.
These settings must be made before you call MAPILogonEx. Setting them after then would obviously be too late. You can check if you got a referral by reading PR_PROFILE_HOME_SERVER from the MUIDEMSAB profile section after you do your logon. If it's set to one of your GCs, then referral is working.
Some time last year, Jason's blog on referral and reconnect got converted into a KB article. Part of that article included the sample code we were using to test this, called GCReconnect. I've polished this code up to demonstrate the new profile settings and added switches to allow testing. I've posted the updated sample here.
When you build and run the sample, this is what you'll see:
Usage: GCReconnect [-?] -m mailbox -s server [-e] [-x] [-n name] [-l] [-r] [-w]
If you give just a mailbox and server name, GCReconnect will build a profile, connect to the mailbox, and look up your user's name in the directory. The -e and -x switches turn on referral and reconnect, while the -l and -r switches allow you to tell the app to keep hitting the server so you can test scenarios like failover when you turn off a GC. With the -n switch, you can give a different name for the directory lookup test. The last switch, -w, just makes the app wait so you can attach a debugger, if you're in to that sort of thing.
Troubleshooting and Caveats
- As noted above, you can read PR_PROFILE_HOME_SERVER from the MUIDEMSAB profile section to see if it's set to a GC.
- When a client using Exchange's MAPI is installed on an Exchange 2007 server, it cannot get a referral from that server. It could, however, get a referral from another Exchange server in the organization. [Update] I found a workaround for this.
- During profile configuration (ConfigureMsgService), the Exchange provider will do an NSPIBind to the Exchange server to lookup the mailbox name. This single bind and lookup is unavoidable. This is a good argument for creating your profile once and reusing it.
- The NSPI and RFR interfaces have built in event log tracing on the Exchange server:
1 – Open Regedit on the Exchange Server
2 – Navigate to HKLMSystemCurrentControlSetServicesMSExchangeSADiagnostics
3 – Locate the “11 NSPI Proxy” and “12 RFR Interface” values and set each of them to 5
- If there's no GC to connect to, logging on to a MAPI profile that has an Exchange provider in it just isn't going to work. Without referral, the client might hang. With referral, it might take a while to figure out there really is no GC, but it will eventually error out.
- If you want to take on locating the GC yourself and avoid referral altogether, you could set the DS Server key every time you log on. If you do this though, it will be your responsibility to look up a new GC if the one you set goes down.