Merge "res_pjsip_caller_id: Add "party" parameter to RPID header."
[asterisk/asterisk.git] / res / res_pjsip_t38.c
index 2e60493..52dfd75 100644 (file)
@@ -142,7 +142,8 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
                new_state, old_state,
                session->channel ? ast_channel_name(session->channel) : "<gone>");
 
-       if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &state->timer)) {
+       if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+               &state->timer, 0)) {
                ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n",
                        session->channel ? ast_channel_name(session->channel) : "<gone>");
                ao2_ref(session, -1);
@@ -248,10 +249,7 @@ static struct t38_state *t38_state_get_or_alloc(struct ast_sip_session *session)
        state = datastore->data;
 
        /* This will get bumped up before scheduling */
-       state->timer.user_data = session;
-       state->timer.cb = t38_automatic_reject_timer_cb;
-
-       datastore->data = state;
+       pj_timer_entry_init(&state->timer, 0, session, t38_automatic_reject_timer_cb);
 
        return state;
 }
@@ -297,18 +295,21 @@ static int t38_reinvite_response_cb(struct ast_sip_session *session, pjsip_rx_da
        struct t38_state *state;
        struct ast_sip_session_media *session_media = NULL;
 
-       if (status.code == 100) {
+       if (status.code / 100 <= 1) {
+               /* Ignore any non-final responses (1xx) */
                return 0;
        }
 
        state = t38_state_get_or_alloc(session);
-       if (!state) {
-               ast_log(LOG_WARNING, "Received response to T.38 re-invite on '%s' but state unavailable\n",
-                       ast_channel_name(session->channel));
+       if (!session->channel || !state) {
+               ast_log(LOG_WARNING, "Received %d response to T.38 re-invite on '%s' but state unavailable\n",
+                       status.code,
+                       session->channel ? ast_channel_name(session->channel) : "unknown channel");
                return 0;
        }
 
-       if (status.code == 200) {
+       if (status.code / 100 == 2) {
+               /* Accept any 2xx response as successfully negotiated */
                int index;
 
                session_media = session->active_media_state->default_session[AST_MEDIA_TYPE_IMAGE];
@@ -363,7 +364,11 @@ static struct ast_sip_session_media_state *t38_create_media_state(struct ast_sip
        }
 
        ast_stream_set_state(stream, AST_STREAM_STATE_SENDRECV);
-       ast_stream_topology_set_stream(media_state->topology, 0, stream);
+       if (ast_stream_topology_set_stream(media_state->topology, 0, stream)) {
+               ast_stream_free(stream);
+               ast_sip_session_media_state_free(media_state);
+               return NULL;
+       }
 
        caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
        if (!caps) {
@@ -371,9 +376,14 @@ static struct ast_sip_session_media_state *t38_create_media_state(struct ast_sip
                return NULL;
        }
 
-       ast_format_cap_append(caps, ast_format_t38, 0);
        ast_stream_set_formats(stream, caps);
+       /* stream holds a reference to cap, release the local reference
+        * now so we don't have to deal with it in the error condition. */
        ao2_ref(caps, -1);
+       if (ast_format_cap_append(caps, ast_format_t38, 0)) {
+               ast_sip_session_media_state_free(media_state);
+               return NULL;
+       }
 
        session_media = ast_sip_session_media_state_add(session, media_state, AST_MEDIA_TYPE_IMAGE, 0);
        if (!session_media) {
@@ -497,21 +507,42 @@ static struct ast_frame *t38_framehook(struct ast_channel *chan, struct ast_fram
                return f;
        }
 
-       if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
+       if (f->frametype == AST_FRAME_CONTROL
+               && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
                if (channel->session->endpoint->media.t38.enabled) {
-                       struct t38_parameters_task_data *data = t38_parameters_task_data_alloc(channel->session, f);
-
-                       if (!data) {
-                               return f;
-                       }
+                       struct t38_parameters_task_data *data;
 
-                       if (ast_sip_push_task(channel->session->serializer, t38_interpret_parameters, data)) {
+                       data = t38_parameters_task_data_alloc(channel->session, f);
+                       if (data
+                               && ast_sip_push_task(channel->session->serializer,
+                                       t38_interpret_parameters, data)) {
                                ao2_ref(data, -1);
                        }
                } else {
-                       struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REFUSED, };
-                       ast_debug(2, "T.38 support not enabled, rejecting T.38 control packet\n");
-                       ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
+                       static const struct ast_control_t38_parameters rsp_refused = {
+                               .request_response = AST_T38_REFUSED,
+                       };
+                       static const struct ast_control_t38_parameters rsp_terminated = {
+                               .request_response = AST_T38_TERMINATED,
+                       };
+                       const struct ast_control_t38_parameters *parameters = f->data.ptr;
+
+                       switch (parameters->request_response) {
+                       case AST_T38_REQUEST_NEGOTIATE:
+                               ast_debug(2, "T.38 support not enabled on %s, refusing T.38 negotiation\n",
+                                       ast_channel_name(chan));
+                               ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS,
+                                       &rsp_refused, sizeof(rsp_refused));
+                               break;
+                       case AST_T38_REQUEST_TERMINATE:
+                               ast_debug(2, "T.38 support not enabled on %s, 'terminating' T.38 session\n",
+                                       ast_channel_name(chan));
+                               ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS,
+                                       &rsp_terminated, sizeof(rsp_terminated));
+                               break;
+                       default:
+                               break;
+                       }
                }
        }
 
@@ -739,17 +770,17 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
 
        if (!session->endpoint->media.t38.enabled) {
                ast_debug(3, "Declining; T.38 not enabled on session\n");
-               return -1;
+               return 0;
        }
 
        if (!(state = t38_state_get_or_alloc(session))) {
-               return -1;
+               return 0;
        }
 
        if ((session->t38state == T38_REJECTED) || (session->t38state == T38_DISABLED)) {
                ast_debug(3, "Declining; T.38 state is rejected or declined\n");
                t38_change_state(session, session_media, state, T38_DISABLED);
-               return -1;
+               return 0;
        }
 
        ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host));
@@ -758,7 +789,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
        if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_INET) <= 0) {
                /* The provided host was actually invalid so we error out this negotiation */
                ast_debug(3, "Declining; provided host is invalid\n");
-               return -1;
+               return 0;
        }
 
        /* Check the address family to make sure it matches configured */
@@ -766,7 +797,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
                (ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.t38.ipv6)) {
                /* The address does not match configured */
                ast_debug(3, "Declining, provided host does not match configured address family\n");
-               return -1;
+               return 0;
        }
 
        return 1;
