modules/call_control/call_control.c
b8d12a32
 /* $Id$
  *
  * Copyright (C) 2005-2008 Dan Pascu
  *
  * This file is part of Kamailio, a free SIP server.
  *
  * Kamailio is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * Kamailio is distributed in the hope that it will be useful,
  * 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
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
b8d12a32
  *
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <time.h>
 #include <ctype.h>
 #include <errno.h>
 #include <assert.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/un.h>
 
 #include "../../sr_module.h"
 #include "../../mem/mem.h"
 #include "../../dprint.h"
 #include "../../str.h"
 #include "../../pvar.h"
 #include "../../ut.h"
 #include "../../script_cb.h"
 #include "../../parser/digest/digest.h"
 #include "../../parser/parse_from.h"
 #include "../dialog/dlg_load.h"
 #include "../dialog/dlg_hash.h"
 
 
 MODULE_VERSION
 
55812477
 #define FL_USE_CALL_CONTROL       (1<<28) // use call control for a dialog
b8d12a32
 
 #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
 # define INLINE inline
 #else
 # define INLINE
 #endif
 
 #define CANONICAL_URI_AVP_SPEC "$avp(s:can_uri)"
 #define SIGNALING_IP_AVP_SPEC  "$avp(s:signaling_ip)"
0a4519af
 #define SIP_APPLICATION_AVP_SPEC "$avp(s:sip_application)"
b8d12a32
 
 // Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to
 // more systems.  `AF_UNIX' was the traditional name stemming from BSD, so
 // even most POSIX systems support it.  It is also the name of choice in
 // the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX
 #ifndef AF_LOCAL
 # define AF_LOCAL AF_UNIX
 #endif
 
 // Solaris does not have the MSG_NOSIGNAL flag for the send(2) syscall
 #ifndef MSG_NOSIGNAL
 # define MSG_NOSIGNAL 0
 #endif
 
 
 
 typedef int Bool;
 #define True  1
 #define False 0
 
 
 typedef struct AVP_Param {
     str spec;
     int_str name;
     unsigned short type;
 } AVP_Param;
 
66b8b7d0
 typedef struct AVP_List {
     pv_spec_p pv;
     str name;
     struct AVP_List *next;
 } AVP_List;
 
b8d12a32
 #define RETRY_INTERVAL 10
 #define BUFFER_SIZE    8192
 
 typedef struct CallControlSocket {
     char *name;             // name
     int  sock;              // socket
     int  timeout;           // how many miliseconds to wait for an answer
     time_t last_failure;    // time of the last failure
     char data[BUFFER_SIZE]; // buffer for the answer data
 } CallControlSocket;
 
 
 /* Function prototypes */
 static int CallControl(struct sip_msg *msg, char *str1, char *str2);
 
 static int mod_init(void);
 static int child_init(int rank);
66b8b7d0
 static void destroy(void);
f5b0f1dc
 static int postprocess_request(struct sip_msg *, unsigned int, void *);
b8d12a32
 
66b8b7d0
 int parse_param_init(unsigned int type, void *val);
 int parse_param_start(unsigned int type, void *val);
 int parse_param_stop(unsigned int type, void *val);
b8d12a32
 
 /* Local global variables */
 static CallControlSocket callcontrol_socket = {
     "/var/run/callcontrol/socket", // name
     -1,                            // sock
     500,                           // timeout in 500 miliseconds if there is no answer
     0,                             // time of the last failure
     ""                             // data
 };
 
 static int disable = False;
 static int diverter_avp_id = 805;
 
 /* The AVP where the canonical URI is stored (if defined) */
 static AVP_Param canonical_uri_avp = {str_init(CANONICAL_URI_AVP_SPEC), {0}, 0};
 
 /* The AVP where the caller signaling IP is stored (if defined) */
 static AVP_Param signaling_ip_avp = {str_init(SIGNALING_IP_AVP_SPEC), {0}, 0};
 
0a4519af
 /* The AVP where the SIP application type is stored (if defined) */
 static AVP_Param sip_application_avp = {str_init(SIP_APPLICATION_AVP_SPEC), {0}, 0};
 
b8d12a32
 
 struct dlg_binds dlg_api;
 static int dialog_flag = -1;
 
66b8b7d0
 AVP_List *cc_init_avps = NULL, *cc_start_avps = NULL, *cc_stop_avps = NULL;
 
 pv_elem_t *model;
