Browse Source

- added common IP tree functions for fast checking if an address is in group of subnet masks
- both IPv4 and IPv6 are supported
- new script function ip_is_trusted(comma_delimited_subnet_list, ip), for more info see doc

Tomas Mandys 17 năm trước cách đây
mục cha
commit
062f34aa30

+ 305 - 0
modules_s/permissions/doc/functions.xml

@@ -243,4 +243,309 @@ if (method=="REGISTER") {
 	    </programlisting>
 	    </programlisting>
 	</example>
 	</example>
     </section>
     </section>
+    <section id="ip_is_trusted">
+	<title>
+	    <function>ip_is_trusted(ip_set, ip)</function>
+	</title>
+	<para>
+		The function returns true if <emphasis>ip</emphasis> is contained
+		in <emphasis>ip_set</emphasis>. Both IPv4 and IPv6 are supported.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para>
+			<emphasis>ip_set</emphasis> is identified by comma/space/semicolon
+			delimited list of IP addresses or subnet masks. The subnet mask
+			is written in IP_slash_number form. If the number is not mentioned 
+			then default value is taken, the default  value is number of bits
+			of particular IP address, i.e. 32 in case of IPv4, 128 in case of IPv6.
+		</para>
+	    </listitem>
+		<listitem>
+		<para>
+			<emphasis>ip</emphasis> to test. Besides direct address in string form there
+			are extra identifiers to force IP related to current message:
+			<emphasis>Source</emphasis>, <emphasis>Destination</emphasis>, <emphasis>Received</emphasis>.
+			Note that only the first character is essential.
+		</para>
+		</listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>ip_is_trusted</function> usage</title>
+	    <programlisting>
+route[TT2] {
+	if (ip_is_trusted("$net", "$ip")) {
+		xlog("L_E", "'%$ip' - TRUE\n");
+	} else {
+		xlog("L_E", "'%$ip' - FALSE\n");
+	}
+}
+
+route[TT1] {
+	xlog("L_E", "Testing netmask '%$net'\n");
+	
+	$ip = "s";   # source address
+	route(TT2);
+	
+	$ip = "127.0.0.1";
+	route(TT2);
+	
+	$ip = "127.0.0.2";
+	route(TT2);
+	
+	$ip = "10.0.0.1";
+	route(TT2);
+	
+	$ip = "11.0.0.1";
+	route(TT2);
+	
+	$ip = "172.1.8.1";
+	route(TT2);
+	
+	$ip = "192.168.1.1";
+	route(TT2);
+	
+	$ip = "192.168.1.255";
+	route(TT2);
+	
+	$ip = "192.168.2.1";
+	route(TT2);
+	
+	$ip = "192.168.3.1";
+	route(TT2);
+	
+	$ip = "192.168.4.97";
+	route(TT2);
+	
+	$ip = "192.168.4.100";
+	route(TT2);
+
+	$ip = "[0:2:4:A:B:D:E:F301]";
+	route(TT2);
+	
+	$ip = "[0:2:4:A:B:D:E:F401]";
+	route(TT2);
+	
+	$ip = "[0:0:0:0:0:0:0:0]";
+	route(TT2);
+}
+		
+route[TEST] {
+	$net = "0.0.0.0 128.2.3.4/1 127.0.128.16 [0:2:4:A:B:D:E:F301]";
+	route(TT1);
+
+	$net = "255.255.255.255/0"; # all IPv4 addresses, dentical to 0.0.0.0/0
+	route(TT1);
+	
+	$net = "127.0.0.1/24";
+	route(TT1);
+	
+	$net = "10.0.0.0/8";  # All type A addresses
+	route(TT1);	         
+	
+	$net = "192.168.1.0/24";
+	route(TT1);
+	
+	$net = "192.168.4.96/27";
+	route(TT1);
+	
+	$net = "192.168.1.1/32"; # only one IP matches
+	route(TT1);
+	
+	$net = "192.168.1.0/24,192.168.2.0/24";
+	route(TT1);
+	
+	$net = "192.168.1.0/24,192.168.2.0/24,127.0.0.1/31";
+	route(TT1);
+	
+	$net = "[0:0:0:0:0:0:0:0]/0";  # all IPv6 addresses
+	route(TT1);
+	
+	$net = "[0:2:4:A:B:D:E:f300]/120";
+	route(TT1);
+
+
+# the result is:
+
+Testing netmask '0.0.0.0 128.2.3.4/1 127.0.128.16 [0:2:4:A:B:D:E:F301]'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - TRUE
+'192.168.1.1' - TRUE
+'192.168.1.255' - TRUE
+'192.168.2.1' - TRUE
+'192.168.3.1' - TRUE
+'192.168.4.97' - TRUE
+'192.168.4.100' - TRUE
+'[0:2:4:A:B:D:E:F301]' - TRUE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '255.255.255.255/0'
+'s' - TRUE
+'127.0.0.1' - TRUE
+'127.0.0.2' - TRUE
+'10.0.0.1' - TRUE
+'11.0.0.1' - TRUE
+'172.1.8.1' - TRUE
+'192.168.1.1' - TRUE
+'192.168.1.255' - TRUE
+'192.168.2.1' - TRUE
+'192.168.3.1' - TRUE
+'192.168.4.97' - TRUE
+'192.168.4.100' - TRUE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '127.0.0.1/24'
+'s' - FALSE
+'127.0.0.1' - TRUE
+'127.0.0.2' - TRUE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - FALSE
+'192.168.1.255' - FALSE
+'192.168.2.1' - FALSE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '10.0.0.0/8'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - TRUE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - FALSE
+'192.168.1.255' - FALSE
+'192.168.2.1' - FALSE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '192.168.1.0/24'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - TRUE
+'192.168.1.255' - TRUE
+'192.168.2.1' - FALSE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '192.168.4.96/27'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - FALSE
+'192.168.1.255' - FALSE
+'192.168.2.1' - FALSE
+'192.168.3.1' - FALSE
+'192.168.4.97' - TRUE
+'192.168.4.100' - TRUE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '192.168.1.1/32'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - TRUE
+'192.168.1.255' - FALSE
+'192.168.2.1' - FALSE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '192.168.1.0/24,192.168.2.0/24'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - TRUE
+'192.168.1.255' - TRUE
+'192.168.2.1' - TRUE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '192.168.1.0/24,192.168.2.0/24,127.0.0.1/31'
+'s' - FALSE
+'127.0.0.1' - TRUE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - TRUE
+'192.168.1.255' - TRUE
+'192.168.2.1' - TRUE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - FALSE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+Testing netmask '[0:0:0:0:0:0:0:0]/0'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - FALSE
+'192.168.1.255' - FALSE
+'192.168.2.1' - FALSE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - TRUE
+'[0:2:4:A:B:D:E:F401]' - TRUE
+'[0:0:0:0:0:0:0:0]' - TRUE
+Testing netmask '[0:2:4:A:B:D:E:f300]/120'
+'s' - FALSE
+'127.0.0.1' - FALSE
+'127.0.0.2' - FALSE
+'10.0.0.1' - FALSE
+'11.0.0.1' - FALSE
+'172.1.8.1' - FALSE
+'192.168.1.1' - FALSE
+'192.168.1.255' - FALSE
+'192.168.2.1' - FALSE
+'192.168.3.1' - FALSE
+'192.168.4.97' - FALSE
+'192.168.4.100' - FALSE
+'[0:2:4:A:B:D:E:F301]' - TRUE
+'[0:2:4:A:B:D:E:F401]' - FALSE
+'[0:0:0:0:0:0:0:0]' - FALSE
+}
+	    </programlisting>
+	</example>
+    </section>
 </section>
 </section>

+ 142 - 0
modules_s/permissions/ip_set.c

@@ -0,0 +1,142 @@
+/* $Id$
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "ip_set.h"
+#include "../../resolve.h"
+#include <stdio.h>
+#include <string.h>
+
+void ip_set_init(struct ip_set *ip_set) {
+	memset(ip_set, 0, sizeof(*ip_set));
+	ip_tree_init(&ip_set->ipv4_tree);
+	#ifdef USE_IPV6
+	ip_tree_init(&ip_set->ipv6_tree);
+	#endif
+}
+
+void ip_set_destroy(struct ip_set *ip_set) {
+	ip_tree_destroy(&ip_set->ipv4_tree, 0);
+	#ifdef USE_IPV6
+	ip_tree_destroy(&ip_set->ipv6_tree, 0);
+	#endif
+}
+
+int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix) {
+	switch (ip->af) {
+		case AF_INET:
+			return ip_tree_add_ip(&ip_set->ipv4_tree, ip->u.addr, (ip->len*8<network_prefix)?ip->len*8:network_prefix);
+	#ifdef USE_IPV6
+		case AF_INET6:
+			return ip_tree_add_ip(&ip_set->ipv6_tree, ip->u.addr, (ip->len*8<network_prefix)?ip->len*8:network_prefix);
+	#endif
+		default:
+			return -1;				
+		}
+}
+
+int ip_set_ip_exists(struct ip_set *ip_set, struct ip_addr *ip) {
+	struct ip_tree_find h;
+	switch (ip->af) {
+		case AF_INET:
+			return ip_tree_find_ip(ip_set->ipv4_tree, ip->u.addr, ip->len*8, &h) > 0;
+	#ifdef USE_IPV6
+		case AF_INET6:
+			return ip_tree_find_ip(ip_set->ipv6_tree, ip->u.addr, ip->len*8, &h) > 0;
+	#endif
+		default:
+			return -1;				
+		}
+}
+
+void ip_set_print(FILE *stream, struct ip_set *ip_set) {
+	fprintf(stream, "IPv4:\n");
+	ip_tree_print(stream, ip_set->ipv4_tree, 2);
+	#ifdef USE_IPV6
+	fprintf(stream, "IPv6:\n");
+	ip_tree_print(stream, ip_set->ipv6_tree, 2);
+	#endif
+}
+
+int ip_set_parse(struct ip_set *ip_set, str ip_set_s){
+
+	str s;
+	struct ip_addr *ip;
+	unsigned int prefix;
+	
+	ip_set_init(ip_set);
+
+	/* parse comma delimited string of IPs and masks,  e.g. 1.2.3.4,2.3.5.3/24,[abcd:12456:2775:ab::7533] */
+	while (ip_set_s.len) {
+		while (ip_set_s.len && (*ip_set_s.s == ',' || *ip_set_s.s == ';' || *ip_set_s.s == ' ')) {			
+			ip_set_s.s++;
+			ip_set_s.len--;
+		}
+		s.s = ip_set_s.s;
+		s.len = 0;
+		while (s.len < ip_set_s.len && (s.s[s.len] != ',' && s.s[s.len] != ';' && s.s[s.len] != ' ' && s.s[s.len] != '/')) {
+			s.len++;
+		}
+		if ( ((ip = str2ip(&s))==0)
+			#ifdef  USE_IPV6
+						  && ((ip = str2ip6(&s))==0)
+			#endif
+										  ){
+			ip_set_destroy(ip_set);
+			ERR("ip_set_parse: string to ip conversion error near '%.*s'\n", ip_set_s.len, ip_set_s.s);
+			return -1;
+		}
+		ip_set_s.s += s.len;
+		ip_set_s.len -= s.len;
+		if (ip_set_s.len && ip_set_s.s[0] == '/') {
+			ip_set_s.s++;
+			ip_set_s.len--;
+			s.s = ip_set_s.s;
+			s.len = 0;
+			while (s.len < ip_set_s.len && (s.s[s.len] >= '0' && s.s[s.len] <= '9')) {
+				s.len++;
+			}
+			if (str2int(&s, &prefix) < 0) {
+				ERR("ip_set_parse: cannot convert mask near '%.*s'\n", ip_set_s.len, ip_set_s.s);
+				ip_set_destroy(ip_set);
+				return -1;
+			}				
+			ip_set_s.s += s.len;
+			ip_set_s.len -= s.len;
+		}
+		else {
+			prefix = ip->len*8;
+		}
+		if (ip_set_add_ip(ip_set, ip, prefix) < 0) {
+			ERR("ip_set_parse: cannot add IP into ip set\n");
+			ip_set_destroy(ip_set);
+			return -1;
+		}		
+	}
+	return 0;		
+}
+
+

