rtpsession_inet.c 45.8 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
  The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) stack.
  Copyright (C) 2001  Simon MORLAT simon.morlat@linphone.org

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


21 22
#define _GNU_SOURCE

aymeric's avatar
aymeric committed
23 24
#if defined(WIN32) || defined(_WIN32_WCE)
#include "ortp-config-win32.h"
jehan's avatar
jehan committed
25
#elif HAVE_CONFIG_H
26
#include "ortp-config.h" /*needed for HAVE_SYS_UIO_H and HAVE_ARC4RANDOM */
aymeric's avatar
aymeric committed
27
#endif
28 29 30 31
#include "ortp/ortp.h"
#include "utils.h"
#include "ortp/rtpsession.h"
#include "rtpsession_priv.h"
aymeric's avatar
aymeric committed
32

33 34 35
#if (_WIN32_WINNT >= 0x0600)
#include <delayimp.h>
#undef ExternC
36
#ifndef WINAPI_FAMILY_PHONE_APP
37 38
#include <QOS2.h>
#endif
Ghislain MARY's avatar
Ghislain MARY committed
39
#endif
40

41
#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(WINAPI_FAMILY_PHONE_APP)
42
#include <mswsock.h>
43 44
#endif

aymeric's avatar
aymeric committed
45 46 47 48 49 50 51
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#define USE_SENDMSG 1
#endif

#define can_connect(s)	( (s)->use_connect && !(s)->symmetric_rtp)

52 53 54 55 56 57 58 59 60 61 62 63
#if defined(WIN32) || defined(_WIN32_WCE)
#ifndef WSAID_WSARECVMSG
/* http://source.winehq.org/git/wine.git/blob/HEAD:/include/mswsock.h */
#define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
#define MAX_NATURAL_ALIGNMENT sizeof(DWORD)
#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test)
typedef WSACMSGHDR *LPWSACMSGHDR;
#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1)))
#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1)))
#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL)
#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len))))
#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)))
64
#endif
65 66 67 68 69 70 71 72
#undef CMSG_FIRSTHDR
#define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR
#undef CMSG_NXTHDR
#define CMSG_NXTHDR WSA_CMSG_NXTHDR
#undef CMSG_DATA
#define CMSG_DATA WSA_CMSG_DATA
typedef INT  (WINAPI * LPFN_WSARECVMSG)(SOCKET, LPWSAMSG, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE);
static LPFN_WSARECVMSG ortp_WSARecvMsg = NULL;
73

Ghislain MARY's avatar
Ghislain MARY committed
74 75 76 77
#endif

#if defined(WIN32) || defined(_WIN32_WCE) || defined(__QNX__)
/* Mingw32 does not define AI_V4MAPPED, however it is supported starting from Windows Vista. QNX also does not define AI_V4MAPPED. */
78 79 80
#	ifndef AI_V4MAPPED
#	define AI_V4MAPPED 0x00000800
#	endif
81 82
#endif

aymeric's avatar
aymeric committed
83 84 85 86 87 88 89 90
static bool_t try_connect(int fd, const struct sockaddr *dest, socklen_t addrlen){
	if (connect(fd,dest,addrlen)<0){
		ortp_warning("Could not connect() socket: %s",getSocketError());
		return FALSE;
	}
	return TRUE;
}

91
static ortp_socket_t create_and_bind(const char *addr, int *port, int *sock_family, bool_t reuse_addr){
aymeric's avatar
aymeric committed
92 93
	int err;
	int optval = 1;
smorlat's avatar
smorlat committed
94
	ortp_socket_t sock=-1;
aymeric's avatar
aymeric committed
95 96 97
	char num[8];
	struct addrinfo hints, *res0, *res;
	
98 99
	if (*port==-1) *port=0;
	if (*port==0) reuse_addr=FALSE;
aymeric's avatar
aymeric committed
100 101 102 103
	
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM;
104
	snprintf(num, sizeof(num), "%d",*port);
aymeric's avatar
aymeric committed
105 106
	err = getaddrinfo(addr,num, &hints, &res0);
	if (err!=0) {
107
		ortp_warning ("Error in getaddrinfo on (addr=%s port=%i): %s", addr, *port, gai_strerror(err));
aymeric's avatar
aymeric committed
108 109 110 111 112
		return -1;
	}
	
	for (res = res0; res; res = res->ai_next) {
		sock = socket(res->ai_family, res->ai_socktype, 0);
smorlat's avatar
smorlat committed
113 114 115
		if (sock==-1)
			continue;

aymeric's avatar
aymeric committed
116 117 118 119 120 121 122 123
		if (reuse_addr){
			err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
					(SOCKET_OPTION_VALUE)&optval, sizeof (optval));
			if (err < 0)
			{
				ortp_warning ("Fail to set rtp address reusable: %s.", getSocketError());
			}
		}
Yann Diorcet's avatar
Yann Diorcet committed
124 125 126 127 128 129 130 131
#if defined(ORTP_TIMESTAMP)
		err = setsockopt (sock, SOL_SOCKET, SO_TIMESTAMP,
			(SOCKET_OPTION_VALUE)&optval, sizeof (optval));
		if (err < 0)
		{
			ortp_warning ("Fail to set rtp timestamp: %s.",getSocketError());
		}
#endif
132 133 134 135 136
		err = 0;
		switch (res->ai_family) {
			default:
			case AF_INET:
#ifdef IP_RECVTTL
137
				err = setsockopt(sock, IPPROTO_IP, IP_RECVTTL, &optval, sizeof(optval));
138 139 140 141
#endif
				break;
			case AF_INET6:
#ifdef IPV6_RECVHOPLIMIT
142
				err = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &optval, sizeof(optval));
143 144 145 146 147 148
#endif
				break;
		}
		if (err < 0) {
			ortp_warning("Fail to set recv TTL/HL socket option: %s.", getSocketError());
		}
aymeric's avatar
aymeric committed
149 150 151

		*sock_family=res->ai_family;
		err = bind (sock, res->ai_addr, res->ai_addrlen);
smorlat's avatar
smorlat committed
152
		if (err != 0){
153
			ortp_debug ("Fail to bind rtp socket to (addr=%s port=%i) : %s.", addr, *port, getSocketError());
smorlat's avatar
smorlat committed
154 155 156 157
			close_socket (sock);
			sock=-1;
			continue;
		}
aymeric's avatar
aymeric committed
158 159 160 161 162 163 164 165 166 167
#ifndef __hpux
		switch (res->ai_family)
		  {
		    case AF_INET:
		      if (IN_MULTICAST(ntohl(((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr)))
			{
		          struct ip_mreq mreq;
			  mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr;
			  mreq.imr_interface.s_addr = INADDR_ANY;
			  err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (SOCKET_OPTION_VALUE) &mreq, sizeof(mreq));
smorlat's avatar
smorlat committed
168 169 170 171 172
			  if (err < 0){
				ortp_warning ("Fail to join address group: %s.", getSocketError());
				close_socket (sock);
				sock=-1;
				continue;
aymeric's avatar
aymeric committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186
			    }
			}
		      break;
		    case AF_INET6:
		      if (IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr)))
			{
			  struct ipv6_mreq mreq;
			  mreq.ipv6mr_multiaddr = ((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
			  mreq.ipv6mr_interface = 0;
			  err = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (SOCKET_OPTION_VALUE)&mreq, sizeof(mreq));
			  if (err < 0)
			    {
			      ortp_warning ("Fail to join address group: %s.", getSocketError());
			      close_socket (sock);
187
			      sock=-1;
aymeric's avatar
aymeric committed
188 189 190 191 192 193 194 195 196 197
			      continue;
			    }
			}
		      break;
		  }
#endif /*hpux*/
		break;
	}
	freeaddrinfo(res0);

198 199 200 201 202 203 204 205 206
#if defined(WIN32) || defined(_WIN32_WCE)
	if (ortp_WSARecvMsg == NULL) {
		GUID guid = WSAID_WSARECVMSG;
		DWORD bytes_returned;
		if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
			&ortp_WSARecvMsg, sizeof(ortp_WSARecvMsg), &bytes_returned, NULL, NULL) == SOCKET_ERROR) {
			ortp_warning("WSARecvMsg function not found.");
		}
	}
