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;
276 //int media_stream_no = rtp_proxytable[i].media_stream_no;
278 int media_stream_no = rtp_proxytable[i].media_stream_no;
280 callid.number = rtp_proxytable[i].callid_number;
281 callid.host = rtp_proxytable[i].callid_host;
283 for (j=0;(j<RTPPROXY_SIZE);j++) {
284 char *client_id = rtp_proxytable[i].client_id;
286 cid.number = rtp_proxytable[j].callid_number;
287 cid.host = rtp_proxytable[j].callid_host;
291 * - same media stream
292 * - opposite direction
293 * - different client ID
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) )
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);
308 } /* rtp_tx_sock == 0 */
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);
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));
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));
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.*/
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);
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;
350 /* update timestamp of last usage */
351 rtp_proxytable[i].timestamp=t;
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 */
360 /* read from sock rtp_proxytable[i].sock*/
361 count=read(rtp_proxytable[i].rtcp_rx_sock, rtp_buff, RTP_BUFFER_SIZE);
364 * forwarding an RTP packet only makes sense if we really
365 * have got some data in it (count > 0)
368 /* find the corresponding TX socket */
369 if (rtp_proxytable[i].rtcp_tx_sock == 0) {
371 int rtp_direction = rtp_proxytable[i].direction;
372 int media_stream_no = rtp_proxytable[i].media_stream_no;
374 callid.number = rtp_proxytable[i].callid_number;
375 callid.host = rtp_proxytable[i].callid_host;
377 for (j=0;(j<RTPPROXY_SIZE);j++) {
378 char *client_id = rtp_proxytable[i].client_id;
380 cid.number = rtp_proxytable[j].callid_number;
381 cid.host = rtp_proxytable[j].callid_host;
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;
392 } /* rtcp_tx_sock == 0 */
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);
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));
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));
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.*/
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);
427 /* update timestamp of last usage */
428 rtp_proxytable[i].timestamp=t;
433 * age and clean rtp_proxytable (check every 10 seconds)
435 if (t > (last_t+10) ) {
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);
454 /* copy master FD set */
455 memcpy(&fdset, &master_fdset, sizeof(fdset));
456 fd_max=master_fd_max;
461 pthread_mutex_unlock(&rtp_proxytable_mutex);
469 * start an rtp stream on the proxy
472 * STS_SUCCESS on success
473 * STS_FAILURE on error
475 int rtp_relay_start_fwd (osip_call_id_t *callid, char *client_id,
477 int media_stream_no, struct in_addr local_ipaddr,
478 int *local_port, struct in_addr remote_ipaddr,
488 if (callid == NULL) {
489 ERROR("rtp_relay_start_fwd: callid is NULL!");
493 if (client_id == NULL) {
494 ERROR("rtp_relay_start_fwd: did not get a client ID!");
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.
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);
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);
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);
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"),
530 #define return is_forbidden_in_this_code_section
531 pthread_mutex_lock(&rtp_proxytable_mutex);
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
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.
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...
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)) {
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]
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;
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));
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;
587 goto unlock_and_exit;
593 * find first free slot in rtp_proxytable
596 for (j=0; j<RTPPROXY_SIZE; j++) {
597 if (rtp_proxytable[j].rtp_rx_sock==0) {
603 /* rtp_proxytable port pool full? */
605 ERROR("rtp_relay_start_fwd: rtp_proxytable is full!");
607 goto unlock_and_exit;
610 /* TODO: randomize the port allocation - start at a random offset to
611 search in the allowed port range (so some modulo stuff w/
613 - for i=x to (p1-p0)+x; p=p0+mod(x,p1-p0) */
615 /* find a local port number to use and bind to it */
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;
627 /* port is available, try to allocate */
628 if (j == RTPPROXY_SIZE) {
630 sock=sockbind(local_ipaddr, port, 0);
631 /* if success break, else try further on */
633 rtcp_sock=sockbind(local_ipaddr, port+1, 0);
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);
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");
649 goto unlock_and_exit;
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;
656 if (callid->number) {
657 strcpy(rtp_proxytable[freeidx].callid_number, callid->number);
659 rtp_proxytable[freeidx].callid_number[0]='\0';
663 strcpy(rtp_proxytable[freeidx].callid_host, callid->host);
665 rtp_proxytable[freeidx].callid_host[0]='\0';
669 strcpy(rtp_proxytable[freeidx].client_id, client_id);
671 rtp_proxytable[freeidx].client_id[0]='\0';
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);
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);
693 /* prepare FD set for next select operation */
694 rtp_recreate_fdset();
696 /* wakeup/signal rtp_proxythread from select() hibernation */
697 if (!pthread_equal(rtpproxy_tid, pthread_self()))
698 pthread_kill(rtpproxy_tid, SIGALRM);
702 pthread_mutex_unlock(&rtp_proxytable_mutex);
710 * stop a rtp stream on the proxy
713 * STS_SUCCESS on success
714 * STS_FAILURE on error
716 int rtp_relay_stop_fwd (int rtp_rx_sock, osip_call_id_t *callid,
717 int rtp_direction, int nolock) {
719 int retsts=STS_SUCCESS;
723 if (callid == NULL) {
724 ERROR("rtp_relay_stop_fwd: callid is NULL!");
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"));
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.
738 #define return is_forbidden_in_this_code_section
740 pthread_mutex_lock(&rtp_proxytable_mutex);
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
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.
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.
756 if (!pthread_equal(rtpproxy_tid, pthread_self()))
757 pthread_kill(rtpproxy_tid, SIGALRM);
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)
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) {
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);
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);
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);
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);
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);
810 memset(&rtp_proxytable[i], 0, sizeof(rtp_proxytable[0]));
816 /* did not find an active stream... */
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;
827 /* prepare FD set for next select operation */
828 rtp_recreate_fdset();
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.
838 pthread_mutex_unlock(&rtp_proxytable_mutex);
847 * some sockets have been newly created or removed -
848 * recreate the FD set for next select operation
851 * STS_SUCCESS on success (always)
853 static int rtp_recreate_fdset(void) {
856 FD_ZERO(&master_fdset);
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;
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;
877 * kills the rtp_proxy thread
882 void rtpproxy_kill( void ) {
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);
897 /* kill the thread */
899 pthread_cancel(rtpproxy_tid);
900 pthread_kill(rtpproxy_tid, SIGALRM);
901 pthread_join(rtpproxy_tid, &thread_status);
904 DEBUGC(DBCLASS_RTP,"killed RTP proxy thread");