2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
20 <depend>pjproject</depend>
21 <depend>res_pjsip</depend>
22 <support_level>core</support_level>
30 #include "asterisk/res_pjsip.h"
31 #include "asterisk/module.h"
33 /*! \brief Internal function which returns the expiration time for a contact */
34 static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
36 pjsip_expires_hdr *expires;
37 int expiration = aor->default_expiration;
39 if (contact->expires != -1) {
40 /* Expiration was provided with the contact itself */
41 expiration = contact->expires;
42 } else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
43 /* Expiration was provided using the Expires header */
44 expiration = expires->ivalue;
47 /* If the value has explicitly been set to 0, do not enforce */
52 /* Enforce the range that we will allow for expiration */
53 if (expiration < aor->minimum_expiration) {
54 expiration = aor->minimum_expiration;
55 } else if (expiration > aor->maximum_expiration) {
56 expiration = aor->maximum_expiration;
62 /*! \brief Structure used for finding contact */
63 struct registrar_contact_details {
64 /*! \brief Pool used for parsing URI */
66 /*! \brief URI being looked for */
70 /*! \brief Callback function for finding a contact */
71 static int registrar_find_contact(void *obj, void *arg, int flags)
73 struct ast_sip_contact *contact = obj;
74 const struct registrar_contact_details *details = arg;
75 pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);
77 return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0;
80 /*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
81 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)
83 pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
84 struct registrar_contact_details details = {
85 .pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256),
92 while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
93 int expiration = registrar_get_expiration(aor, contact, rdata);
94 RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup);
97 /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
98 if ((expiration != 0) || previous) {
99 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
102 } else if (previous && previous->star) {
103 /* If there is a previous contact and it is a '*' this is a deal breaker */
104 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
109 if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
113 details.uri = pjsip_uri_get_uri(contact->uri);
115 /* Determine if this is an add, update, or delete for policy enforcement purposes */
116 if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) {
120 } else if (expiration) {
127 /* The provided contacts are acceptable, huzzah! */
128 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
132 /*! \brief Callback function which prunes static contacts */
133 static int registrar_prune_static(void *obj, void *arg, int flags)
135 struct ast_sip_contact *contact = obj;
137 return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0;
140 /*! \brief Internal function used to delete all contacts from an AOR */
141 static int registrar_delete_contact(void *obj, void *arg, int flags)
143 struct ast_sip_contact *contact = obj;
145 ast_sip_location_delete_contact(contact);
150 /*! \brief Internal function which adds a contact to a response */
151 static int registrar_add_contact(void *obj, void *arg, int flags)
153 struct ast_sip_contact *contact = obj;
154 pjsip_tx_data *tdata = arg;
155 pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool);
158 pj_strdup2_with_null(tdata->pool, &uri, contact->uri);
159 hdr->uri = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
160 hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000;
162 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
167 /*! \brief Helper function which adds a Date header to a response */
168 static void registrar_add_date_header(pjsip_tx_data *tdata)
172 time_t t = time(NULL);
175 strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);
177 ast_sip_add_header(tdata, "Date", date);
180 static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
182 struct ast_sip_endpoint *endpoint = ast_pjsip_rdata_get_endpoint(rdata);
184 char user_name[64], domain_name[64];
185 char *configured_aors, *aor_name;
186 RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
187 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
188 int added = 0, updated = 0, deleted = 0;
189 pjsip_contact_hdr *contact_hdr = NULL;
190 struct registrar_contact_details details = { 0, };
191 pjsip_tx_data *tdata;
192 pjsip_response_addr addr;
194 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
198 if (ast_strlen_zero(endpoint->aors)) {
199 /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
200 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
201 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
205 if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
206 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
207 ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
211 uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
212 ast_copy_pj_str(user_name, &uri->user, sizeof(user_name));
213 ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name));
215 configured_aors = ast_strdupa(endpoint->aors);
217 /* Iterate the configured AORs to see if the user or the user+domain match */
218 while ((aor_name = strsep(&configured_aors, ","))) {
219 char id[AST_UUID_STR_LEN];
220 RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);
222 snprintf(id, sizeof(id), "%s@%s", user_name, domain_name);
223 if (!strcmp(aor_name, id)) {
227 if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
228 snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain);
229 if (!strcmp(aor_name, id)) {
234 if (!strcmp(aor_name, user_name)) {
239 if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
240 /* The provided AOR name was not found (be it within the configuration or sorcery itself) */
241 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
242 ast_sip_report_failed_acl(endpoint, rdata, "registrar_requested_aor_not_found");
246 if (!aor->max_contacts) {
247 /* Registration is not permitted for this AOR */
248 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
249 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_registration_permitted");
253 /* Retrieve the current contacts, we'll need to know whether to update or not */
254 contacts = ast_sip_location_retrieve_aor_contacts(aor);
256 /* So we don't count static contacts against max_contacts we prune them out from the container */
257 ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);
259 if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
260 /* The provided Contact headers do not conform to the specification */
261 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
262 ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
266 if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
267 /* Enforce the maximum number of contacts */
268 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
269 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
273 if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
274 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
278 /* Iterate each provided Contact header and add, update, or delete */
279 while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
281 char contact_uri[PJSIP_MAX_URL_SIZE];
282 RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
284 if (contact_hdr->star) {
285 /* A star means to unregister everything, so do so for the possible contacts */
286 ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
290 if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
291 /* This registrar only currently supports sip: and sips: URI schemes */
295 expiration = registrar_get_expiration(aor, contact_hdr, rdata);
296 details.uri = pjsip_uri_get_uri(contact_hdr->uri);
297 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
299 if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
300 /* If they are actually trying to delete a contact that does not exist... be forgiving */
302 ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
303 contact_uri, aor_name);
307 ast_sip_location_add_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)));
308 ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
309 contact_uri, aor_name, expiration);
310 } else if (expiration) {
311 RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup);
312 updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
313 updated->qualify_frequency = aor->qualify_frequency;
314 updated->authenticate_qualify = aor->authenticate_qualify;
316 ast_sip_location_update_contact(updated);
317 ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
318 contact_uri, aor_name, expiration);
320 ast_sip_location_delete_contact(contact);
321 ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
325 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
327 /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
330 if (aor->remove_existing) {
331 ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
334 /* Update the contacts as things will probably have changed */
335 ao2_cleanup(contacts);
336 contacts = ast_sip_location_retrieve_aor_contacts(aor);
338 /* Send a response containing all of the contacts (including static) that are present on this AOR */
339 if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 200, NULL, &tdata) != PJ_SUCCESS) {
343 /* Add the date header to the response, some UAs use this to set their date and time */
344 registrar_add_date_header(tdata);
346 ao2_callback(contacts, 0, registrar_add_contact, tdata);
348 if (pjsip_get_response_addr(tdata->pool, rdata, &addr) == PJ_SUCCESS) {
349 pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL);
351 pjsip_tx_data_dec_ref(tdata);
357 static pjsip_module registrar_module = {
358 .name = { "Registrar", 9 },
360 .priority = PJSIP_MOD_PRIORITY_APPLICATION,
361 .on_rx_request = registrar_on_rx_request,
364 static int load_module(void)
366 const pj_str_t STR_REGISTER = { "REGISTER", 8 };
368 if (ast_sip_register_service(®istrar_module)) {
369 return AST_MODULE_LOAD_DECLINE;
372 if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) {
373 ast_sip_unregister_service(®istrar_module);
374 return AST_MODULE_LOAD_DECLINE;
377 return AST_MODULE_LOAD_SUCCESS;
380 static int unload_module(void)
382 ast_sip_unregister_service(®istrar_module);
386 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Registrar Support",
388 .unload = unload_module,
389 .load_pri = AST_MODPRI_APP_DEPEND,