+ 51 - 0
modules_s/permissions/ip_set.h

@@ -0,0 +1,51 @@
+/* $Id$
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _IP_SET_H_
+#define _IP_SET_H_
+
+#include "ip_tree.h"
+#include "../../ip_addr.h"
+#include <stdio.h>
+
+/* ip_set stuff, combines IPv4 and IPv6 tree in one set */
+   
+struct ip_set {
+	struct ip_tree_leaf *ipv4_tree;
+#ifdef USE_IPV6
+	struct ip_tree_leaf *ipv6_tree;	
+#endif
+};
+
+extern void ip_set_init(struct ip_set *ip_set);
+extern void ip_set_destroy(struct ip_set *ip_set);
+extern int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix);
+extern int ip_set_ip_exists(struct ip_set *ip_set, struct ip_addr *ip);
+extern void ip_set_print(FILE *stream, struct ip_set *ip_set);
+extern int ip_set_parse(struct ip_set *ip_set, str ip_set_s);
+
+#endif

+ 254 - 0
modules_s/permissions/ip_tree.c

@@ -0,0 +1,254 @@
+/* $Id$
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "ip_tree.h"
+#include "../../mem/mem.h"
+
+#include <stdio.h>
+#include <string.h>
+
+void ip_tree_init(struct ip_tree_leaf **tree) {
+	*tree = NULL;
+}
+
+void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only) {
+	int i;
+	if (*tree) {
+		for (i=0; i<=1; i++) {
+			if ((*tree)->next[i])
+				ip_tree_destroy(&(*tree)->next[i], 0);
+		}
+		if (!leaves_only) {
+			pkg_free(*tree);
+			*tree = NULL;
+		}
+	}
+}
+
+int ip_tree_find_ip(struct ip_tree_leaf *tree, unsigned char *ip, unsigned int ip_len, struct ip_tree_find *h) {
+	
+	h->leaf = tree;
+		
+	h->ip = ip;
+	h->ip_len = ip_len;	
+	h->ip_mask = 0x80;
+	if (!tree) return IP_TREE_FIND_NOT_FOUND;
+	
+	do {
+	
+		h->leaf_prefix_match_mask = 0x80;
+		h->leaf_prefix_match = &h->leaf->prefix_match[0];
+		h->leaf_prefix_match_len = 0;
+		if (h->ip_len == 0)
+			return IP_TREE_FIND_FOUND_UPPER_SET;
+		while (h->leaf_prefix_match_len < h->leaf->prefix_match_len) {
+			if (((*(h->ip) & h->ip_mask) == 0) != ((*(h->leaf_prefix_match) & h->leaf_prefix_match_mask) == 0)) {
+				return IP_TREE_FIND_NOT_FOUND;
+			}
+			h->leaf_prefix_match_len++;
+			h->ip_len--;
+			if (unlikely(h->ip_len == 0))
+				return IP_TREE_FIND_FOUND_UPPER_SET;
+			
+			if (unlikely(h->ip_mask == 0x01)) {
+				h->ip_mask = 0x80;
+				h->ip++;
+			}
+			else {
+				h->ip_mask /= 2; /* >> 1 */
+			}
+			if (unlikely(h->leaf_prefix_match_mask == 0x01)) {
+				h->leaf_prefix_match_mask = 0x80;
+				h->leaf_prefix_match++;
+			}
+			else {
+				h->leaf_prefix_match_mask /= 2; /* >> 1 */
+			}
+		}
+
+		h->leaf = h->leaf->next[(*(h->ip) & h->ip_mask) != 0];
+		if (unlikely(h->ip_mask == 0x01)) {
+			h->ip_mask = 0x80;
+			h->ip++;
+		}
+		else {
+			h->ip_mask /= 2; /* >> 1 */
+		}
+		h->ip_len--;
+		
+	} while (h->leaf);
+	return IP_TREE_FIND_FOUND;
+}
+
+static inline struct ip_tree_leaf *ip_tree_malloc_leaf(unsigned int ip_len) {
+	return pkg_malloc(sizeof(struct ip_tree_leaf)+((ip_len==0)?0:((ip_len-1)/8 +1)));
+}
+
+int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len) {
+	struct ip_tree_find h;
+	struct ip_tree_leaf *l0, *l1;
+	int ret, i, n;
+	unsigned char mask, *pm;
+	ret = ip_tree_find_ip(*tree, ip, ip_len, &h);
+								
+	switch (ret) {
+		case IP_TREE_FIND_FOUND_UPPER_SET:
+			/* ip covers wider subnet range than already defined range, we can delete all subleaves and reduce prefix match match */
+			h.leaf->prefix_match_len = h.leaf_prefix_match_len;
+			ip_tree_destroy(&h.leaf, 1);
+			break;
+			
+		case IP_TREE_FIND_FOUND:
+			/* ip is already in set */
+			break;
+			
+		case IP_TREE_FIND_NOT_FOUND:
+			if (h.leaf) {
+				/* split leaf into two leaves */
+				n = h.ip_len - 1;
+				l1 = ip_tree_malloc_leaf(n);
+				if (!l1) return -1;
+				l1->prefix_match_len = n;
+				for (i=0; i<=1; i++)
+					l1->next[i] = NULL;				
+				n = h.leaf->prefix_match_len - h.leaf_prefix_match_len - 1;
+				l0 = ip_tree_malloc_leaf(n);
+				if (!l0) {
+					ip_tree_destroy(&l1, 0);
+					return -1;
+				}
+				l0->prefix_match_len = n;
+				for (i=0; i<=1; i++)
+					l0->next[i] = h.leaf->next[i];
+				i = (*h.leaf_prefix_match & h.leaf_prefix_match_mask) != 0;
+				h.leaf->next[i] = l0;
+				h.leaf->next[!i] = l1;
+				
+				n = h.leaf_prefix_match_len;
+				
+				/* copy remaining leaf prefix match bits, first non matched bit is treated in next decision therefore is skipped */
+				mask = 0x80;
+				pm = l0->prefix_match;
+				while (1) {
+					h.leaf_prefix_match_len++;
+					if (h.leaf_prefix_match_len >= h.leaf->prefix_match_len)
+						break;
+					if (unlikely(h.leaf_prefix_match_mask == 0x01)) {
+						h.leaf_prefix_match_mask = 0x80;
+						h.leaf_prefix_match++;
+					}
+					else {
+						h.leaf_prefix_match_mask /= 2; /* >> 1 */
+					}
+					if (mask == 0x80) 
+						*pm = 0x00;
+					if ((*h.leaf_prefix_match) & h.leaf_prefix_match_mask)
+						*pm |= mask;
+					if (mask == 0x01) {
+						mask = 0x80;
+						pm++;
+					}
+					else {
+						mask /= 2;  /* >> 1 */
+					}
+				}
+				h.leaf->prefix_match_len = n;
+				
+				/* copy remaining ip bits, first non matched bit is treated in next decision therefore is skipped */
+                mask = 0x80;
+                pm = l1->prefix_match;
+				while (1) {
+					h.ip_len--;
+					if (h.ip_len <= 0)
+						break;
+					if (unlikely(h.ip_mask == 0x01)) {
+						h.ip_mask = 0x80;
+						h.ip++;
+					}
+					else {
+						h.ip_mask /= 2; /* >> 1 */
+					}
+					if (mask == 0x80) 
+						*pm = 0x00;
+					if ((*h.ip) & h.ip_mask)
+						*pm |= mask;
+					if (mask == 0x01) {
+						mask = 0x80;
+						pm++;
+					}
+					else {
+						mask /= 2;  /* >> 1 */
+					}
+				}
+			}
+			else {
+				/* it's first leaf in tree */
+				*tree = ip_tree_malloc_leaf(ip_len);
+				if (!*tree) return -1;
+				(*tree)->prefix_match_len = ip_len;
+				if (ip_len > 0) {
+					for (i = 0; i <= (ip_len -1) / 8; i++) {
+						(*tree)->prefix_match[i] = ip[i];
+					}
+				}
+				for (i=0; i<=1; i++)
+					(*tree)->next[i] = NULL;
+			}
+			break;
+		default: /* BUG */			
+			ret = -1;
+	}
+	return ret;
+}
+
+void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent) {
+	unsigned char mask, *pm;
+	unsigned int i, j;
+
+	if (!tree) {
+		fprintf(stream, "nil\n"); 
+	}
+	else {
+		fprintf(stream, "match %d bits {", tree->prefix_match_len);
+		for (i=0, pm=tree->prefix_match, mask=0x80; i<tree->prefix_match_len; i++) {
+			fprintf(stream, (*pm & mask)?"1":"0");
+			if (mask == 0x01) {
+				mask = 0x80;
+				pm++;
+			}
+			else {
+				mask /= 2;
+			}
+		}
+		fprintf(stream, "}\n");
+		for (j=0; j<=1; j++) {
+			for (i=0; i<indent; i++) fprintf(stream, " ");
+			fprintf(stream, "%d:", j);
+			ip_tree_print(stream, tree->next[j], indent+2);
+		}		
+	}
+}

