[asterisk-commits] jrose: trunk r369959 - in /trunk: ./ channels/ channels/sip/include/ configs/...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jul 11 13:33:45 CDT 2012


Author: jrose
Date: Wed Jul 11 13:33:36 2012
New Revision: 369959

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=369959
Log:
Named ACLs: Introduces a system for creating and sharing ACLs

This patch adds Named ACL functionality to Asterisk. This allows system
administrators to define an ACL and refer to it by a unique name. Configurable
items can then refer to that name when specifying access control lists.
It also includes updates to all core supported consumers of ACLs. That includes
manager, chan_sip, and chan_iax2. This feature is based on the deluxepine-trunk
by Olle E. Johansson and provides a subset of the Named ACL functionality
implemented in that branch. For more information on this feature, see acl.conf
and/or the Asterisk wiki.

Review: https://reviewboard.asterisk.org/r/1978/

Added:
    trunk/configs/acl.conf.sample   (with props)
    trunk/main/named_acl.c   (with props)
Modified:
    trunk/CHANGES
    trunk/channels/chan_iax2.c
    trunk/channels/chan_sip.c
    trunk/channels/sip/include/sip.h
    trunk/configs/extconfig.conf.sample
    trunk/configs/iax.conf.sample
    trunk/configs/manager.conf.sample
    trunk/configs/sip.conf.sample
    trunk/include/asterisk/acl.h
    trunk/include/asterisk/channel.h
    trunk/include/asterisk/config.h
    trunk/include/asterisk/event_defs.h
    trunk/main/acl.c
    trunk/main/asterisk.c
    trunk/main/config.c
    trunk/main/loader.c
    trunk/main/manager.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=369959&r1=369958&r2=369959
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Wed Jul 11 13:33:36 2012
@@ -46,6 +46,12 @@
    have their own verbosity level.  The command 'core set verbose' will now set
    a separate level for each remote console without affecting any other
    console.
+ * Named ACLs can now be specified in acl.conf and used in configurations that
+   use ACLs. As a general rule, if some derivative of 'permit' or 'deny' is
+   used to specify an ACL, a similar form of 'acl' will add a named ACL to the
+   working ACL. In addition, some CLI commands have
+   been added to provide informational and configuration reload capabilities to
+   this feature ('acl show [named acl]' and 'reload acl').
  * Hangup handlers can be attached to channels using the CHANNEL(hangup_handler_xxx)
    options.  Hangup handlers will run when the channel is hung up similar to the
    h extension.

Modified: trunk/channels/chan_iax2.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_iax2.c?view=diff&rev=369959&r1=369958&r2=369959
==============================================================================
--- trunk/channels/chan_iax2.c (original)
+++ trunk/channels/chan_iax2.c Wed Jul 11 13:33:36 2012
@@ -275,6 +275,7 @@
 static char regcontext[AST_MAX_CONTEXT] = "";
 
 static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
+static struct ast_event_sub *acl_change_event_subscription; /*!< subscription id for ACL change events */
 static int network_change_event_sched_id = -1;
 
 static int maxauthreq = 3;
@@ -438,7 +439,7 @@
 #define IAX_SHRINKCALLERID      (uint64_t)(1 << 31)   /*!< Turn on and off caller id shrinking */
 static int global_rtautoclear = 120;
 
-static int reload_config(void);
+static int reload_config(int forced_reload);
 
 /*!
  * \brief Call token validation settings.
@@ -479,7 +480,7 @@
 	int maxauthreq; /*!< Maximum allowed outstanding AUTHREQs */
 	int curauthreq; /*!< Current number of outstanding AUTHREQs */
 	struct ast_codec_pref prefs;
-	struct ast_ha *ha;
+	struct ast_acl_list *acl;
 	struct iax2_context *contexts;
 	struct ast_variable *vars;
 	enum calltoken_peer_enum calltoken_required;        /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
@@ -539,7 +540,7 @@
 
 	struct ast_event_sub *mwi_event_sub;
 
-	struct ast_ha *ha;
+	struct ast_acl_list *acl;
 	enum calltoken_peer_enum calltoken_required;        /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
 };
 
@@ -1242,6 +1243,7 @@
 static int replace_callno(const void *obj);
 static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
 static void network_change_event_cb(const struct ast_event *, void *);
+static void acl_change_event_cb(const struct ast_event *, void *);
 
 static struct ast_channel_tech iax2_tech = {
 	.type = "IAX2",
@@ -1324,6 +1326,21 @@
 	}
 }
 
+static void acl_change_event_subscribe(void)
+{
+	if (!acl_change_event_subscription) {
+		acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
+			acl_change_event_cb, "IAX2 ACL Change", NULL, AST_EVENT_IE_END);
+	}
+}
+
+static void acl_change_event_unsubscribe(void)
+{
+	if (acl_change_event_subscription) {
+		acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
+	}
+}
+
 static int network_change_event_sched_cb(const void *data)
 {
 	struct iax2_registry *reg;
@@ -1344,6 +1361,12 @@
 		network_change_event_sched_id = iax2_sched_add(sched, 1000, network_change_event_sched_cb, NULL);
 	}
 
+}
+
+static void acl_change_event_cb(const struct ast_event *event, void *userdata)
+{
+	ast_log(LOG_NOTICE, "Reloading chan_iax2 in response to ACL change event.\n");
+	reload_config(1);
 }
 
 
