rtpsession_inet.c 63 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
 * The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) implementation with additional features.
 * Copyright (C) 2017 Belledonne Communications SARL
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
aymeric's avatar
aymeric committed
19 20


21
#ifndef _GNU_SOURCE
22
#define _GNU_SOURCE
23
#endif
24

Benjamin REIS's avatar
Benjamin REIS committed
25 26 27 28
#if __APPLE__
#include "TargetConditionals.h"
#endif

29
#ifdef HAVE_CONFIG_H
30
#include "ortp-config.h" /*needed for HAVE_SYS_UIO_H and HAVE_ARC4RANDOM */
aymeric's avatar
aymeric committed
31
#endif
32
#include <bctoolbox/port.h>
33 34 35 36
#include "ortp/ortp.h"
#include "utils.h"
#include "ortp/rtpsession.h"
#include "rtpsession_priv.h"
aymeric's avatar
aymeric committed
37

38 39 40
#if (_WIN32_WINNT >= 0x0600)
#include <delayimp.h>
#undef ExternC
41
#ifdef ORTP_WINDOWS_DESKTOP
42
#include <QOS2.h>
43
#include <VersionHelpers.h>
44
#endif
Ghislain MARY's avatar
Ghislain MARY committed
45
#endif
46

47
#if (defined(_WIN32) || defined(_WIN32_WCE)) && defined(ORTP_WINDOWS_DESKTOP)
48
#include <mswsock.h>
49 50
#endif

aymeric's avatar
aymeric committed
51 52 53 54 55 56 57
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#define USE_SENDMSG 1
#endif

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

58
#if defined(_WIN32) || defined(_WIN32_WCE)
59 60 61
#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}}
62
#ifndef MAX_NATURAL_ALIGNMENT
63
#define MAX_NATURAL_ALIGNMENT sizeof(DWORD)
64 65
#endif
#ifndef TYPE_ALIGNMENT
66
#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test)
67
#endif
68
typedef WSACMSGHDR *LPWSACMSGHDR;
69
#ifndef WSA_CMSGHDR_ALIGN
70
#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1)))
71 72
#endif
#ifndef WSA_CMSGDATA_ALIGN
73
#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1)))
74 75
#endif
#ifndef WSA_CMSG_FIRSTHDR
76
#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL)
77 78
#endif
#ifndef WSA_CMSG_NXTHDR
79
#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))))
80 81
#endif
#ifndef WSA_CMSG_DATA
82
#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)))
83
#endif
84
#endif
85 86 87 88 89 90 91 92
#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;
93

Ghislain MARY's avatar
Ghislain MARY committed
94 95
#endif

96
#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__QNX__)
Ghislain MARY's avatar
Ghislain MARY committed
97
/* Mingw32 does not define AI_V4MAPPED, however it is supported starting from Windows Vista. QNX also does not define AI_V4MAPPED. */
98 99 100
#	ifndef AI_V4MAPPED
#	define AI_V4MAPPED 0x00000800
#	endif
101
#	ifndef AI_ALL
102
#	define AI_ALL 0x00000100
103
#	endif
Simon Morlat's avatar
Simon Morlat committed
104 105 106
#	ifndef IPV6_V6ONLY
#	define IPV6_V6ONLY 27
#	endif
107 108
#endif

109 110 111 112
#ifndef IN6_IS_ADDR_MULTICAST
#define IN6_IS_ADDR_MULTICAST(i)	(((uint8_t *) (i))[0] == 0xff)
#endif

113 114 115
static int
_rtp_session_set_remote_addr_full (RtpSession * session, const char * rtp_addr, int rtp_port, const char * rtcp_addr, int rtcp_port, bool_t is_aux);

116
static bool_t try_connect(ortp_socket_t fd, const struct sockaddr *dest, socklen_t addrlen){
aymeric's avatar
aymeric committed
117 118 119 120 121 122 123
	if (connect(fd,dest,addrlen)<0){
		ortp_warning("Could not connect() socket: %s",getSocketError());
		return FALSE;
	}
	return TRUE;
}

124 125
static int set_multicast_group(ortp_socket_t sock, const char *addr){
#ifndef __hpux
126
	struct addrinfo *res;
127 128
	int err;

129 130
	res = bctbx_name_to_addrinfo(AF_UNSPEC, SOCK_DGRAM, addr, 0);
	if (res == NULL) return -1;
131

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
	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));
				if (err < 0){
					ortp_warning ("Fail to join address group: %s.", getSocketError());
				} else {
					ortp_message ("RTP socket [%i] has joined address group [%s]",sock, addr);
				}
			}
		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());
				} else {
					ortp_message ("RTP socket 6 [%i] has joined address group [%s]",sock, addr);
				}
			}
		break;
	}
	freeaddrinfo(res);
	return 0;
#else
	return -1;
#endif
}

169
static ortp_socket_t create_and_bind(const char *addr, int *port, int *sock_family, bool_t reuse_addr,struct sockaddr_storage* bound_addr,socklen_t *bound_addr_len){
aymeric's avatar
aymeric committed
170 171
	int err;
	int optval = 1;
smorlat's avatar
smorlat committed
172
	ortp_socket_t sock=-1;
173
	struct addrinfo *res0, *res;
174

175 176
	if (*port==-1) *port=0;
	if (*port==0) reuse_addr=FALSE;
177

178 179
	res0 = bctbx_name_to_addrinfo(AF_UNSPEC, SOCK_DGRAM, addr, *port);
	if (res0 == NULL) return -1;
180

aymeric's avatar
aymeric committed
181 182
	for (res = res0; res; res = res->ai_next) {
		sock = socket(res->ai_family, res->ai_socktype, 0);
smorlat's avatar
smorlat committed
183 184 185
		if (sock==-1)
			continue;

aymeric's avatar
aymeric committed
186 187 188 189 190 191 192
		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());
			}
193
#ifdef SO_REUSEPORT
194 195 196 197 198 199 200 201
			/*SO_REUSEPORT is required on mac and ios especially for doing multicast*/
			err = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
					(SOCKET_OPTION_VALUE)&optval, sizeof (optval));
			if (err < 0)
			{
				ortp_warning ("Fail to set rtp port reusable: %s.", getSocketError());
			}
#endif
aymeric's avatar
aymeric committed
202
		}
203 204 205 206 207 208 209 210
		/*enable dual stack operation, default is enabled on unix, disabled on windows.*/
		if (res->ai_family==AF_INET6){
			optval=0;
			err=setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&optval, sizeof(optval));
			if (err < 0){
				ortp_warning ("Fail to IPV6_V6ONLY: %s.",getSocketError());
			}
		}
211

