diff --git a/res/res_pjsip_volte.c b/res/res_pjsip_volte.c new file mode 100644 index 0000000000..11b199e224 --- /dev/null +++ b/res/res_pjsip_volte.c @@ -0,0 +1,287 @@ +/*** MODULEINFO + pjproject + res_pjsip + res_pjsip_session + core + ***/ + +#include "asterisk.h" + +#include +#include +#include +#include + +#include "asterisk/res_pjsip.h" +#include "asterisk/res_pjsip_session.h" +#include "asterisk/utils.h" +#include "asterisk/module.h" +#include "asterisk/causes.h" +#include "asterisk/threadpool.h" + +#define PJ_CONSUME PJ_TRUE + +const pj_str_t STR_SUPPORTED = { "Supported", 9 }; +const pj_str_t STR_REQUIRE = { "Require", 7 }; +const pj_str_t STR_PROXY_REQUIRE = { "Proxy-Require", 13 }; +const pj_str_t STR_PATH = { "path", 4 }; +const pj_str_t STR_SEC_AGREE = { "sec-agree", 9 }; + +const pj_str_t STR_SECURITY_CLIENT = { "Security-Client", 15 }; + +const char *ipsec_alg[] = { + "hmac-md5-96", + "hmac-sha-1-96", + NULL +}; + +const char *ipsec_ealg[] = { + "null", + NULL +}; + + +static pj_status_t on_load(pjsip_endpoint *endpt) +{ + printf("endpoint!\n"); + return PJ_SUCCESS; +} + +/* Check if message is exchanged with IMS. + * This approach uses the host name of the URI. */ +static pj_bool_t is_ims(const pjsip_msg *msg) +{ + pjsip_fromto_hdr *hdr; + pjsip_sip_uri *uri; + size_t uri_len; + + /* Get URI. */ + hdr = pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL); + if (!hdr) + return PJ_FALSE; + uri = pjsip_uri_get_uri(hdr->uri); + uri_len = strlen(uri->host.ptr); + + if (uri_len < 20) + return PJ_FALSE; + + if (!!strncmp(uri->host.ptr, "ims.", 4)) + return PJ_FALSE; + + if (!!strncmp(uri->host.ptr + uri_len - 16, ".3gppnetwork.org", 16)) + return PJ_FALSE; + + return PJ_TRUE; +} + + +#warning ip xfrm state add src dst proto esp spi auth hmac-sha1-96 0x + +static void add_securety_client_hdr(pjsip_tx_data *tdata, const char *alg[], const char *ealg[], uint32_t spi_c, uint32_t spi_s, uint16_t port_c, uint16_t port_s) +{ + pjsip_generic_array_hdr *hdr; + char str[256]; + int i, j; + + puts("add sec"); + /* Add Security-Client header. */ + hdr = pjsip_generic_array_hdr_create(tdata->pool, &STR_SECURITY_CLIENT); + if (!hdr) { + ast_log(LOG_ERROR, "Failed to create header."); + return; + } + + /* Create tupple for given algorithms. */ + for (i = 0; alg[i]; i++) { + for (j = 0; ealg[j]; j++) { + snprintf(str, sizeof(str), "ipsec-3gpp; alg=%s; ealg=%s; spi-c=%u; spi-s=%u; port-c=%u; port-s=%u", alg[i], ealg[j], spi_c, spi_s, port_c, port_s); + if (hdr->count == PJSIP_GENERIC_ARRAY_MAX_COUNT) { + ast_log(LOG_ERROR, "Too many evalue in array, skipping '%s'.", str); + continue; + } + pj_strdup2(tdata->pool, &hdr->values[hdr->count++], str); + } + } + + /* Append header */ + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr); +} + +static void add_value_string_hdr(pjsip_tx_data *tdata, const pj_str_t *name, const pj_str_t *value) +{ + pjsip_generic_string_hdr *hdr; + + /* Add Proxy-Require header. */ + hdr = pjsip_generic_string_hdr_create(tdata->pool, name, value); + if (!hdr) { + ast_log(LOG_ERROR, "Failed to create header."); + return; + } + + /* Append header */ + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr); +} + +static void add_value_array_hdr(pjsip_tx_data *tdata, const pj_str_t *name, const pj_str_t *value) +{ + pjsip_generic_array_hdr *hdr; + pj_bool_t created = PJ_FALSE; + + /* Create header, if not yet created. */ + hdr = pjsip_msg_find_hdr_by_name(tdata->msg, name, NULL); + if (!hdr) { + hdr = pjsip_generic_array_hdr_create(tdata->pool, name); + created = PJ_TRUE; + } + if (!hdr) { + ast_log(LOG_ERROR, "Failed to create header."); + return; + } + if (hdr->count == PJSIP_GENERIC_ARRAY_MAX_COUNT) { + ast_log(LOG_ERROR, "Too many evalue in array, skipping %s.", value->ptr); + return; + } + pj_strdup(tdata->pool, &hdr->values[hdr->count++], value); + + /* Append header, if created. */ + if (created) + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr); +} + +static pj_status_t on_tx_register_request(pjsip_tx_data *tdata) +{ + if (!is_ims(tdata->msg)) + return PJ_SUCCESS; + + /* "Require: sec-agree" */ + add_value_array_hdr(tdata, &STR_REQUIRE, &STR_SEC_AGREE); + /* "Proxy-Require: sec-agree" */ + add_value_string_hdr(tdata, &STR_PROXY_REQUIRE, &STR_SEC_AGREE); + /* "Supported: path,sec-agree" */ + add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_PATH); + /* "Seciruty-Client: ..." */ + add_value_array_hdr(tdata, &STR_SUPPORTED, &STR_SEC_AGREE); + add_securety_client_hdr(tdata, ipsec_alg, ipsec_ealg, 0xbea96586, 0x14ac7e17, 43419, 42318); + + + + + +#warning HACKING: Asterisk must do this on first register! Or we must do it! + const pj_str_t STR_AUTHORIZATION = { "Authorization", 13 }; + char xxx[] = "Digest uri=\"sip:ims.mnc001.mcc238.3gppnetwork.org\",username=\"238010000090828@ims.mnc001.mcc238.3gppnetwork.org\",response=\"\",realm=\"ims.mnc001.mcc238.3gppnetwork.org\",nonce=\"\""; + const pj_str_t STR_XXX = { xxx, strlen(xxx)}; + add_value_string_hdr(tdata, &STR_AUTHORIZATION, &STR_XXX); + + + + + + return PJ_SUCCESS; +} + +/* What PJSIP will requests */ +static pj_status_t on_tx_request(pjsip_tx_data *tdata) +{ + const pjsip_msg *msg = tdata->msg; + + puts("tx request"); + if (!msg) { + ast_log(LOG_ERROR, "No message in request."); + return PJ_CONSUME; + + } + const pj_str_t REFER_TO = { "User-Agent", 10 }; + pjsip_generic_string_hdr *refer_to_hdr; + + switch (msg->line.req.method.id) { + case PJSIP_REGISTER_METHOD: + puts("register"); + return on_tx_register_request(tdata); + break; + case PJSIP_INVITE_METHOD: + break; + default: + break; + } + + return PJ_SUCCESS; +} + +/* What response PJSIP receives */ +static pj_bool_t on_rx_response(pjsip_rx_data *rdata) +{ + const pjsip_msg *msg = rdata->msg_info.msg; + + puts("rx response"); + if (!msg) { + ast_log(LOG_ERROR, "No message in request."); + return PJ_CONSUME; + } + + const pj_str_t REFER_TO = { "Server", 6 }; + pjsip_generic_string_hdr *refer_to_hdr; + + refer_to_hdr = pjsip_msg_find_hdr_by_name(msg, &REFER_TO, NULL); + printf("server=%s\n", refer_to_hdr->hvalue.ptr); + + printf("%d\n", msg->line.req.method.id); + switch ((int)msg->line.req.method.id) { + case 403: + puts("forbidden"); + break; + default: + break; + } + + return PJ_FALSE; +} + +/* What request PJSIP receives */ +static pj_bool_t on_rx_request(pjsip_rx_data *rdata) +{ + puts("rx request"); + return PJ_TRUE; +} + +/* What PJSIP will response */ +static pj_status_t on_tx_response(pjsip_tx_data *tdata) +{ + puts("tx response"); + return PJ_SUCCESS; +} + +static pjsip_module volte_module = { + .name = {"VoLTE Support Module", 18}, + .priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, + .load = on_load, + .on_tx_request = on_tx_request, + .on_rx_response = on_rx_response, + .on_tx_response = on_tx_response, + .on_rx_request = on_rx_request, +}; + + +static int load_module(void) +{ + if (ast_sip_register_service(&volte_module) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Failed to load VoLTE support module.\n"); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_service(&volte_module); + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "VoLTE support", + .support_level = AST_MODULE_SUPPORT_CORE, + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + .requires = "res_pjsip,res_pjsip_session", );