static pj_ssize_t pjsip_name_addr_print(pjsip_uri_context_e context, const pjsip_name_addr *name, char *buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf + size; pjsip_uri *uri; uri = (pjsip_uri*) pjsip_uri_get_uri(name->uri); pj_assert(uri != NULL); if (context != PJSIP_URI_IN_REQ_URI) { if (name->display.slen) { if (endbuf-buf < 8) return -1; *buf++ = '"'; copy_advance(buf, name->display); *buf++ = '"'; *buf++ = ' '; } *buf++ = '<'; } printed = pjsip_uri_print(context,uri, buf, size-(buf-startbuf)); if (printed < 1) return -1; buf += printed; if (context != PJSIP_URI_IN_REQ_URI) { *buf++ = '>'; } *buf = '\0'; return buf-startbuf; }
/*! * \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) { #define CHECK_RES(z_) do { if (z_) { ast_msg_destroy(msg); \ return PJSIP_SC_INTERNAL_SERVER_ERROR; } } while (0) int size; char buf[MAX_BODY_SIZE]; pjsip_name_addr *name_addr; const char *field; pjsip_status_code code; struct ast_sip_endpoint *endpt = ast_pjsip_rdata_get_endpoint(rdata); const char *context = S_OR(endpt->message_context, endpt->context); /* make sure there is an appropriate context and extension*/ if ((code = get_destination(rdata, context, buf)) != PJSIP_SC_OK) { return code; } CHECK_RES(ast_msg_set_context(msg, "%s", context)); CHECK_RES(ast_msg_set_exten(msg, "%s", buf)); /* to header */ name_addr = (pjsip_name_addr *)rdata->msg_info.to->uri; if ((size = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, name_addr, buf, sizeof(buf)-1)) > 0) { buf[size] = '\0'; /* prepend the tech */ CHECK_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; if ((size = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, name_addr, buf, sizeof(buf)-1)) > 0) { buf[size] = '\0'; CHECK_RES(ast_msg_set_from(msg, "%s", buf)); } /* receive address */ field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf)-1, 1); CHECK_RES(ast_msg_set_var(msg, "PJSIP_RECVADDR", field)); /* body */ if (print_body(rdata, buf, sizeof(buf) - 1) > 0) { CHECK_RES(ast_msg_set_body(msg, "%s", buf)); } /* endpoint name */ if (endpt->id.self.name.valid) { CHECK_RES(ast_msg_set_var(msg, "PJSIP_PEERNAME", endpt->id.self.name.str)); } CHECK_RES(headers_to_vars(rdata, msg)); return PJSIP_SC_OK; }
bool SIPCall::attendedTransfer(const std::string& to) { const auto toCall = Manager::instance().callFactory.getCall<SIPCall>(to); if (!toCall) return false; if (not toCall->inv or not toCall->inv->dlg) return false; pjsip_dialog *target_dlg = toCall->inv->dlg; pjsip_uri *uri = (pjsip_uri*) pjsip_uri_get_uri(target_dlg->remote.info->uri); char str_dest_buf[PJSIP_MAX_URL_SIZE * 2] = { '<' }; pj_str_t dst = { str_dest_buf, 1 }; dst.slen += pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, str_dest_buf + 1, sizeof(str_dest_buf) - 1); dst.slen += pj_ansi_snprintf(str_dest_buf + dst.slen, sizeof(str_dest_buf) - dst.slen, "?" "Replaces=%.*s" "%%3Bto-tag%%3D%.*s" "%%3Bfrom-tag%%3D%.*s>", (int)target_dlg->call_id->id.slen, target_dlg->call_id->id.ptr, (int)target_dlg->remote.info->tag.slen, target_dlg->remote.info->tag.ptr, (int)target_dlg->local.info->tag.slen, target_dlg->local.info->tag.ptr); return transferCommon(&dst); }
/* * Create message body. */ static pj_status_t pres_create_msg_body( pjsip_pres *pres, pjsip_tx_data *tdata) { pj_str_t entity; /* Get publisher URI */ entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->dlg->local.info->uri, entity.ptr, PJSIP_MAX_URL_SIZE); if (entity.slen < 1) return PJ_ENOMEM; if (pres->content_type == CONTENT_TYPE_PIDF) { return pjsip_pres_create_pidf(tdata->pool, &pres->status, &entity, &tdata->msg->body); } else if (pres->content_type == CONTENT_TYPE_XPIDF) { return pjsip_pres_create_xpidf(tdata->pool, &pres->status, &entity, &tdata->msg->body); } else { return PJSIP_SIMPLE_EBADCONTENT; } }
int identity_hdr_print(pjsip_routing_hdr* hdr, char* buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf + size; const pjsip_parser_const_t *pc = pjsip_parser_const(); /* Route and Record-Route don't compact forms */ copy_advance(buf, hdr->name); *buf++ = ':'; *buf++ = ' '; printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf); if (printed < 1) { return -1; // LCOV_EXCL_LINE } buf += printed; printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) { return -1; // LCOV_EXCL_LINE } buf += printed; return buf-startbuf; }
pj_str_t PJUtils::uri_to_pj_str(pjsip_uri_context_e context, const pjsip_uri* uri, pj_pool_t* pool) { pj_str_t s; char buf[500]; s.slen = pjsip_uri_print(context, uri, buf, sizeof(buf)); s.ptr = (char*)pj_pool_alloc(pool, s.slen); memcpy(s.ptr, buf, s.slen); return s; }
std::string PJUtils::uri_to_string(pjsip_uri_context_e context, const pjsip_uri* uri) { int uri_clen = 0; char uri_cstr[500]; if (uri != NULL) { uri_clen = pjsip_uri_print(context, uri, uri_cstr, sizeof(uri_cstr)); } return std::string(uri_cstr, uri_clen); }
/* Callback when transaction state has changed. */ static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) { pjsua_buddy *buddy; pjsip_contact_hdr *contact_hdr; PJSUA_LOCK(); buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); if (!buddy) { PJSUA_UNLOCK(); return; } /* We only use this to update buddy's Contact, when it's not * set. */ if (buddy->contact.slen != 0) { /* Contact already set */ PJSUA_UNLOCK(); return; } /* Only care about 2xx response to outgoing SUBSCRIBE */ if (tsx->status_code/100 != 2 || tsx->role != PJSIP_UAC_ROLE || event->type != PJSIP_EVENT_RX_MSG || pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0) { PJSUA_UNLOCK(); return; } /* Find contact header. */ contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (!contact_hdr) { PJSUA_UNLOCK(); return; } buddy->contact.ptr = (char*) pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE); buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri, buddy->contact.ptr, PJSIP_MAX_URL_SIZE); if (buddy->contact.slen < 0) buddy->contact.slen = 0; PJSUA_UNLOCK(); }
pjsip_redirect_op on_call_redirected_wrapper (pjsua_call_id call_id, const pjsip_uri *target, const pjsip_event *e) { char uristr[PJSIP_MAX_URL_SIZE]; int len; pj_str_t uri_pstr; len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr, sizeof(uristr)); if (len < 1) { pj_ansi_strcpy(uristr, "--URI too long--"); } uri_pstr = pj_str(uristr); return registeredCallbackObject->on_call_redirected(call_id, &uri_pstr); }
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); } }
/* This is called when request is received. * We need to check for incoming SUBSCRIBE request. */ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata) { int acc_id; pjsua_acc *acc; pj_str_t contact; pjsip_method *req_method = &rdata->msg_info.msg->line.req.method; pjsua_srv_pres *uapres; pjsip_evsub *sub; pjsip_evsub_user pres_cb; pjsip_dialog *dlg; pjsip_status_code st_code; pj_str_t reason; pjsip_expires_hdr *expires_hdr; pjsua_msg_data msg_data; pj_status_t status; if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0) return PJ_FALSE; /* Incoming SUBSCRIBE: */ PJSUA_LOCK(); /* Find which account for the incoming request. */ acc_id = pjsua_acc_find_for_incoming(rdata); acc = &pjsua_var.acc[acc_id]; PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d", acc_id)); /* Create suitable Contact header */ if (acc->contact.slen) { contact = acc->contact; } else { status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact, acc_id, rdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); PJSUA_UNLOCK(); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL, NULL, NULL); return PJ_TRUE; } } /* Create UAS dialog: */ status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create UAS dialog for subscription", status); PJSUA_UNLOCK(); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL, NULL, NULL); return PJ_TRUE; } /* Set credentials and preference. */ pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred); pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref); /* Init callback: */ pj_bzero(&pres_cb, sizeof(pres_cb)); pres_cb.on_evsub_state = &pres_evsub_on_srv_state; /* Create server presence subscription: */ status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub); if (status != PJ_SUCCESS) { int code = PJSIP_ERRNO_TO_SIP_STATUS(status); pjsip_tx_data *tdata; pjsua_perror(THIS_FILE, "Unable to create server subscription", status); if (code==599 || code > 699 || code < 300) { code = 400; } status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata); if (status == PJ_SUCCESS) { status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); } PJSUA_UNLOCK(); return PJ_TRUE; } /* If account is locked to specific transport, then lock dialog * to this transport too. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_dlg_set_transport(dlg, &tp_sel); } /* Attach our data to the subscription: */ uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres); uapres->sub = sub; uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE); uapres->acc_id = acc_id; uapres->dlg = dlg; status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, uapres->remote, PJSIP_MAX_URL_SIZE); if (status < 1) pj_ansi_strcpy(uapres->remote, "<-- url is too long-->"); else uapres->remote[status] = '\0'; pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres); /* Add server subscription to the list: */ pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres); /* Capture the value of Expires header. */ expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); if (expires_hdr) uapres->expires = expires_hdr->ivalue; else uapres->expires = -1; st_code = (pjsip_status_code)200; reason = pj_str("OK"); pjsua_msg_data_init(&msg_data); /* Notify application callback, if any */ if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) { pjsua_buddy_id buddy_id; buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri); (*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id, &dlg->remote.info_str, rdata, &st_code, &reason, &msg_data); } /* Handle rejection case */ if (st_code >= 300) { pjsip_tx_data *tdata; /* Create response */ status = pjsip_dlg_create_response(dlg, rdata, st_code, &reason, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating response", status); pj_list_erase(uapres); pjsip_pres_terminate(sub, PJ_FALSE); PJSUA_UNLOCK(); return PJ_FALSE; } /* Add header list, if any */ pjsua_process_msg_data(tdata, &msg_data); /* Send the response */ status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error sending response", status); /* This is not fatal */ } /* Terminate presence subscription */ pj_list_erase(uapres); pjsip_pres_terminate(sub, PJ_FALSE); PJSUA_UNLOCK(); return PJ_TRUE; } /* Create and send 2xx response to the SUBSCRIBE request: */ status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to accept presence subscription", status); pj_list_erase(uapres); pjsip_pres_terminate(sub, PJ_FALSE); PJSUA_UNLOCK(); return PJ_FALSE; } /* If code is 200, send NOTIFY now */ if (st_code == 200) { pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE, NULL, NULL, PJ_TRUE, &msg_data); } /* Done: */ PJSUA_UNLOCK(); return PJ_TRUE; }
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; }
/* Initialize outgoing request. */ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata ) { const pjsip_method *method; pjsip_cached_auth *auth; pjsip_hdr added; PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Init list */ pj_list_init(&added); /* Get the method. */ method = &tdata->msg->line.req.method; auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { /* Reset stale counter */ auth->stale_cnt = 0; if (auth->qop_value == PJSIP_AUTH_QOP_NONE) { # if defined(PJSIP_AUTH_HEADER_CACHING) && \ PJSIP_AUTH_HEADER_CACHING!=0 { pjsip_cached_auth_hdr *entry = auth->cached_hdr.next; while (entry != &auth->cached_hdr) { if (pjsip_method_cmp(&entry->method, method)==0) { pjsip_authorization_hdr *hauth; hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); break; } entry = entry->next; } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { if (entry == &auth->cached_hdr) new_auth_for_req( tdata, sess, auth, NULL); } # endif } # elif defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { new_auth_for_req( tdata, sess, auth, NULL); } # endif } # if defined(PJSIP_AUTH_QOP_SUPPORT) && \ defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) else if (auth->qop_value == PJSIP_AUTH_QOP_AUTH) { /* For qop="auth", we have to re-create the authorization header. */ const pjsip_cred_info *cred; pjsip_authorization_hdr *hauth; pj_status_t status; cred = auth_find_cred(sess, &auth->realm, &auth->last_chal->scheme); if (!cred) { auth = auth->next; continue; } status = auth_respond( tdata->pool, auth->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, sess->pool, auth, &hauth); if (status != PJ_SUCCESS) return status; //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); } # endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ auth = auth->next; } if (sess->pref.initial_auth == PJ_FALSE) { pjsip_hdr *h; /* Don't want to send initial empty Authorization header, so * just send whatever available in the list (maybe empty). */ h = added.next; while (h != &added) { pjsip_hdr *next = h->next; pjsip_msg_add_hdr(tdata->msg, h); h = next; } } else { /* For each realm, add either the cached authorization header * or add an empty authorization header. */ unsigned i; char *uri_str; int len; uri_str = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri, uri_str, PJSIP_MAX_URL_SIZE); if (len < 1 || len >= PJSIP_MAX_URL_SIZE) return PJSIP_EURITOOLONG; for (i=0; i<sess->cred_cnt; ++i) { pjsip_cred_info *c = &sess->cred_info[i]; pjsip_authorization_hdr *h; h = get_header_for_realm(&added, &c->realm); if (h) { pj_list_erase(h); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h); } else { pjsip_authorization_hdr *hs; hs = pjsip_authorization_hdr_create(tdata->pool); pj_strdup(tdata->pool, &hs->scheme, &c->scheme); pj_strdup(tdata->pool, &hs->credential.digest.username, &c->username); pj_strdup(tdata->pool, &hs->credential.digest.realm, &c->realm); pj_strdup2(tdata->pool, &hs->credential.digest.uri, uri_str); pj_strdup(tdata->pool, &hs->credential.digest.algorithm, &sess->pref.algorithm); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs); } } } return PJ_SUCCESS; }
/* * Create Authorization/Proxy-Authorization response header based on the challege * in WWW-Authenticate/Proxy-Authenticate header. */ static pj_status_t auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **p_h_auth) { pjsip_authorization_hdr *hauth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(req_pool && hdr && uri && cred_info && method && sess_pool && cached_auth && p_h_auth, PJ_EINVAL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); return PJSIP_EURITOOLONG; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) hauth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) hauth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(!"Invalid response header!"); return PJSIP_EINVALIDHDR; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (cached_auth) { update_digest_session( sess_pool, cached_auth, hdr ); cnonce = &cached_auth->cnonce; nc = cached_auth->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ hauth->scheme = pjsip_DIGEST_STR; status = respond_digest( pool, &hauth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (status != PJ_SUCCESS) return status; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && cached_auth) { if (cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &hauth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { cached_auth->qop_value = PJSIP_AUTH_QOP_AUTH; } else { cached_auth->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { return PJSIP_EINVALIDAUTHSCHEME; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (hauth && cached_auth && cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = cached_auth->cached_hdr.next; while (cached_hdr != &cached_auth->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &cached_auth->cached_hdr) { cached_hdr->hdr = hauth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = hauth; pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr ); } } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (hdr != cached_auth->last_chal) { cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr); } # endif } # endif *p_h_auth = hauth; return PJ_SUCCESS; }
/** * Private: process pager message. * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing(). */ void pjsua_im_process_pager(int call_id, const pj_str_t *from, const pj_str_t *to, pjsip_rx_data *rdata) { pjsip_contact_hdr *contact_hdr; pj_str_t contact; pjsip_msg_body *body = rdata->msg_info.msg->body; #if 0 /* Ticket #693: allow incoming MESSAGE without message body */ /* Body MUST have been checked before */ pj_assert(body != NULL); #endif /* Build remote contact */ contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (contact_hdr && contact_hdr->uri) { contact.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri, contact.ptr, PJSIP_MAX_URL_SIZE); } else { contact.slen = 0; } if (body && pj_stricmp(&body->content_type.type, &STR_MIME_APP)==0 && pj_stricmp(&body->content_type.subtype, &STR_MIME_ISCOMPOSING)==0) { /* Expecting typing indication */ pj_status_t status; pj_bool_t is_typing; status = pjsip_iscomposing_parse(rdata->tp_info.pool, (char*)body->data, body->len, &is_typing, NULL, NULL, NULL ); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Invalid MESSAGE body", status); return; } if (pjsua_var.ua_cfg.cb.on_typing) { (*pjsua_var.ua_cfg.cb.on_typing)(call_id, from, to, &contact, is_typing); } if (pjsua_var.ua_cfg.cb.on_typing2) { pjsua_acc_id acc_id; if (call_id == PJSUA_INVALID_ID) { acc_id = pjsua_acc_find_for_incoming(rdata); } else { pjsua_call *call = &pjsua_var.calls[call_id]; acc_id = call->acc_id; } (*pjsua_var.ua_cfg.cb.on_typing2)(call_id, from, to, &contact, is_typing, rdata, acc_id); } } else { pj_str_t mime_type; char buf[256]; pjsip_media_type *m; pj_str_t text_body; /* Save text body */ if (body) { text_body.ptr = (char*)rdata->msg_info.msg->body->data; text_body.slen = rdata->msg_info.msg->body->len; /* Get mime type */ m = &rdata->msg_info.msg->body->content_type; mime_type.ptr = buf; mime_type.slen = pj_ansi_snprintf(buf, sizeof(buf), "%.*s/%.*s", (int)m->type.slen, m->type.ptr, (int)m->subtype.slen, m->subtype.ptr); if (mime_type.slen < 1) mime_type.slen = 0; } else { text_body.ptr = mime_type.ptr = ""; text_body.slen = mime_type.slen = 0; } if (pjsua_var.ua_cfg.cb.on_pager) { (*pjsua_var.ua_cfg.cb.on_pager)(call_id, from, to, &contact, &mime_type, &text_body); } if (pjsua_var.ua_cfg.cb.on_pager2) { pjsua_acc_id acc_id; if (call_id == PJSUA_INVALID_ID) { acc_id = pjsua_acc_find_for_incoming(rdata); } else { pjsua_call *call = &pjsua_var.calls[call_id]; acc_id = call->acc_id; } (*pjsua_var.ua_cfg.cb.on_pager2)(call_id, from, to, &contact, &mime_type, &text_body, rdata, acc_id); } } }
/* * Handler to receive incoming MESSAGE */ static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata) { pj_str_t from, to; pjsip_accept_hdr *accept_hdr; pjsip_msg *msg; msg = rdata->msg_info.msg; /* Only want to handle MESSAGE requests. */ if (pjsip_method_cmp(&msg->line.req.method, &pjsip_message_method) != 0) { return PJ_FALSE; } /* Should not have any transaction attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_FALSE); /* Should not have any dialog attached to rdata. */ PJ_ASSERT_RETURN(pjsip_rdata_get_dlg(rdata)==NULL, PJ_FALSE); /* Check if we can accept the message. */ if (!pjsua_im_accept_pager(rdata, &accept_hdr)) { pjsip_hdr hdr_list; pj_list_init(&hdr_list); pj_list_push_back(&hdr_list, accept_hdr); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, &hdr_list, NULL); return PJ_TRUE; } /* Respond with 200 first, so that remote doesn't retransmit in case * the UI takes too long to process the message. */ pjsip_endpt_respond( pjsua_var.endpt, NULL, rdata, 200, NULL, NULL, NULL, NULL); /* For the source URI, we use Contact header if present, since * Contact header contains the port number information. If this is * not available, then use From header. */ from.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from.ptr, PJSIP_MAX_URL_SIZE); if (from.slen < 1) from = pj_str("<--URI is too long-->"); /* Build the To text. */ to.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); to.slen = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.to->uri, to.ptr, PJSIP_MAX_URL_SIZE); if (to.slen < 1) to = pj_str("<--URI is too long-->"); /* Process pager. */ pjsua_im_process_pager(-1, &from, &to, rdata); /* Done. */ return PJ_TRUE; }
static int rx_task(void *data) { RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup); RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); int added = 0, updated = 0, deleted = 0; pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; pjsip_response_addr addr; const char *aor_name = ast_sorcery_object_get_id(task_data->aor); /* Retrieve the current contacts, we'll need to know whether to update or not */ contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); /* 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(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(task_data->endpoint)); return PJ_TRUE; } if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) { /* Enforce the maximum number of contacts */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %d\n", ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->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(), task_data->rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } /* Iterate each provided Contact header and add, update, or delete */ while ((contact_hdr = pjsip_msg_find_hdr(task_data->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(task_data->aor, contact_hdr, task_data->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; } ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 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", contact_uri, aor_name, expiration); } else if (expiration) { RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup); updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); updated->qualify_frequency = task_data->aor->qualify_frequency; updated->authenticate_qualify = task_data->aor->authenticate_qualify; ast_sip_location_update_contact(updated); 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", contact_uri, aor_name, expiration); } else { 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", contact_uri, aor_name); } } 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 (task_data->aor->remove_existing) { ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); } /* Update the contacts as things will probably have changed */ ao2_cleanup(contacts); contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), task_data->rdata, 200, NULL, &tdata) != PJ_SUCCESS) { return PJ_TRUE; } /* 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 (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) { pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL); } else { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; }
/*! * \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; }
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; }
/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *pool, struct ao2_container *contacts, struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted) { pjsip_contact_hdr *previous = NULL; pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { .pool = pool, }; for (; (contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next)); pj_pool_reset(pool)) { int expiration = registrar_get_expiration(aor, contact, rdata); struct ast_sip_contact *existing; char contact_uri[pjsip_max_url_size]; if (contact->star) { /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ if (expiration != 0 || previous) { return -1; } /* Count all contacts to delete */ *deleted = ao2_container_count(contacts) - permanent; previous = contact; continue; } else if (previous && previous->star) { /* If there is a previous contact and it is a '*' this is a deal breaker */ return -1; } previous = contact; if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { continue; } details.uri = pjsip_uri_get_uri(contact->uri); /* pjsip_uri_print returns -1 if there's not enough room in the buffer */ if (pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)) < 0) { /* If the total length of the uri is greater than pjproject can handle, go no further */ return -1; } if (details.uri->host.slen >= pj_max_hostname) { /* If the length of the hostname is greater than pjproject can handle, go no further */ return -1; } /* Determine if this is an add, update, or delete for policy enforcement purposes */ existing = ao2_callback(contacts, 0, registrar_find_contact, &details); ao2_cleanup(existing); if (!existing) { if (expiration) { ++*added; } } else if (expiration) { ++*updated; } else { ++*deleted; } } return 0; }
/* * Test one test entry. */ static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry) { pj_status_t status; int len; char *input; pjsip_uri *parsed_uri, *ref_uri; pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0}; pj_timestamp t1, t2; if (entry->len == 0) entry->len = pj_ansi_strlen(entry->str); #if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0 input = pj_pool_alloc(pool, entry->len + 1); pj_memcpy(input, entry->str, entry->len); input[entry->len] = '\0'; #else input = entry->str; #endif /* Parse URI text. */ pj_get_timestamp(&t1); var.parse_len = var.parse_len + entry->len; parsed_uri = pjsip_parse_uri(pool, input, entry->len, 0); if (!parsed_uri) { /* Parsing failed. If the entry says that this is expected, then * return OK. */ status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10; if (status != 0) { PJ_LOG(3,(THIS_FILE, " uri parse error!\n" " uri='%s'\n", input)); } goto on_return; } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.parse_time, &t2); /* Create the reference URI. */ ref_uri = entry->creator(pool); /* Print both URI. */ s1.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); s2.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); pj_get_timestamp(&t1); len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE); if (len < 1) { status = -20; goto on_return; } s1.ptr[len] = '\0'; s1.slen = len; var.print_len = var.print_len + len; pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.print_time, &t2); len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE); if (len < 1) { status = -30; goto on_return; } s2.ptr[len] = '\0'; s2.slen = len; /* Full comparison of parsed URI with reference URI. */ pj_get_timestamp(&t1); status = pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri); if (status != 0) { /* Not equal. See if this is the expected status. */ status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40; if (status != 0) { PJ_LOG(3,(THIS_FILE, " uri comparison mismatch, status=%d:\n" " uri1='%s'\n" " uri2='%s'", status, s1.ptr, s2.ptr)); } goto on_return; } else { /* Equal. See if this is the expected status. */ status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50; if (status != PJ_SUCCESS) { goto on_return; } } var.cmp_len = var.cmp_len + len; pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.cmp_time, &t2); /* Compare text. */ if (entry->printed) { if (pj_strcmp2(&s1, entry->printed) != 0) { /* Not equal. */ PJ_LOG(3,(THIS_FILE, " uri print mismatch:\n" " printed='%s'\n" " expectd='%s'", s1.ptr, entry->printed)); status = -60; } } else { if (pj_strcmp(&s1, &s2) != 0) { /* Not equal. */ PJ_LOG(3,(THIS_FILE, " uri print mismatch:\n" " uri1='%s'\n" " uri2='%s'", s1.ptr, s2.ptr)); status = -70; } } on_return: return status; }
/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted) { pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; struct registrar_contact_details details = { .pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256), }; if (!details.pool) { return -1; } while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { int expiration = registrar_get_expiration(aor, contact, rdata); RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup); char contact_uri[pjsip_max_url_size]; if (contact->star) { /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ if ((expiration != 0) || previous) { pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return -1; } continue; } else if (previous && previous->star) { /* If there is a previous contact and it is a '*' this is a deal breaker */ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return -1; } previous = contact; if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { continue; } details.uri = pjsip_uri_get_uri(contact->uri); /* pjsip_uri_print returns -1 if there's not enough room in the buffer */ if (pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)) < 0) { /* If the total length of the uri is greater than pjproject can handle, go no further */ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return -1; } if (details.uri->host.slen >= pj_max_hostname) { /* If the length of the hostname is greater than pjproject can handle, go no further */ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return -1; } /* Determine if this is an add, update, or delete for policy enforcement purposes */ if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) { if (expiration) { (*added)++; } } else if (expiration) { (*updated)++; } else { (*deleted)++; } } /* The provided contacts are acceptable, huzzah! */ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return 0; } /*! \brief Callback function which prunes static contacts */ static int registrar_prune_static(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0; } /*! \brief Internal function used to delete a contact from an AOR */ static int registrar_delete_contact(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; const char *aor_name = arg; ast_sip_location_delete_contact(contact); if (!ast_strlen_zero(aor_name)) { 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); } return 0; } /*! \brief Internal function which adds a contact to a response */ static int registrar_add_contact(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; pjsip_tx_data *tdata = arg; pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool); pj_str_t uri; pj_strdup2_with_null(tdata->pool, &uri, contact->uri); hdr->uri = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR); hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); return 0; } /*! \brief Helper function which adds a Date header to a response */ static void registrar_add_date_header(pjsip_tx_data *tdata) { char date[256]; struct tm tm; time_t t = time(NULL); gmtime_r(&t, &tm); strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm); ast_sip_add_header(tdata, "Date", date); }
pjsip_auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_auth_session *auth_sess) { pjsip_authorization_hdr *auth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_assert(hdr != NULL); pj_assert(uri != NULL); pj_assert(cred_info != NULL); pj_assert(method != NULL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!")); return NULL; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) auth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) auth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(0); return NULL; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_status_t rc; pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (auth_sess) { update_digest_session( sess_pool, auth_sess, hdr ); cnonce = &auth_sess->cnonce; nc = auth_sess->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ auth->scheme = pjsip_DIGEST_STR; rc = respond_digest( pool, &auth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (rc != 0) return NULL; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && auth_sess) { if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &auth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH; } else { auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { auth = NULL; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = auth_sess->cached_hdr.next; while (cached_hdr != &auth_sess->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &auth_sess->cached_hdr) { cached_hdr->hdr = auth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = auth; pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr ); } } } # endif return auth; }