aymeric's avatar
aymeric committed
207
#endif
smorlat's avatar
smorlat committed
208
	if (sock!=-1){
aymeric's avatar
aymeric committed
209
		set_non_blocking_socket (sock);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
		if (*port==0){
			struct sockaddr_storage saddr;
			socklen_t slen=sizeof(saddr);
			err=getsockname(sock,(struct sockaddr*)&saddr,&slen);
			if (err==-1){
				ortp_error("getsockname(): %s",getSocketError());
				close(sock);
				return (ortp_socket_t)-1;
			}
			err=getnameinfo((struct sockaddr*)&saddr, slen, NULL, 0, num, sizeof(num), NI_NUMERICHOST | NI_NUMERICSERV);
			if (err!=0){
				ortp_error("getnameinfo(): %s",gai_strerror(err));
				close(sock);
				return (ortp_socket_t)-1;
			}
			*port=atoi(num);
		}
		
aymeric's avatar
aymeric committed
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
	}
	return sock;
}

static void set_socket_sizes(int sock, unsigned int sndbufsz, unsigned int rcvbufsz){
	int err;
	bool_t done=FALSE;
	if (sndbufsz>0){
#ifdef SO_SNDBUFFORCE
		err = setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&sndbufsz, sizeof(sndbufsz)); 
		if (err == -1) {
			ortp_error("Fail to increase socket's send buffer size with SO_SNDBUFFORCE: %s.", getSocketError());
		}else done=TRUE;
#endif
		if (!done){
			err = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sndbufsz, sizeof(sndbufsz)); 
			if (err == -1) {
				ortp_error("Fail to increase socket's send buffer size with SO_SNDBUF: %s.", getSocketError());
			}
		}
	}
	done=FALSE;
	if (rcvbufsz>0){
#ifdef SO_RCVBUFFORCE
		err = setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&rcvbufsz, sizeof(rcvbufsz)); 
		if (err == -1) {
			ortp_error("Fail to increase socket's recv buffer size with SO_RCVBUFFORCE: %s.", getSocketError());
		}
#endif
		if (!done){
			err = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbufsz, sizeof(rcvbufsz)); 
			if (err == -1) {
				ortp_error("Fail to increase socket's recv buffer size with SO_RCVBUF: %s.", getSocketError());
			}
		}
		
	}
}

/**
 *rtp_session_set_local_addr:
 *@session:		a rtp session freshly created.
 *@addr:		a local IP address in the xxx.xxx.xxx.xxx form.
271 272
 *@rtp_port:		a local port or -1 to let oRTP choose the port randomly
 *@rtcp_port:		a local port or -1 to let oRTP choose the port randomly
aymeric's avatar
aymeric committed
273 274 275 276 277 278 279 280 281 282 283
 *
 *	Specify the local addr to be use to listen for rtp packets or to send rtp packet from.
 *	In case where the rtp session is send-only, then it is not required to call this function:
 *	when calling rtp_session_set_remote_addr(), if no local address has been set, then the 
 *	default INADRR_ANY (0.0.0.0) IP address with a random port will be used. Calling 
 *	rtp_sesession_set_local_addr() is mandatory when the session is recv-only or duplex.
 *
 *	Returns: 0 on success.
**/

int
284
rtp_session_set_local_addr (RtpSession * session, const char * addr, int rtp_port, int rtcp_port)
aymeric's avatar
aymeric committed
285 286 287
{
	ortp_socket_t sock;
	int sockfamily;
288
	if (session->rtp.socket!=(ortp_socket_t)-1){
aymeric's avatar
aymeric committed
289 290 291 292
		/* don't rebind, but close before*/
		rtp_session_release_sockets(session);
	}
	/* try to bind the rtp port */
293 294
	
	sock=create_and_bind(addr,&rtp_port,&sockfamily,session->reuseaddr);
smorlat's avatar
smorlat committed
295
	if (sock!=-1){
aymeric's avatar
aymeric committed
296 297 298
		set_socket_sizes(sock,session->rtp.snd_socket_size,session->rtp.rcv_socket_size);
		session->rtp.sockfamily=sockfamily;
		session->rtp.socket=sock;
299
		session->rtp.loc_port=rtp_port;
aymeric's avatar
aymeric committed
300
		/*try to bind rtcp port */
301
		sock=create_and_bind(addr,&rtcp_port,&sockfamily,session->reuseaddr);
302
		if (sock!=(ortp_socket_t)-1){
aymeric's avatar
aymeric committed
303 304
			session->rtcp.sockfamily=sockfamily;
			session->rtcp.socket=sock;
305
			session->rtcp.loc_port=rtcp_port;
306 307 308
		}else {
			ortp_debug("Could not create and bind rtcp socket.");
			return -1;
aymeric's avatar
aymeric committed
309 310 311 312 313 314 315 316
		}
		
		/* set socket options (but don't change chosen states) */
		rtp_session_set_dscp( session, -1 );
		rtp_session_set_multicast_ttl( session, -1 );
		rtp_session_set_multicast_loopback( session, -1 );
		return 0;
	}
317
	ortp_debug("Could not bind RTP socket on port to %s port %i",addr,rtp_port);
aymeric's avatar
aymeric committed
318 319 320 321
	return -1;
}


322 323 324 325 326 327 328 329 330 331 332 333 334
/**
 *rtp_session_set_pktinfo:
 *@session: a rtp session
 *@activate: activation flag (0 to deactivate, other value to activate)
 *
 * (De)activates packet info for incoming and outgoing packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_pktinfo(RtpSession *session, int activate)
{
	int retval;
335
	int optname;
336 337 338 339 340 341 342
#if defined(WIN32) || defined(_WIN32_WCE)
	char optval[sizeof(DWORD)];
	int optlen = sizeof(optval);
#else
	int *optval = &activate;
	int optlen = sizeof(activate);
#endif
343 344 345 346

	// Dont't do anything if socket hasn't been created yet
	if (session->rtp.socket == (ortp_socket_t)-1) return 0;

347 348 349 350
#if defined(WIN32) || defined(_WIN32_WCE)
	memset(optval, activate, sizeof(optval));
#endif

351 352
	switch (session->rtp.sockfamily) {
		case AF_INET:
353 354 355 356 357
#ifdef IP_PKTINFO
			optname = IP_PKTINFO;
#else
			optname = IP_RECVDSTADDR;
#endif
358
			retval = setsockopt(session->rtp.socket, IPPROTO_IP, optname, optval, optlen);
359
			if (retval < 0) break;
360
			retval = setsockopt(session->rtcp.socket, IPPROTO_IP, optname, optval, optlen);
361 362
			break;
		case AF_INET6:
363 364 365 366 367
#ifdef IPV6_PKTINFO
			optname = IPV6_PKTINFO;
#else
			optname = IPV6_RECVDSTADDR;
#endif
368
			retval = setsockopt(session->rtp.socket, IPPROTO_IPV6, optname, optval, optlen);
369
			if (retval < 0) break;
370
			retval = setsockopt(session->rtcp.socket, IPPROTO_IPV6, optname, optval, optlen);
371 372 373 374 375 376 377 378 379 380 381
			break;
		default:
			retval = -1;
			break;
	}

	if (retval < 0) ortp_warning("Failed to set packet info on socket.");
	return retval;
}


aymeric's avatar
aymeric committed
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
/**
 *rtp_session_set_multicast_ttl:
 *@session: a rtp session
 *@ttl: desired Multicast Time-To-Live
 *
 * Sets the TTL (Time-To-Live) for outgoing multicast packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_multicast_ttl(RtpSession *session, int ttl)
{
    int retval;
    
    // Store new TTL if one is specified
    if (ttl>0) session->multicast_ttl = ttl;
    
    // Don't do anything if socket hasn't been created yet
400
    if (session->rtp.socket == (ortp_socket_t)-1) return 0;
aymeric's avatar
aymeric committed
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422

    switch (session->rtp.sockfamily) {
        case AF_INET: {
 
			retval= setsockopt(session->rtp.socket, IPPROTO_IP, IP_MULTICAST_TTL,
						 (SOCKET_OPTION_VALUE)  &session->multicast_ttl, sizeof(session->multicast_ttl));
            
			if (retval<0) break;

			retval= setsockopt(session->rtcp.socket, IPPROTO_IP, IP_MULTICAST_TTL,
					 (SOCKET_OPTION_VALUE)	   &session->multicast_ttl, sizeof(session->multicast_ttl));

 		} break;
        case AF_INET6: {

			retval= setsockopt(session->rtp.socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 
					 (SOCKET_OPTION_VALUE)&session->multicast_ttl, sizeof(session->multicast_ttl));
					
			if (retval<0) break;
			
			retval= setsockopt(session->rtcp.socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 
					 (SOCKET_OPTION_VALUE) &session->multicast_ttl, sizeof(session->multicast_ttl));
423 424 425 426
		} break;
	default:
		retval=-1;
	}
aymeric's avatar
aymeric committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472

	if (retval<0)
		ortp_warning("Failed to set multicast TTL on socket.");
  

	return retval;
}


/**
 *rtp_session_get_multicast_ttl:
 *@session: a rtp session
 *
 * Returns the TTL (Time-To-Live) for outgoing multicast packets.
 *
**/
int rtp_session_get_multicast_ttl(RtpSession *session)
{
	return session->multicast_ttl;
}


