Add test suite events for when contacts are added or removed from an AOR
[asterisk/asterisk.git] / res / res_pjsip_registrar.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*** MODULEINFO
20         <depend>pjproject</depend>
21         <depend>res_pjsip</depend>
22         <support_level>core</support_level>
23  ***/
24
25 #include "asterisk.h"
26
27 #include <pjsip.h>
28 #include <pjsip_ua.h>
29
30 #include "asterisk/res_pjsip.h"
31 #include "asterisk/module.h"
32 #include "asterisk/test.h"
33
34 /*! \brief Internal function which returns the expiration time for a contact */
35 static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
36 {
37         pjsip_expires_hdr *expires;
38         int expiration = aor->default_expiration;
39
40         if (contact->expires != -1) {
41                 /* Expiration was provided with the contact itself */
42                 expiration = contact->expires;
43         } else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
44                 /* Expiration was provided using the Expires header */
45                 expiration = expires->ivalue;
46         }
47
48         /* If the value has explicitly been set to 0, do not enforce */
49         if (!expiration) {
50                 return expiration;
51         }
52
53         /* Enforce the range that we will allow for expiration */
54         if (expiration < aor->minimum_expiration) {
55                 expiration = aor->minimum_expiration;
56         } else if (expiration > aor->maximum_expiration) {
57                 expiration = aor->maximum_expiration;
58         }
59
60         return expiration;
61 }
62
63 /*! \brief Structure used for finding contact */
64 struct registrar_contact_details {
65         /*! \brief Pool used for parsing URI */
66         pj_pool_t *pool;
67         /*! \brief URI being looked for */
68         pjsip_uri *uri;
69 };
70
71 /*! \brief Callback function for finding a contact */
72 static int registrar_find_contact(void *obj, void *arg, int flags)
73 {
74         struct ast_sip_contact *contact = obj;
75         const struct registrar_contact_details *details = arg;
76         pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);
77
78         return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0;
79 }
80
81 /*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
82 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 {
84         pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
85         struct registrar_contact_details details = {
86                 .pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256),
87         };
88
89         if (!details.pool) {
90                 return -1;
91         }
92
93         while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) {
94                 int expiration = registrar_get_expiration(aor, contact, rdata);
95                 RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup);
96
97                 if (contact->star) {
98                         /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
99                         if ((expiration != 0) || previous) {
100                                 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
101                                 return -1;
102                         }
103                         continue;
104                 } else if (previous && previous->star) {
105                         /* If there is a previous contact and it is a '*' this is a deal breaker */
106                         pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
107                         return -1;
108                 }
109                 previous = contact;
110
111                 if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
112                         continue;
113                 }
114
115                 details.uri = pjsip_uri_get_uri(contact->uri);
116
117                 /* Determine if this is an add, update, or delete for policy enforcement purposes */
118                 if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) {
119                         if (expiration) {
120                                 (*added)++;
121                         }
122                 } else if (expiration) {
123                         (*updated)++;
124                 } else {
125                         (*deleted)++;
126                 }
127         }
128
129         /* The provided contacts are acceptable, huzzah! */
130         pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
131         return 0;
132 }
133
134 /*! \brief Callback function which prunes static contacts */
135 static int registrar_prune_static(void *obj, void *arg, int flags)
136 {
137         struct ast_sip_contact *contact = obj;
138
139         return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0;
140 }
141
142 /*! \brief Internal function used to delete all contacts from an AOR */
143 static int registrar_delete_contact(void *obj, void *arg, int flags)
144 {
145         struct ast_sip_contact *contact = obj;
146         const char *aor_name = arg;
147
148         ast_sip_location_delete_contact(contact);
149         if (!ast_strlen_zero(aor_name)) {
150                 ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact->uri, aor_name);
151                 ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
152                                 "Contact: %s\r\n"
153                                 "AOR: %s",
154                                 contact->uri,
155                                 aor_name);
156         }
157
158         return 0;
159 }
160
161 /*! \brief Internal function which adds a contact to a response */
162 static int registrar_add_contact(void *obj, void *arg, int flags)
163 {
164         struct ast_sip_contact *contact = obj;
165         pjsip_tx_data *tdata = arg;
166         pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool);
167         pj_str_t uri;
168
169         pj_strdup2_with_null(tdata->pool, &uri, contact->uri);
170         hdr->uri = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
171         hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000;
172
173         pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
174
175         return 0;
176 }
177
178 /*! \brief Helper function which adds a Date header to a response */
179 static void registrar_add_date_header(pjsip_tx_data *tdata)
180 {
181         char date[256];
182         struct tm tm;
183         time_t t = time(NULL);
184
185         gmtime_r(&t, &tm);
186         strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);
187
188         ast_sip_add_header(tdata, "Date", date);
189 }
190
191 static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
192 {
193         struct ast_sip_endpoint *endpoint = ast_pjsip_rdata_get_endpoint(rdata);
194         pjsip_sip_uri *uri;
195         char user_name[64], domain_name[64];
196         char *configured_aors, *aor_name;
197         RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
198         RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
199         int added = 0, updated = 0, deleted = 0;
200         pjsip_contact_hdr *contact_hdr = NULL;
201         struct registrar_contact_details details = { 0, };
202         pjsip_tx_data *tdata;
203         pjsip_response_addr addr;
204
205         if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
206                 return PJ_FALSE;
207         }
208
209         if (ast_strlen_zero(endpoint->aors)) {
210                 /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
211                 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
212                 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
213                 return PJ_TRUE;
214         }
215
216         if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
217                 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
218                 ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
219                 return PJ_TRUE;
220         }
221
222         uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
223         ast_copy_pj_str(user_name, &uri->user, sizeof(user_name));
224         ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name));
225
226         configured_aors = ast_strdupa(endpoint->aors);
227
228         /* Iterate the configured AORs to see if the user or the user+domain match */
229         while ((aor_name = strsep(&configured_aors, ","))) {
230                 char id[AST_UUID_STR_LEN];
231                 RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);
232
233                 snprintf(id, sizeof(id), "%s@%s", user_name, domain_name);
234                 if (!strcmp(aor_name, id)) {
235                         break;
236                 }
237
238                 if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
239                         snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain);
240                         if (!strcmp(aor_name, id)) {
241                                 break;
242                         }
243                 }
244
245                 if (!strcmp(aor_name, user_name)) {
246                         break;
247                 }
248         }
249
250         if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
251                 /* The provided AOR name was not found (be it within the configuration or sorcery itself) */
252                 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
253                 ast_sip_report_failed_acl(endpoint, rdata, "registrar_requested_aor_not_found");
254                 return PJ_TRUE;
255         }
256
257         if (!aor->max_contacts) {
258                 /* Registration is not permitted for this AOR */
259                 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
260                 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_registration_permitted");
261                 return PJ_TRUE;
262         }
263
264         /* Retrieve the current contacts, we'll need to know whether to update or not */
265         contacts = ast_sip_location_retrieve_aor_contacts(aor);
266
267         /* So we don't count static contacts against max_contacts we prune them out from the container */
268         ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);
269
270         if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
271                 /* The provided Contact headers do not conform to the specification */
272                 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
273                 ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
274                 return PJ_TRUE;
275         }
276
277         if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
278                 /* Enforce the maximum number of contacts */
279                 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
280                 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
281                 return PJ_TRUE;
282         }
283
284         if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
285                 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
286                 return PJ_TRUE;
287         }
288
289         /* Iterate each provided Contact header and add, update, or delete */
290         while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
291                 int expiration;
292                 char contact_uri[PJSIP_MAX_URL_SIZE];
293                 RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
294
295                 if (contact_hdr->star) {
296                         /* A star means to unregister everything, so do so for the possible contacts */
297                         ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, aor_name);
298                         break;
299                 }
300
301                 if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
302                         /* This registrar only currently supports sip: and sips: URI schemes */
303                         continue;
304                 }
305
306                 expiration = registrar_get_expiration(aor, contact_hdr, rdata);
307                 details.uri = pjsip_uri_get_uri(contact_hdr->uri);
308                 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
309
310                 if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
311                         /* If they are actually trying to delete a contact that does not exist... be forgiving */
312                         if (!expiration) {
313                                 ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
314                                         contact_uri, aor_name);
315                                 continue;
316                         }
317
318                         ast_sip_location_add_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)));
319                         ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
320                                 contact_uri, aor_name, expiration);
321                         ast_test_suite_event_notify("AOR_CONTACT_ADDED",
322                                         "Contact: %s\r\n"
323                                         "AOR: %s\r\n"
324                                         "Expiration: %d",
325                                         contact_uri,
326                                         aor_name,
327                                         expiration);
328                 } else if (expiration) {
329                         RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup);
330                         updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
331                         updated->qualify_frequency = aor->qualify_frequency;
332                         updated->authenticate_qualify = aor->authenticate_qualify;
333
334                         ast_sip_location_update_contact(updated);
335                         ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
336                                 contact_uri, aor_name, expiration);
337                         ast_test_suite_event_notify("AOR_CONTACT_REFRESHED",
338                                         "Contact: %s\r\n"
339                                         "AOR: %s\r\n"
340                                         "Expiration: %d",
341                                         contact_uri,
342                                         aor_name,
343                                         expiration);
344                 } else {
345                         ast_sip_location_delete_contact(contact);
346                         ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
347                         ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
348                                         "Contact: %s\r\n"
349                                         "AOR: %s",
350                                         contact_uri,
351                                         aor_name);
352                 }
353         }
354
355         pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
356
357         /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
358          * do so
359          */
360         if (aor->remove_existing) {
361                 ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
362         }
363
364         /* Update the contacts as things will probably have changed */
365         ao2_cleanup(contacts);
366         contacts = ast_sip_location_retrieve_aor_contacts(aor);
367
368         /* Send a response containing all of the contacts (including static) that are present on this AOR */
369         if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 200, NULL, &tdata) != PJ_SUCCESS) {
370                 return PJ_TRUE;
371         }
372
373         /* Add the date header to the response, some UAs use this to set their date and time */
374         registrar_add_date_header(tdata);
375
376         ao2_callback(contacts, 0, registrar_add_contact, tdata);
377
378         if (pjsip_get_response_addr(tdata->pool, rdata, &addr) == PJ_SUCCESS) {
379                 pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL);
380         } else {
381                 pjsip_tx_data_dec_ref(tdata);
382         }
383
384         return PJ_TRUE;
385 }
386
387 static pjsip_module registrar_module = {
388         .name = { "Registrar", 9 },
389         .id = -1,
390         .priority = PJSIP_MOD_PRIORITY_APPLICATION,
391         .on_rx_request = registrar_on_rx_request,
392 };
393
394 static int load_module(void)
395 {
396         const pj_str_t STR_REGISTER = { "REGISTER", 8 };
397
398         if (ast_sip_register_service(&registrar_module)) {
399                 return AST_MODULE_LOAD_DECLINE;
400         }
401
402         if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) {
403                 ast_sip_unregister_service(&registrar_module);
404                 return AST_MODULE_LOAD_DECLINE;
405         }
406
407         return AST_MODULE_LOAD_SUCCESS;
408 }
409
410 static int unload_module(void)
411 {
412         ast_sip_unregister_service(&registrar_module);
413         return 0;
414 }
415
416 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Registrar Support",
417                 .load = load_module,
418                 .unload = unload_module,
419                 .load_pri = AST_MODPRI_APP_DEPEND,
420                );