mediaproxy.c

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

Generated on Wed May 23 18:00:28 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6