From 8686b3135348bcd69bdb3c3cb6660a82d989bb30 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Wed, 23 Jan 2008 20:39:07 +0000 Subject: Ticket #61: Implement SRTP support in PJMEDIA and PJSUA-LIB, and updated applications because of the changes. This is a major modification back ported from SRTP branch. See ticket #61 for changelog detail of this commit git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1735 74dad513-b988-da41-8d7b-12977e46ad98 --- build.mak.in | 2 +- pjmedia/build/Makefile | 10 +- pjmedia/build/pjmedia.dsp | 12 +- pjmedia/build/pjmedia.vcproj | 12 +- pjmedia/include/pjmedia.h | 1 + pjmedia/include/pjmedia/config.h | 8 + pjmedia/include/pjmedia/errno.h | 81 +- pjmedia/include/pjmedia/transport.h | 125 +++ pjmedia/include/pjmedia/transport_ice.h | 80 +- pjmedia/include/pjmedia/transport_srtp.h | 200 +++++ pjmedia/include/pjmedia/transport_udp.h | 55 -- pjmedia/src/pjmedia/errno.c | 48 +- pjmedia/src/pjmedia/transport_ice.c | 223 +++--- pjmedia/src/pjmedia/transport_srtp.c | 1253 ++++++++++++++++++++++++++++++ pjmedia/src/pjmedia/transport_udp.c | 88 ++- pjproject-vs8.sln | 88 ++- pjproject.dsw | 49 +- pjsip-apps/build/Samples-vc.mak | 3 +- pjsip-apps/src/pjsua/pjsua_app.c | 27 +- pjsip-apps/src/samples/simpleua.c | 8 +- pjsip-apps/src/samples/siprtp.c | 6 +- pjsip-apps/src/samples/streamutil.c | 116 ++- pjsip/include/pjsua-lib/pjsua.h | 75 ++ pjsip/include/pjsua-lib/pjsua_internal.h | 9 +- pjsip/src/pjsua-lib/pjsua_call.c | 147 +++- pjsip/src/pjsua-lib/pjsua_core.c | 8 + pjsip/src/pjsua-lib/pjsua_media.c | 128 +-- 27 files changed, 2454 insertions(+), 408 deletions(-) create mode 100644 pjmedia/include/pjmedia/transport_srtp.h create mode 100644 pjmedia/src/pjmedia/transport_srtp.c diff --git a/build.mak.in b/build.mak.in index 8bab9d0d..27f43919 100644 --- a/build.mak.in +++ b/build.mak.in @@ -8,7 +8,7 @@ export CROSS_COMPILE := @ac_cross_compile@ export LINUX_POLL := @ac_linux_poll@ # Determine which party libraries to use -export APP_THIRD_PARTY_LIBS := -lresample-$(TARGET_NAME) -lmilenage-$(TARGET_NAME) +export APP_THIRD_PARTY_LIBS := -lresample-$(TARGET_NAME) -lmilenage-$(TARGET_NAME) -lsrtp-$(TARGET_NAME) ifneq (@ac_no_gsm_codec@,1) APP_THIRD_PARTY_LIBS += -lgsmcodec-$(TARGET_NAME) diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index ce0b887a..42f4684c 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -3,6 +3,10 @@ include ../../build.mak THIRD_PARTY:=$(PJDIR)/third_party PA_DIR := $(THIRD_PARTY)/build/portaudio/src +SRTP_INC=$(CC_INC)$(THIRD_PARTY)/build/srtp \ + $(CC_INC)$(THIRD_PARTY)/srtp/crypto/include \ + $(CC_INC)$(THIRD_PARTY)/srtp/include + include $(PJDIR)/build/common.mak RULES_MAK := $(PJDIR)/build/rules.mak @@ -25,7 +29,8 @@ export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CC_INC)../../pjlib/include \ $(CC_INC)../../pjlib-util/include \ $(CC_INC)../../pjnath/include \ - $(CC_INC)../.. + $(CC_INC)../.. \ + $(SRTP_INC) export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \ @@ -48,7 +53,8 @@ export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ resample_resample.o resample_libsamplerate.o \ resample_port.o rtcp.o rtp.o sdp.o sdp_cmp.o sdp_neg.o \ session.o silencedet.o sound_port.o stream.o \ - tonegen.o transport_ice.o transport_udp.o \ + tonegen.o transport_ice.o transport_srtp.o \ + transport_udp.o \ wav_player.o wav_playlist.o wav_writer.o wave.o \ $(SOUND_OBJS) $(NULLSOUND_OBJS) diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp index 2a386070..8533138c 100644 --- a/pjmedia/build/pjmedia.dsp +++ b/pjmedia/build/pjmedia.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Target_Dir "" F90=df.exe # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../.." /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c +# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../../third_party/build/srtp" /I "../../third_party/srtp/crypto/include" /I "../../third_party/srtp/include" /I "../.." /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -67,7 +67,7 @@ LIB32=link.exe -lib # PROP Target_Dir "" F90=df.exe # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../.." /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjnath/include" /I "../../third_party/portaudio/include" /I "../../third_party/speex/include" /I "../../third_party/build/srtp" /I "../../third_party/srtp/crypto/include" /I "../../third_party/srtp/include" /I "../.." /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -249,6 +249,10 @@ SOURCE=..\src\pjmedia\transport_ice.c # End Source File # Begin Source File +SOURCE=..\src\pjmedia\transport_srtp.c +# End Source File +# Begin Source File + SOURCE=..\src\pjmedia\transport_udp.c # End Source File # Begin Source File @@ -409,6 +413,10 @@ SOURCE=..\include\pjmedia\transport_ice.h # End Source File # Begin Source File +SOURCE=..\include\pjmedia\transport_srtp.h +# End Source File +# Begin Source File + SOURCE=..\include\pjmedia\transport_udp.h # End Source File # Begin Source File diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj index 8e45f9f8..e3ed7298 100644 --- a/pjmedia/build/pjmedia.vcproj +++ b/pjmedia/build/pjmedia.vcproj @@ -43,7 +43,7 @@ Name="VCCLCompilerTool" Optimization="2" InlineFunctionExpansion="1" - AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../.." + AdditionalIncludeDirectories="../include;../../pjlib/include;"../../pjlib-util/include";../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../.." PreprocessorDefinitions="NDEBUG;WIN32;_LIB;PJ_WIN32=1;PJ_M_I386=1" StringPooling="true" RuntimeLibrary="2" @@ -119,7 +119,7 @@ + + @@ -1216,6 +1220,10 @@ RelativePath="..\include\pjmedia\transport_ice.h" > + + diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h index 4a104512..bf580675 100644 --- a/pjmedia/include/pjmedia.h +++ b/pjmedia/include/pjmedia.h @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 4c7b26be..ecfd371a 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -484,6 +484,14 @@ #endif +/** + * SRTP Transport + * By default it is enabled. + */ +#ifndef PJMEDIA_HAS_SRTP +# define PJMEDIA_HAS_SRTP 1 +#endif + /** * @} diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h index f14e3599..6be64979 100644 --- a/pjmedia/include/pjmedia/errno.h +++ b/pjmedia/include/pjmedia/errno.h @@ -42,18 +42,35 @@ PJ_BEGIN_DECL * Start of error code relative to PJ_ERRNO_START_USER. */ #define PJMEDIA_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) +#define PJMEDIA_ERRNO_END (PJMEDIA_ERRNO_START + PJ_ERRNO_SPACE_SIZE - 1) /** * Mapping from PortAudio error codes to pjmedia error space. */ -#define PJMEDIA_PORTAUDIO_ERRNO_START (PJMEDIA_ERRNO_START+PJ_ERRNO_SPACE_SIZE-1000) - +#define PJMEDIA_PORTAUDIO_ERRNO_START (PJMEDIA_ERRNO_END-10000) +#define PJMEDIA_PORTAUDIO_ERRNO_END (PJMEDIA_PORTAUDIO_ERRNO_START + 10000 -1) /** * Convert PortAudio error code to PJMEDIA error code. + * PortAudio error code range: 0 >= err >= -10000 */ -#define PJMEDIA_ERRNO_FROM_PORTAUDIO(err) (err+PJMEDIA_PORTAUDIO_ERRNO_START) +#define PJMEDIA_ERRNO_FROM_PORTAUDIO(err) ((int)PJMEDIA_PORTAUDIO_ERRNO_START-err) + + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /** + * Mapping from LibSRTP error codes to pjmedia error space. + */ +#define PJMEDIA_LIBSRTP_ERRNO_START (PJMEDIA_ERRNO_END-10200) +#define PJMEDIA_LIBSRTP_ERRNO_END (PJMEDIA_LIBSRTP_ERRNO_START + 200 - 1) +/** + * Convert LibSRTP error code to PJMEDIA error code. + * LibSRTP error code range: 0 <= err < 200 + */ +#define PJMEDIA_ERRNO_FROM_LIBSRTP(err) (PJMEDIA_LIBSRTP_ERRNO_START+err) + +#endif /************************************************************ * GENERIC/GENERAL PJMEDIA ERRORS @@ -526,6 +543,64 @@ PJ_BEGIN_DECL #define PJMEDIA_ESNDINSAMPLEFMT (PJMEDIA_ERRNO_START+203) /* 220203 */ +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) +/************************************************************ + * SRTP TRANSPORT ERRORS + ***********************************************************/ +/** + * @hideinitializer + * SRTP crypto-suite name not match the offerer tag. + */ +#define PJMEDIA_SRTP_ECRYPTONOTMATCH (PJMEDIA_ERRNO_START+220) /* 220220 */ +/** + * @hideinitializer + * Invalid SRTP key length for specific crypto. + */ +#define PJMEDIA_SRTP_EINKEYLEN (PJMEDIA_ERRNO_START+221) /* 220221 */ +/** + * @hideinitializer + * Unsupported SRTP crypto-suite. + */ +#define PJMEDIA_SRTP_ENOTSUPCRYPTO (PJMEDIA_ERRNO_START+222) /* 220222 */ +/** + * @hideinitializer + * SRTP SDP contains ambigue answer. + */ +#define PJMEDIA_SRTP_ESDPAMBIGUEANS (PJMEDIA_ERRNO_START+223) /* 220223 */ +/** + * @hideinitializer + * Duplicated crypto tag. + */ +#define PJMEDIA_SRTP_ESDPDUPCRYPTOTAG (PJMEDIA_ERRNO_START+224) /* 220224 */ +/** + * @hideinitializer + * Invalid crypto attribute. + */ +#define PJMEDIA_SRTP_ESDPINCRYPTO (PJMEDIA_ERRNO_START+225) /* 220225 */ +/** + * @hideinitializer + * Invalid crypto tag. + */ +#define PJMEDIA_SRTP_ESDPINCRYPTOTAG (PJMEDIA_ERRNO_START+226) /* 220226 */ +/** + * @hideinitializer + * Invalid SDP media transport for SRTP. + */ +#define PJMEDIA_SRTP_ESDPINTRANSPORT (PJMEDIA_ERRNO_START+227) /* 220227 */ +/** + * @hideinitializer + * SRTP crypto attribute required in SDP. + */ +#define PJMEDIA_SRTP_ESDPREQCRYPTO (PJMEDIA_ERRNO_START+228) /* 220228 */ +/** + * @hideinitializer + * Secure transport required in SDP media descriptor. + */ +#define PJMEDIA_SRTP_ESDPREQSECTP (PJMEDIA_ERRNO_START+229) /* 220229 */ + +#endif /* PJMEDIA_HAS_SRTP */ + + /** * Get error message for the specified error code. Note that this * function is only able to decode PJMEDIA specific error code. diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h index 1e7d67db..e9fbb1f3 100644 --- a/pjmedia/include/pjmedia/transport.h +++ b/pjmedia/include/pjmedia/transport.h @@ -166,6 +166,7 @@ PJ_BEGIN_DECL +#include /* * Forward declaration for media transport. @@ -243,6 +244,50 @@ struct pjmedia_transport_op const void *pkt, pj_size_t size); + /** + * This function is called by application to generate the SDP parts + * related to transport type, e.g: ICE, SRTP. + * + * Application should call #pjmedia_transport_media_create() instead of + * calling this function directly. + */ + pj_status_t (*media_create)(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); + + /** + * This function is called by application to start the transport + * based on SDP negotiation result. + * + * Application should call #pjmedia_transport_media_start() instead of + * calling this function directly. + */ + pj_status_t (*media_start) (pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); + + /** + * This function is called by application to stop the transport. + * + * Application should call #pjmedia_transport_media_stop() instead of + * calling this function directly. + */ + pj_status_t (*media_stop) (pjmedia_transport *tp); + + /** + * This function can be called to simulate packet lost. + * + * Application should call #pjmedia_transport_simulate_lost() instead of + * calling this function directly. + */ + pj_status_t (*simulate_lost)(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost); + /** * This function can be called to destroy this transport. * @@ -409,6 +454,67 @@ PJ_INLINE(pj_status_t) pjmedia_transport_send_rtcp(pjmedia_transport *tp, } +/** + * Generate local SDP parts that are related to the specified media transport. + * Remote SDP might be needed as reference when application is in deciding + * side of negotiation (callee side), otherwise it should be NULL. + * This is just a simple wrapper which calls media_create() member + * of the transport. + * + * @param tp The media transport. + * @param pool The memory pool. + * @param sdp_local Local SDP. + * @param sdp_remote Remote SDP. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_INLINE(pj_status_t) pjmedia_transport_media_create(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) +{ + return (*tp->op->media_create)(tp, pool, sdp_local, sdp_remote, + media_index); +} + +/** + * Start the transport with regards to SDP negotiation result. + * This is just a simple wrapper which calls media_start() member + * of the transport. + * + * @param tp The media transport. + * @param pool The memory pool. + * @param sdp_local Local SDP. + * @param sdp_remote Remote SDP. + * @param media_index Media index to start. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_INLINE(pj_status_t) pjmedia_transport_media_start(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) +{ + return (*tp->op->media_start)(tp, pool, sdp_local, sdp_remote, media_index); +} + + +/** + * Stop the transport. + * This is just a simple wrapper which calls media_stop() member + * of the transport. + * + * @param tp The media transport. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_INLINE(pj_status_t) pjmedia_transport_media_stop(pjmedia_transport *tp) +{ + return (*tp->op->media_stop)(tp); +} + /** * Close media transport. This is just a simple wrapper which calls * destroy() member of the transport. This function will free @@ -426,6 +532,25 @@ PJ_INLINE(pj_status_t) pjmedia_transport_close(pjmedia_transport *tp) return PJ_SUCCESS; } +/** + * Simulate packet lost in the specified direction (for testing purposes). + * When enabled, the transport will randomly drop packets to the specified + * direction. + * + * @param tp The media transport. + * @param dir Media direction to which packets will be randomly dropped. + * @param pct_lost Percent lost (0-100). Set to zero to disable packet + * lost simulation. + * + * @return PJ_SUCCESS on success. + */ +PJ_INLINE(pj_status_t) pjmedia_transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost) +{ + return (*tp->op->simulate_lost)(tp, dir, pct_lost); +} + PJ_END_DECL diff --git a/pjmedia/include/pjmedia/transport_ice.h b/pjmedia/include/pjmedia/transport_ice.h index da1e0512..c64ac9bc 100644 --- a/pjmedia/include/pjmedia/transport_ice.h +++ b/pjmedia/include/pjmedia/transport_ice.h @@ -16,8 +16,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __pjmedia_ice_H__ -#define __pjmedia_ice_H__ +#ifndef __PJMEDIA_TRANSPORT_ICE_H__ +#define __PJMEDIA_TRANSPORT_ICE_H__ /** @@ -75,15 +75,6 @@ PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, const pjmedia_ice_cb *cb, pjmedia_transport **p_tp); -/** - * Destroy the media transport. - * - * @param tp The media transport. - * - * @return PJ_SUCCESS. - */ -PJ_DECL(pj_status_t) pjmedia_ice_destroy(pjmedia_transport *tp); - /** * Start the initialization process of this media transport. This function @@ -169,71 +160,6 @@ PJ_DECL(pj_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp, const pj_str_t *local_ufrag, const pj_str_t *local_passwd); -/** - * Modify the SDP to add ICE specific SDP attributes before sending - * the SDP to remote host. - * - * @param tp The media transport. - * @param pool Pool to allocate memory for the SDP elements. - * @param sdp The SDP descriptor to be modified. - * - * @return PJ_SUCCESS, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pjmedia_ice_modify_sdp(pjmedia_transport *tp, - pj_pool_t *pool, - pjmedia_sdp_session *sdp); - -/** - * Start ICE connectivity checks. - * - * This function will pair the local and remote candidates to create - * check list. Once the check list is created and sorted based on the - * priority, ICE periodic checks will be started. This function will - * return immediately, and application will be notified about the - * connectivity check status in the callback. - * - * @param tp The media transport. - * @param pool Memory pool to parse the SDP. - * @param rem_sdp The SDP received from remote agent. - * @param media_index The media index (in SDP) to process. - * - * @return PJ_SUCCESS, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, - pj_pool_t *pool, - const pjmedia_sdp_session *rem_sdp, - unsigned media_index); - -/** - * Stop the ICE session (typically when the call is terminated). Application - * may restart the ICE session again by calling #pjmedia_ice_init_ice(), - * for example to use this media transport for the next call. - * - * @param tp The media transport. - * - * @return PJ_SUCCESS, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pjmedia_ice_stop_ice(pjmedia_transport *tp); - - -/** - * Simulate packet lost in the specified direction (for testing purposes). - * When enabled, the transport will randomly drop packets to the specified - * direction. - * - * @param tp The ICE media transport. - * @param dir Media direction to which packets will be randomly dropped. - * @param pct_lost Percent lost (0-100). Set to zero to disable packet - * lost simulation. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_ice_simulate_lost(pjmedia_transport *tp, - pjmedia_dir dir, - unsigned pct_lost); - - - PJ_END_DECL @@ -243,6 +169,6 @@ PJ_END_DECL */ -#endif /* __pjmedia_ice_H__ */ +#endif /* __PJMEDIA_TRANSPORT_ICE_H__ */ diff --git a/pjmedia/include/pjmedia/transport_srtp.h b/pjmedia/include/pjmedia/transport_srtp.h new file mode 100644 index 00000000..f7ea291d --- /dev/null +++ b/pjmedia/include/pjmedia/transport_srtp.h @@ -0,0 +1,200 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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 + */ +#ifndef __PJMEDIA_TRANSPORT_SRTP_H__ +#define __PJMEDIA_TRANSPORT_SRTP_H__ + +/** + * @file srtp.h + * @brief transport SRTP encapsulates secure media transport. + */ + +#include + + +PJ_BEGIN_DECL + + +/** + * Crypto option. + */ +typedef enum pjmedia_srtp_crypto_option +{ + /** When this flag is specified, encryption will be disabled. */ + PJMEDIA_SRTP_NO_ENCRYPTION = 1, + + /** When this flag is specified, authentication will be disabled. */ + PJMEDIA_SRTP_NO_AUTHENTICATION = 2 + +} pjmedia_srtp_crypto_option; + + +/** + * This structure describes an individual crypto setting. + */ +typedef struct pjmedia_srtp_crypto +{ + /** Optional key. If empty, a random key will be autogenerated. */ + pj_str_t key; + + /** Crypto name. */ + pj_str_t name; + + /* Flags, bitmask from #pjmedia_srtp_crypto_option */ + unsigned flags; + +} pjmedia_srtp_crypto; + + +/** + * This enumeration specifies the behavior of the SRTP transport regarding + * media security offer and answer. + */ +typedef enum pjmedia_srtp_use +{ + /** + * When this flag is specified, SRTP will be disabled, and the transport + * will reject RTP/SAVP offer. + */ + PJMEDIA_SRTP_DISABLED, + + /** + * When this flag is specified, SRTP will be advertised as optional and + * incoming SRTP offer will be accepted. + */ + PJMEDIA_SRTP_OPTIONAL, + + /** + * When this flag is specified, the transport will require that RTP/SAVP + * media shall be used. + */ + PJMEDIA_SRTP_MANDATORY + +} pjmedia_srtp_use; + + +/** + * Settings to be given when creating SRTP transport. Application should call + * #pjmedia_srtp_setting_default() to initialize this structure with its + * default values. + */ +typedef struct pjmedia_srtp_setting +{ + /** + * Specify the usage policy. Default is PJMEDIA_SRTP_OPTIONAL. + */ + pjmedia_srtp_use use; + + /** + * Specify whether the SRTP transport should close the member transport + * when it is destroyed. Default: PJ_TRUE. + */ + pj_bool_t close_member_tp; + + /** + * Specify the number of crypto suite settings. + */ + unsigned crypto_count; + + /** + * Specify individual crypto suite setting. + */ + pjmedia_srtp_crypto crypto[8]; + +} pjmedia_srtp_setting; + + +/** + * Initialize SRTP setting with its default values. + * + * @param opt SRTP setting to be initialized. + */ +PJ_DECL(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt); + + +/** + * Create an SRTP media transport. + * + * @param endpt The media endpoint instance. + * @param tp The actual media transport to send and receive + * RTP/RTCP packets. This media transport will be + * kept as member transport of this SRTP instance. + * @param opt Optional settings. If NULL is given, default + * settings will be used. + * @param p_tp Pointer to receive the transport SRTP instance. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_transport_srtp_create( + pjmedia_endpt *endpt, + pjmedia_transport *tp, + const pjmedia_srtp_setting *opt, + pjmedia_transport **p_tp); + + +/** + * Manually start SRTP session with the given parameters. Application only + * needs to call this function when the SRTP transport is used without SDP + * offer/answer. When SDP offer/answer framework is used, the SRTP transport + * will be started/stopped by #pjmedia_transport_media_start() and + * #pjmedia_transport_media_stop() respectively. + * + * Please note that even if an RTP stream is only one direction, application + * will still need to provide both crypto suites, because it is needed by + * RTCP. + + * If application specifies the crypto keys, the keys for transmit and receive + * direction MUST be different. + * + * @param srtp The SRTP transport. + * @param tx Crypto suite setting for transmit direction. + * @param rx Crypto suite setting for receive direction. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_transport_srtp_start( + pjmedia_transport *tp, + const pjmedia_srtp_crypto *tx, + const pjmedia_srtp_crypto *rx); + +/** + * Stop SRTP session. + * + * @param srtp The SRTP media transport. + * + * @return PJ_SUCCESS on success. + * + * @see #pjmedia_transport_srtp_start() + */ +PJ_DECL(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *tp); + + +/** + * Query member transport of SRTP. + * + * @param srtp The SRTP media transport. + * + * @return member media transport. + */ +PJ_DECL(pjmedia_transport*) pjmedia_transport_srtp_get_member( + pjmedia_transport *tp); + + +PJ_END_DECL + +#endif /* __PJMEDIA_TRANSPORT_SRTP_H__ */ diff --git a/pjmedia/include/pjmedia/transport_udp.h b/pjmedia/include/pjmedia/transport_udp.h index 12f819d1..5bf5b19d 100644 --- a/pjmedia/include/pjmedia/transport_udp.h +++ b/pjmedia/include/pjmedia/transport_udp.h @@ -55,19 +55,6 @@ enum pjmedia_transport_udp_options }; -/** - * UDP transport info. - */ -typedef struct pjmedia_transport_udp_info -{ - /** - * Media socket info. - */ - pjmedia_sock_info skinfo; - -} pjmedia_transport_udp_info; - - /** * Create an RTP and RTCP sockets and bind the sockets to the specified * port to create media transport. @@ -137,18 +124,6 @@ PJ_DECL(pj_status_t) pjmedia_transport_udp_create3(pjmedia_endpt *endpt, unsigned options, pjmedia_transport **p_tp); -/** - * Get media socket info from the specified UDP transport. - * - * @param tp The UDP transport interface. - * @param info Media socket info to be initialized. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) -pjmedia_transport_udp_get_info( pjmedia_transport *tp, - pjmedia_transport_udp_info *info); - /** * Create UDP stream transport from existing sockets. Use this function when @@ -169,36 +144,6 @@ PJ_DECL(pj_status_t) pjmedia_transport_udp_attach(pjmedia_endpt *endpt, pjmedia_transport **p_tp); -/** - * Simulate packet lost in the specified direction (for testing purposes). - * When enabled, the transport will randomly drop packets to the specified - * direction. - * - * @param tp The UDP media transport. - * @param dir Media direction to which packets will be randomly dropped. - * @param pct_lost Percent lost (0-100). Set to zero to disable packet - * lost simulation. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_transport_udp_simulate_lost(pjmedia_transport *tp, - pjmedia_dir dir, - unsigned pct_lost); - - - -/** - * Close UDP transport. Application can also use the "destroy" member of - * media transport interface to close the UDP transport. - * - * @param tp The UDP media transport. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_transport_udp_close(pjmedia_transport *tp); - - - PJ_END_DECL diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c index 300cbd44..f8e39328 100644 --- a/pjmedia/src/pjmedia/errno.c +++ b/pjmedia/src/pjmedia/errno.c @@ -23,6 +23,9 @@ # include #endif +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + const char* get_libsrtp_errstr(int err); +#endif /* PJMEDIA's own error codes/messages @@ -141,6 +144,21 @@ static const struct PJ_BUILD_ERR( PJMEDIA_ENOSNDPLAY, "No suitable sound playback device" ), PJ_BUILD_ERR( PJMEDIA_ESNDINDEVID, "Invalid sound device ID" ), PJ_BUILD_ERR( PJMEDIA_ESNDINSAMPLEFMT, "Invalid sample format for sound device" ), + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* SRTP transport errors: */ + PJ_BUILD_ERR( PJMEDIA_SRTP_ECRYPTONOTMATCH, "SRTP crypto-suite name not match the offerer tag" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_EINKEYLEN, "Invalid SRTP key length for specific crypto" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ENOTSUPCRYPTO, "Unsupported SRTP crypto-suite" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPAMBIGUEANS, "SRTP SDP contains ambigue answer" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPDUPCRYPTOTAG,"Duplicated SRTP crypto tag" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINCRYPTO, "Invalid SRTP crypto attribute" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINCRYPTOTAG, "Invalid SRTP crypto tag" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINTRANSPORT, "Invalid SDP media transport for SRTP" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPREQCRYPTO, "SRTP crypto attribute required" ), + PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPREQSECTP, "Secure transport required in SDP media descriptor" ) +#endif + }; #endif /* PJ_HAS_ERROR_STRING */ @@ -159,11 +177,12 @@ PJ_DEF(pj_str_t) pjmedia_strerror( pj_status_t statcode, /* See if the error comes from PortAudio. */ #if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND - if (statcode >= PJMEDIA_ERRNO_FROM_PORTAUDIO(paNotInitialized) && - statcode < PJMEDIA_ERRNO_FROM_PORTAUDIO(paNotInitialized + 10000)) + if (statcode >= PJMEDIA_PORTAUDIO_ERRNO_START && + statcode <= PJMEDIA_PORTAUDIO_ERRNO_END) { - int pa_err = statcode - PJMEDIA_ERRNO_FROM_PORTAUDIO(0); + //int pa_err = statcode - PJMEDIA_ERRNO_FROM_PORTAUDIO(0); + int pa_err = PJMEDIA_PORTAUDIO_ERRNO_START - statcode; pj_str_t msg; msg.ptr = (char*)Pa_GetErrorText(pa_err); @@ -175,8 +194,26 @@ PJ_DEF(pj_str_t) pjmedia_strerror( pj_status_t statcode, } else #endif /* PJMEDIA_SOUND_IMPLEMENTATION */ + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* LIBSRTP error */ + if (statcode >= PJMEDIA_LIBSRTP_ERRNO_START && + statcode < PJMEDIA_LIBSRTP_ERRNO_END) + { + int err = statcode - PJMEDIA_PORTAUDIO_ERRNO_START; + pj_str_t msg; + + msg = pj_str((char*)get_libsrtp_errstr(err)); + + pj_strncpy_with_null(&errstr, &msg, bufsize); + return errstr; + + } else +#endif + + /* PJMEDIA error */ if (statcode >= PJMEDIA_ERRNO_START && - statcode < PJMEDIA_ERRNO_START + PJ_ERRNO_SPACE_SIZE) + statcode < PJMEDIA_ERRNO_END) { /* Find the error in the table. * Use binary search! @@ -211,8 +248,7 @@ PJ_DEF(pj_str_t) pjmedia_strerror( pj_status_t statcode, return errstr; } - } - + } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c index 649aeac7..87c2ed8d 100644 --- a/pjmedia/src/pjmedia/transport_ice.c +++ b/pjmedia/src/pjmedia/transport_ice.c @@ -51,27 +51,42 @@ struct transport_ice /* * These are media transport operations. */ -static pj_status_t tp_get_info(pjmedia_transport *tp, - pjmedia_sock_info *info); -static pj_status_t tp_attach( pjmedia_transport *tp, - void *stream, - const pj_sockaddr_t *rem_addr, - const pj_sockaddr_t *rem_rtcp, - unsigned addr_len, - void (*rtp_cb)(void*, - const void*, - pj_ssize_t), - void (*rtcp_cb)(void*, - const void*, - pj_ssize_t)); -static void tp_detach( pjmedia_transport *tp, - void *strm); -static pj_status_t tp_send_rtp( pjmedia_transport *tp, - const void *pkt, - pj_size_t size); -static pj_status_t tp_send_rtcp( pjmedia_transport *tp, - const void *pkt, - pj_size_t size); +static pj_status_t transport_get_info (pjmedia_transport *tp, + pjmedia_sock_info *info); +static pj_status_t transport_attach (pjmedia_transport *tp, + void *user_data, + const pj_sockaddr_t *rem_addr, + const pj_sockaddr_t *rem_rtcp, + unsigned addr_len, + void (*rtp_cb)(void*, + const void*, + pj_ssize_t), + void (*rtcp_cb)(void*, + const void*, + pj_ssize_t)); +static void transport_detach (pjmedia_transport *tp, + void *strm); +static pj_status_t transport_send_rtp( pjmedia_transport *tp, + const void *pkt, + pj_size_t size); +static pj_status_t transport_send_rtcp(pjmedia_transport *tp, + const void *pkt, + pj_size_t size); +static pj_status_t transport_media_create(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); +static pj_status_t transport_media_start (pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); +static pj_status_t transport_media_stop(pjmedia_transport *tp); +static pj_status_t transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost); +static pj_status_t transport_destroy (pjmedia_transport *tp); /* * And these are ICE callbacks. @@ -84,14 +99,18 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st, pj_status_t status); -static pjmedia_transport_op tp_ice_op = +static pjmedia_transport_op transport_ice_op = { - &tp_get_info, - &tp_attach, - &tp_detach, - &tp_send_rtp, - &tp_send_rtcp, - &pjmedia_ice_destroy + &transport_get_info, + &transport_attach, + &transport_detach, + &transport_send_rtp, + &transport_send_rtcp, + &transport_media_create, + &transport_media_start, + &transport_media_stop, + &transport_simulate_lost, + &transport_destroy }; static const pj_str_t STR_CANDIDATE = {"candidate", 9}; @@ -132,7 +151,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, tp_ice = PJ_POOL_ZALLOC_T(ice_st->pool, struct transport_ice); tp_ice->ice_st = ice_st; pj_ansi_strcpy(tp_ice->base.name, ice_st->obj_name); - tp_ice->base.op = &tp_ice_op; + tp_ice->base.op = &transport_ice_op; tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE; if (cb) @@ -148,25 +167,6 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, } -/* - * Destroy ICE media transport. - */ -PJ_DEF(pj_status_t) pjmedia_ice_destroy(pjmedia_transport *tp) -{ - struct transport_ice *tp_ice = (struct transport_ice*)tp; - - if (tp_ice->ice_st) { - pj_ice_strans_destroy(tp_ice->ice_st); - /*Must not touch tp_ice after ice_st is destroyed! - (it has the pool) - tp_ice->ice_st = NULL; - */ - } - - return PJ_SUCCESS; -} - - /* * Start media transport initialization. */ @@ -230,7 +230,8 @@ PJ_DEF(pj_status_t) pjmedia_ice_get_comp( pjmedia_transport *tp, PJ_ASSERT_RETURN(tp && comp_id && comp_id <= tp_ice->ice_st->comp_cnt && comp, PJ_EINVAL); - pj_memcpy(comp, tp_ice->ice_st->comp[comp_id-1], sizeof(pj_ice_strans_comp)); + pj_memcpy(comp, tp_ice->ice_st->comp[comp_id-1], + sizeof(pj_ice_strans_comp)); return PJ_SUCCESS; } @@ -246,7 +247,8 @@ PJ_DEF(pj_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp, const pj_str_t *local_passwd) { struct transport_ice *tp_ice = (struct transport_ice*)tp; - return pj_ice_strans_init_ice(tp_ice->ice_st, role, local_ufrag, local_passwd); + return pj_ice_strans_init_ice(tp_ice->ice_st, role, local_ufrag, + local_passwd); } @@ -254,32 +256,46 @@ PJ_DEF(pj_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp, * For both UAC and UAS, pass in the SDP before sending it to remote. * This will add ICE attributes to the SDP. */ -PJ_DEF(pj_status_t) pjmedia_ice_modify_sdp(pjmedia_transport *tp, - pj_pool_t *pool, - pjmedia_sdp_session *sdp) +static pj_status_t transport_media_create(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) { struct transport_ice *tp_ice = (struct transport_ice*)tp; + pj_ice_sess_role ice_role; enum { MAXLEN = 256 }; char *buffer; pjmedia_sdp_attr *attr; unsigned i, cand_cnt; + pj_status_t status; + + /* Init ICE */ + ice_role = (sdp_remote==NULL ? PJ_ICE_SESS_ROLE_CONTROLLING : + PJ_ICE_SESS_ROLE_CONTROLLED); + + status = pjmedia_ice_init_ice(tp, ice_role, NULL, NULL); + if (status != PJ_SUCCESS) + return status; + buffer = (char*) pj_pool_alloc(pool, MAXLEN); /* Create ice-ufrag attribute */ attr = pjmedia_sdp_attr_create(pool, "ice-ufrag", &tp_ice->ice_st->ice->rx_ufrag); - sdp->attr[sdp->attr_count++] = attr; + sdp_local->attr[sdp_local->attr_count++] = attr; /* Create ice-pwd attribute */ attr = pjmedia_sdp_attr_create(pool, "ice-pwd", &tp_ice->ice_st->ice->rx_pass); - sdp->attr[sdp->attr_count++] = attr; + sdp_local->attr[sdp_local->attr_count++] = attr; /* Add all candidates (to media level) */ cand_cnt = tp_ice->ice_st->ice->lcand_cnt; for (i=0; imedia[0]->attr[sdp->media[0]->attr_count++] = attr; + m = sdp_local->media[media_index]; + m->attr[m->attr_count++] = attr; } /* Done */ @@ -456,17 +473,18 @@ static void set_no_ice(struct transport_ice *tp_ice, const char *reason) { PJ_LOG(4,(tp_ice->ice_st->obj_name, "Disabling local ICE, reason=%s", reason)); - pjmedia_ice_stop_ice(&tp_ice->base); + transport_media_stop(&tp_ice->base); } /* * Start ICE checks when both offer and answer are available. */ -PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, - pj_pool_t *pool, - const pjmedia_sdp_session *rem_sdp, - unsigned media_index) +static pj_status_t transport_media_start(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) { struct transport_ice *tp_ice = (struct transport_ice*)tp; const pjmedia_sdp_attr *attr; @@ -481,10 +499,12 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, pj_str_t uname, pass; pj_status_t status; - PJ_ASSERT_RETURN(tp && pool && rem_sdp, PJ_EINVAL); - PJ_ASSERT_RETURN(media_index < rem_sdp->media_count, PJ_EINVAL); + PJ_UNUSED_ARG(sdp_local); + + PJ_ASSERT_RETURN(tp && pool && sdp_remote, PJ_EINVAL); + PJ_ASSERT_RETURN(media_index < sdp_remote->media_count, PJ_EINVAL); - sdp_med = rem_sdp->media[media_index]; + sdp_med = sdp_remote->media[media_index]; /* Get the SDP connection for the media stream. * We'll verify later if the SDP connection address is specified @@ -492,7 +512,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, */ conn = sdp_med->conn; if (conn == NULL) - conn = rem_sdp->conn; + conn = sdp_remote->conn; if (conn == NULL) { /* Unable to find SDP connection */ @@ -507,7 +527,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, "ice-ufrag", NULL); if (attr == NULL) { /* Find ice-ufrag attribute in session descriptor */ - attr = pjmedia_sdp_attr_find2(rem_sdp->attr_count, rem_sdp->attr, + attr = pjmedia_sdp_attr_find2(sdp_remote->attr_count, sdp_remote->attr, "ice-ufrag", NULL); if (attr == NULL) { set_no_ice(tp_ice, "ice-ufrag attribute not found"); @@ -521,7 +541,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, "ice-pwd", NULL); if (attr == NULL) { /* Find ice-pwd attribute in session descriptor */ - attr = pjmedia_sdp_attr_find2(rem_sdp->attr_count, rem_sdp->attr, + attr = pjmedia_sdp_attr_find2(sdp_remote->attr_count, sdp_remote->attr, "ice-pwd", NULL); if (attr == NULL) { set_no_ice(tp_ice, "ice-pwd attribute not found"); @@ -608,15 +628,15 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, } -PJ_DEF(pj_status_t) pjmedia_ice_stop_ice(pjmedia_transport *tp) +static pj_status_t transport_media_stop(pjmedia_transport *tp) { struct transport_ice *tp_ice = (struct transport_ice*)tp; return pj_ice_strans_stop_ice(tp_ice->ice_st); } -static pj_status_t tp_get_info(pjmedia_transport *tp, - pjmedia_sock_info *info) +static pj_status_t transport_get_info(pjmedia_transport *tp, + pjmedia_sock_info *info) { struct transport_ice *tp_ice = (struct transport_ice*)tp; pj_ice_strans *ice_st = tp_ice->ice_st; @@ -648,17 +668,17 @@ static pj_status_t tp_get_info(pjmedia_transport *tp, } -static pj_status_t tp_attach( pjmedia_transport *tp, - void *stream, - const pj_sockaddr_t *rem_addr, - const pj_sockaddr_t *rem_rtcp, - unsigned addr_len, - void (*rtp_cb)(void*, - const void*, - pj_ssize_t), - void (*rtcp_cb)(void*, - const void*, - pj_ssize_t)) +static pj_status_t transport_attach (pjmedia_transport *tp, + void *stream, + const pj_sockaddr_t *rem_addr, + const pj_sockaddr_t *rem_rtcp, + unsigned addr_len, + void (*rtp_cb)(void*, + const void*, + pj_ssize_t), + void (*rtcp_cb)(void*, + const void*, + pj_ssize_t)) { struct transport_ice *tp_ice = (struct transport_ice*)tp; @@ -673,8 +693,8 @@ static pj_status_t tp_attach( pjmedia_transport *tp, } -static void tp_detach(pjmedia_transport *tp, - void *strm) +static void transport_detach(pjmedia_transport *tp, + void *strm) { struct transport_ice *tp_ice = (struct transport_ice*)tp; @@ -686,9 +706,9 @@ static void tp_detach(pjmedia_transport *tp, } -static pj_status_t tp_send_rtp(pjmedia_transport *tp, - const void *pkt, - pj_size_t size) +static pj_status_t transport_send_rtp(pjmedia_transport *tp, + const void *pkt, + pj_size_t size) { struct transport_ice *tp_ice = (struct transport_ice*)tp; @@ -708,9 +728,9 @@ static pj_status_t tp_send_rtp(pjmedia_transport *tp, } -static pj_status_t tp_send_rtcp(pjmedia_transport *tp, - const void *pkt, - pj_size_t size) +static pj_status_t transport_send_rtcp(pjmedia_transport *tp, + const void *pkt, + pj_size_t size) { struct transport_ice *tp_ice = (struct transport_ice*)tp; if (tp_ice->ice_st->comp_cnt > 1) { @@ -798,9 +818,9 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st, /* Simulate lost */ -PJ_DEF(pj_status_t) pjmedia_ice_simulate_lost( pjmedia_transport *tp, - pjmedia_dir dir, - unsigned pct_lost) +static pj_status_t transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost) { struct transport_ice *ice = (struct transport_ice*) tp; @@ -815,3 +835,22 @@ PJ_DEF(pj_status_t) pjmedia_ice_simulate_lost( pjmedia_transport *tp, return PJ_SUCCESS; } + +/* + * Destroy ICE media transport. + */ +static pj_status_t transport_destroy(pjmedia_transport *tp) +{ + struct transport_ice *tp_ice = (struct transport_ice*)tp; + + if (tp_ice->ice_st) { + pj_ice_strans_destroy(tp_ice->ice_st); + /*Must not touch tp_ice after ice_st is destroyed! + (it has the pool) + tp_ice->ice_st = NULL; + */ + } + + return PJ_SUCCESS; +} + diff --git a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c new file mode 100644 index 00000000..405f72c2 --- /dev/null +++ b/pjmedia/src/pjmedia/transport_srtp.c @@ -0,0 +1,1253 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + +#include + +#define THIS_FILE "transport_srtp.c" + +/* Maximum size of packet */ +#define MAX_BUFFER_LEN 1500 +#define MAX_KEY_LEN 32 +#define DEACTIVATE_MEDIA(pool, m) {\ + attr = pjmedia_sdp_attr_create(pool, ID_INACTIVE.ptr, NULL); \ + m->attr[m->attr_count++] = attr; \ + m->desc.port = 0; \ + } + +static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; +static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; +static const pj_str_t ID_INACTIVE = { "inactive", 8 }; +static const pj_str_t ID_CRYPTO = { "crypto", 6 }; + +typedef struct crypto_suite +{ + char *name; + cipher_type_id_t cipher_type; + unsigned cipher_key_len; + auth_type_id_t auth_type; + unsigned auth_key_len; + unsigned srtp_auth_tag_len; + unsigned srtcp_auth_tag_len; + sec_serv_t service; +} crypto_suite; + +/* Crypto suites as defined on RFC 4568 */ +static crypto_suite crypto_suites[] = { + /* plain RTP/RTCP (no cipher & no auth) */ + {"NULL", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none}, + + /* cipher AES_CM, auth HMAC_SHA1, auth tag len = 10 octets */ + {"AES_CM_128_HMAC_SHA1_80", AES_128_ICM, 30, HMAC_SHA1, 20, 10, 10, + sec_serv_conf_and_auth}, + + /* cipher AES_CM, auth HMAC_SHA1, auth tag len = 4 octets */ + {"AES_CM_128_HMAC_SHA1_32", AES_128_ICM, 30, HMAC_SHA1, 20, 4, 10, + sec_serv_conf_and_auth}, + + /* + * F8_128_HMAC_SHA1_8 not supported by libsrtp? + * {"F8_128_HMAC_SHA1_8", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none} + */ +}; + +typedef struct transport_srtp +{ + pjmedia_transport base; /**< Base transport interface. */ + pj_pool_t *pool; + pj_lock_t *mutex; + char tx_buffer[MAX_BUFFER_LEN]; + char rx_buffer[MAX_BUFFER_LEN]; + + pjmedia_srtp_setting setting; + /* SRTP policy */ + pj_bool_t session_inited; + pj_bool_t offerer_side; + pj_bool_t bypass_srtp; + char tx_key[MAX_KEY_LEN]; + char rx_key[MAX_KEY_LEN]; + pjmedia_srtp_crypto tx_policy; + pjmedia_srtp_crypto rx_policy; + + /* libSRTP contexts */ + srtp_t srtp_tx_ctx; + srtp_t srtp_rx_ctx; + + /* Stream information */ + void *user_data; + void (*rtp_cb)( void *user_data, + const void *pkt, + pj_ssize_t size); + void (*rtcp_cb)(void *user_data, + const void *pkt, + pj_ssize_t size); + + /* Transport information */ + pjmedia_transport *real_tp; /**< Underlying transport. */ + +} transport_srtp; + + +/* + * This callback is called by transport when incoming rtp is received + */ +static void srtp_rtp_cb( void *user_data, const void *pkt, pj_ssize_t size); + +/* + * This callback is called by transport when incoming rtcp is received + */ +static void srtp_rtcp_cb( void *user_data, const void *pkt, pj_ssize_t size); + + +/* + * These are media transport operations. + */ +static pj_status_t transport_get_info (pjmedia_transport *tp, + pjmedia_sock_info *info); +static pj_status_t transport_attach (pjmedia_transport *tp, + void *user_data, + const pj_sockaddr_t *rem_addr, + const pj_sockaddr_t *rem_rtcp, + unsigned addr_len, + void (*rtp_cb)(void*, + const void*, + pj_ssize_t), + void (*rtcp_cb)(void*, + const void*, + pj_ssize_t)); +static void transport_detach (pjmedia_transport *tp, + void *strm); +static pj_status_t transport_send_rtp( pjmedia_transport *tp, + const void *pkt, + pj_size_t size); +static pj_status_t transport_send_rtcp(pjmedia_transport *tp, + const void *pkt, + pj_size_t size); +static pj_status_t transport_media_create(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); +static pj_status_t transport_media_start (pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); +static pj_status_t transport_media_stop(pjmedia_transport *tp); +static pj_status_t transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost); +static pj_status_t transport_destroy (pjmedia_transport *tp); + + + +static pjmedia_transport_op transport_srtp_op = +{ + &transport_get_info, + &transport_attach, + &transport_detach, + &transport_send_rtp, + &transport_send_rtcp, + &transport_media_create, + &transport_media_start, + &transport_media_stop, + &transport_simulate_lost, + &transport_destroy +}; + +const char* get_libsrtp_errstr(int err) +{ +#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) + static char *liberr[] = { + "ok", /* err_status_ok = 0 */ + "unspecified failure", /* err_status_fail = 1 */ + "unsupported parameter", /* err_status_bad_param = 2 */ + "couldn't allocate memory", /* err_status_alloc_fail = 3 */ + "couldn't deallocate properly", /* err_status_dealloc_fail = 4 */ + "couldn't initialize", /* err_status_init_fail = 5 */ + "can't process as much data as requested", + /* err_status_terminus = 6 */ + "authentication failure", /* err_status_auth_fail = 7 */ + "cipher failure", /* err_status_cipher_fail = 8 */ + "replay check failed (bad index)", /* err_status_replay_fail = 9 */ + "replay check failed (index too old)", + /* err_status_replay_old = 10 */ + "algorithm failed test routine", /* err_status_algo_fail = 11 */ + "unsupported operation", /* err_status_no_such_op = 12 */ + "no appropriate context found", /* err_status_no_ctx = 13 */ + "unable to perform desired validation", + /* err_status_cant_check = 14 */ + "can't use key any more", /* err_status_key_expired = 15 */ + "error in use of socket", /* err_status_socket_err = 16 */ + "error in use POSIX signals", /* err_status_signal_err = 17 */ + "nonce check failed", /* err_status_nonce_bad = 18 */ + "couldn't read data", /* err_status_read_fail = 19 */ + "couldn't write data", /* err_status_write_fail = 20 */ + "error pasring data", /* err_status_parse_err = 21 */ + "error encoding data", /* err_status_encode_err = 22 */ + "error while using semaphores", /* err_status_semaphore_err = 23 */ + "error while using pfkey" /* err_status_pfkey_err = 24 */ + }; + return liberr[err]; +#else + return NULL; +#endif +} + +static pj_status_t pjmedia_srtp_init_lib(void) +{ + static pj_bool_t initialized = PJ_FALSE; + + if (initialized == PJ_FALSE) { + err_status_t err; + err = srtp_init(); + if (err != err_status_ok) { + PJ_LOG(4, (THIS_FILE, "Failed to init libsrtp.")); + return PJMEDIA_ERRNO_FROM_LIBSRTP(err); + } + + initialized = PJ_TRUE; + } + + return PJ_SUCCESS; +} + +static int get_crypto_idx(const pj_str_t* crypto_name) +{ + int i; + int cs_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); + + /* treat unspecified crypto_name as crypto 'NULL' */ + if (crypto_name->slen == 0) + return 0; + + for (i=0; iclose_member_tp = PJ_TRUE; + opt->use = PJMEDIA_SRTP_OPTIONAL; + + /* Copy default crypto-suites, but skip crypto 'NULL' */ + opt->crypto_count = sizeof(crypto_suites)/sizeof(crypto_suites[0]) - 1; + for (i=0; icrypto_count; ++i) + opt->crypto[i].name = pj_str(crypto_suites[i+1].name); +} + + +/* + * Create an SRTP media transport. + */ +PJ_DEF(pj_status_t) pjmedia_transport_srtp_create( + pjmedia_endpt *endpt, + pjmedia_transport *tp, + const pjmedia_srtp_setting *opt, + pjmedia_transport **p_tp) +{ + pj_pool_t *pool; + transport_srtp *srtp; + pj_status_t status; + int i; + + PJ_ASSERT_RETURN(endpt && p_tp, PJ_EINVAL); + + /* Check crypto availability */ + if (opt->crypto_count == 0 && + opt->use == PJMEDIA_SRTP_MANDATORY) + return PJMEDIA_SRTP_ESDPREQCRYPTO; + + /* Check crypto */ + if (opt->use != PJMEDIA_SRTP_DISABLED) { + for (i=0; i < opt->crypto_count; ++i) { + int cs_idx = get_crypto_idx(&opt->crypto[i].name); + + /* check crypto name */ + if (cs_idx == -1) + return PJMEDIA_SRTP_ENOTSUPCRYPTO; + + /* check key length */ + if (opt->crypto[i].key.slen && + opt->crypto[i].key.slen < + (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len) + return PJMEDIA_SRTP_EINKEYLEN; + } + } + + /* Init libsrtp. */ + status = pjmedia_srtp_init_lib(); + if (status != PJ_SUCCESS) + return status; + + pool = pjmedia_endpt_create_pool(endpt, "srtp%p", 1000, 1000); + srtp = PJ_POOL_ZALLOC_T(pool, transport_srtp); + + srtp->pool = pool; + srtp->session_inited = PJ_FALSE; + srtp->bypass_srtp = PJ_FALSE; + + if (opt) { + srtp->setting = *opt; + if (opt->use == PJMEDIA_SRTP_DISABLED) + srtp->setting.crypto_count = 0; + + for (i=0; i < srtp->setting.crypto_count; ++i) { + int cs_idx = get_crypto_idx(&opt->crypto[i].name); + pj_str_t tmp_key = opt->crypto[i].key; + + /* re-set crypto */ + srtp->setting.crypto[i].name = pj_str(crypto_suites[cs_idx].name); + /* cut key length */ + if (tmp_key.slen) + tmp_key.slen = crypto_suites[cs_idx].cipher_key_len; + pj_strdup(pool, &srtp->setting.crypto[i].key, &tmp_key); + } + } else { + pjmedia_srtp_setting_default(&srtp->setting); + } + + status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &srtp->mutex); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + + /* Initialize base pjmedia_transport */ + pj_memcpy(srtp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); + srtp->base.type = tp->type; + srtp->base.op = &transport_srtp_op; + + /* Set underlying transport */ + srtp->real_tp = tp; + + /* Done */ + *p_tp = &srtp->base; + + return PJ_SUCCESS; +} + + +/* + * Initialize and start SRTP session with the given parameters. + */ +PJ_DEF(pj_status_t) pjmedia_transport_srtp_start( + pjmedia_transport *tp, + const pjmedia_srtp_crypto *tx, + const pjmedia_srtp_crypto *rx) +{ + transport_srtp *srtp = (transport_srtp*) tp; + srtp_policy_t tx_; + srtp_policy_t rx_; + err_status_t err; + int cr_tx_idx = 0; + int au_tx_idx = 0; + int cr_rx_idx = 0; + int au_rx_idx = 0; + int crypto_suites_cnt; + + if (srtp->session_inited) { + PJ_LOG(4, (THIS_FILE, "SRTP could not be re-init'd before deinit'd")); + return PJ_EINVALIDOP; + } + + crypto_suites_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); + + /* Get encryption and authentication method */ + cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name); + if (tx->flags && PJMEDIA_SRTP_NO_ENCRYPTION) + cr_tx_idx = 0; + if (tx->flags && PJMEDIA_SRTP_NO_AUTHENTICATION) + au_tx_idx = 0; + + cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name); + if (rx->flags && PJMEDIA_SRTP_NO_ENCRYPTION) + cr_rx_idx = 0; + if (rx->flags && PJMEDIA_SRTP_NO_AUTHENTICATION) + au_rx_idx = 0; + + /* Check whether the crypto-suite requested is supported */ + if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 || + au_rx_idx == -1) + return PJMEDIA_SRTP_ENOTSUPCRYPTO; + + /* If all options points to 'NULL' method, just bypass SRTP */ + if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) { + srtp->bypass_srtp = PJ_TRUE; + return PJ_SUCCESS; + } + + /* Check key length */ + if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len || + rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len) + return PJMEDIA_SRTP_EINKEYLEN; + + /* Init transmit direction */ + pj_bzero(&tx_, sizeof(srtp_policy_t)); + pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen); + if (cr_tx_idx && au_tx_idx) + tx_.rtp.sec_serv = sec_serv_conf_and_auth; + else if (cr_tx_idx) + tx_.rtp.sec_serv = sec_serv_conf; + else if (au_tx_idx) + tx_.rtp.sec_serv = sec_serv_auth; + else + tx_.rtp.sec_serv = sec_serv_none; + tx_.key = (uint8_t*)srtp->tx_key; + tx_.ssrc.type = ssrc_any_outbound; + tx_.ssrc.value = 0; + tx_.rtp.cipher_type = crypto_suites[cr_tx_idx].cipher_type; + tx_.rtp.cipher_key_len = crypto_suites[cr_tx_idx].cipher_key_len; + tx_.rtp.auth_type = crypto_suites[au_tx_idx].auth_type; + tx_.rtp.auth_key_len = crypto_suites[au_tx_idx].auth_key_len; + tx_.rtp.auth_tag_len = crypto_suites[au_tx_idx].srtp_auth_tag_len; + tx_.rtcp = tx_.rtp; + tx_.rtcp.auth_tag_len = crypto_suites[au_tx_idx].srtcp_auth_tag_len; + tx_.next = NULL; + err = srtp_create(&srtp->srtp_tx_ctx, &tx_); + if (err != err_status_ok) { + return PJMEDIA_ERRNO_FROM_LIBSRTP(err); + } + srtp->tx_policy = *tx; + pj_strset(&srtp->tx_policy.key, srtp->tx_key, tx->key.slen); + srtp->tx_policy.name = + pj_str(crypto_suites[get_crypto_idx(&tx->name)].name); + + + /* Init receive direction */ + pj_bzero(&rx_, sizeof(srtp_policy_t)); + pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen); + if (cr_rx_idx && au_rx_idx) + rx_.rtp.sec_serv = sec_serv_conf_and_auth; + else if (cr_rx_idx) + rx_.rtp.sec_serv = sec_serv_conf; + else if (au_rx_idx) + rx_.rtp.sec_serv = sec_serv_auth; + else + rx_.rtp.sec_serv = sec_serv_none; + rx_.key = (uint8_t*)srtp->rx_key; + rx_.ssrc.type = ssrc_any_inbound; + rx_.ssrc.value = 0; + rx_.rtp.sec_serv = crypto_suites[cr_rx_idx].service; + rx_.rtp.cipher_type = crypto_suites[cr_rx_idx].cipher_type; + rx_.rtp.cipher_key_len = crypto_suites[cr_rx_idx].cipher_key_len; + rx_.rtp.auth_type = crypto_suites[au_rx_idx].auth_type; + rx_.rtp.auth_key_len = crypto_suites[au_rx_idx].auth_key_len; + rx_.rtp.auth_tag_len = crypto_suites[au_rx_idx].srtp_auth_tag_len; + rx_.rtcp = rx_.rtp; + rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len; + rx_.next = NULL; + err = srtp_create(&srtp->srtp_rx_ctx, &rx_); + if (err != err_status_ok) { + srtp_dealloc(srtp->srtp_tx_ctx); + return PJMEDIA_ERRNO_FROM_LIBSRTP(err); + } + srtp->rx_policy = *rx; + pj_strset(&srtp->rx_policy.key, srtp->rx_key, rx->key.slen); + srtp->rx_policy.name = + pj_str(crypto_suites[get_crypto_idx(&rx->name)].name); + + /* Declare SRTP session initialized */ + srtp->session_inited = PJ_TRUE; + + PJ_LOG(5, (THIS_FILE, "TX: %s key=%s", srtp->tx_policy.name.ptr, + octet_string_hex_string(tx->key.ptr, tx->key.slen))); + if (srtp->tx_policy.flags) { + PJ_LOG(5, (THIS_FILE, "TX: disable%s%s", (cr_tx_idx?"":" enc"), + (au_tx_idx?"":" auth"))); + } + + PJ_LOG(5, (THIS_FILE, "RX: %s key=%s", srtp->rx_policy.name.ptr, + octet_string_hex_string(rx->key.ptr, rx->key.slen))); + if (srtp->rx_policy.flags) { + PJ_LOG(5, (THIS_FILE, "RX: disable%s%s", (cr_rx_idx?"":" enc"), + (au_rx_idx?"":" auth"))); + } + + return PJ_SUCCESS; +} + +/* + * Stop SRTP session. + */ +PJ_DEF(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp) +{ + transport_srtp *p_srtp = (transport_srtp*) srtp; + err_status_t err; + + if (!p_srtp->session_inited) + return PJ_SUCCESS; + + err = srtp_dealloc(p_srtp->srtp_rx_ctx); + if (err != err_status_ok) { + PJ_LOG(4, (THIS_FILE, "Failed to dealloc RX SRTP context")); + } + err = srtp_dealloc(p_srtp->srtp_tx_ctx); + if (err != err_status_ok) { + PJ_LOG(4, (THIS_FILE, "Failed to dealloc TX SRTP context")); + } + + p_srtp->session_inited = PJ_FALSE; + + return PJ_SUCCESS; +} + +PJ_DEF(pjmedia_transport *) pjmedia_transport_srtp_get_member( + pjmedia_transport *tp) +{ + transport_srtp *srtp = (transport_srtp*) tp; + + PJ_ASSERT_RETURN(tp, NULL); + + return srtp->real_tp; +} + + +static pj_status_t transport_get_info(pjmedia_transport *tp, + pjmedia_sock_info *info) +{ + transport_srtp *srtp = (transport_srtp*) tp; + + /* put SRTP info as well? */ + return pjmedia_transport_get_info(srtp->real_tp, info); +} + +static pj_status_t transport_attach(pjmedia_transport *tp, + void *user_data, + const pj_sockaddr_t *rem_addr, + const pj_sockaddr_t *rem_rtcp, + unsigned addr_len, + void (*rtp_cb) (void*, const void*, + pj_ssize_t), + void (*rtcp_cb)(void*, const void*, + pj_ssize_t)) +{ + transport_srtp *srtp = (transport_srtp*) tp; + pj_status_t status; + + /* Attach itself to transport */ + status = pjmedia_transport_attach(srtp->real_tp, srtp, rem_addr, rem_rtcp, + addr_len, &srtp_rtp_cb, &srtp_rtcp_cb); + if (status != PJ_SUCCESS) + return status; + + /* Save the callbacks */ + srtp->rtp_cb = rtp_cb; + srtp->rtcp_cb = rtcp_cb; + srtp->user_data = user_data; + + return status; +} + +static void transport_detach(pjmedia_transport *tp, void *strm) +{ + transport_srtp *srtp = (transport_srtp*) tp; + + PJ_ASSERT_ON_FAIL(tp && srtp->real_tp, return); + + PJ_UNUSED_ARG(strm); + pjmedia_transport_detach(srtp->real_tp, srtp); + + /* Clear up application infos from transport */ + srtp->rtp_cb = NULL; + srtp->rtcp_cb = NULL; + srtp->user_data = NULL; +} + +static pj_status_t transport_send_rtp( pjmedia_transport *tp, + const void *pkt, + pj_size_t size) +{ + pj_status_t status; + transport_srtp *srtp = (transport_srtp*) tp; + int len = size; + err_status_t err; + + if (srtp->bypass_srtp) + return pjmedia_transport_send_rtp(srtp->real_tp, pkt, size); + + if (!srtp->session_inited) + return PJ_SUCCESS; + + if (size > sizeof(srtp->tx_buffer)) + return PJ_ETOOBIG; + + pj_lock_acquire(srtp->mutex); + pj_memcpy(srtp->tx_buffer, pkt, size); + + err = srtp_protect(srtp->srtp_tx_ctx, srtp->tx_buffer, &len); + if (err == err_status_ok) { + status = pjmedia_transport_send_rtp(srtp->real_tp, srtp->tx_buffer, len); + } else { + status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); + } + + pj_lock_release(srtp->mutex); + + return status; +} + +static pj_status_t transport_send_rtcp(pjmedia_transport *tp, + const void *pkt, + pj_size_t size) +{ + pj_status_t status; + transport_srtp *srtp = (transport_srtp*) tp; + int len = size; + err_status_t err; + + if (srtp->bypass_srtp) + return pjmedia_transport_send_rtcp(srtp->real_tp, pkt, size); + + if (!srtp->session_inited) + return PJ_SUCCESS; + + if (size > sizeof(srtp->tx_buffer)) + return PJ_ETOOBIG; + + pj_lock_acquire(srtp->mutex); + pj_memcpy(srtp->tx_buffer, pkt, size); + + err = srtp_protect_rtcp(srtp->srtp_tx_ctx, srtp->tx_buffer, &len); + + if (err == err_status_ok) { + status = pjmedia_transport_send_rtcp(srtp->real_tp, srtp->tx_buffer, + len); + } else { + status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); + } + + pj_lock_release(srtp->mutex); + + return status; +} + +static pj_status_t transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost) +{ + transport_srtp *srtp = (transport_srtp *) tp; + + return pjmedia_transport_simulate_lost(srtp->real_tp, dir, pct_lost); +} + +static pj_status_t transport_destroy (pjmedia_transport *tp) +{ + transport_srtp *srtp = (transport_srtp *) tp; + pj_status_t status; + + pj_lock_acquire(srtp->mutex); + + pjmedia_transport_detach(tp, NULL); + + if (srtp->setting.close_member_tp) { + pjmedia_transport_close(srtp->real_tp); + } + + status = pjmedia_transport_srtp_stop(tp); + + pj_lock_release(srtp->mutex); + + pj_lock_destroy(srtp->mutex); + pj_pool_release(srtp->pool); + + return status; +} + +/* + * This callback is called by transport when incoming rtp is received + */ +static void srtp_rtp_cb( void *user_data, const void *pkt, pj_ssize_t size) +{ + transport_srtp *srtp = (transport_srtp *) user_data; + int len = size; + err_status_t err; + + if (srtp->bypass_srtp) { + srtp->rtp_cb(srtp->user_data, pkt, size); + return; + } + + if (size < 0 || size > sizeof(srtp->rx_buffer) || !srtp->session_inited) { + return; + } + + pj_lock_acquire(srtp->mutex); + pj_memcpy(srtp->rx_buffer, pkt, size); + + err = srtp_unprotect(srtp->srtp_rx_ctx, srtp->rx_buffer, &len); + + if (err == err_status_ok) { + srtp->rtp_cb(srtp->user_data, srtp->rx_buffer, len); + } else { + PJ_LOG(5, (THIS_FILE, "Failed to unprotect SRTP size=%d, err=%d", + size, err)); + } + + pj_lock_release(srtp->mutex); +} + +/* + * This callback is called by transport when incoming rtcp is received + */ +static void srtp_rtcp_cb( void *user_data, const void *pkt, pj_ssize_t size) +{ + transport_srtp *srtp = (transport_srtp *) user_data; + int len = size; + err_status_t err; + + if (srtp->bypass_srtp) { + srtp->rtcp_cb(srtp->user_data, pkt, size); + return; + } + + if (size < 0 || size > sizeof(srtp->rx_buffer) || !srtp->session_inited) { + return; + } + + pj_lock_acquire(srtp->mutex); + pj_memcpy(srtp->rx_buffer, pkt, size); + + err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, srtp->rx_buffer, &len); + + if (err == err_status_ok) { + srtp->rtcp_cb(srtp->user_data, srtp->rx_buffer, len); + } else { + PJ_LOG(5, (THIS_FILE, "Failed to unprotect SRTCP size=%d, err=%d", + size, err)); + } + + pj_lock_release(srtp->mutex); +} + +/* Generate crypto attribute, including crypto key. + * If crypto-suite chosen is crypto NULL, just return PJ_SUCCESS, + * and set buffer_len = 0. + */ +static pj_status_t generate_crypto_attr_value(pj_pool_t *pool, + char *buffer, int *buffer_len, + pjmedia_srtp_crypto *crypto, + int tag) +{ + pj_status_t status; + int cs_idx = get_crypto_idx(&crypto->name); + char b64_key[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)+1]; + int b64_key_len = sizeof(b64_key); + + if (cs_idx == -1) + return PJMEDIA_SRTP_ENOTSUPCRYPTO; + + /* Crypto-suite NULL. */ + if (cs_idx == 0) { + *buffer_len = 0; + return PJ_SUCCESS; + } + + /* Generate key if not specified. */ + if (crypto->key.slen == 0) { + pj_bool_t key_ok; + char key[MAX_KEY_LEN]; + err_status_t err; + int i; + + PJ_ASSERT_RETURN(MAX_KEY_LEN >= crypto_suites[cs_idx].cipher_key_len, + PJ_ETOOSMALL); + + do { + key_ok = PJ_TRUE; + + err = crypto_get_random((unsigned char*)key, + crypto_suites[cs_idx].cipher_key_len); + if (err != err_status_ok) { + PJ_LOG(5,(THIS_FILE, "Failed generating random key")); + return PJMEDIA_ERRNO_FROM_LIBSRTP(err); + } + for (i=0; ikey.ptr = pj_pool_zalloc(pool, + crypto_suites[cs_idx].cipher_key_len); + pj_memcpy(crypto->key.ptr, key, crypto_suites[cs_idx].cipher_key_len); + crypto->key.slen = crypto_suites[cs_idx].cipher_key_len; + } + + if (crypto->key.slen != (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len) + return PJMEDIA_SRTP_EINKEYLEN; + + /* Key transmitted via SDP should be base64 encoded. */ + status = pj_base64_encode((pj_uint8_t*)crypto->key.ptr, crypto->key.slen, + b64_key, &b64_key_len); + if (status != PJ_SUCCESS) { + PJ_LOG(5,(THIS_FILE, "Failed encoding plain key to base64")); + return status; + } + + b64_key[b64_key_len] = '\0'; + + PJ_ASSERT_RETURN(*buffer_len >= (crypto->name.slen + \ + b64_key_len + 16), PJ_ETOOSMALL); + + /* Print the crypto attribute value. */ + *buffer_len = pj_ansi_snprintf(buffer, *buffer_len, "%d %s inline:%s", + tag, + crypto_suites[cs_idx].name, + b64_key); + + return PJ_SUCCESS; +} + +/* Parse crypto attribute line */ +static pj_status_t parse_attr_crypto(pj_pool_t *pool, + const pjmedia_sdp_attr *attr, + pjmedia_srtp_crypto *crypto, + int *tag) +{ + pj_str_t input; + char *token; + pj_str_t tmp; + pj_status_t status; + int itmp; + + pj_bzero(crypto, sizeof(*crypto)); + pj_strdup_with_null(pool, &input, &attr->value); + + /* Tag */ + token = strtok(input.ptr, " "); + if (!token) { + PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag")); + return PJMEDIA_SDP_EINATTR; + } + *tag = atoi(token); + if (*tag == 0) + return PJMEDIA_SDP_EINATTR; + + /* Crypto-suite */ + token = strtok(NULL, " "); + if (!token) { + PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite")); + return PJMEDIA_SDP_EINATTR; + } + crypto->name = pj_str(token); + + /* Key method */ + token = strtok(NULL, ":"); + if (!token) { + PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method")); + return PJMEDIA_SDP_EINATTR; + } + if (pj_ansi_stricmp(token, "inline")) { + PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!", + token)); + return PJMEDIA_SDP_EINATTR; + } + + /* Key */ + token = strtok(NULL, "| "); + if (!token) { + PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key")); + return PJMEDIA_SDP_EINATTR; + } + tmp = pj_str(token); + crypto->key.ptr = pj_pool_zalloc(pool, MAX_KEY_LEN); + + /* Decode key */ + itmp = MAX_KEY_LEN; + status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr, + &itmp); + if (status != PJ_SUCCESS) { + PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64")); + return status; + } + crypto->key.slen = itmp; + + return PJ_SUCCESS; +} + +static pj_status_t transport_media_create(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) +{ + struct transport_srtp *srtp = (struct transport_srtp*) tp; + pjmedia_sdp_media *m_rem, *m_loc; + enum { MAXLEN = 512 }; + char buffer[MAXLEN]; + int buffer_len; + pj_status_t status; + pjmedia_sdp_attr *attr; + pj_str_t attr_value; + int i, j; + + PJ_ASSERT_RETURN(tp && pool && sdp_local, PJ_EINVAL); + + pj_bzero(&srtp->rx_policy, sizeof(srtp->tx_policy)); + pj_bzero(&srtp->tx_policy, sizeof(srtp->rx_policy)); + + m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL; + m_loc = sdp_local->media[media_index]; + + /* bypass if media transport is not RTP/AVP or RTP/SAVP */ + if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) != 0 && + pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0) + goto BYPASS_SRTP; + + /* If the media is inactive, do nothing. */ + if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) || + (m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))) + { + goto BYPASS_SRTP; + } + + srtp->offerer_side = !sdp_remote; + + /* Check remote media transport & set local media transport + * based on SRTP usage option. + */ + if (srtp->offerer_side) { + if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { + goto BYPASS_SRTP; + } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { + m_loc->desc.transport = ID_RTP_AVP; + } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { + m_loc->desc.transport = ID_RTP_SAVP; + } + } else { + if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { + if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPINTRANSPORT; + } + } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { + m_loc->desc.transport = m_rem->desc.transport; + } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { + if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPINTRANSPORT; + } + m_loc->desc.transport = ID_RTP_SAVP; + } + } + + /* Generate crypto attribute */ + if (srtp->offerer_side) { + for (i=0; isetting.crypto_count; ++i) { + /* Offer crypto-suites based on setting. */ + buffer_len = MAXLEN; + status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, + &srtp->setting.crypto[i], + i+1); + if (status != PJ_SUCCESS) + return status; + + /* If buffer_len==0, just skip the crypto attribute. */ + if (buffer_len) { + pj_strset(&attr_value, buffer, buffer_len); + attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr, + &attr_value); + m_loc->attr[m_loc->attr_count++] = attr; + } + } + } else { + /* find supported crypto-suite, get the tag, and assign policy_local */ + pjmedia_srtp_crypto tmp_rx_crypto; + pj_bool_t has_crypto_attr = PJ_FALSE; + pj_bool_t has_match = PJ_FALSE; + int chosen_tag = 0; + int tags[64]; /* assume no more than 64 crypto attrs in a media */ + int cr_attr_count = 0; + int k; + + for (i=0; iattr_count; ++i) { + if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) + continue; + + /* SRTP is disabled but there is crypto attr in remote media */ + /* Put the checking here to save a bit memory for parsing */ + if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPINTRANSPORT; + } + + has_crypto_attr = PJ_TRUE; + + status = parse_attr_crypto(srtp->pool, m_rem->attr[i], + &tmp_rx_crypto, &tags[cr_attr_count]); + if (status != PJ_SUCCESS) + return status; + + /* Check duplicated tag */ + for (k=0; ksetting.crypto_count; ++j) + if (pj_stricmp(&tmp_rx_crypto.name, + &srtp->setting.crypto[j].name) == 0) + { + int cs_idx = get_crypto_idx(&tmp_rx_crypto.name); + + /* Force to use test key */ + //char *hex_test_key = "58b29c5c8f42308120ce857e439f2d" + // "7810a8b10ad0b1446be5470faea496"; + //pj_str_t* test_key = &srtp->setting.crypto[j].key; + //char *raw_test_key = pj_pool_zalloc(srtp->pool, 64); + //hex_string_to_octet_string( + // raw_test_key, + // hex_test_key, + // strlen(hex_test_key)); + //pj_strset(test_key, raw_test_key, + // crypto_suites[cs_idx].cipher_key_len); + + if (tmp_rx_crypto.key.slen != + (int)crypto_suites[cs_idx].cipher_key_len) + return PJMEDIA_SRTP_EINKEYLEN; + + srtp->tx_policy = srtp->setting.crypto[j]; + srtp->rx_policy = tmp_rx_crypto; + chosen_tag = tags[cr_attr_count]; + has_match = PJ_TRUE; + break; + } + } + cr_attr_count++; + } + + if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { + /* At this point, it is ensured remote has no crypto attr */ + goto BYPASS_SRTP; + } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { + /* bypass SRTP when no crypto-attr but remote uses RTP/AVP */ + if (!has_crypto_attr && + pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) + goto BYPASS_SRTP; + /* bypass SRTP when nothing match but remote uses RTP/AVP */ + if (!has_match && + pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) + goto BYPASS_SRTP; + } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { + /* do nothing, this is intended */ + } + + /* No crypto attr */ + if (!has_crypto_attr) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPREQCRYPTO; + } + + /* No crypto match */ + if (!has_match) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ENOTSUPCRYPTO; + } + + /* we have to generate crypto answer, + * with srtp->tx_policy matched the offer + * and rem_tag contains matched offer tag. + */ + buffer_len = MAXLEN; + status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, + &srtp->tx_policy, + chosen_tag); + if (status != PJ_SUCCESS) + return status; + + /* If buffer_len==0, just skip the crypto attribute. */ + if (buffer_len) { + pj_strset(&attr_value, buffer, buffer_len); + attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr, + &attr_value); + m_loc->attr[m_loc->attr_count++] = attr; + } + + /* At this point, + * we should have valid rx_policy & tx_policy. + */ + } + goto PROPAGATE_MEDIA_CREATE; + +BYPASS_SRTP: + srtp->bypass_srtp = PJ_TRUE; + +PROPAGATE_MEDIA_CREATE: + return pjmedia_transport_media_create(srtp->real_tp, pool, sdp_local, + sdp_remote, media_index); +} + + + +static pj_status_t transport_media_start(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) +{ + struct transport_srtp *srtp = (struct transport_srtp*) tp; + pjmedia_sdp_media *m_rem, *m_loc; + pj_status_t status; + pjmedia_sdp_attr *attr; + int i; + + PJ_ASSERT_RETURN(tp && pool && sdp_local && sdp_remote, PJ_EINVAL); + + if (srtp->bypass_srtp) + goto BYPASS_SRTP; + + m_rem = sdp_remote->media[media_index]; + m_loc = sdp_local->media[media_index]; + + /* For answerer side, this function will just have to start SRTP */ + + /* Check remote media transport & set local media transport + * based on SRTP usage option. + */ + if (srtp->offerer_side) { + if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { + if (pjmedia_sdp_media_find_attr(m_rem, &ID_CRYPTO, NULL)) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPINCRYPTO; + } + goto BYPASS_SRTP; + } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { + if (pj_stricmp(&m_rem->desc.transport, &m_loc->desc.transport)) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SDP_EINPROTO; + } + } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { + if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP)) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SDP_EINPROTO; + } + } + } + + if (srtp->offerer_side) { + /* find supported crypto-suite, get the tag, and assign policy_local */ + pjmedia_srtp_crypto tmp_tx_crypto; + pj_bool_t has_crypto_attr = PJ_FALSE; + int rem_tag; + + for (i=0; iattr_count; ++i) { + if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) + continue; + + /* more than one crypto attribute in media answer */ + if (has_crypto_attr) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPAMBIGUEANS; + } + + has_crypto_attr = PJ_TRUE; + + status = parse_attr_crypto(srtp->pool, m_rem->attr[i], + &tmp_tx_crypto, &rem_tag); + if (status != PJ_SUCCESS) + return status; + + + /* our offer tag is always ordered by setting */ + if (rem_tag < 1 || rem_tag > srtp->setting.crypto_count) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPINCRYPTOTAG; + } + + /* match the crypto name */ + if (pj_stricmp(&tmp_tx_crypto.name, + &srtp->setting.crypto[rem_tag-1].name) != 0) + { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ECRYPTONOTMATCH; + } + + srtp->tx_policy = srtp->setting.crypto[rem_tag-1]; + srtp->rx_policy = tmp_tx_crypto; + } + + if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { + /* should never reach here */ + goto BYPASS_SRTP; + } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { + if (!has_crypto_attr) + goto BYPASS_SRTP; + } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { + if (!has_crypto_attr) { + DEACTIVATE_MEDIA(pool, m_loc); + return PJMEDIA_SRTP_ESDPREQCRYPTO; + } + } + + /* At this point, + * we should have valid rx_policy & tx_policy. + */ + } + + /* Got policy_local & policy_remote, let's initalize the SRTP */ + status = pjmedia_transport_srtp_start(tp, &srtp->tx_policy, &srtp->rx_policy); + if (status != PJ_SUCCESS) + return status; + + goto PROPAGATE_MEDIA_START; + +BYPASS_SRTP: + srtp->bypass_srtp = PJ_TRUE; + +PROPAGATE_MEDIA_START: + return pjmedia_transport_media_start(srtp->real_tp, pool, + sdp_local, sdp_remote, + media_index); +} + +static pj_status_t transport_media_stop(pjmedia_transport *tp) +{ + struct transport_srtp *srtp = (struct transport_srtp*) tp; + pj_status_t status; + + status = pjmedia_transport_media_stop(srtp->real_tp); + if (status != PJ_SUCCESS) + PJ_LOG(4, (THIS_FILE, "SRTP failed stop underlying media transport.")); + + return pjmedia_transport_srtp_stop(tp); +} + +#endif + + diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c index c15b1b17..a9a4df70 100644 --- a/pjmedia/src/pjmedia/transport_udp.c +++ b/pjmedia/src/pjmedia/transport_udp.c @@ -94,9 +94,12 @@ static void on_rx_rtcp(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); -static pj_status_t transport_get_info(pjmedia_transport *tp, - pjmedia_sock_info *info); -static pj_status_t transport_attach( pjmedia_transport *tp, +/* + * These are media transport operations. + */ +static pj_status_t transport_get_info (pjmedia_transport *tp, + pjmedia_sock_info *info); +static pj_status_t transport_attach (pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, @@ -107,7 +110,7 @@ static pj_status_t transport_attach( pjmedia_transport *tp, void (*rtcp_cb)(void*, const void*, pj_ssize_t)); -static void transport_detach( pjmedia_transport *tp, +static void transport_detach (pjmedia_transport *tp, void *strm); static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, @@ -115,6 +118,21 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp, static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size); +static pj_status_t transport_media_create(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); +static pj_status_t transport_media_start (pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index); +static pj_status_t transport_media_stop(pjmedia_transport *tp); +static pj_status_t transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost); +static pj_status_t transport_destroy (pjmedia_transport *tp); static pjmedia_transport_op transport_udp_op = @@ -124,7 +142,11 @@ static pjmedia_transport_op transport_udp_op = &transport_detach, &transport_send_rtp, &transport_send_rtcp, - &pjmedia_transport_udp_close + &transport_media_create, + &transport_media_start, + &transport_media_stop, + &transport_simulate_lost, + &transport_destroy }; @@ -341,25 +363,15 @@ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, on_error: - pjmedia_transport_udp_close(&tp->base); + transport_destroy(&tp->base); return status; } -/* - * Get media socket info. - */ -PJ_DEF(pj_status_t) pjmedia_transport_udp_get_info( pjmedia_transport *tp, - pjmedia_transport_udp_info *inf) -{ - return transport_get_info(tp, &inf->skinfo); -} - - /** * Close UDP transport. */ -PJ_DEF(pj_status_t) pjmedia_transport_udp_close(pjmedia_transport *tp) +static pj_status_t transport_destroy(pjmedia_transport *tp) { struct transport_udp *udp = (struct transport_udp*) tp; @@ -726,9 +738,45 @@ static pj_status_t transport_send_rtcp(pjmedia_transport *tp, } -PJ_DEF(pj_status_t) pjmedia_transport_udp_simulate_lost(pjmedia_transport *tp, - pjmedia_dir dir, - unsigned pct_lost) +static pj_status_t transport_media_create(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) +{ + PJ_UNUSED_ARG(tp); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(sdp_local); + PJ_UNUSED_ARG(sdp_remote); + + return PJ_SUCCESS; +} + +static pj_status_t transport_media_start(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp_local, + const pjmedia_sdp_session *sdp_remote, + unsigned media_index) +{ + PJ_UNUSED_ARG(tp); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(sdp_local); + PJ_UNUSED_ARG(sdp_remote); + PJ_UNUSED_ARG(media_index); + + return PJ_SUCCESS; +} + +static pj_status_t transport_media_stop(pjmedia_transport *tp) +{ + PJ_UNUSED_ARG(tp); + + return PJ_SUCCESS; +} + +static pj_status_t transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, + unsigned pct_lost) { struct transport_udp *udp = (struct transport_udp*)tp; diff --git a/pjproject-vs8.sln b/pjproject-vs8.sln index 50820bd1..904a5b58 100644 --- a/pjproject-vs8.sln +++ b/pjproject-vs8.sln @@ -1,6 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2005 +# Visual Studio 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib", "pjlib\build\pjlib.vcproj", "{DA0E03ED-53A7-4050-8A85-90541C5509F8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib_test", "pjlib\build\pjlib_test.vcproj", "{6AC3EF61-5A9E-4F43-A809-5B2FD1A43B16}" @@ -12,8 +12,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib_util", "pjlib-util\bu EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib_util_test", "pjlib-util\build\pjlib_util_test.vcproj", "{ED02BE13-8297-4770-8097-27DC2CCABF9A}" ProjectSection(ProjectDependencies) = postProject - {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia", "pjmedia\build\pjmedia.vcproj", "{7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}" @@ -28,65 +28,67 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_ua", "pjsip\build\pjs EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua", "pjsip-apps\build\pjsua.vcproj", "{8310649E-A25E-4AF0-91E8-9E3CC659BB89}" ProjectSection(ProjectDependencies) = postProject + {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} = {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} {2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031} {4BF51C21-5A30-423B-82FE-1ED410E5769D} = {4BF51C21-5A30-423B-82FE-1ED410E5769D} - {E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D} - {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} - {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} - {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} - {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} - {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} - {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} - {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} - {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} - {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} - {6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E} - {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} + {6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E} + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} + {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} + {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} + {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} + {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} + {E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua_lib", "pjsip\build\pjsua_lib.vcproj", "{9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample_debug", "pjsip-apps\build\sample_debug.vcproj", "{A0F1AA62-0F6F-420D-B09A-AC04B6862821}" ProjectSection(ProjectDependencies) = postProject - {E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D} - {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} - {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} - {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} - {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} - {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} - {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} - {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} - {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} - {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} - {6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E} - {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} + {6794B975-4E84-4F49-B2DC-C31F2224E03E} = {6794B975-4E84-4F49-B2DC-C31F2224E03E} + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} + {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} = {3CF9FFA9-8387-4635-9D1B-E7944CBEFEAA} + {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} = {4B059DBA-CD9C-4D0F-BE8C-FFB4EFD498E9} + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} + {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} + {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} + {E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D} {2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031} + {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} = {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "samples", "pjsip-apps\build\samples.vcproj", "{E378A1FC-0C9C-4462-860F-7E60BC1BF84E}" ProjectSection(ProjectDependencies) = postProject - {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} - {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} - {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} - {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} - {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} - {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} - {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} {2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031} + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} + {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_pjsip", "pjsip\build\test_pjsip.vcproj", "{B3F7D4E9-702F-4EB4-ADA8-098D0A83D770}" ProjectSection(ProjectDependencies) = postProject - {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} - {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} - {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} - {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} - {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} - {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} - {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} {2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031} + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} + {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjnath", "pjnath\build\pjnath.vcproj", "{A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}" @@ -105,6 +107,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libresample_dll", "third_pa EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmilenage", "third_party\build\milenage\libmilenage.vcproj", "{4BF51C21-5A30-423B-82FE-1ED410E5769D}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsrtp", "third_party\build\srtp\libsrtp.vcproj", "{F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -199,6 +203,10 @@ Global {4BF51C21-5A30-423B-82FE-1ED410E5769D}.Debug|Win32.Build.0 = Debug|Win32 {4BF51C21-5A30-423B-82FE-1ED410E5769D}.Release|Win32.ActiveCfg = Release|Win32 {4BF51C21-5A30-423B-82FE-1ED410E5769D}.Release|Win32.Build.0 = Release|Win32 + {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}.Debug|Win32.ActiveCfg = Debug|Win32 + {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}.Debug|Win32.Build.0 = Debug|Win32 + {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}.Release|Win32.ActiveCfg = Release|Win32 + {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/pjproject.dsw b/pjproject.dsw index 63860798..7054e5aa 100644 --- a/pjproject.dsw +++ b/pjproject.dsw @@ -3,7 +3,7 @@ Microsoft Developer Studio Workspace File, Format Version 6.00 ############################################################################### -Project: "libgsmcodec"=.\THIRD_PARTY\BUILD\GSM\libgsmcodec.dsp - Package Owner=<4> +Project: "libgsmcodec"=".\THIRD_PARTY\BUILD\GSM\libgsmcodec.dsp" - Package Owner=<4> Package=<5> {{{ @@ -15,7 +15,7 @@ Package=<4> ############################################################################### -Project: "libilbccodec"=.\THIRD_PARTY\BUILD\ILBC\libilbccodec.dsp - Package Owner=<4> +Project: "libilbccodec"=".\THIRD_PARTY\BUILD\ILBC\libilbccodec.dsp" - Package Owner=<4> Package=<5> {{{ @@ -27,7 +27,7 @@ Package=<4> ############################################################################### -Project: "libmilenage"=.\third_party\build\milenage\libmilenage.dsp - Package Owner=<4> +Project: "libmilenage"=".\third_party\build\milenage\libmilenage.dsp" - Package Owner=<4> Package=<5> {{{ @@ -39,7 +39,7 @@ Package=<4> ############################################################################### -Project: "libportaudio"=.\THIRD_PARTY\BUILD\PORTAUDIO\libportaudio.dsp - Package Owner=<4> +Project: "libportaudio"=".\THIRD_PARTY\BUILD\PORTAUDIO\libportaudio.dsp" - Package Owner=<4> Package=<5> {{{ @@ -51,7 +51,7 @@ Package=<4> ############################################################################### -Project: "libresample"=.\THIRD_PARTY\BUILD\RESAMPLE\libresample.dsp - Package Owner=<4> +Project: "libresample"=".\THIRD_PARTY\BUILD\RESAMPLE\libresample.dsp" - Package Owner=<4> Package=<5> {{{ @@ -63,7 +63,7 @@ Package=<4> ############################################################################### -Project: "libresample_dll"=.\THIRD_PARTY\BUILD\RESAMPLE\libresample_dll.dsp - Package Owner=<4> +Project: "libresample_dll"=".\THIRD_PARTY\BUILD\RESAMPLE\libresample_dll.dsp" - Package Owner=<4> Package=<5> {{{ @@ -75,7 +75,7 @@ Package=<4> ############################################################################### -Project: "libspeex"=.\third_party\build\speex\libspeex.dsp - Package Owner=<4> +Project: "libspeex"=".\third_party\build\speex\libspeex.dsp" - Package Owner=<4> Package=<5> {{{ @@ -87,7 +87,7 @@ Package=<4> ############################################################################### -Project: "pjlib"=.\pjlib\build\pjlib.dsp - Package Owner=<4> +Project: "libsrtp"=".\third_party\build\srtp\libsrtp.dsp" - Package Owner=<4> Package=<5> {{{ @@ -99,7 +99,19 @@ Package=<4> ############################################################################### -Project: "pjlib_test"=.\pjlib\build\pjlib_test.dsp - Package Owner=<4> +Project: "pjlib"=".\pjlib\build\pjlib.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pjlib_test"=".\pjlib\build\pjlib_test.dsp" - Package Owner=<4> Package=<5> {{{ @@ -144,7 +156,7 @@ Package=<4> ############################################################################### -Project: "pjmedia"=.\pjmedia\build\pjmedia.dsp - Package Owner=<4> +Project: "pjmedia"=".\pjmedia\build\pjmedia.dsp" - Package Owner=<4> Package=<5> {{{ @@ -156,7 +168,7 @@ Package=<4> ############################################################################### -Project: "pjmedia_codec"=.\pjmedia\build\pjmedia_codec.dsp - Package Owner=<4> +Project: "pjmedia_codec"=".\pjmedia\build\pjmedia_codec.dsp" - Package Owner=<4> Package=<5> {{{ @@ -168,7 +180,7 @@ Package=<4> ############################################################################### -Project: "pjnath"=.\pjnath\build\pjnath.dsp - Package Owner=<4> +Project: "pjnath"=".\pjnath\build\pjnath.dsp" - Package Owner=<4> Package=<5> {{{ @@ -180,7 +192,7 @@ Package=<4> ############################################################################### -Project: "pjsip_core"=.\pjsip\build\pjsip_core.dsp - Package Owner=<4> +Project: "pjsip_core"=".\pjsip\build\pjsip_core.dsp" - Package Owner=<4> Package=<5> {{{ @@ -192,7 +204,7 @@ Package=<4> ############################################################################### -Project: "pjsip_simple"=.\pjsip\build\pjsip_simple.dsp - Package Owner=<4> +Project: "pjsip_simple"=".\pjsip\build\pjsip_simple.dsp" - Package Owner=<4> Package=<5> {{{ @@ -204,7 +216,7 @@ Package=<4> ############################################################################### -Project: "pjsip_ua"=.\pjsip\build\pjsip_ua.dsp - Package Owner=<4> +Project: "pjsip_ua"=".\pjsip\build\pjsip_ua.dsp" - Package Owner=<4> Package=<5> {{{ @@ -269,11 +281,14 @@ Package=<4> Begin Project Dependency Project_Dep_Name libmilenage End Project Dependency + Begin Project Dependency + Project_Dep_Name libsrtp + End Project Dependency }}} ############################################################################### -Project: "pjsua_lib"=.\pjsip\build\pjsua_lib.dsp - Package Owner=<4> +Project: "pjsua_lib"=".\pjsip\build\pjsua_lib.dsp" - Package Owner=<4> Package=<5> {{{ @@ -393,7 +408,7 @@ Package=<4> ############################################################################### -Project: "test_pjsip"=.\pjsip\build\test_pjsip.dsp - Package Owner=<4> +Project: "test_pjsip"=".\pjsip\build\test_pjsip.dsp" - Package Owner=<4> Package=<5> {{{ diff --git a/pjsip-apps/build/Samples-vc.mak b/pjsip-apps/build/Samples-vc.mak index de000dc1..0d862e32 100644 --- a/pjsip-apps/build/Samples-vc.mak +++ b/pjsip-apps/build/Samples-vc.mak @@ -23,9 +23,10 @@ ILBC_LIB = ..\..\third_party\lib\libilbccodec-$(TARGET)$(LIBEXT) PORTAUDIO_LIB = ..\..\third_party\lib\libportaudio-$(TARGET)$(LIBEXT) RESAMPLE_LIB = ..\..\third_party\lib\libresample-$(TARGET)$(LIBEXT) SPEEX_LIB = ..\..\third_party\lib\libspeex-$(TARGET)$(LIBEXT) +SRTP_LIB = ..\..\third_party\lib\libsrtp-$(TARGET)$(LIBEXT) THIRD_PARTY_LIBS = $(GSM_LIB) $(ILBC_LIB) $(PORTAUDIO_LIB) $(RESAMPLE_LIB) \ - $(SPEEX_LIB) + $(SPEEX_LIB) $(SRTP_LIB) LIBS = $(PJSUA_LIB_LIB) $(PJSIP_UA_LIB) $(PJSIP_SIMPLE_LIB) \ $(PJSIP_LIB) $(PJMEDIA_CODEC_LIB) $(PJMEDIA_LIB) $(PJNATH_LIB) \ diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 90f42026..8cc57723 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -120,6 +120,9 @@ static void usage(void) puts (""); puts ("SIP Account options:"); puts (" --use-ims Enable 3GPP/IMS related settings on this account"); +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + puts (" --use-srtp=N Use SRTP N= 0: disabled, 1: optional, 2: mandatory"); +#endif puts (" --registrar=url Set the URL of registrar server"); puts (" --id=url Set the URL of local ID (used in From header)"); puts (" --contact=url Optionally override the Contact information"); @@ -382,7 +385,7 @@ static pj_status_t parse_args(int argc, char *argv[], OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV, OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP, - OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_USE_ICE, + OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_USE_ICE, OPT_USE_SRTP, OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC, OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD, @@ -441,6 +444,9 @@ static pj_status_t parse_args(int argc, char *argv[], { "rec-file", 1, 0, OPT_REC_FILE}, { "rtp-port", 1, 0, OPT_RTP_PORT}, { "use-ice", 0, 0, OPT_USE_ICE}, +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + { "use-srtp", 1, 0, OPT_USE_SRTP}, +#endif { "add-codec", 1, 0, OPT_ADD_CODEC}, { "dis-codec", 1, 0, OPT_DIS_CODEC}, { "complexity", 1, 0, OPT_COMPLEXITY}, @@ -796,6 +802,16 @@ static pj_status_t parse_args(int argc, char *argv[], cfg->media_cfg.enable_ice = PJ_TRUE; break; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + case OPT_USE_SRTP: + app_config.cfg.use_srtp = my_atoi(pj_optarg); + if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 2) { + PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option")); + return -1; + } + break; +#endif + case OPT_RTP_PORT: cfg->rtp_cfg.port = my_atoi(pj_optarg); if (cfg->rtp_cfg.port == 0) { @@ -1111,6 +1127,15 @@ static void write_account_settings(int acc_index, pj_str_t *result) pj_strcat2(result, line); } +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* SRTP */ + if (acc_cfg->use_srtp) { + pj_ansi_sprintf(line, "--use-srtp %i\n", + (int)acc_cfg->use_srtp); + pj_strcat2(result, line); + } +#endif + /* Proxy */ for (i=0; iproxy_cnt; ++i) { pj_ansi_sprintf(line, "--proxy %.*s\n", diff --git a/pjsip-apps/src/samples/simpleua.c b/pjsip-apps/src/samples/simpleua.c index edf0b838..b2881906 100644 --- a/pjsip-apps/src/samples/simpleua.c +++ b/pjsip-apps/src/samples/simpleua.c @@ -285,13 +285,7 @@ int main(int argc, char *argv[]) * need this info to create SDP (i.e. the address and port info in * the SDP). */ - { - pjmedia_transport_udp_info udp_info; - - pjmedia_transport_udp_get_info(g_med_transport, &udp_info); - pj_memcpy(&g_med_skinfo, &udp_info.skinfo, - sizeof(pjmedia_sock_info)); - } + pjmedia_transport_get_info(g_med_transport, &g_med_skinfo); /* diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c index 47a97b27..9ab0b1f4 100644 --- a/pjsip-apps/src/samples/siprtp.c +++ b/pjsip-apps/src/samples/siprtp.c @@ -1005,14 +1005,14 @@ static pj_status_t create_sdp( pj_pool_t *pool, pjmedia_sdp_session *sdp; pjmedia_sdp_media *m; pjmedia_sdp_attr *attr; - pjmedia_transport_udp_info tpinfo; + pjmedia_sock_info tpinfo; struct media_stream *audio = &call->media[0]; PJ_ASSERT_RETURN(pool && p_sdp, PJ_EINVAL); /* Get transport info */ - pjmedia_transport_udp_get_info(audio->transport, &tpinfo); + pjmedia_transport_get_info(audio->transport, &tpinfo); /* Create and initialize basic SDP session */ sdp = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_session)); @@ -1046,7 +1046,7 @@ static pj_status_t create_sdp( pj_pool_t *pool, /* Standard media info: */ m->desc.media = pj_str("audio"); - m->desc.port = pj_ntohs(tpinfo.skinfo.rtp_addr_name.ipv4.sin_port); + m->desc.port = pj_ntohs(tpinfo.rtp_addr_name.ipv4.sin_port); m->desc.port_count = 1; m->desc.transport = pj_str("RTP/AVP"); diff --git a/pjsip-apps/src/samples/streamutil.c b/pjsip-apps/src/samples/streamutil.c index a9dae3ab..c9d28026 100644 --- a/pjsip-apps/src/samples/streamutil.c +++ b/pjsip-apps/src/samples/streamutil.c @@ -55,6 +55,17 @@ static const char *desc = " --send-recv Set stream direction to bidirectional. \n" " --send-only Set stream direction to send only \n" " --recv-only Set stream direction to recv only (default) \n" + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + " --use-srtp[=NAME] Enable SRTP with crypto suite NAME \n" + " e.g: AES_CM_128_HMAC_SHA1_80 (default), \n" + " AES_CM_128_HMAC_SHA1_32 \n" + " Use this option along with the TX & RX keys, \n" + " formated of 60 hex digits (e.g: E148DA..) \n" + " --srtp-tx-key SRTP key for transmiting \n" + " --srtp-rx-key SRTP key for receiving \n" +#endif + "\n" ; @@ -64,6 +75,7 @@ static const char *desc = #include #include #include +#include #include /* atoi() */ #include @@ -78,6 +90,8 @@ static const char *desc = /* Prototype */ static void print_stream_stat(pjmedia_stream *stream); +/* Prototype for LIBSRTP utility in file datatypes.c */ +int hex_string_to_octet_string(char *raw, char *hex, int len); /* * Register all codecs. @@ -122,11 +136,20 @@ static pj_status_t create_stream( pj_pool_t *pool, pjmedia_dir dir, pj_uint16_t local_port, const pj_sockaddr_in *rem_addr, +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + pj_bool_t use_srtp, + const pj_str_t *crypto_suite, + const pj_str_t *srtp_tx_key, + const pj_str_t *srtp_rx_key, +#endif pjmedia_stream **p_stream ) { pjmedia_stream_info info; - pjmedia_transport *transport; + pjmedia_transport *transport = NULL; pj_status_t status; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + pjmedia_transport *srtp_tp = NULL; +#endif /* Reset stream info. */ @@ -158,17 +181,43 @@ static pj_status_t create_stream( pj_pool_t *pool, if (status != PJ_SUCCESS) return status; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* Check if SRTP enabled */ + if (use_srtp) { + pjmedia_srtp_crypto tx_plc, rx_plc; + + status = pjmedia_transport_srtp_create(med_endpt, transport, + NULL, &srtp_tp); + if (status != PJ_SUCCESS) + return status; + + pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto)); + pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto)); + + tx_plc.key = *srtp_tx_key; + tx_plc.name = *crypto_suite; + rx_plc.key = *srtp_rx_key; + rx_plc.name = *crypto_suite; + + status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc); + if (status != PJ_SUCCESS) + return status; + + transport = srtp_tp; + } +#endif /* Now that the stream info is initialized, we can create the * stream. */ status = pjmedia_stream_create( med_endpt, pool, &info, - transport, NULL, p_stream); + transport, + NULL, p_stream); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating stream", status); - pjmedia_transport_udp_close(transport); + pjmedia_transport_close(transport); return status; } @@ -201,6 +250,16 @@ int main(int argc, char *argv[]) char tmp[10]; pj_status_t status; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* SRTP variables */ + pj_bool_t use_srtp = PJ_FALSE; + char tmp_tx_key[64]; + char tmp_rx_key[64]; + pj_str_t srtp_tx_key = {NULL, 0}; + pj_str_t srtp_rx_key = {NULL, 0}; + pj_str_t srtp_crypto_suite = {NULL, 0}; + int tmp_key_len; +#endif /* Default values */ const pjmedia_codec_info *codec_info; @@ -220,6 +279,11 @@ int main(int argc, char *argv[]) OPT_SEND_RECV = 'b', OPT_SEND_ONLY = 's', OPT_RECV_ONLY = 'i', +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + OPT_USE_SRTP = 'S', +#endif + OPT_SRTP_TX_KEY = 'x', + OPT_SRTP_RX_KEY = 'y', OPT_HELP = 'h', }; @@ -232,6 +296,11 @@ int main(int argc, char *argv[]) { "send-recv", 0, 0, OPT_SEND_RECV }, { "send-only", 0, 0, OPT_SEND_ONLY }, { "recv-only", 0, 0, OPT_RECV_ONLY }, +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + { "use-srtp", 2, 0, OPT_USE_SRTP }, + { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY }, + { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY }, +#endif { "help", 0, 0, OPT_HELP }, { NULL, 0, 0, 0 }, }; @@ -298,6 +367,27 @@ int main(int argc, char *argv[]) dir = PJMEDIA_DIR_DECODING; break; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + case OPT_USE_SRTP: + use_srtp = PJ_TRUE; + if (pj_optarg) { + pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg)); + } else { + srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80"); + } + break; + + case OPT_SRTP_TX_KEY: + tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, strlen(pj_optarg)); + pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2); + break; + + case OPT_SRTP_RX_KEY: + tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, strlen(pj_optarg)); + pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2); + break; +#endif + case OPT_HELP: usage(); return 1; @@ -323,6 +413,16 @@ int main(int argc, char *argv[]) dir = PJMEDIA_DIR_ENCODING; } +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* SRTP validation */ + if (use_srtp) { + if (!srtp_tx_key.slen || !srtp_rx_key.slen) + { + printf("Error: Key for each SRTP stream direction must be set\n"); + return 1; + } + } +#endif /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); @@ -368,7 +468,12 @@ int main(int argc, char *argv[]) /* Create stream based on program arguments */ status = create_stream(pool, med_endpt, codec_info, dir, local_port, - &remote_addr, &stream); + &remote_addr, +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + use_srtp, &srtp_crypto_suite, + &srtp_tx_key, &srtp_rx_key, +#endif + &stream); if (status != PJ_SUCCESS) goto on_exit; @@ -537,7 +642,8 @@ on_exit: tp = pjmedia_stream_get_transport(stream); pjmedia_stream_destroy(stream); - pjmedia_transport_udp_close(tp); + + pjmedia_transport_close(tp); } /* Destroy file ports */ diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index a1c5c86d..d78eeb78 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -403,7 +403,28 @@ typedef int pjsua_conf_port_id; # define PJSUA_ACC_MAX_PROXIES 8 #endif +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) +/** + * Default value of SRTP mode usage. Valid values are PJMEDIA_SRTP_DISABLED, + * PJMEDIA_SRTP_OPTIONAL, and PJMEDIA_SRTP_MANDATORY. + */ +#ifndef PJSUA_DEFAULT_USE_SRTP + #define PJSUA_DEFAULT_USE_SRTP PJMEDIA_SRTP_DISABLED +#endif + +/** + * Default value of secure signaling requirement for SRTP. + * Valid values are: + * 0: SRTP does not require secure signaling + * 1: SRTP requires secure transport such as TLS + * 2: SRTP requires secure end-to-end transport (SIPS) + */ +#ifndef PJSUA_DEFAULT_SRTP_SECURE_SIGNALING + #define PJSUA_DEFAULT_SRTP_SECURE_SIGNALING 1 +#endif + +#endif /** * Logging configuration, which can be (optionally) specified when calling @@ -1034,6 +1055,36 @@ typedef struct pjsua_config */ pj_str_t user_agent; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /** + * Specify default value of secure media transport usage. + * Valid values are PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, and + * PJMEDIA_SRTP_MANDATORY. + * + * Note that this setting can be further customized in account + * configuration (#pjsua_acc_config). + * + * Default: #PJSUA_DEFAULT_USE_SRTP + */ + pjmedia_srtp_use use_srtp; + + /** + * Specify whether SRTP requires secure signaling to be used. This option + * is only used when \a use_srtp option above is non-zero. + * + * Valid values are: + * 0: SRTP does not require secure signaling + * 1: SRTP requires secure transport such as TLS + * 2: SRTP requires secure end-to-end transport (SIPS) + * + * Note that this setting can be further customized in account + * configuration (#pjsua_acc_config). + * + * Default: #PJSUA_DEFAULT_SRTP_SECURE_SIGNALING + */ + int srtp_secure_signaling; +#endif + } pjsua_config; @@ -1971,6 +2022,30 @@ typedef struct pjsua_acc_config */ pj_str_t ka_data; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /** + * Specify whether secure media transport should be used for this account. + * Valid values are PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, and + * PJMEDIA_SRTP_MANDATORY. + * + * Default: #PJSUA_DEFAULT_USE_SRTP + */ + pjmedia_srtp_use use_srtp; + + /** + * Specify whether SRTP requires secure signaling to be used. This option + * is only used when \a use_srtp option above is non-zero. + * + * Valid values are: + * 0: SRTP does not require secure signaling + * 1: SRTP requires secure transport such as TLS + * 2: SRTP requires secure end-to-end transport (SIPS) + * + * Default: #PJSUA_DEFAULT_SRTP_SECURE_SIGNALING + */ + int srtp_secure_signaling; +#endif + } pjsua_acc_config; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 943eb694..261cea0e 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -50,7 +50,8 @@ typedef struct pjsua_call int conf_slot; /**< Slot # in conference bridge. */ pjsip_evsub *xfer_sub; /**< Xfer server subscription, if this call was triggered by xfer. */ - pjmedia_transport *med_tp; /**< Media transport. */ + pjmedia_transport *med_tp; /**< Current media transport. */ + pjmedia_transport *med_orig; /**< Original media transport */ pj_timer_entry refresh_tm;/**< Timer to send re-INVITE. */ pj_timer_entry hangup_tm; /**< Timer to hangup call. */ pj_stun_nat_type rem_nat_type; /**< NAT type of remote endpoint. */ @@ -313,12 +314,14 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata); * Media channel. */ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, - pjsip_role_e role); + pjsip_role_e role, + int security_level); pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, pj_pool_t *pool, + const pjmedia_sdp_session *rem_sdp, pjmedia_sdp_session **p_sdp); pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, - const pjmedia_sdp_session *local_sdp, + pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp); pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id); diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 68a18350..6cff391c 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -255,6 +255,44 @@ static pjsua_call_id alloc_call_id(void) return PJSUA_INVALID_ID; } +static pj_bool_t pj_stristr(const pj_str_t *str, const pj_str_t *substr) +{ + int i; + + for (i=0; i<(str->slen-substr->slen); ++i) { + pj_str_t s; + s.ptr = str->ptr+i; + s.slen = substr->slen; + + if (pj_stricmp(&s, substr)==0) + return PJ_TRUE; + } + return PJ_FALSE; +} + +/* Get signaling secure level. + * Return: + * 0: if signaling is not secure + * 1: if TLS transport is used for immediate hop + * 2: if end-to-end signaling is secure. + * + * NOTE: + * THIS IS WRONG. It should take into account the route-set. + */ +static int get_secure_level(const pj_str_t *dst_uri) +{ + const pj_str_t tls = pj_str(";transport=tls"); + const pj_str_t sips = pj_str("sips:"); + + PJ_TODO(Fix_get_secure_level); + + if (pj_stristr(dst_uri, &sips)) + return 2; + if (pj_stristr(dst_uri, &tls)) + return 1; + return 0; +} + /* * Make outgoing call to the specified URI using the specified account. */ @@ -362,7 +400,8 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, } /* Init media channel */ - status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC); + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC, + get_secure_level(dest_uri)); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing media channel", status); goto on_error; @@ -372,7 +411,7 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, #if LATE_SDP offer = NULL; #else - status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer); + status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL, &offer); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status); goto on_error; @@ -519,7 +558,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) int acc_id; pjsua_call *call; int call_id = -1; - pjmedia_sdp_session *answer; + int secure_level; + pjmedia_sdp_session *offer, *answer; pj_status_t status; /* Don't want to handle anything but INVITE */ @@ -614,9 +654,44 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) } } + /* + * Get which account is most likely to be associated with this incoming + * call. We need the account to find which contact URI to put for + * the call. + */ + acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata); + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* Get signaling security level, only when required by SRTP */ + if (pjsua_var.acc[acc_id].cfg.srtp_secure_signaling < 2) { + secure_level = PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport)!=0; + } else +#endif + + { + char *uri; + int uri_len; + pj_str_t dst; + + uri = pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); + uri_len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, + rdata->msg_info.msg->line.req.uri, + uri, PJSIP_MAX_URL_SIZE); + if (uri_len < 1) { + pjsua_perror(THIS_FILE, "Error analyzing dst URI", + PJSIP_EURITOOLONG); + uri_len = 0; + } + + dst.ptr = uri; + dst.slen = uri_len; + + secure_level = get_secure_level(&dst); + } /* Init media channel */ - status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS); + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS, + secure_level); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); @@ -624,9 +699,26 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) return PJ_TRUE; } + /* Parse SDP from incoming request */ + if (rdata->msg_info.msg->body) { + status = pjmedia_sdp_parse(rdata->tp_info.pool, + rdata->msg_info.msg->body->data, + rdata->msg_info.msg->body->len, &offer); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error parsing SDP in incoming INVITE", status); + pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL, + NULL, NULL); + pjsua_media_channel_deinit(call->index); + PJSUA_UNLOCK(); + return PJ_TRUE; + } + } else { + offer = NULL; + } /* Get media capability from media endpoint: */ - status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer); + status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, + offer, &answer); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); @@ -635,20 +727,13 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) return PJ_TRUE; } - /* - * Get which account is most likely to be associated with this incoming - * call. We need the account to find which contact URI to put for - * the call. - */ - acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata); - /* Verify that we can handle the request. */ options |= PJSIP_INV_SUPPORT_100REL; if (pjsua_var.acc[acc_id].cfg.require_100rel) options |= PJSIP_INV_REQUIRE_100REL; - status = pjsip_inv_verify_request(rdata, &options, answer, NULL, - pjsua_var.endpt, &response); + status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL, + pjsua_var.endpt, &response); if (status != PJ_SUCCESS) { /* @@ -1338,7 +1423,8 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, } /* Init media channel */ - status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC); + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC, + get_secure_level(&dlg->remote.info_str)); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing media channel", status); pjsip_dlg_dec_lock(dlg); @@ -1348,7 +1434,8 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, /* Create SDP */ PJ_UNUSED_ARG(unhold); PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg); - status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp); + status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, + NULL, &sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", status); @@ -1406,7 +1493,8 @@ PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id, return status; /* Init media channel */ - status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC); + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC, + get_secure_level(&dlg->remote.info_str)); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing media channel", status); pjsip_dlg_dec_lock(dlg); @@ -1414,7 +1502,8 @@ PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id, } /* Create SDP */ - status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp); + status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, + NULL, &sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", status); @@ -2355,7 +2444,8 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, pj_status_t status) { pjsua_call *call; - const pjmedia_sdp_session *local_sdp; + const pjmedia_sdp_session *c_local; + pjmedia_sdp_session *local_sdp; const pjmedia_sdp_session *remote_sdp; PJSUA_LOCK(); @@ -2384,7 +2474,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, /* Get local and remote SDP */ - status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); + status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &c_local); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to retrieve currently active local SDP", @@ -2393,6 +2483,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, PJSUA_UNLOCK(); return; } + local_sdp = (pjmedia_sdp_session*) c_local; status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp); if (status != PJ_SUCCESS) { @@ -2524,19 +2615,23 @@ static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, "(media in offer is %s)", call->index, remote_state)); status = create_inactive_sdp( call, &answer ); } else { + int secure_level; PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer", call->index)); /* Init media channel */ - status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS); + secure_level = get_secure_level(&call->inv->dlg->remote.info_str); + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS, + secure_level); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing media channel", status); PJSUA_UNLOCK(); return; } - status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer); + status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, + offer, &answer); } if (status != PJ_SUCCESS) { @@ -2576,19 +2671,23 @@ static void pjsua_call_on_create_offer(pjsip_inv_session *inv, call->index)); status = create_inactive_sdp( call, offer ); } else { + int secure_level; PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer", call->index)); /* Init media channel */ - status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC); + secure_level = get_secure_level(&call->inv->dlg->remote.info_str); + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC, + secure_level); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing media channel", status); PJSUA_UNLOCK(); return; } - status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, offer); + status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, + NULL, offer); } if (status != PJ_SUCCESS) { diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index d0d0ad4b..edb537c4 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -87,6 +87,10 @@ PJ_DEF(void) pjsua_config_default(pjsua_config *cfg) cfg->max_calls = 4; cfg->thread_cnt = 1; cfg->nat_type_in_sdp = 1; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP; + cfg->srtp_secure_signaling = PJSUA_DEFAULT_SRTP_SECURE_SIGNALING; +#endif } PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool, @@ -142,6 +146,10 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) cfg->require_100rel = pjsua_var.ua_cfg.require_100rel; cfg->ka_interval = 15; cfg->ka_data = pj_str("\r\n"); +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + cfg->use_srtp = pjsua_var.ua_cfg.use_srtp; + cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling; +#endif } PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg) diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 89fc1e36..eab0b524 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -534,6 +534,7 @@ static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg) status); goto on_error; } + status = pjmedia_transport_udp_attach(pjsua_var.med_endpt, NULL, &skinfo, 0, &pjsua_var.calls[i].med_tp); @@ -543,13 +544,13 @@ static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg) goto on_error; } - pjmedia_transport_udp_simulate_lost(pjsua_var.calls[i].med_tp, - PJMEDIA_DIR_ENCODING, - pjsua_var.media_cfg.tx_drop_pct); + pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp, + PJMEDIA_DIR_ENCODING, + pjsua_var.media_cfg.tx_drop_pct); - pjmedia_transport_udp_simulate_lost(pjsua_var.calls[i].med_tp, - PJMEDIA_DIR_DECODING, - pjsua_var.media_cfg.rx_drop_pct); + pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp, + PJMEDIA_DIR_DECODING, + pjsua_var.media_cfg.rx_drop_pct); } @@ -645,13 +646,13 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg) goto on_error; } - pjmedia_ice_simulate_lost(pjsua_var.calls[i].med_tp, - PJMEDIA_DIR_ENCODING, - pjsua_var.media_cfg.tx_drop_pct); + pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp, + PJMEDIA_DIR_ENCODING, + pjsua_var.media_cfg.tx_drop_pct); - pjmedia_ice_simulate_lost(pjsua_var.calls[i].med_tp, - PJMEDIA_DIR_DECODING, - pjsua_var.media_cfg.rx_drop_pct); + pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp, + PJMEDIA_DIR_DECODING, + pjsua_var.media_cfg.rx_drop_pct); status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, 0, &addr, &pjsua_var.stun_srv.ipv4, NULL); @@ -744,32 +745,65 @@ PJ_DEF(pj_status_t) pjsua_media_transports_create( pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, - pjsip_role_e role) + pjsip_role_e role, + int security_level) { pjsua_call *call = &pjsua_var.calls[call_id]; - if (pjsua_var.media_cfg.enable_ice) { - pj_ice_sess_role ice_role; - pj_status_t status; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; + pjmedia_srtp_setting srtp_opt; + pjmedia_transport *srtp; + pj_status_t status; +#endif + + PJ_UNUSED_ARG(role); - ice_role = (role==PJSIP_ROLE_UAC ? PJ_ICE_SESS_ROLE_CONTROLLING : - PJ_ICE_SESS_ROLE_CONTROLLED); + /* Return error if media transport has not been created yet + * (e.g. application is starting) + */ + if (call->med_tp == NULL) { + return PJ_EBUSY; + } - /* Restart ICE */ - pjmedia_ice_stop_ice(call->med_tp); + /* Stop media transport (for good measure!) */ + pjmedia_transport_media_stop(call->med_tp); - status = pjmedia_ice_init_ice(call->med_tp, ice_role, NULL, NULL); - if (status != PJ_SUCCESS) - return status; +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* Check if SRTP requires secure signaling */ + if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) { + if (security_level < acc->cfg.srtp_secure_signaling) { + return PJSIP_ESESSIONINSECURE; + } } + /* Always create SRTP adapter */ + pjmedia_srtp_setting_default(&srtp_opt); + srtp_opt.close_member_tp = PJ_FALSE; + srtp_opt.use = acc->cfg.use_srtp; + status = pjmedia_transport_srtp_create(pjsua_var.med_endpt, + call->med_tp, + &srtp_opt, &srtp); + if (status != PJ_SUCCESS) + return status; + + /* Set SRTP as current media transport */ + call->med_orig = call->med_tp; + call->med_tp = srtp; +#else + call->med_orig = call->med_tp; + PJ_UNUSED_ARG(security_level); +#endif + return PJ_SUCCESS; } pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, pj_pool_t *pool, + const pjmedia_sdp_session *rem_sdp, pjmedia_sdp_session **p_sdp) { + enum { MAX_MEDIA = 1, MEDIA_IDX = 0 }; pjmedia_sdp_session *sdp; pjmedia_sock_info skinfo; pjsua_call *call = &pjsua_var.calls[call_id]; @@ -786,7 +820,7 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, pjmedia_transport_get_info(call->med_tp, &skinfo); /* Create SDP */ - status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, 1, + status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, MAX_MEDIA, &skinfo, &sdp); if (status != PJ_SUCCESS) goto on_error; @@ -815,11 +849,11 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, } - if (pjsua_var.media_cfg.enable_ice) { - status = pjmedia_ice_modify_sdp(call->med_tp, pool, sdp); - if (status != PJ_SUCCESS) - goto on_error; - } + /* Give the SDP to media transport */ + status = pjmedia_transport_media_create(call->med_tp, pool, + sdp, rem_sdp, MEDIA_IDX); + if (status != PJ_SUCCESS) + goto on_error; *p_sdp = sdp; return PJ_SUCCESS; @@ -858,10 +892,12 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) stop_media_session(call_id); - if (pjsua_var.media_cfg.enable_ice) { - pjmedia_ice_stop_ice(call->med_tp); - } + pjmedia_transport_media_stop(call->med_tp); + if (call->med_tp != call->med_orig) { + pjmedia_transport_close(call->med_tp); + call->med_tp = call->med_orig; + } return PJ_SUCCESS; } @@ -877,14 +913,14 @@ static void dtmf_callback(pjmedia_stream *strm, void *user_data, if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { pjsua_call_id call_id; - call_id = (pjsua_call_id)user_data; + call_id = (pjsua_call_id)(long)user_data; pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit); } } pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, - const pjmedia_sdp_session *local_sdp, + pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) { unsigned i; @@ -911,7 +947,8 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Find which session is audio (we only support audio for now) */ for (i=0; i < sess_info.stream_cnt; ++i) { if (sess_info.stream_info[i].type == PJMEDIA_TYPE_AUDIO && - sess_info.stream_info[i].proto == PJMEDIA_TP_PROTO_RTP_AVP) + (sess_info.stream_info[i].proto == PJMEDIA_TP_PROTO_RTP_AVP || + sess_info.stream_info[i].proto == PJMEDIA_TP_PROTO_RTP_SAVP)) { si = &sess_info.stream_info[i]; break; @@ -945,21 +982,18 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, call->media_dir = PJMEDIA_DIR_NONE; - /* Shutdown ICE session */ - if (pjsua_var.media_cfg.enable_ice) { - pjmedia_ice_stop_ice(call->med_tp); - } + /* Shutdown transport's session */ + pjmedia_transport_media_stop(call->med_tp); /* No need because we need keepalive? */ } else { - /* Start ICE */ - if (pjsua_var.media_cfg.enable_ice) { - status = pjmedia_ice_start_ice(call->med_tp, call->inv->pool, - remote_sdp, 0); - if (status != PJ_SUCCESS) - return status; - } + /* Start media transport */ + status = pjmedia_transport_media_start(call->med_tp, + call->inv->pool, + local_sdp, remote_sdp, 0); + if (status != PJ_SUCCESS) + return status; /* Override ptime, if this option is specified. */ if (pjsua_var.media_cfg.ptime != 0) { @@ -1000,7 +1034,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { pjmedia_session_set_dtmf_callback(call->session, 0, &dtmf_callback, - (void*)(call->index)); + (void*)(long)(call->index)); } /* Get the port interface of the first stream in the session. -- cgit v1.2.3