Merge "chan_sip: option 'notifyringing' change and doc fix"
[asterisk/asterisk.git] / channels / chan_sip.c
index fa4b864..9e87027 100644 (file)
@@ -997,9 +997,9 @@ static struct ao2_container *threadt;
 static struct ao2_container *peers;
 static struct ao2_container *peers_by_ip;
 
-/*! \brief  A bogus peer, to be used when authentication should fail */
-static struct sip_peer *bogus_peer;
-/*! \brief  We can recognise the bogus peer by this invalid MD5 hash */
+/*! \brief A bogus peer, to be used when authentication should fail */
+static AO2_GLOBAL_OBJ_STATIC(g_bogus_peer);
+/*! \brief We can recognize the bogus peer by this invalid MD5 hash */
 #define BOGUS_PEER_MD5SECRET "intentionally_invalid_md5_string"
 
 /*! \brief  The register list: Other SIP proxies we register with and receive calls from */
@@ -1399,6 +1399,7 @@ static char *remove_uri_parameters(char *uri);
 static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req);
 static int get_also_info(struct sip_pvt *p, struct sip_request *oreq);
 static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
+static int use_reason_header(struct sip_pvt *pvt, struct sip_request *req);
 static int set_address_from_contact(struct sip_pvt *pvt);
 static void check_via(struct sip_pvt *p, const struct sip_request *req);
 static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
@@ -1709,6 +1710,7 @@ static int publish_expire(const void *data)
        ast_assert(esc != NULL);
 
        ao2_unlink(esc->compositor, esc_entry);
+       esc_entry->sched_id = -1;
        ao2_ref(esc_entry, -1);
        return 0;
 }
@@ -1741,6 +1743,11 @@ static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc
        /* Bump refcount for scheduler */
        ao2_ref(esc_entry, +1);
        esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry);
+       if (esc_entry->sched_id == -1) {
+               ao2_ref(esc_entry, -1);
+               ao2_ref(esc_entry, -1);
+               return NULL;
+       }
 
        /* Note: This links the esc_entry into the ESC properly */
        create_new_sip_etag(esc_entry, 0);
@@ -4750,6 +4757,11 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
        struct sip_pvt *p = (struct sip_pvt *) ast_channel_tech_pvt(chan);
        char *cp;
 
+       if (!p) {
+               ast_debug(1, "Attempt to Ref a null pointer. Sip private structure is gone!\n");
+               return -1;
+       }
+
        sip_pvt_lock(p);
 
        switch (option) {
@@ -9358,6 +9370,9 @@ static int sip_register(const char *value, int lineno)
                return -1;
        }
 
+       reg->expire = -1;
+       reg->timeout = -1;
+
        if (ast_string_field_init(reg, 256)) {
                ao2_t_ref(reg, -1, "failed to string_field_init, drop reg");
                return -1;
@@ -9431,6 +9446,8 @@ static int sip_subscribe_mwi(const char *value, int lineno)
                return -1;
        }
 
+       mwi->resub = -1;
+
        if (ast_string_field_init(mwi, 256)) {
                ao2_t_ref(mwi, -1, "failed to string_field_init, drop mwi");
                return -1;
@@ -9445,7 +9462,6 @@ static int sip_subscribe_mwi(const char *value, int lineno)
        }
        ast_string_field_set(mwi, hostname, hostname);
        ast_string_field_set(mwi, mailbox, mailbox);
-       mwi->resub = -1;
        mwi->portno = portnum;
        mwi->transport = transport;
 
@@ -10618,7 +10634,11 @@ 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, (req->method != SIP_RESPONSE) ? 0 : 1);
+                       /* Start ICE negotiation here, only when it is response, and setting that we are conrolling agent,
+                          as we are offerer */
+                       if (req->method == SIP_RESPONSE) {
+                               start_ice(p->rtp, 1);
+                       }
                        ast_sockaddr_set_port(sa, portno);
                        ast_rtp_instance_set_remote_address(p->rtp, sa);
                        if (debug) {
@@ -13392,6 +13412,11 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                if (!doing_directmedia) {
                        if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
                                add_ice_to_sdp(p->rtp, &a_audio);
+                               /* Start ICE negotiation, and setting that we are controlled agent,
+                                  as this is response to offer */
+                               if (resp->method == SIP_RESPONSE) {
+                                       start_ice(p->rtp, 0);
+                               }
                        }
 
                        add_dtls_to_sdp(p->rtp, &a_audio);
@@ -16111,6 +16136,49 @@ static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
        return TRUE;
 }
 
