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

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

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

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


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

25
#ifdef HAVE_CONFIG_H
26
#include "ortp-config.h" /*needed for HAVE_SYS_UIO_H and HAVE_ARC4RANDOM */
aymeric's avatar
aymeric committed
27
#endif
28 29 30 31
#include "ortp/ortp.h"
#include "utils.h"
#include "ortp/rtpsession.h"
#include "rtpsession_priv.h"
aymeric's avatar
aymeric committed
32

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

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

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

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

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

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

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

86 87 88 89
#ifndef IN6_IS_ADDR_MULTICAST
#define IN6_IS_ADDR_MULTICAST(i)	(((uint8_t *) (i))[0] == 0xff)
#endif

90 91 92
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);

aymeric's avatar
aymeric committed
93 94 95 96 97 98 99 100
static bool_t try_connect(int fd, const struct sockaddr *dest, socklen_t addrlen){
	if (connect(fd,dest,addrlen)<0){
		ortp_warning("Could not connect() socket: %s",getSocketError());
		return FALSE;
	}
	return TRUE;
}

101
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
102 103
	int err;
	int optval = 1;
smorlat's avatar
smorlat committed
104
	ortp_socket_t sock=-1;
aymeric's avatar
aymeric committed
105 106
	char num[8];
	struct addrinfo hints, *res0, *res;
107

108 109
	if (*port==-1) *port=0;
	if (*port==0) reuse_addr=FALSE;
110

aymeric's avatar
aymeric committed
111
	memset(&hints, 0, sizeof(hints));
Simon Morlat's avatar
Simon Morlat committed
112
	hints.ai_family = AF_UNSPEC;
aymeric's avatar
aymeric committed
113
	hints.ai_socktype = SOCK_DGRAM;
114

115
	snprintf(num, sizeof(num), "%d",*port);
aymeric's avatar
aymeric committed
116 117
	err = getaddrinfo(addr,num, &hints, &res0);
	if (err!=0) {
118
		ortp_warning ("Error in getaddrinfo on (addr=%s port=%i): %s", addr, *port, gai_strerror(err));
aymeric's avatar
aymeric committed
119 120
		return -1;
	}
121

aymeric's avatar
aymeric committed
122 123
	for (res = res0; res; res = res->ai_next) {
		sock = socket(res->ai_family, res->ai_socktype, 0);
smorlat's avatar
smorlat committed
124 125 126
		if (sock==-1)
			continue;

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

		*sock_family=res->ai_family;
		err = bind (sock, res->ai_addr, res->ai_addrlen);
smorlat's avatar
smorlat committed
163
		if (err != 0){
164
			ortp_error ("Fail to bind rtp socket to (addr=%s port=%i) : %s.", addr, *port, getSocketError());
smorlat's avatar
smorlat committed
165 166 167 168
			close_socket (sock);
			sock=-1;
			continue;
		}
aymeric's avatar
aymeric committed
169 170
#ifndef __hpux
		switch (res->ai_family)
171 172 173
		{
		case AF_INET:
			if (IN_MULTICAST(ntohl(((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr)))
aymeric's avatar
aymeric committed
174
			{
175 176 177 178 179 180 181 182 183
				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());
					close_socket (sock);
					sock=-1;
					continue;
184 185
				} else {
					ortp_message ("RTP socket [%i] has joined address group [%s]",sock, addr);
186
				}
aymeric's avatar
aymeric committed
187
			}
188 189 190
		break;
		case AF_INET6:
			if IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr))
aymeric's avatar
aymeric committed
191
			{
192 193 194 195 196
				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)
197
				{
198 199 200 201
					ortp_warning ("Fail to join address group: %s.", getSocketError());
					close_socket (sock);
					sock=-1;
					continue;
202 203
				} else {
					ortp_message ("RTP socket 6 [%i] has joined address group [%s]",sock, addr);
204
				}
aymeric's avatar
aymeric committed
205
			}
206 207
		break;
		}
aymeric's avatar
aymeric committed
208 209 210
#endif /*hpux*/
		break;
	}
211 212 213
	memcpy(bound_addr,res0->ai_addr,res0->ai_addrlen);
	*bound_addr_len=res0->ai_addrlen;

aymeric's avatar
aymeric committed
214 215
	freeaddrinfo(res0);

216 217 218 219 220 221 222 223 224
#if defined(WIN32) || defined(_WIN32_WCE)
	if (ortp_WSARecvMsg == NULL) {
		GUID guid = WSAID_WSARECVMSG;
		DWORD bytes_returned;
		if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid),
			&ortp_WSARecvMsg, sizeof(ortp_WSARecvMsg), &bytes_returned, NULL, NULL) == SOCKET_ERROR) {
			ortp_warning("WSARecvMsg function not found.");
		}
	}
aymeric's avatar
aymeric committed
225
#endif
smorlat's avatar
smorlat committed
226
	if (sock!=-1){
aymeric's avatar
aymeric committed
227
		set_non_blocking_socket (sock);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
		if (*port==0){
			struct sockaddr_storage saddr;
			socklen_t slen=sizeof(saddr);
			err=getsockname(sock,(struct sockaddr*)&saddr,&slen);
			if (err==-1){
				ortp_error("getsockname(): %s",getSocketError());
				close(sock);
				return (ortp_socket_t)-1;
			}
			err=getnameinfo((struct sockaddr*)&saddr, slen, NULL, 0, num, sizeof(num), NI_NUMERICHOST | NI_NUMERICSERV);
			if (err!=0){
				ortp_error("getnameinfo(): %s",gai_strerror(err));
				close(sock);
				return (ortp_socket_t)-1;
			}
			*port=atoi(num);
		}
245

aymeric's avatar
aymeric committed
246 247 248 249 250 251 252 253 254
	}
	return sock;
}

