core/AmConfig.cpp
37abd537
 /*
  * Copyright (C) 2002-2003 Fhg Fokus
  *
7dcb7e2a
  * This file is part of SEMS, a free SIP media server.
37abd537
  *
7dcb7e2a
  * SEMS is free software; you can redistribute it and/or modify
37abd537
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
7dcb7e2a
  * (at your option) any later version. This program is released under
  * the GPL with the additional exemption that compiling, linking,
  * and/or using OpenSSL is allowed.
37abd537
  *
7dcb7e2a
  * For a license to use the SEMS software under conditions
37abd537
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
7dcb7e2a
  * SEMS is distributed in the hope that it will be useful,
37abd537
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include <sys/types.h>
 #include <sys/socket.h>
c60b508e
 #include <sys/ioctl.h>
37abd537
 #include <netinet/in.h>
 #include <arpa/inet.h>
c60b508e
 #include <net/if.h>
37abd537
 #include <netdb.h>
1d82032c
 #include <ifaddrs.h>
c7a51805
 #include <stdio.h>
1d82032c
 
37abd537
 #include "AmConfig.h"
 #include "sems.h"
 #include "log.h"
 #include "AmConfigReader.h"
a1d2158b
 #include "AmUtils.h"
a7383254
 #include "AmSessionContainer.h"
4822be21
 #include "Am100rel.h"
5de33efe
 #include "sip/transport.h"
a98f124d
 #include "sip/resolver.h"
56a14def
 #include "sip/ip_util.h"
70edcfc7
 #include "sip/sip_timers.h"
0014fab0
 #include "sip/raw_sender.h"
37abd537
 
ccec6fe3
 #include <cctype>
 #include <algorithm>
a76d6bd5
 
c60b508e
 using std::make_pair;
 
37abd537
 string       AmConfig::ConfigurationFile       = CONFIG_FILE;
 string       AmConfig::ModConfigPath           = MOD_CFG_PATH;
 string       AmConfig::PlugInPath              = PLUG_IN_PATH;
2d0d8f2d
 string       AmConfig::LoadPlugins             = "";
82323810
 string       AmConfig::ExcludePlugins          = "";
94350923
 string       AmConfig::ExcludePayloads         = "";
ccec6fe3
 int          AmConfig::LogLevel                = L_INFO;
 bool         AmConfig::LogStderr               = false;
 
6d93ae38
 vector<AmConfig::SIP_interface> AmConfig::SIP_Ifs;
 vector<AmConfig::RTP_interface> AmConfig::RTP_Ifs;
 map<string,unsigned short>      AmConfig::SIP_If_names;
 map<string,unsigned short>      AmConfig::RTP_If_names;
 map<string,unsigned short>      AmConfig::LocalSIPIP2If;
5e2aa29c
 vector<AmConfig::SysIntf>         AmConfig::SysIfs;
c60b508e
 
ccec6fe3
 #ifndef DISABLE_DAEMON_MODE
 bool         AmConfig::DaemonMode              = DEFAULT_DAEMON_MODE;
 string       AmConfig::DaemonPidFile           = DEFAULT_DAEMON_PID_FILE;
 string       AmConfig::DaemonUid               = DEFAULT_DAEMON_UID;
 string       AmConfig::DaemonGid               = DEFAULT_DAEMON_GID;
 #endif
 
cbf59af4
 unsigned int AmConfig::MaxShutdownTime         = DEFAULT_MAX_SHUTDOWN_TIME;
 
ceb2030a
 int          AmConfig::SessionProcessorThreads = NUM_SESSION_PROCESSORS;
bbfe61da
 int          AmConfig::MediaProcessorThreads   = NUM_MEDIA_PROCESSORS;
680cbc50
 int          AmConfig::RTPReceiverThreads      = NUM_RTP_RECEIVERS;
d31ef867
 int          AmConfig::SIPServerThreads        = NUM_SIP_SERVERS;
0e9bc036
 string       AmConfig::OutboundProxy           = "";
b24f51ef
 bool         AmConfig::ForceOutboundProxy      = false;
50727c62
 string       AmConfig::NextHop                 = "";
c3bb4772
 bool         AmConfig::NextHop1stReq           = false;
23c333c7
 bool         AmConfig::ProxyStickyAuth         = false;
3c118eb2
 bool         AmConfig::ForceOutboundIf         = false;
099f6b66
 bool         AmConfig::ForceSymmetricRtp       = false;
1394da66
 bool         AmConfig::SipNATHandling          = false;
0014fab0
 bool         AmConfig::UseRawSockets           = false;
79a44629
 bool         AmConfig::IgnoreNotifyLowerCSeq   = false;
dbbdd8c6
 string       AmConfig::Signature               = "";
c4c926bd
 unsigned int AmConfig::MaxForwards             = MAX_FORWARDS;
3b527c23
 bool	     AmConfig::SingleCodecInOK	       = false;
5eb2a732
 unsigned int AmConfig::DeadRtpTime             = DEAD_RTP_TIME;
019791dd
 bool         AmConfig::IgnoreRTPXHdrs          = false;
a76d6bd5
 string       AmConfig::Application             = "";
 AmConfig::ApplicationSelector AmConfig::AppSelect        = AmConfig::App_SPECIFIED;
c60b508e
 RegexMappingVector AmConfig::AppMapping;
d5ab4c2d
 bool         AmConfig::LogSessions             = false;
64d8ace6
 bool         AmConfig::LogEvents               = false;
79f01591
 int          AmConfig::UnhandledReplyLoglevel  = 0;
019791dd
 
7e312d72
 unsigned int AmConfig::SessionLimit            = 0;
9c0e638f
 unsigned int AmConfig::SessionLimitErrCode     = 503;
7e312d72
 string       AmConfig::SessionLimitErrReason   = "Server overload";
 
33876e9d
 unsigned int AmConfig::OptionsSessionLimit            = 0;
 unsigned int AmConfig::OptionsSessionLimitErrCode     = 503;
 string       AmConfig::OptionsSessionLimitErrReason   = "Server overload";
 
a7383254
 unsigned int AmConfig::CPSLimitErrCode     = 503;
 string       AmConfig::CPSLimitErrReason   = "Server overload";
 
1e561833
 bool         AmConfig::AcceptForkedDialogs     = true;
 
7774944f
 bool         AmConfig::ShutdownMode            = false;
 unsigned int AmConfig::ShutdownModeErrCode     = 503;
 string       AmConfig::ShutdownModeErrReason   = "Server shutting down";
c794c457
   
 string AmConfig::OptionsTranscoderOutStatsHdr; // empty by default
 string AmConfig::OptionsTranscoderInStatsHdr; // empty by default
42e1d05c
 string AmConfig::TranscoderOutStatsHdr; // empty by default
 string AmConfig::TranscoderInStatsHdr; // empty by default
7774944f
 
4822be21
 Am100rel::State AmConfig::rel100 = Am100rel::REL100_SUPPORTED;
6d27feb3
 
a1d2158b
 vector <string> AmConfig::CodecOrder;
37abd537
 
f379c6e3
 Dtmf::InbandDetectorType 
 AmConfig::DefaultDTMFDetector     = Dtmf::SEMSInternal;
665f9c14
 bool AmConfig::IgnoreSIGCHLD      = true;
f300e8ec
 bool AmConfig::IgnoreSIGPIPE      = true;
f379c6e3
 
440323b8
 #ifdef USE_LIBSAMPLERATE
 #ifndef USE_INTERNAL_RESAMPLER
 AmAudio::ResamplingImplementationType AmConfig::ResamplingImplementationType = AmAudio::LIBSAMPLERATE;
 #endif
 #endif
 #ifdef USE_INTERNAL_RESAMPLER
 AmAudio::ResamplingImplementationType AmConfig::ResamplingImplementationType = AmAudio::INTERNAL_RESAMPLER;
 #endif
 #ifndef USE_LIBSAMPLERATE
 #ifndef USE_INTERNAL_RESAMPLER
 AmAudio::ResamplingImplementationType AmConfig::ResamplingImplementationType = AmAudio::UNAVAILABLE;
 #endif
 #endif
 
c60b508e
 static int readInterfaces(AmConfigReader& cfg);
 
 AmConfig::IP_interface::IP_interface()
6d93ae38
   : LocalIP(),
     PublicIP()
 {
 }
 
 AmConfig::SIP_interface::SIP_interface()
   : IP_interface(),
     LocalPort(5060),
     SigSockOpts(0),
     RtpInterface(-1)
 {
 }
 
 AmConfig::RTP_interface::RTP_interface()
   : IP_interface(),
c60b508e
     RtpLowPort(RTP_LOWPORT),
f513f803
     RtpHighPort(RTP_HIGHPORT),
     next_rtp_port(-1)
b36cacb4
 {
 }
 
6d93ae38
 int AmConfig::RTP_interface::getNextRtpPort()
37abd537
 {
c60b508e
     
   int port=0;
 
   next_rtp_port_mut.lock();
   if(next_rtp_port < 0){
     next_rtp_port = RtpLowPort;
7c964b9b
   }
c60b508e
     
   port = next_rtp_port & 0xfffe;
   next_rtp_port += 2;
37abd537
 
c60b508e
   if(next_rtp_port >= RtpHighPort){
     next_rtp_port = RtpLowPort;
7c964b9b
   }
c60b508e
   next_rtp_port_mut.unlock();
     
   return port;
37abd537
 }
 
c60b508e
 
ccec6fe3
 int AmConfig::setLogLevel(const string& level, bool apply)
 {
   int n;
 
   if (sscanf(level.c_str(), "%i", &n) == 1) {
     if (n < L_ERR || n > L_DBG) {
       return 0;
     }
   } else {
     string s(level);
     std::transform(s.begin(), s.end(), s.begin(), ::tolower);
 
     if (s == "error" || s == "err") {
       n = L_ERR;
     } else if (s == "warning" || s == "warn") {
       n = L_WARN;
     } else if (s == "info") {
       n = L_INFO;
     } else if (s=="debug" || s == "dbg") {
       n = L_DBG;
     } else {
       return 0;
     }
   }
 
   LogLevel = n;
   if (apply) {
     log_level = LogLevel;
7c964b9b
   }
   return 1;
37abd537
 }
 
ccec6fe3
 int AmConfig::setLogStderr(const string& s, bool apply)
 {
   if ( strcasecmp(s.c_str(), "yes") == 0 ) {
     LogStderr = true;
   } else if ( strcasecmp(s.c_str(), "no") == 0 ) {
     LogStderr = false;
7c964b9b
   } else {
     return 0;
ccec6fe3
   }
   if (apply) {
     log_stderr = LogStderr;
   }
7c964b9b
   return 1;
ccec6fe3
 }
37abd537
 
ccec6fe3
 #ifndef DISABLE_DAEMON_MODE
 
 int AmConfig::setDaemonMode(const string& fork) {
   if ( strcasecmp(fork.c_str(), "yes") == 0 ) {
     DaemonMode = true;
   } else if ( strcasecmp(fork.c_str(), "no") == 0 ) {
     DaemonMode = false;
7c964b9b
   } else {
     return 0;
ccec6fe3
   }
7c964b9b
   return 1;
37abd537
 }		
 
ccec6fe3
 #endif /* !DISABLE_DAEMON_MODE */
 