+/*!
+ * \brief Parses SIP reason header according to RFC3326 and sets channel's hangupcause if configured so
+ *  and header present
+ *
+ * \note This is used in BYE and CANCEL request and SIP response, but according to RFC3326 it could
+ *       appear in any request, but makes not a lot of sense in others than BYE or CANCEL.
+ *       Currently only implemented for Q.850 status codes.
+ * \retval 0 success
+ * \retval -1 on failure or if not configured
+ */
+static int use_reason_header(struct sip_pvt *pvt, struct sip_request *req)
+{
+       int ret, cause;
+       const char *rp, *rh;
+
+       if (!pvt->owner) {
+               return -1;
+       }
+
+       if (!ast_test_flag(&pvt->flags[1], SIP_PAGE2_Q850_REASON) ||
+               !(rh = sip_get_header(req, "Reason"))) {
+               return -1;
+       }
+
+       rh = ast_skip_blanks(rh);
+       if (strncasecmp(rh, "Q.850", 5)) {
+               return -1;
+       }
+
+       ret = -1;
+       cause = ast_channel_hangupcause(pvt->owner);
+       rp = strstr(rh, "cause=");
+       if (rp && sscanf(rp + 6, "%3d", &cause) == 1) {
+               ret = 0;
+               ast_channel_hangupcause_set(pvt->owner, cause & 0x7f);
+               if (req->debug) {
+                       ast_verbose("Using Reason header for cause code: %d\n",
+                                               ast_channel_hangupcause(pvt->owner));
+               }
+       }
+       return ret;
+}
+
 /*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled
  *
  * \note This calls parse_uri which has the unexpected property that passing more
@@ -17174,8 +17242,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
        /* If we don't want username disclosure, use the bogus_peer when a user
         * is not found. */
        if (!peer && sip_cfg.alwaysauthreject && sip_cfg.autocreatepeer == AUTOPEERS_DISABLED) {
-               peer = bogus_peer;
-               sip_ref_peer(peer, "register_verify: ref the bogus_peer");
+               peer = ao2_t_global_obj_ref(g_bogus_peer, "register_verify: Get the bogus peer.");
        }
 
        if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
@@ -18428,6 +18495,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
        enum check_auth_result res;
        int debug = sip_debug_test_addr(addr);
        struct sip_peer *peer;
+       struct sip_peer *bogus_peer;
 
        if (sipmethod == SIP_SUBSCRIBE) {
                /* For subscribes, match on device name only; for other methods,
@@ -18467,8 +18535,13 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
                /* If you do mind, we use a peer that will never authenticate.
                 * This ensures that we follow the same code path as regular
                 * auth: less chance for username disclosure. */
-               peer = bogus_peer;
-               sip_ref_peer(peer, "sip_ref_peer: check_peer_ok: must ref bogus_peer so unreffing it does not fail");
+               peer = ao2_t_global_obj_ref(g_bogus_peer, "check_peer_ok: Get the bogus peer.");
+               if (!peer) {
+                       return AUTH_DONT_KNOW;
+               }
+               bogus_peer = peer;
+       } else {
+               bogus_peer = NULL;
        }
 
        /*  build_peer, called through sip_find_peer, is not able to check the
@@ -24097,27 +24170,13 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
                msg = "";
 
        sipmethod = find_sip_method(msg);
-
        owner = p->owner;
        if (owner) {
-               const char *rp = NULL, *rh = NULL;
-
                ast_channel_hangupcause_set(owner, 0);
-               if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON) && (rh = sip_get_header(req, "Reason"))) {
-                       rh = ast_skip_blanks(rh);
-                       if (!strncasecmp(rh, "Q.850", 5)) {
-                               int cause = ast_channel_hangupcause(owner);
-                               rp = strstr(rh, "cause=");
-                               if (rp && sscanf(rp + 6, "%30d", &cause) == 1) {
-                                       ast_channel_hangupcause_set(owner, cause & 0x7f);
-                                       if (req->debug)
-                                               ast_verbose("Using Reason header for cause code: %d\n", ast_channel_hangupcause(owner));
-                               }
-                       }
-               }
-
-               if (!ast_channel_hangupcause(owner))
+               if (use_reason_header(p, req)) {
+                       /* Use the SIP cause */
                        ast_channel_hangupcause_set(owner, hangup_sip2cause(resp));
+               }
        }
 
        if (p->socket.type == AST_TRANSPORT_UDP) {
@@ -26409,6 +26468,8 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
                return 0;
        }
 
+       use_reason_header(p, req);
+
        /* At this point, we could have cancelled the invite at the same time
           as the other side sends a CANCEL. Our final reply with error code
           might not have been received by the other side before the CANCEL
@@ -26425,7 +26486,7 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
 
        stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
        if (p->owner) {
-               sip_queue_hangup_cause(p, 0);
+               sip_queue_hangup_cause(p, ast_channel_hangupcause(p->owner));
        } else {
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
        }
@@ -26604,6 +26665,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
                stop_session_timer(p); /* Stop Session-Timer */
        }
 
+       use_reason_header(p, req);
        if (!ast_strlen_zero(sip_get_header(req, "Also"))) {
                ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method.  Ask vendor to support REFER instead\n",
                        ast_sockaddr_stringify(&p->recv));
