static int get_endpoint_details(pjsip_rx_data *rdata, char *domain, size_t domain_size) { pjsip_uri *from = rdata->msg_info.from->uri; pjsip_sip_uri *sip_from; if (!PJSIP_URI_SCHEME_IS_SIP(from) && !PJSIP_URI_SCHEME_IS_SIPS(from)) { return -1; } sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from); ast_copy_pj_str(domain, &sip_from->host, domain_size); return 0; }
/*! * \internal * \brief Copies any other request header data over to ast_msg structure. * * \param rdata The SIP request * \param msg The msg structure to copy headers into */ static int headers_to_vars(const pjsip_rx_data *rdata, struct ast_msg *msg) { char *c; char name[MAX_HDR_SIZE]; char buf[MAX_HDR_SIZE]; int res = 0; pjsip_hdr *h = rdata->msg_info.msg->hdr.next; pjsip_hdr *end= &rdata->msg_info.msg->hdr; while (h != end) { if ((res = pjsip_hdr_print_on(h, buf, sizeof(buf)-1)) > 0) { buf[res] = '\0'; if ((c = strchr(buf, ':'))) { ast_copy_string(buf, ast_skip_blanks(c + 1), sizeof(buf)); } ast_copy_pj_str(name, &h->name, sizeof(name)); if ((res = ast_msg_set_var(msg, name, buf)) != 0) { break; } } h = h->next; } return 0; }
static pjsip_authorization_hdr *get_auth_header(pjsip_rx_data *rdata, char *username, size_t username_size, char *realm, size_t realm_size, pjsip_authorization_hdr *start) { pjsip_authorization_hdr *header; header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, start); if (!header || pj_stricmp2(&header->scheme, "digest")) { return NULL; } ast_copy_pj_str(username, &header->credential.digest.username, username_size); ast_copy_pj_str(realm, &header->credential.digest.realm, realm_size); return header; }
static void rfc3326_use_reason_header(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { const pj_str_t str_reason = { "Reason", 6 }; pjsip_generic_string_hdr *header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_reason, NULL); char buf[20], *cause, *text; int code; if (!header) { return; } ast_copy_pj_str(buf, &header->hvalue, sizeof(buf)); cause = ast_skip_blanks(buf); if (strncasecmp(cause, "Q.850", 5) || !(cause = strstr(cause, "cause="))) { return; } /* If text is present get rid of it */ if ((text = strstr(cause, ";"))) { *text = '\0'; } if (sscanf(cause, "cause=%30d", &code) != 1) { return; } ast_channel_hangupcause_set(session->channel, code & 0x7f); }
static char *assign_uuid(const pj_str_t *call_id, const pj_str_t *local_tag, const pj_str_t *remote_tag) { RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); pjsip_dialog *dlg; char *uuid = NULL; enum hep_uuid_type uuid_type = hepv3_get_uuid_type(); if ((uuid_type == HEP_UUID_TYPE_CHANNEL) && (dlg = pjsip_ua_find_dialog(call_id, local_tag, remote_tag, PJ_FALSE)) && (session = ast_sip_dialog_get_session(dlg)) && (session->channel)) { uuid = ast_strdup(ast_channel_name(session->channel)); } /* If we couldn't get the channel or we never wanted it, default to the call-id */ if (!uuid) { uuid = ast_malloc(pj_strlen(call_id) + 1); if (uuid) { ast_copy_pj_str(uuid, call_id, pj_strlen(call_id) + 1); } } return uuid; }
/*! \brief Function which applies a negotiated stream */ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream, const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream) { RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); char host[NI_MAXHOST]; struct t38_state *state; if (!session_media->udptl) { ast_debug(3, "Not applying negotiated SDP stream: no UDTPL session\n"); return 0; } if (!(state = t38_state_get_or_alloc(session))) { return -1; } ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); return -1; } ast_sockaddr_set_port(addrs, remote_stream->desc.port); ast_udptl_set_peer(session_media->udptl, addrs); t38_interpret_sdp(state, session, session_media, remote_stream); return 0; }
static void set_redirecting_value(char **dst, const pj_str_t *src) { ast_free(*dst); *dst = ast_malloc(pj_strlen(src) + 1); if (*dst) { ast_copy_pj_str(*dst, src, pj_strlen(src) + 1); } }
static void log_failed_request(pjsip_rx_data *rdata, char *msg, unsigned int count, unsigned int period) { char from_buf[PJSIP_MAX_URL_SIZE]; char callid_buf[PJSIP_MAX_URL_SIZE]; char method_buf[PJSIP_MAX_URL_SIZE]; pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from_buf, PJSIP_MAX_URL_SIZE); ast_copy_pj_str(callid_buf, &rdata->msg_info.cid->id, PJSIP_MAX_URL_SIZE); ast_copy_pj_str(method_buf, &rdata->msg_info.msg->line.req.method.name, PJSIP_MAX_URL_SIZE); if (count) { ast_log(LOG_NOTICE, "Request '%s' from '%s' failed for '%s:%d' (callid: %s) - %s" " after %u tries in %.3f ms\n", method_buf, from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, msg, count, period / 1000.0); } else { ast_log(LOG_NOTICE, "Request '%s' from '%s' failed for '%s:%d' (callid: %s) - %s\n", method_buf, from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, msg); } }
static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs) { pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; pjmedia_sdp_fmtp fmtp; struct ast_format *format; int i, num = 0; char name[256]; char media[20]; char fmt_param[256]; ast_rtp_codecs_payloads_initialize(codecs); /* Iterate through provided formats */ for (i = 0; i < stream->desc.fmt_count; ++i) { /* The payload is kept as a string for things like t38 but for video it is always numerical */ ast_rtp_codecs_payloads_set_m_type(codecs, NULL, pj_strtoul(&stream->desc.fmt[i])); /* Look for the optional rtpmap attribute */ if (!(attr = pjmedia_sdp_media_find_attr2(stream, "rtpmap", &stream->desc.fmt[i]))) { continue; } /* Interpret the attribute as an rtpmap */ if ((pjmedia_sdp_attr_to_rtpmap(session->inv_session->pool_prov, attr, &rtpmap)) != PJ_SUCCESS) { continue; } ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name)); ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media)); ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, pj_strtoul(&stream->desc.fmt[i]), media, name, 0, rtpmap->clock_rate); /* Look for an optional associated fmtp attribute */ if (!(attr = pjmedia_sdp_media_find_attr2(stream, "fmtp", &rtpmap->pt))) { continue; } if ((pjmedia_sdp_attr_get_fmtp(attr, &fmtp)) == PJ_SUCCESS) { sscanf(pj_strbuf(&fmtp.fmt), "%d", &num); if ((format = ast_rtp_codecs_get_payload_format(codecs, num))) { ast_copy_pj_str(fmt_param, &fmtp.fmt_param, sizeof(fmt_param)); ast_format_sdp_parse(format, fmt_param); } } } }
static int parse_dtls_attrib(struct ast_sip_session_media *session_media, const struct pjmedia_sdp_media *stream) { int i; struct ast_rtp_engine_dtls *dtls = ast_rtp_instance_get_dtls(session_media->rtp); for (i = 0; i < stream->attr_count; i++) { pjmedia_sdp_attr *attr = stream->attr[i]; pj_str_t *value; if (!attr->value.ptr) { continue; } value = pj_strtrim(&attr->value); if (!pj_strcmp2(&attr->name, "setup")) { if (!pj_stricmp2(value, "active")) { dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_ACTIVE); } else if (!pj_stricmp2(value, "passive")) { dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_PASSIVE); } else if (!pj_stricmp2(value, "actpass")) { dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_ACTPASS); } else if (!pj_stricmp2(value, "holdconn")) { dtls->set_setup(session_media->rtp, AST_RTP_DTLS_SETUP_HOLDCONN); } else { ast_log(LOG_WARNING, "Unsupported setup attribute value '%*s'\n", (int)value->slen, value->ptr); } } else if (!pj_strcmp2(&attr->name, "connection")) { if (!pj_stricmp2(value, "new")) { dtls->reset(session_media->rtp); } else if (!pj_stricmp2(value, "existing")) { /* Do nothing */ } else { ast_log(LOG_WARNING, "Unsupported connection attribute value '%*s'\n", (int)value->slen, value->ptr); } } else if (!pj_strcmp2(&attr->name, "fingerprint")) { char hash_value[256], hash[32]; char fingerprint_text[value->slen + 1]; ast_copy_pj_str(fingerprint_text, value, sizeof(fingerprint_text)); if (sscanf(fingerprint_text, "%31s %255s", hash, hash_value) == 2) { if (!strcasecmp(hash, "sha-1")) { dtls->set_fingerprint(session_media->rtp, AST_RTP_DTLS_HASH_SHA1, hash_value); } else if (!strcasecmp(hash, "sha-256")) { dtls->set_fingerprint(session_media->rtp, AST_RTP_DTLS_HASH_SHA256, hash_value); } else { ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s'\n", hash); } } } } ast_set_flag(session_media->srtp, AST_SRTP_CRYPTO_OFFER_OK); return 0; }
/*! * \internal * \brief Set an ast_party_id name and number based on an identity header. * \param hdr From, P-Asserted-Identity, or Remote-Party-ID header on incoming message * \param[out] id The ID to set data on */ static void set_id_from_hdr(pjsip_fromto_hdr *hdr, struct ast_party_id *id) { char cid_name[AST_CHANNEL_NAME]; char cid_num[AST_CHANNEL_NAME]; pjsip_sip_uri *uri; pjsip_name_addr *id_name_addr = (pjsip_name_addr *) hdr->uri; uri = pjsip_uri_get_uri(id_name_addr); ast_copy_pj_str(cid_name, &id_name_addr->display, sizeof(cid_name)); ast_copy_pj_str(cid_num, &uri->user, sizeof(cid_num)); ast_free(id->name.str); id->name.str = ast_strdup(cid_name); if (!ast_strlen_zero(cid_name)) { id->name.valid = 1; } ast_free(id->number.str); id->number.str = ast_strdup(cid_num); if (!ast_strlen_zero(cid_num)) { id->number.valid = 1; } }
static struct ast_sip_aor *find_aor(struct ast_sip_endpoint *endpoint, pjsip_uri *uri) { char *configured_aors, *aor_name; pjsip_sip_uri *sip_uri; char *domain_name; RAII_VAR(struct ast_str *, id, NULL, ast_free); if (ast_strlen_zero(endpoint->aors)) { return NULL; } sip_uri = pjsip_uri_get_uri(uri); domain_name = ast_alloca(sip_uri->host.slen + 1); ast_copy_pj_str(domain_name, &sip_uri->host, sip_uri->host.slen + 1); configured_aors = ast_strdupa(endpoint->aors); /* Iterate the configured AORs to see if the user or the user+domain match */ while ((aor_name = strsep(&configured_aors, ","))) { struct ast_sip_domain_alias *alias = NULL; if (!pj_strcmp2(&sip_uri->user, aor_name)) { break; } if (!id && !(id = ast_str_create(sip_uri->user.slen + sip_uri->host.slen + 2))) { return NULL; } ast_str_set(&id, 0, "%.*s@", (int)sip_uri->user.slen, sip_uri->user.ptr); if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { ast_str_append(&id, 0, "%s", alias->domain); ao2_cleanup(alias); } else { ast_str_append(&id, 0, "%s", domain_name); } if (!strcmp(aor_name, ast_str_buffer(id))) { ast_free(id); break; } } if (ast_strlen_zero(aor_name)) { return NULL; } return ast_sip_location_retrieve_aor(aor_name); }
/*! \brief Function which negotiates an incoming media stream */ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream) { char host[NI_MAXHOST]; RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr); enum ast_format_type media_type = stream_to_media_type(session_media->stream_type); /* If no type formats have been configured reject this stream */ if (!ast_format_cap_has_type(session->endpoint->media.codecs, media_type)) { return 0; } /* Ensure incoming transport is compatible with the endpoint's configuration */ if (!session->endpoint->media.rtp.use_received_transport && check_endpoint_media_transport(session->endpoint, stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) { return -1; } ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ return -1; } /* Using the connection information create an appropriate RTP instance */ if (!session_media->rtp && create_rtp(session, session_media, ast_sockaddr_is_ipv6(addrs))) { return -1; } if (session->endpoint->media.rtp.use_received_transport) { pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport); } if (setup_media_encryption(session, session_media, stream)) { return -1; } if (set_caps(session, session_media, stream)) { return -1; } if (media_type == AST_FORMAT_TYPE_AUDIO) { apply_packetization(session, session_media, stream); } return 1; }
static int find_challenge(const pjsip_rx_data *rdata, const struct ast_sip_auth *auth) { struct pjsip_authorization_hdr *auth_hdr = (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr; int challenge_found = 0; char nonce[64]; while ((auth_hdr = (pjsip_authorization_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, auth_hdr->next))) { ast_copy_pj_str(nonce, &auth_hdr->credential.digest.nonce, sizeof(nonce)); if (check_nonce(nonce, rdata, auth) && !pj_strcmp2(&auth_hdr->credential.digest.realm, auth->realm)) { challenge_found = 1; break; } } return challenge_found; }
static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr **addrs) { pjsip_sip_uri *sip_uri; char host[256]; if (!contact || contact->star) { *addrs = NULL; return 0; } if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { *addrs = NULL; return 0; } sip_uri = pjsip_uri_get_uri(contact->uri); ast_copy_pj_str(host, &sip_uri->host, sizeof(host)); return ast_sockaddr_resolve(addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC); }
/*! * \internal * \brief Determine where in the dialplan a call should go * * \details This uses the username in the request URI to try to match * an extension in an endpoint's context in order to route the call. * * \param rdata The SIP request * \param context The context to use * \param exten The extension to use */ static enum pjsip_status_code get_destination(const pjsip_rx_data *rdata, const char *context, char *exten) { pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri; pjsip_sip_uri *sip_ruri; if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { return PJSIP_SC_UNSUPPORTED_URI_SCHEME; } sip_ruri = pjsip_uri_get_uri(ruri); ast_copy_pj_str(exten, &sip_ruri->user, MAX_EXTEN_SIZE); if (ast_exists_extension(NULL, context, exten, 1, NULL)) { return PJSIP_SC_OK; } return PJSIP_SC_NOT_FOUND; }
static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) { RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); pjsip_uri *ruri; pjsip_sip_uri *sip_ruri; char exten[AST_MAX_EXTENSION]; if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_options_method)) { return PJ_FALSE; } if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) { return PJ_FALSE; } ruri = rdata->msg_info.msg->line.req.uri; if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { send_options_response(rdata, 416); return PJ_TRUE; } sip_ruri = pjsip_uri_get_uri(ruri); ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten)); /* * We may want to match in the dialplan without any user * options getting in the way. */ AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(exten); if (ast_shutting_down()) { /* * Not taking any new calls at this time. * Likely a server availability OPTIONS poll. */ send_options_response(rdata, 503); } else if (!ast_strlen_zero(exten) && !ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) { send_options_response(rdata, 404); } else { send_options_response(rdata, 200); } return PJ_TRUE; }
/*! \brief Function which updates the media stream with external media address, if applicable */ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport) { char host[NI_MAXHOST]; struct ast_sockaddr addr = { { 0, } }; /* If the stream has been rejected there will be no connection line */ if (!stream->conn) { return; } ast_copy_pj_str(host, &stream->conn->addr, sizeof(host)); ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID); /* Is the address within the SDP inside the same network? */ if (ast_apply_ha(transport->localnet, &addr) == AST_SENSE_ALLOW) { return; } pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address); }
/*! \brief Function which negotiates an incoming media stream */ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream) { struct t38_state *state; char host[NI_MAXHOST]; RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); if (!session->endpoint->media.t38.enabled) { ast_debug(3, "Declining; T.38 not enabled on session\n"); return -1; } if (!(state = t38_state_get_or_alloc(session))) { return -1; } if ((session->t38state == T38_REJECTED) || (session->t38state == T38_DISABLED)) { ast_debug(3, "Declining; T.38 state is rejected or declined\n"); t38_change_state(session, session_media, state, T38_DISABLED); return -1; } ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_INET) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ ast_debug(3, "Declining; provided host is invalid\n"); return -1; } /* Check the address family to make sure it matches configured */ if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.t38.ipv6) || (ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.t38.ipv6)) { /* The address does not match configured */ ast_debug(3, "Declining, provided host does not match configured address family\n"); return -1; } return 1; }
static char *assign_uuid(const pj_str_t *call_id, const pj_str_t *local_tag, const pj_str_t *remote_tag) { RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); pjsip_dialog *dlg; char *uuid = NULL; if ((dlg = pjsip_ua_find_dialog(call_id, local_tag, remote_tag, PJ_FALSE)) && (session = ast_sip_dialog_get_session(dlg)) && (session->channel)) { uuid = ast_strdup(ast_channel_name(session->channel)); } else { uuid = ast_malloc(pj_strlen(call_id) + 1); if (uuid) { ast_copy_pj_str(uuid, call_id, pj_strlen(call_id) + 1); } } return uuid; }
/*! * \internal * \brief Converts a pjsip_rx_data structure to an ast_msg structure. * * \details Attempts to fill in as much information as possible into the given * msg structure copied from the given request data. * * \param rdata The SIP request * \param msg The asterisk message structure to fill in. */ static enum pjsip_status_code rx_data_to_ast_msg(pjsip_rx_data *rdata, struct ast_msg *msg) { RAII_VAR(struct ast_sip_endpoint *, endpt, NULL, ao2_cleanup); pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri; pjsip_sip_uri *sip_ruri; pjsip_name_addr *name_addr; char buf[MAX_BODY_SIZE]; const char *field; const char *context; char exten[AST_MAX_EXTENSION]; int res = 0; int size; if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { return PJSIP_SC_UNSUPPORTED_URI_SCHEME; } sip_ruri = pjsip_uri_get_uri(ruri); ast_copy_pj_str(exten, &sip_ruri->user, AST_MAX_EXTENSION); endpt = ast_pjsip_rdata_get_endpoint(rdata); ast_assert(endpt != NULL); context = S_OR(endpt->message_context, endpt->context); res |= ast_msg_set_context(msg, "%s", context); res |= ast_msg_set_exten(msg, "%s", exten); /* to header */ name_addr = (pjsip_name_addr *)rdata->msg_info.to->uri; size = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, name_addr, buf, sizeof(buf) - 1); if (size <= 0) { return PJSIP_SC_INTERNAL_SERVER_ERROR; } buf[size] = '\0'; res |= ast_msg_set_to(msg, "%s", sip_to_pjsip(buf, ++size, sizeof(buf) - 1)); /* from header */ name_addr = (pjsip_name_addr *)rdata->msg_info.from->uri; size = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, name_addr, buf, sizeof(buf) - 1); if (size <= 0) { return PJSIP_SC_INTERNAL_SERVER_ERROR; } buf[size] = '\0'; res |= ast_msg_set_from(msg, "%s", buf); field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf) - 1, 1); res |= ast_msg_set_var(msg, "PJSIP_RECVADDR", field); if (print_body(rdata, buf, sizeof(buf) - 1) > 0) { res |= ast_msg_set_body(msg, "%s", buf); } /* endpoint name */ res |= ast_msg_set_tech(msg, "%s", "PJSIP"); res |= ast_msg_set_endpoint(msg, "%s", ast_sorcery_object_get_id(endpt)); if (endpt->id.self.name.valid) { res |= ast_msg_set_var(msg, "PJSIP_ENDPOINT", endpt->id.self.name.str); } res |= headers_to_vars(rdata, msg); return !res ? PJSIP_SC_OK : PJSIP_SC_INTERNAL_SERVER_ERROR; }
/*! \brief Function which negotiates an incoming media stream */ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream) { char host[NI_MAXHOST]; RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE; int res; /* If port is 0, ignore this media stream */ if (!stream->desc.port) { ast_debug(3, "Media stream '%s' is already declined\n", session_media->stream_type); return 0; } /* If no type formats have been configured reject this stream */ if (!ast_format_cap_has_type(session->endpoint->media.codecs, media_type)) { ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", session_media->stream_type); return 0; } /* 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); if (encryption == AST_SIP_MEDIA_TRANSPORT_INVALID) { return -1; } } ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ return -1; } /* Using the connection information create an appropriate RTP instance */ if (!session_media->rtp && create_rtp(session, session_media, ast_sockaddr_is_ipv6(addrs))) { return -1; } res = setup_media_encryption(session, session_media, sdp, stream); if (res) { if (!session->endpoint->media.rtp.encryption_optimistic) { /* If optimistic encryption is disabled and crypto should have been enabled * but was not this session must fail. */ return -1; } /* There is no encryption, sad. */ session_media->encryption = AST_SIP_MEDIA_ENCRYPT_NONE; } /* If we've been explicitly configured to use the received transport OR if * encryption is on and crypto is present use the received transport. * This is done in case of optimistic because it may come in as RTP/AVP or RTP/SAVP depending * on the configuration of the remote endpoint (optimistic themselves or mandatory). */ if ((session->endpoint->media.rtp.use_received_transport) || ((encryption == AST_SIP_MEDIA_ENCRYPT_SDES) && !res)) { pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport); } if (set_caps(session, session_media, stream)) { return 0; } return 1; }
/*! \brief Function which processes ICE attributes in an audio stream */ static void process_ice_attributes(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream) { struct ast_rtp_engine_ice *ice; const pjmedia_sdp_attr *attr; char attr_value[256]; unsigned int attr_i; /* If ICE support is not enabled or available exit early */ if (!session->endpoint->media.rtp.ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp))) { return; } attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-ufrag", NULL); if (!attr) { attr = pjmedia_sdp_attr_find2(remote->attr_count, remote->attr, "ice-ufrag", NULL); } if (attr) { ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value)); ice->set_authentication(session_media->rtp, attr_value, NULL); } else { return; } attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-pwd", NULL); if (!attr) { attr = pjmedia_sdp_attr_find2(remote->attr_count, remote->attr, "ice-pwd", NULL); } if (attr) { ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value)); ice->set_authentication(session_media->rtp, NULL, attr_value); } else { return; } if (pjmedia_sdp_media_find_attr2(remote_stream, "ice-lite", NULL)) { ice->ice_lite(session_media->rtp); } /* Find all of the candidates */ for (attr_i = 0; attr_i < remote_stream->attr_count; ++attr_i) { char foundation[32], transport[32], address[PJ_INET6_ADDRSTRLEN + 1], cand_type[6], relay_address[PJ_INET6_ADDRSTRLEN + 1] = ""; unsigned int port, relay_port = 0; struct ast_rtp_engine_ice_candidate candidate = { 0, }; attr = remote_stream->attr[attr_i]; /* If this is not a candidate line skip it */ if (pj_strcmp2(&attr->name, "candidate")) { continue; } ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value)); if (sscanf(attr_value, "%31s %30u %31s %30u %46s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport, (unsigned *)&candidate.priority, address, &port, cand_type, relay_address, &relay_port) < 7) { /* Candidate did not parse properly */ continue; } candidate.foundation = foundation; candidate.transport = transport; ast_sockaddr_parse(&candidate.address, address, PARSE_PORT_FORBID); ast_sockaddr_set_port(&candidate.address, port); if (!strcasecmp(cand_type, "host")) { candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST; } else if (!strcasecmp(cand_type, "srflx")) { candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX; } else if (!strcasecmp(cand_type, "relay")) { candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED; } else { continue; } if (!ast_strlen_zero(relay_address)) { ast_sockaddr_parse(&candidate.relay_address, relay_address, PARSE_PORT_FORBID); } if (relay_port) { ast_sockaddr_set_port(&candidate.relay_address, relay_port); } ice->add_remote_candidate(session_media->rtp, &candidate); } ice->set_role(session_media->rtp, pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_TRUE ? AST_RTP_ICE_ROLE_CONTROLLING : AST_RTP_ICE_ROLE_CONTROLLED); ice->start(session_media->rtp); }
static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs, struct ast_sip_session_media *session_media) { pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; pjmedia_sdp_fmtp fmtp; struct ast_format *format; int i, num = 0, tel_event = 0; char name[256]; char media[20]; char fmt_param[256]; enum ast_rtp_options options = session->endpoint->media.g726_non_standard ? AST_RTP_OPT_G726_NONSTANDARD : 0; ast_rtp_codecs_payloads_initialize(codecs); /* Iterate through provided formats */ for (i = 0; i < stream->desc.fmt_count; ++i) { /* The payload is kept as a string for things like t38 but for video it is always numerical */ ast_rtp_codecs_payloads_set_m_type(codecs, NULL, pj_strtoul(&stream->desc.fmt[i])); /* Look for the optional rtpmap attribute */ if (!(attr = pjmedia_sdp_media_find_attr2(stream, "rtpmap", &stream->desc.fmt[i]))) { continue; } /* Interpret the attribute as an rtpmap */ if ((pjmedia_sdp_attr_to_rtpmap(session->inv_session->pool_prov, attr, &rtpmap)) != PJ_SUCCESS) { continue; } ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name)); if (strcmp(name,"telephone-event") == 0) { tel_event++; } ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media)); ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, pj_strtoul(&stream->desc.fmt[i]), media, name, options, rtpmap->clock_rate); /* Look for an optional associated fmtp attribute */ if (!(attr = pjmedia_sdp_media_find_attr2(stream, "fmtp", &rtpmap->pt))) { continue; } if ((pjmedia_sdp_attr_get_fmtp(attr, &fmtp)) == PJ_SUCCESS) { ast_copy_pj_str(fmt_param, &fmtp.fmt, sizeof(fmt_param)); if (sscanf(fmt_param, "%30d", &num) != 1) { continue; } if ((format = ast_rtp_codecs_get_payload_format(codecs, num))) { struct ast_format *format_parsed; ast_copy_pj_str(fmt_param, &fmtp.fmt_param, sizeof(fmt_param)); format_parsed = ast_format_parse_sdp_fmtp(format, fmt_param); if (format_parsed) { ast_rtp_codecs_payload_replace_format(codecs, num, format_parsed); ao2_ref(format_parsed, -1); } ao2_ref(format, -1); } } } if ((tel_event==0) && (session->endpoint->dtmf == AST_SIP_DTMF_AUTO)) { ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND); } /* Get the packetization, if it exists */ if ((attr = pjmedia_sdp_media_find_attr2(stream, "ptime", NULL))) { unsigned long framing = pj_strtoul(pj_strltrim(&attr->value)); if (framing && session->endpoint->media.rtp.use_ptime) { ast_rtp_codecs_set_framing(codecs, framing); } } }
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) { struct ast_sip_endpoint *endpoint; struct unidentified_request *unid; int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; if (endpoint) { /* * ao2_find with OBJ_UNLINK always write locks the container before even searching * for the object. Since the majority case is that the object won't be found, do * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink * if needed. */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } apply_acls(rdata); return PJ_FALSE; } endpoint = ast_sip_identify_endpoint(rdata); if (endpoint) { unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { ao2_unlink(unidentified_requests, unid); ao2_ref(unid, -1); } } if (!endpoint) { /* always use an artificial endpoint - per discussion no reason to have "alwaysauthreject" as an option. It is felt using it was a bug fix and it is not needed since we are not worried about breaking old stuff and we really don't want to enable the discovery of SIP accounts */ endpoint = ast_sip_get_artificial_endpoint(); } /* endpoint ref held by mod_data[] */ rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint; if (endpoint == artificial_endpoint && !is_ack) { char name[AST_UUID_STR_LEN] = ""; pjsip_uri *from = rdata->msg_info.from->uri; if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) { pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from); ast_copy_pj_str(name, &sip_from->user, sizeof(name)); } unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY); if (unid) { check_endpoint(rdata, unid, name); ao2_ref(unid, -1); } else if (using_auth_username) { ao2_wrlock(unidentified_requests); /* Checking again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */ unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (unid) { check_endpoint(rdata, unid, name); } else { unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1, NULL, AO2_ALLOC_OPT_LOCK_RWLOCK); if (!unid) { ao2_unlock(unidentified_requests); pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */ unid->first_seen = ast_tvnow(); unid->count = 1; ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK); } ao2_ref(unid, -1); ao2_unlock(unidentified_requests); } else { log_failed_request(rdata, "No matching endpoint found", 0, 0); ast_sip_report_invalid_endpoint(name, rdata); } } apply_acls(rdata); return PJ_FALSE; }
static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) { RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup); struct rx_task_data *task_data; RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); pjsip_sip_uri *uri; char user_name[64], domain_name[64]; char *configured_aors, *aor_name; if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) { return PJ_FALSE; } if (ast_strlen_zero(endpoint->aors)) { /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors"); ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received"); ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } uri = pjsip_uri_get_uri(rdata->msg_info.to->uri); ast_copy_pj_str(user_name, &uri->user, sizeof(user_name)); ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name)); configured_aors = ast_strdupa(endpoint->aors); /* Iterate the configured AORs to see if the user or the user+domain match */ while ((aor_name = strsep(&configured_aors, ","))) { char id[AST_UUID_STR_LEN]; RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup); snprintf(id, sizeof(id), "%s@%s", user_name, domain_name); if (!strcmp(aor_name, id)) { break; } if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain); if (!strcmp(aor_name, id)) { break; } } if (!strcmp(aor_name, user_name)) { break; } } if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) { /* The provided AOR name was not found (be it within the configuration or sorcery itself) */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found"); ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", user_name, ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!aor->max_contacts) { /* Registration is not permitted for this AOR */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted"); ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n", ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (!(ser = serializer_find_or_create(aor_name))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); return PJ_TRUE; } if (ast_sip_push_task(ser->serializer, rx_task, task_data)) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_mem_limit(endpoint, rdata); ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n", ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); ao2_ref(task_data, -1); } return PJ_TRUE; }
static struct ast_sip_aor *find_registrar_aor(struct pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint) { struct ast_sip_aor *aor = NULL; char *aor_name = NULL; char *domain_name; char *username = NULL; int i; for (i = 0; i < AST_VECTOR_SIZE(&endpoint->ident_method_order); ++i) { pjsip_sip_uri *uri; pjsip_authorization_hdr *header = NULL; switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) { case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME: uri = pjsip_uri_get_uri(rdata->msg_info.to->uri); domain_name = ast_alloca(uri->host.slen + 1); ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1); username = ast_alloca(uri->user.slen + 1); ast_copy_pj_str(username, &uri->user, uri->user.slen + 1); /* * We may want to match without any user options getting * in the way. */ AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(username); aor_name = find_aor_name(username, domain_name, endpoint->aors); if (aor_name) { ast_debug(3, "Matched aor '%s' by To username\n", aor_name); } break; case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME: while ((header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, header ? header->next : NULL))) { if (header && !pj_stricmp2(&header->scheme, "digest")) { username = ast_alloca(header->credential.digest.username.slen + 1); ast_copy_pj_str(username, &header->credential.digest.username, header->credential.digest.username.slen + 1); domain_name = ast_alloca(header->credential.digest.realm.slen + 1); ast_copy_pj_str(domain_name, &header->credential.digest.realm, header->credential.digest.realm.slen + 1); aor_name = find_aor_name(username, domain_name, endpoint->aors); if (aor_name) { ast_debug(3, "Matched aor '%s' by Authentication username\n", aor_name); break; } } } break; default: continue; } if (aor_name) { break; } } if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) { /* The provided AOR name was not found (be it within the configuration or sorcery itself) */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL); ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found"); ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", username ?: "", ast_sorcery_object_get_id(endpoint)); }
static int register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name, struct ao2_container *contacts) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; int added = 0, updated = 0, deleted = 0; pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; pjsip_via_hdr *via_hdr; pjsip_via_hdr *via_hdr_last; char *via_addr = NULL; int via_port = 0; pjsip_cid_hdr *call_id_hdr; char *call_id = NULL; size_t alloc_size; /* So we don't count static contacts against max_contacts we prune them out from the container */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL); if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL); 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)); return PJ_TRUE; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 420, NULL, NULL, NULL); ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) { /* Enforce the maximum number of contacts */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts); return PJ_TRUE; } if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Find the first Via header */ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL); if (via_hdr) { /* Find the last Via header */ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, via_hdr->next)) != NULL) { via_hdr_last = via_hdr; } alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1; via_addr = ast_alloca(alloc_size); ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size); via_port=via_hdr_last->sent_by.port; } call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL); if (call_id_hdr) { alloc_size = pj_strlen(&call_id_hdr->id) + 1; call_id = ast_alloca(alloc_size); ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { int expiration; char contact_uri[pjsip_max_url_size]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(aor, contact_hdr, rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, via_addr, via_port, call_id, endpoint)) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = aor->qualify_frequency; contact_update->authenticate_qualify = aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) { ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sip_location_delete_contact(contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_cleanup(contact_update); } else { /* We want to report the user agent that was actually in the removed contact */ ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, contact->user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER * do so */ if (aor->remove_existing) { ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); } /* Re-retrieve contacts. Caller will clean up the original container. */ contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor); response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); ao2_cleanup(contacts); return PJ_TRUE; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); ao2_cleanup(contacts); 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)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } ast_sip_send_stateful_response(rdata, tdata, endpoint); return PJ_TRUE; }
static void register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name, struct ao2_container *contacts, struct aor_core_response *response) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; int added = 0; int updated = 0; int deleted = 0; int permanent = 0; int contact_count; struct ao2_container *existing_contacts = NULL; pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; pjsip_via_hdr *via_hdr; pjsip_via_hdr *via_hdr_last; char *via_addr = NULL; int via_port = 0; pjsip_cid_hdr *call_id_hdr; char *call_id = NULL; size_t alloc_size; /* 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(), "Contact Comparison", 1024, 256); if (!details.pool) { response->code = 500; return; } /* If there are any permanent contacts configured on the AOR we need to take them * into account when counting contacts. */ if (aor->permanent_contacts) { permanent = ao2_container_count(aor->permanent_contacts); } if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) { /* 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; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); response->code = 420; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (aor->remove_existing) { /* Cumulative number of contacts affected by this registration */ contact_count = MAX(updated + added - deleted, 0); /* We need to keep track of only existing contacts so we can later * remove them if need be. */ existing_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, ast_sorcery_object_id_compare); if (!existing_contacts) { response->code = 500; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts); } else { /* Total contacts after this registration */ contact_count = ao2_container_count(contacts) - permanent + added - deleted; } if (contact_count > aor->max_contacts) { /* Enforce the maximum number of contacts */ ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts); response->code = 403; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); ao2_cleanup(existing_contacts); return; } user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Find the first Via header */ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL); if (via_hdr) { /* Find the last Via header */ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, via_hdr->next)) != NULL) { via_hdr_last = via_hdr; } alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1; via_addr = ast_alloca(alloc_size); ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size); via_port=via_hdr_last->sent_by.port; } call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL); if (call_id_hdr) { alloc_size = pj_strlen(&call_id_hdr->id) + 1; call_id = ast_alloca(alloc_size); ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ for (; (contact_hdr = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next)); pj_pool_reset(details.pool)) { int expiration; char contact_uri[pjsip_max_url_size]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); /* If we are keeping track of existing contacts for removal then, well, there is * absolutely nothing left so no need to try to remove any. */ if (existing_contacts) { ao2_ref(existing_contacts, -1); existing_contacts = NULL; } break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(aor, contact_hdr, rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details); /* If a contact was returned and we need to keep track of existing contacts then it * should be removed. */ if (contact && existing_contacts) { ao2_unlink(existing_contacts, contact); } if (!contact) { int prune_on_boot; /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata); contact = ast_sip_location_create_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint); if (!contact) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } if (prune_on_boot) { const char *contact_name; struct contact_transport_monitor *monitor; /* * Monitor the transport in case it gets disconnected because * the contact won't be valid anymore if that happens. */ contact_name = ast_sorcery_object_get_id(contact); monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (monitor) { strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_register(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor); ao2_ref(monitor, -1); } } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); ao2_link(contacts, contact); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = aor->qualify_frequency; contact_update->authenticate_qualify = aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) { ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sip_location_delete_contact(contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_link(contacts, contact_update); ao2_cleanup(contact_update); } else { if (contact->prune_on_boot) { struct contact_transport_monitor *monitor; const char *contact_name = ast_sorcery_object_get_id(contact); monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name)); strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_unregister(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher); } /* We want to report the user agent that was actually in the removed contact */ ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, contact->user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* * If the AOR is configured to remove any contacts over max_contacts * that have not been updated/added/deleted as a result of this * REGISTER do so. * * The existing contacts container holds all contacts that were not * involved in this REGISTER. * The contacts container holds the current contacts of the AOR. */ if (aor->remove_existing && existing_contacts) { /* Total contacts after this registration */ contact_count = ao2_container_count(existing_contacts) + updated + added; if (contact_count > aor->max_contacts) { /* Remove excess existing contacts that expire the soonest */ remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts); } ao2_ref(existing_contacts, -1); } response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); ao2_cleanup(contacts); response->code = 500; return; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); 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)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } response->tdata = tdata; }