mediaproxy.c

00001 /* 
00002  * Copyright (C) 2004-2008 Dan Pascu
00003  * Copyright (C) 2009 Juha Heinanen (multipart hack)
00004  *
00005  * This file is part of SIP-Router, a free SIP server.
00006  *
00007  * SIP-router is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version
00011  *
00012  * SIP-router is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  */
00021 
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <unistd.h>
00025 #include <string.h>
00026 #include <time.h>
00027 #include <ctype.h>
00028 #include <errno.h>
00029 #include <arpa/inet.h>
00030 #include <sys/time.h>
00031 #include <sys/types.h>
00032 #include <sys/socket.h>
00033 #include <sys/select.h>
00034 #include <sys/un.h>
00035 
00036 #include "../../sr_module.h"
00037 #include "../../dprint.h"
00038 #include "../../str.h"
00039 #include "../../pvar.h"
00040 #include "../../error.h"
00041 #include "../../data_lump.h"
00042 #include "../../mem/mem.h"
00043 #include "../../ut.h"
00044 #include "../../parser/msg_parser.h"
00045 #include "../../parser/parse_from.h"
00046 #include "../../parser/parse_to.h"
00047 #include "../../parser/parse_param.h"
00048 #include "../../msg_translator.h"
00049 #include "../../modules_k/dialog/dlg_load.h"
00050 #include "../../modules_k/dialog/dlg_hash.h"
00051 
00052 
00053 MODULE_VERSION
00054 
00055 
00056 #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
00057 # define INLINE inline
00058 #else
00059 # define INLINE
00060 #endif
00061 
00062 /* WARNING: Keep this aligned with parser/msg_parser.h! */
00063 #define FL_USE_MEDIA_PROXY (1<<30)
00064 
00065 #define SIGNALING_IP_AVP_SPEC  "$avp(s:signaling_ip)"
00066 #define MEDIA_RELAY_AVP_SPEC   "$avp(s:media_relay)"
00067 #define ICE_CANDIDATE_AVP_SPEC "$avp(s:ice_candidate)"
00068 
00069 #define NO_CANDIDATE -1
00070 
00071 // Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to
00072 // more systems.  `AF_UNIX' was the traditional name stemming from BSD, so
00073 // even most POSIX systems support it.  It is also the name of choice in
00074 // the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX
00075 #ifndef AF_LOCAL
00076 # define AF_LOCAL AF_UNIX
00077 #endif
00078 
00079 // As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall,
00080 // it is defined as 0
00081 #ifndef MSG_NOSIGNAL
00082 # define MSG_NOSIGNAL 0
00083 #endif
00084 
00085 
00086 #define isnulladdr(adr)  ((adr).len==7 && memcmp("0.0.0.0", (adr).s, 7)==0)
00087 #define isnullport(port) ((port).len==1 && (port).s[0]=='0')
00088 
00089 #define STR_MATCH(str, buf)  ((str).len==strlen(buf) && memcmp(buf, (str).s, (str).len)==0)
00090 #define STR_IMATCH(str, buf) ((str).len==strlen(buf) && strncasecmp(buf, (str).s, (str).len)==0)
00091 
00092 #define STR_HAS_PREFIX(str, prefix)  ((str).len>=(prefix).len && memcmp((prefix).s, (str).s, (prefix).len)==0)
00093 #define STR_HAS_IPREFIX(str, prefix) ((str).len>=(prefix).len && strncasecmp((prefix).s, (str).s, (prefix).len)==0)
00094 
00095 
00096 typedef int Bool;
00097 #define True  1
00098 #define False 0
00099 
00100 
00101 typedef Bool (*NatTestFunction)(struct sip_msg *msg);
00102 
00103 
00104 typedef enum {
00105     TNone=0,
00106     TSupported,
00107     TUnsupported
00108 } TransportType;
00109 
00110 #define RETRY_INTERVAL 10
00111 #define BUFFER_SIZE    8192
00112 
00113 typedef struct MediaproxySocket {
00114     char *name;             // name
00115     int  sock;              // socket
00116     int  timeout;           // how many miliseconds to wait for an answer
00117     time_t last_failure;    // time of the last failure
00118     char data[BUFFER_SIZE]; // buffer for the answer data
00119 } MediaproxySocket;
00120 
00121 
00122 typedef struct {
00123     const char *name;
00124     uint32_t address;
00125     uint32_t mask;
00126 } NetInfo;
00127 
00128 typedef struct {
00129     str type;      // stream type (`audio', `video', `image', ...)
00130     str ip;
00131     str port;
00132     str rtcp_ip;   // pointer to the rtcp IP if explicitly specified by stream
00133     str rtcp_port; // pointer to the rtcp port if explicitly specified by stream
00134     str direction;
00135     Bool local_ip; // true if the IP is locally defined inside this media stream
00136     Bool has_ice;
00137     Bool has_rtcp_ice;
00138     TransportType transport;
00139     char *start_line;
00140     char *next_line;
00141     char *first_ice_candidate;
00142 } StreamInfo;
00143 
00144 #define MAX_STREAMS 32
00145 typedef struct SessionInfo {
00146     str ip;
00147     str ip_line;   // pointer to the whole session level ip line
00148     str direction;
00149     str separator;
00150     StreamInfo streams[MAX_STREAMS];
00151     unsigned int stream_count;
00152     unsigned int supported_count;
00153 } SessionInfo;
00154 
00155 typedef struct AVP_Param {
00156     str spec;
00157     int_str name;
00158     unsigned short type;
00159 } AVP_Param;
00160 
00161 typedef struct ice_candidate_data {
00162     unsigned int priority;
00163     Bool skip_next_reply;
00164 } ice_candidate_data;
00165 
00166 // Function prototypes
00167 //
00168 static int EngageMediaProxy(struct sip_msg *msg);
00169 static int UseMediaProxy(struct sip_msg *msg);
00170 static int EndMediaSession(struct sip_msg *msg);
00171 
00172 static int mod_init(void);
00173 static int child_init(int rank);
00174 
00175 
00176 // Module global variables and state
00177 //
00178 static int mediaproxy_disabled = False;
00179 static str ice_candidate = str_init("none");
00180 
00181 static MediaproxySocket mediaproxy_socket = {
00182     "/var/run/mediaproxy/dispatcher.sock", // name
00183     -1,                                    // sock
00184     500,                                   // timeout in 500 miliseconds if there is no answer
00185     0,                                     // time of the last failure
00186     ""                                     // data
00187 };
00188 
00189 
00190 struct dlg_binds dlg_api;
00191 Bool have_dlg_api = False;
00192 static int dialog_flag = -1;
00193 
00194 // The AVP where the caller signaling IP is stored (if defined)
00195 static AVP_Param signaling_ip_avp = {str_init(SIGNALING_IP_AVP_SPEC), {0}, 0};
00196 
00197 // The AVP where the application-defined media relay IP is stored
00198 static AVP_Param media_relay_avp = {str_init(MEDIA_RELAY_AVP_SPEC), {0}, 0};
00199 
00200 // The AVP where the ICE candidate priority is stored (if defined)
00201 static AVP_Param ice_candidate_avp = {str_init(ICE_CANDIDATE_AVP_SPEC), {0}, 0};
00202 
00203 static cmd_export_t commands[] = {
00204     {"engage_media_proxy", (cmd_function)EngageMediaProxy, 0, 0, 0, REQUEST_ROUTE},
00205     {"use_media_proxy",    (cmd_function)UseMediaProxy,    0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE},
00206     {"end_media_session",  (cmd_function)EndMediaSession,  0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE},
00207     {0, 0, 0, 0, 0, 0}
00208 };
00209 
00210 static param_export_t parameters[] = {
00211     {"disable",            INT_PARAM, &mediaproxy_disabled},
00212     {"mediaproxy_socket",  STR_PARAM, &(mediaproxy_socket.name)},
00213     {"mediaproxy_timeout", INT_PARAM, &(mediaproxy_socket.timeout)},
00214     {"signaling_ip_avp",   STR_PARAM, &(signaling_ip_avp.spec.s)},
00215     {"media_relay_avp",    STR_PARAM, &(media_relay_avp.spec.s)},
00216     {"ice_candidate",      STR_PARAM, &(ice_candidate.s)},
00217     {"ice_candidate_avp",  STR_PARAM, &(ice_candidate_avp.spec.s)},
00218     {0, 0, 0}
00219 };
00220 
00221 struct module_exports exports = {
00222     "mediaproxy",    // module name
00223     DEFAULT_DLFLAGS, // dlopen flags
00224     commands,        // exported functions
00225     parameters,      // exported parameters
00226     NULL,            // exported statistics
00227     NULL,            // exported MI functions
00228     NULL,            // exported pseudo-variables
00229     NULL,            // extra processes
00230     mod_init,        // module init function (before fork. kids will inherit)
00231     NULL,            // reply processing function
00232     NULL,            // destroy function
00233     child_init       // child init function
00234 };
00235 
00236 
00237 
00238 // String processing functions
00239 //
00240 
00241 // strfind() finds the start of the first occurrence of the substring needle
00242 // of length nlen in the memory area haystack of length len.
00243 static void*
00244 strfind(const void *haystack, size_t len, const void *needle, size_t nlen)
00245 {
00246     char *sp;
00247 
00248     // Sanity check
00249     if(!(haystack && needle && nlen && len>=nlen))
00250         return NULL;
00251 
00252     for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
00253         if (*sp == *(char*)needle && memcmp(sp, needle, nlen)==0) {
00254             return sp;
00255         }
00256     }
00257 
00258     return NULL;
00259 }
00260 
00261 // strcasefind() finds the start of the first occurrence of the substring
00262 // needle of length nlen in the memory area haystack of length len by doing
00263 // a case insensitive search
00264 static void*
00265 strcasefind(const char *haystack, size_t len, const char *needle, size_t nlen)
00266 {
00267     char *sp;
00268 
00269     // Sanity check
00270     if(!(haystack && needle && nlen && len>=nlen))
00271         return NULL;
00272 
00273     for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
00274         if (tolower(*sp) == tolower(*(char*)needle) &&
00275             strncasecmp(sp, needle, nlen)==0) {
00276             return sp;
00277         }
00278     }
00279 
00280     return NULL;
00281 }
00282 
00283 // returns string with whitespace trimmed from left end
00284 static INLINE void
00285 ltrim(str *string)
00286 {
00287     while (string->len>0 && isspace((int)*(string->s))) {
00288         string->len--;
00289         string->s++;
00290     }
00291 }
00292 
00293 // returns string with whitespace trimmed from right end
00294 static INLINE void
00295 rtrim(str *string)
00296 {
00297     char *ptr;
00298 
00299     ptr = string->s + string->len - 1;
00300     while (string->len>0 && (*ptr==0 || isspace((int)*ptr))) {
00301         string->len--;
00302         ptr--;
00303     }
00304 }
00305 
00306 // returns string with whitespace trimmed from both ends
00307 static INLINE void
00308 trim(str *string)
00309 {
00310     ltrim(string);
00311     rtrim(string);
00312 }
00313 
00314 // returns a pointer to first CR or LF char found or the end of string
00315 static char*
00316 findendline(char *string, int len)
00317 {
00318     char *ptr = string;
00319 
00320     while(ptr - string < len && *ptr != '\n' && *ptr != '\r')
00321         ptr++;
00322 
00323     return ptr;
00324 }
00325 
00326 
00327 static int
00328 strtoint(str *data)
00329 {
00330     long int result;
00331     char c;
00332 
00333     // hack to avoid copying the string
00334     c = data->s[data->len];
00335     data->s[data->len] = 0;
00336     result = strtol(data->s, NULL, 10);
00337     data->s[data->len] = c;
00338 
00339     return (int)result;
00340 }
00341 
00342 
00343 // find a line in str `block' that starts with `start'.
00344 static char*
00345 find_line_starting_with(str *block, char *start, int ignoreCase)
00346 {
00347     char *ptr, *bend;
00348     str zone;
00349     int tlen;
00350 
00351     bend = block->s + block->len;
00352     tlen = strlen(start);
00353     ptr = NULL;
00354 
00355     for (zone = *block; zone.len > 0; zone.len = bend - zone.s) {
00356         if (ignoreCase)
00357             ptr = strcasefind(zone.s, zone.len, start, tlen);
00358         else
00359             ptr = strfind(zone.s, zone.len, start, tlen);
00360         if (!ptr || ptr==block->s || ptr[-1]=='\n' || ptr[-1]=='\r')
00361             break;
00362         zone.s = ptr + tlen;
00363     }
00364 
00365     return ptr;
00366 }
00367 
00368 
00369 // count all lines in str `block' that starts with `start'.
00370 static unsigned int
00371 count_lines_starting_with(str *block, char *start, int ignoreCase)
00372 {
00373     char *ptr, *bend;
00374     str zone;
00375     int tlen;
00376     unsigned count;
00377 
00378     bend = block->s + block->len;
00379     tlen = strlen(start);
00380 
00381     count = 0;
00382 
00383     for (zone = *block; zone.len > 0; zone.len = bend - zone.s) {
00384         if (ignoreCase)
00385             ptr = strcasefind(zone.s, zone.len, start, tlen);
00386         else
00387             ptr = strfind(zone.s, zone.len, start, tlen);
00388         if (!ptr)
00389             break;
00390         if (ptr==block->s || ptr[-1]=='\n' || ptr[-1]=='\r')
00391             count++;
00392         zone.s = ptr + tlen;
00393     }
00394 
00395     return count;
00396 }
00397 
00398 
00399 // get up to `limit' whitespace separated tokens from `char *string'
00400 static int
00401 get_tokens(char *string, str *tokens, int limit)
00402 {
00403     int i, len, size;
00404     char *ptr;
00405 
00406     if (!string) {
00407         return 0;
00408     }
00409 
00410     len  = strlen(string);
00411 
00412     for (ptr=string, i=0; i<limit && len>0; i++) {
00413         size = strspn(ptr, " \t\n\r");
00414         ptr += size;
00415         len -= size;
00416         if (len <= 0)
00417             break;
00418         size = strcspn(ptr, " \t\n\r");
00419         if (size==0)
00420             break;
00421         tokens[i].s = ptr;
00422         tokens[i].len = size;
00423         ptr += size;
00424         len -= size;
00425     }
00426 
00427     return i;
00428 }
00429 
00430 // get up to `limit' whitespace separated tokens from `str *string'
00431 static int
00432 get_str_tokens(str *string, str *tokens, int limit)
00433 {
00434     int count;
00435     char c;
00436 
00437     if (!string || !string->s) {
00438         return 0;
00439     }
00440 
00441     c = string->s[string->len];
00442     string->s[string->len] = 0;
00443 
00444     count = get_tokens(string->s, tokens, limit);
00445 
00446     string->s[string->len] = c;
00447 
00448     return count;
00449 }
00450 
00451 
00452 // Functions to extract the info we need from the SIP/SDP message
00453 //
00454 
00455 static Bool
00456 get_callid(struct sip_msg* msg, str *cid)
00457 {
00458     if (msg->callid == NULL) {
00459         if (parse_headers(msg, HDR_CALLID_F, 0) == -1) {
00460             LM_ERR("cannot parse Call-ID header\n");
00461             return False;
00462         }
00463         if (msg->callid == NULL) {
00464             LM_ERR("missing Call-ID header\n");
00465             return False;
00466         }
00467     }
00468 
00469     *cid = msg->callid->body;
00470 
00471     trim(cid);
00472 
00473     return True;
00474 }
00475 
00476 static Bool
00477 get_cseq_number(struct sip_msg *msg, str *cseq)
00478 {
00479     if (msg->cseq == NULL) {
00480         if (parse_headers(msg, HDR_CSEQ_F, 0)==-1) {
00481             LM_ERR("cannot parse CSeq header\n");
00482             return False;
00483         }
00484         if (msg->cseq == NULL) {
00485             LM_ERR("missing CSeq header\n");
00486             return False;
00487         }
00488         }
00489 
00490         *cseq = get_cseq(msg)->number;
00491 
00492     if (cseq->s==NULL || cseq->len==0) {
00493         LM_ERR("missing CSeq number\n");
00494         return False;
00495     }
00496 
00497     return True;
00498 }
00499 
00500 static str
00501 get_from_uri(struct sip_msg *msg)
00502 {
00503     static str unknown = str_init("unknown");
00504     str uri;
00505     char *ptr;
00506 
00507     if (parse_from_header(msg) < 0) {
00508         LM_ERR("cannot parse the From header\n");
00509         return unknown;
00510     }
00511 
00512     uri = get_from(msg)->uri;
00513 
00514     if (uri.len == 0)
00515         return unknown;
00516 
00517     if (strncasecmp(uri.s, "sip:", 4)==0) {
00518         uri.s += 4;
00519         uri.len -= 4;
00520     }
00521 
00522     if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
00523         uri.len = ptr - uri.s;
00524     }
00525 
00526     return uri;
00527 }
00528 
00529 
00530 static str
00531 get_to_uri(struct sip_msg *msg)
00532 {
00533     static str unknown = str_init("unknown");
00534     str uri;
00535     char *ptr;
00536 
00537     if (!msg->to) {
00538         LM_ERR("missing To header\n");
00539         return unknown;
00540     }
00541 
00542     uri = get_to(msg)->uri;
00543 
00544     if (uri.len == 0)
00545         return unknown;
00546 
00547     if (strncasecmp(uri.s, "sip:", 4)==0) {
00548         uri.s += 4;
00549         uri.len -= 4;
00550     }
00551 
00552     if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
00553         uri.len = ptr - uri.s;
00554     }
00555 
00556     return uri;
00557 }
00558 
00559 
00560 static str
00561 get_from_tag(struct sip_msg *msg)
00562 {
00563     static str undefined = str_init("");
00564     str tag;
00565 
00566     if (parse_from_header(msg) < 0) {
00567         LM_ERR("cannot parse the From header\n");
00568         return undefined;
00569     }
00570 
00571     tag = get_from(msg)->tag_value;
00572 
00573     if (tag.len == 0)
00574         return undefined;
00575 
00576     return tag;
00577 }
00578 
00579 
00580 static str
00581 get_to_tag(struct sip_msg *msg)
00582 {
00583     static str undefined = str_init("");
00584     str tag;
00585 
00586     if (msg->first_line.type==SIP_REPLY && msg->REPLY_STATUS<200) {
00587         // Ignore the To tag for provisional replies
00588         return undefined;
00589     }
00590 
00591     if (!msg->to) {
00592         LM_ERR("missing To header\n");
00593         return undefined;
00594     }
00595 
00596     tag = get_to(msg)->tag_value;
00597 
00598     if (tag.len == 0)
00599         return undefined;
00600 
00601     return tag;
00602 }
00603 
00604 
00605 static str
00606 get_user_agent(struct sip_msg* msg)
00607 {
00608     static str unknown = str_init("unknown agent");
00609     str block, server;
00610     char *ptr;
00611 
00612     if (parse_headers(msg, HDR_USERAGENT_F, 0)==0 && msg->user_agent &&
00613         msg->user_agent->body.s && msg->user_agent->body.len>0) {
00614         return msg->user_agent->body;
00615     }
00616 
00617     // If we can't find user-agent, look after the `Server' header
00618     // This is a temporary hack. Normally it should be extracted by sip-router.
00619 
00620     block.s   = msg->buf;
00621     block.len = msg->len;
00622 
00623     ptr = find_line_starting_with(&block, "Server:", True);
00624     if (!ptr)
00625         return unknown;
00626 
00627     server.s   = ptr + 7;
00628     server.len = findendline(server.s, block.s+block.len-server.s) - server.s;
00629 
00630     trim(&server);
00631     if (server.len == 0)
00632         return unknown;
00633 
00634     return server;
00635 }
00636 
00637 
00638 // Get caller signaling IP
00639 static str
00640 get_signaling_ip(struct sip_msg* msg)
00641 {
00642     int_str value;
00643 
00644     if (!search_first_avp(signaling_ip_avp.type | AVP_VAL_STR,
00645                           signaling_ip_avp.name, &value, NULL) ||
00646         value.s.s==NULL || value.s.len==0) {
00647 
00648         value.s.s = ip_addr2a(&msg->rcv.src_ip);
00649         value.s.len = strlen(value.s.s);
00650     }
00651 
00652     return value.s;
00653 }
00654 
00655 // Get the application-defined media_relay if defined
00656 static str
00657 get_media_relay(struct sip_msg* msg)
00658 {
00659     static str undefined = str_init("");
00660     int_str value;
00661 
00662     if (!search_first_avp(media_relay_avp.type | AVP_VAL_STR,
00663                           media_relay_avp.name, &value, NULL) || value.s.s==NULL || value.s.len==0) {
00664         return undefined;
00665     }
00666 
00667     return value.s;
00668 }
00669 
00670 
00671 // Functions to manipulate the SDP message body
00672 //
00673 
00674 
00675 static int
00676 find_content_type_application_sdp(struct sip_msg *msg, str *sdp)
00677 {
00678     str type, params, boundary;
00679     char *start, *s;
00680     unsigned int len;
00681     Bool done;
00682     param_hooks_t hooks;
00683     param_t *p, *list;
00684 
00685     if (!msg->content_type) {
00686         LM_WARN("the Content-Type header is missing! Assume the content type is text/plain\n");
00687         return 1;
00688     }
00689 
00690     type = msg->content_type->body;
00691     trim(&type);
00692 
00693     if (strncasecmp(type.s, "application/sdp", 15) == 0) {
00694         done = True;
00695     } else if (strncasecmp(type.s, "multipart/mixed", 15) == 0) {
00696         done = False;
00697     } else {
00698         LM_ERR("invalid Content-Type for SDP: %.*s\n", type.len, type.s);
00699         return -1;
00700     }
00701 
00702     if (!(isspace((int)type.s[15]) || type.s[15] == ';' || type.s[15] == 0)) {
00703         LM_ERR("invalid character after Content-Type: `%c'\n", type.s[15]);
00704         return -1;
00705     }
00706 
00707     if (done) return 1;
00708 
00709     // Hack to find application/sdp bodypart
00710     params.s = memchr(msg->content_type->body.s, ';', 
00711                       msg->content_type->body.len);
00712     if (params.s == NULL) {
00713         LM_ERR("Content-Type hdr has no params\n");
00714         return -1;
00715     }
00716     params.len = msg->content_type->body.len - 
00717         (params.s - msg->content_type->body.s);
00718     if (parse_params(&params, CLASS_ANY, &hooks, &list) < 0) {
00719         LM_ERR("while parsing Content-Type params\n");
00720         return -1;
00721     }
00722     boundary.s = NULL;
00723     boundary.len = 0;
00724     for (p = list; p; p = p->next) {
00725         if ((p->name.len == 8)
00726             && (strncasecmp(p->name.s, "boundary", 8) == 0)) {
00727             boundary.s = pkg_malloc(p->body.len + 2 + 1);
00728             if (boundary.s == NULL) {
00729                 free_params(list);
00730                 LM_ERR("no memory for boundary string\n");
00731                 return -1;
00732             }
00733             *(boundary.s) = '-';
00734             *(boundary.s + 1) = '-';
00735             memcpy(boundary.s + 2, p->body.s, p->body.len);
00736             boundary.len = 2 + p->body.len;
00737             *(boundary.s + boundary.len) = 0;
00738             LM_DBG("boundary is <%.*s>\n", boundary.len, boundary.s);
00739             break;
00740         }
00741     }
00742     free_params(list);
00743     if (boundary.s == NULL) {
00744         LM_ERR("no mandatory param \";boundary\"\n");
00745         return -1;
00746     }
00747 
00748     while ((s = find_line_starting_with(sdp, "Content-Type: ", True))) {
00749         start = s + 14;
00750         len = sdp->len - (s - sdp->s) - 14;
00751         if (len > 15 + 2) {
00752             if (strncasecmp(start, "application/sdp", 15) == 0) {
00753                 start = start + 15;
00754                 if ((*start != 13) || (*(start + 1) != 10)) {
00755                     LM_ERR("no CRLF found after content type\n");
00756                     goto err;
00757                 }
00758                 start = start + 2;
00759                 len = len - 15 - 2;
00760                 while ((len > 0) && ((*start == 13) || (*start == 10))) {
00761                     len = len - 1;
00762                     start = start + 1;
00763                 }
00764                 sdp->s = start;
00765                 sdp->len = len;
00766                 s = find_line_starting_with(sdp, boundary.s, False);
00767                 if (s == NULL) {
00768                     LM_ERR("boundary not found after bodypart\n");
00769                     goto err;
00770                 }
00771                 sdp->len = s - start - 2;
00772                 pkg_free(boundary.s);
00773                 return 1;
00774             }
00775         }
00776     }
00777     LM_ERR("no application/sdp bodypart found\n");
00778 
00779  err:
00780     pkg_free(boundary.s);
00781     return -1;
00782 }
00783 
00784 
00785 // Get the SDP message from SIP message and check it's Content-Type
00786 // Return values:
00787 //    1 - success
00788 //   -1 - error in getting body or invalid content type
00789 //   -2 - empty message
00790 static int
00791 get_sdp_message(struct sip_msg *msg, str *sdp)
00792 {
00793     sdp->s = get_body(msg);
00794     if (sdp->s==NULL) {
00795         LM_ERR("cannot get the SDP body\n");
00796         return -1;
00797     }
00798 
00799     sdp->len = msg->buf + msg->len - sdp->s;
00800     if (sdp->len == 0)
00801         return -2;
00802 
00803     return find_content_type_application_sdp(msg, sdp);
00804 }
00805 
00806 
00807 // Return a str containing the line separator used in the SDP body
00808 static str
00809 get_sdp_line_separator(str *sdp)
00810 {
00811     char *ptr, *end_ptr, *sdp_end;
00812     str separator;
00813 
00814     sdp_end = sdp->s + sdp->len;
00815 
00816     ptr = find_line_starting_with(sdp, "v=", False);
00817     end_ptr = findendline(ptr, sdp_end-ptr);
00818     separator.s = ptr = end_ptr;
00819     while ((*ptr=='\n' || *ptr=='\r') && ptr<sdp_end)
00820         ptr++;
00821     separator.len = ptr - separator.s;
00822     if (separator.len > 2)
00823         separator.len = 2; // safety check
00824 
00825     return separator;
00826 }
00827 
00828 
00829 // will return the direction attribute defined in the given block.
00830 // if missing, default is used if provided, else `sendrecv' is used.
00831 static str
00832 get_direction_attribute(str *block, str *default_direction)
00833 {
00834     str direction, zone, line;
00835     char *ptr;
00836 
00837     for (zone=*block;;) {
00838         ptr = find_line_starting_with(&zone, "a=", False);
00839         if (!ptr) {
00840             if (default_direction)
00841                 return *default_direction;
00842             direction.s = "sendrecv";
00843             direction.len = 8;
00844             return direction;
00845         }
00846 
00847         line.s = ptr + 2;
00848         line.len = findendline(line.s, zone.s + zone.len - line.s) - line.s;
00849 
00850         if (line.len==8) {
00851             if (strncasecmp(line.s, "sendrecv", 8)==0 || strncasecmp(line.s, "sendonly", 8)==0 ||
00852                 strncasecmp(line.s, "recvonly", 8)==0 || strncasecmp(line.s, "inactive", 8)==0) {
00853                 return line;
00854             }
00855         }
00856 
00857         zone.s   = line.s + line.len;
00858         zone.len = block->s + block->len - zone.s;
00859     }
00860 }
00861 
00862 
00863 // will return the rtcp port of the stream in the given block
00864 // if defined by the stream, otherwise will return {NULL, 0}.
00865 static str
00866 get_rtcp_port_attribute(str *block)
00867 {
00868     str zone, rtcp_port, undefined = {NULL, 0};
00869     char *ptr;
00870     int count;
00871 
00872     ptr = find_line_starting_with(block, "a=rtcp:", False);
00873 
00874     if (!ptr)
00875         return undefined;
00876 
00877     zone.s = ptr + 7;
00878     zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s;
00879 
00880     count = get_str_tokens(&zone, &rtcp_port, 1);
00881 
00882     if (count != 1) {
00883         LM_ERR("invalid `a=rtcp' line in SDP body\n");
00884         return undefined;
00885     }
00886 
00887     return rtcp_port;
00888 }
00889 
00890 
00891 // will return the rtcp IP of the stream in the given block
00892 // if defined by the stream, otherwise will return {NULL, 0}.
00893 static str
00894 get_rtcp_ip_attribute(str *block)
00895 {
00896     str zone, tokens[4], undefined = {NULL, 0};
00897     char *ptr;
00898     int count;
00899 
00900     ptr = find_line_starting_with(block, "a=rtcp:", False);
00901 
00902     if (!ptr)
00903         return undefined;
00904 
00905     zone.s = ptr + 7;
00906     zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s;
00907 
00908     count = get_str_tokens(&zone, tokens, 4);
00909 
00910     if (count != 4) {
00911         return undefined;
00912     }
00913 
00914     return tokens[3];
00915 }
00916 
00917 
00918 // will return true if the given block has both
00919 // a=ice-pwd and a=ice-ufrag attributes.
00920 static Bool
00921 has_ice_attributes(str *block)
00922 {
00923     char *ptr;
00924     ptr = find_line_starting_with(block, "a=ice-pwd:", False);
00925     if (ptr) {
00926         ptr = find_line_starting_with(block, "a=ice-ufrag:", False);
00927         if (ptr) {
00928             return True;
00929         }
00930     }
00931     return False;
00932 }
00933 
00934 
00935 // will return true if the given SDP has both
00936 // a=ice-pwd and a=ice-ufrag attributes at the
00937 // session level.
00938 static Bool
00939 has_session_ice_attributes(str *sdp)
00940 {
00941     str block;
00942     char *ptr;
00943 
00944     // session level ICE attributes can be found from the beginning up to the first media block
00945     ptr = find_line_starting_with(sdp, "m=", False);
00946     if (ptr) {
00947         block.s   = sdp->s;
00948         block.len = ptr - block.s;
00949     } else {
00950         block = *sdp;
00951     }
00952 
00953     return has_ice_attributes(&block);
00954 }
00955 
00956 
00957 // will return true if the given block contains
00958 // a a=candidate attribute. This should be called
00959 // for a stream, as a=candidate attribute is not
00960 // allowed at the session level
00961 static Bool
00962 has_ice_candidates(str *block)
00963 {
00964     char *ptr;
00965     ptr = find_line_starting_with(block, "a=candidate:", False);
00966     if (ptr) {
00967         return True;
00968     }
00969     return False;
00970 }
00971 
00972 
00973 // will return true if given block contains an ICE 
00974 // candidate with the given component ID
00975 static Bool
00976 has_ice_candidate_component(str *block, int id)
00977 {
00978     char *ptr, *block_end;
00979     int i, components, count;
00980     str chunk, zone, tokens[2];
00981 
00982     block_end = block->s + block->len;
00983     components = count_lines_starting_with(block, "a=candidate:", False);
00984     for (i=0, chunk=*block; i<components; i++) {
00985         ptr = find_line_starting_with(&chunk, "a=candidate:", False);
00986         if (!ptr)
00987             break;
00988 
00989         zone.s = ptr + 12;
00990         zone.len = findendline(zone.s, block_end - zone.s) - zone.s;
00991         count = get_str_tokens(&zone, tokens, 2);
00992 
00993         if (count == 2) {
00994             if (strtoint(&tokens[1]) == id) {
00995                 return True;
00996             }
00997         }
00998         
00999         chunk.s   = zone.s + zone.len;
01000         chunk.len = block_end - chunk.s;
01001     }
01002     return False;
01003 }
01004 
01005 
01006 // will return the priority (string value) that will be used
01007 // for the candidate(s) inserted
01008 static str
01009 get_ice_candidate(void)
01010 {
01011     int_str value;
01012 
01013     if (!search_first_avp(ice_candidate_avp.type | AVP_VAL_STR,
01014                           ice_candidate_avp.name, &value, NULL) || value.s.s==NULL || value.s.len==0) {
01015         // if AVP is not set use global module parameter
01016         return ice_candidate;
01017     } else {
01018         return value.s;
01019     }
01020 }
01021 
01022 
01023 // will return the priority (integer value) that will be used
01024 // for the candidate(s) inserted
01025 static unsigned int
01026 get_ice_candidate_priority(str priority)
01027 {
01028     int type_pref;
01029 
01030     if (STR_IMATCH(priority, "high-priority")) {
01031         // Use type preference even higher than host candidates
01032         type_pref = 130;
01033     } else if (STR_IMATCH(priority, "low-priority")) {
01034         type_pref = 0;
01035     } else {
01036         return NO_CANDIDATE;
01037     }
01038     // This will return the priority for the RTP component, the RTCP
01039     // component is RTP - 1
01040     return ((type_pref << 24) + 16777215);
01041 }
01042 
01043 
01044 // will return the ip address present in a `c=' line in the given block
01045 // returns: -1 on error, 0 if not found, 1 if found
01046 static int
01047 get_media_ip_from_block(str *block, str *mediaip)
01048 {
01049     str tokens[3], zone;
01050     char *ptr;
01051     int count;
01052 
01053     ptr = find_line_starting_with(block, "c=", False);
01054 
01055     if (!ptr) {
01056         mediaip->s   = NULL;
01057         mediaip->len = 0;
01058         return 0;
01059     }
01060 
01061     zone.s = ptr + 2;
01062     zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s;
01063 
01064     count = get_str_tokens(&zone, tokens, 3);
01065 
01066     if (count != 3) {
01067         LM_ERR("invalid `c=' line in SDP body\n");
01068         return -1;
01069     }
01070 
01071     // can also check if tokens[1] == 'IP4'
01072     *mediaip = tokens[2];
01073 
01074     return 1;
01075 }
01076 
01077 
01078 static Bool
01079 get_sdp_session_ip(str *sdp, str *mediaip, str *ip_line)
01080 {
01081     char *ptr, *end_ptr;
01082     str block;
01083 
01084     // session IP can be found from the beginning up to the first media block
01085     ptr = find_line_starting_with(sdp, "m=", False);
01086     if (ptr) {
01087         block.s   = sdp->s;
01088         block.len = ptr - block.s;
01089     } else {
01090         block = *sdp;
01091     }
01092 
01093     if (get_media_ip_from_block(&block, mediaip) == -1) {
01094         LM_ERR("parse error while getting session-level media IP from SDP\n");
01095         return False;
01096     }
01097 
01098     if (ip_line != NULL) {
01099         ptr = find_line_starting_with(&block, "c=", False);
01100         if (!ptr) {
01101             ip_line->s = NULL;
01102             ip_line->len = 0;
01103         } else {
01104             end_ptr = findendline(ptr, block.s + block.len - ptr);
01105             while ((*end_ptr=='\n' || *end_ptr=='\r'))
01106                 end_ptr++;
01107             ip_line->s = ptr;
01108             ip_line->len = end_ptr - ptr;
01109         }
01110     }
01111 
01112     // it's not an error to be missing. it can be locally defined
01113     // by each media stream. thus we return true even if not found
01114     return True;
01115 }
01116 
01117 
01118 // will return the direction as defined at the session level
01119 // in the SDP. if missing, `sendrecv' is used.
01120 static str
01121 get_session_direction(str *sdp)
01122 {
01123     static str default_direction = str_init("sendrecv");
01124     str block;
01125     char *ptr;
01126 
01127     // session level direction can be found from the beginning up to the first media block
01128     ptr = find_line_starting_with(sdp, "m=", False);
01129     if (ptr) {
01130         block.s   = sdp->s;
01131         block.len = ptr - block.s;
01132     } else {
01133         block = *sdp;
01134     }
01135 
01136     return get_direction_attribute(&block, &default_direction);
01137 }
01138 
01139 
01140 // will return the method ID for a reply by inspecting the Cseq header
01141 static int
01142 get_method_from_reply(struct sip_msg *reply)
01143 {
01144     struct cseq_body *cseq;
01145 
01146     if (reply->first_line.type != SIP_REPLY)
01147         return -1;
01148 
01149     if (!reply->cseq && parse_headers(reply, HDR_CSEQ_F, 0) < 0) {
01150         LM_ERR("failed to parse the CSeq header\n");
01151         return -1;
01152     }
01153     if (!reply->cseq) {
01154         LM_ERR("missing CSeq header\n");
01155         return -1;
01156     }
01157     cseq = reply->cseq->parsed;
01158     return cseq->method_id;
01159 }
01160 
01161 static Bool
01162 supported_transport(str transport)
01163 {
01164     // supported transports: RTP/AVP, RTP/AVPF, RTP/SAVP, RTP/SAVPF, udp, udptl
01165     str prefixes[] = {str_init("RTP"), str_init("udp"), {NULL, 0}};
01166     int i;
01167 
01168     for (i=0; prefixes[i].s != NULL; i++) {
01169         if (STR_HAS_IPREFIX(transport, prefixes[i])) {
01170             return True;
01171         }
01172     }
01173 
01174     return False;
01175 }
01176 
01177 
01178 static int
01179 get_session_info(str *sdp, SessionInfo *session)
01180 {
01181     str tokens[3], ip, ip_line, block, zone;
01182     char *ptr, *sdp_end;
01183     int i, count, result;
01184 
01185     count = count_lines_starting_with(sdp, "v=", False);
01186     if (count != 1) {
01187         LM_ERR("cannot handle more than 1 media session in SDP\n");
01188         return -1;
01189     }
01190 
01191     count = count_lines_starting_with(sdp, "m=", False);
01192     if (count > MAX_STREAMS) {
01193         LM_ERR("cannot handle more than %d media streams in SDP\n", MAX_STREAMS);
01194         return -1;
01195     }
01196 
01197     memset(session, 0, sizeof(SessionInfo));
01198 
01199     if (count == 0)
01200         return 0;
01201 
01202     if (!get_sdp_session_ip(sdp, &ip, &ip_line)) {
01203         LM_ERR("failed to parse the SDP message\n");
01204         return -1;
01205     }
01206 
01207     ptr = memchr(ip.s, '/', ip.len);
01208     if (ptr) {
01209         LM_ERR("unsupported multicast IP specification in SDP: %.*s\n", ip.len, ip.s);
01210         return -1;
01211     }
01212 
01213     session->ip = ip;
01214     session->ip_line = ip_line;
01215     session->direction = get_session_direction(sdp);
01216     session->separator = get_sdp_line_separator(sdp);
01217     session->stream_count = count;
01218 
01219     sdp_end = sdp->s + sdp->len;
01220 
01221     for (i=0, block=*sdp; i<MAX_STREAMS; i++) {
01222         ptr = find_line_starting_with(&block, "m=", False);
01223 
01224         if (!ptr)
01225             break;
01226 
01227         zone.s = ptr + 2;
01228         zone.len = findendline(zone.s, sdp_end - zone.s) - zone.s;
01229 
01230         count = get_str_tokens(&zone, tokens, 3);
01231         if (count != 3) {
01232             LM_ERR("invalid `m=' line in the SDP body\n");
01233             return -1;
01234         }
01235 
01236         session->streams[i].start_line = ptr;
01237         session->streams[i].next_line = zone.s + zone.len + session->separator.len;
01238         if (session->streams[i].next_line > sdp_end)
01239             session->streams[i].next_line = sdp_end; //safety check
01240 
01241         if (supported_transport(tokens[2])) {
01242             // handle case where port is specified like <port>/<nr_of_ports>
01243             // as defined by RFC2327. ex: m=audio 5012/1 RTP/AVP 18 0 8
01244             // TODO: also handle case where nr_of_ports > 1  -Dan
01245             ptr = memchr(tokens[1].s, '/', tokens[1].len);
01246             if (ptr != NULL) {
01247                 str port_nr;
01248 
01249                 port_nr.s = ptr + 1;
01250                 port_nr.len = tokens[1].s + tokens[1].len - port_nr.s;
01251                 if (port_nr.len==0) {
01252                     LM_ERR("invalid port specification in `m=' line: %.*s\n", tokens[1].len, tokens[1].s);
01253                     return -1;
01254                 }
01255                 if (!(port_nr.len==1 && port_nr.s[0]=='1')) {
01256                     LM_ERR("unsupported number of ports specified in `m=' line\n");
01257                     return -1;
01258                 }
01259                 tokens[1].len = ptr - tokens[1].s;
01260             }
01261 
01262             session->streams[i].type = tokens[0];
01263             session->streams[i].port = tokens[1];
01264 
01265             session->streams[i].transport = TSupported;
01266             session->supported_count++;
01267         } else {
01268             // mark that we have an unsupported transport so we can ignore this stream later
01269             LM_INFO("unsupported transport in stream nr %d's `m=' line: %.*s\n", i+1, tokens[2].len, tokens[2].s);
01270             session->streams[i].type = tokens[0];
01271             session->streams[i].port = tokens[1];
01272             session->streams[i].transport = TUnsupported;
01273         }
01274 
01275         block.s   = zone.s + zone.len;
01276         block.len = sdp_end - block.s;
01277     }
01278 
01279     for (i=0; i<session->stream_count; i++) {
01280         block.s = session->streams[i].port.s;
01281         if (i < session->stream_count-1)
01282             block.len = session->streams[i+1].port.s - block.s;
01283         else
01284             block.len = sdp_end - block.s;
01285 
01286         result = get_media_ip_from_block(&block, &ip);
01287         if (result == -1) {
01288             LM_ERR("parse error while getting the contact IP for the "
01289                    "media stream number %d\n", i+1);
01290             return -1;
01291         } else if (result == 0) {
01292             if (session->ip.s == NULL) {
01293                 LM_ERR("media stream number %d doesn't define a contact IP "
01294                        "and the session-level IP is missing\n", i+1);
01295                 return -1;
01296             }
01297             session->streams[i].ip = session->ip;
01298             session->streams[i].local_ip = 0;
01299         } else {
01300             if (session->streams[i].transport == TSupported) {
01301                 ptr = memchr(ip.s, '/', ip.len);
01302                 if (ptr) {
01303                     LM_ERR("unsupported multicast IP specification in stream nr %d: %.*s\n", i+1, ip.len, ip.s);
01304                     return -1;
01305                 }
01306             }
01307             session->streams[i].ip = ip;
01308             session->streams[i].local_ip = 1;
01309         }
01310 
01311         session->streams[i].rtcp_ip = get_rtcp_ip_attribute(&block);
01312         session->streams[i].rtcp_port = get_rtcp_port_attribute(&block);
01313         session->streams[i].direction = get_direction_attribute(&block, &session->direction);
01314         session->streams[i].has_ice = ((has_ice_attributes(&block) || has_session_ice_attributes(sdp)) && has_ice_candidates(&block));
01315         session->streams[i].has_rtcp_ice = has_ice_candidate_component(&block, 2);
01316         session->streams[i].first_ice_candidate = find_line_starting_with(&block, "a=candidate:", False);
01317     }
01318 
01319     return session->stream_count;
01320 }
01321 
01322 
01323 static Bool
01324 insert_element(struct sip_msg *msg, char *position, char *element)
01325 {
01326     struct lump *anchor;
01327     char *buf;
01328     int len;
01329 
01330     len = strlen(element);
01331 
01332     buf = pkg_malloc(len);
01333     if (!buf) {
01334         LM_ERR("out of memory\n");
01335         return False;
01336     }
01337 
01338     anchor = anchor_lump(msg, position - msg->buf, 0, 0);
01339     if (!anchor) {
01340         LM_ERR("failed to get anchor for new element\n");
01341         pkg_free(buf);
01342         return False;
01343     }
01344 
01345     memcpy(buf, element, len);
01346 
01347     if (insert_new_lump_after(anchor, buf, len, 0)==0) {
01348         LM_ERR("failed to insert new element\n");
01349         pkg_free(buf);
01350         return False;
01351     }
01352 
01353     return True;
01354 }
01355 
01356 
01357 static Bool
01358 replace_element(struct sip_msg *msg, str *old_element, str *new_element)
01359 {
01360     struct lump *anchor;
01361     char *buf;
01362 
01363     if (new_element->len==old_element->len &&
01364         memcmp(new_element->s, old_element->s, new_element->len)==0) {
01365         return True;
01366     }
01367 
01368     buf = pkg_malloc(new_element->len);
01369     if (!buf) {
01370         LM_ERR("out of memory\n");
01371         return False;
01372     }
01373 
01374     anchor = del_lump(msg, old_element->s - msg->buf, old_element->len, 0);
01375     if (!anchor) {
01376         LM_ERR("failed to delete old element\n");
01377         pkg_free(buf);
01378         return False;
01379     }
01380 
01381     memcpy(buf, new_element->s, new_element->len);
01382 
01383     if (insert_new_lump_after(anchor, buf, new_element->len, 0)==0) {
01384         LM_ERR("failed to insert new element\n");
01385         pkg_free(buf);
01386         return False;
01387     }
01388 
01389     return True;
01390 }
01391 
01392 
01393 static Bool
01394 remove_element(struct sip_msg *msg, str *element)
01395 {
01396     if (!del_lump(msg, element->s - msg->buf, element->len, 0)) {
01397         LM_ERR("failed to delete old element\n");
01398         return False;
01399     }
01400 
01401     return True;
01402 }
01403 
01404 
01405 // Functions dealing with the external mediaproxy helper
01406 //
01407 
01408 static Bool
01409 mediaproxy_connect(void)
01410 {
01411     struct sockaddr_un addr;
01412 
01413     if (mediaproxy_socket.sock >= 0)
01414         return True;
01415 
01416     if (mediaproxy_socket.last_failure + RETRY_INTERVAL > time(NULL))
01417         return False;
01418 
01419     memset(&addr, 0, sizeof(addr));
01420     addr.sun_family = AF_LOCAL;
01421     strncpy(addr.sun_path, mediaproxy_socket.name, sizeof(addr.sun_path) - 1);
01422 #ifdef HAVE_SOCKADDR_SA_LEN
01423     addr.sun_len = strlen(addr.sun_path);
01424 #endif
01425 
01426     mediaproxy_socket.sock = socket(AF_LOCAL, SOCK_STREAM, 0);
01427     if (mediaproxy_socket.sock < 0) {
01428         LM_ERR("can't create socket\n");
01429         mediaproxy_socket.last_failure = time(NULL);
01430         return False;
01431     }
01432     if (connect(mediaproxy_socket.sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
01433         LM_ERR("failed to connect to %s: %s\n", mediaproxy_socket.name, strerror(errno));
01434         close(mediaproxy_socket.sock);
01435         mediaproxy_socket.sock = -1;
01436         mediaproxy_socket.last_failure = time(NULL);
01437         return False;
01438     }
01439 
01440     return True;
01441 }
01442 
01443 static void
01444 mediaproxy_disconnect(void)
01445 {
01446     if (mediaproxy_socket.sock < 0)
01447         return;
01448 
01449     close(mediaproxy_socket.sock);
01450     mediaproxy_socket.sock = -1;
01451     mediaproxy_socket.last_failure = time(NULL);
01452 }
01453 
01454 static char*
01455 send_command(char *command)
01456 {
01457     int cmd_len, bytes, tries, sent, received, count;
01458     struct timeval timeout;
01459     fd_set rset;
01460 
01461     if (!mediaproxy_connect())
01462         return NULL;
01463 
01464     cmd_len = strlen(command);
01465 
01466     for (sent=0, tries=0; sent<cmd_len && tries<3; tries++, sent+=bytes) {
01467         do
01468             bytes = send(mediaproxy_socket.sock, command+sent, cmd_len-sent, MSG_DONTWAIT|MSG_NOSIGNAL);
01469         while (bytes == -1 && errno == EINTR);
01470         if (bytes == -1) {
01471             switch (errno) {
01472             case ECONNRESET:
01473             case EPIPE:
01474                 mediaproxy_disconnect();
01475                 mediaproxy_socket.last_failure = 0; // we want to reconnect immediately
01476                 if (mediaproxy_connect()) {
01477                     sent = bytes = 0;
01478                     continue;
01479                 } else {
01480                     LM_ERR("connection with mediaproxy did die\n");
01481                 }
01482                 break;
01483             case EACCES:
01484                 LM_ERR("got permission denied while sending to %s\n", mediaproxy_socket.name);
01485                 break;
01486             case EWOULDBLOCK:
01487                 // this shouldn't happen as we read back all the answer after a request.
01488                 // if it would block, it means there is an error.
01489                 LM_ERR("sending command would block!\n");
01490                 break;
01491             default:
01492                 LM_ERR("%d: %s\n", errno, strerror(errno));
01493                 break;
01494             }
01495             mediaproxy_disconnect();
01496             return NULL;
01497         }
01498     }
01499     if (sent < cmd_len) {
01500         LM_ERR("couldn't send complete command after 3 tries\n");
01501         mediaproxy_disconnect();
01502         return NULL;
01503     }
01504 
01505     mediaproxy_socket.data[0] = 0;
01506     received = 0;
01507     while (True) {
01508         FD_ZERO(&rset);
01509         FD_SET(mediaproxy_socket.sock, &rset);
01510         timeout.tv_sec = mediaproxy_socket.timeout / 1000;
01511         timeout.tv_usec = (mediaproxy_socket.timeout % 1000) * 1000;
01512 
01513         do
01514             count = select(mediaproxy_socket.sock + 1, &rset, NULL, NULL, &timeout);
01515         while (count == -1 && errno == EINTR);
01516 
01517         if (count == -1) {
01518             LM_ERR("select failed: %d: %s\n", errno, strerror(errno));
01519             mediaproxy_disconnect();
01520             return NULL;
01521         } else if (count == 0) {
01522             LM_ERR("did timeout waiting for an answer\n");
01523             mediaproxy_disconnect();
01524             return NULL;
01525         } else {
01526             do
01527                 bytes = recv(mediaproxy_socket.sock, mediaproxy_socket.data+received, BUFFER_SIZE-1-received, 0);
01528             while (bytes == -1 && errno == EINTR);
01529             if (bytes == -1) {
01530                 LM_ERR("failed to read answer: %d: %s\n", errno, strerror(errno));
01531                 mediaproxy_disconnect();
01532                 return NULL;
01533             } else if (bytes == 0) {
01534                 LM_ERR("connection with mediaproxy closed\n");
01535                 mediaproxy_disconnect();
01536                 return NULL;
01537             } else {
01538                 mediaproxy_socket.data[received+bytes] = 0;
01539                 if (strstr(mediaproxy_socket.data+received, "\r\n")!=NULL) {
01540                     break;
01541                 }
01542                 received += bytes;
01543             }
01544         }
01545     }
01546 
01547     return mediaproxy_socket.data;
01548 }
01549 
01550 
01551 // Exported API implementation
01552 //
01553 
01554 // ice_candidate_data: it carries data across the dialog when using engage_media_proxy:
01555 //   - priority: the priority that should be used for the ICE candidate
01556 //      * -1: no candidate should be added.
01557 //      * other: the specified type preference should be used for calculating 
01558 //   - skip_next_reply: flag for knowing the fact that the next reply with SDP must be skipped
01559 //     because it is a reply to a re-INVITE or UPDATE *after* the ICE negotiation
01560 static int
01561 use_media_proxy(struct sip_msg *msg, char *dialog_id, ice_candidate_data *ice_data)
01562 {
01563     str callid, cseq, from_uri, to_uri, from_tag, to_tag, user_agent;
01564     str signaling_ip, media_relay, sdp, str_buf, tokens[MAX_STREAMS+1];
01565     str priority_str, candidate;
01566     char request[8192], media_str[4096], buf[128], *result, *type;
01567     int i, j, port, len, status;
01568     Bool removed_session_ip, have_sdp;
01569     SessionInfo session;
01570     StreamInfo stream;
01571     unsigned int priority;
01572 
01573     if (msg == NULL)
01574         return -1;
01575 
01576     if (msg->first_line.type == SIP_REQUEST) {
01577         type = "request";
01578     } else if (msg->first_line.type == SIP_REPLY) {
01579         if (ice_data != NULL && ice_data->skip_next_reply) {
01580             // we don't process replies to ICE negotiation end requests 
01581             // (those containing a=remote-candidates)
01582             ice_data->skip_next_reply = False;
01583             return -1;
01584         }
01585         type = "reply";
01586     } else {
01587         return -1;
01588     }
01589 
01590     if (!get_callid(msg, &callid)) {
01591         LM_ERR("failed to get Call-ID\n");
01592         return -1;
01593     }
01594 
01595     if (!get_cseq_number(msg, &cseq)) {
01596         LM_ERR("failed to get CSeq\n");
01597         return -1;
01598     }
01599 
01600     status = get_sdp_message(msg, &sdp);
01601     // status = -1 is error, -2 is missing SDP body
01602     if (status == -1 || (status == -2 && msg->first_line.type == SIP_REQUEST)) {
01603         return status;
01604     } else if (status == -2 && !(msg->REPLY_STATUS == 200 && get_method_from_reply(msg) == METHOD_INVITE)) {
01605         return -2;
01606     }
01607     have_sdp = (status == 1);
01608 
01609     if (have_sdp) {
01610         if (msg->first_line.type == SIP_REQUEST && find_line_starting_with(&sdp, "a=remote-candidates", False)) {
01611             // we don't process requests with a=remote-candidates, this indicates the end of an ICE
01612             // negotiation and we must not mangle the SDP.
01613             if (ice_data != NULL) {
01614                 ice_data->skip_next_reply = True;
01615             }
01616             return -1;
01617         }
01618        
01619         status = get_session_info(&sdp, &session);
01620         if (status < 0) {
01621             LM_ERR("can't extract media streams from the SDP message\n");
01622             return -1;
01623         }
01624 
01625         if (session.supported_count == 0)
01626             return 1; // there are no supported media streams. we have nothing to do.
01627 
01628         len = sprintf(media_str, "%s", "media: ");
01629         for (i=0, str_buf.len=sizeof(media_str)-len-2, str_buf.s=media_str+len; i<session.stream_count; i++) {
01630             stream = session.streams[i];
01631             if (stream.transport != TSupported)
01632                 continue; // skip streams with unsupported transports
01633             if (stream.type.len + stream.ip.len + stream.port.len + stream.direction.len + 4 > str_buf.len) {
01634                 LM_ERR("media stream description is longer than %lu bytes\n", (unsigned long)sizeof(media_str));
01635                 return -1;
01636             }
01637             len = sprintf(str_buf.s, "%.*s:%.*s:%.*s:%.*s:%s,",
01638                           stream.type.len, stream.type.s,
01639                           stream.ip.len, stream.ip.s,
01640                           stream.port.len, stream.port.s,
01641                           stream.direction.len, stream.direction.s,
01642                           stream.has_ice?"ice=yes":"ice=no");
01643             str_buf.s   += len;
01644             str_buf.len -= len;
01645         }
01646         *(str_buf.s-1) = 0; // remove the last comma
01647         sprintf(str_buf.s-1, "%s", "\r\n");
01648     } else {
01649         media_str[0] = 0;
01650     }
01651 
01652     from_uri     = get_from_uri(msg);
01653     to_uri       = get_to_uri(msg);
01654     from_tag     = get_from_tag(msg);
01655     to_tag       = get_to_tag(msg);
01656     user_agent   = get_user_agent(msg);
01657     signaling_ip = get_signaling_ip(msg);
01658     media_relay  = get_media_relay(msg);
01659 
01660     len = snprintf(request, sizeof(request),
01661                    "update\r\n"
01662                    "type: %s\r\n"
01663                    "dialog_id: %s\r\n"
01664                    "call_id: %.*s\r\n"
01665                    "cseq: %.*s\r\n"
01666                    "from_uri: %.*s\r\n"
01667                    "to_uri: %.*s\r\n"
01668                    "from_tag: %.*s\r\n"
01669                    "to_tag: %.*s\r\n"
01670                    "user_agent: %.*s\r\n"
01671                    "signaling_ip: %.*s\r\n"
01672                    "media_relay: %.*s\r\n"
01673                    "%s"
01674                    "\r\n",
01675                    type, dialog_id, callid.len, callid.s, cseq.len, cseq.s,
01676                    from_uri.len, from_uri.s, to_uri.len, to_uri.s,
01677                    from_tag.len, from_tag.s, to_tag.len, to_tag.s,
01678                    user_agent.len, user_agent.s,
01679                    signaling_ip.len, signaling_ip.s,
01680                    media_relay.len, media_relay.s, media_str);
01681 
01682     if (len >= sizeof(request)) {
01683         LM_ERR("mediaproxy request is longer than %lu bytes\n", (unsigned long)sizeof(request));
01684         return -1;
01685     }
01686 
01687     result = send_command(request);
01688 
01689     if (result == NULL)
01690         return -1;
01691 
01692     if (!have_sdp) {
01693         // we updated the dispatcher, we can't do anything else as
01694         // there is no SDP
01695         return 1;
01696     }
01697 
01698     len = get_tokens(result, tokens, sizeof(tokens)/sizeof(str));
01699 
01700     if (len == 0) {
01701         LM_ERR("empty response from mediaproxy\n");
01702         return -1;
01703     } else if (len==1 && STR_MATCH(tokens[0], "error")) {
01704         LM_ERR("mediaproxy returned error\n");
01705         return -1;
01706     } else if (len<session.supported_count+1) {
01707         if (msg->first_line.type == SIP_REQUEST) {
01708             LM_ERR("insufficient ports returned from mediaproxy: got %d, "
01709                    "expected %d\n", len-1, session.supported_count);
01710             return -1;
01711         } else {
01712             LM_WARN("broken client. Called UA added extra media stream(s) "
01713                     "in the OK reply\n");
01714         }
01715     }
01716 
01717     removed_session_ip = False;
01718 
01719     // only replace the session ip if there are no streams with unsupported
01720     // transports otherwise we insert an ip line in the supported streams
01721     // and remove the session level ip
01722     if (session.ip.s && !isnulladdr(session.ip)) {
01723         if (session.stream_count == session.supported_count) {
01724             if (!replace_element(msg, &session.ip, &tokens[0])) {
01725                 LM_ERR("failed to replace session-level media IP in the SDP body\n");
01726                 return -1;
01727             }
01728         } else {
01729             if (!remove_element(msg, &session.ip_line)) {
01730                 LM_ERR("failed to remove session-level media IP in the SDP body\n");
01731                 return -1;
01732             }
01733             removed_session_ip = True;
01734         }
01735     }
01736 
01737     for (i=0, j=1; i<session.stream_count; i++) {
01738         stream = session.streams[i];
01739         if (stream.transport != TSupported) {
01740             if (!stream.local_ip && removed_session_ip) {
01741                 strcpy(buf, "c=IN IP4 ");
01742                 strncat(buf, session.ip.s, session.ip.len);
01743                 strncat(buf, session.separator.s, session.separator.len);
01744                 if (!insert_element(msg, stream.next_line, buf)) {
01745                     LM_ERR("failed to insert IP address in media stream number %d\n", i+1);
01746                     return -1;
01747                 }
01748             }
01749             continue;
01750         }
01751 
01752         if (j >= len) {
01753             break;
01754         }
01755         
01756         if (!isnullport(stream.port)) {
01757             if (!replace_element(msg, &stream.port, &tokens[j])) {
01758                 LM_ERR("failed to replace port in media stream number %d\n", i+1);
01759                 return -1;
01760             }
01761         }
01762 
01763         if (stream.rtcp_port.len>0 && !isnullport(stream.rtcp_port)) {
01764             str rtcp_port;
01765 
01766             port = strtoint(&tokens[j]);
01767             rtcp_port.s = int2str(port+1, &rtcp_port.len);
01768             if (!replace_element(msg, &stream.rtcp_port, &rtcp_port)) {
01769                 LM_ERR("failed to replace RTCP port in media stream number %d\n", i+1);
01770                 return -1;
01771             }
01772         }
01773 
01774         if (stream.rtcp_ip.len > 0) {
01775             if (!replace_element(msg, &stream.rtcp_ip, &tokens[0])) {
01776                 LM_ERR("failed to replace RTCP IP in media stream number %d\n", i+1);
01777                 return -1;
01778             }
01779         }
01780 
01781         if (stream.local_ip && !isnulladdr(stream.ip)) {
01782             if (!replace_element(msg, &stream.ip, &tokens[0])) {
01783                 LM_ERR("failed to replace IP address in media stream number %d\n", i+1);
01784                 return -1;
01785             }
01786         } else if (!stream.local_ip && removed_session_ip) {
01787             strcpy(buf, "c=IN IP4 ");
01788             strncat(buf, tokens[0].s, tokens[0].len);
01789             strncat(buf, session.separator.s, session.separator.len);
01790             if (!insert_element(msg, stream.next_line, buf)) {
01791                 LM_ERR("failed to insert IP address in media stream number %d\n", i+1);
01792                 return -1;
01793             }
01794         }
01795 
01796         if (ice_data == NULL) {
01797             priority_str = get_ice_candidate();
01798         } else if (ice_data->priority == NO_CANDIDATE) {
01799             priority_str.s = "none";
01800         } else {
01801             // we don't need the string value, we'll use the number
01802             priority_str.s = "";
01803         }
01804         priority_str.len = strlen(priority_str.s);
01805 
01806         if (stream.has_ice && stream.first_ice_candidate && !STR_IMATCH(priority_str, "none")) {
01807             // add some pseudo-random string to the foundation
01808             struct in_addr hexip;
01809             inet_aton(tokens[0].s, &hexip);
01810 
01811             priority = (ice_data == NULL)?get_ice_candidate_priority(priority_str):ice_data->priority;
01812             port = strtoint(&tokens[j]);
01813             candidate.s = buf;
01814             candidate.len = sprintf(candidate.s, "a=candidate:R%x 1 UDP %u %.*s %i typ relay%.*s",
01815                                     hexip.s_addr,
01816                                     priority,
01817                                     tokens[0].len, tokens[0].s, 
01818                                     port,
01819                                     session.separator.len, session.separator.s);
01820 
01821             if (!insert_element(msg, stream.first_ice_candidate, candidate.s)) {
01822                 LM_ERR("failed to insert ICE candidate in media stream number %d\n", i+1);
01823                 return -1;
01824             }
01825 
01826             if (stream.has_rtcp_ice) {
01827                 candidate.s = buf;
01828                 candidate.len = sprintf(candidate.s, "a=candidate:R%x 2 UDP %u %.*s %i typ relay%.*s",
01829                                         hexip.s_addr,
01830                                         priority-1,
01831                                         tokens[0].len, tokens[0].s, 
01832                                         port+1,
01833                                         session.separator.len, session.separator.s);
01834 
01835                 if (!insert_element(msg, stream.first_ice_candidate, candidate.s)) {
01836                     LM_ERR("failed to insert ICE candidate in media stream number %d\n", i+1);
01837                     return -1;
01838                 }
01839             }
01840         }
01841 
01842         j++;
01843     }
01844 
01845     return 1;
01846 }
01847 
01848 
01849 static int
01850 end_media_session(str callid, str from_tag, str to_tag)
01851 {
01852     char request[2048], *result;
01853     int len;
01854 
01855     len = snprintf(request, sizeof(request),
01856                    "remove\r\n"
01857                    "call_id: %.*s\r\n"
01858                    "from_tag: %.*s\r\n"
01859                    "to_tag: %.*s\r\n"
01860                    "\r\n",
01861                    callid.len, callid.s,
01862                    from_tag.len, from_tag.s,
01863                    to_tag.len, to_tag.s);
01864 
01865     if (len >= sizeof(request)) {
01866         LM_ERR("mediaproxy request is longer than %lu bytes\n", (unsigned long)sizeof(request));
01867         return -1;
01868     }
01869 
01870     result = send_command(request);
01871 
01872     return result==NULL ? -1 : 1;
01873 }
01874 
01875 
01876 // Dialog callbacks and helpers
01877 //
01878 
01879 typedef enum {
01880     MPInactive = 0,
01881     MPActive
01882 } MediaProxyState;
01883 
01884 
01885 static INLINE char*
01886 get_dialog_id(struct dlg_cell *dlg)
01887 {
01888     static char buffer[64];
01889 
01890     snprintf(buffer, sizeof(buffer), "%d:%d", dlg->h_entry, dlg->h_id);
01891 
01892     return buffer;
01893 }
01894 
01895 
01896 static void
01897 __free_dialog_data(void *data)
01898 {
01899     shm_free((ice_candidate_data*)data);
01900 }
01901 
01902 
01903 static void
01904 __dialog_requests(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
01905 {
01906     use_media_proxy(_params->req, get_dialog_id(dlg), (ice_candidate_data*)*_params->param);
01907 }
01908 
01909 
01910 static void
01911 __dialog_replies(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
01912 {
01913     struct sip_msg *reply = _params->rpl;
01914 
01915     if (reply == FAKED_REPLY)
01916         return;
01917 
01918     if (reply->REPLY_STATUS>100 && reply->REPLY_STATUS<300) {
01919         use_media_proxy(reply, get_dialog_id(dlg), (ice_candidate_data*)*_params->param);
01920     }
01921 }
01922 
01923 
01924 static void
01925 __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
01926 {
01927     if ((int)(long)*_params->param == MPActive) {
01928         end_media_session(dlg->callid, dlg->tag[DLG_CALLER_LEG], dlg->tag[DLG_CALLEE_LEG]);
01929         *_params->param = MPInactive;
01930     }
01931 }
01932 
01933 
01934 static void
01935 __dialog_created(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
01936 {
01937     struct sip_msg *request = _params->req;
01938     ice_candidate_data *ice_data;
01939 
01940     if (request->REQ_METHOD != METHOD_INVITE)
01941         return;
01942 
01943     if ((request->msg_flags & FL_USE_MEDIA_PROXY) == 0)
01944         return;
01945 
01946     ice_data = (ice_candidate_data*)shm_malloc(sizeof(ice_candidate_data));
01947     if (!ice_data) {
01948         LM_ERR("failed to allocate shm memory for ice_candidate_data\n");
01949         return;
01950     }
01951 
01952     ice_data->priority = get_ice_candidate_priority(get_ice_candidate());
01953     ice_data->skip_next_reply = False;
01954 
01955     if (dlg_api.register_dlgcb(dlg, DLGCB_REQ_WITHIN | DLGCB_CONFIRMED, __dialog_requests, (void*)ice_data, __free_dialog_data) != 0)
01956         LM_ERR("cannot register callback for in-dialog requests\n");
01957     if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_FWDED | DLGCB_RESPONSE_WITHIN, __dialog_replies, (void*)ice_data, NULL) != 0)
01958         LM_ERR("cannot register callback for dialog and in-dialog replies\n");
01959     if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_DESTROY, __dialog_ended, (void*)MPActive, NULL) != 0)
01960         LM_ERR("cannot register callback for dialog termination\n");
01961 
01962     use_media_proxy(request, get_dialog_id(dlg), ice_data);
01963 }
01964 
01965 
01966 //
01967 // The public functions that are exported by this module
01968 //
01969 
01970 
01971 static int
01972 EngageMediaProxy(struct sip_msg *msg)
01973 {
01974     if (mediaproxy_disabled)
01975         return -1;
01976 
01977     if (!have_dlg_api) {
01978         LM_ERR("engage_media_proxy requires the dialog module to be loaded and configured\n");
01979         return -1;
01980     }
01981     msg->msg_flags |= FL_USE_MEDIA_PROXY;
01982     setflag(msg, dialog_flag); // have the dialog module trace this dialog
01983     return 1;
01984 }
01985 
01986 
01987 static int
01988 UseMediaProxy(struct sip_msg *msg)
01989 {
01990     if (mediaproxy_disabled)
01991         return -1;
01992 
01993     return use_media_proxy(msg, "", NULL);
01994 }
01995 
01996 
01997 static int
01998 EndMediaSession(struct sip_msg *msg)
01999 {
02000     str callid, from_tag, to_tag;
02001 
02002     if (mediaproxy_disabled)
02003         return -1;
02004 
02005     if (!get_callid(msg, &callid)) {
02006         LM_ERR("failed to get Call-ID\n");
02007         return -1;
02008     }
02009 
02010     from_tag = get_from_tag(msg);
02011     to_tag   = get_to_tag(msg);
02012 
02013     return end_media_session(callid, from_tag, to_tag);
02014 }
02015 
02016 
02017 //
02018 // Module management: initialization/destroy/function-parameter-fixing/...
02019 //
02020 
02021 
02022 static int
02023 mod_init(void)
02024 {
02025     pv_spec_t avp_spec;
02026     int *param;
02027     modparam_t type;
02028 
02029     // initialize the signaling_ip_avp structure
02030     if (signaling_ip_avp.spec.s==NULL || *(signaling_ip_avp.spec.s)==0) {
02031         LM_WARN("missing/empty signaling_ip_avp parameter. will use default.\n");
02032         signaling_ip_avp.spec.s = SIGNALING_IP_AVP_SPEC;
02033     }
02034     signaling_ip_avp.spec.len = strlen(signaling_ip_avp.spec.s);
02035     if (pv_parse_spec(&(signaling_ip_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
02036         LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s);
02037         return -1;
02038     }
02039     if (pv_get_avp_name(0, &(avp_spec.pvp), &(signaling_ip_avp.name), &(signaling_ip_avp.type))!=0) {
02040         LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s);
02041         return -1;
02042     }
02043 
02044     // initialize the media_relay_avp structure
02045     if (media_relay_avp.spec.s==NULL || *(media_relay_avp.spec.s)==0) {
02046         LM_WARN("missing/empty media_relay_avp parameter. will use default.\n");
02047         media_relay_avp.spec.s = MEDIA_RELAY_AVP_SPEC;
02048     }
02049     media_relay_avp.spec.len = strlen(media_relay_avp.spec.s);
02050     if (pv_parse_spec(&(media_relay_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
02051         LM_CRIT("invalid AVP specification for media_relay_avp: `%s'\n", media_relay_avp.spec.s);
02052         return -1;
02053     }
02054     if (pv_get_avp_name(0, &(avp_spec.pvp), &(media_relay_avp.name), &(media_relay_avp.type))!=0) {
02055         LM_CRIT("invalid AVP specification for media_relay_avp: `%s'\n", media_relay_avp.spec.s);
02056         return -1;
02057     }
02058 
02059     // initialize the ice_candidate_avp structure
02060     if (ice_candidate_avp.spec.s==NULL || *(ice_candidate_avp.spec.s)==0) {
02061         LM_WARN("missing/empty ice_candidate_avp parameter. will use default.\n");
02062         ice_candidate_avp.spec.s = ICE_CANDIDATE_AVP_SPEC;
02063     }
02064     ice_candidate_avp.spec.len = strlen(ice_candidate_avp.spec.s);
02065     if (pv_parse_spec(&(ice_candidate_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
02066         LM_CRIT("invalid AVP specification for ice_candidate_avp: `%s'\n", ice_candidate_avp.spec.s);
02067         return -1;
02068     }
02069     if (pv_get_avp_name(0, &(avp_spec.pvp), &(ice_candidate_avp.name), &(ice_candidate_avp.type))!=0) {
02070         LM_CRIT("invalid AVP specification for ice_candidate_avp: `%s'\n", ice_candidate_avp.spec.s);
02071         return -1;
02072     }
02073 
02074     // initialize ice_candidate module parameter
02075     ice_candidate.len = strlen(ice_candidate.s);
02076     if (!STR_IMATCH(ice_candidate, "none") && !STR_IMATCH(ice_candidate, "low-priority") && !STR_IMATCH(ice_candidate, "high-priority")) {
02077         LM_CRIT("invalid value specified for ice_candidate: `%s'\n", ice_candidate.s);
02078         return -1;
02079     }
02080 
02081     // bind to the dialog API
02082     if (load_dlg_api(&dlg_api)==0) {
02083         have_dlg_api = True;
02084 
02085         // load dlg_flag and default_timeout parameters from the dialog module
02086         param = find_param_export(find_module_by_name("dialog"), "dlg_flag", INT_PARAM, &type);
02087         if (!param) {
02088             LM_CRIT("cannot find dlg_flag parameter in the dialog module\n");
02089             return -1;
02090         }
02091 
02092         if (type != INT_PARAM) {
02093             LM_CRIT("dlg_flag parameter found but with wrong type: %d\n", type);
02094             return -1;
02095         }
02096 
02097         dialog_flag = *param;
02098 
02099         // register dialog creation callback
02100         if (dlg_api.register_dlgcb(NULL, DLGCB_CREATED, __dialog_created, NULL, NULL) != 0) {
02101             LM_CRIT("cannot register callback for dialog creation\n");
02102             return -1;
02103         }
02104     } else {
02105         LM_NOTICE("engage_media_proxy() will not work because the dialog module is not loaded\n");
02106     }
02107 
02108     return 0;
02109 }
02110 
02111 
02112 static int
02113 child_init(int rank)
02114 {
02115     // initialize the connection to mediaproxy if needed
02116     if (!mediaproxy_disabled)
02117         mediaproxy_connect();
02118 
02119     return 0;
02120 }
02121 
02122