ceb2030a
 int AmConfig::setSessionProcessorThreads(const string& th) {
   if(sscanf(th.c_str(),"%u",&SessionProcessorThreads) != 1) {
     return 0;
   }
   return 1;
 }
 
bbfe61da
 int AmConfig::setMediaProcessorThreads(const string& th) {
7c964b9b
   if(sscanf(th.c_str(),"%u",&MediaProcessorThreads) != 1) {
     return 0;
   }
   return 1;
37abd537
 }
 
680cbc50
 int AmConfig::setRTPReceiverThreads(const string& th) {
   if(sscanf(th.c_str(),"%u",&RTPReceiverThreads) != 1) {
     return 0;
   }
   return 1;
 }
 
d31ef867
 int AmConfig::setSIPServerThreads(const string& th){
   if(sscanf(th.c_str(),"%u",&SIPServerThreads) != 1) {
     return 0;
   }
   return 1;
 }
 
 
5eb2a732
 int AmConfig::setDeadRtpTime(const string& drt)
 {
ccec6fe3
   if(sscanf(drt.c_str(),"%u",&DeadRtpTime) != 1) {
5eb2a732
     return 0;
   }
   return 1;
 }
 
37abd537
 int AmConfig::readConfiguration()
 {
ccec6fe3
   DBG("Reading configuration...\n");
3c7c8297
   
7c964b9b
   AmConfigReader cfg;
791132ad
   int            ret=0;
37abd537
 
ccec6fe3
   if(cfg.loadFile(AmConfig::ConfigurationFile.c_str())){
7c964b9b
     ERROR("while loading main configuration file\n");
     return -1;
   }
37abd537
        
7c964b9b
   // take values from global configuration file
   // they will be overwritten by command line args
37abd537
 
12b78127
   // log_level
   if(cfg.hasParameter("loglevel")){
     if(!setLogLevel(cfg.getParameter("loglevel"))){
       ERROR("invalid log level specified\n");
       ret = -1;
     }
   }
 
791132ad
   // stderr 
   if(cfg.hasParameter("stderr")){
     if(!setLogStderr(cfg.getParameter("stderr"), true)){
       ERROR("invalid stderr value specified,"
 	    " valid are only yes or no\n");
       ret = -1;
     }
   }
 
ccec6fe3
 #ifndef DISABLE_SYSLOG_LOG
08f1c065
   if (cfg.hasParameter("syslog_facility")) {
ccec6fe3
     set_syslog_facility(cfg.getParameter("syslog_facility").c_str());
08f1c065
   }
ccec6fe3
 #endif
08f1c065
 
7c964b9b
   // plugin_config_path
ccec6fe3
   if (cfg.hasParameter("plugin_config_path")) {
     ModConfigPath = cfg.getParameter("plugin_config_path",ModConfigPath);
   }
37abd537
 
cd9d3580
   if(!ModConfigPath.empty() && (ModConfigPath[ModConfigPath.length()-1] != '/'))
     ModConfigPath += '/';
37abd537
 
c60b508e
   // Reads IP and port parameters
   if(readInterfaces(cfg) == -1)
     ret = -1;
3c7c8297
   
0e9bc036
   // outbound_proxy
ccec6fe3
   if (cfg.hasParameter("outbound_proxy"))
     OutboundProxy = cfg.getParameter("outbound_proxy");
b24f51ef
 
   // force_outbound_proxy
   if(cfg.hasParameter("force_outbound_proxy")) {
     ForceOutboundProxy = (cfg.getParameter("force_outbound_proxy") == "yes");
   }
23c333c7
 
50727c62
   if(cfg.hasParameter("next_hop")) {
     NextHop = cfg.getParameter("next_hop");
c3bb4772
     NextHop1stReq = (cfg.getParameter("next_hop_1st_req") == "yes");
51a41371
   }
 
23c333c7
   if(cfg.hasParameter("proxy_sticky_auth")) {
     ProxyStickyAuth = (cfg.getParameter("proxy_sticky_auth") == "yes");
   }
c60b508e
 
3c118eb2
   if(cfg.hasParameter("force_outbound_if")) {
     ForceOutboundIf = (cfg.getParameter("force_outbound_if") == "yes");
   }
 
0014fab0
   if(cfg.hasParameter("use_raw_sockets")) {
     UseRawSockets = (cfg.getParameter("use_raw_sockets") == "yes");
     if(UseRawSockets && (raw_sender::init() < 0)) {
       UseRawSockets = false;
     }
   }
 
79a44629
   if(cfg.hasParameter("ignore_notify_lower_cseq")) {
     IgnoreNotifyLowerCSeq = (cfg.getParameter("ignore_notify_lower_cseq") == "yes");
   }
 
099f6b66
   if(cfg.hasParameter("force_symmetric_rtp")) {
     ForceSymmetricRtp = (cfg.getParameter("force_symmetric_rtp") == "yes");
   }
 
1394da66
   if(cfg.hasParameter("sip_nat_handling")) {
     SipNATHandling = (cfg.getParameter("sip_nat_handling") == "yes");
   }
 
c60b508e
   if(cfg.hasParameter("disable_dns_srv")) {
a98f124d
     _resolver::disable_srv = (cfg.getParameter("disable_dns_srv") == "yes");
c60b508e
   }
0e9bc036
   
70edcfc7
 
ee958659
   for (int t = STIMER_A; t < __STIMER_MAX; t++) {
 
     string timer_cfg = string("sip_timer_") + timer_name(t);
     if(cfg.hasParameter(timer_cfg)) {
 
       sip_timers[t] = cfg.getParameterInt(timer_cfg, sip_timers[t]);
0dfe6e5f
       INFO("Set SIP Timer '%s' to %u ms\n", timer_name(t), sip_timers[t]);
70edcfc7
     }
   }
 
   if (cfg.hasParameter("sip_timer_t2")) {
     sip_timer_t2 = cfg.getParameterInt("sip_timer_t2", DEFAULT_T2_TIMER);
0dfe6e5f
     INFO("Set SIP Timer T2 to %u ms\n", sip_timer_t2);
70edcfc7
   }
 
7c964b9b
   // plugin_path
ccec6fe3
   if (cfg.hasParameter("plugin_path"))
     PlugInPath = cfg.getParameter("plugin_path");
dbbdd8c6
 
7c964b9b
   // load_plugins
ccec6fe3
   if (cfg.hasParameter("load_plugins"))
     LoadPlugins = cfg.getParameter("load_plugins");
2d0d8f2d
 
b0079d18
   if (cfg.hasParameter("load_plugins_rtld_global")) {
     vector<string> rtld_global_plugins =
       explode(cfg.getParameter("load_plugins_rtld_global"), ",");
     for (vector<string>::iterator it=
 	   rtld_global_plugins.begin(); it != rtld_global_plugins.end(); it++) {
       AmPlugIn::instance()->set_load_rtld_global(*it);
     }
   }
 
 
82323810
   // exclude_plugins
ccec6fe3
   if (cfg.hasParameter("exclude_plugins"))
     ExcludePlugins = cfg.getParameter("exclude_plugins");
82323810
 
94350923
   // exclude_plugins
304e653b
   if (cfg.hasParameter("exclude_payloads"))
ccec6fe3
     ExcludePayloads = cfg.getParameter("exclude_payloads");
94350923
 
7c964b9b
   // user_agent
   if (cfg.getParameter("use_default_signature")=="yes")
     Signature = DEFAULT_SIGNATURE;
   else 
     Signature = cfg.getParameter("signature");
37abd537
 
c4c926bd
   if (cfg.hasParameter("max_forwards")) {
       unsigned int mf=0;
       if(str2i(cfg.getParameter("max_forwards"), mf)) {
 	  ERROR("invalid max_forwards specified\n");
       }
       else {
 	  MaxForwards = mf;
       }
   }
 
ccec6fe3
   if(cfg.hasParameter("log_sessions"))
     LogSessions = cfg.getParameter("log_sessions")=="yes";
   
   if(cfg.hasParameter("log_events"))
     LogEvents = cfg.getParameter("log_events")=="yes";
d5ab4c2d
 
79f01591
   if (cfg.hasParameter("unhandled_reply_loglevel")) {
     string msglog = cfg.getParameter("unhandled_reply_loglevel");
     if (msglog == "no") UnhandledReplyLoglevel = -1;
     else if (msglog == "error") UnhandledReplyLoglevel = 0;
     else if (msglog == "warn")  UnhandledReplyLoglevel = 1;
     else if (msglog == "info")  UnhandledReplyLoglevel = 2;
     else if (msglog == "debug") UnhandledReplyLoglevel = 3;
     else ERROR("Could not interpret unhandled_reply_loglevel \"%s\"\n",
 	       msglog.c_str());
   }
 
a76d6bd5
   Application  = cfg.getParameter("application");
 
   if (Application == "$(ruri.user)") {
     AppSelect = App_RURIUSER;
   } else if (Application == "$(ruri.param)") {
     AppSelect = App_RURIPARAM;
a2eb9229
   } else if (Application == "$(apphdr)") {
     AppSelect = App_APPHDR;
a76d6bd5
   } else if (Application == "$(mapping)") {
     AppSelect = App_MAPPING;  
     string appcfg_fname = ModConfigPath + "app_mapping.conf"; 
     DBG("Loading application mapping...\n");
c60b508e
     if (!read_regex_mapping(appcfg_fname, "=>", "application mapping",
 			    AppMapping)) {
       ERROR("reading application mapping\n");
791132ad
       ret = -1;
a76d6bd5
     }
   } else {
     AppSelect = App_SPECIFIED;
   }
5911a413
 
ccec6fe3
 #ifndef DISABLE_DAEMON_MODE
 
7c964b9b
   // fork 
   if(cfg.hasParameter("fork")){
ccec6fe3
     if(!setDaemonMode(cfg.getParameter("fork"))){
7c964b9b
       ERROR("invalid fork value specified,"
 	    " valid are only yes or no\n");
791132ad
       ret = -1;
37abd537
     }
7c964b9b
   }
37abd537
 
ccec6fe3
   // daemon (alias for fork)
   if(cfg.hasParameter("daemon")){
     if(!setDaemonMode(cfg.getParameter("daemon"))){
       ERROR("invalid daemon value specified,"
 	    " valid are only yes or no\n");
791132ad
       ret = -1;
ccec6fe3
     }
   }
 
   if(cfg.hasParameter("daemon_uid")){
     DaemonUid = cfg.getParameter("daemon_uid");
   }
 
   if(cfg.hasParameter("daemon_gid")){
     DaemonGid = cfg.getParameter("daemon_gid");
   }
 
 #endif /* !DISABLE_DAEMON_MODE */
 
cbf59af4
   MaxShutdownTime = cfg.getParameterInt("max_shutdown_time",
 					DEFAULT_MAX_SHUTDOWN_TIME);
37abd537
 
ceb2030a
   if(cfg.hasParameter("session_processor_threads")){
 #ifdef SESSION_THREADPOOL
     if(!setSessionProcessorThreads(cfg.getParameter("session_processor_threads"))){
       ERROR("invalid session_processor_threads value specified\n");
791132ad
       ret = -1;
ceb2030a
     }
     if (SessionProcessorThreads<1) {
       ERROR("invalid session_processor_threads value specified."
 	    " need at least one thread\n");
791132ad
       ret = -1;
ceb2030a
     }
 #else
     WARN("session_processor_threads specified in sems.conf,\n");
     WARN("but SEMS is compiled without SESSION_THREADPOOL support.\n");
     WARN("set USE_THREADPOOL in Makefile.defs to enable session thread pool.\n");
     WARN("SEMS will start now, but every call will have its own thread.\n");    
 #endif
   }
 
7c964b9b
   if(cfg.hasParameter("media_processor_threads")){
     if(!setMediaProcessorThreads(cfg.getParameter("media_processor_threads"))){
       ERROR("invalid media_processor_threads value specified");
791132ad
       ret = -1;
37abd537
     }
7c964b9b
   }
a1d2158b
 
680cbc50
   if(cfg.hasParameter("rtp_receiver_threads")){
     if(!setRTPReceiverThreads(cfg.getParameter("rtp_receiver_threads"))){
       ERROR("invalid rtp_receiver_threads value specified");
       ret = -1;
     }
   }
 
9ed85e14
   if(cfg.hasParameter("sip_server_threads")){
     if(!setSIPServerThreads(cfg.getParameter("sip_server_threads"))){
       ERROR("invalid sip_server_threads value specified");
791132ad
       ret = -1;
9ed85e14
     }
   }
 
3b527c23
   // single codec in 200 OK
   if(cfg.hasParameter("single_codec_in_ok")){
     SingleCodecInOK = (cfg.getParameter("single_codec_in_ok") == "yes");
   }
37abd537
 
019791dd
   // single codec in 200 OK
   if(cfg.hasParameter("ignore_rtpxheaders")){
     IgnoreRTPXHdrs = (cfg.getParameter("ignore_rtpxheaders") == "yes");
   }
 
a1d2158b
   // codec_order
   CodecOrder = explode(cfg.getParameter("codec_order"), ",");
5eb2a732
 
   // dead_rtp_time
   if(cfg.hasParameter("dead_rtp_time")){
     if(!setDeadRtpTime(cfg.getParameter("dead_rtp_time"))){
       ERROR("invalid dead_rtp_time value specified");
791132ad
       ret = -1;
5eb2a732
     }
   }
 
f379c6e3
   if(cfg.hasParameter("dtmf_detector")){
     if (cfg.getParameter("dtmf_detector") == "spandsp") {
 #ifndef USE_SPANDSP
       WARN("spandsp support not compiled in.\n");
 #endif
       DefaultDTMFDetector = Dtmf::SpanDSP;
     }
   }
 
7e312d72
   if(cfg.hasParameter("session_limit")){ 
     vector<string> limit = explode(cfg.getParameter("session_limit"), ";");
     if (limit.size() != 3) {
       ERROR("invalid session_limit specified.\n");
     } else {
       if (str2i(limit[0], SessionLimit) || str2i(limit[1], SessionLimitErrCode)) {
 	ERROR("invalid session_limit specified.\n");
       }
       SessionLimitErrReason = limit[2];
     }
   }
 
33876e9d
   if(cfg.hasParameter("options_session_limit")){ 
     vector<string> limit = explode(cfg.getParameter("options_session_limit"), ";");
     if (limit.size() != 3) {
       ERROR("invalid options_session_limit specified.\n");
     } else {
       if (str2i(limit[0], OptionsSessionLimit) || str2i(limit[1], OptionsSessionLimitErrCode)) {
 	ERROR("invalid options_session_limit specified.\n");
       }
       OptionsSessionLimitErrReason = limit[2];
     }
   }
 
a7383254
   if(cfg.hasParameter("cps_limit")){ 
     unsigned int CPSLimit;
     vector<string> limit = explode(cfg.getParameter("cps_limit"), ";");
     if (limit.size() != 3) {
       ERROR("invalid cps_limit specified.\n");
     } else {
       if (str2i(limit[0], CPSLimit) || str2i(limit[1], CPSLimitErrCode)) {
 	ERROR("invalid cps_limit specified.\n");
       }
       CPSLimitErrReason = limit[2];
     }
     AmSessionContainer::instance()->setCPSLimit(CPSLimit);
   }
 
1e561833
   if(cfg.hasParameter("accept_forked_dialogs"))
     AcceptForkedDialogs = !(cfg.getParameter("accept_forked_dialogs") == "no");
 
7774944f
   if(cfg.hasParameter("shutdown_mode_reply")){
     string c_reply = cfg.getParameter("shutdown_mode_reply");    
     size_t spos = c_reply.find(" ");
     if (spos == string::npos || spos == c_reply.length()) {
       ERROR("invalid shutdown_mode_reply specified, expected \"<code> <reason>\","
 	    "e.g. shutdown_mode_reply=\"503 Not At The Moment, Please\".\n");
       ret = -1;
 
     } else {
       if (str2i(c_reply.substr(0, spos), ShutdownModeErrCode)) {
 	ERROR("invalid shutdown_mode_reply specified, expected \"<code> <reason>\","
 	      "e.g. shutdown_mode_reply=\"503 Not At The Moment, Please\".\n");
 	ret = -1;
       }
       ShutdownModeErrReason = c_reply.substr(spos+1);
     }
   }
 
c794c457
   OptionsTranscoderOutStatsHdr = cfg.getParameter("options_transcoder_out_stats_hdr");
   OptionsTranscoderInStatsHdr = cfg.getParameter("options_transcoder_in_stats_hdr");
42e1d05c
   TranscoderOutStatsHdr = cfg.getParameter("transcoder_out_stats_hdr");
   TranscoderInStatsHdr = cfg.getParameter("transcoder_in_stats_hdr");
c794c457
 
6d27feb3
   if (cfg.hasParameter("100rel")) {
     string rel100s = cfg.getParameter("100rel");
     if (rel100s == "disabled" || rel100s == "off") {
4822be21
       rel100 = Am100rel::REL100_DISABLED;
6d27feb3
     } else if (rel100s == "supported") {
4822be21
       rel100 = Am100rel::REL100_SUPPORTED;
6d27feb3
     } else if (rel100s == "require") {
4822be21
       rel100 = Am100rel::REL100_REQUIRE;
6d27feb3
     } else {
6e503d41
       ERROR("unknown setting for '100rel' config option: '%s'.\n",
 	    rel100s.c_str());
791132ad
       ret = -1;
6d27feb3
     }
   }
6e503d41
 
440323b8
   if (cfg.hasParameter("resampling_library")) {
 	string resamplings = cfg.getParameter("resampling_library");
 	if (resamplings == "libsamplerate") {
 	  ResamplingImplementationType = AmAudio::LIBSAMPLERATE;
 	}
   }
 
791132ad
   return ret;
37abd537
 }	
