build: Update config.guess and config.sub
[asterisk/asterisk.git] / res / res_pjsip_transport_websocket.c
index a9f5268..2abbf15 100644 (file)
@@ -38,8 +38,8 @@
 #include "asterisk/res_pjsip_session.h"
 #include "asterisk/taskprocessor.h"
 
-static int transport_type_ws;
 static int transport_type_wss;
+static int transport_type_wss_ipv6;
 
 /*!
  * \brief Wrapper for pjsip_transport, for storing the WebSocket session
@@ -145,10 +145,12 @@ static int transport_create(void *data)
 {
        struct transport_create_data *create_data = data;
        struct ws_transport *newtransport = NULL;
+       pjsip_tp_state_callback state_cb;
 
        pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
        struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);
 
+       char *ws_addr_str;
        pj_pool_t *pool;
        pj_str_t buf;
        pj_status_t status;
@@ -160,6 +162,10 @@ static int transport_create(void *data)
                goto on_error;
        }
 
+       /* Give websocket transport a unique name for its lifetime */
+       snprintf(newtransport->transport.obj_name, PJ_MAX_OBJ_NAME, "ws%p",
+               &newtransport->transport);
+
        newtransport->transport.endpt = endpt;
 
        if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
@@ -183,23 +189,40 @@ static int transport_create(void *data)
                goto on_error;
        }
 
-       pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(newtransport->ws_session))), &newtransport->transport.key.rem_addr);
-       newtransport->transport.key.rem_addr.addr.sa_family = pj_AF_INET();
-       newtransport->transport.key.type = ast_websocket_is_secure(newtransport->ws_session) ? transport_type_wss : transport_type_ws;
+       /*
+        * The type_name here is mostly used by log messages eihter in
+        * pjproject or Asterisk.  Other places are reconstituting subscriptions
+        * after a restart (which could never work for a websocket connection anyway),
+        * received MESSAGE requests to set PJSIP_TRANSPORT, and most importantly
+        * by pjproject when generating the Via header.
+        */
+       newtransport->transport.type_name = ast_websocket_is_secure(newtransport->ws_session)
+               ? "WSS" : "WS";
+
+       ws_addr_str = ast_sockaddr_stringify(ast_websocket_remote_address(newtransport->ws_session));
+       ast_debug(4, "Creating websocket transport for %s:%s\n",
+               newtransport->transport.type_name, ws_addr_str);
+
+       newtransport->transport.info = (char *) pj_pool_alloc(newtransport->transport.pool,
+               strlen(newtransport->transport.type_name) + strlen(ws_addr_str) + sizeof(" to "));
+       sprintf(newtransport->transport.info, "%s to %s", newtransport->transport.type_name, ws_addr_str);
+
+       pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ws_addr_str), &newtransport->transport.key.rem_addr);
+       if (newtransport->transport.key.rem_addr.addr.sa_family == pj_AF_INET6()) {
+               newtransport->transport.key.type = transport_type_wss_ipv6;
+       } else {
+               newtransport->transport.key.type = transport_type_wss;
+       }
 
        newtransport->transport.addr_len = pj_sockaddr_get_len(&newtransport->transport.key.rem_addr);
 
-       pj_sockaddr_cp(&newtransport->transport.local_addr, &newtransport->transport.key.rem_addr);
+       ws_addr_str = ast_sockaddr_stringify(ast_websocket_local_address(newtransport->ws_session));
+       pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ws_addr_str), &newtransport->transport.local_addr);
+       pj_strdup2(pool, &newtransport->transport.local_name.host, ast_sockaddr_stringify_host(ast_websocket_local_address(newtransport->ws_session)));
+       newtransport->transport.local_name.port = ast_sockaddr_port(ast_websocket_local_address(newtransport->ws_session));
 
-       newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, newtransport->transport.addr_len+4);
-       pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, newtransport->transport.addr_len+4, 0);
-       newtransport->transport.local_name.host.slen = pj_ansi_strlen(newtransport->transport.local_name.host.ptr);
-       newtransport->transport.local_name.port = pj_sockaddr_get_port(&newtransport->transport.key.rem_addr);
-
-       newtransport->transport.type_name = (char *)pjsip_transport_get_type_name(newtransport->transport.key.type);
        newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
-       newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64);
-
+       newtransport->transport.dir = PJSIP_TP_DIR_INCOMING;
        newtransport->transport.tpmgr = tpmgr;
        newtransport->transport.send_msg = &ws_send_msg;
        newtransport->transport.destroy = &ws_destroy;
@@ -223,6 +246,16 @@ static int transport_create(void *data)
        }
 
        create_data->transport = newtransport;
+
+       /* Notify application of transport state */
+       state_cb = pjsip_tpmgr_get_state_cb(newtransport->transport.tpmgr);
+       if (state_cb) {
+               pjsip_transport_state_info state_info;
+
+               memset(&state_info, 0, sizeof(state_info));
+               state_cb(&newtransport->transport, PJSIP_TP_STATE_CONNECTED, &state_info);
+       }
+
        return 0;
 
 on_error:
@@ -258,11 +291,9 @@ static int transport_read(void *data)
        rdata->pkt_info.zero = 0;
 
        pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(session))), &rdata->pkt_info.src_addr);
-       rdata->pkt_info.src_addr.addr.sa_family = pj_AF_INET();
-
        rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr);
 
