[svn-commits] twilson: trunk r354458 - in /trunk: ./ channels/ channels/sip/include/ contri...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Feb 8 15:29:01 CST 2012


Author: twilson
Date: Wed Feb  8 15:28:55 2012
New Revision: 354458

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=354458
Log:
Add callbackextension matching & realtime callbackextensions

This patch is based on the one by David Vossel, developer extrodinaire, at
https://reviewboard.asterisk.org/r/344/. If multiple peers are defined with the
same host/port, but differing callbackextensions, it chooses the peer with the
matching callbackextension. Since callbackextension creates an outbound
registration with the callbackextension as the Contact address, matching an
incoming request by that (in addition to the host/port) makes a lot of sense.

This patch also adds support for callbackextension to realtime by querying all
peers with callbackextensions on reload and adding registrations for them.

(closes issue ASTERISK-13456)
Review: https://reviewboard.asterisk.org/r/344/
Review: https://reviewboard.asterisk.org/r/1717/

Modified:
    trunk/CHANGES
    trunk/channels/chan_sip.c
    trunk/channels/sip/include/sip.h
    trunk/contrib/realtime/postgresql/realtime.sql

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=354458&r1=354457&r2=354458
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Wed Feb  8 15:28:55 2012
@@ -54,6 +54,11 @@
    lookup service.
  * A new setting for autocreatepeer (autocreatepeer=persistent) allows peers
    created using that setting to not be removed during SIP reload.
+ * Add support to realtime for the 'callbackextension' option
+ * When multiple peers exist with the same address, but differing
+   callbackextension options, incoming requests that are matched by address
+   will be matched to the peer with the matching callbackextension if it is
+   available.
 
 Chan_local changes
 ------------------

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=354458&r1=354457&r2=354458
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Wed Feb  8 15:28:55 2012
@@ -1456,13 +1456,14 @@
 static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno);
 static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v);
 static void set_socket_transport(struct sip_socket *socket, int transport);
+static int peer_ipcmp_cb_full(void *obj, void *arg, void *data, int flags);
 
 /* Realtime device support */
 static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *username, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms);
 static void update_peer(struct sip_peer *p, int expire);
 static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config);
 static const char *get_name_from_variable(const struct ast_variable *var);
-static struct sip_peer *realtime_peer(const char *peername, struct ast_sockaddr *sin, int devstate_only, int which_objects);
+static struct sip_peer *realtime_peer(const char *peername, struct ast_sockaddr *sin, char *callbackexten, int devstate_only, int which_objects);
 static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 
 /*--- Internal UA client handling (outbound registrations) */
@@ -4893,7 +4894,7 @@
 /* If varregs is NULL, we don't use sipregs. If we return true, then *name is
  * set. Using empty if-bodies instead of goto's while avoiding unnecessary
  * indents. */
-static int realtime_peer_by_addr(const char **name, struct ast_sockaddr *addr, const char *ipaddr, struct ast_variable **var, struct ast_variable **varregs)
+static int realtime_peer_by_addr(const char **name, struct ast_sockaddr *addr, const char *ipaddr, const char *callbackexten, struct ast_variable **var, struct ast_variable **varregs)
 {
 	char portstring[6]; /* up to 5 digits plus null terminator */
 	ast_copy_string(portstring, ast_sockaddr_stringify_port(addr), sizeof(portstring));
@@ -4901,8 +4902,11 @@
 	/* We're not finding this peer by this name anymore. Reset it. */
 	*name = NULL;
 
-	/* First check for fixed IP hosts */
-	if ((*var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL))) {
+	/* First check for fixed IP hosts with matching callbackextensions, if specified */
+	if (!ast_strlen_zero(callbackexten) && (*var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, "callbackextension", callbackexten, SENTINEL))) {
+		;
+	/* Check for fixed IP hosts */
+	} else if ((*var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL))) {
 		;
 	/* Check for registered hosts (in sipregs) */
 	} else if (varregs && (*varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL)) &&
@@ -4953,6 +4957,38 @@
 	return 1;
 }
 