c60b508e
 
6d93ae38
 int AmConfig::insert_SIP_interface(const SIP_interface& intf)
 {
   if(SIP_If_names.find(intf.name) !=
      SIP_If_names.end()) {
 
     if(intf.name != "default") {
       ERROR("duplicated interface name '%s'\n",intf.name.c_str());
       return -1;
     }
 
     unsigned int idx = SIP_If_names[intf.name];
     SIP_Ifs[idx] = intf;
   }
   else {
     SIP_Ifs.push_back(intf);
     unsigned int idx = SIP_Ifs.size()-1;
     SIP_If_names[intf.name] = idx;
 
     if(LocalSIPIP2If.find(intf.LocalIP) == 
        LocalSIPIP2If.end()) {
 
       LocalSIPIP2If.insert(make_pair(intf.LocalIP,idx));
     }
     else {
       map<string,unsigned short>::iterator it = 
 	LocalSIPIP2If.find(intf.LocalIP);
 
       const SIP_interface& old_intf = SIP_Ifs[it->second];
       if(intf.LocalPort == old_intf.LocalPort) {
 	ERROR("duplicated signaling interfaces "
 	      "(%s and %s) detected using %s:%u",
 	      old_intf.name.c_str(),intf.name.c_str(),
 	      intf.LocalIP.c_str(),intf.LocalPort);
 
 	return -1;
       }
22976049
       //FIXME: what happens now? shouldn't we insert the interface????
6d93ae38
     }
   }
 
   return 0;
 }
 
 static int readSIPInterface(AmConfigReader& cfg, const string& i_name)