b8d12a32
 
 static cmd_export_t commands[] = {
     {"call_control",  (cmd_function)CallControl, 0, 0, 0, REQUEST_ROUTE },
     {0, 0, 0, 0, 0, 0}
 };
 
 static param_export_t parameters[] = {
979c7ef0
     {"init",                PARAM_STRING | USE_FUNC_PARAM, parse_param_init},
     {"start",               PARAM_STRING | USE_FUNC_PARAM, parse_param_start},
     {"stop",                PARAM_STRING | USE_FUNC_PARAM, parse_param_stop},
b8d12a32
     {"disable",             INT_PARAM, &disable},
979c7ef0
     {"socket_name",         PARAM_STRING, &(callcontrol_socket.name)},
b8d12a32
     {"socket_timeout",      INT_PARAM, &(callcontrol_socket.timeout)},
     {"diverter_avp_id",     INT_PARAM, &diverter_avp_id},
979c7ef0
     {"canonical_uri_avp",   PARAM_STR, &(canonical_uri_avp.spec)},
     {"signaling_ip_avp",    PARAM_STR, &(signaling_ip_avp.spec)},
     {"sip_application_avp", PARAM_STR, &(sip_application_avp.spec)},
b8d12a32
     {0, 0, 0}
 };
 
 struct module_exports exports = {
     "call_control",  // module name
     DEFAULT_DLFLAGS, // dlopen flags
     commands,        // exported functions
     parameters,      // exported parameters
     NULL,            // exported statistics
     NULL,            // exported MI functions
     NULL,            // exported pseudo-variables
     NULL,            // extra processes
     mod_init,        // module init function (before fork. kids will inherit)
     NULL,            // reply processing function
66b8b7d0
     destroy,         // destroy function
b8d12a32
     child_init       // child init function
 };
 
 
 
 typedef enum CallControlAction {
     CAInitialize = 1,
     CAStart,
     CAStop
 } CallControlAction;
 
 
 typedef struct Contact {
     str username;
     str ip;
     str port;
 } Contact;
 
 typedef struct DialogID {
     unsigned int h_entry;
     unsigned int h_id;
 } DialogID;
 
 typedef struct CallInfo {
     CallControlAction action;
     DialogID dialog_id;
     str ruri;
     str diverter;
     str source_ip;
     str callid;
     str from;
     str from_tag;
0a4519af
     str sip_application;
b8d12a32
 } CallInfo;
 
 
66b8b7d0
 
 #define CHECK_COND(cond) \
     if ((cond) == 0) { \
         LM_ERR("malformed modparam\n"); \
         return -1;                            \
     }
 
 #define CHECK_ALLOC(p) \
     if (!(p)) {    \
         LM_ERR("no memory left\n"); \
         return -1;    \
     }
 
 
 void
 destroy_list(AVP_List *list) {
 	AVP_List *cur, *next;
 
 	cur = list;
 	while (cur) {
 		next = cur->next;
 		pkg_free(cur);
 		cur = next;
 	}
 }
 
 
 int
 parse_param(void *val, AVP_List** avps) {
 
     char *p;
af81862f
     str s, content;
66b8b7d0
     AVP_List *mp = NULL;
 
     //LM_DBG("%.*s\n", content.len, content.s);
 
     content.s = (char*) val;
     content.len = strlen(content.s);
 
 
     p = (char*) pkg_malloc (content.len + 1);
     CHECK_ALLOC(p);
 
     p[content.len] = '\0';
     memcpy(p, content.s, content.len);
 
 
     for (;*p != '\0';) {
 
         mp = (AVP_List*) pkg_malloc (sizeof(AVP_List));
         CHECK_ALLOC(mp);
         mp->next = *avps;
         mp->pv = (pv_spec_p) pkg_malloc (sizeof(pv_spec_t));
         CHECK_ALLOC(mp->pv);
 
         for (; isspace(*p); p++);
         CHECK_COND(*p != '\0');
 
         mp->name.s = p;
 
         for(; isgraph(*p) && *p != '='; p++)
             CHECK_COND(*p != '\0');
 
         mp->name.len = p - mp->name.s;
 
         for (; isspace(*p); p++);
         CHECK_COND(*p != '\0' && *p == '=');
         p++;
 
         //LM_DBG("%.*s\n", mp->name.len, mp->name.s);
 
         for (; isspace(*p); p++);
         CHECK_COND(*p != '\0' && *p == '$');
 
af81862f
         s.s = p;
         s.len = strlen(p);
66b8b7d0
 
af81862f
         p = pv_parse_spec(&s, mp->pv);
66b8b7d0
 
         for (; isspace(*p); p++);
         *avps = mp;
     }
 
     return 0;
 }
 
 
 int
 parse_param_init(unsigned int type, void *val) {
     if (parse_param(val, &cc_init_avps) == -1)
         return E_CFG;
     return 0;
 }
 
 int
 parse_param_start(unsigned int type, void *val) {
     if (parse_param(val, &cc_start_avps) == -1)
         return E_CFG;
     return 0;
 }
 
 int
 parse_param_stop(unsigned int type, void *val) {
     if (parse_param(val, &cc_stop_avps) == -1)
         return E_CFG;
     return 0;
 }
 
 
