PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt) { #if PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT if (libsrtp_initialized == PJ_FALSE) { err_status_t err; err = srtp_init(); if (err != err_status_ok) { PJ_LOG(4, (THIS_FILE, "Failed to initialize libsrtp: %s", get_libsrtp_errstr(err))); return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS) { /* There will be memory leak when it fails to schedule libsrtp * deinitialization, however the memory leak could be harmless, * since in modern OS's memory used by an application is released * when the application terminates. */ PJ_LOG(4, (THIS_FILE, "Failed to register libsrtp deinit.")); } libsrtp_initialized = PJ_TRUE; } #else PJ_UNUSED_ARG(endpt); #endif return PJ_SUCCESS; }
static void pjmedia_srtp_deinit_lib(void) { err_status_t err; err = srtp_deinit(); if (err != err_status_ok) { PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s", get_libsrtp_errstr(err))); } libsrtp_initialized = PJ_FALSE; }
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 initialize libsrtp: %s", get_libsrtp_errstr(err))); return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } initialized = PJ_TRUE; } return PJ_SUCCESS; }
/* * This callback is called by transport when incoming rtcp is received */ static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = size; err_status_t err; void (*cb)(void*, void*, pj_ssize_t) = NULL; void *cb_data = NULL; if (srtp->bypass_srtp) { srtp->rtcp_cb(srtp->user_data, pkt, size); return; } if (size < 0) { return; } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return; } err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (err != err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTCP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } else { cb = srtp->rtcp_cb; cb_data = srtp->user_data; } pj_lock_release(srtp->mutex); if (cb) { (*cb)(cb_data, pkt, len); } }
static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt) { err_status_t err; /* Note that currently this SRTP init/deinit is not equipped with * reference counter, it should be safe as normally there is only * one single instance of media endpoint and even if it isn't, the * pjmedia_transport_srtp_create() will invoke SRTP init (the only * drawback should be the delay described by #788). */ PJ_UNUSED_ARG(endpt); err = srtp_shutdown(); if (err != err_status_ok) { PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s", get_libsrtp_errstr(err))); } libsrtp_initialized = PJ_FALSE; }
/* Utility */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp, pj_bool_t is_rtp, void *pkt, int *pkt_len) { transport_srtp *srtp = (transport_srtp *)tp; srtp_err_status_t err; if (srtp->bypass_srtp) return PJ_SUCCESS; PJ_ASSERT_RETURN(tp && pkt && (*pkt_len>0), PJ_EINVAL); PJ_ASSERT_RETURN(srtp->session_inited, PJ_EINVALIDOP); /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return PJ_EINVAL); pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return PJ_EINVALIDOP; } if (is_rtp) err = srtp_unprotect(srtp->srtp_rx_ctx, pkt, pkt_len); else err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, pkt, pkt_len); if (err != srtp_err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", *pkt_len, get_libsrtp_errstr(err))); } pj_lock_release(srtp->mutex); return (err==srtp_err_status_ok) ? PJ_SUCCESS : PJMEDIA_ERRNO_FROM_LIBSRTP(err); }
PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt) { pj_status_t status = PJ_SUCCESS; if (libsrtp_initialized) return PJ_SUCCESS; #if PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT /* Init libsrtp */ { srtp_err_status_t err; err = srtp_init(); if (err != srtp_err_status_ok) { PJ_LOG(4, (THIS_FILE, "Failed to initialize libsrtp: %s", get_libsrtp_errstr(err))); return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } } #endif #if defined(PJMEDIA_SRTP_HAS_DTLS) && (PJMEDIA_SRTP_HAS_DTLS != 0) dtls_init(); #endif if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS) { /* There will be memory leak when it fails to schedule libsrtp * deinitialization, however the memory leak could be harmless, * since in modern OS's memory used by an application is released * when the application terminates. */ PJ_LOG(4, (THIS_FILE, "Failed to register libsrtp deinit.")); } libsrtp_initialized = PJ_TRUE; return status; }
static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt) { srtp_err_status_t err; /* Note that currently this SRTP init/deinit is not equipped with * reference counter, it should be safe as normally there is only * one single instance of media endpoint and even if it isn't, the * pjmedia_transport_srtp_create() will invoke SRTP init (the only * drawback should be the delay described by #788). */ PJ_UNUSED_ARG(endpt); #if !defined(PJMEDIA_SRTP_HAS_DEINIT) && !defined(PJMEDIA_SRTP_HAS_SHUTDOWN) # define PJMEDIA_SRTP_HAS_SHUTDOWN 1 #endif #if PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT # if defined(PJMEDIA_SRTP_HAS_DEINIT) && PJMEDIA_SRTP_HAS_DEINIT!=0 err = srtp_deinit(); # elif defined(PJMEDIA_SRTP_HAS_SHUTDOWN) && PJMEDIA_SRTP_HAS_SHUTDOWN!=0 err = srtp_shutdown(); # else err = srtp_err_status_ok; # endif if (err != srtp_err_status_ok) { PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s", get_libsrtp_errstr(err))); } #endif // PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT #if defined(PJMEDIA_SRTP_HAS_DTLS) && (PJMEDIA_SRTP_HAS_DTLS != 0) dtls_deinit(); #endif libsrtp_initialized = PJ_FALSE; }
/* * pjmedia_strerror() */ PJ_DEF(pj_str_t) pjmedia_strerror( pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) /* See if the error comes from PortAudio. */ #if defined(PJMEDIA_SOUND_IMPLEMENTATION) && \ PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_PORTAUDIO_SOUND if (statcode >= PJMEDIA_PORTAUDIO_ERRNO_START && statcode <= PJMEDIA_PORTAUDIO_ERRNO_END) { //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); msg.slen = pj_ansi_strlen(msg.ptr); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } 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_LIBSRTP_ERRNO_START; pj_str_t msg; msg = pj_str((char*)get_libsrtp_errstr(err)); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } else #endif /* PJMEDIA error */ if (statcode >= PJMEDIA_ERRNO_START && statcode < PJMEDIA_ERRNO_END) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjmedia error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (pj_ssize_t)bufsize) errstr.slen = bufsize - 1; return errstr; }
/* 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; unsigned 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: %s", get_libsrtp_errstr(err))); 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 = (char*) 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; }
/* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = size; err_status_t err; void (*cb)(void*, void*, pj_ssize_t) = NULL; void *cb_data = NULL; if (srtp->bypass_srtp) { srtp->rtp_cb(srtp->user_data, pkt, size); return; } if (size < 0) { return; } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); if (srtp->probation_cnt > 0) --srtp->probation_cnt; pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return; } err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (srtp->probation_cnt > 0 && (err == err_status_replay_old || err == err_status_replay_fail)) { /* Handle such condition that stream is updated (RTP seq is reinited * & SRTP is restarted), but some old packets are still coming * so SRTP is learning wrong RTP seq. While the newly inited RTP seq * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() * will return err_status_replay_*. Restarting SRTP can resolve this. */ pjmedia_srtp_crypto tx, rx; pj_status_t status; tx = srtp->tx_policy; rx = srtp->rx_policy; status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp, &tx, &rx); if (status != PJ_SUCCESS) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", get_libsrtp_errstr(err))); } else if (!srtp->bypass_srtp) { err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); } } if (err != err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } else { cb = srtp->rtp_cb; cb_data = srtp->user_data; } pj_lock_release(srtp->mutex); if (cb) { (*cb)(cb_data, pkt, len); } }
/* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = (int)size; srtp_err_status_t err; void (*cb)(void*, void*, pj_ssize_t) = NULL; void *cb_data = NULL; if (srtp->bypass_srtp) { srtp->rtp_cb(srtp->user_data, pkt, size); return; } if (size < 0) { return; } /* Give the packet to keying first by invoking its send_rtp() op. * Yes, the usage of send_rtp() is rather hacky, but it is convenient * as the signature suits the purpose and it is ready to use * (no futher registration/setting needed), and it may never be used * by any keying method in the future. */ { unsigned i; pj_status_t status; for (i=0; i < srtp->keying_cnt; i++) { if (!srtp->keying[i]->op->send_rtp) continue; status = pjmedia_transport_send_rtp(srtp->keying[i], pkt, size); if (status != PJ_EIGNORED) { /* Packet is already consumed by the keying method */ return; } } } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); if (srtp->probation_cnt > 0) --srtp->probation_cnt; pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return; } err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (srtp->probation_cnt > 0 && (err == srtp_err_status_replay_old || err == srtp_err_status_replay_fail)) { /* Handle such condition that stream is updated (RTP seq is reinited * & SRTP is restarted), but some old packets are still coming * so SRTP is learning wrong RTP seq. While the newly inited RTP seq * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() * will return err_status_replay_*. Restarting SRTP can resolve this. */ pjmedia_srtp_crypto tx, rx; pj_status_t status; tx = srtp->tx_policy; rx = srtp->rx_policy; status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp, &tx, &rx); if (status != PJ_SUCCESS) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", get_libsrtp_errstr(err))); } else if (!srtp->bypass_srtp) { err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); } } if (err != srtp_err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } else { cb = srtp->rtp_cb; cb_data = srtp->user_data; } pj_lock_release(srtp->mutex); if (cb) { (*cb)(cb_data, pkt, len); } }