From 041ce7a1fb0a3518ff08be19ee69f39f75e868d0 Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Sun, 26 Jul 2009 03:12:35 +0200 Subject: [PATCH] Generalized the IP range and mask parsing code. --- autotest/test_ipfilter.tcc | 4 +- src/core/auth.c | 132 +++++++------------------------------ src/core/auth.h | 8 --- src/util/ipcalc.c | 74 +++++++++++++++++++-- src/util/ipcalc.h | 38 +++++++++-- 5 files changed, 129 insertions(+), 127 deletions(-) diff --git a/autotest/test_ipfilter.tcc b/autotest/test_ipfilter.tcc index ad8f069..9f6689f 100644 --- a/autotest/test_ipfilter.tcc +++ b/autotest/test_ipfilter.tcc @@ -9,8 +9,8 @@ static struct ip_addr_encap ip6_a; static struct ip_addr_encap ip6_b; static struct ip_addr_encap ip6_c; static struct ip_addr_encap mask; -static struct ip_ban_record ban6; -static struct ip_ban_record ban4; +static struct ip_range ban6; +static struct ip_range ban4; EXO_TEST(prepare_network, { return net_initialize() == 0; diff --git a/src/core/auth.c b/src/core/auth.c index 3351bda..1f03ed1 100644 --- a/src/core/auth.c +++ b/src/core/auth.c @@ -115,7 +115,7 @@ static int check_cmd_user(const char* cmd, int status, struct linked_list* list, } -static void add_ip_range(struct linked_list* list, struct ip_ban_record* info) +static void add_ip_range(struct linked_list* list, struct ip_range* info) { char buf1[INET6_ADDRSTRLEN+1]; char buf2[INET6_ADDRSTRLEN+1]; @@ -136,119 +136,38 @@ static void add_ip_range(struct linked_list* list, struct ip_ban_record* info) } -static int check_ip_range(const char* lo, const char* hi, struct ip_ban_record* info) -{ - int ret1, ret2; - - if ((ip_is_valid_ipv4(lo) && ip_is_valid_ipv4(hi)) || - (ip_is_valid_ipv6(lo) && ip_is_valid_ipv6(hi))) - { - ret1 = ip_convert_to_binary(lo, &info->lo); - ret2 = ip_convert_to_binary(hi, &info->hi); - if (ret1 == -1 || ret2 == -1 || ret1 != ret2) - { - return -1; - } - return 0; - } - return -1; -} - - -static int check_ip_mask(const char* text_addr, int bits, struct ip_ban_record* info) -{ - LOG_DEBUG("ACL: Deny access for: %s/%d", text_addr, bits); - - if (ip_is_valid_ipv4(text_addr) || - ip_is_valid_ipv6(text_addr)) - { - struct ip_addr_encap addr; - struct ip_addr_encap mask1; - struct ip_addr_encap mask2; - int af = ip_convert_to_binary(text_addr, &addr); /* 192.168.1.2 */ - int maxbits = af == AF_INET6 ? 128 : 32; - ip_mask_create_left(af, bits, &mask1); /* 255.255.255.0 */ - ip_mask_create_right(af, maxbits - bits, &mask2); /* 0.0.0.255 */ - ip_mask_apply_AND(&addr, &mask1, &info->lo); /* 192.168.1.0 */ - ip_mask_apply_OR(&info->lo, &mask2, &info->hi); /* 192.168.1.255 */ - return 0; - } - return -1; -} - - static int check_cmd_addr(const char* cmd, struct linked_list* list, char* line, int line_count) { - char* data1; - char* data2; - struct ip_ban_record* info = 0; - int cidr_bits = 0; - + char* data; + struct ip_range* range = 0; + if (!strncmp(line, cmd, strlen(cmd))) { - data1 = &line[strlen(cmd)]; - data2 = 0; - data1[0] = '\0'; - data1++; - - data1 = strip_white_space(data1); - if (!*data1) + data = &line[strlen(cmd)]; + data[0] = '\0'; + data++; + + data = strip_white_space(data); + if (!*data) { LOG_FATAL("ACL parse error on line %d", line_count); return -1; } - - info = hub_malloc_zero(sizeof(struct ip_ban_record)); - - if (!info) + + range = hub_malloc_zero(sizeof(struct ip_range)); + + if (!range) { LOG_ERROR("ACL parse error. Out of memory!"); return -1; } - - /* Extract IP-range */ - data2 = strrchr(data1, '-'); - if (data2) - { - cidr_bits = -1; - data2[0] = 0; - data2++; - - if (check_ip_range(data1, data2, info) == -1) - { - hub_free(info); - return 0; - } - - add_ip_range(list, info); - return 1; - } - else + if (ip_convert_address_to_range(data, range)) { - /* Extract IP-bitmask */ - data2 = strrchr(data1, '/'); - if (data2) - { - data2[0] = 0; - data2++; - cidr_bits = uhub_atoi(data2); - } - else - { - cidr_bits = 128; - } - - if (check_ip_mask(data1, cidr_bits, info) == -1) - { - hub_free(info); - return 0; - } - - add_ip_range(list, info); - + add_ip_range(list, range); return 1; } + hub_free(range); } return 0; } @@ -480,16 +399,16 @@ int acl_user_unban_cid(struct acl_handle* handle, const char* cid) int acl_is_ip_banned(struct acl_handle* handle, const char* ip_address) { struct ip_addr_encap raw; - struct ip_ban_record* info = (struct ip_ban_record*) list_get_first(handle->networks); + struct ip_range* info = (struct ip_range*) list_get_first(handle->networks); ip_convert_to_binary(ip_address, &raw); while (info) { - if (acl_check_ip_range(&raw, info)) + if (ip_in_range(&raw, info)) { return 1; } - info = (struct ip_ban_record*) list_get_next(handle->networks); + info = (struct ip_range*) list_get_next(handle->networks); } return 0; } @@ -497,26 +416,21 @@ int acl_is_ip_banned(struct acl_handle* handle, const char* ip_address) int acl_is_ip_nat_override(struct acl_handle* handle, const char* ip_address) { struct ip_addr_encap raw; - struct ip_ban_record* info = (struct ip_ban_record*) list_get_first(handle->nat_override); + struct ip_range* info = (struct ip_range*) list_get_first(handle->nat_override); ip_convert_to_binary(ip_address, &raw); while (info) { - if (acl_check_ip_range(&raw, info)) + if (ip_in_range(&raw, info)) { return 1; } - info = (struct ip_ban_record*) list_get_next(handle->nat_override); + info = (struct ip_range*) list_get_next(handle->nat_override); } return 0; } -int acl_check_ip_range(struct ip_addr_encap* addr, struct ip_ban_record* info) -{ - return (addr->af == info->lo.af && ip_compare(&info->lo, addr) <= 0 && ip_compare(addr, &info->hi) <= 0); -} - /* * This will generate the same challenge to the same user, always. * The challenge is made up of the time of the user connected diff --git a/src/core/auth.h b/src/core/auth.h index c40513e..2e40a06 100644 --- a/src/core/auth.h +++ b/src/core/auth.h @@ -45,12 +45,6 @@ struct hub_user_access_info enum user_credentials status; }; -struct ip_ban_record -{ - struct ip_addr_encap lo; - struct ip_addr_encap hi; -}; - struct acl_handle { struct linked_list* users; /* Known users. See enum user_status */ @@ -78,8 +72,6 @@ extern int acl_user_ban_cid(struct acl_handle* handle, const char* cid); extern int acl_user_unban_nick(struct acl_handle* handle, const char* nick); extern int acl_user_unban_cid(struct acl_handle* handle, const char* cid); -extern int acl_check_ip_range(struct ip_addr_encap* addr, struct ip_ban_record* info); - extern const char* acl_password_generate_challenge(struct acl_handle* acl, struct hub_user* user); /** diff --git a/src/util/ipcalc.c b/src/util/ipcalc.c index 8279d1b..03bb308 100644 --- a/src/util/ipcalc.c +++ b/src/util/ipcalc.c @@ -19,7 +19,6 @@ #include "uhub.h" - int ip_is_valid_ipv4(const char* address) { int i = 0; /* address index */ @@ -413,13 +412,80 @@ int ip_compare(struct ip_addr_encap* a, struct ip_addr_encap* b) return ret; } +static int check_ip_mask(const char* text_addr, int bits, struct ip_range* range) +{ + if (ip_is_valid_ipv4(text_addr) || ip_is_valid_ipv6(text_addr)) + { + struct ip_addr_encap addr; + struct ip_addr_encap mask1; + struct ip_addr_encap mask2; + int af = ip_convert_to_binary(text_addr, &addr); /* 192.168.1.2 */ + int maxbits = (af == AF_INET6 ? 128 : 32); + bits = MIN(MAX(bits, 0), maxbits); + ip_mask_create_left(af, bits, &mask1); /* 255.255.255.0 */ + ip_mask_create_right(af, maxbits - bits, &mask2); /* 0.0.0.255 */ + ip_mask_apply_AND(&addr, &mask1, &range->lo); /* 192.168.1.0 */ + ip_mask_apply_OR(&range->lo, &mask2, &range->hi); /* 192.168.1.255 */ + return 1; + } + return 0; +} +static int check_ip_range(const char* lo, const char* hi, struct ip_range* range) +{ + int ret1, ret2; + if ((ip_is_valid_ipv4(lo) && ip_is_valid_ipv4(hi)) || (ip_is_valid_ipv6(lo) && ip_is_valid_ipv6(hi))) + { + ret1 = ip_convert_to_binary(lo, &range->lo); + ret2 = ip_convert_to_binary(hi, &range->hi); + if (ret1 == -1 || ret2 == -1 || ret1 != ret2) + { + return 0; + } + return 1; + } + return 0; +} +int ip_convert_address_to_range(const char* address, struct ip_range* range) +{ + int ret = 0; + char* addr = 0; + if (!address || !range) + return 0; + const char* split = strrchr(address, '/'); + if (split) + { + int mask = uhub_atoi(split+1); + if (mask == 0 && split[1] != '0') return 0; + addr = hub_strndup(address, split - address); + ret = check_ip_mask(addr, mask, range); + hub_free(addr); + return ret; + } + split = strrchr(address, '-'); + if (split) + { + addr = hub_strndup(address, split - address); + ret = check_ip_range(addr, split+1, range); + hub_free(addr); + return ret; + } + if (ip_is_valid_ipv4(address) || ip_is_valid_ipv6(address)) + { + if (ip_convert_to_binary(address, &range->lo) == -1) + return 0; + memcpy(&range->hi, &range->lo, sizeof(struct ip_addr_encap)); + return 1; + } + return 0; +} - - - +int ip_in_range(struct ip_addr_encap* addr, struct ip_range* range) +{ + return (addr->af == range->lo.af && ip_compare(&range->lo, addr) <= 0 && ip_compare(addr, &range->hi) <= 0); +} diff --git a/src/util/ipcalc.h b/src/util/ipcalc.h index 3011b9f..e6a10e2 100644 --- a/src/util/ipcalc.h +++ b/src/util/ipcalc.h @@ -33,26 +33,57 @@ struct ip_addr_encap { } internal_ip_data; }; +struct ip_range +{ + struct ip_addr_encap lo; + struct ip_addr_encap hi; +}; extern int ip_convert_to_binary(const char* text_addr, struct ip_addr_encap* raw); extern const char* ip_convert_to_string(struct ip_addr_encap* raw); +/** + * Convert a string on the form: + * ip-ip or ip/mask to an iprange. + * + * Note: both IPv4 and IPv6 addresses are valid, but if a range is given + * both addresses must be of the same address family. + * + * Valid examples of address + * IPv4: + * 192.168.2.1 + * 192.168.0.0/16 + * 192.168.0.0-192.168.255.255 + * + * IPv6: + * 2001:4860:A005::68 + * 2001:4860:A005::0/80 + * 2001:4860:A005::0-2001:4860:A005:ffff:ffff:ffff:ffff:ffff + * + * @return 0 if invalid, 1 if OK + */ +extern int ip_convert_address_to_range(const char* address, struct ip_range* range); -/* +/** + * @return 1 if addr is inside range, 0 otherwise + */ +extern int ip_in_range(struct ip_addr_encap* addr, struct ip_range* range); + +/** * @return 1 if address is a valid IPv4 address in text notation * 0 if invalid */ extern int ip_is_valid_ipv4(const char* address); -/* +/** * @return 1 if address is a valid IPv6 address in text notation * 0 if invalid */ extern int ip_is_valid_ipv6(const char* address); -/* +/** * This function converts an IP address in text_address to a binary * struct sockaddr. * This will auto-detect if the IP-address is IPv6 (and that is supported), @@ -66,7 +97,6 @@ extern int ip_is_valid_ipv6(const char* address); */ extern int ip_convert_address(const char* text_address, int port, struct sockaddr* addr, socklen_t* addr_len); - extern int ip_mask_create_left(int af, int bits, struct ip_addr_encap* result); extern int ip_mask_create_right(int af, int bits, struct ip_addr_encap* result);