c60b508e
 {
   int ret=0;
6d93ae38
   AmConfig::SIP_interface intf;
c60b508e
 
   string suffix;
   if(!i_name.empty())
     suffix = "_" + i_name;
 
   // listen, sip_ip, sip_port, and media_ip
   if(cfg.hasParameter("sip_ip" + suffix)) {
6d93ae38
     intf.LocalIP = cfg.getParameter("sip_ip" + suffix);
c60b508e
   }
6d93ae38
   else {
     // no sip_ip definition
     return 0;
c60b508e
   }
 
   if(cfg.hasParameter("sip_port" + suffix)){
     string sip_port_str = cfg.getParameter("sip_port" + suffix);
     if(sscanf(sip_port_str.c_str(),"%u",
6d93ae38
 	      &(intf.LocalPort)) != 1){
c60b508e
       ERROR("sip_port%s: invalid sip port specified (%s)\n",
 	    suffix.c_str(),
 	    sip_port_str.c_str());
       ret = -1;
     }
   }
 
6d93ae38
   // public_ip
   if(cfg.hasParameter("public_ip" + suffix)){
     intf.PublicIP = cfg.getParameter("public_ip" + suffix);
   }
 
   if(cfg.hasParameter("sig_sock_opts" + suffix)){
     vector<string> opt_strs = explode(cfg.getParameter("sig_sock_opts" + suffix),",");
     unsigned int opts = 0;
     for(vector<string>::iterator it_opt = opt_strs.begin();
 	it_opt != opt_strs.end(); ++it_opt) {
       if(*it_opt == "force_via_address") {
 	opts |= trsp_socket::force_via_address;
04fc38ec
       } else if(*it_opt == "no_transport_in_contact") {
 	opts |= trsp_socket::no_transport_in_contact;
       } else {
6d93ae38
 	WARN("unknown signaling socket option '%s' set on interface '%s'\n",
 	     it_opt->c_str(),i_name.c_str());
       }
     }
     intf.SigSockOpts = opts;
   }
 
c8bb6fd9
   intf.tcp_connect_timeout =
     cfg.getParameterInt("tcp_connect_timeout" + suffix,
 			DEFAULT_TCP_CONNECT_TIMEOUT);
 
   intf.tcp_idle_timeout =
     cfg.getParameterInt("tcp_idle_timeout" + suffix, DEFAULT_TCP_IDLE_TIMEOUT);
 
6d93ae38
   if(!i_name.empty())
     intf.name = i_name;
   else
     intf.name = "default";
 
   return AmConfig::insert_SIP_interface(intf);
 }
 
 int AmConfig::insert_RTP_interface(const RTP_interface& intf)
 {
   if(RTP_If_names.find(intf.name) !=
      RTP_If_names.end()) {
 
     if(intf.name != "default") {
       ERROR("duplicated interface '%s'\n",intf.name.c_str());
       return -1;
     }
 
     unsigned int idx = RTP_If_names[intf.name];
     RTP_Ifs[idx] = intf;
   }
   else {
     // insert interface
     RTP_Ifs.push_back(intf);
     unsigned short rtp_idx = RTP_Ifs.size()-1;
     RTP_If_names[intf.name] = rtp_idx;
     
     // fix RtpInterface index in SIP interface
     map<string,unsigned short>::iterator sip_idx_it = 
       SIP_If_names.find(intf.name);
 
     if((sip_idx_it != SIP_If_names.end()) &&
        (SIP_Ifs.size() > sip_idx_it->second)) {
       SIP_Ifs[sip_idx_it->second].RtpInterface = rtp_idx;
     }
   }
 
   return 0;
 }
 
 static int readRTPInterface(AmConfigReader& cfg, const string& i_name)
 {
   int ret=0;
   AmConfig::RTP_interface intf;
 
   string suffix;
   if(!i_name.empty())
     suffix = "_" + i_name;
 
   // media_ip
c60b508e
   if(cfg.hasParameter("media_ip" + suffix)) {
     intf.LocalIP = cfg.getParameter("media_ip" + suffix);
   }
6d93ae38
   else {
     // no media definition for this interface name
     return 0;
c60b508e
   }
 
   // public_ip
   if(cfg.hasParameter("public_ip" + suffix)){
     intf.PublicIP = cfg.getParameter("public_ip" + suffix);
   }
 
   // rtp_low_port
   if(cfg.hasParameter("rtp_low_port" + suffix)){
     string rtp_low_port_str = cfg.getParameter("rtp_low_port" + suffix);
     if(sscanf(rtp_low_port_str.c_str(),"%u",
 	      &(intf.RtpLowPort)) != 1){
       ERROR("rtp_low_port%s: invalid port number (%s)\n",
 	    suffix.c_str(),rtp_low_port_str.c_str());
       ret = -1;
     }
   }
 
   // rtp_high_port
   if(cfg.hasParameter("rtp_high_port" + suffix)){
     string rtp_high_port_str = cfg.getParameter("rtp_high_port" + suffix);
     if(sscanf(rtp_high_port_str.c_str(),"%u",
 	      &(intf.RtpHighPort)) != 1){
       ERROR("rtp_high_port%s: invalid port number (%s)\n",
 	    suffix.c_str(),rtp_high_port_str.c_str());
       ret = -1;
     }
   }
 
6d93ae38
   if(!i_name.empty())
     intf.name = i_name;
   else
178c71cc
     intf.name = "default";
c60b508e
 
6d93ae38
   return AmConfig::insert_RTP_interface(intf);
c60b508e
 }
 
 static int readInterfaces(AmConfigReader& cfg)
 {
6d93ae38
   if(!cfg.hasParameter("interfaces")) {
     // no interface list defined:
     // read default params
     readSIPInterface(cfg,"");
     readRTPInterface(cfg,"");
     return 0;
   }
c60b508e
 
6d93ae38
   vector<string> if_names;
   string ifs_str = cfg.getParameter("interfaces");
   if(ifs_str.empty()) {
     ERROR("empty interface list.\n");
c60b508e
     return -1;
   }
6d93ae38
 
   if_names = explode(ifs_str,",");
   if(!if_names.size()) {
     ERROR("could not parse interface list.\n");
     return -1;
c60b508e
   }
 
   for(vector<string>::iterator it = if_names.begin();
       it != if_names.end(); it++) {
 
6d93ae38
     readSIPInterface(cfg,*it);
     readRTPInterface(cfg,*it);
 
     if((AmConfig::SIP_If_names.find(*it) == AmConfig::SIP_If_names.end()) &&
        (AmConfig::RTP_If_names.find(*it) == AmConfig::RTP_If_names.end())) {
       ERROR("missing interface definition for '%s'\n",it->c_str());
       return -1;
c60b508e
     }
   }
 
6d93ae38
   //TODO: check interfaces
   return 0;
c60b508e
 }
 