@@ -3818,7 +3841,7 @@
 		ast_cli(a->fd, "  Encryption   : %s\n", peer->encmethods ? ast_str_buffer(encmethods) : "No");
 		ast_cli(a->fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
 		ast_cli(a->fd, "  Expire       : %d\n", peer->expire);
-		ast_cli(a->fd, "  ACL          : %s\n", (peer->ha ? "Yes" : "No"));
+		ast_cli(a->fd, "  ACL          : %s\n", (ast_acl_list_is_empty(peer->acl) ? "No" : "Yes"));
 		ast_cli(a->fd, "  Addr->IP     : %s Port %d\n",  peer_addr.sin_addr.s_addr ? ast_inet_ntoa(peer_addr.sin_addr) : "(Unspecified)", ntohs(peer_addr.sin_port));
 		ast_cli(a->fd, "  Defaddr->IP  : %s Port %d\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port));
 		ast_cli(a->fd, "  Username     : %s\n", peer->username);
@@ -6701,7 +6724,7 @@
 
 		ast_cli(a->fd, FORMAT2, user->name, auth, user->authmethods, 
 			user->contexts ? user->contexts->context : DEFAULT_CONTEXT,
-			user->ha ? "Yes" : "No", pstr);
+			ast_acl_list_is_empty(user->acl) ? "No" : "Yes", pstr);
 	}
 	ao2_iterator_destroy(&i);
 