+ 114 - 0
modules_s/permissions/ip_tree.h

@@ -0,0 +1,114 @@
+/* $Id$
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _IP_TREE_H_
+#define _IP_TREE_H_ 1
+
+#include <stdio.h>
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../ip_addr.h"
+#include "../../error.h"
+#include "../../mem/mem.h"
+#include "../../trim.h"
+#include "../../ut.h"
+
+/* 
+   Implements algorithm for testing if particular address is in a iP adrress set.
+   IP adresses may also describe a subnetwork, i.e. only prefix is valuable and trailing part
+   of address has no effect for decision. The algorithm is common, both IPv4 and IPv6 
+   are supported.
+   
+   Reduce(match)/decide(fork) algorithm is applied to minimize memory consumption and looping.
+   As many bits as possible are matched in leaf (longest prefix match) . If does not match "match_bit_num" then IP is not in set 
+   otherwise next bit decides what is next leaf and matching goes on.
+   If there is not next leaf then algorithm is over (it's subnet address or special case
+   particular IP address, in this case address is matched completly).
+   
+   Examples of ip tree {prefix_match_len, prefix_match, next}:
+
+	0.0.0.0/0, i.e. all addresses
+	{0, {}, {NULL, NULL}}
+
+	128.0.0.0/1
+	{1, {0x80}, {NULL, NULL}}   
+
+	127.0.1.0/24
+	{24, {0x7F, 0x00, 0x01}, {NULL, NULL}}
+
+	127.0.1.0/24, 127.0.2.0/24
+	01111111.00000000.00000001.00000000 01111111.00000000.00000010.00000000
+	{22, {0x7F, 0x00, 0x00}, {
+		{1, {0x80}, {NULL, NULL}},
+		{1, {0x00}, {NULL, NULL}}
+		}
+	}
+   
+	127.0.0.0/32, 127.0.0.1/32
+	{31, {0x7F, 0x00, 0x00, 0x00}, {NULL, NULL}}
+
+	192.168.5.64/26, 192.168.5.15/32, 10.0.0.0/8
+	11000000.10101000.00000101.01-000000 11000000.10101000.00000101.00001111 00001010-00000000.00000000.00000000
+
+	{0, {}, {
+		{7, {00010100}, {NULL, NULL}},
+		{24, {10000001,01010000,00001010}, {
+			{6, {00111100}, {NULL, NULL}},
+			{0, {}, {NULL, NULL}}
+		}}
+	}}
+				
+
+ */
+
+struct ip_tree_leaf {
+	unsigned int prefix_match_len;  /* next prefix_match_len must be equal to next bit in IP address being compared */
+	struct ip_tree_leaf *next[2];	 /* tree goes on in leaf based on first bit following prefix_match, if next[0] && next[1] are null then IP matches - it's subnet address */
+	unsigned char prefix_match[];  /* match_bits div 8 + 1, the same representation as ip address */
+};
+
+struct ip_tree_find {
+	struct ip_tree_leaf *leaf;
+	unsigned int leaf_prefix_match_len;
+	unsigned char *leaf_prefix_match;
+	unsigned char leaf_prefix_match_mask;
+	unsigned char *ip;
+	unsigned int ip_len;
+	unsigned char ip_mask;
+};
+
+#define IP_TREE_FIND_NOT_FOUND 0
+#define IP_TREE_FIND_FOUND 1
+#define IP_TREE_FIND_FOUND_UPPER_SET 2
+
+extern void ip_tree_init(struct ip_tree_leaf **tree);
+extern void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only);
+extern int ip_tree_find_ip(struct ip_tree_leaf *tree, unsigned char *ip, unsigned int ip_len, struct ip_tree_find *h);
+extern int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len);
+extern void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent);
+
+#endif