1d82032c
 /** Get the list of network interfaces with the associated addresses & flags */
 static bool fillSysIntfList()
c60b508e
 {
1d82032c
   struct ifaddrs *ifap = NULL;
5e2aa29c
 
   // socket to grab MTU
   int fd = socket(AF_INET, SOCK_DGRAM, 0);
   if(fd < 0) {
     ERROR("socket() failed: %s",strerror(errno));
     return false;
   }
1d82032c
   
   if(getifaddrs(&ifap) < 0){
     ERROR("getifaddrs() failed: %s",strerror(errno));
c60b508e
     return false;
   }
 
1d82032c
   char host[NI_MAXHOST];
   for(struct ifaddrs *p_if = ifap; p_if != NULL; p_if = p_if->ifa_next) {
c60b508e
 
1d82032c
     if(p_if->ifa_addr == NULL)
       continue;
     
56a14def
     if( (p_if->ifa_addr->sa_family != AF_INET) &&
         (p_if->ifa_addr->sa_family != AF_INET6) )
1d82032c
       continue;
 
     if( !(p_if->ifa_flags & IFF_UP) || !(p_if->ifa_flags & IFF_RUNNING) )
       continue;
 
9a527cce
     if(p_if->ifa_addr->sa_family == AF_INET6) {
       
       struct sockaddr_in6 *addr = (struct sockaddr_in6 *)p_if->ifa_addr;
       if(IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)){
 	// sorry, we don't support link-local addresses...
 	continue;
 
 	// convert address from kernel-style to userland
 	// addr->sin6_scope_id = ntohs(*(uint16_t *)&addr->sin6_addr.s6_addr[2]);
 	// addr->sin6_addr.s6_addr[2] = addr->sin6_addr.s6_addr[3] = 0;
       }
     }
 
56a14def
     if (am_inet_ntop((const sockaddr_storage*)p_if->ifa_addr,
 		     host, NI_MAXHOST) == NULL) {
       ERROR("am_inet_ntop() failed\n");
5e2aa29c
       continue;
       // freeifaddrs(ifap);
       // return false;
c60b508e
     }
 
1d82032c
     string iface_name(p_if->ifa_name);
5e2aa29c
     vector<AmConfig::SysIntf>::iterator intf_it;
1d82032c
     for(intf_it = AmConfig::SysIfs.begin();
 	intf_it != AmConfig::SysIfs.end(); ++intf_it) {
 
       if(intf_it->name == iface_name)
 	break;
     }
 
     if(intf_it == AmConfig::SysIfs.end()){
5e2aa29c
       unsigned int sys_if_idx = if_nametoindex(iface_name.c_str());
       if(AmConfig::SysIfs.size() < sys_if_idx+1)
 	AmConfig::SysIfs.resize(sys_if_idx+1);
 
       intf_it = AmConfig::SysIfs.begin() + sys_if_idx;
1d82032c
       intf_it->name  = iface_name;
       intf_it->flags = p_if->ifa_flags;
5e2aa29c
 
       struct ifreq ifr;
       strncpy(ifr.ifr_name,p_if->ifa_name,IFNAMSIZ);
 
       if (ioctl(fd, SIOCGIFMTU, &ifr) < 0 ) {
 	ERROR("ioctl: %s",strerror(errno));
 	ERROR("setting MTU for this interface to default (1500)");
 	intf_it->mtu = 1500;
       }
       else {
 	intf_it->mtu = ifr.ifr_mtu;
       }
c60b508e
     }
 
4b78e766
     DBG("iface='%s';ip='%s';flags=0x%x\n",p_if->ifa_name,host,p_if->ifa_flags);
56a14def
     intf_it->addrs.push_back(AmConfig::IPAddr(host,p_if->ifa_addr->sa_family));
c60b508e
   }
 
