Wednesday, November 10, 2010

Asterisk and SIP 911/E911 Support

The Plan:
In our company we have 4 locations, and we have to provide VoIP/SIP E911 support to 3 of them; the 4th is in the Philippines and there is no regional 911 type service there.  I'm sharing what/how I implemented E911 because it may be useful.

The Theory:
First off, for each office we had our ITSP configure a DID for each location with our address and company name.  Then, if somebody from that office calls 911 we must send that DID as the callerid for the call so the 911 responder gets the right address.  To do this, we have different extension ranges for each office so I filtered based on extension.  A 10xx series extension is in San Mateo, 11xx in Makati, 15xx in Redmond, 16xx  in Redmond, and 18xx in Lacey. If you wanted you could probably use another way to decide which location a caller was coming from, maybe look at the SIP Client's IP address, but this method worked for us so we used it.

Additionally, we want to alert some of the people in our office if 911 is ever dialed so we know what is going on.  The receptionist used to get calls saying "we just got a 911 call from you" and had no idea what was going on.   So when anybody dials 911 an email is sent to HR, myself, and the receptionist saying who dialed 911 from what phone, what DID was sent to tell the 911 center where to call, etc.

The final thing we did was added a handy feature that sends the call to the receptionist so they know what is going on.  This has been a problem when somebody in another office called 911 and nobody there was responding to messages, we had no idea what was going on.  Now the receptionist will be able to know what is going on in the event of a 911 call.

The Code:
[from-staff]
exten => 911,1,GotoIf($["${CDR(SRC):0:2}"="10"]?sanmateo911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="11"]?makati911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="15"]?redmond911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="16"]?redmond911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="18"]?lacey911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="19"]?lacey911)
exten => 911,n(makati911),Playback(ss-noservice)
exten => 911,n,Hangup()
exten => 911,n(sanmateo911),System(/etc/asterisk/scripts/911-alert.pl "${CDR(SRC)}" "6505551212" "San Mateo")
exten => 911,n,Set(SPYGROUP=E911)
exten => 911,n,Set(CALLERID(all)=6505551212)
exten => 911,n,Dial(SIP/911@bandwidth-primary)
exten => 911,n,Hangup()
exten => 911,n(redmond911),System(/etc/asterisk/scripts/911-alert.pl "${CDR(SRC)}" "4255551212" "Redmond")
exten => 911,n,Set(SPYGROUP=E911)
exten => 911,n,Set(CALLERID(all)=4255551212)
exten => 911,n,Dial(SIP/911@bandwidth-primary)
exten => 911,n,Hangup()
exten => 911,n(lacey911),System(/etc/asterisk/scripts/911-alert.pl "${CDR(SRC)}" "2535551212" "Lacey")
exten => 911,n,Set(SPYGROUP=E911)
exten => 911,n,Set(CALLERID(all)=2535551212)
exten => 911,n,Dial(SIP/911@bandwidth-primary)
exten => 911,n,Hangup()

;the context that the call will be sent to to listen to the 911 call
[campon911]
exten => _1XXX,1,ChanSpy(SIP/${EXTEN},b)

And you can get the source for 911-alert.pl here, please note I added .txt to the extension so the server wouldn't try and parse it.

Testing:
After you implement your 911 code it is very important that you make a test phone call from each location, tell the responder that the call is a non emergency test call and you would like to verify the address and phone number they received.  This way you know what you've done works properly before something that really needs 911 happens.

Tuesday, November 9, 2010

Using PHP + LDAP to Configure Office Communications Server Users

We have automated the vast majority of the processes in our company, and having our users automatically updated saves a lot of time and missed updates.  Because this is more of a telephony blog I'm mostly covering how this relates to configuring users for Microsoft Office Communications Server, but I use this same code to update user titles, managers, locations and quite a few other settings.

First, you need PHP and LDAP set up and working.  If you do, the steps I've come up with are very simple and straightforward to follow.  If not, i'd recommend you get it installed and loaded up.  There is a good tutorial available here for the basics of what i'm doing.

