| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2013, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Joshua Colp <jcolp@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software, distributed under the terms of | 
					
						
							|  |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*** MODULEINFO
 | 
					
						
							| 
									
										
										
										
											2013-04-26 21:52:06 +00:00
										 |  |  | 	<depend>pjproject</depend> | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | 	<depend>res_pjproject</depend> | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | 	<depend>res_pjsip</depend> | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	<support_level>core</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <pjsip.h>
 | 
					
						
							|  |  |  | #include <pjsip_ua.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | #include "asterisk/res_pjsip.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | #include "asterisk/module.h"
 | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | #include "asterisk/test.h"
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | #include "asterisk/taskprocessor.h"
 | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | #include "asterisk/manager.h"
 | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | #include "asterisk/named_locks.h"
 | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | #include "asterisk/res_pjproject.h"
 | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | #include "res_pjsip/include/res_pjsip_private.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*** DOCUMENTATION
 | 
					
						
							|  |  |  | 	<manager name="PJSIPShowRegistrationsInbound" language="en_US"> | 
					
						
							|  |  |  | 		<synopsis> | 
					
						
							|  |  |  | 			Lists PJSIP inbound registrations. | 
					
						
							|  |  |  | 		</synopsis> | 
					
						
							|  |  |  | 		<syntax /> | 
					
						
							|  |  |  | 		<description> | 
					
						
							|  |  |  | 			<para> | 
					
						
							|  |  |  | 			In response <literal>InboundRegistrationDetail</literal> events showing configuration and status | 
					
						
							|  |  |  | 			information are raised for each inbound registration object.  As well as <literal>AuthDetail</literal> | 
					
						
							|  |  |  | 			events for each associated auth object.  Once all events are completed an | 
					
						
							|  |  |  | 			<literal>InboundRegistrationDetailComplete</literal> is issued. | 
					
						
							|  |  |  |                         </para> | 
					
						
							|  |  |  | 		</description> | 
					
						
							|  |  |  | 	</manager> | 
					
						
							|  |  |  |  ***/ | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | static int pj_max_hostname = PJ_MAX_HOSTNAME; | 
					
						
							|  |  |  | static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | /*! \brief Internal function which returns the expiration time for a contact */ | 
					
						
							|  |  |  | static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pjsip_expires_hdr *expires; | 
					
						
							|  |  |  | 	int expiration = aor->default_expiration; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-21 19:26:41 +00:00
										 |  |  | 	if (contact && contact->expires != -1) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		/* Expiration was provided with the contact itself */ | 
					
						
							|  |  |  | 		expiration = contact->expires; | 
					
						
							|  |  |  | 	} else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { | 
					
						
							|  |  |  | 		/* Expiration was provided using the Expires header */ | 
					
						
							|  |  |  | 		expiration = expires->ivalue; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If the value has explicitly been set to 0, do not enforce */ | 
					
						
							|  |  |  | 	if (!expiration) { | 
					
						
							|  |  |  | 		return expiration; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Enforce the range that we will allow for expiration */ | 
					
						
							|  |  |  | 	if (expiration < aor->minimum_expiration) { | 
					
						
							|  |  |  | 		expiration = aor->minimum_expiration; | 
					
						
							|  |  |  | 	} else if (expiration > aor->maximum_expiration) { | 
					
						
							|  |  |  | 		expiration = aor->maximum_expiration; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return expiration; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \brief Structure used for finding contact */ | 
					
						
							|  |  |  | struct registrar_contact_details { | 
					
						
							|  |  |  | 	/*! \brief Pool used for parsing URI */ | 
					
						
							|  |  |  | 	pj_pool_t *pool; | 
					
						
							|  |  |  | 	/*! \brief URI being looked for */ | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | 	pjsip_sip_uri *uri; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \brief Callback function for finding a contact */ | 
					
						
							|  |  |  | static int registrar_find_contact(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_sip_contact *contact = obj; | 
					
						
							|  |  |  | 	const struct registrar_contact_details *details = arg; | 
					
						
							|  |  |  | 	pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ | 
					
						
							|  |  |  | static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; | 
					
						
							|  |  |  | 	struct registrar_contact_details details = { | 
					
						
							|  |  |  | 		.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256), | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!details.pool) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { | 
					
						
							| 
									
										
										
										
											2013-06-22 14:03:22 +00:00
										 |  |  | 		int expiration = registrar_get_expiration(aor, contact, rdata); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup); | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | 		char contact_uri[pjsip_max_url_size]; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (contact->star) { | 
					
						
							|  |  |  | 			/* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ | 
					
						
							| 
									
										
										
										
											2013-06-22 14:03:22 +00:00
										 |  |  | 			if ((expiration != 0) || previous) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 				pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); | 
					
						
							|  |  |  | 				return -1; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2013-08-01 23:38:00 +00:00
										 |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		} else if (previous && previous->star) { | 
					
						
							|  |  |  | 			/* If there is a previous contact and it is a '*' this is a deal breaker */ | 
					
						
							|  |  |  | 			pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		previous = contact; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		details.uri = pjsip_uri_get_uri(contact->uri); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | 		/* pjsip_uri_print returns -1 if there's not enough room in the buffer */ | 
					
						
							|  |  |  | 		if (pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)) < 0) { | 
					
						
							|  |  |  | 			/* If the total length of the uri is greater than pjproject can handle, go no further */ | 
					
						
							|  |  |  | 			pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (details.uri->host.slen >= pj_max_hostname) { | 
					
						
							|  |  |  | 			/* If the length of the hostname is greater than pjproject can handle, go no further */ | 
					
						
							|  |  |  | 			pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		/* Determine if this is an add, update, or delete for policy enforcement purposes */ | 
					
						
							|  |  |  | 		if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) { | 
					
						
							|  |  |  | 			if (expiration) { | 
					
						
							|  |  |  | 				(*added)++; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else if (expiration) { | 
					
						
							|  |  |  | 			(*updated)++; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			(*deleted)++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* The provided contacts are acceptable, huzzah! */ | 
					
						
							|  |  |  | 	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \brief Callback function which prunes static contacts */ | 
					
						
							|  |  |  | static int registrar_prune_static(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_sip_contact *contact = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-17 22:50:23 +00:00
										 |  |  | /*! \brief Internal function used to delete a contact from an AOR */ | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | static int registrar_delete_contact(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_sip_contact *contact = obj; | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 	const char *aor_name = arg; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ast_sip_location_delete_contact(contact); | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 	if (!ast_strlen_zero(aor_name)) { | 
					
						
							|  |  |  | 		ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact->uri, aor_name); | 
					
						
							|  |  |  | 		ast_test_suite_event_notify("AOR_CONTACT_REMOVED", | 
					
						
							|  |  |  | 				"Contact: %s\r\n" | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 				"AOR: %s\r\n" | 
					
						
							|  |  |  | 				"UserAgent: %s", | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 				contact->uri, | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 				aor_name, | 
					
						
							|  |  |  | 				contact->user_agent); | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \brief Internal function which adds a contact to a response */ | 
					
						
							|  |  |  | static int registrar_add_contact(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_sip_contact *contact = obj; | 
					
						
							|  |  |  | 	pjsip_tx_data *tdata = arg; | 
					
						
							|  |  |  | 	pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool); | 
					
						
							|  |  |  | 	pj_str_t uri; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pj_strdup2_with_null(tdata->pool, &uri, contact->uri); | 
					
						
							|  |  |  | 	hdr->uri = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR); | 
					
						
							|  |  |  | 	hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \brief Helper function which adds a Date header to a response */ | 
					
						
							|  |  |  | static void registrar_add_date_header(pjsip_tx_data *tdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char date[256]; | 
					
						
							|  |  |  | 	struct tm tm; | 
					
						
							|  |  |  | 	time_t t = time(NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gmtime_r(&t, &tm); | 
					
						
							|  |  |  | 	strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_sip_add_header(tdata, "Date", date); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | #define SERIALIZER_BUCKETS 59
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ao2_container *serializers; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \brief Serializer with associated aor key */ | 
					
						
							|  |  |  | struct serializer { | 
					
						
							|  |  |  | 	/* Serializer to distribute tasks to */ | 
					
						
							|  |  |  | 	struct ast_taskprocessor *serializer; | 
					
						
							|  |  |  | 	/* The name of the aor to associate with the serializer */ | 
					
						
							|  |  |  | 	char aor_name[0]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void serializer_destroy(void *obj) | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	struct serializer *ser = obj; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	ast_taskprocessor_unreference(ser->serializer); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | static struct serializer *serializer_create(const char *aor_name) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-06 19:10:16 -06:00
										 |  |  | 	char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1]; | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	size_t size = strlen(aor_name) + 1; | 
					
						
							|  |  |  | 	struct serializer *ser = ao2_alloc( | 
					
						
							|  |  |  | 		sizeof(*ser) + size, serializer_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ser) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-06 19:10:16 -06:00
										 |  |  | 	/* Create name with seq number appended. */ | 
					
						
							|  |  |  | 	ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/aor/%s", | 
					
						
							|  |  |  | 		aor_name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(ser->serializer = ast_sip_create_serializer_named(tps_name))) { | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		ao2_ref(ser, -1); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	strcpy(ser->aor_name, aor_name); | 
					
						
							|  |  |  | 	return ser; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | static struct serializer *serializer_find_or_create(const char *aor_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct serializer *ser = ao2_find(serializers, aor_name, OBJ_SEARCH_KEY); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	if (ser) { | 
					
						
							|  |  |  | 		return ser; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	if (!(ser = serializer_create(aor_name))) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	ao2_link(serializers, ser); | 
					
						
							|  |  |  | 	return ser; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | static int serializer_hash(const void *obj, const int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct serializer *object; | 
					
						
							|  |  |  | 	const char *key; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (flags & OBJ_SEARCH_MASK) { | 
					
						
							|  |  |  | 	case OBJ_SEARCH_KEY: | 
					
						
							|  |  |  | 		key = obj; | 
					
						
							|  |  |  | 		return ast_str_hash(key); | 
					
						
							|  |  |  | 	case OBJ_SEARCH_OBJECT: | 
					
						
							|  |  |  | 		object = obj; | 
					
						
							|  |  |  | 		return ast_str_hash(object->aor_name); | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		/* Hash can only work on something with a full key. */ | 
					
						
							|  |  |  | 		ast_assert(0); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | static int serializer_cmp(void *obj_left, void *obj_right, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct serializer *object_left = obj_left; | 
					
						
							|  |  |  | 	const struct serializer *object_right = obj_right; | 
					
						
							|  |  |  | 	const char *right_key = obj_right; | 
					
						
							|  |  |  | 	int cmp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (flags & OBJ_SEARCH_MASK) { | 
					
						
							|  |  |  | 	case OBJ_SEARCH_OBJECT: | 
					
						
							|  |  |  | 		right_key = object_right->aor_name; | 
					
						
							|  |  |  | 		/* Fall through */ | 
					
						
							|  |  |  | 	case OBJ_SEARCH_KEY: | 
					
						
							|  |  |  | 		cmp = strcmp(object_left->aor_name, right_key); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case OBJ_SEARCH_PARTIAL_KEY: | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * We could also use a partial key struct containing a length | 
					
						
							|  |  |  | 		 * so strlen() does not get called for every comparison instead. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		cmp = strncmp(object_left->aor_name, right_key, strlen(right_key)); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		cmp = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	return cmp ? 0 : CMP_MATCH; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct rx_task_data { | 
					
						
							|  |  |  | 	pjsip_rx_data *rdata; | 
					
						
							|  |  |  | 	struct ast_sip_endpoint *endpoint; | 
					
						
							|  |  |  | 	struct ast_sip_aor *aor; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rx_task_data_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rx_task_data *task_data = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pjsip_rx_data_free_cloned(task_data->rdata); | 
					
						
							|  |  |  | 	ao2_cleanup(task_data->endpoint); | 
					
						
							|  |  |  | 	ao2_cleanup(task_data->aor); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct rx_task_data *rx_task_data_create(pjsip_rx_data *rdata, | 
					
						
							|  |  |  | 						struct ast_sip_endpoint *endpoint, | 
					
						
							|  |  |  | 						struct ast_sip_aor *aor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rx_task_data *task_data = ao2_alloc( | 
					
						
							|  |  |  | 		sizeof(*task_data), rx_task_data_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!task_data) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	pjsip_rx_data_clone(rdata, 0, &task_data->rdata); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	task_data->endpoint = endpoint; | 
					
						
							|  |  |  | 	ao2_ref(task_data->endpoint, +1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	task_data->aor = aor; | 
					
						
							|  |  |  | 	ao2_ref(task_data->aor, +1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return task_data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | static const pj_str_t path_hdr_name = { "Path", 4 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int build_path_data(struct rx_task_data *task_data, struct ast_str **path_str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pjsip_generic_string_hdr *path_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &path_hdr_name, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!path_hdr) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*path_str = ast_str_create(64); | 
					
						
							|  |  |  | 	if (!path_str) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_str_set(path_str, 0, "%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((path_hdr = (pjsip_generic_string_hdr *) pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &path_hdr_name, path_hdr->next))) { | 
					
						
							|  |  |  | 		ast_str_append(path_str, 0, ",%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int registrar_validate_path(struct rx_task_data *task_data, struct ast_str **path_str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const pj_str_t path_supported_name = { "path", 4 }; | 
					
						
							|  |  |  | 	pjsip_supported_hdr *supported_hdr; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!task_data->aor->support_path) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (build_path_data(task_data, path_str)) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!*path_str) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	supported_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_SUPPORTED, NULL); | 
					
						
							|  |  |  | 	if (!supported_hdr) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Find advertised path support */ | 
					
						
							|  |  |  | 	for (i = 0; i < supported_hdr->count; i++) { | 
					
						
							|  |  |  | 		if (!pj_stricmp(&supported_hdr->values[i], &path_supported_name)) { | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Path header present, but support not advertised */ | 
					
						
							|  |  |  | 	return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *contacts, | 
					
						
							|  |  |  | 	const char *aor_name) | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 	static const pj_str_t USER_AGENT = { "User-Agent", 10 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	int added = 0, updated = 0, deleted = 0; | 
					
						
							|  |  |  | 	pjsip_contact_hdr *contact_hdr = NULL; | 
					
						
							|  |  |  | 	struct registrar_contact_details details = { 0, }; | 
					
						
							|  |  |  | 	pjsip_tx_data *tdata; | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	RAII_VAR(struct ast_str *, path_str, NULL, ast_free); | 
					
						
							|  |  |  | 	struct ast_sip_contact *response_contact; | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 	char *user_agent = NULL; | 
					
						
							|  |  |  | 	pjsip_user_agent_hdr *user_agent_hdr; | 
					
						
							| 
									
										
										
										
											2015-02-21 19:26:41 +00:00
										 |  |  | 	pjsip_expires_hdr *expires_hdr; | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	/* So we don't count static contacts against max_contacts we prune them out from the container */ | 
					
						
							|  |  |  | 	ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		/* The provided Contact headers do not conform to the specification */ | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided"); | 
					
						
							| 
									
										
										
										
											2013-08-20 15:27:48 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 				ast_sorcery_object_get_id(task_data->endpoint)); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	if (registrar_validate_path(task_data, &path_str)) { | 
					
						
							|  |  |  | 		/* Ensure that intervening proxies did not make invalid modifications to the request */ | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 420, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", | 
					
						
							|  |  |  | 				ast_sorcery_object_get_id(task_data->endpoint)); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		/* Enforce the maximum number of contacts */ | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); | 
					
						
							| 
									
										
										
										
											2014-05-09 22:49:26 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 				ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 	user_agent_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &USER_AGENT, NULL); | 
					
						
							|  |  |  | 	if (user_agent_hdr) { | 
					
						
							|  |  |  | 		size_t alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; | 
					
						
							|  |  |  | 		user_agent = ast_alloca(alloc_size); | 
					
						
							|  |  |  | 		ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	/* Iterate each provided Contact header and add, update, or delete */ | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		int expiration; | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | 		char contact_uri[pjsip_max_url_size]; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (contact_hdr->star) { | 
					
						
							|  |  |  | 			/* A star means to unregister everything, so do so for the possible contacts */ | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { | 
					
						
							|  |  |  | 			/* This registrar only currently supports sip: and sips: URI schemes */ | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		details.uri = pjsip_uri_get_uri(contact_hdr->uri); | 
					
						
							|  |  |  | 		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { | 
					
						
							|  |  |  | 			/* If they are actually trying to delete a contact that does not exist... be forgiving */ | 
					
						
							|  |  |  | 			if (!expiration) { | 
					
						
							|  |  |  | 				ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", | 
					
						
							|  |  |  | 					contact_uri, aor_name); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | 			if (ast_sip_location_add_contact_nolock(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 				ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, | 
					
						
							| 
									
										
										
										
											2015-06-17 07:04:39 -03:00
										 |  |  | 					user_agent, task_data->endpoint)) { | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", | 
					
						
							|  |  |  | 						contact_uri, aor_name); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", | 
					
						
							|  |  |  | 				contact_uri, aor_name, expiration); | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 			ast_test_suite_event_notify("AOR_CONTACT_ADDED", | 
					
						
							|  |  |  | 					"Contact: %s\r\n" | 
					
						
							|  |  |  | 					"AOR: %s\r\n" | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 					"Expiration: %d\r\n" | 
					
						
							|  |  |  | 					"UserAgent: %s", | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 					contact_uri, | 
					
						
							|  |  |  | 					aor_name, | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 					expiration, | 
					
						
							|  |  |  | 					user_agent); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		} else if (expiration) { | 
					
						
							| 
									
										
										
										
											2014-03-21 16:04:09 +00:00
										 |  |  | 			struct ast_sip_contact *contact_update; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); | 
					
						
							|  |  |  | 			if (!contact_update) { | 
					
						
							|  |  |  | 				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", | 
					
						
							|  |  |  | 					contact->uri, expiration); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); | 
					
						
							|  |  |  | 			contact_update->qualify_frequency = task_data->aor->qualify_frequency; | 
					
						
							|  |  |  | 			contact_update->authenticate_qualify = task_data->aor->authenticate_qualify; | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 			if (path_str) { | 
					
						
							| 
									
										
										
										
											2014-03-21 16:04:09 +00:00
										 |  |  | 				ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 			if (user_agent) { | 
					
						
							| 
									
										
										
										
											2014-03-21 16:04:09 +00:00
										 |  |  | 				ast_string_field_set(contact_update, user_agent, user_agent); | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-06 17:47:20 +00:00
										 |  |  | 			if (ast_sip_location_update_contact(contact_update)) { | 
					
						
							|  |  |  | 				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", | 
					
						
							|  |  |  | 					contact->uri, expiration); | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | 				ast_sip_location_delete_contact(contact); | 
					
						
							| 
									
										
										
										
											2014-05-06 17:47:20 +00:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 			ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", | 
					
						
							|  |  |  | 				contact_uri, aor_name, expiration); | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 			ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", | 
					
						
							|  |  |  | 					"Contact: %s\r\n" | 
					
						
							|  |  |  | 					"AOR: %s\r\n" | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 					"Expiration: %d\r\n" | 
					
						
							|  |  |  | 					"UserAgent: %s", | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 					contact_uri, | 
					
						
							|  |  |  | 					aor_name, | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 					expiration, | 
					
						
							| 
									
										
										
										
											2014-03-21 16:04:09 +00:00
										 |  |  | 					contact_update->user_agent); | 
					
						
							|  |  |  | 			ao2_cleanup(contact_update); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 			/* We want to report the user agent that was actually in the removed contact */ | 
					
						
							|  |  |  | 			user_agent = ast_strdupa(contact->user_agent); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 			ast_sip_location_delete_contact(contact); | 
					
						
							|  |  |  | 			ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 			ast_test_suite_event_notify("AOR_CONTACT_REMOVED", | 
					
						
							|  |  |  | 					"Contact: %s\r\n" | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 					"AOR: %s\r\n" | 
					
						
							|  |  |  | 					"UserAgent: %s", | 
					
						
							| 
									
										
										
										
											2013-08-12 22:05:18 +00:00
										 |  |  | 					contact_uri, | 
					
						
							| 
									
										
										
										
											2014-02-17 15:36:45 +00:00
										 |  |  | 					aor_name, | 
					
						
							|  |  |  | 					user_agent); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
 | 
					
						
							|  |  |  | 	 * do so | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	if (task_data->aor->remove_existing) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | 	/* Re-retrieve contacts.  Caller will clean up the original container. */ | 
					
						
							|  |  |  | 	contacts = ast_sip_location_retrieve_aor_contacts_nolock(task_data->aor); | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	response_contact = ao2_callback(contacts, 0, NULL, NULL); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Send a response containing all of the contacts (including static) that are present on this AOR */ | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	if (ast_sip_create_response(task_data->rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { | 
					
						
							|  |  |  | 		ao2_cleanup(response_contact); | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | 		ao2_cleanup(contacts); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	ao2_cleanup(response_contact); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Add the date header to the response, some UAs use this to set their date and time */ | 
					
						
							|  |  |  | 	registrar_add_date_header(tdata); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_callback(contacts, 0, registrar_add_contact, tdata); | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | 	ao2_cleanup(contacts); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-21 19:26:41 +00:00
										 |  |  | 	if ((expires_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { | 
					
						
							|  |  |  | 		expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(task_data->aor, NULL, task_data->rdata)); | 
					
						
							|  |  |  | 		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-27 20:30:18 +00:00
										 |  |  | 	ast_sip_send_stateful_response(task_data->rdata, tdata, task_data->endpoint); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return PJ_TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-01 12:30:56 -06:00
										 |  |  | static int rx_task(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 	struct rx_task_data *task_data = data; | 
					
						
							|  |  |  | 	struct ao2_container *contacts = NULL; | 
					
						
							|  |  |  | 	struct ast_named_lock *lock; | 
					
						
							|  |  |  | 	const char *aor_name = ast_sorcery_object_get_id(task_data->aor); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", aor_name); | 
					
						
							|  |  |  | 	if (!lock) { | 
					
						
							|  |  |  | 		ao2_cleanup(task_data); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_wrlock(lock); | 
					
						
							|  |  |  | 	contacts = ast_sip_location_retrieve_aor_contacts_nolock(task_data->aor); | 
					
						
							|  |  |  | 	if (!contacts) { | 
					
						
							|  |  |  | 		ao2_unlock(lock); | 
					
						
							|  |  |  | 		ast_named_lock_put(lock); | 
					
						
							|  |  |  | 		ao2_cleanup(task_data); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = rx_task_core(task_data, contacts, aor_name); | 
					
						
							|  |  |  | 	ao2_cleanup(contacts); | 
					
						
							|  |  |  | 	ao2_unlock(lock); | 
					
						
							|  |  |  | 	ast_named_lock_put(lock); | 
					
						
							|  |  |  | 	ao2_cleanup(task_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 	struct rx_task_data *task_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_sip_endpoint *, endpoint, | 
					
						
							|  |  |  | 		 ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 	pjsip_sip_uri *uri; | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	char *domain_name; | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	char *configured_aors, *aor_name; | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	RAII_VAR(struct ast_str *, id, NULL, ast_free); | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) { | 
					
						
							|  |  |  | 		return PJ_FALSE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(endpoint->aors)) { | 
					
						
							|  |  |  | 		/* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */ | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors"); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint)); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) { | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received"); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint)); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uri = pjsip_uri_get_uri(rdata->msg_info.to->uri); | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	domain_name = ast_alloca(uri->host.slen + 1); | 
					
						
							|  |  |  | 	ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1); | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	configured_aors = ast_strdupa(endpoint->aors); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Iterate the configured AORs to see if the user or the user+domain match */ | 
					
						
							| 
									
										
										
											
												res_pjsip:  Strip spaces from items parsed from comma-separated lists
Configurations like "aors = a, b, c" were either ignoring everything after "a"
or trying to look up " b".  Same for mailboxes,  ciphers, contacts and a few
others.
To fix, all the strsep(©, ",") calls have been wrapped in ast_strip.  To
facilitate this, ast_strip, ast_skip_blanks and ast_skip_nonblanks were
updated to handle null pointers.
In some cases, an ast_strlen_zero() test was added to skip consecutive commas.
There was also an attempt to ast_free an ast_strdupa'd string in
ast_sip_for_each_aor which was causing a SEGV.  I removed it.
Although this issue was reported for realtime, the issue was in the res_pjsip
modules so all config mechanisms were affected.
ASTERISK-25829 #close
Reported-by: Mateusz Kowalski
Change-Id: I0b22a2cf22a7c1c50d4ecacbfa540155bec0e7a2
											
										 
											2016-03-06 13:38:41 -07:00
										 |  |  | 	while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) { | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 		struct ast_sip_domain_alias *alias = NULL; | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												res_pjsip:  Strip spaces from items parsed from comma-separated lists
Configurations like "aors = a, b, c" were either ignoring everything after "a"
or trying to look up " b".  Same for mailboxes,  ciphers, contacts and a few
others.
To fix, all the strsep(©, ",") calls have been wrapped in ast_strip.  To
facilitate this, ast_strip, ast_skip_blanks and ast_skip_nonblanks were
updated to handle null pointers.
In some cases, an ast_strlen_zero() test was added to skip consecutive commas.
There was also an attempt to ast_free an ast_strdupa'd string in
ast_sip_for_each_aor which was causing a SEGV.  I removed it.
Although this issue was reported for realtime, the issue was in the res_pjsip
modules so all config mechanisms were affected.
ASTERISK-25829 #close
Reported-by: Mateusz Kowalski
Change-Id: I0b22a2cf22a7c1c50d4ecacbfa540155bec0e7a2
											
										 
											2016-03-06 13:38:41 -07:00
										 |  |  | 		if (ast_strlen_zero(aor_name)) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 		if (!pj_strcmp2(&uri->user, aor_name)) { | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 		if (!id && !(id = ast_str_create(uri->user.slen + uri->host.slen + 2))) { | 
					
						
							|  |  |  | 			return PJ_TRUE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_str_set(&id, 0, "%.*s@", (int)uri->user.slen, uri->user.ptr); | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 			ast_str_append(&id, 0, "%s", alias->domain); | 
					
						
							|  |  |  | 			ao2_cleanup(alias); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ast_str_append(&id, 0, "%s", domain_name); | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 		if (!strcmp(aor_name, ast_str_buffer(id))) { | 
					
						
							|  |  |  | 			ast_free(id); | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) { | 
					
						
							|  |  |  | 		/* The provided AOR name was not found (be it within the configuration or sorcery itself) */ | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found"); | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "AOR '%.*s' not found for endpoint '%s'\n", (int)uri->user.slen, uri->user.ptr, ast_sorcery_object_get_id(endpoint)); | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!aor->max_contacts) { | 
					
						
							|  |  |  | 		/* Registration is not permitted for this AOR */ | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted"); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n", | 
					
						
							|  |  |  | 				ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint)); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(ser = serializer_find_or_create(aor_name))) { | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_mem_limit(endpoint, rdata); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n", | 
					
						
							|  |  |  | 			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) { | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_mem_limit(endpoint, rdata); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n", | 
					
						
							|  |  |  | 			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); | 
					
						
							|  |  |  | 		return PJ_TRUE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_push_task(ser->serializer, rx_task, task_data)) { | 
					
						
							|  |  |  | 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); | 
					
						
							|  |  |  | 		ast_sip_report_mem_limit(endpoint, rdata); | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n", | 
					
						
							|  |  |  | 			ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor)); | 
					
						
							|  |  |  | 		ao2_ref(task_data, -1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return PJ_TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-04 21:42:39 +00:00
										 |  |  | /* function pointer to callback needs to be within the module
 | 
					
						
							|  |  |  |    in order to avoid problems with an undefined symbol */ | 
					
						
							| 
									
										
										
										
											2013-12-20 21:32:13 +00:00
										 |  |  | static int sip_contact_to_str(void *acp, void *arg, int flags) | 
					
						
							| 
									
										
										
										
											2013-12-04 21:42:39 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-12-20 21:32:13 +00:00
										 |  |  | 	return ast_sip_contact_to_str(acp, arg, flags); | 
					
						
							| 
									
										
										
										
											2013-12-04 21:42:39 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | static int ami_registrations_aor(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_sip_aor *aor = obj; | 
					
						
							|  |  |  | 	struct ast_sip_ami *ami = arg; | 
					
						
							|  |  |  | 	int *count = ami->arg; | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_str *, buf, | 
					
						
							|  |  |  | 		 ast_sip_create_ami_event("InboundRegistrationDetail", ami), ast_free); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!buf) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_sip_sorcery_object_to_ami(aor, &buf); | 
					
						
							|  |  |  | 	ast_str_append(&buf, 0, "Contacts: "); | 
					
						
							| 
									
										
										
										
											2013-12-04 21:42:39 +00:00
										 |  |  | 	ast_sip_for_each_contact(aor, sip_contact_to_str, &buf); | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | 	ast_str_append(&buf, 0, "\r\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); | 
					
						
							|  |  |  | 	(*count)++; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ami_registrations_endpoint(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_sip_endpoint *endpoint = obj; | 
					
						
							|  |  |  | 	return ast_sip_for_each_aor( | 
					
						
							|  |  |  | 		endpoint->aors, ami_registrations_aor, arg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ami_registrations_endpoints(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	RAII_VAR(struct ao2_container *, endpoints, | 
					
						
							|  |  |  | 		 ast_sip_get_endpoints(), ao2_cleanup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!endpoints) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_callback(endpoints, OBJ_NODATA, ami_registrations_endpoint, arg); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ami_show_registrations(struct mansession *s, const struct message *m) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int count = 0; | 
					
						
							| 
									
										
										
										
											2014-06-27 13:50:02 +00:00
										 |  |  | 	struct ast_sip_ami ami = { .s = s, .m = m, .arg = &count, .action_id = astman_get_header(m, "ActionID"), }; | 
					
						
							| 
									
										
										
										
											2015-01-09 17:54:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | 	astman_send_listack(s, m, "Following are Events for each Inbound " | 
					
						
							|  |  |  | 			    "registration", "start"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ami_registrations_endpoints(&ami); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-09 17:54:49 +00:00
										 |  |  | 	astman_send_list_complete_start(s, m, "InboundRegistrationDetailComplete", count); | 
					
						
							|  |  |  | 	astman_send_list_complete_end(s); | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define AMI_SHOW_REGISTRATIONS "PJSIPShowRegistrationsInbound"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | static pjsip_module registrar_module = { | 
					
						
							|  |  |  | 	.name = { "Registrar", 9 }, | 
					
						
							|  |  |  | 	.id = -1, | 
					
						
							|  |  |  | 	.priority = PJSIP_MOD_PRIORITY_APPLICATION, | 
					
						
							|  |  |  | 	.on_rx_request = registrar_on_rx_request, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const pj_str_t STR_REGISTER = { "REGISTER", 8 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 07:15:47 -05:00
										 |  |  | 	CHECK_PJPROJECT_MODULE_LOADED(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_pjproject_get_buildopt("PJ_MAX_HOSTNAME", "%d", &pj_max_hostname); | 
					
						
							|  |  |  | 	/* As of pjproject 2.4.5, PJSIP_MAX_URL_SIZE isn't exposed yet but we try anyway. */ | 
					
						
							|  |  |  | 	ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-16 14:35:00 +00:00
										 |  |  | 	CHECK_PJSIP_MODULE_LOADED(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 	if (!(serializers = ao2_container_alloc( | 
					
						
							|  |  |  | 		      SERIALIZER_BUCKETS, serializer_hash, serializer_cmp))) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	if (ast_sip_register_service(®istrar_module)) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) { | 
					
						
							|  |  |  | 		ast_sip_unregister_service(®istrar_module); | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | 	ast_manager_register_xml(AMI_SHOW_REGISTRATIONS, EVENT_FLAG_SYSTEM, | 
					
						
							|  |  |  | 				 ami_show_registrations); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	return AST_MODULE_LOAD_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-11-23 17:26:57 +00:00
										 |  |  | 	ast_manager_unregister(AMI_SHOW_REGISTRATIONS); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	ast_sip_unregister_service(®istrar_module); | 
					
						
							| 
									
										
										
										
											2013-09-26 18:51:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ao2_cleanup(serializers); | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Registrar Support", | 
					
						
							| 
									
										
										
										
											2014-07-25 16:47:17 +00:00
										 |  |  | 		.support_level = AST_MODULE_SUPPORT_CORE, | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		.load = load_module, | 
					
						
							|  |  |  | 		.unload = unload_module, | 
					
						
							|  |  |  | 		.load_pri = AST_MODPRI_APP_DEPEND, | 
					
						
							|  |  |  | 	       ); |