1d82032c
   freeifaddrs(ifap);
5e2aa29c
   close(fd);
a48a03a9
 
6d93ae38
   // add addresses from SysIntfList, if not present
   for(unsigned int idx = 0; idx < AmConfig::SIP_Ifs.size(); idx++) {
 
5e2aa29c
     vector<AmConfig::SysIntf>::iterator intf_it = AmConfig::SysIfs.begin();
6d93ae38
     for(;intf_it != AmConfig::SysIfs.end(); ++intf_it) {
 
       list<AmConfig::IPAddr>::iterator addr_it = intf_it->addrs.begin();
       for(;addr_it != intf_it->addrs.end(); addr_it++) {
 	if(addr_it->addr == AmConfig::SIP_Ifs[idx].LocalIP)
 	  break;
       }
 
       // address not in this interface
       if(addr_it == intf_it->addrs.end())
 	continue;
 
       // address is primary
       if(addr_it == intf_it->addrs.begin())
 	continue;
 
       if(AmConfig::LocalSIPIP2If.find(intf_it->addrs.front().addr)
 	 == AmConfig::LocalSIPIP2If.end()) {
 	
 	AmConfig::LocalSIPIP2If[intf_it->addrs.front().addr] = idx;
       }
     }
   }
 
c60b508e
   return true;
 }
 