/**
 *rtp_session_set_multicast_loopback:
 *@session: a rtp session
 *@ttl: desired Multicast Time-To-Live
 *
 * Sets the TTL (Time-To-Live) for outgoing multicast packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_multicast_loopback(RtpSession *session, int yesno)
{
    int retval;
    
    // Store new loopback state if one is specified
    if (yesno==0) {
    	// Don't loop back
    	session->multicast_loopback = 0;
    } else if (yesno>0) {
    	// Do loop back
    	session->multicast_loopback = 1;
    }
     
    // Don't do anything if socket hasn't been created yet
473
    if (session->rtp.socket == (ortp_socket_t)-1) return 0;
aymeric's avatar
aymeric committed
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495

    switch (session->rtp.sockfamily) {
        case AF_INET: {
 
			retval= setsockopt(session->rtp.socket, IPPROTO_IP, IP_MULTICAST_LOOP,
						 (SOCKET_OPTION_VALUE)   &session->multicast_loopback, sizeof(session->multicast_loopback));
            
			if (retval<0) break;

			retval= setsockopt(session->rtcp.socket, IPPROTO_IP, IP_MULTICAST_LOOP,
						 (SOCKET_OPTION_VALUE)   &session->multicast_loopback, sizeof(session->multicast_loopback));

 		} break;
        case AF_INET6: {

			retval= setsockopt(session->rtp.socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 
				 (SOCKET_OPTION_VALUE)	&session->multicast_loopback, sizeof(session->multicast_loopback));
					
			if (retval<0) break;
			
			retval= setsockopt(session->rtcp.socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 
				 (SOCKET_OPTION_VALUE)	&session->multicast_loopback, sizeof(session->multicast_loopback));
496 497 498 499
		} break;
	default:
		retval=-1;
	}
aymeric's avatar
aymeric committed
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
    
	if (retval<0)
		ortp_warning("Failed to set multicast loopback on socket.");

	return retval;
}


/**
 *rtp_session_get_multicast_loopback:
 *@session: a rtp session
 *
 * Returns the multicast loopback state of rtp session (true or false).
 *
**/
int rtp_session_get_multicast_loopback(RtpSession *session)
{
	return session->multicast_loopback;
}

/**
 *rtp_session_set_dscp:
 *@session: a rtp session
 *@dscp: desired DSCP PHB value
 *
 * Sets the DSCP (Differentiated Services Code Point) for outgoing RTP packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_dscp(RtpSession *session, int dscp){
	int retval=0;
	int tos;
Ghislain MARY's avatar
Ghislain MARY committed
533 534
	int proto;
	int value_type;
535 536 537
#if (_WIN32_WINNT >= 0x0600)
	OSVERSIONINFOEX ovi;
#endif
aymeric's avatar
aymeric committed
538 539 540 541 542

	// Store new DSCP value if one is specified
	if (dscp>=0) session->dscp = dscp;
	
	// Don't do anything if socket hasn't been created yet
543
	if (session->rtp.socket == (ortp_socket_t)-1) return 0;
aymeric's avatar
aymeric committed
544

545
#if (_WIN32_WINNT >= 0x0600) && !defined(WINAPI_FAMILY_PHONE_APP)
546 547 548 549 550 551 552 553
	memset(&ovi, 0, sizeof(ovi));
	ovi.dwOSVersionInfoSize = sizeof(ovi);
	GetVersionEx((LPOSVERSIONINFO) & ovi);

	ortp_message("check OS support for qwave.lib: %i %i %i\n",
				ovi.dwMajorVersion, ovi.dwMinorVersion, ovi.dwBuildNumber);
	if (ovi.dwMajorVersion > 5) {

Simon Morlat's avatar
Simon Morlat committed
554 555
		if (FAILED(__HrLoadAllImportsForDll("qwave.dll"))) {
			ortp_warning("Failed to load qwave.dll: no QoS available\n" );
556 557 558 559
		}
		else
		{
			if (session->dscp==0)
Simon Morlat's avatar
Simon Morlat committed
560 561 562 563 564 565 566 567 568
				tos=QOSTrafficTypeBestEffort;
			else if (session->dscp==0x8)
				tos=QOSTrafficTypeBackground;
			else if (session->dscp==0x28)
				tos=QOSTrafficTypeAudioVideo;
			else if (session->dscp==0x38)
				tos=QOSTrafficTypeVoice;
			else
				tos=QOSTrafficTypeExcellentEffort; /* 0x28 */
569 570

			if (session->rtp.QoSHandle==NULL) {
Simon Morlat's avatar
Simon Morlat committed
571 572 573 574 575 576 577 578 579 580 581 582
				QOS_VERSION version;
				BOOL QoSResult;

				version.MajorVersion = 1;
				version.MinorVersion = 0;

				QoSResult = QOSCreateHandle(&version, &session->rtp.QoSHandle);

				if (QoSResult != TRUE){
					ortp_error("QOSCreateHandle failed to create handle with error %d\n",
						GetLastError());
					retval=-1;
583
				}
Simon Morlat's avatar
Simon Morlat committed
584
			}
585
			if (session->rtp.QoSHandle!=NULL) {
Simon Morlat's avatar
Simon Morlat committed
586 587 588 589 590 591 592 593 594 595 596 597 598
				BOOL QoSResult;
				QoSResult = QOSAddSocketToFlow(
					session->rtp.QoSHandle, 
					session->rtp.socket,
					(struct sockaddr*)&session->rtp.rem_addr,
					tos, 
					QOS_NON_ADAPTIVE_FLOW, 
					&session->rtp.QoSFlowID);

				if (QoSResult != TRUE){
					ortp_error("QOSAddSocketToFlow failed to add a flow with error %d\n", 
						GetLastError());
					retval=-1;
599 600 601 602 603 604 605 606 607
				}
			}
		}
	} else {
#endif
		// DSCP value is in the upper six bits of the TOS field
		tos = (session->dscp << 2) & 0xFC;
		switch (session->rtp.sockfamily) {
			case AF_INET:
Simon Morlat's avatar
Simon Morlat committed
608 609
				proto=IPPROTO_IP;
				value_type=IP_TOS;
610 611
			break;
		case AF_INET6:
Simon Morlat's avatar
Simon Morlat committed
612
			proto=IPPROTO_IPV6;
aymeric's avatar
aymeric committed
613
#	ifdef IPV6_TCLASS /*seems not defined by my libc*/
Simon Morlat's avatar
Simon Morlat committed
614
			value_type=IPV6_TCLASS;
aymeric's avatar
aymeric committed
615
#	else
Simon Morlat's avatar
Simon Morlat committed
616
			value_type=IP_TOS;
617 618 619
#	endif
			break;
		default:
Simon Morlat's avatar
Simon Morlat committed
620 621 622 623 624 625 626 627 628 629
			ortp_error("Cannot set DSCP because socket family is unspecified.");
			return -1;
		}
		retval = setsockopt(session->rtp.socket, proto, value_type, (SOCKET_OPTION_VALUE)&tos, sizeof(tos));
		if (retval==-1)
			ortp_error("Fail to set DSCP value on rtp socket: %s",getSocketError());
		if (session->rtcp.socket != (ortp_socket_t)-1){
			if (setsockopt(session->rtcp.socket, proto, value_type, (SOCKET_OPTION_VALUE)&tos, sizeof(tos))==-1){
				ortp_error("Fail to set DSCP value on rtcp socket: %s",getSocketError());
			}
630
		}
631
#if (_WIN32_WINNT >= 0x0600) && !defined(WINAPI_FAMILY_PHONE_APP)
aymeric's avatar
aymeric committed
632
	}
633
#endif
aymeric's avatar
aymeric committed
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
	return retval;
}