@@ -952,7 +983,7 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
 {
        RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)), ao2_cleanup);
        char host[NI_MAXHOST];
-       struct ast_sockaddr addr = { { 0, } };
+       struct ast_sockaddr our_sdp_addr = { { 0, } };
 
        /* If the stream has been rejected there will be no connection line */
        if (!stream->conn || !transport_state) {
@@ -960,10 +991,13 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
        }
 
        ast_copy_pj_str(host, &stream->conn->addr, sizeof(host));
-       ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID);
+       ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID);
 
-       /* Is the address within the SDP inside the same network? */
-       if (ast_sip_transport_is_local(transport_state, &addr)) {
+       /* Reversed check here. We don't check the remote endpoint being
+        * in our local net, but whether our outgoing session IP is
+        * local. If it is not, we won't do rewriting. No localnet
+        * configured? Always rewrite. */
+       if (ast_sip_transport_is_nonlocal(transport_state, &our_sdp_addr) && transport_state->localnet) {
                return;
        }
        ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
@@ -1011,18 +1045,13 @@ static int unload_module(void)
  */
 static int load_module(void)
 {
-       CHECK_PJSIP_SESSION_MODULE_LOADED();
-
        if (ast_check_ipv6()) {
                ast_sockaddr_parse(&address, "::", 0);
        } else {
                ast_sockaddr_parse(&address, "0.0.0.0", 0);
        }
 
-       if (ast_sip_session_register_supplement(&t38_supplement)) {
-               ast_log(LOG_ERROR, "Unable to register T.38 session supplement\n");
-               goto end;
-       }
+       ast_sip_session_register_supplement(&t38_supplement);
 
        if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) {
                ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n");
@@ -1041,4 +1070,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP T.38 UDPTL Supp
        .load = load_module,
        .unload = unload_module,
        .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+       .requires = "res_pjsip,res_pjsip_session,udptl",
 );