+static int register_realtime_peers_with_callbackextens(void)
+{
+	struct ast_config *cfg;
+	char *cat = NULL;
+
+	if (!(ast_check_realtime("sippeers"))) {
+		return 0;
+	}
+
+	/* This is hacky. We want name to be the cat, so it is the first property */
+	if (!(cfg = ast_load_realtime_multientry("sippeers", "name LIKE", "%", "callbackextension LIKE", "%", SENTINEL))) {
+		return -1;
+	}
+
+	while ((cat = ast_category_browse(cfg, cat))) {
+		struct sip_peer *peer;
+		struct ast_variable *var = ast_category_root(cfg, cat);
+
+		if (!(peer = build_peer(cat, var, NULL, TRUE, FALSE))) {
+			continue;
+		}
+		ast_log(LOG_NOTICE, "Created realtime peer '%s' for registration\n", peer->name);
+
+		peer->is_realtime = 1;
+		sip_unref_peer(peer, "register_realtime_peers: Done registering releasing");
+	}
+
+	ast_config_destroy(cfg);
+
+	return 0;
+}
+
 /*! \brief  realtime_peer: Get peer from realtime storage
  * Checks the "sippeers" realtime family from extconfig.conf
  * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
@@ -4962,7 +4998,7 @@
  * \note This is never called with both newpeername and addr at the same time.
  * If you do, be prepared to get a peer with a different name than newpeername.
  */