b8d12a32
 // Functions dealing with strings
 //
 
 // returns string with whitespace trimmed from left end
 static inline void
 ltrim(str *string)
 {
     while (string->len>0 && isspace((int)*(string->s))) {
         string->len--;
         string->s++;
     }
 }
 
 // returns string with whitespace trimmed from right end
 static inline void
 rtrim(str *string)
 {
     char *ptr;
 
     ptr = string->s + string->len - 1;
     while (string->len>0 && (*ptr==0 || isspace((int)*ptr))) {
         string->len--;
         ptr--;
     }
 }
 
 // returns string with whitespace trimmed from both ends
 static inline void
 trim(str *string)
 {
     ltrim(string);
     rtrim(string);
 }
 
 
 // Message checking and parsing
 //
 
 static Bool
 has_to_tag(struct sip_msg *msg)
 {
     str tag;
 
     if (!msg->to) {
         if (parse_headers(msg, HDR_TO_F, 0)==-1) {
3ac29ef0
             LOG(L_ERR, "cannot parse 'To' header\n");
b8d12a32
             return False;
         }
         if (!msg->to) {
3ac29ef0
             LOG(L_ERR, "missing 'To' header\n");
b8d12a32
             return False;
         }
     }
 
     tag = get_to(msg)->tag_value;
 
     if (tag.s==NULL || tag.len==0) {
         return False;
     }
 
     return True;
 }
 
 
 // Get canonical request URI
 static str
 get_canonical_request_uri(struct sip_msg* msg)
 {
     int_str value;
 
     if (!search_first_avp(canonical_uri_avp.type | AVP_VAL_STR,
                           canonical_uri_avp.name, &value, NULL) ||
         value.s.s==NULL || value.s.len==0) {
 
         return *GET_RURI(msg);
     }
 
     return value.s;
 }
 
 
 // Get caller signaling IP
 static str
 get_signaling_ip(struct sip_msg* msg)
 {
     int_str value;
 
     if (!search_first_avp(signaling_ip_avp.type | AVP_VAL_STR,
                           signaling_ip_avp.name, &value, NULL) ||
         !value.s.s || value.s.len==0) {
 
         value.s.s = ip_addr2a(&msg->rcv.src_ip);
         value.s.len = strlen(value.s.s);
     }
 
     return value.s;
 }
 
0a4519af
 // Get SIP application type
 static str
 get_sip_application(struct sip_msg* msg)
 {
     int_str value;
 
     if (!search_first_avp(sip_application_avp.type | AVP_VAL_STR,
                           sip_application_avp.name, &value, NULL) ||
         !value.s.s || value.s.len==0) {
 
         value.s.s = "audio";
         value.s.len = strlen(value.s.s);
     }
 
     return value.s;
 }
