res_pjsip: Remove ephemeral registered contacts on transport shutdown.
[asterisk/asterisk.git] / res / res_pjsip / pjsip_configuration.c
index a8b4517..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
@@ -105,6 +106,40 @@ static void endpoint_update_state(struct ast_endpoint *endpoint, enum ast_endpoi
        ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "PJSIP/%s", ast_endpoint_get_resource(endpoint));
 }
 
+static void endpoint_publish_contact_status(struct ast_endpoint *endpoint, struct ast_sip_contact_status *contact)
+{
+       struct ast_json *blob;
+       char rtt[32];
+
+       snprintf(rtt, sizeof(rtt), "%" PRId64, contact->rtt);
+       blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
+               "contact_status", ast_sip_get_contact_status_label(contact->status),
+               "aor", contact->aor,
+               "uri", contact->uri,
+               "roundtrip_usec", rtt,
+               "endpoint_name", ast_endpoint_get_resource(endpoint));
+       if (blob) {
+               ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
+               ast_json_unref(blob);
+       }
+}
+
+/*! \brief Callback function for publishing the status of an endpoint */
+static int persistent_endpoint_publish_status(void *obj, void *arg, int flags)
+{
+       struct sip_persistent_endpoint *persistent = obj;
+       struct ast_endpoint *endpoint = persistent->endpoint;
+       struct ast_sip_contact_status *status = arg;
+
+       /* If the status' aor isn't one of the endpoint's, we skip */
+       if (!strstr(persistent->aors, status->aor)) {
+               return 0;
+       }
+
+       endpoint_publish_contact_status(endpoint, status);
+       return 0;
+}
+
 /*! \brief Callback function for changing the state of an endpoint */
 static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
 {
@@ -112,54 +147,40 @@ static int persistent_endpoint_update_state(void *obj, void *arg, int flags)
        struct ast_endpoint *endpoint = persistent->endpoint;
        struct ast_sip_contact_status *status = arg;
        struct ao2_container *contacts;
-       struct ast_json *blob;
-       struct ao2_iterator i;
+       struct ao2_iterator iter;
        struct ast_sip_contact *contact;
        enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE;
 
-       if (status) {
-               char rtt[32];
-
-               /* If the status' aor isn't one of the endpoint's, we skip */
-               if (!strstr(persistent->aors, status->aor)) {
-                       return 0;
-               }
-
-               snprintf(rtt, sizeof(rtt), "%" PRId64, status->rtt);
-               blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
-                       "contact_status", ast_sip_get_contact_status_label(status->status),
-                       "aor", status->aor,
-                       "uri", status->uri,
-                       "roundtrip_usec", rtt,
-                       "endpoint_name", ast_endpoint_get_resource(endpoint));
-               ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob);
-               ast_json_unref(blob);
+       /* If the status' aor isn't one of the endpoint's, we skip */
+       if (!strstr(persistent->aors, status->aor)) {
+               return 0;
        }
 
+       endpoint_publish_contact_status(endpoint, status);
+
        /* Find all the contacts for this endpoint.  If ANY are available,
         * mark the endpoint as ONLINE.
         */
        contacts = ast_sip_location_retrieve_contacts_from_aor_list(persistent->aors);
        if (contacts) {
-               i = ao2_iterator_init(contacts, 0);
-               while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&i))) {
+               iter = ao2_iterator_init(contacts, 0);
+               while (state == AST_ENDPOINT_OFFLINE && (contact = ao2_iterator_next(&iter))) {
                        struct ast_sip_contact_status *contact_status;
                        const char *contact_id = ast_sorcery_object_get_id(contact);
 
                        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;
                        }
                        ao2_cleanup(contact_status);
                        ao2_ref(contact, -1);
                }
-               ao2_iterator_destroy(&i);
+               ao2_iterator_destroy(&iter);
                ao2_ref(contacts, -1);
        }
 
-       endpoint_update_state(endpoint,state);
+       endpoint_update_state(endpoint, state);
 
        return 0;
 }