static void set_socket_sizes(int sock, unsigned int sndbufsz, unsigned int rcvbufsz){
	int err;
	bool_t done=FALSE;
	if (sndbufsz>0){
#ifdef SO_SNDBUFFORCE
255
		err = setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&sndbufsz, sizeof(sndbufsz));
aymeric's avatar
aymeric committed
256 257 258 259 260
		if (err == -1) {
			ortp_error("Fail to increase socket's send buffer size with SO_SNDBUFFORCE: %s.", getSocketError());
		}else done=TRUE;
#endif
		if (!done){
261
			err = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sndbufsz, sizeof(sndbufsz));
aymeric's avatar
aymeric committed
262 263 264 265 266 267 268 269
			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
270
		err = setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&rcvbufsz, sizeof(rcvbufsz));
aymeric's avatar
aymeric committed
271 272 273 274 275
		if (err == -1) {
			ortp_error("Fail to increase socket's recv buffer size with SO_RCVBUFFORCE: %s.", getSocketError());
		}
#endif
		if (!done){
276
			err = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbufsz, sizeof(rcvbufsz));
aymeric's avatar
aymeric committed
277 278 279 280
			if (err == -1) {
				ortp_error("Fail to increase socket's recv buffer size with SO_RCVBUF: %s.", getSocketError());
			}
		}
281

aymeric's avatar
aymeric committed
282 283 284 285 286 287 288
	}
}

/**
 *rtp_session_set_local_addr:
 *@session:		a rtp session freshly created.
 *@addr:		a local IP address in the xxx.xxx.xxx.xxx form.
289 290
 *@rtp_port:		a local port or -1 to let oRTP choose the port randomly
 *@rtcp_port:		a local port or -1 to let oRTP choose the port randomly
aymeric's avatar
aymeric committed
291 292 293
 *
 *	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:
294 295
 *	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
aymeric's avatar
aymeric committed
296 297 298 299 300 301
 *	rtp_sesession_set_local_addr() is mandatory when the session is recv-only or duplex.
 *
 *	Returns: 0 on success.
**/

int
302
rtp_session_set_local_addr (RtpSession * session, const char * addr, int rtp_port, int rtcp_port)
aymeric's avatar
aymeric committed
303 304 305
{
	ortp_socket_t sock;
	int sockfamily;
306
	if (session->rtp.gs.socket!=(ortp_socket_t)-1){
aymeric's avatar
aymeric committed
307 308 309 310
		/* don't rebind, but close before*/
		rtp_session_release_sockets(session);
	}
	/* try to bind the rtp port */
311

312
	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
313
	if (sock!=-1){
aymeric's avatar
aymeric committed
314
		set_socket_sizes(sock,session->rtp.snd_socket_size,session->rtp.rcv_socket_size);
315 316 317
		session->rtp.gs.sockfamily=sockfamily;
		session->rtp.gs.socket=sock;
		session->rtp.gs.loc_port=rtp_port;
aymeric's avatar
aymeric committed
318
		/*try to bind rtcp port */
319
		sock=create_and_bind(addr,&rtcp_port,&sockfamily,session->reuseaddr,&session->rtcp.gs.loc_addr,&session->rtcp.gs.loc_addrlen);
320
		if (sock!=(ortp_socket_t)-1){
321 322 323
			session->rtcp.gs.sockfamily=sockfamily;
			session->rtcp.gs.socket=sock;
			session->rtcp.gs.loc_port=rtcp_port;
324
		}else {
325
			ortp_error("Could not create and bind rtcp socket for session [%p]",session);
326
			return -1;
aymeric's avatar
aymeric committed
327
		}
328

aymeric's avatar
aymeric committed
329 330 331 332 333 334
		/* set socket options (but don't change chosen states) */
		rtp_session_set_dscp( session, -1 );
		rtp_session_set_multicast_ttl( session, -1 );
		rtp_session_set_multicast_loopback( session, -1 );
		return 0;
	}
335
	ortp_error("Could not bind RTP socket to %s on port %i for session [%p]",addr,rtp_port,session);
aymeric's avatar
aymeric committed
336 337 338 339
	return -1;
}


