res_pjsip: Remove ephemeral registered contacts on transport shutdown.
[asterisk/asterisk.git] / res / res_pjsip / pjsip_configuration.c
index 16405eb..715ffe8 100644 (file)
@@ -22,6 +22,7 @@
 #include "asterisk/test.h"
 #include "asterisk/statsd.h"
 #include "asterisk/pbx.h"
+#include "asterisk/stream.h"
 
 /*! \brief Number of buckets for persistent endpoint information */
 #define PERSISTENT_BUCKETS 53
@@ -169,7 +170,6 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
 
                        contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
                                CONTACT_STATUS, contact_id);
-
                        if (contact_status && contact_status->status != UNAVAILABLE) {
                                state = AST_ENDPOINT_ONLINE;
                        }
@@ -296,7 +296,8 @@ static void endpoint_deleted_observer(const void *object)
 {
        const struct ast_sip_endpoint *endpoint = object;
 
-       ao2_find(persistent_endpoints, ast_endpoint_get_resource(endpoint->persistent), OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
+       ao2_find(persistent_endpoints, ast_endpoint_get_resource(endpoint->persistent),
+               OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
 }
 
 static const struct ast_sorcery_observer endpoint_observers = {
@@ -365,42 +366,29 @@ static int contact_acl_to_str(const void *obj, const intptr_t *args, char **buf)
 static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
        struct ast_sip_endpoint *endpoint = obj;
+       enum ast_sip_dtmf_mode dtmf = ast_sip_str_to_dtmf(var->value);
 
-       if (!strcasecmp(var->value, "rfc4733")) {
-               endpoint->dtmf = AST_SIP_DTMF_RFC_4733;
-       } else if (!strcasecmp(var->value, "inband")) {
-               endpoint->dtmf = AST_SIP_DTMF_INBAND;
-       } else if (!strcasecmp(var->value, "info")) {
-               endpoint->dtmf = AST_SIP_DTMF_INFO;
-       } else if (!strcasecmp(var->value, "auto")) {
-               endpoint->dtmf = AST_SIP_DTMF_AUTO;
-       } else if (!strcasecmp(var->value, "none")) {
-               endpoint->dtmf = AST_SIP_DTMF_NONE;
-       } else {
+       if (dtmf == -1) {
                return -1;
        }
 
+       endpoint->dtmf = dtmf;
        return 0;
 }
 
 static int dtmf_to_str(const void *obj, const intptr_t *args, char **buf)
 {
        const struct ast_sip_endpoint *endpoint = obj;
+       char dtmf_str[20];
+       int result = -1;
 
-       switch (endpoint->dtmf) {
-       case AST_SIP_DTMF_RFC_4733 :
-               *buf = "rfc4733"; break;
-       case AST_SIP_DTMF_INBAND :
-               *buf = "inband"; break;
-       case AST_SIP_DTMF_INFO :
-               *buf = "info"; break;
-       case AST_SIP_DTMF_AUTO :
-               *buf = "auto"; break;
-       default:
-               *buf = "none";
-       }
+       result = ast_sip_dtmf_to_str(endpoint->dtmf, dtmf_str, sizeof(dtmf_str));
 
-       *buf = ast_strdup(*buf);
+       if (result == 0) {
+               *buf = ast_strdup(dtmf_str);
+       } else {
+               *buf = ast_strdup("none");
+       }
        return 0;
 }
 
@@ -603,6 +591,10 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var,
                        endpoint->ident_method = 0;
                        return -1;
                }
+               if (endpoint->ident_method & method) {
+                       /* We are already indentifying by this method.  No need to do it again. */
+                       continue;
+               }
 
                endpoint->ident_method |= method;
                AST_VECTOR_APPEND(&endpoint->ident_method_order, method);
@@ -963,7 +955,9 @@ static int dtls_handler(const struct aco_option *opt,
 {
        struct ast_sip_endpoint *endpoint = obj;
        char *name = ast_strdupa(var->name);
-       char *front, *back, *buf = name;
+       char *front = NULL;
+       char *back = NULL;
+       char *buf = name;
 
        /* strip out underscores in the name */
        front = strtok_r(buf, "_", &back);
@@ -1136,6 +1130,37 @@ static int tos_video_to_str(const void *obj, const intptr_t *args, char **buf)
        return 0;
 }
 
+static int from_user_handler(const struct aco_option *opt,
+       struct ast_variable *var, void *obj)
+{
+       struct ast_sip_endpoint *endpoint = obj;
+       /* Valid non-alphanumeric characters for URI */
+       char *valid_uri_marks = "-_.!~*`()";
+       const char *val;
+
+       for (val = var->value; *val; val++) {
+               if (!strchr(valid_uri_marks, *val) && !isdigit(*val) && !isalpha(*val)) {
+                       ast_log(LOG_ERROR, "Error configuring endpoint '%s' - '%s' field "
+                       "contains invalid character '%c'\n",
+                       ast_sorcery_object_get_id(endpoint), var->name, *val);
+                       return -1;
+               }
+       }
+
+       ast_string_field_set(endpoint, fromuser, var->value);
+
+       return 0;
+}
+
+static int from_user_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+       const struct ast_sip_endpoint *endpoint = obj;
+
+       *buf = ast_strdup(endpoint->fromuser);
+
+       return 0;
+}
+
 static int set_var_handler(const struct aco_option *opt,
        struct ast_variable *var, void *obj)
 {
@@ -1208,6 +1233,31 @@ static int voicemail_extension_to_str(const void *obj, const intptr_t *args, cha
        return 0;
 }
 
+static int contact_user_handler(const struct aco_option *opt,
+       struct ast_variable *var, void *obj)
+{
+       struct ast_sip_endpoint *endpoint = obj;
+
+       endpoint->contact_user = ast_strdup(var->value);
+       if (!endpoint->contact_user) {
+               return -1;
+       }
+
+       return 0;
+}
+
+static int contact_user_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+       const struct ast_sip_endpoint *endpoint = obj;
+
+       *buf = ast_strdup(endpoint->contact_user);
+       if (!(*buf)) {
+               return -1;
+       }
+
+       return 0;
+}
+
 static void *sip_nat_hook_alloc(const char *name)
 {
        return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL);
@@ -1224,16 +1274,16 @@ static void persistent_endpoint_destroy(void *obj)
 
 int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state)
 {
-       RAII_VAR(struct sip_persistent_endpoint *, persistent, NULL, ao2_cleanup);
-       SCOPED_AO2LOCK(lock, persistent_endpoints);
+       struct sip_persistent_endpoint *persistent;
 
-       if (!(persistent = ao2_find(persistent_endpoints, endpoint_name, OBJ_KEY | OBJ_NOLOCK))) {
-               return -1;
+       ao2_lock(persistent_endpoints);
+       persistent = ao2_find(persistent_endpoints, endpoint_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (persistent) {
+               endpoint_update_state(persistent->endpoint, state);
+               ao2_ref(persistent, -1);
        }
-
-       endpoint_update_state(persistent->endpoint, state);
-
-       return 0;
+       ao2_unlock(persistent_endpoints);
+       return persistent ? 0 : -1;
 }
 
 /*! \brief Internal function which finds (or creates) persistent endpoint information */
@@ -1242,18 +1292,27 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_
        RAII_VAR(struct sip_persistent_endpoint *, persistent, NULL, ao2_cleanup);
        SCOPED_AO2LOCK(lock, persistent_endpoints);
 
-       if (!(persistent = ao2_find(persistent_endpoints, ast_sorcery_object_get_id(endpoint), OBJ_KEY | OBJ_NOLOCK))) {
-               if (!(persistent = ao2_alloc(sizeof(*persistent), persistent_endpoint_destroy))) {
+       persistent = ao2_find(persistent_endpoints, ast_sorcery_object_get_id(endpoint),
+               OBJ_SEARCH_KEY | OBJ_NOLOCK);
+       if (!persistent) {
+               persistent = ao2_alloc_options(sizeof(*persistent), persistent_endpoint_destroy,
+                       AO2_ALLOC_OPT_LOCK_NOLOCK);
+               if (!persistent) {
                        return NULL;
                }
 
-               if (!(persistent->endpoint = ast_endpoint_create("PJSIP", ast_sorcery_object_get_id(endpoint)))) {
+               persistent->endpoint = ast_endpoint_create("PJSIP",
+                       ast_sorcery_object_get_id(endpoint));
+               if (!persistent->endpoint) {
                        return NULL;
                }
 
                persistent->aors = ast_strdup(endpoint->aors);
+               if (!persistent->aors) {
+                       return NULL;
+               }
 
-               ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN);
+               ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_OFFLINE);
 
                ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK);
        }
@@ -1281,6 +1340,37 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o
                return -1;
        }
 
+       endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs);
+       if (!endpoint->media.topology) {
+               return -1;
+       }
+
+       endpoint->media.rtcp_mux |= endpoint->media.bundle;
+
+       /*
+        * If webrtc has been enabled then enable those attributes, and default
+        * some, that are needed in order for webrtc to work.
+        */
+       endpoint->media.bundle |= endpoint->media.webrtc;
+       endpoint->media.rtcp_mux |= endpoint->media.webrtc;
+       endpoint->media.rtp.use_avpf |= endpoint->media.webrtc;
+       endpoint->media.rtp.ice_support |= endpoint->media.webrtc;
+       endpoint->media.rtp.use_received_transport |= endpoint->media.webrtc;
+
+       if (endpoint->media.webrtc) {
+               endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;
+               endpoint->media.rtp.dtls_cfg.enabled = 1;
+               endpoint->media.rtp.dtls_cfg.default_setup = AST_RTP_DTLS_SETUP_ACTPASS;
+               endpoint->media.rtp.dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;
+
+               if (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.certfile) ||
+                       (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.cafile))) {
+                       ast_log(LOG_ERROR, "WebRTC can't be enabled on endpoint '%s' - a DTLS cert "
+                               "or ca file has not been specified", ast_sorcery_object_get_id(endpoint));
+                       return -1;
+               }
+       }
+
        return 0;
 }
 
