瀏覽代碼

modules/permissions: DNS domain names in address table

Added the possibility to check also against DNS domain names with
allow_address() function.
Now in the address table one group can have exact IPs, subnet IPs
and DNS domain names.
avamanu 12 年之前
父節點
當前提交
7d46ff0e54

+ 65 - 50
modules/permissions/README

@@ -14,9 +14,9 @@ Edited by
 
 Juha Heinanen
 
-   Copyright © 2003 Miklos Tirpak
+   Copyright © 2003 Miklos Tirpak
 
-   Copyright © 2006-2008 Juha Heinanen
+   Copyright © 2006-2008 Juha Heinanen
      __________________________________________________________________
 
    Table of Contents
@@ -250,7 +250,7 @@ Chapter 1. Admin Guide
 
    Function for registration checking is called allow_register and the
    algorithm is very similar to the algorithm described in Section 1.1,
-   "Call Routing". The only difference is in the way how pairs are
+   “Call Routing�. The only difference is in the way how pairs are
    created.
 
    Instead of From header field the function uses To header field because
@@ -261,8 +261,8 @@ Chapter 1. Admin Guide
    Thus, pairs used in matching will look like this: (To, Contact 1), (To,
    Contact 2), (To, Contact 3), and so on..
 
-   The algorithm of matching is same as described in Section 1.1, "Call
-   Routing".
+   The algorithm of matching is same as described in Section 1.1, “Call
+   Routing�.
 
 1.3. URI Permissions
 
@@ -291,16 +291,25 @@ Chapter 1. Admin Guide
 
 1.4. Address Permissions
 
-   The module can be used to determine if an address (IP address and port)
-   matches any of the IP subnets stored in cached Kamailio database table.
-   Port 0 in cached database table matches any port. IP address and port
-   to be matched can be either taken from the request
+   The module can be used to determine if an address (IP address and port
+   or DNS domain name) matches any of the addresses stored in cached
+   Kamailio database table. IP addresses in the database table can be
+   subnet addresses. Port 0 in cached database table matches any port. The
+   address and port to be matched can be either taken from the request
    (allow_source_address) or given as pvar arguments (allow_address).
 
-   Addresses stored in cached database table can be grouped together into
-   one or more groups specified by a group identifier (positive integer
-   value, i.e., equal or greater than 1). Group identifier is given as
-   argument to allow_address and allow_source_address functions.
+   Addresses stored in database table can be grouped together into one or
+   more groups specified by a group identifier (positive integer value,
+   i.e., equal or greater than 1). Group identifier is given as argument
+   to allow_address and allow_source_address functions. One group can
+   contain all of the three types of addresses: exact IP address, subnet
+   IP address or DNS domain name.
+
+   When matching is done if the argument is an IP, it is tried to be
+   matched with the records from that group that are of type exact IP or
+   subnet. If the argument is not an IP it is tried to be matched with the
+   records that are DNS domain names. No DNS lookup is performed, only
+   strict matching.
 
    As a side effect of matching the address, non-NULL tag (see tag_col
    module parameter) is added as value to peer_tag AVP if peer_tag_avp
@@ -378,7 +387,7 @@ Chapter 1. Admin Guide
    specify full pathname then the directory in which is the main config
    file is located will be used.
 
-   Default value is "permissions.allow".
+   Default value is “permissions.allow�.
 
    Example 1.1. Set default_allow_file parameter
 ...
@@ -391,7 +400,7 @@ modparam("permissions", "default_allow_file", "/etc/permissions.allow")
    without parameters. If you don't specify full pathname then the
    directory in which the main config file is located will be used.
 
-   Default value is "permissions.deny".
+   Default value is “permissions.deny�.
 
    Example 1.2. Set default_deny_file parameter
 ...
@@ -426,7 +435,7 @@ Note
 
    Including leading dot.
 
-   Default value is ".allow".
+   Default value is “.allow�.
 
    Example 1.4. Set allow_suffix parameter
 ...
@@ -443,7 +452,7 @@ Note
 
    Including leading dot.
 
-   Default value is ".deny".
+   Default value is “.deny�.
 
    Example 1.5. Set deny_suffix parameter
 ...
@@ -455,7 +464,7 @@ modparam("permissions", "deny_suffix", ".deny")
    This is URL of the database to be used to store rules used by
    allow_trusted function.
 
-   Default value is "NULL".
+   Default value is “NULL�.
 
    Example 1.6. Set db_url parameter
 ...
@@ -464,10 +473,10 @@ modparam("permissions", "db_url", "dbdriver://username:password@dbhost/dbname")
 
 3.7. address_table (string)
 
-   Name of database table containing IP subnet information used by
-   allow_address and allow_source_address functions.
+   Name of database table containing IP subnets and DNS domain names used
+   by allow_address and allow_source_address functions.
 
-   Default value is "address".
+   Default value is “address�.
 
    Example 1.7. Set address_table parameter
 ...
@@ -479,7 +488,7 @@ modparam("permissions", "address_table", "addr")
    Name of address table column containing group identifier of the
    address.
 