Ghislain MARY's avatar
Ghislain MARY committed
212
#ifdef SO_TIMESTAMP
213
		optval=1;
Yann Diorcet's avatar
Yann Diorcet committed
214 215 216 217 218 219
		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());
		}
Ghislain MARY's avatar
Ghislain MARY committed
220
#endif
221
		err = 0;
222
		optval=1;
223 224 225 226
		switch (res->ai_family) {
			default:
			case AF_INET:
#ifdef IP_RECVTTL
Ghislain MARY's avatar
Ghislain MARY committed
227
				err = setsockopt(sock, IPPROTO_IP, IP_RECVTTL, (SOCKET_OPTION_VALUE)&optval, sizeof(optval));
228 229 230 231
#endif
				break;
			case AF_INET6:
#ifdef IPV6_RECVHOPLIMIT
Ghislain MARY's avatar
Ghislain MARY committed
232
				err = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (SOCKET_OPTION_VALUE)&optval, sizeof(optval));
233 234 235 236 237 238
#endif
				break;
		}
		if (err < 0) {
			ortp_warning("Fail to set recv TTL/HL socket option: %s.", getSocketError());
		}
aymeric's avatar
aymeric committed
239 240

		*sock_family=res->ai_family;
241
		err = bind(sock, res->ai_addr, (int)res->ai_addrlen);
smorlat's avatar
smorlat committed
242
		if (err != 0){
243
			ortp_error ("Fail to bind rtp socket to (addr=%s port=%i) : %s.", addr, *port, getSocketError());
244
			close_socket(sock);
smorlat's avatar
smorlat committed
245 246 247
			sock=-1;
			continue;
		}
248 249
		/*compatibility mode. New applications should use rtp_session_set_multicast_group() instead*/
		set_multicast_group(sock, addr);
aymeric's avatar
aymeric committed
250 251
		break;
	}
252
	memcpy(bound_addr,res0->ai_addr,res0->ai_addrlen);
253
	*bound_addr_len=(socklen_t)res0->ai_addrlen;
254

255
	bctbx_freeaddrinfo(res0);
aymeric's avatar
aymeric committed
256

257
#if defined(_WIN32) || defined(_WIN32_WCE)
258 259 260 261 262 263 264 265
	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
266
#endif
smorlat's avatar
smorlat committed
267
	if (sock!=-1){
aymeric's avatar
aymeric committed
268
		set_non_blocking_socket (sock);
269 270 271 272 273 274
		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());
275
				close_socket(sock);
276 277
				return (ortp_socket_t)-1;
			}
278
			err = bctbx_sockaddr_to_ip_address((struct sockaddr *)&saddr, slen, NULL, 0, port);
279
			if (err!=0){
280
				close_socket(sock);
281 282 283
				return (ortp_socket_t)-1;
			}
		}
284

aymeric's avatar
aymeric committed
285 286 287 288
	}
	return sock;
}

289
void _rtp_session_apply_socket_sizes(RtpSession * session){
aymeric's avatar
aymeric committed
290 291
	int err;
	bool_t done=FALSE;
292 293 294
	ortp_socket_t sock = session->rtp.gs.socket;
	unsigned int sndbufsz = session->rtp.snd_socket_size;
	unsigned int rcvbufsz = session->rtp.rcv_socket_size;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
295

296
	if (sock == (ortp_socket_t)-1) return;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
297

aymeric's avatar
aymeric committed
298 299
	if (sndbufsz>0){
#ifdef SO_SNDBUFFORCE
300
		err = setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&sndbufsz, sizeof(sndbufsz));
aymeric's avatar
aymeric committed
301
		if (err == -1) {
302
			ortp_warning("Fail to increase socket's send buffer size with SO_SNDBUFFORCE: %s.", getSocketError());
aymeric's avatar
aymeric committed
303 304 305
		}else done=TRUE;
#endif
		if (!done){
306
			err = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sndbufsz, sizeof(sndbufsz));
aymeric's avatar
aymeric committed
307 308 309 310 311 312 313 314
			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
315
		err = setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&rcvbufsz, sizeof(rcvbufsz));
aymeric's avatar
aymeric committed
316
		if (err == -1) {
317
			ortp_warning("Fail to increase socket's recv buffer size with SO_RCVBUFFORCE: %s.", getSocketError());
aymeric's avatar
aymeric committed
318 319 320
		}
#endif
		if (!done){
321
			err = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbufsz, sizeof(rcvbufsz));
aymeric's avatar
aymeric committed
322 323 324 325
			if (err == -1) {
				ortp_error("Fail to increase socket's recv buffer size with SO_RCVBUF: %s.", getSocketError());
			}
		}
326

aymeric's avatar
aymeric committed
327 328 329 330 331
	}
}

/**
 *rtp_session_set_local_addr:
332 333 334 335
 *@param session:		a rtp session freshly created.
 *@param addr:		a local IP address in the xxx.xxx.xxx.xxx form.
 *@param rtp_port:		a local port or -1 to let oRTP choose the port randomly
 *@param rtcp_port:		a local port or -1 to let oRTP choose the port randomly
aymeric's avatar
aymeric committed
336 337 338
 *
 *	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:
339 340
 *	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
341
 *	rtp_session_set_local_addr() is mandatory when the session is recv-only or duplex.
aymeric's avatar
aymeric committed
342 343 344 345 346
 *
 *	Returns: 0 on success.
**/

int
347
rtp_session_set_local_addr (RtpSession * session, const char * addr, int rtp_port, int rtcp_port)
aymeric's avatar
aymeric committed
348 349 350
{
	ortp_socket_t sock;
	int sockfamily;
351
	if (session->rtp.gs.socket!=(ortp_socket_t)-1){
aymeric's avatar
aymeric committed
352
		/* don't rebind, but close before*/
353
		_rtp_session_release_sockets(session, FALSE);
aymeric's avatar
aymeric committed
354 355
	}
	/* try to bind the rtp port */
356

357
	sock=create_and_bind(addr,&rtp_port,&sockfamily,session->reuseaddr,&session->rtp.gs.loc_addr,&session->rtp.gs.loc_addrlen);
smorlat's avatar
smorlat committed
358
	if (sock!=-1){
359 360 361
		session->rtp.gs.sockfamily=sockfamily;
		session->rtp.gs.socket=sock;
		session->rtp.gs.loc_port=rtp_port;
362
		_rtp_session_apply_socket_sizes(session);
aymeric's avatar
aymeric committed
363
		/*try to bind rtcp port */
364
		sock=create_and_bind(addr,&rtcp_port,&sockfamily,session->reuseaddr,&session->rtcp.gs.loc_addr,&session->rtcp.gs.loc_addrlen);
365
		if (sock!=(ortp_socket_t)-1){
366 367 368
			session->rtcp.gs.sockfamily=sockfamily;
			session->rtcp.gs.socket=sock;
			session->rtcp.gs.loc_port=rtcp_port;
369
		}else {
370
			ortp_error("Could not create and bind rtcp socket for session [%p]",session);
371
			return -1;
aymeric's avatar
aymeric committed
372
		}
373

aymeric's avatar
aymeric committed
374 375 376
		/* set socket options (but don't change chosen states) */
		rtp_session_set_multicast_ttl( session, -1 );
		rtp_session_set_multicast_loopback( session, -1 );
377
		if (session->use_pktinfo) rtp_session_set_pktinfo(session, TRUE);
378
		ortp_message("RtpSession bound to [%s] ports [%i] [%i]", addr, rtp_port, rtcp_port);
aymeric's avatar
aymeric committed
379 380
		return 0;
	}
381
	ortp_error("Could not bind RTP socket to %s on port %i for session [%p]",addr,rtp_port,session);
aymeric's avatar
aymeric committed
382 383 384
	return -1;
}

