8569e7a3831662abe05713aea83b8bd625feef6b
[asterisk/asterisk.git] / res / res_pjsip / location.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 #include "asterisk.h"
20 #include "pjsip.h"
21 #include "pjlib.h"
22
23 #include "asterisk/res_pjsip.h"
24 #include "asterisk/logger.h"
25 #include "asterisk/astobj2.h"
26 #include "asterisk/sorcery.h"
27 #include "include/res_pjsip_private.h"
28
29 #define CONTACT_TRANSPORTS_BUCKETS 7
30 static struct ao2_container *contact_transports;
31
32 /*! \brief Destructor for AOR */
33 static void aor_destroy(void *obj)
34 {
35         struct ast_sip_aor *aor = obj;
36
37         ao2_cleanup(aor->permanent_contacts);
38         ast_string_field_free_memory(aor);
39 }
40
41 /*! \brief Allocator for AOR */
42 static void *aor_alloc(const char *name)
43 {
44         struct ast_sip_aor *aor = ast_sorcery_generic_alloc(sizeof(struct ast_sip_aor), aor_destroy);
45         if (!aor) {
46                 return NULL;
47         }
48         ast_string_field_init(aor, 128);
49         return aor;
50 }
51
52 /*! \brief Destructor for contact */
53 static void contact_destroy(void *obj)
54 {
55         struct ast_sip_contact *contact = obj;
56
57         ast_string_field_free_memory(contact);
58 }
59
60 /*! \brief Allocator for contact */
61 static void *contact_alloc(const char *name)
62 {
63         struct ast_sip_contact *contact = ast_sorcery_generic_alloc(sizeof(*contact), contact_destroy);
64
65         if (!contact) {
66                 return NULL;
67         }
68
69         if (ast_string_field_init(contact, 256)) {
70                 ao2_cleanup(contact);
71                 return NULL;
72         }
73
74         return contact;
75 }
76
77 /*! \brief Callback function for finding a contact_transport by URI */
78 static int contact_transport_find_by_uri(void *obj, void *arg, int flags)
79 {
80         struct ast_sip_contact_transport *ct = obj;
81         const char *contact_uri = arg;
82
83         return (!strcmp(ct->uri, contact_uri)) ? CMP_MATCH | CMP_STOP : 0;
84 }
85
86 /*! \brief Callback function for finding a contact_transport by transport */
87 static int contact_transport_find_by_transport(void *obj, void *arg, int flags)
88 {
89         struct ast_sip_contact_transport *ct = obj;
90         pjsip_transport *transport = arg;
91
92         return (ct->transport == transport) ? CMP_MATCH | CMP_STOP : 0;
93 }
94
95 void ast_sip_location_add_contact_transport(struct ast_sip_contact_transport *ct)
96 {
97         ao2_link(contact_transports, ct);
98
99         return;
100 }
101
102 void ast_sip_location_delete_contact_transport(struct ast_sip_contact_transport *ct)
103 {
104         ao2_unlink(contact_transports, ct);
105
106         return;
107 }
108
109 struct ast_sip_contact_transport *ast_sip_location_retrieve_contact_transport_by_uri(const char *contact_uri)
110 {
111         return ao2_callback(contact_transports, 0, contact_transport_find_by_uri, (void *)contact_uri);
112 }
113
114 struct ast_sip_contact_transport *ast_sip_location_retrieve_contact_transport_by_transport(pjsip_transport *transport)
115 {
116         return ao2_callback(contact_transports, 0, contact_transport_find_by_transport, transport);
117 }
118
119 struct ast_sip_aor *ast_sip_location_retrieve_aor(const char *aor_name)
120 {
121         return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "aor", aor_name);
122 }
123
124 /*! \brief Internal callback function which deletes and unlinks any expired contacts */
125 static int contact_expire(void *obj, void *arg, int flags)
126 {
127         struct ast_sip_contact *contact = obj;
128
129         /* If the contact has not yet expired it is valid */
130         if (ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) > 0) {
131                 return 0;
132         }
133
134         ast_sip_location_delete_contact(contact);
135
136         return CMP_MATCH;
137 }
138
139 /*! \brief Internal callback function which links static contacts into another container */
140 static int contact_link_static(void *obj, void *arg, int flags)
141 {
142         struct ao2_container *dest = arg;
143
144         ao2_link_flags(dest, obj, OBJ_NOLOCK);
145         return 0;
146 }
147
148 /*! \brief Simple callback function which returns immediately, used to grab the first contact of an AOR */
149 static int contact_find_first(void *obj, void *arg, int flags)
150 {
151         return CMP_MATCH | CMP_STOP;
152 }
153
154 struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
155 {
156         RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
157         struct ast_sip_contact *contact;
158
159         contacts = ast_sip_location_retrieve_aor_contacts(aor);
160         if (!contacts || (ao2_container_count(contacts) == 0)) {
161                 return NULL;
162         }
163
164         contact = ao2_callback(contacts, OBJ_NOLOCK, contact_find_first, NULL);
165         return contact;
166 }
167
168 struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor)
169 {
170         /* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */
171         char regex[strlen(ast_sorcery_object_get_id(aor)) + 4];
172         struct ao2_container *contacts;
173
174         snprintf(regex, sizeof(regex), "^%s;@", ast_sorcery_object_get_id(aor));
175
176         if (!(contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex))) {
177                 return NULL;
178         }
179
180         /* Prune any expired contacts and delete them, we do this first because static contacts can never expire */
181         ao2_callback(contacts, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, contact_expire, NULL);
182
183         /* Add any permanent contacts from the AOR */
184         if (aor->permanent_contacts) {
185                 ao2_callback(aor->permanent_contacts, OBJ_NOLOCK | OBJ_NODATA, contact_link_static, contacts);
186         }
187
188         return contacts;
189 }
190
191 struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list)
192 {
193         char *aor_name;
194         char *rest;
195         struct ast_sip_contact *contact = NULL;
196
197         /* If the location is still empty we have nowhere to go */
198         if (ast_strlen_zero(aor_list) || !(rest = ast_strdupa(aor_list))) {
199                 ast_log(LOG_WARNING, "Unable to determine contacts from empty aor list\n");
200                 return NULL;
201         }
202
203         while ((aor_name = strsep(&rest, ","))) {
204                 RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
205                 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
206
207                 if (!aor) {
208                         continue;
209                 }
210                 contact = ast_sip_location_retrieve_first_aor_contact(aor);
211                 /* If a valid contact is available use its URI for dialing */
212                 if (contact) {
213                         break;
214                 }
215         }
216
217         return contact;
218 }
219
220 struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_name)
221 {
222         return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
223 }
224
225 int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time)
226 {
227         char name[AST_UUID_STR_LEN];
228         RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
229
230         snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), uri);
231
232         if (!(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name))) {
233                 return -1;
234         }
235
236         ast_string_field_set(contact, uri, uri);
237         contact->expiration_time = expiration_time;
238         contact->qualify_frequency = aor->qualify_frequency;
239         contact->authenticate_qualify = aor->authenticate_qualify;
240
241         return ast_sorcery_create(ast_sip_get_sorcery(), contact);
242 }
243
244 int ast_sip_location_update_contact(struct ast_sip_contact *contact)
245 {
246         return ast_sorcery_update(ast_sip_get_sorcery(), contact);
247 }
248
249 int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
250 {
251         return ast_sorcery_delete(ast_sip_get_sorcery(), contact);
252 }
253
254 /*! \brief Custom handler for translating from a string timeval to actual structure */
255 static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
256 {
257         struct ast_sip_contact *contact = obj;
258         return ast_get_timeval(var->value, &contact->expiration_time, ast_tv(0, 0), NULL);
259 }
260
261 /*! \brief Custom handler for translating from an actual structure timeval to string */
262 static int expiration_struct2str(const void *obj, const intptr_t *args, char **buf)
263 {
264         const struct ast_sip_contact *contact = obj;
265         return (ast_asprintf(buf, "%lu", contact->expiration_time.tv_sec) < 0) ? -1 : 0;
266 }
267
268 /*! \brief Custom handler for permanent URIs */
269 static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
270 {
271         struct ast_sip_aor *aor = obj;
272         RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
273         pj_pool_t *pool;
274         pj_str_t contact_uri;
275         static const pj_str_t HCONTACT = { "Contact", 7 };
276
277         pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Permanent Contact Validation", 256, 256);
278         if (!pool) {
279                 return -1;
280         }
281
282         pj_strdup2_with_null(pool, &contact_uri, var->value);
283         if (!pjsip_parse_hdr(pool, &HCONTACT, contact_uri.ptr, contact_uri.slen, NULL)) {
284                 ast_log(LOG_ERROR, "Permanent URI on aor '%s' with contact '%s' failed to parse\n",
285                         ast_sorcery_object_get_id(aor), var->value);
286                 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
287                 return -1;
288         }
289
290         pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
291
292         if ((!aor->permanent_contacts && !(aor->permanent_contacts = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) ||
293                 !(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", NULL))) {
294                 return -1;
295         }
296
297         ast_string_field_set(contact, uri, var->value);
298         ao2_link_flags(aor->permanent_contacts, contact, OBJ_NOLOCK);
299
300         return 0;
301 }
302
303 /*! \brief Initialize sorcery with location support */
304 int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery)
305 {
306         ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar");
307         ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor");
308
309         if (ast_sorcery_object_register(sorcery, "contact", contact_alloc, NULL, NULL) ||
310                 ast_sorcery_object_register(sorcery, "aor", aor_alloc, NULL, NULL)) {
311                 return -1;
312         }
313
314         ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
315         ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
316         ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, 0, 0);
317         ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
318                                           PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
319
320         ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0);
321         ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
322         ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration));
323         ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration));
324         ast_sorcery_object_field_register(sorcery, "aor", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_frequency), 0, 86400);
325         ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify));
326         ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
327         ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
328         ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0);
329         ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
330
331         return 0;
332 }
333
334 int ast_res_pjsip_init_contact_transports(void)
335 {
336         if (contact_transports) {
337                 ao2_t_ref(contact_transports, -1, "Remove old contact transports");
338         }
339
340         contact_transports = ao2_t_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CONTACT_TRANSPORTS_BUCKETS, NULL, NULL, "Create container for contact transports");
341         if (!contact_transports) {
342                 return -1;
343         }
344
345         return 0;
346 }