340 341 342 343 344 345 346 347 348 349 350 351 352
/**
 *rtp_session_set_pktinfo:
 *@session: a rtp session
 *@activate: activation flag (0 to deactivate, other value to activate)
 *
 * (De)activates packet info for incoming and outgoing packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_pktinfo(RtpSession *session, int activate)
{
	int retval;
353
	int optname;
354 355 356 357 358 359 360
#if defined(WIN32) || defined(_WIN32_WCE)
	char optval[sizeof(DWORD)];
	int optlen = sizeof(optval);
#else
	int *optval = &activate;
	int optlen = sizeof(activate);
#endif
361 362

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

365 366 367 368
#if defined(WIN32) || defined(_WIN32_WCE)
	memset(optval, activate, sizeof(optval));
#endif

369
	switch (session->rtp.gs.sockfamily) {
370
		case AF_INET:
371 372 373 374 375
#ifdef IP_PKTINFO
			optname = IP_PKTINFO;
#else
			optname = IP_RECVDSTADDR;
#endif
376
			retval = setsockopt(session->rtp.gs.socket, IPPROTO_IP, optname, optval, optlen);
377
			if (retval < 0) break;
378
			retval = setsockopt(session->rtcp.gs.socket, IPPROTO_IP, optname, optval, optlen);
379 380
			break;
		case AF_INET6:
381 382 383 384 385
#ifdef IPV6_PKTINFO
			optname = IPV6_PKTINFO;
#else
			optname = IPV6_RECVDSTADDR;
#endif
386
			retval = setsockopt(session->rtp.gs.socket, IPPROTO_IPV6, optname, optval, optlen);
387
			if (retval < 0) break;
388
			retval = setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, optname, optval, optlen);
389 390 391 392 393 394 395 396 397 398 399
			break;
		default:
			retval = -1;
			break;
	}

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


aymeric's avatar
aymeric committed
400 401 402 403 404 405 406 407 408 409 410 411
/**
 *rtp_session_set_multicast_ttl:
 *@session: a rtp session
 *@ttl: desired Multicast Time-To-Live
 *
 * Sets the TTL (Time-To-Live) for outgoing multicast packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_multicast_ttl(RtpSession *session, int ttl)
{
412 413 414 415 416 417
	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
418
	if (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;
419

420
	switch (session->rtp.gs.sockfamily) {
421 422
		case AF_INET: {

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

aymeric's avatar
aymeric committed
426 427
			if (retval<0) break;

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

431 432
		} break;
		case AF_INET6: {
aymeric's avatar
aymeric committed
433

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

aymeric's avatar
aymeric committed
437
			if (retval<0) break;
438

439
			retval= setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
aymeric's avatar
aymeric committed
440
					 (SOCKET_OPTION_VALUE) &session->multicast_ttl, sizeof(session->multicast_ttl));
441 442 443 444
		} break;
	default:
		retval=-1;
	}
aymeric's avatar
aymeric committed
445 446 447

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

aymeric's avatar
aymeric committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

	return retval;
}


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


/**
 *rtp_session_set_multicast_loopback:
 *@session: a rtp session
 *@ttl: desired Multicast Time-To-Live
 *
 * Sets the TTL (Time-To-Live) for outgoing multicast packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_multicast_loopback(RtpSession *session, int yesno)
{
479 480 481 482 483 484 485 486 487 488 489 490
	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
491
	if (session->rtp.gs.socket == (ortp_socket_t)-1) return 0;
492

493
	switch (session->rtp.gs.sockfamily) {
494 495
		case AF_INET: {

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

aymeric's avatar
aymeric committed
499 500
			if (retval<0) break;

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

504 505
		} break;
		case AF_INET6: {
aymeric's avatar
aymeric committed
506

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

aymeric's avatar
aymeric committed
510
			if (retval<0) break;
511

512
			retval= setsockopt(session->rtcp.gs.socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
aymeric's avatar
aymeric committed
513
				 (SOCKET_OPTION_VALUE)	&session->multicast_loopback, sizeof(session->multicast_loopback));
514 515 516 517
		} break;
	default:
		retval=-1;
	}
518

aymeric's avatar
aymeric committed
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
	if (retval<0)
		ortp_warning("Failed to set multicast loopback on socket.");

	return retval;
}


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

/**
 *rtp_session_set_dscp:
 *@session: a rtp session
 *@dscp: desired DSCP PHB value
 *
 * Sets the DSCP (Differentiated Services Code Point) for outgoing RTP packets.
 *
 * Returns: 0 on success.
 *
**/
int rtp_session_set_dscp(RtpSession *session, int dscp){
	int retval=0;
	int tos;
Ghislain MARY's avatar
Ghislain MARY committed
551 552
	int proto;
	int value_type;
553 554 555
#if (_WIN32_WINNT >= 0x0600)
	OSVERSIONINFOEX ovi;
#endif
aymeric's avatar
aymeric committed
556 557 558

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

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

563
#if (_WIN32_WINNT >= 0x0600) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
564 565 566 567 568 569 570 571
	memset(&ovi, 0, sizeof(ovi));
	ovi.dwOSVersionInfoSize = sizeof(ovi);
	GetVersionEx((LPOSVERSIONINFO) & ovi);

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

Simon Morlat's avatar
Simon Morlat committed
572 573
		if (FAILED(__HrLoadAllImportsForDll("qwave.dll"))) {
			ortp_warning("Failed to load qwave.dll: no QoS available\n" );
574 575 576 577
		}
		else
		{
			if (session->dscp==0)
Simon Morlat's avatar
Simon Morlat committed
578 579 580 581 582 583 584 585 586
				tos=QOSTrafficTypeBestEffort;
			else if (session->dscp==0x8)
				tos=QOSTrafficTypeBackground;
			else if (session->dscp==0x28)
				tos=QOSTrafficTypeAudioVideo;
			else if (session->dscp==0x38)
				tos=QOSTrafficTypeVoice;
			else
				tos=QOSTrafficTypeExcellentEffort; /* 0x28 */
587 588

			if (session->rtp.QoSHandle==NULL) {
Simon Morlat's avatar
Simon Morlat committed
589 590 591 592 593 594 595 596 597 598 599 600
				QOS_VERSION version;
				BOOL QoSResult;

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

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

				if (QoSResult != TRUE){
					ortp_error("QOSCreateHandle failed to create handle with error %d\n",
						GetLastError());
					retval=-1;
601
				}
Simon Morlat's avatar
Simon Morlat committed
602
			}
603
			if (session->rtp.QoSHandle!=NULL) {
Simon Morlat's avatar
Simon Morlat committed
604 605
				BOOL QoSResult;
				QoSResult = QOSAddSocketToFlow(
606
					session->rtp.QoSHandle,
607 608
					session->rtp.gs.socket,
					(struct sockaddr*)&session->rtp.gs.rem_addr,
609 610
					tos,
					QOS_NON_ADAPTIVE_FLOW,
Simon Morlat's avatar
Simon Morlat committed
611 612 613
					&session->rtp.QoSFlowID);

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


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


/**
 *rtp_session_get_local_port:
 *@session:	a rtp session for which rtp_session_set_local_addr() or rtp_session_set_remote_addr() has been called
 *
673
 *	This function can be useful to retrieve the local port that was randomly choosen by
aymeric's avatar
aymeric committed
674 675 676 677 678 679
 *	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){
680
	return (session->rtp.gs.loc_port>0) ? session->rtp.gs.loc_port : -1;
aymeric's avatar
aymeric committed
681 682
}

683
int rtp_session_get_local_rtcp_port(const RtpSession *session){
684
	return (session->rtcp.gs.loc_port>0) ? session->rtcp.gs.loc_port : -1;
685 686
}

aymeric's avatar
aymeric committed
687 688 689 690 691 692 693
/**
 *rtp_session_set_remote_addr:
 *@session:		a rtp session freshly created.
 *@addr:		a local IP address in the xxx.xxx.xxx.xxx form.
 *@port:		a local port.
 *
 *	Sets the remote address of the rtp session, ie the destination address where rtp packet
694
 *	are sent. If the session is recv-only or duplex, it also sets the origin of incoming RTP
aymeric's avatar
aymeric committed
695 696 697 698 699 700
 *	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){
701
	return rtp_session_set_remote_addr_full(session, addr, port, addr, port+1);
aymeric's avatar
aymeric committed
702 703 704 705 706
}

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

int
720 721 722 723 724 725
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){
aymeric's avatar
aymeric committed
726 727 728
	int err;
	struct addrinfo hints, *res0, *res;
	char num[8];
729 730 731 732 733
	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;
734

735 736 737 738 739 740 741 742
	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;
	}
743

aymeric's avatar
aymeric committed
744
	memset(&hints, 0, sizeof(hints));
745
	hints.ai_family = (session->rtp.gs.socket == -1) ? AF_UNSPEC : session->rtp.gs.sockfamily;
aymeric's avatar
aymeric committed
746
	hints.ai_socktype = SOCK_DGRAM;
747 748 749 750 751 752 753 754
#ifndef ANDROID
	hints.ai_flags |= hints.ai_family==AF_INET6 ? AI_V4MAPPED | AI_ALL : 0;
#else
	/*
	 * Bionic has a crappy implementation of getaddrinfo() that doesn't support the AI_V4MAPPED flag.
	 * However since linux kernel is very tolerant, you can pass an IPv4 sockaddr to sendto without causing problem.
	 */
#endif
755

aymeric's avatar
aymeric committed
756
	snprintf(num, sizeof(num), "%d", rtp_port);
757
	err = getaddrinfo(rtp_addr, num, &hints, &res0);
aymeric's avatar
aymeric committed
758
	if (err) {
Simon Morlat's avatar
Simon Morlat committed
759
		ortp_warning("Error in socket address (hints.ai_family=%i, hints.ai_flags=%i): %s", hints.ai_family, hints.ai_flags, gai_strerror(err));
760 761
		err=-1;
		goto end;
aymeric's avatar
aymeric committed
762
	}
763
	if (session->rtp.gs.socket == -1){
aymeric's avatar
aymeric committed
764 765 766 767
		/* 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)
768 769
			err = rtp_session_set_local_addr (session, "::", -1, -1);
		else err=rtp_session_set_local_addr (session, "0.0.0.0", -1, -1);
770 771 772 773
		if (err<0) {
			err=-1;
			goto end;
		}
aymeric's avatar
aymeric committed
774 775
	}

776

777
	err=-1;
aymeric's avatar
aymeric committed
778 779
	for (res = res0; res; res = res->ai_next) {
		/* set a destination address that has the same type as the local address */
780
		if (res->ai_family==session->rtp.gs.sockfamily ) {
781 782
			memcpy(rtp_saddr, res->ai_addr, res->ai_addrlen);
			*rtp_saddr_len=res->ai_addrlen;
783 784
			err=0;
			break;
aymeric's avatar
aymeric committed
785 786 787 788
		}
	}
	freeaddrinfo(res0);
	if (err) {
789
		ortp_warning("Could not set destination for RTP socket to %s:%i.",rtp_addr,rtp_port);
790
		goto end;
aymeric's avatar
aymeric committed
791
	}
792

aymeric's avatar
aymeric committed
793
	memset(&hints, 0, sizeof(hints));
794
	hints.ai_family = (session->rtp.gs.socket == -1) ? AF_UNSPEC : session->rtp.gs.sockfamily;
aymeric's avatar
aymeric committed
795
	hints.ai_socktype = SOCK_DGRAM;
796 797 798
#ifndef ANDROID
	hints.ai_flags |= hints.ai_family==AF_INET6 ? AI_V4MAPPED | AI_ALL : 0;
#endif
aymeric's avatar
aymeric committed
799
	snprintf(num, sizeof(num), "%d", rtcp_port);
800
	err = getaddrinfo(rtcp_addr, num, &hints, &res0);
aymeric's avatar
aymeric committed
801 802
	if (err) {
		ortp_warning ("Error: %s", gai_strerror(err));
803 804
		err=-1;
		goto end;
aymeric's avatar
aymeric committed
805
	}
806
	err=-1;
aymeric's avatar
aymeric committed
807 808
	for (res = res0; res; res = res->ai_next) {
		/* set a destination address that has the same type as the local address */
809
		if (res->ai_family==session->rtp.gs.sockfamily ) {
810
			err=0;
811 812
			memcpy(rtcp_saddr, res->ai_addr, res->ai_addrlen);
			*rtcp_saddr_len=res->ai_addrlen;
813
			break;
aymeric's avatar
aymeric committed
814 815 816 817
		}
	}
	freeaddrinfo(res0);
	if (err) {
818
		ortp_warning("Could not set destination for RCTP socket to %s:%i.",rtcp_addr,rtcp_port);
819
		goto end;
aymeric's avatar
aymeric committed
820 821 822
	}

	if (can_connect(session)){
823
		if (try_connect(session->rtp.gs.socket,(struct sockaddr*)&session->rtp.gs.rem_addr,session->rtp.gs.rem_addrlen))
aymeric's avatar
aymeric committed
824
			session->flags|=RTP_SOCKET_CONNECTED;
825 826
		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))
aymeric's avatar
aymeric committed
827 828 829 830 831 832 833
				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;
834
		if (connect(session->rtp.gs.socket,&sa,sizeof(sa))<0){
aymeric's avatar
aymeric committed
835 836
			ortp_error("Cannot dissolve connect() association for rtp socket: %s", getSocketError());
		}
837
		if (connect(session->rtcp.gs.socket,&sa,sizeof(sa))<0){
aymeric's avatar
aymeric committed
838 839 840 841 842
			ortp_error("Cannot dissolve connect() association for rtcp socket: %s", getSocketError());
		}
		session->flags&=~RTP_SOCKET_CONNECTED;
		session->flags&=~RTCP_SOCKET_CONNECTED;
	}
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
	ortp_message("RtpSession [%p] sending to rtp [%s:%i] rtcp [%s:%i] %s"	,session
									,rtp_addr
									,rtp_port
									,rtcp_addr
									,rtcp_port
									,is_aux ? "as auxiliary destination" : "");
	end:
	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
860 861
}