+ 114 - 0
modules_s/permissions/permissions.c

@@ -46,6 +46,8 @@
 #include "ipmatch.h"
 #include "ipmatch.h"
 #include "im_db.h"
 #include "im_db.h"
 #include "permissions_rpc.h"
 #include "permissions_rpc.h"
+#include "ip_set.h"
+#include "../../resolve.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
 
 
@@ -83,6 +85,7 @@ db_ctx_t	*db_conn = NULL;
 /* fixup function prototypes */
 /* fixup function prototypes */
 static int fixup_files_1(void** param, int param_no);
 static int fixup_files_1(void** param, int param_no);
 static int fixup_files_2(void** param, int param_no);
 static int fixup_files_2(void** param, int param_no);
+static int fixup_ip_is_trusted(void** param, int param_no);
 static int fixup_w_im(void **, int);
 static int fixup_w_im(void **, int);
 static int fixup_w_im_onsend(void **, int);
 static int fixup_w_im_onsend(void **, int);
 
 
@@ -98,6 +101,7 @@ int w_im_2(struct sip_msg *msg, char *str1, char *str2);
 int w_im_1(struct sip_msg *msg, char *str1, char *str2);
 int w_im_1(struct sip_msg *msg, char *str1, char *str2);
 int w_im_onsend(struct sip_msg *msg, char *str1, char *str2);
 int w_im_onsend(struct sip_msg *msg, char *str1, char *str2);
 int w_im_filter(struct sip_msg *msg, char *str1, char *str2);
 int w_im_filter(struct sip_msg *msg, char *str1, char *str2);