@@ -1668,9 +1758,7 @@ static int cli_endpoint_print_body(void *obj, void *arg, int flags)
 
        if (number) {
                print_name_len = strlen(id) + strlen(number) + 2;
-               if (!(print_name = alloca(print_name_len))) {
-                       return -1;
-               }
+               print_name = ast_alloca(print_name_len);
                snprintf(print_name, print_name_len, "%s/%s", id, number);
        }
 
@@ -1757,7 +1845,9 @@ int ast_res_pjsip_initialize_configuration(void)
                return -1;
        }
 
-       if (!(persistent_endpoints = ao2_container_alloc(PERSISTENT_BUCKETS, persistent_endpoint_hash, persistent_endpoint_cmp))) {
+       persistent_endpoints = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+               PERSISTENT_BUCKETS, persistent_endpoint_hash, NULL, persistent_endpoint_cmp);
+       if (!persistent_endpoints) {
                return -1;
        }
 
@@ -1867,7 +1957,7 @@ int ast_res_pjsip_initialize_configuration(void)
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "cos_video", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.cos_video));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_subscribe", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, subscription.allow));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "sub_min_expiry", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, subscription.minexpiry));
-       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromuser));
+       ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "from_user", "", from_user_handler, from_user_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "from_domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, fromdomain));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mwi_from_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, subscription.mwi.fromuser));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_engine", "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.rtp.engine));
@@ -1894,6 +1984,17 @@ int ast_res_pjsip_initialize_configuration(void)
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_permit", "", endpoint_acl_handler, NULL, NULL, 0, 0);
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_acl", "", endpoint_acl_handler, contact_acl_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "subscribe_context", "", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct ast_sip_endpoint, subscription.context));
+       ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_user", "", contact_user_handler, contact_user_to_str, NULL, 0, 0);
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "preferred_codec_only", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, preferred_codec_only));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "asymmetric_rtp_codec", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, asymmetric_rtp_codec));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtcp_mux", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtcp_mux));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_overlap", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_overlap));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "refer_blind_progress", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, refer_blind_progress));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "notify_early_inuse_ringing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, notify_early_inuse_ringing));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_audio_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_audio_streams));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_video_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_video_streams));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "webrtc", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_endpoint, media.webrtc));
 
        if (ast_sip_initialize_sorcery_transport()) {
                ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
@@ -1956,6 +2057,8 @@ int ast_res_pjsip_initialize_configuration(void)
 
        load_all_endpoints();
 
+       ast_sip_location_prune_boot_contacts();
+
        return 0;
 }
 