862
int rtp_session_set_remote_addr_and_port(RtpSession * session, const char * addr, int rtp_port, int rtcp_port){
863
	return rtp_session_set_remote_addr_full(session,addr,rtp_port,addr,rtcp_port);
aymeric's avatar
aymeric committed
864 865
}

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
/**
 *rtp_session_add_remote_aux_addr_full:
 *@session:		a rtp session freshly created.
 *@rtp_addr:		a local IP address in the xxx.xxx.xxx.xxx form.
 *@rtp_port:		a local rtp port.
 *@rtcp_addr:		a local IP address in the xxx.xxx.xxx.xxx form.
 *@rtcp_port:		a local rtcp port.
 *
 *	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){
891 892
	if (rtpfd!=-1) set_non_blocking_socket(rtpfd);
	if (rtcpfd!=-1) set_non_blocking_socket(rtcpfd);
893 894
	session->rtp.gs.socket=rtpfd;
	session->rtcp.gs.socket=rtcpfd;
895
	if (rtpfd!=-1 || rtcpfd!=-1 )
aymeric's avatar
aymeric committed
896 897 898 899 900 901
		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)
{
902 903
	session->rtp.gs.tr = rtptr;
	session->rtcp.gs.tr = rtcptr;
aymeric's avatar
aymeric committed
904 905 906 907 908 909 910 911 912 913
	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);
}

914
void rtp_session_get_transports(const RtpSession *session, RtpTransport **rtptr, RtpTransport **rtcptr){
915 916
	if (rtptr) *rtptr=session->rtp.gs.tr;
	if (rtcptr) *rtcptr=session->rtcp.gs.tr;
917
}
aymeric's avatar
aymeric committed
918 919 920 921 922 923 924 925 926 927 928 929 930


/**
 *rtp_session_flush_sockets:
 *@session: a rtp session
 *
 * Flushes the sockets for all pending incoming packets.
 * This can be usefull if you did not listen to the stream for a while
 * and wishes to start to receive again. During the time no receive is made
 * packets get bufferised into the internal kernel socket structure.
 *
**/
void rtp_session_flush_sockets(RtpSession *session){
931 932 933
	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
934 935 936
}