-   Default value is "grp".
+   Default value is “grp�.
 
    Example 1.8. Set grp_col parameter
 ...
@@ -490,7 +499,7 @@ modparam("permissions", "grp_col", "group_id")
 
    Name of address table column containing IP address part of the address.
 
-   Default value is "ip_addr".
+   Default value is “ip_addr�.
 
    Example 1.9. Set ip_addr_col parameter
 ...
@@ -502,7 +511,7 @@ modparam("permissions", "ip_addr_col", "ip_address")
    Name of address table column containing network mask of the address.
    Possible values are 0-32.
 
-   Default value is "mask".
+   Default value is “mask�.
 
    Example 1.10. Set mask_col parameter
 ...
@@ -513,7 +522,7 @@ modparam("permissions", "mask_col", "subnet_length")
 
    Name of address table column containing port part of the address.
 
-   Default value is "port".
+   Default value is “port�.
 
    Example 1.11. Set port_col parameter
 ...
@@ -537,7 +546,7 @@ modparam("permissions", "db_mode", 1)
    Name of database table containing matching rules used by allow_trusted
    function.
 
-   Default value is "trusted".
+   Default value is “trusted�.
 
    Example 1.13. Set trusted_table parameter
 ...
@@ -549,7 +558,7 @@ modparam("permissions", "trusted_table", "pbx")
    Name of trusted table column containing source IP address that is
    matched against source IP address of received request.
 
-   Default value is "src_ip".
+   Default value is “src_ip�.
 
    Example 1.14. Set source_col parameter
 ...
@@ -560,10 +569,10 @@ modparam("permissions", "source_col", "source_ip_address")
 
    Name of trusted table column containing transport protocol that is
    matched against transport protocol of received request. Possible values
-   that can be stored in proto_col are "any", "udp", "tcp", "tls", "sctp",
-   and "none". Value "any" matches always and value "none" never.
+   that can be stored in proto_col are “any�, “udp�, “tcp�, “tls�, “sctp�,
+   and “none�. Value “any� matches always and value “none� never.
 
-   Default value is "proto".
+   Default value is “proto�.
 
    Example 1.15. Set proto_col parameter
 ...
@@ -575,7 +584,7 @@ modparam("permissions", "proto_col", "transport")
    Name of trusted table column containing regular expression that is
    matched against From URI.
 
-   Default value is "from_pattern".
+   Default value is “from_pattern�.
 
    Example 1.16. Set from_col parameter
 ...
@@ -588,7 +597,7 @@ modparam("permissions", "from_col", "regexp")
    added as value to peer_tag AVP if peer_tag AVP has been defined and if
    the address or peer matches.
 
-   Default value is "tag".
+   Default value is “tag�.
 
    Example 1.17. Set tag_col parameter
 ...
@@ -600,7 +609,7 @@ modparam("permissions", "tag_col", "peer_tag")
    If defined, the AVP will be set as side effect of allow_trusted() call
    to not NULL tag column value of the matching peer.
 
-   Default value is "undefined".
+   Default value is “undefined�.
 
    Example 1.18. Set peer_tag_avp parameter
 ...
@@ -613,7 +622,7 @@ modparam("permissions", "peer_tag_avp", "$avp(i:707)")
    adds the tags of all matches to the avp. In addition the return value
    of allow_trusted() is the number of matches.
 
-   Default value is "0".
+   Default value is “0�.
 
    Example 1.19. Set peer_tag_mode parameter
 ...
@@ -637,7 +646,7 @@ modparam("permissions", "peer_tag_mode", "1")
 4.1.  allow_routing()
 
    Returns true if all pairs constructed as described in Section 1.1,
-   "Call Routing" have appropriate permissions according to the
+   “Call Routing� have appropriate permissions according to the
    configuration files. This function uses default configuration files
    specified in default_allow_file and default_deny_file.
 