@@ -181,7 +202,7 @@ static void persistent_endpoint_contact_created_observer(const void *object)
 
        contact_status->status = CREATED;
 
-       ast_verb(2, "Contact %s/%s has been created\n",contact->aor, contact->uri);
+       ast_verb(2, "Contact %s/%s has been created\n", contact->aor, contact->uri);
 
        ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
        ao2_cleanup(contact_status);
@@ -195,7 +216,7 @@ static void persistent_endpoint_contact_deleted_observer(const void *object)
 
        contact_status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact));
        if (!contact_status) {
-               ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n",
+               ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s/%s\n",
                        contact->aor, contact->uri);
                return;
        }
@@ -222,13 +243,21 @@ static void persistent_endpoint_contact_status_observer(const void *object)
 {
        struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object;
 
+       if (contact_status->refresh) {
+               /* We are only re-publishing the contact status. */
+               ao2_callback(persistent_endpoints, OBJ_NODATA,
+                       persistent_endpoint_publish_status, contact_status);
+               return;
+       }
+
        /* If rtt_start is set (this is the outgoing OPTIONS), ignore. */
        if (contact_status->rtt_start.tv_sec > 0) {
                return;
        }
 
        if (contact_status->status != contact_status->last_status) {
-               ast_verb(3, "Contact %s/%s is now %s.  RTT: %.3f msec\n", contact_status->aor, contact_status->uri,
+               ast_verb(3, "Contact %s/%s is now %s.  RTT: %.3f msec\n",
+                       contact_status->aor, contact_status->uri,
                        ast_sip_get_contact_status_label(contact_status->status),
                        contact_status->rtt / 1000.0);
 
@@ -239,19 +268,23 @@ static void persistent_endpoint_contact_status_observer(const void *object)
 
                ast_test_suite_event_notify("AOR_CONTACT_UPDATE",
                        "Contact: %s\r\n"
-                               "Status: %s",
+                       "Status: %s",
                        ast_sorcery_object_get_id(contact_status),
                        ast_sip_get_contact_status_label(contact_status->status));
 
-               ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status);
+               ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state,
+                       contact_status);
        } else {
                ast_debug(3, "Contact %s/%s status didn't change: %s, RTT: %.3f msec\n",
-                       contact_status->aor, contact_status->uri, ast_sip_get_contact_status_label(contact_status->status),
+                       contact_status->aor, contact_status->uri,
+                       ast_sip_get_contact_status_label(contact_status->status),
                        contact_status->rtt / 1000.0);
        }
 
        ast_statsd_log_full_va("PJSIP.contacts.%s.rtt", AST_STATSD_TIMER,
-               contact_status->status != AVAILABLE ? -1 : contact_status->rtt / 1000, 1.0, ast_sorcery_object_get_id(contact_status));
+               contact_status->status != AVAILABLE ? -1 : contact_status->rtt / 1000,
+               1.0,
+               ast_sorcery_object_get_id(contact_status));
 }
 
 /*! \brief Observer for contacts so state can be updated on respective endpoints */
@@ -263,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 = {
@@ -332,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;
 }
 
@@ -570,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);
@@ -930,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);
@@ -1103,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)
 {
@@ -1175,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);
@@ -1191,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 */
@@ -1209,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);
        }
@@ -1248,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;
 }
 
@@ -1635,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);
        }
 
@@ -1724,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;
        }
 
@@ -1816,6 +1939,7 @@ int ast_res_pjsip_initialize_configuration(void)
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, faxdetect));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect_timeout", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, faxdetect_timeout));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.ipv6));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tone_zone", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, zone));
@@ -1833,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));
@@ -1860,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");
@@ -1922,6 +2057,8 @@ int ast_res_pjsip_initialize_configuration(void)
 
        load_all_endpoints();
 
+       ast_sip_location_prune_boot_contacts();
+
        return 0;
 }
 
@@ -1945,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)
@@ -1978,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);
@@ -1990,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)