937
#ifdef USE_SENDMSG
aymeric's avatar
aymeric committed
938
#define MAX_IOV 30
939
static int rtp_sendmsg(int sock,mblk_t *m, const struct sockaddr *rem_addr, socklen_t addr_len){
aymeric's avatar
aymeric committed
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
	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;
}
961
#endif
aymeric's avatar
aymeric committed
962

963 964 965 966 967 968 969 970 971 972 973 974
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
975

976
static void update_sent_bytes(OrtpStream *os, int nbytes) {
977
	int overhead = ortp_stream_is_ipv6(os) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
978 979
	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. */
980
		ortp_gettimeofday(&os->send_bw_start, NULL);
aymeric's avatar
aymeric committed
981
	}
982
	os->sent_bytes += nbytes + overhead;
aymeric's avatar
aymeric committed
983 984
}

985
static void update_recv_bytes(OrtpStream *os, int nbytes) {
986
	int overhead = ortp_stream_is_ipv6(os) ? IP6_UDP_OVERHEAD : IP_UDP_OVERHEAD;
987 988
	if (os->recv_bytes == 0) {
		ortp_gettimeofday(&os->recv_bw_start, NULL);
aymeric's avatar
aymeric committed
989
	}
990
	os->recv_bytes += nbytes + overhead;
aymeric's avatar
aymeric committed
991 992
}