56a14def
 /** Get the AF_INET[6] address associated with the network interface */
 string fixIface2IP(const string& dev_name, bool v6_for_sip)
c60b508e
 {
   struct sockaddr_storage ss;
56a14def
   if(am_inet_pton(dev_name.c_str(), &ss)) {
     if(v6_for_sip && (ss.ss_family == AF_INET6) && (dev_name[0] != '['))
       return "[" + dev_name + "]";
     else
c60b508e
       return dev_name;
56a14def
   }
c60b508e
 
5e2aa29c
   for(vector<AmConfig::SysIntf>::iterator intf_it = AmConfig::SysIfs.begin();
1d82032c
       intf_it != AmConfig::SysIfs.end(); ++intf_it) {
       
6d93ae38
     if(intf_it->name != dev_name)
       continue;
c60b508e
 
6d93ae38
     if(intf_it->addrs.empty()){
       ERROR("No IP address for interface '%s'\n",intf_it->name.c_str());
       return "";
c60b508e
     }
6d93ae38
       
     DBG("dev_name = '%s'\n",dev_name.c_str());
     return intf_it->addrs.front().addr;
1d82032c
   }    
c60b508e
 
6d93ae38
   return "";
c60b508e
 }
 
6d93ae38
 /** Get IP addrese from first non-loopback interface */
 static string getDefaultIP()