b8d12a32
 
 static str
 get_diverter(struct sip_msg *msg)
 {
     struct hdr_field *header;
     dig_cred_t *credentials;
     int_str avpname, avpvalue;
     static str diverter;
 
     diverter.s   = "None";
     diverter.len = 4;
 
     avpname.n = diverter_avp_id;
 
     if (search_first_avp(AVP_VAL_STR, avpname, &avpvalue, NULL)) {
         // have a diverted call
         diverter = avpvalue.s;
     } else {
         get_authorized_cred(msg->proxy_auth, &header);
         if (header) {
             credentials = &((auth_body_t*)(header->parsed))->digest;
         } else {
             if (parse_headers(msg, HDR_PROXYAUTH_F, 0) == -1) {
3ac29ef0
                 LOG(L_ERR, "cannot parse Proxy-Authorization header\n");
b8d12a32
                 return diverter;
             }
             if (!msg->proxy_auth)
                 return diverter;
             if (parse_credentials(msg->proxy_auth) != 0) {
3ac29ef0
                 LOG(L_ERR, "cannot parse credentials\n");
b8d12a32
                 return diverter;
             }
             credentials = &((auth_body_t*)(msg->proxy_auth->parsed))->digest;
         }
 
         if (credentials->username.user.len > 0 &&
             credentials->username.domain.len > 0 &&
             credentials->realm.len == 0 &&
             credentials->nonce.len == 0 &&
             credentials->response.len == 0) {
             // this is a call diverted from the failure route
             // and sent back to proxy with append_pa_hf()
             diverter = credentials->username.whole;
         }
     }
 
     return diverter;
 }
 
 
 static CallInfo*
 get_call_info(struct sip_msg *msg, CallControlAction action)
 {
     static CallInfo call_info;
     int headers;
 
     memset(&call_info, 0, sizeof(struct CallInfo));
 
     switch (action) {
     case CAInitialize:
         headers = HDR_CALLID_F|HDR_FROM_F;
         break;
     case CAStart:
     case CAStop:
         headers = HDR_CALLID_F;
         break;
     default:
         // Invalid action. Should never get here.
         assert(False);
         return NULL;
     }
 
     if (parse_headers(msg, headers, 0) == -1) {
3ac29ef0
         LOG(L_ERR, "cannot parse required headers\n");
b8d12a32
         return NULL;
     }
 
     if (headers & HDR_CALLID_F) {
         if (msg->callid == NULL) {
3ac29ef0
             LOG(L_ERR, "missing Call-ID header\n");
b8d12a32
             return NULL;
         }
 
         call_info.callid = msg->callid->body;
         trim(&call_info.callid);
     }
 
     if (headers & HDR_FROM_F) {
         struct to_body *from; // yeah. suggestive structure name ;)
 
         if (msg->from == NULL) {
3ac29ef0
             LOG(L_ERR, "missing From header\n");
b8d12a32
             return NULL;
         }
         if (!msg->from->parsed && parse_from_header(msg)==-1) {
3ac29ef0
             LOG(L_ERR, "cannot parse From header\n");
b8d12a32
             return NULL;
         }
 
         from = get_from(msg);
 
         if (from->body.s==NULL || from->body.len==0) {
3ac29ef0
             LOG(L_ERR, "missing From\n");
b8d12a32
             return NULL;
         }
         if (from->tag_value.s==NULL || from->tag_value.len==0) {
3ac29ef0
             LOG(L_ERR, "missing From tag\n");
b8d12a32
             return NULL;
         }
 
         call_info.from = from->body;
         call_info.from_tag = from->tag_value;
     }
 
     if (action == CAInitialize) {
         call_info.ruri = get_canonical_request_uri(msg);
         call_info.diverter = get_diverter(msg);
         call_info.source_ip = get_signaling_ip(msg);
0a4519af
         call_info.sip_application = get_sip_application(msg);
b8d12a32
     }
 
     call_info.action = action;
 
     return &call_info;
 }
 
