Asterisk - The Open Source Telephony Project GIT-master-a358458
res_pjsip_registrar.c
Go to the documentation of this file.
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_pjproject</depend>
22 <depend>res_pjsip</depend>
23 <support_level>core</support_level>
24 ***/
25
26#include "asterisk.h"
27
28#include <signal.h>
29#include <pjsip.h>
30#include <pjsip_ua.h>
31
32#include "asterisk/res_pjsip.h"
33#include "asterisk/module.h"
34#include "asterisk/paths.h"
35#include "asterisk/test.h"
37#include "asterisk/manager.h"
41
42/*** DOCUMENTATION
43 <manager name="PJSIPShowRegistrationsInbound" language="en_US">
44 <synopsis>
45 Lists PJSIP inbound registrations.
46 </synopsis>
47 <syntax />
48 <description>
49 <para>
50 In response, <literal>InboundRegistrationDetail</literal> events showing configuration
51 and status information are raised for all contacts, static or dynamic. Once all events
52 are completed an <literal>InboundRegistrationDetailComplete</literal> is issued.
53 </para>
54 <warning><para>
55 This command just dumps all coonfigured AORs with contacts, even if the contact
56 is a permanent one. To really get just inbound registrations, use
57 <literal>PJSIPShowRegistrationInboundContactStatuses</literal>.
58 </para>
59 </warning>
60 </description>
61 <see-also>
62 <ref type="manager" module="res_pjsip_registrar">PJSIPShowRegistrationInboundContactStatuses</ref>
63 </see-also>
64 </manager>
65 <manager name="PJSIPShowRegistrationInboundContactStatuses" language="en_US">
66 <synopsis>
67 Lists ContactStatuses for PJSIP inbound registrations.
68 </synopsis>
69 <syntax />
70 <description>
71 <para>
72 In response, <literal>ContactStatusDetail</literal> events showing status information
73 are raised for each inbound registration (dynamic contact) object. Once all events
74 are completed a <literal>ContactStatusDetailComplete</literal> event is issued.
75 </para>
76 </description>
77 </manager>
78 ***/
79
80static int pj_max_hostname = PJ_MAX_HOSTNAME;
81static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE;
82
83/*! \brief Internal function which returns the expiration time for a contact */
84static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
85{
86 pjsip_expires_hdr *expires;
87 unsigned int expiration = aor->default_expiration;
88
89 if (contact && contact->expires != PJSIP_EXPIRES_NOT_SPECIFIED) {
90 /* Expiration was provided with the contact itself */
91 expiration = contact->expires;
92 } else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
93 /* Expiration was provided using the Expires header */
94 expiration = expires->ivalue;
95 }
96
97 /* If the value has explicitly been set to 0, do not enforce */
98 if (!expiration) {
99 return expiration;
100 }
101
102 /* Enforce the range that we will allow for expiration */
103 if (expiration < aor->minimum_expiration) {
104 expiration = aor->minimum_expiration;
105 } else if (expiration > aor->maximum_expiration) {
106 expiration = aor->maximum_expiration;
107 }
108
109 return expiration;
110}
111
112/*! \brief Structure used for finding contact */
114 /*! \brief Pool used for parsing URI */
115 pj_pool_t *pool;
116 /*! \brief URI being looked for */
117 pjsip_sip_uri *uri;
118};
119
120/*! \brief Callback function for finding a contact */
121static int registrar_find_contact(void *obj, void *arg, int flags)
122{
123 struct ast_sip_contact *contact = obj;
124 const struct registrar_contact_details *details = arg;
125 pjsip_uri *contact_uri;
126
127 if (ast_tvzero(contact->expiration_time)) {
128 return 0;
129 }
130
131 contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0);
132 if (!contact_uri) {
133 ast_log(LOG_WARNING, "Unable to parse contact URI from '%s'.\n", contact->uri);
134 return 0;
135 }
136
137 return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH : 0;
138}
139
140/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
141static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *pool, struct ao2_container *contacts,
142 struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted)
143{
144 pjsip_contact_hdr *previous = NULL;
145 pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
146 struct registrar_contact_details details = {
147 .pool = pool,
148 };
149
150 for (; (contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next)); pj_pool_reset(pool)) {
151 unsigned int expiration = registrar_get_expiration(aor, contact, rdata);
152 struct ast_sip_contact *existing;
153 char contact_uri[pjsip_max_url_size];
154
155 if (contact->star) {
156 /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
157 if (expiration != 0 || previous) {
158 return -1;
159 }
160 /* Count all contacts to delete */
161 *deleted = ao2_container_count(contacts) - permanent;
162 previous = contact;
163 continue;
164 } else if (previous && previous->star) {
165 /* If there is a previous contact and it is a '*' this is a deal breaker */
166 return -1;
167 }
168 previous = contact;
169
170 if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) {
171 continue;
172 }
173
174 details.uri = pjsip_uri_get_uri(contact->uri);
175
176 /* pjsip_uri_print returns -1 if there's not enough room in the buffer */
177 if (pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)) < 0) {
178 /* If the total length of the uri is greater than pjproject can handle, go no further */
179 return -1;
180 }
181
182 if (details.uri->host.slen >= pj_max_hostname) {
183 /* If the length of the hostname is greater than pjproject can handle, go no further */
184 return -1;
185 }
186
187 /* Determine if this is an add, update, or delete for policy enforcement purposes */
188 existing = ao2_callback(contacts, 0, registrar_find_contact, &details);
189 ao2_cleanup(existing);
190 if (!existing) {
191 if (expiration) {
192 ++*added;
193 }
194 } else if (expiration) {
195 ++*updated;
196 } else {
197 ++*deleted;
198 }
199 }
200
201 return 0;
202}
203
211};
212
213static int registrar_contact_delete(enum contact_delete_type type, pjsip_transport *transport,
214 struct ast_sip_contact *contact, const char *aor_name);
215
216/*! \brief Internal function used to delete a contact from an AOR */
217static int registrar_delete_contact(void *obj, void *arg, int flags)
218{
220 CONTACT_DELETE_REQUEST, NULL, obj, arg) ? 0 : CMP_MATCH;
221}
222
223/*! \brief Internal function which adds a contact to a response */
224static int registrar_add_contact(void *obj, void *arg, int flags)
225{
226 struct ast_sip_contact *contact = obj;
227 pjsip_tx_data *tdata = arg;
228 pj_str_t uri;
229 pjsip_uri *parsed;
230
231 pj_strdup2_with_null(tdata->pool, &uri, contact->uri);
232 parsed = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
233
234 if (parsed && (PJSIP_URI_SCHEME_IS_SIP(parsed) || PJSIP_URI_SCHEME_IS_SIPS(parsed))) {
235 pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool);
236 hdr->uri = parsed;
237 if (!ast_tvzero(contact->expiration_time)) {
238 hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000;
239 } else {
240 hdr->expires = PJSIP_EXPIRES_NOT_SPECIFIED;
241 }
242 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) hdr);
243 } else {
244 ast_log(LOG_WARNING, "Skipping invalid Contact URI \"%.*s\" for AOR %s\n",
245 (int) uri.slen, uri.ptr, contact->aor);
246 }
247
248 return 0;
249}
250
251static const pj_str_t path_hdr_name = { "Path", 4 };
252
253static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
254{
255 pjsip_generic_string_hdr *path_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, NULL);
256
257 if (!path_hdr) {
258 return 0;
259 }
260
261 *path_str = ast_str_create(64);
262 if (!*path_str) {
263 return -1;
264 }
265
266 ast_str_set(path_str, 0, "%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
267
268 while ((path_hdr = (pjsip_generic_string_hdr *) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, path_hdr->next))) {
269 ast_str_append(path_str, 0, ",%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
270 }
271
272 return 0;
273}
274
275static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor, struct ast_str **path_str)
276{
277 const pj_str_t path_supported_name = { "path", 4 };
278 pjsip_supported_hdr *supported_hdr;
279 int i;
280
281 if (!aor->support_path) {
282 return 0;
283 }
284
285 if (build_path_data(rdata, path_str)) {
286 return -1;
287 }
288
289 if (!*path_str) {
290 return 0;
291 }
292
293 supported_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_SUPPORTED, NULL);
294 if (!supported_hdr) {
295 return -1;
296 }
297
298 /* Find advertised path support */
299 for (i = 0; i < supported_hdr->count; i++) {
300 if (!pj_stricmp(&supported_hdr->values[i], &path_supported_name)) {
301 return 0;
302 }
303 }
304
305 /* Path header present, but support not advertised */
306 return -1;
307}
308
309/*! Transport monitor for incoming REGISTER contacts */
311 /*!
312 * \brief Sorcery contact name to remove on transport shutdown
313 * \note Stored after aor_name in space reserved when struct allocated.
314 */
316 /*! Indicates that the monitor is in the process of removing a contact */
318 /*! AOR name the contact is associated */
319 char aor_name[0];
320};
321
322static int contact_transport_monitor_matcher(void *a, void *b)
323{
324 struct contact_transport_monitor *ma = a;
325 struct contact_transport_monitor *mb = b;
326
327 return strcmp(ma->aor_name, mb->aor_name) == 0
328 && strcmp(ma->contact_name, mb->contact_name) == 0;
329}
330
332{
333 struct contact_transport_monitor *monitor = data;
334 struct ast_sip_contact *contact;
335 struct ast_sip_aor *aor;
336
338 if (!aor) {
339 ao2_lock(monitor);
340 monitor->removing = 0;
341 ao2_unlock(monitor);
342 ao2_ref(monitor, -1);
343 return 0;
344 }
345
346 ao2_lock(aor);
347
349 if (contact) {
351 ao2_ref(contact, -1);
352 }
353 ao2_unlock(aor);
354 ao2_ref(aor, -1);
355
356 ao2_ref(monitor, -1);
357 return 0;
358}
359
360/*!
361 * \internal
362 * \brief The reliable transport we registered as a contact has shutdown.
363 *
364 * \param data What contact needs to be removed.
365 *
366 * \note Normally executed by the pjsip monitor thread.
367 */
369{
370 struct contact_transport_monitor *monitor = data;
371
372 /*
373 * It's possible for this shutdown handler to get called multiple times for the
374 * same monitor from different threads. Only one of the calls needs to do the
375 * actual removing of the contact, so if one is currently removing then any
376 * subsequent calls can skip.
377 */
378 ao2_lock(monitor);
379 if (monitor->removing) {
380 ao2_unlock(monitor);
381 return;
382 }
383
384 monitor->removing = 1;
385
386 /*
387 * Push off to a default serializer. This is in case sorcery
388 * does database accesses for contacts. Database accesses may
389 * not be on this machine. We don't want to tie up the pjsip
390 * monitor thread with potentially long access times.
391 */
392 ao2_ref(monitor, +1);
394 monitor->removing = 0;
395 ao2_ref(monitor, -1);
396 }
397
398 ao2_unlock(monitor);
399}
400
401
402static int registrar_contact_delete(enum contact_delete_type type, pjsip_transport *transport,
403 struct ast_sip_contact *contact, const char *aor_name)
404{
405 int aor_size;
406
407 /* Permanent contacts can't be deleted */
408 if (ast_tvzero(contact->expiration_time)) {
409 return -1;
410 }
411
412 aor_size = aor_name ? strlen(aor_name) : 0;
413 if (contact->prune_on_boot && type != CONTACT_DELETE_SHUTDOWN && aor_size) {
414 const char *contact_name = ast_sorcery_object_get_id(contact);
415 size_t contact_name_len = strlen(contact_name) + 1;
416 struct contact_transport_monitor *monitor = ast_alloca(
417 sizeof(*monitor) + 1 + aor_size + contact_name_len);
418
419 strcpy(monitor->aor_name, aor_name); /* Safe */
420 monitor->contact_name = monitor->aor_name + aor_size + 1;
421 ast_copy_string(monitor->contact_name, contact_name, contact_name_len); /* Safe */
422
423 if (transport) {
427 } else {
428 /*
429 * If a specific transport is not supplied then unregister the matching
430 * monitor from all reliable transports.
431 */
434 }
435 }
436
438
439 if (aor_size) {
440 if (VERBOSITY_ATLEAST(3)) {
441 const char *reason = "none";
442
443 switch (type) {
445 reason = "registration failure";
446 break;
448 reason = "remove existing";
449 break;
451 reason = "remove unavailable";
452 break;
454 reason = "expiration";
455 break;
457 reason = "request";
458 break;
460 reason = "shutdown";
461 break;
462 }
463
464 ast_verb(3, "Removed contact '%s' from AOR '%s' due to %s\n",
465 contact->uri, aor_name, reason);
466 }
467
468 ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
469 "Contact: %s\r\n"
470 "AOR: %s\r\n"
471 "UserAgent: %s",
472 contact->uri,
473 aor_name,
474 contact->user_agent);
475 }
476
477 return 0;
478}
479
481
482static int vec_contact_cmp(struct ast_sip_contact *left, struct ast_sip_contact *right)
483{
484 struct ast_sip_contact *left_contact = left;
485 struct ast_sip_contact *right_contact = right;
486
487 /* Sort from soonest to expire to last to expire */
488 int time_sorted = ast_tvcmp(left_contact->expiration_time, right_contact->expiration_time);
489
490 struct ast_sip_aor *aor = ast_sip_location_retrieve_aor(left_contact->aor);
491 struct ast_sip_contact_status *left_status;
492 struct ast_sip_contact_status *right_status;
493 int remove_unavailable = 0;
494 int left_unreachable;
495 int right_unreachable;
496
497 if (aor) {
498 remove_unavailable = aor->remove_unavailable;
499 ao2_ref(aor, -1);
500 }
501
502 if (!remove_unavailable) {
503 return time_sorted;
504 }
505
506 /* Get contact status if available */
507 left_status = ast_sip_get_contact_status(left_contact);
508 if (!left_status) {
509 return time_sorted;
510 }
511
512 right_status = ast_sip_get_contact_status(right_contact);
513 if (!right_status) {
514 ao2_ref(left_status, -1);
515 return time_sorted;
516 }
517
518 left_unreachable = (left_status->status == UNAVAILABLE);
519 right_unreachable = (right_status->status == UNAVAILABLE);
520 ao2_ref(left_status, -1);
521 ao2_ref(right_status, -1);
522 if (left_unreachable != right_unreachable) {
523 /* Set unavailable contact to top of vector */
524 if (left_unreachable) return -1;
525 if (right_unreachable) return 1;
526 }
527
528 /* Either both available or both unavailable */
529 return time_sorted;
530}
531
532static int vec_contact_add(void *obj, void *arg, int flags)
533{
534 struct ast_sip_contact *contact = obj;
535 struct excess_contact_vector *contact_vec = arg;
536
537 /*
538 * Performance wise, an insertion sort is fine because we
539 * shouldn't need to remove more than a handful of contacts.
540 * I expect we'll typically be removing only one contact.
541 */
542 AST_VECTOR_ADD_SORTED(contact_vec, contact, vec_contact_cmp);
543 if (AST_VECTOR_SIZE(contact_vec) == AST_VECTOR_MAX_SIZE(contact_vec)) {
544 /*
545 * We added a contact over the number we need to remove.
546 * Remove the longest to expire contact from the vector
547 * which is the last element in the vector. It may be
548 * the one we just added or the one we just added pushed
549 * out an earlier contact from removal consideration.
550 */
551 --AST_VECTOR_SIZE(contact_vec);
552 }
553 return 0;
554}
555
556/*!
557 * \internal
558 * \brief Remove excess existing contacts that are unavailable or expire soonest.
559 * \since 13.18.0
560 *
561 * \param contacts Container of unmodified contacts that could remove.
562 * \param to_remove Maximum number of contacts to remove.
563 * \param response_contacts, remove_existing
564 */
565static void remove_excess_contacts(struct ao2_container *contacts, struct ao2_container *response_contacts,
566 unsigned int to_remove, unsigned int remove_existing)
567{
568 struct excess_contact_vector contact_vec;
569
570 /*
571 * Create a sorted vector to hold the to_remove soonest to
572 * expire contacts. The vector has an extra space to
573 * temporarily hold the longest to expire contact that we
574 * won't remove.
575 */
576 if (AST_VECTOR_INIT(&contact_vec, to_remove + 1)) {
577 return;
578 }
579 ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, vec_contact_add, &contact_vec);
580
581 /*
582 * The vector should always be populated with the number
583 * of contacts we need to remove. Just in case, we will
584 * remove all contacts in the vector even if the contacts
585 * container had fewer contacts than there should be.
586 */
587 ast_assert(AST_VECTOR_SIZE(&contact_vec) == to_remove);
588 to_remove = AST_VECTOR_SIZE(&contact_vec);
589
590 /* Remove the excess contacts that are unavailable or expire the soonest */
591 while (to_remove--) {
592 struct ast_sip_contact *contact;
593
594 contact = AST_VECTOR_GET(&contact_vec, to_remove);
595
596 if (!remove_existing) {
598 } else {
600 }
601
602 ao2_unlink(response_contacts, contact);
603 }
604
605 AST_VECTOR_FREE(&contact_vec);
606}
607
608/*! \brief Callback function which adds non-permanent contacts to a container */
609static int registrar_add_non_permanent(void *obj, void *arg, int flags)
610{
611 struct ast_sip_contact *contact = obj;
612 struct ao2_container *container = arg;
613
614 if (ast_tvzero(contact->expiration_time)) {
615 return 0;
616 }
617
618 ao2_link(container, contact);
619
620 return 0;
621}
622
623/*! \brief Internal callback function which adds any contact which is unreachable */
624static int registrar_add_unreachable(void *obj, void *arg, int flags)
625{
626 struct ast_sip_contact *contact = obj;
627 struct ao2_container *container = arg;
629 int unreachable;
630
632 if (!status) {
633 return 0;
634 }
635
636 unreachable = (status->status == UNAVAILABLE);
637 ao2_ref(status, -1);
638
639 if (unreachable) {
640 ao2_link(container, contact);
641 }
642
643 return 0;
644}
645
647 /*! Tx data to use for statefull response. NULL for stateless response. */
648 pjsip_tx_data *tdata;
649 /*! SIP response code to send in stateless response */
650 int code;
651};
652
653static void register_aor_core(pjsip_rx_data *rdata,
654 struct ast_sip_endpoint *endpoint,
655 struct ast_sip_aor *aor,
656 const char *aor_name,
657 struct ao2_container *contacts,
658 struct aor_core_response *response)
659{
660 static const pj_str_t USER_AGENT = { "User-Agent", 10 };
661
662 int added = 0;
663 int updated = 0;
664 int deleted = 0;
665 int permanent = 0;
666 int contact_count;
667 struct ao2_container *existing_contacts = NULL;
668 struct ao2_container *unavail_contacts = NULL;
669 pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
670 struct registrar_contact_details details = { 0, };
671 pjsip_tx_data *tdata;
672 RAII_VAR(struct ast_str *, path_str, NULL, ast_free);
673 struct ast_sip_contact *response_contact;
674 char *user_agent = NULL;
675 pjsip_user_agent_hdr *user_agent_hdr;
676 pjsip_expires_hdr *expires_hdr;
677 pjsip_via_hdr *via_hdr;
678 pjsip_via_hdr *via_hdr_last;
679 char *via_addr = NULL;
680 int via_port = 0;
681 pjsip_cid_hdr *call_id_hdr;
682 char *call_id = NULL;
683 size_t alloc_size;
684
685 /* We create a single pool and use it throughout this function where we need one */
686 details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
687 "Contact Comparison", 1024, 256);
688 if (!details.pool) {
689 response->code = 500;
690 return;
691 }
692
693 /* If there are any permanent contacts configured on the AOR we need to take them
694 * into account when counting contacts.
695 */
696 if (aor->permanent_contacts) {
697 permanent = ao2_container_count(aor->permanent_contacts);
698 }
699
700 if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) {
701 /* The provided Contact headers do not conform to the specification */
702 ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
703 ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
705 response->code = 400;
706 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
707 return;
708 }
709
710 if (registrar_validate_path(rdata, aor, &path_str)) {
711 /* Ensure that intervening proxies did not make invalid modifications to the request */
712 ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
714 response->code = 420;
715 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
716 return;
717 }
718
719 if (aor->remove_existing) {
720 /* Cumulative number of contacts affected by this registration */
721 contact_count = MAX(updated + added - deleted, 0);
722
723 /* We need to keep track of only existing contacts so we can later
724 * remove them if need be.
725 */
728 if (!existing_contacts) {
729 response->code = 500;
730 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
731 return;
732 }
733
734 ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts);
735 } else {
736 /* Total contacts after this registration */
737 contact_count = ao2_container_count(contacts) - permanent + added - deleted;
738 }
739
740 if (contact_count > aor->max_contacts && aor->remove_unavailable) {
741 /* Get unavailable contact total */
742 int unavail_count = 0;
743
746 if (!unavail_contacts) {
747 response->code = 500;
748 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
749 return;
750 }
751 ao2_callback(contacts, OBJ_NODATA, registrar_add_unreachable, unavail_contacts);
752 if (unavail_contacts) {
753 unavail_count = ao2_container_count(unavail_contacts);
754 }
755
756 /* Check to see if removing unavailable contacts will help */
757 if (contact_count - unavail_count <= aor->max_contacts) {
758 /* Remove any unavailable contacts */
759 remove_excess_contacts(unavail_contacts, contacts, contact_count - aor->max_contacts, aor->remove_existing);
760 ao2_cleanup(unavail_contacts);
761 /* We're only here if !aor->remove_existing so this count is correct */
762 contact_count = ao2_container_count(contacts) - permanent + added - deleted;
763 }
764 }
765
766 if (contact_count > aor->max_contacts) {
767 /* Enforce the maximum number of contacts */
768 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
769 ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' (%s:%d) to AOR '%s' will exceed max contacts of %u\n",
770 ast_sorcery_object_get_id(endpoint), rdata->pkt_info.src_name, rdata->pkt_info.src_port,
771 aor_name, aor->max_contacts);
772 response->code = 403;
773 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
774 ao2_cleanup(existing_contacts);
775 return;
776 }
777
778 user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL);
779 if (user_agent_hdr) {
780 alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1;
781 user_agent = ast_alloca(alloc_size);
782 ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size);
783 }
784
785 /* Find the first Via header */
786 via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL);
787 if (via_hdr) {
788 /* Find the last Via header */
789 while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg,
790 PJSIP_H_VIA, via_hdr->next)) != NULL) {
791 via_hdr_last = via_hdr;
792 }
793 alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1;
794 via_addr = ast_alloca(alloc_size);
795 ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size);
796 via_port=via_hdr_last->sent_by.port;
797 }
798
799 call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL);
800 if (call_id_hdr) {
801 alloc_size = pj_strlen(&call_id_hdr->id) + 1;
802 call_id = ast_alloca(alloc_size);
803 ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size);
804 }
805
806 /* Iterate each provided Contact header and add, update, or delete */
807 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)) {
808 int expiration;
809 char contact_uri[pjsip_max_url_size];
810 RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
811
812 if (contact_hdr->star) {
813 /* A star means to unregister everything, so do so for the possible contacts */
815 registrar_delete_contact, (void *)aor_name);
816 /* If we are keeping track of existing contacts for removal then, well, there is
817 * absolutely nothing left so no need to try to remove any.
818 */
819 if (existing_contacts) {
820 ao2_ref(existing_contacts, -1);
821 existing_contacts = NULL;
822 }
823 break;
824 }
825
826 if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
827 /* This registrar only currently supports sip: and sips: URI schemes */
828 continue;
829 }
830
831 expiration = registrar_get_expiration(aor, contact_hdr, rdata);
832 details.uri = pjsip_uri_get_uri(contact_hdr->uri);
833 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
834
835 contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details);
836
837 /* If a contact was returned and we need to keep track of existing contacts then it
838 * should be removed.
839 */
840 if (contact && existing_contacts) {
841 ao2_unlink(existing_contacts, contact);
842 }
843
844 if (!contact) {
845 int prune_on_boot;
846
847 /* If they are actually trying to delete a contact that does not exist... be forgiving */
848 if (!expiration) {
849 ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
850 contact_uri, aor_name);
851 continue;
852 }
853
855
856 contact = ast_sip_location_create_contact(aor, contact_uri,
857 ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)),
858 path_str ? ast_str_buffer(path_str) : NULL,
860 if (!contact) {
861 ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
862 contact_uri, aor_name);
863 continue;
864 }
865
866 if (prune_on_boot) {
867 size_t contact_name_len;
868 const char *contact_name;
869 struct contact_transport_monitor *monitor;
870
871 /*
872 * Monitor the transport in case it gets disconnected because
873 * the contact won't be valid anymore if that happens.
874 */
876 contact_name_len = strlen(contact_name) + 1;
877 monitor = ao2_alloc(sizeof(*monitor) + 1 + strlen(aor_name)
878 + contact_name_len, NULL);
879 if (monitor) {
880 strcpy(monitor->aor_name, aor_name);/* Safe */
881 monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1;
882 ast_copy_string(monitor->contact_name, contact_name, contact_name_len);/* Safe */
883
884 ast_sip_transport_monitor_register_replace(rdata->tp_info.transport,
886 ao2_ref(monitor, -1);
887 }
888 }
889
890 ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
891 contact_uri, aor_name, expiration);
892 ast_test_suite_event_notify("AOR_CONTACT_ADDED",
893 "Contact: %s\r\n"
894 "AOR: %s\r\n"
895 "Expiration: %d\r\n"
896 "UserAgent: %s",
897 contact_uri,
898 aor_name,
899 expiration,
900 user_agent);
901
902 ao2_link(contacts, contact);
903 } else if (expiration) {
904 struct ast_sip_contact *contact_update;
905
906 contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact);
907 if (!contact_update) {
908 ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
909 contact->uri, expiration);
910 continue;
911 }
912
913 contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
914 contact_update->qualify_frequency = aor->qualify_frequency;
915 contact_update->authenticate_qualify = aor->authenticate_qualify;
916 if (path_str) {
917 ast_string_field_set(contact_update, path, ast_str_buffer(path_str));
918 }
919 if (user_agent) {
921 }
924 }
925
926 if (ast_sip_location_update_contact(contact_update)) {
927 ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
928 contact->uri, expiration);
929 registrar_contact_delete(CONTACT_DELETE_ERROR, rdata->tp_info.transport,
930 contact, aor_name);
931 continue;
932 }
933 ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
934 contact_uri, aor_name, expiration);
935 ast_test_suite_event_notify("AOR_CONTACT_REFRESHED",
936 "Contact: %s\r\n"
937 "AOR: %s\r\n"
938 "Expiration: %d\r\n"
939 "UserAgent: %s",
940 contact_uri,
941 aor_name,
942 expiration,
943 contact_update->user_agent);
944 ao2_link(contacts, contact_update);
945 ao2_cleanup(contact_update);
946 } else {
947 registrar_contact_delete(CONTACT_DELETE_REQUEST, rdata->tp_info.transport,
948 contact, aor_name);
949 }
950 }
951
952 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
953
954 /*
955 * If the AOR is configured to remove any contacts over max_contacts
956 * that have not been updated/added/deleted as a result of this
957 * REGISTER do so.
958 *
959 * The existing contacts container holds all contacts that were not
960 * involved in this REGISTER.
961 * The contacts container holds the current contacts of the AOR.
962 */
963 if (aor->remove_existing && existing_contacts) {
964 /* Total contacts after this registration */
965 contact_count = ao2_container_count(existing_contacts) + updated + added;
966 if (contact_count > aor->max_contacts) {
967 /* Remove excess existing contacts that are unavailable or expire soonest */
968 remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts,
969 aor->remove_existing);
970 }
971 ao2_ref(existing_contacts, -1);
972 }
973
974 response_contact = ao2_callback(contacts, 0, NULL, NULL);
975
976 /* Send a response containing all of the contacts (including static) that are present on this AOR */
977 if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
978 ao2_cleanup(response_contact);
979 ao2_cleanup(contacts);
980 response->code = 500;
981 return;
982 }
983 ao2_cleanup(response_contact);
984
985 /* Add the date header to the response, some UAs use this to set their date and time */
987
988 ao2_callback(contacts, 0, registrar_add_contact, tdata);
989
990 if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
991 expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata));
992 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
993 }
994
995 response->tdata = tdata;
996}
997
998static int register_aor(pjsip_rx_data *rdata,
1000 struct ast_sip_aor *aor,
1001 const char *aor_name)
1002{
1003 struct aor_core_response response = {
1004 .code = 500,
1005 };
1006 struct ao2_container *contacts = NULL;
1007
1008 ao2_lock(aor);
1010 if (!contacts) {
1011 ao2_unlock(aor);
1012 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(),
1013 rdata, response.code, NULL, NULL, NULL);
1014 return PJ_TRUE;
1015 }
1016
1017 register_aor_core(rdata, endpoint, aor, aor_name, contacts, &response);
1018 ao2_cleanup(contacts);
1019 ao2_unlock(aor);
1020
1021 /* Now send the REGISTER response to the peer */
1022 if (response.tdata) {
1023 ast_sip_send_stateful_response(rdata, response.tdata, endpoint);
1024 } else {
1025 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(),
1026 rdata, response.code, NULL, NULL, NULL);
1027 }
1028 return PJ_TRUE;
1029}
1030
1031static int match_aor(const char *aor_name, const char *id)
1032{
1033 if (ast_strlen_zero(aor_name)) {
1034 return 0;
1035 }
1036
1037 if (!strcmp(aor_name, id)) {
1038 ast_debug(3, "Matched id '%s' to aor '%s'\n", id, aor_name);
1039 return 1;
1040 }
1041
1042 return 0;
1043}
1044
1045static char *find_aor_name(const pj_str_t *pj_username, const pj_str_t *pj_domain, const char *aors)
1046{
1047 char *configured_aors;
1048 char *aors_buf;
1049 char *aor_name;
1050 char *id_domain;
1051 char *username, *domain;
1052 struct ast_sip_domain_alias *alias;
1053
1054 /* Turn these into C style strings for convenience */
1055 username = ast_alloca(pj_strlen(pj_username) + 1);
1056 ast_copy_pj_str(username, pj_username, pj_strlen(pj_username) + 1);
1057 domain = ast_alloca(pj_strlen(pj_domain) + 1);
1058 ast_copy_pj_str(domain, pj_domain, pj_strlen(pj_domain) + 1);
1059
1060 id_domain = ast_alloca(strlen(username) + strlen(domain) + 2);
1061 sprintf(id_domain, "%s@%s", username, domain);
1062
1063 aors_buf = ast_strdupa(aors);
1064
1065 /* Look for exact match on username@domain */
1066 configured_aors = aors_buf;
1067 while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
1068 if (match_aor(aor_name, id_domain)) {
1069 return ast_strdup(aor_name);
1070 }
1071 }
1072
1073 /* If there's a domain alias, look for exact match on username@domain_alias */
1074 alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain);
1075 if (alias) {
1076 char *id_domain_alias = ast_alloca(strlen(username) + strlen(alias->domain) + 2);
1077
1078 sprintf(id_domain_alias, "%s@%s", username, alias->domain);
1079 ao2_cleanup(alias);
1080
1081 configured_aors = strcpy(aors_buf, aors);/* Safe */
1082 while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
1083 if (match_aor(aor_name, id_domain_alias)) {
1084 return ast_strdup(aor_name);
1085 }
1086 }
1087 }
1088
1089 if (ast_strlen_zero(username)) {
1090 /* No username, no match */
1091 return NULL;
1092 }
1093
1094 /* Look for exact match on username only */
1095 configured_aors = strcpy(aors_buf, aors);/* Safe */
1096 while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
1097 if (match_aor(aor_name, username)) {
1098 return ast_strdup(aor_name);
1099 }
1100 }
1101
1102 return NULL;
1103}
1104
1105static struct ast_sip_aor *find_registrar_aor(struct pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
1106{
1107 struct ast_sip_aor *aor = NULL;
1108 char *aor_name = NULL;
1109 int i;
1110
1111 for (i = 0; i < AST_VECTOR_SIZE(&endpoint->ident_method_order); ++i) {
1112 pj_str_t username;
1113 pjsip_sip_uri *uri;
1114 pjsip_authorization_hdr *header = NULL;
1115
1116 switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) {
1118 uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
1119
1120 pj_strassign(&username, &uri->user);
1121
1122 /*
1123 * We may want to match without any user options getting
1124 * in the way.
1125 *
1126 * Logic adapted from AST_SIP_USER_OPTIONS_TRUNCATE_CHECK for pj_str_t.
1127 */
1129 pj_ssize_t semi = pj_strcspn2(&username, ";");
1130 if (semi < pj_strlen(&username)) {
1131 username.slen = semi;
1132 }
1133 }
1134
1135 aor_name = find_aor_name(&username, &uri->host, endpoint->aors);
1136 if (aor_name) {
1137 ast_debug(3, "Matched aor '%s' by To username\n", aor_name);
1138 }
1139 break;
1141 while ((header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION,
1142 header ? header->next : NULL))) {
1143 if (header && !pj_stricmp2(&header->scheme, "digest")) {
1144 aor_name = find_aor_name(&header->credential.digest.username,
1145 &header->credential.digest.realm, endpoint->aors);
1146 if (aor_name) {
1147 ast_debug(3, "Matched aor '%s' by Authentication username\n", aor_name);
1148 break;
1149 }
1150 }
1151 }
1152 break;
1153 default:
1154 continue;
1155 }
1156
1157 if (aor_name) {
1158 break;
1159 }
1160 }
1161
1162 if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) {
1163 /* The provided AOR name was not found (be it within the configuration or sorcery itself) */
1164 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
1165 ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
1166 ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s' (%s:%d)\n",
1167 aor_name ?: "", ast_sorcery_object_get_id(endpoint),
1168 rdata->pkt_info.src_name, rdata->pkt_info.src_port);
1169 }
1170 ast_free(aor_name);
1171 return aor;
1172}
1173
1174static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
1175{
1176 RAII_VAR(struct ast_sip_endpoint *, endpoint,
1178 struct ast_sip_aor *aor;
1179 const char *aor_name;
1180
1181 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
1182 return PJ_FALSE;
1183 }
1184
1185 if (ast_strlen_zero(endpoint->aors)) {
1186 /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
1187 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
1188 ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
1189 ast_log(LOG_WARNING, "Endpoint '%s' (%s:%d) has no configured AORs\n", ast_sorcery_object_get_id(endpoint),
1190 rdata->pkt_info.src_name, rdata->pkt_info.src_port);
1191 return PJ_TRUE;
1192 }
1193
1194 if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
1195 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
1196 ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
1197 ast_log(LOG_WARNING, "Endpoint '%s' (%s:%d) attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint),
1198 rdata->pkt_info.src_name, rdata->pkt_info.src_port);
1199 return PJ_TRUE;
1200 }
1201
1202 aor = find_registrar_aor(rdata, endpoint);
1203 if (!aor) {
1204 /* We've already responded about not finding an AOR. */
1205 return PJ_TRUE;
1206 }
1207
1208 aor_name = ast_sorcery_object_get_id(aor);
1209
1210 if (!aor->max_contacts) {
1211 /* Registration is not permitted for this AOR */
1212 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
1213 ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted");
1214 ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' (%s:%d) unable to register\n",
1215 aor_name, ast_sorcery_object_get_id(endpoint),
1216 rdata->pkt_info.src_name, rdata->pkt_info.src_port);
1217 } else {
1218 register_aor(rdata, endpoint, aor, aor_name);
1219 }
1220 ao2_ref(aor, -1);
1221 return PJ_TRUE;
1222}
1223
1224/* function pointer to callback needs to be within the module
1225 in order to avoid problems with an undefined symbol */
1226static int sip_contact_to_str(void *acp, void *arg, int flags)
1227{
1228 return ast_sip_contact_to_str(acp, arg, flags);
1229}
1230
1231static int ami_registrations_aor(void *obj, void *arg, int flags)
1232{
1233 struct ast_sip_aor *aor = obj;
1234 struct ast_sip_ami *ami = arg;
1235 int *count = ami->arg;
1236 RAII_VAR(struct ast_str *, buf,
1237 ast_sip_create_ami_event("InboundRegistrationDetail", ami), ast_free);
1238
1239 if (!buf) {
1240 return -1;
1241 }
1242
1244 ast_str_append(&buf, 0, "Contacts: ");
1246 ast_str_append(&buf, 0, "\r\n");
1247
1248 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
1249 (*count)++;
1250 return 0;
1251}
1252
1253static int ami_registrations_endpoint(void *obj, void *arg, int flags)
1254{
1255 struct ast_sip_endpoint *endpoint = obj;
1256 return ast_sip_for_each_aor(
1257 endpoint->aors, ami_registrations_aor, arg);
1258}
1259
1260static int ami_registrations_endpoints(void *arg)
1261{
1264
1265 if (!endpoints) {
1266 return 0;
1267 }
1268
1270 return 0;
1271}
1272
1273static int ami_show_registrations(struct mansession *s, const struct message *m)
1274{
1275 int count = 0;
1276 struct ast_sip_ami ami = { .s = s, .m = m, .arg = &count, .action_id = astman_get_header(m, "ActionID"), };
1277
1278 astman_send_listack(s, m, "Following are Events for each Inbound registration",
1279 "start");
1280
1282
1283 astman_send_list_complete_start(s, m, "InboundRegistrationDetailComplete", count);
1285 return 0;
1286}
1287
1289{
1290 int count = 0;
1291 struct ast_sip_ami ami = { .s = s, .m = m, .arg = NULL, .action_id = astman_get_header(m, "ActionID"), };
1294 struct ao2_iterator i;
1295 struct ast_sip_contact *contact;
1296
1297 astman_send_listack(s, m, "Following are ContactStatusEvents for each Inbound "
1298 "registration", "start");
1299
1300 if (contacts) {
1301 i = ao2_iterator_init(contacts, 0);
1302 while ((contact = ao2_iterator_next(&i))) {
1303 struct ast_sip_contact_wrapper wrapper;
1304
1305 wrapper.aor_id = (char *)contact->aor;
1306 wrapper.contact = contact;
1307 wrapper.contact_id = (char *)ast_sorcery_object_get_id(contact);
1308
1309 ast_sip_format_contact_ami(&wrapper, &ami, 0);
1310 count++;
1311
1312 ao2_ref(contact, -1);
1313 }
1315 ao2_ref(contacts, -1);
1316 }
1317
1318 astman_send_list_complete_start(s, m, "ContactStatusDetailComplete", count);
1320 return 0;
1321}
1322
1323#define AMI_SHOW_REGISTRATION_CONTACT_STATUSES "PJSIPShowRegistrationInboundContactStatuses"
1324#define AMI_SHOW_REGISTRATIONS "PJSIPShowRegistrationsInbound"
1325
1326static pjsip_module registrar_module = {
1327 .name = { "Registrar", 9 },
1328 .id = -1,
1329 .priority = PJSIP_MOD_PRIORITY_APPLICATION,
1330 .on_rx_request = registrar_on_rx_request,
1331};
1332
1333/*! \brief Thread keeping things alive */
1335
1336/*! \brief The global interval at which to check for contact expiration */
1337static unsigned int check_interval;
1338
1339/*! \brief Callback function which deletes a contact */
1340static int expire_contact(void *obj, void *arg, int flags)
1341{
1342 struct ast_sip_contact *contact = obj;
1343 struct ast_named_lock *lock;
1344
1346 if (!lock) {
1347 return 0;
1348 }
1349
1350 /*
1351 * We need to check the expiration again with the aor lock held
1352 * in case another thread is attempting to renew the contact.
1353 */
1354 ao2_lock(lock);
1355 if (ast_tvdiff_ms(ast_tvnow(), contact->expiration_time) > 0) {
1357 }
1360
1361 return 0;
1362}
1363
1364static void *check_expiration_thread(void *data)
1365{
1366 struct ao2_container *contacts;
1367 struct ast_variable *var;
1368 char time[AST_TIME_T_LEN];
1369
1370 while (check_interval) {
1371 sleep(check_interval);
1372
1373 ast_time_t_to_string(ast_tvnow().tv_sec, time, sizeof(time));
1374
1375 var = ast_variable_new("expiration_time <=", time, "");
1376
1377 ast_debug(4, "Woke up at %s Interval: %d\n", time, check_interval);
1378
1381
1383 if (contacts) {
1384 ast_debug(3, "Expiring %d contacts\n", ao2_container_count(contacts));
1386 ao2_ref(contacts, -1);
1387 }
1388 }
1389
1390 return NULL;
1391}
1392
1393static void expiration_global_loaded(const char *object_type)
1394{
1396
1397 /* Observer calls are serialized so this is safe without it's own lock */
1398 if (check_interval) {
1401 ast_log(LOG_ERROR, "Could not create thread for checking contact expiration.\n");
1402 return;
1403 }
1404 ast_debug(3, "Interval = %d, starting thread\n", check_interval);
1405 }
1406 } else {
1408 pthread_kill(check_thread, SIGURG);
1409 pthread_join(check_thread, NULL);
1411 ast_debug(3, "Interval = 0, shutting thread down\n");
1412 }
1413 }
1414}
1415
1416/*! \brief Observer which is used to update our interval when the global setting changes */
1419};
1420
1421static int load_module(void)
1422{
1423 const pj_str_t STR_REGISTER = { "REGISTER", 8 };
1424
1425 ast_pjproject_get_buildopt("PJ_MAX_HOSTNAME", "%d", &pj_max_hostname);
1426 /* As of pjproject 2.4.5, PJSIP_MAX_URL_SIZE isn't exposed yet but we try anyway. */
1427 ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size);
1428
1431 }
1432
1433 if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) {
1436 }
1437
1442
1445
1447}
1448
1449static int unload_module(void)
1450{
1452 check_interval = 0;
1453 pthread_kill(check_thread, SIGURG);
1454 pthread_join(check_thread, NULL);
1455
1457 }
1458
1460
1465 return 0;
1466}
1467
1468AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Registrar Support",
1469 .support_level = AST_MODULE_SUPPORT_CORE,
1470 .load = load_module,
1471 .unload = unload_module,
1472 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 3,
1473 .requires = "res_pjproject,res_pjsip",
jack_status_t status
Definition: app_jack.c:146
ast_mutex_t lock
Definition: app_sla.c:331
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static const char type[]
Definition: chan_ooh323.c:109
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:3423
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:3459
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3042
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3467
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3302
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8041
#define ast_named_lock_put(lock)
Put a named lock handle away.
Definition: named_locks.h:93
#define ast_named_lock_get(lock_type, keyspace, key)
Geta named lock handle.
Definition: named_locks.h:83
@ AST_NAMED_LOCK_TYPE_MUTEX
Definition: named_locks.h:59
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:2099
char * strsep(char **str, const char *delims)
#define ast_variable_new(name, value, filename)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define VERBOSITY_ATLEAST(level)
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
#define AST_PTHREADT_NULL
Definition: lock.h:66
static struct ao2_container * endpoints
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_SYSTEM
Definition: manager.h:75
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Named Locks.
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_SYSTEM_NAME
Definition: options.c:170
struct ao2_container * container
Definition: res_fax.c:501
int ast_pjproject_get_buildopt(char *option, char *format_string,...)
Retrieve a pjproject build option.
int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Definition: res_pjsip.c:525
void ast_sip_unregister_service(pjsip_module *module)
Definition: res_pjsip.c:133
@ AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME
Definition: res_pjsip.h:611
@ AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME
Definition: res_pjsip.h:613
void ast_sip_report_req_no_support(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *req_type)
Send a security event notification for when a request is not supported.
struct ao2_container * ast_sip_get_endpoints(void)
Retrieve any endpoints available to sorcery.
int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code, struct ast_sip_contact *contact, pjsip_tx_data **p_tdata)
General purpose method for creating a SIP response.
Definition: res_pjsip.c:2468
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition: res_pjsip.c:117
struct ast_sip_aor * ast_sip_location_retrieve_aor(const char *aor_name)
Retrieve a named AOR.
Definition: location.c:147
int ast_sip_for_each_contact(const struct ast_sip_aor *aor, ao2_callback_fn on_contact, void *arg)
For every contact on an AOR call the given 'on_contact' handler.
Definition: location.c:722
int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
For every aor in the comma separated aors string call the given 'on_aor' handler.
Definition: location.c:687
void ast_sip_add_date_header(pjsip_tx_data *tdata)
Adds a Date header to the tdata, formatted like: Date: Wed, 01 Jan 2021 14:53:01 GMT.
Definition: res_pjsip.c:90
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb, void *ao2_data, ast_transport_monitor_data_matcher matches)
Register a reliable transport shutdown monitor callback replacing any duplicate.
void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a reliable transport shutdown monitor.
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:520
struct ast_sip_endpoint * ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
Get the looked-up endpoint on an out-of dialog request or response.
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition: res_pjsip.c:2201
void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a transport shutdown monitor from all reliable transports.
int ast_sip_format_contact_ami(void *obj, void *arg, int flags)
Formats the contact and sends over AMI.
unsigned int ast_sip_get_contact_expiration_check_interval(void)
Retrieve the system contact expiration check interval setting.
struct ao2_container * ast_sip_location_retrieve_aor_contacts_nolock(const struct ast_sip_aor *aor)
Retrieve all contacts currently available for an AOR without locking the AOR.
Definition: location.c:214
unsigned int ast_sip_get_ignore_uri_user_options(void)
Retrieve the global setting 'ignore_uri_user_options'.
struct ast_sip_contact * ast_sip_location_retrieve_contact(const char *contact_name)
Retrieve a named contact.
Definition: location.c:350
int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf)
Converts a sorcery object to a string of object properties.
struct ast_str * ast_sip_create_ami_event(const char *event, struct ast_sip_ami *ami)
Creates a string to store AMI event data in.
struct ast_sip_contact * ast_sip_location_create_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time, const char *path_info, const char *user_agent, const char *via_addr, int via_port, const char *call_id, int prune_on_boot, struct ast_sip_endpoint *endpoint)
Create a new contact for an AOR without locking the AOR.
Definition: location.c:355
#define PJSIP_EXPIRES_NOT_SPECIFIED
Definition: res_pjsip.h:68
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
struct ast_sip_contact_status * ast_sip_get_contact_status(const struct ast_sip_contact *contact)
Retrieve the current status for a contact.
int ast_sip_location_update_contact(struct ast_sip_contact *contact)
Update a contact.
Definition: location.c:445
int ast_sip_send_stateful_response(pjsip_rx_data *rdata, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint)
Send a stateful response to an out of dialog request.
Definition: res_pjsip.c:2424
@ UNAVAILABLE
Definition: res_pjsip.h:436
int ast_sip_contact_to_str(void *object, void *arg, int flags)
Handler used to convert a contact to a string.
Definition: location.c:770
int ast_sip_location_delete_contact(struct ast_sip_contact *contact)
Delete a contact.
Definition: location.c:450
void ast_sip_report_failed_acl(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *name)
Send a security event notification for when an ACL check fails.
static int ami_registrations_endpoint(void *obj, void *arg, int flags)
static int vec_contact_cmp(struct ast_sip_contact *left, struct ast_sip_contact *right)
static int registrar_contact_delete(enum contact_delete_type type, pjsip_transport *transport, struct ast_sip_contact *contact, const char *aor_name)
static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor, struct ast_str **path_str)
static int match_aor(const char *aor_name, const char *id)
static int registrar_add_unreachable(void *obj, void *arg, int flags)
Internal callback function which adds any contact which is unreachable.
static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
static int ami_registrations_endpoints(void *arg)
static int register_contact_transport_remove_cb(void *data)
static void * check_expiration_thread(void *data)
static int sip_contact_to_str(void *acp, void *arg, int flags)
#define AMI_SHOW_REGISTRATION_CONTACT_STATUSES
static int pjsip_max_url_size
static pthread_t check_thread
Thread keeping things alive.
static int registrar_find_contact(void *obj, void *arg, int flags)
Callback function for finding a contact.
static int ami_show_registration_contact_statuses(struct mansession *s, const struct message *m)
static void expiration_global_loaded(const char *object_type)
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 void remove_excess_contacts(struct ao2_container *contacts, struct ao2_container *response_contacts, unsigned int to_remove, unsigned int remove_existing)
static int registrar_add_contact(void *obj, void *arg, int flags)
Internal function which adds a contact to a response.
static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
Internal function which returns the expiration time for a contact.
static int vec_contact_add(void *obj, void *arg, int flags)
static int expire_contact(void *obj, void *arg, int flags)
Callback function which deletes a contact.
static int ami_registrations_aor(void *obj, void *arg, int flags)
static struct ast_sip_aor * find_registrar_aor(struct pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
static int register_aor(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name)
contact_delete_type
@ CONTACT_DELETE_EXISTING
@ CONTACT_DELETE_ERROR
@ CONTACT_DELETE_SHUTDOWN
@ CONTACT_DELETE_REQUEST
@ CONTACT_DELETE_EXPIRE
@ CONTACT_DELETE_UNAVAILABLE
static struct ast_sorcery_observer expiration_global_observer
Observer which is used to update our interval when the global setting changes.
static int pj_max_hostname
static unsigned int check_interval
The global interval at which to check for contact expiration.
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)
Internal function which validates provided Contact headers to confirm that they are acceptable,...
static pjsip_module registrar_module
#define AMI_SHOW_REGISTRATIONS
static int registrar_add_non_permanent(void *obj, void *arg, int flags)
Callback function which adds non-permanent contacts to a container.
static int load_module(void)
static void register_contact_transport_shutdown_cb(void *data)
static int unload_module(void)
static int contact_transport_monitor_matcher(void *a, void *b)
static const pj_str_t path_hdr_name
static int ami_show_registrations(struct mansession *s, const struct message *m)
static int registrar_delete_contact(void *obj, void *arg, int flags)
Internal function used to delete a contact from an AOR.
static char * find_aor_name(const pj_str_t *pj_username, const pj_str_t *pj_domain, const char *aors)
#define NULL
Definition: resample.c:96
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
Definition: sorcery.c:2418
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition: sorcery.h:120
@ AST_RETRIEVE_FLAG_ALL
Perform no matching, return all objects.
Definition: sorcery.h:123
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
Definition: sorcery.c:2386
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition: sorcery.c:2459
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
Definition: sorcery.c:1442
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
void * ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object)
Create a copy of an object.
Definition: sorcery.c:1778
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
pjsip_tx_data * tdata
AMI variable container.
Definition: res_pjsip.h:3047
struct mansession * s
Definition: res_pjsip.h:3049
void * arg
Definition: res_pjsip.h:3055
const struct message * m
Definition: res_pjsip.h:3051
A SIP address of record.
Definition: res_pjsip.h:478
unsigned int minimum_expiration
Definition: res_pjsip.h:488
unsigned int max_contacts
Definition: res_pjsip.h:498
unsigned int maximum_expiration
Definition: res_pjsip.h:490
unsigned int default_expiration
Definition: res_pjsip.h:492
A contact's status.
Definition: res_pjsip.h:451
enum ast_sip_contact_status_type status
Definition: res_pjsip.h:468
const ast_string_field aor
Definition: res_pjsip.h:457
A wrapper for contact that adds the aor_id and a consistent contact id. Used by ast_sip_for_each_cont...
Definition: res_pjsip.h:517
struct ast_sip_contact * contact
Definition: res_pjsip.h:523
Contact associated with an address of record.
Definition: res_pjsip.h:392
const ast_string_field uri
Definition: res_pjsip.h:414
struct ast_sip_endpoint * endpoint
Definition: res_pjsip.h:424
const ast_string_field via_addr
Definition: res_pjsip.h:414
const ast_string_field call_id
Definition: res_pjsip.h:414
const ast_string_field aor
Definition: res_pjsip.h:414
struct timeval expiration_time
Definition: res_pjsip.h:416
const ast_string_field path
Definition: res_pjsip.h:414
int authenticate_qualify
Definition: res_pjsip.h:420
const ast_string_field reg_server
Definition: res_pjsip.h:414
const ast_string_field user_agent
Definition: res_pjsip.h:414
unsigned int qualify_frequency
Definition: res_pjsip.h:418
const ast_string_field domain
Definition: res_pjsip.h:323
An entity with which Asterisk communicates.
Definition: res_pjsip.h:963
const ast_string_field aors
Definition: res_pjsip.h:992
struct ast_sip_identify_by_vector ident_method_order
Definition: res_pjsip.h:1016
Interface for a sorcery object type observer.
Definition: sorcery.h:332
void(* loaded)(const char *object_type)
Callback for when an object type is loaded/reloaded.
Definition: sorcery.h:343
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
char * contact_name
Sorcery contact name to remove on transport shutdown.
struct header * next
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:1777
Structure used for finding contact.
pj_pool_t * pool
Pool used for parsing URI.
pjsip_sip_uri * uri
URI being looked for.
An API for managing task processing threads that can be shared across modules.
Test Framework API.
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
static struct test_val b
static struct test_val a
int ast_time_t_to_string(time_t time, char *buf, size_t length)
Converts to a string representation of a time_t as decimal seconds since the epoch....
Definition: time.c:152
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:282
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compress two struct timeval instances returning -1, 0, 1 if the first arg is smaller,...
Definition: time.h:137
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
#define AST_TIME_T_LEN
Definition: time.h:45
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:592
#define MAX(a, b)
Definition: utils.h:233
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_MAX_SIZE(vec)
Get the maximum number of elements the vector can currently hold.
Definition: vector.h:617
#define AST_VECTOR_ADD_SORTED(vec, elem, cmp)
Add an element into a sorted vector.
Definition: vector.h:371
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680