2 Copyright (C) 2003-2005 Thomas Ries <tries@gmx.net>
4 This file is part of Siproxd.
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.
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.
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
31 #include <sys/socket.h>
32 #include <netinet/in.h>
35 #ifdef HAVE_PTHREAD_SETSCHEDPARAM
39 #include <osipparser2/osip_parser.h>
45 static char const ident[]="$Id: rtpproxy_relay.c,v 1.30 2005/01/24 19:12:49 hb9xar Exp $";
47 /* configuration storage */
48 extern struct siproxd_config configuration;
51 * table to remember all active rtp proxy streams
53 rtp_proxytable_t rtp_proxytable[RTPPROXY_SIZE];
56 * Mutex for thread synchronization (locking when accessing common
57 * data structures -> rtp_proxytable[]).
59 * use a 'fast' mutex for synchronizing - as these are portable...
61 static pthread_mutex_t rtp_proxytable_mutex = PTHREAD_MUTEX_INITIALIZER;
63 /* thread id of RTP proxy */
64 static pthread_t rtpproxy_tid=0;
67 static fd_set master_fdset;
68 static int master_fd_max;
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() */};
78 * initialize and create rtp_relay proxy thread
81 * STS_SUCCESS on success
83 int rtp_relay_init( void ) {
86 struct sigaction sigact;
88 atexit(rtpproxy_kill); /* cancel RTP thread at exit */
90 /* clean proxy table */
91 memset (rtp_proxytable, 0, sizeof(rtp_proxytable));
93 /* initialize fd set for RTP proxy thread */
94 FD_ZERO(&master_fdset); /* start with an empty fdset */
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);
102 sigaction(SIGALRM, &sigact, NULL);
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);
108 /* set realtime scheduling - if started by root */
109 #ifdef HAVE_PTHREAD_SETSCHEDPARAM
112 struct sched_param schedparam;
117 DEBUGC(DBCLASS_RTP,"uid=%i, euid=%i", uid, euid);
118 if (uid != euid) seteuid(0);
123 #if defined(HAVE_SCHED_GET_PRIORITY_MAX) && defined(HAVE_SCHED_GET_PRIORITY_MIN)
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);
132 /* just taken a number out of thin air */
133 schedparam.sched_priority=10;
134 DEBUGC(DBCLASS_RTP,"using p=%i", schedparam.sched_priority);
136 sts=pthread_setschedparam(rtpproxy_tid, SCHED_RR, &schedparam);
138 ERROR("pthread_setschedparam failed: %s", strerror(errno));
142 INFO("Unable to use realtime scheduling for RTP proxy");
143 INFO("You may want to start siproxd as root and switch UID afterwards");
145 if (uid != euid) seteuid(euid);
157 static void *rtpproxy_main(void *arg) {
164 osip_call_id_t callid;
165 static char rtp_buff[RTP_BUFFER_SIZE];
169 memcpy(&fdset, &master_fdset, sizeof(fdset));
170 fd_max=master_fd_max;
172 /* loop forever... */
178 num_fd=select(fd_max+1, &fdset, NULL, NULL, &tv);
179 pthread_testcancel();
180 if ((num_fd<0) && (errno==EINTR)) {
182 * wakeup due to a change in the proxy table:
183 * lock mutex, copy master FD set and unlock
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);
197 pthread_mutex_lock(&rtp_proxytable_mutex);
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 */
206 /* read from sock rtp_proxytable[i].sock*/
207 count=read(rtp_proxytable[i].rtp_rx_sock, rtp_buff, RTP_BUFFER_SIZE);
209 /* check if something went banana */
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.
219 * We catch this here with this workaround (pronounce "HACK")
220 * and hope that next time we pass by it will be ok again.
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);
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
237 * So I should *not* do this - or ignore errors originating
238 * by this -> ECONNREFUSED
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
244 if (errno != ECONNREFUSED) {
245 /* some other error that I probably want to know about */
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",
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));
264 } /* if errno != ECONNREFUSED */
268 * forwarding an RTP packet only makes sense if we really
269 * have got some data in it (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;
277 callid.number = rtp_proxytable[i].callid_number;
278 callid.host = rtp_proxytable[i].callid_host;
280 for (j=0;(j<RTPPROXY_SIZE);j++) {
281 char *client_id = rtp_proxytable[i].client_id;
283 cid.number = rtp_proxytable[j].callid_number;
284 cid.host = rtp_proxytable[j].callid_host;
288 * - same media stream
289 * - opposite direction
290 * - different client ID
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);
304 } /* rtp_tx_sock == 0 */
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);
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));
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));
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.*/
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);
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;
346 /* update timestamp of last usage */
347 rtp_proxytable[i].timestamp=t;
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 */
356 /* read from sock rtp_proxytable[i].sock*/
357 count=read(rtp_proxytable[i].rtcp_rx_sock, rtp_buff, RTP_BUFFER_SIZE);
360 * forwarding an RTP packet only makes sense if we really
361 * have got some data in it (count > 0)
364 /* find the corresponding TX socket */
365 if (rtp_proxytable[i].rtcp_tx_sock == 0) {
367 int rtp_direction = rtp_proxytable[i].direction;
368 //int media_stream_no = rtp_proxytable[i].media_stream_no;
370 callid.number = rtp_proxytable[i].callid_number;
371 callid.host = rtp_proxytable[i].callid_host;
373 for (j=0;(j<RTPPROXY_SIZE);j++) {
374 char *client_id = rtp_proxytable[i].client_id;
376 cid.number = rtp_proxytable[j].callid_number;
377 cid.host = rtp_proxytable[j].callid_host;
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;
388 } /* rtcp_tx_sock == 0 */
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);
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));
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));
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.*/
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);
423 /* update timestamp of last usage */
424 rtp_proxytable[i].timestamp=t;
429 * age and clean rtp_proxytable (check every 10 seconds)
431 if (t > (last_t+10) ) {
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);
450 /* copy master FD set */
451 memcpy(&fdset, &master_fdset, sizeof(fdset));
452 fd_max=master_fd_max;
457 pthread_mutex_unlock(&rtp_proxytable_mutex);
465 * start an rtp stream on the proxy
468 * STS_SUCCESS on success
469 * STS_FAILURE on error
471 int rtp_relay_start_fwd (osip_call_id_t *callid, char *client_id,
473 int media_stream_no, struct in_addr local_ipaddr,
474 int *local_port, struct in_addr remote_ipaddr,
484 if (callid == NULL) {
485 ERROR("rtp_relay_start_fwd: callid is NULL!");
489 if (client_id == NULL) {
490 ERROR("rtp_relay_start_fwd: did not get a client ID!");
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.
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);
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);
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);
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"),
526 #define return is_forbidden_in_this_code_section
527 pthread_mutex_lock(&rtp_proxytable_mutex);
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
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.
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...
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)) {
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]
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;
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));
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;
583 goto unlock_and_exit;
589 * find first free slot in rtp_proxytable
592 for (j=0; j<RTPPROXY_SIZE; j++) {
593 if (rtp_proxytable[j].rtp_rx_sock==0) {
599 /* rtp_proxytable port pool full? */
601 ERROR("rtp_relay_start_fwd: rtp_proxytable is full!");
603 goto unlock_and_exit;
606 /* TODO: randomize the port allocation - start at a random offset to
607 search in the allowed port range (so some modulo stuff w/
609 - for i=x to (p1-p0)+x; p=p0+mod(x,p1-p0) */
611 /* find a local port number to use and bind to it */
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;
623 /* port is available, try to allocate */
624 if (j == RTPPROXY_SIZE) {
626 sock=sockbind(local_ipaddr, port, 0);
627 /* if success break, else try further on */
629 rtcp_sock=sockbind(local_ipaddr, port+1, 0);
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);
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");
645 goto unlock_and_exit;
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;
652 if (callid->number) {
653 strcpy(rtp_proxytable[freeidx].callid_number, callid->number);
655 rtp_proxytable[freeidx].callid_number[0]='\0';
659 strcpy(rtp_proxytable[freeidx].callid_host, callid->host);
661 rtp_proxytable[freeidx].callid_host[0]='\0';
665 strcpy(rtp_proxytable[freeidx].client_id, client_id);
667 rtp_proxytable[freeidx].client_id[0]='\0';
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);
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);
689 /* prepare FD set for next select operation */
690 rtp_recreate_fdset();
692 /* wakeup/signal rtp_proxythread from select() hibernation */
693 if (!pthread_equal(rtpproxy_tid, pthread_self()))
694 pthread_kill(rtpproxy_tid, SIGALRM);
698 pthread_mutex_unlock(&rtp_proxytable_mutex);
706 * stop a rtp stream on the proxy
709 * STS_SUCCESS on success
710 * STS_FAILURE on error
712 int rtp_relay_stop_fwd (int rtp_rx_sock, osip_call_id_t *callid,
713 int rtp_direction, int nolock) {
715 int retsts=STS_SUCCESS;
719 if (callid == NULL) {
720 ERROR("rtp_relay_stop_fwd: callid is NULL!");
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"));
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.
734 #define return is_forbidden_in_this_code_section
736 pthread_mutex_lock(&rtp_proxytable_mutex);
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
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.
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.
752 if (!pthread_equal(rtpproxy_tid, pthread_self()))
753 pthread_kill(rtpproxy_tid, SIGALRM);
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)
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) {
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);
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);
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);
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);
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);
806 memset(&rtp_proxytable[i], 0, sizeof(rtp_proxytable[0]));
812 /* did not find an active stream... */
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;
823 /* prepare FD set for next select operation */
824 rtp_recreate_fdset();
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.
834 pthread_mutex_unlock(&rtp_proxytable_mutex);
843 * some sockets have been newly created or removed -
844 * recreate the FD set for next select operation
847 * STS_SUCCESS on success (always)
849 static int rtp_recreate_fdset(void) {
852 FD_ZERO(&master_fdset);
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;
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;
873 * kills the rtp_proxy thread
878 void rtpproxy_kill( void ) {
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);
893 /* kill the thread */
895 pthread_cancel(rtpproxy_tid);
896 pthread_kill(rtpproxy_tid, SIGALRM);
897 pthread_join(rtpproxy_tid, &thread_status);
900 DEBUGC(DBCLASS_RTP,"killed RTP proxy thread");