385 386
static void _rtp_session_recreate_sockets(RtpSession *session){
	char addr[NI_MAXHOST];
387 388
	int err = bctbx_sockaddr_to_ip_address((struct sockaddr *)&session->rtp.gs.loc_addr, session->rtp.gs.loc_addrlen, addr, sizeof(addr), NULL);
	if (err != 0) return;
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
	/*re create and bind sockets as they were done previously*/
	ortp_message("RtpSession %p is going to re-create its socket.", session);
	rtp_session_set_local_addr(session, addr, session->rtp.gs.loc_port, session->rtcp.gs.loc_port);
}

static void _rtp_session_check_socket_refresh(RtpSession *session){
	if (session->flags & RTP_SESSION_SOCKET_REFRESH_REQUESTED){
		session->flags &= ~RTP_SESSION_SOCKET_REFRESH_REQUESTED;
		_rtp_session_recreate_sockets(session);
	}
}

/**
 * Requests the session to re-create and bind its RTP and RTCP sockets same as they are currently.
 * This is used when a change in the routing rules of the host or process was made, in order to have
 * this routing rules change taking effect on the RTP/RTCP packets sent by the session.
**/
void rtp_session_refresh_sockets(RtpSession *session){
	if (session->rtp.gs.socket != (ortp_socket_t) -1){
		session->flags |= RTP_SESSION_SOCKET_REFRESH_REQUESTED;
	}
}
aymeric's avatar
aymeric committed
411

412 413 414 415 416 417 418 419 420 421 422
int rtp_session_join_multicast_group(RtpSession *session, const char *ip){
	int err;
	if (session->rtp.gs.socket==(ortp_socket_t)-1){
		ortp_error("rtp_session_set_multicast_group() must be done only on bound sockets, use rtp_session_set_local_addr() first");
		return -1;
	}
	err=set_multicast_group(session->rtp.gs.socket,ip);
	set_multicast_group(session->rtcp.gs.socket,ip);
	return err;
}

423 424
/**
 *rtp_session_set_pktinfo:
425 426
 *@param session: a rtp session
 *@param activate: activation flag (0 to deactivate, other value to activate)
427 428 429 430 431 432 433 434 435
 *
 * (De)activates packet info for incoming and outgoing packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_pktinfo(RtpSession *session, int activate)
{
	int retval;
436
	int optname;
437
#if defined(_WIN32) || defined(_WIN32_WCE)
438 439 440 441 442 443
	char optval[sizeof(DWORD)];
	int optlen = sizeof(optval);
#else
	int *optval = &activate;
	int optlen = sizeof(activate);
#endif
444
	session->use_pktinfo = activate;
445
	// Dont't do anything if socket hasn't been created yet
446
	if (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;
447

448
#if defined(_WIN32) || defined(_WIN32_WCE)
449 450 451
	memset(optval, activate, sizeof(optval));
#endif

452
#ifdef IP_PKTINFO
453
	optname = IP_PKTINFO;
454
#else
455
	optname = IP_RECVDSTADDR;
456
#endif
457 458
	retval = setsockopt(session->rtp.gs.socket, IPPROTO_IP, optname, optval, optlen);
	if (retval < 0) {
459
		ortp_warning ("Fail to set IPv4 packet info on RTP socket: %s.", getSocketError());
460 461 462
	}
	retval = setsockopt(session->rtcp.gs.socket, IPPROTO_IP, optname, optval, optlen);
	if (retval < 0) {
463
		ortp_warning ("Fail to set IPv4 packet info on RTCP socket: %s.", getSocketError());
464 465
	}

466
	if (session->rtp.gs.sockfamily != AF_INET) {
467
#if defined(_WIN32) || defined(_WIN32_WCE)
468
		memset(optval, activate, sizeof(optval));
469 470
#endif

471
#ifdef IPV6_RECVPKTINFO
472
		optname = IPV6_RECVPKTINFO;
473
#else
474
		optname = IPV6_RECVDSTADDR;
475
#endif
476 477 478 479 480 481 482 483
		retval = setsockopt(session->rtp.gs.socket, IPPROTO_IPV6, optname, optval, optlen);
		if (retval < 0) {
			ortp_warning("Fail to set IPv6 packet info on RTP socket: %s.", getSocketError());
		}
		retval = setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, optname, optval, optlen);
		if (retval < 0) {
			ortp_warning("Fail to set IPv6 packet info on RTCP socket: %s.", getSocketError());
		}
484 485 486 487 488 489
	}

	return retval;
}


aymeric's avatar
aymeric committed
490 491
/**
 *rtp_session_set_multicast_ttl:
492 493
 *@param session: a rtp session
 *@param ttl: desired Multicast Time-To-Live
aymeric's avatar
aymeric committed
494 495 496 497 498 499 500 501
 *
 * Sets the TTL (Time-To-Live) for outgoing multicast packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_multicast_ttl(RtpSession *session, int ttl)
{
502 503 504 505 506 507
	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
508
	if (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;
509

510
	switch (session->rtp.gs.sockfamily) {
511 512
		case AF_INET: {

513
			retval= setsockopt(session->rtp.gs.socket, IPPROTO_IP, IP_MULTICAST_TTL,
aymeric's avatar
aymeric committed
514
						 (SOCKET_OPTION_VALUE)  &session->multicast_ttl, sizeof(session->multicast_ttl));
515

aymeric's avatar
aymeric committed
516 517
			if (retval<0) break;

518
			retval= setsockopt(session->rtcp.gs.socket, IPPROTO_IP, IP_MULTICAST_TTL,
aymeric's avatar
aymeric committed
519 520
					 (SOCKET_OPTION_VALUE)	   &session->multicast_ttl, sizeof(session->multicast_ttl));

521 522
		} break;
		case AF_INET6: {
aymeric's avatar
aymeric committed
523

524
			retval= setsockopt(session->rtp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
aymeric's avatar
aymeric committed
525
					 (SOCKET_OPTION_VALUE)&session->multicast_ttl, sizeof(session->multicast_ttl));
526

aymeric's avatar
aymeric committed
527
			if (retval<0) break;
528

529
			retval= setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
aymeric's avatar
aymeric committed
530
					 (SOCKET_OPTION_VALUE) &session->multicast_ttl, sizeof(session->multicast_ttl));
531 532 533 534
		} break;
	default:
		retval=-1;
	}
aymeric's avatar
aymeric committed
535 536 537

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

aymeric's avatar
aymeric committed
539 540 541 542 543 544 545

	return retval;
}


/**
 *rtp_session_get_multicast_ttl:
546
 *@param session: a rtp session
aymeric's avatar
aymeric committed
547 548 549 550 551 552 553 554 555 556 557
 *
 * Returns the TTL (Time-To-Live) for outgoing multicast packets.
 *
**/
int rtp_session_get_multicast_ttl(RtpSession *session)
{
	return session->multicast_ttl;
}