@@ -7681,7 +7704,7 @@
 	while ((user = ao2_iterator_next(&i))) {
 		if ((ast_strlen_zero(iaxs[callno]->username) ||				/* No username specified */
 			!strcmp(iaxs[callno]->username, user->name))	/* Or this username specified */
-			&& ast_apply_ha(user->ha, &addr) 	/* Access is permitted from this IP */
+			&& ast_apply_acl(user->acl, &addr, "IAX2 user ACL: ")	/* Access is permitted from this IP */
 			&& (ast_strlen_zero(iaxs[callno]->context) ||			/* No context specified */
 			     apply_context(user->contexts, iaxs[callno]->context))) {			/* Context is permitted */
 			if (!ast_strlen_zero(iaxs[callno]->username)) {
@@ -7692,7 +7715,7 @@
 				break;
 			} else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->dbsecret) && ast_strlen_zero(user->inkeys)) {
 				/* No required authentication */
-				if (user->ha) {
+				if (user->acl) {
 					/* There was host authentication and we passed, bonus! */
 					if (bestscore < 4) {
 						bestscore = 4;
@@ -7712,7 +7735,7 @@
 					}
 				}
 			} else {
-				if (user->ha) {
+				if (user->acl) {
 					/* Authentication, but host access too, eh, it's something.. */
 					if (bestscore < 2) {
 						bestscore = 2;
@@ -8072,7 +8095,7 @@
 	}
 
 	ast_sockaddr_from_sin(&addr, sin);
-	if (!ast_apply_ha(p->ha, &addr)) {
+	if (!ast_apply_acl(p->acl, &addr, "IAX2 Peer ACL: ")) {
 		if (authdebug)
 			ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
 		goto return_unref;
@@ -12514,7 +12537,7 @@
 	struct iax2_peer *peer = obj;
 	int callno = peer->callno;
 
-	ast_free_ha(peer->ha);
+	ast_free_acl_list(peer->acl);
 
 	if (callno > 0) {
 		ast_mutex_lock(&iaxsl[callno]);
@@ -12537,10 +12560,11 @@
 static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
 {
 	struct iax2_peer *peer = NULL;
-	struct ast_ha *oldha = NULL;
+	struct ast_acl_list *oldacl = NULL;
 	int maskfound = 0;
 	int found = 0;
 	int firstpass = 1;
+	int subscribe_acl_change = 0;
 
 	if (!temponly) {
 		peer = ao2_find(peers, name, OBJ_KEY);
@@ -12551,8 +12575,8 @@
 	if (peer) {
 		found++;
 		if (firstpass) {
-			oldha = peer->ha;
-			peer->ha = NULL;
+			oldacl = peer->acl;
+			peer->acl = NULL;
 		}
 		unlink_peer(peer);
 	} else if ((peer = ao2_alloc(sizeof(*peer), peer_destructor))) {
@@ -12683,8 +12707,9 @@
 			} else if (!strcasecmp(v->name, "sourceaddress")) {
 				peer_set_srcaddr(peer, v->value);
 			} else if (!strcasecmp(v->name, "permit") ||
-					   !strcasecmp(v->name, "deny")) {
-				peer->ha = ast_append_ha(v->name, v->value, peer->ha, NULL);
+					   !strcasecmp(v->name, "deny") ||
+					   !strcasecmp(v->name, "acl")) {
+				ast_append_acl(v->name, v->value, &peer->acl, NULL, &subscribe_acl_change);
 			} else if (!strcasecmp(v->name, "mask")) {
 				maskfound++;
 				inet_aton(v->value, &peer->mask);
@@ -12795,8 +12820,8 @@
 		ast_clear_flag64(peer, IAX_DELME);
 	}
 
-	if (oldha)
-		ast_free_ha(oldha);
+	if (oldacl)
+		ast_free_acl_list(oldacl);
 
 	if (!ast_strlen_zero(peer->mailbox)) {
 		char *mailbox, *context;
@@ -12810,6 +12835,10 @@
 			AST_EVENT_IE_END);
 	}
 
+	if (subscribe_acl_change) {
+		acl_change_event_subscribe();
+	}
+
 	return peer;
 }
 
@@ -12817,7 +12846,7 @@
 {
 	struct iax2_user *user = obj;
 
-	ast_free_ha(user->ha);
+	ast_free_acl_list(user->acl);
 	free_context(user->contexts);
 	if(user->vars) {
 		ast_variables_destroy(user->vars);
@@ -12831,11 +12860,12 @@
 {
 	struct iax2_user *user = NULL;
 	struct iax2_context *con, *conl = NULL;
-	struct ast_ha *oldha = NULL;
+	struct ast_acl_list *oldacl = NULL;
 	struct iax2_context *oldcon = NULL;
 	int format;
 	int firstpass=1;
 	int oldcurauthreq = 0;
+	int subscribe_acl_change = 0;
 	char *varname = NULL, *varval = NULL;
 	struct ast_variable *tmpvar = NULL;
 
@@ -12848,9 +12878,9 @@
 	if (user) {
 		if (firstpass) {
 			oldcurauthreq = user->curauthreq;
-			oldha = user->ha;
+			oldacl = user->acl;
 			oldcon = user->contexts;
-			user->ha = NULL;
+			user->acl = NULL;
 			user->contexts = NULL;
 		}
 		/* Already in the list, remove it and it will be added back (or FREE'd) */
@@ -12899,8 +12929,9 @@
 					conl = con;
 				}
 			} else if (!strcasecmp(v->name, "permit") ||
-					   !strcasecmp(v->name, "deny")) {
-				user->ha = ast_append_ha(v->name, v->value, user->ha, NULL);
+					   !strcasecmp(v->name, "deny") ||
+					   !strcasecmp(v->name, "acl")) {
+				ast_append_acl(v->name, v->value, &user->acl, NULL, &subscribe_acl_change);
 			} else if (!strcasecmp(v->name, "setvar")) {
 				varname = ast_strdupa(v->value);
 				if (varname && (varval = strchr(varname,'='))) {
@@ -13069,10 +13100,17 @@
 		ast_clear_flag64(user, IAX_DELME);
 	}
 cleanup:
-	if (oldha)
-		ast_free_ha(oldha);
-	if (oldcon)
+	if (oldacl) {
+		ast_free_acl_list(oldacl);
+	}
+	if (oldcon) {
 		free_context(oldcon);
+	}
+
+	if (subscribe_acl_change) {
+		acl_change_event_subscribe();
+	}
+
 	return user;
 }
 
@@ -13171,7 +13209,7 @@
 }
 
 /*! \brief Load configuration */
-static int set_config(const char *config_file, int reload)
+static int set_config(const char *config_file, int reload, int forced)
 {
 	struct ast_config *cfg, *ucfg;
 	iax2_format capability = iax2_capability;
@@ -13187,7 +13225,7 @@
 	struct iax2_user *user;
 	struct iax2_peer *peer;
 	struct ast_netsock *ns;
-	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+	struct ast_flags config_flags = { (reload && !forced) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
 #if 0
 	static unsigned short int last_port=0;
 #endif
@@ -13667,12 +13705,12 @@
 	}
 	ao2_iterator_destroy(&i);
 }
-static int reload_config(void)
+static int reload_config(int forced_reload)
 {
 	static const char config[] = "iax.conf";
 	struct iax2_registry *reg;
 
-	if (set_config(config, 1) > 0) {
+	if (set_config(config, 1, forced_reload) > 0) {
 		prune_peers();
 		prune_users();
 		ao2_callback(callno_limits, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
@@ -13711,14 +13749,14 @@
 		return NULL;
 	}
 
-	reload_config();
+	reload_config(0);
 
 	return CLI_SUCCESS;
 }
 
 static int reload(void)
 {
-	return reload_config();
+	return reload_config(0);
 }
 
 static int cache_get_callno_locked(const char *data)
@@ -14483,6 +14521,7 @@
 	int x;
 
 	network_change_event_unsubscribe();
+	acl_change_event_unsubscribe();
 
 	ast_manager_unregister("IAXpeers");
 	ast_manager_unregister("IAXpeerlist");
@@ -14818,7 +14857,7 @@
 		ast_data_add_int(data_enum_node, "value", user->amaflags);
 		ast_data_add_str(data_enum_node, "text", ast_cdr_flags2str(user->amaflags));
 
-		ast_data_add_bool(data_user, "access-control", user->ha ? 1 : 0);
+		ast_data_add_bool(data_user, "access-control", ast_acl_list_is_empty(user->acl) ? 0 : 1);
 
 		if (ast_test_flag64(user, IAX_CODEC_NOCAP)) {
 			pstr = "REQ only";
@@ -14922,7 +14961,7 @@
 		ast_timer_set_rate(timer, 1000 / trunkfreq);
 	}
 
-	if (set_config(config, 0) == -1) {
+	if (set_config(config, 0, 0) == -1) {
 		if (timer) {
 			ast_timer_close(timer);
 		}

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=369959&r1=369958&r2=369959
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Wed Jul 11 13:33:36 2012
@@ -793,6 +793,7 @@
 static int global_t38_maxdatagram;                /*!< global T.38 FaxMaxDatagram override */
 
 static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
+static struct ast_event_sub *acl_change_event_subscription; /*!< subscription id for named ACL system change events */
 static int network_change_event_sched_id = -1;
 
 static char used_context[AST_MAX_CONTEXT];        /*!< name of automatically created context for unloading */
@@ -1407,6 +1408,7 @@
 static void sip_peer_hold(struct sip_pvt *p, int hold);
 static void mwi_event_cb(const struct ast_event *, void *);
 static void network_change_event_cb(const struct ast_event *, void *);
+static void acl_change_event_cb(const struct ast_event *event, void *userdata);
 static void sip_keepalive_all_peers(void);
 
 /*--- Applications, functions, CLI and manager command helpers */
@@ -4711,8 +4713,8 @@
 	}
 
 	register_peer_exten(peer, FALSE);
-	ast_free_ha(peer->ha);
-	ast_free_ha(peer->directmediaha);
+	ast_free_acl_list(peer->acl);
+	ast_free_acl_list(peer->directmediaacl);
 	if (peer->selfdestruct)
 		ast_atomic_fetchadd_int(&apeerobjs, -1);
 	else if (!ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && peer->is_realtime) {
@@ -5499,7 +5501,9 @@
 		dialog->noncodeccapability |= AST_RTP_DTMF;
 	else
 		dialog->noncodeccapability &= ~AST_RTP_DTMF;
-	dialog->directmediaha = ast_duplicate_ha_list(peer->directmediaha);
+
+	dialog->directmediaacl = ast_duplicate_acl_list(peer->directmediaacl);
+
 	if (peer->call_limit)
 		ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
 	if (!dialog->portinuri)
@@ -5992,9 +5996,8 @@
 		p->tsrtp = NULL;
 	}
 
-	if (p->directmediaha) {
-		ast_free_ha(p->directmediaha);
-		p->directmediaha = NULL;
+	if (p->directmediaacl) {
+		p->directmediaacl = ast_free_acl_list(p->directmediaacl);
 	}
 
 	ast_string_field_free_memory(p);
@@ -15034,8 +15037,8 @@
 	}
 
 	/* Check that they're allowed to register at this IP */
-	if (ast_apply_ha(sip_cfg.contact_ha, &peer->addr) != AST_SENSE_ALLOW ||
-			ast_apply_ha(peer->contactha, &peer->addr) != AST_SENSE_ALLOW) {
+	if (ast_apply_acl(sip_cfg.contact_acl, &peer->addr, "SIP contact ACL: ") != AST_SENSE_ALLOW ||
+			ast_apply_acl(peer->contactacl, &peer->addr, "SIP contact ACL: ") != AST_SENSE_ALLOW) {
 		ast_log(LOG_WARNING, "Domain '%s' disallowed by contact ACL (violating IP %s)\n", hostport,
 			ast_sockaddr_stringify_addr(&testsa));
 		ast_string_field_set(peer, fullcontact, "");
@@ -15485,6 +15488,22 @@
 	}
 }
 
+static void acl_change_event_subscribe(void)
+{
+	if (!acl_change_event_subscription) {
+		acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
+		acl_change_event_cb, "Manager must react to Named ACL changes", NULL, AST_EVENT_IE_END);
+	}
+
+}
+
+static void acl_change_event_unsubscribe(void)
+{
+	if (acl_change_event_subscription) {
+		acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
+	}
+}
+
 static int network_change_event_sched_cb(const void *data)
 {
 	network_change_event_sched_id = -1;
@@ -15796,7 +15815,7 @@
 	}
 	peer = sip_find_peer(name, NULL, TRUE, FINDPEERS, FALSE, 0);
 
-	if (!(peer && ast_apply_ha(peer->ha, addr))) {
+	if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
 		/* Peer fails ACL check */
 		if (peer) {
 			sip_unref_peer(peer, "register_verify: sip_unref_peer: from sip_find_peer operation");
@@ -16950,7 +16969,7 @@
 		return AUTH_DONT_KNOW;
 	}
 
-	if (!ast_apply_ha(peer->ha, addr)) {
+	if (!ast_apply_acl(peer->acl, addr, "SIP Peer ACL: ")) {
 		ast_debug(2, "Found peer '%s' for '%s', but fails host access\n", peer->name, of);
 		sip_unref_peer(peer, "sip_unref_peer: check_peer_ok: from sip_find_peer call, early return of AUTH_ACL_FAILED");
 		return AUTH_ACL_FAILED;
@@ -17754,7 +17773,7 @@
 			user->secret,
 			user->accountcode,
 			user->context,
-			AST_CLI_YESNO(user->ha != NULL),
+			AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0),
 			AST_CLI_YESNO(ast_test_flag(&user->flags[0], SIP_NAT_FORCE_RPORT)));
 		ao2_unlock(user);
 		sip_unref_peer(user, "sip show users");
@@ -18013,7 +18032,7 @@
 			ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
 				ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " A " : " a " :
 				ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : "   ",	/* NAT=yes? */
-			peer->ha ? " A " : "   ",       /* permit/deny */
+			(!ast_acl_list_is_empty(peer->acl)) ? " A " : "   ",       /* permit/deny */
 			tmp_port, status,
 			peer->description ? peer->description : "",
 			realtimepeers ? (peer->is_realtime ? "Cached RT" : "") : "");
@@ -18048,7 +18067,7 @@
 			ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "yes" : "no",
 			ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no",	/* VIDEOSUPPORT=yes? */
 			ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no",	/* TEXTSUPPORT=yes? */
-			peer->ha ? "yes" : "no",       /* permit/deny */
+			ast_acl_list_is_empty(peer->acl) ? "no" : "yes",       /* permit/deny/acl */
 			status,
 			realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no",
 			peer->description);
@@ -18719,8 +18738,8 @@
 		ast_cli(fd, "  Insecure     : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
 		ast_cli(fd, "  Force rport  : %s\n", force_rport_string(peer->flags));
 		ast_cli(fd, "  Symmetric RTP: %s\n", comedia_string(peer->flags));
-		ast_cli(fd, "  ACL          : %s\n", AST_CLI_YESNO(peer->ha != NULL));
-		ast_cli(fd, "  DirectMedACL : %s\n", AST_CLI_YESNO(peer->directmediaha != NULL));
+		ast_cli(fd, "  ACL          : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(peer->acl) == 0));
+		ast_cli(fd, "  DirectMedACL : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(peer->directmediaacl) == 0));
 		ast_cli(fd, "  T.38 support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
 		ast_cli(fd, "  T.38 EC mode : %s\n", faxec2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
 		ast_cli(fd, "  T.38 MaxDtgrm: %d\n", peer->t38_maxdatagram);
@@ -18838,7 +18857,7 @@
 		astman_append(s, "SIP-Comedia: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ?
 				(ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "A" : "a") :
 				(ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "Y" : "N"));
-		astman_append(s, "ACL: %s\r\n", (peer->ha?"Y":"N"));
+		astman_append(s, "ACL: %s\r\n", (ast_acl_list_is_empty(peer->acl) ? "N" : "Y"));
 		astman_append(s, "SIP-CanReinvite: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
 		astman_append(s, "SIP-DirectMedia: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
 		astman_append(s, "SIP-PromiscRedir: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Y":"N"));
@@ -18989,7 +19008,7 @@
 		ast_cli(a->fd, "  Pickupgroup  : ");
 		print_group(a->fd, user->pickupgroup, 0);
 		ast_cli(a->fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
-		ast_cli(a->fd, "  ACL          : %s\n", AST_CLI_YESNO(user->ha != NULL));
+		ast_cli(a->fd, "  ACL          : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0));
  		ast_cli(a->fd, "  Sess-Timers  : %s\n", stmode2str(user->stimer.st_mode_oper));
  		ast_cli(a->fd, "  Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref));
  		ast_cli(a->fd, "  Sess-Expires : %d secs\n", user->stimer.st_max_se);
@@ -27643,6 +27662,23 @@
 	return 0;
 }
 
+static void acl_change_event_cb(const struct ast_event *event, void *userdata)
+{
+	ast_log(LOG_NOTICE, "Reloading chan_sip in response to ACL change event.\n");
+
+	ast_mutex_lock(&sip_reload_lock);
+
+	if (sip_reloading) {
+		ast_verbose("Previous SIP reload not yet done\n");
+	} else {
+		sip_reloading = TRUE;
+		sip_reloadreason = CHANNEL_ACL_RELOAD;
+	}
+
+	ast_mutex_unlock(&sip_reload_lock);
+
+	restart_monitor();
+}
 
 /*! \brief Session-Timers: Restart session timer */
 static void restart_session_timer(struct sip_pvt *p)
@@ -29041,8 +29077,8 @@
 static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only)
 {
 	struct sip_peer *peer = NULL;
-	struct ast_ha *oldha = NULL;
-	struct ast_ha *olddirectmediaha = NULL;
+	struct ast_acl_list *oldacl = NULL;
+	struct ast_acl_list *olddirectmediaacl = NULL;
 	int found = 0;
 	int firstpass = 1;
 	uint16_t port = 0;
@@ -29056,6 +29092,7 @@
 	static int deprecation_warning = 1;
 	int alt_fullcontact = alt ? 1 : 0, headercount = 0;
 	struct ast_str *fullcontact = ast_str_alloca(512);
+	int acl_change_subscription_needed = 0;
 
 	if (!realtime || ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
 		/* Note we do NOT use sip_find_peer here, to avoid realtime recursion */
@@ -29104,10 +29141,10 @@
 
 	/* Note that our peer HAS had its reference count increased */
 	if (firstpass) {
-		oldha = peer->ha;
-		peer->ha = NULL;
-		olddirectmediaha = peer->directmediaha;
-		peer->directmediaha = NULL;
+		oldacl = peer->acl;
+		peer->acl = NULL;
+		olddirectmediaacl = peer->directmediaacl;
+		peer->directmediaacl = NULL;
 		set_peer_defaults(peer);	/* Set peer defaults */
 		peer->type = 0;
 	}
@@ -29308,25 +29345,25 @@
 					sip_unref_peer(peer, "sip_unref_peer: from build_peer defaultip");
 					return NULL;
 				}
-			} else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
+			} else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny") || !strcasecmp(v->name, "acl")) {
 				int ha_error = 0;
 				if (!ast_strlen_zero(v->value)) {
-					peer->ha = ast_append_ha(v->name, v->value, peer->ha, &ha_error);
+					ast_append_acl(v->name, v->value, &peer->acl, &ha_error, &acl_change_subscription_needed);
 				}
 				if (ha_error) {
 					ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
 				}
-			} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny")) {
+			} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny") || !strcasecmp(v->name, "contactacl")) {
 				int ha_error = 0;
 				if (!ast_strlen_zero(v->value)) {
-					peer->contactha = ast_append_ha(v->name + 7, v->value, peer->contactha, &ha_error);
+					ast_append_acl(v->name + 7, v->value, &peer->contactacl, &ha_error, &acl_change_subscription_needed);
 				}
 				if (ha_error) {
 					ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
 				}
-			} else if (!strcasecmp(v->name, "directmediapermit") || !strcasecmp(v->name, "directmediadeny")) {
+			} else if (!strcasecmp(v->name, "directmediapermit") || !strcasecmp(v->name, "directmediadeny") || !strcasecmp(v->name, "directmediaacl")) {
 				int ha_error = 0;
-				peer->directmediaha = ast_append_ha(v->name + 11, v->value, peer->directmediaha, &ha_error);
+				ast_append_acl(v->name + 11, v->value, &peer->directmediaacl, &ha_error, &acl_change_subscription_needed);
 				if (ha_error) {
 					ast_log(LOG_ERROR, "Bad directmedia ACL entry in configuration line %d : %s\n", v->lineno, v->value);
 				}
@@ -29674,8 +29711,8 @@
 
 		if (global_dynamic_exclude_static && !ast_sockaddr_isnull(&peer->addr)) {
 			int ha_error = 0;
-			sip_cfg.contact_ha = ast_append_ha("deny", ast_sockaddr_stringify_addr(&peer->addr), 
-							sip_cfg.contact_ha, &ha_error);
+
+			ast_append_acl("deny", ast_sockaddr_stringify_addr(&peer->addr), &sip_cfg.contact_acl, &ha_error, NULL);
 			if (ha_error) {
 				ast_log(LOG_ERROR, "Bad or unresolved host/IP entry in configuration for peer %s, cannot add to contact ACL\n", peer->name);
 			}
@@ -29746,8 +29783,8 @@
 
 	peer->the_mark = 0;
 
-	ast_free_ha(oldha);
-	ast_free_ha(olddirectmediaha);
+	oldacl = ast_free_acl_list(oldacl);
+	olddirectmediaacl = ast_free_acl_list(olddirectmediaacl);
 	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, peer->callback) < 0) {
@@ -29757,6 +29794,12 @@
 			ast_free(reg_string);
 		}
 	}
+
+	/* If an ACL change subscription is needed and doesn't exist, we need one. */
+	if (acl_change_subscription_needed) {
+		acl_change_event_subscribe();
+	}
+
 	return peer;
 }
 
@@ -29838,13 +29881,14 @@
 	char *cat, *stringp, *context, *oldregcontext;
 	char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
 	struct ast_flags dummy[3];
-	struct ast_flags config_flags = { reason == CHANNEL_MODULE_LOAD ? 0 : ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? 0 : CONFIG_FLAG_FILEUNCHANGED };
+	struct ast_flags config_flags = { (reason == CHANNEL_MODULE_LOAD || reason == CHANNEL_ACL_RELOAD) ? 0 : ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? 0 : CONFIG_FLAG_FILEUNCHANGED };
 	int auto_sip_domains = FALSE;
 	struct ast_sockaddr old_bindaddr = bindaddr;
 	int registry_count = 0, peer_count = 0, timerb_set = 0, timert1_set = 0;
 	int subscribe_network_change = 1;
 	time_t run_start, run_end;
 	int bindport = 0;
+	int acl_change_subscription_needed = 0;
 
 	run_start = time(0);
 	ast_unload_realtime("sipregs");
@@ -29887,8 +29931,7 @@
 		}
 	}
 
-	ast_free_ha(sip_cfg.contact_ha);
-	sip_cfg.contact_ha = NULL;
+	sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
 
 	default_tls_cfg.enabled = FALSE;		/* Default: Disable TLS */
 
@@ -29981,7 +30024,7 @@
 	sip_cfg.accept_outofcall_message = DEFAULT_ACCEPT_OUTOFCALL_MESSAGE;
 	sip_cfg.allowsubscribe = FALSE;
 	sip_cfg.disallowed_methods = SIP_UNKNOWN;
-	sip_cfg.contact_ha = NULL;		/* Reset the contact ACL */
+	sip_cfg.contact_acl = NULL;		/* Reset the contact ACL */
 	snprintf(global_useragent, sizeof(global_useragent), "%s %s", DEFAULT_USERAGENT, ast_get_version());
 	snprintf(global_sdpsession, sizeof(global_sdpsession), "%s %s", DEFAULT_SDPSESSION, ast_get_version());
 	snprintf(global_sdpowner, sizeof(global_sdpowner), "%s", DEFAULT_SDPOWNER);
@@ -30179,9 +30222,9 @@
 				  ast_sockaddr_stringify(&sip_tcp_desc.local_address));
 		} else if (!strcasecmp(v->name, "dynamic_exclude_static") || !strcasecmp(v->name, "dynamic_excludes_static")) {
 			global_dynamic_exclude_static = ast_true(v->value);
-		} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny")) {
+		} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny") || !strcasecmp(v->name, "contactacl")) {
 			int ha_error = 0;
-			sip_cfg.contact_ha = ast_append_ha(v->name + 7, v->value, sip_cfg.contact_ha, &ha_error);
+			ast_append_acl(v->name + 7, v->value, &sip_cfg.contact_acl, &ha_error, &acl_change_subscription_needed);
 			if (ha_error) {
 				ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
 			}
@@ -30959,10 +31002,15 @@
 	run_end = time(0);
 	ast_debug(4, "SIP reload_config done...Runtime= %d sec\n", (int)(run_end-run_start));
 
+	/* If an ACL change subscription is needed and doesn't exist, we need one. */
+	if (acl_change_subscription_needed) {
+		acl_change_event_subscribe();
+	}
+
 	return 0;
 }
 
-static int apply_directmedia_ha(struct sip_pvt *p, struct ast_ha *directmediaha, const char *op)
+static int apply_directmedia_acl(struct sip_pvt *p, struct ast_acl_list *directmediaacl, const char *op)
 {
 	struct ast_sockaddr us = { { 0, }, }, them = { { 0, }, };
 	int res = AST_SENSE_ALLOW;
@@ -30970,7 +31018,7 @@
 	ast_rtp_instance_get_remote_address(p->rtp, &them);
 	ast_rtp_instance_get_local_address(p->rtp, &us);
 
-	if ((res = ast_apply_ha(directmediaha, &them)) == AST_SENSE_DENY) {
+	if ((res = ast_apply_acl(directmediaacl, &them, "SIP Direct Media ACL: ")) == AST_SENSE_DENY) {
 		const char *us_addr = ast_strdupa(ast_sockaddr_stringify(&us));
 		const char *them_addr = ast_strdupa(ast_sockaddr_stringify(&them));
 
@@ -31045,8 +31093,8 @@
 static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_channel *chan2, char *rtptype)
 {
 	struct sip_pvt *p1 = NULL, *p2 = NULL;
-	struct ast_ha *p2_directmediaha = NULL; /* opposed directmediaha for comparing against first channel host address */
-	struct ast_ha *p1_directmediaha = NULL; /* opposed directmediaha for comparing against second channel host address */
+	struct ast_acl_list *p2_directmediaacl = NULL; /* opposed directmediaha for comparing against first channel host address */
+	struct ast_acl_list *p1_directmediaacl = NULL; /* opposed directmediaha for comparing against second channel host address */
 	int res = 1;
 
 	if (!(p1 = ast_channel_tech_pvt(chan1))) {
@@ -31058,18 +31106,18 @@
 	}
 
 	sip_pvt_lock(p2);
-	if (p2->relatedpeer && p2->relatedpeer->directmediaha) {
-		p2_directmediaha = ast_duplicate_ha_list(p2->relatedpeer->directmediaha);
+	if (p2->relatedpeer && p2->relatedpeer->directmediaacl) {
+		p2_directmediaacl = ast_duplicate_acl_list(p2->relatedpeer->directmediaacl);
 	}
 	sip_pvt_unlock(p2);
 
 	sip_pvt_lock(p1);
-	if (p1->relatedpeer && p1->relatedpeer->directmediaha) {
-		p1_directmediaha = ast_duplicate_ha_list(p1->relatedpeer->directmediaha);
-	}
-
-	if (p2_directmediaha && ast_test_flag(&p1->flags[0], SIP_DIRECT_MEDIA)) {
-		if (!apply_directmedia_ha(p1, p2_directmediaha, rtptype)) {
+	if (p1->relatedpeer && p1->relatedpeer->directmediaacl) {
+		p1_directmediaacl = ast_duplicate_acl_list(p1->relatedpeer->directmediaacl);
+	}
+
+	if (p2_directmediaacl && ast_test_flag(&p1->flags[0], SIP_DIRECT_MEDIA)) {
+		if (!apply_directmedia_acl(p1, p2_directmediaacl, rtptype)) {
 			res = 0;
 		}
 	}
@@ -31080,8 +31128,8 @@
 	}
 
 	sip_pvt_lock(p2);
-	if (p1_directmediaha && ast_test_flag(&p2->flags[0], SIP_DIRECT_MEDIA)) {
-		if (!apply_directmedia_ha(p2, p1_directmediaha, rtptype)) {
+	if (p1_directmediaacl && ast_test_flag(&p2->flags[0], SIP_DIRECT_MEDIA)) {
+		if (!apply_directmedia_acl(p2, p1_directmediaacl, rtptype)) {
 			res = 0;
 		}
 	}
@@ -31089,12 +31137,12 @@
 
 allow_anyrtp_remote_end:
 
-	if (p2_directmediaha) {
-		ast_free_ha(p2_directmediaha);
-	}
-
-	if (p1_directmediaha) {
-		ast_free_ha(p1_directmediaha);
+	if (p2_directmediaacl) {
+		p2_directmediaacl = ast_free_acl_list(p2_directmediaacl);
+	}
+
+	if (p1_directmediaacl) {
+		p1_directmediaacl = ast_free_acl_list(p1_directmediaacl);
 	}
 
 	return res;
@@ -32574,6 +32622,7 @@
 	int wait_count;
 
 	network_change_event_unsubscribe();
+	acl_change_event_unsubscribe();
 
 	ast_sched_dump(sched);
 	
@@ -32723,7 +32772,7 @@
 	ao2_t_ref(sip_monitor_instances, -1, "unref the sip_monitor_instances table");
 
 	clear_sip_domains();
-	ast_free_ha(sip_cfg.contact_ha);
+	sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
 	close(sipsock);
 	ast_sched_context_destroy(sched);
 	con = ast_context_find(used_context);

Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=369959&r1=369958&r2=369959
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Wed Jul 11 13:33:36 2012
@@ -757,7 +757,7 @@
 	char default_subscribecontext[AST_MAX_CONTEXT];
 	char default_record_on_feature[FEATURE_MAX_LEN];
 	char default_record_off_feature[FEATURE_MAX_LEN];
-	struct ast_ha *contact_ha;  /*! \brief Global list of addresses dynamic peers are not allowed to use */
+	struct ast_acl_list *contact_acl;  /*! \brief Global list of addresses dynamic peers are not allowed to use */
 	struct ast_format_cap *caps; /*!< Supported codecs */
 	int tcp_enabled;
 	int default_max_forwards;    /*!< Default max forwards (SIP Anti-loop) */
@@ -1117,7 +1117,7 @@
 	int rtptimeout;                     /*!< RTP timeout time */
 	int rtpholdtimeout;                 /*!< RTP timeout time on hold*/
 	int rtpkeepalive;                   /*!< RTP send packets for keepalive */
-	struct ast_ha *directmediaha;		/*!< Which IPs are allowed to interchange direct media with this peer - copied from sip_peer */
+	struct ast_acl_list *directmediaacl; /*!< Which IPs are allowed to interchange direct media with this peer - copied from sip_peer */
 	struct ast_sockaddr recv;            /*!< Received as */
 	struct ast_sockaddr ourip;           /*!< Our IP (as seen from the outside) */
 	enum transfermodes allowtransfer;   /*!< REFER: restriction scheme */
@@ -1335,9 +1335,9 @@
 	int keepalive;                  /*!<  Keepalive: How often to send keep alive packet */
 	int keepalivesend;              /*!<  Keepalive: Scheduled item for sending keep alive packet */
 	struct ast_sockaddr defaddr;     /*!<  Default IP address, used until registration */
-	struct ast_ha *ha;              /*!<  Access control list */
-	struct ast_ha *contactha;       /*!<  Restrict what IPs are allowed in the Contact header (for registration) */
-	struct ast_ha *directmediaha;   /*!<  Restrict what IPs are allowed to interchange direct media with */
+	struct ast_acl_list *acl;              /*!<  Access control list */
+	struct ast_acl_list *contactacl;       /*!<  Restrict what IPs are allowed in the Contact header (for registration) */
+	struct ast_acl_list *directmediaacl;   /*!<  Restrict what IPs are allowed to interchange direct media with */
 	struct ast_variable *chanvars;  /*!<  Variables to set for channel created by user */
 	struct sip_pvt *mwipvt;         /*!<  Subscription for MWI */
 	struct sip_st_cfg stimer;       /*!<  SIP Session-Timers */

Added: trunk/configs/acl.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/acl.conf.sample?view=auto&rev=369959
==============================================================================
--- trunk/configs/acl.conf.sample (added)
+++ trunk/configs/acl.conf.sample Wed Jul 11 13:33:36 2012
@@ -1,0 +1,86 @@
+;
+; Named Access Control Lists (ACLs)
+;
+; A convenient way to share acl definitions
+;
+; This configuration file is read on startup
+;
+; CLI Commands
+; -----------------------------------------------------------
+;   acl show                         Show all named ACLs configured
+;   acl show <name>                  Show contents of a particular named ACL
+;   reload acl                       Reload configuration file
+;
+;[general]
+;systemname=asterisksystem1          ; If a system name is specified, realtime
+;                                    ; ACLs will only be retrieved if they have
+;                                    ; a systemname field that matches this value.
+;                                    ; If it's less blank, the field is ignored.
+;
+; Any configuration that uses ACLs which has been made to be able to use named
+; ACLs will specify a named ACL with the 'acl' option in its configuration in
+; a similar fashion to the usual 'permit' and 'deny' options. Example:
+; acl=my_named_acl
+;
+; Multiple named ACLs can be applied by either comma separating the arguments or
+; just by adding additional ACL lines. Example:
+; acl=my_named_acl
+; acl=my_named_acl2
+;
+; or
+;
+; acl=my_named_acl,my_named_acl2
+;
+; ACLs specified by name are evaluated independently from the ACL specified via
+; permit/deny. In order for an address to pass a given ACL, it must pass both
+; the ACL specified by permit/deny for a given item as well as any named ACLs
+; that were specified.
+;
+;[example_named_acl1]
+;deny=0.0.0.0/0.0.0.0
+;permit=209.16.236.0
+;permit=209.16.236.1
+;
+;[example_named_acl2]
+;permit=0.0.0.0/0.0.0.0
+;deny=10.24.20.171
+;deny=10.24.20.103
+;deny=209.16.236.1
+;
+; example_named_acl1 above shows an example of whitelisting. When whitelisting, the
+; named ACLs should follow a deny that blocks everything (like deny=0.0.0.0/0.0.0.0)
+; The following example explains how combining the ACLs works:
+; <in another configuration>
+; [example_item_with_acl]
+; acl=example_named_acl1
+; acl=example_named_acl2
+;
+; Suppose 209.16.236.0 tries to communicate and the ACL for that example is applied to it...
+; First, example_named_acl1 is evaluated. The address is allowed by that ACL.
+; Next, example_named_acl2 is evaluated. The address isn't blocked by example_named_acl2
+; either, so it passes.
+;
+; Suppose instead 209.16.236.1 tries to communicate and the same ACL is applied.
+; First, example_named_acl1 is evaluated and the address is allowed.
+; However, it is blocked by example_named_acl2, so the address is blocked from the combined
+; ACL.
+;
+; Similarly, the permits/denies in specific configurations that make up an ACL definition
+; are also treated as a separate ACL for evaluation. So if we change the example above to:
+; <in another configuration>
+; [example_item_with_acl]
+; acl=example_named_acl1
+; acl=example_named_acl2
+; deny=209.16.236.0
+;
+; Then 209.16.236.0 will be rejected by the non-named component of the combined ACL even
+; though it passes the two named components.
+;
+;
+; Named ACLs can use ipv6 addresses just like normal ACLs.
+;[ipv6_example_1]
+;deny = ::
+;permit = ::1/128
+;
+;[ipv6_example_2]
+;permit = fe80::21d:bad:fad:2323

Propchange: trunk/configs/acl.conf.sample
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/configs/acl.conf.sample
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: trunk/configs/acl.conf.sample
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: trunk/configs/extconfig.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/extconfig.conf.sample?view=diff&rev=369959&r1=369958&r2=369959
==============================================================================
--- trunk/configs/extconfig.conf.sample (original)
+++ trunk/configs/extconfig.conf.sample Wed Jul 11 13:33:36 2012
@@ -32,6 +32,11 @@
 ;	cdr.conf

[... 1722 lines stripped ...]



More information about the asterisk-commits mailing list