Table of Contents
This is a tutorial on how to use OpenSER with Asterisk functioning as a voicemail server.
Create the Database Views
First, create the views described in OpenSER to Asterisk Realtime MySQL Views. This allows Asterisk to use the OpenSER's user table, so you don't have to manually replicate them.
Modify Asterisk Codebase
Next, you will need to modify the asterisk code base. This is unfortunate, but otherwise you will be unable to update the voicemail passwords.
In asterisk-1.2.0/apps/app_voicemail.c, struct ast_vm_user should have a uniqueid size of 128. Like this:
struct ast_vm_user { char context[AST_MAX_CONTEXT]; /*!< Voicemail context */ char mailbox[AST_MAX_EXTENSION];/*!< Mailbox id, unique within vm context */ char password[80]; /*!< Secret pin code, numbers only */ char fullname[80]; /*!< Full name, for directory app */ char email[80]; /*!< E-mail address */ char pager[80]; /*!< E-mail address to pager (no attachment) */ char serveremail[80]; /*!< From: Mail address */ char mailcmd[160]; /*!< Configurable mail command */ char language[MAX_LANGUAGE]; /*!< Config: Language setting */ char zonetag[80]; /*!< Time zone */ char callback[80]; char dialout[80]; char uniqueid[128]; /*!< Unique integer identifier */ char exit[80]; unsigned int flags; /*!< VM_ flags */ int saydurationm; int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */ struct ast_vm_user *next; };
Install Asterisk Add-ons
Now, download the latest Asterisk addons package from the website. Compile and install both asterisk packages.
Configure Asterisk Realtime
Now, it is time to specify where Asterisk should look for its realtime data. In /etc/asterisk/extconfig.conf, you will need to have:
[settings] sipusers => mysql,asterisk,sip sippeers => mysql,asterisk,sip voicemail => mysql,asterisk,voicemail
Now you will need to specify how to connect asterisk. In /etc/asterisk/res_mysql.conf insert:
[general] dbhost = localhost dbname = asterisk dbuser = asterisk dbpass = asterisk dbport = 3306
This assumes that the user asterisk@localhost with password asterisk will be allowed to access the asterisk database. The code for adding this user is:
GRANT ALL ON asterisk.* to asterisk@localhost IDENTIFIED BY 'asterisk';
Configure Asterisk SIP
Allow sip calls in your sip.conf file (/etc/asterisk/sip.conf):
[general] context=default ; Default context for incoming calls allowguest=yes ; Allow or reject guest calls (default is yes, this can also be set to 'osp' bindport=5060 ; UDP Port to bind to (SIP standard port is 5060) bindaddr=0.0.0.0 ; IP address to bind to (0.0.0.0 binds to all) srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;domain=voicemail ; Set default domain for this host ;domain=mydomain.tld,mydomain-incoming ;domain=127.0.0.1 ; Add IP address as local domain ;allowexternalinvites=no ; Disable INVITE and REFER to non-local domains autodomain=yes ; Turn this on to have Asterisk add local host pedantic=yes ; Enable slow, pedantic checking for Pingtel ;tos=184 ; Set IP QoS to either a keyword or numeric val ;tos=lowdelay ; lowdelay,throughput,reliability,mincost,none ;maxexpiry=3600 ; Max length of incoming registration we allow ;defaultexpiry=120 ; Default length of incoming/outoing registration ;notifymimetype=text/plain ; Allow overriding of mime type in MWI NOTIFY checkmwi=10 ; Default time between mailbox checks for peers vmexten=default ; dialplan extension to reach mailbox sets the ;videosupport=yes ; Turn on support for SIP video ;recordhistory=yes ; Record SIP history by default disallow=all ; First disallow all codecs allow=ulaw ; Allow codecs in order of preference language=en ; Default language setting for all users/peers ;rtpholdtimeout=300 ; Terminate call if 300 seconds of no RTP activity ;trustrpid = no ; If Remote-Party-ID should be trusted ;sendrpid = yes ; If Remote-Party-ID should be sent useragent=Asterisk PBX ; Allows you to change the user agent string ;dtmfmode = rfc2833 ; Set default dtmfmode for sending DTMF. Default: rfc2833 ; Other options: ; info : SIP INFO messages ; inband : Inband audio (requires 64 kbit codec -alaw, ulaw) ; auto : Use rfc2833 if offered, inband otherwise [openser] type=friend context=default host=localhost insecure=very
Make sure to substitute the correct host for openser where it says host=localhost, or, if you are running OpenSER and Asterisk on the same host, change the port setting so that these servers will not conflict.
Configure Voicemail
Edit your voicemail.conf to how you like it. You should remove all default user and other user accounts listed there.
Configure Extensions
This is the equivalent of OpenSER's route{}. Here is a sample extensions file to send incoming calls to voicemail:
[general] static=yes writeprotect=no autofallthrough=yes clearglobalvars=no priorityjumping=no [globals] CONSOLE=Console/dsp ; Console interface for demo [default] exten => _XXXXXXXXXXX,1,Voicemail(su${EXTEN}) exten => _XXXXXXXXXXX,2,Hangup exten => a,1,VoicemailMain(${EXTEN}) exten => a,2,Playback(Goodbye) exten => a,3,Hangup exten => *86,1,VoicemailMain(${CALLERIDNUM}) exten => *86,2,Hangup
Set OpenSER to Route Calls to Voicemail
In your openser.cfg, you will need to have it route calls to asterisk. This is usually done with some kind of failure route. Of some importance is making sure that calls timeout sooner than the default, 200 seconds. Here is a basic route. This is very new, and seems to work, but more testing is needed. You mileage may vary.
#tm timeout for voicemail params modparam("tm", "fr_timer", 3) modparam("tm", "fr_inv_timer", 35) modparam("tm", "noisy_ctimer", 1) # ------------------------- request routing logic ------------------- # main routing logic #Routing Script route { #check for old messages: could mean a problem with the DNS entries or some other loop-causer... if (!mf_process_maxfwd_header("10")) { xlog("L_WARN", "WARNING: Too many hops\n"); sl_send_reply("483", "Too many hops, forward count exceeded limit\n"); return; }; #check for extremely large messages; we don't need a sip dos attack if (msg:len >= 2048) { xlog("L_WARN", "WARNING: Message too large, >= 2048 bytes\n"); sl_send_reply("513", "Message too large, exceeded limit\n"); return; }; #record everything besides registers and acks if(method!="REGISTER" && method!="ACK") { setflag(1); }; #do not send to voicemail if BYE or CANCEL #is used to end call before user pickup or timeout if(method=="CANCEL" || method=="BYE") { setflag(10); }; #grant route if route headers already present if (loose_route()) { route(1); return; }; #Always require authentication, which could result in a PSTN, ie $$$ if (method=="REGISTER") { if(!www_authorize("domain.net", "subscriber")) { www_challenge("domain.net", "0"); return; } else { if (!check_to()) { sl_send_reply("401", "Unauthorized"); return; }; #Save into user database, used below when checking if user is available xlog("L_INFO", "REGISTER: User Authenticated Correctly\n"); save("location"); return; }; }; if (method=="INVITE") { if(uri=~"sip:\*86@.*") { #authorize if a call is going to PSTN if(!proxy_authorize("domain.net", "subscriber")) { proxy_challenge("domain.net", "0"); return; }; xlog("L_INFO", "CALL: Call to check voicemail\n"); rewritehostport("vmail.domain.net:5060"); } else { if (does_uri_exist()) { #Call is to sip client, so do nothing but route xlog("L_INFO", "CALL: Sip client\n"); if (!lookup("location")) { sl_send_reply("404", "Not Found"); log(1, "ERROR: User Not Found\n"); return; }; } else { #authorize if a call is going to PSTN if(!proxy_authorize("domain.net", "subscriber")) { proxy_challenge("domain.net", "0"); return; }; #Call destination is PSTN, so send it to the gateway (Net.com) xlog("L_INFO", "CALL: PSTN gateway\n"); rewritehostport("voip_gw.domain.net:5060"); }; }; #Make sure that all subsequent requests go through us; record_route(); } else { if (does_uri_exist()) { #Call is to sip client, so do nothing but route xlog("L_INFO", "CALL: Sip client\n"); if (!lookup("location")) { sl_send_reply("404", "Not Found"); log(1, "ERROR: User Not Found\n"); return; }; } else { #Call destination is PSTN, so send it to the gateway (Net.com) xlog("L_INFO", "CALL: PSTN gateway\n"); rewritehostport("voip_gw.domain.net:5060"); }; record_route(); }; #ALL PROCESSING IS DONE, SO ROUTE route(1); } route[1] { #send the call outward if(method=="INVITE" && !isflagset(10)) { t_on_failure("2"); }; if (!t_relay()) { xlog("L_WARN", "ERROR: t_relay failed"); sl_reply_error(); }; } failure_route[2] { if(!t_was_cancelled()) { revert_uri(); rewritehostport("vmail.domain.net:5060"); append_branch(); #PREVENT SOME CRAZY VOICEMAIL LOOP xlog("L_INFO", "INFO: CALL TO VOICEMAIL"); setflag(10); route(1); } }
You can contact me at mikebwilliams@gmail.com
Good Luck!