@@ -26644,7 +26706,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
                                ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
                }
        } else if (p->owner) {
-               sip_queue_hangup_cause(p, 0);
+               sip_queue_hangup_cause(p, ast_channel_hangupcause(p->owner));
                sip_scheddestroy_final(p, DEFAULT_TRANS_TIMEOUT);
                ast_debug(3, "Received bye, issuing owner hangup\n");
        } else {
@@ -31448,6 +31510,8 @@ static int reload_config(enum channelreloadreason reason)
        sip_cfg.peer_rtupdate = TRUE;
        global_dynamic_exclude_static = 0;      /* Exclude static peers */
        sip_cfg.tcp_enabled = FALSE;
+       sip_cfg.websocket_enabled = TRUE;
+       sip_cfg.websocket_write_timeout = AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
 
        /* Session-Timers */
        global_st_mode = SESSION_TIMER_MODE_ACCEPT;
@@ -32231,6 +32295,12 @@ static int reload_config(enum channelreloadreason reason)
                ast_log(LOG_ERROR, "SIP TCP Server start failed. Not listening on TCP socket.\n");
        } else {
                ast_debug(2, "SIP TCP server started\n");
+               if (sip_tcp_desc.accept_fd >= 0) {
+                       int flags = 1;
+                       if (setsockopt(sip_tcp_desc.accept_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
+                               ast_log(LOG_ERROR, "Error enabling TCP keep-alive on sip socket: %s\n", strerror(errno));
+                       }
+               }
        }
 
        /* Start TLS server if needed */
@@ -32251,6 +32321,13 @@ static int reload_config(enum channelreloadreason reason)
                        ast_log(LOG_ERROR, "TLS Server start failed. Not listening on TLS socket.\n");
                        sip_tls_desc.tls_cfg = NULL;
                }
+               if (sip_tls_desc.accept_fd >= 0) {
+                       int flags = 1;
+                       if (setsockopt(sip_tls_desc.accept_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
+                               ast_log(LOG_ERROR, "Error enabling TCP keep-alive on sip socket: %s\n", strerror(errno));
+                               sip_tls_desc.tls_cfg = NULL;
+                       }
+               }
        } else if (sip_tls_desc.tls_cfg->enabled) {
                sip_tls_desc.tls_cfg = NULL;
                ast_log(LOG_WARNING, "SIP TLS server did not load because of errors.\n");
@@ -33155,7 +33232,7 @@ static int sip_do_reload(enum channelreloadreason reason)
 /*! \brief Force reload of module from cli */
 static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       static struct sip_peer *tmp_peer, *new_peer;
+       static struct sip_peer *new_peer;
 
        switch (cmd) {
        case CLI_INIT:
@@ -33178,13 +33255,13 @@ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
        ast_mutex_unlock(&sip_reload_lock);
        restart_monitor();
 
-       tmp_peer = bogus_peer;
        /* Create new bogus peer possibly with new global settings. */
        if ((new_peer = temp_peer("(bogus_peer)"))) {
                ast_string_field_set(new_peer, md5secret, BOGUS_PEER_MD5SECRET);
                ast_clear_flag(&new_peer->flags[0], SIP_INSECURE);
-               bogus_peer = new_peer;
-               ao2_t_ref(tmp_peer, -1, "unref the old bogus_peer during reload");
+               ao2_t_global_obj_replace_unref(g_bogus_peer, new_peer,
+                       "Replacing the old bogus peer during reload.");
+               ao2_t_ref(new_peer, -1, "done with new_peer");
        } else {
                ast_log(LOG_ERROR, "Could not update the fake authentication peer.\n");
                /* You probably have bigger (memory?) issues to worry about though.. */
@@ -34435,6 +34512,8 @@ static int unload_module(void);
  */
 static int load_module(void)
 {
+       struct sip_peer *bogus_peer;
+
        ast_verbose("SIP channel loading...\n");
 
        if (STASIS_MESSAGE_TYPE_INIT(session_timeout_type)) {
@@ -34506,6 +34585,8 @@ static int load_module(void)
        /* Make sure the auth will always fail. */
        ast_string_field_set(bogus_peer, md5secret, BOGUS_PEER_MD5SECRET);
        ast_clear_flag(&bogus_peer->flags[0], SIP_INSECURE);
+       ao2_t_global_obj_replace_unref(g_bogus_peer, bogus_peer, "Set the initial bogus peer.");
+       ao2_t_ref(bogus_peer, -1, "Module load is done with the bogus peer.");
 
        /* Prepare the version that does not require DTMF BEGIN frames.
         * We need to use tricks such as memcpy and casts because the variable
@@ -34790,7 +34871,7 @@ static int unload_module(void)
                ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
        }
 
-       ao2_t_cleanup(bogus_peer, "unref the bogus_peer");
+       ao2_t_global_obj_release(g_bogus_peer, "Release the bogus peer.");
 
        ao2_t_cleanup(peers, "unref the peers table");
        ao2_t_cleanup(peers_by_ip, "unref the peers_by_ip table");