993 994 995 996 997 998 999
static void log_send_error(RtpSession *session, const char *type, mblk_t *m, struct sockaddr *destaddr, socklen_t destlen){
	char host[65]={0};
	char port[12]={0};
	getnameinfo(destaddr,destlen,host,sizeof(host)-1,port,sizeof(port)-1,NI_NUMERICHOST|NI_NUMERICSERV);
	ortp_warning ("RtpSession [%p] error sending [%s] packet [%p] to ip=[%s] port=[%s]: %s",
		session, type, m, host, port, getSocketError());
}
1000

1001
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
1002
	int error;
1003
	ortp_socket_t sockfd=session->rtp.gs.socket;
1004

1005 1006 1007
	if (rtp_session_using_transport(session, rtp)){
		error = (session->rtp.gs.tr->t_sendto) (session->rtp.gs.tr,m,0,destaddr,destlen);
	}else{
1008
		error=_ortp_sendto(sockfd,m,0,destaddr,destlen);
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
	}
	if (!is_aux){
		/*errors to auxiliary destinations are not notified*/
		if (error < 0){
			if (session->on_network_error.count>0){
				rtp_signal_table_emit3(&session->on_network_error,(long)"Error sending RTP packet",INT_TO_POINTER(getSocketErrorCode()));
			}else log_send_error(session,"rtp",m,destaddr,destlen);
			session->rtp.send_errno=getSocketErrorCode();
		}else{
			update_sent_bytes(&session->rtp.gs, error);
		}
	}
	return error;
}