/**
 *rtp_session_get_dscp:
 *@session: a rtp session
 *
 * Returns the DSCP (Differentiated Services Code Point) for outgoing RTP packets.
 *
**/
int rtp_session_get_dscp(const RtpSession *session)
{
	return session->dscp;
}


/**
 *rtp_session_get_local_port:
 *@session:	a rtp session for which rtp_session_set_local_addr() or rtp_session_set_remote_addr() has been called
 *
 *	This function can be useful to retrieve the local port that was randomly choosen by 
 *	rtp_session_set_remote_addr() when rtp_session_set_local_addr() was not called.
 *
 *	Returns: the local port used to listen for rtp packets, -1 if not set.
**/

int rtp_session_get_local_port(const RtpSession *session){
	return (session->rtp.loc_port>0) ? session->rtp.loc_port : -1;
}

665 666 667 668
int rtp_session_get_local_rtcp_port(const RtpSession *session){
	return (session->rtcp.loc_port>0) ? session->rtcp.loc_port : -1;
}

aymeric's avatar
aymeric committed
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693

static char * ortp_inet_ntoa(struct sockaddr *addr, int addrlen, char *dest, int destlen){
	int err;
	dest[0]=0;
	err=getnameinfo(addr,addrlen,dest,destlen,NULL,0,NI_NUMERICHOST);
	if (err!=0){
		ortp_warning("getnameinfo error: %s",gai_strerror(err));
	}
	return dest;
}

/**
 *rtp_session_set_remote_addr:
 *@session:		a rtp session freshly created.
 *@addr:		a local IP address in the xxx.xxx.xxx.xxx form.
 *@port:		a local port.
 *
 *	Sets the remote address of the rtp session, ie the destination address where rtp packet
 *	are sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP 
 *	packets. Rtp packets that don't come from addr:port are discarded.
 *
 *	Returns: 0 on success.
**/
int
rtp_session_set_remote_addr (RtpSession * session, const char * addr, int port){
694
	return rtp_session_set_remote_addr_full (session, addr, port, addr, port+1);
aymeric's avatar
aymeric committed
695 696 697 698 699
}

/**
 *rtp_session_set_remote_addr_full:
 *@session:		a rtp session freshly created.
700
 *@rtp_addr:		a local IP address in the xxx.xxx.xxx.xxx form.
aymeric's avatar
aymeric committed
701
 *@rtp_port:		a local rtp port.
702
 *@rtcp_addr:		a local IP address in the xxx.xxx.xxx.xxx form.
aymeric's avatar
aymeric committed
703 704 705 706 707 708 709 710 711 712
 *@rtcp_port:		a local rtcp port.
 *
 *	Sets the remote address of the rtp session, ie the destination address where rtp packet
 *	are sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP 
 *	packets. Rtp packets that don't come from addr:port are discarded.
 *
 *	Returns: 0 on success.
**/

int
713
rtp_session_set_remote_addr_full (RtpSession * session, const char * rtp_addr, int rtp_port, const char * rtcp_addr, int rtcp_port)
aymeric's avatar
aymeric committed
714 715 716 717
{
	int err;
	struct addrinfo hints, *res0, *res;
	char num[8];
718
	
aymeric's avatar
aymeric committed
719
	memset(&hints, 0, sizeof(hints));
720
	hints.ai_family = (session->rtp.socket == -1) ? AF_UNSPEC : session->rtp.sockfamily;
aymeric's avatar
aymeric committed
721
	hints.ai_socktype = SOCK_DGRAM;
722 723
	hints.ai_flags = hints.ai_family==AF_INET6 ? AI_V4MAPPED : 0;
	
aymeric's avatar
aymeric committed
724
	snprintf(num, sizeof(num), "%d", rtp_port);
725
	err = getaddrinfo(rtp_addr, num, &hints, &res0);
aymeric's avatar
aymeric committed
726 727 728 729 730 731 732 733 734
	if (err) {
		ortp_warning ("Error in socket address: %s", gai_strerror(err));
		return -1;
	}
	if (session->rtp.socket == -1){
		/* the session has not its socket bound, do it */
		ortp_message ("Setting random local addresses.");
		/* bind to an address type that matches the destination address */
		if (res0->ai_addr->sa_family==AF_INET6)
735 736
			err = rtp_session_set_local_addr (session, "::", -1, -1);
		else err=rtp_session_set_local_addr (session, "0.0.0.0", -1, -1);
aymeric's avatar
aymeric committed
737 738 739
		if (err<0) return -1;
	}

740

aymeric's avatar
aymeric committed
741 742 743 744 745 746 747 748 749 750 751 752
	err=1;
	for (res = res0; res; res = res->ai_next) {
		/* set a destination address that has the same type as the local address */
		if (res->ai_family==session->rtp.sockfamily ) {
			memcpy( &session->rtp.rem_addr, res->ai_addr, res->ai_addrlen);
			session->rtp.rem_addrlen=res->ai_addrlen;
		  	err=0;
		  	break;
		}
	}
	freeaddrinfo(res0);
	if (err) {
753
		ortp_warning("Could not set destination for RTP socket to %s:%i.",rtp_addr,rtp_port);
aymeric's avatar
aymeric committed
754 755 756 757
		return -1;
	}
	
	memset(&hints, 0, sizeof(hints));
758
	hints.ai_family = (session->rtp.socket == -1) ? AF_UNSPEC : session->rtp.sockfamily;
aymeric's avatar
aymeric committed
759
	hints.ai_socktype = SOCK_DGRAM;
760
	hints.ai_flags = hints.ai_family==AF_INET6 ? AI_V4MAPPED : 0;
aymeric's avatar
aymeric committed
761
	snprintf(num, sizeof(num), "%d", rtcp_port);
762
	err = getaddrinfo(rtcp_addr, num, &hints, &res0);
aymeric's avatar
aymeric committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
	if (err) {
		ortp_warning ("Error: %s", gai_strerror(err));
		return err;
	}
	err=1;
	for (res = res0; res; res = res->ai_next) {
		/* set a destination address that has the same type as the local address */
		if (res->ai_family==session->rtp.sockfamily ) {
		  	err=0;
		  	memcpy( &session->rtcp.rem_addr, res->ai_addr, res->ai_addrlen);
			session->rtcp.rem_addrlen=res->ai_addrlen;
		  	break;
		}
	}
	freeaddrinfo(res0);
	if (err) {
779
		ortp_warning("Could not set destination for RCTP socket to %s:%i.",rtcp_addr,rtcp_port);
aymeric's avatar
aymeric committed
780 781 782 783 784 785
		return -1;
	}

	if (can_connect(session)){
		if (try_connect(session->rtp.socket,(struct sockaddr*)&session->rtp.rem_addr,session->rtp.rem_addrlen))
			session->flags|=RTP_SOCKET_CONNECTED;
786
		if (session->rtcp.socket!=(ortp_socket_t)-1){
aymeric's avatar
aymeric committed
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
			if (try_connect(session->rtcp.socket,(struct sockaddr*)&session->rtcp.rem_addr,session->rtcp.rem_addrlen))
				session->flags|=RTCP_SOCKET_CONNECTED;
		}
	}else if (session->flags & RTP_SOCKET_CONNECTED){
		/*must dissolve association done by connect().
		See connect(2) manpage*/
		struct sockaddr sa;
		sa.sa_family=AF_UNSPEC;
		if (connect(session->rtp.socket,&sa,sizeof(sa))<0){
			ortp_error("Cannot dissolve connect() association for rtp socket: %s", getSocketError());
		}
		if (connect(session->rtcp.socket,&sa,sizeof(sa))<0){
			ortp_error("Cannot dissolve connect() association for rtcp socket: %s", getSocketError());
		}
		session->flags&=~RTP_SOCKET_CONNECTED;
		session->flags&=~RTCP_SOCKET_CONNECTED;
	}
jehan's avatar
jehan committed
804 805 806 807 808
	ortp_message("rtp session [%p] set to rtp [%s:%i] rtcp [%s:%i]"	,session
																	,rtp_addr
																	,rtp_port
																	,rtcp_addr
																	,rtcp_port);
aymeric's avatar
aymeric committed
809 810 811
	return 0;
}

812
int rtp_session_set_remote_addr_and_port(RtpSession * session, const char * addr, int rtp_port, int rtcp_port){
813
	return rtp_session_set_remote_addr_full(session,addr,rtp_port,addr,rtcp_port);
aymeric's avatar
aymeric committed
814 815 816 817
}