Microsoft Office Communications Server 2007 R2 Settings:
  1. First I set up a user with the options I wanted using the Office Communications Server User management tools. 
    1.  For Asterisk integration you want to enable Enterprise Voice (but not PBX Integration). 
    2. Set the Line URI to tel:+ and an extension.  Mine is 2593 in OCS.  
    3. You may notice I have ext=1593 added.  This is to add support for the new teleconferencing added to OCS R2; if I dial in to an OCS R2 conference from my desk phone the call will come from 1593, and OCS will know (by looking at ext=) that I am Andrew Parisio. This bypasses the need to authenticate myself from my own phone.
  2. The finished product:
  3. Now to see what this looks like in LDAP I opened adsiedit, navigated to my users properties, and found the settings that pertain to OCS, this is what I found:
  4. Now we've found the settings, using this document you can find what optionflags value you want, or you can set the flags on a user and see what it ends up as in adsiedit, either way works.  In order to get the telephony settings set up like they are above, you need to set OptionFlags to 448.  If you set it to 449 you will enable Public IM Connectivity on the Other Options tab.
  5. Please note that you need to get the msRTCSIP-PrimaryHomeServer value, and use it in place of mine.  
  6. These are the settings I ended up with that are necessary for OCS & Telephony to work (and how I set them in my PHP script):
    1. $datam['msrtcsip-userenabled'] = "TRUE";
    2. $datam['msrtcsip-optionflags'] = "449";
    3. $datam['msrtcsip-primaryhomeserver'] = "CN=LC Services,CN=Microsoft,CN=ocs,CN=Pools,CN=RTC Service,CN=Microsoft,CN=System,DC=mydomain,DC=com";
    4. $datam['msrtcsip-primaryuseraddress'] = "sip:andrewp@mydomain.com";
    5. $datam['msrtcsip-line'] = "tel:+2593;ext=1593";
  7. If you have a user you would like to configure for OCS but aren't worried about the Telephony integration you can use these settings instead:
    1. $datam['msrtcsip-userenabled'] = "TRUE";
    2. $datam['msrtcsip-optionflags'] = "321";
    3. $datam['msrtcsip-primaryhomeserver'] = "CN=LC Services,CN=Microsoft,CN=ocs,CN=Pools,CN=RTC Service,CN=Microsoft,CN=System,DC=mydomain,DC=com";
    4. $datam['msrtcsip-primaryuseraddress'] = "sip:andrewp@mydomain.com";
  8. Once you have the settings, you need to get them in to a script that will allow you to run them easily.  I've stripped down the script I use in my office to just the OCS settings I've talked about above.  If you would like to use this as a base for updating all of the information about users in your company I have some examples at the bottom of my sample code for how to set some other information.

Generic LDAP Entry information and source code is available here:
The example source for what I use is available here, please note that I've renamed it to .inc because my server would parse it if it were named .php

Monday, November 8, 2010

Asterisk 1.6 and Microsoft Office Communications Server 2007 R2

In our office we have integrated Asterisk with Microsoft OCS 2007 R2 to provide our staff members with the ability to seamlessly make and receive phone calls from their desk phone or their computer (including from home not on VPN).  Our current software stack looks like this:


We originally started this project in July of 2009 using Asterisk 1.6.0, and OCS 2007.  At the time there were bugs in several releases of Asterisk which broke TCP SIP compatibility that made our deployment take longer than I had originally anticipated.  Considering this I would recommend sticking to the latest builds of 1.6.0 or move up to 1.6.1/1.6.2, additionally we upgraded from OCS 2007 to R2 earlier this year.   I originally got help from gclark and his blog post here which I would recommend as a good starting point for anybody interested in */OCS integration.


