提交 dabb85c3 authored 作者: Anthony Minessale's avatar Anthony Minessale

wip

上级 e00ede7e
...@@ -232,6 +232,7 @@ libfreeswitch_la_SOURCES = \ ...@@ -232,6 +232,7 @@ libfreeswitch_la_SOURCES = \
src/switch_core_memory.c \ src/switch_core_memory.c \
src/switch_core_codec.c \ src/switch_core_codec.c \
src/switch_core_file.c \ src/switch_core_file.c \
src/switch_core_cert.c \
src/switch_core_hash.c \ src/switch_core_hash.c \
src/switch_core_sqldb.c \ src/switch_core_sqldb.c \
src/switch_core_session.c \ src/switch_core_session.c \
......
...@@ -76,6 +76,11 @@ AC_ARG_WITH([grammardir], ...@@ -76,6 +76,11 @@ AC_ARG_WITH([grammardir],
AC_SUBST(grammardir) AC_SUBST(grammardir)
AC_DEFINE_UNQUOTED([SWITCH_GRAMMAR_DIR],"${grammardir}",[where to put grammar files]) AC_DEFINE_UNQUOTED([SWITCH_GRAMMAR_DIR],"${grammardir}",[where to put grammar files])
AC_ARG_WITH([certsdir],
[AS_HELP_STRING([--with-certsdir=DIR], [Put certs files into this location (default: $prefix/certs)])], [certsdir="$withval"], [certsdir="$prefix/certs"])
AC_SUBST(certsdir)
AC_DEFINE_UNQUOTED([SWITCH_CERTS_DIR],"${certsdir}",[where to put certs files])
AC_ARG_WITH([scriptdir], AC_ARG_WITH([scriptdir],
[AS_HELP_STRING([--with-scriptdir=DIR], [Put script files into this location (default: $prefix/scripts)])], [scriptdir="$withval"], [scriptdir="$prefix/scripts"]) [AS_HELP_STRING([--with-scriptdir=DIR], [Put script files into this location (default: $prefix/scripts)])], [scriptdir="$withval"], [scriptdir="$prefix/scripts"])
AC_SUBST(scriptdir) AC_SUBST(scriptdir)
......
...@@ -40,6 +40,12 @@ ...@@ -40,6 +40,12 @@
#define SWITCH_CORE_H #define SWITCH_CORE_H
#include <switch.h> #include <switch.h>
#include <openssl/pem.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
SWITCH_BEGIN_EXTERN_C SWITCH_BEGIN_EXTERN_C
#define SWITCH_MAX_CORE_THREAD_SESSION_OBJS 128 #define SWITCH_MAX_CORE_THREAD_SESSION_OBJS 128
...@@ -77,6 +83,37 @@ typedef struct switch_thread_data_s { ...@@ -77,6 +83,37 @@ typedef struct switch_thread_data_s {
} switch_thread_data_t; } switch_thread_data_t;
#define DTLS_SRTP_FNAME "dtls-srtp"
#define MAX_FPLEN 64
#define MAX_FPSTRLEN 192
typedef struct dtls_fp_s {
uint32_t len;
uint8_t data[MAX_FPLEN+1];
char *type;
char str[MAX_FPSTRLEN];
} dtls_fingerprint_t;
typedef enum {
DTLS_TYPE_CLIENT = (1 << 0),
DTLS_TYPE_SERVER = (1 << 1),
DTLS_TYPE_RTP = (1 << 2),
DTLS_TYPE_RTCP = (1 << 3)
} dtls_type_t;
typedef enum {
DS_HANDSHAKE,
DS_SETUP,
DS_READY,
DS_FAIL,
DS_INVALID,
} dtls_state_t;
#define MESSAGE_STAMP_FFL(_m) _m->_file = __FILE__; _m->_func = __SWITCH_FUNC__; _m->_line = __LINE__ #define MESSAGE_STAMP_FFL(_m) _m->_file = __FILE__; _m->_func = __SWITCH_FUNC__; _m->_line = __LINE__
#define MESSAGE_STRING_ARG_MAX 10 #define MESSAGE_STRING_ARG_MAX 10
...@@ -2478,6 +2515,12 @@ SWITCH_DECLARE(void) switch_sql_queue_manger_execute_sql_event_callback(switch_s ...@@ -2478,6 +2515,12 @@ SWITCH_DECLARE(void) switch_sql_queue_manger_execute_sql_event_callback(switch_s
SWITCH_DECLARE(pid_t) switch_fork(void); SWITCH_DECLARE(pid_t) switch_fork(void);
SWITCH_DECLARE(int) switch_core_gen_certs(const char *prefix);
SWITCH_DECLARE(int) switch_core_cert_gen_fingerprint(const char *prefix, dtls_fingerprint_t *fp);
SWITCH_DECLARE(int) switch_core_cert_expand_fingerprint(dtls_fingerprint_t *fp, const char *str);
SWITCH_DECLARE(int) switch_core_cert_extract_fingerprint(X509* x509, dtls_fingerprint_t *fp);
SWITCH_DECLARE(int) switch_core_cert_verify(dtls_fingerprint_t *fp);
SWITCH_END_EXTERN_C SWITCH_END_EXTERN_C
#endif #endif
/* For Emacs: /* For Emacs:
......
...@@ -96,7 +96,7 @@ typedef struct icand_s { ...@@ -96,7 +96,7 @@ typedef struct icand_s {
#define MAX_CAND 25 #define MAX_CAND 25
typedef struct ice_s { typedef struct ice_s {
icand_t cands[2][MAX_CAND]; icand_t cands[MAX_CAND][2];
int cand_idx; int cand_idx;
int chosen; int chosen;
char *ufrag; char *ufrag;
...@@ -506,6 +506,9 @@ SWITCH_DECLARE(switch_rtp_stats_t *) switch_rtp_get_stats(switch_rtp_t *rtp_sess ...@@ -506,6 +506,9 @@ SWITCH_DECLARE(switch_rtp_stats_t *) switch_rtp_get_stats(switch_rtp_t *rtp_sess
SWITCH_DECLARE(switch_byte_t) switch_rtp_check_auto_adj(switch_rtp_t *rtp_session); SWITCH_DECLARE(switch_byte_t) switch_rtp_check_auto_adj(switch_rtp_t *rtp_session);
SWITCH_DECLARE(void) switch_rtp_set_interdigit_delay(switch_rtp_t *rtp_session, uint32_t delay); SWITCH_DECLARE(void) switch_rtp_set_interdigit_delay(switch_rtp_t *rtp_session, uint32_t delay);
SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, dtls_fingerprint_t *local_fp, dtls_fingerprint_t *remote_fp, dtls_type_t type);
/*! /*!
\} \}
*/ */
......
...@@ -486,6 +486,7 @@ struct switch_directories { ...@@ -486,6 +486,7 @@ struct switch_directories {
char *recordings_dir; char *recordings_dir;
char *sounds_dir; char *sounds_dir;
char *lib_dir; char *lib_dir;
char *certs_dir;
}; };
typedef struct switch_directories switch_directories; typedef struct switch_directories switch_directories;
...@@ -1297,6 +1298,7 @@ typedef enum { ...@@ -1297,6 +1298,7 @@ typedef enum {
CF_RTP_NOTIMER_DURING_BRIDGE, CF_RTP_NOTIMER_DURING_BRIDGE,
CF_WEBRTC, CF_WEBRTC,
CF_ICE, CF_ICE,
CF_DTLS,
/* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */ /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
/* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */ /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */
CF_FLAG_MAX CF_FLAG_MAX
......
...@@ -711,6 +711,17 @@ SWITCH_DECLARE(void) switch_core_set_globals(void) ...@@ -711,6 +711,17 @@ SWITCH_DECLARE(void) switch_core_set_globals(void)
#endif #endif
} }
if (!SWITCH_GLOBAL_dirs.certs_dir && (SWITCH_GLOBAL_dirs.certs_dir = (char *) malloc(BUFSIZE))) {
if (SWITCH_GLOBAL_dirs.base_dir)
switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s%scert", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
else
#ifdef SWITCH_CERTS_DIR
switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s", SWITCH_CERTS_DIR);
#else
switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s%scert", base_dir, SWITCH_PATH_SEPARATOR);
#endif
}
if (!SWITCH_GLOBAL_dirs.temp_dir && (SWITCH_GLOBAL_dirs.temp_dir = (char *) malloc(BUFSIZE))) { if (!SWITCH_GLOBAL_dirs.temp_dir && (SWITCH_GLOBAL_dirs.temp_dir = (char *) malloc(BUFSIZE))) {
#ifdef SWITCH_TEMP_DIR #ifdef SWITCH_TEMP_DIR
switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", SWITCH_TEMP_DIR); switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", SWITCH_TEMP_DIR);
...@@ -745,6 +756,7 @@ SWITCH_DECLARE(void) switch_core_set_globals(void) ...@@ -745,6 +756,7 @@ SWITCH_DECLARE(void) switch_core_set_globals(void)
switch_assert(SWITCH_GLOBAL_dirs.grammar_dir); switch_assert(SWITCH_GLOBAL_dirs.grammar_dir);
switch_assert(SWITCH_GLOBAL_dirs.recordings_dir); switch_assert(SWITCH_GLOBAL_dirs.recordings_dir);
switch_assert(SWITCH_GLOBAL_dirs.sounds_dir); switch_assert(SWITCH_GLOBAL_dirs.sounds_dir);
switch_assert(SWITCH_GLOBAL_dirs.certs_dir);
switch_assert(SWITCH_GLOBAL_dirs.temp_dir); switch_assert(SWITCH_GLOBAL_dirs.temp_dir);
} }
...@@ -1593,7 +1605,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc ...@@ -1593,7 +1605,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc
switch_dir_make_recursive(SWITCH_GLOBAL_dirs.recordings_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.recordings_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
switch_dir_make_recursive(SWITCH_GLOBAL_dirs.sounds_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.sounds_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
switch_dir_make_recursive(SWITCH_GLOBAL_dirs.temp_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool); switch_dir_make_recursive(SWITCH_GLOBAL_dirs.temp_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
switch_dir_make_recursive(SWITCH_GLOBAL_dirs.certs_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
switch_mutex_init(&runtime.uuid_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); switch_mutex_init(&runtime.uuid_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool);
......
...@@ -137,6 +137,10 @@ typedef struct switch_rtp_engine_s { ...@@ -137,6 +137,10 @@ typedef struct switch_rtp_engine_s {
int8_t rtcp_mux; int8_t rtcp_mux;
dtls_fingerprint_t local_dtls_fingerprint;
dtls_fingerprint_t remote_dtls_fingerprint;
} switch_rtp_engine_t; } switch_rtp_engine_t;
...@@ -742,7 +746,7 @@ SWITCH_DECLARE(void) switch_core_session_apply_crypto(switch_core_session_t *ses ...@@ -742,7 +746,7 @@ SWITCH_DECLARE(void) switch_core_session_apply_crypto(switch_core_session_t *ses
engine = &session->media_handle->engines[type]; engine = &session->media_handle->engines[type];
if (engine->ssec.remote_crypto_key && switch_channel_test_flag(session->channel, CF_SECURE)) { if (engine->ssec.remote_crypto_key && switch_channel_test_flag(session->channel, CF_SECURE) && !switch_channel_test_flag(session->channel, CF_DTLS)) {
switch_core_media_add_crypto(&engine->ssec, engine->ssec.remote_crypto_key, SWITCH_RTP_CRYPTO_RECV); switch_core_media_add_crypto(&engine->ssec, engine->ssec.remote_crypto_key, SWITCH_RTP_CRYPTO_RECV);
...@@ -801,7 +805,8 @@ SWITCH_DECLARE(int) switch_core_session_check_incoming_crypto(switch_core_sessio ...@@ -801,7 +805,8 @@ SWITCH_DECLARE(int) switch_core_session_check_incoming_crypto(switch_core_sessio
switch_channel_set_variable(session->channel, "srtp_remote_audio_crypto_key", crypto); switch_channel_set_variable(session->channel, "srtp_remote_audio_crypto_key", crypto);
engine->ssec.crypto_tag = crypto_tag; engine->ssec.crypto_tag = crypto_tag;
if (switch_rtp_ready(engine->rtp_session) && switch_channel_test_flag(session->channel, CF_SECURE)) { if (switch_rtp_ready(engine->rtp_session) && switch_channel_test_flag(session->channel, CF_SECURE) &&
!switch_channel_test_flag(session->channel, CF_DTLS)) {
switch_core_media_add_crypto(&engine->ssec, engine->ssec.remote_crypto_key, SWITCH_RTP_CRYPTO_RECV); switch_core_media_add_crypto(&engine->ssec, engine->ssec.remote_crypto_key, SWITCH_RTP_CRYPTO_RECV);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_RECV, engine->ssec.crypto_tag, switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_RECV, engine->ssec.crypto_tag,
engine->ssec.crypto_type, engine->ssec.remote_raw_key, SWITCH_RTP_KEY_LEN); engine->ssec.crypto_type, engine->ssec.remote_raw_key, SWITCH_RTP_KEY_LEN);
...@@ -840,7 +845,7 @@ SWITCH_DECLARE(void) switch_core_session_check_outgoing_crypto(switch_core_sessi ...@@ -840,7 +845,7 @@ SWITCH_DECLARE(void) switch_core_session_check_outgoing_crypto(switch_core_sessi
switch_channel_t *channel = switch_core_session_get_channel(session); switch_channel_t *channel = switch_core_session_get_channel(session);
const char *var; const char *var;
if (!switch_core_session_media_handle_ready(session) == SWITCH_STATUS_SUCCESS) { if (!switch_core_session_media_handle_ready(session) == SWITCH_STATUS_SUCCESS || switch_channel_test_flag(channel, CF_DTLS)) {
return; return;
} }
...@@ -1771,6 +1776,21 @@ SWITCH_DECLARE(void) switch_core_media_check_video_codecs(switch_core_session_t ...@@ -1771,6 +1776,21 @@ SWITCH_DECLARE(void) switch_core_media_check_video_codecs(switch_core_session_t
} }
} }
//?
static void generate_local_fingerprint(switch_media_handle_t *smh, switch_media_type_t type)
{
switch_rtp_engine_t *engine = &smh->engines[type];
engine->local_dtls_fingerprint.type = "sha-256";
switch_core_cert_gen_fingerprint(DTLS_SRTP_FNAME, &engine->local_dtls_fingerprint);
//engine->local_dtls_fingerprint.data[];
}
//? //?
static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_media_t *m) static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_media_t *m)
{ {
...@@ -1799,6 +1819,28 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_ ...@@ -1799,6 +1819,28 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_
engine->ice_in.pwd = switch_core_session_strdup(smh->session, attr->a_value); engine->ice_in.pwd = switch_core_session_strdup(smh->session, attr->a_value);
} else if (!strcasecmp(attr->a_name, "ice-options")) { } else if (!strcasecmp(attr->a_name, "ice-options")) {
engine->ice_in.options = switch_core_session_strdup(smh->session, attr->a_value); engine->ice_in.options = switch_core_session_strdup(smh->session, attr->a_value);
} else if (!strcasecmp(attr->a_name, "fingerprint") && !zstr(attr->a_value)) {
//a=fingerprint:sha-256 B6:14:E2:59:58:C9:DD:44:50:91:D4:75:AE:23:9F:67:9F:8E:C2:B3:36:62:C7:9C:F4:25:1F:F3:EF:58:B1:BF
char *p;
engine->remote_dtls_fingerprint.type = switch_core_session_strdup(smh->session, attr->a_value);
if ((p = strchr(engine->remote_dtls_fingerprint.type, ' '))) {
*p++ = '\0';
switch_set_string(engine->local_dtls_fingerprint.str, p);
}
if (strcasecmp(engine->remote_dtls_fingerprint.type, "sha-256")) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(smh->session), SWITCH_LOG_WARNING, "Unsupported fingerprint type.\n");
engine->local_dtls_fingerprint.type = NULL;
engine->remote_dtls_fingerprint.type = NULL;
}
generate_local_fingerprint(smh, type);
switch_channel_set_flag(smh->session->channel, CF_DTLS);
#ifdef RTCP_MUX #ifdef RTCP_MUX
} else if (!strcasecmp(attr->a_name, "rtcp-mux")) { } else if (!strcasecmp(attr->a_name, "rtcp-mux")) {
...@@ -3652,6 +3694,16 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi ...@@ -3652,6 +3694,16 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi
} }
if (!zstr(a_engine->local_dtls_fingerprint.str)) {
dtls_type_t dtype = switch_channel_direction(smh->session->channel) == SWITCH_CALL_DIRECTION_INBOUND ? DTLS_TYPE_SERVER : DTLS_TYPE_CLIENT;
dtype |= DTLS_TYPE_RTP;
if (a_engine->rtcp_mux > 0) dtype |= DTLS_TYPE_RTCP;
switch_rtp_add_dtls(a_engine->rtp_session, &a_engine->local_dtls_fingerprint, &a_engine->remote_dtls_fingerprint, dtype);
}
if (a_engine->ice_in.cands[a_engine->ice_in.chosen][0].ready) { if (a_engine->ice_in.cands[a_engine->ice_in.chosen][0].ready) {
gen_ice(session, SWITCH_MEDIA_TYPE_AUDIO, NULL, 0); gen_ice(session, SWITCH_MEDIA_TYPE_AUDIO, NULL, 0);
...@@ -4756,8 +4808,11 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess ...@@ -4756,8 +4808,11 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
} }
if (!zstr(a_engine->local_dtls_fingerprint.type)) {
switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=fingerprint:%s %s\n", a_engine->local_dtls_fingerprint.type,
a_engine->local_dtls_fingerprint.str);
}
if (smh->mparams->rtcp_audio_interval_msec) { if (smh->mparams->rtcp_audio_interval_msec) {
if (a_engine->rtcp_mux > 0) { if (a_engine->rtcp_mux > 0) {
switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp-mux\n"); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp-mux\n");
...@@ -4837,7 +4892,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess ...@@ -4837,7 +4892,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
if (!zstr(local_audio_crypto_key) && switch_channel_test_flag(session->channel, CF_SECURE)) { if (!zstr(local_audio_crypto_key) && switch_channel_test_flag(session->channel, CF_SECURE) && !switch_channel_test_flag(session->channel, CF_DTLS)) {
switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=crypto:%s\n", local_audio_crypto_key); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=crypto:%s\n", local_audio_crypto_key);
//switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=encryption:optional\n"); //switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=encryption:optional\n");
} }
...@@ -5051,6 +5106,11 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess ...@@ -5051,6 +5106,11 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
} }
if (!zstr(v_engine->local_dtls_fingerprint.type)) {
switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=fingerprint:%s %s\n", v_engine->local_dtls_fingerprint.type,
v_engine->local_dtls_fingerprint.str);
}
if (smh->mparams->rtcp_audio_interval_msec) { if (smh->mparams->rtcp_audio_interval_msec) {
if (v_engine->rtcp_mux > 0) { if (v_engine->rtcp_mux > 0) {
...@@ -5130,7 +5190,8 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess ...@@ -5130,7 +5190,8 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess
if (switch_channel_test_flag(session->channel, CF_SECURE) && !zstr(local_video_crypto_key)) { if (switch_channel_test_flag(session->channel, CF_SECURE) && !zstr(local_video_crypto_key) &&
!switch_channel_test_flag(session->channel, CF_DTLS)) {
switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=crypto:%s\n", local_video_crypto_key); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=crypto:%s\n", local_video_crypto_key);
//switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=encryption:optional\n"); //switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=encryption:optional\n");
} }
...@@ -6807,7 +6868,7 @@ SWITCH_DECLARE (void) switch_core_media_recover_session(switch_core_session_t *s ...@@ -6807,7 +6868,7 @@ SWITCH_DECLARE (void) switch_core_media_recover_session(switch_core_session_t *s
SWITCH_DECLARE(void) switch_core_media_init(void) SWITCH_DECLARE(void) switch_core_media_init(void)
{ {
switch_core_gen_certs(DTLS_SRTP_FNAME);
} }
SWITCH_DECLARE(void) switch_core_media_deinit(void) SWITCH_DECLARE(void) switch_core_media_deinit(void)
......
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论