void rtp_session_set_sockets(RtpSession *session, int rtpfd, int rtcpfd)
{
818 819
	if (rtpfd!=-1) set_non_blocking_socket(rtpfd);
	if (rtcpfd!=-1) set_non_blocking_socket(rtcpfd);
aymeric's avatar
aymeric committed
820 821
	session->rtp.socket=rtpfd;
	session->rtcp.socket=rtcpfd;
822
	if (rtpfd!=-1 || rtcpfd!=-1 )
aymeric's avatar
aymeric committed
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
		session->flags|=(RTP_SESSION_USING_EXT_SOCKETS|RTP_SOCKET_CONNECTED|RTCP_SOCKET_CONNECTED);
	else session->flags&=~(RTP_SESSION_USING_EXT_SOCKETS|RTP_SOCKET_CONNECTED|RTCP_SOCKET_CONNECTED);
}

void rtp_session_set_transports(RtpSession *session, struct _RtpTransport *rtptr, struct _RtpTransport *rtcptr)
{
	session->rtp.tr = rtptr;
	session->rtcp.tr = rtcptr;
	if (rtptr)
		rtptr->session=session;
	if (rtcptr)
		rtcptr->session=session;

	if (rtptr || rtcptr )
		session->flags|=(RTP_SESSION_USING_TRANSPORT);
	else session->flags&=~(RTP_SESSION_USING_TRANSPORT);
}

841 842 843 844
void rtp_session_get_transports(RtpSession *session, RtpTransport **rtptr, RtpTransport **rtcptr){
	if (rtptr) *rtptr=session->rtp.tr;
	if (rtcptr) *rtcptr=session->rtcp.tr;
}
aymeric's avatar
aymeric committed
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859


/**
 *rtp_session_flush_sockets:
 *@session: a rtp session
 *
 * Flushes the sockets for all pending incoming packets.
 * This can be usefull if you did not listen to the stream for a while
 * and wishes to start to receive again. During the time no receive is made
 * packets get bufferised into the internal kernel socket structure.
 *
**/
void rtp_session_flush_sockets(RtpSession *session){
	unsigned char trash[4096];
	struct sockaddr_storage from;
860

aymeric's avatar
aymeric committed
861 862
	socklen_t fromlen=sizeof(from);
	if (rtp_session_using_transport(session, rtp))
863
		{
aymeric's avatar
aymeric committed
864 865
		mblk_t *trashmp=esballoc(trash,sizeof(trash),0,NULL);
		
866
		while (session->rtp.tr->t_recvfrom(session->rtp.tr,trashmp,0,(struct sockaddr *)&from,&fromlen)>0){};
aymeric's avatar
aymeric committed
867

868 869 870 871 872
		if (session->rtcp.tr)
			while (session->rtcp.tr->t_recvfrom(session->rtcp.tr,trashmp,0,(struct sockaddr *)&from,&fromlen)>0){};
			freemsg(trashmp);
			return;
		}
aymeric's avatar
aymeric committed
873

874
	if (session->rtp.socket!=(ortp_socket_t)-1){
875
		while (recvfrom(session->rtp.socket,(char*)trash,sizeof(trash),0,(struct sockaddr *)&from,&fromlen)>0){};
aymeric's avatar
aymeric committed
876
	}
877
	if (session->rtcp.socket!=(ortp_socket_t)-1){
878
		while (recvfrom(session->rtcp.socket,(char*)trash,sizeof(trash),0,(struct sockaddr*)&from,&fromlen)>0){};
aymeric's avatar
aymeric committed
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
	}
}


#ifdef USE_SENDMSG 
#define MAX_IOV 30
static int rtp_sendmsg(int sock,mblk_t *m, struct sockaddr *rem_addr, int addr_len){
	int error;
	struct msghdr msg;
	struct iovec iov[MAX_IOV];
	int iovlen;
	for(iovlen=0; iovlen<MAX_IOV && m!=NULL; m=m->b_cont,iovlen++){
		iov[iovlen].iov_base=m->b_rptr;
		iov[iovlen].iov_len=m->b_wptr-m->b_rptr;
	}
	if (iovlen==MAX_IOV){
		ortp_error("Too long msgb, didn't fit into iov, end discarded.");
	}
	msg.msg_name=(void*)rem_addr;
	msg.msg_namelen=addr_len;
	msg.msg_iov=&iov[0];
	msg.msg_iovlen=iovlen;
	msg.msg_control=NULL;
	msg.msg_controllen=0;
	msg.msg_flags=0;
	error=sendmsg(sock,&msg,0);
	return error;
}
#endif	

#define IP_UDP_OVERHEAD (20+8)
910
#define IP6_UDP_OVERHEAD (40+8)
aymeric's avatar
aymeric committed
911

912 913 914 915 916 917 918 919
static bool_t is_ipv6(RtpSession *s){
	if (s->rtp.sockfamily==AF_INET6){
		struct sockaddr_in6 *in6=(struct sockaddr_in6*)&s->rtp.rem_addr;
		return !IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr);
	}
	return FALSE;
}

aymeric's avatar
aymeric committed
920
static void update_sent_bytes(RtpSession*s, int nbytes){
921
	int overhead=is_ipv6(s) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
aymeric's avatar
aymeric committed
922
	if (s->rtp.sent_bytes==0){
923
		ortp_gettimeofday(&s->rtp.send_bw_start,NULL);
aymeric's avatar
aymeric committed
924
	}
925
	s->rtp.sent_bytes+=nbytes+overhead;
aymeric's avatar
aymeric committed
926 927 928
}

static void update_recv_bytes(RtpSession*s, int nbytes){
929
	int overhead=is_ipv6(s) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
930

aymeric's avatar
aymeric committed
931
	if (s->rtp.recv_bytes==0){
932
		ortp_gettimeofday(&s->rtp.recv_bw_start,NULL);
aymeric's avatar
aymeric committed
933
	}
934
	s->rtp.recv_bytes+=nbytes+overhead;
aymeric's avatar
aymeric committed
935 936 937 938 939 940 941 942 943 944 945 946 947
}

int
rtp_session_rtp_send (RtpSession * session, mblk_t * m)
{
	int error;
	int i;
	rtp_header_t *hdr;
	struct sockaddr *destaddr=(struct sockaddr*)&session->rtp.rem_addr;
	socklen_t destlen=session->rtp.rem_addrlen;
	ortp_socket_t sockfd=session->rtp.socket;

	hdr = (rtp_header_t *) m->b_rptr;
948 949 950 951 952 953 954 955 956 957
	if (hdr->version == 0) {
		/* We are probably trying to send a STUN packet so don't change its content. */
	} else {
		/* perform host to network conversions */
		hdr->ssrc = htonl (hdr->ssrc);
		hdr->timestamp = htonl (hdr->timestamp);
		hdr->seq_number = htons (hdr->seq_number);
		for (i = 0; i < hdr->cc; i++)
			hdr->csrc[i] = htonl (hdr->csrc[i]);
	}
aymeric's avatar
aymeric committed
958 959 960 961 962 963 964 965 966 967 968 969 970 971

	if (session->flags & RTP_SOCKET_CONNECTED) {
		destaddr=NULL;
		destlen=0;
	}

	if (rtp_session_using_transport(session, rtp)){
		error = (session->rtp.tr->t_sendto) (session->rtp.tr,m,0,destaddr,destlen);
	}else{
#ifdef USE_SENDMSG
		error=rtp_sendmsg(sockfd,m,destaddr,destlen);
#else
		if (m->b_cont!=NULL)
			msgpullup(m,-1);
972
		error = sendto (sockfd, (char*)m->b_rptr, (int) (m->b_wptr - m->b_rptr),
aymeric's avatar
aymeric committed
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
			 0,destaddr,destlen);
#endif
	}
	if (error < 0){
		if (session->on_network_error.count>0){
			rtp_signal_table_emit3(&session->on_network_error,(long)"Error sending RTP packet",INT_TO_POINTER(getSocketErrorCode()));
		}else ortp_warning ("Error sending rtp packet: %s ; socket=%i", getSocketError(), sockfd);
		session->rtp.send_errno=getSocketErrorCode();
	}else{
		update_sent_bytes(session,error);
	}
	freemsg (m);
	return error;
}

