/*! \brief Custom handler for turning a string bind into a pj_sockaddr */ static int transport_bind_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_transport *transport = obj; pj_str_t buf; return (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, var->value), &transport->host) != PJ_SUCCESS) ? -1 : 0; }
/*! * \brief Pass WebSocket data into pjsip transport manager. */ static int transport_read(void *data) { struct transport_read_data *read_data = data; struct ws_transport *newtransport = read_data->transport; struct ast_websocket *session = newtransport->ws_session; pjsip_rx_data *rdata = &newtransport->rdata; int recvd; pj_str_t buf; int pjsip_pkt_len; pj_gettimeofday(&rdata->pkt_info.timestamp); pjsip_pkt_len = PJSIP_MAX_PKT_LEN < read_data->payload_len ? PJSIP_MAX_PKT_LEN : read_data->payload_len; pj_memcpy(rdata->pkt_info.packet, read_data->payload, pjsip_pkt_len); rdata->pkt_info.len = pjsip_pkt_len; 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))); rdata->pkt_info.src_port = ast_sockaddr_port(ast_websocket_remote_address(session)); recvd = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); pj_pool_reset(rdata->tp_info.pool); return (read_data->payload_len == recvd) ? 0 : -1; }
/*! \brief Custom handler for turning a string bind into a pj_sockaddr */ static int transport_bind_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_transport *transport = obj; pj_str_t buf; int rc; RAII_VAR(struct ast_sip_transport_state *, state, find_or_create_temporary_state(transport), ao2_cleanup); if (!state) { return -1; } rc = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, var->value), &state->host); return rc != PJ_SUCCESS ? -1 : 0; }
static int parse_test(void) { #define IPv4 1 #define IPv6 2 struct test_t { const char *input; int result_af; const char *result_ip; pj_uint16_t result_port; }; struct test_t valid_tests[] = { /* IPv4 */ { "10.0.0.1:80", IPv4, "10.0.0.1", 80}, { "10.0.0.1", IPv4, "10.0.0.1", 0}, { "10.0.0.1:", IPv4, "10.0.0.1", 0}, { "10.0.0.1:0", IPv4, "10.0.0.1", 0}, { ":80", IPv4, "0.0.0.0", 80}, { ":", IPv4, "0.0.0.0", 0}, #if !PJ_SYMBIAN { "localhost", IPv4, "127.0.0.1", 0}, { "localhost:", IPv4, "127.0.0.1", 0}, { "localhost:80", IPv4, "127.0.0.1", 80}, #endif #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 { "fe::01:80", IPv6, "fe::01:80", 0}, { "[fe::01]:80", IPv6, "fe::01", 80}, { "fe::01", IPv6, "fe::01", 0}, { "[fe::01]", IPv6, "fe::01", 0}, { "fe::01:", IPv6, "fe::01", 0}, { "[fe::01]:", IPv6, "fe::01", 0}, { "::", IPv6, "::0", 0}, { "[::]", IPv6, "::", 0}, { ":::", IPv6, "::", 0}, { "[::]:", IPv6, "::", 0}, { ":::80", IPv6, "::", 80}, { "[::]:80", IPv6, "::", 80}, #endif }; struct test_t invalid_tests[] = { /* IPv4 */ { "10.0.0.1:abcd", IPv4}, /* port not numeric */ { "10.0.0.1:-1", IPv4}, /* port contains illegal character */ { "10.0.0.1:123456", IPv4}, /* port too big */ { "1.2.3.4.5:80", IPv4}, /* invalid IP */ { "10:0:80", IPv4}, /* hostname has colon */ #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 { "[fe::01]:abcd", IPv6}, /* port not numeric */ { "[fe::01]:-1", IPv6}, /* port contains illegal character */ { "[fe::01]:123456", IPv6}, /* port too big */ { "fe::01:02::03:04:80", IPv6}, /* invalid IP */ { "[fe::01:02::03:04]:80", IPv6}, /* invalid IP */ { "[fe:01", IPv6}, /* Unterminated bracket */ #endif }; unsigned i; PJ_LOG(3,("test", "...IP address parsing")); for (i=0; i<PJ_ARRAY_SIZE(valid_tests); ++i) { pj_status_t status; pj_str_t input; pj_sockaddr addr, result; switch (valid_tests[i].result_af) { case IPv4: valid_tests[i].result_af = PJ_AF_INET; break; case IPv6: valid_tests[i].result_af = PJ_AF_INET6; break; default: pj_assert(!"Invalid AF!"); continue; } /* Try parsing with PJ_AF_UNSPEC */ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, pj_cstr(&input, valid_tests[i].input), &addr); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... failed when parsing %s (i=%d)", valid_tests[i].input, i)); return -10; } /* Check "sin_len" member of parse result */ CHECK_SA_ZERO_LEN(&addr, -20); /* Build the correct result */ status = pj_sockaddr_init(valid_tests[i].result_af, &result, pj_cstr(&input, valid_tests[i].result_ip), valid_tests[i].result_port); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... error building IP address %s", valid_tests[i].input)); return -30; } /* Compare the result */ if (pj_sockaddr_cmp(&addr, &result) != 0) { PJ_LOG(1,("test", ".... parsed result mismatched for %s", valid_tests[i].input)); return -40; } /* Parse again with the specified af */ status = pj_sockaddr_parse(valid_tests[i].result_af, 0, pj_cstr(&input, valid_tests[i].input), &addr); if (status != PJ_SUCCESS) { PJ_LOG(1,("test", ".... failed when parsing %s", valid_tests[i].input)); return -50; } /* Check "sin_len" member of parse result */ CHECK_SA_ZERO_LEN(&addr, -55); /* Compare the result again */ if (pj_sockaddr_cmp(&addr, &result) != 0) { PJ_LOG(1,("test", ".... parsed result mismatched for %s", valid_tests[i].input)); return -60; } } for (i=0; i<PJ_ARRAY_SIZE(invalid_tests); ++i) { pj_status_t status; pj_str_t input; pj_sockaddr addr; switch (invalid_tests[i].result_af) { case IPv4: invalid_tests[i].result_af = PJ_AF_INET; break; case IPv6: invalid_tests[i].result_af = PJ_AF_INET6; break; default: pj_assert(!"Invalid AF!"); continue; } /* Try parsing with PJ_AF_UNSPEC */ status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, pj_cstr(&input, invalid_tests[i].input), &addr); if (status == PJ_SUCCESS) { PJ_LOG(1,("test", ".... expecting failure when parsing %s", invalid_tests[i].input)); return -100; } } return 0; }
/*! * \brief Create a pjsip transport. */ static int transport_create(void *data) { struct transport_create_data *create_data = data; struct ws_transport *newtransport = NULL; pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt); pj_pool_t *pool; pj_str_t buf; pj_status_t status; newtransport = ao2_t_alloc_options(sizeof(*newtransport), transport_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK, "pjsip websocket transport"); if (!newtransport) { ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n"); goto on_error; } newtransport->transport.endpt = endpt; if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) { ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n"); goto on_error; } newtransport->transport.pool = pool; newtransport->ws_session = create_data->ws_session; /* Keep the session until transport dies */ ast_websocket_ref(newtransport->ws_session); status = pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock); if (status != PJ_SUCCESS) { 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; 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); 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.tpmgr = tpmgr; newtransport->transport.send_msg = &ws_send_msg; newtransport->transport.destroy = &ws_destroy; status = pjsip_transport_register(newtransport->transport.tpmgr, (pjsip_transport *)newtransport); if (status != PJ_SUCCESS) { goto on_error; } /* Add a reference for pjsip transport manager */ ao2_ref(newtransport, +1); newtransport->rdata.tp_info.transport = &newtransport->transport; newtransport->rdata.tp_info.pool = pjsip_endpt_create_pool(endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!newtransport->rdata.tp_info.pool) { ast_log(LOG_ERROR, "Failed to allocate WebSocket rdata.\n"); pjsip_transport_destroy((pjsip_transport *)newtransport); goto on_error; } create_data->transport = newtransport; return 0; on_error: ao2_cleanup(newtransport); return -1; }