int
rtp_session_rtp_send (RtpSession * session, mblk_t * m){
	int error=0;
aymeric's avatar
aymeric committed
1027 1028
	int i;
	rtp_header_t *hdr;
1029 1030
	struct sockaddr *destaddr=(struct sockaddr*)&session->rtp.gs.rem_addr;
	socklen_t destlen=session->rtp.gs.rem_addrlen;
1031
	OList *elem=NULL;
1032

aymeric's avatar
aymeric committed
1033
	hdr = (rtp_header_t *) m->b_rptr;
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
	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
1044 1045 1046 1047 1048

	if (session->flags & RTP_SOCKET_CONNECTED) {
		destaddr=NULL;
		destlen=0;
	}
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
	/*first send to main destination*/
	if (destlen) error=rtp_session_rtp_sendto(session,m,destaddr,destlen,FALSE);
	/*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
1059

1060 1061 1062
static int rtp_session_rtcp_sendto(RtpSession * session, mblk_t * m, struct sockaddr *destaddr, socklen_t destlen, bool_t is_aux){
	int error=0;
	ortp_socket_t sockfd=session->rtcp.gs.socket;
1063

1064 1065 1066 1067 1068
	if (rtp_session_using_transport(session, rtcp)){
			error = (session->rtcp.gs.tr->t_sendto) (session->rtcp.gs.tr, m, 0,
			destaddr, destlen);
	}
	else{
aymeric's avatar
aymeric committed
1069
#ifdef USE_SENDMSG
1070
		error=rtp_sendmsg(sockfd,m,destaddr, destlen);
aymeric's avatar
aymeric committed
1071
#else
1072
		if (m->b_cont!=NULL){
aymeric's avatar
aymeric committed
1073
			msgpullup(m,-1);
1074 1075
		}
		error = sendto(sockfd, (char*)m->b_rptr,(int) (m->b_wptr - m->b_rptr), 0, destaddr, destlen);
aymeric's avatar
aymeric committed
1076 1077
#endif
	}
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
	if (!is_aux){
		if (error < 0){
			if (session->on_network_error.count>0){
				rtp_signal_table_emit3(&session->on_network_error,(long)"Error sending RTCP packet",INT_TO_POINTER(getSocketErrorCode()));
			}else{
				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
1089 1090 1091 1092 1093
	}
	return error;
}

int
1094
rtp_session_rtcp_send (RtpSession * session, mblk_t * m){
aymeric's avatar
aymeric committed
1095
	int error=0;
1096 1097 1098
	ortp_socket_t sockfd=session->rtcp.gs.socket;
	struct sockaddr *destaddr=(struct sockaddr*)&session->rtcp.gs.rem_addr;
	socklen_t destlen=session->rtcp.gs.rem_addrlen;
1099
	OList *elem=NULL;
aymeric's avatar
aymeric committed
1100 1101 1102 1103 1104 1105 1106
	bool_t using_connected_socket=(session->flags & RTCP_SOCKET_CONNECTED)!=0;

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

1107 1108 1109 1110
	if (session->rtcp.enabled){
		if ( (sockfd!=(ortp_socket_t)-1 && (destlen>0 || using_connected_socket))
			|| rtp_session_using_transport(session, rtcp) ) {
			rtp_session_rtcp_sendto(session,m,destaddr,destlen,FALSE);
aymeric's avatar
aymeric committed
1111
		}
1112 1113 1114
		for(elem=session->rtcp.gs.aux_destinations;elem!=NULL;elem=elem->next){
			OrtpAddress *addr=(OrtpAddress*)elem->data;
			rtp_session_rtcp_sendto(session,m,(struct sockaddr*)&addr->addr,addr->len,TRUE);
aymeric's avatar
aymeric committed
1115
		}
1116

1117
	}else ortp_message("Not sending rtcp report, rtcp disabled.");
aymeric's avatar
aymeric committed
1118 1119 1120 1121
	freemsg (m);
	return error;
}

Yann Diorcet's avatar
Yann Diorcet committed
1122
int rtp_session_rtp_recv_abstract(ortp_socket_t socket, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen) {
1123
	int ret;
Yann Diorcet's avatar
Yann Diorcet committed
1124
	int bufsz = (int) (msg->b_datap->db_lim - msg->b_datap->db_base);
1125
#ifndef _WIN32
Yann Diorcet's avatar
Yann Diorcet committed
1126 1127 1128
	struct iovec   iov;
	struct msghdr  msghdr;
	struct cmsghdr *cmsghdr;
1129
	char control[512];
Yann Diorcet's avatar
Yann Diorcet committed
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
	memset(&msghdr, 0, sizeof(msghdr));
	memset(&iov, 0, sizeof(iov));
	iov.iov_base = msg->b_wptr;
	iov.iov_len  = bufsz;
	if(from != NULL && fromlen != NULL) {
		msghdr.msg_name = from;
		msghdr.msg_namelen = *fromlen;
	}
	msghdr.msg_iov     = &iov;
	msghdr.msg_iovlen  = 1;
	msghdr.msg_control = &control;
	msghdr.msg_controllen = sizeof(control);
1142
	ret = recvmsg(socket, &msghdr, flags);
Yann Diorcet's avatar
Yann Diorcet committed
1143 1144 1145
	if(fromlen != NULL)
		*fromlen = msghdr.msg_namelen;
	if(ret >= 0) {
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
#else
	char control[512];
	WSAMSG msghdr;
	WSACMSGHDR *cmsghdr;
	WSABUF data_buf;
	DWORD bytes_received;

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

	memset(&msghdr, 0, sizeof(msghdr));
	memset(control, 0, sizeof(control));
	if(from != NULL && fromlen != NULL) {
		msghdr.name = from;
		msghdr.namelen = *fromlen;
	}
	data_buf.buf = (char *)msg->b_wptr;
	data_buf.len = bufsz;
	msghdr.lpBuffers = &data_buf;
	msghdr.dwBufferCount = 1;
	msghdr.Control.buf = control;
	msghdr.Control.len = sizeof(control);
	msghdr.dwFlags = flags;
	ret = ortp_WSARecvMsg(socket, &msghdr, &bytes_received, NULL, NULL);
	if(fromlen != NULL)
		*fromlen = msghdr.namelen;
	if(ret >= 0) {
		ret = bytes_received;
#endif
Yann Diorcet's avatar
Yann Diorcet committed
1176
		for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr != NULL ; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
1177
#if defined(ORTP_TIMESTAMP)
Yann Diorcet's avatar
Yann Diorcet committed
1178 1179 1180 1181
			if (cmsghdr->cmsg_level == SOL_SOCKET && cmsghdr->cmsg_type == SO_TIMESTAMP) {
				memcpy(&msg->timestamp, (struct timeval *)CMSG_DATA(cmsghdr), sizeof(struct timeval));
			}
#endif
1182 1183
#ifdef IP_PKTINFO
			if ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_PKTINFO)) {
1184
				struct in_pktinfo *pi = (struct in_pktinfo *)CMSG_DATA(cmsghdr);
1185
				memcpy(&msg->recv_addr.addr.ipi_addr, &pi->ipi_addr, sizeof(msg->recv_addr.addr.ipi_addr));
1186 1187 1188 1189 1190 1191
				msg->recv_addr.family = AF_INET;
			}
#endif
#ifdef IPV6_PKTINFO
			if ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_PKTINFO)) {
				struct in6_pktinfo *pi = (struct in6_pktinfo *)CMSG_DATA(cmsghdr);
1192
				memcpy(&msg->recv_addr.addr.ipi6_addr, &pi->ipi6_addr, sizeof(msg->recv_addr.addr.ipi6_addr));
1193 1194 1195 1196 1197 1198
				msg->recv_addr.family = AF_INET6;
			}
#endif
#ifdef IP_RECVDSTADDR
			if ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_RECVDSTADDR)) {
				struct in_addr *ia = (struct in_addr *)CMSG_DATA(cmsghdr);
1199
				memcpy(&msg->recv_addr.addr.ipi_addr, ia, sizeof(msg->recv_addr.addr.ipi_addr));
1200
				msg->recv_addr.family = AF_INET;
1201
			}
1202 1203
#endif
#ifdef IPV6_RECVDSTADDR
1204
			if ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_RECVDSTADDR)) {
1205
				struct in6_addr *ia = (struct in6_addr *)CMSG_DATA(cmsghdr);
1206
				memcpy(&msg->recv_addr.addr.ipi6_addr, ia, sizeof(msg->recv_addr.addr.ipi6_addr));
1207 1208
				msg->recv_addr.family = AF_INET6;
			}
1209 1210
#endif
#ifdef IP_RECVTTL
1211
			if ((cmsghdr->cmsg_level == IPPROTO_IP) && (cmsghdr->cmsg_type == IP_TTL)) {
1212 1213 1214 1215 1216
				uint32_t *ptr = (uint32_t *)CMSG_DATA(cmsghdr);
				msg->ttl_or_hl = (*ptr & 0xFF);
			}
#endif
#ifdef IPV6_RECVHOPLIMIT
1217
			if ((cmsghdr->cmsg_level == IPPROTO_IPV6) && (cmsghdr->cmsg_type == IPV6_HOPLIMIT)) {
1218 1219 1220
				uint32_t *ptr = (uint32_t *)CMSG_DATA(cmsghdr);
				msg->ttl_or_hl = (*ptr & 0xFF);
			}
1221
#endif
1222 1223
		}
	}
Yann Diorcet's avatar
Yann Diorcet committed
1224 1225 1226
	return ret;
}

aymeric's avatar
aymeric committed
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
void rtp_session_notify_inc_rtcp(RtpSession *session, mblk_t *m){
	if (session->eventqs!=NULL){
		OrtpEvent *ev=ortp_event_new(ORTP_EVENT_RTCP_PACKET_RECEIVED);
		OrtpEventData *d=ortp_event_get_data(ev);
		d->packet=m;
		rtp_session_dispatch_event(session,ev);
	}
	else freemsg(m);  /* avoid memory leak */
}

