diff --git a/apps/app_dial.c b/apps/app_dial.c index 8a3c70e0b7adc943fcecfbc56db86be72f9c3b13..0d30ebf4186be9686c3fbbd2caa57891827e0712 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -1587,6 +1587,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, if (single || (!single && !pa->sentringing)) { ast_channel_codec_set(in, ast_channel_codec_get(c)); //for early media codec sync ast_indicate(in, AST_CONTROL_PROGRESS); + pa->sentringing = 0; } } if (!sent_progress) { diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index d29490cea32a86389e0c3954374d13636adcb1d0..edc5c0d265ecf7881b05989aa6189d9476058e7a 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -73,6 +73,7 @@ #include "pjsip/include/dialplan_functions.h" #include "pjsip/include/cli_functions.h" +#define EARLY_MEDIA_WAIT_MS 500 // 500 mseconds waiting time for incoming RTP of early media AST_THREADSTORAGE(uniqueid_threadbuf); #define UNIQUEID_BUFSIZE 256 @@ -690,7 +691,15 @@ static int answer(void *data) pjsip_dlg_inc_lock(session->inv_session->dlg); if (session->inv_session->invite_tsx) { - status = pjsip_inv_answer(session->inv_session, 200, NULL, NULL, &packet); + /* + * Now the session has been answered and we know the capability of the local channel. Recalculate the joint + * codecs as if a re-INVITE is received within an established session. + */ + if (ast_sip_session_sdp_answer(session) == 0) { + status = pjsip_inv_answer(session->inv_session, 200, NULL, NULL, &packet); + } else { + status = -1; + } } else { ast_log(LOG_ERROR,"Cannot answer '%s' because there is no associated SIP transaction\n", ast_channel_name(session->channel)); @@ -824,6 +833,14 @@ static int is_compatible_format(struct ast_sip_session *session, struct ast_fram return ast_format_cap_iscompatible_format(cap, f->subclass.format) != AST_FORMAT_CMP_NOT_EQUAL; } +static void incoming_rtp_stop_timer(struct ast_sip_session *session) +{ + if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), + &session->incoming_rtp_check_timer, 0)) { + ao2_ref(session, -1); + } +} + /*! * \brief Function called by core to read any waiting frames * @@ -849,6 +866,14 @@ static struct ast_frame *chan_pjsip_read_stream(struct ast_channel *ast) return f; } + // Do not process incoming early media packets if the received P-Early-Media header is inactive/recvonly + enum early_media_direction early_media_dir = ast_channel_early_media_get(ast); + if (ast_channel_state(ast) != AST_STATE_UP && f->frametype == AST_FRAME_VOICE) { + incoming_rtp_stop_timer(session); + if (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_RECVONLY) + return &ast_null_frame; + } + for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) { if (cur->frametype == AST_FRAME_VOICE) { break; @@ -947,6 +972,12 @@ static int chan_pjsip_write_stream(struct ast_channel *ast, int stream_num, stru struct ast_sip_session_media *media = NULL; int res = 0; + // Do not process outgoing early media packets if the received P-Early-Media header is inactive/sendonly + enum early_media_direction early_media_dir = ast_channel_early_media_get(ast); + if (ast_channel_state(ast) != AST_STATE_UP && frame->frametype == AST_FRAME_VOICE && + (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_SENDONLY)) + return 0; + /* The core provides a guarantee that the stream will exist when we are called if stream_num is provided */ if (stream_num >= 0) { /* What is not guaranteed is that a media session will exist */ @@ -2160,9 +2191,10 @@ static void sip_build_replaces_param(const struct ast_channel *ast, const char * // Create URI encoded Replaces parameter // ?Replaces=<callid>;to-tag=<totag>;from-tag=<fromtag> - snprintf(replaces, len - 1, "%s;to-tag=%s;from-tag=%s", callid, totag, fromtag); + // snprintf(replaces, len - 1, "%s;to-tag=%s;from-tag=%s", callid, totag, fromtag); + snprintf(replaces, len - 1, "%s", callid); ast_uri_encode(replaces, tmp, sizeof(tmp), ast_uri_http); - snprintf(replaces, len - 1, "?Replaces=%s", tmp); + snprintf(replaces, len - 1, "?Require=replaces&Replaces=%s", tmp); } else { ast_log(LOG_NOTICE, "Asked to replace a call on channel type %s\n", type); @@ -2221,6 +2253,7 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) strncpy(method, "tel", sizeof(method) - 1); else { ast_log(LOG_ERROR, "Unsupported URI for Refer-To header. Refer will not be sent!\n"); + ast_log_dt(LOG_EVENT_CODE_SV109); goto failure; } @@ -2229,10 +2262,12 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) strncpy(method, strtok(contact_uri, ":"), sizeof(method) - 1); } else { ast_log(LOG_ERROR, "Unsupported URI for Refer-To header. Refer will not be sent!\n"); + ast_log_dt(LOG_EVENT_CODE_SV109); goto failure; } - snprintf(referto, sizeof(referto), "<%s:%s@%s%s>", method, target, session->endpoint->fromdomain, replaces); + //snprintf(referto, sizeof(referto), "<%s:%s@%s%s>", method, target, session->endpoint->fromdomain, replaces); + snprintf(referto, sizeof(referto), "<%s:%s@%s;user=phone%s>", method, target, session->endpoint->fromdomain, replaces); ast_debug(1, "Refer-To: %s\n", referto); if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, referto), &packet) != PJ_SUCCESS) { @@ -2333,6 +2368,11 @@ static int chan_pjsip_digit_begin(struct ast_channel *chan, char digit) struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan); struct ast_sip_session_media *media; + // Do not process outgoing dtmf packets if the received P-Early-Media header is inactive/sendonly + enum early_media_direction early_media_dir = ast_channel_early_media_get(chan); + if (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_SENDONLY) + return 0; + media = channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]; switch (channel->session->dtmf) { @@ -2451,6 +2491,11 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in return -1; } + // Do not process outgoing dtmf packets if the received P-Early-Media header is inactive/sendonly + enum early_media_direction early_media_dir = ast_channel_early_media_get(ast); + if (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_SENDONLY) + return 0; + media = channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]; switch (channel->session->dtmf) { @@ -2617,6 +2662,8 @@ static int hangup_cause2sip(int cause) return 488; case AST_CAUSE_INTERWORKING: /* Unspecified Interworking issues */ return 500; + case AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE: + return 504; /* could contains xml with init register request */ case AST_CAUSE_NOTDEFINED: default: ast_debug(1, "AST hangup cause %d (no match found in PJSIP)\n", cause); @@ -3125,6 +3172,39 @@ static void set_sipdomain_variable(struct ast_sip_session *session) return; } +static int find_contact(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + char *contact_url = arg; + + return !!strstr(contact->uri, contact_url); +} + +static struct ast_sip_contact *contact_user_find_by_addr(char *contact_user, const pj_sockaddr *src_addr) +{ + RAII_VAR(struct ast_sip_aor *, aor_obj, NULL, ao2_cleanup); + RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + struct ast_sorcery *pjsip_sorcery; + struct ao2_container *contacts; + char contact_url[PJ_INET6_ADDRSTRLEN]; + + pjsip_sorcery = ast_sip_get_sorcery(); + if (!pjsip_sorcery) + return 0; + + aor_obj = ast_sorcery_retrieve_by_id(pjsip_sorcery, "aor", contact_user); + if (!aor_obj) + return 0; + + contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor_obj); + if (!contacts) + return 0; + + pj_sockaddr_print(src_addr, contact_url, sizeof(contact_url), 3); + return ao2_callback(contacts, OBJ_UNLINK, find_contact, contact_url); +} + + /*! \brief Function called when a request is received on the session */ static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { @@ -3137,29 +3217,30 @@ static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct p SCOPE_EXIT_RTN_VALUE(0, "%s: No channel\n", ast_sip_session_get_name(session)); } - if (ast_sip_get_accept_proxy_req_only() && + if (ast_sip_get_accept_proxy_req_only() && session->inv_session && session->inv_session->dlg && session->inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { - char buf[256]; - struct ast_sockaddr resolved_addr; + char buf[PJ_INET6_ADDRSTRLEN]; + pj_str_t proxy_config_val; + pj_sockaddr proxy_address; pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf), 3); - ast_log(LOG_NOTICE, "Request received from address: <%s>\n", buf); + ast_log(LOG_DEBUG, "Request received from address: <%s>\n", buf); - resolved_addr.ss.ss_family = AST_AF_UNSPEC; - if (ast_get_ip(&resolved_addr, session->endpoint->fromdomain)) { - ast_log(LOG_ERROR, "Could not resolve the domain name: %s\n", session->endpoint->fromdomain); + pj_cstr(&proxy_config_val, session->endpoint->fromdomain); + if (contact_user_find_by_addr(session->endpoint->contact_user, &rdata->pkt_info.src_addr)) { + ast_log(LOG_DEBUG, "Request from known contact %s\n", ast_sip_session_get_name(session)); + } + // 'fromdomain' is not an IPv4 or IPv6 address + else if (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &proxy_config_val, &proxy_address) != PJ_SUCCESS) { + if (!ast_sip_is_address_in_dns_records(&rdata->pkt_info.src_addr)) { + SCOPE_EXIT_RTN_VALUE(-1, "%s: Request not from DNS resolutions\n", ast_sip_session_get_name(session)); + } } else { - pj_sockaddr proxy_addr; - - ast_sockaddr_to_pj_sockaddr(&resolved_addr, &proxy_addr); - pj_sockaddr_set_port(&proxy_addr, rdata->pkt_info.src_port); - - pj_sockaddr_print(&proxy_addr, buf, sizeof(buf), 3); - ast_log(LOG_NOTICE, "Outgoing proxy address: <%s>\n", buf); - - if (pj_sockaddr_cmp(&rdata->pkt_info.src_addr, &proxy_addr)) { - ast_sip_session_terminate(session, 400); - SCOPE_EXIT_RTN_VALUE(-1, "%s: Request not from Outgoing SIP proxy. Terminating session\n", ast_sip_session_get_name(session)); + pj_sockaddr_set_port(&proxy_address, rdata->pkt_info.src_port); + pj_sockaddr_print(&proxy_address, buf, sizeof(buf), 3); + ast_log(LOG_DEBUG, "Outgoing proxy address: <%s>\n", buf); + if (pj_sockaddr_cmp(&rdata->pkt_info.src_addr, &proxy_address)) { + SCOPE_EXIT_RTN_VALUE(-1, "%s: Request not from configured SIP proxy\n", ast_sip_session_get_name(session)); } } } @@ -3307,6 +3388,8 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se struct pjsip_status_line status = rdata->msg_info.msg->line.status; struct ast_control_pvt_cause_code *cause_code; int data_size = sizeof(*cause_code); + char * client_num = NULL; + char * saveptr = NULL; SCOPE_ENTER(3, "%s: Status: %d\n", ast_sip_session_get_name(session), status.code); if (!session->channel) { @@ -3324,6 +3407,22 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "SIP %d %.*s", status.code, (int) pj_strlen(&status.reason), pj_strbuf(&status.reason)); + /* V003 & V006 event log*/ + if (session && session->contact) { + char *contact_uri = ast_strdupa(session->contact->uri); + strtok_r(contact_uri, ":", &saveptr); + client_num = strtok_r(NULL, "@", &saveptr); + if(status.code == 488) + { + /* V003 */ + ast_log_dt(LOG_EVENT_CODE_V003); + } + else if( (status.code >= 400 && status.code <= 600) && (client_num != NULL) ){ + /* V006 4xx & 5xx call return error code */ + ast_log_dt(LOG_EVENT_CODE_V006, client_num, (int) pj_strlen(&status.reason), pj_strbuf(&status.reason)); + } + } + cause_code->ast_cause = ast_sip_hangup_sip2cause(status.code); ast_queue_control_data(session->channel, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size); ast_channel_hangupcause_hash_set(session->channel, cause_code, data_size); @@ -3331,6 +3430,21 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se SCOPE_EXIT_RTN("%s\n", ast_sip_session_get_name(session)); } +static int handle_incoming_rtp_check_timeout (pj_timer_heap_t *timer, pj_timer_entry *entry) +{ + struct ast_sip_session *session = entry->user_data; + + ast_queue_control(session->channel, AST_CONTROL_RINGING); + ast_channel_lock(session->channel); + if (ast_channel_state(session->channel) != AST_STATE_UP) + ast_setstate(session->channel, AST_STATE_RINGING); + ast_channel_unlock(session->channel); + // change early media direction to EARLY_MEDIA_RECVONLY to prevent from playing both local ringtone + // and backword early media (in case if incoming RTP from remote will occur later than EARLY_MEDIA_WAIT_MS + // we started the local ringtone here) + ast_channel_early_media_set(session->channel, EARLY_MEDIA_RECVONLY); +} + /*! \brief Function called when a response is received on the session */ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { @@ -3342,18 +3456,83 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct } switch (status.code) { - case 180: { + case 180: + case 181: + case 182: + case 183: { pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata); - if (sdp && sdp->body.ptr) { - /* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration - * of early media */ - ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); - ast_queue_control(session->channel, AST_CONTROL_PROGRESS); - } else { - ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session)); - ast_queue_control(session->channel, AST_CONTROL_RINGING); + static const pj_str_t headerName = { "P-Early-Media", 13 }; + pjsip_generic_string_hdr *earlyMediaHdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &headerName, NULL); + enum early_media_direction early_media_dir = EARLY_MEDIA_NONE; + + if(earlyMediaHdr) { + if (!pj_stricmp2(&earlyMediaHdr->hvalue, "inactive")) + early_media_dir = EARLY_MEDIA_INACTIVE; + else if (!pj_stricmp2(&earlyMediaHdr->hvalue, "recvonly")) + early_media_dir = EARLY_MEDIA_RECVONLY; + else if (!pj_stricmp2(&earlyMediaHdr->hvalue, "sendonly")) + early_media_dir = EARLY_MEDIA_SENDONLY; + else if (!pj_stricmp2(&earlyMediaHdr->hvalue, "sendrecv")) + early_media_dir = EARLY_MEDIA_SENDRECV; + else + early_media_dir = EARLY_MEDIA_INACTIVE; + } else if (sdp && sdp->body.ptr) { + // receipt of SDP w/o P-Early-Media: P-Early-Media is implicitly treated as sendonly + early_media_dir = EARLY_MEDIA_SENDONLY; + } + ast_channel_early_media_set(session->channel, early_media_dir); + + if (status.code == 183) { + if (session->endpoint->ignore_183_without_sdp) { + if (sdp && sdp->body.ptr) { + ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); + ast_trace(1, "%s Method: %.*s Status: %d Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session), + (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code); + ast_queue_control(session->channel, AST_CONTROL_PROGRESS); + } + } else { + ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); + ast_trace(1, "%s Method: %.*s Status: %d Queueing PROGRESS\n", ast_sip_session_get_name(session), + (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code); + /* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration + * of early media */ + ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); + ast_queue_control(session->channel, AST_CONTROL_PROGRESS); + } + } else if (status.code == 180) { + if (early_media_dir != EARLY_MEDIA_SENDONLY && early_media_dir != EARLY_MEDIA_SENDRECV) { + ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session)); + ast_queue_control(session->channel, AST_CONTROL_SRCUPDATE); + ast_queue_control(session->channel, AST_CONTROL_RINGING); + ast_channel_lock(session->channel); + if (ast_channel_state(session->channel) != AST_STATE_UP) { + ast_setstate(session->channel, AST_STATE_RINGING); + } + ast_channel_unlock(session->channel); + } else { + // immediately with receipt of P-Early-Media=sendonly/sendrecv any media received from + // the network shall be rendered. If within 500 ms no RTP is received then device should + // play "local ringtone" + pj_time_val tv; + tv.sec = 0; + tv.msec = EARLY_MEDIA_WAIT_MS; + pj_timer_entry_init(&session->incoming_rtp_check_timer, 0, session, handle_incoming_rtp_check_timeout); + + ao2_ref(session, +1); + if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), + &session->incoming_rtp_check_timer, &tv) != PJ_SUCCESS) { + ao2_ref(session, -1); + SCOPE_EXIT_RTN("%s: Couldn't schedule incoming_rtp_check_timer\n", ast_sip_session_get_name(session)); + } + } } - + break; + } + case 199: { + enum early_media_direction early_media_dir = EARLY_MEDIA_NONE; + ast_channel_early_media_set(session->channel, early_media_dir); + ast_queue_control(session->channel, AST_CONTROL_SRCUPDATE); + ast_queue_control(session->channel, AST_CONTROL_RINGING); ast_channel_lock(session->channel); if (ast_channel_state(session->channel) != AST_STATE_UP) { ast_setstate(session->channel, AST_STATE_RINGING); @@ -3361,25 +3540,6 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct ast_channel_unlock(session->channel); break; } - case 183: - if (session->endpoint->ignore_183_without_sdp) { - pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata); - if (sdp && sdp->body.ptr) { - ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); - ast_trace(1, "%s Method: %.*s Status: %d Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session), - (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code); - ast_queue_control(session->channel, AST_CONTROL_PROGRESS); - } - } else { - ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); - ast_trace(1, "%s Method: %.*s Status: %d Queueing PROGRESS without SDP\n", ast_sip_session_get_name(session), - (int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code); - /* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration - * of early media */ - ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session)); - ast_queue_control(session->channel, AST_CONTROL_PROGRESS); - } - break; case 200: ast_trace(-1, "%s: Queueing ANSWER\n", ast_sip_session_get_name(session)); ast_queue_control(session->channel, AST_CONTROL_ANSWER); diff --git a/configure.ac b/configure.ac index d3ded2799858991fe4f99b1d95dcb4a6c051134a..377d6c1ce6534ff29d5ce9cf83bc113017be1195 100644 --- a/configure.ac +++ b/configure.ac @@ -740,16 +740,6 @@ fi AST_EXT_LIB_CHECK([RT], [rt], [clock_gettime], []) AST_PKG_CONFIG_CHECK([LIBXML2], [libxml-2.0]) -AST_EXT_TOOL_CHECK([LIBXML2], [xml2-config], , , - [#include <libxml/tree.h> - #include <libxml/parser.h>], - [LIBXML_TEST_VERSION]) - -if test "${PBX_LIBXML2}" != 1; then - AC_MSG_NOTICE(*** The Asterisk menuselect tool requires the 'libxml2' development package.) - AC_MSG_NOTICE(*** Please install the 'libxml2' development package.) - exit 1 -fi AST_EXT_LIB_CHECK([URIPARSER], [uriparser], [uriParseUriA], [uriparser/Uri.h]) @@ -1616,6 +1606,8 @@ fi # do the package library checks now +AST_PKG_CONFIG_CHECK([LIBXML2], [libxml-2.0]) + AST_EXT_LIB_CHECK([ALSA], [asound], [snd_pcm_open], [alsa/asoundlib.h]) AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h]) @@ -2813,7 +2805,7 @@ AC_CHECK_HEADER([linux/compiler.h], AST_C_DEFINE_CHECK([MSG_NOSIGNAL], [MSG_NOSIGNAL], [sys/socket.h]) AST_C_DEFINE_CHECK([SO_NOSIGPIPE], [SO_NOSIGPIPE], [sys/socket.h]) -AST_EXT_TOOL_CHECK([SDL], [sdl-config]) +AST_PKG_CONFIG_CHECK([SDL], [sdl]) AST_EXT_LIB_CHECK([SDL_IMAGE], [SDL_image], [IMG_Load], [SDL_image.h], [${SDL_LIB}], [${SDL_INCLUDE}]) AST_EXT_LIB_CHECK([FFMPEG], [avcodec], [sws_getContext], [ffmpeg/avcodec.h], [${PTHREAD_LIBS} -lz -lm], [${PTHREAD_CFLAGS}]) diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index 35ba85f3100ebf78996afcfe577797f01954e817..558ba66b270bcc5a8f9cebc86f3849b4030d10e6 100644 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -260,6 +260,7 @@ enum ast_cdr_disposition { AST_CDR_BUSY = (1 << 2), AST_CDR_ANSWERED = (1 << 3), AST_CDR_CONGESTION = (1 << 4), + AST_CDR_ANSWERED_ELSEWHERE = (1 << 5), }; @@ -359,7 +360,7 @@ struct ast_cdr { /*! SessionId */ unsigned int sessionId; /*! SIPSessionID */ - char SIPSessionID[33]; + char SIPSessionID[128]; /*! sipIpAddress */ char sipIpAddress[40]; /*! farEndIPAddress */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index dcc191cf32f64a35fd6dff108dc675065eecad65..fa79a6901bedc328e7d3ea40cd9fdc2c4b880b97 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -203,6 +203,14 @@ extern "C" { #define AST_GENERATOR_FD (AST_MAX_FDS-4) /*!< used by generator */ #define AST_JITTERBUFFER_FD (AST_MAX_FDS-5) /*!< used by generator */ +enum early_media_direction{ + EARLY_MEDIA_NONE, + EARLY_MEDIA_INACTIVE, + EARLY_MEDIA_RECVONLY, + EARLY_MEDIA_SENDONLY, + EARLY_MEDIA_SENDRECV, +}; + enum ast_bridge_result { AST_BRIDGE_COMPLETE = 0, AST_BRIDGE_FAILED = -1, @@ -4339,6 +4347,10 @@ const char *ast_channel_farEndIPAddress(const struct ast_channel *chan); void ast_channel_farEndIPAddress_set(struct ast_channel *chan, const char *value, size_t size); unsigned int ast_channel_sipResponseCode(const struct ast_channel *chan); void ast_channel_sipResponseCode_set(struct ast_channel *chan, unsigned int value); +int ast_channel_narrow_band_only(const struct ast_channel *chan); +void ast_channel_narrow_band_only_set(struct ast_channel *chan, int value); +struct ast_channel *ast_channel_bridged_chan(const struct ast_channel *chan); +void ast_channel_bridged_chan_set(struct ast_channel *chan, struct ast_channel *value); const char *ast_channel_codec_get(const struct ast_channel *chan); void ast_channel_codec_set(struct ast_channel *chan, const char *value); struct ast_channel_snapshot *ast_channel_snapshot(const struct ast_channel *chan); @@ -4354,7 +4366,8 @@ enum ast_sip_dtmf_mode ast_channel_dtmf_mode_get(const struct ast_channel *chan) void ast_channel_dtmf_mode_set(struct ast_channel *chan, enum ast_sip_dtmf_mode dtmf_mode); int ast_channel_dtmf_pt_get(const struct ast_channel *chan); void ast_channel_dtmf_pt_set(struct ast_channel *chan, int dtmf_pt); - +enum early_media_direction ast_channel_early_media_get(const struct ast_channel *chan); +void ast_channel_early_media_set(struct ast_channel *chan, enum early_media_direction early_media_dir); /*! * \pre chan is locked diff --git a/include/asterisk/dt_logger.h b/include/asterisk/dt_logger.h new file mode 100644 index 0000000000000000000000000000000000000000..b85bf12550e45b5fdb617124c4c67b0e75df04ef --- /dev/null +++ b/include/asterisk/dt_logger.h @@ -0,0 +1,71 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu> + * + * \brief Support for the DT syslog logging mechanism + */ + +#ifndef _ASTERISK_DT_LOGGER_H +#define _ASTERISK_DT_LOGGER_H + +// Define language support +typedef enum { + LOG_LANGUAGE_DT_ENGLISH = 0, + LOG_LANGUAGE_DT_GERMAN, + // Add more languages here + LOG_LANGUAGE_DT_MAX +} LOG_LANGUAGE_DT; + +// Define log event codes +typedef enum { + LOG_EVENT_CODE_SPD001 = 1, + LOG_EVENT_CODE_SPD002, + LOG_EVENT_CODE_V001, + LOG_EVENT_CODE_V002, + LOG_EVENT_CODE_V003, + LOG_EVENT_CODE_V004, + LOG_EVENT_CODE_V005, + LOG_EVENT_CODE_V006, + LOG_EVENT_CODE_V100, + LOG_EVENT_CODE_V101, + LOG_EVENT_CODE_IPX001, + LOG_EVENT_CODE_IPX002, + LOG_EVENT_CODE_IPX003, + LOG_EVENT_CODE_IPX004, + LOG_EVENT_CODE_IPX100, + LOG_EVENT_CODE_IPX101, + LOG_EVENT_CODE_SV101, + LOG_EVENT_CODE_SV102, + LOG_EVENT_CODE_SV103, + LOG_EVENT_CODE_SV107, + LOG_EVENT_CODE_SV108, + LOG_EVENT_CODE_SV109, + LOG_EVENT_CODE_SV112, + LOG_EVENT_CODE_SV113, + LOG_EVENT_CODE_SV114, + LOG_EVENT_CODE_SV116, + // Add more event codes here +} LOG_EVENT_CODE_DT; + +void dt_set_log_language(LOG_LANGUAGE_DT language); +void ast_log_dt(LOG_EVENT_CODE_DT event_code, ...); + +#endif /* _ASTERISK_DT_LOGGER_H */ diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h index e2a21c7f14a47a8796c9aa145189ab3356b10233..05dde0f66e7b8c673c692cd8ee1fa6e7047b5236 100644 --- a/include/asterisk/logger.h +++ b/include/asterisk/logger.h @@ -27,6 +27,7 @@ #define _ASTERISK_LOGGER_H #include "asterisk/options.h" /* need option_debug */ +#include "asterisk/dt_logger.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index d144157c460183c11d9b92d6a4ea58f0ee8942d9..d3c8884ceee44fefb378d826eefe751e4c9c61bd 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -23,6 +23,7 @@ /* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */ #include <pjsip_simple.h> #include <pjsip/sip_transaction.h> +#include <pjsip/sip_auth.h> #include <pj/timer.h> /* Needed for pj_sockaddr */ #include <pjlib.h> @@ -313,6 +314,8 @@ struct ast_sip_transport { int symmetric_transport; /*! This is a flow to another target */ int flow; + /*! Enable the transport */ + int enable; }; #define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias" @@ -759,6 +762,8 @@ struct ast_sip_endpoint_id_configuration { unsigned int trust_outbound; /*! Do we send P-Asserted-Identity headers to this endpoint? */ unsigned int send_pai; + /*! Do we send P-Preferred-Identity headers to this endpoint? */ + unsigned int send_ppi; /*! Do we send Remote-Party-ID headers to this endpoint? */ unsigned int send_rpid; /*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */ @@ -954,8 +959,12 @@ struct ast_sip_endpoint { AST_DECLARE_STRING_FIELDS( /*! Context to send incoming calls to */ AST_STRING_FIELD(context); - /*! Name of an explicit transport to use */ + /*! Name of an primary transport to use */ AST_STRING_FIELD(transport); + /*! Name of an secondary transport to use */ + AST_STRING_FIELD(transport2); + /*! Name of an secondary transport to use */ + AST_STRING_FIELD(transport_emergency); /*! Outbound proxy to use */ AST_STRING_FIELD(outbound_proxy); /*! Explicit AORs to dial if none are specified */ @@ -1073,6 +1082,27 @@ struct ast_sip_endpoint { enum ast_sip_100rel_mode rel100; /*! Send Advice-of-Charge messages */ unsigned int send_aoc; + bool Emergency; + /* Information of register destination */ + struct pjsip_register_dest *register_dest; + unsigned int failover_reg_addr; // 0/1/2 -> none/504-register-ongoing/504-register-failed + int failover_reg_addr_timer_id; // timer for reset flag if register failed. + /* the transport name of latest registration */ + char *register_transport; + unsigned int max_sessions; + char *realms; + bool cached_auth; + /* Authorization sessions. */ + pjsip_auth_clt_sess auth_sess_reg; + pjsip_auth_clt_sess auth_sess_inv; + ast_mutex_t auth_sess_reg_lock; + ast_mutex_t auth_sess_inv_lock; +}; + +struct pjsip_register_dest { + char *host; + pjsip_server_addresses addr; + unsigned cur_addr; }; /*! URI parameter for symmetric transport */ @@ -1086,6 +1116,7 @@ extern pjsip_media_type pjsip_media_type_application_pidf_xml; extern pjsip_media_type pjsip_media_type_application_xpidf_xml; extern pjsip_media_type pjsip_media_type_application_cpim_xpidf_xml; extern pjsip_media_type pjsip_media_type_application_rlmi_xml; +extern pjsip_media_type pjsip_media_type_application_3gpp_ims_xml; extern pjsip_media_type pjsip_media_type_application_simple_message_summary; extern pjsip_media_type pjsip_media_type_application_sdp; extern pjsip_media_type pjsip_media_type_multipart_alternative; @@ -3545,6 +3576,11 @@ struct ao2_container *ast_sip_get_transport_states(void); */ int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector); +/*! + * \brief Check if the NTP has synchronized + */ +int ast_sip_is_X_RDK_NTP_Synchronized(void); + /*! * \brief Sets pjsip_tpselector from ast_sip_transport * \since 13.8.0 @@ -4059,5 +4095,18 @@ unsigned int ast_sip_get_all_codecs_on_empty_reinvite(void); */ const int ast_sip_hangup_sip2cause(int cause); +/*! + * \brief Retrieve the global setting 'max_sessions_per_line'. + * + * \retval value of max_sessions_per_line + */ +unsigned int ast_sip_get_max_sessions_per_line(void); + +/*! + * \brief Retrieve the global setting 'max_sessions_total'. + * + * \retval value of max_sessions_total + */ +unsigned int ast_sip_get_max_sessions_total(void); #endif /* _RES_PJSIP_H */ diff --git a/include/asterisk/res_pjsip_outbound_registration.h b/include/asterisk/res_pjsip_outbound_registration.h new file mode 100644 index 0000000000000000000000000000000000000000..0a47b416d048293baeff8c15be0617508fabbe35 --- /dev/null +++ b/include/asterisk/res_pjsip_outbound_registration.h @@ -0,0 +1,9 @@ +#ifndef _RES_PJSIP_OUTBOUND_REGISTRATION_H +#define _RES_PJSIP_OUTBOUND_REGISTRATION_H + +void queue_registration_recovery_flow(const char *registration_name); +void update_emergency_registration_ongoing_status(const char *registration_name, bool action); +void check_and_update_nextnonce(pjsip_auth_clt_sess *sess, const char *realm, pjsip_rx_data *rdata); +void sync_cached_auth_between_reg_inv(pjsip_auth_clt_sess *sess_from, pjsip_auth_clt_sess *sess_to, const pj_str_t *realm ); + +#endif /* _RES_PJSIP_OUTBOUND_REGISTRATION_H */ diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index 1e2943090ae215044098f179da46632b9a4788ac..c7e8a3c33af92af9856edfaf625b642720137d3a 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -245,6 +245,9 @@ struct ast_sip_session { int ani2; int ring_cw; int early_media; + /*! The working transport of the session */ + struct ast_sip_transport_state *transport_state; + pj_timer_entry incoming_rtp_check_timer; }; typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata); @@ -799,6 +802,19 @@ void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip */ struct ast_sip_session *ast_sip_dialog_get_session(pjsip_dialog *dlg); +/*! + * \brief Recalculate the joint codecs as if a re-INVITE is received within an established session + * + * This function calculates the joint codecs between the answered channel's capability and the SDP + * offer from the remote. + * + * \param session The session which contains the SDP offer + * + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_session_sdp_answer(struct ast_sip_session *session); + /*! * \brief Resumes processing of a deferred incoming re-invite * diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index f5533c31ffced6fb5882662cdf919c4a3db7a97a..5a017de14e55439fe6532799e7c2eeee68176b07 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -700,6 +700,8 @@ struct ast_rtp_engine { int (*get_stat)(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat); /*! Callback for setting QoS values */ int (*qos)(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); + /*! Callback for setting RTCP QoS values */ + void (*rtcp_qos)(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); /*! Callback for retrieving a file descriptor to poll on, not always required */ int (*fd)(struct ast_rtp_instance *instance, int rtcp); /*! Callback for initializing RED support */ @@ -2021,7 +2023,7 @@ void ast_rtp_instance_change_source(struct ast_rtp_instance *instance); * \since 1.8 */ int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); - +void ast_rtp_instance_set_rtcp_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc); /*! * \brief Stop an RTP instance * diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h index 5a754f6715d532903c2966053a6bfb5ddc137384..0fbab12adb516e9633f668c118606cbb49f5a8b0 100644 --- a/include/asterisk/stasis_channels.h +++ b/include/asterisk/stasis_channels.h @@ -158,7 +158,7 @@ struct ast_channel_snapshot { struct varshead *manager_vars; /*!< Variables to be appended to manager events */ struct varshead *ari_vars; /*!< Variables to be appended to ARI events */ unsigned int sessionId; /*!< SessionId */ - char SIPSessionID[33]; /*!< Session-ID */ + char SIPSessionID[128]; /*!< Session-ID */ char sipIpAddress[40]; /*!< SIP IP Address */ char farEndIPAddress[40]; /*!< Far End IP Address */ unsigned int sipResponseCode; /*!< SIP Response Code for Invite */ diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index d46fed6fda529abbf686833afa8e392cdba8bc3b..55a5ad044ff8d251381ae087fe4160f00abd92e2 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -1137,4 +1137,6 @@ int ast_check_command_in_path(const char *cmd); */ void ast_ubus_free_context(struct ubus_context *ctx); +void ast_send_network_info_ubus_event(int enabled, char* protocol, char* addr_buf, int port); + #endif /* _ASTERISK_UTILS_H */ diff --git a/main/Makefile b/main/Makefile index 2e0b84f31628601d1dc8501c581755e5680b5d3b..b4c9b7c501fc9f285d16632aa8ff7a5d2603d6b0 100644 --- a/main/Makefile +++ b/main/Makefile @@ -17,7 +17,7 @@ all: asterisk include $(ASTTOPDIR)/Makefile.moddir_rules -MOD_SRC:=cdr.c cel.c config.c ccss.c dnsmgr.c dsp.c enum.c features.c http.c indications.c logger.c manager.c named_acl.c plc.c sounds.c udptl.c +MOD_SRC:=cdr.c cel.c config.c ccss.c dnsmgr.c dsp.c enum.c features.c http.c indications.c logger.c manager.c named_acl.c plc.c sounds.c udptl.c dt_logger.c # Allow deletion of built-in modules without needing to modify this source. MOD_SRC:=$(wildcard $(MOD_SRC)) MOD_OBJS:=$(sort $(MOD_SRC:.c=.o)) diff --git a/main/asterisk.c b/main/asterisk.c index dea849f10c1f0a6fdcd64ebcd9e82fcab0c43e30..454ec4a6c523a2d0bb9dcbddaa69b12c0f150362 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -246,6 +246,10 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "../defaults.h" +#ifdef INCLUDE_BREAKPAD +#include "breakpad_wrapper.h" +#endif + /*** DOCUMENTATION <managerEvent language="en_US" name="FullyBooted"> <managerEventInstance class="EVENT_FLAG_SYSTEM"> @@ -3862,7 +3866,8 @@ int main(int argc, char *argv[]) /* Must install this signal handler up here to ensure that if the canary * fails to execute that it doesn't kill the Asterisk process. */ - sigaction(SIGCHLD, &child_handler, NULL); + if (ast_opt_high_priority) + sigaction(SIGCHLD, &child_handler, NULL); /* It's common on some platforms to clear /var/run at boot. Create the * socket file directory before we drop privileges. */ @@ -4200,6 +4205,10 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou check_init(ast_endpoint_stasis_init(), "Stasis Endpoint"); ast_makesocket(); + +#ifdef INCLUDE_BREAKPAD + breakpad_ExceptionHandler(); +#endif /* GCC 4.9 gives a bogus "right-hand operand of comma expression has * no effect" warning */ (void) sigemptyset(&sigs); diff --git a/main/cdr.c b/main/cdr.c index acae1fb60fdf8b5bbae3dc1ebbc31d7a90f3538b..964892fc7fbeab27a3077f54822122e3a227131b 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -1518,6 +1518,9 @@ static void cdr_object_set_disposition(struct cdr_object *cdr, int hangupcause) case AST_CAUSE_NO_ANSWER: cdr->disposition = AST_CDR_NOANSWER; break; + case AST_CAUSE_ANSWERED_ELSEWHERE: + cdr->disposition = AST_CDR_ANSWERED_ELSEWHERE; + break; default: break; } @@ -1541,6 +1544,14 @@ static void cdr_object_finalize(struct cdr_object *cdr) } cdr->end = cdr->lastevent; + if (cdr->disposition == AST_CDR_NOANSWER) { + // Retrieve hangup cause and update disposition to answered_elsewhere when incoming cancel request with sip cause=200, rfc3326 + if (cdr->party_a.snapshot->hangup->cause == AST_CAUSE_ANSWERED_ELSEWHERE + || (cdr->party_b.snapshot && cdr->party_b.snapshot->hangup->cause == AST_CAUSE_ANSWERED_ELSEWHERE)) { + + cdr_object_set_disposition(cdr, AST_CAUSE_ANSWERED_ELSEWHERE); + } + } if (cdr->disposition == AST_CDR_NULL) { if (!ast_tvzero(cdr->answer)) { cdr->disposition = AST_CDR_ANSWERED; @@ -3642,6 +3653,8 @@ const char *ast_cdr_disp2str(int disposition) return "ANSWERED"; case AST_CDR_CONGESTION: return "CONGESTION"; + case AST_CDR_ANSWERED_ELSEWHERE: + return "ANSWERED ELSEWHERE"; } return "UNKNOWN"; } diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index 128a9e70be2a072400d69954ced55175cdf7118a..f102ad674f68847352aabf0a2b1b5d7073982a25 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -225,7 +225,7 @@ struct ast_channel { struct ast_channel_snapshot *snapshot; /*!< The current up to date snapshot of the channel */ struct ast_flags snapshot_segment_flags; /*!< Flags regarding the segments of the snapshot */ unsigned int sessionId; /*!< Session Id from SDP for channel */ - char SIPSessionID[33]; /* SIPSessionID from Session-ID header */ + char SIPSessionID[128]; /* SIPSessionID from Session-ID header */ char sipIpAddress[40]; /*!< local IP address that sip client binds to */ char farEndIPAddress[40]; /*!< Far End IP Address */ unsigned int sipResponseCode; /*!< SIP response Code */ @@ -235,6 +235,9 @@ struct ast_channel { int ptime; /*!< Negotiated ptime */ enum ast_sip_dtmf_mode dtmf_mode; int dtmf_pt; + enum early_media_direction early_media_dir; /*!< The direction of the early media*/ + int narrow_band_only; /*!< Whether the channel supports narrow band only */ + struct ast_channel *bridged_chan; /*!< Channel that will be bridged with */ }; /*! \brief The monotonically increasing integer counter for channel uniqueids */ @@ -869,6 +872,26 @@ void ast_channel_sipResponseCode_set(struct ast_channel *chan, unsigned int valu chan->sipResponseCode = value; } +int ast_channel_narrow_band_only(const struct ast_channel *chan) +{ + return chan->narrow_band_only; +} + +void ast_channel_narrow_band_only_set(struct ast_channel *chan, int value) +{ + chan->narrow_band_only = value; +} + +struct ast_channel *ast_channel_bridged_chan(const struct ast_channel *chan) +{ + return chan->bridged_chan; +} + +void ast_channel_bridged_chan_set(struct ast_channel *chan, struct ast_channel *value) +{ + chan->bridged_chan = value; +} + const char *ast_channel_codec_get(const struct ast_channel *chan) { return chan->codec; @@ -918,11 +941,22 @@ int ast_channel_ptime_get(const struct ast_channel *chan) { return chan->ptime; } + void ast_channel_ptime_set(struct ast_channel *chan, int ptime) { chan->ptime = ptime; } +enum early_media_direction ast_channel_early_media_get(const struct ast_channel *chan) +{ + return chan->early_media_dir; +} +void ast_channel_early_media_set(struct ast_channel *chan, enum early_media_direction early_media_dir) +{ + chan->early_media_dir = early_media_dir; + ast_debug(3, "Set early_media_dir to %d for channel %s\n", chan->early_media_dir, ast_channel_name(chan)); +} + void ast_channel_callid_set(struct ast_channel *chan, ast_callid callid) { char call_identifier_from[AST_CALLID_BUFFER_LENGTH]; diff --git a/main/codec_builtin.c b/main/codec_builtin.c index bd69d46be1f08c1490f616e768c06fad5554f5a3..dfd3b0a0fb8469f129ded15e9eeae4740523be6e 100644 --- a/main/codec_builtin.c +++ b/main/codec_builtin.c @@ -169,7 +169,8 @@ static struct ast_codec ulaw = { .type = AST_MEDIA_TYPE_AUDIO, .sample_rate = 8000, .minimum_ms = 10, - .maximum_ms = 150, + .maximum_ms = 20, /* Changed according to DT requirements */ + /* .maximum_ms = 150, */ .default_ms = 20, .minimum_bytes = 80, .samples_count = ulaw_samples, @@ -183,7 +184,8 @@ static struct ast_codec alaw = { .type = AST_MEDIA_TYPE_AUDIO, .sample_rate = 8000, .minimum_ms = 10, - .maximum_ms = 150, + .maximum_ms = 20, /* Changed according to DT requirements */ + /* .maximum_ms = 150, */ .default_ms = 20, .minimum_bytes = 80, .samples_count = ulaw_samples, @@ -649,7 +651,8 @@ static struct ast_codec g722 = { .type = AST_MEDIA_TYPE_AUDIO, .sample_rate = 16000, .minimum_ms = 10, - .maximum_ms = 150, + .maximum_ms = 20, /* Changed according to DT requirements */ + /* .maximum_ms = 150, */ .default_ms = 20, .minimum_bytes = 80, .samples_count = g726_samples, diff --git a/main/dns_naptr.c b/main/dns_naptr.c index caeb6ac616d3f765990fb7a5fd514afae1848e9a..865a67c408ae0d80598f5a582af95850d20878e5 100644 --- a/main/dns_naptr.c +++ b/main/dns_naptr.c @@ -446,6 +446,7 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char * replacement, sizeof(replacement) - 1); if (replacement_size < 0) { ast_log(LOG_ERROR, "Failed to expand domain name: %s\n", strerror(errno)); + ast_log_dt(LOG_EVENT_CODE_SV108); return NULL; } @@ -453,6 +454,7 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char * if (ptr != end_of_record) { ast_log(LOG_ERROR, "NAPTR record gave undersized string indications.\n"); + ast_log_dt(LOG_EVENT_CODE_SV108); return NULL; } @@ -462,16 +464,19 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char * flags_res = interpret_flags(flags, flags_size); if (flags_res == FLAGS_INVALID) { ast_log(LOG_ERROR, "NAPTR Record contained invalid flags %.*s\n", flags_size, flags); + ast_log_dt(LOG_EVENT_CODE_SV108); return NULL; } if (services_invalid(services, services_size)) { ast_log(LOG_ERROR, "NAPTR record contained invalid services %.*s\n", services_size, services); + ast_log_dt(LOG_EVENT_CODE_SV108); return NULL; } if (regexp_invalid(regexp, regexp_size)) { ast_log(LOG_ERROR, "NAPTR record contained invalid regexp %.*s\n", regexp_size, regexp); + ast_log_dt(LOG_EVENT_CODE_SV108); return NULL; } @@ -480,6 +485,7 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char * */ if (regexp_size && replacement_size > 1) { ast_log(LOG_ERROR, "NAPTR record contained both a regexp and replacement\n"); + ast_log_dt(LOG_EVENT_CODE_SV108); return NULL; } diff --git a/main/dt_logger.c b/main/dt_logger.c new file mode 100644 index 0000000000000000000000000000000000000000..c9ae9a2415efd2daeba3b9a94b61e2a5c0a12cfc --- /dev/null +++ b/main/dt_logger.c @@ -0,0 +1,369 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu> + * + * \brief Support for the DT syslog logging mechanism + */ + +#include <stdio.h> +#include <syslog.h> +#include <stdarg.h> +#include <time.h> +#include <pthread.h> +#include <sys/file.h> +#include "asterisk.h" +#include "asterisk/dt_logger.h" + +#define LOG_FILE_PATH "/rdklogs/logs/VoIPlog.txt" + +// Define flags for extended syslog +#define LOG_FLAG_EXTENDED_SYSLOG_ONLY 0x01 + +// Current language setting +static LOG_LANGUAGE_DT current_language = LOG_LANGUAGE_DT_ENGLISH; + +// Structure to store log messages in different languages and log options (flags) +struct ast_log_message_table_dt { + LOG_EVENT_CODE_DT code; + const char *messages[LOG_LANGUAGE_DT_MAX]; + uint32_t category; // priority of syslog message: + // (LOG_EMERG|LOG_ALERT|LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUG) + uint32_t flags; // Flags, not used for now +}; + +// Log message table with messages in different languages and flags +struct ast_log_message_table_dt log_message_table_dt[] = { + { + .code = LOG_EVENT_CODE_SPD001, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The Speeddial memory is erased by entering the keycode **#93# in a connected telefon.", + [LOG_LANGUAGE_DT_GERMAN] = "Der Nummernspeicher wurde durch die Eingabe des Tastencodes **#93# in ein angeschlossen Telefon gelöscht." + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_SPD002, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The Speeddial memory is erased via the graphical user interface of the router.", + [LOG_LANGUAGE_DT_GERMAN] = "Der Nummernspeicher wurde über das grafische Benutzermenü gelöscht." + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_V001, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Registering with VoIP number ( %s ) failed: Reason: DNS error.", + [LOG_LANGUAGE_DT_GERMAN] = "Anmeldung der Internet-Telefonnummer ( %s ) war nicht erfolgreich. Ursache: DNS-Fehler." + }, + .category = LOG_ERR, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_V002, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Registering with VoIP number ( %s ) failed: Reason: %s", + [LOG_LANGUAGE_DT_GERMAN] = "Anmeldung der Internet-Telefonnummer ( %s ) war nicht erfolgreich. Gegenstelle meldet Ursache: %s" + }, + .category = LOG_ERR, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_V003, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "A general error occurred during Internet telephony: This action is not allowed (SIP Error code 488)", + [LOG_LANGUAGE_DT_GERMAN] = "Es ist ein allgemeiner Fehler bei der Internet-Telefonie aufgetreten: Diese Aktion ist nicht erlaubt (SIP Fehlercode 488)" + }, + .category = LOG_ERR, + .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY // Included in extended syslog only + }, + { + .code = LOG_EVENT_CODE_V004, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Registration of the Internet telephone number (%s) was not successful. Cause: Remote station does not respond: Timeout.", + [LOG_LANGUAGE_DT_GERMAN] = "Anmeldung der Internet-Telefonnummer (%s) war nicht erfolgreich. Ursache: Gegenstelle antwortet nicht: Zeitüberschreitung." + }, + .category = LOG_ERR, + .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY // Included in extended syslog only + }, + { + .code = LOG_EVENT_CODE_V005, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Deregistration of the Internet telephone number %s was not successful. The other party reports the reason: %.*s", + [LOG_LANGUAGE_DT_GERMAN] = "Abmeldung der Internet-Telefonnummer %s war nicht erfolgreich. Gegenstelle meldet Ursache: %.*s" + }, + .category = LOG_ERR, + .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY // Included in extended syslog only + }, + { + .code = LOG_EVENT_CODE_V006, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The Internet voice connection with %s was not successful. Reason: %.*s", + [LOG_LANGUAGE_DT_GERMAN] = "Die Internet-Sprachverbindung mit %s war nicht erfolgreich. Ursache: %.*s" + }, + .category = LOG_ERR, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_V100, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The Speeddial memory was emptied by the user.", + [LOG_LANGUAGE_DT_GERMAN] = "Der Speeddial Speicher wurde durch den Nutzer geleert." + }, + .category = LOG_ERR, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_V101, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The Internet telephony connection (registration) for %s was successfully established: IP address (registrar): %s", + [LOG_LANGUAGE_DT_GERMAN] = "Die Internet Telefonie Verbindung (Registrierung) für %s wurde erfolgreich hergestellt: IP-Adresse (Registrar): %s" + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_IPX001, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %.*s with IP %.*s has registered successfully.", + [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %.*s mit IP %.*s hat sich erfolgreich registriert." + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_IPX002, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %.*s with IP %.*s has deregistered.", + [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %.*s mit IP %.*s hat sich deregistriert." + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_IPX003, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %.*s with IP %.*s did not register successfully due to missing authorization.", + [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %.*s mit IP %.*s hat sich aufgrund fehlender Autorisierung nicht erfolgreich registriert." + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_IPX004, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %s with IP %s could not register successfully due to a timeout.", + [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %s mit IP %s konnte sich aufgrund eines Timeouts nicht erfolgreich registrieren." + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_IPX100, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "An error %d occurred with IP phone %.*s.", + [LOG_LANGUAGE_DT_GERMAN] = "Es ist ein Fehler %d mit IP-Telefon %.*s aufgetreten" + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_IPX101, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The registration of the local IP phone %.*s with the MAC address <mac address of registering client> failed more than five times in a row due to an authentication error %d. The telephony function of the IP phone %.*s has been deactivated.", + [LOG_LANGUAGE_DT_GERMAN] = "Die Registrierung des lokalen IP-Telefons %.*s mit der MAC-Adresse <mac address of registering client> ist mehr als fünfmal in Folge aufgrund eines Authentifizierungsfehler %d fehlgeschlagen. Die Telephonie Funktion des IP-Telefons %.*s wurde deaktiviert." + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_SV101, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Security setting set to ‘Off’", + [LOG_LANGUAGE_DT_GERMAN] = "Sicherheitseinstellung auf ‚Aus’ gestellt" + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_SV102, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Security setting set to ‘Level 1’", + [LOG_LANGUAGE_DT_GERMAN] = "Sicherheitseinstellung auf ‚Stufe 1’ gestellt" + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_SV103, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Security setting set to ‘Level 2’", + [LOG_LANGUAGE_DT_GERMAN] = "Sicherheitseinstellung auf ‚Stufe 2’ gestellt" + }, + .category = LOG_INFO, + .flags = 0 // Not extended syslog + }, + { + .code = LOG_EVENT_CODE_SV107, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Exceeding TLS", + [LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolglos" + }, + .category = LOG_ERR, + .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY // Included in extended syslog only + }, + { + .code = LOG_EVENT_CODE_SV108, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Invalid NAPTR entry for SIPS", + [LOG_LANGUAGE_DT_GERMAN] = "Ungültiger NAPTR Eintrag für SIPS" + }, + .category = LOG_ERR, + .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY // Included in extended syslog only + }, + { + .code = LOG_EVENT_CODE_SV109, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Invalid request URI like e.g. „sips:%s“ used", + [LOG_LANGUAGE_DT_GERMAN] = "Invalid request URI like e.g. „sips:%s“ used" + }, + .category = LOG_ERR, + .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY // Included in extended syslog only + }, + { + .code = LOG_EVENT_CODE_SV112, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Registration error in position ‘Level 1’", + [LOG_LANGUAGE_DT_GERMAN] = "Registrierungsfehler in Stellung ‚Stufe 1’" + }, + .category = LOG_ERR, + .flags = 0 + }, + { + .code = LOG_EVENT_CODE_SV113, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Registration error in position ‘Level 2’", + [LOG_LANGUAGE_DT_GERMAN] = "Registrierungsfehler in Stellung ‚Stufe 2’" + }, + .category = LOG_ERR, + .flags = 0 + }, + { + .code = LOG_EVENT_CODE_SV114, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "Relapse to ‘Stage 1’", + [LOG_LANGUAGE_DT_GERMAN] = "Rückfall zu ‚Stufe 1’" + }, + .category = LOG_INFO, + .flags = 0 + }, + { + .code = LOG_EVENT_CODE_SV116, + .messages = { + [LOG_LANGUAGE_DT_ENGLISH] = "The secure Internet telephony connection (registration) for <%s> was successfully established: IP address (registrar): <%s>", + [LOG_LANGUAGE_DT_GERMAN] = "Die sichere Internet Telefonie Verbindung (Registrierung) für <%s>) wurde erfolgreich hergestellt: IP-Adresse (Registrar): <%s>" + }, + .category = LOG_INFO, + .flags = 0 + } + // More log entries can be added here... +}; + +static void get_current_timestamp(char *buffer, size_t size) { + time_t now = time(NULL); + struct tm *tstruct = localtime(&now); + strftime(buffer, size, "%Y-%m-%dT%H:%M:%S", tstruct); +} + +static const char *translate_syslog_priority(int priority) { + switch (priority) { + case LOG_EMERG: + return "EMERG"; + case LOG_ALERT: + return "ALERT"; + case LOG_CRIT: + return "CRIT"; + case LOG_ERR: + return "ERROR"; + case LOG_WARNING: + return "WARN"; + case LOG_NOTICE: + return "NOTICE"; + case LOG_INFO: + return "INFO"; + case LOG_DEBUG: + return "DEBUG"; + default: + return "UNKNOWN"; + } +} + +// Function to set language +void dt_set_log_language(LOG_LANGUAGE_DT language) { + if (language >= 0 && language < LOG_LANGUAGE_DT_MAX) { + current_language = language; + } +} + +// Function to log a message based on the event code +void ast_log_dt(LOG_EVENT_CODE_DT event_code, ...) { + const char *message = NULL; + uint32_t category = 0; + int flags = 0; + char full_message[1024]; + char log_message[1280]; + va_list args; + + // Find the log message for the event code in the correct language + for (size_t i = 0; i < sizeof(log_message_table_dt) / sizeof(log_message_table_dt[0]); i++) { + if (log_message_table_dt[i].code == event_code) { + message = log_message_table_dt[i].messages[current_language]; + category = log_message_table_dt[i].category; + flags = log_message_table_dt[i].flags; + break; + } + } + + if (!message) + return; + + // Format the log message with variable arguments + va_start(args, event_code); + vsnprintf(full_message, sizeof(full_message), message, args); + va_end(args); + + // get additional data for the final log message + char timestamp[64]; + get_current_timestamp(timestamp, sizeof(timestamp)); + const char *log_level = translate_syslog_priority(category); + unsigned long tid = gettid(); + + snprintf(log_message, sizeof(log_message), "%s telekom: %s.%s [tid=%lu] %s", + timestamp, "VOIP", log_level, tid, full_message); + + // Write the log message to a file + FILE *log_file = fopen(LOG_FILE_PATH, "a"); + if (log_file) { + fprintf(log_file, "%s\n", log_message); + fclose(log_file); + } +} diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 0a18a02282b0d294818ac04cd122523223c2f1db..37ccd1f3aa1a59d6260c4dc0c8ecfad0c4fde67d 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -2193,6 +2193,15 @@ int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos return res; } +void ast_rtp_instance_set_rtcp_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc) +{ + if (instance->engine->rtcp_qos) { + ao2_lock(instance); + instance->engine->rtcp_qos(instance, tos, cos, desc); + ao2_unlock(instance); + } +} + void ast_rtp_instance_stop(struct ast_rtp_instance *instance) { if (instance->engine->stop) { diff --git a/main/utils.c b/main/utils.c index 1e5b8abdf575264d0277106a55f7395ecc886aaf..b29bb01361d7f86beb62a7cdbb671d45f5e28fc9 100644 --- a/main/utils.c +++ b/main/utils.c @@ -81,6 +81,7 @@ static char base64[64]; static char base64url[64]; static char b2a[256]; static char b2a_url[256]; +static const char broadcast_path[] = "voice.sip.data"; AST_THREADSTORAGE(inet_ntoa_buf); @@ -3266,3 +3267,33 @@ void ast_ubus_free_context(struct ubus_context *ctx) free(ctx->msgbuf.data); free(ctx); } + +void ast_send_network_info_ubus_event(int enabled, char* protocol, char* addr_buf, int port) +{ + struct blob_buf blob; + struct ubus_context *ubusContext = NULL; + + ubusContext = ubus_connect(NULL); + if(!ubusContext){ + return; + } + + memset(&blob, 0, sizeof(blob)); + if(blob_buf_init(&blob, 0)) { + ast_ubus_free_context(ubusContext); + return; + } + + blobmsg_add_u8(&blob, "enabled", enabled); + blobmsg_add_string(&blob, "protocol", protocol); + blobmsg_add_string(&blob, "ip", addr_buf); + blobmsg_add_u32(&blob, "port", port); + + if(ubus_send_event(ubusContext, broadcast_path, blob.head) != UBUS_STATUS_OK) + ast_log(LOG_ERROR,"Error sending ubus message\n"); + + ast_ubus_free_context(ubusContext); + blob_buf_free(&blob); + + return; +} diff --git a/menuselect/configure.ac b/menuselect/configure.ac index 589f0828e07f79136bbeae5f581f93a65f2f29ae..096d42fec64074268fb98fdf428fbce8af95697d 100644 --- a/menuselect/configure.ac +++ b/menuselect/configure.ac @@ -92,14 +92,6 @@ else fi AST_PKG_CONFIG_CHECK([LIBXML2], [libxml-2.0]) -AST_EXT_TOOL_CHECK([LIBXML2], [xml2-config], , , - [#include <libxml/tree.h> - #include <libxml/parser.h>], - [LIBXML_TEST_VERSION]) - -if test "${PBX_LIBXML2}" != 1; then - AC_MSG_ERROR([Could not find required 'Libxml2' development package]) -fi AST_PKG_CONFIG_CHECK([GTK2], [gtk+-2.0]) AC_SUBST(PBX_GTK2) diff --git a/res/Makefile b/res/Makefile index b8756b17538bc8ab9b1d8e503de625b65c588c95..36b55a1721540e7242b5154d52e6b3c779a149ed 100644 --- a/res/Makefile +++ b/res/Makefile @@ -19,6 +19,12 @@ all: _all include $(ASTTOPDIR)/Makefile.moddir_rules +LIBS += ../main/dt_logger.o + +ifneq ($(DATAMODEL_UCI_PATH),) +_ASTCFLAGS += -DUCI_CONFIG_DIR=\"$(DATAMODEL_UCI_PATH)\" +endif + ifneq ($(findstring $(OSARCH), mingw32 cygwin ),) # cygwin has some dependencies among res_ things. # We use order-only dependencies, and then add the libraries as required. @@ -28,6 +34,7 @@ endif res_pjsip_pubsub.so: LIBS+=-lubus -lubox -lpicoevent res_pjsip_outbound_registration.so: LIBS+=-lubus -lubox -lpicoevent +res_pjsip.so: LIBS+=-luci res_config_ldap.o: _ASTCFLAGS+=-DLDAP_DEPRECATED diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 9d786bde024ccb0b9499e4b0c58cb5af38c8322d..8c71b70bd663315b9fb9a2a9f873ad494e64a335 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -591,6 +591,7 @@ int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dia pjsip_tpselector *selector) { pjsip_sip_uri *uri; + struct ast_sip_transport_state *transport2 = NULL; pjsip_tpselector sel = { .type = PJSIP_TPSELECTOR_NONE, }; uri = pjsip_uri_get_uri(dlg->target); @@ -598,7 +599,31 @@ int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dia selector = &sel; } - ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector); + if (endpoint->register_transport && + !ast_sip_set_tpselector_from_transport_name(endpoint->register_transport, selector)) { + + transport2 = ast_sip_get_transport_state(endpoint->register_transport); + ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->register_transport); + + } else if (!ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector)) { + ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->transport); + + } else { + ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport); + + if (ast_strlen_zero(endpoint->transport2) || + ast_sip_set_tpselector_from_transport_name(endpoint->transport2, selector)) { + ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport2\n", ast_sorcery_object_get_id(endpoint)); + return -1; + } + + ast_log(LOG_NOTICE, "%s: Switched to transport2 '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport2); + + transport2 = ast_sip_get_transport_state(endpoint->transport2); + } + + if (transport2) + uri->port = pj_sockaddr_get_port(&transport2->host); pjsip_dlg_set_transport(dlg, selector); @@ -703,6 +728,16 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u return 0; } +int ast_sip_is_X_RDK_NTP_Synchronized(void) +{ + int sync = 0; + + if (access("/tmp/ntpd_synchronized", F_OK) == 0) + sync = 1; + + return sync; +} + int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector) { int res = 0; @@ -776,9 +811,8 @@ int ast_sip_set_tpselector_from_ep_or_uri(const struct ast_sip_endpoint *endpoin { char transport_name[128]; - if (ast_sip_get_transport_name(endpoint, sip_uri, transport_name, sizeof(transport_name))) { - return 0; - } + if (ast_sip_get_transport_name(endpoint, sip_uri, transport_name, sizeof(transport_name))) + return -1; return ast_sip_set_tpselector_from_transport_name(transport_name, selector); } @@ -989,9 +1023,25 @@ static pjsip_dialog *create_dialog_uas(const struct ast_sip_endpoint *endpoint, ast_assert(status != NULL); contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); - if (!contact_hdr || ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(contact_hdr->uri), - &selector)) { + if (!contact_hdr) return NULL; + + if (endpoint->register_transport && + !ast_sip_set_tpselector_from_transport_name(endpoint->register_transport, &selector)) { + ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->register_transport); + + } else if (!ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(contact_hdr->uri), &selector)) { + ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->transport); + } else { + ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport); + + if (ast_strlen_zero(endpoint->transport2) || + ast_sip_set_tpselector_from_transport_name(endpoint->transport2, &selector)) { + ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport2\n", ast_sorcery_object_get_id(endpoint)); + return NULL; + } + + ast_log(LOG_NOTICE, "%s: Switched to transport2 '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport2); } transport = rdata->tp_info.transport; @@ -1227,7 +1277,25 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s return -1; } - ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(sip_uri), &selector); + if (endpoint->register_transport && + !ast_sip_set_tpselector_from_transport_name(endpoint->register_transport, &selector)) { + ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->register_transport); + + } else if (!ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(sip_uri), &selector)) { + ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->transport); + + } else { + ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport); + + if (ast_strlen_zero(endpoint->transport2) || + ast_sip_set_tpselector_from_transport_name(endpoint->transport2, &selector)) { + ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport2\n", ast_sorcery_object_get_id(endpoint)); + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + return -1; + } + + ast_log(LOG_NOTICE, "%s: Switched to transport2 '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport2); + } fromuser = endpoint ? (!ast_strlen_zero(endpoint->fromuser) ? endpoint->fromuser : ast_sorcery_object_get_id(endpoint)) : NULL; if (sip_dialog_create_from(pool, &from, fromuser, @@ -1241,7 +1309,7 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s } if (pjsip_endpt_create_request(ast_sip_get_pjsip_endpoint(), method, &remote_uri, - &from, &remote_uri, &from, NULL, -1, NULL, tdata) != PJ_SUCCESS) { + &from, &remote_uri, &from, NULL, NULL, -1, NULL, tdata) != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to create outbound %.*s request to endpoint %s\n", (int) pj_strlen(&method->name), pj_strbuf(&method->name), endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>"); @@ -1722,14 +1790,14 @@ static int check_request_status(struct send_request_data *req_data, pjsip_event case 408: case 503: case 500: - if ((res = ast_sip_failover_request(tsx->last_tx))) { - tdata = tsx->last_tx; + //if ((res = ast_sip_failover_request(tsx->last_tx))) { + // tdata = tsx->last_tx; /* * Bump the ref since it will be on a new transaction and * we don't want it to go away along with the old transaction. */ - pjsip_tx_data_add_ref(tdata); - } + // pjsip_tx_data_add_ref(tdata); + //} break; } @@ -2423,7 +2491,7 @@ const char *ast_sip_call_codec_pref_to_str(struct ast_flags pref) if (ast_sip_call_codec_pref_test(pref, LOCAL) && ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) { value = "local"; - } else if (ast_sip_call_codec_pref_test(pref, LOCAL) && ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) { + } else if (ast_sip_call_codec_pref_test(pref, LOCAL) && ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) { value = "local_merge"; } else if (ast_sip_call_codec_pref_test(pref, LOCAL) && ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, FIRST)) { value = "local_first"; @@ -2431,7 +2499,7 @@ const char *ast_sip_call_codec_pref_to_str(struct ast_flags pref) value = "remote"; } else if (ast_sip_call_codec_pref_test(pref, REMOTE) && ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) { value = "remote_merge"; - } else if (ast_sip_call_codec_pref_test(pref, REMOTE) && ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, FIRST)) { + } else if (ast_sip_call_codec_pref_test(pref, REMOTE) && ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, FIRST)) { value = "remote_first"; } else { value = "unknown"; @@ -2447,7 +2515,7 @@ int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str, if (strcmp(pref_str, "local") == 0) { ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL); } else if (is_outgoing && strcmp(pref_str, "local_merge") == 0) { - ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL); + ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL); } else if (strcmp(pref_str, "local_first") == 0) { ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_FIRST); } else if (strcmp(pref_str, "remote") == 0) { @@ -2455,7 +2523,7 @@ int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str, } else if (is_outgoing && strcmp(pref_str, "remote_merge") == 0) { ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL); } else if (strcmp(pref_str, "remote_first") == 0) { - ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_FIRST); + ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_FIRST); } else { return -1; } @@ -2830,6 +2898,8 @@ const int ast_sip_hangup_sip2cause(int cause) /* Possible values taken from causes.h */ switch(cause) { + case 200: /* Answered elsewhere, rfc3326 */ + return AST_CAUSE_ANSWERED_ELSEWHERE; case 401: /* Unauthorized */ return AST_CAUSE_CALL_REJECTED; case 403: /* Not found */ @@ -2976,6 +3046,7 @@ static int reload_configuration_task(void *obj) { ast_res_pjsip_reload_configuration(); ast_res_pjsip_init_options_handling(1); + ast_sip_initialize_resolver(); ast_sip_initialize_dns(); return 0; } @@ -3097,6 +3168,7 @@ pjsip_media_type pjsip_media_type_application_pidf_xml; pjsip_media_type pjsip_media_type_application_xpidf_xml; pjsip_media_type pjsip_media_type_application_cpim_xpidf_xml; pjsip_media_type pjsip_media_type_application_rlmi_xml; +pjsip_media_type pjsip_media_type_application_3gpp_ims_xml; pjsip_media_type pjsip_media_type_application_simple_message_summary; pjsip_media_type pjsip_media_type_application_sdp; pjsip_media_type pjsip_media_type_multipart_alternative; @@ -3130,6 +3202,7 @@ static int load_module(void) pjsip_media_type_init2(&pjsip_media_type_application_xpidf_xml, "application", "xpidf+xml"); pjsip_media_type_init2(&pjsip_media_type_application_cpim_xpidf_xml, "application", "cpim-xpidf+xml"); pjsip_media_type_init2(&pjsip_media_type_application_rlmi_xml, "application", "rlmi+xml"); + pjsip_media_type_init2(&pjsip_media_type_application_3gpp_ims_xml, "application", "3gpp-ims+xml"); pjsip_media_type_init2(&pjsip_media_type_application_sdp, "application", "sdp"); pjsip_media_type_init2(&pjsip_media_type_application_simple_message_summary, "application", "simple-message-summary"); pjsip_media_type_init2(&pjsip_media_type_multipart_alternative, "multipart", "alternative"); diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c index 0148116f7f41c4a2e69d2027d91cefe755590efa..394e2e1f8bcbf2857e578924ca1c49cd7cab40aa 100644 --- a/res/res_pjsip/config_global.c +++ b/res/res_pjsip/config_global.c @@ -56,6 +56,8 @@ #define DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL #define DEFAULT_NOREFERSUB 1 #define DEFAULT_ALL_CODECS_ON_EMPTY_REINVITE 0 +#define DEFAULT_MAX_SESSIONS_PER_LINE 2 +#define DEFAULT_MAX_SESSIONS_TOTAL 4 /*! * \brief Cached global config object @@ -126,7 +128,8 @@ struct global_config { unsigned int all_codecs_on_empty_reinvite; /*! Allow hash ('#') to appear in outgoing URIs. Default: NO */ unsigned int allow_tx_hash_in_uri; - + unsigned int max_sessions_per_line; + unsigned int max_sessions_total; }; static void global_destructor(void *obj) @@ -577,6 +580,36 @@ unsigned int ast_sip_get_all_codecs_on_empty_reinvite(void) return all_codecs_on_empty_reinvite; } +unsigned int ast_sip_get_max_sessions_per_line(void) +{ + unsigned int max_sessions_per_line; + struct global_config *cfg; + + cfg = get_global_cfg(); + if (!cfg) { + return DEFAULT_MAX_SESSIONS_PER_LINE; + } + + max_sessions_per_line = cfg->max_sessions_per_line; + ao2_ref(cfg, -1); + return max_sessions_per_line; +} + +unsigned int ast_sip_get_max_sessions_total(void) +{ + unsigned int max_sessions_total; + struct global_config *cfg; + + cfg = get_global_cfg(); + if (!cfg) { + return DEFAULT_MAX_SESSIONS_TOTAL; + } + + max_sessions_total = cfg->max_sessions_total; + ao2_ref(cfg, -1); + return max_sessions_total; +} + static int overload_trigger_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -791,6 +824,10 @@ int ast_sip_initialize_sorcery_global(void) OPT_BOOL_T, 1, FLDSET(struct global_config, all_codecs_on_empty_reinvite)); ast_sorcery_object_field_register(sorcery, "global", "allow_tx_hash_in_uri", "no", OPT_YESNO_T, 1, FLDSET(struct global_config, allow_tx_hash_in_uri)); + ast_sorcery_object_field_register(sorcery, "global", "max_sessions_per_line", + __stringify(DEFAULT_MAX_SESSIONS_PER_LINE), OPT_UINT_T, 0, FLDSET(struct global_config, max_sessions_per_line)); + ast_sorcery_object_field_register(sorcery, "global", "max_sessions_total", + __stringify(DEFAULT_MAX_SESSIONS_TOTAL), OPT_UINT_T, 0, FLDSET(struct global_config, max_sessions_total)); if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) { return -1; diff --git a/res/res_pjsip/config_system.c b/res/res_pjsip/config_system.c index eb8fd12d125145a8a5cdb63cfa19661d5c11d82c..f4c522856f37c1139e4259a99d0698107a4014fd 100644 --- a/res/res_pjsip/config_system.c +++ b/res/res_pjsip/config_system.c @@ -30,12 +30,18 @@ #define TIMER_T1_MIN 100 #define DEFAULT_TIMER_T1 500 +#define DEFAULT_TIMER_T2 4000 +#define DEFAULT_TIMER_T4 5000 #define DEFAULT_TIMER_B 32000 struct system_config { SORCERY_OBJECT(details); /*! Transaction Timer T1 value */ unsigned int timert1; + /*! Transaction Timer T2 value */ + unsigned int timert2; + /*! Transaction Timer T4 value */ + unsigned int timert4; /*! Transaction Timer B value */ unsigned int timerb; /*! Should we use short forms for headers? */ @@ -104,6 +110,8 @@ static int system_apply(const struct ast_sorcery *sorcery, void *obj) pjsip_cfg()->tsx.t1 = system->timert1; pjsip_cfg()->tsx.td = system->timerb; + ast_log(LOG_NOTICE, "Timer Setting to T1:%d, T2:%d, T4:%d, TB/TD:%d\n", system->timert1, system->timert2, system->timert4,system->timerb); + pjsip_tsx_set_timers(system->timert1, system->timert2, system->timert4, system->timerb); pjsip_cfg()->endpt.follow_early_media_fork = system->follow_early_media_fork; #ifdef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS @@ -195,6 +203,10 @@ int ast_sip_initialize_system(void) ast_sorcery_object_field_register(system_sorcery, "system", "type", "", OPT_NOOP_T, 0, 0); ast_sorcery_object_field_register(system_sorcery, "system", "timer_t1", __stringify(DEFAULT_TIMER_T1), OPT_UINT_T, 0, FLDSET(struct system_config, timert1)); + ast_sorcery_object_field_register(system_sorcery, "system", "timer_t2", __stringify(DEFAULT_TIMER_T2), + OPT_UINT_T, 0, FLDSET(struct system_config, timert2)); + ast_sorcery_object_field_register(system_sorcery, "system", "timer_t4", __stringify(DEFAULT_TIMER_T4), + OPT_UINT_T, 0, FLDSET(struct system_config, timert4)); ast_sorcery_object_field_register(system_sorcery, "system", "timer_b", __stringify(DEFAULT_TIMER_B), OPT_UINT_T, 0, FLDSET(struct system_config, timerb)); ast_sorcery_object_field_register(system_sorcery, "system", "compact_headers", "no", diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c index c8655c2eea62059cb181c852ba402a351212ef36..103015cfd288e12a0ea20f11d176a47e966b69ab 100644 --- a/res/res_pjsip/config_transport.c +++ b/res/res_pjsip/config_transport.c @@ -799,7 +799,9 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) usleep(BIND_DELAY_US); } - if (temp_state->state->host.addr.sa_family == pj_AF_INET()) { + if (!transport->enable) + ast_log(LOG_WARNING, "UDP transport is disabled\n"); + else if (temp_state->state->host.addr.sa_family == pj_AF_INET()) { res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv4, NULL, transport->async_operations, &temp_state->state->transport); @@ -847,8 +849,12 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) usleep(BIND_DELAY_US); } - res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, - &temp_state->state->factory); + if (!transport->enable) { + ast_log(LOG_WARNING, "TCP transport is disabled\n"); + } else { + res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, + &temp_state->state->factory); + } } } else if (transport->type == AST_TRANSPORT_TLS) { #if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 @@ -877,9 +883,13 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) usleep(BIND_DELAY_US); } - res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, - &temp_state->state->host, NULL, transport->async_operations, - &temp_state->state->factory); + if (!transport->enable) { + ast_log(LOG_WARNING, "TLS transport is disabled\n"); + } else { + res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, + &temp_state->state->host, NULL, transport->async_operations, + &temp_state->state->factory); + } } if (res == PJ_SUCCESS) { @@ -894,6 +904,8 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) * later to look up the transport state. */ sprintf(temp_state->state->factory->info, "%s", transport_id); + }else{ + ast_log_dt(LOG_EVENT_CODE_SV107); } #else ast_log(LOG_ERROR, "Transport: %s: PJSIP has not been compiled with TLS transport support, ensure OpenSSL development packages are installed\n", @@ -1033,6 +1045,38 @@ static int privkey_file_to_str(const void *obj, const intptr_t *args, char **buf return 0; } +static int transport_tls_timeout_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + const struct ast_sip_transport *transport = obj; + RAII_VAR(struct ast_sip_transport_state *, state, find_or_create_temporary_state(transport), ao2_cleanup); + + if (transport->type == AST_TRANSPORT_TLS) { + + if (!state) + return -1; + + state->tls.timeout.sec = atoi(var->value); + } + + return 0; +} + +static int transport_tls_timeout_to_str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_transport *transport = obj; + RAII_VAR(struct ast_sip_transport_state *, state, find_state_by_transport(transport), ao2_cleanup); + + if (transport->type == AST_TRANSPORT_TLS) { + + if (!state) + return -1; + + ast_asprintf(buf, "%u", state->tls.timeout.sec); + } + + return 0; +} + /*! \brief Custom handler for turning a string protocol into an enum */ static int transport_protocol_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -1226,6 +1270,8 @@ static int transport_tls_method_handler(const struct aco_option *opt, struct ast state->tls.method = PJSIP_TLSV1_1_METHOD; } else if (!strcasecmp(var->value, "tlsv1_2")) { state->tls.method = PJSIP_TLSV1_2_METHOD; + } else if (!strcasecmp(var->value, "tlsv1_3")) { + state->tls.method = PJSIP_TLSV1_3_METHOD; #endif } else if (!strcasecmp(var->value, "sslv2")) { state->tls.method = PJSIP_SSLV2_METHOD; @@ -1730,6 +1776,7 @@ int ast_sip_initialize_sorcery_transport(void) /* Normally type is a OPT_NOOP_T but we're using it to make sure that state is created */ ast_sorcery_object_field_register_custom(sorcery, "transport", "type", "", transport_state_init, NULL, NULL, 0, 0); + ast_sorcery_object_field_register(sorcery, "transport", "enable", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, enable)); ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, transport_protocol_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, transport_bind_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations)); @@ -1738,6 +1785,7 @@ int ast_sip_initialize_sorcery_transport(void) ast_sorcery_object_field_register_custom(sorcery, "transport", "ca_list_path", "", transport_tls_file_handler, ca_list_path_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sorcery, "transport", "cert_file", "", transport_tls_file_handler, cert_file_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sorcery, "transport", "priv_key_file", "", transport_tls_file_handler, privkey_file_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "tls_timeout", "", transport_tls_timeout_handler, transport_tls_timeout_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sorcery, "transport", "password", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, password)); ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_signaling_address)); diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml index b8f6775e298bb6f84cde5b582d61c4ec6bbdad03..4f19f56b03e0b11d7709eb47829d0ff9e872cd71 100644 --- a/res/res_pjsip/pjsip_config.xml +++ b/res/res_pjsip/pjsip_config.xml @@ -651,6 +651,9 @@ <configOption name="send_pai" default="no"> <synopsis>Send the P-Asserted-Identity header</synopsis> </configOption> + <configOption name="send_ppi" default="no"> + <synopsis>Send the P-Preffered-Identity header</synopsis> + </configOption> <configOption name="send_rpid" default="no"> <synopsis>Send the Remote-Party-ID header</synopsis> </configOption> diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index bae02465da79052509ac52ff93c04806c4f18c04..e9bba44565f789d6c8398e142473b28fd9ee3440 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -47,6 +47,8 @@ struct sip_persistent_endpoint { struct ast_endpoint *endpoint; }; +#define DEFAULT_MAX_SESSIONS_PER_CLIENT 0 + /*! \brief Container for persistent endpoint information */ static struct ao2_container *persistent_endpoints; @@ -1625,6 +1627,15 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o } } + if (!ast_strlen_zero(endpoint->transport)) { + if (!ast_strlen_zero(endpoint->transport2)) { + ast_log_dt(LOG_EVENT_CODE_SV102); + }else if (strncmp(endpoint->transport, "transport-tls", 13)) { + ast_log_dt(LOG_EVENT_CODE_SV101); + } else { + ast_log_dt(LOG_EVENT_CODE_SV103); + } + } return 0; } @@ -2197,6 +2208,8 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_rport", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.force_rport)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rewrite_contact", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.rewrite_contact)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport2", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport2)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport_emergency", "transport-udp", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport_emergency)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "moh_suggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest)); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, prack_to_str, NULL, 0, 0); @@ -2223,6 +2236,7 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_inbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_inbound)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_outbound)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_pai", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_pai)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_ppi", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_ppi)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_rpid", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_rpid)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rpid_immediate", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.rpid_immediate)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion)); @@ -2341,6 +2355,8 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_mechanisms", "", security_mechanism_handler, security_mechanism_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_negotiation", "no", security_negotiation_handler, security_negotiation_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_aoc", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, send_aoc)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "Emergency", "false", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, Emergency)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_sessions", __stringify(DEFAULT_MAX_SESSIONS_PER_CLIENT), OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, max_sessions)); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c index 092e012a773b102bf4945f6ba2b6e6964e11b8fc..87549a6eb4ab9e4915b310f1d123668b6dc2f170 100644 --- a/res/res_pjsip/pjsip_distributor.c +++ b/res/res_pjsip/pjsip_distributor.c @@ -19,6 +19,7 @@ #include "asterisk.h" #include <pjsip.h> +#include <uci.h> #include "asterisk/res_pjsip.h" #include "asterisk/acl.h" @@ -27,6 +28,11 @@ #include "asterisk/threadpool.h" #include "asterisk/res_pjsip_cli.h" +#ifndef UCI_CONFIG_DIR +#define UCI_CONFIG_DIR "/etc/config/" +#endif +static const char uciStrConfig[] = "asterisk"; + static int distribute(void *data); static pj_bool_t distributor(pjsip_rx_data *rdata); static pj_status_t record_serializer(pjsip_tx_data *tdata); @@ -877,11 +883,89 @@ static int apply_endpoint_contact_acl(pjsip_rx_data *rdata, struct ast_sip_endpo return forbidden; } +static void disable_registrar_uci(char* registrar) +{ + struct uci_context *context = NULL; + struct uci_package *package = NULL; + struct uci_section *section = NULL; + struct uci_element *element = NULL; + struct uci_ptr ptr = {0}; + char option_enable[32]; + const char *ext_number = NULL, *ext_provider = NULL; + int res; + FILE *fp = NULL; + char file_name[32] = ""; + char package_path[32]; + + context = uci_alloc_context(); + if (!context) { + ast_log(LOG_ERROR, "failed to alloc context %s\n"); + return; + } + + uci_set_confdir(context, UCI_CONFIG_DIR); + snprintf(package_path, sizeof(package_path), "%s%s", UCI_CONFIG_DIR, uciStrConfig); + res = uci_load(context, package_path, &package); + if (res != 0 || !package) { + ast_log(LOG_ERROR, "failed to load load uci, conf dir: %s, conf file: %s\n", UCI_CONFIG_DIR, uciStrConfig); + } else { + uci_foreach_element(&package->sections, element) { + section = uci_to_section(element); + if (strcasecmp(section->type, "extension") == 0) { + ext_number = uci_lookup_option_string(context, section, "extension_number"); + if (ext_number && strcasecmp(ext_number, registrar) == 0) { + ext_provider = uci_lookup_option_string(context, section, "provider"); + if (ext_provider) { + snprintf(option_enable, sizeof(option_enable), "asterisk.%s.enable", ext_provider); + if (uci_lookup_ptr(context, &ptr, option_enable, true)) { + ast_log(LOG_ERROR, "Failed to find section %s\n", option_enable); + } else { + ptr.value = "2"; + if (uci_set(context, &ptr) != UCI_OK) { + ast_log(LOG_ERROR, "Failed to uci_set, package: %s\n", ptr.package); + } else { + if (uci_commit(context, &ptr.p, true) != UCI_OK) { + ast_log(LOG_ERROR, "Failed to uci_commit\n"); + } else { + snprintf(file_name, sizeof(file_name), "/tmp/registrar.%s", ext_provider); + break; + } + } + } + } + } + } + } + } + // Free the memory + if (package) + uci_unload(context, package); + if (context) + uci_free_context(context); + + // create /tmp/registrar.ext_provider file and reload asterisk + // reload will trigger config_asterisk.sh re-run + if (file_name[0] != '\0') { + fp = fopen(file_name, "w"); + if (fp) { + fclose(fp); + } + system("systemctl reload asterisk"); + } +} + static pj_bool_t authenticate(pjsip_rx_data *rdata) { + pjsip_sip_uri *client_uri = NULL; + pjsip_contact_hdr *contact_hdr = NULL; RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; + contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); + if(contact_hdr){ + client_uri = pjsip_uri_get_uri(contact_hdr->uri); + } + ast_assert(endpoint != NULL); if (is_ack) { @@ -891,6 +975,7 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata) if (ast_sip_requires_authentication(endpoint, rdata)) { pjsip_tx_data *tdata; struct unidentified_request *unid; + char client[24] = {0}; pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata); switch (ast_sip_check_authentication(endpoint, rdata, tdata)) { @@ -913,6 +998,13 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata) case AST_SIP_AUTHENTICATION_FAILED: log_failed_request(rdata, "Failed to authenticate", 0, 0); ast_sip_report_auth_failed_challenge_response(endpoint, rdata); + struct pjsip_status_line status = tdata->msg->line.status; + /* IPX101 and disable registrar */ + ast_log_dt(LOG_EVENT_CODE_IPX101, (int) client_uri->user.slen, client_uri->user.ptr, status.code, (int) client_uri->host.slen, client_uri->host.ptr); + ast_copy_pj_str(client, &client_uri->user, sizeof(client)); + if (client[0] != '\0') { + disable_registrar_uci(client); + } if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } diff --git a/res/res_pjsip/pjsip_message_ip_updater.c b/res/res_pjsip/pjsip_message_ip_updater.c index 4a6bd0ea9be7a8373a73a387527715b6bc1b46d2..79b719afd204ab0aec4fc788f56a1fdcd59da2a1 100644 --- a/res/res_pjsip/pjsip_message_ip_updater.c +++ b/res/res_pjsip/pjsip_message_ip_updater.c @@ -327,6 +327,16 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata) } } + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + if(!pj_strcmp2(&cseq->method.name, "REGISTER")) { + pjsip_sip_uri *uri = (pjsip_sip_uri*) tdata->msg->line.req.uri; + uri->port = 0; // remove the un-wanted port to the REQUEST URI that been add from registration_client_send due to #15901 + pjsip_tx_data_invalidate_msg(tdata); + } + } + + /* If there's no body in the tdata we can just return here. */ if (!tdata->msg->body) { return PJ_SUCCESS; diff --git a/res/res_pjsip/pjsip_resolver.c b/res/res_pjsip/pjsip_resolver.c index 5ea8d0dc7903c1505fc9c3c3fec398050745b31e..0a98b0435781378020f40951f2f6c128653de4b0 100644 --- a/res/res_pjsip/pjsip_resolver.c +++ b/res/res_pjsip/pjsip_resolver.c @@ -24,6 +24,7 @@ #include <arpa/nameser.h> #include "asterisk/astobj2.h" +#include "asterisk/cli.h" #include "asterisk/dns_core.h" #include "asterisk/dns_query_set.h" #include "asterisk/dns_srv.h" @@ -32,6 +33,7 @@ #include "include/res_pjsip_private.h" #include "asterisk/taskprocessor.h" #include "asterisk/threadpool.h" +#include "asterisk/dns_internal.h" #ifdef HAVE_PJSIP_EXTERNAL_RESOLVER @@ -41,48 +43,194 @@ struct sip_target { pjsip_transport_type_e transport; /*! \brief The port */ int port; + int priority; // SRV priority + int weight; // SRV weight }; /*! \brief The vector used for current targets */ AST_VECTOR(targets, struct sip_target); -/*! \brief Structure which contains latest resolved server addresses for tracking */ -struct host_track_entry { - struct { - char *host; - int port; - unsigned flag; - pjsip_transport_type_e type; - } target; +/*! \brief Structure which keeps track of resolution */ +struct sip_resolve { + pjsip_host_info target; + /*! \brief Addresses currently being resolved, indexed based on index of queries in query set */ + struct targets resolving; + /*! \brief Active queries */ + struct ast_dns_query_set *queries; + /*! \brief Serializer to run async callback into pjlib. */ + struct ast_taskprocessor *serializer; + /*! \brief Callback to invoke upon completion */ + pjsip_resolver_callback *callback; + struct host_cache_entry *host_cache; + AST_LIST_ENTRY(sip_resolve) list; + /*! \brief User provided data */ + void *token; +}; + +/*! \brief Our own defined transports, reduces the size of sip_available_transports */ +enum sip_resolver_transport { + SIP_RESOLVER_TRANSPORT_UDP, + SIP_RESOLVER_TRANSPORT_TCP, + SIP_RESOLVER_TRANSPORT_TLS, + SIP_RESOLVER_TRANSPORT_UDP6, + SIP_RESOLVER_TRANSPORT_TCP6, + SIP_RESOLVER_TRANSPORT_TLS6, +}; + +struct dns_cache_record { + char *name; + struct sip_target target; + time_t expiry; + struct ast_dns_record *dns_record; + AST_LIST_ENTRY(dns_cache_record) list; +}; + +struct host_cache_entry { + pjsip_host_info target; + int querying; pjsip_server_addresses addresses; unsigned cur_addr; - AST_LIST_ENTRY(host_track_entry) next; + /*! \brief list of cached DNS records */ + AST_LIST_HEAD_NOLOCK(naptr_records, dns_cache_record) naptr_records; + AST_LIST_HEAD_NOLOCK(srv_records, dns_cache_record) srv_records; + AST_LIST_HEAD_NOLOCK(a_records, dns_cache_record) a_records; + AST_LIST_HEAD_NOLOCK(resolves, sip_resolve) resolves; + AST_LIST_ENTRY(host_cache_entry) list; }; -/*! \brief the list of all tracked hosts */ -AST_LIST_HEAD(, host_track_entry) sip_resolve_track; +AST_LIST_HEAD(, host_cache_entry) dns_host_cache; -/*! \brief compare the target host info with track record */ -static int host_track_target_cmp(pjsip_host_info *host, struct host_track_entry *hostc) +/*! \brief compare the target host info with cache entry */ +static int host_target_cmp(const pjsip_host_info *host1, const struct pjsip_host_info *host2) { - if(!host || !hostc) + if (!host1 || !host2) return -1; - if (host->type != hostc->target.type) + if (host1->type != host2->type) return -1; - if (host->flag != hostc->target.flag) + if (host1->flag != host2->flag) return -1; - if (host->addr.port != hostc->target.port) + if (host1->addr.port != host2->addr.port) return -1; - if (pj_strcmp2(&host->addr.host, hostc->target.host)) + if (pj_strcmp(&host1->addr.host, &host2->addr.host)) return -1; return 0; } +static struct host_cache_entry * get_host_cache_entry(const pjsip_host_info *target) +{ + struct host_cache_entry *host = NULL; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) { + if (!host_target_cmp(target, &host->target)) { + return host; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + return NULL; +} + +/* the host cache is valid when none of cached DNS records is expired */ +static int is_host_cache_valid(struct host_cache_entry *host) +{ + struct dns_cache_record *record; + time_t now = time(NULL); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) { + if (record->expiry <= now) { + ast_debug(5, "NAPTR record '%s' (%s) is not valid after %s\n", record->name, + ast_dns_naptr_get_replacement(record->dns_record), ctime(&record->expiry)); + return 0; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) { + if (record->expiry <= now) { + ast_debug(5, "SRV record '%s' (%s) is not valid after %s\n", record->name, + ast_dns_srv_get_host(record->dns_record), ctime(&record->expiry)); + return 0; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) { + if (record->expiry <= now) { + ast_debug(5, "A/AAAA record '%s' (0x%x) is not valid after %s\n", record->name, + *(unsigned *) ast_dns_record_get_data(record->dns_record), ctime(&record->expiry)); + return 0; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + ast_debug(3, "host cache '%s:%s' is valid\n", + host->target.addr.host.ptr, + pjsip_transport_get_type_desc(host->target.type)); + + return 1; +} + +/* the host cache is expired when all cached DNS records are expired */ +static int is_host_cache_expired(struct host_cache_entry *host) +{ + struct dns_cache_record *record; + time_t now = time(NULL); + int expired = 1; + + if (host->querying) { + ast_debug(5, "host cache '%s' is updating\n", host->target.addr.host.ptr); + return 0; + } + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) { + if (record->expiry > now) { + ast_debug(5, "NAPTR record '%s' (%s) is not expired before %s\n", record->name, + ast_dns_naptr_get_replacement(record->dns_record), ctime(&record->expiry)); + expired = 0; + } else { + ast_debug(5, "NAPTR record '%s' (%s) is expired at %s\n", record->name, + ast_dns_naptr_get_replacement(record->dns_record), ctime(&record->expiry)); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) { + if (record->expiry > now) { + ast_debug(5, "SRV record '%s' (%s) is not expired before %s\n", record->name, + ast_dns_srv_get_host(record->dns_record), ctime(&record->expiry)); + expired = 0; + } else { + ast_debug(5, "SRV record '%s' (%s) is expired at %s\n", record->name, + ast_dns_srv_get_host(record->dns_record), ctime(&record->expiry)); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) { + if (record->expiry > now) { + ast_debug(5, "A/AAAA record '%s' (0x%x) is not expired before %s\n", record->name, + *(unsigned *) ast_dns_record_get_data(record->dns_record), ctime(&record->expiry)); + expired = 0; + } else { + ast_debug(5, "A/AAAA record '%s' (0x%x) is expired at %s\n", record->name, + *(unsigned *) ast_dns_record_get_data(record->dns_record), ctime(&record->expiry)); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + ast_debug(3, "host cache '%s:%s' is%s expired\n", + host->target.addr.host.ptr, + pjsip_transport_get_type_desc(host->target.type), + !expired ? " not" : ""); + + return expired; +} + /*! \brief compare 2 entries in 2 pjsip_server_addresses */ static int pjsip_server_addresses_entry_cmp( pjsip_server_addresses *addr1, int i1, @@ -130,154 +278,180 @@ static int pjsip_server_addresses_cmp(pjsip_server_addresses *addr1, pjsip_serve return 0; } -/*! \brief update the tracked pjsip_server_addresses with new resolve - * Note: The tracked and new resolved server addresses must same, but maybe in different order. - * The order of resolved addresses may be round-shifted, to make the current using server address - * always be the first entry. - */ -static void pjsip_server_addresses_track_update(struct host_track_entry *host, pjsip_server_addresses *addrs) +static pjsip_server_addresses *get_cached_host_addrs(struct host_cache_entry *host) { - int c, i; - char buffer[512]; + if (host->querying || !is_host_cache_valid(host)) + return NULL; + + // current addr must be the head of addresses + if (host->cur_addr != 0) { + pjsip_server_addresses addrs; + int c = host->cur_addr; + int i; + + pj_memcpy(&addrs, &host->addresses, sizeof(addrs)); + for (i = 0; i < addrs.count; i++) { + pj_memcpy(&host->addresses.entry[i], &addrs.entry[c], sizeof(addrs.entry[0])); + if (++c >= addrs.count) + c = 0; + } - // Get the index of current server address in the new address table - for (c = 0; c < addrs->count; c++) { - if (!pjsip_server_addresses_entry_cmp(&host->addresses, host->cur_addr, addrs, c)) - break; + host->cur_addr = 0; } - // The current server address is the first entry in the new table - if (c == 0) { - pj_memcpy(&host->addresses, addrs, sizeof(*addrs)); - host->cur_addr = 0; - pj_sockaddr_print(&host->addresses.entry[0].addr, buffer, sizeof(buffer), 3); + return &host->addresses; +} - for (i = 0; i < addrs->count; i++) { - pj_sockaddr_print(&host->addresses.entry[i].addr, buffer, sizeof(buffer), 3); - ast_debug(5, "host track '%s': address[%d] = '%s'\n", host->target.host, i, buffer); - } - return; +static void host_cache_record_add(struct host_cache_entry *host, + struct ast_dns_query *query, + struct ast_dns_record *dns_record, + struct sip_target *target) +{ + struct dns_cache_record *record; + + record = ast_calloc(1, sizeof(*record)); + + record->expiry = time(NULL) + ast_dns_record_get_ttl(dns_record); + record->name = ast_strdup(ast_dns_query_get_name(query)); + record->dns_record = dns_record; + + if (target) { + record->target.transport = target->transport; + record->target.port = target->port; + record->target.priority = target->priority; + record->target.weight = target->weight; } - // Re-order the new table, to make sure the current server address is the first entry - for (i = 0; i < addrs->count; i++) { - pj_memcpy(&host->addresses.entry[i], &addrs->entry[c], sizeof(addrs->entry[0])); - if (++c >= addrs->count) - c = 0; + switch(ast_dns_record_get_rr_type(dns_record)) { + case T_A: + case T_AAAA: + AST_LIST_INSERT_TAIL(&host->a_records, record, list); + ast_debug(5, "A record %s is add to host cache '%s'\n", record->name, host->target.addr.host.ptr); + break; + case T_SRV: + ast_debug(5, "SRV record %s is add to host cache '%s'\n", record->name, host->target.addr.host.ptr); + AST_LIST_INSERT_TAIL(&host->srv_records, record, list); + break; + case T_NAPTR: + ast_debug(5, "NAPTR record %s is add to host cache '%s'\n", record->name, host->target.addr.host.ptr); + AST_LIST_INSERT_TAIL(&host->naptr_records, record, list); + break; + } +} + +static struct host_cache_entry * host_cache_add(const pjsip_host_info *target) +{ + struct host_cache_entry *host; + char buf[NI_MAXHOST]; + + host = ast_calloc(1, sizeof(*host)); + if (host) { + ast_copy_pj_str(buf, &target->addr.host, sizeof(buf)); + + host->target.addr.host.ptr = ast_strdup(buf); + host->target.addr.host.slen = strlen(buf); + host->target.addr.port = target->addr.port; + host->target.type = target->type; + host->target.flag = target->flag; - pj_sockaddr_print(&host->addresses.entry[i].addr, buffer, sizeof(buffer), 3); - ast_debug(5, "host track '%s': address[%d] = '%s'\n", host->target.host, i, buffer); + AST_LIST_HEAD_INIT_NOLOCK(&host->a_records); + AST_LIST_HEAD_INIT_NOLOCK(&host->srv_records); + AST_LIST_HEAD_INIT_NOLOCK(&host->naptr_records); + AST_LIST_HEAD_INIT_NOLOCK(&host->resolves); + + AST_LIST_INSERT_HEAD(&dns_host_cache, host, list); + + ast_debug(3, "Add target '%s:%s' to host cache\n", + host->target.addr.host.ptr, + pjsip_transport_get_type_desc(target->type)); } - pj_memcpy(addrs, &host->addresses, sizeof(*addrs)); - host->cur_addr = 0; + + return host; } -/*! \brief make or update track record for the resolve target */ -static void sip_resolve_target_track(pjsip_host_info *target, pjsip_server_addresses *addresses) +static void host_cache_add_resolve(struct host_cache_entry *host, struct sip_resolve *resolve) { - struct host_track_entry *host = NULL; - struct host_track_entry *track_host = NULL; + resolve->host_cache = host; + AST_LIST_INSERT_TAIL(&host->resolves, resolve, list); +} - if (!target || !addresses) - return; +static void host_cache_free(struct host_cache_entry *host) +{ + struct dns_cache_record *record; - AST_LIST_LOCK(&sip_resolve_track); + ast_debug(3, "Remove target '%s:%s' from host cache\n", + host->target.addr.host.ptr, + pjsip_transport_get_type_desc(host->target.type)); - AST_LIST_TRAVERSE_SAFE_BEGIN(&sip_resolve_track, host, next) { - if (!host_track_target_cmp(target, host)) { - ast_debug(5, "host track '%s': record found\n", host->target.host); + ast_free(host->target.addr.host.ptr); - // Remove the track entry if the addresses are different - if (pjsip_server_addresses_cmp(&host->addresses, addresses)) { - ast_debug(5, "host track '%s': different in the new resolve. record removed\n", host->target.host); - // Free the track entry - AST_LIST_REMOVE_CURRENT(next); - ast_free(host->target.host); - ast_free(host); - break; - } + while ((record = AST_LIST_REMOVE_HEAD(&host->a_records, list))) { + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); + } - // The new addresses are same as tracked, but maybe in different order - pjsip_server_addresses_track_update(host, addresses); + while ((record = AST_LIST_REMOVE_HEAD(&host->srv_records, list))) { + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); + } - track_host = host; - break; - } + while ((record = AST_LIST_REMOVE_HEAD(&host->naptr_records, list))) { + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); } - AST_LIST_TRAVERSE_SAFE_END; +} - // Make a new track entry - if (!track_host) { - track_host = ast_calloc(1, sizeof(*track_host)); - if (track_host) { - track_host->target.host = ast_strndup(pj_strbuf(&target->addr.host), pj_strlen(&target->addr.host)); - track_host->target.port = target->addr.port; - track_host->target.type = target->type; - track_host->target.flag = target->flag; +// clean up all expired cache entries +static void host_cache_cleanup(void) +{ + struct host_cache_entry *host = NULL; - pj_memcpy(&track_host->addresses, addresses, sizeof(*addresses)); - track_host->cur_addr = 0; + AST_LIST_LOCK(&dns_host_cache); - AST_LIST_INSERT_HEAD(&sip_resolve_track, track_host, next); - ast_debug(5, "host track '%s': record added\n", track_host->target.host); + AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) { + if (!host->querying && is_host_cache_expired(host)) { + AST_LIST_REMOVE_CURRENT(list); + host_cache_free(host); } } + AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&sip_resolve_track); + AST_LIST_UNLOCK(&dns_host_cache); } /*! \brief update current used address in the host track record */ void ast_sip_resolve_track_cur_addr(pjsip_tx_data *tdata) { - struct host_track_entry *host = NULL; + struct host_cache_entry *host = NULL; + pjsip_transport_type_e transport_type; if (!tdata || !pj_strlen(&tdata->dest_info.name)) return; - AST_LIST_LOCK(&sip_resolve_track); + transport_type = pjsip_transport_get_type_from_flag(tdata->tp_info.transport->flag); + + AST_LIST_LOCK(&dns_host_cache); - AST_LIST_TRAVERSE(&sip_resolve_track, host, next) { - if (!pj_strcmp2(&tdata->dest_info.name, host->target.host) && + AST_LIST_TRAVERSE(&dns_host_cache, host, list) { + if (!pj_strcmp(&tdata->dest_info.name, &host->target.addr.host) && + host->target.type == transport_type && !pjsip_server_addresses_cmp(&host->addresses, &tdata->dest_info.addr)) { if(host->cur_addr != tdata->dest_info.cur_addr) { host->cur_addr = tdata->dest_info.cur_addr; - ast_debug(5, "host track '%s': current addr changed to '%d'.\n", host->target.host, host->cur_addr); + ast_debug(5, "host track %s: current addr changed to '%d'.\n", + host->target.addr.host.ptr, host->cur_addr); } break; } } - AST_LIST_UNLOCK(&sip_resolve_track); + AST_LIST_UNLOCK(&dns_host_cache); } -/*! \brief Structure which keeps track of resolution */ -struct sip_resolve { - pjsip_host_info target; - /*! \brief Addresses currently being resolved, indexed based on index of queries in query set */ - struct targets resolving; - /*! \brief Active queries */ - struct ast_dns_query_set *queries; - /*! \brief Current viable server addresses */ - pjsip_server_addresses addresses; - /*! \brief Serializer to run async callback into pjlib. */ - struct ast_taskprocessor *serializer; - /*! \brief Callback to invoke upon completion */ - pjsip_resolver_callback *callback; - /*! \brief User provided data */ - void *token; -}; - -/*! \brief Our own defined transports, reduces the size of sip_available_transports */ -enum sip_resolver_transport { - SIP_RESOLVER_TRANSPORT_UDP, - SIP_RESOLVER_TRANSPORT_TCP, - SIP_RESOLVER_TRANSPORT_TLS, - SIP_RESOLVER_TRANSPORT_UDP6, - SIP_RESOLVER_TRANSPORT_TCP6, - SIP_RESOLVER_TRANSPORT_TLS6, -}; - /*! \brief Available transports on the system */ static int sip_available_transports[] = { /* This is a list of transports with whether they are available as a valid transport @@ -355,11 +529,20 @@ static int sip_transport_is_available(enum pjsip_transport_type_e transport) * \retval 0 success * \retval -1 failure */ -static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr_type, int rr_class, pjsip_transport_type_e transport, int port) +static int sip_resolve_add(struct sip_resolve *resolve, + const char *name, + int rr_type, + int rr_class, + pjsip_transport_type_e transport, + int port, + int priority, + int weight) { struct sip_target target = { .transport = transport, .port = port, + .priority = priority, + .weight = weight, }; if (!resolve->queries) { @@ -383,6 +566,128 @@ static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr return ast_dns_query_set_add(resolve->queries, name, rr_type, rr_class); } +static void host_cache_sort_a_records(struct host_cache_entry *host) +{ + struct a_records newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + struct dns_cache_record *current; + + if (DEBUG_ATLEAST(8)) { + AST_LIST_TRAVERSE(&host->a_records, current, list) { + ast_debug(8, "'%s': addr=0x%x, pri=%d, weight=%d\n", current->name, + *(unsigned *) ast_dns_record_get_data(current->dns_record), + current->target.priority, current->target.weight); + } + } + + while (AST_LIST_FIRST(&host->a_records)) { + struct a_records temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + int cur_pri = ((struct dns_cache_record *)(AST_LIST_FIRST(&host->a_records)))->target.priority; + + // Get the lowest priority + if (cur_pri) { + AST_LIST_TRAVERSE(&host->a_records, current, list) { + if (current->target.priority < cur_pri) + cur_pri = current->target.priority; + } + } + + /* Find all records which match this priority */ + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, current, list) { + if (current->target.priority != cur_pri) + continue; + + AST_LIST_REMOVE_CURRENT(list); + + /* Records with a weight of zero must always be at the head */ + if (current->target.weight == 0) { + AST_LIST_INSERT_HEAD(&temp_list, current, list); + } else { + AST_LIST_INSERT_TAIL(&temp_list, current, list); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + while (AST_LIST_FIRST(&temp_list)) { + int cur_weight = ((struct dns_cache_record *)(AST_LIST_FIRST(&temp_list)))->target.weight; + + AST_LIST_TRAVERSE(&temp_list, current, list) { + if (current->target.weight > cur_weight) + cur_weight = current->target.weight; + } + + if (cur_weight == 0) { + AST_LIST_APPEND_LIST(&newlist, &temp_list, list); + break; + } + + AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) { + if (current->target.weight == cur_weight) + AST_LIST_MOVE_CURRENT(&newlist, list); + } + AST_LIST_TRAVERSE_SAFE_END; + } + } + + AST_LIST_APPEND_LIST(&host->a_records, &newlist, list); + + if (DEBUG_ATLEAST(8)) { + AST_LIST_TRAVERSE(&host->a_records, current, list) { + ast_debug(8, "'%s': addr=0x%x, pri=%d, weight=%d\n", current->name, + *(unsigned *) ast_dns_record_get_data(current->dns_record), + current->target.priority, current->target.weight); + } + } +} + +static void host_cache_update_addresses(struct host_cache_entry *host) +{ + struct dns_cache_record *current; + int index = 0; + + ast_debug(2, "Updating addresses in host cache for '%s:%s'\n", + host->target.addr.host.ptr, + pjsip_transport_get_type_desc(host->target.type)); + + host_cache_sort_a_records(host); + + AST_LIST_TRAVERSE(&host->a_records, current, list) { + /* PJSIP has a fixed maximum number of addresses that can exist, so limit ourselves to that */ + if (index == PJSIP_MAX_RESOLVED_ADDRESSES) + break; + + host->addresses.entry[index].type = current->target.transport; + host->addresses.entry[index].priority = current->target.priority; + host->addresses.entry[index].weight = current->target.weight; + + if (ast_dns_record_get_rr_type(current->dns_record) == T_A) { + host->addresses.entry[index].addr_len = sizeof(pj_sockaddr_in); + pj_sockaddr_init(pj_AF_INET(), &host->addresses.entry[index].addr, NULL, current->target.port); + host->addresses.entry[index].addr.ipv4.sin_addr = *(pj_in_addr *) ast_dns_record_get_data(current->dns_record); + } else { + host->addresses.entry[index].addr_len = sizeof(pj_sockaddr_in6); + pj_sockaddr_init(pj_AF_INET6(), &host->addresses.entry[index].addr, NULL, current->target.port); + pj_memcpy(&host->addresses.entry[index].addr.ipv6.sin6_addr, ast_dns_record_get_data(current->dns_record), + ast_dns_record_get_data_size(current->dns_record)); + } + + if (DEBUG_ATLEAST(2)) { + char addr[PJ_INET6_ADDRSTRLEN + 10]; + + pj_sockaddr_print(&host->addresses.entry[index].addr, addr, sizeof(addr), 3); + ast_log(LOG_DEBUG, "[%s] Address[%d]: %s, priority: %d, weight: %d, transport: %s\n", + host->target.addr.host.ptr, index, addr, + host->addresses.entry[index].priority, + host->addresses.entry[index].weight, + pjsip_transport_get_type_desc(host->addresses.entry[index].type)); + } + + index++; + } + + host->addresses.count = index; + host->cur_addr = 0; +} + /*! * \internal * \brief Task used to invoke the user specific callback @@ -394,26 +699,32 @@ static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr static int sip_resolve_invoke_user_callback(void *data) { struct sip_resolve *resolve = data; + struct host_cache_entry *host = resolve->host_cache; + char buf[NI_MAXHOST]; - if (DEBUG_ATLEAST(2)) { - /* This includes space for the IP address, [, ], :, and the port */ - char addr[PJ_INET6_ADDRSTRLEN + 10]; - int idx; + ast_copy_pj_str(buf, &resolve->target.addr.host, sizeof(buf)); - for (idx = 0; idx < resolve->addresses.count; ++idx) { - pj_sockaddr_print(&resolve->addresses.entry[idx].addr, addr, sizeof(addr), 3); - ast_log(LOG_DEBUG, "[%p] Address '%d' is %s with transport '%s'\n", - resolve, idx, addr, - pjsip_transport_get_type_desc(resolve->addresses.entry[idx].type)); - } - } + ast_debug(2, "[%p] Invoking user callback %s\n", resolve, buf); - sip_resolve_target_track(&resolve->target, &resolve->addresses); + AST_LIST_LOCK(&dns_host_cache); - ast_debug(2, "[%p] Invoking user callback with '%d' addresses\n", resolve, resolve->addresses.count); - resolve->callback(resolve->addresses.count ? PJ_SUCCESS : PJLIB_UTIL_EDNSNOANSWERREC, resolve->token, &resolve->addresses); + host->querying = 0; - ao2_ref(resolve, -1); + host_cache_update_addresses(host); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->resolves, resolve, list) { + AST_LIST_REMOVE_CURRENT(list); + + ast_debug(2, "[%p] Invoking user callback with '%d' addresses\n", resolve, host->addresses.count); + /* Always return PJ_SUCCESS regardless of host->addresses.count. + Let pjproject handle address count validation and related logic. */ + resolve->callback(PJ_SUCCESS, resolve->token, &host->addresses); + + ao2_ref(resolve, -1); + } + AST_LIST_TRAVERSE_SAFE_END; + + AST_LIST_UNLOCK(&dns_host_cache); return 0; } @@ -459,8 +770,7 @@ static int sip_resolve_handle_naptr(struct sip_resolve *resolve, const struct as return -1; } - return sip_resolve_add(resolve, ast_dns_naptr_get_replacement(record), T_SRV, C_IN, - transport, 0); + return sip_resolve_add(resolve, ast_dns_naptr_get_replacement(record), T_SRV, C_IN, transport, 0, 0, 0); } /*! @@ -497,7 +807,7 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set) struct ast_dns_query *query = ast_dns_query_set_get(queries, idx); struct ast_dns_result *result = ast_dns_query_get_result(query); struct sip_target *target; - const struct ast_dns_record *record; + struct ast_dns_record *record; if (!result) { ast_debug(2, "[%p] No result information for target '%s' of type '%d'\n", resolve, @@ -506,7 +816,8 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set) } target = AST_VECTOR_GET_ADDR(&resolving, idx); - for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) { + + AST_LIST_TRAVERSE_SAFE_BEGIN(&result->records, record, list) { if (ast_dns_record_get_rr_type(record) == T_A || ast_dns_record_get_rr_type(record) == T_AAAA) { @@ -518,30 +829,10 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set) continue; } - /* PJSIP has a fixed maximum number of addresses that can exist, so limit ourselves to that */ - if (address_count == PJSIP_MAX_RESOLVED_ADDRESSES) { - break; - } - - resolve->addresses.entry[address_count].type = target->transport; - - /* Populate address information for the new address entry */ - if (ast_dns_record_get_rr_type(record) == T_A) { - ast_debug(2, "[%p] A record received on target '%s'\n", resolve, ast_dns_query_get_name(query)); - resolve->addresses.entry[address_count].addr_len = sizeof(pj_sockaddr_in); - pj_sockaddr_init(pj_AF_INET(), &resolve->addresses.entry[address_count].addr, NULL, - target->port); - resolve->addresses.entry[address_count].addr.ipv4.sin_addr = *(pj_in_addr *) ast_dns_record_get_data(record); - } else { - ast_debug(2, "[%p] AAAA record received on target '%s'\n", resolve, ast_dns_query_get_name(query)); - resolve->addresses.entry[address_count].addr_len = sizeof(pj_sockaddr_in6); - pj_sockaddr_init(pj_AF_INET6(), &resolve->addresses.entry[address_count].addr, NULL, - target->port); - pj_memcpy(&resolve->addresses.entry[address_count].addr.ipv6.sin6_addr, ast_dns_record_get_data(record), - ast_dns_record_get_data_size(record)); - } - address_count++; + + AST_LIST_REMOVE_CURRENT(list); + host_cache_record_add(resolve->host_cache, query, record, target); } else if (ast_dns_record_get_rr_type(record) == T_SRV) { if (have_naptr) { ast_debug(2, "[%p] SRV record being skipped on target '%s' because NAPTR record exists\n", @@ -556,23 +847,34 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set) if ((target->transport & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(target->transport)) { sip_resolve_add(resolve, ast_dns_srv_get_host(record), T_AAAA, C_IN, target->transport, - ast_dns_srv_get_port(record)); + ast_dns_srv_get_port(record), + ast_dns_srv_get_priority(record), + ast_dns_srv_get_weight(record)); have_srv = 1; } else if (!(target->transport & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(target->transport | PJSIP_TRANSPORT_IPV6)) { sip_resolve_add(resolve, ast_dns_srv_get_host(record), T_AAAA, C_IN, target->transport | PJSIP_TRANSPORT_IPV6, - ast_dns_srv_get_port(record)); + ast_dns_srv_get_port(record), + ast_dns_srv_get_priority(record), + ast_dns_srv_get_weight(record)); have_srv = 1; } if (!(target->transport & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(target->transport)) { sip_resolve_add(resolve, ast_dns_srv_get_host(record), T_A, C_IN, target->transport, - ast_dns_srv_get_port(record)); + ast_dns_srv_get_port(record), + ast_dns_srv_get_priority(record), + ast_dns_srv_get_weight(record)); have_srv = 1; } + + AST_LIST_REMOVE_CURRENT(list); + host_cache_record_add(resolve->host_cache, query, record, NULL); } else if (ast_dns_record_get_rr_type(record) == T_NAPTR) { - int added = -1; + int added_UDP = -1; + int added_TCP = -1; + int added_TLS = -1; ast_debug(2, "[%p] NAPTR record received on target '%s'\n", resolve, ast_dns_query_get_name(query)); @@ -582,35 +884,52 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set) continue; } - if (target->transport == PJSIP_TRANSPORT_UNSPECIFIED || target->transport == PJSIP_TRANSPORT_UDP || - target->transport == PJSIP_TRANSPORT_UDP6) { - added = sip_resolve_handle_naptr(resolve, record, "sip+d2u", - target->transport == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : target->transport); - } - if (target->transport == PJSIP_TRANSPORT_UNSPECIFIED || target->transport == PJSIP_TRANSPORT_TCP || - target->transport == PJSIP_TRANSPORT_TCP6) { - added = sip_resolve_handle_naptr(resolve, record, "sip+d2t", - target->transport == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TCP : target->transport); - } - if (target->transport == PJSIP_TRANSPORT_UNSPECIFIED || target->transport == PJSIP_TRANSPORT_TLS || - target->transport == PJSIP_TRANSPORT_TLS6) { - added = sip_resolve_handle_naptr(resolve, record, "sips+d2t", - target->transport == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TLS : target->transport); + switch(target->transport) { + case PJSIP_TRANSPORT_UNSPECIFIED: + sip_resolve_handle_naptr(resolve, record, "sip+d2u", PJSIP_TRANSPORT_UDP); + sip_resolve_handle_naptr(resolve, record, "sip+d2u", PJSIP_TRANSPORT_UDP6); + + sip_resolve_handle_naptr(resolve, record, "sip+d2t", PJSIP_TRANSPORT_TCP); + sip_resolve_handle_naptr(resolve, record, "sip+d2t", PJSIP_TRANSPORT_TCP6); + + sip_resolve_handle_naptr(resolve, record, "sips+d2t", PJSIP_TRANSPORT_TLS); + sip_resolve_handle_naptr(resolve, record, "sips+d2t", PJSIP_TRANSPORT_TLS6); + break; + + case PJSIP_TRANSPORT_UDP: + case PJSIP_TRANSPORT_UDP6: + added_UDP = sip_resolve_handle_naptr(resolve, record, "sip+d2u", target->transport); + break; + + case PJSIP_TRANSPORT_TCP: + case PJSIP_TRANSPORT_TCP6: + added_TCP = sip_resolve_handle_naptr(resolve, record, "sip+d2t", target->transport); + break; + + case PJSIP_TRANSPORT_TLS: + case PJSIP_TRANSPORT_TLS6: + added_TLS = sip_resolve_handle_naptr(resolve, record, "sips+d2t", target->transport); + break; + + default: + break; } /* If this record was successfully handled then we need to limit ourselves to this order */ - if (!added) { + if (!added_UDP || !added_TCP || !added_TLS) { have_naptr = 1; strict_order = 1; order = ast_dns_naptr_get_order(record); + + host_cache_record_add(resolve->host_cache, query, record, NULL); } + + AST_LIST_REMOVE_CURRENT(list); } + AST_LIST_TRAVERSE_SAFE_END; } } - /* Update the server addresses count, this is not limited as it can never exceed the max allowed */ - resolve->addresses.count = address_count; - /* Free the vector we stole as we are responsible for it */ AST_VECTOR_FREE(&resolving); @@ -622,7 +941,7 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set) return; } - ast_debug(2, "[%p] Resolution completed - %d viable targets\n", resolve, resolve->addresses.count); + ast_debug(2, "[%p] Resolution completed - %d viable targets\n", resolve, address_count); /* Push a task to invoke the callback, we do this so it is guaranteed to run in a PJSIP thread */ ao2_ref(resolve, +1); @@ -659,6 +978,190 @@ static int sip_resolve_get_ip_addr_ver(const pj_str_t *host) return 0; } +static void dns_a_record_free(struct host_cache_entry *host, const char *host_name) +{ + struct dns_cache_record *record; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) { + if (!strcmp(host_name, record->name)) { + AST_LIST_REMOVE_CURRENT(list); + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + return; +} + +static void dns_srv_record_free(struct host_cache_entry *host, const char *srv_name) +{ + struct dns_cache_record *record; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) { + if (!strcmp(srv_name, record->name)) { + AST_LIST_REMOVE_CURRENT(list); + + dns_a_record_free(host, ast_dns_srv_get_host(record->dns_record)); + + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + return; +} + +static int dns_a_records_update(struct sip_resolve *resolve, struct host_cache_entry *host) +{ + struct dns_cache_record *record; + int update = 0; + time_t now = time(NULL); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) { + if (record->expiry <= now) { + ast_debug(5, "%s: Updating A/AAAA record '%s'(0x%x,TTL=%d)\n", host->target.addr.host.ptr, + record->name, + *(unsigned *) ast_dns_record_get_data(record->dns_record), + ast_dns_record_get_ttl(record->dns_record)); + + sip_resolve_add(resolve, record->name, ast_dns_record_get_rr_type(record->dns_record), + C_IN, record->target.transport, record->target.port, + record->target.priority, record->target.weight); + + AST_LIST_REMOVE_CURRENT(list); + + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); + + update = 1; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + return update; +} + +static int dns_srv_records_update(struct sip_resolve *resolve, struct host_cache_entry *host) +{ + struct dns_cache_record *record; + int update = 0; + char *srv_name = NULL; + time_t now = time(NULL); + + do { + // Find the name of expired SRV + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) { + if (record->expiry <= now) { + ast_debug(5, "%s: Updating SRV record '%s'(%s, TTL=%d)\n", host->target.addr.host.ptr, + record->name, + ast_dns_srv_get_host(record->dns_record), + ast_dns_record_get_ttl(record->dns_record)); + + sip_resolve_add(resolve, record->name, T_SRV, C_IN, host->target.type, 0, 0, 0); + srv_name = ast_strdup(record->name); + + update = 1; + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (!srv_name) + break; + + // One SRV may have more than one records, so clean them all. + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) { + if (!strcmp(record->name, srv_name)) { + ast_debug(5, "%s: Cleaning SRV record '%s'(%s, TTL=%d)\n", host->target.addr.host.ptr, + record->name, + ast_dns_srv_get_host(record->dns_record), + ast_dns_record_get_ttl(record->dns_record)); + + AST_LIST_REMOVE_CURRENT(list); + + dns_a_record_free(host, ast_dns_srv_get_host(record->dns_record)); + + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + ast_free(srv_name); + srv_name = NULL; + } while (true); + + return update; +} + +static int dns_naptr_records_update(struct sip_resolve *resolve, struct host_cache_entry *host) +{ + struct dns_cache_record *record; + int update = 0; + time_t now = time(NULL); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) { + if (record->expiry <= now) { + + ast_debug(5, "%s: Updating NAPTR record '%s'(%s, TTL=%d)\n", host->target.addr.host.ptr, + record->name, + ast_dns_naptr_get_replacement(record->dns_record), + ast_dns_record_get_ttl(record->dns_record)); + + sip_resolve_add(resolve, record->name, T_NAPTR, C_IN, host->target.type, 0, 0, 0); + + AST_LIST_REMOVE_CURRENT(list); + + dns_srv_record_free(host, ast_dns_naptr_get_replacement(record->dns_record)); + + ast_free(record->name); + ast_free(record->dns_record); + ast_free(record); + + update = 1; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + return update; +} + +static void host_cache_update(struct sip_resolve *resolve) +{ + struct host_cache_entry *host = resolve->host_cache; + int update = 0; + + ast_debug(2, "Updating DNS records in host cache for '%s:%s'\n", + host->target.addr.host.ptr, + pjsip_transport_get_type_desc(host->target.type)); + + update |= dns_naptr_records_update(resolve, host); + + update |= dns_srv_records_update(resolve, host); + + update |= dns_a_records_update(resolve, host); + + if (!update) { + resolve->callback(PJ_SUCCESS, resolve->token, &host->addresses); + } else { + resolve->serializer = ao2_bump(ast_threadpool_serializer_get_current()); + + ast_debug(2, "[%p] Starting queries for updating target '%s'\n", resolve, host->target.addr.host.ptr); + + ast_dns_query_set_resolve_async(resolve->queries, sip_resolve_callback, resolve); + } + + ao2_ref(resolve, -1); + + return; +} + /*! * \internal * \brief Perform SIP resolution of a host @@ -672,6 +1175,7 @@ static int sip_resolve_get_ip_addr_ver(const pj_str_t *host) static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { + struct host_cache_entry *host_cache; int ip_addr_ver; pjsip_transport_type_e type = target->type; struct sip_resolve *resolve; @@ -680,7 +1184,10 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip ast_copy_pj_str(host, &target->addr.host, sizeof(host)); - ast_debug(2, "Performing SIP DNS resolution of target '%s'\n", host); + ast_debug(2, "Performing SIP DNS resolution of target '%s:%s'\n", host, + pjsip_transport_get_type_desc(type)); + + host_cache_cleanup(); /* If the provided target is already an address don't bother resolving */ ip_addr_ver = sip_resolve_get_ip_addr_ver(&target->addr.host); @@ -735,9 +1242,30 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip return; } + AST_LIST_LOCK(&dns_host_cache); + + host_cache = get_host_cache_entry(target); + if (host_cache) { + pjsip_server_addresses *cached_addr; + + ast_debug(2, "Target '%s:%s' is found in host cache\n", host, pjsip_transport_get_type_desc(type)); + + // Use cached addresses, if all cache records are valid. + cached_addr = get_cached_host_addrs(host_cache); + if (cached_addr) { + ast_debug(2, "Target '%s' is valid in cache, skipping resolution\n", host); + + cb(PJ_SUCCESS, token, cached_addr); + + AST_LIST_UNLOCK(&dns_host_cache); + return; + } + } + resolve = ao2_alloc_options(sizeof(*resolve), sip_resolve_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!resolve) { cb(PJ_ENOMEM, token, NULL); + AST_LIST_UNLOCK(&dns_host_cache); return; } @@ -748,9 +1276,40 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip if (AST_VECTOR_INIT(&resolve->resolving, 4)) { ao2_ref(resolve, -1); cb(PJ_ENOMEM, token, NULL); + AST_LIST_UNLOCK(&dns_host_cache); return; } + if (host_cache) { + host_cache_add_resolve(host_cache, resolve); + + ast_debug(2, "Target '%s:%s' is being updated in host cache\n", host, pjsip_transport_get_type_desc(type)); + + // Try to re-query expired records + if (!host_cache->querying) { + host_cache->querying = 1; + host_cache_update(resolve); + } + AST_LIST_UNLOCK(&dns_host_cache); + return; + } + + ast_debug(2, "Target '%s:%s' is not found in host cache\n", host, pjsip_transport_get_type_desc(type)); + + // Create a cache entry for the target + host_cache = host_cache_add(target); + if (!host_cache) { + ao2_ref(resolve, -1); + cb(PJ_ENOMEM, token, NULL); + AST_LIST_UNLOCK(&dns_host_cache); + return; + } + + host_cache_add_resolve(host_cache, resolve); + host_cache->querying = 1; + + AST_LIST_UNLOCK(&dns_host_cache); + ast_debug(2, "[%p] Created resolution tracking for target '%s'\n", resolve, host); /* NAPTR + SRV */ @@ -767,14 +1326,14 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip * explicitly and only looks for IPv6 records. */ - res |= sip_resolve_add(resolve, host, T_NAPTR, C_IN, type, 0); + res |= sip_resolve_add(resolve, host, T_NAPTR, C_IN, type, 0, 0, 0); if (type == PJSIP_TRANSPORT_UNSPECIFIED || (type == PJSIP_TRANSPORT_TLS && sip_transport_is_available(PJSIP_TRANSPORT_TLS)) || (type == PJSIP_TRANSPORT_TLS6 && sip_transport_is_available(PJSIP_TRANSPORT_TLS6))) { if (snprintf(srv, sizeof(srv), "_sips._tcp.%s", host) < NI_MAXHOST) { res |= sip_resolve_add(resolve, srv, T_SRV, C_IN, - type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TLS : type, 0); + type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TLS : type, 0, 0, 0); } } if (type == PJSIP_TRANSPORT_UNSPECIFIED || @@ -782,7 +1341,7 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip (type == PJSIP_TRANSPORT_TCP6 && sip_transport_is_available(PJSIP_TRANSPORT_TCP6))) { if (snprintf(srv, sizeof(srv), "_sip._tcp.%s", host) < NI_MAXHOST) { res |= sip_resolve_add(resolve, srv, T_SRV, C_IN, - type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TCP : type, 0); + type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TCP : type, 0, 0, 0); } } if (type == PJSIP_TRANSPORT_UNSPECIFIED || @@ -790,20 +1349,22 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip (type == PJSIP_TRANSPORT_UDP6 && sip_transport_is_available(PJSIP_TRANSPORT_UDP6))) { if (snprintf(srv, sizeof(srv), "_sip._udp.%s", host) < NI_MAXHOST) { res |= sip_resolve_add(resolve, srv, T_SRV, C_IN, - type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type, 0); + type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type, 0, 0, 0); } } /* AAAA + A */ if ((type == PJSIP_TRANSPORT_UNSPECIFIED && sip_transport_is_available(PJSIP_TRANSPORT_UDP6)) || ((type & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(type))) { - res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP6 : type), target->addr.port); + res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP6 : type), + target->addr.port, 0, 0); } else if (!(type & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(type | PJSIP_TRANSPORT_IPV6)) { - res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, type | PJSIP_TRANSPORT_IPV6, target->addr.port); + res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, type | PJSIP_TRANSPORT_IPV6, target->addr.port, 0, 0); } if ((type == PJSIP_TRANSPORT_UNSPECIFIED && sip_transport_is_available(PJSIP_TRANSPORT_UDP)) || (!(type & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(type))) { - res |= sip_resolve_add(resolve, host, T_A, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type), target->addr.port); + res |= sip_resolve_add(resolve, host, T_A, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type), + target->addr.port, 0, 0); } if (res) { @@ -869,6 +1430,145 @@ static void sip_check_transport(pj_pool_t *pool, pjsip_transport_type_e transpor } } +static char * get_host_cache_addr_expiry(char *buf, size_t buflen, struct host_cache_entry *host, pj_sockaddr *addr) +{ + char strbuf[32]; + struct dns_cache_record *record; + int ttl = 0; + time_t expiry = LONG_MAX; + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 0, + }; + struct ast_tm tm; + pj_sockaddr addr_c; + char *hostname = NULL; + char *srvname = NULL; + + if (host->querying) { + ast_debug(5, "host cache '%s' is updating\n", host->target.addr.host.ptr); + memset(buf, 0, buflen); + return buf; + } + + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) { + if (addr->addr.sa_family == PJ_AF_INET && ast_dns_record_get_rr_type(record->dns_record) == T_A) { + pj_sockaddr_init(pj_AF_INET(), &addr_c, NULL, record->target.port); + addr_c.ipv4.sin_addr = *(pj_in_addr *) ast_dns_record_get_data(record->dns_record); + + } else if (addr->addr.sa_family == PJ_AF_INET6 && ast_dns_record_get_rr_type(record->dns_record) == T_AAAA) { + pj_sockaddr_init(pj_AF_INET6(), &addr_c, NULL, record->target.port); + pj_memcpy(&addr_c.ipv6.sin6_addr, ast_dns_record_get_data(record->dns_record), + ast_dns_record_get_data_size(record->dns_record)); + } else + continue; + + if (!pj_sockaddr_cmp(addr, &addr_c)) { + hostname = record->name; + expiry = record->expiry; + ttl = ast_dns_record_get_ttl(record->dns_record); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (hostname) { + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) { + if (!strcmp(hostname, ast_dns_srv_get_host(record->dns_record))) { + srvname = record->name; + if (record->expiry < expiry) { + expiry = record->expiry; + ttl = ast_dns_record_get_ttl(record->dns_record); + } + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + } + + if (srvname) { + AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) { + if (!strcmp(srvname, ast_dns_naptr_get_replacement(record->dns_record))) { + if (record->expiry < expiry) { + expiry = record->expiry; + ttl = ast_dns_record_get_ttl(record->dns_record); + } + } + } + AST_LIST_TRAVERSE_SAFE_END; + } + + tv.tv_sec = expiry; + ast_localtime(&tv, &tm, NULL); + ast_strftime(strbuf, sizeof(strbuf), "%F %T %Z", &tm); + snprintf(buf, buflen, "%s (TTL=%d)", strbuf, ttl); + + return buf; +} + +static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + char addr[PJ_INET6_ADDRSTRLEN + 10]; + struct host_cache_entry *host = NULL; + struct dns_cache_record *current; + struct dns_cache_record *next; + int idx, cnt; + char tbuf[64]; + + switch (cmd) { + case CLI_INIT: + e->command = "pjsip show srv_lookups"; + e->usage = "Usage: pjsip show SRV DNS lookup records\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + ast_cli(a->fd, + "<Host name.............................> " + "<Target name......................................> " + "<IP address.............> <Port> <Transport.....> " + "<Priority> <Weight> <Expired at>\n" + "=============================================================" + "=============================================================" + "=============================================================" + "=====\n"); + + AST_LIST_LOCK(&dns_host_cache); + AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) { + if (host->querying) { + ast_cli(a->fd, "%-40.40s Resolving...\n", host->target.addr.host.ptr); + continue; + } + if (host->target.type == PJSIP_TRANSPORT_UNSPECIFIED) + continue; + for (cnt = 0, current = host->a_records.first, next = current ? current->list.next : NULL; current; + cnt++, current = next, next = current ? current->list.next : NULL) { + ast_cli(a->fd, "%-40.40s %-40.40s %-25.25s %-6d %-15.15s %10d %8d %-24s\n", + host->target.addr.host.ptr, + current->name, + pj_sockaddr_print(&host->addresses.entry[cnt].addr, addr, sizeof(addr), 2), + pj_sockaddr_get_port(&host->addresses.entry[cnt].addr), + pjsip_transport_get_type_desc(host->target.type), + host->addresses.entry[cnt].priority, + host->addresses.entry[cnt].weight, + get_host_cache_addr_expiry(tbuf, sizeof(tbuf), host, &host->addresses.entry[cnt].addr)); + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&dns_host_cache); + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_commands[] = { + AST_CLI_DEFINE(cli_show_tasks, "Show pjsip SRV DNS lookup records"), +}; + /*! \brief External resolver implementation for PJSIP */ static pjsip_ext_resolver ext_resolver = { .resolve = sip_resolve, @@ -910,6 +1610,7 @@ void ast_sip_initialize_resolver(void) { /* Replace the existing PJSIP resolver with our own implementation */ ast_sip_push_task_wait_servant(NULL, sip_replace_resolver, NULL); + ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); } #else @@ -921,3 +1622,50 @@ void ast_sip_initialize_resolver(void) } #endif + +int ast_sip_destroy_resolver(void) +{ + ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands)); +} + +/*! + * \brief Check if the given IP address matches any DNS-resolved address. + * \param src_addr The source address to be checked in DNS cache list + * \return 1 if the address matches a resolved DNS entry, 0 otherwise. + */ +int ast_sip_is_address_in_dns_records(const pj_sockaddr *src_addr) +{ + struct host_cache_entry *host = NULL; + struct dns_cache_record *current; + struct dns_cache_record *next; + int cnt; + char src_addr_buf[PJ_INET6_ADDRSTRLEN]; + char resolved_addr_buf[PJ_INET6_ADDRSTRLEN]; + int match_found = 0; + + pj_sockaddr_print(src_addr, src_addr_buf, sizeof(src_addr_buf), 3); + + AST_LIST_LOCK(&dns_host_cache); + AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) { + if (host->querying) + continue; + for (cnt = 0, current = host->a_records.first, next = current ? current->list.next : NULL; current; + cnt++, current = next, next = current ? current->list.next : NULL) { + if (!pj_sockaddr_cmp(src_addr, &host->addresses.entry[cnt].addr)) { + pj_sockaddr_print(&host->addresses.entry[cnt].addr, resolved_addr_buf, sizeof(resolved_addr_buf), 3); + ast_log(LOG_NOTICE, "Source address %s matches resolved DNS entry %s\n", src_addr_buf, resolved_addr_buf); + match_found = 1; + break; + } + } + if (match_found) + break; + } + + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&dns_host_cache); + if (!match_found) + ast_log(LOG_NOTICE, "Source address %s did not match any resolved DNS entries\n", src_addr_buf); + + return match_found; +} diff --git a/res/res_pjsip_authenticator_digest.c b/res/res_pjsip_authenticator_digest.c index 2cdbe6eff5866193894523980bbf84bd908300b2..0a882ecb6a82ddd6e9850256ad8c463cd1bfb95e 100644 --- a/res/res_pjsip_authenticator_digest.c +++ b/res/res_pjsip_authenticator_digest.c @@ -485,6 +485,19 @@ static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint if (verify_res[idx] == AUTH_FAIL) { failures++; } + if(verify_res[idx] == AUTH_NOAUTH) { + if (rdata && rdata->msg_info.msg) { + pjsip_contact_hdr *contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); + if (contact_hdr && contact_hdr->uri) { + pjsip_sip_uri *client_uri = pjsip_uri_get_uri(contact_hdr->uri); + if (client_uri) { + /* lack of auth. IPX003 */ + ast_log_dt(LOG_EVENT_CODE_IPX003, (int)client_uri->user.slen, client_uri->user.ptr, + (int)client_uri->host.slen, client_uri->host.ptr); + } + } + } + } } for (idx = 0; idx < auth_size; ++idx) { diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c index b11e5908bfe26fb8d30bf76ba9206f2a0dbed057..665e3565f4a34117ad88e30d00f4a3209e09fc0c 100644 --- a/res/res_pjsip_caller_id.c +++ b/res/res_pjsip_caller_id.c @@ -370,6 +370,45 @@ static void add_pai_header(const struct ast_sip_session *session, pjsip_tx_data pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr); } +/*! + * \internal + * \brief Add a P-Preffered-Identity header to an outbound message + * \param session The session on which communication is happening + * \param tdata The message to add the header to + */ +static void add_ppi_header(const struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + static const pj_str_t pj_ppi_name = { "P-Preferred-Identity", 20 }; + pjsip_fromto_hdr *old_ppi; + pjsip_fromto_hdr *base; + pjsip_fromto_hdr *ppi_hdr; + + /* Since inv_session reuses responses, we have to make sure there's not already + * a P-Preffered-Identity present. If there is, we just skip adding a new one. + */ + old_ppi = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_ppi_name, NULL); + if (old_ppi) { + if (old_ppi->type == PJSIP_H_OTHER) + pj_list_erase(old_ppi); + else + return; + } + + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + base = session->saved_from_hdr ? session->saved_from_hdr : PJSIP_MSG_FROM_HDR(tdata->msg); + } else { + base = PJSIP_MSG_TO_HDR(tdata->msg); + } + + ppi_hdr = pjsip_from_hdr_create(tdata->pool); + ppi_hdr->type = PJSIP_H_OTHER; + ppi_hdr->sname = ppi_hdr->name = pj_ppi_name; + + ppi_hdr->uri = (pjsip_uri *) pjsip_uri_clone(tdata->pool, base->uri); + + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)ppi_hdr); +} + /*! * \internal * \brief Add party parameter to a Remote-Party-ID header. @@ -529,6 +568,9 @@ static void add_id_headers(const struct ast_sip_session *session, pjsip_tx_data if (session->endpoint->id.send_pai) { add_pai_header(session, tdata, id); } + if (session->endpoint->id.send_ppi) { + add_ppi_header(session, tdata); + } if (session->endpoint->id.send_rpid) { add_rpid_header(session, tdata, id); } diff --git a/res/res_pjsip_logger.c b/res/res_pjsip_logger.c index 456bb224a94b48687a184b8627bdf34bb111d028..c282567f47e45d0eb0bf8aec577d59721e631c61 100644 --- a/res/res_pjsip_logger.c +++ b/res/res_pjsip_logger.c @@ -327,6 +327,11 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) (int) (tdata->buf.end - tdata->buf.start), tdata->buf.start); } + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + char addr_buf[AST_SOCKADDR_BUFLEN] = {'\0'}; + pj_sockaddr_print(&tdata->tp_info.dst_addr, addr_buf, sizeof(addr_buf), 0); + } + if (default_logger->log_to_pcap) { pjsip_logger_write_to_pcap(default_logger, tdata->buf.start, (int) (tdata->buf.cur - tdata->buf.start), NULL, &tdata->tp_info.dst_addr); diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c index aee4afc90ec5a23b9b4090ce1c278eb9c5f24380..d3a6b825ba79b72212dc131098a6a587ed96b5e3 100644 --- a/res/res_pjsip_outbound_authenticator_digest.c +++ b/res/res_pjsip_outbound_authenticator_digest.c @@ -31,6 +31,7 @@ #include "asterisk/module.h" #include "asterisk/strings.h" #include "asterisk/vector.h" +#include "asterisk/res_pjsip_outbound_registration.h" pj_str_t supported_digest_algorithms[] = { { "MD5", 3} @@ -478,16 +479,25 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut goto cleanup; } - if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(), - old_request->pool, 0) != PJ_SUCCESS) { - ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n", - id_type, id); - res = -1; - goto cleanup; + pjsip_hdr_e search_type = get_auth_search_type(challenge); + struct ast_sip_auth *auth = NULL; + if (search_type != PJSIP_H_OTHER) { + pjsip_www_authenticate_hdr *auth_hdr = NULL; + while ((auth_hdr = pjsip_msg_find_hdr(challenge->msg_info.msg, search_type, auth_hdr ? auth_hdr->next : NULL))) { + for (int i = 0; i < auth_object_count; ++i) { + auth = AST_VECTOR_GET(&auth_objects_vector, i); + + if (pj_stricmp2(&auth_hdr->challenge.digest.realm, auth->realm) == 0) { + ast_debug(3, "Found matching auth '%s' with realm '%s'\n", ast_sorcery_object_get_id(auth), auth->realm); + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", ast_sorcery_object_get_id(auth)); + break; + } + } + } } /* - * realms is used only for displaying good error messages. + * realms is used for displaying good error messages, and for caching auth header */ realms = ast_str_create(32); if (!realms) { @@ -495,11 +505,25 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut goto cleanup; } - /* - * Load pjproject with the valid credentials for the Authentication headers - * received on the 401 or 407 response. - */ - status = set_outbound_authentication_credentials(&auth_sess, &auth_objects_vector, challenge, &realms); + if (endpoint && (pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_register_method) ==0 || pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_invite_method) ==0)) { + if(auth && auth->realm){ + ast_str_append(&realms, 0, "%s ", auth->realm); + } + goto add_header; // auth_sess for REGISTER and INVITE has been inited during sip_outbound_registration_regc_alloc + } else if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(), + old_request->pool, 0) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n", + id_type, id); + res = -1; + goto cleanup; + } else { + /* + * Load pjproject with the valid credentials for the Authentication headers + * received on the 401 or 407 response. + */ + status = set_outbound_authentication_credentials(&auth_sess, &auth_objects_vector, challenge, &realms); + } + switch (status) { case PJ_SUCCESS: break; @@ -515,13 +539,40 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut goto cleanup; } +add_header: /* * reinit_req actually creates the Authorization headers to send on * the next request. If reinit_req already has a cached credential * from an earlier successful authorization, it'll use it. Otherwise * it'll create a new authorization and cache it. */ - status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request); + if(endpoint &&pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_register_method) ==0 ){ + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + status = pjsip_auth_clt_reinit_req(&endpoint->auth_sess_reg, challenge, old_request, new_request); + //if success, sync cached info to inv + if (status == PJ_SUCCESS){ + ast_debug(3, "sync cached auth info from auth_sess_reg to auth_sess_inv\n"); + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_reg, &endpoint->auth_sess_inv, &realms_pj_str); + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + } + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } else if (endpoint && pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_invite_method) ==0 ){ + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + status = pjsip_auth_clt_reinit_req(&endpoint->auth_sess_inv, challenge, old_request, new_request); + //if success, sync cached info to reg + if (status == PJ_SUCCESS){ + ast_debug(3, "sync cached auth info from auth_sess_inv to auth_sess_reg\n"); + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_inv, &endpoint->auth_sess_reg, &realms_pj_str); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + } else { + status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request); + } switch (status) { case PJ_SUCCESS: diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index b7ef2b0e2b9da7fbefb07ac00b55c58e06a9e583..78244f54e9286f4fd165d6f34598f6f9f954ecdf 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -38,10 +38,9 @@ #include "asterisk/threadstorage.h" #include "asterisk/threadpool.h" #include "asterisk/statsd.h" -#include "res_pjsip/include/res_pjsip_private.h" #include "asterisk/vector.h" #include "asterisk/pbx.h" - +#include "asterisk/res_pjsip_outbound_registration.h" /*** DOCUMENTATION <configInfo name="res_pjsip_outbound_registration" language="en_US"> <synopsis>SIP resource for outbound registrations</synopsis> @@ -138,6 +137,9 @@ <configOption name="retry_interval" default="1"> <synopsis>Interval in seconds between retries if outbound registration is unsuccessful</synopsis> </configOption> + <configOption name="retry_percent" default="90"> + <synopsis>Percentage of Register Expire timer on which CPE will perform re-Register</synopsis> + </configOption> <configOption name="forbidden_retry_interval" default="0"> <synopsis>Interval used when receiving a 403 Forbidden response.</synopsis> <description><para> @@ -173,13 +175,6 @@ the registrar, e.g. sip:sip.example.com. </para></description> </configOption> - <configOption name="transport"> - <synopsis>Transport used for outbound authentication</synopsis> - <description> - <note><para>A <replaceable>transport</replaceable> configured in - <literal>pjsip.conf</literal>. As with other <literal>res_pjsip</literal> modules, this will use the first available transport of the appropriate type if unconfigured.</para></note> - </description> - </configOption> <configOption name="line"> <synopsis>Whether to add a 'line' parameter to the Contact for inbound call matching</synopsis> <description><para> @@ -293,6 +288,9 @@ AST_THREADSTORAGE(register_callback_invoked); #define LINE_PARAMETER_SIZE 8 static const char broadcast_path[] = "voice.sip.client"; +/*! \brief timer for REGISTER attempts between two addresses in the record list */ +static struct ast_sched_context *sched; + /*! \brief Various states that an outbound registration may be in */ enum sip_outbound_registration_status { /*! \brief Currently unregistered */ @@ -313,6 +311,24 @@ static pjsip_module mod_mwi = { .id = -1, }; +/* for counting register request that has been sent out before success response */ +static pj_status_t log_register_on_tx_msg(pjsip_tx_data *tdata); // +static pjsip_module log_register_module = { + .name = { "log_register_module", 19 }, + .priority = 0, + .on_tx_request = log_register_on_tx_msg, +}; + +static pj_status_t emergency_hack_on_tx_message(pjsip_tx_data *tdata); // +static pjsip_module emergency_hack_module = { + .name = { "emergency_hack_module", 21 }, + .id = -1, + .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1, + .on_tx_request = emergency_hack_on_tx_message, + .on_tx_response = emergency_hack_on_tx_message, +}; + + /*! * \internal * \brief Convert the internal registration state to an external status string. @@ -357,8 +373,6 @@ struct sip_outbound_registration { AST_STRING_FIELD(contact_user); /*! \brief Optional header parameters for contact */ AST_STRING_FIELD(contact_header_params); - /*! \brief Explicit transport to use for registration */ - AST_STRING_FIELD(transport); /*! \brief Outbound proxy to use */ AST_STRING_FIELD(outbound_proxy); /*! \brief Endpoint to use for related incoming calls */ @@ -368,6 +382,8 @@ struct sip_outbound_registration { unsigned int expiration; /*! \brief Maximum random initial delay interval for initial registrations */ unsigned int max_random_initial_delay; + /*! \brief Percentage of Register Expire timer on which CPE will perform re-Register */ + unsigned int retry_percent; /*! \brief Interval at which retries should occur for temporal responses */ unsigned int retry_interval; /*! \brief Interval at which retries should occur for permanent responses */ @@ -398,6 +414,14 @@ struct sip_outbound_registration { unsigned int mediasec; /*! \brief Whether Outbound support is enabled */ unsigned int support_outbound; + /*! \brief max_time (RFC 5626), default 1800 seconds */ + unsigned int retry_max_time; + /*! \brief base_time_all_failed (RFC 5626), default 30 seconds */ + unsigned int retry_base_time1; + /*! \brief base_time_not_all_failed (RFC 5626), default 90 seconds */ + unsigned int retry_base_time2; + /*! \brief 1TR114, retry after time if no retry-after header included in response, default 15 seconds */ + unsigned int retry_after; }; /*! \brief Outbound registration client state information (persists for lifetime of regc) */ @@ -425,8 +449,14 @@ struct sip_outbound_registration_client_state { char line[LINE_PARAMETER_SIZE]; /*! \brief Current number of retries */ unsigned int retries; + /*! \brief number of consecutive unsuccessful initial registrations to one P-CSCF address, 1TR114 */ + unsigned int attempts; + /*! \brief number of consecutive unsuccessful initial registrations in total, 1TR114 */ + unsigned int failures; /*! \brief Maximum number of retries permitted */ unsigned int max_retries; + /*! \brief Percentage of Register Expire timer on which CPE will perform re-Register */ + unsigned int retry_percent; /*! \brief Interval at which retries should occur for temporal responses */ unsigned int retry_interval; /*! \brief Interval at which retries should occur for permanent responses */ @@ -441,6 +471,10 @@ struct sip_outbound_registration_client_state { unsigned int failover_retry_interval1; /*! \brief Interval2 at which retries should occur for failover */ unsigned int failover_retry_interval2; + /*! \brief the upper-bound wait time, RFC 5626 section 4.5 */ + int waiting_time_upper_bound; + /*! \brief control the time for REGISTER attempts between two addresses in the record list */ + int registration_resend_timer_id; /*! \brief Treat authentication challenges that we cannot handle as permanent failures */ unsigned int auth_rejection_permanent; /*! \brief Determines whether SIP Path support should be advertised */ @@ -465,6 +499,8 @@ struct sip_outbound_registration_client_state { unsigned int destroy:1; /*! \brief Non-zero if we have attempted sending a REGISTER with authentication */ unsigned int auth_attempted:1; + /*! \brief Non-zero if we have sent a REGISTER successful with security_verify */ + unsigned int cached_security_verify:1; /*! \brief Status code of last response if we have tried to register before */ int last_status_code; /*! \brief The name of the transport to be used for the registration */ @@ -475,6 +511,10 @@ struct sip_outbound_registration_client_state { unsigned int is494; /*! \brief Expected time of registration lapse/expiration */ unsigned int registration_expires; + /*! \brief Emergency Registration Flag */ + bool Emergency_reg_ongoing; + bool Emergency_reg_hangup; + bool Emergency_call_ongoing; }; /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */ @@ -485,6 +525,9 @@ struct sip_outbound_registration_state { struct sip_outbound_registration_client_state *client_state; }; +/* RFC5626, Section 4.5: waiting randomly between 50-100% of upper_bound */ +#define RFC5626_RETRY_DELAY(time) (time ? ((ast_random() % 50 + 50) * time / 100) : 15) + /*! Time needs to be long enough for a transaction to timeout if nothing replies. */ #define MAX_UNLOAD_TIMEOUT_TIME 35 /* Seconds */ @@ -686,8 +729,8 @@ out: static void add_security_headers(struct sip_outbound_registration_client_state *client_state, pjsip_tx_data *tdata) { - int add_require_header = 1; - int add_proxy_require_header = 1; + int add_require_header; + int add_proxy_require_header; int add_sec_client_header = 0; struct sip_outbound_registration *reg = NULL; struct ast_sip_endpoint *endpt = NULL; @@ -699,10 +742,6 @@ static void add_security_headers(struct sip_outbound_registration_client_state * static const pj_str_t proxy_require = { "Proxy-Require", 13 }; static const pj_str_t require = { "Require", 7 }; - if (client_state->security_negotiation != AST_SIP_SECURITY_NEG_MEDIASEC) { - return; - } - /* Get contact status through registration -> endpoint name -> aor -> contact (if set) */ if ((reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", client_state->registration_name)) && !ast_strlen_zero(reg->endpoint) && (endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint)) @@ -721,9 +760,15 @@ static void add_security_headers(struct sip_outbound_registration_client_state * if (!contact_status && AST_VECTOR_SIZE(&client_state->server_security_mechanisms)) { sec_mechs = &client_state->server_security_mechanisms; } - if (client_state->status == SIP_REGISTRATION_REJECTED_TEMPORARY || client_state->auth_attempted) { + if (client_state->status == SIP_REGISTRATION_REJECTED_TEMPORARY || client_state->auth_attempted || client_state->cached_security_verify ) { if (sec_mechs != NULL && pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL) == NULL) { ast_sip_add_security_headers(sec_mechs, "Security-Verify", 0, tdata); + } else if ((endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name)) && !AST_LIST_EMPTY(&endpt->secur_mechanisms) && pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL) == NULL) { + struct security_mechanism *sec_mechanism; + AST_LIST_TRAVERSE(&endpt->secur_mechanisms, sec_mechanism, entry) { + ast_debug(3, "Adding security header: %s\n", sec_mechanism->value); + ast_sip_add_header(tdata,"Security-Verify",sec_mechanism->value); + } } if (client_state->last_status_code == 494) { ast_sip_remove_headers_by_name_and_value(tdata->msg, &security_client, NULL); @@ -731,22 +776,21 @@ static void add_security_headers(struct sip_outbound_registration_client_state * /* necessary if a retry occures */ add_sec_client_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &security_client, NULL) == NULL) ? 1 : 0; } - add_require_header = - (pjsip_msg_find_hdr_by_name(tdata->msg, &require, NULL) == NULL) ? 1 : 0; - add_proxy_require_header = - (pjsip_msg_find_hdr_by_name(tdata->msg, &proxy_require, NULL) == NULL) ? 1 : 0; } else { - ast_sip_add_security_headers(&client_state->security_mechanisms, "Security-Client", 0, tdata); + add_sec_client_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &security_client, NULL) == NULL) ? 1 : 0; } - if (add_require_header) { - ast_sip_add_header(tdata, "Require", "mediasec"); + add_require_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &require, NULL) == NULL) ? 1 : 0; + add_proxy_require_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &proxy_require, NULL) == NULL) ? 1 : 0; + + if (add_sec_client_header) { + ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec"); } if (add_proxy_require_header) { ast_sip_add_header(tdata, "Proxy-Require", "mediasec"); } - if (add_sec_client_header) { - ast_sip_add_security_headers(&client_state->security_mechanisms, "Security-Client", 0, tdata); + if (add_require_header) { + ast_sip_add_header(tdata, "Require", "mediasec"); } /* Cleanup */ @@ -758,6 +802,28 @@ static void add_security_headers(struct sip_outbound_registration_client_state * ao2_cleanup(reg); } +/*! \brief Helper function which sets up the timer to re-register in a specific amount of time */ +static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds); +static int queue_register(struct sip_outbound_registration_state *state); + +// Clean current transport data. Then, pjsip will resolve with new transport. +static void clean_transport_data_and_apply_new_transport(pjsip_tx_data *tdata, struct sip_outbound_registration_client_state *client_state, const char *newtransport){ + tdata->dest_info.cur_addr = 0; + tdata->dest_info.addr.count = 0; + tdata->tp_info.transport = NULL; + pjsip_via_hdr *via; + via = (pjsip_via_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); + if (via) + via->branch_param.slen = 0; + + // Apply new transport + ast_free(client_state->transport_name); + client_state->transport_name = ast_strdup(newtransport); + ast_log_dt(LOG_EVENT_CODE_SV114); + ast_debug(2, "REGISTER %s: switch transport to %s\n", + client_state->registration_name, client_state->transport_name); +} + /*! \brief Helper function which sends a message and cleans up, if needed, on failure */ static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state, pjsip_tx_data *tdata) @@ -765,6 +831,7 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli pj_status_t status; int *callback_invoked; pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + struct ast_sip_transport_state *transport_state; callback_invoked = ast_threadstorage_get(®ister_callback_invoked, sizeof(int)); if (!callback_invoked) { @@ -782,20 +849,135 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli */ pjsip_tx_data_add_ref(tdata); - /* Add Security-Verify or Security-Client headers */ - add_security_headers(client_state, tdata); - /* * Set the transport in case transports were reloaded. * When pjproject removes the extraneous error messages produced, * we can check status and only set the transport and resend if there was an error */ - ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if (client_state->Emergency_reg_ongoing && !client_state->Emergency_reg_hangup && (ast_strlen_zero(client_state->transport_name) || strcmp(client_state->transport_name, endpoint->transport_emergency)) ){ + RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); + transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", endpoint->transport_emergency); + if (!transport) { + ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport transport_emergency: %s\n", endpoint->transport_emergency); + return -1; + } + ast_log(LOG_NOTICE,"===== EMERGENCY REGISTER with UDP =====\n"); + + // Clean current transport data. Then, pjsip will resolve with transport_emergency. + clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport_emergency); + + } else if (client_state->Emergency_reg_hangup && client_state->Emergency_call_ongoing != true){ + if(client_state->Emergency_reg_ongoing){ + // unreg when reg still ongoing, waiting for reg transaction stop, and resend + schedule_registration(client_state, 5); + ast_log(LOG_NOTICE,"===== E-reg-hangup While E-reg still ongoing, waiting 5s =====\n"); + return 0; + } + ast_log(LOG_NOTICE,"===== E-reg-hangup, new registration with primary transport =====\n"); + if (strcmp(client_state->transport_name, endpoint->transport)) { + clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport); + } + client_state->Emergency_reg_hangup = false; + endpoint->Emergency = false; + } + + transport_state = ast_sip_get_transport_state(client_state->transport_name); + if (transport_state->type == AST_TRANSPORT_TLS && !ast_sip_is_X_RDK_NTP_Synchronized()) { + if (ast_strlen_zero(endpoint->transport2) || !strcmp(client_state->transport_name, endpoint->transport2)) { + ast_log(LOG_ERROR, "%s: '%s' is unavailable, as NTP is not synchronized\n", + client_state->registration_name, client_state->transport_name); + return -1; + } + + /* Fallback to transport2 */ + ast_log(LOG_WARNING, "%s: NTP is not synchronized. Transport falls back from '%s' to '%s'\n", + client_state->registration_name, client_state->transport_name, endpoint->transport2); + + ast_free(client_state->transport_name); + client_state->transport_name = ast_strdup(endpoint->transport2); + + transport_state = ast_sip_get_transport_state(client_state->transport_name); + } + + if (client_state->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) { + /* Add Security-Verify or Security-Client headers */ + if (transport_state->type == AST_TRANSPORT_TLS) + add_security_headers(client_state, tdata); + } + + if (ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector)) { + ast_log(LOG_ERROR, "Failed to set tpselector for %s\n", client_state->transport_name); + return -1; + } pjsip_regc_set_transport(client_state->client, &selector); ast_sip_tpselector_unref(&selector); + ast_debug(2, "%s: Outbound REGISTER sending with transport '%s'\n", + client_state->registration_name, client_state->transport_name); + + if (endpoint->register_dest && endpoint->failover_reg_addr == 1) { + char addr_buf[64]; + pj_sockaddr_print(&endpoint->register_dest->addr.entry[endpoint->register_dest->cur_addr].addr, addr_buf, sizeof(addr_buf), 0); + ast_log(LOG_NOTICE,"Current Registraion addr: %s\n", addr_buf); + pj_strdup2(tdata->pool, &tdata->dest_info.name, endpoint->register_dest->host); + memcpy(&tdata->dest_info.addr, &endpoint->register_dest->addr, sizeof(pjsip_server_addresses)); + tdata->dest_info.cur_addr = endpoint->register_dest->cur_addr; + + if(!ast_sip_failover_request(tdata)){ + // failover to the next address in the list or go back to the head of the list if current at the end. + tdata->dest_info.cur_addr = 0; + } + pj_sockaddr_print(&tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr, addr_buf, sizeof(addr_buf), 0); + ast_log(LOG_NOTICE,"Failover Registration request to addr: %s\n", addr_buf); + + } + + pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(tdata->msg->line.req.uri); + sip_uri->port = pj_sockaddr_get_port(&transport_state->host); + + /* Expires header is missing in an outgoing REGISTER request in some use scenarios and that causes the default value + * from the registrar side being used. Here we add Expires header with the configured expiration value if the header + * is absent */ + if (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL) == NULL) { + struct sip_outbound_registration *reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), + "registration", client_state->registration_name); + if (reg) { + pjsip_expires_hdr *expires_hdr = pjsip_expires_hdr_create(tdata->pool, reg->expiration); + if (expires_hdr) { + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); + } + ao2_cleanup(reg); + } + } + //if cached_auth, no auth header, call pjsip_auth_clt_init_req + if (endpoint->cached_auth == true && (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL) == NULL)){ + //check and sync info from auth_sess_inv, nc, nonce + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_inv, &endpoint->auth_sess_reg, &realms_pj_str); + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + ast_debug(3, "Add cached auth header\n"); + pjsip_auth_clt_init_req(&endpoint->auth_sess_reg, tdata); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } + + client_state->attempts++; + ast_sip_mod_data_set(tdata->pool, tdata->mod_data, log_register_module.id, + "client_state_on_register", client_state); status = pjsip_regc_send(client_state->client, tdata); + if (tdata->dest_info.addr.count) { + int i = tdata->dest_info.cur_addr; + char addr[64]; + + pj_sockaddr_print(&tdata->dest_info.addr.entry[i].addr, addr, sizeof(addr), 3); + ast_debug(2, "%s: Outbound REGISTER attempt %u to address[%d] %s\n", + client_state->registration_name, + client_state->attempts, i, addr); + } + /* * If the attempt to send the message failed and the callback was not invoked we need to * drop the references we just added @@ -926,12 +1108,6 @@ static int handle_client_registration(void *data) } } } - else { - ast_debug(1, "Adding default security headers\n"); - ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec"); - ast_sip_add_header(tdata,"Proxy-Require","mediasec"); - ast_sip_add_header(tdata,"Require","mediasec"); - } } registration_client_send(client_state, tdata); @@ -943,9 +1119,62 @@ static int handle_client_registration(void *data) static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { struct sip_outbound_registration_client_state *client_state = entry->user_data; + struct ast_sip_transport_state *transport = ast_sip_get_transport_state(client_state->transport_name); entry->id = 0; + /* No TLS connection available when NTP is not synchronized */ + if (!client_state->Emergency_reg_ongoing && transport->type == AST_TRANSPORT_TLS && !ast_sip_is_X_RDK_NTP_Synchronized()) { + struct sip_outbound_registration *registration = NULL; + struct ast_sip_endpoint *endpoint = NULL; + + registration = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", client_state->registration_name); + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + + /* If the client first time registers, wait NTP synchronization for some time */ + if (client_state->status == SIP_REGISTRATION_UNREGISTERED) { + pj_time_val delay = { .sec = 0, .msec = 0}; + + client_state->failures++; + + if (client_state->failures <= 10) + delay.sec = registration->retry_base_time1; // 30s by default + else + delay.sec = registration->retry_base_time2; // 90s by default + + /* Connection retry logic: + * 1) If transport2 exists: Try up to 3 times, then fall back to transport2 + * 2) If transport2 does not exist: Retry up to 10 times, then continue waiting with a longer delay + */ + if ((client_state->failures <= 3 || + ast_strlen_zero(endpoint->transport2) || + !strcmp(client_state->transport_name, endpoint->transport2)) && + client_state->failures <= 10) { + + ast_log(LOG_NOTICE, "%s: NTP is not synchronized. Try TLS connection later after %ld seconds\n", + client_state->registration_name, delay.sec); + pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay); + + return; + } + + client_state->failures = 0; + } + + if (ast_strlen_zero(endpoint->transport2) || !strcmp(client_state->transport_name, endpoint->transport2)) { + ast_log(LOG_ERROR, "%s: '%s' is unavailable, as NTP is not synchronized\n", + client_state->registration_name, client_state->transport_name); + return; + } + + /* Fallback to transport2 */ + ast_log(LOG_WARNING, "%s: NTP is not synchronized. Transport falls back from '%s' to '%s'\n", + client_state->registration_name, client_state->transport_name, endpoint->transport2); + + ast_free(client_state->transport_name); + client_state->transport_name = ast_strdup(endpoint->transport2); + } + /* * Transfer client_state reference to serializer task so the * nominal path will not dec the client_state ref in this @@ -966,7 +1195,8 @@ static void schedule_registration(struct sip_outbound_registration_client_state cancel_registration(client_state); pjsip_regc_get_info(client_state->client, &info); - ast_debug(1, "Scheduling outbound registration to server '%.*s' from client '%.*s' in %d seconds\n", + ast_debug(1, "Scheduling %s outbound registration to server '%.*s' from client '%.*s' in %d seconds\n", + client_state->registration_name, (int) info.server_uri.slen, info.server_uri.ptr, (int) info.client_uri.slen, info.client_uri.ptr, seconds); @@ -978,7 +1208,11 @@ static void schedule_registration(struct sip_outbound_registration_client_state (int) info.client_uri.slen, info.client_uri.ptr); ao2_ref(client_state, -1); } - client_state->registration_expires = ((int) time(NULL)) + seconds; + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if (!(endpoint && endpoint->failover_reg_addr == 1)){ + // do not update expires time for 2nd reg while 504 with xml, required while 2nd reg failed. + client_state->registration_expires = ((int) time(NULL)) + seconds; + } } static void update_client_state_status(struct sip_outbound_registration_client_state *client_state, enum sip_outbound_registration_status status) @@ -1042,11 +1276,24 @@ static int handle_client_state_destruction(void *data) (int) info.server_uri.slen, info.server_uri.ptr, (int) info.client_uri.slen, info.client_uri.ptr); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", + client_state->registration_name); + if (endpoint && endpoint->failover_reg_addr == 1){ + // skip unregister if INVITE failed with 504 xml. request could have no response and will messed up with following re-register + return 0; + } + update_client_state_status(client_state, SIP_REGISTRATION_STOPPING); client_state->destroy = 1; if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS && add_configured_supported_headers(client_state, tdata) && registration_client_send(client_state, tdata) == PJ_SUCCESS) { + pjsip_uri *uri = pjsip_parse_uri(pjsip_regc_get_pool(client_state->client), info.client_uri.ptr, (int) info.client_uri.slen, 0); + pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri); + if (sip_uri) { + ast_log_dt(LOG_EVENT_CODE_IPX002, (int) sip_uri->user.slen, sip_uri->user.ptr, + (int) sip_uri->host.slen, sip_uri->host.ptr); + } ao2_ref(client_state, -1); return 0; } @@ -1061,7 +1308,25 @@ static int handle_client_state_destruction(void *data) pjsip_regc_destroy(client_state->client); client_state->client = NULL; } - + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if(endpoint){ + if(endpoint->auth_sess_reg.endpt){ + pjsip_auth_clt_deinit(&endpoint->auth_sess_reg); + if (endpoint->auth_sess_reg.pool) { + pj_pool_release(endpoint->auth_sess_reg.pool); + endpoint->auth_sess_reg.pool = NULL; + } + ast_mutex_destroy(&endpoint->auth_sess_reg_lock); + } + if(endpoint->auth_sess_inv.endpt){ + pjsip_auth_clt_deinit(&endpoint->auth_sess_inv); + if (endpoint->auth_sess_inv.pool) { + pj_pool_release(endpoint->auth_sess_inv.pool); + endpoint->auth_sess_inv.pool = NULL; + } + ast_mutex_destroy(&endpoint->auth_sess_inv_lock); + } + } update_client_state_status(client_state, SIP_REGISTRATION_STOPPED); ast_sip_auth_vector_destroy(&client_state->outbound_auths); ast_sip_security_mechanisms_vector_destroy(&client_state->security_mechanisms); @@ -1096,13 +1361,16 @@ static void registration_response_destroy(void *obj) if (response->rdata) { pjsip_rx_data_free_cloned(response->rdata); + response->rdata = NULL; } if (response->old_request) { pjsip_tx_data_dec_ref(response->old_request); + response->old_request = NULL; } ao2_cleanup(response->client_state); + response->client_state = NULL; } /*! \brief Helper function which determines if a response code is temporal or not */ @@ -1125,7 +1393,7 @@ static int sip_outbound_registration_is_temporal(unsigned int code, } } -static int sip_outbound_registration_send_ubus_event(char *ev_name,int time ,char *client) +static int sip_outbound_registration_send_ubus_event(char *ev_name, int time, char *client) { struct blob_buf blob; int res = 0; @@ -1159,7 +1427,7 @@ static int sip_outbound_registration_send_ubus_event(char *ev_name,int time ,cha } static void schedule_retry(struct registration_response *response, unsigned int interval, - const char *server_uri, const char *client_uri) + const char *server_uri, const char *client_uri, const char *client_num) { update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); schedule_registration(response->client_state, interval); @@ -1168,10 +1436,12 @@ static void schedule_retry(struct registration_response *response, unsigned int ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on " "registration attempt to '%s', retrying in '%u'\n", response->code, server_uri, client_uri, interval); + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Temporal fail response received"); } else { ast_log(LOG_WARNING, "No response received from '%s' on " "registration attempt to '%s', retrying in '%u'\n", server_uri, client_uri, interval); + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "No response received"); } sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); } @@ -1377,49 +1647,206 @@ static void mwi_send_subscribe(struct ast_sip_endpoint *endpoint, pj_uint32_t ex ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create MWI subsription\n", ast_sorcery_object_get_id(endpoint)); ao2_cleanup(contact); - return NULL; + return; } dlg = ast_sip_create_dialog_uac(endpoint, contact->uri, NULL); ao2_cleanup(contact); if (!dlg) { ast_log(LOG_WARNING, "Unable to create dialog for MWI subscription\n"); - return NULL; + return; } pjsip_mwi_init_module(ast_sip_get_pjsip_endpoint(), pjsip_mwi_instance()); - pjsip_mwi_create_uac(dlg, &mwi_cb, NULL, &evsub); + pjsip_mwi_create_uac(dlg, &mwi_cb, 0, &evsub); pjsip_evsub_set_mod_data( evsub, mod_mwi.id, endpoint); pjsip_mwi_initiate (evsub, expires, &tdata); pjsip_mwi_send_request (evsub, tdata); } +/* Check if any client has been registered in the same device */ +static bool has_registered(void) +{ + bool result = false; + + struct sip_outbound_registration *registration; + struct ao2_container *registrations; + struct ao2_iterator i; + struct sip_outbound_registration_state *state; + + registrations = get_registrations(); + if (!registrations) { + return false; + } + + i = ao2_iterator_init(registrations, 0); + while ((registration = ao2_iterator_next(&i))) { + state = get_state(ast_sorcery_object_get_id(registration)); + if (state && state->client_state->status == SIP_REGISTRATION_REGISTERED) { + result = true; + } + ao2_ref(state, -1); + ao2_ref(registration, -1); + if (result) { + break; + } + } + ao2_iterator_destroy(&i); + ao2_ref(registrations, -1); + return result; +} + +/* REGISTER attempts between two addresses in the record list */ +static ast_sched_cb registration_resend(struct registration_response *response) +{ + struct sip_outbound_registration_client_state *client_state = response->client_state; + pjsip_tx_data *tdata = response->old_request; + pjsip_via_hdr *via; + + if (!client_state || client_state->registration_resend_timer_id == -1 || !tdata) { + ast_debug(2, "Expired registration request, skipping\n"); + return 0; + } + + ast_debug(2, "%s: resend registration on %s\n", + client_state->registration_name, client_state->transport_name); + client_state->registration_resend_timer_id = -1; + if (client_state->status == SIP_REGISTRATION_STOPPED) { + ao2_ref(response, -1); + return 0; + } + + via = (pjsip_via_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); + if (via) + via->branch_param.slen = 0; + + registration_client_send(client_state, tdata); + /* The tdata ref was stolen */ + response->old_request = NULL; + ao2_ref(response, -1); + return 0; +} + +/* switch to next server address if possible */ +static int sip_registration_failover(struct registration_response *response) +{ + pjsip_tx_data *tdata = response->old_request; + + if (tdata && ast_sip_failover_request(tdata)) + return 1; + + ast_debug(2, "%s: No more address to try\n", response->client_state->registration_name); + + return 0; +} + +/* switch to transport2 if possible */ +static int sip_registration_transport2(struct registration_response *response) +{ + struct sip_outbound_registration_client_state *client_state = response->client_state; + pjsip_tx_data *tdata = response->old_request; + struct ast_sip_endpoint *endpoint = NULL; + + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if (endpoint && !ast_strlen_zero(endpoint->transport2)) { + if (ast_strlen_zero(client_state->transport_name) || strcmp(client_state->transport_name, endpoint->transport2)) { + + ast_log(LOG_NOTICE, "%s: Registration transport fallback to '%s'\n", client_state->registration_name, endpoint->transport2); + + // Clean current transport data. Then, pjsip will resolve with transport2. + clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport2); + return 1; + } + } + + return 0; +} + +// Reset the transport back to the primary transport +static void reset_transport_to_primary(struct sip_outbound_registration_client_state *client_state, struct ast_sip_endpoint *endpoint, struct registration_response *response, const char *client_num){ + if (strcmp(client_state->transport_name, endpoint->transport)) { + ast_free(client_state->transport_name); + client_state->transport_name = ast_strdup(endpoint->transport); + + }else if (strncmp(client_state->transport_name, "transport-tls", 13) == 0){ + if ( (NULL != response->old_request) && (NULL != response->old_request->msg) ){ + pjsip_uri *uri = response->old_request->msg->line.req.uri; + if ( NULL != uri ){ + pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri); + if ( NULL != sip_uri ){ + ast_log_dt(LOG_EVENT_CODE_SV116, client_num, sip_uri->host); + } + } + } + } +} +static ast_sched_cb reset_504_flag_after_failed(struct ast_sip_endpoint *endpoint){ + endpoint->failover_reg_addr = 0; + endpoint->failover_reg_addr_timer_id = -1; + return 0; +} /*! \brief Callback function for handling a response to a registration attempt */ static int handle_registration_response(void *data) { struct registration_response *response = data; + struct sip_outbound_registration_client_state *client_state = response->client_state; pjsip_regc_info info; char server_uri[PJSIP_MAX_URL_SIZE]; char client_uri[PJSIP_MAX_URL_SIZE]; - - if (response->client_state->status == SIP_REGISTRATION_STOPPED) { + struct sip_outbound_registration *registration = NULL; + pjsip_tx_data *tdata = response->old_request; + char s_uri[PJSIP_MAX_URL_SIZE]; + char c_uri[PJSIP_MAX_URL_SIZE]; + char * client_addr = NULL; + char *client_num = NULL; + char * saveptr = NULL; + + if (client_state->status == SIP_REGISTRATION_STOPPED) { ao2_ref(response, -1); return 0; } - pjsip_regc_get_info(response->client_state->client, &info); + registration = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", client_state->registration_name); + if (!registration) { + ast_log(LOG_ERROR, "No registration found for '%s'\n", client_state->registration_name); + return -1; + } + + pjsip_regc_get_info(client_state->client, &info); ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri)); ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri)); - response->client_state->last_status_code = response->code; + client_state->last_status_code = response->code; + + if (tdata && tdata->dest_info.addr.count){ + char addr[AST_SOCKADDR_BUFLEN] = {'\0'}; + pj_sockaddr_print(&tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr, addr, sizeof(addr), 3); + ast_debug(1, "Processing response %d from %s(%s) for %s(%s)\n", + response->code, addr, server_uri, + client_state->registration_name, client_uri); + } else { + ast_debug(1, "Processing response %d from %s for %s(%s)\n", + response->code, server_uri, client_state->registration_name, client_uri); + ast_debug(1, "No destination addresses available (tdata=%p, addr.count=%d)\n" + "Setting client's retry counter to max_retries(%d) immediately\n", + tdata, tdata ? tdata->dest_info.addr.count : 0, client_state->max_retries); + client_state->retries = client_state->max_retries; + } + + + strncpy(s_uri, server_uri, sizeof(s_uri)-1); + strtok_r(s_uri, ":", &saveptr); + client_addr = strtok_r(NULL, ":", &saveptr); + strncpy(c_uri, client_uri, sizeof(c_uri)-1); + saveptr = NULL; + strtok_r(c_uri, ":", &saveptr); + client_num = strtok_r(NULL, "@", &saveptr); - ast_debug(1, "Processing REGISTER response %d from server '%s' for client '%s'\n", - response->code, server_uri, client_uri); if (response->code == 401 || response->code == 407 || response->code == 494) { /* Store MEDIASEC headers */ - if (response->client_state->mediasec) { + if (client_state->mediasec) { struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", - response->client_state->registration_name); + client_state->registration_name); static const pj_str_t headerName = { "Security-Server", 15 }; pjsip_generic_string_hdr *secSrv = NULL; @@ -1431,7 +1858,7 @@ static int handle_registration_response(void *data) secSrv = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &headerName, NULL); struct security_mechanism *sec_mechanism; while (secSrv) { - response->client_state->is494=0; + client_state->is494=0; sec_mechanism = ast_calloc(1, sizeof(*sec_mechanism) + pj_strlen(&secSrv->hvalue)); if (!sec_mechanism) { @@ -1448,77 +1875,98 @@ static int handle_registration_response(void *data) } } - if (response->code == 408 || response->code == 503 || response->code == 500) { - if ((ast_sip_failover_request(response->old_request))) { - int res = registration_client_send(response->client_state, response->old_request); - /* The tdata ref was stolen */ - response->old_request = NULL; - if (res == PJ_SUCCESS) { - ao2_ref(response, -1); - return 0; + if (response->code == 408 || response->code == 444 || response->code == 503 || response->code == 500 || + response->code == 504 || response->code == 600) { + int retry = 1; + int waiting_time = registration->retry_after; + + ast_log(LOG_NOTICE, "%s: Registration failed %d times (code=%d)\n", + client_state->registration_name, client_state->failures, response->code); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + // Emergency registration related failure case + if (client_state->Emergency_reg_hangup){ + update_client_state_status(client_state, SIP_REGISTRATION_UNREGISTERED); + sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); + + if (client_state->Emergency_reg_ongoing){ + // E-reg failed while E-reg-hangup queued, stop further procedure and waiting for normal reg. + client_state->Emergency_reg_ongoing=false; + ast_log(LOG_NOTICE,"===== E-reg failed while E-reg-hangup queued =====\n"); } + ast_log(LOG_NOTICE,"===== E-reg-hangup and start normal reg =====\n"); + client_state->registration_resend_timer_id = ast_sched_add(sched, 1, registration_resend, response); + return 0; + } + if((!response->retry_after && endpoint && endpoint->failover_reg_addr == 1) && (tdata && tdata->dest_info.addr.count)){ + // 2nd registration on 504 with xml failed, back to the original registration schedule. + endpoint->failover_reg_addr = 2; + + int next_round = client_state->registration_expires - ((int) time(NULL)); + if(next_round>3){ + // update the status back to the original + update_client_state_status(client_state, SIP_REGISTRATION_REGISTERED); + } else { + next_round=3; // wait 3s(2s for reseting the flag + 1s in case of delay) to make sure flag been reset before the new registration. + } + client_state->retries = 0; + client_state->failover_retries = 0; + client_state->waiting_time_upper_bound = 0; + client_state->failures = 0; + client_state->attempts = 0; + schedule_registration(client_state, next_round); + // reset flag after 2s, to make sure chan_voicemngr could retrieve the flag info. + endpoint->failover_reg_addr_timer_id = ast_sched_add(sched, 2000, reset_504_flag_after_failed, endpoint); + return 0; + } + + if (response->retry_after){ + waiting_time = response->retry_after; + } else if (response->code == 444 || client_state->attempts >= 2 || !tdata || !tdata->dest_info.addr.count || client_state->waiting_time_upper_bound) { + /* failover directly when not received any response(444 from pjsip), or received twice with other response, or under recovery-flow(second round), + or no DNS records are found */ + client_state->attempts = 0; + retry = sip_registration_failover(response); + } + + if (retry) { + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Bad response received"); + update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); + sip_outbound_registration_send_ubus_event("UNREGISTERED", response->expiration, client_uri); + + if (response->code == 444 || client_state->attempts == 0){ + ast_debug(1, "%s: Registration re-try to the next address immediately.\n", client_state->registration_name); + client_state->registration_resend_timer_id = ast_sched_add(sched, 1, registration_resend, response); + } else { + ast_debug(1, "%s: Registration re-try after %d seconds.\n", client_state->registration_name, waiting_time); + client_state->registration_resend_timer_id = ast_sched_add(sched, waiting_time * 1000, registration_resend, response); + } + return 0; + } else if (client_state->Emergency_reg_ongoing) { + ast_log(LOG_NOTICE,"===== E-reg failed on all addr before E-reg-hangup being triggered, stop and send normal reg direct=====\n"); + client_state->Emergency_reg_ongoing=false; + client_state->Emergency_reg_hangup=true; + update_client_state_status(client_state, SIP_REGISTRATION_UNREGISTERED); + sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); + client_state->registration_resend_timer_id = ast_sched_add(sched, 1, registration_resend, response); + return 0; } - // change the condition to use iopsys mediasec implementation instead of asterisk one - //} else if ((response->code == 401 || response->code == 407 || response->code == 494) } else if ((response->code == 401 || response->code == 407) - && (!response->client_state->auth_attempted - || response->rdata->msg_info.cseq->cseq != response->client_state->auth_cseq)) { + && (!client_state->auth_attempted + || response->rdata->msg_info.cseq->cseq != client_state->auth_cseq)) { int res; pjsip_cseq_hdr *cseq_hdr; pjsip_tx_data *tdata; - // comment out this code to use iopsys mediasec implementation instead of asterisk one - //if (response->client_state->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) { - // struct sip_outbound_registration *reg = NULL; - // struct ast_sip_endpoint *endpt = NULL; - // struct ao2_container *contact_container = NULL; - // pjsip_generic_string_hdr *header; - // struct pjsip_generic_string_hdr_vector header_vector; - // static const pj_str_t security_server = { "Security-Server", 15 }; - // - // if ((reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", - // response->client_state->registration_name)) && reg->endpoint && - // (endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint))) { - // /* Retrieve all contacts associated with aors from this endpoint (if set). */ - // contact_container = ast_sip_location_retrieve_contacts_from_aor_list(endpt->aors); - // } - // /* Add server list of security mechanism to client_state and contact status if exists. */ - // AST_VECTOR_INIT(&header_vector, 1); - // header = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &security_server, NULL); - // for (; header; - // header = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &security_server, header->next)) { - // AST_VECTOR_APPEND(&header_vector, header); - // ast_sip_header_to_security_mechanism(header, &response->client_state->server_security_mechanisms); - // } - // if (contact_container) { - // /* Add server security mechanisms to contact status of all associated contacts to be able to send correct - // * Security-Verify headers on subsequent non-REGISTER requests through this outbound registration. - // */ - // ao2_callback(contact_container, OBJ_NODATA, contact_add_security_headers_to_status, &header_vector); - // ao2_cleanup(contact_container); - // } - // AST_VECTOR_FREE(&header_vector); - // ao2_cleanup(endpt); - // ao2_cleanup(reg); - //} - // - //if (response->code == 494) { - // update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); - // response->client_state->retries++; - // schedule_registration(response->client_state, 0); - // ao2_ref(response, -1); - // return 0; - //} else if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths, - if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths, + if (!ast_sip_create_request_with_auth(&client_state->outbound_auths, response->rdata, response->old_request, &tdata)) { - response->client_state->auth_attempted = 1; + client_state->auth_attempted = 1; ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n", server_uri, client_uri); /* Add MEDIASEC headers */ - if (response->client_state->mediasec) { + if (client_state->mediasec) { struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", - response->client_state->registration_name); + client_state->registration_name); if (!endpoint) { ast_log(LOG_ERROR, "No endpoint found to store/add mediasec headers\n"); @@ -1535,14 +1983,16 @@ static int handle_registration_response(void *data) pjsip_tx_data_add_ref(tdata); - res = registration_client_send(response->client_state, tdata); + res = registration_client_send(client_state, tdata); /* Save the cseq that actually got sent. */ cseq_hdr = (pjsip_cseq_hdr *) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); - response->client_state->auth_cseq = cseq_hdr->cseq; + client_state->auth_cseq = cseq_hdr->cseq; pjsip_tx_data_dec_ref(tdata); if (res == PJ_SUCCESS) { + client_state->failures--; // not count for auth re-send + client_state->attempts--; // not count for auth re-send ao2_ref(response, -1); return 0; } @@ -1556,9 +2006,27 @@ static int handle_registration_response(void *data) ast_sip_resolve_track_cur_addr(response->old_request); } - response->client_state->auth_attempted = 0; + client_state->auth_attempted = 0; + client_state->cached_security_verify = 0; if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) { + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + static const pj_str_t security_verify = { "Security-Verify", 15 }; + + if(response->old_request && pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_AUTHORIZATION, NULL)){ + if(endpoint){ + endpoint->cached_auth = true; + } + ast_debug(8, "Outbound registration request success with Authorization.\n"); + if(pjsip_msg_find_hdr_by_name(response->old_request->msg, &security_verify, NULL)){ + client_state->cached_security_verify = 1; + ast_debug(8, "Outbound registration request success with Security-Verify.\n"); + } + } else { + if(endpoint){ + endpoint->cached_auth = false; + } + } /* Check if this is in regards to registering or unregistering * Retrieve the requested expiration if possible, * and check it combined with the received expiration to identify if it is reg or un-reg @@ -1568,27 +2036,71 @@ static int handle_registration_response(void *data) if(response->old_request && (expires = pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_EXPIRES, NULL))){ exp_expiration = expires->ivalue; } + // DNS resolve: stick to the server address + ast_sip_resolve_track_cur_addr(response->old_request); + + save_response_fields_to_transport(response); + if (exp_expiration && response->expiration) { int next_registration_round; - response->client_state->is494=0; + client_state->is494=0; + + /* update register dest info */ + if (endpoint->register_dest) { + if (endpoint->register_dest->host) + ast_free(endpoint->register_dest->host); + + ast_free(endpoint->register_dest); + } + + endpoint->register_dest = ast_malloc(sizeof(struct pjsip_register_dest)); + + endpoint->register_dest->cur_addr = tdata->dest_info.cur_addr; + memcpy(&endpoint->register_dest->addr, &tdata->dest_info.addr, sizeof(pjsip_server_addresses)); + + endpoint->register_dest->host = ast_calloc(1, pj_strlen(&tdata->dest_info.name) + 1); + ast_copy_pj_str(endpoint->register_dest->host, &tdata->dest_info.name, pj_strlen(&tdata->dest_info.name) + 1); + char addr_buf[AST_SOCKADDR_BUFLEN] = {'\0'}; + pj_sockaddr_print(&endpoint->register_dest->addr.entry[endpoint->register_dest->cur_addr].addr, addr_buf, sizeof(addr_buf), 1); /* If the registration went fine simply reschedule registration for the future */ - ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri); - update_client_state_status(response->client_state, SIP_REGISTRATION_REGISTERED); + ast_debug(1, "Outbound registration to '%s' with client '%s' successful to addr: %s\n", server_uri, client_uri, addr_buf); + + update_client_state_status(client_state, SIP_REGISTRATION_REGISTERED); + if (endpoint->register_transport) + ast_free(endpoint->register_transport); + endpoint->register_transport = ast_strdup(client_state->transport_name); + ast_log_dt(LOG_EVENT_CODE_V101, client_num, client_addr); sip_outbound_registration_send_ubus_event("REGISTERED",response->expiration,client_uri); - response->client_state->retries = 0; - response->client_state->failover_retries = 0; - next_registration_round = response->expiration - REREGISTER_BUFFER_TIME; + client_state->retries = 0; + client_state->failover_retries = 0; + client_state->waiting_time_upper_bound = 0; + client_state->failures = 0; + client_state->attempts = 0; + next_registration_round = (response->expiration * client_state->retry_percent) / 100; + ast_debug(1, "Setting next retry in %d seconds (%d percent of %d)\n", + next_registration_round, client_state->retry_percent, response->expiration); + if (next_registration_round < 0) { /* Re-register immediately. */ next_registration_round = 0; } - schedule_registration(response->client_state, next_registration_round); + + if (client_state->Emergency_reg_ongoing){ + endpoint->Emergency=true; + client_state->Emergency_reg_ongoing=false; + } else if (!endpoint->Emergency) { + // Reset the transport back to the primary transport + reset_transport_to_primary(client_state, endpoint, response, client_num); + } + endpoint->failover_reg_addr = 0; + + schedule_registration(client_state, next_registration_round); /* See if we should monitor for transport shutdown */ if (PJSIP_TRANSPORT_IS_RELIABLE(response->rdata->tp_info.transport)) { registration_transport_monitor_setup(response->transport_key, - response->client_state->registration_name); + client_state->registration_name); } } else { if (!exp_expiration && !response->expiration) { @@ -1597,102 +2109,177 @@ static int handle_registration_response(void *data) // case1: unregistration request with expiration=0, but got response with non-zero expiration; // case2: registration request with non-zero expiration but got expiration=0 in response ast_debug(1, "Outbound registration/unregistration to '%s' with client '%s' failed, expiration not valid\n", server_uri, client_uri); + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Expiration not valid"); } - response->client_state->is494=0; - update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED); + client_state->is494=0; + update_client_state_status(client_state, SIP_REGISTRATION_UNREGISTERED); sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); + if (PJSIP_TRANSPORT_IS_RELIABLE(response->rdata->tp_info.transport)) { ast_sip_transport_monitor_unregister_key(response->transport_key, - registration_transport_shutdown_cb, response->client_state->registration_name, + registration_transport_shutdown_cb, client_state->registration_name, monitor_matcher); } } - save_response_fields_to_transport(response); - - struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", response->client_state->registration_name); if (strlen(endpoint->incoming_mwi_mailbox)) mwi_send_subscribe(endpoint, response->expiration); - } else if (response->client_state->destroy) { + if (endpoint){ + if (endpoint->realms){ + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + check_and_update_nextnonce(&endpoint->auth_sess_reg, endpoint->realms, response->rdata); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } + if (endpoint->cached_auth){ + //if cached, sync to auth_sess_inv, nc, nonce + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_debug(3, "Sync cached auth sess from auth_sess_reg to auth_sess_inv\n"); + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_reg, &endpoint->auth_sess_inv, &realms_pj_str); // (from, to, realm) + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } + } + + } else if (client_state->destroy) { /* We need to deal with the pending destruction instead. */ } else if (response->code == 494) { - if (response->client_state->is494) { + if (client_state->is494) { ast_log(LOG_WARNING, "MEDIASEC registration to '%s' with client '%s' failed (494-loop detected), stopping registration attempt\n", server_uri, client_uri); + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "494-loop detected"); /* 494 loop detected! This is fatal! */ - update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT); + update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT); sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); + /* reset is494 */ - response->client_state->is494=0; + client_state->is494=0; } else { /* Try (initial) registration again - but now with additional headers */ - response->client_state->is494=1; - ao2_ref(response->client_state, +1); - handle_client_registration(response->client_state); + client_state->is494=1; + ao2_ref(client_state, +1); + handle_client_registration(client_state); ao2_ref(response, -1); return 0; } - } else if (response->retry_after) { + } else if (response->retry_after && tdata && tdata->dest_info.addr.count) { /* If we have been instructed to retry after a period of time, schedule it as such */ - schedule_retry(response, response->retry_after, server_uri, client_uri); - } else if (response->client_state->failover_max_retries && - (response->code == 408 || response->code == 503 || response->code == 500)) { - if (response->client_state->failover_max_retries >= 0 && - response->client_state->failover_retries >= response->client_state->failover_max_retries) { - update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT); + schedule_retry(response, response->retry_after, server_uri, client_uri, client_num); + } else if (client_state->failover_max_retries && + (response->code == 408 || response->code == 444 || response->code == 503 || response->code == 500)) { + if (client_state->failover_max_retries >= 0 && + client_state->failover_retries >= client_state->failover_max_retries) { + update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT); sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); - ast_log(LOG_WARNING, "Maximum failover retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n", - server_uri, client_uri); + + ast_log(LOG_WARNING, "Maximum failover retries reached when attempting outbound registration to '%s' with " + "client '%s', stopping registration attempt\n", server_uri, client_uri); + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Maximum failover retries reached"); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if ((strncmp(endpoint->transport, "transport-tls", 13) == 0) && ast_strlen_zero(endpoint->transport2)){ + ast_log_dt(LOG_EVENT_CODE_SV113); + } else if(!ast_strlen_zero(endpoint->transport2) && strncmp(client_state->transport_name, "transport-udp", 13)) { + ast_log_dt(LOG_EVENT_CODE_SV112); + } } else { unsigned int retry_interval; // If tried all addresses for 3 times, then start to use retry interval2, otherwise interval1 - ++response->client_state->failover_retries; - if (!response->old_request || response->client_state->failover_retries % (3 * response->old_request->dest_info.addr.count)) - retry_interval = response->client_state->failover_retry_interval1; + if (response->old_request && ++client_state->failover_retries % (3 * response->old_request->dest_info.addr.count)) + retry_interval = client_state->failover_retry_interval1; else - retry_interval = response->client_state->failover_retry_interval2; + retry_interval = client_state->failover_retry_interval2; - ast_log(LOG_NOTICE, "Schedule failover retry [%d] in %d seconds", response->client_state->failover_retries, retry_interval); - schedule_retry(response, retry_interval, server_uri, client_uri); + ast_log(LOG_NOTICE, "Schedule failover retry [%d] in %d seconds", client_state->failover_retries, retry_interval); + client_state->retries++; + schedule_retry(response, retry_interval, server_uri, client_uri, client_num); + } + } else if (client_state->retry_interval && + sip_outbound_registration_is_temporal(response->code, client_state)) { + if (!tdata || !tdata->dest_info.addr.count) { + client_state->retries = client_state->max_retries; + ast_log(LOG_ERROR, "No destination addresses available (tdata=%p, addr.count=%d)\n" + "Setting client's retry counter to max_retries(%d) immediately\n", + tdata, tdata ? tdata->dest_info.addr.count : 0, client_state->max_retries); } - } else if (response->client_state->retry_interval - && sip_outbound_registration_is_temporal(response->code, response->client_state)) { - if (response->client_state->retries == response->client_state->max_retries) { + + if (client_state->retries == client_state->max_retries) { /* If we received enough temporal responses to exceed our maximum give up permanently */ - update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT); + update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT); sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); + ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n", server_uri, client_uri); + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Maximum retries reached"); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if ((strncmp(endpoint->transport, "transport-tls", 13) == 0) && ast_strlen_zero(endpoint->transport2)){ + ast_log_dt(LOG_EVENT_CODE_SV113); + } else if((strncmp(client_state->transport_name, "transport-udp", 13) == 0) &&!ast_strlen_zero(endpoint->transport2)) { + ast_log_dt(LOG_EVENT_CODE_SV112); + } } else { /* On the other hand if we can still try some more do so */ - response->client_state->retries++; - schedule_retry(response, response->client_state->retry_interval, server_uri, client_uri); + client_state->retries++; + schedule_retry(response, client_state->retry_interval, server_uri, client_uri, client_num); + } + } else if (response->code == 408 || response->code == 444 || response->code == 503 || response->code == 500 || response->code == 504 || response->code == 600) { + int waiting_time = registration->retry_after; + + // All addresses had been tried, now try transport2 if possible + if ((client_state->failures >= 2 || !tdata || !tdata->dest_info.addr.count) && !sip_registration_transport2(response)) { + // if transport2 not available and failed twice or more, then wait as RFC5626 required + if (client_state->waiting_time_upper_bound < registration->retry_max_time){ + int base_time = has_registered() ? registration->retry_base_time2 : registration->retry_base_time1; + client_state->waiting_time_upper_bound = + MIN(registration->retry_max_time, base_time * (int)pow(2.0, (double)client_state->failures)); + ast_log(LOG_NOTICE, "The upper_bound wait time set to: '%d = min(%d, %d * 2 ^ %d)' seconds\n", + client_state->waiting_time_upper_bound, registration->retry_max_time, base_time, client_state->failures); + } else { + ast_log(LOG_NOTICE, "The upper_bound wait time was set to %d seconds\n", client_state->waiting_time_upper_bound); + } + ast_log(LOG_NOTICE, "Queue registration recovery flow for %s\n", response->client_state->registration_name); + queue_registration_recovery_flow(client_state->registration_name); + } else { + ast_debug(1, "%s: Registration re-try after %d seconds.\n", client_state->registration_name, waiting_time); + schedule_retry(response, waiting_time, server_uri, client_uri, client_num); } } else { + if (!tdata || !tdata->dest_info.addr.count) { + client_state->retries = client_state->max_retries; + ast_log(LOG_ERROR, "No destination addresses available (tdata=%p, addr.count=%d)\n" + "Setting client's retry counter to max_retries(%d) immediately\n", + tdata, tdata ? tdata->dest_info.addr.count : 0, client_state->max_retries); + } if (response->code == 403 - && response->client_state->forbidden_retry_interval - && response->client_state->retries < response->client_state->max_retries) { + && client_state->forbidden_retry_interval + && client_state->retries < client_state->max_retries) { /* A forbidden response retry interval is configured and there are retries remaining */ - update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); + update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); - response->client_state->retries++; - schedule_registration(response->client_state, response->client_state->forbidden_retry_interval); + client_state->retries++; + schedule_registration(client_state, client_state->forbidden_retry_interval); ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n", - server_uri, client_uri, response->client_state->forbidden_retry_interval); - } else if (response->client_state->fatal_retry_interval - && response->client_state->retries < response->client_state->max_retries) { + server_uri, client_uri, client_state->forbidden_retry_interval); + } else if (client_state->fatal_retry_interval + && client_state->retries < client_state->max_retries) { /* Some kind of fatal failure response received, so retry according to configured interval */ - update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); + update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); - response->client_state->retries++; - schedule_registration(response->client_state, response->client_state->fatal_retry_interval); + client_state->retries++; + schedule_registration(client_state, client_state->fatal_retry_interval); ast_log(LOG_WARNING, "'%d' fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n", - response->code, server_uri, client_uri, response->client_state->fatal_retry_interval); + response->code, server_uri, client_uri, client_state->fatal_retry_interval); } else { /* Finally if there's no hope of registering give up */ - update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT); + update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT); sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if ((strncmp(endpoint->transport, "transport-tls", 13) == 0) && ast_strlen_zero(endpoint->transport2)){ + ast_log_dt(LOG_EVENT_CODE_SV113); + } else if((strncmp(client_state->transport_name, "transport-udp", 13) == 0) &&!ast_strlen_zero(endpoint->transport2)) { + ast_log_dt(LOG_EVENT_CODE_SV112); + } if (response->rdata) { ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n", response->code, server_uri, client_uri); @@ -1700,15 +2287,17 @@ static int handle_registration_response(void *data) ast_log(LOG_WARNING, "Fatal registration attempt to '%s', stopping outbound registration\n", client_uri); } } + + ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Fatal response received"); } ast_system_publish_registry("PJSIP", client_uri, server_uri, - sip_outbound_registration_status_str(response->client_state->status), NULL); + sip_outbound_registration_status_str(client_state->status), NULL); - if (response->client_state->destroy) { + if (client_state->destroy) { /* We have a pending deferred destruction to complete now. */ - ao2_ref(response->client_state, +1); - handle_client_state_destruction(response->client_state); + ao2_ref(client_state, +1); + handle_client_state_destruction(client_state); } ao2_ref(response, -1); @@ -1743,6 +2332,17 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par struct sip_outbound_registration_client_state *client_state = param->token; struct registration_response *response; int *callback_invoked; + pjsip_regc_info info; + char client_uri[PJSIP_MAX_URL_SIZE]; + char *client_num = NULL; + char * saveptr = NULL; + pjsip_expires_hdr *expires = NULL; + pjsip_regc_get_info(client_state->client, &info); + ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri)); + + + strtok_r(client_uri, ":", &saveptr); + client_num = strtok_r(NULL, "@", &saveptr); callback_invoked = ast_threadstorage_get(®ister_callback_invoked, sizeof(int)); @@ -1773,6 +2373,31 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par ast_debug(1, "Received REGISTER response %d(%.*s)\n", param->code, (int) param->reason.slen, param->reason.ptr); + + /* get expire to distinguish register and unregister */ + int exp_expiration = response->expiration; + if(response->old_request && (expires = pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_EXPIRES, NULL))){ + exp_expiration = expires->ivalue; + } + + if((param->code != 200) && (param->code != 401) && (param->code != 407) && (client_num != NULL)){ + /* registration */ + if(0 != exp_expiration){ + if(strstr(param->reason.ptr, "PJLIB_UTIL_EDNSNOANSWERREC") != NULL && response->code == 503){ + /* V001 */ + ast_log_dt(LOG_EVENT_CODE_V001, client_num); + } + if(response->code == 408 || response->code == 404){ + /* V004 */ + ast_log_dt(LOG_EVENT_CODE_V004 ,client_num); + } + } + /* unregistration */ + else{ + /* V005 */ + ast_log_dt(LOG_EVENT_CODE_V005,client_num, (int) param->reason.slen, param->reason.ptr); + } + } if (param->rdata) { struct pjsip_retry_after_hdr *retry_after; @@ -1858,6 +2483,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a { struct sip_outbound_registration_state *state; char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1]; + struct ast_sip_endpoint *endpoint = NULL; state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy); if (!state) { @@ -1873,9 +2499,10 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a state->client_state->status = SIP_REGISTRATION_UNREGISTERED; pj_timer_entry_init(&state->client_state->timer, 0, state->client_state, sip_outbound_registration_timer_cb); - state->client_state->transport_name = ast_strdup(registration->transport); - state->client_state->registration_name = - ast_strdup(ast_sorcery_object_get_id(registration)); + state->client_state->registration_name = ast_strdup(ast_sorcery_object_get_id(registration)); + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + if (endpoint) + state->client_state->transport_name = ast_strdup(endpoint->transport); ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0); ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0, @@ -2132,6 +2759,18 @@ static int set_outbound_initial_authentication_credentials(pjsip_regc *regc, ast_free((char *) access_token); } break; + case AST_SIP_AUTH_TYPE_USER_PASS: + pj_cstr(&auth_creds[0].realm, auths[idx]->realm); + pj_cstr(&auth_creds[0].scheme, "digest"); + pj_cstr(&auth_creds[0].username, auths[idx]->auth_user); + auth_creds[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + pj_cstr(&auth_creds[0].data, auths[idx]->auth_pass); + pjsip_regc_set_credentials(regc, 1, auth_creds); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", ast_sorcery_object_get_id(auths[idx])); + endpoint->realms = ast_strdup(auths[idx]->realm); + // set credentials for local auth_sess endpoint->auth_sess_reg/auth_sess_inv + pjsip_auth_clt_set_credentials(&endpoint->auth_sess_reg, 1, auth_creds); + pjsip_auth_clt_set_credentials(&endpoint->auth_sess_inv, 1, auth_creds); default: /* other cases handled after receiving auth rejection */ break; @@ -2201,7 +2840,11 @@ static int sip_outbound_registration_regc_alloc(void *data) return -1; } - ast_sip_set_tpselector_from_transport_name(registration->transport, &selector); + if (ast_sip_set_tpselector_from_transport_name(state->client_state->transport_name, &selector)) { + ast_log(LOG_ERROR, "Failed to set tpselector for %s\n", state->client_state->transport_name); + return -1; + } + pjsip_regc_set_transport(state->client_state->client, &selector); if (!ast_strlen_zero(registration->outbound_proxy)) { @@ -2245,6 +2888,77 @@ static int sip_outbound_registration_regc_alloc(void *data) return -1; } + struct pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + pool = pjsip_endpt_create_pool(endpt, "sip_auth_session_reg%p", 1024, 1024); + if(pjsip_auth_clt_init(&endpoint->auth_sess_reg, endpt, pool, 0) != PJ_SUCCESS){ + ast_log(LOG_WARNING, "auth_sess_reg init failed!\n"); + }else{ + ast_mutex_init(&endpoint->auth_sess_reg_lock); + } + pool = pjsip_endpt_create_pool(endpt, "sip_auth_session_inv%p", 1024, 1024); + if(pjsip_auth_clt_init(&endpoint->auth_sess_inv, endpt, pool, 0) != PJ_SUCCESS){ + ast_log(LOG_WARNING, "auth_sess_inv init failed!\n"); + }else{ + ast_mutex_init(&endpoint->auth_sess_inv_lock); + } + return 0; +} +/*! \brief Helper function which performs a single registration for recovery flow*/ +static int sip_outbound_registration_recovery(void *data) +{ + struct sip_outbound_registration_state *state = data; + struct sip_outbound_registration *registration = ao2_bump(state->registration); + size_t i; + + /* Just in case the client state is being reused for this registration, free the auth information */ + ast_sip_auth_vector_destroy(&state->client_state->outbound_auths); + ast_sip_security_mechanisms_vector_destroy(&state->client_state->security_mechanisms); + ast_sip_security_mechanisms_vector_destroy(&state->client_state->server_security_mechanisms); + + AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(®istration->outbound_auths)); + for (i = 0; i < AST_VECTOR_SIZE(®istration->outbound_auths); ++i) { + char *name = ast_strdup(AST_VECTOR_GET(®istration->outbound_auths, i)); + + if (name && AST_VECTOR_APPEND(&state->client_state->outbound_auths, name)) { + ast_free(name); + } + } + ast_sip_security_mechanisms_vector_copy(&state->client_state->security_mechanisms, + ®istration->security_mechanisms); + state->client_state->retry_interval = registration->retry_interval; + state->client_state->retry_percent = registration->retry_percent; + state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval; + state->client_state->fatal_retry_interval = registration->fatal_retry_interval; + state->client_state->max_retries = registration->max_retries; + if (state->client_state->registration_resend_timer_id != -1) { + if (ast_sched_del(sched, state->client_state->registration_resend_timer_id)) { + ast_log(LOG_ERROR, "Failed to remove scheduled registration resend timer\n"); + } + state->client_state->registration_resend_timer_id = -1; + } + state->client_state->failover_retry_interval1 = registration->failover_retry_interval1; + state->client_state->failover_retry_interval2 = registration->failover_retry_interval2; + state->client_state->failover_max_retries = registration->failover_max_retries; + state->client_state->support_path = registration->support_path; + state->client_state->mediasec = registration->mediasec; + state->client_state->support_outbound = registration->support_outbound; + state->client_state->security_negotiation = registration->security_negotiation; + state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent; + + pjsip_regc_update_expires(state->client_state->client, registration->expiration); + + // waiting_time is randomly between 50-100% of upper_bound according to RFC5626 Section 4.5 + int waiting_time = RFC5626_RETRY_DELAY(state->client_state->waiting_time_upper_bound); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + if (endpoint && endpoint->failover_reg_addr == 1){ + waiting_time = 1; // Trigger registration immediately for 504 with xml re-register during INVITE + } + ast_log(LOG_NOTICE, "Schedule registration for recovery flow after %d s\n", waiting_time); + schedule_registration(state->client_state, waiting_time); + + ao2_ref(registration, -1); + ao2_ref(state, -1); return 0; } @@ -2272,14 +2986,24 @@ static int sip_outbound_registration_perform(void *data) ast_sip_security_mechanisms_vector_copy(&state->client_state->security_mechanisms, ®istration->security_mechanisms); state->client_state->retry_interval = registration->retry_interval; + state->client_state->retry_percent = registration->retry_percent; state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval; state->client_state->fatal_retry_interval = registration->fatal_retry_interval; state->client_state->max_retries = registration->max_retries; state->client_state->retries = 0; + if (state->client_state->registration_resend_timer_id != -1) { + if (ast_sched_del(sched, state->client_state->registration_resend_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled registration resend timer\n"); + } + state->client_state->registration_resend_timer_id = -1; + } state->client_state->failover_retry_interval1 = registration->failover_retry_interval1; state->client_state->failover_retry_interval2 = registration->failover_retry_interval2; state->client_state->failover_max_retries = registration->failover_max_retries; state->client_state->failover_retries = 0; + state->client_state->waiting_time_upper_bound = 0; + state->client_state->failures = 0; + state->client_state->attempts = 0; state->client_state->support_path = registration->support_path; state->client_state->mediasec = registration->mediasec; state->client_state->support_outbound = registration->support_outbound; @@ -2290,13 +3014,67 @@ static int sip_outbound_registration_perform(void *data) pjsip_regc_update_expires(state->client_state->client, registration->expiration); /* n mod 0 is undefined, so don't let that happen */ - schedule_registration(state->client_state, (max_delay ? ast_random() % max_delay : 0) + 1); - + if(state->client_state->Emergency_reg_ongoing){ + schedule_registration(state->client_state, 1); + } else { + schedule_registration(state->client_state, (max_delay ? ast_random() % max_delay : 0) + 1); + } ao2_ref(registration, -1); ao2_ref(state, -1); return 0; } +/*! \brief Helper function which performs re-registration with new state info during recovery flow */ +static void re_register_with_new_state(const struct ast_sorcery *sorcery, struct sip_outbound_registration *applied) +{ + RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup); + RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup); + RAII_VAR(struct sip_outbound_registration_state *, new_state, NULL, ao2_cleanup); + + if (!states) { + /* Global container has gone. Likely shutting down. */ + return; + } + state = ao2_find(states, ast_sorcery_object_get_id(applied), OBJ_SEARCH_KEY); + if (state){ + if (!(new_state = sip_outbound_registration_state_alloc(applied))) { + return; + } + + if (ast_sip_push_task_wait_serializer(new_state->client_state->serializer, + sip_outbound_registration_regc_alloc, new_state)) { + return; + } + ast_debug(4, "Re-applying configuration to outbound registration '%s'\n", ast_sorcery_object_get_id(applied)); + + new_state->client_state->retries = state->client_state->retries; + new_state->client_state->failover_retries = state->client_state->failover_retries; + new_state->client_state->waiting_time_upper_bound = state->client_state->waiting_time_upper_bound; + new_state->client_state->failures = state->client_state->failures; + new_state->client_state->attempts = 0; + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + if (endpoint && endpoint->failover_reg_addr==1){ + // keep the original expires for 504 re-register in case of failure. + new_state->client_state->registration_expires = state->client_state->registration_expires; + } + + if (ast_sip_push_task(new_state->client_state->serializer, + sip_outbound_registration_recovery, ao2_bump(new_state))) { + ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", + ast_sorcery_object_get_id(new_state->registration)); + ao2_ref(new_state, -1); + return; + } + + ao2_lock(states); + if (state) { + ao2_unlink(states, state); + ao2_cleanup(state); + } + ao2_link(states, new_state); + ao2_unlock(states); + } +} /*! \brief Apply function which finds or allocates a state structure */ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj) { @@ -2363,7 +3141,10 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo sip_outbound_registration_regc_alloc, new_state)) { return -1; } - + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", new_state->client_state->registration_name); + endpoint->failover_reg_addr=0; + endpoint->failover_reg_addr_timer_id = -1; if (ast_sip_push_task(new_state->client_state->serializer, sip_outbound_registration_perform, ao2_bump(new_state))) { ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n", @@ -2382,6 +3163,39 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo return 0; } +/* for counting register request that has been sent out before success response */ +static pj_status_t log_register_on_tx_msg(pjsip_tx_data *tdata) +{ + SCOPE_ENTER(1, "log_register_on_tx_msg, check and update register request count\n"); + pjsip_cseq_hdr *cseq; + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + + if(!pj_strcmp2(&cseq->method.name, "REGISTER")) { + struct sip_outbound_registration_client_state *client_state = ast_sip_mod_data_get(tdata->mod_data, log_register_module.id, "client_state_on_register"); + if(client_state){ + client_state->failures++; + ast_log(LOG_NOTICE, "%s, requests sent:%d\n",client_state->registration_name, client_state->failures); + } + } + SCOPE_EXIT_RTN_VALUE(PJ_SUCCESS); +} +static pj_status_t emergency_hack_on_tx_message(pjsip_tx_data *tdata) +{ + ast_debug(8, "===== emergency_hack_on_tx_message, check and stop the unwanted transmission =====\n"); + pjsip_cseq_hdr *cseq; + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + + if(!pj_strcmp2(&cseq->method.name, "REGISTER")) { + struct sip_outbound_registration_client_state *client_state = ast_sip_mod_data_get(tdata->mod_data, log_register_module.id, "client_state_on_register"); + if(client_state){ + if(client_state->Emergency_reg_hangup && client_state->Emergency_reg_ongoing){ + ast_log(LOG_NOTICE, "===== skip unwanted transmission =====\n"); + return -1; + } + } + } + return PJ_SUCCESS; +} static int security_mechanism_to_str(const void *obj, const intptr_t *args, char **buf) { const struct sip_outbound_registration *registration = obj; @@ -2454,25 +3268,20 @@ static int unregister_task(void *obj) struct sip_outbound_registration_state *state = obj; struct pjsip_regc *client = state->client_state->client; pjsip_tx_data *tdata; - pjsip_regc_info info; - - pjsip_regc_get_info(client, &info); + pjsip_uri *uri = pjsip_parse_uri(pjsip_regc_get_pool(client), state->registration->client_uri, strlen(state->registration->client_uri), 0); + pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri); ast_debug(1, "Unregistering contacts with server '%s' from client '%s'\n", state->registration->server_uri, state->registration->client_uri); cancel_registration(state->client_state); - if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS - && add_configured_supported_headers(state->client_state, tdata)) { - if (state->client_state->mediasec) { - struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", - state->client_state->registration_name); - ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec"); - ast_sip_add_header(tdata,"Proxy-Require","mediasec"); - ast_sip_add_header(tdata,"Require","mediasec"); - clear_endpoint_security_mechanisms(endpoint); + if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS && + add_configured_supported_headers(state->client_state, tdata)) { + if (state->client_state->status == SIP_REGISTRATION_REGISTERED && registration_client_send(state->client_state, tdata) == PJ_SUCCESS) { + if (sip_uri) + ast_log_dt(LOG_EVENT_CODE_IPX002, (int) sip_uri->user.slen, sip_uri->user.ptr, + (int) sip_uri->host.slen, sip_uri->host.ptr); } - registration_client_send(state->client_state, tdata); } ao2_ref(state, -1); @@ -2501,6 +3310,135 @@ static int queue_register(struct sip_outbound_registration_state *state) return 0; } +void update_emergency_registration_ongoing_status(const char *registration_name, bool action){ + struct sip_outbound_registration_state *state; + state = get_state(registration_name); + if (!state) { + ast_log(LOG_WARNING, "Unable to retrieve registration %s\n", registration_name); + } else { + state->client_state->Emergency_call_ongoing = action; + } + +} + +void queue_registration_recovery_flow(const char *registration_name) +{ + struct sip_outbound_registration_state *state; + state = get_state(registration_name); + if (!state) { + ast_log(LOG_WARNING, "Unable to retrieve registration %s\n", registration_name); + } else { + cancel_registration(state->client_state); + ast_log(LOG_NOTICE, "Queue registration with new state \n"); + re_register_with_new_state(ast_sip_get_sorcery(), state->registration); + } +} + +void check_and_update_nextnonce(pjsip_auth_clt_sess *sess, const char *realm, pjsip_rx_data *rdata){ + pjsip_generic_string_hdr *auth_info = NULL; + static const pj_str_t headerName = { "Authentication-Info", 19 }; + auth_info = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &headerName, NULL); + if(auth_info){ + char value[pj_strlen(&((pjsip_generic_string_hdr*)auth_info)->hvalue) + 1]; + ast_copy_pj_str(value, &((pjsip_generic_string_hdr*)auth_info)->hvalue, sizeof(value)); + char nextnonce[64]; + char *_buf = strstr(value, "nextnonce="); + if (_buf) { + sscanf(_buf, "nextnonce=\"%s", nextnonce); + char *buf_s = strstr(nextnonce, "\""); + nextnonce[((int)strlen(nextnonce)-(int)strlen(buf_s))]='\0'; + ast_log(LOG_NOTICE, "received nextnonce: %s\n", nextnonce); + pj_str_t nextnonce_pj_str = pj_str(nextnonce); + pj_str_t realms_pj_str = pj_str(realm); + pjsip_cached_auth *cached_auth; + cached_auth = pjsip_auth_find_cached_auth(sess, &realms_pj_str); + if (cached_auth) { + pj_strdup(cached_auth->pool, &cached_auth->last_chal->challenge.digest.nonce, &nextnonce_pj_str); + cached_auth->nc=0; + ast_debug(3, "update nextnonce to cached auth\n"); + } + } + } +} + +void sync_cached_auth_between_reg_inv(pjsip_auth_clt_sess *sess_from, pjsip_auth_clt_sess *sess_to, const pj_str_t *realm ) +{ + pjsip_cached_auth *cached_auth_from, *cached_auth_to; + cached_auth_from = pjsip_auth_find_cached_auth(sess_from, realm); + if (cached_auth_from) { + const pjsip_cred_info *cred; + pjsip_authorization_hdr *hauth; + pj_status_t status; + cred = pjsip_auth_find_cred(sess_from, &cached_auth_from->realm, &cached_auth_from->last_chal->scheme ); + if (!cred) + return; + // Find cached_auth for INVITE, create new if not exists. + cached_auth_to = pjsip_auth_find_cached_auth(sess_to, realm); + if (!cached_auth_to) { + cached_auth_to = PJ_POOL_ZALLOC_T(sess_to->pool, pjsip_cached_auth); + cached_auth_to->pool = pjsip_endpt_create_pool(sess_to->endpt, "auth_sync_cli%p", 1024, 1024); + pj_strdup(cached_auth_to->pool, &cached_auth_to->realm, realm); + cached_auth_to->is_proxy = PJ_TRUE; + pj_list_init(&cached_auth_to->cached_hdr); + pj_list_insert_before(&sess_to->cached_auth, cached_auth_to); + ast_debug(8, "New cached auth created\n"); + } + // Sync with cached_auth info + cached_auth_to->nc = cached_auth_from->nc; + cached_auth_to->qop_value = cached_auth_from->qop_value; + cached_auth_to->stale_cnt = cached_auth_from->stale_cnt; + pj_strdup(cached_auth_to->pool, &cached_auth_to->cnonce, &cached_auth_from->cnonce); + pj_pool_t *pool = pjsip_endpt_create_pool(sess_from->endpt, "m_auth_cli%p", 1024, 1024); + pjsip_www_authenticate_hdr *hdr; + if (cached_auth_from->last_chal->type == PJSIP_H_WWW_AUTHENTICATE) + hdr = pjsip_proxy_authenticate_hdr_create(pool); + else + hdr = pjsip_www_authenticate_hdr_create(pool); + + pj_strdup(pool, &hdr->scheme, &cached_auth_from->last_chal->scheme); + if (pj_stricmp2(&hdr->scheme, "digest") == 0) { + pj_strdup(pool, &hdr->challenge.digest.realm, &cached_auth_from->last_chal->challenge.digest.realm); + pj_strdup(pool, &hdr->challenge.digest.domain, &cached_auth_from->last_chal->challenge.digest.domain); + pj_strdup(pool, &hdr->challenge.digest.nonce, &cached_auth_from->last_chal->challenge.digest.nonce); + pj_strdup(pool, &hdr->challenge.digest.opaque, &cached_auth_from->last_chal->challenge.digest.opaque); + hdr->challenge.digest.stale = cached_auth_from->last_chal->challenge.digest.stale; + pj_strdup(pool, &hdr->challenge.digest.algorithm, &cached_auth_from->last_chal->challenge.digest.algorithm); + pj_strdup(pool, &hdr->challenge.digest.qop, &cached_auth_from->last_chal->challenge.digest.qop); + pjsip_param_clone(pool, &hdr->challenge.digest.other_param, + &cached_auth_from->last_chal->challenge.digest.other_param); + } + pjsip_endpt_release_pool(sess_from->endpt, pool); + cached_auth_to->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(cached_auth_to->pool, hdr); + ast_debug(8, "cached auth synced\n"); + } +} + +void de_register_all_with_transport(const char *id) +{ + struct sip_outbound_registration *registration; + struct ao2_container *registrations; + struct ao2_iterator i; + struct sip_outbound_registration_state *state; + + registrations = get_registrations(); + if (!registrations) { + return; + } + + i = ao2_iterator_init(registrations, 0); + while ((registration = ao2_iterator_next(&i))) { + state = get_state(ast_sorcery_object_get_id(registration)); + if (state && state->client_state && state->client_state->status == SIP_REGISTRATION_REGISTERED && strcmp(state->client_state->transport_name, id) == 0) { + queue_unregister(state); + } + ao2_ref(state, -1); + ao2_ref(registration, -1); + } + ao2_iterator_destroy(&i); + ao2_ref(registrations, -1); + return; +} + static void unregister_all(void) { struct ao2_container *states; @@ -2569,20 +3507,27 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg { struct sip_outbound_registration_state *state; const char *registration_name; + bool emergency=false; switch (cmd) { case CLI_INIT: e->command = "pjsip send unregister"; e->usage = - "Usage: pjsip send unregister <registration> | *all\n" - " Unregisters the specified (or all) outbound registration(s) " - "and stops future registration attempts.\n"; + "Usage: pjsip send unregister <registration> | *all | <transport> [e]\n" + " Unregisters the specified(sipX) (or all) outbound registration(s),\n" + " or unregister all registrations that registered with specified transport(transport-X),\n" + " and stops future registration attempts.\n" + " Optional flag e to reset the triggered emergency registration.\n"; return NULL; case CLI_GENERATE: return cli_complete_registration(a->line, a->word, a->pos, a->n); } - if (a->argc != 4) { + if (a->argc == 5 && strcmp(a->argv[4], "e") == 0){ + emergency=true; // user hangup after triggered emergency registration. + } + + if (a->argc != 4 && !emergency) { return CLI_SHOWUSAGE; } @@ -2592,6 +3537,10 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg unregister_all(); ast_cli(a->fd, "Unregister all queued\n"); return CLI_SUCCESS; + } else if (strncmp(registration_name, "transport", 9) == 0) { + de_register_all_with_transport(registration_name); + ast_cli(a->fd, "Unregister %s\n", registration_name); + return CLI_SUCCESS;; } state = get_state(registration_name); @@ -2600,6 +3549,18 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg return CLI_FAILURE; } + if (emergency){ + ast_log(LOG_NOTICE,"Emergency Registration hangup\n"); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + if((endpoint && endpoint->Emergency) || state->client_state->Emergency_reg_ongoing){ + // E-register hangup triggered while 1.emergency registered 2.emergency register still ongoing + state->client_state->Emergency_reg_hangup = true; + } + // else; E-register hangup triggered after registration reset to primary, just return. + ao2_ref(state, -1); + return CLI_SUCCESS; + } + if (queue_unregister(state)) { ast_cli(a->fd, "Failed to queue unregistration\n"); } @@ -2612,20 +3573,26 @@ static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args { struct sip_outbound_registration_state *state; const char *registration_name; + bool emergency=false; switch (cmd) { case CLI_INIT: e->command = "pjsip send register"; e->usage = - "Usage: pjsip send register <registration> | *all \n" + "Usage: pjsip send register <registration> | *all [e]\n" " Unregisters the specified (or all) outbound " - "registration(s) then starts registration(s) and schedules re-registrations.\n"; + "registration(s) then starts registration(s) and schedules re-registrations.\n" + " Optional flag e to trigger emergency registration through UDP\n"; return NULL; case CLI_GENERATE: return cli_complete_registration(a->line, a->word, a->pos, a->n); } - if (a->argc != 4) { + if (a->argc == 5 && strcmp(a->argv[4], "e") == 0){ + emergency=true; + } + + if (a->argc != 4 && !emergency) { return CLI_SHOWUSAGE; } @@ -2646,7 +3613,14 @@ static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args /* We need to serialize the unregister and register so they need * to be queued as separate tasks. */ - if (queue_unregister(state)) { + if (emergency) { + state->client_state->Emergency_reg_ongoing=true; + state->client_state->Emergency_reg_hangup=false; + cancel_registration(state->client_state); + pjsip_tx_data *tdata; + pjsip_regc_unregister(state->client_state->client, &tdata); + queue_register(state); + } else if (queue_unregister(state)) { ast_cli(a->fd, "Failed to queue unregistration\n"); } else if (queue_register(state)) { ast_cli(a->fd, "Failed to queue registration\n"); @@ -2863,7 +3837,7 @@ static int cli_print_header(void *obj, void *arg, int flags) ast_assert(context->output_buffer != NULL); ast_str_append(&context->output_buffer, 0, - " <Registration/ServerURI..............................> <Auth....................> <Status.......>\n"); + " <Registration/ServerURI(Primarily)...................> <Auth..........> <Latest Registration.....> <Status.......>\n"); return 0; } @@ -2880,7 +3854,17 @@ static int cli_print_body(void *obj, void *arg, int flags) ast_assert(context->output_buffer != NULL); expsecs = state ? state->client_state->registration_expires - ((int) time(NULL)) : 0; - ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s %-26s %-16s %s%d%s\n", + char addr_buf[AST_SOCKADDR_BUFLEN] = {'\0'}; + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + if (endpoint && endpoint->register_dest) { + pj_sockaddr_print(&endpoint->register_dest->addr.entry[endpoint->register_dest->cur_addr].addr, addr_buf, sizeof(addr_buf), 1); + } + int flag_504 = 0; + if (endpoint){ + flag_504 = endpoint->failover_reg_addr; + } + + ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s %-16s %-26s %-16s %s%d%s %s\n", id, (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)), (int) (REGISTRATION_URI_FIELD_LEN - strlen(id)), @@ -2888,8 +3872,10 @@ static int cli_print_body(void *obj, void *arg, int flags) AST_VECTOR_SIZE(®istration->outbound_auths) ? AST_VECTOR_GET(®istration->outbound_auths, 0) : "n/a", + addr_buf, (state ? sip_outbound_registration_status_str(state->client_state->status) : "Unregistered"), - state ? " (exp. " : "", abs(expsecs), state ? (expsecs < 0 ? "s ago)" : "s)") : ""); + state ? " (exp. " : "", abs(expsecs), state ? (expsecs < 0 ? "s ago)" : "s)") : "", + flag_504 ? (flag_504==1 ? "504_ongoing" : "504_failed") : ""); ao2_cleanup(state); if (context->show_details @@ -3097,7 +4083,10 @@ static int unload_module(void) remaining); return -1; } + ast_sched_context_destroy(sched); ast_sip_unregister_service(&mod_mwi); + ast_sip_unregister_service(&emergency_hack_module); + ast_sip_unregister_service(&log_register_module); ast_debug(2, "Successful shutdown.\n"); ao2_cleanup(shutdown_group); @@ -3114,6 +4103,15 @@ static int load_module(void) if (!shutdown_group) { return AST_MODULE_LOAD_DECLINE; } + if (!(sched = ast_sched_context_create())) { + ast_log(LOG_ERROR, "Could not create scheduler for registration re attempt\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (ast_sched_start_thread(sched)) { + ast_log(LOG_ERROR, "Could not start scheduler thread for registration re attempt\n"); + ast_sched_context_destroy(sched); + return AST_MODULE_LOAD_DECLINE; + } /* Create outbound registration states container. */ new_states = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, @@ -3142,10 +4140,10 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_header_params", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_header_params)); - ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_random_initial_delay", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_random_initial_delay)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_percent", "90", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_percent)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "1", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "fatal_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, fatal_retry_interval)); @@ -3162,6 +4160,10 @@ static int load_module(void) ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint)); ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "mediasec", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, mediasec)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_max_time", "1800", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_max_time)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_base_time1", "30", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_base_time1)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_base_time2", "90", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_base_time2)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_after", "15", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_after)); /* * Register sorcery observers. @@ -3215,6 +4217,8 @@ static int load_module(void) network_change_stasis_cb, NULL); stasis_subscription_accept_message_type(network_change_sub, ast_network_change_type()); stasis_subscription_set_filter(network_change_sub, STASIS_SUBSCRIPTION_FILTER_SELECTIVE); + ast_sip_register_service(&log_register_module); + ast_sip_register_service(&emergency_hack_module); ast_sip_register_service(&mod_mwi); return AST_MODULE_LOAD_SUCCESS; } @@ -3225,7 +4229,7 @@ static int reload_module(void) return 0; } -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support", +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support", .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .reload = reload_module, diff --git a/res/res_pjsip_outbound_registration.exports.in b/res/res_pjsip_outbound_registration.exports.in new file mode 100644 index 0000000000000000000000000000000000000000..05c3efb00a057c91db3019db17449cea5d69241a --- /dev/null +++ b/res/res_pjsip_outbound_registration.exports.in @@ -0,0 +1,9 @@ +{ + global: + LINKER_SYMBOL_PREFIXqueue_registration_recovery_flow; + LINKER_SYMBOL_PREFIXupdate_emergency_registration_ongoing_status; + LINKER_SYMBOL_PREFIXcheck_and_update_nextnonce; + LINKER_SYMBOL_PREFIXsync_cached_auth_between_reg_inv; + local: + *; +}; diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index f2b785bab99b3a9899ed7ca52f8775386f031f93..71bdc961c3c3d15d8bfd6303958d3e51d75077d2 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -81,7 +81,7 @@ static int pj_max_hostname = PJ_MAX_HOSTNAME; static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE; /*! \brief Internal function which returns the expiration time for a contact */ -static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata) +static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata, int *expiration_err) { pjsip_expires_hdr *expires; unsigned int expiration = aor->default_expiration; @@ -101,6 +101,8 @@ static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, cons /* Enforce the range that we will allow for expiration */ if (expiration < aor->minimum_expiration) { + if (expiration_err) + *expiration_err = 1; expiration = aor->minimum_expiration; } else if (expiration > aor->maximum_expiration) { expiration = aor->maximum_expiration; @@ -139,7 +141,7 @@ static int registrar_find_contact(void *obj, void *arg, int flags) /*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *pool, struct ao2_container *contacts, - struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted) + struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted, int *expiration_err) { pjsip_contact_hdr *previous = NULL; pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; @@ -148,10 +150,12 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *po }; for (; (contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next)); pj_pool_reset(pool)) { - unsigned int expiration = registrar_get_expiration(aor, contact, rdata); + unsigned int expiration = registrar_get_expiration(aor, contact, rdata, expiration_err); struct ast_sip_contact *existing; char contact_uri[pjsip_max_url_size]; + if (expiration_err && *expiration_err) + return -1; if (contact->star) { /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ if (expiration != 0 || previous) { @@ -403,6 +407,10 @@ static int registrar_contact_delete(enum contact_delete_type type, pjsip_transpo struct ast_sip_contact *contact, const char *aor_name) { int aor_size; + char * addr = NULL; + char * client_num = NULL; + char * saveptr = NULL; + char client_uri[PJSIP_MAX_URL_SIZE] = {0}; /* Permanent contacts can't be deleted */ if (ast_tvzero(contact->expiration_time)) { @@ -463,6 +471,16 @@ static int registrar_contact_delete(enum contact_delete_type type, pjsip_transpo ast_verb(3, "Removed contact '%s' from AOR '%s' due to %s\n", contact->uri, aor_name, reason); + + /* fail reason is expireation */ + if(!strcasecmp(reason, "expiration")){ + strncpy(client_uri, contact->uri, sizeof(client_uri)-1); + strtok_r(client_uri, ":", &saveptr); + client_num = strtok_r(NULL, "@", &saveptr); + addr = strtok_r(NULL, ":", &saveptr); + /* IPX004 */ + ast_log_dt(LOG_EVENT_CODE_IPX004, client_num, addr); + } } ast_test_suite_event_notify("AOR_CONTACT_REMOVED", @@ -681,6 +699,7 @@ static void register_aor_core(pjsip_rx_data *rdata, pjsip_cid_hdr *call_id_hdr; char *call_id = NULL; size_t alloc_size; + int expiration_err = 0; /* We create a single pool and use it throughout this function where we need one */ details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), @@ -697,12 +716,15 @@ static void register_aor_core(pjsip_rx_data *rdata, permanent = ao2_container_count(aor->permanent_contacts); } - if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) { + if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted, &expiration_err)) { /* The provided Contact headers do not conform to the specification */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(endpoint)); - response->code = 400; + if (expiration_err) + response->code = 423; + else + response->code = 400; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } @@ -828,7 +850,7 @@ static void register_aor_core(pjsip_rx_data *rdata, continue; } - expiration = registrar_get_expiration(aor, contact_hdr, rdata); + expiration = registrar_get_expiration(aor, contact_hdr, rdata, NULL); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); @@ -988,7 +1010,7 @@ static void register_aor_core(pjsip_rx_data *rdata, ao2_callback(contacts, 0, registrar_add_contact, tdata); if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { - expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata)); + expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata, NULL)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } @@ -1004,6 +1026,10 @@ static int register_aor(pjsip_rx_data *rdata, .code = 500, }; struct ao2_container *contacts = NULL; + pjsip_min_expires_hdr *hdr = NULL; + pjsip_hdr hdr_list = {}; + pjsip_sip_uri *uri = NULL; + pjsip_contact_hdr *contact_hdr = NULL; ao2_lock(aor); contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor); @@ -1017,13 +1043,37 @@ static int register_aor(pjsip_rx_data *rdata, register_aor_core(rdata, endpoint, aor, aor_name, contacts, &response); ao2_cleanup(contacts); ao2_unlock(aor); + contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); + if(contact_hdr){ + uri = pjsip_uri_get_uri(contact_hdr->uri); + } /* Now send the REGISTER response to the peer */ if (response.tdata) { ast_sip_send_stateful_response(rdata, response.tdata, endpoint); + + /* registration */ + if(registrar_get_expiration(aor, NULL, rdata, NULL)){ + /* register success. IPX001 */ + ast_log_dt(LOG_EVENT_CODE_IPX001, (int) uri->user.slen, uri->user.ptr, (int) uri->host.slen, uri->host.ptr); + } + /* unregistration */ + else{ + /* unregister success. IPX002 */ + ast_log_dt(LOG_EVENT_CODE_IPX002, (int) uri->user.slen, uri->user.ptr, (int) uri->host.slen, uri->host.ptr); + } } else { + if (response.code == 423) { + pj_list_init(&hdr_list); + hdr = pjsip_min_expires_hdr_create(rdata->tp_info.pool, aor->minimum_expiration); + pj_list_push_back(&hdr_list, hdr); + } + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), - rdata, response.code, NULL, NULL, NULL); + rdata, response.code, NULL, (pjsip_hdr *)&hdr_list, NULL); + pj_list_erase(hdr); + /* sip error occured. IPX100 */ + ast_log_dt(LOG_EVENT_CODE_IPX100, response.code, (int) uri->user.slen, uri->user.ptr); } return PJ_TRUE; } diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 8c6b8a4033288e7f4e35624bde713ca98b4f3404..32d3c5c6125d6e31a72bd19f58a9190b19a2b17f 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -172,6 +172,10 @@ static void enable_rtcp(struct ast_sip_session *session, struct ast_sip_session_ } ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type); + /* set QoS/DSCP */ + ast_rtp_instance_set_rtcp_qos(session_media->rtp, session->endpoint->media.tos_audio, + session->endpoint->media.cos_audio, "SIP RTCP Audio"); + } /*! @@ -980,9 +984,11 @@ static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t * \retval The encryption requested in the SDP */ static enum ast_sip_session_media_encryption check_endpoint_media_transport( - struct ast_sip_endpoint *endpoint, + struct ast_sip_session *session, const struct pjmedia_sdp_media *stream) { + struct ast_sip_endpoint *endpoint = session->endpoint; + enum ast_sip_session_media_encryption local_encryption = AST_SIP_MEDIA_ENCRYPT_NONE; enum ast_sip_session_media_encryption incoming_encryption; char transport_end = stream->desc.transport.ptr[stream->desc.transport.slen - 1]; unsigned int optimistic; @@ -992,9 +998,12 @@ static enum ast_sip_session_media_encryption check_endpoint_media_transport( return AST_SIP_MEDIA_TRANSPORT_INVALID; } + if (session->transport_state->type == AST_TRANSPORT_TLS || session->transport_state->type == AST_TRANSPORT_WSS) + local_encryption = endpoint->media.rtp.encryption; + incoming_encryption = get_media_encryption_type(stream->desc.transport, stream, &optimistic); - if (incoming_encryption == endpoint->media.rtp.encryption) { + if (incoming_encryption == local_encryption) { return incoming_encryption; } @@ -1168,6 +1177,11 @@ static int setup_media_encryption(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream) { + // Only enable media encryption for TLS/WSS transport + if (session->transport_state->type != AST_TRANSPORT_TLS && + session->transport_state->type != AST_TRANSPORT_WSS) + return 0; + switch (session_media->encryption) { case AST_SIP_MEDIA_ENCRYPT_SDES: if (setup_sdes_srtp(session_media, stream)) { @@ -1517,7 +1531,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, /* Ensure incoming transport is compatible with the endpoint's configuration */ if (!session->endpoint->media.rtp.use_received_transport) { - encryption = check_endpoint_media_transport(session->endpoint, stream); + encryption = check_endpoint_media_transport(session, stream); if (encryption == AST_SIP_MEDIA_TRANSPORT_INVALID) { SCOPE_EXIT_RTN_VALUE(-1, "Incompatible transport\n"); @@ -1618,6 +1632,10 @@ static int add_crypto_to_stream(struct ast_sip_session *session, static const pj_str_t STR_MEDSECREQ = { "requested", 9 }; enum ast_rtp_dtls_setup setup; + if (session->transport_state->type != AST_TRANSPORT_TLS && + session->transport_state->type != AST_TRANSPORT_WSS) + return 0; + switch (session_media->encryption) { case AST_SIP_MEDIA_ENCRYPT_NONE: case AST_SIP_MEDIA_TRANSPORT_INVALID: @@ -1632,7 +1650,8 @@ static int add_crypto_to_stream(struct ast_sip_session *session, tmp = session_media->srtp; - if (session->endpoint->mediasec) { + if (session->endpoint->mediasec || + session->endpoint->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) { attr = pjmedia_sdp_attr_create(pool, "3ge2ae", &STR_MEDSECREQ); media->attr[media->attr_count++] = attr; } @@ -1651,11 +1670,6 @@ static int add_crypto_to_stream(struct ast_sip_session *session, media->attr[media->attr_count++] = attr; } while ((tmp = AST_LIST_NEXT(tmp, sdp_srtp_list))); - if (session->endpoint->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) { - attr = pjmedia_sdp_attr_create(pool, "3ge2ae", &STR_MEDSECREQ); - media->attr[media->attr_count++] = attr; - } - break; case AST_SIP_MEDIA_ENCRYPT_DTLS: if (setup_dtls_srtp(session, session_media)) { @@ -1842,6 +1856,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } else { media->desc.transport = pj_str(ast_sdp_get_rtp_profile( /* Optimistic encryption places crypto in the normal RTP/AVP profile */ + (session->transport_state->type == AST_TRANSPORT_TLS || + session->transport_state->type == AST_TRANSPORT_WSS) && !session->endpoint->media.rtp.encryption_optimistic && (session_media->encryption == AST_SIP_MEDIA_ENCRYPT_SDES), session_media->rtp, session->endpoint->media.rtp.use_avpf, @@ -2115,16 +2131,18 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, char host[NI_MAXHOST]; int res; struct ast_sip_session_media *session_media_transport; + struct ast_channel *bridged_chan; SCOPE_ENTER(1, "%s Stream: %s\n", ast_sip_session_get_name(session), ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP))); if (!session->channel) { SCOPE_EXIT_RTN_VALUE(1, "No channel\n"); } + bridged_chan = ast_channel_bridged_chan(session->channel); /* Ensure incoming transport is compatible with the endpoint's configuration */ if (!session->endpoint->media.rtp.use_received_transport && - check_endpoint_media_transport(session->endpoint, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) { + check_endpoint_media_transport(session, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) { SCOPE_EXIT_RTN_VALUE(-1, "Incompatible transport\n"); } @@ -2197,12 +2215,16 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, if (caps) { ast_format_cap_append(caps, ast_format_cap_get_format(ast_stream_get_formats(asterisk_stream), 0), ast_channel_ptime_get(session->channel)); ast_channel_nativeformats_set(session->channel, caps); + if (bridged_chan) { + ast_channel_nativeformats_set(bridged_chan, caps); + } ao2_ref(caps, -1); } else { ao2_cleanup(caps); ast_log(LOG_WARNING, "Unable to allocate caps\n"); } } + /* Get and set the ptime, get from remote_stream if it has, otherwise get from local_stream */ pjmedia_sdp_attr *attr; unsigned long framing; @@ -2215,6 +2237,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, framing = pj_strtoul(pj_strltrim(&attr->value)); ast_channel_ptime_set(session->channel, framing); } + pjmedia_sdp_rtpmap rtpmap; for (int i = 0; i < remote_stream->desc.fmt_count; ++i) { /* Look for the optional rtpmap attribute */ @@ -2227,15 +2250,20 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, char name[256]; ast_copy_pj_str(name, &rtpmap.enc_name, sizeof(name)); if ((strcmp(name, "telephone-event") == 0) && (8000 == rtpmap.clock_rate)) { - char payload[4]; - ast_copy_pj_str(payload, &rtpmap.pt, sizeof(name)); - ast_channel_dtmf_pt_set(session->channel, atoi(payload)); - break; + char payload[4]; + ast_copy_pj_str(payload, &rtpmap.pt, sizeof(name)); + ast_channel_dtmf_pt_set(session->channel, atoi(payload)); + break; } } ast_channel_codec_set(session->channel, ast_format_get_name(ast_format_cap_get_format(ast_stream_get_formats(asterisk_stream), 0))); - ast_log(LOG_NOTICE, "channel %s, codec: %s, ptime: %d \n", ast_channel_name(session->channel), ast_channel_codec_get(session->channel), ast_channel_ptime_get(session->channel)); + ast_log(LOG_NOTICE, "channel %s, codec: %s, ptime: %d \n", ast_channel_name(session->channel), + ast_channel_codec_get(session->channel), ast_channel_ptime_get(session->channel)); + if (bridged_chan) { + ast_channel_codec_set(bridged_chan, ast_channel_codec_get(session->channel)); + ast_channel_ptime_set(bridged_chan, ast_channel_ptime_get(session->channel)); + } /* Set the channel uniqueid on the RTP instance now that it is becoming active */ ast_rtp_instance_set_channel_id(session_media->rtp, ast_channel_uniqueid(session->channel)); @@ -2310,6 +2338,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, session_media->timeout_sched_id = ast_sched_add_variable(sched, 500, rtp_check_timeout, session_media, 1); } + ast_rtp_instance_sendcng(session_media->rtp, 0); SCOPE_EXIT_RTN_VALUE(1, "Handled\n"); } diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 89bd3b5319092de9566d987a82b3c0a09e62e4ff..bcea9dd36c18b045d69410bbf79011965a5276c5 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -51,7 +51,7 @@ #include "asterisk/test.h" #include "asterisk/stream.h" #include "asterisk/vector.h" - +#include "asterisk/res_pjsip_outbound_registration.h" #define SDP_HANDLER_BUCKETS 11 #define MOD_DATA_ON_RESPONSE "on_response" @@ -60,11 +60,6 @@ /* Most common case is one audio and one video stream */ #define DEFAULT_NUM_SESSION_MEDIA 2 -/* Max call count per line and Max call count generally*/ -#define DEFAULT_MAX_SESSION_PER_LINE 2 -#define DEFAULT_MAX_SESSION 4 -static int max_sessions_per_line = DEFAULT_MAX_SESSION_PER_LINE; -static int max_sessions = DEFAULT_MAX_SESSION; static int current_session_count = 0; // current number of active sessions #define MAX_BUFFER_LEN 20 /* Some forward declarations */ @@ -1686,7 +1681,7 @@ static void set_from_header(struct ast_sip_session *session) if (session->endpoint->id.trust_outbound || !restricted) { ast_sip_modify_id_header(dlg_pool, dlg_info, &connected_id); - if (ast_sip_get_use_callerid_contact() && ast_strlen_zero(session->endpoint->contact_user)) { + if (ast_sip_get_use_callerid_contact() && (connected_id.number.valid || ast_strlen_zero(session->endpoint->contact_user))) { pj_strdup2(dlg_pool, &dlg_contact_uri->user, S_COR(connected_id.number.valid, connected_id.number.str, "")); } else { pj_strdup2(dlg_pool, &dlg_info_uri->user, session->endpoint->contact_user); @@ -2523,11 +2518,10 @@ static int sip_session_refresh(struct ast_sip_session *session, } } - if (session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) { + if ((session->transport_state->type == AST_TRANSPORT_TLS || session->transport_state->type == AST_TRANSPORT_WSS) && + session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) { ast_debug(3, "Session Refresh: Adding MEDIASEC headers\n"); - ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec"); - ast_sip_add_header(tdata,"Proxy-Require","mediasec"); - ast_sip_add_header(tdata,"Require","mediasec"); + if(!AST_LIST_EMPTY(&session->endpoint->secur_mechanisms)) { struct security_mechanism *sec_mechanism; AST_LIST_TRAVERSE(&session->endpoint->secur_mechanisms, sec_mechanism, entry) { @@ -2674,6 +2668,7 @@ static pj_status_t log_sipipaddress_on_tx_msg(pjsip_tx_data *tdata) static int sdp_requires_deferral(struct ast_sip_session *session, const pjmedia_sdp_session *sdp) { int i; + SCOPE_ENTER(3, "%s\n", ast_sip_session_get_name(session)); if (!session->pending_media_state->topology) { session->pending_media_state->topology = ast_stream_topology_alloc(); @@ -2801,7 +2796,8 @@ static int sdp_requires_deferral(struct ast_sip_session *session, const pjmedia_ break; } } - return 0; + + SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session)); } static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata) @@ -2810,6 +2806,7 @@ static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata) RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); pjsip_rdata_sdp_info *sdp_info; int deferred; + SCOPE_ENTER(3, "starts\n"); if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD || !(dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag, PJ_FALSE)) || @@ -2866,7 +2863,7 @@ static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata) pjsip_rx_data_clone(rdata, 0, &session->deferred_reinvite); - return PJ_TRUE; + SCOPE_EXIT_RTN_VALUE(PJ_TRUE, "ends\n"); } void ast_sip_session_resume_reinvite(struct ast_sip_session *session) @@ -2924,6 +2921,7 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data pjsip_generic_string_hdr *content_header = NULL; char buffer[MAX_BUFFER_LEN] = {0}; static const pj_str_t headerName = { "Content-Disposition", 19 }; + SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session)); if (!(offer = create_local_sdp(session->inv_session, session, NULL, 0))) { @@ -2945,15 +2943,21 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data */ set_from_header(session); + if (pjsip_inv_invite(session->inv_session, tdata) != PJ_SUCCESS) { SCOPE_EXIT_RTN_VALUE(-1, "pjsip_inv_invite failed\n"); } - if (session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) { + /* Send Invite to the same server address as the last registeraion */ + if (session->endpoint->register_dest) { + pj_strdup2((*tdata)->pool, &(*tdata)->dest_info.name, session->endpoint->register_dest->host); + memcpy(&(*tdata)->dest_info.addr, &session->endpoint->register_dest->addr, sizeof(pjsip_server_addresses)); + (*tdata)->dest_info.cur_addr = session->endpoint->register_dest->cur_addr; + } + + if ((session->transport_state->type == AST_TRANSPORT_TLS || session->transport_state->type == AST_TRANSPORT_WSS) && + session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) { ast_debug(3, "INVITE: Adding MEDIASEC headers\n"); - ast_sip_add_header(*tdata,"Security-Client","sdes-srtp;mediasec"); - ast_sip_add_header(*tdata,"Proxy-Require","mediasec"); - ast_sip_add_header(*tdata,"Require","mediasec"); if(!AST_LIST_EMPTY(&session->endpoint->secur_mechanisms)) { struct security_mechanism *sec_mechanism; @@ -2964,6 +2968,15 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data } } + if (session->endpoint->cached_auth){ + ast_debug(3, "INVITE: Attach cached auth headers\n"); + if (pjsip_msg_find_hdr((*tdata)->msg, PJSIP_H_PROXY_AUTHORIZATION, NULL) == NULL){ + ast_mutex_lock(&session->endpoint->auth_sess_inv_lock); + pjsip_auth_clt_init_req(&session->endpoint->auth_sess_inv, *tdata); + ast_mutex_unlock(&session->endpoint->auth_sess_inv_lock); + } + } + content_header = pjsip_msg_find_hdr_by_name((*tdata)->msg, &headerName, NULL); /*If content dispostion is present ,P-Early-Media header header is not added */ if (content_header) @@ -2987,6 +3000,9 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data ast_debug(3, "INVITE: Adding Priority and Resource-Priority headers \n"); ast_sip_add_header(*tdata,"Priority","emergency"); ast_sip_add_header(*tdata,"Resource-Priority","emrg.0"); + if(session->endpoint){ + update_emergency_registration_ongoing_status(ast_sorcery_object_get_id(session->endpoint), true); + } } SCOPE_EXIT_RTN_VALUE(0); @@ -3335,6 +3351,25 @@ void ast_sip_session_unsuspend(struct ast_sip_session *session) ast_taskprocessor_unsuspend(session->serializer); } +static void invite_nextnonce_update(pjsip_rx_data *rdata){ + pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); + pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); + if (!dlg || !tsx) { + return; + } + if (tsx->method.id != PJSIP_INVITE_METHOD) { + /* Not an INVITE that needs authentication */ + return; + } + pjsip_inv_session *inv = pjsip_dlg_get_inv_session(dlg); + struct ast_sip_session *session = inv->mod_data[session_module.id]; + if (session->endpoint && session->endpoint->realms){ + ast_mutex_lock(&session->endpoint->auth_sess_inv_lock); + check_and_update_nextnonce(&session->endpoint->auth_sess_inv, session->endpoint->realms, rdata); + ast_mutex_unlock(&session->endpoint->auth_sess_inv_lock); + } +} + /*! * \internal * \brief Handle initial INVITE challenge response message. @@ -3355,6 +3390,8 @@ static pj_bool_t outbound_invite_auth(pjsip_rx_data *rdata) if (rdata->msg_info.msg->line.status.code != 401 && rdata->msg_info.msg->line.status.code != 407) { + // check and update for nextnonce if needed + invite_nextnonce_update(rdata); /* Doesn't pertain to us. Move on */ return PJ_FALSE; } @@ -3441,8 +3478,8 @@ int get_line_calls (struct ast_endpoint_snapshot * endpoint_snapshot) continue; } - if( (strcmp(snapshot->dialplan->appl, "Dial") == 0) && (strcmp(snapshot->dialplan->context, "call_line") == 0) || - (strcmp(snapshot->dialplan->appl, "AppDial") == 0) && (strcmp(snapshot->dialplan->context, "incoming_calls") == 0)) + if ((!strcmp(snapshot->dialplan->appl, "Dial") && !strcmp(snapshot->dialplan->context, "call_line")) || + (!strcmp(snapshot->dialplan->appl, "AppDial") && !strcmp(snapshot->dialplan->context, "incoming_calls"))) count++; } @@ -3461,6 +3498,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint struct pjsip_inv_session *inv_session; RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); struct ast_sip_session *ret_session; + struct ast_sip_transport_state *transport_state; SCOPE_ENTER(1, "%s %s Topology: %s\n", ast_sorcery_object_get_id(endpoint), request_user, ast_str_tmp(256, ast_stream_topology_to_str(req_topology, &STR_TMP))); @@ -3484,12 +3522,15 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint int line_calls = get_line_calls(endpoint_snapshot); - if (line_calls >= max_sessions_per_line) { - ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, max_sessions_per_line); + if (line_calls >= ast_sip_get_max_sessions_per_line()) { + ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, ast_sip_get_max_sessions_per_line()); SCOPE_EXIT_RTN_VALUE(NULL, "Max call per line limit exceeded\n"); - } else if (((current_session_count >= max_sessions) && (ast_active_calls() != 3)) || - ((current_session_count >= max_sessions) && (ast_active_channels() >= 7))) { - ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, max_sessions); + } else if (endpoint->max_sessions && line_calls >= endpoint->max_sessions) { + ast_log(LOG_WARNING, "Max call per client limit exceeded [%d/%d]\n", line_calls, endpoint->max_sessions); + SCOPE_EXIT_RTN_VALUE(NULL, "Max call per client limit exceeded\n"); + } else if (((current_session_count >= ast_sip_get_max_sessions_total()) && (ast_active_calls() != 3)) || + ((current_session_count >= ast_sip_get_max_sessions_total()) && (ast_active_channels() >= 7))) { + ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, ast_sip_get_max_sessions_total()); SCOPE_EXIT_RTN_VALUE(NULL, "Max call limit exceeded\n"); } @@ -3530,6 +3571,25 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint } session->aor = ao2_bump(found_aor); session->call_direction = AST_SIP_SESSION_OUTGOING_CALL; + // Checker for Emergency Registration and change transport to upd + if (endpoint->Emergency) { + ast_log(LOG_NOTICE,"===== EMERGENCY CALL get UDP transport =====\n"); + transport_state = ast_sip_get_transport_state(endpoint->transport_emergency); + ast_log(LOG_NOTICE,"===== EMERGENCY CALL with UDP =====\n"); + } if (endpoint->register_transport) { + transport_state = ast_sip_get_transport_state(endpoint->register_transport); + } else { + transport_state = ast_sip_get_transport_state(endpoint->transport); + if (transport_state && transport_state->type == AST_TRANSPORT_TLS + && !ast_sip_is_X_RDK_NTP_Synchronized()) + transport_state = NULL; + } + if (!transport_state) { + ast_log(LOG_NOTICE,"===== No Transport! =====\n"); + pjsip_inv_terminate(inv_session, 500, PJ_FALSE); + return NULL; + } + session->transport_state = transport_state; ast_party_id_copy(&session->id, &endpoint->id.self); @@ -4259,6 +4319,66 @@ end: SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(invite->session)); } +static struct ast_sip_transport_state* get_invite_transport_state(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) +{ + struct ast_sip_transport_state *transport_state; + enum ast_transport transport_type; + pj_str_t type_name; + + ast_debug(3, "Transport of incoming request: %s\n", rdata->tp_info.transport->type_name); + + pj_cstr(&type_name, rdata->tp_info.transport->type_name); + + switch (pjsip_transport_get_type_from_name(&type_name)) { + case PJSIP_TRANSPORT_UDP: + case PJSIP_TRANSPORT_UDP6: + transport_type = AST_TRANSPORT_UDP; + break; + + case PJSIP_TRANSPORT_TLS: + case PJSIP_TRANSPORT_TLS6: + transport_type = AST_TRANSPORT_TLS; + break; + + case PJSIP_TRANSPORT_TCP: + case PJSIP_TRANSPORT_TCP6: + transport_type = AST_TRANSPORT_TCP; + break; + + default: + ast_debug(3, "Transport %s is not supported\n", rdata->tp_info.transport->type_name); + return NULL; + } + + if (endpoint->transport) { + transport_state = ast_sip_get_transport_state(endpoint->transport); + if (transport_state && transport_state->type == transport_type) { + ast_debug(3, "Request from the primary transport %s\n", rdata->tp_info.transport->type_name); + return transport_state; + } + } + + if (endpoint->transport2) { + transport_state = ast_sip_get_transport_state(endpoint->transport2); + if (transport_state && transport_state->type == transport_type) { + ast_debug(3, "Request from the secondary transport %s\n", rdata->tp_info.transport->type_name); + return transport_state; + } + } + + if (endpoint->Emergency && endpoint->transport_emergency) { + transport_state = ast_sip_get_transport_state(endpoint->transport_emergency); + if (transport_state && transport_state->type == transport_type) { + ast_debug(3, "Request from the emergency transport %s\n", rdata->tp_info.transport->type_name); + return transport_state; + } + } + + ast_debug(3, "Transport %s is not configured\n", rdata->tp_info.transport->type_name); + + return NULL; +} + static void handle_new_invite_request(pjsip_rx_data *rdata) { RAII_VAR(struct ast_sip_endpoint *, endpoint, @@ -4269,6 +4389,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) strlen(AST_STIR_SHAKEN_RESPONSE_STR_USE_IDENTITY_HEADER) }; pjsip_inv_session *inv_session = NULL; + struct ast_sip_transport_state *transport_state; struct ast_sip_session *session; struct new_invite invite; char *req_uri = TRACE_ATLEAST(1) ? ast_alloca(256) : ""; @@ -4277,6 +4398,11 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) ast_assert(endpoint != NULL); + transport_state = get_invite_transport_state(endpoint, rdata); + if (!transport_state) { + SCOPE_EXIT_RTN("Failure: No transport_state in enpoint\n"); + } + if ((endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_VERIFY) && !ast_sip_rdata_get_header_value(rdata, identity_str)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, @@ -4296,16 +4422,24 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) int line_calls = get_line_calls(endpoint_snapshot); - if (line_calls >= max_sessions_per_line) { - ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, max_sessions_per_line); + if (line_calls >= ast_sip_get_max_sessions_per_line()) { + ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, ast_sip_get_max_sessions_per_line()); /* Dialog's lock and reference are removed in new_invite_initial_answer */ if (!new_invite_initial_answer(inv_session, rdata, 486, 486, PJ_FALSE)) { /* Terminate the session if it wasn't done in the answer */ pjsip_inv_terminate(inv_session, 486, PJ_FALSE); } SCOPE_EXIT_RTN("Max call per line limit exceeded\n"); - } else if ((current_session_count == max_sessions) || (ast_active_calls() >= max_sessions)) { - ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, max_sessions); + } else if (endpoint->max_sessions && line_calls >= endpoint->max_sessions) { + ast_log(LOG_WARNING, "Max call per client limit exceeded [%d/%d]\n", line_calls, endpoint->max_sessions); + /* Dialog's lock and reference are removed in new_invite_initial_answer */ + if (!new_invite_initial_answer(inv_session, rdata, 486, 486, PJ_FALSE)) { + /* Terminate the session if it wasn't done in the answer */ + pjsip_inv_terminate(inv_session, 486, PJ_FALSE); + } + SCOPE_EXIT_RTN("Max call per client limit exceeded\n"); + } else if ((current_session_count == ast_sip_get_max_sessions_total()) || (ast_active_calls() >= ast_sip_get_max_sessions_total())) { + ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, ast_sip_get_max_sessions_total()); /* Dialog's lock and reference are removed in new_invite_initial_answer */ if (!new_invite_initial_answer(inv_session, rdata, 486, 486, PJ_FALSE)) { /* Terminate the session if it wasn't done in the answer */ @@ -4347,6 +4481,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) SCOPE_EXIT_RTN("Couldn't create session\n"); } session->call_direction = AST_SIP_SESSION_INCOMING_CALL; + session->transport_state = transport_state; /* * The current thread is supposed be the session serializer to prevent @@ -4409,10 +4544,8 @@ static int parse_earlymedia_direction(char *direction) { return 1; } - if(!strcmp(direction ,"none")) - { - return 0; - } +// if(!strcmp(direction ,"none")) + return 0; } static pj_status_t session_on_tx_request(pjsip_tx_data *tdata) @@ -4428,7 +4561,12 @@ static pj_status_t session_on_tx_request(pjsip_tx_data *tdata) { /* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration * of early media */ - ast_sip_add_header(tdata,"P-Early-Media", "sendonly"); + static const pj_str_t headerName = { "P-Early-Media", 13 }; + int earlymedia = pjsip_msg_find_hdr_by_name(tdata->msg, &headerName, NULL); + /* avoid repeating the same headers*/ + if (!earlymedia) { + ast_sip_add_header(tdata,"P-Early-Media", "supported"); + } } } } @@ -4769,6 +4907,11 @@ static void handle_session_end(struct ast_sip_session *session) current_session_count--; if (session->channel && ast_channel_emergency_ongoing_get(session->channel)) ast_channel_emergency_ongoing_set(session->channel, 0); + + if(session->endpoint){ + // clear the call status for emergency registration. + update_emergency_registration_ongoing_status(ast_sorcery_object_get_id(session->endpoint), false); + } /* Session is dead. Notify the supplements. */ AST_LIST_TRAVERSE(&session->supplements, iter, next) { if (iter->session_end) { @@ -4782,6 +4925,7 @@ static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_d { struct ast_sip_session_supplement *supplement; struct pjsip_status_line status = rdata->msg_info.msg->line.status; + SCOPE_ENTER(3, "%s: Response is %d %.*s\n", ast_sip_session_get_name(session), status.code, (int) pj_strlen(&status.reason), pj_strbuf(&status.reason)); @@ -4799,9 +4943,9 @@ static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_d pjsip_generic_string_hdr *SessionID = NULL; static const pj_str_t headerName = { "Session-ID", 10 }; SessionID = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &headerName, NULL); - char value[33] = {0}; + char value[128] = {0}; if (SessionID) - ast_copy_pj_str(&value, &SessionID->hvalue, pj_strlen(&SessionID->hvalue) + 1); + ast_copy_pj_str(&value, &SessionID->hvalue, sizeof(value)); ast_channel_SIPSessionID_set(session->channel, value); } @@ -4829,8 +4973,36 @@ static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_da SCOPE_ENTER(3, "%s: Method is %.*s\n", ast_sip_session_get_name(session), (int) pj_strlen(&req.method.name), pj_strbuf(&req.method.name)); - ast_sip_message_apply_transport(session->endpoint->transport, tdata); + if(session->endpoint->Emergency){ + ast_sip_message_apply_transport(session->endpoint->transport_emergency, tdata); + RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); + transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", session->endpoint->transport_emergency); + if (!transport) { + ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport transport-udp\n"); + return; + } + ast_log(LOG_NOTICE,"===== EMERGENCY Requset with UDP =====\n"); + if (req.method.id == PJSIP_INVITE_METHOD && session->endpoint->transport_emergency) { + struct ast_sip_transport_state *transport_emergency; + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + + transport_emergency = ast_sip_get_transport_state(session->endpoint->transport_emergency); + if (transport_emergency) { + ast_sip_set_tpselector_from_transport_name(session->endpoint->transport_emergency, &selector); + pjsip_inv_session *inv = session->inv_session; + pjsip_dlg_set_transport(inv->dlg, &selector); + session->transport_state = transport_emergency; + } else { + ast_log(LOG_ERROR, "transport_emergency %s is not available\n", session->endpoint->transport_emergency); + } + } + + ast_log(LOG_NOTICE, "Sending SIP Request with transport (%s)\n", session->transport_state->id); + + } else { + ast_sip_message_apply_transport(session->endpoint->transport, tdata); + } AST_LIST_TRAVERSE(&session->supplements, supplement, next) { if (supplement->outgoing_request && does_method_match(&req.method.name, supplement->method)) { supplement->outgoing_request(session, tdata); @@ -4913,28 +5085,124 @@ static int session_end_completion(void *vsession) return 0; } +static struct ast_sip_transport_state* get_udp_transport_state() +{ + struct ao2_container *transport_states = ast_sip_get_transport_states(); + struct ast_sip_transport_state *state; + struct ao2_iterator it; + + if (!transport_states) + return NULL; + + it = ao2_iterator_init(transport_states, 0); + while (state = ao2_iterator_next(&it)) { + if (!state->flow && state->type == AST_TRANSPORT_UDP) { + ast_debug(3, "found UDP transport '%s'\n", state->id); + ao2_ref(state, -1); + break; + } + + ao2_ref(state, -1); + } + ao2_iterator_destroy(&it); + + ao2_ref(transport_states, -1); + + return state; +} + +static void sip_inv_transport_emergency(pjsip_inv_session *inv) +{ + struct ast_sip_session *session = inv->mod_data[session_module.id]; + struct ast_sip_transport_state *transport_udp; + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + + ast_log(LOG_NOTICE, "Try UDP transport for emergency ongoing call\n"); + + transport_udp = get_udp_transport_state(); + + if (transport_udp && transport_udp != session->transport_state && + !ast_sip_set_tpselector_from_transport_name(transport_udp->id, &selector)) { + pjsip_sip_uri *target_uri; + pjsip_tx_data *tdata; + + ast_log(LOG_NOTICE, "Sending SIP INVITE with transport (%s)\n", transport_udp->id); + + session->transport_state = transport_udp; + pjsip_dlg_set_transport(inv->dlg, &selector); + + pjsip_inv_uac_restart(inv, PJ_FALSE); + pjmedia_sdp_neg_cancel_offer(inv->neg); + + target_uri = pjsip_uri_get_uri(inv->dlg->target); + if (target_uri) + target_uri->port = pj_sockaddr_get_port(&transport_udp->host); + + ast_sip_session_create_invite(session, &tdata); + ast_sip_session_send_request(session, tdata); + + } else if (session->endpoint) { + ast_log(LOG_NOTICE, "%s: Failed to INVITE, schedule re-registration\n", ast_sorcery_object_get_id(session->endpoint)); + + session->endpoint->failover_reg_addr = 1; + queue_registration_recovery_flow(ast_sorcery_object_get_id(session->endpoint)); + } +} + +static int check_content_xml(pjsip_rx_data *rdata) +{ + pjsip_msg_body *body = rdata->msg_info.msg->body; + pjsip_ctype_hdr *ctype_hdr = rdata->msg_info.ctype; + if (body && ctype_hdr && + ast_sip_is_media_type_in(&ctype_hdr->media, &pjsip_media_type_application_3gpp_ims_xml, SENTINEL)) { + int res; + char buf[body->len + 1]; + + res = body->print_body(body, buf, sizeof(buf)); + if (res <= 0) { + // no real content body + return 0; + } + buf[res] = '\0'; + if (strcasestr(buf, "<ims-3gpp") && strcasestr(buf, "<action>initial-registration</action>")) { + // trigger registration recovery to the next address + return 1; + } + } + return 0; +} + static int check_request_status(pjsip_inv_session *inv, pjsip_event *e) { struct ast_sip_session *session = inv->mod_data[session_module.id]; pjsip_transaction *tsx = e->body.tsx_state.tsx; - if (tsx->status_code != 503 && tsx->status_code != 408 && tsx->status_code != 500) { + ast_debug(3, "%s: Received response '%d'\n", ast_sip_session_get_name(session), tsx->status_code); + + if (tsx->status_code != 503 && tsx->status_code != 408 && tsx->status_code != 500 && tsx->status_code != 504 && tsx->status_code != 600) { // DNS resolve: stick to the server address ast_sip_resolve_track_cur_addr(tsx->last_tx); return 0; } - if (!ast_sip_failover_request(tsx->last_tx)) { - return 0; + if (tsx->status_code == 504 && e->body.rx_msg.rdata && check_content_xml(e->body.rx_msg.rdata) && session->endpoint) { + ast_log(LOG_NOTICE, "%s: Failed to INVITE, 504 with 3gpp-ims+xml action initial-registration, schedule re-registration\n", + ast_sorcery_object_get_id(session->endpoint)); + + session->endpoint->failover_reg_addr = 1; + queue_registration_recovery_flow(ast_sorcery_object_get_id(session->endpoint)); + return 1; // hangup the current session with cause 504. + } + ast_log(LOG_NOTICE, "Failed to send SIP INVITE with transport '%s'\n", session->transport_state->id); + + // Try more for emergency ongoing INVITE + if (session->channel && ast_channel_emergency_ongoing_get(session->channel)) { + struct pjsip_msg *msg = tsx->last_tx->msg; + + if (msg && msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD) + sip_inv_transport_emergency(inv); } - pjsip_inv_uac_restart(inv, PJ_FALSE); - /* - * Bump the ref since it will be on a new transaction and - * we don't want it to go away along with the old transaction. - */ - pjsip_tx_data_add_ref(tsx->last_tx); - ast_sip_session_send_request(session, tsx->last_tx); return 1; } @@ -5335,6 +5603,25 @@ static int add_sdp_streams(struct ast_sip_session_media *session_media, handler_list = ao2_find(sdp_handlers, ast_codec_media_type2str(session_media->type), OBJ_KEY); if (!handler_list) { + /* If there is no handler for this media, set port as 0 in m line and copy all media specific + * attributes from the remote (offer) */ + if ((ast_stream_get_state(stream) == AST_STREAM_STATE_REMOVED || !ast_stream_get_formats(stream) || + !ast_format_cap_count(ast_stream_get_formats(stream))) && remote && remote->media[ast_stream_get_position(stream)]) { + pj_pool_t *pool = session->inv_session->pool_prov; + struct pjmedia_sdp_media *media = pjmedia_sdp_media_clone(pool, remote->media[ast_stream_get_position(stream)]); + if (media) { + /* Update port */ + media->desc.port = 0; + media->desc.port_count = 1; + + /* Add this media to the local SDP */ + answer->media[answer->media_count++] = media; + ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED); + + SCOPE_EXIT_RTN_VALUE(0, "Add a rejected media stream and its attributes\n"); + } + } + SCOPE_EXIT_RTN_VALUE(0, "No handlers\n"); } @@ -5502,7 +5789,6 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru /* This code does not enforce any maximum stream count limitations as that is done on either * the handling of an incoming SDP offer or on the handling of a session refresh. */ - session_media = ast_sip_session_media_state_add(session, session->pending_media_state, ast_stream_get_type(stream), i); if (!session_media) { local = NULL; @@ -5858,6 +6144,39 @@ static pjsip_inv_callback inv_callback = { .on_redirected = session_inv_on_redirected, }; +int ast_sip_session_sdp_answer(struct ast_sip_session *session) +{ + const pjmedia_sdp_session *offer = NULL, *answer; + struct ast_channel *chan; + SCOPE_ENTER(3, "%s\n", ast_sip_session_get_name(session)); + + if (!session || !session->inv_session || !session->inv_session->neg || !(chan = session->channel)) { + SCOPE_EXIT_RTN_VALUE(-1, "%s: invalid parameter(s)\n", ast_sip_session_get_name(session)); + } + + if (ast_channel_narrow_band_only(chan)) { + if (pjmedia_sdp_neg_get_neg_remote(session->inv_session->neg, &offer) != PJ_SUCCESS) { + SCOPE_EXIT_RTN_VALUE(-1, "%s: pjmedia_sdp_neg_get_neg_remote() failed\n", ast_sip_session_get_name(session)); + } + + if (handle_incoming_sdp(session, offer)) { + ast_sip_session_media_state_reset(session->pending_media_state); + SCOPE_EXIT_RTN_VALUE(-1, "%s: handle_incoming_sdp() failed and media state is reset\n", ast_sip_session_get_name(session)); + } + + answer = create_local_sdp(session->inv_session, session, offer, 0); + if (!answer) { + SCOPE_EXIT_RTN_VALUE(-1, "%s: create_local_sdp() failed\n", ast_sip_session_get_name(session)); + } + + if (pjsip_inv_set_sdp_answer(session->inv_session, answer) != PJ_SUCCESS) { + SCOPE_EXIT_RTN_VALUE(-1, "%s: pjsip_inv_set_sdp_answer() failed\n", ast_sip_session_get_name(session)); + } + } + + SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session)); +} + /*! \brief Hook for modifying outgoing messages with SDP to contain the proper address information */ static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_transport *transport) { @@ -6490,6 +6809,7 @@ AST_TEST_DEFINE(test_resolve_refresh_media_states) static int load_module(void) { pjsip_endpoint *endpt; + static const pj_str_t str_199 = { "199", 3 }; if (!ast_sip_get_sorcery() || !ast_sip_get_pjsip_endpoint()) { return AST_MODULE_LOAD_DECLINE; @@ -6508,6 +6828,9 @@ static int load_module(void) pjsip_inv_usage_init(endpt, &inv_callback); pjsip_100rel_init_module(endpt); pjsip_timer_init_module(endpt); + + pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_199); + if (ast_sip_register_service(&session_module)) { return AST_MODULE_LOAD_DECLINE; } @@ -6542,5 +6865,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, - .requires = "res_pjsip", + .requires = "res_pjsip,res_pjsip_outbound_registration", ); diff --git a/res/res_pjsip_session/pjsip_session_caps.c b/res/res_pjsip_session/pjsip_session_caps.c index d44f1a6bb0a1dbea7b2f4455e0fdf345e5bc4ed7..2735ae66950270456dc52f28c21d6da2bc261777 100644 --- a/res/res_pjsip_session/pjsip_session_caps.c +++ b/res/res_pjsip_session/pjsip_session_caps.c @@ -28,6 +28,7 @@ #include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip_session.h" #include "asterisk/res_pjsip_session_caps.h" +#include "asterisk/format_cache.h" static void log_caps(int level, const char *file, int line, const char *function, const struct ast_sip_session *session, enum ast_media_type media_type, @@ -152,13 +153,29 @@ struct ast_format_cap *ast_sip_session_create_joint_call_cap( const struct ast_sip_session *session, enum ast_media_type media_type, const struct ast_format_cap *remote) { - struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote, - session->endpoint->media.codecs, media_type, - session->call_direction == AST_SIP_SESSION_OUTGOING_CALL - ? session->endpoint->media.outgoing_call_offer_pref - : session->endpoint->media.incoming_call_offer_pref); + struct ast_format_cap *joint, *local = session->endpoint->media.codecs, *cap = NULL; + struct ast_format *format = ast_format_cache_get("g722"); + + if (local && session->call_direction == AST_SIP_SESSION_INCOMING_CALL && session->channel && + ast_channel_narrow_band_only(session->channel) && format) { + cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!cap || ast_format_cap_append_from_cap(cap, local, AST_MEDIA_TYPE_UNKNOWN)) { + ast_log(LOG_ERROR, "Failed to create a new cap\n"); + ao2_cleanup(cap); + cap = NULL; + } else { + ast_debug(3, "Remove g722 from the local capability\n"); + ast_format_cap_remove(cap, format); + local = cap; + } + } - log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, joint); + joint = ast_sip_create_joint_call_cap(remote, local, media_type, session->call_direction == AST_SIP_SESSION_OUTGOING_CALL ? + session->endpoint->media.outgoing_call_offer_pref : session->endpoint->media.incoming_call_offer_pref); + log_caps(LOG_DEBUG, session, media_type, local, remote, joint); + if (cap) { + ao2_cleanup(cap); + } return joint; } diff --git a/res/res_resolver_unbound.c b/res/res_resolver_unbound.c index 93bb3cadafd8d5f37ee0e6a96a8b5ed6adf47155..47cd883ceb35bf15ac2a8cef9850565a6a196e4f 100644 --- a/res/res_resolver_unbound.c +++ b/res/res_resolver_unbound.c @@ -257,6 +257,10 @@ static void unbound_resolver_callback(void *data, int err, struct ub_result *ub_ { struct ast_dns_query *query = data; + if (err || !ub_result) { + ast_log(LOG_ERROR, "Resolution query failed. Error: %s\n", ub_strerror(err)); + goto Exit; + } if (!ast_dns_resolver_set_result(query, ub_result->secure, ub_result->bogus, ub_result->rcode, S_OR(ub_result->canonname, ast_dns_query_get_name(query)), ub_result->answer_packet, ub_result->answer_len)) { int i; @@ -269,7 +273,7 @@ static void unbound_resolver_callback(void *data, int err, struct ub_result *ub_ } } } - +Exit: ast_dns_resolver_completed(query); ao2_ref(query, -1); ub_resolve_free(ub_result); diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 995ac58f83cee9571f5c7e6d867a257a418a8d6b..eaa0c75d0303e338d7f7905d8416196927b84ddd 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -213,6 +213,7 @@ static int dtmftimeout = DEFAULT_DTMF_TIMEOUT; static int rtpstart = DEFAULT_RTP_START; /*!< First port for RTP sessions (set in rtp.conf) */ static int rtpend = DEFAULT_RTP_END; /*!< Last port for RTP sessions (set in rtp.conf) */ +static int next_rtp_port = DEFAULT_RTP_START; static int rtcpstats; /*!< Are we debugging RTCP? */ static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */ static struct ast_sockaddr rtpdebugaddr; /*!< Debug packets to/from this host */ @@ -664,6 +665,7 @@ static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_ins static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username); static void ast_rtp_stop(struct ast_rtp_instance *instance); static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc); +static void ast_rtcp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc); static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level); static int ast_rtp_send_dtmf_bypass(struct ast_rtp_instance *instance, struct ast_frame *frame); static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance); @@ -2565,6 +2567,7 @@ static struct ast_rtp_engine asterisk_rtp_engine = { .stun_request = ast_rtp_stun_request, .stop = ast_rtp_stop, .qos = ast_rtp_qos_set, + .rtcp_qos = ast_rtcp_qos_set, .sendcng = ast_rtp_sendcng, .dtmfbypass = ast_rtp_send_dtmf_bypass, #ifdef HAVE_PJPROJECT @@ -2635,6 +2638,29 @@ static void dtls_perform_setup(struct dtls_details *dtls) } #endif +static void ast_rtp_network_info_notify(struct ast_rtp_instance *instance, struct ast_sockaddr *addr) +{ + struct ast_sockaddr prev_address; + char *addr_str, *addr_str_tmp; + + if (ast_sockaddr_isnull(addr)) { + ast_rtp_instance_get_remote_address(instance, &prev_address); + addr_str = ast_sockaddr_stringify(&prev_address); + addr_str_tmp = strstr(addr_str, ":"); + if (addr_str_tmp) + *addr_str_tmp = '\0'; + ast_send_network_info_ubus_event(0, "rtp", addr_str, ast_sockaddr_port(&prev_address)); + } + else { + addr_str = ast_sockaddr_stringify(addr); + addr_str_tmp = strstr(addr_str, ":"); + if (addr_str_tmp) + *addr_str_tmp = '\0'; + ast_send_network_info_ubus_event(1, "rtp", addr_str, ast_sockaddr_port(addr)); + } + ast_rtp_instance_set_remote_address(instance, addr); +} + #ifdef HAVE_PJPROJECT static void rtp_learning_start(struct ast_rtp *rtp); @@ -2655,7 +2681,7 @@ static void ast_rtp_ice_start_media(pj_ice_sess *ice, pj_status_t status) /* Symmetric RTP must be disabled for the remote address to not get overwritten */ ast_rtp_instance_set_prop(instance, AST_RTP_PROPERTY_NAT, 0); - ast_rtp_instance_set_remote_address(instance, &remote_address); + ast_rtp_network_info_notify(instance, &remote_address); } if (rtp->rtcp) { @@ -3280,7 +3306,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s if (rtcp) { ast_sockaddr_copy(&rtp->rtcp->them, sa); } else { - ast_rtp_instance_set_remote_address(instance, sa); + ast_rtp_network_info_notify(instance, sa); } } return 0; @@ -3903,8 +3929,11 @@ static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_ } /* Now actually find a free RTP port to use */ + /* Comment out to make rtp port sequentially increased x = (ast_random() % (rtpend - rtpstart)) + rtpstart; x = x & ~1; + */ + x = next_rtp_port; startplace = x; /* Protection against infinite loops in the case there is a potential case where the loop is not broken such as an odd @@ -3960,6 +3989,7 @@ static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_ rtp->dtls.timeout_timer = -1; #endif + next_rtp_port = x + 2; return 0; } @@ -8221,7 +8251,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc if ((ast_stun_handle_packet(rtp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT) && ast_sockaddr_isnull(&remote_address)) { ast_sockaddr_from_sin(&addr, &addr_tmp); - ast_rtp_instance_set_remote_address(instance, &addr); + ast_rtp_network_info_notify(instance, &addr); } return &ast_null_frame; } @@ -9036,7 +9066,17 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) { struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index); - ast_rtp_instance_set_remote_address(mapping->instance, addr); + ast_rtp_network_info_notify(mapping->instance, addr); + } + + if (!ast_sockaddr_isnull(addr)) { + char *addr_str, *addr_str_tmp; + + addr_str = ast_sockaddr_stringify(addr); + addr_str_tmp = strstr(addr_str, ":"); + if (addr_str_tmp) + *addr_str_tmp = '\0'; + ast_send_network_info_ubus_event(1, "rtp", addr_str, ast_sockaddr_port(addr)); } /* Need to reset the DTMF last sequence number and the timestamp of the last END packet */ @@ -9315,7 +9355,7 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance) rtp->red = NULL; } - ast_rtp_instance_set_remote_address(instance, &addr); + ast_rtp_network_info_notify(instance, &addr); ast_set_flag(rtp, FLAG_NEED_MARKER_BIT); } @@ -9328,6 +9368,14 @@ static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, return ast_set_qos(rtp->s, tos, cos, desc); } +static void ast_rtcp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char *desc) +{ + struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + if (rtp->rtcp && rtp->rtcp->s) { + ast_set_qos(rtp->rtcp->s, tos, cos, desc); + } +} + /*! * \brief generate comfort noice (CNG) * @@ -9571,7 +9619,7 @@ static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instanc ao2_lock(child); - ast_rtp_instance_set_remote_address(child, &them); + ast_rtp_network_info_notify(child, &them); return 0; } @@ -10010,6 +10058,7 @@ static int rtp_reload(int reload, int by_external_config) rtpstart = DEFAULT_RTP_START; rtpend = DEFAULT_RTP_END; + next_rtp_port = rtpstart; rtcpinterval = RTCP_DEFAULT_INTERVALMS; dtmftimeout = DEFAULT_DTMF_TIMEOUT; strictrtp = DEFAULT_STRICT_RTP; @@ -10263,6 +10312,7 @@ static int rtp_reload(int reload, int by_external_config) rtpend = DEFAULT_RTP_END; } ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend); + next_rtp_port = rtpstart; return 0; } diff --git a/sounds/audio_voip.gsm b/sounds/audio_voip.gsm new file mode 100644 index 0000000000000000000000000000000000000000..6db498565e1f158357de1a6798e4ba379f15d3cb Binary files /dev/null and b/sounds/audio_voip.gsm differ diff --git a/sounds/audio_wan.gsm b/sounds/audio_wan.gsm new file mode 100644 index 0000000000000000000000000000000000000000..82dec159991c1d7cf5519d50ee92f83dfa7feddc Binary files /dev/null and b/sounds/audio_wan.gsm differ diff --git a/third-party/apply_patches b/third-party/apply_patches index 3c0a6bc76e70e7a341934da82aa5189967d7425b..f847fd60833c363d4c1645fe9b5a392a7db09189 100755 --- a/third-party/apply_patches +++ b/third-party/apply_patches @@ -21,7 +21,7 @@ if [ ! -d "$sourcedir" ] ; then exit 1 fi -patches=$(${FIND} "$patchdir" -name "*.patch") +patches=$(${FIND} "$patchdir" -name "*.patch" | sort) if [ x"$patches" = x"" ] ; then echo "No patches in $patchdir" >&2 exit 0 diff --git a/third-party/jansson/Makefile b/third-party/jansson/Makefile index f24a1c6a6db9864ae493364075e7cd20d80585e3..e3b6f5aa0fe398c8628eba64492783a96a267901 100644 --- a/third-party/jansson/Makefile +++ b/third-party/jansson/Makefile @@ -84,8 +84,9 @@ source/.unpacked: $(DOWNLOAD_DIR)/$(TARBALL_FILE) $(ECHO_PREFIX) Rebuilding $(CMD_PREFIX) $(MAKE) clean $(REALLY_QUIET) +JANSSON_CONFIG_OPTS += --host=aarch64-rdk-linux source/config.status: source/.unpacked Makefile.rules .rebuild_needed - $(ECHO_PREFIX) Configuring + $(ECHO_PREFIX) Configuring with $(JANSSON_CONFIG_OPTS) $(CMD_PREFIX) (cd source ; ./configure $(QUIET_CONFIGURE) $(JANSSON_CONFIG_OPTS) --disable-shared \ --enable-static --prefix=$(JANSSON_DIR)/dest --libdir=$(JANSSON_DIR)/dest/lib CFLAGS="$(OPTIMIZE_CFLAGS)") diff --git a/third-party/pjproject/patches/0011-Add-support-for-Session-ID-header.patch b/third-party/pjproject/patches/0011-Add-support-for-Session-ID-header.patch new file mode 100644 index 0000000000000000000000000000000000000000..b52f8449506ef72cd9d0a5dcc78a9175c18e635b --- /dev/null +++ b/third-party/pjproject/patches/0011-Add-support-for-Session-ID-header.patch @@ -0,0 +1,691 @@ +From 3c5be9f8088766785db5f18e306d187fb0065280 Mon Sep 17 00:00:00 2001 +From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu> +Date: Mon, 24 Jun 2024 12:12:59 +0200 +Subject: [PATCH] Add support for Session-ID header + +According to RFC7329 there is a need for having a globally unique +session identifier for the same SIP session that can be consistently +maintained across SIP Proxies. There is a Call-ID header value already +as a globally unique identifier but in practice it is often changed by +SIP Back-to-Back User Agents. Therefore in order to provide an +identifier that will not be modified/replaced by B2BUAs a new SIP header +"Session-ID" is added. + +The general concept of "Session-ID" header is that the UAC generating an +out-of-dialog request generates a new, unique value that remains +constant for the duration of the transaction, any dialog created from +that request or for a registration. +--- + pjsip-apps/src/samples/footprint.c | 4 +-- + pjsip-apps/src/samples/pjsip-perf.c | 4 +-- + pjsip/include/pjsip/sip_dialog.h | 1 + + pjsip/include/pjsip/sip_transport.h | 3 ++ + pjsip/include/pjsip/sip_util.h | 4 +++ + pjsip/src/pjsip-simple/publishc.c | 8 +++++ + pjsip/src/pjsip-ua/sip_reg.c | 8 +++++ + pjsip/src/pjsip/sip_dialog.c | 17 ++++++++++ + pjsip/src/pjsip/sip_endpoint.c | 3 +- + pjsip/src/pjsip/sip_util.c | 52 ++++++++++++++++++++++++++--- + pjsip/src/pjsua-lib/pjsua_acc.c | 2 +- + pjsip/src/pjsua-lib/pjsua_im.c | 4 +-- + pjsip/src/test/transport_test.c | 6 ++-- + pjsip/src/test/tsx_basic_test.c | 6 ++-- + pjsip/src/test/tsx_bench.c | 4 +-- + pjsip/src/test/tsx_uac_test.c | 2 +- + pjsip/src/test/tsx_uas_test.c | 3 +- + pjsip/src/test/txdata_test.c | 11 +++--- + 18 files changed, 115 insertions(+), 27 deletions(-) + +diff --git a/pjsip-apps/src/samples/footprint.c b/pjsip-apps/src/samples/footprint.c +index 6fa4425..7ac1170 100644 +--- a/pjsip-apps/src/samples/footprint.c ++++ b/pjsip-apps/src/samples/footprint.c +@@ -275,10 +275,10 @@ int dummy_function() + #endif + + #ifdef HAS_PJSIP_CORE_MSG_UTIL +- pjsip_endpt_create_request(NULL, NULL, NULL, NULL, NULL, NULL, NULL, ++ pjsip_endpt_create_request(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + -1, NULL, NULL); + pjsip_endpt_create_request_from_hdr(NULL, NULL, NULL, NULL, NULL, NULL, +- NULL, -1, NULL, NULL); ++ NULL, NULL, -1, NULL, NULL); + pjsip_endpt_create_response(NULL, NULL, -1, NULL, NULL); + pjsip_endpt_create_ack(NULL, NULL, NULL, NULL); + pjsip_endpt_create_cancel(NULL, NULL, NULL); +diff --git a/pjsip-apps/src/samples/pjsip-perf.c b/pjsip-apps/src/samples/pjsip-perf.c +index e13d593..df04017 100644 +--- a/pjsip-apps/src/samples/pjsip-perf.c ++++ b/pjsip-apps/src/samples/pjsip-perf.c +@@ -1329,7 +1329,7 @@ static pj_status_t submit_stateless_job(void) + status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method, + &app.client.dst_uri, &app.local_uri, + &app.client.dst_uri, &app.local_contact, +- NULL, -1, NULL, &tdata); ++ NULL, NULL, -1, NULL, &tdata); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Error creating request", status); + report_completion(701); +@@ -1395,7 +1395,7 @@ static pj_status_t submit_job(void) + status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method, + &app.client.dst_uri, &app.local_uri, + &app.client.dst_uri, &app.local_contact, +- NULL, -1, NULL, &tdata); ++ NULL, NULL, -1, NULL, &tdata); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Error creating request", status); + report_completion(701); +diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h +index 49853ce..48cb048 100644 +--- a/pjsip/include/pjsip/sip_dialog.h ++++ b/pjsip/include/pjsip/sip_dialog.h +@@ -161,6 +161,7 @@ struct pjsip_dialog + pj_bool_t secure; /**< Use secure transport? */ + pj_bool_t add_allow; /**< Add Allow header in requests? */ + pjsip_cid_hdr *call_id; /**< Call-ID header. */ ++ pjsip_generic_string_hdr *session_id; /**< Session-ID header. */ + pjsip_route_hdr route_set; /**< Route set. */ + pj_bool_t route_set_frozen; /**< Route set has been set. */ + pjsip_auth_clt_sess auth_sess; /**< Client authentication session. */ +diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h +index 87f982c..ea8acac 100644 +--- a/pjsip/include/pjsip/sip_transport.h ++++ b/pjsip/include/pjsip/sip_transport.h +@@ -371,6 +371,9 @@ struct pjsip_rx_data + /** The Call-ID header as found in the message. */ + pjsip_cid_hdr *cid; + ++ /** The Session-ID header as found in the message. */ ++ pjsip_generic_string_hdr *sid; ++ + /** The From header as found in the message. */ + pjsip_from_hdr *from; + +diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h +index bba120f..8f44f0f 100644 +--- a/pjsip/include/pjsip/sip_util.h ++++ b/pjsip/include/pjsip/sip_util.h +@@ -259,6 +259,7 @@ PJ_DECL(pj_status_t) pjsip_target_assign_status(pjsip_target *target, + * can be tokens, or a quoted string, if a larger + * character set is desired. + * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). ++ * @param session_id Optional Session-ID (put NULL to generate unique Session-ID). + * @param cseq Optional CSeq (put -1 to generate random CSeq). + * @param text Optional text body (put NULL to omit body). + * @param p_tdata Pointer to receive the transmit data. +@@ -272,6 +273,7 @@ PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + const pj_str_t *to, + const pj_str_t *contact, + const pj_str_t *call_id, ++ const pj_str_t *session_id, + int cseq, + const pj_str_t *text, + pjsip_tx_data **p_tdata); +@@ -298,6 +300,7 @@ PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + * @param to To header. + * @param contact Contact header. + * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). ++ * @param session_id Optional Session-ID (put NULL to generate unique Session-ID). + * @param cseq Optional CSeq (put -1 to generate random CSeq). + * @param text Optional text body (put NULL to omit body). + * @param p_tdata Pointer to receive the transmit data. +@@ -312,6 +315,7 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + const pjsip_to_hdr *to, + const pjsip_contact_hdr *contact, + const pjsip_cid_hdr *call_id, ++ const pjsip_generic_string_hdr *session_id, + int cseq, + const pj_str_t *text, + pjsip_tx_data **p_tdata); +diff --git a/pjsip/src/pjsip-simple/publishc.c b/pjsip/src/pjsip-simple/publishc.c +index a18a32f..8ed6c2a 100644 +--- a/pjsip/src/pjsip-simple/publishc.c ++++ b/pjsip/src/pjsip-simple/publishc.c +@@ -85,6 +85,7 @@ struct pjsip_publishc + pj_str_t str_target_uri; + pjsip_uri *target_uri; + pjsip_cid_hdr *cid_hdr; ++ pjsip_generic_string_hdr *sid_hdr; + pjsip_cseq_hdr *cseq_hdr; + pj_str_t from_uri; + pjsip_from_hdr *from_hdr; +@@ -255,6 +256,7 @@ PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc, + pj_uint32_t expires) + { + pj_str_t tmp; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; + + PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri && + expires, PJ_EINVAL); +@@ -299,6 +301,11 @@ PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc, + pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool); + pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id); + ++ /* Generate Session-ID header. */ ++ pj_str_t session_id_uuid; ++ pj_create_unique_string(pubc->pool, &session_id_uuid); ++ pubc->sid_hdr = pjsip_generic_string_hdr_create(pubc->pool, &STR_SESSION, &session_id_uuid); ++ + /* Set "CSeq" header. */ + pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool); + pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF; +@@ -387,6 +394,7 @@ static pj_status_t create_request(pjsip_publishc *pubc, + pubc->to_hdr, + NULL, + pubc->cid_hdr, ++ pubc->sid_hdr, + pubc->cseq_hdr->cseq, + NULL, + &tdata); +diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c +index 947e9ed..fbb1dde 100644 +--- a/pjsip/src/pjsip-ua/sip_reg.c ++++ b/pjsip/src/pjsip-ua/sip_reg.c +@@ -79,6 +79,7 @@ struct pjsip_regc + pj_str_t str_srv_url; + pjsip_uri *srv_url; + pjsip_cid_hdr *cid_hdr; ++ pjsip_generic_string_hdr *sid_hdr; + pjsip_cseq_hdr *cseq_hdr; + pj_str_t from_uri; + pjsip_from_hdr *from_hdr; +@@ -350,6 +351,7 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, + { + pj_str_t tmp; + pj_status_t status; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; + + PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && + expires, PJ_EINVAL); +@@ -400,6 +402,11 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, + regc->cid_hdr = pjsip_cid_hdr_create(regc->pool); + pj_create_unique_string(regc->pool, ®c->cid_hdr->id); + ++ /* Generate Session-ID header. */ ++ pj_str_t session_id_uuid; ++ pj_create_unique_string(regc->pool, &session_id_uuid); ++ regc->sid_hdr = pjsip_generic_string_hdr_create(regc->pool, &STR_SESSION, &session_id_uuid); ++ + /* Set "CSeq" header. */ + regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool); + regc->cseq_hdr->cseq = pj_rand() % 0xFFFF; +@@ -522,6 +529,7 @@ static pj_status_t create_request(pjsip_regc *regc, + regc->to_hdr, + NULL, + regc->cid_hdr, ++ regc->sid_hdr, + regc->cseq_hdr->cseq, + NULL, + &tdata); +diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c +index bba63b7..a8e15ac 100644 +--- a/pjsip/src/pjsip/sip_dialog.c ++++ b/pjsip/src/pjsip/sip_dialog.c +@@ -177,6 +177,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac2( + pj_status_t status; + pj_str_t tmp; + pjsip_dialog *dlg; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; + + /* Check arguments. */ + PJ_ASSERT_RETURN(create_param->ua && create_param->local_uri.slen && +@@ -327,6 +328,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac2( + dlg->call_id = pjsip_cid_hdr_create(dlg->pool); + pj_create_unique_string(dlg->pool, &dlg->call_id->id); + ++ /* Generate Session-ID header. */ ++ pj_str_t session_id_uuid; ++ pj_create_unique_string(dlg->pool, &session_id_uuid); ++ dlg->session_id = pjsip_generic_string_hdr_create(dlg->pool, &STR_SESSION, &session_id_uuid); ++ + /* Initial route set is empty. */ + pj_list_init(&dlg->route_set); + +@@ -522,6 +528,11 @@ pj_status_t create_uas_dialog( pjsip_user_agent *ua, + dlg->call_id = (pjsip_cid_hdr*) + pjsip_hdr_clone(dlg->pool, rdata->msg_info.cid); + ++ /* Session-ID */ ++ if (rdata->msg_info.sid) ++ dlg->session_id = (pjsip_generic_string_hdr*) ++ pjsip_hdr_clone(dlg->pool, rdata->msg_info.sid); ++ + /* Route set. + * RFC 3261 Section 12.1.1: + * The route set MUST be set to the list of URIs in the Record-Route +@@ -773,6 +784,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, + dlg->call_id = (pjsip_cid_hdr*) + pjsip_hdr_clone(dlg->pool, first_dlg->call_id); + ++ /* Session-ID */ ++ if (first_dlg->session_id) ++ dlg->session_id = (pjsip_generic_string_hdr*) ++ pjsip_hdr_clone(dlg->pool, first_dlg->session_id); ++ + /* Get route-set from the response. */ + pj_list_init(&dlg->route_set); + end_hdr = &msg->hdr; +@@ -1166,6 +1182,7 @@ static pj_status_t dlg_create_request_throw( pjsip_dialog *dlg, + dlg->remote.info, + contact, + dlg->call_id, ++ dlg->session_id, + cseq, + NULL, + &tdata); +diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c +index f77e217..e70a0f6 100644 +--- a/pjsip/src/pjsip/sip_endpoint.c ++++ b/pjsip/src/pjsip/sip_endpoint.c +@@ -967,7 +967,6 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt, + pj_bool_t handled = PJ_FALSE; + + PJ_UNUSED_ARG(msg); +- + if (status != PJ_SUCCESS) { + char info[30]; + char errmsg[PJ_ERR_MSG_SIZE]; +@@ -981,6 +980,8 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt, + + if (rdata->msg_info.cid == NULL || rdata->msg_info.cid->id.slen) + pj_strcpy2(&p, "Call-ID"); ++ if (rdata->msg_info.sid == NULL || rdata->msg_info.sid->hvalue.slen) ++ pj_strcpy2(&p, "Session-ID"); + if (rdata->msg_info.from == NULL) + pj_strcpy2(&p, " From"); + if (rdata->msg_info.to == NULL) +diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c +index 86853f4..2d08cd9 100644 +--- a/pjsip/src/pjsip/sip_util.c ++++ b/pjsip/src/pjsip/sip_util.c +@@ -199,6 +199,7 @@ static void init_request_throw( pjsip_endpoint *endpt, + pjsip_to_hdr *param_to, + pjsip_contact_hdr *param_contact, + pjsip_cid_hdr *param_call_id, ++ pjsip_generic_string_hdr *param_session_id, + pjsip_cseq_hdr *param_cseq, + const pj_str_t *param_text) + { +@@ -239,6 +240,9 @@ static void init_request_throw( pjsip_endpoint *endpt, + /* Add Call-ID header. */ + pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_call_id); + ++ /* Add Session-ID header. */ ++ pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_session_id); ++ + /* Add CSeq header. */ + pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_cseq); + +@@ -293,6 +297,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + const pj_str_t *param_to, + const pj_str_t *param_contact, + const pj_str_t *param_call_id, ++ const pj_str_t *param_session_id, + int param_cseq, + const pj_str_t *param_text, + pjsip_tx_data **p_tdata) +@@ -304,6 +309,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + pjsip_contact_hdr *contact; + pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */ + pjsip_cid_hdr *call_id; ++ pjsip_generic_string_hdr *session_id; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; + pj_str_t tmp; + pj_status_t status; + const pj_str_t STR_CONTACT = { "Contact", 7 }; +@@ -367,6 +374,15 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + else + pj_create_unique_string(tdata->pool, &call_id->id); + ++ /* Session-ID */ ++ if (param_session_id != NULL && param_session_id->slen) { ++ session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, param_session_id); ++ } else { ++ pj_str_t session_id_uuid; ++ pj_create_unique_string(tdata->pool, &session_id_uuid); ++ session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, &session_id_uuid); ++ } ++ + /* CSeq */ + cseq = pjsip_cseq_hdr_create(tdata->pool); + if (param_cseq >= 0) +@@ -379,7 +395,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, + + /* Create the request. */ + init_request_throw( endpt, tdata, &cseq->method, target, from, to, +- contact, call_id, cseq, param_text); ++ contact, call_id, session_id, cseq, param_text); + } + PJ_CATCH_ANY { + status = PJ_ENOMEM; +@@ -402,6 +418,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + const pjsip_to_hdr *param_to, + const pjsip_contact_hdr *param_contact, + const pjsip_cid_hdr *param_call_id, ++ const pjsip_generic_string_hdr *param_session_id, + int param_cseq, + const pj_str_t *param_text, + pjsip_tx_data **p_tdata) +@@ -412,6 +429,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + pjsip_to_hdr *to; + pjsip_contact_hdr *contact; + pjsip_cid_hdr *call_id; ++ pjsip_generic_string_hdr *session_id; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; + pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */ + pj_status_t status; + PJ_USE_EXCEPTION; +@@ -447,6 +466,15 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + else + pj_create_unique_string(tdata->pool, &call_id->id); + ++ /* Session-ID */ ++ if (param_session_id != NULL && param_session_id->hvalue.slen) { ++ session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, ¶m_session_id->hvalue); ++ } else { ++ pj_str_t session_id_uuid; ++ pj_create_unique_string(tdata->pool, &session_id_uuid); ++ session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, &session_id_uuid); ++ } ++ + cseq = pjsip_cseq_hdr_create(tdata->pool); + if (param_cseq >= 0) + cseq->cseq = param_cseq; +@@ -456,7 +484,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + + /* Copy headers to the request. */ + init_request_throw(endpt, tdata, &cseq->method, target, from, to, +- contact, call_id, cseq, param_text); ++ contact, call_id, session_id, cseq, param_text); + } + PJ_CATCH_ANY { + status = PJ_ENOMEM; +@@ -488,6 +516,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt, + pjsip_via_hdr *top_via = NULL, *via; + pjsip_rr_hdr *rr; + pj_status_t status; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; + + /* Check arguments. */ + PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL); +@@ -559,6 +588,11 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt, + hdr = (pjsip_hdr*) pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); + ++ /* Copy Session-ID header. */ ++ hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_name( req_msg, &STR_SESSION, NULL); ++ if (hdr) ++ pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); ++ + /* Copy From header. */ + hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, rdata->msg_info.from); + pjsip_msg_add_hdr( msg, hdr); +@@ -604,6 +638,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt, + const pjsip_from_hdr *from_hdr; + const pjsip_to_hdr *to_hdr; + const pjsip_cid_hdr *cid_hdr; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; ++ const pjsip_generic_string_hdr *sid_hdr; + const pjsip_cseq_hdr *cseq_hdr; + const pjsip_hdr *hdr; + pjsip_hdr *via; +@@ -632,6 +668,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt, + cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(invite_msg, CALL_ID); + PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); + ++ sid_hdr = (const pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(invite_msg, &STR_SESSION, NULL); ++ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); ++ + cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(invite_msg, CSEQ); + PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); + +@@ -642,7 +681,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt, + pjsip_get_ack_method(), + tdata->msg->line.req.uri, + from_hdr, to_hdr, +- NULL, cid_hdr, ++ NULL, cid_hdr, sid_hdr, + cseq_hdr->cseq, NULL, + &ack); + +@@ -701,6 +740,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + const pjsip_from_hdr *from_hdr; + const pjsip_to_hdr *to_hdr; + const pjsip_cid_hdr *cid_hdr; ++ const pj_str_t STR_SESSION = { "Session-ID", 10 }; ++ const pjsip_generic_string_hdr *sid_hdr; + const pjsip_cseq_hdr *cseq_hdr; + const pjsip_hdr *hdr; + pjsip_hdr *via; +@@ -723,6 +764,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(req_tdata->msg, CALL_ID); + PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); + ++ sid_hdr = (const pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(req_tdata->msg, &STR_SESSION, NULL); ++ PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); ++ + cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(req_tdata->msg, CSEQ); + PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); + +@@ -733,7 +777,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + pjsip_get_cancel_method(), + req_tdata->msg->line.req.uri, + from_hdr, to_hdr, +- NULL, cid_hdr, ++ NULL, cid_hdr, sid_hdr, + cseq_hdr->cseq, NULL, + &cancel_tdata); + +diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c +index 9982b03..d41ebe6 100644 +--- a/pjsip/src/pjsua-lib/pjsua_acc.c ++++ b/pjsip/src/pjsua-lib/pjsua_acc.c +@@ -3307,7 +3307,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id, + + status = pjsip_endpt_create_request(pjsua_var.endpt, method, target, + &acc->cfg.id, target, +- NULL, NULL, -1, NULL, &tdata); ++ NULL, NULL, NULL, -1, NULL, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create request", status); + return status; +diff --git a/pjsip/src/pjsua-lib/pjsua_im.c b/pjsip/src/pjsua-lib/pjsua_im.c +index eb8212e..3567749 100644 +--- a/pjsip/src/pjsua-lib/pjsua_im.c ++++ b/pjsip/src/pjsua-lib/pjsua_im.c +@@ -561,7 +561,7 @@ PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id, + (msg_data && msg_data->target_uri.slen? + &msg_data->target_uri: to), + &acc->cfg.id, +- to, NULL, NULL, -1, NULL, &tdata); ++ to, NULL, NULL, NULL, -1, NULL, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create request", status); + return status; +@@ -683,7 +683,7 @@ PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id, + /* Create request. */ + status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method, + to, &acc->cfg.id, +- to, NULL, NULL, -1, NULL, &tdata); ++ to, NULL, NULL, NULL, -1, NULL, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create request", status); + return status; +diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c +index 766c47a..62bcd95 100644 +--- a/pjsip/src/test/transport_test.c ++++ b/pjsip/src/test/transport_test.c +@@ -213,7 +213,7 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type, + + pjsip_method_set(&method, PJSIP_OPTIONS_METHOD); + status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to, +- &contact, &call_id, CSEQ_VALUE, ++ &contact, &call_id, NULL, CSEQ_VALUE, + &body, &tdata ); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +@@ -386,7 +386,7 @@ static pj_status_t rt_send_request(int thread_id) + + status = pjsip_endpt_create_request( endpt, &pjsip_options_method, + &target, &from, &to, +- &contact, &call_id, -1, ++ &contact, &call_id, NULL, -1, + NULL, &tdata ); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +@@ -730,7 +730,7 @@ int transport_load_test(char *target_url) + call_id = pj_str("thecallid"); + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + &target, &from, +- &target, &from, &call_id, ++ &target, &from, &call_id, NULL, + i, NULL, &tdata ); + if (status != PJ_SUCCESS) { + app_perror("error creating request", status); +diff --git a/pjsip/src/test/tsx_basic_test.c b/pjsip/src/test/tsx_basic_test.c +index 8c1317e..8e4209c 100644 +--- a/pjsip/src/test/tsx_basic_test.c ++++ b/pjsip/src/test/tsx_basic_test.c +@@ -41,7 +41,7 @@ static int tsx_layer_test(void) + from = pj_str(FROM_URI); + + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, +- &from, &target, NULL, NULL, -1, NULL, ++ &from, &target, NULL, NULL, NULL, -1, NULL, + &tdata); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +@@ -86,7 +86,7 @@ static int double_terminate(void) + + /* Create request. */ + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, +- &from, &target, NULL, NULL, -1, NULL, ++ &from, &target, NULL, NULL, NULL, -1, NULL, + &tdata); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +@@ -238,7 +238,7 @@ static int tsx_create_and_send_req(void *arg) + + status = pjsip_endpt_create_request(endpt, &pjsip_options_method, + &dst_uri, &from_uri, &dst_uri, +- NULL, NULL, -1, NULL, ++ NULL, NULL, -1, NULL, NULL, + &tdata); + if (status != PJ_SUCCESS) + return status; +diff --git a/pjsip/src/test/tsx_bench.c b/pjsip/src/test/tsx_bench.c +index 87fb35e..42a4c3c 100644 +--- a/pjsip/src/test/tsx_bench.c ++++ b/pjsip/src/test/tsx_bench.c +@@ -42,7 +42,7 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) + + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + &str_target, &str_from, &str_to, +- &str_contact, NULL, -1, NULL, ++ &str_contact, NULL, NULL, -1, NULL, + &request); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +@@ -114,7 +114,7 @@ static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) + + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + &str_target, &str_from, &str_to, +- &str_contact, NULL, -1, NULL, ++ &str_contact, NULL, NULL, -1, NULL, + &request); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c +index ff1e682..c9d80d5 100644 +--- a/pjsip/src/test/tsx_uac_test.c ++++ b/pjsip/src/test/tsx_uac_test.c +@@ -977,7 +977,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri, + + /* Create request. */ + status = pjsip_endpt_create_request( endpt, method, &target, +- &from, &target, NULL, NULL, -1, ++ &from, &target, NULL, NULL, NULL, -1, + NULL, &tdata); + if (status != PJ_SUCCESS) { + app_perror(" Error: unable to create request", status); +diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c +index 6a0a9a4..2e4931d 100644 +--- a/pjsip/src/test/tsx_uas_test.c ++++ b/pjsip/src/test/tsx_uas_test.c +@@ -1107,6 +1107,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) + rdata->msg_info.to, + NULL, + rdata->msg_info.cid, ++ NULL, + rdata->msg_info.cseq->cseq, + NULL, + &tdata); +@@ -1214,7 +1215,7 @@ static int perform_test( char *target_uri, char *from_uri, + + /* Create request. */ + status = pjsip_endpt_create_request( endpt, method, &target, +- &from, &target, NULL, NULL, -1, ++ &from, &target, NULL, NULL, NULL, -1, + NULL, &tdata); + if (status != PJ_SUCCESS) { + app_perror(" Error: unable to create request", status); +diff --git a/pjsip/src/test/txdata_test.c b/pjsip/src/test/txdata_test.c +index 3fd9330..e738fd6 100644 +--- a/pjsip/src/test/txdata_test.c ++++ b/pjsip/src/test/txdata_test.c +@@ -54,7 +54,7 @@ static int core_txdata_test(void) + body = pj_str("Hello world!"); + + status = pjsip_endpt_create_request( endpt, &pjsip_invite_method, &target, +- &from, &to, &contact, NULL, 10, &body, ++ &from, &to, &contact, NULL, NULL, 10, &body, + &invite); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +@@ -107,6 +107,7 @@ static int core_txdata_test(void) + HFIND(invite->msg,to,TO), + HFIND(invite->msg,contact,CONTACT), + HFIND(invite->msg,cid,CALL_ID), ++ NULL, + 10, &body, &invite2); + if (status != PJ_SUCCESS) { + app_perror(" error: create second request failed", status); +@@ -354,7 +355,7 @@ static int gcc_test() + + /* Create request with header param in target URI. */ + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, +- &target, &target, &target, NULL, -1, ++ &target, &target, &target, NULL, NULL, -1, + NULL, &tdata); + if (status != 0) { + app_perror(" error: Unable to create request", status); +@@ -445,7 +446,7 @@ static int txdata_test_uri_params(void) + + /* Create request with header param in target URI. */ + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target, +- &target, &target, &contact, NULL, -1, ++ &target, &target, &contact, NULL, NULL, -1, + NULL, &tdata); + if (status != 0) { + app_perror(" error: Unable to create request", status); +@@ -650,7 +651,7 @@ static int create_request_bench(pj_timestamp *p_elapsed) + for (j=0; j<COUNT; ++j) { + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + &str_target, &str_from, &str_to, +- &str_contact, NULL, -1, NULL, ++ &str_contact, NULL, NULL, -1, NULL, + &tdata[j]); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +@@ -701,7 +702,7 @@ static int create_response_bench(pj_timestamp *p_elapsed) + + status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, + &str_target, &str_from, &str_to, +- &str_contact, NULL, -1, NULL, ++ &str_contact, NULL, NULL, -1, NULL, + &request); + if (status != PJ_SUCCESS) { + app_perror(" error: unable to create request", status); +-- +2.34.1 + diff --git a/third-party/pjproject/patches/0012-increase-cseq-for-PRACK-per-each-dialog.patch b/third-party/pjproject/patches/0012-increase-cseq-for-PRACK-per-each-dialog.patch new file mode 100644 index 0000000000000000000000000000000000000000..a91a86e64985ec6a23f9a0c69422271901213ec7 --- /dev/null +++ b/third-party/pjproject/patches/0012-increase-cseq-for-PRACK-per-each-dialog.patch @@ -0,0 +1,188 @@ +From d64d70b72b3b00a8fb26267518f555a7f9a33aab Mon Sep 17 00:00:00 2001 +From: "wenpeng.song" <wenpeng.song@iopsys.eu> +Date: Tue, 9 Jul 2024 13:58:23 +0200 +Subject: [PATCH] increase cseq for PRACK per each dialog + +--- + pjsip/include/pjsip-ua/sip_100rel.h | 19 +++++++++++++ + pjsip/include/pjsip/sip_dialog.h | 2 ++ + pjsip/src/pjsip-ua/sip_100rel.c | 22 +++++---------- + pjsip/src/pjsip/sip_dialog.c | 42 +++++++++++++++++++++++++++++ + 4 files changed, 69 insertions(+), 16 deletions(-) + +diff --git a/pjsip/include/pjsip-ua/sip_100rel.h b/pjsip/include/pjsip-ua/sip_100rel.h +index 089ab91fd..6e0dbde45 100644 +--- a/pjsip/include/pjsip-ua/sip_100rel.h ++++ b/pjsip/include/pjsip-ua/sip_100rel.h +@@ -27,6 +27,25 @@ + + #include <pjsip-ua/sip_inv.h> + ++typedef struct uas_state_t uas_state_t; ++/* UAC state */ ++typedef struct uac_state_t ++{ ++ pj_str_t tag; /* To tag */ ++ pj_int32_t cseq; ++ pj_int32_t out_cseq; /* current outgoing cseq for PRACK */ ++ pj_uint32_t rseq; /* Initialized to -1 */ ++ struct uac_state_t *next; /* next call leg */ ++} uac_state_t; ++ ++/* State attached to each dialog. */ ++struct dlg_data ++{ ++ pjsip_inv_session *inv; ++ uas_state_t *uas_state; ++ uac_state_t *uac_state_list; ++}; ++typedef struct dlg_data dlg_data; + + /** + * @defgroup PJSIP_100REL 100rel/PRACK - Reliability of Provisional Responses +diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h +index 49853ce47..7a2086f61 100644 +--- a/pjsip/include/pjsip/sip_dialog.h ++++ b/pjsip/include/pjsip/sip_dialog.h +@@ -190,6 +190,8 @@ struct pjsip_dialog + */ + pjsip_host_port via_addr; /**< Via address. */ + const void *via_tp; /**< Via transport. */ ++ int prack_flag; ++ int mod_100rel_id; + }; + + /** +diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c +index 9fa1b9bfd..f31ab1522 100644 +--- a/pjsip/src/pjsip-ua/sip_100rel.c ++++ b/pjsip/src/pjsip-ua/sip_100rel.c +@@ -38,7 +38,6 @@ PJ_DEF_DATA(const pjsip_method) pjsip_prack_method = + { "PRACK", 5 } + }; + +-typedef struct dlg_data dlg_data; + + /* + * Static prototypes. +@@ -101,23 +100,8 @@ typedef struct uas_state_t + } uas_state_t; + + +-/* UAC state */ +-typedef struct uac_state_t +-{ +- pj_str_t tag; /* To tag */ +- pj_int32_t cseq; +- pj_uint32_t rseq; /* Initialized to -1 */ +- struct uac_state_t *next; /* next call leg */ +-} uac_state_t; + + +-/* State attached to each dialog. */ +-struct dlg_data +-{ +- pjsip_inv_session *inv; +- uas_state_t *uas_state; +- uac_state_t *uac_state_list; +-}; + + + /***************************************************************************** +@@ -281,6 +265,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, + if (uac_state == NULL) { + uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t); + uac_state->cseq = rdata->msg_info.cseq->cseq; ++ uac_state->out_cseq = uac_state->cseq; + uac_state->rseq = rseq - 1; + pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag); + uac_state->next = dd->uac_state_list; +@@ -290,6 +275,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, + /* If this is from new INVITE transaction, reset UAC state. */ + if (rdata->msg_info.cseq->cseq != uac_state->cseq) { + uac_state->cseq = rdata->msg_info.cseq->cseq; ++ uac_state->out_cseq = uac_state->cseq; + uac_state->rseq = rseq - 1; + } + +@@ -310,6 +296,10 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, + /* Update our RSeq */ + uac_state->rseq = rseq; + ++ /* Update outgoing cseq*/ ++ uac_state->out_cseq++; ++ inv->dlg->mod_100rel_id = mod_100rel.mod.id; ++ + /* Create PRACK */ + status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method, + -1, &tdata); +diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c +index bba63b78c..30dfd7168 100644 +--- a/pjsip/src/pjsip/sip_dialog.c ++++ b/pjsip/src/pjsip/sip_dialog.c +@@ -16,6 +16,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ ++#include <pjsip-ua/sip_100rel.h> + #include <pjsip/sip_dialog.h> + #include <pjsip/sip_ua_layer.h> + #include <pjsip/sip_errno.h> +@@ -97,6 +98,7 @@ static pj_status_t create_dialog( pjsip_user_agent *ua, + dlg->endpt = endpt; + dlg->state = PJSIP_DIALOG_STATE_NULL; + dlg->add_allow = pjsip_include_allow_hdr_in_dlg; ++ dlg->prack_flag = 0; + + pj_list_init(&dlg->inv_hdr); + pj_list_init(&dlg->rem_cap_hdr); +@@ -1321,6 +1323,46 @@ PJ_DEF(pj_status_t) pjsip_dlg_send_request( pjsip_dialog *dlg, + ch = PJSIP_MSG_CSEQ_HDR(msg); + PJ_ASSERT_RETURN(ch!=NULL, PJ_EBUG); + ++ if (pj_strncmp2(&msg->line.req.method.name, "PRACK", 5) == 0 ){ ++ if(dlg->prack_flag == 1){ ++ /* sync cseq for each dlg if the previous request was PRACK as well */ ++ /* Find UAC state for the specified dlg */ ++ const pj_str_t *to_tag = &dlg->remote.info->tag; ++ dlg_data *dd; ++ dd = (dlg_data*) dlg->mod_data[dlg->mod_100rel_id]; ++ uac_state_t *uac_state = dd->uac_state_list; ++ while (uac_state) { ++ if (pj_stricmp(&uac_state->tag, to_tag)==0) ++ break; ++ uac_state = uac_state->next; ++ } ++ /* sync cseq */ ++ if(uac_state){ ++ dlg->local.cseq = uac_state->out_cseq; ++ } ++ } else { ++ /* just set the flag for PRACK if its the first PRACK request */ ++ dlg->prack_flag = 1; ++ } ++ } else if (dlg->prack_flag == 1){ ++ /* other request after PRACK */ ++ /* Find UAC state for the specified dlg */ ++ const pj_str_t *to_tag = &dlg->remote.info->tag; ++ dlg_data *dd; ++ dd = (dlg_data*) dlg->mod_data[dlg->mod_100rel_id]; ++ uac_state_t *uac_state = dd->uac_state_list; ++ while (uac_state) { ++ if (pj_stricmp(&uac_state->tag, to_tag)==0) ++ break; ++ dlg->local.cseq = uac_state->cseq + 1; // update cseq with the original request cseq + 1 ++ uac_state = uac_state->next; ++ } ++ if(uac_state){ ++ // if previous PRACK exists for the same dlg, then sync and continue the cseq ++ dlg->local.cseq = uac_state->out_cseq + 1; ++ } ++ dlg->prack_flag = 0; ++ } + ch->cseq = dlg->local.cseq++; + + /* Force the whole message to be re-printed. */ +-- +2.34.1 + diff --git a/third-party/pjproject/patches/0013-1TR114-failover.patch b/third-party/pjproject/patches/0013-1TR114-failover.patch new file mode 100644 index 0000000000000000000000000000000000000000..a551237ae4171f31c0e9a3e8406b5eb08fb73e6c --- /dev/null +++ b/third-party/pjproject/patches/0013-1TR114-failover.patch @@ -0,0 +1,15 @@ +--- a/pjsip/src/pjsip/sip_util.c ++++ a/pjsip/src/pjsip/sip_util.c +@@ -1200,7 +1200,12 @@ static void stateless_send_transport_cb( + * first invocation. + */ + if (sent != -PJ_EPENDING) { ++#if 0 // Disable the failover, as it breaks the retry timers required by 1TR114 + tdata->dest_info.cur_addr++; ++#else ++ pjsip_tx_data_dec_ref(tdata); ++ return; ++#endif + } + + /* Have next address? */ diff --git a/third-party/pjproject/patches/0014-Implementation-for-pjproject-DT-logging-mechanism.patch b/third-party/pjproject/patches/0014-Implementation-for-pjproject-DT-logging-mechanism.patch new file mode 100644 index 0000000000000000000000000000000000000000..cceec3808fd3251b18359d0dd37bdbd5d6318ad5 --- /dev/null +++ b/third-party/pjproject/patches/0014-Implementation-for-pjproject-DT-logging-mechanism.patch @@ -0,0 +1,283 @@ +From bd4d4c18c876168af26bc1ee94cbd4239c334b28 Mon Sep 17 00:00:00 2001 +From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu> +Date: Wed, 2 Oct 2024 10:58:33 +0200 +Subject: [PATCH] Implementation for pjproject DT logging mechanism + +--- + pjlib/build/Makefile | 2 +- + pjlib/build/pjlib.vcxproj | 3 +- + pjlib/include/pj/dt_logger.h | 46 +++++++++++ + pjlib/include/pj/log.h | 1 + + pjlib/src/pj/dt_logger.c | 149 +++++++++++++++++++++++++++++++++++ + pjlib/src/pj/ssl_sock_ossl.c | 1 + + 6 files changed, 200 insertions(+), 2 deletions(-) + create mode 100644 pjlib/include/pj/dt_logger.h + create mode 100644 pjlib/src/pj/dt_logger.c + +diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile +index 7d16780..67c2081 100644 +--- a/pjlib/build/Makefile ++++ b/pjlib/build/Makefile +@@ -34,7 +34,7 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ + activesock.o array.o config.o ctype.o errno.o except.o fifobuf.o \ + guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \ + os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \ +- rbtree.o sock_common.o sock_qos_common.o \ ++ rbtree.o sock_common.o sock_qos_common.o dt_logger.o \ + ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \ + ssl_sock_darwin.o string.o timer.o types.o + export PJLIB_CFLAGS += $(_CFLAGS) +diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj +index 227c3f0..204f60e 100644 +--- a/pjlib/build/pjlib.vcxproj ++++ b/pjlib/build/pjlib.vcxproj +@@ -817,6 +817,7 @@ + <ClCompile Include="..\src\pj\list.c" /> + <ClCompile Include="..\src\pj\lock.c" /> + <ClCompile Include="..\src\pj\log.c" /> ++ <ClCompile Include="..\src\pj\dt_logger.c" /> + <ClCompile Include="..\src\pj\log_writer_printk.c"> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild> + <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|ARM'">true</ExcludedFromBuild> +@@ -1105,4 +1106,4 @@ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +-</Project> +\ No newline at end of file ++</Project> +diff --git a/pjlib/include/pj/dt_logger.h b/pjlib/include/pj/dt_logger.h +new file mode 100644 +index 0000000..d8e7fe2 +--- /dev/null ++++ b/pjlib/include/pj/dt_logger.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) ++ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/*! \file ++ * ++ * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu> ++ * ++ * \brief Support for the DT syslog logging mechanism ++ */ ++ ++#ifndef __PJPROJECT_DT_LOGGER_H ++#define __PJPROJECT_DT_LOGGER_H ++ ++// Define language support ++typedef enum { ++ PJ_LOG_LANGUAGE_DT_ENGLISH = 0, ++ PJ_LOG_LANGUAGE_DT_GERMAN, ++ // Add more languages here ++ PJ_LOG_LANGUAGE_DT_MAX ++} PJ_LOG_LANGUAGE_DT; ++ ++// Define log event codes ++typedef enum { ++ PJ_LOG_EVENT_CODE_SV005 = 1, ++ // Add more event codes here ++} PJ_LOG_EVENT_CODE_DT; ++ ++void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language); ++void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...); ++#endif /* __PJPROJECT_DT_LOGGER_H */ +\ No newline at end of file +diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h +index c89f2b5..8f61b22 100644 +--- a/pjlib/include/pj/log.h ++++ b/pjlib/include/pj/log.h +@@ -26,6 +26,7 @@ + + #include <pj/types.h> + #include <stdarg.h> ++#include <pj/dt_logger.h> + + PJ_BEGIN_DECL + +diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c +new file mode 100644 +index 0000000..7654032 +--- /dev/null ++++ b/pjlib/src/pj/dt_logger.c +@@ -0,0 +1,149 @@ ++/* ++ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) ++ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/*! \file ++ * ++ * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu> ++ * ++ * \brief Support for the DT syslog logging mechanism ++ */ ++ ++#include <stdio.h> ++#include <syslog.h> ++#include <stdarg.h> ++#include <time.h> ++#include <pthread.h> ++#include <sys/file.h> ++#include <pj/log.h> ++ ++ ++#define LOG_FILE_PATH "/rdklogs/logs/VoIPlog.txt" ++ ++// Define flags for extended syslog ++#define LOG_FLAG_EXTENDED_SYSLOG_ONLY 0x01 ++ ++// Current language setting ++static PJ_LOG_LANGUAGE_DT current_language = PJ_LOG_LANGUAGE_DT_ENGLISH; ++ ++// Structure to store log messages in different languages and log options (flags) ++struct ast_log_message_table_dt { ++ PJ_LOG_EVENT_CODE_DT code; ++ const char *code_string; ++ const char *messages[PJ_LOG_LANGUAGE_DT_MAX]; ++ pj_uint32_t category; // priority of syslog message: ++ // (LOG_EMERG|LOG_ALERT|LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUG) ++ pj_uint32_t flags; // Flags, not used for now ++}; ++ ++// Log message table with messages in different languages and flags ++struct ast_log_message_table_dt log_message_table_dt[] = { ++ { ++ .code = PJ_LOG_EVENT_CODE_SV005, ++ .code_string = "SV005", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Handshake error", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Handshake Fehler" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ } ++ // More log entries can be added here... ++}; ++ ++static void get_current_timestamp(char *buffer, size_t size) { ++ time_t now = time(NULL); ++ struct tm *tstruct = localtime(&now); ++ strftime(buffer, size, "%Y-%m-%dT%H:%M:%S", tstruct); ++} ++ ++static const char *translate_syslog_priority(int priority) { ++ switch (priority) { ++ case LOG_EMERG: ++ return "EMERG"; ++ case LOG_ALERT: ++ return "ALERT"; ++ case LOG_CRIT: ++ return "CRIT"; ++ case LOG_ERR: ++ return "ERROR"; ++ case LOG_WARNING: ++ return "WARN"; ++ case LOG_NOTICE: ++ return "NOTICE"; ++ case LOG_INFO: ++ return "INFO"; ++ case LOG_DEBUG: ++ return "DEBUG"; ++ default: ++ return "UNKNOWN"; ++ } ++} ++ ++// Function to set language ++void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language) { ++ if (language >= 0 && language < PJ_LOG_LANGUAGE_DT_MAX) { ++ current_language = language; ++ } ++} ++ ++// Function to log a message based on the event code ++void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) { ++ const char *message = NULL; ++ const char *code_string = NULL; ++ pj_uint32_t category = 0; ++ int flags = 0; ++ char full_message[1024]; ++ char log_message[1280]; ++ va_list args; ++ ++ // Find the log message for the event code in the correct language ++ for (size_t i = 0; i < sizeof(log_message_table_dt) / sizeof(log_message_table_dt[0]); i++) { ++ if (log_message_table_dt[i].code == event_code) { ++ message = log_message_table_dt[i].messages[current_language]; ++ code_string = log_message_table_dt[i].code_string; ++ category = log_message_table_dt[i].category; ++ flags = log_message_table_dt[i].flags; ++ break; ++ } ++ } ++ ++ if (!message) ++ return; ++ ++ // Format the log message with variable arguments ++ va_start(args, message); ++ vsnprintf(full_message, sizeof(full_message), message, args); ++ va_end(args); ++ ++ // get additional data for the final log message ++ char timestamp[64]; ++ get_current_timestamp(timestamp, sizeof(timestamp)); ++ const char *log_level = translate_syslog_priority(category); ++ unsigned long tid = gettid(); ++ ++ snprintf(log_message, sizeof(log_message), "%s telekom: %s %s.%s [tid=%lu] %s", ++ timestamp, code_string, "VOIP", log_level, tid, full_message); ++ ++ // Write the log message to a file ++ FILE *log_file = fopen(LOG_FILE_PATH, "a"); ++ if (log_file) { ++ fprintf(log_file, "%s\n", log_message); ++ fclose(log_file); ++ } ++} +diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c +index 7d17302..96ca03e 100644 +--- a/pjlib/src/pj/ssl_sock_ossl.c ++++ b/pjlib/src/pj/ssl_sock_ossl.c +@@ -2346,6 +2346,7 @@ static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock) + { + /* Handshake fails */ + status = STATUS_FROM_SSL_ERR2("Handshake", ssock, err, err2, 0); ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV005); + return status; + } + } +-- +2.34.1 + diff --git a/third-party/pjproject/patches/0015-Scure-Sip-syslog-template-related-code-in-pjproject.patch b/third-party/pjproject/patches/0015-Scure-Sip-syslog-template-related-code-in-pjproject.patch new file mode 100644 index 0000000000000000000000000000000000000000..e2e881a288df54b894905f846436e695ec4d2dea --- /dev/null +++ b/third-party/pjproject/patches/0015-Scure-Sip-syslog-template-related-code-in-pjproject.patch @@ -0,0 +1,313 @@ +From dceea86c7b0a67cc1d513ab64ebbe69320c18df6 Mon Sep 17 00:00:00 2001 +From: macdux <macdux@telekom.de> +Date: Thu, 10 Oct 2024 20:29:04 +0800 +Subject: [PATCH] Scure Sip syslog template related code in pjproject + +--- + pjlib/include/pj/dt_logger.h | 28 +++- + pjlib/src/pj/dt_logger.c | 240 +++++++++++++++++++++++++++++++++++ + 2 files changed, 266 insertions(+), 2 deletions(-) + +diff --git a/pjlib/include/pj/dt_logger.h b/pjlib/include/pj/dt_logger.h +index d8e7fe2e1..34557edc9 100644 +--- a/pjlib/include/pj/dt_logger.h ++++ b/pjlib/include/pj/dt_logger.h +@@ -37,10 +37,34 @@ typedef enum { + + // Define log event codes + typedef enum { +- PJ_LOG_EVENT_CODE_SV005 = 1, ++ PJ_LOG_EVENT_CODE_SV001 = 1, ++ PJ_LOG_EVENT_CODE_SV002, ++ PJ_LOG_EVENT_CODE_SV003, ++ PJ_LOG_EVENT_CODE_SV004, ++ PJ_LOG_EVENT_CODE_SV005, ++ PJ_LOG_EVENT_CODE_SV006, ++ PJ_LOG_EVENT_CODE_SV007, ++ PJ_LOG_EVENT_CODE_SV008, ++ PJ_LOG_EVENT_CODE_SV009, ++ PJ_LOG_EVENT_CODE_SV010, ++ PJ_LOG_EVENT_CODE_SV011, ++ PJ_LOG_EVENT_CODE_SV012, ++ PJ_LOG_EVENT_CODE_SV013, ++ PJ_LOG_EVENT_CODE_SV014, ++ PJ_LOG_EVENT_CODE_SV015, ++ PJ_LOG_EVENT_CODE_SV016, ++ PJ_LOG_EVENT_CODE_SV017, ++ PJ_LOG_EVENT_CODE_SV018, ++ PJ_LOG_EVENT_CODE_SV019, ++ PJ_LOG_EVENT_CODE_SV020, ++ PJ_LOG_EVENT_CODE_SV021, ++ PJ_LOG_EVENT_CODE_SV105, ++ PJ_LOG_EVENT_CODE_SV106, ++ PJ_LOG_EVENT_CODE_SV110, ++ PJ_LOG_EVENT_CODE_SV111, + // Add more event codes here + } PJ_LOG_EVENT_CODE_DT; + + void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language); + void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...); +-#endif /* __PJPROJECT_DT_LOGGER_H */ +\ No newline at end of file ++#endif /* __PJPROJECT_DT_LOGGER_H */ +diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c +index 765403242..90050d268 100644 +--- a/pjlib/src/pj/dt_logger.c ++++ b/pjlib/src/pj/dt_logger.c +@@ -53,6 +53,46 @@ struct ast_log_message_table_dt { + + // Log message table with messages in different languages and flags + struct ast_log_message_table_dt log_message_table_dt[] = { ++ { ++ .code = PJ_LOG_EVENT_CODE_SV001, ++ .code_string = "SV001", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unexpected message", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unerwartete Meldung" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV002, ++ .code_string = "SV002", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message authentication error", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Message Authentication fehlerhaft" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV003, ++ .code_string = "SV003", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS ciphertext too long", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Ciphertext zu lang" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV004, ++ .code_string = "SV004", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error unpacking", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim Entpacken" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, + { + .code = PJ_LOG_EVENT_CODE_SV005, + .code_string = "SV005", +@@ -62,6 +102,206 @@ struct ast_log_message_table_dt log_message_table_dt[] = { + }, + .category = LOG_ERR, + .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV006, ++ .code_string = "SV006", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate damaged", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat beschädigt" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV007, ++ .code_string = "SV007", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate not supported", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat nicht unterstützt" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV008, ++ .code_string = "SV008", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate withdrawn", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat zurückgezogen" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV009, ++ .code_string = "SV009", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat abgelaufen" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV010, ++ .code_string = "SV010", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unknown certificate" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV011, ++ .code_string = "SV011", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Incorrect handshake parameters", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehlerhafte Handshake Parameter" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV012, ++ .code_string = "SV012", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unknown certification authority", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unbekannte Zertifizierungsstelle" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV013, ++ .code_string = "SV013", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Access denied", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zugang verweigert" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV014, ++ .code_string = "SV014", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message incorrect", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Nachricht fehlerhaft" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV015, ++ .code_string = "SV015", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error decrypting", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim entschlüsseln" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV016, ++ .code_string = "SV016", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Version not supported", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Version nicht unterstützt" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV017, ++ .code_string = "SV017", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Insecure encryption", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unsichere Verschlüsselung" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV018, ++ .code_string = "SV018", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "General internal error", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Allgemeiner interner Fehler" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV019, ++ .code_string = "SV019", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Cancellation by user", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Abbruch durch Benutzer" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV020, ++ .code_string = "SV020", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "No repetition desired", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Keine Wiederholung erwünscht" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV021, ++ .code_string = "SV021", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Extension not supported", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Erweiterung nicht unterstützt" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV105, ++ .code_string = "SV105", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection successful", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolgreich" ++ }, ++ .category = LOG_INFO, ++ .flags = 0 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV106, ++ .code_string = "SV106", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection unsuccessful", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolglos" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV110, ++ .code_string = "SV110", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "SRTP negotiation failed", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "SRTP Aushandlung fehlgeschlagen" ++ }, ++ .category = LOG_ERR, ++ .flags = 1 ++ }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV111, ++ .code_string = "SV111", ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Port <%s> not available", //Port <port number> not available ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Port <%s> nicht verfügbar" //Port <port number> nicht verfügbar ++ }, ++ .category = LOG_ERR, ++ .flags = 1 + } + // More log entries can be added here... + }; +-- +2.17.1 + diff --git a/third-party/pjproject/patches/0016-Remove-event-code-from-log-string-in-VoIPlog.txt.patch b/third-party/pjproject/patches/0016-Remove-event-code-from-log-string-in-VoIPlog.txt.patch new file mode 100644 index 0000000000000000000000000000000000000000..5a5858ce6f7ff4d46c617c32855d651e3c12b642 --- /dev/null +++ b/third-party/pjproject/patches/0016-Remove-event-code-from-log-string-in-VoIPlog.txt.patch @@ -0,0 +1,336 @@ +From 793c260813acbeabd612fe06da9de0349df709fc Mon Sep 17 00:00:00 2001 +From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu> +Date: Fri, 11 Oct 2024 11:46:14 +0200 +Subject: [PATCH] Remove event code from log string in VoIPlog.txt + +--- + pjlib/src/pj/dt_logger.c | 82 +++++++++++++--------------------------- + 1 file changed, 27 insertions(+), 55 deletions(-) + +diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c +index 90050d2..378aaf9 100644 +--- a/pjlib/src/pj/dt_logger.c ++++ b/pjlib/src/pj/dt_logger.c +@@ -44,7 +44,6 @@ static PJ_LOG_LANGUAGE_DT current_language = PJ_LOG_LANGUAGE_DT_ENGLISH; + // Structure to store log messages in different languages and log options (flags) + struct ast_log_message_table_dt { + PJ_LOG_EVENT_CODE_DT code; +- const char *code_string; + const char *messages[PJ_LOG_LANGUAGE_DT_MAX]; + pj_uint32_t category; // priority of syslog message: + // (LOG_EMERG|LOG_ALERT|LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUG) +@@ -55,217 +54,195 @@ struct ast_log_message_table_dt { + struct ast_log_message_table_dt log_message_table_dt[] = { + { + .code = PJ_LOG_EVENT_CODE_SV001, +- .code_string = "SV001", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unexpected message", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unerwartete Meldung" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV002, +- .code_string = "SV002", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message authentication error", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Message Authentication fehlerhaft" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV003, +- .code_string = "SV003", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS ciphertext too long", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Ciphertext zu lang" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV004, +- .code_string = "SV004", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error unpacking", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim Entpacken" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV005, +- .code_string = "SV005", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Handshake error", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Handshake Fehler" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV006, +- .code_string = "SV006", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate damaged", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat beschädigt" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV007, +- .code_string = "SV007", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate not supported", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat nicht unterstützt" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV008, +- .code_string = "SV008", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate withdrawn", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat zurückgezogen" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV009, +- .code_string = "SV009", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat abgelaufen" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV010, +- .code_string = "SV010", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unknown certificate" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV011, +- .code_string = "SV011", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Incorrect handshake parameters", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehlerhafte Handshake Parameter" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV012, +- .code_string = "SV012", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unknown certification authority", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unbekannte Zertifizierungsstelle" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV013, +- .code_string = "SV013", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Access denied", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zugang verweigert" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV014, +- .code_string = "SV014", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message incorrect", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Nachricht fehlerhaft" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV015, +- .code_string = "SV015", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error decrypting", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim entschlüsseln" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV016, +- .code_string = "SV016", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Version not supported", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Version nicht unterstützt" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV017, +- .code_string = "SV017", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Insecure encryption", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unsichere Verschlüsselung" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV018, +- .code_string = "SV018", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "General internal error", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Allgemeiner interner Fehler" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV019, +- .code_string = "SV019", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Cancellation by user", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Abbruch durch Benutzer" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV020, +- .code_string = "SV020", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "No repetition desired", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Keine Wiederholung erwünscht" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV021, +- .code_string = "SV021", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Extension not supported", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Erweiterung nicht unterstützt" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV105, +- .code_string = "SV105", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection successful", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolgreich" +@@ -275,33 +252,30 @@ struct ast_log_message_table_dt log_message_table_dt[] = { + }, + { + .code = PJ_LOG_EVENT_CODE_SV106, +- .code_string = "SV106", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection unsuccessful", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolglos" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV110, +- .code_string = "SV110", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "SRTP negotiation failed", + [PJ_LOG_LANGUAGE_DT_GERMAN] = "SRTP Aushandlung fehlgeschlagen" + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, + { + .code = PJ_LOG_EVENT_CODE_SV111, +- .code_string = "SV111", + .messages = { + [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Port <%s> not available", //Port <port number> not available + [PJ_LOG_LANGUAGE_DT_GERMAN] = "Port <%s> nicht verfügbar" //Port <port number> nicht verfügbar + }, + .category = LOG_ERR, +- .flags = 1 ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + } + // More log entries can be added here... + }; +@@ -345,7 +319,6 @@ void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language) { + // Function to log a message based on the event code + void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) { + const char *message = NULL; +- const char *code_string = NULL; + pj_uint32_t category = 0; + int flags = 0; + char full_message[1024]; +@@ -356,7 +329,6 @@ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) { + for (size_t i = 0; i < sizeof(log_message_table_dt) / sizeof(log_message_table_dt[0]); i++) { + if (log_message_table_dt[i].code == event_code) { + message = log_message_table_dt[i].messages[current_language]; +- code_string = log_message_table_dt[i].code_string; + category = log_message_table_dt[i].category; + flags = log_message_table_dt[i].flags; + break; +@@ -367,7 +339,7 @@ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) { + return; + + // Format the log message with variable arguments +- va_start(args, message); ++ va_start(args, event_code); + vsnprintf(full_message, sizeof(full_message), message, args); + va_end(args); + +@@ -377,8 +349,8 @@ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) { + const char *log_level = translate_syslog_priority(category); + unsigned long tid = gettid(); + +- snprintf(log_message, sizeof(log_message), "%s telekom: %s %s.%s [tid=%lu] %s", +- timestamp, code_string, "VOIP", log_level, tid, full_message); ++ snprintf(log_message, sizeof(log_message), "%s telekom: %s.%s [tid=%lu] %s", ++ timestamp, "VOIP", log_level, tid, full_message); + + // Write the log message to a file + FILE *log_file = fopen(LOG_FILE_PATH, "a"); +-- +2.34.1 + diff --git a/third-party/pjproject/patches/0017-securesip-syslog-under-pjproject-part-1-implementati.patch b/third-party/pjproject/patches/0017-securesip-syslog-under-pjproject-part-1-implementati.patch new file mode 100644 index 0000000000000000000000000000000000000000..ae14a55d67c7215f0b99a4406d742760127935da --- /dev/null +++ b/third-party/pjproject/patches/0017-securesip-syslog-under-pjproject-part-1-implementati.patch @@ -0,0 +1,170 @@ +From 0161cd583156e16069ab975e0d3354d1f2fa4c24 Mon Sep 17 00:00:00 2001 +From: macdux <macdux@telekom.de> +Date: Mon, 14 Oct 2024 08:46:16 +0800 +Subject: [PATCH] securesip syslog under pjproject part 1 implementation + +--- + pjlib/include/pj/dt_logger.h | 1 + + pjlib/src/pj/dt_logger.c | 9 ++++ + pjlib/src/pj/ssl_sock_ossl.c | 99 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 109 insertions(+) + +diff --git a/pjlib/include/pj/dt_logger.h b/pjlib/include/pj/dt_logger.h +index 34557edc9..d3b64c629 100644 +--- a/pjlib/include/pj/dt_logger.h ++++ b/pjlib/include/pj/dt_logger.h +@@ -58,6 +58,7 @@ typedef enum { + PJ_LOG_EVENT_CODE_SV019, + PJ_LOG_EVENT_CODE_SV020, + PJ_LOG_EVENT_CODE_SV021, ++ PJ_LOG_EVENT_CODE_SV104, + PJ_LOG_EVENT_CODE_SV105, + PJ_LOG_EVENT_CODE_SV106, + PJ_LOG_EVENT_CODE_SV110, +diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c +index 378aaf943..c1942e523 100644 +--- a/pjlib/src/pj/dt_logger.c ++++ b/pjlib/src/pj/dt_logger.c +@@ -241,6 +241,15 @@ struct ast_log_message_table_dt log_message_table_dt[] = { + .category = LOG_ERR, + .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY + }, ++ { ++ .code = PJ_LOG_EVENT_CODE_SV104, ++ .messages = { ++ [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Connection error in level 2", ++ [PJ_LOG_LANGUAGE_DT_GERMAN] = "Verbindungsfehler in Stufe 2" ++ }, ++ .category = LOG_INFO, ++ .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY ++ }, + { + .code = PJ_LOG_EVENT_CODE_SV105, + .messages = { +diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c +index 96ca03e9f..63b42f444 100644 +--- a/pjlib/src/pj/ssl_sock_ossl.c ++++ b/pjlib/src/pj/ssl_sock_ossl.c +@@ -2347,6 +2347,73 @@ static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock) + /* Handshake fails */ + status = STATUS_FROM_SSL_ERR2("Handshake", ssock, err, err2, 0); + pjproject_log_dt(PJ_LOG_EVENT_CODE_SV005); ++ if (err2 == SSL_ERROR_ZERO_RETURN){ ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV019); ++ } ++ else if (err2 == SSL_ERROR_SSL) ++ { ++ unsigned long err_code = ERR_get_error(); ++ unsigned long r; ++ r = ERR_GET_REASON(err_code); ++ switch (r) ++ { ++ case SSL_R_UNEXPECTED_MESSAGE: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV001); ++ break; ++ case SSL_R_CERTIFICATE_VERIFY_FAILED: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV006); ++ break; ++ case SSL_AD_UNSUPPORTED_CERTIFICATE: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV007); ++ break; ++ case X509_V_ERR_CERT_REVOKED: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV008); ++ break; ++ case X509_V_ERR_CERT_HAS_EXPIRED: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV009); ++ break; ++ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV010); ++ break; ++ case SSL_R_BAD_HANDSHAKE_LENGTH: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV011); ++ break; ++ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV012); ++ break; ++ case SSL_R_NO_CIPHER_MATCH: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV013); ++ break; ++ case SSL_R_RECORD_LENGTH_MISMATCH: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV014); ++ break; ++ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV015); ++ break; ++ case SSL_R_UNSUPPORTED_PROTOCOL: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV016); ++ break; ++ case SSL_R_NO_SHARED_CIPHER: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV017); ++ break; ++ case ERR_R_INTERNAL_ERROR: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV018); ++ break; ++ case SSL_R_NO_RENEGOTIATION: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV020); ++ break; ++ case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV021); ++ break; ++ case SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG: ++ case SSL_R_NO_SRTP_PROFILES: ++ case SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV110); ++ break; ++ default: ++ break; ++ } ++ } + return status; + } + } +@@ -2424,6 +2491,27 @@ static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size) + * re-negotiation. + */ + if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) { ++ unsigned long err_code = ERR_get_error(); ++ unsigned long r; ++ r = ERR_GET_REASON(err_code); ++ switch (r) ++ { ++ case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV002); ++ break; ++ case SSL_R_ENCRYPTED_LENGTH_TOO_LONG: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV003); ++ break; ++ case SSL_R_BAD_DECOMPRESSION: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV004); ++ break; ++ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV015); ++ break; ++ default: ++ break; ++ } ++ + if (err == SSL_ERROR_SYSCALL && size_ == -1 && + ERR_peek_error() == 0 && errno == 0) + { +@@ -2467,6 +2555,17 @@ static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data, + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) { + status = PJ_EEOF; + } else { ++ unsigned long err_code = ERR_get_error(); ++ unsigned long r; ++ r = ERR_GET_REASON(err_code); ++ switch (r) ++ { ++ case SSL_R_ENCRYPTED_LENGTH_TOO_LONG: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV003); ++ break; ++ default: ++ break; ++ } + /* Some problem occured */ + status = STATUS_FROM_SSL_ERR2("Write", ssock, *nwritten, + err, size); +-- +2.17.1 + diff --git a/third-party/pjproject/patches/0018-implementation-of-securesip-syslog-part2-under-pjpro.patch b/third-party/pjproject/patches/0018-implementation-of-securesip-syslog-part2-under-pjpro.patch new file mode 100644 index 0000000000000000000000000000000000000000..6c50ffdf72d6145e8c405802d5c4502dce721abb --- /dev/null +++ b/third-party/pjproject/patches/0018-implementation-of-securesip-syslog-part2-under-pjpro.patch @@ -0,0 +1,70 @@ +From 961449f28b023dd26f27d81e13ba0cc0922c50e5 Mon Sep 17 00:00:00 2001 +From: macdux <macdux@telekom.de> +Date: Mon, 14 Oct 2024 20:50:34 +0800 +Subject: [PATCH] implementation of securesip syslog part2 under pjproject + +--- + pjlib/src/pj/activesock.c | 9 ++++++++- + pjsip/src/pjsip/sip_transport_tls.c | 5 +++++ + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/pjlib/src/pj/activesock.c b/pjlib/src/pj/activesock.c +index f901545b7..963190ed7 100644 +--- a/pjlib/src/pj/activesock.c ++++ b/pjlib/src/pj/activesock.c +@@ -932,7 +932,14 @@ PJ_DEF(pj_status_t) pj_activesock_start_connect( pj_activesock_t *asock, + if (asock->shutdown) + return PJ_EINVALIDOP; + +- return pj_ioqueue_connect(asock->key, remaddr, addr_len); ++ pj_status_t connect_status = pj_ioqueue_connect(asock->key, remaddr, addr_len); ++ if (connect_status != PJ_SUCCESS) { ++ if (errno == ECONNREFUSED){ ++ pj_uint16_t port = pj_sockaddr_get_port(remaddr); ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV111, port); ++ } ++ } ++ return connect_status; + } + + static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, +diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c +index ad59a1e60..903a640f8 100644 +--- a/pjsip/src/pjsip/sip_transport_tls.c ++++ b/pjsip/src/pjsip/sip_transport_tls.c +@@ -1271,6 +1271,8 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, + if (status == PJ_SUCCESS) { + on_connect_complete(tls->ssock, PJ_SUCCESS); + } else if (status != PJ_EPENDING) { ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV104); ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV106); + tls_destroy(&tls->base, status); + return status; + } +@@ -1995,6 +1997,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, + status = tls->close_reason; + tls_perror(tls->base.obj_name, "TLS connect() error", status, + &tls->remote_name); ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV106); + + /* Cancel all delayed transmits */ + while (!pj_list_empty(&tls->delayed_list)) { +@@ -2024,6 +2027,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, + pj_addr_str_print(&tls->base.remote_name.host, + tls->base.remote_name.port, remote_addr_buf, + sizeof(remote_addr_buf), 1))); ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV105); + + /* Start pending read */ + status = tls_start_read(tls); +@@ -2046,6 +2050,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, + return PJ_TRUE; + + on_error: ++ pjproject_log_dt(PJ_LOG_EVENT_CODE_SV106); + tls_init_shutdown(tls, status); + + return PJ_FALSE; +-- +2.17.1 + diff --git a/third-party/pjproject/patches/0019-TLSv13-ciphers.patch b/third-party/pjproject/patches/0019-TLSv13-ciphers.patch new file mode 100644 index 0000000000000000000000000000000000000000..1ebefe0ce9a4cb0b29b49bdb134e4253b89d69ce --- /dev/null +++ b/third-party/pjproject/patches/0019-TLSv13-ciphers.patch @@ -0,0 +1,197 @@ +diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c +index 63b42f4..4844d5e 100644 +--- a/pjlib/src/pj/ssl_sock_ossl.c ++++ b/pjlib/src/pj/ssl_sock_ossl.c +@@ -231,6 +231,57 @@ typedef struct ossl_sock_t + /* Expected maximum value of reason component in OpenSSL error code */ + #define MAX_OSSL_ERR_REASON 1200 + ++/* TLSv13 ciphers */ ++static struct tlsv13_cipher { ++ int id; ++ const char *name; ++} tlsv13_ciphers[] = { ++ {0x1301, "TLS_AES_128_GCM_SHA256"}, ++ {0x1302, "TLS_AES_256_GCM_SHA384"}, ++ {0x1303, "TLS_CHACHA20_POLY1305_SHA256"}, ++ {0x1304, "TLS_AES_128_CCM_SHA256"}, ++ {0x1305, "TLS_AES_128_CCM_8_SHA256"}, ++ {0, NULL} ++}; ++ ++/* all TLS v1.2 ciphers required by 1tr114 */ ++#define PJ_OSSL_TLS1_2_CIPHERS_DT_DEFAULT \ ++ "ECDHE-RSA-AES256-GCM-SHA384:"\ ++ "AES256-GCM-SHA384:"\ ++ "AES256-SHA256:"\ ++ "ECDHE-RSA-AES128-GCM-SHA256:"\ ++ "ECDHE-RSA-AES128-SHA256:"\ ++ "AES128-GCM-SHA256:"\ ++ "AES128-SHA256" ++ ++/* all TLS v1.3 ciphers supported by openssl 1.1.11 */ ++#define PJ_OSSL_TLS1_3_CIPHERS_ALL \ ++ "TLS_AES_128_GCM_SHA256:"\ ++ "TLS_AES_256_GCM_SHA384:"\ ++ "TLS_AES_128_CCM_SHA256:"\ ++ "TLS_AES_128_CCM_8_SHA256:"\ ++ "TLS_CHACHA20_POLY1305_SHA256" ++ ++/* all TLS v1.3 ciphers required by 1tr114 */ ++#define PJ_OSSL_TLS1_3_CIPHERS_DT_DEFAULT \ ++ "TLS_AES_128_GCM_SHA256:"\ ++ "TLS_AES_256_GCM_SHA384:"\ ++ "TLS_AES_128_CCM_SHA256:"\ ++ "TLS_CHACHA20_POLY1305_SHA256" ++ ++static const char * get_tlsv13_cipher_name(int id) ++{ ++ struct tlsv13_cipher *cipher = &tlsv13_ciphers[0]; ++ ++ while (cipher->id) { ++ if (cipher->id == id) ++ return cipher->name; ++ ++ ++cipher; ++ } ++ ++ return NULL; ++} + + static char *SSLErrorString (int err) + { +@@ -732,6 +783,7 @@ static pj_status_t init_openssl(void) + + ctx=SSL_CTX_new(meth); + SSL_CTX_set_cipher_list(ctx, "ALL:COMPLEMENTOFALL"); ++ SSL_CTX_set_ciphersuites(ctx, PJ_OSSL_TLS1_3_CIPHERS_ALL); + + ssl = SSL_new(ctx); + +@@ -1765,11 +1817,20 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) + int ret; + + if (ssock->param.ciphers_num == 0) { +- ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, PJ_SSL_SOCK_OSSL_CIPHERS); +- if (ret < 1) { +- return GET_SSL_STATUS(ssock); +- } +- ++ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) { ++ ret = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, PJ_OSSL_TLS1_3_CIPHERS_DT_DEFAULT); ++ if (ret < 1) { ++ return GET_SSL_STATUS(ssock); ++ } ++ } ++ ++ if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) { ++ ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, PJ_OSSL_TLS1_2_CIPHERS_DT_DEFAULT); ++ if (ret < 1) { ++ return GET_SSL_STATUS(ssock); ++ } ++ } ++ + return PJ_SUCCESS; + } + +@@ -1785,16 +1846,20 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) + + /* Generate user specified cipher list in OpenSSL format */ + for (i = 0; i < ssock->param.ciphers_num; ++i) { ++ // Skip TLS 1.3 cipers ++ if (get_tlsv13_cipher_name(ssock->param.ciphers[i])) ++ continue; ++ + for (j = 0; j < ssl_cipher_num; ++j) { + if (ssock->param.ciphers[i] == ssl_ciphers[j].id) + { + const char *c_name = ssl_ciphers[j].name; + + /* Check buffer size */ +- if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > +- BUF_SIZE) ++ if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > BUF_SIZE) + { + pj_assert(!"Insufficient temporary buffer for cipher"); ++ pj_pool_release(tmp_pool); + return PJ_ETOOMANY; + } + +@@ -1806,14 +1871,61 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) + pj_strcat2(&cipher_list, c_name); + break; + } +- } ++ } + } + + /* Put NULL termination in the generated cipher list */ + cipher_list.ptr[cipher_list.slen] = '\0'; + +- /* Finally, set chosen cipher list */ +- ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, buf); ++ /* Set chosen cipher list */ ++ if (cipher_list.slen) ++ ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, buf); ++ else ++ ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, "COMPLEMENTOFALL"); ++ ++ if (ret < 1) { ++ pj_pool_release(tmp_pool); ++ return GET_SSL_STATUS(ssock); ++ } ++ ++ /* Only apply TLSv13 cipher suites for TLS1_3 PROTO */ ++ if (!ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) { ++ pj_pool_release(tmp_pool); ++ return PJ_SUCCESS; ++ } ++ ++ pj_strset(&cipher_list, buf, 0); ++ ++ /* Generate user specified TLSv13 cipher suites in OpenSSL format */ ++ for (i = 0; i < ssock->param.ciphers_num; ++i) { ++ const char *c_name = get_tlsv13_cipher_name(ssock->param.ciphers[i]); ++ if (c_name) { ++ /* Check buffer size */ ++ if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > BUF_SIZE) ++ { ++ pj_assert(!"Insufficient temporary buffer for cipher"); ++ pj_pool_release(tmp_pool); ++ return PJ_ETOOMANY; ++ } ++ ++ /* Add colon separator */ ++ if (cipher_list.slen) ++ pj_strcat2(&cipher_list, ":"); ++ ++ /* Add the cipher */ ++ pj_strcat2(&cipher_list, c_name); ++ } ++ } ++ ++ /* Put NULL termination in the generated cipher list */ ++ cipher_list.ptr[cipher_list.slen] = '\0'; ++ ++ /* Set chosen TLSv13 cipher suites */ ++ if (cipher_list.slen) ++ ret = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, buf); ++ else ++ ret = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, PJ_OSSL_TLS1_3_CIPHERS_DT_DEFAULT); ++ + if (ret < 1) { + pj_pool_release(tmp_pool); + return GET_SSL_STATUS(ssock); +diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c +index 903a640..9e7f264 100644 +--- a/pjsip/src/pjsip/sip_transport_tls.c ++++ b/pjsip/src/pjsip/sip_transport_tls.c +@@ -223,7 +223,7 @@ static pj_uint32_t ssl_get_proto(pjsip_ssl_method ssl_method, pj_uint32_t proto) + out_proto = PJ_SSL_SOCK_PROTO_TLS1_2; + break; + case PJSIP_TLSV1_3_METHOD: +- out_proto = PJ_SSL_SOCK_PROTO_TLS1_3; ++ out_proto = PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_3; + break; + case PJSIP_SSLV23_METHOD: + out_proto = PJ_SSL_SOCK_PROTO_SSL23; diff --git a/third-party/pjproject/patches/0020-return-444-while-register-request-timeout.patch b/third-party/pjproject/patches/0020-return-444-while-register-request-timeout.patch new file mode 100644 index 0000000000000000000000000000000000000000..bd1f918824066cef030207c524942afc9ddcf660 --- /dev/null +++ b/third-party/pjproject/patches/0020-return-444-while-register-request-timeout.patch @@ -0,0 +1,43 @@ +From 42a3189210f3790acdd128845403b96c5d03d090 Mon Sep 17 00:00:00 2001 +From: "wenpeng.song" <wenpeng.song@iopsys.eu> +Date: Mon, 3 Mar 2025 12:48:19 +0100 +Subject: [PATCH] return 444 while register request timeout + +--- + pjsip/src/pjsip/sip_msg.c | 2 ++ + pjsip/src/pjsip/sip_transaction.c | 6 +++++- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c +index c375ca9b1..7a67783c4 100644 +--- a/pjsip/src/pjsip/sip_msg.c ++++ b/pjsip/src/pjsip/sip_msg.c +@@ -243,6 +243,8 @@ static int init_status_phrase() + pj_strset2( &status_phrase[702], "Unable to resolve destination server"); + pj_strset2( &status_phrase[703], "Error sending message to destination server"); + ++ pj_strset2( &status_phrase[444], "No response received"); ++ + return 1; + } + +diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c +index cf9795dbb..7d3ebe258 100644 +--- a/pjsip/src/pjsip/sip_transaction.c ++++ b/pjsip/src/pjsip/sip_transaction.c +@@ -2630,7 +2630,11 @@ static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, + tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED); + + /* Set status code */ +- tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); ++ if(tsx->method.id == PJSIP_REGISTER_METHOD){ ++ tsx_set_status_code(tsx, 444, NULL); ++ } else { ++ tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); ++ } + + /* Inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, +-- +2.43.0 + diff --git a/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch b/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch new file mode 100644 index 0000000000000000000000000000000000000000..286675b2ef1980050eaf21e8d439bf51f636092f --- /dev/null +++ b/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch @@ -0,0 +1,117 @@ +From d0879fbe17d213a267b6015b85f5810fb34ec78b Mon Sep 17 00:00:00 2001 +From: "wenpeng.song" <wenpeng.song@iopsys.eu> +Date: Wed, 14 May 2025 15:14:16 +0200 +Subject: [PATCH] Update for Authorization header handling + + * Call callback to let Asterisk control the REGISTER challenge handling + * Add wrapper api for find_cached_auth, auth_find_cred +--- + build.symbian/pjsipU.def | 2 ++ + pjsip/include/pjsip/sip_auth.h | 8 ++++++++ + pjsip/src/pjsip-ua/sip_reg.c | 22 ++++++++++++---------- + pjsip/src/pjsip/sip_auth_client.c | 12 ++++++++++++ + 4 files changed, 34 insertions(+), 10 deletions(-) + +diff --git a/build.symbian/pjsipU.def b/build.symbian/pjsipU.def +index 68d31eba3..5490d1e33 100644 +--- a/build.symbian/pjsipU.def ++++ b/build.symbian/pjsipU.def +@@ -278,3 +278,5 @@ EXPORTS + pjsip_warning_hdr_create @ 277 NONAME + pjsip_warning_hdr_create_from_status @ 278 NONAME + pjsip_www_authenticate_hdr_create @ 279 NONAME ++ pjsip_auth_find_cached_auth @ 280 NONAME ++ pjsip_auth_find_cred @ 281 NONAME +diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h +index fa55830fd..02a8a7cde 100644 +--- a/pjsip/include/pjsip/sip_auth.h ++++ b/pjsip/include/pjsip/sip_auth.h +@@ -628,6 +628,14 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t* result, + * @} + */ + ++/* Find cached authentication in the list for the specified realm. */ ++PJ_DECL(pjsip_cached_auth) *pjsip_auth_find_cached_auth( pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm ); ++ ++/* Find credential to use for the specified realm and auth scheme. */ ++PJ_DECL(pjsip_cred_info*) pjsip_auth_find_cred( const pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm, ++ const pj_str_t *auth_scheme); + + + PJ_END_DECL +diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c +index 947e9edb7..6a3aad194 100644 +--- a/pjsip/src/pjsip-ua/sip_reg.c ++++ b/pjsip/src/pjsip-ua/sip_reg.c +@@ -1210,25 +1210,27 @@ static void regc_tsx_callback(void *token, pjsip_event *event) + pj_list_erase(chdr); + } + } +- ++ status = PJ_SUCCESS; ++/* + status = pjsip_auth_clt_reinit_req( ®c->auth_sess, + rdata, + tsx->last_tx, + &tdata); +- +- if (status == PJ_SUCCESS) { ++*/ ++// Call callback anyway to let asterisk control the handling of the challenge ++// if (status == PJ_SUCCESS) { + /* Need to unlock the regc temporarily while sending the message + * to prevent deadlock (see ticket #2260 and #1247). + * It should be safe to do this since the regc's refcount has been + * incremented. + */ +- pj_lock_release(regc->lock); +- status = pjsip_regc_send(regc, tdata); +- pj_lock_acquire(regc->lock); +- } ++// pj_lock_release(regc->lock); ++// status = pjsip_regc_send(regc, tdata); ++// pj_lock_acquire(regc->lock); ++// } + +- if (status != PJ_SUCCESS) { +- ++// if (status != PJ_SUCCESS) { ++// + /* Only call callback if application is still interested + * in it. + */ +@@ -1242,7 +1244,7 @@ static void regc_tsx_callback(void *token, pjsip_event *event) + rdata, NOEXP, 0, NULL, is_unreg); + pj_lock_acquire(regc->lock); + } +- } ++// } + + } else if (regc->_delete_flag) { + +diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c +index ee2876d99..fd5c93007 100644 +--- a/pjsip/src/pjsip/sip_auth_client.c ++++ b/pjsip/src/pjsip/sip_auth_client.c +@@ -1528,3 +1528,15 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, + + } + ++/* Find cached authentication in the list for the specified realm. */ ++PJ_DEF(pjsip_cached_auth) *pjsip_auth_find_cached_auth( pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm ){ ++ return find_cached_auth(sess, realm); ++} ++ ++/* Find credential to use for the specified realm and auth scheme. */ ++PJ_DEF(pjsip_cred_info*) pjsip_auth_find_cred( const pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm, ++ const pj_str_t *auth_scheme){ ++ return auth_find_cred(sess, realm, auth_scheme); ++} +-- +2.43.0 + + diff --git a/third-party/pjproject/patches/0021-stateless-send-transport-handle-no-dest-addr.patch b/third-party/pjproject/patches/0021-stateless-send-transport-handle-no-dest-addr.patch new file mode 100644 index 0000000000000000000000000000000000000000..1b74418a6612680cb851446856d6af10ac842a0e --- /dev/null +++ b/third-party/pjproject/patches/0021-stateless-send-transport-handle-no-dest-addr.patch @@ -0,0 +1,28 @@ +diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c +index 3d5c3912c..1e0e31627 100644 +--- a/pjsip/src/pjsip/sip_util.c ++++ b/pjsip/src/pjsip/sip_util.c +@@ -1157,7 +1157,7 @@ static void stateless_send_transport_cb( void *token, + * (2) Failure (i.e. sent <= 0) + */ + cont = (sent > 0) ? PJ_FALSE : +- (tdata->dest_info.cur_addr<tdata->dest_info.addr.count-1); ++ (tdata->dest_info.addr.count > 0 && tdata->dest_info.cur_addr<tdata->dest_info.addr.count-1); + if (stateless_data->app_cb) { + (*stateless_data->app_cb)(stateless_data, sent, &cont); + } else { +@@ -1187,6 +1187,14 @@ static void stateless_send_transport_cb( void *token, + tdata->dest_info.cur_addr++; + } + ++ if (!tdata->dest_info.cur_addr && !tdata->dest_info.addr.count) { ++ if (stateless_data->app_cb) { ++ (*stateless_data->app_cb)(stateless_data, sent, &cont); ++ } ++ pjsip_tx_data_dec_ref(tdata); ++ return; ++ } ++ + /* Have next address? */ + if (tdata->dest_info.cur_addr >= tdata->dest_info.addr.count) { + /* This only happens when a rather buggy application has diff --git a/third-party/pjproject/patches/config_site.h b/third-party/pjproject/patches/config_site.h index 9f4d6787822e2aa2ac75578ef2c8e1f2a871cf06..3536af964a9402781245375bc06f622e96b0937c 100644 --- a/third-party/pjproject/patches/config_site.h +++ b/third-party/pjproject/patches/config_site.h @@ -19,8 +19,9 @@ * versions, newer versions of pjproject won't compile with them. */ #define PJMEDIA_HAS_SRTP 0 +#define PJSIP_HAS_RESOLVER 0 -#define PJ_HAS_IPV6 1 +#define PJ_HAS_IPV6 0 #if !defined(AST_DEVMODE) && !defined(PJPROJECT_BUNDLED_ASSERTIONS) #define NDEBUG 1 #endif @@ -91,3 +92,5 @@ #define PJSIP_TSX_UAS_CONTINUE_ON_TP_ERROR 0 #define PJ_SSL_SOCK_OSSL_USE_THREAD_CB 0 #define PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER 1 +#define PJSIP_AUTH_HEADER_CACHING 1 +#define PJSIP_AUTH_AUTO_SEND_NEXT 1