@@ -1979,6 +2082,7 @@ void ast_res_pjsip_destroy_configuration(void)
        ast_sip_unregister_cli_formatter(endpoint_formatter);
        ast_sip_destroy_cli();
        ao2_cleanup(persistent_endpoints);
+       persistent_endpoints = NULL;
 }
 
 int ast_res_pjsip_reload_configuration(void)
@@ -2012,7 +2116,8 @@ static void endpoint_destructor(void* obj)
 
        ast_string_field_free_memory(endpoint);
 
-       ao2_ref(endpoint->media.codecs, -1);
+       ao2_cleanup(endpoint->media.codecs);
+       ast_stream_topology_free(endpoint->media.topology);
        subscription_configuration_destroy(&endpoint->subscription);
        info_configuration_destroy(&endpoint->info);
        media_configuration_destroy(&endpoint->media);
@@ -2024,6 +2129,9 @@ static void endpoint_destructor(void* obj)
        ao2_cleanup(endpoint->persistent);
        ast_variables_destroy(endpoint->channel_vars);
        AST_VECTOR_FREE(&endpoint->ident_method_order);
+       ast_free(endpoint->contact_user);
+       ast_free_acl_list(endpoint->contact_acl);
+       ast_free_acl_list(endpoint->acl);
 }
 
 static int init_subscription_configuration(struct ast_sip_endpoint_subscription_configuration *subscription)