+static int w_ip_is_trusted(struct sip_msg *msg, char *str1, char *str2);
 
 
 
 
 /* module interface function prototypes */
 /* module interface function prototypes */
@@ -120,6 +124,8 @@ static cmd_export_t cmds[] = {
 	{"ipmatch",        w_im_2,           2, fixup_w_im,     REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
 	{"ipmatch",        w_im_2,           2, fixup_w_im,     REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
 	{"ipmatch_onsend", w_im_onsend,      1, fixup_w_im_onsend, ONSEND_ROUTE },
 	{"ipmatch_onsend", w_im_onsend,      1, fixup_w_im_onsend, ONSEND_ROUTE },
 	{"ipmatch_filter", w_im_filter,      1, fixup_int_1,     REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | ONSEND_ROUTE},
 	{"ipmatch_filter", w_im_filter,      1, fixup_int_1,     REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | ONSEND_ROUTE},
+	{"ip_is_trusted",  w_ip_is_trusted,  2, fixup_ip_is_trusted, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | ONSEND_ROUTE | BRANCH_ROUTE},
+	       	       
         {0, 0, 0, 0, 0}
         {0, 0, 0, 0, 0}
 };
 };
 
 
@@ -522,3 +528,111 @@ int w_im_filter(struct sip_msg *msg, char *str1, char *str2)
 
 
 	return ipmatch_filter(msg, str1, str2);
 	return ipmatch_filter(msg, str1, str2);
 }
 }