-       pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_host(ast_websocket_remote_address(session)));
+       pj_ansi_strcpy(rdata->pkt_info.src_name, ast_sockaddr_stringify_addr(ast_websocket_remote_address(session)));
        rdata->pkt_info.src_port = ast_sockaddr_port(ast_websocket_remote_address(session));
 
        recvd = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata);
@@ -285,10 +316,14 @@ static int get_write_timeout(void)
 
                for (; (transport_state = ao2_iterator_next(&it_transport_states)); ao2_cleanup(transport_state)) {
                        struct ast_sip_transport *transport;
+
                        if (transport_state->type != AST_TRANSPORT_WS && transport_state->type != AST_TRANSPORT_WSS) {
                                continue;
                        }
                        transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_state->id);
+                       if (!transport) {
+                               continue;
+                       }
                        ast_debug(5, "Found %s transport with write timeout: %d\n",
                                transport->type == AST_TRANSPORT_WS ? "WS" : "WSS",
                                transport->write_timeout);
@@ -342,8 +377,9 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
 
        create_data.ws_session = session;
 
-       if (ast_sip_push_task_synchronous(serializer, transport_create, &create_data)) {
+       if (ast_sip_push_task_wait_serializer(serializer, transport_create, &create_data)) {
                ast_log(LOG_ERROR, "Could not create WebSocket transport.\n");
+               ast_taskprocessor_unreference(serializer);
                ast_websocket_unref(session);
                return;
        }
@@ -360,13 +396,13 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
                }
 
                if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
-                       ast_sip_push_task_synchronous(serializer, transport_read, &read_data);
+                       ast_sip_push_task_wait_serializer(serializer, transport_read, &read_data);
                } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
                        break;
                }
        }
 
-       ast_sip_push_task_synchronous(serializer, transport_shutdown, transport);
+       ast_sip_push_task_wait_serializer(serializer, transport_shutdown, transport);
 
        ast_taskprocessor_unreference(serializer);
        ast_websocket_unref(session);
@@ -382,19 +418,38 @@ static pj_bool_t websocket_on_rx_msg(pjsip_rx_data *rdata)
 
        long type = rdata->tp_info.transport->key.type;
 
-       if (type != (long)transport_type_ws && type != (long)transport_type_wss) {
+       if (type != (long) transport_type_wss && type != (long) transport_type_wss_ipv6) {
                return PJ_FALSE;
        }
 
-       if ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) && !contact->star &&
-               (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
+       contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+       if (contact
+               && !contact->star
+               && (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) {
                pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri);
+               const pj_str_t *txp_str = &STR_WS;
+
+               if (DEBUG_ATLEAST(4)) {
+                       char src_addr_buffer[AST_SOCKADDR_BUFLEN];
+                       const char *ipv6_s = "", *ipv6_e = "";
+
+                       if (pj_strchr(&uri->host, ':')) {
+                               ipv6_s = "[";
+                               ipv6_e = "]";
+                       }
+
+                       ast_log(LOG_DEBUG, "%s re-writing Contact URI from %s%.*s%s:%d%s%.*s to %s;transport=%s\n",
+                               pjsip_rx_data_get_info(rdata),
+                               ipv6_s, (int) pj_strlen(&uri->host), pj_strbuf(&uri->host), ipv6_e, uri->port,
+                               pj_strlen(&uri->transport_param) ? ";transport=" : "",
+                               (int) pj_strlen(&uri->transport_param), pj_strbuf(&uri->transport_param),
+                               pj_sockaddr_print(&rdata->pkt_info.src_addr, src_addr_buffer, sizeof(src_addr_buffer), 3),
+                               pj_strbuf(txp_str));
+               }
 
                pj_cstr(&uri->host, rdata->pkt_info.src_name);
                uri->port = rdata->pkt_info.src_port;
-               ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n",
-                       (int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port);
-               pj_strdup(rdata->tp_info.pool, &uri->transport_param, &STR_WS);
+               pj_strdup(rdata->tp_info.pool, &uri->transport_param, txp_str);
        }
 
        rdata->msg_info.via->rport_param = 0;
@@ -427,19 +482,24 @@ static struct ast_sip_session_supplement websocket_supplement = {
 
 static int load_module(void)
 {
-       CHECK_PJSIP_MODULE_LOADED();
-
-       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WS", 5060, &transport_type_ws);
-       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "WS", 5060, &transport_type_wss);
+       /*
+        * We only need one transport type name (ws) defined.  Firefox
+        * and Chrome do not support anything other than secure websockets
+        * anymore.
+        *
+        * Also we really cannot have two transports with the same name
+        * and address family because it would be ambiguous.  Outgoing
+        * requests may try to find the transport by name and pjproject
+        * only finds the first one registered.
+        */
+       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "ws", 5060, &transport_type_wss);
+       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_IPV6, "ws", 5060, &transport_type_wss_ipv6);
 
        if (ast_sip_register_service(&websocket_module) != PJ_SUCCESS) {
                return AST_MODULE_LOAD_DECLINE;
        }
 
-       if (ast_sip_session_register_supplement(&websocket_supplement)) {
-               ast_sip_unregister_service(&websocket_module);
-               return AST_MODULE_LOAD_DECLINE;
-       }
+       ast_sip_session_register_supplement(&websocket_supplement);
 
        if (ast_websocket_add_protocol("sip", websocket_cb)) {
                ast_sip_session_unregister_supplement(&websocket_supplement);
@@ -464,4 +524,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP WebSocket Trans
        .load = load_module,
        .unload = unload_module,
        .load_pri = AST_MODPRI_APP_DEPEND,
+       .requires = "res_pjsip,res_http_websocket",
 );