int
rtp_session_rtcp_send (RtpSession * session, mblk_t * m)
{
	int error=0;
	ortp_socket_t sockfd=session->rtcp.socket;
	struct sockaddr *destaddr=(struct sockaddr*)&session->rtcp.rem_addr;
	socklen_t destlen=session->rtcp.rem_addrlen;
	bool_t using_connected_socket=(session->flags & RTCP_SOCKET_CONNECTED)!=0;

	if (using_connected_socket) {
		destaddr=NULL;
		destlen=0;
	}

	if (session->rtcp.enabled &&
1003
		( (sockfd!=(ortp_socket_t)-1 && (session->rtcp.rem_addrlen>0 ||using_connected_socket))
aymeric's avatar
aymeric committed
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
			|| rtp_session_using_transport(session, rtcp) ) ){
		if (rtp_session_using_transport(session, rtcp)){
			error = (session->rtcp.tr->t_sendto) (session->rtcp.tr, m, 0,
			destaddr, destlen);
		}
		else{
#ifdef USE_SENDMSG
			error=rtp_sendmsg(sockfd,m,destaddr, destlen);
#else
			if (m->b_cont!=NULL){
				msgpullup(m,-1);
			}
1016
			error = sendto (sockfd, (char*)m->b_rptr,
aymeric's avatar
aymeric committed
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
			(int) (m->b_wptr - m->b_rptr), 0,
			destaddr, destlen);
#endif
		}
		if (error < 0){
			char host[65];
			if (session->on_network_error.count>0){
				rtp_signal_table_emit3(&session->on_network_error,(long)"Error sending RTCP packet",INT_TO_POINTER(getSocketErrorCode()));
			}else ortp_warning ("Error sending rtcp packet: %s ; socket=%i; addr=%s", getSocketError(), session->rtcp.socket, ortp_inet_ntoa((struct sockaddr*)&session->rtcp.rem_addr,session->rtcp.rem_addrlen,host,sizeof(host)) );
		}
	}else ortp_message("Not sending rtcp report: sockfd=%i, rem_addrlen=%i, connected=%i",sockfd,session->rtcp.rem_addrlen,using_connected_socket);
	freemsg (m);
	return error;
}

Yann Diorcet's avatar
Yann Diorcet committed
1032
int rtp_session_rtp_recv_abstract(ortp_socket_t socket, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen) {
1033
	int ret;
Yann Diorcet's avatar
Yann Diorcet committed
1034
	int bufsz = (int) (msg->b_datap->db_lim - msg->b_datap->db_base);
1035
#ifndef _WIN32
Yann Diorcet's avatar
Yann Diorcet committed
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
	struct iovec   iov;
	struct msghdr  msghdr;
	struct cmsghdr *cmsghdr;
	struct {
			struct cmsghdr cm;
			char control[512];
		} control;
	memset(&msghdr, 0, sizeof(msghdr));
	memset(&iov, 0, sizeof(iov));
	iov.iov_base = msg->b_wptr;
	iov.iov_len  = bufsz;
	if(from != NULL && fromlen != NULL) {
		msghdr.msg_name = from;
		msghdr.msg_namelen = *fromlen;
	}
	msghdr.msg_iov     = &iov;
	msghdr.msg_iovlen  = 1;
	msghdr.msg_control = &control;
	msghdr.msg_controllen = sizeof(control);

1056
	ret = recvmsg(socket, &msghdr, flags);
Yann Diorcet's avatar
Yann Diorcet committed
1057 1058 1059
	if(fromlen != NULL)
		*fromlen = msghdr.msg_namelen;
	if(ret >= 0) {
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
#else
	char control[512];
	WSAMSG msghdr;
	WSACMSGHDR *cmsghdr;
	WSABUF data_buf;
	DWORD bytes_received;

	if (ortp_WSARecvMsg == NULL) {
		return recvfrom(socket, (char *)msg->b_wptr, bufsz, flags, from, fromlen);
	}

	memset(&msghdr, 0, sizeof(msghdr));
	memset(control, 0, sizeof(control));
	if(from != NULL && fromlen != NULL) {
		msghdr.name = from;
		msghdr.namelen = *fromlen;
	}
	data_buf.buf = (char *)msg->b_wptr;
	data_buf.len = bufsz;
	msghdr.lpBuffers = &data_buf;
	msghdr.dwBufferCount = 1;
	msghdr.Control.buf = control;
	msghdr.Control.len = sizeof(control);
	msghdr.dwFlags = flags;
	ret = ortp_WSARecvMsg(socket, &msghdr, &bytes_received, NULL, NULL);
	if(fromlen != NULL)
		*fromlen = msghdr.namelen;
	if(ret >= 0) {
		ret = bytes_received;
#endif
Yann Diorcet's avatar
Yann Diorcet committed
1090
		for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr != NULL ; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
1091
#if defined(ORTP_TIMESTAMP)
Yann Diorcet's avatar
Yann Diorcet committed
1092 1093 1094 1095
			if (cmsghdr->cmsg_level == SOL_SOCKET && cmsghdr->cmsg_type == SO_TIMESTAMP) {
				memcpy(&msg->timestamp, (struct timeval *)CMSG_DATA(cmsghdr), sizeof(struct timeval));
			}
#endif
1096 1097
#ifdef IP_PKTINFO
			if ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_PKTINFO)) {
1098
				struct in_pktinfo *pi = (struct in_pktinfo *)CMSG_DATA(cmsghdr);
1099
				memcpy(&msg->recv_addr.addr.ipi_addr, &pi->ipi_addr, sizeof(msg->recv_addr.addr.ipi_addr));
1100 1101 1102 1103 1104 1105
				msg->recv_addr.family = AF_INET;
			}
#endif
#ifdef IPV6_PKTINFO
			if ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_PKTINFO)) {
				struct in6_pktinfo *pi = (struct in6_pktinfo *)CMSG_DATA(cmsghdr);
1106
				memcpy(&msg->recv_addr.addr.ipi6_addr, &pi->ipi6_addr, sizeof(msg->recv_addr.addr.ipi6_addr));
1107 1108 1109 1110 1111 1112
				msg->recv_addr.family = AF_INET6;
			}
#endif
#ifdef IP_RECVDSTADDR
			if ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_RECVDSTADDR)) {
				struct in_addr *ia = (struct in_addr *)CMSG_DATA(cmsghdr);
1113
				memcpy(&msg->recv_addr.addr.ipi_addr, ia, sizeof(msg->recv_addr.addr.ipi_addr));
1114
				msg->recv_addr.family = AF_INET;
1115
			}
1116 1117
#endif
#ifdef IPV6_RECVDSTADDR
1118
			if ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_RECVDSTADDR)) {
1119
				struct in6_addr *ia = (struct in6_addr *)CMSG_DATA(cmsghdr);
1120
				memcpy(&msg->recv_addr.addr.ipi6_addr, ia, sizeof(msg->recv_addr.addr.ipi6_addr));
1121 1122
				msg->recv_addr.family = AF_INET6;
			}
1123 1124
#endif
#ifdef IP_RECVTTL
1125
			if ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_TTL)) {
1126 1127 1128 1129 1130
				uint32_t *ptr = (uint32_t *)CMSG_DATA(cmsghdr);
				msg->ttl_or_hl = (*ptr & 0xFF);
			}
#endif
#ifdef IPV6_RECVHOPLIMIT
1131
			if ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_HOPLIMIT)) {
1132 1133 1134
				uint32_t *ptr = (uint32_t *)CMSG_DATA(cmsghdr);
				msg->ttl_or_hl = (*ptr & 0xFF);
			}
1135
#endif
1136 1137
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
1138 1139 1140 1141
	return ret;
}