1237
static void compute_rtt(RtpSession *session, const struct timeval *now, uint32_t lrr, uint32_t dlrr){
Simon Morlat's avatar
Simon Morlat committed
1238
	uint64_t curntp=ortp_timeval_to_ntp(now);
1239
	uint32_t approx_ntp=(curntp>>16) & 0xFFFFFFFF;
1240 1241
	/*ortp_message("rtt approx_ntp=%u, lrr=%u, dlrr=%u",approx_ntp,lrr,dlrr);*/
	if (lrr!=0 && dlrr!=0){
1242 1243
		/*we cast to int32_t to check for crazy RTT time (negative)*/
		double rtt_frac=(int32_t)(approx_ntp-lrr-dlrr);
1244 1245
		if (rtt_frac>=0){
			rtt_frac/=65536.0;
1246

1247
			session->rtt=rtt_frac;
Simon Morlat's avatar
Simon Morlat committed
1248
			/*ortp_message("rtt estimated to %f s",session->rtt);*/
1249
		}else ortp_warning("Negative RTT computation, maybe due to clock adjustments.");
1250 1251 1252
	}
}

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

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

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

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

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

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

	if (rtcp->version != 2)
	{
		/* try to see if it is a STUN packet */
		uint16_t stunlen = *((uint16_t *)(block->b_rptr + sizeof(uint16_t)));
		stunlen = ntohs(stunlen);
		if (stunlen + 20 == block->b_wptr - block->b_rptr) {
			/* this looks like a stun packet */
			if (session->eventqs != NULL) {
				OrtpEvent *ev = ortp_event_new(ORTP_EVENT_STUN_PACKET_RECEIVED);
				OrtpEventData *ed = ortp_event_get_data(ev);
				ed->packet = block;
1309 1310
				ed->source_addrlen=addrlen;
				memcpy(&ed->source_addr,addr,addrlen);
1311
				ed->info.socket_type = OrtpRTCPSocket;
1312 1313 1314 1315 1316 1317 1318 1319
				rtp_session_dispatch_event(session, ev);
				return -1;
			}
		}
		/* discard in two case: the packet is not stun OR nobody is interested by STUN (no eventqs) */
		ortp_debug("Receiving rtcp packet with version number !=2...discarded");
		return 0;
	}
1320 1321 1322

	update_recv_bytes(&session->rtcp.gs, block->b_wptr - block->b_rptr);

1323
	/* compound rtcp packet can be composed by more than one rtcp message */
Simon Morlat's avatar
Simon Morlat committed
1324 1325
	do{
		struct timeval reception_date;
1326
		const report_block_t *rb;
1327 1328

		/* Getting the reception date from the main clock */
1329
		ortp_gettimeofday( &reception_date, NULL );
1330

Simon Morlat's avatar
Simon Morlat committed
1331
		if (rtcp_is_SR(block) ) {
1332
			rtcp_sr_t *sr = (rtcp_sr_t *) rtcp;
1333

1334 1335 1336 1337 1338 1339 1340
			/* The session descriptor values are reset in case there is an error in the SR block parsing */
			rtpstream->last_rcv_SR_ts = 0;
			rtpstream->last_rcv_SR_time.tv_usec = 0;
			rtpstream->last_rcv_SR_time.tv_sec = 0;

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

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