diff --git a/UPGRADE.txt b/UPGRADE.txt index 0940bbd267ea0a93b409f8e433ef86b2f867bce6..ce548d083b090517249cd33ffcde286f1ae1c598 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -26,6 +26,23 @@ From 11.6-cert2 to 11.6-cert3: * MixMonitorMute - call or system * StopMixMonitor - call or system +* Added a 'force_avp' option for chan_sip. When enabled this option will + cause the media transport in the offer or answer SDP to be 'RTP/AVP', + 'RTP/AVPF', 'RTP/SAVP', or 'RTP/SAVPF' even if a DTLS stream has been + configured. This option can be set to improve interoperability with WebRTC + clients that don't use the RFC defined transport for DTLS. + +* The 'dtlsverify' option in chan_sip now has additional values besides + 'yes' and 'no'. If 'yes' is specified both the certificate and fingerprint + will be verified. If 'no' is specified then neither the certificate or + fingerprint is verified. If 'certificate' is specified then only the + certificate is verified. If 'fingerprint' is specified then only the + fingerprint is verified. + +* A 'dtlsfingerprint' option has been added to chan_sip which allows the + hash to be specified for the DTLS fingerprint placed in SDP. Supported + values are 'sha-1' and 'sha-256' with 'sha-256' being the default. + * Added http.conf session_inactivity timer option to close HTTP connections that aren't doing anything. diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 5a71388d5bb5d5b8e523617c9a9703af48d0ae8f..4c1cb6f694612d3ddeb51b3941f5ade337223b84 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1401,7 +1401,7 @@ static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_c static int process_sdp_a_image(const char *a, struct sip_pvt *p); static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf); static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf); -static void start_ice(struct ast_rtp_instance *instance); +static void start_ice(struct ast_rtp_instance *instance, int offer); static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec, struct ast_str **m_buf, struct ast_str **a_buf, int debug, int *min_packet_size); @@ -10040,12 +10040,21 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (process_sdp_a_dtls(value, p, p->rtp)) { processed = TRUE; + if (p->srtp) { + ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK); + } } if (process_sdp_a_dtls(value, p, p->vrtp)) { processed = TRUE; + if (p->vsrtp) { + ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK); + } } if (process_sdp_a_dtls(value, p, p->trtp)) { processed = TRUE; + if (p->tsrtp) { + ast_set_flag(p->tsrtp, SRTP_CRYPTO_OFFER_OK); + } } break; @@ -10412,7 +10421,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (process_sdp_a_ice(value, p, p->rtp)) { processed = TRUE; } else if (process_sdp_a_dtls(value, p, p->rtp)) { + processed_crypto = TRUE; processed = TRUE; + if (p->srtp) { + ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK); + } } else if (process_sdp_a_sendonly(value, &sendonly)) { processed = TRUE; } else if (!processed_crypto && process_crypto(p, p->rtp, &p->srtp, value)) { @@ -10427,7 +10440,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (process_sdp_a_ice(value, p, p->vrtp)) { processed = TRUE; } else if (process_sdp_a_dtls(value, p, p->vrtp)) { + processed_crypto = TRUE; processed = TRUE; + if (p->vsrtp) { + ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK); + } } else if (!processed_crypto && process_crypto(p, p->vrtp, &p->vsrtp, value)) { processed_crypto = TRUE; processed = TRUE; @@ -10578,7 +10595,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action /* Setup audio address and port */ if (p->rtp) { if (sa && portno > 0) { - start_ice(p->rtp); + start_ice(p->rtp, (req->method != SIP_RESPONSE) ? 0 : 1); ast_sockaddr_set_port(sa, portno); ast_rtp_instance_set_remote_address(p->rtp, sa); if (debug) { @@ -10626,7 +10643,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action /* Setup video address and port */ if (p->vrtp) { if (vsa && vportno > 0) { - start_ice(p->vrtp); + start_ice(p->vrtp, (req->method != SIP_RESPONSE) ? 0 : 1); ast_sockaddr_set_port(vsa, vportno); ast_rtp_instance_set_remote_address(p->vrtp, vsa); if (debug) { @@ -10644,7 +10661,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action /* Setup text address and port */ if (p->trtp) { if (tsa && tportno > 0) { - start_ice(p->trtp); + start_ice(p->trtp, (req->method != SIP_RESPONSE) ? 0 : 1); ast_sockaddr_set_port(tsa, tportno); ast_rtp_instance_set_remote_address(p->trtp, tsa); if (debug) { @@ -10971,7 +10988,7 @@ static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_i { struct ast_rtp_engine_dtls *dtls; int found = FALSE; - char value[256], hash[6]; + char value[256], hash[32]; if (!instance || !p->dtls_cfg.enabled || !(dtls = ast_rtp_instance_get_dtls(instance))) { return found; @@ -11003,11 +11020,13 @@ static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_i ast_log(LOG_WARNING, "Unsupported connection attribute value '%s' received on dialog '%s'\n", value, p->callid); } - } else if (sscanf(a, "fingerprint: %5s %255s", hash, value) == 2) { + } else if (sscanf(a, "fingerprint: %31s %255s", hash, value) == 2) { found = TRUE; if (!strcasecmp(hash, "sha-1")) { dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1, value); + } else if (!strcasecmp(hash, "sha-256")) { + dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA256, value); } else { ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s' received on dialog '%s'\n", hash, p->callid); @@ -12594,7 +12613,13 @@ static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a while ((candidate = ao2_iterator_next(&i))) { ast_str_append(a_buf, 0, "a=candidate:%s %d %s %d ", candidate->foundation, candidate->id, candidate->transport, candidate->priority); ast_str_append(a_buf, 0, "%s ", ast_sockaddr_stringify_host(&candidate->address)); - ast_str_append(a_buf, 0, "%s typ ", ast_sockaddr_stringify_port(&candidate->address)); + + if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX + && candidate->id == AST_RTP_ICE_COMPONENT_RTCP) { + ast_str_append(a_buf, 0, "%d typ ", ast_sockaddr_port(&candidate->address) + 1); + } else { + ast_str_append(a_buf, 0, "%s typ ", ast_sockaddr_stringify_port(&candidate->address)); + } if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) { ast_str_append(a_buf, 0, "host"); @@ -12619,7 +12644,7 @@ static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a } /*! \brief Start ICE negotiation on an RTP instance */ -static void start_ice(struct ast_rtp_instance *instance) +static void start_ice(struct ast_rtp_instance *instance, int offer) { struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(instance); @@ -12627,6 +12652,8 @@ static void start_ice(struct ast_rtp_instance *instance) return; } + /* If we are the offerer then we are the controlling agent, otherwise they are */ + ice->set_role(instance, offer ? AST_RTP_ICE_ROLE_CONTROLLING : AST_RTP_ICE_ROLE_CONTROLLED); ice->start(instance); } @@ -12634,6 +12661,7 @@ static void start_ice(struct ast_rtp_instance *instance) static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf) { struct ast_rtp_engine_dtls *dtls; + enum ast_rtp_dtls_hash hash; const char *fingerprint; if (!instance || !(dtls = ast_rtp_instance_get_dtls(instance)) || !dtls->active(instance)) { @@ -12668,8 +12696,11 @@ static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str ** break; } - if ((fingerprint = dtls->get_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1))) { - ast_str_append(a_buf, 0, "a=fingerprint:SHA-1 %s\r\n", fingerprint); + hash = dtls->get_fingerprint_hash(instance); + fingerprint = dtls->get_fingerprint(instance); + if (fingerprint && (hash == AST_RTP_DTLS_HASH_SHA1 || hash == AST_RTP_DTLS_HASH_SHA256)) { + ast_str_append(a_buf, 0, "a=fingerprint:%s %s\r\n", hash == AST_RTP_DTLS_HASH_SHA1 ? "SHA-1" : "SHA-256", + fingerprint); } } @@ -12977,7 +13008,11 @@ static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure, s struct ast_rtp_engine_dtls *dtls; if ((dtls = ast_rtp_instance_get_dtls(instance)) && dtls->active(instance)) { - return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP"; + if (ast_test_flag(&p->flags[2], SIP_PAGE3_FORCE_AVP)) { + return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "RTP/SAVPF" : "RTP/SAVP"; + } else { + return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP"; + } } else { if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) { return secure ? "RTP/SAVPF" : "RTP/AVPF"; @@ -30925,6 +30960,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF); } else if (!strcasecmp(v->name, "icesupport")) { ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_ICE_SUPPORT); + } else if (!strcasecmp(v->name, "force_avp")) { + ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_FORCE_AVP); } else { ast_rtp_dtls_cfg_parse(&peer->dtls_cfg, v->name, v->value); } diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 073f5f4d6ad45c871482877f11811b4c6125d090..69304927ffeacdfd976e475f4a15a2512ff6a025 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -374,10 +374,11 @@ #define SIP_PAGE3_DIRECT_MEDIA_OUTGOING (1 << 4) /*!< DP: Only send direct media reinvites on outgoing calls */ #define SIP_PAGE3_USE_AVPF (1 << 5) /*!< DGP: Support a minimal AVPF-compatible profile */ #define SIP_PAGE3_ICE_SUPPORT (1 << 6) /*!< DGP: Enable ICE support */ +#define SIP_PAGE3_FORCE_AVP (1 << 7) /*!< DGP: Force 'RTP/AVP' for all streams, even DTLS */ #define SIP_PAGE3_FLAGS_TO_COPY \ (SIP_PAGE3_SNOM_AOC | SIP_PAGE3_SRTP_TAG_32 | SIP_PAGE3_NAT_AUTO_RPORT | SIP_PAGE3_NAT_AUTO_COMEDIA | \ - SIP_PAGE3_DIRECT_MEDIA_OUTGOING | SIP_PAGE3_USE_AVPF | SIP_PAGE3_ICE_SUPPORT) + SIP_PAGE3_DIRECT_MEDIA_OUTGOING | SIP_PAGE3_USE_AVPF | SIP_PAGE3_ICE_SUPPORT | SIP_PAGE3_FORCE_AVP) #define CHECK_AUTH_BUF_INITLEN 256 diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 702f6e5f37b9652f46474902f5eeb07674fadc1f..554d3ea69d72d6a4fede1c0cdf6961893f1297f8 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -1007,6 +1007,8 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;avpf=yes ; Enable inter-operability with media streams using the AVPF RTP profile. ; This will cause all offers and answers to use AVPF (or SAVPF). This ; option may be specified at the global or peer scope. +;force_avp=yes ; Force 'RTP/AVP', 'RTP/AVPF', 'RTP/SAVP', and 'RTP/SAVPF' to be used for + ; media streams when appropriate, even if a DTLS stream is present. ;----------------------------------------- REALTIME SUPPORT ------------------------ ; For additional information on ARA, the Asterisk Realtime Architecture, ; please read https://wiki.asterisk.org/wiki/display/AST/Realtime+Database+Configuration @@ -1261,6 +1263,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; dtlscafile ; dtlscapath ; dtlssetup +; dtlsfingerprint ; ;------------------------------------------------------------------------------ @@ -1269,7 +1272,11 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; DTLS-SRTP support is available if the underlying RTP engine in use supports it. ; ; dtlsenable = yes ; Enable or disable DTLS-SRTP support -; dtlsverify = yes ; Verify that the provided peer certificate is valid +; dtlsverify = yes ; Verify that provided peer certificate and fingerprint are valid +; ; A value of 'yes' will perform both certificate and fingerprint verification +; ; A value of 'no' will perform no certificate or fingerprint verification +; ; A value of 'fingerprint' will perform ONLY fingerprint verification +; ; A value of 'certificate' will perform ONLY certficiate verification ; dtlsrekey = 60 ; Interval at which to renegotiate the TLS session and rekey the SRTP session ; ; If this is not set or the value provided is 0 rekeying will be disabled ; dtlscertfile = file ; Path to certificate file to present @@ -1284,6 +1291,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; ; accept connections only), and actpass (we will do both). This value will be used in ; ; the outgoing SDP when offering and for incoming SDP offers when the remote party sends ; ; actpass +; dtlsfingerprint = sha-1 ; The hash to use for the fingerprint in SDP (valid options are sha-1 and sha-256) ;[sip_proxy] ; For incoming calls only. Example: FWD (Free World Dialup) diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index 51455e92ac321bd257da30dc7a31dee872660b8c..3d7bd8fce3c88dcc28808676f0bd2ac13465e9fe 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -322,10 +322,22 @@ enum ast_rtp_ice_candidate_type { AST_RTP_ICE_CANDIDATE_TYPE_RELAYED, /*!< ICE relayed candidate, which represents the address allocated in TURN server. */ }; +/*! \brief ICE component types */ +enum ast_rtp_ice_component_type { + AST_RTP_ICE_COMPONENT_RTP = 1, + AST_RTP_ICE_COMPONENT_RTCP = 2, +}; + +/*! \brief ICE role during negotiation */ +enum ast_rtp_ice_role { + AST_RTP_ICE_ROLE_CONTROLLED, + AST_RTP_ICE_ROLE_CONTROLLING, +}; + /*! \brief Structure for an ICE candidate */ struct ast_rtp_engine_ice_candidate { char *foundation; /*!< Foundation identifier */ - unsigned int id; /*!< Component identifier */ + enum ast_rtp_ice_component_type id; /*!< Component identifier */ char *transport; /*!< Transport for the media */ int priority; /*!< Priority which is used if multiple candidates can be used */ struct ast_sockaddr address; /*!< Address of the candidate */ @@ -351,6 +363,8 @@ struct ast_rtp_engine_ice { struct ao2_container *(*get_local_candidates)(struct ast_rtp_instance *instance); /*! Callback for telling the ICE support that it is talking to an ice-lite implementation */ void (*ice_lite)(struct ast_rtp_instance *instance); + /*! Callback for changing our role in negotiation */ + void (*set_role)(struct ast_rtp_instance *instance, enum ast_rtp_ice_role role); }; /*! \brief DTLS setup types */ @@ -363,22 +377,31 @@ enum ast_rtp_dtls_setup { /*! \brief DTLS connection states */ enum ast_rtp_dtls_connection { - AST_RTP_DTLS_CONNECTION_NEW, /*!< Endpoint wants to use a new connection */ + AST_RTP_DTLS_CONNECTION_NEW, /*!< Endpoint wants to use a new connection */ AST_RTP_DTLS_CONNECTION_EXISTING, /*!< Endpoint wishes to use existing connection */ }; /*! \brief DTLS fingerprint hashes */ enum ast_rtp_dtls_hash { - AST_RTP_DTLS_HASH_SHA1, /*!< SHA-1 fingerprint hash */ + AST_RTP_DTLS_HASH_SHA256, /*!< SHA-256 fingerprint hash */ + AST_RTP_DTLS_HASH_SHA1, /*!< SHA-1 fingerprint hash */ +}; + +/*! \brief DTLS verification settings */ +enum ast_rtp_dtls_verify { + AST_RTP_DTLS_VERIFY_NONE = 0, /*!< Don't verify anything */ + AST_RTP_DTLS_VERIFY_FINGERPRINT = (1 << 0), /*!< Verify the fingerprint */ + AST_RTP_DTLS_VERIFY_CERTIFICATE = (1 << 1), /*!< Verify the certificate */ }; /*! \brief DTLS configuration structure */ struct ast_rtp_dtls_cfg { unsigned int enabled:1; /*!< Whether DTLS support is enabled or not */ - unsigned int verify:1; /*!< Whether to request and verify a client certificate when acting as server */ unsigned int rekey; /*!< Interval at which to renegotiate and rekey - defaults to 0 (off) */ enum ast_rtp_dtls_setup default_setup; /*!< Default setup type to use for outgoing */ enum ast_srtp_suite suite; /*!< Crypto suite in use */ + enum ast_rtp_dtls_hash hash; /*!< Hash to use for fingerprint */ + enum ast_rtp_dtls_verify verify; /*!< What should be verified */ char *certfile; /*!< Certificate file */ char *pvtfile; /*!< Private key file */ char *cipher; /*!< Cipher to use */ @@ -404,8 +427,10 @@ struct ast_rtp_engine_dtls { void (*set_setup)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup); /*! Set the remote fingerprint */ void (*set_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint); + /*! Get the local fingerprint hash type */ + enum ast_rtp_dtls_hash (*get_fingerprint_hash)(struct ast_rtp_instance *instance); /*! Get the local fingerprint */ - const char *(*get_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash); + const char *(*get_fingerprint)(struct ast_rtp_instance *instance); }; /*! Structure that represents an RTP stack (engine) */ diff --git a/main/rtp_engine.c b/main/rtp_engine.c index dc2983637c26e7d1f2187f32f44248e7ffca0bef..01c1cea8a8085246009d2e430b1b98ca11906cc0 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -2089,7 +2089,17 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, if (!strcasecmp(name, "dtlsenable")) { dtls_cfg->enabled = ast_true(value) ? 1 : 0; } else if (!strcasecmp(name, "dtlsverify")) { - dtls_cfg->verify = ast_true(value) ? 1 : 0; + if (!strcasecmp(value, "yes")) { + dtls_cfg->verify = AST_RTP_DTLS_VERIFY_FINGERPRINT | AST_RTP_DTLS_VERIFY_CERTIFICATE; + } else if (!strcasecmp(value, "fingerprint")) { + dtls_cfg->verify = AST_RTP_DTLS_VERIFY_FINGERPRINT; + } else if (!strcasecmp(value, "certificate")) { + dtls_cfg->verify = AST_RTP_DTLS_VERIFY_CERTIFICATE; + } else if (!strcasecmp(value, "no")) { + dtls_cfg->verify = AST_RTP_DTLS_VERIFY_NONE; + } else { + return -1; + } } else if (!strcasecmp(name, "dtlsrekey")) { if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) { return -1; @@ -2117,6 +2127,12 @@ int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, } else if (!strcasecmp(value, "actpass")) { dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTPASS; } + } else if (!strcasecmp(name, "dtlsfingerprint")) { + if (!strcasecmp(value, "sha-256")) { + dtls_cfg->hash = AST_RTP_DTLS_HASH_SHA256; + } else if (!strcasecmp(value, "sha-1")) { + dtls_cfg->hash = AST_RTP_DTLS_HASH_SHA1; + } } else { return -1; } @@ -2130,6 +2146,7 @@ void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rt dst_cfg->verify = src_cfg->verify; dst_cfg->rekey = src_cfg->rekey; dst_cfg->suite = src_cfg->suite; + dst_cfg->hash = src_cfg->hash; dst_cfg->certfile = ast_strdup(src_cfg->certfile); dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile); dst_cfg->cipher = ast_strdup(src_cfg->cipher); diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 78c1ea9eaebb0bee04aaa5c670b7866de302308e..97e68c939a42dc7e9e682ed0c38ba4e9f483ff51 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -70,6 +70,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/unaligned.h" #include "asterisk/module.h" #include "asterisk/rtp_engine.h" +#include "asterisk/test.h" #define MAX_TIMESTAMP_SKEW 640 @@ -176,15 +177,22 @@ static int worker_terminate; #define TRANSPORT_TURN_RTP 3 #define TRANSPORT_TURN_RTCP 4 -#define COMPONENT_RTP 1 -#define COMPONENT_RTCP 2 - /*! \brief RTP learning mode tracking information */ struct rtp_learning_info { int max_seq; /*!< The highest sequence number received */ int packets; /*!< The number of remaining packets before the source is accepted */ }; +#ifdef HAVE_OPENSSL_SRTP +struct dtls_details { + SSL *ssl; /*!< SSL session */ + BIO *read_bio; /*!< Memory buffer for reading */ + BIO *write_bio; /*!< Memory buffer for writing */ + enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */ + enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */ +}; +#endif + /*! \brief RTP session description */ struct ast_rtp { int s; @@ -268,7 +276,7 @@ struct ast_rtp { pj_turn_state_t turn_state; /*!< Current state of the TURN relay session */ ast_cond_t cond; /*!< Condition for signaling */ unsigned int passthrough:1; /*!< Bit to indicate that the received packet should be passed through */ - unsigned int ice_started:1; /*!< Bit to indicate ICE connectivity checks have started */ + unsigned int ice_port; /*!< Port that ICE was started with if it was previously started */ char remote_ufrag[256]; /*!< The remote ICE username */ char remote_passwd[256]; /*!< The remote ICE password */ @@ -276,23 +284,25 @@ struct ast_rtp { char local_ufrag[256]; /*!< The local ICE username */ char local_passwd[256]; /*!< The local ICE password */ - struct ao2_container *local_candidates; /*!< The local ICE candidates */ - struct ao2_container *remote_candidates; /*!< The remote ICE candidates */ + struct ao2_container *ice_local_candidates; /*!< The local ICE candidates */ + struct ao2_container *ice_active_remote_candidates; /*!< The remote ICE candidates */ + struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */ + struct ast_sockaddr ice_original_rtp_addr; /*!< rtp address that ICE started on first session */ #endif #ifdef HAVE_OPENSSL_SRTP SSL_CTX *ssl_ctx; /*!< SSL context */ - SSL *ssl; /*!< SSL session */ - BIO *read_bio; /*!< Memory buffer for reading */ - BIO *write_bio; /*!< Memory buffer for writing */ - enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */ + ast_mutex_t dtls_timer_lock; /*!< Lock for synchronization purposes */ + enum ast_rtp_dtls_verify dtls_verify; /*!< What to verify */ enum ast_srtp_suite suite; /*!< SRTP crypto suite */ + enum ast_rtp_dtls_hash local_hash; /*!< Local hash used for the fingerprint */ char local_fingerprint[160]; /*!< Fingerprint of our certificate */ + enum ast_rtp_dtls_hash remote_hash; /*!< Remote hash used for the fingerprint */ unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */ - enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */ - unsigned int dtls_failure:1; /*!< Failure occurred during DTLS negotiation */ unsigned int rekey; /*!< Interval at which to renegotiate and rekey */ int rekeyid; /*!< Scheduled item id for rekeying */ + int dtlstimerid; /*!< Scheduled item id for DTLS retransmission for RTP */ + struct dtls_details dtls; /*!< DTLS state information */ #endif }; @@ -355,6 +365,10 @@ struct ast_rtcp { double normdevrtt; double stdevrtt; unsigned int rtt_count; + +#ifdef HAVE_OPENSSL_SRTP + struct dtls_details dtls; /*!< DTLS state information */ +#endif }; struct rtp_red { @@ -402,6 +416,7 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level); #ifdef HAVE_OPENSSL_SRTP static int ast_rtp_activate(struct ast_rtp_instance *instance); +static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp); #endif static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp); @@ -434,17 +449,32 @@ static void ast_rtp_ice_set_authentication(struct ast_rtp_instance *instance, co } } +static int ice_candidate_cmp(void *obj, void *arg, int flags) +{ + struct ast_rtp_engine_ice_candidate *candidate1 = obj, *candidate2 = arg; + + if (strcmp(candidate1->foundation, candidate2->foundation) || + candidate1->id != candidate2->id || + ast_sockaddr_cmp(&candidate1->address, &candidate2->address) || + candidate1->type != candidate1->type) { + return 0; + } + + return CMP_MATCH | CMP_STOP; +} + static void ast_rtp_ice_add_remote_candidate(struct ast_rtp_instance *instance, const struct ast_rtp_engine_ice_candidate *candidate) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); struct ast_rtp_engine_ice_candidate *remote_candidate; - if (!rtp->remote_candidates && !(rtp->remote_candidates = ao2_container_alloc(1, NULL, NULL))) { + if (!rtp->ice_proposed_remote_candidates && + !(rtp->ice_proposed_remote_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) { return; } /* If this is going to exceed the maximum number of ICE candidates don't even add it */ - if (ao2_container_count(rtp->remote_candidates) == PJ_ICE_MAX_CAND) { + if (ao2_container_count(rtp->ice_proposed_remote_candidates) == PJ_ICE_MAX_CAND) { return; } @@ -460,7 +490,7 @@ static void ast_rtp_ice_add_remote_candidate(struct ast_rtp_instance *instance, ast_sockaddr_copy(&remote_candidate->relay_address, &candidate->relay_address); remote_candidate->type = candidate->type; - ao2_link(rtp->remote_candidates, remote_candidate); + ao2_link(rtp->ice_proposed_remote_candidates, remote_candidate); ao2_ref(remote_candidate, -1); } @@ -502,6 +532,67 @@ static void update_address_with_ice_candidate(struct ast_rtp *rtp, int component ast_sockaddr_set_port(cand_address, pj_sockaddr_get_port(&rtp->ice->comp[component - 1].valid_check->rcand->addr)); } +static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *addr, + int port, int replace); + +static void ast_rtp_ice_stop(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (!rtp->ice) { + return; + } + + pj_thread_register_check(); + + pj_ice_sess_destroy(rtp->ice); + rtp->ice = NULL; +} + +static int ice_reset_session(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + pj_ice_sess_role role = rtp->ice->role; + int res; + + ast_rtp_ice_stop(instance); + + res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1); + if (!res) { + /* Preserve the role that the old ICE session used */ + pj_ice_sess_change_role(rtp->ice, role); + } + + return res; +} + +static int ice_candidates_compare(struct ao2_container *left, struct ao2_container *right) +{ + struct ao2_iterator i; + struct ast_rtp_engine_ice_candidate *right_candidate; + + if (ao2_container_count(left) != ao2_container_count(right)) { + return -1; + } + + i = ao2_iterator_init(right, 0); + while ((right_candidate = ao2_iterator_next(&i))) { + struct ast_rtp_engine_ice_candidate *left_candidate = ao2_find(left, right_candidate, OBJ_POINTER); + + if (!left_candidate) { + ao2_ref(right_candidate, -1); + ao2_iterator_destroy(&i); + return -1; + } + + ao2_ref(left_candidate, -1); + ao2_ref(right_candidate, -1); + } + ao2_iterator_destroy(&i); + + return 0; +} + static void ast_rtp_ice_start(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); @@ -511,13 +602,32 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance) struct ast_rtp_engine_ice_candidate *candidate; int cand_cnt = 0; - if (!rtp->ice || !rtp->remote_candidates || rtp->ice_started) { + if (!rtp->ice || !rtp->ice_proposed_remote_candidates) { + return; + } + + /* Check for equivalence in the lists */ + if (rtp->ice_active_remote_candidates && + !ice_candidates_compare(rtp->ice_proposed_remote_candidates, rtp->ice_active_remote_candidates)) { + ao2_cleanup(rtp->ice_proposed_remote_candidates); + rtp->ice_proposed_remote_candidates = NULL; + return; + } + + /* Out with the old, in with the new */ + ao2_cleanup(rtp->ice_active_remote_candidates); + rtp->ice_active_remote_candidates = rtp->ice_proposed_remote_candidates; + rtp->ice_proposed_remote_candidates = NULL; + + /* Reset the ICE session. Is this going to work? */ + if (ice_reset_session(instance)) { + ast_log(LOG_NOTICE, "Failed to create replacement ICE session\n"); return; } pj_thread_register_check(); - i = ao2_iterator_init(rtp->remote_candidates, 0); + i = ao2_iterator_init(rtp->ice_active_remote_candidates, 0); while ((candidate = ao2_iterator_next(&i)) && (cand_cnt < PJ_ICE_MAX_CAND)) { pj_str_t address; @@ -540,37 +650,36 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance) candidates[cand_cnt].type = PJ_ICE_CAND_TYPE_RELAYED; } - if (candidate->id == COMPONENT_RTP && rtp->turn_rtp) { + if (candidate->id == AST_RTP_ICE_COMPONENT_RTP && rtp->turn_rtp) { pj_turn_sock_set_perm(rtp->turn_rtp, 1, &candidates[cand_cnt].addr, 1); - } else if (candidate->id == COMPONENT_RTCP && rtp->turn_rtcp) { + } else if (candidate->id == AST_RTP_ICE_COMPONENT_RTCP && rtp->turn_rtcp) { pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1); } cand_cnt++; + ao2_ref(candidate, -1); } ao2_iterator_destroy(&i); - if (pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(rtp->remote_candidates), &candidates[0]) == PJ_SUCCESS) { + if (pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(rtp->ice_active_remote_candidates), &candidates[0]) == PJ_SUCCESS) { + ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS"); pj_ice_sess_start_check(rtp->ice); pj_timer_heap_poll(timerheap, NULL); - rtp->ice_started = 1; rtp->strict_rtp_state = STRICT_RTP_OPEN; - } -} - -static void ast_rtp_ice_stop(struct ast_rtp_instance *instance) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - if (!rtp->ice) { return; } - pj_thread_register_check(); + ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: FAILURE"); - pj_ice_sess_destroy(rtp->ice); - rtp->ice = NULL; + /* even though create check list failed don't stop ice as + it might still work */ + ast_debug(1, "Failed to create ICE session check list\n"); + /* however we do need to reset remote candidates since + this function may be re-entered */ + ao2_ref(rtp->ice_active_remote_candidates, -1); + rtp->ice_active_remote_candidates = NULL; + rtp->ice->rcand_cnt = rtp->ice->clist.count = 0; } static const char *ast_rtp_ice_get_ufrag(struct ast_rtp_instance *instance) @@ -591,11 +700,11 @@ static struct ao2_container *ast_rtp_ice_get_local_candidates(struct ast_rtp_ins { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - if (rtp->local_candidates) { - ao2_ref(rtp->local_candidates, +1); + if (rtp->ice_local_candidates) { + ao2_ref(rtp->ice_local_candidates, +1); } - return rtp->local_candidates; + return rtp->ice_local_candidates; } static void ast_rtp_ice_lite(struct ast_rtp_instance *instance) @@ -611,18 +720,18 @@ static void ast_rtp_ice_lite(struct ast_rtp_instance *instance) pj_ice_sess_change_role(rtp->ice, PJ_ICE_SESS_ROLE_CONTROLLING); } -static int ice_candidate_cmp(void *obj, void *arg, int flags) +static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp_ice_role role) { - struct ast_rtp_engine_ice_candidate *candidate1 = obj, *candidate2 = arg; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - if ((strcmp(candidate1->foundation, candidate2->foundation)) || - (candidate1->id != candidate2->id) || - (ast_sockaddr_cmp(&candidate1->address, &candidate2->address)) || - (candidate1->type != candidate1->type)) { - return 0; + if (!rtp->ice) { + return; } - return CMP_MATCH | CMP_STOP; + pj_thread_register_check(); + + pj_ice_sess_change_role(rtp->ice, role == AST_RTP_ICE_ROLE_CONTROLLED ? + PJ_ICE_SESS_ROLE_CONTROLLED : PJ_ICE_SESS_ROLE_CONTROLLING); } static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned transport_id, pj_ice_cand_type type, pj_uint16_t local_pref, @@ -636,7 +745,7 @@ static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned pj_ice_calc_foundation(rtp->ice->pool, &foundation, type, addr); - if (!rtp->local_candidates && !(rtp->local_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) { + if (!rtp->ice_local_candidates && !(rtp->ice_local_candidates = ao2_container_alloc(1, NULL, ice_candidate_cmp))) { return; } @@ -664,13 +773,13 @@ static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned candidate->type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED; } - if ((existing = ao2_find(rtp->local_candidates, candidate, OBJ_POINTER))) { + if ((existing = ao2_find(rtp->ice_local_candidates, candidate, OBJ_POINTER))) { ao2_ref(existing, -1); ao2_ref(candidate, -1); return; } - if (pj_ice_sess_add_cand(rtp->ice, comp_id, transport_id, type, local_pref, &foundation, addr, addr, rel_addr, addr_len, NULL) != PJ_SUCCESS) { + if (pj_ice_sess_add_cand(rtp->ice, comp_id, transport_id, type, local_pref, &foundation, addr, base_addr, rel_addr, addr_len, NULL) != PJ_SUCCESS) { ao2_ref(candidate, -1); return; } @@ -678,7 +787,7 @@ static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned /* By placing the candidate into the ICE session it will have produced the priority, so update the local candidate with it */ candidate->priority = rtp->ice->lcand[rtp->ice->lcand_cnt - 1].prio; - ao2_link(rtp->local_candidates, candidate); + ao2_link(rtp->ice_local_candidates, candidate); ao2_ref(candidate, -1); } @@ -704,25 +813,83 @@ static struct ast_rtp_engine_ice ast_rtp_ice = { .get_password = ast_rtp_ice_get_password, .get_local_candidates = ast_rtp_ice_get_local_candidates, .ice_lite = ast_rtp_ice_lite, + .set_role = ast_rtp_ice_set_role, }; #endif #ifdef HAVE_OPENSSL_SRTP -static void dtls_info_callback(const SSL *ssl, int where, int ret) +static int dtls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { - struct ast_rtp *rtp = SSL_get_ex_data(ssl, 0); + /* We don't want to actually verify the certificate so just accept what they have provided */ + return 1; +} - /* We only care about alerts */ - if (!(where & SSL_CB_ALERT)) { - return; +static int dtls_details_initialize(struct dtls_details *dtls, SSL_CTX *ssl_ctx, + enum ast_rtp_dtls_setup setup) +{ + dtls->dtls_setup = setup; + + if (!(dtls->ssl = SSL_new(ssl_ctx))) { + ast_log(LOG_ERROR, "Failed to allocate memory for SSL\n"); + goto error; + } + + if (!(dtls->read_bio = BIO_new(BIO_s_mem()))) { + ast_log(LOG_ERROR, "Failed to allocate memory for inbound SSL traffic\n"); + goto error; + } + BIO_set_mem_eof_return(dtls->read_bio, -1); + + if (!(dtls->write_bio = BIO_new(BIO_s_mem()))) { + ast_log(LOG_ERROR, "Failed to allocate memory for outbound SSL traffic\n"); + goto error; + } + BIO_set_mem_eof_return(dtls->write_bio, -1); + + SSL_set_bio(dtls->ssl, dtls->read_bio, dtls->write_bio); + + if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { + SSL_set_accept_state(dtls->ssl); + } else { + SSL_set_connect_state(dtls->ssl); + } + dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; + + return 0; + +error: + if (dtls->read_bio) { + BIO_free(dtls->read_bio); + dtls->read_bio = NULL; + } + + if (dtls->write_bio) { + BIO_free(dtls->write_bio); + dtls->write_bio = NULL; + } + + if (dtls->ssl) { + SSL_free(dtls->ssl); + dtls->ssl = NULL; + } + return -1; +} + +static int dtls_setup_rtcp(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (!rtp->ssl_ctx || !rtp->rtcp) { + return 0; } - rtp->dtls_failure = 1; + return dtls_details_initialize(&rtp->rtcp->dtls, rtp->ssl_ctx, rtp->dtls.dtls_setup); } static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + int res; if (!dtls_cfg->enabled) { return 0; @@ -736,7 +903,11 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con return -1; } - SSL_CTX_set_verify(rtp->ssl_ctx, dtls_cfg->verify ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, NULL); + rtp->dtls_verify = dtls_cfg->verify; + + SSL_CTX_set_verify(rtp->ssl_ctx, (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) || (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ? + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, !(rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) ? + dtls_verify_callback : NULL); if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_80) { SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_80"); @@ -744,13 +915,16 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_32"); } else { ast_log(LOG_ERROR, "Unsupported suite specified for DTLS-SRTP on RTP instance '%p'\n", instance); - goto error; + return -1; } + rtp->local_hash = dtls_cfg->hash; + if (!ast_strlen_zero(dtls_cfg->certfile)) { char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile; BIO *certbio; X509 *cert; + const EVP_MD *type; unsigned int size, i; unsigned char fingerprint[EVP_MAX_MD_SIZE]; char *local_fingerprint = rtp->local_fingerprint; @@ -758,30 +932,40 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) { ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n", dtls_cfg->certfile, instance); - goto error; + return -1; } if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) || !SSL_CTX_check_private_key(rtp->ssl_ctx)) { ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n", private, instance); - goto error; + return -1; } if (!(certbio = BIO_new(BIO_s_file()))) { ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n", instance); - goto error; + return -1; + } + + if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA1) { + type = EVP_sha1(); + } else if (rtp->local_hash == AST_RTP_DTLS_HASH_SHA256) { + type = EVP_sha256(); + } else { + ast_log(LOG_ERROR, "Unsupported fingerprint hash type on RTP instance '%p'\n", + instance); + return -1; } if (!BIO_read_filename(certbio, dtls_cfg->certfile) || !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) || - !X509_digest(cert, EVP_sha1(), fingerprint, &size) || + !X509_digest(cert, type, fingerprint, &size) || !size) { ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n", dtls_cfg->certfile, instance); BIO_free_all(certbio); - goto error; + return -1; } for (i = 0; i < size; i++) { @@ -798,7 +982,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con if (!SSL_CTX_set_cipher_list(rtp->ssl_ctx, dtls_cfg->cipher)) { ast_log(LOG_ERROR, "Invalid cipher specified in cipher list '%s' for RTP instance '%p'\n", dtls_cfg->cipher, instance); - goto error; + return -1; } } @@ -806,69 +990,19 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con if (!SSL_CTX_load_verify_locations(rtp->ssl_ctx, S_OR(dtls_cfg->cafile, NULL), S_OR(dtls_cfg->capath, NULL))) { ast_log(LOG_ERROR, "Invalid certificate authority file '%s' or path '%s' specified for RTP instance '%p'\n", S_OR(dtls_cfg->cafile, ""), S_OR(dtls_cfg->capath, ""), instance); - goto error; + return -1; } } rtp->rekey = dtls_cfg->rekey; - rtp->dtls_setup = dtls_cfg->default_setup; rtp->suite = dtls_cfg->suite; - if (!(rtp->ssl = SSL_new(rtp->ssl_ctx))) { - ast_log(LOG_ERROR, "Failed to allocate memory for SSL context on RTP instance '%p'\n", - instance); - goto error; - } - - SSL_set_ex_data(rtp->ssl, 0, rtp); - SSL_set_info_callback(rtp->ssl, dtls_info_callback); - - if (!(rtp->read_bio = BIO_new(BIO_s_mem()))) { - ast_log(LOG_ERROR, "Failed to allocate memory for inbound SSL traffic on RTP instance '%p'\n", - instance); - goto error; - } - BIO_set_mem_eof_return(rtp->read_bio, -1); - - if (!(rtp->write_bio = BIO_new(BIO_s_mem()))) { - ast_log(LOG_ERROR, "Failed to allocate memory for outbound SSL traffic on RTP instance '%p'\n", - instance); - goto error; - } - BIO_set_mem_eof_return(rtp->write_bio, -1); - - SSL_set_bio(rtp->ssl, rtp->read_bio, rtp->write_bio); - - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { - SSL_set_accept_state(rtp->ssl); - } else { - SSL_set_connect_state(rtp->ssl); - } - - rtp->connection = AST_RTP_DTLS_CONNECTION_NEW; - - return 0; - -error: - if (rtp->read_bio) { - BIO_free(rtp->read_bio); - rtp->read_bio = NULL; - } - - if (rtp->write_bio) { - BIO_free(rtp->write_bio); - rtp->write_bio = NULL; - } - - if (rtp->ssl) { - SSL_free(rtp->ssl); - rtp->ssl = NULL; + res = dtls_details_initialize(&rtp->dtls, rtp->ssl_ctx, dtls_cfg->default_setup); + if (!res) { + dtls_setup_rtcp(instance); } - SSL_CTX_free(rtp->ssl_ctx); - rtp->ssl_ctx = NULL; - - return -1; + return res; } static int ast_rtp_dtls_active(struct ast_rtp_instance *instance) @@ -887,9 +1021,14 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) rtp->ssl_ctx = NULL; } - if (rtp->ssl) { - SSL_free(rtp->ssl); - rtp->ssl = NULL; + if (rtp->dtls.ssl) { + SSL_free(rtp->dtls.ssl); + rtp->dtls.ssl = NULL; + } + + if (rtp->rtcp && rtp->rtcp->dtls.ssl) { + SSL_free(rtp->rtcp->dtls.ssl); + rtp->rtcp->dtls.ssl = NULL; } } @@ -897,49 +1036,50 @@ static void ast_rtp_dtls_reset(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - /* If the SSL session is not yet finalized don't bother resetting */ - if (!SSL_is_init_finished(rtp->ssl)) { - return; + if (SSL_is_init_finished(rtp->dtls.ssl)) { + SSL_shutdown(rtp->dtls.ssl); + rtp->dtls.connection = AST_RTP_DTLS_CONNECTION_NEW; } - SSL_shutdown(rtp->ssl); - rtp->connection = AST_RTP_DTLS_CONNECTION_NEW; + if (rtp->rtcp && SSL_is_init_finished(rtp->rtcp->dtls.ssl)) { + SSL_shutdown(rtp->rtcp->dtls.ssl); + rtp->rtcp->dtls.connection = AST_RTP_DTLS_CONNECTION_NEW; + } } static enum ast_rtp_dtls_connection ast_rtp_dtls_get_connection(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - return rtp->connection; + return rtp->dtls.connection; } static enum ast_rtp_dtls_setup ast_rtp_dtls_get_setup(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - return rtp->dtls_setup; + return rtp->dtls.dtls_setup; } -static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup) +static void dtls_set_setup(enum ast_rtp_dtls_setup *dtls_setup, enum ast_rtp_dtls_setup setup, SSL *ssl) { - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - enum ast_rtp_dtls_setup old = rtp->dtls_setup; + enum ast_rtp_dtls_setup old = *dtls_setup; switch (setup) { case AST_RTP_DTLS_SETUP_ACTIVE: - rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; + *dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; break; case AST_RTP_DTLS_SETUP_PASSIVE: - rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE; + *dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE; break; case AST_RTP_DTLS_SETUP_ACTPASS: /* We can't respond to an actpass setup with actpass ourselves... so respond with active, as we can initiate connections */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { - rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE; + if (*dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { + *dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE; } break; case AST_RTP_DTLS_SETUP_HOLDCONN: - rtp->dtls_setup = AST_RTP_DTLS_SETUP_HOLDCONN; + *dtls_setup = AST_RTP_DTLS_SETUP_HOLDCONN; break; default: /* This should never occur... if it does exit early as we don't know what state things are in */ @@ -947,46 +1087,64 @@ static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_r } /* If the setup state did not change we go on as if nothing happened */ - if (old == rtp->dtls_setup) { + if (old == *dtls_setup) { return; } /* If they don't want us to establish a connection wait until later */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_HOLDCONN) { + if (*dtls_setup == AST_RTP_DTLS_SETUP_HOLDCONN) { return; } - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) { - SSL_set_connect_state(rtp->ssl); - } else if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { - SSL_set_accept_state(rtp->ssl); + if (*dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) { + SSL_set_connect_state(ssl); + } else if (*dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { + SSL_set_accept_state(ssl); } else { return; } } +static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (rtp->dtls.ssl) { + dtls_set_setup(&rtp->dtls.dtls_setup, setup, rtp->dtls.ssl); + } + + if (rtp->rtcp && rtp->rtcp->dtls.ssl) { + dtls_set_setup(&rtp->rtcp->dtls.dtls_setup, setup, rtp->rtcp->dtls.ssl); + } +} + static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint) { char *tmp = ast_strdupa(fingerprint), *value; int pos = 0; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - if (hash != AST_RTP_DTLS_HASH_SHA1) { + if (hash != AST_RTP_DTLS_HASH_SHA1 && hash != AST_RTP_DTLS_HASH_SHA256) { return; } + rtp->remote_hash = hash; + while ((value = strsep(&tmp, ":")) && (pos != (EVP_MAX_MD_SIZE - 1))) { sscanf(value, "%02x", (unsigned int*)&rtp->remote_fingerprint[pos++]); } } -static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash) +static enum ast_rtp_dtls_hash ast_rtp_dtls_get_fingerprint_hash(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - if (hash != AST_RTP_DTLS_HASH_SHA1) { - return NULL; - } + return rtp->local_hash; +} + +static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); return rtp->local_fingerprint; } @@ -1001,6 +1159,7 @@ static struct ast_rtp_engine_dtls ast_rtp_dtls = { .get_setup = ast_rtp_dtls_get_setup, .set_setup = ast_rtp_dtls_set_setup, .set_fingerprint = ast_rtp_dtls_set_fingerprint, + .get_fingerprint_hash = ast_rtp_dtls_get_fingerprint_hash, .get_fingerprint = ast_rtp_dtls_get_fingerprint, }; @@ -1044,10 +1203,42 @@ static struct ast_rtp_engine asterisk_rtp_engine = { static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq); +#ifdef HAVE_OPENSSL_SRTP +static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtls_details *dtls, int rtcp) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (!dtls->ssl) { + return; + } + + if (SSL_is_init_finished(dtls->ssl)) { + SSL_clear(dtls->ssl); + if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { + SSL_set_accept_state(dtls->ssl); + } else { + SSL_set_connect_state(dtls->ssl); + } + dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; + } + SSL_do_handshake(dtls->ssl); + dtls_srtp_check_pending(instance, rtp, rtcp); +} +#endif + #ifdef USE_PJPROJECT static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) { - struct ast_rtp *rtp = ice->user_data; + struct ast_rtp_instance *instance = ice->user_data; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + +#ifdef HAVE_OPENSSL_SRTP + dtls_perform_handshake(instance, &rtp->dtls, 0); + + if (rtp->rtcp) { + dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1); + } +#endif if (!strictrtp) { return; @@ -1059,7 +1250,8 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status) static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { - struct ast_rtp *rtp = ice->user_data; + struct ast_rtp_instance *instance = ice->user_data; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); /* Instead of handling the packet here (which really doesn't work with our architecture) we set a bit to indicate that it should be handled after pj_ice_sess_on_rx_pkt * returns */ @@ -1068,7 +1260,8 @@ static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned static pj_status_t ast_rtp_on_ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) { - struct ast_rtp *rtp = ice->user_data; + struct ast_rtp_instance *instance = ice->user_data; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); pj_status_t status = PJ_EINVALIDOP; pj_ssize_t _size = (pj_ssize_t)size; @@ -1247,9 +1440,54 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr) } #ifdef HAVE_OPENSSL_SRTP -static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp) + +static int dtls_srtp_handle_timeout(const void *data) { - size_t pending = BIO_ctrl_pending(rtp->write_bio); + struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + if (!rtp) + { + return 0; + } + + ast_mutex_lock(&rtp->dtls_timer_lock); + if (rtp->dtlstimerid == -1) + { + ast_mutex_unlock(&rtp->dtls_timer_lock); + ao2_ref(instance, -1); + return 0; + } + + rtp->dtlstimerid = -1; + ast_mutex_unlock(&rtp->dtls_timer_lock); + + if (rtp->dtls.ssl && !SSL_is_init_finished(rtp->dtls.ssl)) { + DTLSv1_handle_timeout(rtp->dtls.ssl); + } + dtls_srtp_check_pending(instance, rtp, 0); + + if (rtp->rtcp && rtp->rtcp->dtls.ssl && !SSL_is_init_finished(rtp->rtcp->dtls.ssl)) { + DTLSv1_handle_timeout(rtp->rtcp->dtls.ssl); + } + dtls_srtp_check_pending(instance, rtp, 1); + + ao2_ref(instance, -1); + + return 0; +} + +static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp) +{ + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; + size_t pending; + struct timeval dtls_timeout; /* timeout on DTLS */ + + if (!dtls->ssl || !dtls->write_bio) { + return; + } + + pending = BIO_ctrl_pending(dtls->write_bio); if (pending > 0) { char outgoing[pending]; @@ -1257,16 +1495,37 @@ static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct as struct ast_sockaddr remote_address = { {0, } }; int ice; - ast_rtp_instance_get_remote_address(instance, &remote_address); + if (!rtcp) { + ast_rtp_instance_get_remote_address(instance, &remote_address); + } else { + ast_sockaddr_copy(&remote_address, &rtp->rtcp->them); + } /* If we do not yet know an address to send this to defer it until we do */ if (ast_sockaddr_isnull(&remote_address)) { return; } - out = BIO_read(rtp->write_bio, outgoing, sizeof(outgoing)); + out = BIO_read(dtls->write_bio, outgoing, sizeof(outgoing)); + + /* Stop existing DTLS timer if running */ + ast_mutex_lock(&rtp->dtls_timer_lock); + if (rtp->dtlstimerid > -1) { + AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1)); + rtp->dtlstimerid = -1; + } + + if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) { + int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000; + ao2_ref(instance, +1); + if ((rtp->dtlstimerid = ast_sched_add(rtp->sched, timeout, dtls_srtp_handle_timeout, instance)) < 0) { + ao2_ref(instance, -1); + ast_log(LOG_WARNING, "scheduling DTLS retransmission for RTP instance [%p] failed.\n", instance); + } + } + ast_mutex_unlock(&rtp->dtls_timer_lock); - __rtp_sendto(instance, outgoing, out, 0, &remote_address, 0, &ice, 0); + __rtp_sendto(instance, outgoing, out, 0, &remote_address, rtcp, &ice, 0); } } @@ -1275,9 +1534,15 @@ static int dtls_srtp_renegotiate(const void *data) struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - SSL_renegotiate(rtp->ssl); - SSL_do_handshake(rtp->ssl); - dtls_srtp_check_pending(instance, rtp); + SSL_renegotiate(rtp->dtls.ssl); + SSL_do_handshake(rtp->dtls.ssl); + dtls_srtp_check_pending(instance, rtp, 0); + + if (rtp->rtcp && rtp->rtcp->dtls.ssl) { + SSL_renegotiate(rtp->rtcp->dtls.ssl); + SSL_do_handshake(rtp->rtcp->dtls.ssl); + dtls_srtp_check_pending(instance, rtp, 1); + } rtp->rekeyid = -1; ao2_ref(instance, -1); @@ -1293,20 +1558,30 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as struct ast_rtp_instance_stats stats = { 0, }; /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ - if (SSL_CTX_get_verify_mode(rtp->ssl_ctx) != SSL_VERIFY_NONE) { + if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) { X509 *certificate; - if (!(certificate = SSL_get_peer_certificate(rtp->ssl))) { + if (!(certificate = SSL_get_peer_certificate(rtp->dtls.ssl))) { ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance); return -1; } /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ if (rtp->remote_fingerprint[0]) { + const EVP_MD *type; unsigned char fingerprint[EVP_MAX_MD_SIZE]; unsigned int size; - if (!X509_digest(certificate, EVP_sha1(), fingerprint, &size) || + if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA1) { + type = EVP_sha1(); + } else if (rtp->remote_hash == AST_RTP_DTLS_HASH_SHA256) { + type = EVP_sha256(); + } else { + ast_log(LOG_WARNING, "Unsupported fingerprint hash type on RTP instance '%p'\n", instance); + return -1; + } + + if (!X509_digest(certificate, type, fingerprint, &size) || !size || memcmp(fingerprint, rtp->remote_fingerprint, size)) { X509_free(certificate); @@ -1320,21 +1595,21 @@ static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct as } /* Ensure that certificate verification was successful */ - if (SSL_get_verify_result(rtp->ssl) != X509_V_OK) { + if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(rtp->dtls.ssl) != X509_V_OK) { ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n", instance); return -1; } /* Produce key information and set up SRTP */ - if (!SSL_export_keying_material(rtp->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { + if (!SSL_export_keying_material(rtp->dtls.ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n", instance); return -1; } /* Whether we are acting as a server or client determines where the keys/salts are */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) { + if (rtp->dtls.dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) { local_key = material; remote_key = local_key + SRTP_MASTER_KEY_LEN; local_salt = remote_key + SRTP_MASTER_KEY_LEN; @@ -1420,50 +1695,51 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s } #ifdef HAVE_OPENSSL_SRTP - if (!rtcp) { - dtls_srtp_check_pending(instance, rtp); + dtls_srtp_check_pending(instance, rtp, rtcp); - /* If this is an SSL packet pass it to OpenSSL for processing */ - if ((*in >= 20) && (*in <= 64)) { - int res = 0; + /* If this is an SSL packet pass it to OpenSSL for processing */ + if ((*in >= 20) && (*in <= 64)) { + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; + int res = 0; - /* If no SSL session actually exists terminate things */ - if (!rtp->ssl) { - ast_log(LOG_ERROR, "Received SSL traffic on RTP instance '%p' without an SSL session\n", - instance); - return -1; - } - - /* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { - rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; - SSL_set_accept_state(rtp->ssl); - } + /* If no SSL session actually exists terminate things */ + if (!dtls->ssl) { + ast_log(LOG_ERROR, "Received SSL traffic on RTP instance '%p' without an SSL session\n", + instance); + return -1; + } - dtls_srtp_check_pending(instance, rtp); + /* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */ + if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { + dtls->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; + SSL_set_accept_state(dtls->ssl); + } - BIO_write(rtp->read_bio, buf, len); + dtls_srtp_check_pending(instance, rtp, rtcp); - len = SSL_read(rtp->ssl, buf, len); + BIO_write(dtls->read_bio, buf, len); - dtls_srtp_check_pending(instance, rtp); + len = SSL_read(dtls->ssl, buf, len); - if (rtp->dtls_failure) { - ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p', terminating\n", - instance); - return -1; - } + if ((len < 0) && (SSL_get_error(dtls->ssl, len) == SSL_ERROR_SSL)) { + unsigned long error = ERR_get_error(); + ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p' due to reason '%s', terminating\n", + instance, ERR_reason_error_string(error)); + return -1; + } - if (SSL_is_init_finished(rtp->ssl)) { - /* Any further connections will be existing since this is now established */ - rtp->connection = AST_RTP_DTLS_CONNECTION_EXISTING; + dtls_srtp_check_pending(instance, rtp, rtcp); + if (SSL_is_init_finished(dtls->ssl)) { + /* Any further connections will be existing since this is now established */ + dtls->connection = AST_RTP_DTLS_CONNECTION_EXISTING; + if (!rtcp) { /* Use the keying material to set up key/salt information */ res = dtls_srtp_setup(rtp, srtp, instance); } - - return res; } + + return res; } #endif @@ -1477,7 +1753,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &combined, &address); - status = pj_ice_sess_on_rx_pkt(rtp->ice, rtcp ? COMPONENT_RTCP : COMPONENT_RTP, + status = pj_ice_sess_on_rx_pkt(rtp->ice, rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, rtcp ? TRANSPORT_SOCKET_RTCP : TRANSPORT_SOCKET_RTP, buf, len, &address, pj_sockaddr_get_len(&address)); if (status != PJ_SUCCESS) { @@ -1529,9 +1805,9 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz if (rtp->ice) { pj_thread_register_check(); - if (pj_ice_sess_send_data(rtp->ice, rtcp ? COMPONENT_RTCP : COMPONENT_RTP, temp, len) == PJ_SUCCESS) { + if (pj_ice_sess_send_data(rtp->ice, rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len) == PJ_SUCCESS) { *ice = 1; - return 0; + return len; } } #endif @@ -1669,15 +1945,19 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct } /* If configured to use a STUN server to get our external mapped address do so */ - if (stunaddr.sin_addr.s_addr && ast_sockaddr_is_ipv4(addr)) { + if (stunaddr.sin_addr.s_addr && ast_sockaddr_is_ipv4(addr) && count) { struct sockaddr_in answer; - if (!ast_stun_request(rtp->s, &stunaddr, NULL, &answer)) { + if (!ast_stun_request(component == AST_RTP_ICE_COMPONENT_RTCP ? rtp->rtcp->s : rtp->s, &stunaddr, NULL, &answer)) { + pj_sockaddr base; pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr))); + /* Use the first local host candidate as the base */ + pj_sockaddr_cp(&base, &address[0]); + pj_sockaddr_init(pj_AF_INET(), &address[0], &mapped, ntohs(answer.sin_port)); - ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &address[0], &address[0], + ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &address[0], &base, NULL, pj_sockaddr_get_len(&address[0])); } } @@ -1719,16 +1999,68 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct } #endif +#ifdef USE_PJPROJECT +/*! + * \internal + * \brief Creates an ICE session. Can be used to replace a destroyed ICE session. + * + * \param instance RTP instance for which the ICE session is being replaced + * \param addr ast_sockaddr to use for adding RTP candidates to the ICE session + * \param port port to use for adding RTP candidates to the ICE session + * \param replace 0 when creating a new session, 1 when replacing a destroyed session + * + * \retval 0 on success + * \retval -1 on failure + */ +static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *addr, + int port, int replace) +{ + pj_stun_config stun_config; + pj_str_t ufrag, passwd; + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + + ao2_cleanup(rtp->ice_local_candidates); + rtp->ice_local_candidates = NULL; + + pj_thread_register_check(); + + pj_stun_config_init(&stun_config, &cachingpool.factory, 0, ioqueue, timerheap); + + ufrag = pj_str(rtp->local_ufrag); + passwd = pj_str(rtp->local_passwd); + + /* Create an ICE session for ICE negotiation */ + if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2, + &ast_rtp_ice_sess_cb, &ufrag, &passwd, &rtp->ice) == PJ_SUCCESS) { + /* Make this available for the callbacks */ + rtp->ice->user_data = instance; + + /* Add all of the available candidates to the ICE session */ + rtp_add_candidates_to_ice(instance, rtp, addr, port, AST_RTP_ICE_COMPONENT_RTP, + TRANSPORT_SOCKET_RTP, &ast_rtp_turn_rtp_sock_cb, &rtp->turn_rtp); + + /* Only add the RTCP candidates to ICE when replacing the session. New sessions + * handle this in a separate part of the setup phase */ + if (replace && rtp->rtcp) { + rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, + ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, + TRANSPORT_SOCKET_RTCP, &ast_rtp_turn_rtcp_sock_cb, &rtp->turn_rtcp); + } + + return 0; + } + + return -1; + +} +#endif + static int ast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data) { struct ast_rtp *rtp = NULL; int x, startplace; -#ifdef USE_PJPROJECT - pj_stun_config stun_config; - pj_str_t ufrag, passwd; -#endif /* Create a new RTP structure to hold all of our data */ if (!(rtp = ast_calloc(1, sizeof(*rtp)))) { @@ -1791,22 +2123,17 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, ast_rtp_instance_set_data(instance, rtp); #ifdef USE_PJPROJECT - pj_thread_register_check(); - - pj_stun_config_init(&stun_config, &cachingpool.factory, 0, ioqueue, timerheap); - generate_random_string(rtp->local_ufrag, sizeof(rtp->local_ufrag)); - ufrag = pj_str(rtp->local_ufrag); generate_random_string(rtp->local_passwd, sizeof(rtp->local_passwd)); - passwd = pj_str(rtp->local_passwd); /* Create an ICE session for ICE negotiation */ - if (icesupport && pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2, &ast_rtp_ice_sess_cb, &ufrag, &passwd, &rtp->ice) == PJ_SUCCESS) { - /* Make this available for the callbacks */ - rtp->ice->user_data = rtp; - - /* Add all of the available candidates to the ICE session */ - rtp_add_candidates_to_ice(instance, rtp, addr, x, COMPONENT_RTP, TRANSPORT_SOCKET_RTP, &ast_rtp_turn_rtp_sock_cb, &rtp->turn_rtp); + if (icesupport) { + if (ice_create(instance, addr, x, 0)) { + ast_log(LOG_NOTICE, "Failed to start ICE session\n"); + } else { + rtp->ice_port = x; + ast_sockaddr_copy(&rtp->ice_original_rtp_addr, addr); + } } #endif @@ -1815,6 +2142,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, #ifdef HAVE_OPENSSL_SRTP rtp->rekeyid = -1; + rtp->dtlstimerid = -1; #endif return 0; @@ -1842,6 +2170,11 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) * RTP instance while it's active. */ close(rtp->rtcp->s); +#ifdef HAVE_OPENSSL_SRTP + if (rtp->rtcp->dtls.ssl) { + SSL_free(rtp->rtcp->dtls.ssl); + } +#endif ast_free(rtp->rtcp); } @@ -1872,12 +2205,12 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) } /* Destroy any candidates */ - if (rtp->local_candidates) { - ao2_ref(rtp->local_candidates, -1); + if (rtp->ice_local_candidates) { + ao2_ref(rtp->ice_local_candidates, -1); } - if (rtp->remote_candidates) { - ao2_ref(rtp->remote_candidates, -1); + if (rtp->ice_active_remote_candidates) { + ao2_ref(rtp->ice_active_remote_candidates, -1); } /* Destroy synchronization items */ @@ -1892,8 +2225,8 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) } /* Destroy the SSL session if present */ - if (rtp->ssl) { - SSL_free(rtp->ssl); + if (rtp->dtls.ssl) { + SSL_free(rtp->dtls.ssl); } #endif @@ -1971,7 +2304,7 @@ static int ast_rtp_dtmf_begin(struct ast_rtp_instance *instance, char digit) strerror(errno)); } #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address); #endif if (rtp_debug_test_addr(&remote_address)) { ast_verbose("Sent RTP DTMF packet to %s%s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", @@ -2023,7 +2356,7 @@ static int ast_rtp_dtmf_continuation(struct ast_rtp_instance *instance) } #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address); #endif if (rtp_debug_test_addr(&remote_address)) { @@ -2100,7 +2433,7 @@ static int ast_rtp_dtmf_end_with_duration(struct ast_rtp_instance *instance, cha } #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address); #endif if (rtp_debug_test_addr(&remote_address)) { @@ -2285,7 +2618,7 @@ static int ast_rtcp_write_rr(struct ast_rtp_instance *instance) rtp->rtcp->rr_count++; #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTCP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTCP, &remote_address); #endif if (rtcp_debug_test_addr(&remote_address)) { @@ -2398,7 +2731,7 @@ static int ast_rtcp_write_sr(struct ast_rtp_instance *instance) rtp->rtcp->lastsrtxcount = rtp->txcount; #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTCP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTCP, &remote_address); #endif if (rtcp_debug_test_addr(&rtp->rtcp->them)) { @@ -2589,7 +2922,7 @@ static int ast_rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame } #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address); #endif if (rtp_debug_test_addr(&remote_address)) { @@ -3470,7 +3803,7 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int } #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address); #endif if (rtp_debug_test_addr(&remote_address)) { @@ -3930,11 +4263,15 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro #ifdef USE_PJPROJECT if (rtp->ice) { - rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP, + rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP, &ast_rtp_turn_rtcp_sock_cb, &rtp->turn_rtcp); } #endif +#ifdef HAVE_OPENSSL_SRTP + dtls_setup_rtcp(instance); +#endif + return; } else { if (rtp->rtcp) { @@ -3950,6 +4287,11 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro rtp->rtcp->schedid = -1; } close(rtp->rtcp->s); +#ifdef HAVE_OPENSSL_SRTP + if (rtp->rtcp->dtls.ssl) { + SSL_free(rtp->rtcp->dtls.ssl); + } +#endif ast_free(rtp->rtcp); rtp->rtcp = NULL; } @@ -4154,6 +4496,9 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance) #ifdef HAVE_OPENSSL_SRTP AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1)); + ast_mutex_lock(&rtp->dtls_timer_lock); + AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1)); + ast_mutex_unlock(&rtp->dtls_timer_lock); #endif if (rtp->rtcp && rtp->rtcp->schedid > 0) { @@ -4223,7 +4568,7 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level) } #ifdef USE_PJPROJECT - update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address); + update_address_with_ice_candidate(rtp, AST_RTP_ICE_COMPONENT_RTP, &remote_address); #endif if (rtp_debug_test_addr(&remote_address)) { @@ -4243,13 +4588,18 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - if (!rtp->ssl) { + /* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */ +#ifdef USE_PJPROJECT + if (rtp->ice) { return 0; } +#endif - SSL_do_handshake(rtp->ssl); + dtls_perform_handshake(instance, &rtp->dtls, 0); - dtls_srtp_check_pending(instance, rtp); + if (rtp->rtcp) { + dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1); + } return 0; }