Click here to Skip to main content
Click here to Skip to main content

How to build a simple SIP PBX in C# extended with dial plan functionality

, 29 Jul 2014
Rate this:
Please Sign up or sign in to vote.
It demonstrates how to develop a fully-functional SIP PBX in the most simplest way and explains how to create such useful VoIP features as dial plan.

Introduction, background information

PBX is the short term for Private Branch eXchange. It is a system that connects telephone extensions of a company to outside public telephone network as well as to mobile networks. Today’s software-based IP PBXs can replace the traditional hardware business phone systems. PBXs work with VoIP technology, that is why an IP PBX provides audio/video calling and instant messaging communication through the TCP/IP protocol stack for its internal network and interconnects its internal network with the PSTN (Public Switched Telephone Network) for telephony communication.

As VoIP technology is evolving day by day, it is quite easy to boost your PBX with some additional functionalities, as well as dial plan that can be very useful in those communication networks where usually there are numerous simultaneous calls. A dial plan is the automated system in the server that manages the internal and external calls, the call forwarding, call hold and restrictions.

Due to these facts, I have implemented my own phone system based on SIP (Session Initiation Protocol) and I decided to share my project. Below I am going to show how to build a PBX then create some useful features to extend your communication system. (I especially recommend this article for beginners, since I have tried to explain the whole project step-by-step.)

Basic requirements for PBX development

For this development, as the program code has been written in C# programming language, you need an IDE (Integrated Development Environment) that supports C#. I prefer Microsoft Visual Studio. .NET Framework installed on your PC is also needed.

To define the default behaviour of the PBX I used the background support of Ozeki VoIP SIP SDK. This way my project requires Ozeki SDK installed on your PC as well.

For testing your phone system you will also need at least two IP telefones. For this purpose I used two softphones (X-Lite).

Creating a new Microsoft Visual Studio project

  1. After opening Visual Studio you need to create a new project by clicking on File then New project.
  2. As this PBX system will be a simple console application, in the next window you need to select the Visual C# Console Application option.
  3. After you have specified a name for your new project (such as MyPBX), click on the OK button.
  4. To achieve and use the PBX components of the SDK you need to add them to your references. (For this, you need to right-click on References than select the Add references option. After this, browse the SDK.dll file that can be found where the SDK has been installed to. Select the .dll and click on the OK button.)

Writing the code

Now you need to make a new class for your PBX. (Right-click on the project name, select add then choose the Class option. Provide a name for it – such as MyPBX – then click on the OK button.)

To use the SDK components and methods you need to add some using lines to the code:

using Ozeki.VoIP.PBX;
using Ozeki.VoIP.PBX.Extensions;
using Ozeki.VoIP.PBX.PhoneCalls;
using Ozeki.VoIP.SIP;
using Ozeki.VoIP.Network;

Code example 1: New using lines