@@ -653,7 +662,7 @@ if (allow_routing()) {
 4.2.  allow_routing(basename)
 
    Returns true if all pairs constructed as described in Section 1.1,
-   "Call Routing" have appropriate permissions according to the
+   “Call Routing� have appropriate permissions according to the
    configuration files given as parameters.
 
    Meaning of the parameters is as follows:
@@ -676,7 +685,7 @@ if (allow_routing("basename")) {
 4.3.  allow_routing(allow_file,deny_file)
 
    Returns true if all pairs constructed as described in Section 1.1,
-   "Call Routing" have appropriate permissions according to the
+   “Call Routing� have appropriate permissions according to the
    configuration files given as parameters.
 
    Meaning of the parameters is as follows:
@@ -701,7 +710,7 @@ if (allow_routing("rules.allow", "rules.deny")) {
 4.4.  allow_register(basename)
 
    The function returns true if all pairs constructed as described in
-   Section 1.2, "Registration Permissions" have appropriate permissions
+   Section 1.2, “Registration Permissions� have appropriate permissions
    according to the configuration files given as parameters.
 
    Meaning of the parameters is as follows:
@@ -729,7 +738,7 @@ if (method=="REGISTER") {
 4.5.  allow_register(allow_file, deny_file)
 
    The function returns true if all pairs constructed as described in
-   Section 1.2, "Registration Permissions" have appropriate permissions
+   Section 1.2, “Registration Permissions� have appropriate permissions
    according to the configuration files given as parameters.
 
    Meaning of the parameters is as follows:
@@ -758,8 +767,8 @@ if (method=="REGISTER") {
 
 4.6.  allow_uri(basename, pvar)
 
-   Returns true if the pair constructed as described in Section 1.3, "URI
-   Permissions" have appropriate permissions according to the
+   Returns true if the pair constructed as described in Section 1.3, “URI
+   Permissions� have appropriate permissions according to the
    configuration files specified by the parameter.
 
    Meaning of the parameter is as follows:
@@ -785,11 +794,15 @@ if (allow_uri("basename", "$avp(i:705)") {  // Check URI stored in $avp(i:705)
 
 4.7.  allow_address(group_id, ip_addr_pvar, port_pvar)
 
-   Returns true if IP address and port given as values of pvar arguments
-   belonging to a group given as group_id argument matches an IP subnet
-   found in cached address table. Cached address table entry containing
-   port value 0 matches any port. group_id argument can be an integer
-   string or a pseudo variable.
+   Returns true if address and port given as values of pvar arguments
+   belonging to a group given as group_id argument matches an IP subnet or
+   a DNS domain name found in cached address table. When matching is done
+   if the argument is an IP, it is tried to be matched with the records
+   from that group that are of type exact IP or subnet. If the argument is
+   not an IP it is tried to be matched with the records that are DNS
+   domain names. No DNS lookup is performed, only strict matching. Cached
+   address table entry containing port value 0 matches any port. group_id
+   argument can be an integer string or a pseudo variable.
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
@@ -800,8 +813,10 @@ if (allow_uri("basename", "$avp(i:705)") {  // Check URI stored in $avp(i:705)
 if (!allow_address("1", "$si", "$sp")) {
         sl_send_reply("403", "Forbidden");
 };
-// Check IP address/port stored in AVPs i:704/i:705 is in group 2
-if (!allow_address("2", "$avp(i:704)", "$avp(i:705)") {
+// Check address/port stored in AVPs src_adr/src_port is in group 2
+$avp(dst_adr) = "sipdomain.com";
+$avp(dst_port) = "0";
+if (!allow_address("2", "$avp(dst_adr)", "$avp(dst_port)") {
         sl_send_reply("403", "Forbidden");
 };
 ...
@@ -862,8 +877,8 @@ if ($var(group) != -1) {
    Checks based either on request's source address and transport protocol
    or source address and transport protocol given in pvar arguments, and
    From URI of request if request can be trusted without authentication.
-   Returns 1 if a match is found as described in Section 1.5, "Trusted
-   Requests" and -1 otherwise. If a match is found and peer_tag_avp has
+   Returns 1 if a match is found as described in Section 1.5, “Trusted
+   Requests� and -1 otherwise. If a match is found and peer_tag_avp has
    been defined, adds a non-NULL tag column value of the matching peer to
    AVP peer_tag_avp.
 

+ 119 - 50
modules/permissions/address.c

@@ -54,6 +54,11 @@ struct subnet **subnet_table;        /* Ptr to current subnet table */
 struct subnet *subnet_table_1;       /* Ptr to subnet table 1 */
 struct subnet *subnet_table_2;       /* Ptr to subnet table 2 */
 
+struct domain_name_list ***domain_list_table;        /* Ptr to current domain name table */
+static struct domain_name_list **domain_list_table_1;       /* Ptr to domain name table 1 */
+static struct domain_name_list **domain_list_table_2;       /* Ptr to domain name table 2 */
+
+
 static db1_con_t* db_handle = 0;
 static db_func_t perm_dbf;
 
@@ -87,6 +92,7 @@ int reload_address_table(void)
 
 	struct addr_list **new_hash_table;
 	struct subnet *new_subnet_table;
+	struct domain_name_list **new_domain_name_table;
 	int i;
 	unsigned int gid;
 	unsigned int port;
@@ -129,6 +135,16 @@ int reload_address_table(void)
 		new_subnet_table = subnet_table_1;
 	}
 
+	/* Choose new domain name table */
+	if (*domain_list_table == domain_list_table_1) {
+		empty_domain_name_table(domain_list_table_2);
+		new_domain_name_table = domain_list_table_2;
+	} else {
+		empty_domain_name_table(domain_list_table_1);
+		new_domain_name_table = domain_list_table_1;
+	}
+
+
 	row = RES_ROWS(res);
 
 	LM_DBG("Number of rows in address table: %d\n", RES_ROW_N(res));
@@ -173,42 +189,55 @@ int reload_address_table(void)
 		port = VAL_UINT(val + 3);
 		tagv = VAL_NULL(val + 4)?NULL:(char *)VAL_STRING(val + 4);
 		ipa = strtoipX(&ips);
-		if(ipa==NULL)
+		if ( ipa==NULL )
 		{
-			LM_DBG("failure during IP address conversion\n");
-			goto dberror;
-		}
-		if(ipa->af == AF_INET6) {
-			if(mask<0 || mask>128) {
-				LM_DBG("failure during IP mask check for v6\n");
-				goto dberror;
-			}
+			LM_DBG("Domain name: %.*s\n", ips.len, ips.s);
+		//	goto dberror;
 		} else {
-			if(mask<0 || mask>32) {
-				LM_DBG("failure during IP mask check for v4\n");
-				goto dberror;
+			if(ipa->af == AF_INET6) {
+				if(mask<0 || mask>128) {
+					LM_DBG("failure during IP mask check for v6\n");
+					goto dberror;
+				}
+			} else {
+				if(mask<0 || mask>32) {
+					LM_DBG("failure during IP mask check for v4\n");
+					goto dberror;
+				}
 			}
 		}
-		if((ipa->af==AF_INET6 && mask==128) || (ipa->af==AF_INET && mask==32))
-		{
-			if (addr_hash_table_insert(new_hash_table, gid, ipa, port, tagv)
-					== -1) {
-				LM_ERR("hash table problem\n");
-				perm_dbf.free_result(db_handle, res);
-				return -1;
+
+		if ( ipa ) {
+			if ( (ipa->af==AF_INET6 && mask==128) || (ipa->af==AF_INET && mask==32) ) {
+				if (addr_hash_table_insert(new_hash_table, gid, ipa, port, tagv)
+						== -1) {
+					LM_ERR("hash table problem\n");
+					perm_dbf.free_result(db_handle, res);
+					return -1;
+				}
+				LM_DBG("Tuple <%u, %s, %u> inserted into address hash table\n",
+						gid, ips.s, port);
+			} else {
+				if (subnet_table_insert(new_subnet_table, gid, ipa, mask,
+								port, tagv)
+							== -1) {
+					LM_ERR("subnet table problem\n");
+					perm_dbf.free_result(db_handle, res);
+					return -1;
+				}
+				LM_DBG("Tuple <%u, %s, %u, %u> inserted into subnet table\n",
+						gid, ips.s, port, mask);
 			}
-			LM_DBG("Tuple <%u, %s, %u> inserted into address hash table\n",
-					gid, ips.s, port);
 		} else {
-			if (subnet_table_insert(new_subnet_table, gid, ipa, mask,
+				if (domain_name_table_insert(new_domain_name_table, gid, &ips,
 							port, tagv)
 						== -1) {
-				LM_ERR("subnet table problem\n");
+				LM_ERR("domain name table problem\n");
 				perm_dbf.free_result(db_handle, res);
 				return -1;
 			}
-			LM_DBG("Tuple <%u, %s, %u, %u> inserted into subnet table\n",
-					gid, ips.s, port, mask);
+			LM_DBG("Tuple <%u, %s, %u> inserted into domain name table\n",
+					gid, ips.s, port);
 		}
 	}
 
@@ -216,6 +245,7 @@ int reload_address_table(void)
 
 	*addr_hash_table = new_hash_table;
 	*subnet_table = new_subnet_table;
+	*domain_list_table = new_domain_name_table;
 
 	LM_DBG("address table reloaded successfully.\n");
 
@@ -293,6 +323,21 @@ int init_addresses(void)
 
 	*subnet_table = subnet_table_1;
 
+	domain_list_table_1 = new_domain_name_table();
+	if (!domain_list_table_1) goto error;
+
+	domain_list_table_2 = new_domain_name_table();
+	if (!domain_list_table_2) goto error;
+
+	domain_list_table = (struct domain_name_list ***)shm_malloc(sizeof(struct domain_name_list **));
+	if (!domain_list_table) {
+		LM_ERR("no more shm memory for domain name table\n");
+		goto error;
+	}
+
+	*domain_list_table = domain_list_table_1;
+
+
 	if (reload_address_table() == -1) {
 		LM_CRIT("reload of address table failed\n");
 		goto error;
@@ -328,6 +373,20 @@ error:
 		shm_free(subnet_table);
 		subnet_table = 0;
 	}
+
+	if (domain_list_table_1) {
+		free_domain_name_table(domain_list_table_1);
+		domain_list_table_1 = 0;
+	}
+	if (domain_list_table_2) {
+		free_domain_name_table(domain_list_table_2);
+		domain_list_table_2 = 0;
+	}
+	if (domain_list_table) {
+		shm_free(domain_list_table);
+		domain_list_table = 0;
+	}
+
 	perm_dbf.close(db_handle);
 	db_handle = 0;
 	return -1;
@@ -361,6 +420,9 @@ void clean_addresses(void)
 	if (subnet_table_1) free_subnet_table(subnet_table_1);
 	if (subnet_table_2) free_subnet_table(subnet_table_2);
 	if (subnet_table) shm_free(subnet_table);
+	if (domain_list_table_1) free_domain_name_table(domain_list_table_1);
+	if (domain_list_table_2) free_domain_name_table(domain_list_table_2);
+	if (domain_list_table) shm_free(domain_list_table);
 }
 
 
@@ -387,10 +449,8 @@ int allow_address(struct sip_msg* _msg, char* _addr_group, char* _addr_sp,
 		LM_ERR("cannot get value of address pvar\n");
 		return -1;
 	}
-	if ( (ipa=strtoipX(&ips)) == NULL ) {
-		LM_ERR("failed to convert IP address string to in_addr\n");
-		return -1;
-	}
+
+	ipa=strtoipX(&ips);
 
 	if (_port_sp==NULL
 			|| (fixup_get_ivalue(_msg, (gparam_p)_port_sp, (int*)&port) < 0)) {
@@ -398,10 +458,14 @@ int allow_address(struct sip_msg* _msg, char* _addr_group, char* _addr_sp,
 		return -1;
 	}
 
-	if (match_addr_hash_table(*addr_hash_table, addr_group, ipa, port) == 1)
-		return 1;
-	else
-		return match_subnet_table(*subnet_table, addr_group, ipa, port);
+	if ( ipa ) {
+		if (match_addr_hash_table(*addr_hash_table, addr_group, ipa, port) == 1)
+			return 1;
+		else
+			return match_subnet_table(*subnet_table, addr_group, ipa, port);
+	} else {
+		return match_domain_name_table(*domain_list_table, addr_group, &ips, port);
+	}
 }
 
 
@@ -479,30 +543,35 @@ int allow_address_group(struct sip_msg* _msg, char* _addr, char* _port)
 		LM_ERR("cannot get value of address pvar\n");
 		return -1;
 	}
-	if ( (ipa=strtoipX(&ips)) == NULL ) {
-		LM_ERR("failed to convert IP address string to in_addr\n");
-		return -1;
-	}
-
 	if (_port==NULL
 			|| (fixup_get_ivalue(_msg, (gparam_p)_port, (int*)&port) < 0)) {
 		LM_ERR("cannot get value of port pvar\n");
 		return -1;
 	}
 
-	LM_DBG("looking for <%.*s, %u> in address table\n",
-			ips.len, ips.s, port);
-	group = find_group_in_addr_hash_table(*addr_hash_table,
-			ipa, port);
-	LM_DBG("Found address in group <%d>\n", group);
+	ipa=strtoipX(&ips);
 
-	if (group != -1) return group;
+	if ( ipa ) {
+		LM_DBG("looking for <%.*s, %u> in address table\n",
+				ips.len, ips.s, port);
+		group = find_group_in_addr_hash_table(*addr_hash_table,
+				ipa, port);
+		LM_DBG("Found address in group <%d>\n", group);
+
+		if (group != -1) return group;
+
+		LM_DBG("looking for <%.*s, %u> in subnet table\n",
+				ips.len, ips.s, port);
+		group = find_group_in_subnet_table(*subnet_table,
+				ipa, port);
+		LM_DBG("Found a match of subnet in group <%d>\n", group);
+	} else {
+		LM_DBG("looking for <%.*s, %u> in domain_name table\n",
+				ips.len, ips.s, port);
+		group = find_group_in_domain_name_table(*domain_list_table,
+				&ips, port);
+		LM_DBG("Found a match of domain_name in group <%d>\n", group);
+	}
 
-	LM_DBG("looking for <%.*s, %u> in subnet table\n",
-			ips.len, ips.s, port);
-	group = find_group_in_subnet_table(*subnet_table,
-			&_msg->rcv.src_ip,
-			_msg->rcv.src_port);
-	LM_DBG("Found a match of subnet in group <%d>\n", group);
 	return group;
 }

+ 3 - 0
modules/permissions/address.h

@@ -34,6 +34,9 @@ extern struct addr_list ***addr_hash_table;
 extern struct subnet **subnet_table; 
 
 
+/* Pointer to current domain name table */
+extern struct domain_name_list ***domain_list_table;
+
 /*
  * Initialize data structures
  */

+ 27 - 9
modules/permissions/doc/permissions_admin.xml

@@ -169,19 +169,29 @@
 		<title>Address Permissions</title>
 		<para>
 		The module can be used to determine if an address (IP
-		address and port) matches any of the IP subnets
-		stored in cached &kamailio; database table.
-		Port 0 in cached database table matches any port.  IP
+		address and port or DNS domain name) matches any of the
+		addresses stored in cached &kamailio; database table.
+		IP addresses in the database table can be subnet addresses.
+		Port 0 in cached database table matches any port. The
 		address and port to be matched can be either taken from
 		the request (allow_source_address) or given as pvar
 		arguments (allow_address).
 		</para>
 		<para>
-		Addresses stored in cached database table can be grouped
+		Addresses stored in database table can be grouped
 		together into one or more groups specified by a group
 		identifier (positive integer value, i.e., equal or greater than 1).
 		Group identifier is given as argument to allow_address and
 		allow_source_address functions.
+		One group can contain all of the three types of addresses: exact 
+		IP address, subnet IP address or DNS domain name.
+		</para>
+		<para>
+		When matching is done if the argument is an IP, it is tried to
+		be matched with the records from that group that are of type exact
+		IP or subnet. If the argument is not an IP it is tried to be matched
+		with the records that are DNS domain names. No DNS lookup is performed,
+		only strict matching.
 		</para>
 		<para>
 		As a side effect of matching the address, non-NULL tag 
@@ -424,7 +434,7 @@ modparam("permissions", "db_url", "&exampledb;")
 	<section>
 		<title><varname>address_table</varname> (string)</title>
 		<para>
-		Name of database table containing IP subnet information used by
+		Name of database table containing IP subnets and DNS domain names used by
 		<function moreinfo="none">allow_address</function> and
                 <function moreinfo="none">allow_source_address</function>
                 functions.
@@ -947,9 +957,15 @@ if (allow_uri("basename", "$avp(i:705)") {  // Check URI stored in $avp(i:705)
 		<function moreinfo="none">allow_address(group_id, ip_addr_pvar, port_pvar)</function>
 		</title>
 		<para>
-		Returns true if IP address and port given as values of pvar
+		Returns true if address and port given as values of pvar
 		arguments belonging to a group given as group_id argument
-		matches an IP subnet found in cached address table.
+		matches an IP subnet or a DNS domain name found in cached
+		address table.
+		When matching is done if the argument is an IP, it is tried to
+		be matched with the records from that group that are of type exact
+		IP or subnet. If the argument is not an IP it is tried to be matched
+		with the records that are DNS domain names. No DNS lookup is performed,
+		only strict matching.
 		Cached address table entry containing port value 0
 		matches any port.  group_id argument can be an integer
 		string or a pseudo variable. 
@@ -966,8 +982,10 @@ if (allow_uri("basename", "$avp(i:705)") {  // Check URI stored in $avp(i:705)
 if (!allow_address("1", "$si", "$sp")) {
 	sl_send_reply("403", "Forbidden");
 };
-// Check IP address/port stored in AVPs i:704/i:705 is in group 2
-if (!allow_address("2", "$avp(i:704)", "$avp(i:705)") {
+// Check address/port stored in AVPs src_adr/src_port is in group 2
+$avp(dst_adr) = "sipdomain.com";
+$avp(dst_port) = "0";
+if (!allow_address("2", "$avp(dst_adr)", "$avp(dst_port)") {
 	sl_send_reply("403", "Forbidden");
 };
 ...

+ 217 - 0
modules/permissions/hash.c

@@ -830,3 +830,220 @@ void free_subnet_table(struct subnet* table)
 
 	shm_free(table);
 }
+
+/*
+ * Create and initialize a domain_name table
+ */
+struct domain_name_list** new_domain_name_table(void)
+{
+	struct domain_name_list** ptr;
+
+	/* Initializing hash tables and hash table variable */
+	ptr = (struct domain_name_list **)shm_malloc
+		(sizeof(struct domain_name_list*) * PERM_HASH_SIZE);
+	if (!ptr) {
+		LM_ERR("no shm memory for hash table\n");
+		return 0;
+	}
+
+	memset(ptr, 0, sizeof(struct domain_name*) * PERM_HASH_SIZE);
+	return ptr;
+}
+
+
+/* 
+ * Free contents of hash table, it doesn't destroy the
+ * hash table itself
+ */
+void empty_domain_name_table(struct domain_name_list **table)
+{
+	int i;
+	struct domain_name_list *np, *next;
+
+	for (i = 0; i < PERM_HASH_SIZE; i++) {
+		np = table[i];
+		while (np) {
+			next = np->next;
+			shm_free(np);
+			np = next;
+		}
+		table[i] = 0;
+	}
+}
+
+/*
+ * Release all memory allocated for a hash table
+ */
+void free_domain_name_table(struct domain_name_list** table)
+{
+	if (!table)
+		return;
+
+	empty_domain_name_table(table);
+	shm_free(table);
+}
+
+
+/* 
+ * Check if an entry exists in hash table that has given group, domain_name, and
+ * port.  Port 0 in hash table matches any port.
+ */
+int match_domain_name_table(struct domain_name_list** table, unsigned int group,
+		str *domain_name, unsigned int port)
+{
+	struct domain_name_list *np;
+	avp_value_t val;
+
+	for (np = table[perm_hash(*domain_name)]; np != NULL; np = np->next) {
+		if ( (np->grp == group)
+				&& ((np->port == 0) || (np->port == port))
+				&& np->domain.len == domain_name->len
+				&& strncmp(np->domain.s, domain_name->s, domain_name->len)==0 ) {
+
+			if (tag_avp.n && np->tag.s) {
+				val.s = np->tag;
+				if (add_avp(tag_avp_type|AVP_VAL_STR, tag_avp, val) != 0) {
+					LM_ERR("setting of tag_avp failed\n");
+					return -1;
+				}
+			}
+
+			return 1;
+		}
+	}
+
+	return -1;
+}
+
+
+/* 
+ * Check if an domain_name/port entry exists in hash table in any group.
+ * Returns first group in which ip_addr/port is found.
+ * Port 0 in hash table matches any port. 
+ */
+int find_group_in_domain_name_table(struct domain_name_list** table,
+		str *domain_name, unsigned int port)
+{
+	struct domain_name_list *np;
+
+	for (np = table[perm_hash(*domain_name)]; np != NULL; np = np->next) {
+		if ( ((np->port == 0) || (np->port == port))
+				&& np->domain.len == domain_name->len
+				&& strncmp(np->domain.s, domain_name->s, domain_name->len)==0 ) {
+			return np->grp;
+		}
+	}
+
+	return -1;
+}
+
+
+/* 
+ * Add <grp, domain_name, port> into hash table
+ */
+int domain_name_table_insert(struct domain_name_list** table, unsigned int grp,
+		str *domain_name, unsigned int port, char *tagv)
+{
+	struct domain_name_list *np;
+	unsigned int hash_val;
+	int len;
+
+	len = sizeof(struct domain_name_list) + domain_name->len;
+	if(tagv!=NULL)
+		len += strlen(tagv) + 1;
+
+	np = (struct domain_name_list *) shm_malloc(len);
+	if (np == NULL) {
+		LM_ERR("no shm memory for table entry\n");
+		return -1;
+	}
+
+	memset(np, 0, len);
+
+	np->grp = grp;
+	np->domain.s = (char*)np + sizeof(struct domain_name_list);
+	memcpy(np->domain.s, domain_name->s, domain_name->len);
+	np->domain.len = domain_name->len;
+	np->port = port;
+	if(tagv!=NULL) {
+		np->tag.s = (char*)np + sizeof(struct domain_name_list) + domain_name->len;
+		np->tag.len = strlen(tagv);
+		strcpy(np->tag.s, tagv);
+	}
+
+	LM_DBG("** Added domain name: %.*s\n", np->domain.len, np->domain.s);
+
+	hash_val = perm_hash(*domain_name);
+	np->next = table[hash_val];
+	table[hash_val] = np;
+
+	return 1;
+}
+
+
+/*! \brief
+ * RPC: Print addresses stored in hash table 
+ */
+int domain_name_table_rpc_print(struct domain_name_list** table, rpc_t* rpc, void* c)
+{
+	int i;
+	void* th;
+	void* ih;
+	struct domain_name_list *np;
+
+
+	if (rpc->add(c, "{", &th) < 0)
+	{
+		rpc->fault(c, 500, "Internal error creating rpc");
+		return -1;
+	}
+
+	for (i = 0; i < PERM_HASH_SIZE; i++) {
+		np = table[i];
+		while (np) {
+			if(rpc->struct_add(th, "dd{", 
+					"table", i,
+					"group", np->grp,
+					"item", &ih) < 0) {
+				rpc->fault(c, 500, "Internal error creating rpc ih");
+				return -1;
+			}
+
+			if(rpc->struct_add(ih, "S", "domain_name", &np->domain) < 0) {
+				rpc->fault(c, 500, "Internal error creating rpc data (ip)");
+				return -1;
+			}
+			if(rpc->struct_add(ih, "ds", "port",  np->port,
+						"tag",  np->tag.len ? np->tag.s : "NULL") < 0) {
+				rpc->fault(c, 500, "Internal error creating rpc data");
+				return -1;
+			}
+			np = np->next;
+		}
+	}
+	return 0;
+}
+
+/*! \brief
+ * MI: Print domain name stored in hash table 
+ */
+int domain_name_table_mi_print(struct domain_name_list** table, struct mi_node* rpl)
+{
+	int i;
+	struct domain_name_list *np;
+
+	for (i = 0; i < PERM_HASH_SIZE; i++) {
+		np = table[i];
+		while (np) {
+			if (addf_mi_node_child(rpl, 0, 0, 0,
+						"%4d <%u, %.*s, %u> [%s]",
+						i, np->grp, np->domain.len, np->domain.s,
+						np->port, (np->tag.s==NULL)?"":np->tag.s) == 0)
+				return -1;
+			np = np->next;
+		}
+	}
+	return 0;
+}
+
+

+ 54 - 0
modules/permissions/hash.h

@@ -240,4 +240,58 @@ int subnet_table_mi_print(struct subnet* table, struct mi_node* rpl);
 int subnet_table_rpc_print(struct subnet* table, rpc_t* rpc, void* c);
 
 
+/*
+ * Structure used to store domain names
+ */
+struct domain_name_list {
+	unsigned int grp;        /* address group */
+	str  domain;        /* domain_name */
+	unsigned int port;       /* port or 0 */
+	str tag;
+	struct domain_name_list* next;
+};
+
+/*
+ * Create a domain_name table
+ */
+struct domain_name_list** new_domain_name_table(void);
+
+/*
+ * Release memory allocated for a subnet table
+ */
+void free_domain_name_table(struct domain_name_list** table);
+
+/* 
+ * Empty contents of domain_name hash table
+ */
+void empty_domain_name_table(struct domain_name_list** table);
+
+/* 
+ * Check if an entry exists in domain_name table that matches given group, domain_name,
+ * and port.  Port 0 in  matches any port.
+ */
+int match_domain_name_table(struct domain_name_list** table, unsigned int group,
+		str *domain_name, unsigned int port);
+
+/* 
+ * Add <grp, domain_name, port> into hash table
+ */
+int domain_name_table_insert(struct domain_name_list** table, unsigned int grp,
+		str *domain_name, unsigned int port, char *tagv);
+
+/* 
+ * Check if an domain_name/port entry exists in hash table in any group.
+ * Returns first group in which ip_addr/port is found.
+ * Port 0 in hash table matches any port. 
+ */
+int find_group_in_domain_name_table(struct domain_name_list** table,
+		str *domain_name, unsigned int port);
+
+/*! \brief
+ * RPC: Print addresses stored in hash table 
+ */
+void domain_name_table_print(struct subnet* table, FILE* reply_file);
+int domain_name_table_rpc_print(struct domain_name_list** table, rpc_t* rpc, void* c);
+int domain_name_table_mi_print(struct domain_name_list** table, struct mi_node* rpl);
+
 #endif /* _PERM_HASH_H_ */

+ 32 - 0
modules/permissions/mi.c

@@ -196,6 +196,38 @@ void rpc_subnet_dump(rpc_t* rpc, void* c) {
 	return;
 }
 
+/*
+ * MI function to print domain name table
+ */
+struct mi_root* mi_domain_name_dump(struct mi_root *cmd_tree, void *param)
+{
+	struct mi_root* rpl_tree;
+
+	rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
+	if (rpl_tree==NULL) return 0;
+
+	if(domain_list_table && domain_name_table_mi_print(*domain_list_table, &rpl_tree->node) <  0) {
+		LM_ERR("failed to add a node\n");
+		free_mi_tree(rpl_tree);
+		return 0;
+	}
+
+	return rpl_tree;
+}
+
+
+/*! \brief
+ * RPC function to dump domain name table
+ */
+void rpc_domain_name_dump(rpc_t* rpc, void* c) {
+
+	if ( domain_name_table_rpc_print(*domain_list_table, rpc, c) < 0 ) {
+		LM_DBG("failed to print a subnet_table dump\n");
+	}
+	return;
+}
+
+
 #define MAX_FILE_LEN 128
 
 /*! \brief

+ 4 - 0
modules/permissions/mi.h

@@ -36,6 +36,7 @@
 #define MI_ADDRESS_RELOAD "address_reload"
 #define MI_ADDRESS_DUMP "address_dump"
 #define MI_SUBNET_DUMP "subnet_dump"
+#define MI_DOMAIN_DUMP "perm_domain_dump"
 
 #define MI_ALLOW_URI "allow_uri"
 
@@ -54,6 +55,9 @@ void rpc_address_dump(rpc_t* rpc, void* c);
 struct mi_root* mi_subnet_dump(struct mi_root *cmd_tree, void *param);
 void rpc_subnet_dump(rpc_t* rpc, void* c);
 
+struct mi_root* mi_domain_name_dump(struct mi_root *cmd_tree, void *param);
+void rpc_domain_name_dump(rpc_t* rpc, void* c);
+
 struct mi_root* mi_allow_uri(struct mi_root *cmd, void *param);
 void rpc_test_uri(rpc_t* rpc, void* c);
 

+ 8 - 0
modules/permissions/permissions.c

@@ -188,6 +188,7 @@ static mi_export_t mi_cmds[] = {
 		mi_addr_child_init },
 	{ MI_ADDRESS_DUMP,    mi_address_dump,    MI_NO_INPUT_FLAG,  0,  0 },
 	{ MI_SUBNET_DUMP,     mi_subnet_dump,     MI_NO_INPUT_FLAG,  0,  0 },
+	{ MI_DOMAIN_DUMP,     mi_domain_name_dump,MI_NO_INPUT_FLAG,  0,  0 },
 	{ MI_ALLOW_URI,       mi_allow_uri,       0,  0,  0 },
 	{ 0, 0, 0, 0, 0 }
 };
@@ -1009,6 +1010,12 @@ static const char* rpc_subnet_dump_doc[2] = {
 	0
 };
 
+static const char* rpc_domain_name_dump_doc[2] = {
+	"Dump permissions domain name table",
+	0
+};
+
+
 static const char* rpc_test_uri_doc[2] = {
 	"Tests if (URI, Contact) pair is allowed according to allow/deny files",
 	0
@@ -1020,6 +1027,7 @@ rpc_export_t permissions_rpc[] = {
 	{"permissions.trustedDump", rpc_trusted_dump, rpc_trusted_dump_doc, 0},
 	{"permissions.addressDump", rpc_address_dump, rpc_address_dump_doc, 0},
 	{"permissions.subnetDump", rpc_subnet_dump, rpc_subnet_dump_doc, 0},
+	{"permissions.domainDump", rpc_domain_name_dump, rpc_domain_name_dump_doc, 0},
 	{"permissions.testUri", rpc_test_uri, rpc_test_uri_doc, 0},
 	{0, 0, 0, 0}
 };