modules/mediaproxy: take boundary string from Content-Type header.
[sip-router] / modules / mediaproxy / mediaproxy.c
1 /* $Id$
2  *
3  * Copyright (C) 2004-2008 Dan Pascu
4  * Copyright (C) 2009 Juha Heinanen (multipart hack)
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * Kamailio is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * Kamailio is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <time.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <sys/un.h>
36
37 #include "../../sr_module.h"
38 #include "../../dprint.h"
39 #include "../../str.h"
40 #include "../../pvar.h"
41 #include "../../error.h"
42 #include "../../data_lump.h"
43 #include "../../mem/mem.h"
44 #include "../../ut.h"
45 #include "../../parser/msg_parser.h"
46 #include "../../parser/parse_from.h"
47 #include "../../parser/parse_to.h"
48 #include "../../parser/parse_param.h"
49 #include "../../msg_translator.h"
50 #include "../../modules_k/dialog/dlg_load.h"
51 #include "../../modules_k/dialog/dlg_hash.h"
52
53
54 MODULE_VERSION
55
56
57 #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
58 # define INLINE inline
59 #else
60 # define INLINE
61 #endif
62
63 /* WARNING: Keep this aligned with parser/msg_parser.h! */
64 #define FL_USE_MEDIA_PROXY (1<<30)
65
66 #define SIGNALING_IP_AVP_SPEC  "$avp(s:signaling_ip)"
67 #define MEDIA_RELAY_AVP_SPEC   "$avp(s:media_relay)"
68
69
70 // Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to
71 // more systems.  `AF_UNIX' was the traditional name stemming from BSD, so
72 // even most POSIX systems support it.  It is also the name of choice in
73 // the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX
74 #ifndef AF_LOCAL
75 # define AF_LOCAL AF_UNIX
76 #endif
77
78 // As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall,
79 //it is defined as 0
80 #ifndef MSG_NOSIGNAL
81 # define MSG_NOSIGNAL 0
82 #endif
83
84
85 #define isnulladdr(adr)  ((adr).len==7 && memcmp("0.0.0.0", (adr).s, 7)==0)
86 #define isnullport(port) ((port).len==1 && (port).s[0]=='0')
87
88 #define STR_MATCH(str, buf)  ((str).len==strlen(buf) && memcmp(buf, (str).s, (str).len)==0)
89 #define STR_IMATCH(str, buf) ((str).len==strlen(buf) && strncasecmp(buf, (str).s, (str).len)==0)
90
91 #define STR_HAS_PREFIX(str, prefix)  ((str).len>=(prefix).len && memcmp((prefix).s, (str).s, (prefix).len)==0)
92 #define STR_HAS_IPREFIX(str, prefix) ((str).len>=(prefix).len && strncasecmp((prefix).s, (str).s, (prefix).len)==0)
93
94
95 typedef int Bool;
96 #define True  1
97 #define False 0
98
99
100 typedef Bool (*NatTestFunction)(struct sip_msg *msg);
101
102
103 typedef enum {
104     TNone=0,
105     TSupported,
106     TUnsupported
107 } TransportType;
108
109 #define RETRY_INTERVAL 10
110 #define BUFFER_SIZE    8192
111
112 typedef struct MediaproxySocket {
113     char *name;             // name
114     int  sock;              // socket
115     int  timeout;           // how many miliseconds to wait for an answer
116     time_t last_failure;    // time of the last failure
117     char data[BUFFER_SIZE]; // buffer for the answer data
118 } MediaproxySocket;
119
120
121 typedef struct {
122     const char *name;
123     uint32_t address;
124     uint32_t mask;
125 } NetInfo;
126
127 typedef struct {
128     str type;      // stream type (`audio', `video', `image', ...)
129     str ip;
130     str port;
131     str rtcp_port; // pointer to the rtcp port if explicitly specified by stream
132     str direction;
133     Bool local_ip; // true if the IP is locally defined inside this media stream
134     TransportType transport;
135     char *start_line;
136     char *next_line;
137 } StreamInfo;
138
139 #define MAX_STREAMS 32
140 typedef struct SessionInfo {
141     str ip;
142     str ip_line;   // pointer to the whole session level ip line
143     str direction;
144     str separator;
145     StreamInfo streams[MAX_STREAMS];
146     unsigned int stream_count;
147     unsigned int supported_count;
148 } SessionInfo;
149
150 typedef struct AVP_Param {
151     str spec;
152     int_str name;
153     unsigned short type;
154 } AVP_Param;
155
156
157 // Function prototypes
158 //
159 static int EngageMediaProxy(struct sip_msg *msg);
160 static int UseMediaProxy(struct sip_msg *msg);
161 static int EndMediaSession(struct sip_msg *msg);
162
163 static int mod_init(void);
164 static int child_init(int rank);
165
166
167 // Module global variables and state
168 //
169 static int mediaproxy_disabled = False;
170
171 static MediaproxySocket mediaproxy_socket = {
172     "/var/run/mediaproxy/dispatcher.sock", // name
173     -1,                                    // sock
174     500,                                   // timeout in 500 miliseconds if there is no answer
175     0,                                     // time of the last failure
176     ""                                     // data
177 };
178
179
180 struct dlg_binds dlg_api;
181 Bool have_dlg_api = False;
182 static int dialog_flag = -1;
183
184 // The AVP where the caller signaling IP is stored (if defined)
185 static AVP_Param signaling_ip_avp = {str_init(SIGNALING_IP_AVP_SPEC), {0}, 0};
186
187 // The AVP where the application-defined media relay IP is stored
188 static AVP_Param media_relay_avp = {str_init(MEDIA_RELAY_AVP_SPEC), {0}, 0};
189
190 static cmd_export_t commands[] = {
191     {"engage_media_proxy", (cmd_function)EngageMediaProxy, 0, 0, 0, REQUEST_ROUTE},
192     {"use_media_proxy",    (cmd_function)UseMediaProxy,    0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE},
193     {"end_media_session",  (cmd_function)EndMediaSession,  0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | LOCAL_ROUTE},
194     {0, 0, 0, 0, 0, 0}
195 };
196
197 static param_export_t parameters[] = {
198     {"disable",            INT_PARAM, &mediaproxy_disabled},
199     {"mediaproxy_socket",  STR_PARAM, &(mediaproxy_socket.name)},
200     {"mediaproxy_timeout", INT_PARAM, &(mediaproxy_socket.timeout)},
201     {"signaling_ip_avp",   STR_PARAM, &(signaling_ip_avp.spec.s)},
202     {"media_relay_avp",    STR_PARAM, &(media_relay_avp.spec.s)},
203     {0, 0, 0}
204 };
205
206 struct module_exports exports = {
207     "mediaproxy",    // module name
208     DEFAULT_DLFLAGS, // dlopen flags
209     commands,        // exported functions
210     parameters,      // exported parameters
211     NULL,            // exported statistics
212     NULL,            // exported MI functions
213     NULL,            // exported pseudo-variables
214     NULL,            // extra processes
215     mod_init,        // module init function (before fork. kids will inherit)
216     NULL,            // reply processing function
217     NULL,            // destroy function
218     child_init       // child init function
219 };
220
221
222
223 // String processing functions
224 //
225
226 // strfind() finds the start of the first occurrence of the substring needle
227 // of length nlen in the memory area haystack of length len.
228 static void*
229 strfind(const void *haystack, size_t len, const void *needle, size_t nlen)
230 {
231     char *sp;
232
233     // Sanity check
234     if(!(haystack && needle && nlen && len>=nlen))
235         return NULL;
236
237     for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
238         if (*sp == *(char*)needle && memcmp(sp, needle, nlen)==0) {
239             return sp;
240         }
241     }
242
243     return NULL;
244 }
245
246 // strcasefind() finds the start of the first occurrence of the substring
247 // needle of length nlen in the memory area haystack of length len by doing
248 // a case insensitive search
249 static void*
250 strcasefind(const char *haystack, size_t len, const char *needle, size_t nlen)
251 {
252     char *sp;
253
254     // Sanity check
255     if(!(haystack && needle && nlen && len>=nlen))
256         return NULL;
257
258     for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
259         if (tolower(*sp) == tolower(*(char*)needle) &&
260             strncasecmp(sp, needle, nlen)==0) {
261             return sp;
262         }
263     }
264
265     return NULL;
266 }
267
268 // returns string with whitespace trimmed from left end
269 static INLINE void
270 ltrim(str *string)
271 {
272     while (string->len>0 && isspace((int)*(string->s))) {
273         string->len--;
274         string->s++;
275     }
276 }
277
278 // returns string with whitespace trimmed from right end
279 static INLINE void
280 rtrim(str *string)
281 {
282     char *ptr;
283
284     ptr = string->s + string->len - 1;
285     while (string->len>0 && (*ptr==0 || isspace((int)*ptr))) {
286         string->len--;
287         ptr--;
288     }
289 }
290
291 // returns string with whitespace trimmed from both ends
292 static INLINE void
293 trim(str *string)
294 {
295     ltrim(string);
296     rtrim(string);
297 }
298
299 // returns a pointer to first CR or LF char found or the end of string
300 static char*
301 findendline(char *string, int len)
302 {
303     char *ptr = string;
304
305     while(ptr - string < len && *ptr != '\n' && *ptr != '\r')
306         ptr++;
307
308     return ptr;
309 }
310
311
312 static int
313 strtoint(str *data)
314 {
315     long int result;
316     char c;
317
318     // hack to avoid copying the string
319     c = data->s[data->len];
320     data->s[data->len] = 0;
321     result = strtol(data->s, NULL, 10);
322     data->s[data->len] = c;
323
324     return (int)result;
325 }
326
327
328 // find a line in str `block' that starts with `start'.
329 static char*
330 find_line_starting_with(str *block, char *start, int ignoreCase)
331 {
332     char *ptr, *bend;
333     str zone;
334     int tlen;
335
336     bend = block->s + block->len;
337     tlen = strlen(start);
338     ptr = NULL;
339
340     for (zone = *block; zone.len > 0; zone.len = bend - zone.s) {
341         if (ignoreCase)
342             ptr = strcasefind(zone.s, zone.len, start, tlen);
343         else
344             ptr = strfind(zone.s, zone.len, start, tlen);
345         if (!ptr || ptr==block->s || ptr[-1]=='\n' || ptr[-1]=='\r')
346             break;
347         zone.s = ptr + tlen;
348     }
349
350     return ptr;
351 }
352
353
354 // count all lines in str `block' that starts with `start'.
355 static unsigned int
356 count_lines_starting_with(str *block, char *start, int ignoreCase)
357 {
358     char *ptr, *bend;
359     str zone;
360     int tlen;
361     unsigned count;
362
363     bend = block->s + block->len;
364     tlen = strlen(start);
365
366     count = 0;
367
368     for (zone = *block; zone.len > 0; zone.len = bend - zone.s) {
369         if (ignoreCase)
370             ptr = strcasefind(zone.s, zone.len, start, tlen);
371         else
372             ptr = strfind(zone.s, zone.len, start, tlen);
373         if (!ptr)
374             break;
375         if (ptr==block->s || ptr[-1]=='\n' || ptr[-1]=='\r')
376             count++;
377         zone.s = ptr + tlen;
378     }
379
380     return count;
381 }
382
383
384 // get up to `limit' whitespace separated tokens from `char *string'
385 static int
386 get_tokens(char *string, str *tokens, int limit)
387 {
388     int i, len, size;
389     char *ptr;
390
391     if (!string) {
392         return 0;
393     }
394
395     len  = strlen(string);
396
397     for (ptr=string, i=0; i<limit && len>0; i++) {
398         size = strspn(ptr, " \t\n\r");
399         ptr += size;
400         len -= size;
401         if (len <= 0)
402             break;
403         size = strcspn(ptr, " \t\n\r");
404         if (size==0)
405             break;
406         tokens[i].s = ptr;
407         tokens[i].len = size;
408         ptr += size;
409         len -= size;
410     }
411
412     return i;
413 }
414
415 // get up to `limit' whitespace separated tokens from `str *string'
416 static int
417 get_str_tokens(str *string, str *tokens, int limit)
418 {
419     int count;
420     char c;
421
422     if (!string || !string->s) {
423         return 0;
424     }
425
426     c = string->s[string->len];
427     string->s[string->len] = 0;
428
429     count = get_tokens(string->s, tokens, limit);
430
431     string->s[string->len] = c;
432
433     return count;
434 }
435
436
437 // Functions to extract the info we need from the SIP/SDP message
438 //
439
440 static Bool
441 get_callid(struct sip_msg* msg, str *cid)
442 {
443     if (msg->callid == NULL) {
444         if (parse_headers(msg, HDR_CALLID_F, 0) == -1) {
445             LM_ERR("cannot parse Call-ID header\n");
446             return False;
447         }
448         if (msg->callid == NULL) {
449             LM_ERR("missing Call-ID header\n");
450             return False;
451         }
452     }
453
454     *cid = msg->callid->body;
455
456     trim(cid);
457
458     return True;
459 }
460
461 static Bool
462 get_cseq_number(struct sip_msg *msg, str *cseq)
463 {
464     if (msg->cseq == NULL) {
465         if (parse_headers(msg, HDR_CSEQ_F, 0)==-1) {
466             LM_ERR("cannot parse CSeq header\n");
467             return False;
468         }
469         if (msg->cseq == NULL) {
470             LM_ERR("missing CSeq header\n");
471             return False;
472         }
473         }
474
475         *cseq = get_cseq(msg)->number;
476
477     if (cseq->s==NULL || cseq->len==0) {
478         LM_ERR("missing CSeq number\n");
479         return False;
480     }
481
482     return True;
483 }
484
485 static str
486 get_from_uri(struct sip_msg *msg)
487 {
488     static str notfound = str_init("unknown");
489     str uri;
490     char *ptr;
491
492     if (parse_from_header(msg) < 0) {
493         LM_ERR("cannot parse the From header\n");
494         return notfound;
495     }
496
497     uri = get_from(msg)->uri;
498
499     if (uri.len == 0)
500         return notfound;
501
502     if (strncmp(uri.s, "sip:", 4)==0) {
503         uri.s += 4;
504         uri.len -= 4;
505     }
506
507     if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
508         uri.len = ptr - uri.s;
509     }
510
511     return uri;
512 }
513
514
515 static str
516 get_to_uri(struct sip_msg *msg)
517 {
518     static str notfound = str_init("unknown");
519     str uri;
520     char *ptr;
521
522     if (!msg->to) {
523         LM_ERR("missing To header\n");
524         return notfound;
525     }
526
527     uri = get_to(msg)->uri;
528
529     if (uri.len == 0)
530         return notfound;
531
532     if (strncmp(uri.s, "sip:", 4)==0) {
533         uri.s += 4;
534         uri.len -= 4;
535     }
536
537     if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
538         uri.len = ptr - uri.s;
539     }
540
541     return uri;
542 }
543
544
545 static str
546 get_from_tag(struct sip_msg *msg)
547 {
548     static str notfound = str_init("");
549     str tag;
550
551     if (parse_from_header(msg) < 0) {
552         LM_ERR("cannot parse the From header\n");
553         return notfound;
554     }
555
556     tag = get_from(msg)->tag_value;
557
558     if (tag.len == 0)
559         return notfound;
560
561     return tag;
562 }
563
564
565 static str
566 get_to_tag(struct sip_msg *msg)
567 {
568     static str notfound = str_init("");
569     str tag;
570
571     if (!msg->to) {
572         LM_ERR("missing To header\n");
573         return notfound;
574     }
575
576     if (msg->first_line.type==SIP_REPLY && msg->REPLY_STATUS<200) {
577         // Ignore the To tag for provisional replies
578         return notfound;
579     }
580
581     tag = get_to(msg)->tag_value;
582
583     if (tag.len == 0)
584         return notfound;
585
586     return tag;
587 }
588
589
590 static str
591 get_user_agent(struct sip_msg* msg)
592 {
593     static str notfound = str_init("unknown agent");
594     str block, server;
595     char *ptr;
596
597     if (parse_headers(msg, HDR_USERAGENT_F, 0)==0 && msg->user_agent &&
598         msg->user_agent->body.s && msg->user_agent->body.len>0) {
599         return msg->user_agent->body;
600     }
601
602     // If we can't find user-agent, look after the `Server' header
603     // This is a temporary hack. Normally it should be extracted by openser.
604
605     block.s   = msg->buf;
606     block.len = msg->len;
607
608     ptr = find_line_starting_with(&block, "Server:", True);
609     if (!ptr)
610         return notfound;
611
612     server.s   = ptr + 7;
613     server.len = findendline(server.s, block.s+block.len-server.s) - server.s;
614
615     trim(&server);
616     if (server.len == 0)
617         return notfound;
618
619     return server;
620 }
621
622
623 // Get caller signaling IP
624 static str
625 get_signaling_ip(struct sip_msg* msg)
626 {
627     int_str value;
628
629     if (!search_first_avp(signaling_ip_avp.type | AVP_VAL_STR,
630                           signaling_ip_avp.name, &value, NULL) ||
631         value.s.s==NULL || value.s.len==0) {
632
633         value.s.s = ip_addr2a(&msg->rcv.src_ip);
634         value.s.len = strlen(value.s.s);
635     }
636
637     return value.s;
638 }
639
640 // Get the application-defined media_relay if defined
641 static str
642 get_media_relay(struct sip_msg* msg)
643 {
644     static str notfound = str_init("");
645     int_str value;
646
647     if (!search_first_avp(media_relay_avp.type | AVP_VAL_STR,
648                           media_relay_avp.name, &value, NULL) || value.s.s==NULL || value.s.len==0) {
649         return notfound;
650     }
651
652     return value.s;
653 }
654
655
656 // Functions to manipulate the SDP message body
657 //
658
659 static int
660 find_content_type_application_sdp(struct sip_msg *msg, str *sdp)
661 {
662     str type, params, boundary;
663     char *start, *s;
664     unsigned int len;
665     Bool done;
666     param_hooks_t hooks;
667     param_t *p, *list;
668
669     if (!msg->content_type) {
670         LM_WARN("the Content-Type header is missing! Assume the content type is text/plain\n");
671         return 1;
672     }
673
674     type = msg->content_type->body;
675     trim(&type);
676
677     if (strncasecmp(type.s, "application/sdp", 15) == 0) {
678         done = True;
679     } else if (strncasecmp(type.s, "multipart/mixed", 15) == 0) {
680         done = False;
681     } else {
682         LM_ERR("invalid Content-Type for SDP: %.*s\n", type.len, type.s);
683         return -1;
684     }
685
686     if (!(isspace((int)type.s[15]) || type.s[15] == ';' || type.s[15] == 0)) {
687         LM_ERR("invalid character after Content-Type: `%c'\n", type.s[15]);
688         return -1;
689     }
690
691     if (done) return 1;
692
693     // Hack to find application/sdp bodypart
694     params.s = memchr(msg->content_type->body.s, ';', 
695                       msg->content_type->body.len);
696     if (params.s == NULL) {
697         LM_ERR("Content-Type hdr has no params\n");
698         return -1;
699     }
700     params.len = msg->content_type->body.len - 
701         (params.s - msg->content_type->body.s);
702     if (parse_params(&params, CLASS_ANY, &hooks, &list) < 0) {
703         LM_ERR("while parsing Content-Type params\n");
704         return -1;
705     }
706     boundary.s = NULL;
707     boundary.len = 0;
708     for (p = list; p; p = p->next) {
709         if ((p->name.len == 8)
710             && (strncasecmp(p->name.s, "boundary", 8) == 0)) {
711             boundary.s = pkg_malloc(p->body.len + 2 + 1);
712             if (boundary.s == NULL) {
713                 free_params(list);
714                 LM_ERR("no memory for boundary string\n");
715                 return -1;
716             }
717             *(boundary.s) = '-';
718             *(boundary.s + 1) = '-';
719             memcpy(boundary.s + 2, p->body.s, p->body.len);
720             boundary.len = 2 + p->body.len;
721             *(boundary.s + boundary.len) = 0;
722             LM_DBG("boundary is <%.*s>\n", boundary.len, boundary.s);
723             break;
724         }
725     }
726     free_params(list);
727     if (boundary.s == NULL) {
728         LM_ERR("no mandatory param \";boundary\"\n");
729         return -1;
730     }
731
732     while ((s = find_line_starting_with(sdp, "Content-Type: ", True))) {
733         start = s + 14;
734         len = sdp->len - (s - sdp->s) - 14;
735         if (len > 15 + 2) {
736             if (strncasecmp(start, "application/sdp", 15) == 0) {
737                 start = start + 15;
738                 if ((*start != 13) || (*(start + 1) != 10)) {
739                     LM_ERR("no CRLF found after content type\n");
740                     goto err;
741                 }
742                 start = start + 2;
743                 len = len - 15 - 2;
744                 while ((len > 0) && ((*start == 13) || (*start == 10))) {
745                     len = len - 1;
746                     start = start + 1;
747                 }
748                 sdp->s = start;
749                 sdp->len = len;
750                 s = find_line_starting_with(sdp, boundary.s, False);
751                 if (s == NULL) {
752                     LM_ERR("boundary not found after bodypart\n");
753                     goto err;
754                 }
755                 sdp->len = s - start - 2;
756                 pkg_free(boundary.s);
757                 return 1;
758             }
759         }
760     }
761     LM_ERR("no application/sdp bodypart found\n");
762
763  err:
764     pkg_free(boundary.s);
765     return -1;
766 }
767
768
769 // Get the SDP message from SIP message and check it's Content-Type
770 // Return values:
771 //    1 - success
772 //   -1 - error in getting body or invalid content type
773 //   -2 - empty message
774 static int
775 get_sdp_message(struct sip_msg *msg, str *sdp)
776 {
777     sdp->s = get_body(msg);
778     if (sdp->s==NULL) {
779         LM_ERR("cannot get the SDP body\n");
780         return -1;
781     }
782
783     sdp->len = msg->buf + msg->len - sdp->s;
784     if (sdp->len == 0)
785         return -2;
786
787     return find_content_type_application_sdp(msg, sdp);
788 }
789
790
791 // Return a str containing the line separator used in the SDP body
792 static str
793 get_sdp_line_separator(str *sdp)
794 {
795     char *ptr, *end_ptr, *sdp_end;
796     str separator;
797
798     sdp_end = sdp->s + sdp->len;
799
800     ptr = find_line_starting_with(sdp, "v=", False);
801     end_ptr = findendline(ptr, sdp_end-ptr);
802     separator.s = ptr = end_ptr;
803     while ((*ptr=='\n' || *ptr=='\r') && ptr<sdp_end)
804         ptr++;
805     separator.len = ptr - separator.s;
806     if (separator.len > 2)
807         separator.len = 2; // safety check
808
809     return separator;
810 }
811
812
813 // will return the direction attribute defined in the given block.
814 // if missing, default is used if provided, else `sendrecv' is used.
815 static str
816 get_direction_attribute(str *block, str *default_direction)
817 {
818     str direction, zone, line;
819     char *ptr;
820
821     for (zone=*block;;) {
822         ptr = find_line_starting_with(&zone, "a=", False);
823         if (!ptr) {
824             if (default_direction)
825                 return *default_direction;
826             direction.s = "sendrecv";
827             direction.len = 8;
828             return direction;
829         }
830
831         line.s = ptr + 2;
832         line.len = findendline(line.s, zone.s + zone.len - line.s) - line.s;
833
834         if (line.len==8) {
835             if (strncmp(line.s, "sendrecv", 8)==0 || strncmp(line.s, "sendonly", 8)==0 ||
836                 strncmp(line.s, "recvonly", 8)==0 || strncmp(line.s, "inactive", 8)==0) {
837                 return line;
838             }
839         }
840
841         zone.s   = line.s + line.len;
842         zone.len = block->s + block->len - zone.s;
843     }
844 }
845
846
847 // will return the rtcp port of the stream in the given block
848 // if defined by the stream, otherwise will return {NULL, 0}.
849 static str
850 get_rtcp_port_attribute(str *block)
851 {
852     str zone, rtcp_port, notfound = {NULL, 0};
853     char *ptr;
854     int count;
855
856     ptr = find_line_starting_with(block, "a=rtcp:", False);
857
858     if (!ptr)
859         return notfound;
860
861     zone.s = ptr + 7;
862     zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s;
863
864     count = get_str_tokens(&zone, &rtcp_port, 1);
865
866     if (count != 1) {
867         LM_ERR("invalid `a=rtcp' line in SDP body\n");
868         return notfound;
869     }
870
871     return rtcp_port;
872 }
873
874
875 // will return the ip address present in a `c=' line in the given block
876 // returns: -1 on error, 0 if not found, 1 if found
877 static int
878 get_media_ip_from_block(str *block, str *mediaip)
879 {
880     str tokens[3], zone;
881     char *ptr;
882     int count;
883
884     ptr = find_line_starting_with(block, "c=", False);
885
886     if (!ptr) {
887         mediaip->s   = NULL;
888         mediaip->len = 0;
889         return 0;
890     }
891
892     zone.s = ptr + 2;
893     zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s;
894
895     count = get_str_tokens(&zone, tokens, 3);
896
897     if (count != 3) {
898         LM_ERR("invalid `c=' line in SDP body\n");
899         return -1;
900     }
901
902     // can also check if tokens[1] == 'IP4'
903     *mediaip = tokens[2];
904
905     return 1;
906 }
907
908
909 static Bool
910 get_sdp_session_ip(str *sdp, str *mediaip, str *ip_line)
911 {
912     char *ptr, *end_ptr;
913     str block;
914
915     // session IP can be found from the beginning up to the first media block
916     ptr = find_line_starting_with(sdp, "m=", False);
917     if (ptr) {
918         block.s   = sdp->s;
919         block.len = ptr - block.s;
920     } else {
921         block = *sdp;
922     }
923
924     if (get_media_ip_from_block(&block, mediaip) == -1) {
925         LM_ERR("parse error while getting session-level media IP from SDP\n");
926         return False;
927     }
928
929     if (ip_line != NULL) {
930         ptr = find_line_starting_with(&block, "c=", False);
931         if (!ptr) {
932             ip_line->s = NULL;
933             ip_line->len = 0;
934         } else {
935             end_ptr = findendline(ptr, block.s + block.len - ptr);
936             while ((*end_ptr=='\n' || *end_ptr=='\r'))
937                 end_ptr++;
938             ip_line->s = ptr;
939             ip_line->len = end_ptr - ptr;
940         }
941     }
942
943     // it's not an error to be missing. it can be locally defined
944     // by each media stream. thus we return true even if not found
945     return True;
946 }
947
948
949 // will return the direction as defined at the session level
950 // in the SDP. if missing, `sendrecv' is used.
951 static str
952 get_session_direction(str *sdp)
953 {
954     static str default_direction = str_init("sendrecv");
955     str block;
956     char *ptr;
957
958     // session level direction can be found from the beginning up to the first media block
959     ptr = find_line_starting_with(sdp, "m=", False);
960     if (ptr) {
961         block.s   = sdp->s;
962         block.len = ptr - block.s;
963     } else {
964         block = *sdp;
965     }
966
967     return get_direction_attribute(&block, &default_direction);
968 }
969
970
971 static Bool
972 supported_transport(str transport)
973 {
974     // supported transports: RTP/AVP, RTP/AVPF, RTP/SAVP, RTP/SAVPF, udp, udptl
975     str prefixes[] = {str_init("RTP"), str_init("udp"), {NULL, 0}};
976     int i;
977
978     for (i=0; prefixes[i].s != NULL; i++) {
979         if (STR_HAS_IPREFIX(transport, prefixes[i])) {
980             return True;
981         }
982     }
983
984     return False;
985 }
986
987
988 static int
989 get_session_info(str *sdp, SessionInfo *session)
990 {
991     str tokens[3], ip, ip_line, block, zone;
992     char *ptr, *sdp_end;
993     int i, count, result;
994
995     count = count_lines_starting_with(sdp, "v=", False);
996     if (count != 1) {
997         LM_ERR("cannot handle more than 1 media session in SDP\n");
998         return -1;
999     }
1000
1001     count = count_lines_starting_with(sdp, "m=", False);
1002     if (count > MAX_STREAMS) {
1003         LM_ERR("cannot handle more than %d media streams in SDP\n", MAX_STREAMS);
1004         return -1;
1005     }
1006
1007     memset(session, 0, sizeof(SessionInfo));
1008
1009     if (count == 0)
1010         return 0;
1011
1012     if (!get_sdp_session_ip(sdp, &ip, &ip_line)) {
1013         LM_ERR("failed to parse the SDP message\n");
1014         return -1;
1015     }
1016
1017     ptr = memchr(ip.s, '/', ip.len);
1018     if (ptr) {
1019         LM_ERR("unsupported multicast IP specification in SDP: %.*s\n", ip.len, ip.s);
1020         return -1;
1021     }
1022
1023     session->ip = ip;
1024     session->ip_line = ip_line;
1025     session->direction = get_session_direction(sdp);
1026     session->separator = get_sdp_line_separator(sdp);
1027     session->stream_count = count;
1028
1029     sdp_end = sdp->s + sdp->len;
1030
1031     for (i=0, block=*sdp; i<MAX_STREAMS; i++) {
1032         ptr = find_line_starting_with(&block, "m=", False);
1033
1034         if (!ptr)
1035             break;
1036
1037         zone.s = ptr + 2;
1038         zone.len = findendline(zone.s, sdp_end - zone.s) - zone.s;
1039
1040         count = get_str_tokens(&zone, tokens, 3);
1041         if (count != 3) {
1042             LM_ERR("invalid `m=' line in the SDP body\n");
1043             return -1;
1044         }
1045
1046         session->streams[i].start_line = ptr;
1047         session->streams[i].next_line = zone.s + zone.len + session->separator.len;
1048         if (session->streams[i].next_line > sdp_end)
1049             session->streams[i].next_line = sdp_end; //safety check
1050
1051         if (supported_transport(tokens[2])) {
1052             // handle case where port is specified like <port>/<nr_of_ports>
1053             // as defined by RFC2327. ex: m=audio 5012/1 RTP/AVP 18 0 8
1054             // TODO: also handle case where nr_of_ports > 1  -Dan
1055             ptr = memchr(tokens[1].s, '/', tokens[1].len);
1056             if (ptr != NULL) {
1057                 str port_nr;
1058
1059                 port_nr.s = ptr + 1;
1060                 port_nr.len = tokens[1].s + tokens[1].len - port_nr.s;
1061                 if (port_nr.len==0) {
1062                     LM_ERR("invalid port specification in `m=' line: %.*s\n", tokens[1].len, tokens[1].s);
1063                     return -1;
1064                 }
1065                 if (!(port_nr.len==1 && port_nr.s[0]=='1')) {
1066                     LM_ERR("unsupported number of ports specified in `m=' line\n");
1067                     return -1;
1068                 }
1069                 tokens[1].len = ptr - tokens[1].s;
1070             }
1071
1072             session->streams[i].type = tokens[0];
1073             session->streams[i].port = tokens[1];
1074
1075             session->streams[i].transport = TSupported;
1076             session->supported_count++;
1077         } else {
1078             // mark that we have an unsupported transport so we can ignore this stream later
1079             LM_INFO("unsupported transport in stream nr %d's `m=' line: %.*s\n", i+1, tokens[2].len, tokens[2].s);
1080             session->streams[i].type = tokens[0];
1081             session->streams[i].port = tokens[1];
1082             session->streams[i].transport = TUnsupported;
1083         }
1084
1085         block.s   = zone.s + zone.len;
1086         block.len = sdp_end - block.s;
1087     }
1088
1089     for (i=0; i<session->stream_count; i++) {
1090         block.s = session->streams[i].port.s;
1091         if (i < session->stream_count-1)
1092             block.len = session->streams[i+1].port.s - block.s;
1093         else
1094             block.len = sdp_end - block.s;
1095
1096         result = get_media_ip_from_block(&block, &ip);
1097         if (result == -1) {
1098             LM_ERR("parse error while getting the contact IP for the "
1099                    "media stream number %d\n", i+1);
1100             return -1;
1101         } else if (result == 0) {
1102             if (session->ip.s == NULL) {
1103                 LM_ERR("media stream number %d doesn't define a contact IP "
1104                        "and the session-level IP is missing\n", i+1);
1105                 return -1;
1106             }
1107             session->streams[i].ip = session->ip;
1108             session->streams[i].local_ip = 0;
1109         } else {
1110             if (session->streams[i].transport == TSupported) {
1111                 ptr = memchr(ip.s, '/', ip.len);
1112                 if (ptr) {
1113                     LM_ERR("unsupported multicast IP specification in stream nr %d: %.*s\n", i+1, ip.len, ip.s);
1114                     return -1;
1115                 }
1116             }
1117             session->streams[i].ip = ip;
1118             session->streams[i].local_ip = 1;
1119         }
1120
1121         session->streams[i].rtcp_port = get_rtcp_port_attribute(&block);
1122         session->streams[i].direction = get_direction_attribute(&block, &session->direction);
1123     }
1124
1125     return session->stream_count;
1126 }
1127
1128
1129 static Bool
1130 insert_element(struct sip_msg *msg, char *position, char *element)
1131 {
1132     struct lump *anchor;
1133     char *buf;
1134     int len;
1135
1136     len = strlen(element);
1137
1138     buf = pkg_malloc(len);
1139     if (!buf) {
1140         LM_ERR("out of memory\n");
1141         return False;
1142     }
1143
1144     anchor = anchor_lump(msg, position - msg->buf, 0, 0);
1145     if (!anchor) {
1146         LM_ERR("failed to get anchor for new element\n");
1147         pkg_free(buf);
1148         return False;
1149     }
1150
1151     memcpy(buf, element, len);
1152
1153     if (insert_new_lump_after(anchor, buf, len, 0)==0) {
1154         LM_ERR("failed to insert new element\n");
1155         pkg_free(buf);
1156         return False;
1157     }
1158
1159     return True;
1160 }
1161
1162
1163 static Bool
1164 replace_element(struct sip_msg *msg, str *old_element, str *new_element)
1165 {
1166     struct lump *anchor;
1167     char *buf;
1168
1169     if (new_element->len==old_element->len &&
1170         memcmp(new_element->s, old_element->s, new_element->len)==0) {
1171         return True;
1172     }
1173
1174     buf = pkg_malloc(new_element->len);
1175     if (!buf) {
1176         LM_ERR("out of memory\n");
1177         return False;
1178     }
1179
1180     anchor = del_lump(msg, old_element->s - msg->buf, old_element->len, 0);
1181     if (!anchor) {
1182         LM_ERR("failed to delete old element\n");
1183         pkg_free(buf);
1184         return False;
1185     }
1186
1187     memcpy(buf, new_element->s, new_element->len);
1188
1189     if (insert_new_lump_after(anchor, buf, new_element->len, 0)==0) {
1190         LM_ERR("failed to insert new element\n");
1191         pkg_free(buf);
1192         return False;
1193     }
1194
1195     return True;
1196 }
1197
1198
1199 static Bool
1200 remove_element(struct sip_msg *msg, str *element)
1201 {
1202     if (!del_lump(msg, element->s - msg->buf, element->len, 0)) {
1203         LM_ERR("failed to delete old element\n");
1204         return False;
1205     }
1206
1207     return True;
1208 }
1209
1210
1211 // Functions dealing with the external mediaproxy helper
1212 //
1213
1214 static Bool
1215 mediaproxy_connect(void)
1216 {
1217     struct sockaddr_un addr;
1218
1219     if (mediaproxy_socket.sock >= 0)
1220         return True;
1221
1222     if (mediaproxy_socket.last_failure + RETRY_INTERVAL > time(NULL))
1223         return False;
1224
1225     memset(&addr, 0, sizeof(addr));
1226     addr.sun_family = AF_LOCAL;
1227     strncpy(addr.sun_path, mediaproxy_socket.name, sizeof(addr.sun_path) - 1);
1228 #ifdef HAVE_SOCKADDR_SA_LEN
1229     addr.sun_len = strlen(addr.sun_path);
1230 #endif
1231
1232     mediaproxy_socket.sock = socket(AF_LOCAL, SOCK_STREAM, 0);
1233     if (mediaproxy_socket.sock < 0) {
1234         LM_ERR("can't create socket\n");
1235         mediaproxy_socket.last_failure = time(NULL);
1236         return False;
1237     }
1238     if (connect(mediaproxy_socket.sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
1239         LM_ERR("failed to connect to %s: %s\n", mediaproxy_socket.name, strerror(errno));
1240         close(mediaproxy_socket.sock);
1241         mediaproxy_socket.sock = -1;
1242         mediaproxy_socket.last_failure = time(NULL);
1243         return False;
1244     }
1245
1246     return True;
1247 }
1248
1249 static void
1250 mediaproxy_disconnect(void)
1251 {
1252     if (mediaproxy_socket.sock < 0)
1253         return;
1254
1255     close(mediaproxy_socket.sock);
1256     mediaproxy_socket.sock = -1;
1257     mediaproxy_socket.last_failure = time(NULL);
1258 }
1259
1260 static char*
1261 send_command(char *command)
1262 {
1263     int cmd_len, bytes, tries, sent, received, count;
1264     struct timeval timeout;
1265     fd_set rset;
1266
1267     if (!mediaproxy_connect())
1268         return NULL;
1269
1270     cmd_len = strlen(command);
1271
1272     for (sent=0, tries=0; sent<cmd_len && tries<3; tries++, sent+=bytes) {
1273         do
1274             bytes = send(mediaproxy_socket.sock, command+sent, cmd_len-sent, MSG_DONTWAIT|MSG_NOSIGNAL);
1275         while (bytes == -1 && errno == EINTR);
1276         if (bytes == -1) {
1277             switch (errno) {
1278             case ECONNRESET:
1279             case EPIPE:
1280                 mediaproxy_disconnect();
1281                 mediaproxy_socket.last_failure = 0; // we want to reconnect immediately
1282                 if (mediaproxy_connect()) {
1283                     sent = bytes = 0;
1284                     continue;
1285                 } else {
1286                     LM_ERR("connection with mediaproxy did die\n");
1287                 }
1288                 break;
1289             case EACCES:
1290                 LM_ERR("got permission denied while sending to %s\n", mediaproxy_socket.name);
1291                 break;
1292             case EWOULDBLOCK:
1293                 // this shouldn't happen as we read back all the answer after a request.
1294                 // if it would block, it means there is an error.
1295                 LM_ERR("sending command would block!\n");
1296                 break;
1297             default:
1298                 LM_ERR("%d: %s\n", errno, strerror(errno));
1299                 break;
1300             }
1301             mediaproxy_disconnect();
1302             return NULL;
1303         }
1304     }
1305     if (sent < cmd_len) {
1306         LM_ERR("couldn't send complete command after 3 tries\n");
1307         mediaproxy_disconnect();
1308         return NULL;
1309     }
1310
1311     mediaproxy_socket.data[0] = 0;
1312     received = 0;
1313     while (True) {
1314         FD_ZERO(&rset);
1315         FD_SET(mediaproxy_socket.sock, &rset);
1316         timeout.tv_sec = mediaproxy_socket.timeout / 1000;
1317         timeout.tv_usec = (mediaproxy_socket.timeout % 1000) * 1000;
1318
1319         do
1320             count = select(mediaproxy_socket.sock + 1, &rset, NULL, NULL, &timeout);
1321         while (count == -1 && errno == EINTR);
1322
1323         if (count == -1) {
1324             LM_ERR("select failed: %d: %s\n", errno, strerror(errno));
1325             mediaproxy_disconnect();
1326             return NULL;
1327         } else if (count == 0) {
1328             LM_ERR("did timeout waiting for an answer\n");
1329             mediaproxy_disconnect();
1330             return NULL;
1331         } else {
1332             do
1333                 bytes = recv(mediaproxy_socket.sock, mediaproxy_socket.data+received, BUFFER_SIZE-1-received, 0);
1334             while (bytes == -1 && errno == EINTR);
1335             if (bytes == -1) {
1336                 LM_ERR("failed to read answer: %d: %s\n", errno, strerror(errno));
1337                 mediaproxy_disconnect();
1338                 return NULL;
1339             } else if (bytes == 0) {
1340                 LM_ERR("connection with mediaproxy closed\n");
1341                 mediaproxy_disconnect();
1342                 return NULL;
1343             } else {
1344                 mediaproxy_socket.data[received+bytes] = 0;
1345                 if (strstr(mediaproxy_socket.data+received, "\r\n")!=NULL) {
1346                     break;
1347                 }
1348                 received += bytes;
1349             }
1350         }
1351     }
1352
1353     return mediaproxy_socket.data;
1354 }
1355
1356
1357 // Exported API implementation
1358 //
1359
1360 static int
1361 use_media_proxy(struct sip_msg *msg, char *dialog_id)
1362 {
1363     str callid, cseq, from_uri, to_uri, from_tag, to_tag, user_agent;
1364     str signaling_ip, media_relay, sdp, str_buf, tokens[MAX_STREAMS+1];
1365     char request[8192], media_str[4096], buf[64], *result, *type;
1366     int i, j, port, len, status;
1367     Bool removed_session_ip;
1368     SessionInfo session;
1369     StreamInfo stream;
1370
1371     if (msg == NULL)
1372         return -1;
1373
1374     if (msg->first_line.type == SIP_REQUEST) {
1375         type = "request";
1376     } else if (msg->first_line.type == SIP_REPLY) {
1377         type = "reply";
1378     } else {
1379         return -1;
1380     }
1381
1382     if (!get_callid(msg, &callid)) {
1383         LM_ERR("failed to get Call-ID\n");
1384         return -1;
1385     }
1386
1387     if (!get_cseq_number(msg, &cseq)) {
1388         LM_ERR("failed to get CSeq\n");
1389         return -1;
1390     }
1391
1392     status = get_sdp_message(msg, &sdp);
1393     // status = -1 is error, -2 is missing SDP body
1394     if (status < 0)
1395         return status;
1396
1397     status = get_session_info(&sdp, &session);
1398     if (status < 0) {
1399         LM_ERR("can't extract media streams from the SDP message\n");
1400         return -1;
1401     }
1402
1403     if (session.supported_count == 0)
1404         return 1; // there are no supported media streams. we have nothing to do.
1405
1406     for (i=0, str_buf.len=sizeof(media_str), str_buf.s=media_str; i<session.stream_count; i++) {
1407         stream = session.streams[i];
1408         if (stream.transport != TSupported)
1409             continue; // skip streams with unsupported transports
1410         if (stream.type.len + stream.ip.len + stream.port.len + stream.direction.len + 4 > str_buf.len) {
1411             LM_ERR("media stream description is longer than %lu bytes\n",
1412                                 (unsigned long)sizeof(media_str));
1413             return -1;
1414         }
1415         len = sprintf(str_buf.s, "%.*s:%.*s:%.*s:%.*s,",
1416                       stream.type.len, stream.type.s,
1417                       stream.ip.len, stream.ip.s,
1418                       stream.port.len, stream.port.s,
1419                       stream.direction.len, stream.direction.s);
1420         str_buf.s   += len;
1421         str_buf.len -= len;
1422     }
1423
1424     *(str_buf.s-1) = 0; // remove the last comma
1425
1426     from_uri     = get_from_uri(msg);
1427     to_uri       = get_to_uri(msg);
1428     from_tag     = get_from_tag(msg);
1429     to_tag       = get_to_tag(msg);
1430     user_agent   = get_user_agent(msg);
1431     signaling_ip = get_signaling_ip(msg);
1432     media_relay  = get_media_relay(msg);
1433
1434     len = snprintf(request, sizeof(request),
1435                    "update\r\n"
1436                    "type: %s\r\n"
1437                    "dialog_id: %s\r\n"
1438                    "call_id: %.*s\r\n"
1439                    "cseq: %.*s\r\n"
1440                    "from_uri: %.*s\r\n"
1441                    "to_uri: %.*s\r\n"
1442                    "from_tag: %.*s\r\n"
1443                    "to_tag: %.*s\r\n"
1444                    "user_agent: %.*s\r\n"
1445                    "media: %s\r\n"
1446                    "signaling_ip: %.*s\r\n"
1447                    "media_relay: %.*s\r\n"
1448                    "\r\n",
1449                    type, dialog_id, callid.len, callid.s, cseq.len, cseq.s,
1450                    from_uri.len, from_uri.s, to_uri.len, to_uri.s,
1451                    from_tag.len, from_tag.s, to_tag.len, to_tag.s,
1452                    user_agent.len, user_agent.s, media_str,
1453                    signaling_ip.len, signaling_ip.s,
1454                    media_relay.len, media_relay.s);
1455
1456     if (len >= sizeof(request)) {
1457         LM_ERR("mediaproxy request is longer than %lu bytes\n",
1458                         (unsigned long)sizeof(request));
1459         return -1;
1460     }
1461
1462     result = send_command(request);
1463
1464     if (result == NULL)
1465         return -1;
1466
1467     len = get_tokens(result, tokens, sizeof(tokens)/sizeof(str));
1468
1469     if (len == 0) {
1470         LM_ERR("empty response from mediaproxy\n");
1471         return -1;
1472     } else if (len==1 && STR_MATCH(tokens[0], "error")) {
1473         LM_ERR("mediaproxy returned error\n");
1474         return -1;
1475     } else if (len<session.supported_count+1) {
1476         if (msg->first_line.type == SIP_REQUEST) {
1477             LM_ERR("insufficient ports returned from mediaproxy: got %d, "
1478                    "expected %d\n", len-1, session.supported_count);
1479             return -1;
1480         } else {
1481             LM_WARN("broken client. Called UA added extra media stream(s) "
1482                     "in the OK reply\n");
1483         }
1484     }
1485
1486     removed_session_ip = False;
1487
1488     // only replace the session ip if there are no streams with unsupported
1489     // transports otherwise we insert an ip line in the supported streams
1490     // and remove the session level ip
1491     if (session.ip.s && !isnulladdr(session.ip)) {
1492         if (session.stream_count == session.supported_count) {
1493             if (!replace_element(msg, &session.ip, &tokens[0])) {
1494                 LM_ERR("failed to replace session-level media IP in the SDP body\n");
1495                 return -1;
1496             }
1497         } else {
1498             if (!remove_element(msg, &session.ip_line)) {
1499                 LM_ERR("failed to remove session-level media IP in the SDP body\n");
1500                 return -1;
1501             }
1502             removed_session_ip = True;
1503         }
1504     }
1505
1506     for (i=0, j=1; i<session.stream_count; i++) {
1507         stream = session.streams[i];
1508         if (stream.transport != TSupported) {
1509             if (!stream.local_ip && removed_session_ip) {
1510                 strcpy(buf, "c=IN IP4 ");
1511                 strncat(buf, session.ip.s, session.ip.len);
1512                 strncat(buf, session.separator.s, session.separator.len);
1513                 if (!insert_element(msg, stream.next_line, buf)) {
1514                     LM_ERR("failed to insert IP address in media stream number %d\n", i+1);
1515                     return -1;
1516                 }
1517             }
1518             continue;
1519         }
1520
1521         if (!isnullport(stream.port)) {
1522             if (!replace_element(msg, &stream.port, &tokens[j])) {
1523                 LM_ERR("failed to replace port in media stream number %d\n", i+1);
1524                 return -1;
1525             }
1526         }
1527
1528         if (stream.rtcp_port.len>0 && !isnullport(stream.rtcp_port)) {
1529             str rtcp_port;
1530
1531             port = strtoint(&tokens[j]);
1532             rtcp_port.s = int2str(port+1, &rtcp_port.len);
1533             if (!replace_element(msg, &stream.rtcp_port, &rtcp_port)) {
1534                 LM_ERR("failed to replace RTCP port in media stream number %d\n", i+1);
1535                 return -1;
1536             }
1537         }
1538
1539         if (stream.local_ip && !isnulladdr(stream.ip)) {
1540             if (!replace_element(msg, &stream.ip, &tokens[0])) {
1541                 LM_ERR("failed to replace IP address in media stream number %d\n", i+1);
1542                 return -1;
1543             }
1544         } else if (!stream.local_ip && removed_session_ip) {
1545             strcpy(buf, "c=IN IP4 ");
1546             strncat(buf, tokens[0].s, tokens[0].len);
1547             strncat(buf, session.separator.s, session.separator.len);
1548             if (!insert_element(msg, stream.next_line, buf)) {
1549                 LM_ERR("failed to insert IP address in media stream number %d\n", i+1);
1550                 return -1;
1551             }
1552         }
1553
1554         j++;
1555     }
1556
1557     return 1;
1558 }
1559
1560
1561 static int
1562 end_media_session(str callid, str from_tag, str to_tag)
1563 {
1564     char request[2048], *result;
1565     int len;
1566
1567     len = snprintf(request, sizeof(request),
1568                    "remove\r\n"
1569                    "call_id: %.*s\r\n"
1570                    "from_tag: %.*s\r\n"
1571                    "to_tag: %.*s\r\n"
1572                    "\r\n",
1573                    callid.len, callid.s,
1574                    from_tag.len, from_tag.s,
1575                    to_tag.len, to_tag.s);
1576
1577     if (len >= sizeof(request)) {
1578         LM_ERR("mediaproxy request is longer than %lu bytes\n",
1579                         (unsigned long)sizeof(request));
1580         return -1;
1581     }
1582
1583     result = send_command(request);
1584
1585     return result==NULL ? -1 : 1;
1586 }
1587
1588
1589 // Dialog callbacks and helpers
1590 //
1591
1592 typedef enum {
1593     MPInactive = 0,
1594     MPActive
1595 } MediaProxyState;
1596
1597
1598 static INLINE char*
1599 get_dialog_id(struct dlg_cell *dlg)
1600 {
1601     static char buffer[64];
1602
1603     snprintf(buffer, sizeof(buffer), "%d:%d", dlg->h_entry, dlg->h_id);
1604
1605     return buffer;
1606 }
1607
1608
1609 static void
1610 __dialog_requests(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
1611 {
1612     use_media_proxy(_params->msg, get_dialog_id(dlg));
1613 }
1614
1615
1616 static void
1617 __dialog_replies(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
1618 {
1619     struct sip_msg *reply = _params->msg;
1620
1621     if (reply == FAKED_REPLY)
1622         return;
1623
1624     if (reply->REPLY_STATUS>100 && reply->REPLY_STATUS<300) {
1625         use_media_proxy(reply, get_dialog_id(dlg));
1626     }
1627 }
1628
1629
1630 static void
1631 __dialog_ended(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
1632 {
1633     if ((int)(long)*_params->param == MPActive) {
1634         end_media_session(dlg->callid, dlg->tag[DLG_CALLER_LEG], dlg->tag[DLG_CALLEE_LEG]);
1635         *_params->param = MPInactive;
1636     }
1637 }
1638
1639
1640 static void
1641 __dialog_created(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
1642 {
1643     struct sip_msg *request = _params->msg;
1644
1645     if (request->REQ_METHOD != METHOD_INVITE)
1646         return;
1647
1648     if ((request->msg_flags & FL_USE_MEDIA_PROXY) == 0)
1649         return;
1650
1651     if (dlg_api.register_dlgcb(dlg, DLGCB_REQ_WITHIN, __dialog_requests, NULL, NULL) != 0)
1652         LM_ERR("cannot register callback for in-dialog requests\n");
1653     if (dlg_api.register_dlgcb(dlg, DLGCB_RESPONSE_FWDED | DLGCB_RESPONSE_WITHIN, __dialog_replies, NULL, NULL) != 0)
1654         LM_ERR("cannot register callback for dialog and in-dialog replies\n");
1655     if (dlg_api.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_DESTROY, __dialog_ended, (void*)MPActive, NULL) != 0)
1656         LM_ERR("cannot register callback for dialog termination\n");
1657
1658     use_media_proxy(request, get_dialog_id(dlg));
1659 }
1660
1661
1662 //
1663 // The public functions that are exported by this module
1664 //
1665
1666
1667 static int
1668 EngageMediaProxy(struct sip_msg *msg)
1669 {
1670     if (mediaproxy_disabled)
1671         return -1;
1672
1673     if (!have_dlg_api) {
1674         LM_ERR("engage_media_proxy requires the dialog module to be loaded and configured\n");
1675         return -1;
1676     }
1677     msg->msg_flags |= FL_USE_MEDIA_PROXY;
1678     setflag(msg, dialog_flag); // have the dialog module trace this dialog
1679     return 1;
1680 }
1681
1682
1683 static int
1684 UseMediaProxy(struct sip_msg *msg)
1685 {
1686     if (mediaproxy_disabled)
1687         return -1;
1688
1689     return use_media_proxy(msg, "");
1690 }
1691
1692
1693 static int
1694 EndMediaSession(struct sip_msg *msg)
1695 {
1696     str callid, from_tag, to_tag;
1697
1698     if (mediaproxy_disabled)
1699         return -1;
1700
1701     if (!get_callid(msg, &callid)) {
1702         LM_ERR("failed to get Call-ID\n");
1703         return -1;
1704     }
1705
1706     from_tag = get_from_tag(msg);
1707     to_tag   = get_to_tag(msg);
1708
1709     return end_media_session(callid, from_tag, to_tag);
1710 }
1711
1712
1713 //
1714 // Module management: initialization/destroy/function-parameter-fixing/...
1715 //
1716
1717
1718 static int
1719 mod_init(void)
1720 {
1721     pv_spec_t avp_spec;
1722     int *param;
1723         modparam_t type;
1724
1725     // initialize the signaling_ip_avp structure
1726     if (signaling_ip_avp.spec.s==NULL || *(signaling_ip_avp.spec.s)==0) {
1727         LM_WARN("missing/empty signaling_ip_avp parameter. will use default.\n");
1728         signaling_ip_avp.spec.s = SIGNALING_IP_AVP_SPEC;
1729     }
1730     signaling_ip_avp.spec.len = strlen(signaling_ip_avp.spec.s);
1731     if (pv_parse_spec(&(signaling_ip_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
1732         LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s);
1733         return -1;
1734     }
1735     if (pv_get_avp_name(0, &(avp_spec.pvp), &(signaling_ip_avp.name), &(signaling_ip_avp.type))!=0) {
1736         LM_CRIT("invalid AVP specification for signaling_ip_avp: `%s'\n", signaling_ip_avp.spec.s);
1737         return -1;
1738     }
1739
1740     // initialize the media_relay_avp structure
1741     if (media_relay_avp.spec.s==NULL || *(media_relay_avp.spec.s)==0) {
1742         LM_WARN("missing/empty media_relay_avp parameter. will use default.\n");
1743         media_relay_avp.spec.s = MEDIA_RELAY_AVP_SPEC;
1744     }
1745     media_relay_avp.spec.len = strlen(media_relay_avp.spec.s);
1746     if (pv_parse_spec(&(media_relay_avp.spec), &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
1747         LM_CRIT("invalid AVP specification for media_relay_avp: `%s'\n", media_relay_avp.spec.s);
1748         return -1;
1749     }
1750     if (pv_get_avp_name(0, &(avp_spec.pvp), &(media_relay_avp.name), &(media_relay_avp.type))!=0) {
1751         LM_CRIT("invalid AVP specification for media_relay_avp: `%s'\n", media_relay_avp.spec.s);
1752         return -1;
1753     }
1754
1755     // bind to the dialog API
1756     if (load_dlg_api(&dlg_api)==0) {
1757         have_dlg_api = True;
1758
1759         // load dlg_flag and default_timeout parameters from the dialog module
1760         param = find_param_export(find_module_by_name("dialog"), "dlg_flag", INT_PARAM, &type);
1761         if (!param) {
1762             LM_CRIT("cannot find dlg_flag parameter in the dialog module\n");
1763             return -1;
1764         }
1765
1766                 if (type != INT_PARAM) {
1767                         LM_CRIT("dlg_flag parameter found but with wrong type: %d\n", type);
1768                         return -1;
1769                 }
1770
1771         dialog_flag = *param;
1772
1773         // register dialog creation callback
1774         if (dlg_api.register_dlgcb(NULL, DLGCB_CREATED, __dialog_created, NULL, NULL) != 0) {
1775             LM_CRIT("cannot register callback for dialog creation\n");
1776             return -1;
1777         }
1778     } else {
1779         LM_NOTICE("engage_media_proxy() will not work because the dialog module is not loaded\n");
1780     }
1781
1782     return 0;
1783 }
1784
1785
1786 static int
1787 child_init(int rank)
1788 {
1789     // initialize the connection to mediaproxy if needed
1790     if (!mediaproxy_disabled)
1791         mediaproxy_connect();
1792
1793     return 0;
1794 }
1795
1796