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