/**
558
 *@param session: a rtp session
559
 *@param yesno: enable multicast loopback
aymeric's avatar
aymeric committed
560
 *
561
 * Enable multicast loopback.
aymeric's avatar
aymeric committed
562 563 564 565 566 567
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_multicast_loopback(RtpSession *session, int yesno)
{
568 569 570 571 572 573 574 575 576 577 578 579
	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
580
	if (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;
581

582
	switch (session->rtp.gs.sockfamily) {
583 584
		case AF_INET: {

585
			retval= setsockopt(session->rtp.gs.socket, IPPROTO_IP, IP_MULTICAST_LOOP,
aymeric's avatar
aymeric committed
586
						 (SOCKET_OPTION_VALUE)   &session->multicast_loopback, sizeof(session->multicast_loopback));
587

aymeric's avatar
aymeric committed
588 589
			if (retval<0) break;

590
			retval= setsockopt(session->rtcp.gs.socket, IPPROTO_IP, IP_MULTICAST_LOOP,
aymeric's avatar
aymeric committed
591 592
						 (SOCKET_OPTION_VALUE)   &session->multicast_loopback, sizeof(session->multicast_loopback));

593 594
		} break;
		case AF_INET6: {
aymeric's avatar
aymeric committed
595

596
			retval= setsockopt(session->rtp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
aymeric's avatar
aymeric committed
597
				 (SOCKET_OPTION_VALUE)	&session->multicast_loopback, sizeof(session->multicast_loopback));
598

aymeric's avatar
aymeric committed
599
			if (retval<0) break;
600

601
			retval= setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
aymeric's avatar
aymeric committed
602
				 (SOCKET_OPTION_VALUE)	&session->multicast_loopback, sizeof(session->multicast_loopback));
603 604 605 606
		} break;
	default:
		retval=-1;
	}
607

aymeric's avatar
aymeric committed
608 609 610 611 612 613 614 615 616
	if (retval<0)
		ortp_warning("Failed to set multicast loopback on socket.");

	return retval;
}


/**
 *rtp_session_get_multicast_loopback:
617
 *@param session: a rtp session
aymeric's avatar
aymeric committed
618 619 620 621 622 623 624 625 626 627 628
 *
 * 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:
629 630
 *@param session: a rtp session
 *@param dscp: desired DSCP PHB value
aymeric's avatar
aymeric committed
631 632 633 634 635 636 637 638 639
 *
 * 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
640 641
	int proto;
	int value_type;
aymeric's avatar
aymeric committed
642 643 644

	// Store new DSCP value if one is specified
	if (dscp>=0) session->dscp = dscp;
645

aymeric's avatar
aymeric committed
646
	// Don't do anything if socket hasn't been created yet
647
	if (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;
aymeric's avatar
aymeric committed
648

649
#if (_WIN32_WINNT >= 0x0600) && defined(ORTP_WINDOWS_DESKTOP)
650 651
	ortp_message("check OS support for qwave.lib");
	if (IsWindowsVistaOrGreater()) {
652 653 654 655 656 657 658 659
		if (session->dscp==0)
			tos=QOSTrafficTypeBestEffort;
		else if (session->dscp==0x8)
			tos=QOSTrafficTypeBackground;
		else if (session->dscp==0x28)
			tos=QOSTrafficTypeAudioVideo;
		else if (session->dscp==0x38)
			tos=QOSTrafficTypeVoice;
660
		else
661 662 663 664 665 666 667 668 669 670 671 672
			tos=QOSTrafficTypeExcellentEffort; /* 0x28 */

		if (session->rtp.QoSHandle==NULL) {
			QOS_VERSION version;
			BOOL QoSResult;

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

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

			if (QoSResult != TRUE){
673
				ortp_error("QOSCreateHandle failed to create handle with error %d", GetLastError());
674
				retval=-1;
Simon Morlat's avatar
Simon Morlat committed
675
			}
676 677 678 679 680 681 682 683 684 685 686 687
		}
		if (session->rtp.QoSHandle!=NULL) {
			BOOL QoSResult;
			QoSResult = QOSAddSocketToFlow(
				session->rtp.QoSHandle,
				session->rtp.gs.socket,
				(struct sockaddr*)&session->rtp.gs.rem_addr,
				tos,
				QOS_NON_ADAPTIVE_FLOW,
				&session->rtp.QoSFlowID);

			if (QoSResult != TRUE){
688
				ortp_error("QOSAddSocketToFlow failed to add a flow with error %d", GetLastError());
689
				retval=-1;
690 691 692 693 694 695
			}
		}
	} else {
#endif
		// DSCP value is in the upper six bits of the TOS field
		tos = (session->dscp << 2) & 0xFC;
696
		switch (session->rtp.gs.sockfamily) {
697
			case AF_INET:
Simon Morlat's avatar
Simon Morlat committed
698 699
				proto=IPPROTO_IP;
				value_type=IP_TOS;
700 701
			break;
		case AF_INET6:
Simon Morlat's avatar
Simon Morlat committed
702
			proto=IPPROTO_IPV6;
aymeric's avatar
aymeric committed
703
#	ifdef IPV6_TCLASS /*seems not defined by my libc*/
Simon Morlat's avatar
Simon Morlat committed
704
			value_type=IPV6_TCLASS;
aymeric's avatar
aymeric committed
705
#	else
Simon Morlat's avatar
Simon Morlat committed
706
			value_type=IP_TOS;
707 708 709
#	endif
			break;
		default:
Simon Morlat's avatar
Simon Morlat committed
710 711 712
			ortp_error("Cannot set DSCP because socket family is unspecified.");
			return -1;
		}
713
		retval = setsockopt(session->rtp.gs.socket, proto, value_type, (SOCKET_OPTION_VALUE)&tos, sizeof(tos));
Simon Morlat's avatar
Simon Morlat committed
714 715
		if (retval==-1)
			ortp_error("Fail to set DSCP value on rtp socket: %s",getSocketError());
716 717
		if (session->rtcp.gs.socket != (ortp_socket_t)-1){
			if (setsockopt(session->rtcp.gs.socket, proto, value_type, (SOCKET_OPTION_VALUE)&tos, sizeof(tos))==-1){
Simon Morlat's avatar
Simon Morlat committed
718 719
				ortp_error("Fail to set DSCP value on rtcp socket: %s",getSocketError());
			}
720
		}
721
#if (_WIN32_WINNT >= 0x0600) && defined(ORTP_WINDOWS_DESKTOP)
aymeric's avatar
aymeric committed
722
	}
723
#endif
aymeric's avatar
aymeric committed
724 725 726 727 728 729
	return retval;
}


/**
 *rtp_session_get_dscp:
730
 *@param session: a rtp session
aymeric's avatar
aymeric committed
731 732 733 734 735 736 737 738 739 740 741 742
 *
 * 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:
743
 *@param session:	a rtp session for which rtp_session_set_local_addr() or rtp_session_set_remote_addr() has been called
aymeric's avatar
aymeric committed
744
 *
745
 *	This function can be useful to retrieve the local port that was randomly choosen by
aymeric's avatar
aymeric committed
746 747 748 749 750 751
 *	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){
752
	return (session->rtp.gs.loc_port>0) ? session->rtp.gs.loc_port : -1;
aymeric's avatar
aymeric committed
753 754
}

755
int rtp_session_get_local_rtcp_port(const RtpSession *session){
756
	return (session->rtcp.gs.loc_port>0) ? session->rtcp.gs.loc_port : -1;
757 758
}

aymeric's avatar
aymeric committed
759 760
/**
 *rtp_session_set_remote_addr:
761
 *@param session:		a rtp session freshly created.
762 763
 *@param addr:		a remote IP address in the xxx.xxx.xxx.xxx form.
 *@param port:		a remote port.
aymeric's avatar
aymeric committed
764 765
 *
 *	Sets the remote address of the rtp session, ie the destination address where rtp packet
766
 *	are sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP
aymeric's avatar
aymeric committed
767 768 769 770 771 772
 *	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){
773
	return rtp_session_set_remote_addr_full(session, addr, port, addr, port+1);
aymeric's avatar
aymeric committed
774 775 776 777
}

/**
 *rtp_session_set_remote_addr_full:
778
 *@param session:		a rtp session freshly created.
779 780 781 782
 *@param rtp_addr:		a remote IP address in the xxx.xxx.xxx.xxx form.
 *@param rtp_port:		a remote rtp port.
 *@param rtcp_addr:		a remote IP address in the xxx.xxx.xxx.xxx form.
 *@param rtcp_port:		a remote rtcp port.
aymeric's avatar
aymeric committed
783 784
 *
 *	Sets the remote address of the rtp session, ie the destination address where rtp packet
785
 *	are sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP
aymeric's avatar
aymeric committed
786 787 788 789 790 791
 *	packets. Rtp packets that don't come from addr:port are discarded.
 *
 *	Returns: 0 on success.
**/

int
792 793 794 795 796 797
rtp_session_set_remote_addr_full (RtpSession * session, const char * rtp_addr, int rtp_port, const char * rtcp_addr, int rtcp_port){
	return _rtp_session_set_remote_addr_full(session,rtp_addr,rtp_port,rtcp_addr,rtcp_port,FALSE);
}

static int
_rtp_session_set_remote_addr_full (RtpSession * session, const char * rtp_addr, int rtp_port, const char * rtcp_addr, int rtcp_port, bool_t is_aux){
798 799
	char rtp_printable_addr[64];
	char rtcp_printable_addr[64];
aymeric's avatar
aymeric committed
800
	int err;
801
	struct addrinfo *res0, *res;
802 803 804 805 806
	struct sockaddr_storage *rtp_saddr=&session->rtp.gs.rem_addr;
	socklen_t *rtp_saddr_len=&session->rtp.gs.rem_addrlen;
	struct sockaddr_storage *rtcp_saddr=&session->rtcp.gs.rem_addr;
	socklen_t *rtcp_saddr_len=&session->rtcp.gs.rem_addrlen;
	OrtpAddress *aux_rtp=NULL,*aux_rtcp=NULL;
807

808 809 810 811 812 813 814 815
	if (is_aux){
		aux_rtp=ortp_malloc0(sizeof(OrtpAddress));
		rtp_saddr=&aux_rtp->addr;
		rtp_saddr_len=&aux_rtp->len;
		aux_rtcp=ortp_malloc0(sizeof(OrtpAddress));
		rtcp_saddr=&aux_rtcp->addr;
		rtcp_saddr_len=&aux_rtcp->len;
	}
816

817 818
	res0 = bctbx_name_to_addrinfo((session->rtp.gs.socket == -1) ? AF_UNSPEC : session->rtp.gs.sockfamily, SOCK_DGRAM, rtp_addr, rtp_port);
	if (res0 == NULL) {
819
		ortp_error("_rtp_session_set_remote_addr_full(): cannot set RTP destination to %s port %i.", rtp_addr, rtp_port);
820 821
		err=-1;
		goto end;
822 823
	} else {
		bctbx_addrinfo_to_printable_ip_address(res0, rtp_printable_addr, sizeof(rtp_printable_addr));
aymeric's avatar
aymeric committed
824
	}
825
	if (session->rtp.gs.socket == -1){
aymeric's avatar
aymeric committed
826 827 828 829
		/* 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)
830 831
			err = rtp_session_set_local_addr (session, "::", -1, -1);
		else err=rtp_session_set_local_addr (session, "0.0.0.0", -1, -1);
832 833 834 835
		if (err<0) {
			err=-1;
			goto end;
		}
aymeric's avatar
aymeric committed
836 837
	}

838
	err=-1;
aymeric's avatar
aymeric committed
839 840
	for (res = res0; res; res = res->ai_next) {
		/* set a destination address that has the same type as the local address */
841
		if (res->ai_family==session->rtp.gs.sockfamily ) {
842
			memcpy(rtp_saddr, res->ai_addr, res->ai_addrlen);
843
			*rtp_saddr_len=(socklen_t)res->ai_addrlen;
844 845
			err=0;
			break;
aymeric's avatar
aymeric committed
846 847
		}
	}
848
	bctbx_freeaddrinfo(res0);
aymeric's avatar
aymeric committed
849
	if (err) {
850
		ortp_warning("Could not set destination for RTP socket to %s:%i.",rtp_addr,rtp_port);
851
		goto end;
aymeric's avatar
aymeric committed
852
	}
853

854 855 856 857 858 859 860 861
	if ((rtcp_addr != NULL) && (rtcp_port > 0)) {
		res0 = bctbx_name_to_addrinfo((session->rtcp.gs.socket == -1) ? AF_UNSPEC : session->rtcp.gs.sockfamily, SOCK_DGRAM, rtcp_addr, rtcp_port);
		if (res0 == NULL) {
			ortp_error("_rtp_session_set_remote_addr_full(): cannot set RTCP destination to %s port %i.", rtcp_addr, rtcp_port);
			err=-1;
			goto end;
		} else {
			bctbx_addrinfo_to_printable_ip_address(res0, rtcp_printable_addr, sizeof(rtcp_printable_addr));
aymeric's avatar
aymeric committed
862
		}
863 864 865 866 867 868 869 870 871
		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->rtcp.gs.sockfamily ) {
				err=0;
				memcpy(rtcp_saddr, res->ai_addr, res->ai_addrlen);
				*rtcp_saddr_len=(socklen_t)res->ai_addrlen;
				break;
			}
aymeric's avatar
aymeric committed
872
		}
873 874 875 876
		bctbx_freeaddrinfo(res0);
		if (err) {
			ortp_warning("Could not set destination for RCTP socket to %s:%i.",rtcp_addr,rtcp_port);
			goto end;
aymeric's avatar
aymeric committed
877
		}
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898

		if (can_connect(session)){
			if (try_connect(session->rtp.gs.socket,(struct sockaddr*)&session->rtp.gs.rem_addr,session->rtp.gs.rem_addrlen))
				session->flags|=RTP_SOCKET_CONNECTED;
			if (session->rtcp.gs.socket!=(ortp_socket_t)-1){
				if (try_connect(session->rtcp.gs.socket,(struct sockaddr*)&session->rtcp.gs.rem_addr,session->rtcp.gs.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.gs.socket,&sa,sizeof(sa))<0){
				ortp_error("Cannot dissolve connect() association for rtp socket: %s", getSocketError());
			}
			if (connect(session->rtcp.gs.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;
aymeric's avatar
aymeric committed
899
		}
900 901 902 903

		ortp_message("RtpSession [%p] sending to rtp %s rtcp %s %s", session, rtp_printable_addr, rtcp_printable_addr, is_aux ? "as auxiliary destination" : "");
	} else {
		ortp_message("RtpSession [%p] sending to rtp %s %s", session, rtp_printable_addr, is_aux ? "as auxiliary destination" : "");
aymeric's avatar
aymeric committed
904
	}
905 906
	/*Apply DSCP setting. On windows the destination address is required for doing this.*/
	rtp_session_set_dscp(session, -1);
907
end:
908 909 910 911 912 913 914 915 916 917
	if (is_aux){
		if (err==-1){
			ortp_free(aux_rtp);
			ortp_free(aux_rtcp);
		}else{
			session->rtp.gs.aux_destinations=o_list_append(session->rtp.gs.aux_destinations,aux_rtp);
			session->rtcp.gs.aux_destinations=o_list_append(session->rtcp.gs.aux_destinations,aux_rtcp);
		}
	}
	return err;
aymeric's avatar
aymeric committed
918 919
}

920
int rtp_session_set_remote_addr_and_port(RtpSession * session, const char * addr, int rtp_port, int rtcp_port){
921
	return rtp_session_set_remote_addr_full(session,addr,rtp_port,addr,rtcp_port);
aymeric's avatar
aymeric committed
922 923
}

924 925
/**
 *rtp_session_add_remote_aux_addr_full:
926 927 928 929 930
 *@param session:		a rtp session freshly created.
 *@param rtp_addr:		a local IP address in the xxx.xxx.xxx.xxx form.
 *@param rtp_port:		a local rtp port.
 *@param rtcp_addr:		a local IP address in the xxx.xxx.xxx.xxx form.
 *@param rtcp_port:		a local rtcp port.
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
 *
 *	Add an auxiliary remote address for the rtp session, ie a destination address where rtp packet
 *	are sent.
 *
 *	Returns: 0 on success.
**/

int
rtp_session_add_aux_remote_addr_full(RtpSession * session, const char * rtp_addr, int rtp_port, const char * rtcp_addr, int rtcp_port){
	return _rtp_session_set_remote_addr_full(session,rtp_addr,rtp_port,rtcp_addr,rtcp_port,TRUE);
}

void rtp_session_clear_aux_remote_addr(RtpSession * session){
	ortp_stream_clear_aux_addresses(&session->rtp.gs);
	ortp_stream_clear_aux_addresses(&session->rtcp.gs);
}

void rtp_session_set_sockets(RtpSession *session, int rtpfd, int rtcpfd){
949 950
	if (rtpfd!=-1) set_non_blocking_socket(rtpfd);
	if (rtcpfd!=-1) set_non_blocking_socket(rtcpfd);
951 952
	session->rtp.gs.socket=rtpfd;
	session->rtcp.gs.socket=rtcpfd;
953
	if (rtpfd!=-1 || rtcpfd!=-1 )
aymeric's avatar
aymeric committed
954 955 956 957 958 959
		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)
{
960 961
	session->rtp.gs.tr = rtptr;
	session->rtcp.gs.tr = rtcptr;
aymeric's avatar
aymeric committed
962 963 964 965 966 967 968 969 970 971
	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);
}

972
void rtp_session_get_transports(const RtpSession *session, RtpTransport **rtptr, RtpTransport **rtcptr){
973 974
	if (rtptr) *rtptr=session->rtp.gs.tr;
	if (rtcptr) *rtcptr=session->rtcp.gs.tr;
975
}
aymeric's avatar
aymeric committed
976 977 978 979


/**
 *rtp_session_flush_sockets:
980
 *@param session: a rtp session
aymeric's avatar
aymeric committed
981 982 983 984 985 986 987 988
 *
 * 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){
989 990 991
	rtp_session_set_flag(session, RTP_SESSION_FLUSH);
	rtp_session_rtp_recv(session, 0);
	rtp_session_unset_flag(session, RTP_SESSION_FLUSH);
aymeric's avatar
aymeric committed
992 993 994
}


995
#ifdef USE_SENDMSG
aymeric's avatar
aymeric committed
996
#define MAX_IOV 30
997
static int rtp_sendmsg(int sock,mblk_t *m, const struct sockaddr *rem_addr, socklen_t addr_len){
aymeric's avatar
aymeric committed
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
	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;
}
1019
#endif
aymeric's avatar
aymeric committed
1020

Simon Morlat's avatar
Simon Morlat committed
1021
ortp_socket_t rtp_session_get_socket(RtpSession *session, bool_t is_rtp){
1022
	return is_rtp ? session->rtp.gs.socket : session->rtcp.gs.socket;
Simon Morlat's avatar
Simon Morlat committed
1023 1024
}

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
int _ortp_sendto(ortp_socket_t sockfd, mblk_t *m, int flags, const struct sockaddr *destaddr, socklen_t destlen){
	int error;
#ifdef USE_SENDMSG
	error=rtp_sendmsg(sockfd,m,destaddr,destlen);
#else
	if (m->b_cont!=NULL)
		msgpullup(m,-1);
	error = sendto (sockfd, (char*)m->b_rptr, (int) (m->b_wptr - m->b_rptr),
		0,destaddr,destlen);
#endif
	return error;
}
aymeric's avatar
aymeric committed
1037

1038
int rtp_session_sendto(RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, const struct sockaddr *destaddr, socklen_t destlen){
1039
	int ret;
1040

1041
	_rtp_session_check_socket_refresh(session);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
1042

1043 1044
	if (session->net_sim_ctx && (session->net_sim_ctx->params.mode==OrtpNetworkSimulatorOutbound
			|| session->net_sim_ctx->params.mode==OrtpNetworkSimulatorOutboundControlled)){
1045
		ret=(int)msgdsize(m);
1046 1047 1048 1049 1050 1051 1052 1053
		m=dupmsg(m);
		memcpy(&m->net_addr,destaddr,destlen);
		m->net_addrlen=destlen;
		m->reserved1=is_rtp;
		ortp_mutex_lock(&session->net_sim_ctx->mutex);
		putq(&session->net_sim_ctx->send_q, m);
		ortp_mutex_unlock(&session->net_sim_ctx->mutex);
	}else{
Simon Morlat's avatar
Simon Morlat committed
1054
		ortp_socket_t sockfd = rtp_session_get_socket(session, is_rtp);
1055 1056 1057 1058 1059
		ret=_ortp_sendto(sockfd, m, flags, destaddr, destlen);
	}
	return ret;
}

1060 1061 1062 1063 1064 1065 1066 1067 1068
static const ortp_recv_addr_t * lookup_recv_addr(RtpSession *session, struct sockaddr *from, socklen_t fromlen) {
	const ortp_recv_addr_t *result = NULL;
	bctbx_list_t *iterator = session->recv_addr_map;
	while (iterator != NULL) {
		ortp_recv_addr_map_t *item = (ortp_recv_addr_map_t *)bctbx_list_get_data(iterator);
		uint64_t curtime = ortp_get_cur_time_ms();
		if ((curtime - item->ts) > 2000) {
			bctbx_list_t *to_remove = iterator;
			iterator = bctbx_list_next(iterator);
Simon Morlat's avatar
Simon Morlat committed
1069
			session->recv_addr_map = bctbx_list_erase_link(session->recv_addr_map, to_remove);
1070 1071 1072 1073 1074 1075 1076 1077
		} else {
			if (memcmp(&item->ss, from, fromlen) == 0) result = &item->recv_addr;
			iterator = bctbx_list_next(iterator);
		}
	}
	return result;
}

1078
static const ortp_recv_addr_t * get_recv_addr(RtpSession *session, struct sockaddr *from, socklen_t fromlen) {
1079 1080 1081 1082 1083 1084 1085
	char result[NI_MAXHOST] = { 0 };
	char dest[NI_MAXHOST] = { 0 };
	struct addrinfo *ai = NULL;
	int port = 0;
	int family = from->sa_family;
	int err;
	err = bctbx_sockaddr_to_ip_address(from, fromlen, dest, sizeof(dest), &port);
1086 1087
	if (err != 0) {
		ortp_error("bctbx_sockaddr_to_ip_address failed");
1088 1089
		return NULL;
	}
1090 1091 1092
	err = bctbx_get_local_ip_for(family, dest, port, result, sizeof(result));
	if (err != 0) {
		ortp_error("bctbx_get_local_ip_for failed: dest=%s, port=%d", dest, port);
1093 1094
		return NULL;
	}
1095 1096 1097 1098 1099
	ai = bctbx_ip_address_to_addrinfo(family, SOCK_DGRAM, result, port);
	if (ai == NULL) {
		ortp_error("bctbx_ip_address_to_addrinfo failed: result=%s, port=%d", result, port);
		return NULL;
	} else {
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
		ortp_recv_addr_map_t *item = bctbx_new0(ortp_recv_addr_map_t, 1);
		memcpy(&item->ss, from, fromlen);
		item->recv_addr.family = family;
		if (family == AF_INET) {
			memcpy(&item->recv_addr.addr.ipi_addr, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(item->recv_addr.addr.ipi_addr));
		} else if (family == AF_INET6) {
			memcpy(&item->recv_addr.addr.ipi6_addr, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, sizeof(item->recv_addr.addr.ipi6_addr));
		}
		bctbx_freeaddrinfo(ai);
		item->ts = ortp_get_cur_time_ms();
		session->recv_addr_map = bctbx_list_append(session->recv_addr_map, item);
		return &item->recv_addr;
	}
}

1115
int rtp_session_recvfrom(RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen) {
1116
	int ret = rtp_session_rtp_recv_abstract(is_rtp ? session->rtp.gs.socket : session->rtcp.gs.socket, m, flags, from, fromlen);
1117 1118 1119 1120 1121 1122
	if ((ret >= 0) && (session->use_pktinfo == TRUE)) {
		if (m->recv_addr.family == AF_UNSPEC) {
			/* The receive address has not been filled, this typically happens on Mac OS X when receiving an IPv4 packet on
			 * a dual stack socket. Try to guess the address because it is mandatory for ICE. */
			const ortp_recv_addr_t *recv_addr = lookup_recv_addr(session, from, *fromlen);
			if (recv_addr == NULL) {
1123
				recv_addr = get_recv_addr(session, from, *fromlen);
1124 1125 1126
			}
			if (recv_addr != NULL) {
				memcpy(&m->recv_addr, recv_addr, sizeof(ortp_recv_addr_t));
1127 1128
			} else {
				ortp_error("Did not succeed to fill the receive address, this should not happen! [family=%d, len=%d]", from->sa_family, (int)*fromlen);
1129 1130
			}
		}
1131 1132 1133 1134
		/* Store the local port in the recv_addr of the mblk_t, the address is already filled in rtp_session_rtp_recv_abstract */
		m->recv_addr.port = htons(is_rtp ? session->rtp.gs.loc_port : session->rtcp.gs.loc_port);
	}
	return ret;
1135 1136
}

1137
void update_sent_bytes(OrtpStream *os, int nbytes) {
1138
	int overhead = ortp_stream_is_ipv6(os) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
1139 1140
	if ((os->sent_bytes == 0) && (os->send_bw_start.tv_sec == 0) && (os->send_bw_start.tv_usec == 0)) {
		/* Initialize bandwidth computing time when has not been started yet. */
1141
		ortp_gettimeofday(&os->send_bw_start, NULL);
aymeric's avatar
aymeric committed
1142
	}
1143
	os->sent_bytes += nbytes + overhead;
aymeric's avatar
aymeric committed
1144 1145
}

1146
static void update_recv_bytes(OrtpStream *os, size_t nbytes, const struct timeval *recv_time) {
1147
	int overhead = ortp_stream_is_ipv6(os) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
1148
	if ((os->recv_bytes == 0) && (os->recv_bw_start.tv_sec == 0) && (os->recv_bw_start.tv_usec == 0)) {
1149
		ortp_gettimeofday(&os->recv_bw_start, NULL);
aymeric's avatar
aymeric committed
1150
	}
1151
	os->recv_bytes += (unsigned int)(nbytes + overhead);
1152
	ortp_bw_estimator_packet_received(&os->recv_bw_estimator, nbytes + overhead, recv_time);
aymeric's avatar
aymeric committed
1153 1154
}

1155
static void log_send_error(RtpSession *session, const char *type, mblk_t *m, struct sockaddr *destaddr, socklen_t destlen){
1156
	char printable_ip_address[65]={0};
1157 1158
	int errnum = getSocketErrorCode();
	const char *errstr = getSocketError();
1159
	bctbx_sockaddr_to_printable_ip_address(destaddr, destlen, printable_ip_address, sizeof(printable_ip_address));
1160 1161
	ortp_error ("RtpSession [%p] error sending [%s] packet [%p] to %s: %s [%d]",
		session, type, m, printable_ip_address, errstr, errnum);
1162
}
1163

1164
static int rtp_session_rtp_sendto(RtpSession * session, mblk_t * m, struct sockaddr *destaddr, socklen_t destlen, bool_t is_aux){
aymeric's avatar
aymeric committed
1165
	int error;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
1166

1167 1168 1169
	if (rtp_session_using_transport(session, rtp)){
		error = (session->rtp.gs.tr->t_sendto) (session->rtp.gs.tr,m,0,destaddr,destlen);
	}else{
1170
		error=rtp_session_sendto(session, TRUE,m,0,destaddr,destlen);
1171 1172 1173 1174 1175
	}
	if (!is_aux){
		/*errors to auxiliary destinations are not notified*/
		if (error < 0){
			if (session->on_network_error.count>0){
Ghislain MARY's avatar
Ghislain MARY committed
1176
				rtp_signal_table_emit3(&session->on_network_error,"Error sending RTP packet",ORTP_INT_TO_POINTER(getSocketErrorCode()));
1177 1178 1179 1180 1181 1182 1183 1184 1185
			}else log_send_error(session,"rtp",m,destaddr,destlen);
			session->rtp.send_errno=getSocketErrorCode();
		}else{
			update_sent_bytes(&session->rtp.gs, error);
		}
	}
	return error;
}

1186
int rtp_session_rtp_send (RtpSession * session, mblk_t * m){
1187
	int error=0;
aymeric's avatar
aymeric committed
1188 1189
	int i;
	rtp_header_t *hdr;
1190 1191
	struct sockaddr *destaddr=(struct sockaddr*)&session->rtp.gs.rem_addr;
	socklen_t destlen=session->rtp.gs.rem_addrlen;
1192
	OList *elem=NULL;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
1193

1194 1195 1196 1197
	if (session->is_spliced) {
		freemsg(m);
		return 0;
	}
1198

aymeric's avatar
aymeric committed
1199
	hdr = (rtp_header_t *) m->b_rptr;
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
	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
1210 1211 1212 1213 1214

	if (session->flags & RTP_SOCKET_CONNECTED) {
		destaddr=NULL;
		destlen=0;
	}
1215
	/*first send to main destination*/
1216
	error=rtp_session_rtp_sendto(session,m,destaddr,destlen,FALSE);
1217 1218 1219 1220 1221 1222 1223 1224
	/*then iterate over auxiliary destinations*/
	for(elem=session->rtp.gs.aux_destinations;elem!=NULL;elem=elem->next){
		OrtpAddress *addr=(OrtpAddress*)elem->data;
		rtp_session_rtp_sendto(session,m,(struct sockaddr*)&addr->addr,addr->len,TRUE);
	}
	freemsg(m);
	return error;
}
aymeric's avatar
aymeric committed
1225

1226 1227
static int rtp_session_rtcp_sendto(RtpSession * session, mblk_t * m, struct sockaddr *destaddr, socklen_t destlen, bool_t is_aux){
	int error=0;
1228

1229 1230
	/* Even in RTCP mux, we send through the RTCP RtpTransport, which will itself take in charge to do the sending of the packet
	 * through the RTP endpoint*/
1231
	if (rtp_session_using_transport(session, rtcp)){
1232
		error = (session->rtcp.gs.tr->t_sendto) (session->rtcp.gs.tr, m, 0, destaddr, destlen);
1233
	}else{
1234
		error=_ortp_sendto(rtp_session_get_socket(session, session->rtcp_mux),m,0,destaddr,destlen);
aymeric's avatar
aymeric committed
1235
	}
1236

1237 1238 1239
	if (!is_aux){
		if (error < 0){
			if (session->on_network_error.count>0){
Ghislain MARY's avatar
Ghislain MARY committed
1240
				rtp_signal_table_emit3(&session->on_network_error,"Error sending RTCP packet",ORTP_INT_TO_POINTER(getSocketErrorCode()));
1241 1242 1243 1244 1245 1246 1247
			}else{
				log_send_error(session,"rtcp",m,destaddr,destlen);
			}
		} else {
			update_sent_bytes(&session->rtcp.gs, error);
			update_avg_rtcp_size(session, error);
		}
aymeric's avatar
aymeric committed
1248 1249 1250 1251 1252
	}
	return error;
}

int
1253
rtp_session_rtcp_send (RtpSession * session, mblk_t * m){