Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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"
36 #include "asterisk/taskprocessor.h"
37 #include "asterisk/manager.h"
38 #include "asterisk/named_locks.h"
39 #include "asterisk/res_pjproject.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 
80 static int pj_max_hostname = PJ_MAX_HOSTNAME;
81 static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE;
82 
83 /*! \brief Internal function which returns the expiration time for a contact */
84 static 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 
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 */
121 static 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 */
141 static 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 
213 static 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 */
217 static 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 */
224 static 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 
251 static const pj_str_t path_hdr_name = { "Path", 4 };
252 
253 static 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 
275 static 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 */
317  int removing;
318  /*! AOR name the contact is associated */
319  char aor_name[0];
320 };
321 
322 static 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 
337  aor = ast_sip_location_retrieve_aor(monitor->aor_name);
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 
402 static 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) {
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 
482 static 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 
532 static 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  */
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  */
565 static 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 */
609 static 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 
619 
620  return 0;
621 }
622 
623 /*! \brief Internal callback function which adds any contact which is unreachable */
624 static 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) {
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 
653 static 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];
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 
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) {
920  ast_string_field_set(contact_update, user_agent, 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 
998 static int register_aor(pjsip_rx_data *rdata,
999  struct ast_sip_endpoint *endpoint,
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 
1031 static 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 
1045 static 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 
1105 static 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 
1174 static 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 */
1226 static int sip_contact_to_str(void *acp, void *arg, int flags)
1227 {
1228  return ast_sip_contact_to_str(acp, arg, flags);
1229 }
1230 
1231 static 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 
1253 static 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 
1260 static int ami_registrations_endpoints(void *arg)
1261 {
1262  RAII_VAR(struct ao2_container *, endpoints,
1264 
1265  if (!endpoints) {
1266  return 0;
1267  }
1268 
1270  return 0;
1271 }
1272 
1273 static 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"), };
1292  struct ao2_container *contacts = ast_sorcery_retrieve_by_fields(
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 
1326 static 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 */
1334 static pthread_t check_thread = AST_PTHREADT_NULL;
1335 
1336 /*! \brief The global interval at which to check for contact expiration */
1337 static unsigned int check_interval;
1338 
1339 /*! \brief Callback function which deletes a contact */
1340 static 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  }
1358  ao2_unlock(lock);
1360 
1361  return 0;
1362 }
1363 
1364 static 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 
1379  contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact",
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 
1393 static 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 
1421 static 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 
1430  return AST_MODULE_LOAD_DECLINE;
1431  }
1432 
1433  if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) {
1435  return AST_MODULE_LOAD_DECLINE;
1436  }
1437 
1442 
1445 
1446  return AST_MODULE_LOAD_SUCCESS;
1447 }
1448 
1449 static 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 
1468 AST_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",
1474 );
jack_status_t status
Definition: app_jack.c:146
ast_mutex_t lock
Definition: app_meetme.c:1093
#define var
Definition: ast_expr2f.c:614
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:3208
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:3244
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3252
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3087
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:2827
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7268
#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:1933
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.
struct ast_sip_contact_status * ast_sip_get_contact_status(const struct ast_sip_contact *contact)
Retrieve the current status for a contact.
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_sip_unregister_service(pjsip_module *module)
Definition: res_pjsip.c:126
@ AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME
Definition: res_pjsip.h:495
@ AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME
Definition: res_pjsip.h:497
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:2271
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition: res_pjsip.c:110
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
struct ast_sip_aor * ast_sip_location_retrieve_aor(const char *aor_name)
Retrieve a named AOR.
Definition: location.c:147
struct ast_sip_contact * ast_sip_location_retrieve_contact(const char *contact_name)
Retrieve a named contact.
Definition: location.c:350
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:83
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
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.
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.
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
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:513
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:2035
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.
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
unsigned int ast_sip_get_contact_expiration_check_interval(void)
Retrieve the system contact expiration check interval setting.
unsigned int ast_sip_get_ignore_uri_user_options(void)
Retrieve the global setting 'ignore_uri_user_options'.
int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf)
Converts a sorcery object to a string of object properties.
#define PJSIP_EXPIRES_NOT_SPECIFIED
Definition: res_pjsip.h:63
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:2243
@ UNAVAILABLE
Definition: res_pjsip.h:341
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.
int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Definition: res_pjsip.c:518
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 struct ast_sip_aor * find_registrar_aor(struct pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
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 int sip_contact_to_str(void *acp, void *arg, int flags)
static void * check_expiration_thread(void *data)
#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 char * find_aor_name(const pj_str_t *pj_username, const pj_str_t *pj_domain, const char *aors)
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 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.
#define NULL
Definition: resample.c:96
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
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
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
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_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
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
#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:1117
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:739
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:640
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:1091
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:406
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:2819
struct mansession * s
Definition: res_pjsip.h:2821
void * arg
Definition: res_pjsip.h:2827
const struct message * m
Definition: res_pjsip.h:2823
A SIP address of record.
Definition: res_pjsip.h:376
unsigned int minimum_expiration
Definition: res_pjsip.h:386
unsigned int max_contacts
Definition: res_pjsip.h:396
unsigned int maximum_expiration
Definition: res_pjsip.h:388
unsigned int default_expiration
Definition: res_pjsip.h:390
A contact's status.
Definition: res_pjsip.h:356
enum ast_sip_contact_status_type status
Definition: res_pjsip.h:366
const ast_string_field aor
Definition: res_pjsip.h:362
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:415
struct ast_sip_contact * contact
Definition: res_pjsip.h:421
Contact associated with an address of record.
Definition: res_pjsip.h:297
const ast_string_field uri
Definition: res_pjsip.h:319
struct ast_sip_endpoint * endpoint
Definition: res_pjsip.h:329
const ast_string_field via_addr
Definition: res_pjsip.h:319
const ast_string_field call_id
Definition: res_pjsip.h:319
const ast_string_field aor
Definition: res_pjsip.h:319
struct timeval expiration_time
Definition: res_pjsip.h:321
const ast_string_field path
Definition: res_pjsip.h:319
int authenticate_qualify
Definition: res_pjsip.h:325
const ast_string_field reg_server
Definition: res_pjsip.h:319
const ast_string_field user_agent
Definition: res_pjsip.h:319
unsigned int qualify_frequency
Definition: res_pjsip.h:323
const ast_string_field domain
Definition: res_pjsip.h:281
An entity with which Asterisk communicates.
Definition: res_pjsip.h:854
const ast_string_field aors
Definition: res_pjsip.h:883
struct ast_sip_identify_by_vector ident_method_order
Definition: res_pjsip.h:907
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:604
Structure for variables, used for configurations and for channel variables.
char * contact_name
Sorcery contact name to remove on transport shutdown.
char * expires
Domain data structure.
Definition: sip.h:887
struct header * next
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:1632
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:245
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:115
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:135
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:43
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:105
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:157
#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:936
#define ast_assert(a)
Definition: utils.h:734
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:587
#define MAX(a, b)
Definition: utils.h:228
#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