/* Compare media descriptor */ PJ_DEF(pj_status_t) pjmedia_sdp_media_cmp( const pjmedia_sdp_media *sd1, const pjmedia_sdp_media *sd2, unsigned option) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL); PJ_UNUSED_ARG(option); /* Compare media type. */ if (pj_strcmp(&sd1->desc.media, &sd2->desc.media) != 0) return PJMEDIA_SDP_EMEDIANOTEQUAL; /* Compare port number. */ if (sd1->desc.port != sd2->desc.port) return PJMEDIA_SDP_EPORTNOTEQUAL; /* Compare port count. */ if (sd1->desc.port_count != sd2->desc.port_count) return PJMEDIA_SDP_EPORTNOTEQUAL; /* Compare transports. */ if (pj_strcmp(&sd1->desc.transport, &sd2->desc.transport) != 0) return PJMEDIA_SDP_ETPORTNOTEQUAL; /* For zeroed port media, stop comparing here */ if (sd1->desc.port == 0) return PJ_SUCCESS; /* Compare number of formats. */ if (sd1->desc.fmt_count != sd2->desc.fmt_count) return PJMEDIA_SDP_EFORMATNOTEQUAL; /* Compare formats, in order. */ for (i=0; i<sd1->desc.fmt_count; ++i) { if (pj_strcmp(&sd1->desc.fmt[i], &sd2->desc.fmt[i]) != 0) return PJMEDIA_SDP_EFORMATNOTEQUAL; } /* Compare connection line, if they exist. */ if (sd1->conn) { if (!sd2->conn) return PJMEDIA_SDP_EMEDIANOTEQUAL; status = compare_conn(sd1->conn, sd2->conn); } else { if (sd2->conn) return PJMEDIA_SDP_EMEDIANOTEQUAL; } /* Compare attributes. */ status = compare_attr(sd1->attr_count, sd1->attr, sd2->attr_count, sd2->attr); if (status != PJ_SUCCESS) return status; /* Looks equal */ return PJ_SUCCESS; }
/** * VoX Mobile :: negative SIP response handler */ int get_vox_error_info(pjsip_event *e) { int cause = 0; const pj_str_t HDR = { "Error-Info", 10 }; const pj_str_t BLOCKED_PSTN = { "PSTN not allowed", 16 }; const pj_str_t BLOCKED_DESTINATION = { "Blocked destination", 19 }; if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pjsip_generic_string_hdr *hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name( e->body.tsx_state.src.rdata->msg_info.msg, &HDR, NULL); if (hdr) { if (pj_strcmp(&hdr->hvalue, &BLOCKED_PSTN) == 0) cause = 403; else if (pj_strcmp(&hdr->hvalue, &BLOCKED_DESTINATION) == 0) cause = 580; else { cause = (int)pj_strtoul(&hdr->hvalue); } } } return cause; }
/* * Update authentication session with a challenge. */ static void update_digest_session( pj_pool_t *ses_pool, pjsip_cached_auth *cached_auth, const pjsip_www_authenticate_hdr *hdr ) { if (hdr->challenge.digest.qop.slen == 0) return; /* Initialize cnonce and qop if not present. */ if (cached_auth->cnonce.slen == 0) { /* Save the whole challenge */ cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); /* Create cnonce */ pj_create_unique_string( ses_pool, &cached_auth->cnonce ); /* Initialize nonce-count */ cached_auth->nc = 1; /* Save realm. */ pj_assert(cached_auth->realm.slen != 0); if (cached_auth->realm.slen == 0) { pj_strdup(ses_pool, &cached_auth->realm, &hdr->challenge.digest.realm); } } else { /* Update last_nonce and nonce-count */ if (!pj_strcmp(&hdr->challenge.digest.nonce, &cached_auth->last_chal->challenge.digest.nonce)) { /* Same nonce, increment nonce-count */ ++cached_auth->nc; } else { /* Server gives new nonce. */ pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.nonce, &hdr->challenge.digest.nonce); /* Has the opaque changed? */ if (pj_strcmp(&cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque)) { pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque); } cached_auth->nc = 1; } } }
static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata) { const pj_str_t stateless_user = { "0", 1 }; pjsip_uri *uri; pjsip_sip_uri *sip_uri; uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri); /* Only want to receive SIP/SIPS scheme */ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) return PJ_FALSE; sip_uri = (pjsip_sip_uri*) uri; /* Check for matching user part */ if (pj_strcmp(&sip_uri->user, &stateless_user)!=0) return PJ_FALSE; /* * Yes, this is for us. */ /* Ignore ACK request */ if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) return PJ_TRUE; /* * Respond statelessly with 200/OK. */ pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL, NULL, NULL); app.server.cur_state.stateless_cnt++; return PJ_TRUE; }
static int pjsip_name_addr_compare( pjsip_uri_context_e context, const pjsip_name_addr *naddr1, const pjsip_name_addr *naddr2) { int d; /* Check that naddr2 is also a name_addr */ if (naddr1->vptr != naddr2->vptr) return -1; /* I'm not sure whether display name is included in the comparison. */ if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) { return -1; } pj_assert( naddr1->uri != NULL ); pj_assert( naddr2->uri != NULL ); /* Compare name-addr as URL */ d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri); if (d) return d; return 0; }
PJ_DEF(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count, pjmedia_sdp_attr *attr_array[], const char *name) { unsigned i, removed = 0; pj_str_t attr_name; PJ_ASSERT_RETURN(count && attr_array && name, PJ_EINVAL); attr_name.ptr = (char*)name; attr_name.slen = pj_ansi_strlen(name); for (i=0; i<*count; ) { if (pj_strcmp(&attr_array[i]->name, &attr_name)==0) { pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*), *count, i); --(*count); ++removed; } else { ++i; } } return removed; }
/* Set the published address of the transport */ static void udp_set_pub_name(struct udp_transport *tp, const pjsip_host_port *a_name) { enum { INFO_LEN = 80 }; char local_addr[PJ_INET6_ADDRSTRLEN+10]; char pub_addr[PJ_INET6_ADDRSTRLEN+10]; pj_assert(a_name->host.slen != 0); if (pj_strcmp(&tp->base.local_name.host, &a_name->host) == 0 && tp->base.local_name.port == a_name->port) { return; } pj_strdup_with_null(tp->base.pool, &tp->base.local_name.host, &a_name->host); tp->base.local_name.port = a_name->port; /* Update transport info. */ if (tp->base.info == NULL) { tp->base.info = (char*) pj_pool_alloc(tp->base.pool, INFO_LEN); } pj_sockaddr_print(&tp->base.local_addr, local_addr, sizeof(local_addr), 3); pj_addr_str_print(&tp->base.local_name.host, tp->base.local_name.port, pub_addr, sizeof(pub_addr), 1), pj_ansi_snprintf( tp->base.info, INFO_LEN, "udp %s [published as %s]", local_addr, pub_addr); }
/*! \brief Helper function which returns a UDP transport bound to the given address and port */ static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port) { struct ao2_container *transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); struct ast_sip_transport *transport; struct ao2_iterator iter; pjsip_transport *sip_transport = NULL; if (!transports) { return NULL; } for (iter = ao2_iterator_init(transports, 0); (transport = ao2_iterator_next(&iter)); ao2_ref(transport, -1)) { if ((transport->type != AST_TRANSPORT_UDP) || (pj_strcmp(&transport->state->transport->local_name.host, address)) || (transport->state->transport->local_name.port != port)) { continue; } sip_transport = transport->state->transport; ao2_ref(transport, -1); break; } ao2_iterator_destroy(&iter); ao2_ref(transports, -1); return sip_transport; }
pjmedia_sdp_attr_find (unsigned count, pjmedia_sdp_attr *const attr_array[], const pj_str_t *name, const pj_str_t *c_fmt) { unsigned i; unsigned c_pt = 0xFFFF; if (c_fmt) c_pt = pj_strtoul(c_fmt); for (i=0; i<count; ++i) { if (pj_strcmp(&attr_array[i]->name, name) == 0) { const pjmedia_sdp_attr *a = attr_array[i]; if (c_fmt) { unsigned pt = (unsigned) pj_strtoul2(&a->value, NULL, 10); if (pt == c_pt) { return (pjmedia_sdp_attr*)a; } } else return (pjmedia_sdp_attr*)a; } } return NULL; }
/* Compare connection line. */ static pj_status_t compare_conn(const pjmedia_sdp_conn *c1, const pjmedia_sdp_conn *c2) { /* Compare network type. */ if (pj_strcmp(&c1->net_type, &c2->net_type) != 0) return PJMEDIA_SDP_ECONNNOTEQUAL; /* Compare address type. */ if (pj_strcmp(&c1->addr_type, &c2->addr_type) != 0) return PJMEDIA_SDP_ECONNNOTEQUAL; /* Compare address. */ if (pj_strcmp(&c1->addr, &c2->addr) != 0) return PJMEDIA_SDP_ECONNNOTEQUAL; return PJ_SUCCESS; }
PJ_DEF(int) pjsip_cred_info_cmp(const pjsip_cred_info *cred1, const pjsip_cred_info *cred2) { int result; result = pj_strcmp(&cred1->realm, &cred2->realm); if (result) goto on_return; result = pj_strcmp(&cred1->scheme, &cred2->scheme); if (result) goto on_return; result = pj_strcmp(&cred1->username, &cred2->username); if (result) goto on_return; result = pj_strcmp(&cred1->data, &cred2->data); if (result) goto on_return; result = (cred1->data_type != cred2->data_type); if (result) goto on_return; if ((cred1->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { result = pj_strcmp(&cred1->ext.aka.k, &cred2->ext.aka.k); if (result) goto on_return; result = pj_strcmp(&cred1->ext.aka.op, &cred2->ext.aka.op); if (result) goto on_return; result = pj_strcmp(&cred1->ext.aka.amf, &cred2->ext.aka.amf); if (result) goto on_return; } on_return: return result; }
/* * Find a dialog. */ PJ_DEF(pjsip_dialog*) pjsip_ua_find_dialog(const pj_str_t *call_id, const pj_str_t *local_tag, const pj_str_t *remote_tag, pj_bool_t lock_dialog) { struct dlg_set *dlg_set; pjsip_dialog *dlg; PJ_ASSERT_RETURN(call_id && local_tag && remote_tag, NULL); /* Lock user agent. */ pj_mutex_lock(mod_ua.mutex); /* Lookup the dialog set. */ dlg_set = (struct dlg_set*) pj_hash_get(mod_ua.dlg_table, local_tag->ptr, local_tag->slen, NULL); if (dlg_set == NULL) { /* Not found */ pj_mutex_unlock(mod_ua.mutex); return NULL; } /* Dialog set is found, now find the matching dialog based on the * remote tag. */ dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { if (pj_strcmp(&dlg->remote.info->tag, remote_tag) == 0) break; dlg = dlg->next; } if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { /* Not found */ pj_mutex_unlock(mod_ua.mutex); return NULL; } /* Dialog has been found. It SHOULD have the right Call-ID!! */ PJ_ASSERT_ON_FAIL(pj_strcmp(&dlg->call_id->id, call_id)==0, {pj_mutex_unlock(mod_ua.mutex); return NULL;});
/* Verify incoming Authorization/Proxy-Authorization header against the * specified credential. */ static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr, const pj_str_t *method, const pjsip_cred_info *cred_info ) { if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { char digest_buf[PJSIP_MD5STRLEN]; pj_str_t digest; const pjsip_digest_credential *dig = &hdr->credential.digest; /* Check that username and realm match. * These checks should have been performed before entering this * function. */ PJ_ASSERT_RETURN(pj_strcmp(&dig->username, &cred_info->username) == 0, PJ_EINVALIDOP); PJ_ASSERT_RETURN(pj_strcmp(&dig->realm, &cred_info->realm) == 0, PJ_EINVALIDOP); /* Prepare for our digest calculation. */ digest.ptr = digest_buf; digest.slen = PJSIP_MD5STRLEN; /* Create digest for comparison. */ pjsip_auth_create_digest(&digest, &hdr->credential.digest.nonce, &hdr->credential.digest.nc, &hdr->credential.digest.cnonce, &hdr->credential.digest.qop, &hdr->credential.digest.uri, &cred_info->realm, cred_info, method ); /* Compare digest. */ return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ? PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST; } else { pj_assert(!"Unsupported authentication scheme"); return PJSIP_EINVALIDAUTHSCHEME; } }
static int find_transport_in_use(void *obj, void *arg, int flags) { struct ast_sip_transport *transport = obj; pjsip_rx_data *rdata = arg; if ((transport->state->transport == rdata->tp_info.transport) || (transport->state->factory && !pj_strcmp(&transport->state->factory->addr_name.host, &rdata->tp_info.transport->local_name.host) && transport->state->factory->addr_name.port == rdata->tp_info.transport->local_name.port)) { return CMP_MATCH | CMP_STOP; } return 0; }
/* Verify incoming Authorization/Proxy-Authorization header against existing * credentials. Will return TRUE if the authorization request matches any of * the credential. */ PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr, const pj_str_t *method, const pjsip_cred_info *cred_info ) { if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { char digest_buf[MD5STRLEN]; pj_str_t digest; const pjsip_digest_credential *dig = &hdr->credential.digest; /* Check that username match. */ if (pj_strcmp(&dig->username, &cred_info->username) != 0) return PJ_FALSE; /* Check that realm match. */ if (pj_strcmp(&dig->realm, &cred_info->realm) != 0) return PJ_FALSE; /* Prepare for our digest calculation. */ digest.ptr = digest_buf; digest.slen = MD5STRLEN; /* Create digest for comparison. */ create_digest( &digest, &hdr->credential.digest.nonce, &hdr->credential.digest.nc, &hdr->credential.digest.cnonce, &hdr->credential.digest.qop, &hdr->credential.digest.uri, cred_info, method ); return pj_stricmp(&digest, &hdr->credential.digest.response) == 0; } else { pj_assert(0); return PJ_FALSE; } }
/* * Apply direction attribute in session to all media. */ static void apply_media_direction(pjmedia_sdp_session *sdp) { pjmedia_sdp_attr *dir_attr = NULL; unsigned i; const pj_str_t inactive = { "inactive", 8 }; const pj_str_t sendonly = { "sendonly", 8 }; const pj_str_t recvonly = { "recvonly", 8 }; const pj_str_t sendrecv = { "sendrecv", 8 }; /* Find direction attribute in session, don't need to find default * direction "sendrecv". */ for (i = 0; i < sdp->attr_count && !dir_attr; ++i) { if (!pj_strcmp(&sdp->attr[i]->name, &sendonly) || !pj_strcmp(&sdp->attr[i]->name, &recvonly) || !pj_strcmp(&sdp->attr[i]->name, &inactive)) { dir_attr = sdp->attr[i]; } } /* Found the direction attribute */ if (dir_attr) { /* Remove the direction attribute in session */ pjmedia_sdp_attr_remove(&sdp->attr_count, sdp->attr, dir_attr); /* Apply the direction attribute to all media, but not overriding it * if media already has direction attribute. */ for (i = 0; i < sdp->media_count; ++i) { pjmedia_sdp_media *m; unsigned j; /* Find direction attribute in this media */ m = sdp->media[i]; for (j = 0; j < m->attr_count; ++j) { if (!pj_strcmp(&m->attr[j]->name, &sendrecv) || !pj_strcmp(&m->attr[j]->name, &sendonly) || !pj_strcmp(&m->attr[j]->name, &recvonly) || !pj_strcmp(&m->attr[j]->name, &inactive)) { break; } } /* Not found, apply direction attribute from session */ if (j == m->attr_count) pjmedia_sdp_media_add_attr(m, dir_attr); } } }
/* Internal function to apply symmetric PT for the local answer. */ static void apply_answer_symmetric_pt(pj_pool_t *pool, pjmedia_sdp_media *answer, unsigned pt_cnt, const pj_str_t pt_offer[], const pj_str_t pt_answer[]) { pjmedia_sdp_attr *a_tmp[PJMEDIA_MAX_SDP_ATTR]; unsigned i, a_tmp_cnt = 0; /* Rewrite the payload types in the answer if different to * the ones in the offer. */ for (i = 0; i < pt_cnt; ++i) { pjmedia_sdp_attr *a; /* Skip if the PTs are the same already, e.g: static PT. */ if (pj_strcmp(&pt_answer[i], &pt_offer[i]) == 0) continue; /* Rewrite payload type in the answer to match to the offer */ pj_strdup(pool, &answer->desc.fmt[i], &pt_offer[i]); /* Also update payload type in rtpmap */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &pt_answer[i]); if (a) { rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]); /* Temporarily remove the attribute in case the new payload * type is being used by another format in the media. */ pjmedia_sdp_media_remove_attr(answer, a); a_tmp[a_tmp_cnt++] = a; } /* Also update payload type in fmtp */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &pt_answer[i]); if (a) { rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]); /* Temporarily remove the attribute in case the new payload * type is being used by another format in the media. */ pjmedia_sdp_media_remove_attr(answer, a); a_tmp[a_tmp_cnt++] = a; } } /* Return back 'rtpmap' and 'fmtp' attributes */ for (i = 0; i < a_tmp_cnt; ++i) pjmedia_sdp_media_add_attr(answer, a_tmp[i]); }
static int srtp_crypto_cmp(const pjmedia_srtp_crypto* c1, const pjmedia_srtp_crypto* c2) { int r; r = pj_strcmp(&c1->key, &c2->key); if (r != 0) return r; r = pj_stricmp(&c1->name, &c2->name); if (r != 0) return r; return (c1->flags != c2->flags); }
static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata) { const pj_str_t stateful_user = { "1", 1 }; pjsip_uri *uri; pjsip_sip_uri *sip_uri; uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri); /* Only want to receive SIP/SIPS scheme */ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) return PJ_FALSE; sip_uri = (pjsip_sip_uri*) uri; /* Check for matching user part */ if (pj_strcmp(&sip_uri->user, &stateful_user)!=0) return PJ_FALSE; /* * Yes, this is for us. * Respond statefully with 200/OK. */ switch (rdata->msg_info.msg->line.req.method.id) { case PJSIP_INVITE_METHOD: { pjsip_msg_body *body; if (dummy_sdp_str.slen == 0) dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr); body = pjsip_msg_body_create(rdata->tp_info.pool, &mime_application, &mime_sdp, &dummy_sdp_str); pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata, 200, NULL, NULL, body, NULL); } break; case PJSIP_ACK_METHOD: return PJ_TRUE; default: pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata, 200, NULL, NULL, NULL, NULL); break; } app.server.cur_state.stateful_cnt++; return PJ_TRUE; }
/* Schedule timer to terminate transaction. */ static void schedule_terminate_tsx( pjsip_transaction *tsx, int status_code, int msec_delay ) { pj_time_val delay; delay.sec = 0; delay.msec = msec_delay; pj_time_val_normalize(&delay); pj_assert(pj_strcmp(&tsx->transaction_key, &tsx_key)==0); timer.user_data = NULL; timer.id = status_code; timer.cb = &terminate_tsx_timer; pjsip_endpt_schedule_timer(endpt, &timer, &delay); }
PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc, pjsip_host_port *via_addr, pjsip_transport *via_tp) { PJ_ASSERT_RETURN(regc, PJ_EINVAL); if (!via_addr) pj_bzero(®c->via_addr, sizeof(regc->via_addr)); else { if (pj_strcmp(®c->via_addr.host, &via_addr->host)) pj_strdup(regc->pool, ®c->via_addr.host, &via_addr->host); regc->via_addr.port = via_addr->port; } regc->via_tp = via_tp; return PJ_SUCCESS; }
/* Start ICE session with the specified remote SDP */ static pj_status_t start_ice(struct transport_ice *tp_ice, pj_pool_t *tmp_pool, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { pjmedia_sdp_media *rem_m = rem_sdp->media[media_index]; const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; pj_ice_sess_cand *cand; unsigned i, cand_cnt; pj_status_t status; get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); /* Allocate candidate array */ cand = (pj_ice_sess_cand*) pj_pool_calloc(tmp_pool, PJ_ICE_MAX_CAND, sizeof(pj_ice_sess_cand)); /* Get all candidates in the media */ cand_cnt = 0; for (i=0; i<rem_m->attr_count && cand_cnt < PJ_ICE_MAX_CAND; ++i) { pjmedia_sdp_attr *attr; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &STR_CANDIDATE)!=0) continue; /* Parse candidate */ status = parse_cand(tp_ice->base.name, tmp_pool, &attr->value, &cand[cand_cnt]); if (status != PJ_SUCCESS) { PJ_LOG(4,(tp_ice->base.name, "Error in parsing SDP candidate attribute '%.*s', " "candidate is ignored", (int)attr->value.slen, attr->value.ptr)); continue; } cand_cnt++; } /* Start ICE */ return pj_ice_strans_start_ice(tp_ice->ice_st, &ufrag_attr->value, &pwd_attr->value, cand_cnt, cand); }
bool SessionExpiresHelper::timer_supported(pjsip_msg* msg) { pjsip_supported_hdr* supported_hdr = (pjsip_supported_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL); if (supported_hdr != NULL) { for (unsigned ii = 0; ii < supported_hdr->count; ++ii) { if (pj_strcmp(&supported_hdr->values[ii], &STR_TIMER) == 0) { return true; } } } return false; }
/*! \brief Callback function for finding the transport the request is going out on */ static int find_transport_in_use(void *obj, void *arg, int flags) { struct ast_sip_transport *transport = obj; struct request_transport_details *details = arg; /* If an explicit transport or factory matches then this is what is in use, if we are unavailable * to compare based on that we make sure that the type is the same and the source IP address/port are the same */ if ((details->transport && details->transport == transport->state->transport) || (details->factory && details->factory == transport->state->factory) || ((details->type == transport->type) && (transport->state->factory) && !pj_strcmp(&transport->state->factory->addr_name.host, &details->local_address) && transport->state->factory->addr_name.port == details->local_port)) { return CMP_MATCH | CMP_STOP; } return 0; }
/*! \brief Helper function which determines if the existing address has priority over new one */ static int multihomed_rewrite_header(pj_str_t *source, pjsip_transport *transport) { pj_uint32_t loop6[4] = {0, 0, 0, 0}; /* If the transport is bound to any it should always rewrite */ if ((transport->local_addr.addr.sa_family == pj_AF_INET() && transport->local_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) || (transport->local_addr.addr.sa_family == pj_AF_INET6() && !pj_memcmp(&transport->local_addr.ipv6.sin6_addr, loop6, sizeof(loop6)))) { return 1; } /* If the transport is explicitly bound but the determined source differs favor the transport */ if (!pj_strcmp(source, &transport->local_name.host)) { return 1; } return 0; }
// Reject request unless it's a SUBSCRIBE targeted at the home domain / this node. pj_bool_t subscription_on_rx_request(pjsip_rx_data *rdata) { SAS::TrailId trail = get_trail(rdata); if (rdata->tp_info.transport->local_name.port != stack_data.scscf_port) { // Not an S-CSCF, so don't handle SUBSCRIBEs. return PJ_FALSE; // LCOV_EXCL_LINE } if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) { // This isn't a SUBSCRIBE, so this module can't process it. return PJ_FALSE; } if (!((PJUtils::is_home_domain(rdata->msg_info.msg->line.req.uri) || (PJUtils::is_uri_local(rdata->msg_info.msg->line.req.uri))) && PJUtils::check_route_headers(rdata))) { LOG_DEBUG("Rejecting subscription request not targeted at this domain or node"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_DOMAIN, 0); SAS::report_event(event); return PJ_FALSE; } // SUBSCRIBE request targeted at the home domain or specifically at this node. Check // whether it should be processed by this module or passed up to an AS. pjsip_msg *msg = rdata->msg_info.msg; // A valid subscription must have the Event header set to "reg". This is case-sensitive pj_str_t event_name = pj_str("Event"); pjsip_event_hdr* event = (pjsip_event_hdr*)pjsip_msg_find_hdr_by_name(msg, &event_name, NULL); if (!event || (PJUtils::pj_str_to_string(&event->event_type) != "reg")) { // The Event header is missing or doesn't match "reg" LOG_DEBUG("Rejecting subscription request with invalid event header"); SAS::Event sas_event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EVENT, 0); if (event) { char event_hdr_str[256]; memset(event_hdr_str, 0, 256); pjsip_hdr_print_on(event, event_hdr_str, 255); sas_event.add_var_param(event_hdr_str); } SAS::report_event(sas_event); return PJ_FALSE; } // Accept header may be present - if so must include the application/reginfo+xml pjsip_accept_hdr* accept = (pjsip_accept_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_ACCEPT, NULL); if (accept) { bool found = false; pj_str_t reginfo = pj_str("application/reginfo+xml"); for (uint32_t i = 0; i < accept->count; i++) { if (!pj_strcmp(accept->values + i, ®info)) { found = true; } } if (!found) { // The Accept header (if it exists) doesn't contain "application/reginfo+xml" LOG_DEBUG("Rejecting subscription request with invalid accept header"); char accept_hdr_str[256]; memset(accept_hdr_str, 0, 256); pjsip_hdr_print_on(accept, accept_hdr_str, 255); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_ACCEPT, 0); event.add_var_param(accept_hdr_str); SAS::report_event(event); return PJ_FALSE; } } process_subscription_request(rdata); return PJ_TRUE; }
static pj_int32_t calculate_response_expiration(const pjsip_regc *regc, const pjsip_rx_data *rdata, unsigned *contact_cnt, unsigned max_contact, pjsip_contact_hdr *contacts[]) { pj_int32_t expiration = NOEXP; const pjsip_msg *msg = rdata->msg_info.msg; const pjsip_hdr *hdr; /* Enumerate all Contact headers in the response */ *contact_cnt = 0; for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { if (hdr->type == PJSIP_H_CONTACT && *contact_cnt < max_contact) { contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr; ++(*contact_cnt); } } if (regc->current_op == REGC_REGISTERING) { pj_bool_t has_our_contact = PJ_FALSE; const pjsip_expires_hdr *expires; /* Get Expires header */ expires = (const pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); /* Try to find the Contact URIs that we register, in the response * to get the expires value. We'll try both with comparing the URI * and comparing the extension param only. */ if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) { unsigned i; for (i=0; i<*contact_cnt; ++i) { const pjsip_contact_hdr *our_hdr; our_hdr = (const pjsip_contact_hdr*) regc->contact_hdr_list.next; /* Match with our Contact header(s) */ while ((void*)our_hdr != (void*)®c->contact_hdr_list) { const pjsip_uri *uri1, *uri2; pj_bool_t matched = PJ_FALSE; /* Exclude the display name when comparing the URI * since server may not return it. */ uri1 = (const pjsip_uri*) pjsip_uri_get_uri(contacts[i]->uri); uri2 = (const pjsip_uri*) pjsip_uri_get_uri(our_hdr->uri); /* First try with exact matching, according to RFC 3261 * Section 19.1.4 URI Comparison */ if (pjsip_cfg()->regc.check_contact) { matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, uri1, uri2)==0; } /* If no match is found, try with matching the extension * parameter only if extension parameter was added. */ if (!matched && regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(uri1) || PJSIP_URI_SCHEME_IS_SIPS(uri1)) && (PJSIP_URI_SCHEME_IS_SIP(uri2) || PJSIP_URI_SCHEME_IS_SIPS(uri2))) { const pjsip_sip_uri *sip_uri1, *sip_uri2; const pjsip_param *p1, *p2; sip_uri1 = (const pjsip_sip_uri*)uri1; sip_uri2 = (const pjsip_sip_uri*)uri2; p1 = pjsip_param_cfind(&sip_uri1->other_param, &XUID_PARAM_NAME); p2 = pjsip_param_cfind(&sip_uri2->other_param, &XUID_PARAM_NAME); matched = p1 && p2 && pj_strcmp(&p1->value, &p2->value)==0; } if (matched) { has_our_contact = PJ_TRUE; if (contacts[i]->expires >= 0 && contacts[i]->expires < expiration) { /* Get the lowest expiration time. */ expiration = contacts[i]->expires; } break; } our_hdr = our_hdr->next; } /* while ((void.. */ } /* for (i=.. */ /* If matching Contact header(s) are found but the * header doesn't contain expires parameter, get the * expiration value from the Expires header. And * if Expires header is not present, get the expiration * value from the request. */ if (has_our_contact && expiration == NOEXP) { if (expires) { expiration = expires->ivalue; } else if (regc->expires_hdr) { expiration = regc->expires_hdr->ivalue; } else { /* We didn't request explicit expiration value, * and server doesn't specify it either. This * shouldn't happen unless we have a broken * registrar. */ expiration = 3600; } } } /* If we still couldn't get matching Contact header(s), it means * there must be something wrong with the registrar (e.g. it may * have modified the URI's in the response, which is prohibited). */ if (expiration==NOEXP) { /* If the number of Contact headers in the response matches * ours, they're all probably ours. Get the expiration * from there if this is the case, or from Expires header * if we don't have exact Contact header count, or * from the request as the last resort. */ pj_size_t our_contact_cnt; our_contact_cnt = pj_list_size(®c->contact_hdr_list); if (*contact_cnt == our_contact_cnt && *contact_cnt && contacts[0]->expires >= 0) { expiration = contacts[0]->expires; } else if (expires) expiration = expires->ivalue; else if (regc->expires_hdr) expiration = regc->expires_hdr->ivalue; else expiration = 3600; } } else { /* Just assume that the unregistration has been successful. */ expiration = 0; } /* Must have expiration value by now */ pj_assert(expiration != NOEXP); return expiration; }
static pj_status_t pjsip_url_compare( pjsip_uri_context_e context, const pjsip_sip_uri *url1, const pjsip_sip_uri *url2) { const pjsip_param *p1; /* * Compare two SIP URL's according to Section 19.1.4 of RFC 3261. */ /* SIP and SIPS URI are never equivalent. * Note: just compare the vptr to avoid string comparison. * Pretty neat huh!! */ if (url1->vptr != url2->vptr) return PJSIP_ECMPSCHEME; /* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive. * This includes userinfo containing passwords or formatted as * telephone-subscribers. */ if (pj_strcmp(&url1->user, &url2->user) != 0) return PJSIP_ECMPUSER; if (pj_strcmp(&url1->passwd, &url2->passwd) != 0) return PJSIP_ECMPPASSWD; /* Comparison of all other components of the URI is * case-insensitive unless explicitly defined otherwise. */ /* The ordering of parameters and header fields is not significant * in comparing SIP and SIPS URIs. */ /* Characters other than those in the reserved set (see RFC 2396 [5]) * are equivalent to their encoding. */ /* An IP address that is the result of a DNS lookup of a host name * does not match that host name. */ if (pj_stricmp(&url1->host, &url2->host) != 0) return PJSIP_ECMPHOST; /* A URI omitting any component with a default value will not match a URI * explicitly containing that component with its default value. * For instance, a URI omitting the optional port component will not match * a URI explicitly declaring port 5060. * The same is true for the transport-parameter, ttl-parameter, * user-parameter, and method components. */ /* Port is not allowed in To and From header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { if (url1->port != url2->port) return PJSIP_ECMPPORT; } /* Transport is not allowed in From/To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { if (pj_stricmp(&url1->transport_param, &url2->transport_param) != 0) return PJSIP_ECMPTRANSPORTPRM; } /* TTL param is not allowed in From, To, Route, and Record-Route header. */ if (context != PJSIP_URI_IN_FROMTO_HDR && context != PJSIP_URI_IN_ROUTING_HDR) { if (url1->ttl_param != url2->ttl_param) return PJSIP_ECMPTTLPARAM; } /* User param is allowed in all contexes */ if (pj_stricmp(&url1->user_param, &url2->user_param) != 0) return PJSIP_ECMPUSERPARAM; /* Method param is only allowed in external/other context. */ if (context == PJSIP_URI_IN_OTHER) { if (pj_stricmp(&url1->method_param, &url2->method_param) != 0) return PJSIP_ECMPMETHODPARAM; } /* maddr param is not allowed in From and To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { if (pj_stricmp(&url1->maddr_param, &url2->maddr_param) != 0) return PJSIP_ECMPMADDRPARAM; } /* lr parameter is ignored (?) */ /* lr param is not allowed in From, To, and Contact header. */ /* All other uri-parameters appearing in only one URI are ignored when * comparing the URIs. */ if (pjsip_param_cmp(&url1->other_param, &url2->other_param, 1)!=0) return PJSIP_ECMPOTHERPARAM; /* URI header components are never ignored. Any present header component * MUST be present in both URIs and match for the URIs to match. * The matching rules are defined for each header field in Section 20. */ p1 = url1->header_param.next; while (p1 != &url1->header_param) { const pjsip_param *p2; p2 = pjsip_param_find(&url2->header_param, &p1->name); if (p2) { /* It seems too much to compare two header params according to * the rule of each header. We'll just compare them string to * string.. */ if (pj_stricmp(&p1->value, &p2->value) != 0) return PJSIP_ECMPHEADERPARAM; } else { return PJSIP_ECMPHEADERPARAM; } p1 = p1->next; } /* Equal!! Pheuww.. */ return PJ_SUCCESS; }
/* Compare attributes array. */ static pj_status_t compare_attr_imp(int inst_id, unsigned count1, pjmedia_sdp_attr *const attr1[], unsigned count2, pjmedia_sdp_attr *const attr2[]) { pj_status_t status; unsigned i; const pj_str_t inactive = { "inactive", 8 }; const pj_str_t sendrecv = { "sendrecv", 8 }; const pj_str_t sendonly = { "sendonly", 8 }; const pj_str_t recvonly = { "recvonly", 8 }; const pj_str_t fmtp = { "fmtp", 4 }; const pj_str_t rtpmap = { "rtpmap", 6 }; /* For simplicity, we only compare the following attributes, and ignore * the others: * - direction, eg. inactive, sendonly, recvonly, sendrecv * - fmtp for each payload. * - rtpmap for each payload. */ for (i=0; i<count1; ++i) { const pjmedia_sdp_attr *a1 = attr1[i]; if (pj_strcmp(&a1->name, &inactive) == 0 || pj_strcmp(&a1->name, &sendrecv) == 0 || pj_strcmp(&a1->name, &sendonly) == 0 || pj_strcmp(&a1->name, &recvonly) == 0) { /* For inactive, sendrecv, sendonly, and recvonly attributes, * the same attribute must be present on the other SDP. */ const pjmedia_sdp_attr *a2; a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, NULL); if (!a2) return PJMEDIA_SDP_EDIRNOTEQUAL; } else if (pj_strcmp(&a1->name, &fmtp) == 0) { /* For fmtp attribute, find the fmtp attribute in the other SDP * for the same payload type, and compare the fmtp param/value. */ pjmedia_sdp_fmtp fmtp1, fmtp2; const pjmedia_sdp_attr *a2; status = pjmedia_sdp_attr_get_fmtp(a1, &fmtp1); if (status != PJ_SUCCESS) return PJMEDIA_SDP_EFMTPNOTEQUAL; a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &fmtp1.fmt); if (!a2) return PJMEDIA_SDP_EFMTPNOTEQUAL; status = pjmedia_sdp_attr_get_fmtp(a2, &fmtp2); if (status != PJ_SUCCESS) return PJMEDIA_SDP_EFMTPNOTEQUAL; if (pj_strcmp(&fmtp1.fmt_param, &fmtp2.fmt_param) != 0) return PJMEDIA_SDP_EFMTPNOTEQUAL; } else if (pj_strcmp(&a1->name, &rtpmap) == 0) { /* For rtpmap attribute, find rtpmap attribute on the other SDP * for the same payload type, and compare both rtpmap atribute * values. */ pjmedia_sdp_rtpmap r1, r2; const pjmedia_sdp_attr *a2; status = pjmedia_sdp_attr_get_rtpmap(inst_id, a1, &r1); if (status != PJ_SUCCESS) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &r1.pt); if (!a2) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; status = pjmedia_sdp_attr_get_rtpmap(inst_id, a2, &r2); if (status != PJ_SUCCESS) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (pj_strcmp(&r1.pt, &r2.pt) != 0) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (pj_strcmp(&r1.enc_name, &r2.enc_name) != 0) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (r1.clock_rate != r2.clock_rate) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (pj_strcmp(&r1.param, &r2.param) != 0) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; } } return PJ_SUCCESS; }
/* * Compare two SDP session for equality. */ PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( int inst_id, const pjmedia_sdp_session *sd1, const pjmedia_sdp_session *sd2, unsigned option) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL); PJ_UNUSED_ARG(option); /* Compare the origin line. */ if (pj_strcmp(&sd1->origin.user, &sd2->origin.user) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (sd1->origin.id != sd2->origin.id) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (sd1->origin.version != sd2->origin.version) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (pj_strcmp(&sd1->origin.net_type, &sd2->origin.net_type) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (pj_strcmp(&sd1->origin.addr_type, &sd2->origin.addr_type) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (pj_strcmp(&sd1->origin.addr, &sd2->origin.addr) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; /* Compare the subject line. */ if (pj_strcmp(&sd1->name, &sd2->name) != 0) return PJMEDIA_SDP_ENAMENOTEQUAL; /* Compare connection line, when they exist */ if (sd1->conn) { if (!sd2->conn) return PJMEDIA_SDP_ECONNNOTEQUAL; status = compare_conn(sd1->conn, sd2->conn); if (status != PJ_SUCCESS) return status; } else { if (sd2->conn) return PJMEDIA_SDP_ECONNNOTEQUAL; } /* Compare time line. */ if (sd1->time.start != sd2->time.start) return PJMEDIA_SDP_ETIMENOTEQUAL; if (sd1->time.stop != sd2->time.stop) return PJMEDIA_SDP_ETIMENOTEQUAL; /* Compare attributes. */ status = compare_attr(inst_id, sd1->attr_count, sd1->attr, sd2->attr_count, sd2->attr); if (status != PJ_SUCCESS) return status; /* Compare media lines. */ if (sd1->media_count != sd2->media_count) return PJMEDIA_SDP_EMEDIANOTEQUAL; for (i=0; i<sd1->media_count; ++i) { status = pjmedia_sdp_media_cmp(inst_id, sd1->media[i], sd2->media[i], 0); if (status != PJ_SUCCESS) return status; } /* Looks equal. */ return PJ_SUCCESS; }