-static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockaddr *addr, int devstate_only, int which_objects)
+static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockaddr *addr, char *callbackexten, int devstate_only, int which_objects)
 {
 	struct sip_peer *peer = NULL;
 	struct ast_variable *var = NULL;
@@ -4978,7 +5014,7 @@
 
 	if (newpeername && realtime_peer_by_name(&newpeername, addr, ipaddr, &var, realtimeregs ? &varregs : NULL)) {
 		;
-	} else if (addr && realtime_peer_by_addr(&newpeername, addr, ipaddr, &var, realtimeregs ? &varregs : NULL)) {
+	} else if (addr && realtime_peer_by_addr(&newpeername, addr, ipaddr, callbackexten, &var, realtimeregs ? &varregs : NULL)) {
 		;
 	} else {
 		return NULL;
@@ -5054,20 +5090,7 @@
 	return CMP_MATCH | CMP_STOP;
 }
 
-/*!
- * \brief Locate device by name or ip address
- * \param peer, sin, realtime, devstate_only, transport
- * \param which_objects Define which objects should be matched when doing a lookup
- *        by name.  Valid options are FINDUSERS, FINDPEERS, or FINDALLDEVICES.
- *        Note that this option is not used at all when doing a lookup by IP.
- *
- *	This is used on find matching device on name or ip/port.
- * If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
- *
- * \note Avoid using this function in new functions if there is a way to avoid it,
- * since it might cause a database lookup.
- */
-struct sip_peer *sip_find_peer(const char *peer, struct ast_sockaddr *addr, int realtime, int which_objects, int devstate_only, int transport)
+static struct sip_peer *sip_find_peer_full(const char *peer, struct ast_sockaddr *addr, char *callbackexten, int realtime, int which_objects, int devstate_only, int transport)
 {
 	struct sip_peer *p = NULL;
 	struct sip_peer tmp_peer;
@@ -5079,10 +5102,10 @@
 		ast_sockaddr_copy(&tmp_peer.addr, addr);
 		tmp_peer.flags[0].flags = 0;
 		tmp_peer.transports = transport;
-		p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+		p = ao2_t_callback_data(peers_by_ip, OBJ_POINTER, peer_ipcmp_cb_full, &tmp_peer, callbackexten, "ao2_find in peers_by_ip table");
 		if (!p) {
 			ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT);
-			p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS:  p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+			p = ao2_t_callback_data(peers_by_ip, OBJ_POINTER, peer_ipcmp_cb_full, &tmp_peer, callbackexten, "ao2_find in peers_by_ip table 2");
 			if (p) {
 				return p;
 			}
@@ -5090,7 +5113,9 @@
 	}
 
 	if (!p && (realtime || devstate_only)) {
-		p = realtime_peer(peer, addr, devstate_only, which_objects);
+		/* realtime_peer will return a peer with matching callbackexten if possible, otherwise one matching
+		 * without the callbackexten */
+		p = realtime_peer(peer, addr, callbackexten, devstate_only, which_objects);
 		if (p) {
 			switch (which_objects) {
 			case FINDUSERS:
@@ -5112,6 +5137,29 @@
 	}
 
 	return p;
+}
+
+/*!
+ * \brief Locate device by name or ip address
+ * \param peer, sin, realtime, devstate_only, transport
+ * \param which_objects Define which objects should be matched when doing a lookup
+ *        by name.  Valid options are FINDUSERS, FINDPEERS, or FINDALLDEVICES.
+ *        Note that this option is not used at all when doing a lookup by IP.
+ *
+ *	This is used on find matching device on name or ip/port.
+ * If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
+ *
+ * \note Avoid using this function in new functions if there is a way to avoid it,
+ * since it might cause a database lookup.
+ */
+struct sip_peer *sip_find_peer(const char *peer, struct ast_sockaddr *addr, int realtime, int which_objects, int devstate_only, int transport)
+{
+	return sip_find_peer_full(peer, addr, NULL, realtime, which_objects, devstate_only, transport);
+}
+
+static struct sip_peer *sip_find_peer_by_ip_and_exten(struct ast_sockaddr *addr, char *callbackexten, int transport)
+{
+	return sip_find_peer_full(NULL, addr, callbackexten, TRUE, FINDPEERS, FALSE, transport);
 }
 
 /*! \brief Set nat mode on the various data sockets */
@@ -8410,16 +8458,16 @@
 /*! \brief create sip_registry object from register=> line in sip.conf and link into reg container */
 static int sip_register(const char *value, int lineno)
 {
-	struct sip_registry *reg;
+	struct sip_registry *reg, *tmp;
 
 	if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
 		ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n");
 		return -1;
 	}
 
-	ast_atomic_fetchadd_int(&regobjs, 1);
 	ASTOBJ_INIT(reg);
 
+	ast_copy_string(reg->name, value, sizeof(reg->name));
 	if (sip_parse_register_line(reg, default_expiry, value, lineno)) {
 		registry_unref(reg, "failure to parse, unref the reg pointer");
 		return -1;
@@ -8430,8 +8478,13 @@
 		reg->refresh = reg->expiry = reg->configured_expiry = default_expiry;
 	}
 
-	/* Add the new registry entry to the list */
-	ASTOBJ_CONTAINER_LINK(&regl, reg);
+	/* Add the new registry entry to the list, but only if it isn't already there */
+	if ((tmp = ASTOBJ_CONTAINER_FIND(&regl, reg->name))) {
+		registry_unref(tmp, "throw away found registry");
+	} else {
+		ast_atomic_fetchadd_int(&regobjs, 1);
+		ASTOBJ_CONTAINER_LINK(&regl, reg);
+	}
 
 	/* release the reference given by ASTOBJ_INIT. The container has another reference */
 	registry_unref(reg, "unref the reg pointer");
@@ -16330,7 +16383,14 @@
 
 		/* Then find devices based on IP */
 		if (!peer) {
-			peer = sip_find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE, p->socket.type);
+			char *uri_tmp, *callback = NULL, *dummy;
+			uri_tmp = ast_strdupa(uri2);
+			parse_uri(uri_tmp, "sip:,sips:", &callback, &dummy, &dummy, &dummy);
+			if (!ast_strlen_zero(callback) && (peer = sip_find_peer_by_ip_and_exten(&p->recv, callback, p->socket.type))) {
+				; /* found, fall through */
+			} else {
+				peer = sip_find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE, p->socket.type);
+			}
 		}
 	}
 
@@ -28180,7 +28240,6 @@
 	time_t regseconds = 0;
 	struct ast_flags peerflags[3] = {{(0)}};
 	struct ast_flags mask[3] = {{(0)}};
-	char callback[256] = "";
 	struct sip_peer tmp_peer;
 	const char *srvlookup = NULL;
 	static int deprecation_warning = 1;