c60b508e
 {
5e2aa29c
   for(vector<AmConfig::SysIntf>::iterator intf_it = AmConfig::SysIfs.begin();
6d93ae38
       intf_it != AmConfig::SysIfs.end(); ++intf_it) {
       
     if(intf_it->flags & IFF_LOOPBACK)
       continue;
c60b508e
 
6d93ae38
     if(intf_it->addrs.empty())
       continue;
4b78e766
 
6d93ae38
     DBG("dev_name = '%s'\n",intf_it->name.c_str());
     return intf_it->addrs.front().addr;
   }
4b78e766
 
6d93ae38
   return "";
 }
56a14def
 
6d93ae38
 static int setNetInterface(AmConfig::IP_interface* ip_if)
 {
5e2aa29c
   for(unsigned int i=0; i < AmConfig::SysIfs.size(); i++) {
6d93ae38
     
5e2aa29c
     list<AmConfig::IPAddr>::iterator addr_it = AmConfig::SysIfs[i].addrs.begin();
     while(addr_it != AmConfig::SysIfs[i].addrs.end()) {
6d93ae38
       if(ip_if->LocalIP == addr_it->addr) {
5e2aa29c
 	ip_if->NetIf = AmConfig::SysIfs[i].name;
 	ip_if->NetIfIdx = i;
6d93ae38
 	return 0;
56a14def
       }
6d93ae38
       addr_it++;
     }
   }
   
   // not interface found
   return -1;
 }
56a14def
 
6d93ae38
 int AmConfig::finalizeIPConfig()
 {
   fillSysIntfList();
4b78e766
 
6d93ae38
   // replace system interface names with IPs
   for(vector<SIP_interface>::iterator it = SIP_Ifs.begin();
       it != SIP_Ifs.end(); it++) {
     
     it->LocalIP = fixIface2IP(it->LocalIP,true);
     if(it->LocalIP.empty()) {
       ERROR("could not determine signaling IP for "
 	    "interface '%s'\n", it->name.c_str());
       return -1;
1d82032c
     }
3c118eb2
 
6d93ae38
     if(!it->LocalPort)
       it->LocalPort = 5060;
3c118eb2
 
6d93ae38
     setNetInterface(&(*it));
   }
3c118eb2
 
6d93ae38
   for(vector<RTP_interface>::iterator it = RTP_Ifs.begin();
       it != RTP_Ifs.end(); it++) {
     
     if(it->LocalIP.empty()) {
       // try the IP from the signaling interface
       map<string, unsigned short>::iterator sip_if = 
 	SIP_If_names.find(it->name);
       if(sip_if != SIP_If_names.end()) {
 	it->LocalIP = SIP_Ifs[sip_if->second].LocalIP;
       }
       else {
 	ERROR("could not determine media IP for "
 	      "interface '%s'\n", it->name.c_str());
 	return -1;
3c118eb2
       }
     }
6d93ae38
     else {
       it->LocalIP = fixIface2IP(it->LocalIP,false);
       if(it->LocalIP.empty()) {
 	ERROR("could not determine media IP for "
 	      "interface '%s'\n", it->name.c_str());
 	return -1;
       }
     }
 
     setNetInterface(&(*it));
   }
3c118eb2
 
6d93ae38
   if(!SIP_Ifs.size()) {
     SIP_interface intf;
     intf.LocalIP = getDefaultIP();
     if(intf.LocalIP.empty()){
       ERROR("could not determine default signaling IP.");
       return -1;
     }
     SIP_Ifs.push_back(intf);
     SIP_If_names["default"] = 0;
   }
3c118eb2
 
6d93ae38
   if(!RTP_Ifs.size()) {
     RTP_interface intf;
     intf.LocalIP = SIP_Ifs[0].LocalIP;
     if(intf.LocalIP.empty()){
       ERROR("could not determine default media IP.");
       return -1;
3c118eb2
     }
6d93ae38
     RTP_Ifs.push_back(intf);
     RTP_If_names["default"] = 0;
c60b508e
   }
 
   return 0;
 }
 
 void AmConfig::dump_Ifs()
 {
6d93ae38
   INFO("Signaling interfaces:");
   for(int i=0; i<(int)SIP_Ifs.size(); i++) {
c60b508e
     
6d93ae38
     SIP_interface& it_ref = SIP_Ifs[i];
c60b508e
 
6d93ae38
     INFO("\t(%i) name='%s'" ";LocalIP='%s'" 
c8bb6fd9
 	 ";LocalPort='%u'" ";PublicIP='%s';TCP=%u/%u",
6d93ae38
 	 i,it_ref.name.c_str(),it_ref.LocalIP.c_str(),
c8bb6fd9
 	 it_ref.LocalPort,it_ref.PublicIP.c_str(),
 	 it_ref.tcp_connect_timeout,
 	 it_ref.tcp_idle_timeout);
c60b508e
   }
   
   INFO("Signaling address map:");
4b78e766
   for(multimap<string,unsigned short>::iterator it = LocalSIPIP2If.begin();
c60b508e
       it != LocalSIPIP2If.end(); ++it) {
 
6d93ae38
     if(SIP_Ifs[it->second].name.empty()){
c60b508e
       INFO("\t%s -> default",it->first.c_str());
     }
     else {
       INFO("\t%s -> %s",it->first.c_str(),
6d93ae38
 	   SIP_Ifs[it->second].name.c_str());
c60b508e
     }
   }
6d93ae38
 
   INFO("Media interfaces:");
   for(int i=0; i<(int)RTP_Ifs.size(); i++) {
     
     RTP_interface& it_ref = RTP_Ifs[i];
 
     INFO("\t(%i) name='%s'" ";LocalIP='%s'" 
 	 ";Ports=[%u;%u]" ";PublicIP='%s'",
 	 i,it_ref.name.c_str(),it_ref.LocalIP.c_str(),
 	 it_ref.RtpLowPort,it_ref.RtpHighPort,
 	 it_ref.PublicIP.c_str());
   }
c60b508e
 }