int rtp_session_rtp_recv (RtpSession * session, uint32_t user_ts)
aymeric's avatar
aymeric committed
1142 1143 1144 1145
{
	int error;
	ortp_socket_t sockfd=session->rtp.socket;
	struct sockaddr_storage remaddr;
1146

aymeric's avatar
aymeric committed
1147 1148 1149
	socklen_t addrlen = sizeof (remaddr);
	mblk_t *mp;
	
1150
	if ((sockfd==(ortp_socket_t)-1) && !rtp_session_using_transport(session, rtp)) return -1;  /*session has no sockets for the moment*/
aymeric's avatar
aymeric committed
1151 1152 1153 1154 1155 1156 1157 1158 1159

	while (1)
	{
		bool_t sock_connected=!!(session->flags & RTP_SOCKET_CONNECTED);

		if (session->rtp.cached_mp==NULL)
			 session->rtp.cached_mp = msgb_allocator_alloc(&session->allocator,session->recv_buf_size);
		mp=session->rtp.cached_mp;
		if (sock_connected){
Yann Diorcet's avatar
Yann Diorcet committed
1160 1161
			error=rtp_session_rtp_recv_abstract(sockfd, mp, 0, NULL, NULL);
		}else if (rtp_session_using_transport(session, rtp)) {
aymeric's avatar
aymeric committed
1162 1163 1164
			error = (session->rtp.tr->t_recvfrom)(session->rtp.tr, mp, 0,
				  (struct sockaddr *) &remaddr,
				  &addrlen);
Yann Diorcet's avatar
Yann Diorcet committed
1165
		} else { error = rtp_session_rtp_recv_abstract(sockfd, mp, 0,
aymeric's avatar
aymeric committed
1166 1167
				  (struct sockaddr *) &remaddr,
				  &addrlen);
Yann Diorcet's avatar
Yann Diorcet committed
1168
		}
aymeric's avatar
aymeric committed
1169
		if (error > 0){
1170 1171 1172
			if (session->use_connect){
				/* In the case where use_connect is false, symmetric RTP is handled in rtp_session_rtp_parse() */
				if (session->symmetric_rtp && !sock_connected){
1173 1174 1175
					/* store the sender rtp address to do symmetric RTP */
					memcpy(&session->rtp.rem_addr,&remaddr,addrlen);
					session->rtp.rem_addrlen=addrlen;
aymeric's avatar
aymeric committed
1176 1177 1178 1179 1180
					if (try_connect(sockfd,(struct sockaddr*)&remaddr,addrlen))
						session->flags|=RTP_SOCKET_CONNECTED;
				}
			}
			mp->b_wptr+=error;
1181 1182 1183 1184 1185
			if (session->net_sim_ctx)
				mp=rtp_session_network_simulate(session,mp);
			/* then parse the message and put on jitter buffer queue */
			if (mp){
				update_recv_bytes(session,mp->b_wptr-mp->b_rptr);
1186
				rtp_session_rtp_parse(session, mp, user_ts, (struct sockaddr*)&remaddr,addrlen);	
1187
			}
aymeric's avatar
aymeric committed
1188 1189 1190 1191 1192
			session->rtp.cached_mp=NULL;
			/*for bandwidth measurements:*/
		}
		else
		{
1193 1194
			int errnum;
			if (error==-1 && !is_would_block_error((errnum=getSocketErrorCode())) )
aymeric's avatar
aymeric committed
1195 1196 1197
			{
				if (session->on_network_error.count>0){
					rtp_signal_table_emit3(&session->on_network_error,(long)"Error receiving RTP packet",INT_TO_POINTER(getSocketErrorCode()));
1198
				}else ortp_warning("Error receiving RTP packet: %s, err num  [%i],error [%i]",getSocketError(),errnum,error);
Simon Morlat's avatar
Simon Morlat committed
1199 1200 1201 1202
#ifdef __ios
				/*hack for iOS and non-working socket because of background mode*/
				if (errnum==ENOTCONN){
					/*re-create new sockets */
1203
					rtp_session_set_local_addr(session,session->rtp.sockfamily==AF_INET ? "0.0.0.0" : "::0",session->rtp.loc_port,session->rtcp.loc_port);
Simon Morlat's avatar
Simon Morlat committed
1204 1205
				}
#endif
1206 1207 1208 1209 1210 1211 1212 1213
			}else{
				/*EWOULDBLOCK errors or transports returning 0 are ignored.*/
				if (session->net_sim_ctx){
					/*drain possible packets queued in the network simulator*/
					mp=rtp_session_network_simulate(session,NULL);
					if (mp){
						/* then parse the message and put on jitter buffer queue */
						update_recv_bytes(session,msgdsize(mp));
1214
						rtp_session_rtp_parse(session, mp, user_ts, (struct sockaddr*)&session->rtp.rem_addr,session->rtp.rem_addrlen);						
1215 1216
					}
				}
aymeric's avatar
aymeric committed
1217 1218
			}
			/* don't free the cached_mp, it will be reused next time */
1219
			return -1;
aymeric's avatar
aymeric committed
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
		}
	}
	return error;
}

void rtp_session_notify_inc_rtcp(RtpSession *session, mblk_t *m){
	if (session->eventqs!=NULL){
		OrtpEvent *ev=ortp_event_new(ORTP_EVENT_RTCP_PACKET_RECEIVED);
		OrtpEventData *d=ortp_event_get_data(ev);
		d->packet=m;
		rtp_session_dispatch_event(session,ev);
	}
	else freemsg(m);  /* avoid memory leak */
}

1235
static void compute_rtt(RtpSession *session, const struct timeval *now, uint32_t lrr, uint32_t dlrr){
Simon Morlat's avatar
Simon Morlat committed
1236
	uint64_t curntp=ortp_timeval_to_ntp(now);
1237
	uint32_t approx_ntp=(curntp>>16) & 0xFFFFFFFF;
1238 1239 1240
	/*ortp_message("rtt approx_ntp=%u, lrr=%u, dlrr=%u",approx_ntp,lrr,dlrr);*/
	if (lrr!=0 && dlrr!=0){
		double rtt_frac=approx_ntp-lrr-dlrr;
1241 1242 1243 1244 1245 1246 1247 1248
		if (rtt_frac>=0){
			rtt_frac/=65536.0;
			/*take into account the network simulator */
			if (session->net_sim_ctx && session->net_sim_ctx->params.max_bandwidth>0){
				double sim_delay=(double)session->net_sim_ctx->qsize/(double)session->net_sim_ctx->params.max_bandwidth;
				rtt_frac+=sim_delay;
			}
			session->rtt=rtt_frac;
Simon Morlat's avatar
Simon Morlat committed
1249
			/*ortp_message("rtt estimated to %f s",session->rtt);*/
1250
		}else ortp_warning("Negative RTT computation, maybe due to clock adjustments.");
1251 1252 1253
	}
}

1254 1255 1256 1257 1258 1259 1260
static void compute_rtt_from_report_block(RtpSession *session, const struct timeval *now, const report_block_t *rb) {
	uint32_t last_sr_time = report_block_get_last_SR_time(rb);
	uint32_t sr_delay = report_block_get_last_SR_delay(rb);
	compute_rtt(session, now, last_sr_time, sr_delay);
}

static void compute_rtcp_xr_statistics(RtpSession *session, mblk_t *block, const struct timeval *now) {
1261 1262 1263 1264 1265 1266 1267
	uint64_t ntp_timestamp;
	OrtpRtcpXrStats *stats = &session->rtcp_xr_stats;

	switch (rtcp_XR_get_block_type(block)) {
		case RTCP_XR_RCVR_RTT:
			ntp_timestamp = rtcp_XR_rcvr_rtt_get_ntp_timestamp(block);
			stats->last_rcvr_rtt_ts = (ntp_timestamp >> 16) & 0xffffffff;
1268 1269 1270 1271 1272
			stats->last_rcvr_rtt_time.tv_sec = now->tv_sec;
			stats->last_rcvr_rtt_time.tv_usec = now->tv_usec;
			break;
		case RTCP_XR_DLRR:
			compute_rtt(session, now, rtcp_XR_dlrr_get_lrr(block), rtcp_XR_dlrr_get_dlrr(block));
1273 1274 1275 1276 1277 1278
			break;
		default:
			break;
	}
}

1279 1280 1281 1282
/*
 * @brief : for SR packets, retrieves their timestamp, gets the date, and stores these information into the session descriptor. The date values may be used for setting some fields of the report block of the next RTCP packet to be sent.
 * @param session : the current session descriptor.
 * @param block : the block descriptor that may contain a SR RTCP message.
1283
 * @return -1 if we detect that the packet is in fact a STUN packet, otherwise 0.
1284 1285
 * @note a basic parsing is done on the block structure. However, if it fails, no error is returned, and the session descriptor is left as is, so it does not induce any change in the caller procedure behaviour.
 */
