summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.mak.in2
-rw-r--r--pjmedia/build/Makefile10
-rw-r--r--pjmedia/build/pjmedia.dsp12
-rw-r--r--pjmedia/build/pjmedia.vcproj12
-rw-r--r--pjmedia/include/pjmedia.h1
-rw-r--r--pjmedia/include/pjmedia/config.h8
-rw-r--r--pjmedia/include/pjmedia/errno.h81
-rw-r--r--pjmedia/include/pjmedia/transport.h125
-rw-r--r--pjmedia/include/pjmedia/transport_ice.h80
-rw-r--r--pjmedia/include/pjmedia/transport_srtp.h200
-rw-r--r--pjmedia/include/pjmedia/transport_udp.h55
-rw-r--r--pjmedia/src/pjmedia/errno.c48
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c223
-rw-r--r--pjmedia/src/pjmedia/transport_srtp.c1253
-rw-r--r--pjmedia/src/pjmedia/transport_udp.c88
-rw-r--r--pjproject-vs8.sln88
-rw-r--r--pjproject.dsw49
-rw-r--r--pjsip-apps/build/Samples-vc.mak3
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c27
-rw-r--r--pjsip-apps/src/samples/simpleua.c8
-rw-r--r--pjsip-apps/src/samples/siprtp.c6
-rw-r--r--pjsip-apps/src/samples/streamutil.c116
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h75
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h9
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c147
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c8
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c128
27 files changed, 2454 insertions, 408 deletions
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 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
- AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../.."
+ AdditionalIncludeDirectories="../include;../../pjlib/include;&quot;../../pjlib-util/include&quot;;../../pjnath/include;../../third_party/portaudio/include;../../third_party/speex/include;../../third_party/build/srtp;../../third_party/srtp/include;../../third_party/srtp/crypto/include;../.."
PreprocessorDefinitions="_DEBUG;WIN32;_LIB;PJ_WIN32=1;PJ_M_I386=1"
MinimalRebuild="true"
BasicRuntimeChecks="3"
@@ -966,6 +966,10 @@
>
</File>
<File
+ RelativePath="..\src\pjmedia\transport_srtp.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\transport_udp.c"
>
<FileConfiguration
@@ -1217,6 +1221,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\transport_srtp.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\transport_udp.h"
>
</File>
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 <pjmedia/tonegen.h>
#include <pjmedia/transport.h>
#include <pjmedia/transport_ice.h>
+#include <pjmedia/transport_srtp.h>
#include <pjmedia/transport_udp.h>
#include <pjmedia/wav_playlist.h>
#include <pjmedia/wav_port.h>
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 <pjmedia/sdp.h>
/*
* Forward declaration for media transport.
@@ -244,6 +245,50 @@ struct pjmedia_transport_op
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.
*
* Application should call #pjmedia_transport_close() instead of
@@ -410,6 +455,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 <tt>media_create()</tt> 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 <tt>media_start()</tt> 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 <tt>media_stop()</tt> 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
* <tt>destroy()</tt> member of the transport. This function will free
* all resources created by this transport (such as sockets, memory, etc.).
@@ -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 <benny@prijono.org>
+ *
+ * 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 <pjmedia/transport.h>
+
+
+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
@@ -56,19 +56,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 <portaudio.h>
#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)
@@ -149,25 +168,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.
*/
PJ_DEF(pj_status_t) pjmedia_ice_start_init( pjmedia_transport *tp,
@@ -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; i<cand_cnt; ++i) {
pj_ice_sess_cand *cand;
+ pjmedia_sdp_media *m;
pj_str_t value;
int len;
@@ -329,7 +345,8 @@ PJ_DEF(pj_status_t) pjmedia_ice_modify_sdp(pjmedia_transport *tp,
value = pj_str(buffer);
attr = pjmedia_sdp_attr_create(pool, "candidate", &value);
- sdp->media[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 <benny@prijono.org>
+ *
+ * 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 <pjmedia/transport_srtp.h>
+#include <pjmedia/endpoint.h>
+#include <pjlib-util/base64.h>
+#include <pj/assert.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+
+#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
+
+#include <srtp.h>
+
+#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; i<cs_cnt; ++i) {
+ if (!pj_stricmp2(crypto_name, crypto_suites[i].name))
+ return i;
+ }
+
+ return -1;
+}
+
+PJ_DEF(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt)
+{
+ int i;
+
+ pj_bzero(opt, sizeof(pjmedia_srtp_setting));
+ opt->close_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; i<opt->crypto_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; i<crypto_suites[cs_idx].cipher_key_len && key_ok; ++i)
+ if (key[i] == 0) key_ok = PJ_FALSE;
+
+ } while (!key_ok);
+ crypto->key.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; i<srtp->setting.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; i<m_rem->attr_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; k<cr_attr_count; ++k) {
+ if (tags[k] == tags[cr_attr_count]) {
+ DEACTIVATE_MEDIA(pool, m_loc);
+ return PJMEDIA_SRTP_ESDPDUPCRYPTOTAG;
+ }
+ }
+
+ if (!has_match) {
+ /* lets see if the crypto-suite offered is supported */
+ for (j=0; j<srtp->setting.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; i<m_rem->attr_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; i<acc_cfg->proxy_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 <pjlib-util.h>
#include <pjmedia.h>
#include <pjmedia-codec.h>
+#include <pjmedia/transport_srtp.h>
#include <stdlib.h> /* atoi() */
#include <stdio.h>
@@ -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.