What I'm going to post about over the next few weeks:
  1. Basic integration steps (I'll skip some of the stuff that's already been covered in gclark's blog)
  2. Dialplan I've used to integrate Asterisk & OCS
  3. Using LDAP to auto configure your users for OCS (Posted 11/9/2010)
  4. OCS R2 teleconferencing
  5. Problems I have come across
  6. Realtime Asterisk
  7. anything else I can think of

Asterisk, PHP, and Exchange Web Services for Contact and Calendar Integration

A couple days ago I had an idea that it would be useful to access my Outlook/Exchange contact list on my desk phone in my office and decided to pursue it.  We use an Asterisk 1.6.1 based IP PBX, with Aastra 6757i Handsets which have XML browsers built in to them, so all I needed to figure out was how to access the contact list in exchange. With a quick Google search I discovered you can do with the Microsoft Exchange Web Services (EWS) Platform. I ended up using a solution combining PHP, some help from a blog post here to get PHP + EWS working, and a fair bit of trial and error.

What I learned:
  1. My EWS platform was broken
  2. My EWS platform permissions were improperly configured
  3. The SOAP calls that get generated do not provide a method to do exchange impersonation

What I did:
  1. Fixed my EWS Platform
  2. Found the permissions settings (NTLM authentication was disabled), and fixing that fixed my SOAP calls
  3. I wrote a patch to the SOAP classes provided by Eric on his blog to allow impersonation.  It's a hacky fix but it's functional, if somebody has a better way of doing this please let me know!

How did I do it?
  1. I found my EWS platform was broken due to a mis-configured Exchange 2007 server.  I was getting close to decommissioning it anyways so I did that ahead of schedule.  After removing that server and recreating my EWS virtual directory everything started working properly and I was able to move forward.  The instructions available here explain how to set a new virtual directory.  I used the like-named delete command to remove the old directory first.
  2. The permissions on my EWS folder in IIS were not configured for NTLM, and the classes provided by Eric were specifying NTLM encryption by name.  I noticed this line curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM); and when i changed it to AUTH_BASIC everything functioned properly.  Going in to IIS Authentication on the EWS folder in IIS I added NTLM as a provider for Windows Authentication
  3. Once I had the permissions ironed out I was able to make progress with the SOAP calls themselves.  Following a few other tutorials including Eric's I pieced together my soap.inc.php library that I'm now using.  It took me quite a bit of time to get everything working the way I wanted, here's what i did.
  4. In order to provide accessibility to every user in my company I needed to use exchange impersonation so I could have a service account access every persons mailbox.  Using the Exchange Impersonation instructions available here on MSDN I configured my user account.  On my exchange server I ran the following command:
    New-ManagementRoleAssignment –Name:impersonationAssignmentName –Role:ApplicationImpersonation –User:serviceAccount
  5. Having created an account and granted it appropriate permissions I was able to tweak soap.inc to support Impersonation. At line 94 of soap.inc I wrote in this custom SIP header and inserted it in the SOAP request. For the impersonation request itself the Microsoft WSDL provides support for using UPN, SID, or Primary SMTP Address.  I chose SMTP address:
  6. <SOAP-ENV:Header><ns1:ExchangeImpersonation> <ns1:ConnectingSID> <ns1:PrimarySmtpAddress>othermailbox@csgchannels.com</ns1:PrimarySmtpAddress> </ns1:ConnectingSID> </ns1:ExchangeImpersonation> </SOAP-ENV:Header>
  7. Once I had my XML Response I needed to parse the results.  I wrote function getExchangeContacts to generate the SOAP request and parse the SOAP response, returning an associative array of entries containing every contact in the users contact list, along with each phone number.  Then using the XML generation libraries provided by Aastra I turned the array of contact info in to an XML formatted document for the phone to display.  What I ended up with is this, pardon the low quality Camera Phone pictures (which I doctored to remove last names and phone numbers):


You may view my soap.inc file here

This is an abridged version of what I did and doesn't include many of the intermediary steps where I was using trial and error to find issues I was having.  If you are having troubles with PHP + EWS I may have more insight than is included here, so feel free to contact me.