www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / userapps / opensource / siproxd / src / rtpproxy_relay.c
1 /*
2     Copyright (C) 2003-2005  Thomas Ries <tries@gmx.net>
3
4     This file is part of Siproxd.
5     
6     Siproxd is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10     
11     Siproxd is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warrantry of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15     
16     You should have received a copy of the GNU General Public License
17     along with Siproxd; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19 */
20
21 #include "config.h"
22
23 #include <pthread.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <sys/time.h>
30
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <signal.h>
34
35 #ifdef HAVE_PTHREAD_SETSCHEDPARAM
36    #include <sched.h>
37 #endif
38
39 #include <osipparser2/osip_parser.h>
40
41 #include "siproxd.h"
42 #include "rtpproxy.h"
43 #include "log.h"
44
45 static char const ident[]="$Id: rtpproxy_relay.c,v 1.30 2005/01/24 19:12:49 hb9xar Exp $";
46
47 /* configuration storage */
48 extern struct siproxd_config configuration;
49
50 /*
51  * table to remember all active rtp proxy streams
52  */
53 rtp_proxytable_t rtp_proxytable[RTPPROXY_SIZE];
54
55 /*
56  * Mutex for thread synchronization (locking when accessing common 
57  * data structures -> rtp_proxytable[]).
58  *
59  * use a 'fast' mutex for synchronizing - as these are portable... 
60  */
61 static pthread_mutex_t rtp_proxytable_mutex = PTHREAD_MUTEX_INITIALIZER;
62
63 /* thread id of RTP proxy */
64 static pthread_t rtpproxy_tid=0;
65
66 /* master fd_set */
67 static fd_set master_fdset;
68 static int    master_fd_max;
69
70 /* forward declarations */
71 static void *rtpproxy_main(void *i);
72 static int rtp_recreate_fdset(void);
73 void rtpproxy_kill( void );
74 static void sighdl_alm(int sig) {/* just wake up from select() */};
75
76
77 /*
78  * initialize and create rtp_relay proxy thread
79  *
80  * RETURNS
81  *      STS_SUCCESS on success
82  */
83 int rtp_relay_init( void ) {
84    int sts;
85    int arg=0;
86    struct sigaction sigact;
87
88    atexit(rtpproxy_kill);  /* cancel RTP thread at exit */
89
90    /* clean proxy table */
91    memset (rtp_proxytable, 0, sizeof(rtp_proxytable));
92
93    /* initialize fd set for RTP proxy thread */
94    FD_ZERO(&master_fdset); /* start with an empty fdset */
95    master_fd_max=-1;
96
97    /* install signal handler for SIGALRM - used to wake up
98       the rtpproxy thread from select() hibernation */
99    sigact.sa_handler = sighdl_alm;
100    sigemptyset(&sigact.sa_mask);
101    sigact.sa_flags=0;
102    sigaction(SIGALRM, &sigact, NULL);
103
104    DEBUGC(DBCLASS_RTP,"create thread");
105    sts=pthread_create(&rtpproxy_tid, NULL, rtpproxy_main, (void *)&arg);
106    DEBUGC(DBCLASS_RTP,"created, sts=%i", sts);
107
108    /* set realtime scheduling - if started by root */
109 #ifdef HAVE_PTHREAD_SETSCHEDPARAM
110    {
111       int uid,euid;
112       struct sched_param schedparam;
113
114 #ifndef _CYGWIN
115       uid=getuid();
116       euid=geteuid();
117       DEBUGC(DBCLASS_RTP,"uid=%i, euid=%i", uid, euid);
118       if (uid != euid) seteuid(0);
119
120       if (geteuid()==0) {
121 #endif
122
123 #if defined(HAVE_SCHED_GET_PRIORITY_MAX) && defined(HAVE_SCHED_GET_PRIORITY_MIN)
124          int pmin, pmax;
125          /* place ourself at 1/3 of the available priority space */
126          pmin=sched_get_priority_min(SCHED_RR);
127          pmax=sched_get_priority_max(SCHED_RR);
128          schedparam.sched_priority=pmin+(pmax-pmin)/3;
129          DEBUGC(DBCLASS_RTP,"pmin=%i, pmax=%i, using p=%i", pmin, pmax,
130                 schedparam.sched_priority);
131 #else
132          /* just taken a number out of thin air */
133          schedparam.sched_priority=10;
134          DEBUGC(DBCLASS_RTP,"using p=%i", schedparam.sched_priority);
135 #endif
136          sts=pthread_setschedparam(rtpproxy_tid, SCHED_RR, &schedparam);
137          if (sts != 0) {
138             ERROR("pthread_setschedparam failed: %s", strerror(errno));
139          }
140 #ifndef _CYGWIN
141       } else {
142          INFO("Unable to use realtime scheduling for RTP proxy");
143          INFO("You may want to start siproxd as root and switch UID afterwards");
144       }
145       if (uid != euid)  seteuid(euid);
146 #endif
147    }
148 #endif
149
150    return STS_SUCCESS;
151 }
152
153
154 /*
155  * main() of rtpproxy
156  */
157 static void *rtpproxy_main(void *arg) {
158    struct timeval tv;
159    fd_set fdset;
160    int fd_max;
161    time_t t, last_t=0;
162    int i, sts;
163    int num_fd;
164    osip_call_id_t callid;
165    static char rtp_buff[RTP_BUFFER_SIZE];
166    int count;
167    int j;
168
169    memcpy(&fdset, &master_fdset, sizeof(fdset));
170    fd_max=master_fd_max;
171
172    /* loop forever... */
173    for (;;) {
174
175       tv.tv_sec = 5;
176       tv.tv_usec = 0;
177
178       num_fd=select(fd_max+1, &fdset, NULL, NULL, &tv);
179       pthread_testcancel();
180       if ((num_fd<0) && (errno==EINTR)) {
181          /*
182           * wakeup due to a change in the proxy table:
183           * lock mutex, copy master FD set and unlock
184           */
185          pthread_mutex_lock(&rtp_proxytable_mutex);
186          memcpy(&fdset, &master_fdset, sizeof(fdset));
187          fd_max=master_fd_max;
188          pthread_mutex_unlock(&rtp_proxytable_mutex);
189          continue;
190       }
191
192       time(&t);
193
194       /*
195        * LOCK the MUTEX
196        */
197       pthread_mutex_lock(&rtp_proxytable_mutex);
198
199       /* check for data available and send to destination */
200       for (i=0;(i<RTPPROXY_SIZE) && (num_fd>0);i++) {
201          if ( (rtp_proxytable[i].rtp_rx_sock != 0) && 
202             FD_ISSET(rtp_proxytable[i].rtp_rx_sock, &fdset) ) {
203             /* yup, have some data to send */
204             num_fd--;
205
206             /* read from sock rtp_proxytable[i].sock*/
207             count=read(rtp_proxytable[i].rtp_rx_sock, rtp_buff, RTP_BUFFER_SIZE);
208
209             /* check if something went banana */
210             if (count < 0) {
211                /*
212                 * It has been seen on linux 2.2.x systems that for some
213                 * reason (ICMP issue? -> below) inside the RTP relay, select()
214                 * claims that a certain file descriptor has data available to
215                 * read, a subsequent call to read() or recv() then does block!!
216                 * So lets make the FD's we are going to use non-blocking, so
217                 * we will at least survive and not run into a deadlock.
218                 * 
219                 * We catch this here with this workaround (pronounce "HACK")
220                 * and hope that next time we pass by it will be ok again.
221                 */
222                if (errno == EAGAIN) {
223                   /*&&&& I may want to remove this WARNing */
224                   WARN("read() [fd=%i, %s:%i] would block, but select() "
225                        "claimed to be readable!",
226                        rtp_proxytable[i].rtp_rx_sock,
227                        utils_inet_ntoa(rtp_proxytable[i].local_ipaddr),
228                        rtp_proxytable[i].local_port);
229                   continue;
230                }
231
232                /*
233                 * I *MAY* receive ICMP destination unreachable messages when I
234                 * try to send RTP traffic to a destination that is in HOLD
235                 * (better: is not listening on the UDP port where I send
236                 * my RTP data to).
237                 * So I should *not* do this - or ignore errors originating
238                 * by this -> ECONNREFUSED
239                 *
240                 * Note: This error is originating from a previous send() on the
241                 *       same socket and has nothing to do with the read() we have
242                 *       done above!
243                 */
244                if (errno != ECONNREFUSED) {
245                   /* some other error that I probably want to know about */
246                   int j;
247                   WARN("read() [fd=%i, %s:%i] returned error [%i:%s]",
248                   rtp_proxytable[i].rtp_rx_sock,
249                   utils_inet_ntoa(rtp_proxytable[i].local_ipaddr),
250                   rtp_proxytable[i].local_port, errno, strerror(errno));
251                   for (j=0; j<RTPPROXY_SIZE;j++) {
252                      DEBUGC(DBCLASS_RTP, "%i - rx:%i tx:%i %s@%s dir:%i "
253                             "lp:%i, rp:%i rip:%s",
254                             j,
255                             rtp_proxytable[j].rtp_rx_sock,
256                             rtp_proxytable[j].rtp_tx_sock,
257                             rtp_proxytable[j].callid_number,
258                             rtp_proxytable[j].callid_host,
259                             rtp_proxytable[j].direction,
260                             rtp_proxytable[j].local_port,
261                             rtp_proxytable[j].remote_port,
262                             utils_inet_ntoa(rtp_proxytable[j].remote_ipaddr));
263                   } /* for j */
264               } /* if errno != ECONNREFUSED */
265             } /* count < 0 */
266
267             /*
268              * forwarding an RTP packet only makes sense if we really
269              * have got some data in it (count > 0)
270              */
271             if (count > 0) {
272                /* find the corresponding TX socket */
273                if (rtp_proxytable[i].rtp_tx_sock == 0) {
274                   int rtp_direction = rtp_proxytable[i].direction;
275                     
276                   //int media_stream_no = rtp_proxytable[i].media_stream_no;
277                    //added by polowang;
278                   int media_stream_no = rtp_proxytable[i].media_stream_no;
279
280                   callid.number = rtp_proxytable[i].callid_number;
281                   callid.host = rtp_proxytable[i].callid_host;
282
283                   for (j=0;(j<RTPPROXY_SIZE);j++) {
284                      char *client_id = rtp_proxytable[i].client_id;
285                      osip_call_id_t cid;
286                      cid.number = rtp_proxytable[j].callid_number;
287                      cid.host = rtp_proxytable[j].callid_host;
288
289                      /* match on:
290                       * - same call ID
291                       * - same media stream
292                       * - opposite direction
293                       * - different client ID
294                       */
295                      if ( (rtp_proxytable[j].rtp_rx_sock != 0) &&
296                           (compare_callid(&callid, &cid) == STS_SUCCESS) &&
297                           (media_stream_no == rtp_proxytable[j].media_stream_no) &&
298                           (rtp_direction != rtp_proxytable[j].direction) &&
299                           (strcmp(rtp_proxytable[j].client_id, client_id) != 0) )
300                          {
301                         rtp_proxytable[i].rtp_tx_sock = rtp_proxytable[j].rtp_rx_sock;
302                         DEBUGC(DBCLASS_RTP, "connected entry %i (fd=%i) <-> entry %i (fd=%i)",
303                                j, rtp_proxytable[j].rtp_rx_sock,
304                                i, rtp_proxytable[i].rtp_rx_sock);
305                         break;
306                      }
307                   }
308                } /* rtp_tx_sock == 0 */
309
310                if (rtp_proxytable[i].rtp_tx_sock != 0) {
311                   /* write to dest via socket rtp_tx_sock */
312                   struct sockaddr_in dst_addr;
313                   dst_addr.sin_family = AF_INET;
314                   memcpy(&dst_addr.sin_addr.s_addr,
315                          &rtp_proxytable[i].remote_ipaddr, 
316                          sizeof(struct in_addr));
317                   dst_addr.sin_port= htons(rtp_proxytable[i].remote_port);
318
319                   sts = sendto(rtp_proxytable[i].rtp_tx_sock, rtp_buff,
320                                count, 0, (const struct sockaddr *)&dst_addr,
321                                (socklen_t)sizeof(dst_addr));
322                   if (sts == -1) {
323                      if (errno != ECONNREFUSED) {
324                         ERROR("sendto() [%s:%i size=%i] call failed: %s",
325                         utils_inet_ntoa(rtp_proxytable[i].remote_ipaddr),
326                         rtp_proxytable[i].remote_port, count, strerror(errno));
327
328                     /* if sendto() fails with bad filedescriptor,
329                      * this means that the opposite stream has been
330                      * canceled or timed out.
331                      * we should then cancel this stream as well.*/
332
333                     WARN("stopping opposite stream");
334                     /* don't lock the mutex, as we own the lock */
335                     callid.number=rtp_proxytable[i].callid_number;
336                     callid.host=rtp_proxytable[i].callid_host;
337                     rtp_relay_stop_fwd(rtp_proxytable[i].rtp_rx_sock, &callid, rtp_proxytable[i].direction, 1);
338                      }
339                   }
340                }
341                /* in case of rtp_tx_sock is not same as rtp_rx_sock, need update rtp_tx_sock too */
342                for (j=0;(j<RTPPROXY_SIZE);j++) {
343                   if (rtp_proxytable[i].rtp_tx_sock == rtp_proxytable[j].rtp_rx_sock) {
344                      rtp_proxytable[j].timestamp=t;
345                      break;
346                   }
347                }
348             } /* count > 0 */
349
350             /* update timestamp of last usage */
351             rtp_proxytable[i].timestamp=t;
352          }
353
354          /* handle rtcp packet */
355          if ( (rtp_proxytable[i].rtcp_rx_sock != 0) && 
356             FD_ISSET(rtp_proxytable[i].rtcp_rx_sock, &fdset) ) {
357             /* yup, have some data to send */
358             num_fd--;
359
360             /* read from sock rtp_proxytable[i].sock*/
361             count=read(rtp_proxytable[i].rtcp_rx_sock, rtp_buff, RTP_BUFFER_SIZE);
362
363             /*
364              * forwarding an RTP packet only makes sense if we really
365              * have got some data in it (count > 0)
366              */
367             if (count > 0) {
368                /* find the corresponding TX socket */
369                if (rtp_proxytable[i].rtcp_tx_sock == 0) {
370                   int j;
371                   int rtp_direction = rtp_proxytable[i].direction;
372                   int media_stream_no = rtp_proxytable[i].media_stream_no;
373
374                   callid.number = rtp_proxytable[i].callid_number;
375                   callid.host = rtp_proxytable[i].callid_host;
376
377                   for (j=0;(j<RTPPROXY_SIZE);j++) {
378                      char *client_id = rtp_proxytable[i].client_id;
379                      osip_call_id_t cid;
380                      cid.number = rtp_proxytable[j].callid_number;
381                      cid.host = rtp_proxytable[j].callid_host;
382
383                      if ( (rtp_proxytable[j].rtcp_rx_sock != 0) &&
384                           (compare_callid(&callid, &cid) == STS_SUCCESS) &&
385                           (media_stream_no == rtp_proxytable[j].media_stream_no) &&
386                           (rtp_direction != rtp_proxytable[j].direction) &&
387                           (strcmp(rtp_proxytable[j].client_id, client_id) != 0) ) {
388                         rtp_proxytable[i].rtcp_tx_sock = rtp_proxytable[j].rtcp_rx_sock;
389                         break;
390                      }
391                   }
392                } /* rtcp_tx_sock == 0 */
393
394                if (rtp_proxytable[i].rtcp_tx_sock != 0) {
395                   /* write to dest via socket rtcp_tx_sock */
396                   struct sockaddr_in dst_addr;
397                   dst_addr.sin_family = AF_INET;
398                   memcpy(&dst_addr.sin_addr.s_addr,
399                          &rtp_proxytable[i].remote_ipaddr, 
400                          sizeof(struct in_addr));
401                   dst_addr.sin_port= htons(rtp_proxytable[i].remote_port+1);
402
403                   sts = sendto(rtp_proxytable[i].rtcp_tx_sock, rtp_buff,
404                                count, 0, (const struct sockaddr *)&dst_addr,
405                                (socklen_t)sizeof(dst_addr));
406                   if (sts == -1) {
407                      if (errno != ECONNREFUSED) {
408                         ERROR("sendto() [%s:%i size=%i] call failed: %s",
409                         utils_inet_ntoa(rtp_proxytable[i].remote_ipaddr),
410                         rtp_proxytable[i].remote_port+1, count, strerror(errno));
411
412                     /* if sendto() fails with bad filedescriptor,
413                      * this means that the opposite stream has been
414                      * canceled or timed out.
415                      * we should then cancel this stream as well.*/
416
417                     WARN("stopping opposite stream");
418                     /* don't lock the mutex, as we own the lock */
419                     callid.number=rtp_proxytable[i].callid_number;
420                     callid.host=rtp_proxytable[i].callid_host;
421                     rtp_relay_stop_fwd(rtp_proxytable[i].rtcp_rx_sock, &callid, rtp_proxytable[i].direction, 1);
422                      }
423                   }
424                }
425             } /* count > 0 */
426
427             /* update timestamp of last usage */
428             rtp_proxytable[i].timestamp=t;
429          }
430      } /* for i */
431
432       /*
433        * age and clean rtp_proxytable (check every 10 seconds)
434        */
435       if (t > (last_t+10) ) {
436          last_t = t;
437          for (i=0;i<RTPPROXY_SIZE; i++) {
438             if ( (rtp_proxytable[i].rtp_rx_sock != 0) &&
439                  ((rtp_proxytable[i].timestamp+configuration.rtp_timeout)<t)) {
440                /* this one has expired, clean it up */
441                callid.number=rtp_proxytable[i].callid_number;
442                callid.host=rtp_proxytable[i].callid_host;
443                DEBUGC(DBCLASS_RTP,"RTP stream rx_sock=%i tx_sock=%i "
444                       "%s@%s (idx=%i) has expired",
445                       rtp_proxytable[i].rtp_rx_sock,
446                       rtp_proxytable[i].rtp_tx_sock,
447                       callid.number, callid.host, i);
448                /* don't lock the mutex, as we own the lock already here */
449                rtp_relay_stop_fwd(rtp_proxytable[i].rtp_rx_sock, &callid, rtp_proxytable[i].direction, 1);
450             }
451          }
452       } /* if (t>...) */
453
454       /* copy master FD set */
455       memcpy(&fdset, &master_fdset, sizeof(fdset));
456       fd_max=master_fd_max;
457
458       /*
459        * UNLOCK the MUTEX
460        */
461       pthread_mutex_unlock(&rtp_proxytable_mutex);
462    } /* for(;;) */
463
464    return NULL;
465 }
466
467
468 /*
469  * start an rtp stream on the proxy
470  *
471  * RETURNS
472  *      STS_SUCCESS on success
473  *      STS_FAILURE on error
474  */
475 int rtp_relay_start_fwd (osip_call_id_t *callid, char *client_id,
476                          int rtp_direction,
477                          int media_stream_no, struct in_addr local_ipaddr,
478                          int *local_port, struct in_addr remote_ipaddr,
479                          int remote_port) {
480    int i, j;
481    int sock, port;
482    int freeidx;
483    int sts=STS_SUCCESS;
484    osip_call_id_t cid;
485    int rtcp_sock;
486    
487
488    if (callid == NULL) {
489       ERROR("rtp_relay_start_fwd: callid is NULL!");
490       return STS_FAILURE;
491    }
492
493    if (client_id == NULL) {
494       ERROR("rtp_relay_start_fwd: did not get a client ID!");
495       return STS_FAILURE;
496    }
497
498    /*
499     * life insurance: check size of received call_id strings
500     * I don't know what the maximum allowed size within SIP is,
501     * so if this test fails maybe it's just necessary to increase
502     * the constants CALLIDNUM_SIZE and/or CALLIDHOST_SIZE.
503     */
504    if (callid->number && strlen(callid->number) > CALLIDNUM_SIZE) {
505       ERROR("rtp_relay_start_fwd: received callid number "
506             "has too many characters (%i, max=%i)",
507             strlen(callid->number),CALLIDNUM_SIZE);
508       return STS_FAILURE;
509    }
510    if (callid->host && strlen(callid->host) > CALLIDHOST_SIZE) {
511       ERROR("rtp_relay_start_fwd: received callid host "
512             "has too many characters (%i, max=%i)",
513             strlen(callid->host),CALLIDHOST_SIZE);
514       return STS_FAILURE;
515    }
516    if (client_id && strlen(client_id) > CLIENT_ID_SIZE) {
517       ERROR("rtp_relay_start_fwd: client ID has too many characters "
518             "(%i, max=%i) (maybe you need to increase CLIENT_ID_SIZE",
519             strlen(client_id),CLIENT_ID_SIZE);
520       return STS_FAILURE;
521    }
522
523    DEBUGC(DBCLASS_RTP,"rtp_relay_start_fwd: starting RTP proxy "
524           "stream for: %s@%s[%s] (%s) #=%i",
525           callid->number, callid->host, client_id,
526           ((rtp_direction == DIR_INCOMING) ? "incoming RTP" : "outgoing RTP"),
527           media_stream_no);
528
529    /* lock mutex */
530    #define return is_forbidden_in_this_code_section
531    pthread_mutex_lock(&rtp_proxytable_mutex);
532    /*
533     * !! We now have a locked MUTEX! It is forbidden to return() from
534     * !! here up to the end of this funtion where the MUTEX is
535     * !! unlocked again.
536     * !! Per design, a mutex is locked (for one purpose) at *exactly one*
537     * !! place in the code and unlocked also at *exactly one* place.
538     * !! this minimizes the risk of deadlocks.
539     */
540
541    /*
542     * figure out, if this is an request to start an RTP proxy stream
543     * that is already existing (identified by SIP Call-ID, direction,
544     * media_stream_no and some other client unique thing).
545     * This can be due to UDP repetitions of the INVITE request...
546     */
547    for (i=0; i<RTPPROXY_SIZE; i++) {
548       cid.number = rtp_proxytable[i].callid_number;
549       cid.host   = rtp_proxytable[i].callid_host;
550       if (rtp_proxytable[i].rtp_rx_sock &&
551          (compare_callid(callid, &cid) == STS_SUCCESS) &&
552          (rtp_proxytable[i].direction == rtp_direction) &&
553          (rtp_proxytable[i].media_stream_no == media_stream_no) &&
554          (strcmp(rtp_proxytable[i].client_id, client_id) == 0)) {
555          /*
556           * The RTP port number reported by the UA MAY change
557           * for a given media stream
558           * (seen with KPhone during HOLD/unHOLD)
559           * Also the destination IP may change during a re-Invite
560           * (seen with Sipphone.com, re-Invites when using
561           * the SIP - POTS gateway [SIP Minutes] 
562           */
563          /* Port number */
564          if (rtp_proxytable[i].remote_port != remote_port) {
565             DEBUGC(DBCLASS_RTP,"RTP port number changed %i -> %i",
566                    rtp_proxytable[i].remote_port, remote_port);
567             rtp_proxytable[i].remote_port = remote_port;
568          }
569          /* IP address */
570          if (memcmp(&rtp_proxytable[i].remote_ipaddr, &remote_ipaddr,
571                     sizeof(remote_ipaddr))) {
572             DEBUGC(DBCLASS_RTP,"RTP IP address changed to %s",
573                    utils_inet_ntoa(remote_ipaddr));
574             memcpy (&rtp_proxytable[i].remote_ipaddr, &remote_ipaddr,
575                      sizeof(remote_ipaddr));
576          }
577          /* return the already known local port number */
578          DEBUGC(DBCLASS_RTP,"RTP stream already active (remaddr=%s, "
579                 "remport=%i, lclport=%i, id=%s, #=%i)",
580                 utils_inet_ntoa(remote_ipaddr),
581                 rtp_proxytable[i].remote_port,
582                 rtp_proxytable[i].local_port,
583                 rtp_proxytable[i].callid_number,
584                 rtp_proxytable[i].media_stream_no);
585          *local_port=rtp_proxytable[i].local_port;
586          sts = STS_SUCCESS;
587          goto unlock_and_exit;
588       }
589    }
590
591
592    /*
593     * find first free slot in rtp_proxytable
594     */
595    freeidx=-1;
596    for (j=0; j<RTPPROXY_SIZE; j++) {
597       if (rtp_proxytable[j].rtp_rx_sock==0) {
598          freeidx=j;
599          break;
600       }
601    }
602
603    /* rtp_proxytable port pool full? */
604    if (freeidx == -1) {
605       ERROR("rtp_relay_start_fwd: rtp_proxytable is full!");
606       sts = STS_FAILURE;
607       goto unlock_and_exit;
608    }
609
610    /* TODO: randomize the port allocation - start at a random offset to
611          search in the allowed port range (so some modulo stuff w/
612          random start offset 
613          - for i=x to (p1-p0)+x; p=p0+mod(x,p1-p0) */
614
615    /* find a local port number to use and bind to it */
616    sock=0;
617    port=0;
618    rtcp_sock=0;
619    for (i=configuration.rtp_port_low; i<=configuration.rtp_port_high; i+=2) {
620       for (j=0; j<RTPPROXY_SIZE; j++) {
621          /* check if port already in use */
622          if ((memcmp(&rtp_proxytable[j].local_ipaddr,
623                      &local_ipaddr, sizeof(struct in_addr))== 0) &&
624              (rtp_proxytable[j].local_port == i) ) break;
625       }
626
627       /* port is available, try to allocate */
628       if (j == RTPPROXY_SIZE) {
629          port=i;
630          sock=sockbind(local_ipaddr, port, 0);
631          /* if success break, else try further on */
632          if (sock) {
633             rtcp_sock=sockbind(local_ipaddr, port+1, 0);
634             if (rtcp_sock)
635                 break;
636             else
637                 close(sock);
638          }
639       }
640    } /* for i */
641
642    DEBUGC(DBCLASS_RTP,"rtp_relay_start_fwd: addr=%s, port=%i, sock=%i rtcp_sock=%i "
643           "freeidx=%i", utils_inet_ntoa(local_ipaddr), port, sock, rtcp_sock, freeidx);
644
645    /* found an unused port? No -> RTP port pool fully allocated */
646    if ((port == 0) || (sock == 0) || (rtcp_sock == 0)) {
647       ERROR("rtp_relay_start_fwd: no RTP port available or bind() failed");
648       sts = STS_FAILURE;
649       goto unlock_and_exit;
650    }
651
652    /* write entry into rtp_proxytable slot (freeidx) */
653    rtp_proxytable[freeidx].rtp_rx_sock=sock;
654    rtp_proxytable[freeidx].rtcp_rx_sock=rtcp_sock;
655
656    if (callid->number) {
657       strcpy(rtp_proxytable[freeidx].callid_number, callid->number);
658    } else {
659       rtp_proxytable[freeidx].callid_number[0]='\0';
660    }
661
662    if (callid->host) {
663       strcpy(rtp_proxytable[freeidx].callid_host, callid->host);
664    } else {
665       rtp_proxytable[freeidx].callid_host[0]='\0';
666    }
667
668    if (client_id) {
669       strcpy(rtp_proxytable[freeidx].client_id, client_id);
670    } else {
671       rtp_proxytable[freeidx].client_id[0]='\0';
672    }
673
674    rtp_proxytable[freeidx].direction = rtp_direction;
675    rtp_proxytable[freeidx].media_stream_no = media_stream_no;
676    memcpy(&rtp_proxytable[freeidx].local_ipaddr,
677           &local_ipaddr, sizeof(struct in_addr));
678    rtp_proxytable[freeidx].local_port=port;
679    memcpy(&rtp_proxytable[freeidx].remote_ipaddr,
680           &remote_ipaddr, sizeof(struct in_addr));
681    rtp_proxytable[freeidx].remote_port=remote_port;
682    time(&rtp_proxytable[freeidx].timestamp);
683
684    *local_port=port;
685
686    /* call to firewall API */
687    fwapi_start_rtp(rtp_proxytable[freeidx].direction,
688                    rtp_proxytable[freeidx].local_ipaddr,
689                    rtp_proxytable[freeidx].local_port,
690                    rtp_proxytable[freeidx].remote_ipaddr,
691                    rtp_proxytable[freeidx].remote_port);
692
693    /* prepare FD set for next select operation */
694    rtp_recreate_fdset();
695
696    /* wakeup/signal rtp_proxythread from select() hibernation */
697    if (!pthread_equal(rtpproxy_tid, pthread_self()))
698       pthread_kill(rtpproxy_tid, SIGALRM);
699
700 unlock_and_exit:
701    /* unlock mutex */
702    pthread_mutex_unlock(&rtp_proxytable_mutex);
703    #undef return
704
705    return sts;
706 }
707
708
709 /*
710  * stop a rtp stream on the proxy
711  *
712  * RETURNS
713  *      STS_SUCCESS on success
714  *      STS_FAILURE on error
715  */
716 int rtp_relay_stop_fwd (int rtp_rx_sock, osip_call_id_t *callid,
717                         int rtp_direction, int nolock) {
718    int i, sts;
719    int retsts=STS_SUCCESS;
720    int got_match=0;
721    osip_call_id_t cid;
722  
723    if (callid == NULL) {
724       ERROR("rtp_relay_stop_fwd: callid is NULL!");
725       return STS_FAILURE;
726    }
727
728    DEBUGC(DBCLASS_RTP,"rtp_relay_stop_fwd: stopping RTP proxy "
729           "stream for: %s@%s (%s)",
730           callid->number, callid->host,
731           ((rtp_direction == DIR_INCOMING) ? "incoming" : "outgoing"));
732
733    /*
734     * lock mutex - only if not requested to skip the lock.
735     * this is needed as we are also called from within
736     * the RTP thread itself - and there we already own the lock.
737     */
738    #define return is_forbidden_in_this_code_section
739    if (nolock == 0) {
740       pthread_mutex_lock(&rtp_proxytable_mutex);
741       /*
742        * !! We now have a locked MUTEX! It is forbidden to return() from
743        * !! here up to the end of this funtion where the MUTEX is
744        * !! unlocked again.
745        * !! Per design, a mutex is locked (for one purpose) at *exactly one*
746        * !! place in the code and unlocked also at *exactly one* place.
747        * !! this minimizes the risk of deadlocks.
748        */
749    }
750    /* 
751    * wakeup/signal rtp_proxythread from select() hibernation.
752    * This must be done here before we close the socket, otherwise
753    * we may get an select() error later from the proxy thread that
754    * is still hibernating in select() now.
755    */
756    if (!pthread_equal(rtpproxy_tid, pthread_self()))
757       pthread_kill(rtpproxy_tid, SIGALRM);
758
759    /*
760     * find the proper entry in rtp_proxytable
761     * we need to loop the whole table, as there might be multiple
762     * media strema active for the same callid (audio + video stream)
763     */
764    for (i=0; i<RTPPROXY_SIZE; i++) {
765       cid.number = rtp_proxytable[i].callid_number;
766       cid.host   = rtp_proxytable[i].callid_host;
767       if (rtp_proxytable[i].rtp_rx_sock &&
768          (compare_callid(callid, &cid) == STS_SUCCESS) &&
769          (rtp_proxytable[i].direction == rtp_direction)) {
770          if (rtp_rx_sock != 0) {
771             if (rtp_proxytable[i].rtp_rx_sock != rtp_rx_sock) {
772                continue;
773             }
774          }
775          sts = close(rtp_proxytable[i].rtp_rx_sock);
776          DEBUGC(DBCLASS_RTP,"closed socket %i for RTP stream "
777                 "%s:%s == %s:%s  (idx=%i) sts=%i",
778                 rtp_proxytable[i].rtp_rx_sock,
779                 rtp_proxytable[i].callid_number,
780                 rtp_proxytable[i].callid_host,
781                 callid->number, callid->host, i, sts);
782          if (sts < 0) {
783             ERROR("Error in close(%i): %s nolock=%i %s:%s\n",
784                   rtp_proxytable[i].rtp_rx_sock,
785                   strerror(errno), nolock,
786                   callid->number, callid->host);
787          }
788          if (rtp_proxytable[i].rtcp_rx_sock) {
789             sts = close(rtp_proxytable[i].rtcp_rx_sock);
790             DEBUGC(DBCLASS_RTP,"closed socket %i for RTP stream "
791                     "%s:%s == %s:%s  (idx=%i) sts=%i",
792                     rtp_proxytable[i].rtcp_rx_sock,
793                     rtp_proxytable[i].callid_number,
794                     rtp_proxytable[i].callid_host,
795                     callid->number, callid->host, i, sts);
796             if (sts < 0) {
797                 ERROR("Error in close(%i): %s nolock=%i %s:%s\n",
798                     rtp_proxytable[i].rtcp_rx_sock,
799                     strerror(errno), nolock,
800                     callid->number, callid->host);
801             }
802          }
803          /* call to firewall API */
804          fwapi_stop_rtp(rtp_proxytable[i].direction,
805                    rtp_proxytable[i].local_ipaddr,
806                    rtp_proxytable[i].local_port,
807                    rtp_proxytable[i].remote_ipaddr,
808                    rtp_proxytable[i].remote_port);
809          /* clean up */
810          memset(&rtp_proxytable[i], 0, sizeof(rtp_proxytable[0]));
811          got_match=1;
812       }
813  
814    }
815
816    /* did not find an active stream... */
817    if (!got_match) {
818       DEBUGC(DBCLASS_RTP,
819              "rtp_relay_stop_fwd: can't find active stream for %s@%s (%s)",
820              callid->number, callid->host,
821              ((rtp_direction == DIR_INCOMING) ? "incoming RTP" : "outgoing RTP"));
822       retsts = STS_FAILURE;
823       goto unlock_and_exit;
824    }
825
826
827    /* prepare FD set for next select operation */
828    rtp_recreate_fdset();
829    
830
831 unlock_and_exit:
832    /*
833     * unlock mutex - only if not requested to skip the lock.
834     * this is needed as we are also called from within
835     * the RTP thread itself - and there we already own the lock.
836     */
837    if (nolock == 0) {
838       pthread_mutex_unlock(&rtp_proxytable_mutex);
839    }
840    #undef return
841
842    return retsts;
843 }
844
845
846 /*
847  * some sockets have been newly created or removed -
848  * recreate the FD set for next select operation
849  *
850  * RETURNS
851  *      STS_SUCCESS on success (always)
852  */
853 static int rtp_recreate_fdset(void) {
854    int i;
855
856    FD_ZERO(&master_fdset);
857    master_fd_max=-1;
858    for (i=0;i<RTPPROXY_SIZE;i++) {
859       if (rtp_proxytable[i].rtp_rx_sock != 0) {
860          FD_SET(rtp_proxytable[i].rtp_rx_sock, &master_fdset);
861          if (rtp_proxytable[i].rtp_rx_sock > master_fd_max) {
862                master_fd_max=rtp_proxytable[i].rtp_rx_sock;
863          }
864       }
865       if (rtp_proxytable[i].rtcp_rx_sock != 0) {
866          FD_SET(rtp_proxytable[i].rtcp_rx_sock, &master_fdset);
867          if (rtp_proxytable[i].rtcp_rx_sock > master_fd_max) {
868                master_fd_max=rtp_proxytable[i].rtcp_rx_sock;
869          }
870       }
871    } /* for i */
872    return STS_SUCCESS;
873 }
874
875
876 /*
877  * kills the rtp_proxy thread
878  *
879  * RETURNS
880  *      -
881  */
882 void rtpproxy_kill( void ) {
883    void *thread_status;
884    osip_call_id_t cid;
885    int i, sts;
886
887    /* stop any active RTP stream */
888    for (i=0;i<RTPPROXY_SIZE;i++) {
889       if (rtp_proxytable[i].rtp_rx_sock != 0) {
890          cid.number = rtp_proxytable[i].callid_number;
891          cid.host   = rtp_proxytable[i].callid_host;
892          sts = rtp_relay_stop_fwd(rtp_proxytable[i].rtp_rx_sock, &cid, rtp_proxytable[i].direction, 0);
893       }
894    }
895    
896
897    /* kill the thread */
898    if (rtpproxy_tid) {
899       pthread_cancel(rtpproxy_tid);
900       pthread_kill(rtpproxy_tid, SIGALRM);
901       pthread_join(rtpproxy_tid, &thread_status);
902    }
903
904    DEBUGC(DBCLASS_RTP,"killed RTP proxy thread");
905    return;
906 }
907