@@ -28486,7 +28545,7 @@
 			} else if (!strcasecmp(v->name, "regexten")) {
 				ast_string_field_set(peer, regexten, v->value);
 			} else if (!strcasecmp(v->name, "callbackextension")) {
-				ast_copy_string(callback, v->value, sizeof(callback));
+				ast_string_field_set(peer, callback, v->value);
 			} else if (!strcasecmp(v->name, "amaflags")) {
 				format = ast_cdr_amaflags2int(v->value);
 				if (format < 0) {
@@ -28858,9 +28917,9 @@
 
 	ast_free_ha(oldha);
 	ast_free_ha(olddirectmediaha);
-	if (!ast_strlen_zero(callback)) { /* build string from peer info */
+	if (!ast_strlen_zero(peer->callback)) { /* build string from peer info */
 		char *reg_string;
-		if (asprintf(&reg_string, "%s?%s:%s@%s/%s", peer->name, peer->username, !ast_strlen_zero(peer->remotesecret) ? peer->remotesecret : peer->secret, peer->tohost, callback) < 0) {
+		if (asprintf(&reg_string, "%s?%s:%s@%s/%s", peer->name, peer->username, !ast_strlen_zero(peer->remotesecret) ? peer->remotesecret : peer->secret, peer->tohost, peer->callback) < 0) {
 			ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
 		} else if (reg_string) {
 			sip_register(reg_string, 0); /* XXX TODO: count in registry_count */
@@ -30023,6 +30082,8 @@
 	/* Release configuration from memory */
 	ast_config_destroy(cfg);
 
+	register_realtime_peers_with_callbackextens();
+
 	/* Load the list of manual NOTIFY types to support */
 	if (notify_types) {
 		ast_config_destroy(notify_types);
@@ -30811,9 +30872,17 @@
  *
  * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
  */
-static int peer_ipcmp_cb(void *obj, void *arg, int flags)
+static int peer_ipcmp_cb_full(void *obj, void *arg, void *data, int flags)
 {
 	struct sip_peer *peer = obj, *peer2 = arg;
+	char *callback = data;
+
+	if (!ast_strlen_zero(callback) && strcasecmp(peer->callback, callback)) {
+		/* We require a callback extension match, but don't have one */
+		return 0;
+	}
+
+	/* At this point, we match the callback extension if we need to. Carry on. */
 
 	if (ast_sockaddr_cmp_addr(&peer->addr, &peer2->addr)) {
 		/* IP doesn't match */
@@ -30836,6 +30905,10 @@
 			(CMP_MATCH | CMP_STOP) : 0;
 }
 
+static int peer_ipcmp_cb(void *obj, void *arg, int flags)
+{
+	return peer_ipcmp_cb_full(obj, arg, NULL, flags);
+}
 
 static int threadt_hash_cb(const void *obj, const int flags)
 {

Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=354458&r1=354457&r2=354458
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Wed Feb  8 15:28:55 2012
@@ -1261,6 +1261,7 @@
 		AST_STRING_FIELD(zone);         /*!< Tonezone for this device */
 		AST_STRING_FIELD(record_on_feature); /*!< Feature to use when receiving INFO with record: on during a call */
 		AST_STRING_FIELD(record_off_feature); /*!< Feature to use when receiving INFO with record: off during a call */
+		AST_STRING_FIELD(callback); /*!< Callback extension */
 		);
 	struct sip_socket socket;       /*!< Socket used for this peer */
 	enum sip_transport default_outbound_transport;   /*!< Peer Registration may change the default outbound transport.
@@ -1345,7 +1346,7 @@
  * \todo Convert this to astobj2
  */
 struct sip_registry {
-	ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1);
+	ASTOBJ_COMPONENTS_FULL(struct sip_registry, 80, 1);
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(callid);     /*!< Global Call-ID */
 		AST_STRING_FIELD(realm);      /*!< Authorization realm */

Modified: trunk/contrib/realtime/postgresql/realtime.sql
URL: http://svnview.digium.com/svn/asterisk/trunk/contrib/realtime/postgresql/realtime.sql?view=diff&rev=354458&r1=354457&r2=354458
==============================================================================
--- trunk/contrib/realtime/postgresql/realtime.sql (original)
+++ trunk/contrib/realtime/postgresql/realtime.sql Wed Feb  8 15:28:55 2012
@@ -71,7 +71,8 @@
 defaultuser character varying(80),
 fullcontact character varying(80),
 regserver character varying(30),
-useragent character varying(40)
+useragent character varying(40),
+callbackextension character varying(40)
 );
 
 drop table voicemail_users;




More information about the svn-commits mailing list