+
+
+struct ip_set_param {
+	str s;
+	unsigned int sz;
+	struct ip_set ip_set;
+	fparam_t *fparam;
+};
+
+#define MODULE_NAME "permissions"
+
+static int w_ip_is_trusted(struct sip_msg* msg, char* _ip_set, char* _ip) {
+	str ip_set_s, ip_s;
+	struct ip_addr *ip, ip_buf;
+	struct ip_set new_ip_set;
+	
+	if (get_str_fparam(&ip_set_s, msg, ((struct ip_set_param*)_ip_set)->fparam) < 0) {
+	    ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip_set parameter value\n");
+	    return -1;
+	}
+	if (get_str_fparam(&ip_s, msg, (fparam_t*)_ip) < 0) {
+	    ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip parameter value\n");
+	    return -1;
+	}
+	if (!ip_s.len || !ip_set_s.len) return -1;
+	switch (ip_s.s[0]) {
+		case 's':	/* src */
+		case 'S':
+			ip = &msg->rcv.src_ip;
+			break;
+		case 'd':	/* dst */
+		case 'D':
+			ip = &msg->rcv.dst_ip;
+			break;
+		case 'r':	/* rcv */
+		case 'R':
+			ip = &msg->rcv.bind_address->address;
+			break;			
+		default:
+			/* string -> ip */
+
+			if ( ((ip = str2ip(&ip_s))==0)
+				#ifdef  USE_IPV6
+			                  && ((ip = str2ip6(&ip_s))==0)
+				#endif
+							                  ){
+				ERR(MODULE_NAME": ip_is_trusted: string to ip conversion error '%.*s'\n", ip_s.len, ip_s.s);
+				return -1;
+			}
+			ip_buf = *ip;
+			ip = &ip_buf;  /* value has been in static buffer */			
+			break;
+	}
+
+	/* test if ip_set string has changed since last call */
+	if (((struct ip_set_param*)_ip_set)->s.len != ip_set_s.len || 
+		memcmp(((struct ip_set_param*)_ip_set)->s.s, ip_set_s.s, ip_set_s.len) != 0) {
+
+		if (ip_set_parse(&new_ip_set, ip_set_s) < 0) {
+			return -1;
+		};
+		if (((struct ip_set_param*)_ip_set)->sz < ip_set_s.len) {
+			void *p;
+			p = pkg_realloc(((struct ip_set_param*)_ip_set)->s.s, ip_set_s.len);
+			if (!p) {
+				ip_set_destroy(&new_ip_set);
+				return E_OUT_OF_MEM;
+			}
+			((struct ip_set_param*)_ip_set)->s.s = p;			
+			((struct ip_set_param*)_ip_set)->sz = ip_set_s.len;			
+		}
+		memcpy(((struct ip_set_param*)_ip_set)->s.s, ip_set_s.s, ip_set_s.len);
+		((struct ip_set_param*)_ip_set)->s.len = ip_set_s.len;
+		ip_set_destroy(&((struct ip_set_param*)_ip_set)->ip_set);
+		((struct ip_set_param*)_ip_set)->ip_set = new_ip_set;
+/* ip_set_print(stderr, &((struct ip_set_param*)_ip_set)->ip_set); */
+	}
+	switch (ip_set_ip_exists(&((struct ip_set_param*)_ip_set)->ip_set, ip)) {
+		case IP_TREE_FIND_FOUND:
+		case IP_TREE_FIND_FOUND_UPPER_SET:
+			return 1;
+		default:
+			return -1;
+	}
+}
+
+static int fixup_ip_is_trusted(void** param, int param_no) {
+	int ret;
+	struct ip_set_param *p;
+	if (param_no == 1) {
+
+		ret = fixup_var_str_12(param, param_no);
+		if (ret < 0) return ret;
+		p = pkg_malloc(sizeof(*p));
+		if (!p) return E_OUT_OF_MEM;
+		memset(p, 0, sizeof(*p));
+		ip_set_init(&p->ip_set);
+		p->fparam = *param;
+		*param = p;
+	}
+	else {
+		return fixup_var_str_12(param, param_no);
+	
+	}
+	return E_OK;
+}
+
+