66b8b7d0
 static char*
 make_custom_request(struct sip_msg *msg, CallInfo *call)
 {
     static char request[8192];
     int len = 0;
     AVP_List *al;
     pv_value_t pt;
 
     switch (call->action) {
     case CAInitialize:
         al = cc_init_avps;
         break;
     case CAStart:
         al = cc_start_avps;
         break;
     case CAStop:
         al = cc_stop_avps;
         break;
     default:
         // should never get here, but keep gcc from complaining
         assert(False);
         return NULL;
     }
 
     for (; al; al = al->next) {
         pv_get_spec_value(msg, al->pv, &pt);
         if (pt.flags & PV_VAL_INT) {
             len += snprintf(request + len, sizeof(request),
                       "%.*s = %d ", al->name.len, al->name.s,
                    pt.ri);
         } else    if (pt.flags & PV_VAL_STR) {
             len += snprintf(request + len, sizeof(request),
                       "%.*s = %.*s ", al->name.len, al->name.s,
                    pt.rs.len, pt.rs.s);
         }
 
           if (len >= sizeof(request)) {
                LM_ERR("callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request));
             return NULL;
              }
     }
 
 
     return request;
 }
 
b8d12a32
 
 static char*
66b8b7d0
 make_default_request(CallInfo *call)
b8d12a32
 {
     static char request[8192];
     int len;
 
     switch (call->action) {
     case CAInitialize:
         len = snprintf(request, sizeof(request),
                        "init\r\n"
                        "ruri: %.*s\r\n"
                        "diverter: %.*s\r\n"
                        "sourceip: %.*s\r\n"
                        "callid: %.*s\r\n"
                        "from: %.*s\r\n"
                        "fromtag: %.*s\r\n"
0a4519af
                        "sip_application: %.*s\r\n"
b8d12a32
                        "\r\n",
                        call->ruri.len, call->ruri.s,
                        call->diverter.len, call->diverter.s,
                        call->source_ip.len, call->source_ip.s,
                        call->callid.len, call->callid.s,
                        call->from.len, call->from.s,
0a4519af
                        call->from_tag.len, call->from_tag.s,
                        call->sip_application.len, call->sip_application.s);
b8d12a32
 
         if (len >= sizeof(request)) {
3ac29ef0
             LOG(L_ERR, "callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request));
b8d12a32
             return NULL;
         }
 
         break;
 
     case CAStart:
         len = snprintf(request, sizeof(request),
                        "start\r\n"
                        "callid: %.*s\r\n"
                        "dialogid: %d:%d\r\n"
                        "\r\n",
                        call->callid.len, call->callid.s,
                        call->dialog_id.h_entry, call->dialog_id.h_id);
 
         if (len >= sizeof(request)) {
3ac29ef0
             LOG(L_ERR, "callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request));
b8d12a32
             return NULL;
         }
 
         break;
 
     case CAStop:
         len = snprintf(request, sizeof(request),
                        "stop\r\n"
                        "callid: %.*s\r\n"
                        "\r\n",
                        call->callid.len, call->callid.s);
 
         if (len >= sizeof(request)) {
3ac29ef0
             LOG(L_ERR, "callcontrol request is longer than %ld bytes\n", (unsigned long)sizeof(request));
b8d12a32
             return NULL;
         }
 
         break;
 
     default:
         // should never get here, but keep gcc from complaining
         assert(False);
         return NULL;
     }
 
     return request;
 }
 
 
 // Functions dealing with the external call_control helper
 //
 
 static Bool
 callcontrol_connect(void)
 {
     struct sockaddr_un addr;
 
     if (callcontrol_socket.sock >= 0)
         return True;
 
     if (callcontrol_socket.last_failure + RETRY_INTERVAL > time(NULL))
         return False;
 
     memset(&addr, 0, sizeof(addr));
     addr.sun_family = AF_LOCAL;
     strncpy(addr.sun_path, callcontrol_socket.name, sizeof(addr.sun_path) - 1);
 #ifdef HAVE_SOCKADDR_SA_LEN
     addr.sun_len = strlen(addr.sun_path);
 #endif
 
     callcontrol_socket.sock = socket(AF_LOCAL, SOCK_STREAM, 0);
     if (callcontrol_socket.sock < 0) {
3ac29ef0
         LOG(L_ERR, "can't create socket\n");
b8d12a32
         callcontrol_socket.last_failure = time(NULL);
         return False;
     }
     if (connect(callcontrol_socket.sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
3ac29ef0
         LOG(L_ERR, "failed to connect to %s: %s\n", callcontrol_socket.name, strerror(errno));
b8d12a32
         close(callcontrol_socket.sock);
         callcontrol_socket.sock = -1;
         callcontrol_socket.last_failure = time(NULL);
         return False;
     }
 
     return True;
 }
 
 static void
 callcontrol_disconnect(void)
 {
     if (callcontrol_socket.sock < 0)
         return;
 
     close(callcontrol_socket.sock);
     callcontrol_socket.sock = -1;
     callcontrol_socket.last_failure = time(NULL);
 }
 
 static char*
 send_command(char *command)
 {
     int cmd_len, bytes, tries, sent, received, count;
     struct timeval timeout;
     fd_set rset;
 
     if (!callcontrol_connect())
         return NULL;
 
     cmd_len = strlen(command);
 
     for (sent=0, tries=0; sent<cmd_len && tries<3; tries++, sent+=bytes) {
         do
             bytes = send(callcontrol_socket.sock, command+sent, cmd_len-sent, MSG_DONTWAIT|MSG_NOSIGNAL);
         while (bytes == -1 && errno == EINTR);
         if (bytes == -1) {
             switch (errno) {
             case ECONNRESET:
             case EPIPE:
                 callcontrol_disconnect();
                 callcontrol_socket.last_failure = 0; // we want to reconnect immediately
                 if (callcontrol_connect()) {
                     sent = bytes = 0;
                     continue;
                 } else {
3ac29ef0
                     LOG(L_ERR, "connection with callcontrol did die\n");
b8d12a32
                 }
                 break;
             case EACCES:
3ac29ef0
                 LOG(L_ERR, "permission denied sending to %s\n", callcontrol_socket.name);
b8d12a32
                 break;
             case EWOULDBLOCK:
                 // this shouldn't happen as we read back all the answer after a request.
                 // if it would block, it means there is an error.
3ac29ef0
                 LOG(L_ERR, "sending command would block!\n");
b8d12a32
                 break;
             default:
3ac29ef0
                 LOG(L_ERR, "%d: %s\n", errno, strerror(errno));
b8d12a32
                 break;
             }
             callcontrol_disconnect();
             return NULL;
         }
     }
     if (sent < cmd_len) {
3ac29ef0
         LOG(L_ERR, "couldn't send complete command after 3 tries\n");
b8d12a32
         callcontrol_disconnect();
         return NULL;
     }
 
     callcontrol_socket.data[0] = 0;
     received = 0;
     while (True) {
         FD_ZERO(&rset);
         FD_SET(callcontrol_socket.sock, &rset);
         timeout.tv_sec = callcontrol_socket.timeout / 1000;
         timeout.tv_usec = (callcontrol_socket.timeout % 1000) * 1000;
 
         do
             count = select(callcontrol_socket.sock + 1, &rset, NULL, NULL, &timeout);
         while (count == -1 && errno == EINTR);
 
         if (count == -1) {
3ac29ef0
             LOG(L_ERR, "select failed: %d: %s\n", errno, strerror(errno));
b8d12a32
             callcontrol_disconnect();
             return NULL;
         } else if (count == 0) {
3ac29ef0
             LOG(L_ERR, "did timeout waiting for an answer\n");
b8d12a32
             callcontrol_disconnect();
             return NULL;
         } else {
             do
                 bytes = recv(callcontrol_socket.sock, callcontrol_socket.data+received, BUFFER_SIZE-1-received, 0);
             while (bytes == -1 && errno == EINTR);
             if (bytes == -1) {
3ac29ef0
                 LOG(L_ERR, "failed to read answer: %d: %s\n", errno, strerror(errno));
b8d12a32
                 callcontrol_disconnect();
                 return NULL;
             } else if (bytes == 0) {
3ac29ef0
                 LOG(L_ERR, "connection with callcontrol closed\n");
b8d12a32
                 callcontrol_disconnect();
                 return NULL;
             } else {
                 callcontrol_socket.data[received+bytes] = 0;
                 if (strstr(callcontrol_socket.data+received, "\r\n")!=NULL) {
                     break;
                 }
                 received += bytes;
             }
         }
     }
 
     return callcontrol_socket.data;
 }
 
 
 // Call control processing
 //
 
 // Return codes:
 //   2 - No limit
 //   1 - Limited
 //  -1 - No credit
 //  -2 - Locked
 //  -5 - Internal error (message parsing, communication, ...)
 static int
 call_control_initialize(struct sip_msg *msg)
 {
     CallInfo *call;
66b8b7d0
     char *message, *result = NULL;
 
b8d12a32
 
     call = get_call_info(msg, CAInitialize);
     if (!call) {
3ac29ef0
         LOG(L_ERR, "can't retrieve call info\n");
b8d12a32
         return -5;
     }
66b8b7d0
 
 
     if (!cc_init_avps)
         message = make_default_request(call);
     else
         message = make_custom_request(msg, call);
 
b8d12a32
     if (!message)
         return -5;
 
66b8b7d0
    result = send_command(message);
b8d12a32
 
     if (result==NULL) {
         return -5;
     } else if (strcasecmp(result, "No limit\r\n")==0) {
         return 2;
     } else if (strcasecmp(result, "Limited\r\n")==0) {
         return 1;
     } else if (strcasecmp(result, "No credit\r\n")==0) {
         return -1;
     } else if (strcasecmp(result, "Locked\r\n")==0) {
         return -2;
     } else {
         return -5;
     }
 }
 
 
 // Called during a dialog for start and update requests
 //
 // Return codes:
 //   1 - Ok
 //  -1 - Session not found
 //  -5 - Internal error (message parsing, communication, ...)
 static int
 call_control_start(struct sip_msg *msg, struct dlg_cell *dlg)
 {
     CallInfo *call;
     char *message, *result;
 
     call = get_call_info(msg, CAStart);
     if (!call) {
3ac29ef0
         LOG(L_ERR, "can't retrieve call info\n");
b8d12a32
         return -5;
     }
 
     call->dialog_id.h_entry = dlg->h_entry;
     call->dialog_id.h_id = dlg->h_id;
 
66b8b7d0
     if (!cc_start_avps)
         message = make_default_request(call);
     else
         message = make_custom_request(msg, call);
 
b8d12a32
     if (!message)
         return -5;
 
     result = send_command(message);
 
     if (result==NULL) {
         return -5;
     } else if (strcasecmp(result, "Ok\r\n")==0) {
         return 1;
     } else if (strcasecmp(result, "Not found\r\n")==0) {
         return -1;
     } else {
         return -5;
     }
 }
 
 
 // Called during a dialog ending to stop callcontrol
 //
 // Return codes:
 //   1 - Ok
 //  -1 - Session not found
 //  -5 - Internal error (message parsing, communication, ...)
 static int
 call_control_stop(struct sip_msg *msg, str callid)
 {
     CallInfo call;
     char *message, *result;
 
     call.action = CAStop;
     call.callid = callid;
 
66b8b7d0
     if (!cc_stop_avps)
         message = make_default_request(&call);
     else
         message = make_custom_request(msg, &call);
 
b8d12a32
     if (!message)
         return -5;
 
     result = send_command(message);
 
     if (result==NULL) {
         return -5;
     } else if (strcasecmp(result, "Ok\r\n")==0) {
         return 1;
     } else if (strcasecmp(result, "Not found\r\n")==0) {
         return -1;
     } else {
         return -5;
     }
 }
 
 
 // Dialog callbacks and helpers
 //
 
 typedef enum {
     CCInactive = 0,
     CCActive
 } CallControlState;
 
 
 static void
 __dialog_replies(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
f1853aba
     struct sip_msg *reply = _params->rpl;
b8d12a32
 
     if (reply!=FAKED_REPLY && reply->REPLY_STATUS==200) {
         call_control_start(reply, dlg);
     }
 }
 
 
 static void
 __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
     if ((int)(long)*_params->param == CCActive) {
f1853aba
         struct sip_msg* msg = _params->rpl;
         if( !msg || msg == FAKED_REPLY)
             msg = _params->req;
         call_control_stop(msg, dlg->callid);
b8d12a32
         *_params->param = CCInactive;
     }
 }
 
 
 static void
 __dialog_created(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
f1853aba
     struct sip_msg *request = _params->req;
b8d12a32
 
     if (request->REQ_METHOD != METHOD_INVITE)
         return;
 
     if ((request->msg_flags & FL_USE_CALL_CONTROL) == 0)
         return;
 
     if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_FWDED, __dialog_replies, NULL, NULL) != 0)
3ac29ef0
         LOG(L_ERR, "cannot register callback for dialog confirmation\n");
b8d12a32
     if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_DESTROY, __dialog_ended, (void*)CCActive, NULL) != 0)