When creating a PBX, your PBX class needs to be derived from the PBXBase class provided by the SDK. Make a string type localAddress variable and then the constructor. It has three parameters: localAddress, minPortRange, maxPortRange. (Your PC may has more network addresses. The localAddress is that address where the PBX listens the connecting clients. Besides the IP address, your PC has several other ports since each application use another port. You need to set the port range, indicated by the first paramerter as the minPortRange and the maximum parameter maxPortRange. This is the port's interval.) The localAddress will be equal to the parameter localAddress. After that you need to put two lines to the console : PBX Starting…, What is our local address. Now you have a fully functional PBX that is able to register any SIP extensions and can establish phone lines in between them (Code example 2).

Now you need to create an OnStart method. First write the ’PBX started’ line to the console, then set up the listening port (check the following code example). The parameters: the localAddress, the port number and the type of the transport (UDP). Write a new line: Listened port number. Call the base.OnStart method. Finally, create the OnRegisterReceived method that has 3 parameters (the extension, SIPaddress, expires parameters) and the OnUnregisterReceived method that has only 1 parameters (the extension). Please chect the Code example 2.

namespace myPBX
{
    class MyPBX : PBXBase
    {
        string localAddress;
        
        public MyPBX(string localAddress, int minPortRange, int maxPortRange) : base(minPortRange, maxPortRange){
            this.localAddress = localAssress;
            Console.WriteLine("PBX Starting...");
            Console.WriteLine("Local address: " + localAddress);
        } 
        // Now you have a fully functional PBX that is able to register any SIP extensions and can establish phone lines in between them. 
        // If a registered SIP endpoint calls an other SIP endpoint by using the SIP account information, the communication line will be established between them. 
        // This is the default behaviour of a simple SIP PBX.
        // You can extend your PBX functionality with authentication.
        // By default, the OnRegisterReceived method allows any SIP account registrations to the PBX.
        // If you want to determine which extensions you will register and which you will not, just override the OnRegisterReceived method. 
        protected override void OnStart()
        { 
            Console.WriteLine("PBX started.");
            SetListenPort(localAddress,5060,TransportType.Udp);
            
            Console.WriteLine("Listened port: 5060(UDP)");
            base.OnStart();
        } 
        
        protected override RegisterResult OnRegisterReceived(ISIPExtension extension, SIPAddress from, int expires)
        {
            Console.WriteLine("Register received from: " + extension.Account.Username);
            return base.OnRegisterReceived(extension, from, expires);
        }
        
        protected override void OnUnregisterReceived(ISIPExtension extension)
        {
            Console.WriteLine("Unregister received from: " + extension.Account.UserName);
            base.OnUnregisterReceived(extension);
        }
    }
} 

Code example 2: PBX class definition and the OnStart method

Now let’s make the main method for your PBX. For this, first you need to add a new using line to the code:

using Ozeki.Network;

Code example 3: One more new using line

Let’s see how to code the main method:

namespace myPBX
{ 
    class Program
    { 
        static void Main(string[] args)
        {
            var myPBX = new MyPBX(NetworkAddressHelper.GetLocalIP().ToString(), 20000, 20500);
            
            myPBX.Start();
            
            Console.ReadLine();
            myPBX.Stop();
        } 
    } 
} 

Code example 4: The main method

Let’s run the application. If it works, your PBX is ready to accept SIP account registrations then make and receive phone calls using the registered SIP extensions.

You can test SIP registration with your softphone. (You can use for instance X-Lite, or the demo softphone application that has been installed automatically with the SDK.) In case of using any softphone, after providing the required SIP account details, the register line will appear in your console application if the registration was successful (Figure 1):

Figure 1: Successful SIP account registration

To make a test call, you need to register two SIP accounts: Extension ’A’ and Extension ’B’. Having done the registration, you need to dial the phone number of Extension ’B’ by using Extension ’A’. The called party (Extension ’B’) will be ringing. After Extension ’B’ has accepted the call, the phone line is established between them and ready to make conversation.

Coding a dial plan

Below I intended to show how to define what should happen when somebody calls a phone number that is in DND status (Do Not Disturb). To create a dial plan first you need to make a new class. (Right-click on the project name, select add then choose the Class option. Provide a name for it – such as MyDialplanProvider – then click on the OK button.)

Add some using lines to the class:

using Ozeki.VoIP.PBX.Dialplan; 
using Ozeki.VoIP.PBX.PhoneCalls;
using Ozeki.VoIP.PBX.Services; 

Code example 5: Additional using lines required

Below you can see how to implement the IDialplanProvider interface. Make a UserInfoContainer, an IExtensionContainer and a List type variables. The List will contain the dndExtensions (dnd refers to Do Not Disturb). In the constructor you can make the basic settings, then add the IDialplanProvider interface members (the Name, and the GetDestination functions). (IDialplanProvider can be used to control the destination of call forwarding. GetDestination defines where the call will be forwarded to, and the routeParams parameter contains such information related to the call as the called number, caller's phone number, etc.) After this, you need to create the dialedNumber and callerPhoneNumber variables and define some conditions according to the following code example:

namespace myPBX
{
    class MyDialplanProvider : IDialplanProvider
    {
        UserInfoContainer userInfoContainer;
        IExtensionContainer extensionContainer;
        List <string> dndExtensions;
        
        public MyDialplanProvider(UserInfoContainer userInfoContainer, IExtensionContainer extensionContainer)
        {
            dndExtensions = new List<string>();
            this.extensionContainer = extensionContainer;
            this.userInfoContainer = userInfoContainer;
        }
        
        public string Name
        {
            get { return "CustomDialplan"; }
        }
        
        public Destination GetDestination(RouteInfo routeParams)
        {
            var dialedNumber = routeParams.Destination.DialedNumber.UserName;
            var callerPhoneNumber = routeParams.CallerInfo.Owner.Account.UserName;
 
            // If the dialed number is &rsquo;*90&rsquo; and the dnd list does not contain the callerPhoneNumber then add this phone number to the list and finish the call:
            if(dialedNumber == "*90" && !dndExtension.Contains(callerPhoneNumber))
            {
                Console.WriteLine("Extension {0} added to DND list", routeParams.CallerInfo.Owner);
                <br />                dndExtensions.Add(callerPhoneNumber);
                return null;
            }
// If the dialed number is &rsquo;*91&rsquo; a then remove this phone number from the list and finish the call.
            if(dialedNumber == "*91" && !dndExtension.Contains(callerPhoneNumber))
            {
                Console.WriteLine("Extension {0} removed to DND list", routeParams.CallerInfo.Owner);
                <br />                dndExtensions.Remove(callerPhoneNumber);
                return null;
            }
// If the dialed number is in the list, else you can call the number.
            if(dndExtension.Contains(callerPhoneNumber))
            {
                return null;
            }
            return new Destination(dialedNumber);  
    }
} 

Code example 6: The implementation of the IDialplanProvider interface

Let’s go back to your PBX class and modify the OnStart() method with the callManager and extensionContainer variables and with your DialplanProvider:

protected override void OnStart()
        {
            Console.WriteLine("PBX started.");
            SetListenPort(localAddress,5060,TransportType.Udp);
            var callManager = GetService<ICallManager>();
            var extensionContainer = GetService<IExtensionContainer>();
            callManager.DialplanProvider = new MyDialplanProvider(userInfoContainer, extensionContainer);
            Console.WriteLine("Listened port: 5060(UDP)");
            base.OnStart();
        }

Code example 7: Modifications in your PBX class

With two softphone you can easily test your system. If you dial number *90 from Extension 101, your phone number will be added to the DND list and it will impossible to call this user. But if you dial the *91, the number will be removed from the list and you can call it now (Figure 2).

 

Figure 2: How the dial plan functionality works

 

Creating voicemail functionality

Many companies can not afford to miss any incoming call. However, there are cases when it is inevitable. Voicemail service provides a practical solution for this problem as it allows your callers to leave a voice message for you.

Considering that the voicemail development is a bit wider topic, I though it would be better to describe my additional solution as a separate article. If you are interested in building voicemail feature, please study my tutorial that presents its implementation step-by step:

How to improve your VoIP PBX with voicemail functionality in C#:
http://www.codeproject.com/Articles/770111/How-to-improve-your-VoIP-PBX-with-voicemail-functi

Creating call queue functionality

Another possible solution to avoid any missed calls is call queuing.  By using this feature, your PBX can manage the incoming calls in a queued system: if there are no any available call center agents, the incoming calls will be put at the end of the call queue. When an agent finishes the current call, the first call in the queue will be transferred to that agent.

Given the fact that the implementation of the call queue is a bit wider topic, I though it would be better to explain this improvement as a separate article. If you are interested in developing a virtual call queue extension, please study my step-by-step guide below:

How to develop a call queue in C# for improving your VoIP PBX:
http://www.codeproject.com/Articles/782342/How-to-develop-a-call-queue-in-Csharp-for-improvin

Summary

To sum it up, building your own SIP PBX can be pretty easy and fast if you use previously written components. After studying more SDKs, I found Ozeki’s VoIP SIP SDK really effective. It is a great tool if you want to focus on developing your application instead of worrying about necessary network protocols and technical details. Although, I have extended my PBX with only one additional feature (dial plan), it is possible to build several further functionalities of course. (For example, now I am working on a voicemail solution - in the meantime it has been completed, see above.)

Good luck!

References

Theoretical background:

Download required software:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

W.Walker

United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralMy vote of 2 PinmemberZhivotnoya1-Aug-14 18:44 
QuestionMissing something? PinmemberZhivotnoya1-Aug-14 15:24 
GeneralMy vote of 5 PinprofessionalMember 1030465028-Mar-14 11:25 
SuggestionDemo softphone Pinmemberrobert_anderson10-Mar-14 4:22 
QuestionFormatting PinmemberKenneth Haugland5-Mar-14 1:35 
AnswerRe: Formatting PinmemberW.Walker5-Mar-14 1:54 
GeneralRe: Formatting PinmemberW.Walker5-Mar-14 2:37 
GeneralRe: Formatting PinmemberKenneth Haugland5-Mar-14 2:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 29 Jul 2014
Article Copyright 2014 by W.Walker
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid