rtpsession_inet.c 63.4 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
{
François Grisez's avatar
François Grisez committed
349 350 351
	ortp_socket_t sock;
	int sockfamily;

352 353 354
	// Stop async rtp recv thread before recreating the socket
	rtp_session_reset_recvfrom(session);

355
	if (session->rtp.gs.socket!=(ortp_socket_t)-1){
aymeric's avatar
aymeric committed
356
		/* don't rebind, but close before*/
357
		_rtp_session_release_sockets(session, FALSE);
aymeric's avatar
aymeric committed
358 359
	}
	/* try to bind the rtp port */
360

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

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

389 390
static void _rtp_session_recreate_sockets(RtpSession *session){
	char addr[NI_MAXHOST];
391 392
	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;
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
	/*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
415

416 417 418 419 420 421 422 423 424 425 426
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;
}

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

452
#if defined(_WIN32) || defined(_WIN32_WCE)
453 454 455
	memset(optval, activate, sizeof(optval));
#endif

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

470
	if (session->rtp.gs.sockfamily != AF_INET) {
471
#if defined(_WIN32) || defined(_WIN32_WCE)
472
		memset(optval, activate, sizeof(optval));
473 474
#endif

475
#ifdef IPV6_RECVPKTINFO
476
		optname = IPV6_RECVPKTINFO;
477
#else
478
		optname = IPV6_RECVDSTADDR;
479
#endif
480 481 482 483 484 485 486 487
		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());
		}
488 489 490 491 492 493
	}

	return retval;
}


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

514
	switch (session->rtp.gs.sockfamily) {
515 516
		case AF_INET: {

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

aymeric's avatar
aymeric committed
520 521
			if (retval<0) break;

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

525 526
		} break;
		case AF_INET6: {
aymeric's avatar
aymeric committed
527

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

aymeric's avatar
aymeric committed
531
			if (retval<0) break;
532

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

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

aymeric's avatar
aymeric committed
543 544 545 546 547 548 549

	return retval;
}


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


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

586
	switch (session->rtp.gs.sockfamily) {
587 588
		case AF_INET: {

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

aymeric's avatar
aymeric committed
592 593
			if (retval<0) break;

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

597 598
		} break;
		case AF_INET6: {
aymeric's avatar
aymeric committed
599

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

aymeric's avatar
aymeric committed
603
			if (retval<0) break;
604

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

aymeric's avatar
aymeric committed
612 613 614 615 616 617 618 619 620
	if (retval<0)
		ortp_warning("Failed to set multicast loopback on socket.");

	return retval;
}


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

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

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

653
#if (_WIN32_WINNT >= 0x0600) && defined(ORTP_WINDOWS_DESKTOP)
654 655
	ortp_message("check OS support for qwave.lib");
	if (IsWindowsVistaOrGreater()) {
656 657 658 659 660 661 662 663
		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;
664
		else
665 666 667 668 669 670 671 672 673 674 675 676
			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){
677
				ortp_error("QOSCreateHandle failed to create handle with error %d", GetLastError());
678
				retval=-1;
Simon Morlat's avatar
Simon Morlat committed
679
			}
680 681 682 683 684 685 686 687 688 689 690 691
		}
		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){
692
				ortp_error("QOSAddSocketToFlow failed to add a flow with error %d", GetLastError());
693
				retval=-1;
694 695 696 697 698 699
			}
		}
	} else {
#endif
		// DSCP value is in the upper six bits of the TOS field
		tos = (session->dscp << 2) & 0xFC;
700
		switch (session->rtp.gs.sockfamily) {
701
			case AF_INET:
Simon Morlat's avatar
Simon Morlat committed
702 703
				proto=IPPROTO_IP;
				value_type=IP_TOS;
704 705
			break;
		case AF_INET6:
Simon Morlat's avatar
Simon Morlat committed
706
			proto=IPPROTO_IPV6;
aymeric's avatar
aymeric committed
707
#	ifdef IPV6_TCLASS /*seems not defined by my libc*/
Simon Morlat's avatar
Simon Morlat committed
708
			value_type=IPV6_TCLASS;
aymeric's avatar
aymeric committed
709
#	else
Simon Morlat's avatar
Simon Morlat committed
710
			value_type=IP_TOS;
711 712 713
#	endif
			break;
		default:
Simon Morlat's avatar
Simon Morlat committed
714 715 716
			ortp_error("Cannot set DSCP because socket family is unspecified.");
			return -1;
		}
717
		retval = setsockopt(session->rtp.gs.socket, proto, value_type, (SOCKET_OPTION_VALUE)&tos, sizeof(tos));
Simon Morlat's avatar
Simon Morlat committed
718 719
		if (retval==-1)
			ortp_error("Fail to set DSCP value on rtp socket: %s",getSocketError());
720 721
		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
722 723
				ortp_error("Fail to set DSCP value on rtcp socket: %s",getSocketError());
			}
724
		}
725
#if (_WIN32_WINNT >= 0x0600) && defined(ORTP_WINDOWS_DESKTOP)
aymeric's avatar
aymeric committed
726
	}
727
#endif
aymeric's avatar
aymeric committed
728 729 730 731 732 733
	return retval;
}


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

759
int rtp_session_get_local_rtcp_port(const RtpSession *session){
760
	return (session->rtcp.gs.loc_port>0) ? session->rtcp.gs.loc_port : -1;
761 762
}

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

/**
 *rtp_session_set_remote_addr_full:
782
 *@param session:		a rtp session freshly created.
783 784 785 786
 *@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
787 788
 *
 *	Sets the remote address of the rtp session, ie the destination address where rtp packet
789
 *	are sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP
aymeric's avatar
aymeric committed
790 791 792 793 794 795
 *	packets. Rtp packets that don't come from addr:port are discarded.
 *
 *	Returns: 0 on success.
**/

int
796 797 798 799 800 801
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){
802 803
	char rtp_printable_addr[64];
	char rtcp_printable_addr[64];
aymeric's avatar
aymeric committed
804
	int err;
805
	struct addrinfo *res0, *res;
806 807 808 809 810
	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;
811

812 813 814 815 816 817 818 819
	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;
	}
820

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

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

858 859 860 861 862 863 864 865
	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
866
		}
867 868 869 870 871 872 873 874 875
		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
876
		}
877 878 879 880
		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
881
		}
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902

		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
903
		}
904 905 906 907

		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
908
	}
909 910
	/*Apply DSCP setting. On windows the destination address is required for doing this.*/
	rtp_session_set_dscp(session, -1);
911
end:
912 913 914 915 916 917 918 919 920 921
	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
922 923
}

924
int rtp_session_set_remote_addr_and_port(RtpSession * session, const char * addr, int rtp_port, int rtcp_port){
925
	return rtp_session_set_remote_addr_full(session,addr,rtp_port,addr,rtcp_port);
aymeric's avatar
aymeric committed
926 927
}

928 929
/**
 *rtp_session_add_remote_aux_addr_full:
930 931 932 933 934
 *@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.
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
 *
 *	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){
953 954
	if (rtpfd!=-1) set_non_blocking_socket(rtpfd);
	if (rtcpfd!=-1) set_non_blocking_socket(rtcpfd);
955 956
	session->rtp.gs.socket=rtpfd;
	session->rtcp.gs.socket=rtcpfd;
957
	if (rtpfd!=-1 || rtcpfd!=-1 )
aymeric's avatar
aymeric committed
958 959 960 961 962 963
		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)
{
964 965
	session->rtp.gs.tr = rtptr;
	session->rtcp.gs.tr = rtcptr;
aymeric's avatar
aymeric committed
966 967 968 969 970 971 972 973 974 975
	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);
}

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


/**
 *rtp_session_flush_sockets:
984
 *@param session: a rtp session
aymeric's avatar
aymeric committed
985 986 987 988 989 990 991 992
 *
 * 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){
993 994 995
	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
996 997 998
}


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

Simon Morlat's avatar
Simon Morlat committed
1025
ortp_socket_t rtp_session_get_socket(RtpSession *session, bool_t is_rtp){
1026
	return is_rtp ? session->rtp.gs.socket : session->rtcp.gs.socket;
Simon Morlat's avatar
Simon Morlat committed
1027 1028
}

1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
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
1041

1042
int rtp_session_sendto(RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, const struct sockaddr *destaddr, socklen_t destlen){
1043
	int ret;
1044

1045
	_rtp_session_check_socket_refresh(session);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
1046

1047 1048
	if (session->net_sim_ctx && (session->net_sim_ctx->params.mode==OrtpNetworkSimulatorOutbound
			|| session->net_sim_ctx->params.mode==OrtpNetworkSimulatorOutboundControlled)){
1049
		ret=(int)msgdsize(m);
1050 1051 1052 1053 1054 1055 1056 1057
		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
1058
		ortp_socket_t sockfd = rtp_session_get_socket(session, is_rtp);
1059 1060 1061 1062 1063
		ret=_ortp_sendto(sockfd, m, flags, destaddr, destlen);
	}
	return ret;
}

1064 1065 1066 1067 1068 1069 1070 1071 1072
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
1073
			session->recv_addr_map = bctbx_list_erase_link(session->recv_addr_map, to_remove);
1074 1075 1076 1077 1078 1079 1080 1081
		} else {
			if (memcmp(&item->ss, from, fromlen) == 0) result = &item->recv_addr;
			iterator = bctbx_list_next(iterator);
		}
	}
	return result;
}

1082
static const ortp_recv_addr_t * get_recv_addr(RtpSession *session, struct sockaddr *from, socklen_t fromlen) {
1083 1084 1085 1086 1087 1088 1089
	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);
1090 1091
	if (err != 0) {
		ortp_error("bctbx_sockaddr_to_ip_address failed");
1092 1093
		return NULL;
	}
1094 1095 1096
	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);
1097 1098
		return NULL;
	}
1099 1100 1101 1102 1103
	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 {
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
		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;
	}
}

1119
int rtp_session_recvfrom(RtpSession *session, bool_t is_rtp, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen) {
1120
	int ret = rtp_session_rtp_recv_abstract(is_rtp ? session->rtp.gs.socket : session->rtcp.gs.socket, m, flags, from, fromlen);
1121 1122 1123 1124 1125 1126
	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) {
1127
				recv_addr = get_recv_addr(session, from, *fromlen);
1128 1129 1130
			}
			if (recv_addr != NULL) {
				memcpy(&m->recv_addr, recv_addr, sizeof(ortp_recv_addr_t));
1131 1132
			} else {
				ortp_error("Did not succeed to fill the receive address, this should not happen! [family=%d, len=%d]", from->sa_family, (int)*fromlen);
1133 1134
			}
		}
1135 1136 1137 1138
		/* 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;
1139 1140
}

1141
void update_sent_bytes(OrtpStream *os, int nbytes) {
1142
	int overhead = ortp_stream_is_ipv6(os) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
1143 1144
	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. */
1145
		ortp_gettimeofday(&os->send_bw_start, NULL);
aymeric's avatar
aymeric committed
1146
	}
1147
	os->sent_bytes += nbytes + overhead;
aymeric's avatar
aymeric committed
1148 1149
}

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

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

1168
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
1169
	int error;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
1170

1171 1172 1173
	if (rtp_session_using_transport(session, rtp)){
		error = (session->rtp.gs.tr->t_sendto) (session->rtp.gs.tr,m,0,destaddr,destlen);
	}else{
1174
		error=rtp_session_sendto(session, TRUE,m,0,destaddr,destlen);
1175 1176 1177 1178 1179
	}
	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
1180
				rtp_signal_table_emit3(&session->on_network_error,"Error sending RTP packet",ORTP_INT_TO_POINTER(getSocketErrorCode()));
1181 1182 1183 1184 1185 1186 1187 1188 1189
			}else log_send_error(session,"rtp",m,destaddr,destlen);
			session->rtp.send_errno=getSocketErrorCode();
		}else{
			update_sent_bytes(&session->rtp.gs, error);
		}
	}
	return error;
}

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

1198 1199 1200 1201
	if (session->is_spliced) {
		freemsg(m);
		return 0;
	}
1202

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

	if (session->flags & RTP_SOCKET_CONNECTED) {
		destaddr=NULL;
		destlen=0;
	}
1219
	/*first send to main destination*/
1220
	error=rtp_session_rtp_sendto(session,m,destaddr,destlen,FALSE);
1221 1222 1223 1224 1225 1226 1227 1228
	/*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
1229

1230 1231
static int rtp_session_rtcp_sendto(RtpSession * session, mblk_t * m, struct sockaddr *destaddr, socklen_t destlen, bool_t is_aux){
	int error=0;
1232

1233 1234
	/* 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*/
1235
	if (rtp_session_using_transport(session, rtcp)){
1236
		error = (session->rtcp.gs.tr->t_sendto) (session->rtcp.gs.tr, m, 0, destaddr, destlen);
1237
	}else{
1238
		error=_ortp_sendto(rtp_session_get_socket(session, session->rtcp_mux),m,0,destaddr,destlen);
aymeric's avatar
aymeric committed
1239
	}
1240

1241 1242 1243
	if (!is_aux){
		if (error < 0){
			if (session->on_network_error.count>0){
Ghislain MARY's avatar
Ghislain MARY committed
1244
				rtp_signal_table_emit3(&session->on_network_error,"Error sending RTCP packet",ORTP_INT_TO_POINTER(getSocketErrorCode()));
1245 1246 1247 1248 1249 1250 1251
			}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
1252 1253 </