3ac29ef0
         LOG(L_ERR, "cannot register callback for dialog termination\n");
b8d12a32
 
     // reset the flag to indicate that the dialog for callcontrol was created
     request->msg_flags &= ~FL_USE_CALL_CONTROL;
 }
 
 
 static void
 __dialog_loaded(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 {
     if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_FWDED, __dialog_replies, NULL, NULL) != 0)
3ac29ef0
         LOG(L_ERR, "cannot register callback for dialog confirmation\n");
b8d12a32
     if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_DESTROY, __dialog_ended, (void*)CCActive, NULL) != 0)
3ac29ef0
         LOG(L_ERR, "cannot register callback for dialog termination\n");
b8d12a32
 }
 
 
 // Public API
 //
 
 // Return codes:
 //   2 - No limit
 //   1 - Limited
 //  -1 - No credit
 //  -2 - Locked
 //  -3 - No provider
 //  -5 - Internal error (message parsing, communication, ...)
 static int
 CallControl(struct sip_msg *msg, char *str1, char *str2)
 {
     int result;
 
     if (disable)
         return 2;
 
     if (msg->first_line.type!=SIP_REQUEST || msg->REQ_METHOD!=METHOD_INVITE || has_to_tag(msg)) {
3ac29ef0
         LOG(L_WARN, "call_control should only be called for the first INVITE\n");
b8d12a32
         return -5;
     }
 
     result = call_control_initialize(msg);
     if (result == 1) {
         // A call with a time limit that will be traced by callcontrol
         msg->msg_flags |= FL_USE_CALL_CONTROL;
         setflag(msg, dialog_flag); // have the dialog module trace this dialog
     }
 
     return result;
 }
 
 
 // Module management: initialization/destroy/function-parameter-fixing/...
 //
 
 static int
 mod_init(void)
 {
     pv_spec_t avp_spec;
     int *param;
 	 modparam_t type;
 
 
     // initialize the canonical_uri_avp structure
979c7ef0
     if (canonical_uri_avp.spec.s==NULL || canonical_uri_avp.spec.len<=0) {
3ac29ef0
         LOG(L_ERR, "missing/empty canonical_uri_avp parameter. using default.\n");
b8d12a32
         canonical_uri_avp.spec.s = CANONICAL_URI_AVP_SPEC;
     }
979c7ef0
 
b8d12a32
     if (pv_parse_spec(&(canonical_uri_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
3ac29ef0
         LOG(L_CRIT, "invalid AVP specification for canonical_uri_avp: `%s'\n", canonical_uri_avp.spec.s);
b8d12a32
         return -1;
     }
     if (pv_get_avp_name(0, &(avp_spec.pvp), &(canonical_uri_avp.name), &(canonical_uri_avp.type))!=0) {
3ac29ef0
         LOG(L_CRIT, "invalid AVP specification for canonical_uri_avp: `%s'\n", canonical_uri_avp.spec.s);
b8d12a32
         return -1;
     }
 
     // initialize the signaling_ip_avp structure
979c7ef0
     if (signaling_ip_avp.spec.s==NULL || signaling_ip_avp.spec.len<=0) {
3ac29ef0
         LOG(L_ERR, "missing/empty signaling_ip_avp parameter. using default.\n");
b8d12a32
         signaling_ip_avp.spec.s = SIGNALING_IP_AVP_SPEC;
     }
979c7ef0
 
b8d12a32
     if (pv_parse_spec(&(signaling_ip_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
3ac29ef0
         LOG(L_CRIT, "invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s);
b8d12a32
         return -1;
     }
     if (pv_get_avp_name(0, &(avp_spec.pvp), &(signaling_ip_avp.name), &(signaling_ip_avp.type))!=0) {
3ac29ef0
         LOG(L_CRIT, "invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s);
b8d12a32
         return -1;
     }
 
0a4519af
     // initialize the sip_application_avp structure
979c7ef0
     if (sip_application_avp.spec.s==NULL || sip_application_avp.spec.len<=0) {
0a4519af
         LOG(L_ERR, "missing/empty sip_application_avp parameter. using default.\n");
         sip_application_avp.spec.s = SIP_APPLICATION_AVP_SPEC;
     }
979c7ef0
 
0a4519af
     if (pv_parse_spec(&(sip_application_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
         LOG(L_CRIT, "invalid AVP specification for sip_application_avp: `%s'\n", sip_application_avp.spec.s);
         return -1;
     }
     if (pv_get_avp_name(0, &(avp_spec.pvp), &(sip_application_avp.name), &(sip_application_avp.type))!=0) {
         LOG(L_CRIT, "invalid AVP specification for sip_application_avp: `%s'\n", sip_application_avp.spec.s);
         return -1;
     }
 
b8d12a32
     // bind to the dialog API
     if (load_dlg_api(&dlg_api)!=0) {
3ac29ef0
         LOG(L_CRIT, "cannot load the dialog module API\n");
b8d12a32
         return -1;
     }
 
     // load dlg_flag and default_timeout parameters from the dialog module
     param = find_param_export(find_module_by_name("dialog"), "dlg_flag", INT_PARAM, &type);
     if (!param) {
3ac29ef0
         LOG(L_CRIT, "cannot find dlg_flag parameter in the dialog module\n");
b8d12a32
         return -1;
     }
     if (type != INT_PARAM) {
3ac29ef0
         LOG(L_CRIT, "dlg_flag parameter found but with wrong type: %d\n", type);
b8d12a32
         return -1;
     }
 
     dialog_flag = *param;
 
     // register dialog creation callback
     if (dlg_api.register_dlgcb(NULL, DLGCB_CREATED, __dialog_created, NULL, NULL) != 0) {
3ac29ef0
         LOG(L_CRIT, "cannot register callback for dialog creation\n");
b8d12a32
         return -1;
     }
 
     // register dialog loading callback
     if (dlg_api.register_dlgcb(NULL, DLGCB_LOADED, __dialog_loaded, NULL, NULL) != 0) {
3ac29ef0
         LOG(L_ERR, "cannot register callback for dialogs loaded from the database\n");
b8d12a32
     }
 
     // register a pre-script callback to automatically enable dialog tracing
     if (register_script_cb(postprocess_request, POST_SCRIPT_CB|REQUEST_CB, 0) != 0) {
         LOG(L_CRIT, "ERROR:call_control:mod_init: could not register request postprocessing callback\n");
         return -1;
     }
 
     return 0;
 }
 
 
 static int
 child_init(int rank)
 {
     // initialize the connection to callcontrol if needed
     if (!disable)
         callcontrol_connect();
 
     return 0;
 }
 
 
66b8b7d0
 static void
 destroy(void) {
 	if (cc_init_avps)
 		destroy_list(cc_init_avps);
 
 	if (cc_start_avps)
 		destroy_list(cc_start_avps);
 
 	if (cc_stop_avps)
 		destroy_list(cc_stop_avps);
 }
 
 
b8d12a32
 // Postprocess a request after the main script route is done.
 //
 // After all script processing is done, check if the dialog was actually
 // created to take care of call control. If the FL_USE_CALL_CONTROL flag
 // is still set, then the dialog creation callback was not called which
 // means that there was a failure relaying the message and we have to
 // tell the call control application to discard the call, otherwise it
 // would remain dangling until it expires.
 //
 static int
f5b0f1dc
 postprocess_request(struct sip_msg *msg, unsigned int flags, void *_param)
b8d12a32
 {
     CallInfo *call;
 
     if ((msg->msg_flags & FL_USE_CALL_CONTROL) == 0)
         return 1;
 
     // the FL_USE_CALL_CONTROL flag is still set => the dialog was not created
 
3ac29ef0
     LOG(L_WARN, "dialog to trace controlled call was not created. discarding callcontrol.");
b8d12a32
 
     call = get_call_info(msg, CAStop);
     if (!call) {
3ac29ef0
         LOG(L_ERR, "can't retrieve call info\n");
b8d12a32
         return -1;
     }
     call_control_stop(msg, call->callid);
 
     return 1;
 }