1286
static int process_rtcp_packet( RtpSession *session, mblk_t *block, struct sockaddr *addr, socklen_t addrlen ) {
1287 1288 1289 1290 1291 1292
	rtcp_common_header_t *rtcp;
	RtpStream * rtpstream = &session->rtp;

	int msgsize = (int) ( block->b_wptr - block->b_rptr );
	if ( msgsize < RTCP_COMMON_HEADER_SIZE ) {
		ortp_debug( "Receiving a too short RTCP packet" );
1293
		return 0;
1294 1295 1296
	}

	rtcp = (rtcp_common_header_t *)block->b_rptr;
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308

	if (rtcp->version != 2)
	{
		/* try to see if it is a STUN packet */
		uint16_t stunlen = *((uint16_t *)(block->b_rptr + sizeof(uint16_t)));
		stunlen = ntohs(stunlen);
		if (stunlen + 20 == block->b_wptr - block->b_rptr) {
			/* this looks like a stun packet */
			if (session->eventqs != NULL) {
				OrtpEvent *ev = ortp_event_new(ORTP_EVENT_STUN_PACKET_RECEIVED);
				OrtpEventData *ed = ortp_event_get_data(ev);
				ed->packet = block;
1309 1310
				ed->source_addrlen=addrlen;
				memcpy(&ed->source_addr,addr,addrlen);
1311
				ed->info.socket_type = OrtpRTCPSocket;
1312 1313 1314 1315 1316 1317 1318 1319
				rtp_session_dispatch_event(session, ev);
				return -1;
			}
		}
		/* discard in two case: the packet is not stun OR nobody is interested by STUN (no eventqs) */
		ortp_debug("Receiving rtcp packet with version number !=2...discarded");
		return 0;
	}
1320
	/* compound rtcp packet can be composed by more than one rtcp message */
Simon Morlat's avatar
Simon Morlat committed
1321 1322
	do{
		struct timeval reception_date;
1323 1324
		const report_block_t *rb;
		
Simon Morlat's avatar
Simon Morlat committed
1325
		/* Getting the reception date from the main clock */	
1326
		ortp_gettimeofday( &reception_date, NULL );
1327

Simon Morlat's avatar
Simon Morlat committed
1328
		if (rtcp_is_SR(block) ) {
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
			rtcp_sr_t *sr = (rtcp_sr_t *) rtcp;
			
			/* The session descriptor values are reset in case there is an error in the SR block parsing */
			rtpstream->last_rcv_SR_ts = 0;
			rtpstream->last_rcv_SR_time.tv_usec = 0;
			rtpstream->last_rcv_SR_time.tv_sec = 0;

			
			if ( ntohl( sr->ssrc ) != session->rcv.ssrc ) {
				ortp_debug( "Receiving a RTCP SR packet from an unknown ssrc" );
1339
				return 0;
1340 1341 1342 1343
			}

			if ( msgsize < RTCP_COMMON_HEADER_SIZE + RTCP_SSRC_FIELD_SIZE + RTCP_SENDER_INFO_SIZE + ( RTCP_REPORT_BLOCK_SIZE * sr->ch.rc ) ) {
				ortp_debug( "Receiving a too short RTCP SR packet" );
1344
				return 0;
1345 1346 1347 1348 1349 1350 1351 1352
			}

			/* Saving the data to fill LSR and DLSR field in next RTCP report to be transmitted */
			/* This value will be the LSR field of the next RTCP report (only the central 32 bits are kept, as described in par.4 of RC3550) */
			rtpstream->last_rcv_SR_ts = ( ntohl( sr->si.ntp_timestamp_msw ) << 16 ) | ( ntohl( sr->si.ntp_timestamp_lsw ) >> 16 );
			/* This value will help in processing the DLSR of the next RTCP report ( see report_block_init() in rtcp.cc ) */
			rtpstream->last_rcv_SR_time.tv_usec = reception_date.tv_usec;
			rtpstream->last_rcv_SR_time.tv_sec = reception_date.tv_sec;
1353
			rb=rtcp_SR_get_report_block(block,0);
1354
			if (rb) compute_rtt_from_report_block(session,&reception_date,rb);
Simon Morlat's avatar
Simon Morlat committed
1355
		}else if ( rtcp_is_RR(block)){
1356
			rb=rtcp_RR_get_report_block(block,0);
1357
			if (rb) compute_rtt_from_report_block(session,&reception_date,rb);
1358
		} else if (rtcp_is_XR(block)) {
1359
			compute_rtcp_xr_statistics(session, block, &reception_date);
1360
		}
Simon Morlat's avatar
Simon Morlat committed
1361 1362
	}while (rtcp_next_packet(block));
	rtcp_rewind(block);
1363
	return 0;
1364 1365
}

1366 1367 1368 1369 1370 1371
static void reply_to_collaborative_rtcp_xr_packet(RtpSession *session, mblk_t *block) {
	if (rtcp_is_XR(block) && (rtcp_XR_get_block_type(block) == RTCP_XR_RCVR_RTT)) {
		rtp_session_send_rtcp_xr_dlrr(session);
	}
}

1372

aymeric's avatar
aymeric committed
1373 1374 1375 1376 1377
int
rtp_session_rtcp_recv (RtpSession * session)
{
	int error;
	struct sockaddr_storage remaddr;
1378

aymeric's avatar
aymeric committed
1379 1380 1381
	socklen_t addrlen=0;
	mblk_t *mp;

1382
	if (session->rtcp.socket==(ortp_socket_t)-1 && !rtp_session_using_transport(session, rtcp)) return -1;  /*session has no rtcp sockets for the moment*/
aymeric's avatar
aymeric committed
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392
	

	while (1)
	{
		bool_t sock_connected=!!(session->flags & RTCP_SOCKET_CONNECTED);
		if (session->rtcp.cached_mp==NULL)
			 session->rtcp.cached_mp = allocb (RTCP_MAX_RECV_BUFSIZE, 0);
		
		mp=session->rtcp.cached_mp;
		if (sock_connected){
Yann Diorcet's avatar
Yann Diorcet committed
1393
			error=rtp_session_rtp_recv_abstract(session->rtcp.socket, mp, 0, NULL, NULL);
aymeric's avatar
aymeric committed
1394 1395 1396 1397 1398 1399 1400 1401
		}else {
			addrlen=sizeof (remaddr);

			if (rtp_session_using_transport(session, rtcp))
			  error=(session->rtcp.tr->t_recvfrom)(session->rtcp.tr, mp, 0,
				  (struct sockaddr *) &remaddr,
				  &addrlen);
			else
Yann Diorcet's avatar
Yann Diorcet committed
1402
			  error=rtp_session_rtp_recv_abstract (session->rtcp.socket,mp, 0,
aymeric's avatar
aymeric committed
1403 1404 1405 1406 1407 1408
				  (struct sockaddr *) &remaddr,
				  &addrlen);
		}
		if (error > 0)
		{
			mp->b_wptr += error;
1409 1410
			if (process_rtcp_packet( session, mp, (struct sockaddr*)&remaddr, addrlen) >= 0) {
				/* post an event to notify the application*/
aymeric's avatar
aymeric committed
1411
				rtp_session_notify_inc_rtcp(session,mp);
1412 1413 1414 1415
				/* reply to collaborative RTCP XR packets if needed. */
				if (session->rtcp.xr_conf.enabled == TRUE) {
					reply_to_collaborative_rtcp_xr_packet(session, mp);
				}
aymeric's avatar
aymeric committed
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431
			}
			session->rtcp.cached_mp=NULL;
			if (session->symmetric_rtp && !sock_connected){
				/* store the sender rtp address to do symmetric RTP */
				memcpy(&session->rtcp.rem_addr,&remaddr,addrlen);
				session->rtcp.rem_addrlen=addrlen;
				if (session->use_connect){
					if (try_connect(session->rtcp.socket,(struct sockaddr*)&remaddr,addrlen))
						session->flags|=RTCP_SOCKET_CONNECTED;
				}
			}
		}
		else
		{
			int errnum=getSocketErrorCode();

1432
			if (error == 0 || (error==-1 && errnum==0))
aymeric's avatar
aymeric committed
1433
			{
1434 1435 1436
				/*ortp_warning
					("rtcp_recv: strange... recv() returned zero.");*/
				/*(error == -1 && errnum==0) for buggy drivers*/
aymeric's avatar
aymeric committed
1437 1438 1439 1440 1441
			}
			else if (!is_would_block_error(errnum))
			{
				if (session->on_network_error.count>0){
					rtp_signal_table_emit3(&session->on_network_error,(long)"Error receiving RTCP packet",INT_TO_POINTER(errnum));
jehan's avatar
jehan committed
1442
				}else ortp_warning("Error receiving RTCP packet on port [%i]: %s.",session->rtcp.loc_port,getSocketError());
aymeric's avatar
aymeric committed
1443 1444 1445 1446 1447 1448 1449 1450 1451
				session->rtp.recv_errno=errnum;
			}
			/* don't free the cached_mp, it will be reused next time */
			return -1;	/* avoids an infinite loop ! */
		}
	}
	return error;
}