and added files
[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                   //int media_stream_no = rtp_proxytable[i].media_stream_no;
276
277                   callid.number = rtp_proxytable[i].callid_number;
278                   callid.host = rtp_proxytable[i].callid_host;
279
280                   for (j=0;(j<RTPPROXY_SIZE);j++) {
281                      char *client_id = rtp_proxytable[i].client_id;
282                      osip_call_id_t cid;
283                      cid.number = rtp_proxytable[j].callid_number;
284                      cid.host = rtp_proxytable[j].callid_host;
285
286                      /* match on:
287                       * - same call ID
288                       * - same media stream
289                       * - opposite direction
290                       * - different client ID
291                       */
292                      if ( (rtp_proxytable[j].rtp_rx_sock != 0) &&
293                           (compare_callid(&callid, &cid) == STS_SUCCESS) &&
294                           //(media_stream_no == rtp_proxytable[j].media_stream_no) &&
295                           (rtp_direction != rtp_proxytable[j].direction) &&
296                           (strcmp(rtp_proxytable[j].client_id, client_id) != 0) ) {
297                         rtp_proxytable[i].rtp_tx_sock = rtp_proxytable[j].rtp_rx_sock;
298                         DEBUGC(DBCLASS_RTP, "connected entry %i (fd=%i) <-> entry %i (fd=%i)",
299                                j, rtp_proxytable[j].rtp_rx_sock,
300                                i, rtp_proxytable[i].rtp_rx_sock);
301                         break;
302                      }
303                   }
304                } /* rtp_tx_sock == 0 */
305
306                if (rtp_proxytable[i].rtp_tx_sock != 0) {
307                   /* write to dest via socket rtp_tx_sock */
308                   struct sockaddr_in dst_addr;
309                   dst_addr.sin_family = AF_INET;
310                   memcpy(&dst_addr.sin_addr.s_addr,
311                          &rtp_proxytable[i].remote_ipaddr, 
312                          sizeof(struct in_addr));
313                   dst_addr.sin_port= htons(rtp_proxytable[i].remote_port);
314
315                   sts = sendto(rtp_proxytable[i].rtp_tx_sock, rtp_buff,
316                                count, 0, (const struct sockaddr *)&dst_addr,
317                                (socklen_t)sizeof(dst_addr));
318                   if (sts == -1) {
319                      if (errno != ECONNREFUSED) {
320                         ERROR("sendto() [%s:%i size=%i] call failed: %s",
321                         utils_inet_ntoa(rtp_proxytable[i].remote_ipaddr),
322                         rtp_proxytable[i].remote_port, count, strerror(errno));
323
324                     /* if sendto() fails with bad filedescriptor,
325                      * this means that the opposite stream has been
326                      * canceled or timed out.
327                      * we should then cancel this stream as well.*/
328
329                     WARN("stopping opposite stream");
330                     /* don't lock the mutex, as we own the lock */
331                     callid.number=rtp_proxytable[i].callid_number;
332                     callid.host=rtp_proxytable[i].callid_host;
333                     rtp_relay_stop_fwd(rtp_proxytable[i].rtp_rx_sock, &callid, rtp_proxytable[i].direction, 1);
334                      }
335                   }
336                }
337                /* in case of rtp_tx_sock is not same as rtp_rx_sock, need update rtp_tx_sock too */
338                for (j=0;(j<RTPPROXY_SIZE);j++) {
339                   if (rtp_proxytable[i].rtp_tx_sock == rtp_proxytable[j].rtp_rx_sock) {
340                      rtp_proxytable[j].timestamp=t;
341                      break;
342                   }
343                }
344             } /* count > 0 */
345
346             /* update timestamp of last usage */
347             rtp_proxytable[i].timestamp=t;
348          }
349
350          /* handle rtcp packet */
351          if ( (rtp_proxytable[i].rtcp_rx_sock != 0) && 
352             FD_ISSET(rtp_proxytable[i].rtcp_rx_sock, &fdset) ) {
353             /* yup, have some data to send */
354             num_fd--;
355
356             /* read from sock rtp_proxytable[i].sock*/
357             count=read(rtp_proxytable[i].rtcp_rx_sock, rtp_buff, RTP_BUFFER_SIZE);
358
359             /*
360              * forwarding an RTP packet only makes sense if we really
361              * have got some data in it (count > 0)
362              */
363             if (count > 0) {
364                /* find the corresponding TX socket */
365                if (rtp_proxytable[i].rtcp_tx_sock == 0) {
366                   int j;
367                   int rtp_direction = rtp_proxytable[i].direction;
368                   //int media_stream_no = rtp_proxytable[i].media_stream_no;
369
370                   callid.number = rtp_proxytable[i].callid_number;
371                   callid.host = rtp_proxytable[i].callid_host;
372
373                   for (j=0;(j<RTPPROXY_SIZE);j++) {
374                      char *client_id = rtp_proxytable[i].client_id;
375                      osip_call_id_t cid;
376                      cid.number = rtp_proxytable[j].callid_number;
377                      cid.host = rtp_proxytable[j].callid_host;
378
379                      if ( (rtp_proxytable[j].rtcp_rx_sock != 0) &&
380                           (compare_callid(&callid, &cid) == STS_SUCCESS) &&
381                           //(media_stream_no == rtp_proxytable[j].media_stream_no) &&
382                           (rtp_direction != rtp_proxytable[j].direction) &&
383                           (strcmp(rtp_proxytable[j].client_id, client_id) != 0) ) {
384                         rtp_proxytable[i].rtcp_tx_sock = rtp_proxytable[j].rtcp_rx_sock;
385                         break;
386                      }
387                   }
388                } /* rtcp_tx_sock == 0 */
389
390                if (rtp_proxytable[i].rtcp_tx_sock != 0) {
391                   /* write to dest via socket rtcp_tx_sock */
392                   struct sockaddr_in dst_addr;
393                   dst_addr.sin_family = AF_INET;
394                   memcpy(&dst_addr.sin_addr.s_addr,
395                          &rtp_proxytable[i].remote_ipaddr, 
396                          sizeof(struct in_addr));
397                   dst_addr.sin_port= htons(rtp_proxytable[i].remote_port+1);
398
399                   sts = sendto(rtp_proxytable[i].rtcp_tx_sock, rtp_buff,
400                                count, 0, (const struct sockaddr *)&dst_addr,
401                                (socklen_t)sizeof(dst_addr));
402                   if (sts == -1) {
403                      if (errno != ECONNREFUSED) {
404                         ERROR("sendto() [%s:%i size=%i] call failed: %s",
405                         utils_inet_ntoa(rtp_proxytable[i].remote_ipaddr),
406                         rtp_proxytable[i].remote_port+1, count, strerror(errno));
407
408                     /* if sendto() fails with bad filedescriptor,
409                      * this means that the opposite stream has been
410                      * canceled or timed out.
411                      * we should then cancel this stream as well.*/
412
413                     WARN("stopping opposite stream");
414                     /* don't lock the mutex, as we own the lock */
415                     callid.number=rtp_proxytable[i].callid_number;
416                     callid.host=rtp_proxytable[i].callid_host;
417                     rtp_relay_stop_fwd(rtp_proxytable[i].rtcp_rx_sock, &callid, rtp_proxytable[i].direction, 1);
418                      }
419                   }
420                }
421             } /* count > 0 */
422
423             /* update timestamp of last usage */
424             rtp_proxytable[i].timestamp=t;
425          }
426      } /* for i */
427
428       /*
429        * age and clean rtp_proxytable (check every 10 seconds)
430        */
431       if (t > (last_t+10) ) {
432          last_t = t;
433          for (i=0;i<RTPPROXY_SIZE; i++) {
434             if ( (rtp_proxytable[i].rtp_rx_sock != 0) &&
435                  ((rtp_proxytable[i].timestamp+configuration.rtp_timeout)<t)) {
436                /* this one has expired, clean it up */
437                callid.number=rtp_proxytable[i].callid_number;
438                callid.host=rtp_proxytable[i].callid_host;
439                DEBUGC(DBCLASS_RTP,"RTP stream rx_sock=%i tx_sock=%i "
440                       "%s@%s (idx=%i) has expired",
441                       rtp_proxytable[i].rtp_rx_sock,
442                       rtp_proxytable[i].rtp_tx_sock,
443                       callid.number, callid.host, i);
444                /* don't lock the mutex, as we own the lock already here */
445                rtp_relay_stop_fwd(rtp_proxytable[i].rtp_rx_sock, &callid, rtp_proxytable[i].direction, 1);
446             }
447          }
448       } /* if (t>...) */
449
450       /* copy master FD set */
451       memcpy(&fdset, &master_fdset, sizeof(fdset));
452       fd_max=master_fd_max;
453
454       /*
455        * UNLOCK the MUTEX
456        */
457       pthread_mutex_unlock(&rtp_proxytable_mutex);
458    } /* for(;;) */
459
460    return NULL;
461 }
462
463
464 /*
465  * start an rtp stream on the proxy
466  *
467  * RETURNS
468  *      STS_SUCCESS on success
469  *      STS_FAILURE on error
470  */
471 int rtp_relay_start_fwd (osip_call_id_t *callid, char *client_id,
472                          int rtp_direction,
473                          int media_stream_no, struct in_addr local_ipaddr,
474                          int *local_port, struct in_addr remote_ipaddr,
475                          int remote_port) {
476    int i, j;
477    int sock, port;
478    int freeidx;
479    int sts=STS_SUCCESS;
480    osip_call_id_t cid;
481    int rtcp_sock;
482    
483
484    if (callid == NULL) {
485       ERROR("rtp_relay_start_fwd: callid is NULL!");
486       return STS_FAILURE;
487    }
488
489    if (client_id == NULL) {
490       ERROR("rtp_relay_start_fwd: did not get a client ID!");
491       return STS_FAILURE;
492    }
493
494    /*
495     * life insurance: check size of received call_id strings
496     * I don't know what the maximum allowed size within SIP is,
497     * so if this test fails maybe it's just necessary to increase
498     * the constants CALLIDNUM_SIZE and/or CALLIDHOST_SIZE.
499     */
500    if (callid->number && strlen(callid->number) > CALLIDNUM_SIZE) {
501       ERROR("rtp_relay_start_fwd: received callid number "
502             "has too many characters (%i, max=%i)",
503             strlen(callid->number),CALLIDNUM_SIZE);
504       return STS_FAILURE;
505    }
506    if (callid->host && strlen(callid->host) > CALLIDHOST_SIZE) {
507       ERROR("rtp_relay_start_fwd: received callid host "
508             "has too many characters (%i, max=%i)",
509             strlen(callid->host),CALLIDHOST_SIZE);
510       return STS_FAILURE;
511    }
512    if (client_id && strlen(client_id) > CLIENT_ID_SIZE) {
513       ERROR("rtp_relay_start_fwd: client ID has too many characters "
514             "(%i, max=%i) (maybe you need to increase CLIENT_ID_SIZE",
515             strlen(client_id),CLIENT_ID_SIZE);
516       return STS_FAILURE;
517    }
518
519    DEBUGC(DBCLASS_RTP,"rtp_relay_start_fwd: starting RTP proxy "
520           "stream for: %s@%s[%s] (%s) #=%i",
521           callid->number, callid->host, client_id,
522           ((rtp_direction == DIR_INCOMING) ? "incoming RTP" : "outgoing RTP"),
523           media_stream_no);
524
525    /* lock mutex */
526    #define return is_forbidden_in_this_code_section
527    pthread_mutex_lock(&rtp_proxytable_mutex);
528    /*
529     * !! We now have a locked MUTEX! It is forbidden to return() from
530     * !! here up to the end of this funtion where the MUTEX is
531     * !! unlocked again.
532     * !! Per design, a mutex is locked (for one purpose) at *exactly one*
533     * !! place in the code and unlocked also at *exactly one* place.
534     * !! this minimizes the risk of deadlocks.
535     */
536
537    /*
538     * figure out, if this is an request to start an RTP proxy stream
539     * that is already existing (identified by SIP Call-ID, direction,
540     * media_stream_no and some other client unique thing).
541     * This can be due to UDP repetitions of the INVITE request...
542     */
543    for (i=0; i<RTPPROXY_SIZE; i++) {
544       cid.number = rtp_proxytable[i].callid_number;
545       cid.host   = rtp_proxytable[i].callid_host;
546       if (rtp_proxytable[i].rtp_rx_sock &&
547          (compare_callid(callid, &cid) == STS_SUCCESS) &&
548          (rtp_proxytable[i].direction == rtp_direction) &&
549          (rtp_proxytable[i].media_stream_no == media_stream_no) &&
550          (strcmp(rtp_proxytable[i].client_id, client_id) == 0)) {
551          /*
552           * The RTP port number reported by the UA MAY change
553           * for a given media stream
554           * (seen with KPhone during HOLD/unHOLD)
555           * Also the destination IP may change during a re-Invite
556           * (seen with Sipphone.com, re-Invites when using
557           * the SIP - POTS gateway [SIP Minutes] 
558           */
559          /* Port number */
560          if (rtp_proxytable[i].remote_port != remote_port) {
561             DEBUGC(DBCLASS_RTP,"RTP port number changed %i -> %i",
562                    rtp_proxytable[i].remote_port, remote_port);
563             rtp_proxytable[i].remote_port = remote_port;
564          }
565          /* IP address */
566          if (memcmp(&rtp_proxytable[i].remote_ipaddr, &remote_ipaddr,
567                     sizeof(remote_ipaddr))) {
568             DEBUGC(DBCLASS_RTP,"RTP IP address changed to %s",
569                    utils_inet_ntoa(remote_ipaddr));
570             memcpy (&rtp_proxytable[i].remote_ipaddr, &remote_ipaddr,
571                      sizeof(remote_ipaddr));
572          }
573          /* return the already known local port number */
574          DEBUGC(DBCLASS_RTP,"RTP stream already active (remaddr=%s, "
575                 "remport=%i, lclport=%i, id=%s, #=%i)",
576                 utils_inet_ntoa(remote_ipaddr),
577                 rtp_proxytable[i].remote_port,
578                 rtp_proxytable[i].local_port,
579                 rtp_proxytable[i].callid_number,
580                 rtp_proxytable[i].media_stream_no);
581          *local_port=rtp_proxytable[i].local_port;
582          sts = STS_SUCCESS;
583          goto unlock_and_exit;
584       }
585    }
586
587
588    /*
589     * find first free slot in rtp_proxytable
590     */
591    freeidx=-1;
592    for (j=0; j<RTPPROXY_SIZE; j++) {
593       if (rtp_proxytable[j].rtp_rx_sock==0) {
594          freeidx=j;
595          break;
596       }
597    }
598
599    /* rtp_proxytable port pool full? */
600    if (freeidx == -1) {
601       ERROR("rtp_relay_start_fwd: rtp_proxytable is full!");
602       sts = STS_FAILURE;
603       goto unlock_and_exit;
604    }
605
606    /* TODO: randomize the port allocation - start at a random offset to
607          search in the allowed port range (so some modulo stuff w/
608          random start offset 
609          - for i=x to (p1-p0)+x; p=p0+mod(x,p1-p0) */
610
611    /* find a local port number to use and bind to it */
612    sock=0;
613    port=0;
614    rtcp_sock=0;
615    for (i=configuration.rtp_port_low; i<=configuration.rtp_port_high; i+=2) {
616       for (j=0; j<RTPPROXY_SIZE; j++) {
617          /* check if port already in use */
618          if ((memcmp(&rtp_proxytable[j].local_ipaddr,
619                      &local_ipaddr, sizeof(struct in_addr))== 0) &&
620              (rtp_proxytable[j].local_port == i) ) break;
621       }
622
623       /* port is available, try to allocate */
624       if (j == RTPPROXY_SIZE) {
625          port=i;
626          sock=sockbind(local_ipaddr, port, 0);
627          /* if success break, else try further on */
628          if (sock) {
629             rtcp_sock=sockbind(local_ipaddr, port+1, 0);
630             if (rtcp_sock)
631                 break;
632             else
633                 close(sock);
634          }
635       }
636    } /* for i */
637
638    DEBUGC(DBCLASS_RTP,"rtp_relay_start_fwd: addr=%s, port=%i, sock=%i rtcp_sock=%i "
639           "freeidx=%i", utils_inet_ntoa(local_ipaddr), port, sock, rtcp_sock, freeidx);
640
641    /* found an unused port? No -> RTP port pool fully allocated */
642    if ((port == 0) || (sock == 0) || (rtcp_sock == 0)) {
643       ERROR("rtp_relay_start_fwd: no RTP port available or bind() failed");
644       sts = STS_FAILURE;
645       goto unlock_and_exit;
646    }
647
648    /* write entry into rtp_proxytable slot (freeidx) */
649    rtp_proxytable[freeidx].rtp_rx_sock=sock;
650    rtp_proxytable[freeidx].rtcp_rx_sock=rtcp_sock;
651
652    if (callid->number) {
653       strcpy(rtp_proxytable[freeidx].callid_number, callid->number);
654    } else {
655       rtp_proxytable[freeidx].callid_number[0]='\0';
656    }
657
658    if (callid->host) {
659       strcpy(rtp_proxytable[freeidx].callid_host, callid->host);
660    } else {
661       rtp_proxytable[freeidx].callid_host[0]='\0';
662    }
663
664    if (client_id) {
665       strcpy(rtp_proxytable[freeidx].client_id, client_id);
666    } else {
667       rtp_proxytable[freeidx].client_id[0]='\0';
668    }
669
670    rtp_proxytable[freeidx].direction = rtp_direction;
671    rtp_proxytable[freeidx].media_stream_no = media_stream_no;
672    memcpy(&rtp_proxytable[freeidx].local_ipaddr,
673           &local_ipaddr, sizeof(struct in_addr));
674    rtp_proxytable[freeidx].local_port=port;
675    memcpy(&rtp_proxytable[freeidx].remote_ipaddr,
676           &remote_ipaddr, sizeof(struct in_addr));
677    rtp_proxytable[freeidx].remote_port=remote_port;
678    time(&rtp_proxytable[freeidx].timestamp);
679
680    *local_port=port;
681
682    /* call to firewall API */
683    fwapi_start_rtp(rtp_proxytable[freeidx].direction,
684                    rtp_proxytable[freeidx].local_ipaddr,
685                    rtp_proxytable[freeidx].local_port,
686                    rtp_proxytable[freeidx].remote_ipaddr,
687                    rtp_proxytable[freeidx].remote_port);
688
689    /* prepare FD set for next select operation */
690    rtp_recreate_fdset();
691
692    /* wakeup/signal rtp_proxythread from select() hibernation */
693    if (!pthread_equal(rtpproxy_tid, pthread_self()))
694       pthread_kill(rtpproxy_tid, SIGALRM);
695
696 unlock_and_exit:
697    /* unlock mutex */
698    pthread_mutex_unlock(&rtp_proxytable_mutex);
699    #undef return
700
701    return sts;
702 }
703
704
705 /*
706  * stop a rtp stream on the proxy
707  *
708  * RETURNS
709  *      STS_SUCCESS on success
710  *      STS_FAILURE on error
711  */
712 int rtp_relay_stop_fwd (int rtp_rx_sock, osip_call_id_t *callid,
713                         int rtp_direction, int nolock) {
714    int i, sts;
715    int retsts=STS_SUCCESS;
716    int got_match=0;
717    osip_call_id_t cid;
718  
719    if (callid == NULL) {
720       ERROR("rtp_relay_stop_fwd: callid is NULL!");
721       return STS_FAILURE;
722    }
723
724    DEBUGC(DBCLASS_RTP,"rtp_relay_stop_fwd: stopping RTP proxy "
725           "stream for: %s@%s (%s)",
726           callid->number, callid->host,
727           ((rtp_direction == DIR_INCOMING) ? "incoming" : "outgoing"));
728
729    /*
730     * lock mutex - only if not requested to skip the lock.
731     * this is needed as we are also called from within
732     * the RTP thread itself - and there we already own the lock.
733     */
734    #define return is_forbidden_in_this_code_section
735    if (nolock == 0) {
736       pthread_mutex_lock(&rtp_proxytable_mutex);
737       /*
738        * !! We now have a locked MUTEX! It is forbidden to return() from
739        * !! here up to the end of this funtion where the MUTEX is
740        * !! unlocked again.
741        * !! Per design, a mutex is locked (for one purpose) at *exactly one*
742        * !! place in the code and unlocked also at *exactly one* place.
743        * !! this minimizes the risk of deadlocks.
744        */
745    }
746    /* 
747    * wakeup/signal rtp_proxythread from select() hibernation.
748    * This must be done here before we close the socket, otherwise
749    * we may get an select() error later from the proxy thread that
750    * is still hibernating in select() now.
751    */
752    if (!pthread_equal(rtpproxy_tid, pthread_self()))
753       pthread_kill(rtpproxy_tid, SIGALRM);
754
755    /*
756     * find the proper entry in rtp_proxytable
757     * we need to loop the whole table, as there might be multiple
758     * media strema active for the same callid (audio + video stream)
759     */
760    for (i=0; i<RTPPROXY_SIZE; i++) {
761       cid.number = rtp_proxytable[i].callid_number;
762       cid.host   = rtp_proxytable[i].callid_host;
763       if (rtp_proxytable[i].rtp_rx_sock &&
764          (compare_callid(callid, &cid) == STS_SUCCESS) &&
765          (rtp_proxytable[i].direction == rtp_direction)) {
766          if (rtp_rx_sock != 0) {
767             if (rtp_proxytable[i].rtp_rx_sock != rtp_rx_sock) {
768                continue;
769             }
770          }
771          sts = close(rtp_proxytable[i].rtp_rx_sock);
772          DEBUGC(DBCLASS_RTP,"closed socket %i for RTP stream "
773                 "%s:%s == %s:%s  (idx=%i) sts=%i",
774                 rtp_proxytable[i].rtp_rx_sock,
775                 rtp_proxytable[i].callid_number,
776                 rtp_proxytable[i].callid_host,
777                 callid->number, callid->host, i, sts);
778          if (sts < 0) {
779             ERROR("Error in close(%i): %s nolock=%i %s:%s\n",
780                   rtp_proxytable[i].rtp_rx_sock,
781                   strerror(errno), nolock,
782                   callid->number, callid->host);
783          }
784          if (rtp_proxytable[i].rtcp_rx_sock) {
785             sts = close(rtp_proxytable[i].rtcp_rx_sock);
786             DEBUGC(DBCLASS_RTP,"closed socket %i for RTP stream "
787                     "%s:%s == %s:%s  (idx=%i) sts=%i",
788                     rtp_proxytable[i].rtcp_rx_sock,
789                     rtp_proxytable[i].callid_number,
790                     rtp_proxytable[i].callid_host,
791                     callid->number, callid->host, i, sts);
792             if (sts < 0) {
793                 ERROR("Error in close(%i): %s nolock=%i %s:%s\n",
794                     rtp_proxytable[i].rtcp_rx_sock,
795                     strerror(errno), nolock,
796                     callid->number, callid->host);
797             }
798          }
799          /* call to firewall API */
800          fwapi_stop_rtp(rtp_proxytable[i].direction,
801                    rtp_proxytable[i].local_ipaddr,
802                    rtp_proxytable[i].local_port,
803                    rtp_proxytable[i].remote_ipaddr,
804                    rtp_proxytable[i].remote_port);
805          /* clean up */
806          memset(&rtp_proxytable[i], 0, sizeof(rtp_proxytable[0]));
807          got_match=1;
808       }
809  
810    }
811
812    /* did not find an active stream... */
813    if (!got_match) {
814       DEBUGC(DBCLASS_RTP,
815              "rtp_relay_stop_fwd: can't find active stream for %s@%s (%s)",
816              callid->number, callid->host,
817              ((rtp_direction == DIR_INCOMING) ? "incoming RTP" : "outgoing RTP"));
818       retsts = STS_FAILURE;
819       goto unlock_and_exit;
820    }
821
822
823    /* prepare FD set for next select operation */
824    rtp_recreate_fdset();
825    
826
827 unlock_and_exit:
828    /*
829     * unlock mutex - only if not requested to skip the lock.
830     * this is needed as we are also called from within
831     * the RTP thread itself - and there we already own the lock.
832     */
833    if (nolock == 0) {
834       pthread_mutex_unlock(&rtp_proxytable_mutex);
835    }
836    #undef return
837
838    return retsts;
839 }
840
841
842 /*
843  * some sockets have been newly created or removed -
844  * recreate the FD set for next select operation
845  *
846  * RETURNS
847  *      STS_SUCCESS on success (always)
848  */
849 static int rtp_recreate_fdset(void) {
850    int i;
851
852    FD_ZERO(&master_fdset);
853    master_fd_max=-1;
854    for (i=0;i<RTPPROXY_SIZE;i++) {
855       if (rtp_proxytable[i].rtp_rx_sock != 0) {
856          FD_SET(rtp_proxytable[i].rtp_rx_sock, &master_fdset);
857          if (rtp_proxytable[i].rtp_rx_sock > master_fd_max) {
858                master_fd_max=rtp_proxytable[i].rtp_rx_sock;
859          }
860       }
861       if (rtp_proxytable[i].rtcp_rx_sock != 0) {
862          FD_SET(rtp_proxytable[i].rtcp_rx_sock, &master_fdset);
863          if (rtp_proxytable[i].rtcp_rx_sock > master_fd_max) {
864                master_fd_max=rtp_proxytable[i].rtcp_rx_sock;
865          }
866       }
867    } /* for i */
868    return STS_SUCCESS;
869 }
870
871
872 /*
873  * kills the rtp_proxy thread
874  *
875  * RETURNS
876  *      -
877  */
878 void rtpproxy_kill( void ) {
879    void *thread_status;
880    osip_call_id_t cid;
881    int i, sts;
882
883    /* stop any active RTP stream */
884    for (i=0;i<RTPPROXY_SIZE;i++) {
885       if (rtp_proxytable[i].rtp_rx_sock != 0) {
886          cid.number = rtp_proxytable[i].callid_number;
887          cid.host   = rtp_proxytable[i].callid_host;
888          sts = rtp_relay_stop_fwd(rtp_proxytable[i].rtp_rx_sock, &cid, rtp_proxytable[i].direction, 0);
889       }
890    }
891    
892
893    /* kill the thread */
894    if (rtpproxy_tid) {
895       pthread_cancel(rtpproxy_tid);
896       pthread_kill(rtpproxy_tid, SIGALRM);
897       pthread_join(rtpproxy_tid, &thread_status);
898    }
899
900    DEBUGC(DBCLASS_RTP,"killed RTP proxy thread");
901    return;
902 }
903