Merge commit 'refs/pull/1205/head' of github.com:znc/znc

This commit is contained in:
Alexey Sokolov
2015-12-14 18:03:25 +00:00
3 changed files with 218 additions and 1 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ BIN_SRCS := src/main.cpp
LIB_OBJS := $(patsubst %cpp,%o,$(LIB_SRCS))
BIN_OBJS := $(patsubst %cpp,%o,$(BIN_SRCS))
TESTS := StringTest ConfigTest UtilsTest ThreadTest NickTest ClientTest NetworkTest \
MessageTest ModulesTest IRCSockTest QueryTest BufferTest
MessageTest ModulesTest IRCSockTest QueryTest BufferTest UserTest
TESTS := $(addprefix test/,$(addsuffix .o,$(TESTS)))
CLEAN := znc src/*.o test/*.o core core.* .version_extra .depend modules/.depend \
unittest $(LIBZNC)
+98
View File
@@ -852,6 +852,104 @@ bool CUser::IsHostAllowed(const CString& sHostMask) const {
for (const CString& sHost : m_ssAllowedHosts) {
if (sHostMask.WildCmp(sHost)) {
return true;
} else {
// CIDR notation checker, e.g. "192.0.2.0/24" or "2001:db8::/32"
// Try to split the string into an IP and routing prefix
VCString vsSplitCIDR;
if (sHost.Split("/", vsSplitCIDR, false) != 2) continue;
const CString sRoutingPrefix = vsSplitCIDR.back();
const int iRoutingPrefix = sRoutingPrefix.ToInt();
if (iRoutingPrefix < 0 || iRoutingPrefix > 128) continue;
// If iRoutingPrefix is 0, it could be due to ToInt() failing, so
// sRoutingPrefix needs to be all zeroes
if (iRoutingPrefix == 0 && sRoutingPrefix != "0") continue;
// Convert each IP from a numeric string to an addrinfo
addrinfo aiHints;
memset(&aiHints, 0, sizeof(addrinfo));
aiHints.ai_flags = AI_NUMERICHOST;
addrinfo* aiHost;
int iIsHostValid =
getaddrinfo(sHostMask.c_str(), nullptr, &aiHints, &aiHost);
if (iIsHostValid != 0) continue;
aiHints.ai_family = aiHost->ai_family; // Host and range must be in
// the same address family
addrinfo* aiRange;
int iIsRangeValid = getaddrinfo(vsSplitCIDR.front().c_str(),
nullptr, &aiHints, &aiRange);
if (iIsRangeValid != 0) {
freeaddrinfo(aiHost);
continue;
}
// "/0" allows all IPv[4|6] addresses
if (iRoutingPrefix == 0) {
freeaddrinfo(aiHost);
freeaddrinfo(aiRange);
return true;
}
// If both IPs are valid and of the same type, make a bit field mask
// from the routing prefix, AND it to the host and range, and see if
// they match
bool bIsHostInRange = false;
if (aiHost->ai_family == AF_INET) {
if (iRoutingPrefix > 32) {
freeaddrinfo(aiHost);
freeaddrinfo(aiRange);
continue;
}
const sockaddr_in* saHost = (sockaddr_in*)(aiHost->ai_addr);
const sockaddr_in* saRange = (sockaddr_in*)(aiRange->ai_addr);
// Make IPv4 bitmask
const in_addr_t inBitmask =
htonl((~0u) << (32 - iRoutingPrefix));
// Compare masked IPv4s
bIsHostInRange = ((inBitmask & saHost->sin_addr.s_addr) ==
(inBitmask & saRange->sin_addr.s_addr));
} else if (aiHost->ai_family == AF_INET6) {
// Make IPv6 bitmask
in6_addr in6aBitmask;
memset(&in6aBitmask, 0, sizeof(in6aBitmask));
for (int i = 0, iBitsLeft = iRoutingPrefix; iBitsLeft > 0;
++i, iBitsLeft -= 8) {
if (iBitsLeft >= 8) {
in6aBitmask.s6_addr[i] = (uint8_t)(~0u);
} else {
in6aBitmask.s6_addr[i] = (uint8_t)(~0u)
<< (8 - iBitsLeft);
}
}
// Compare masked IPv6s
bIsHostInRange = true;
const sockaddr_in6* sa6Host = (sockaddr_in6*)(aiHost->ai_addr);
const sockaddr_in6* sa6Range =
(sockaddr_in6*)(aiRange->ai_addr);
for (int i = 0; i < 16; ++i) {
if ((in6aBitmask.s6_addr[i] &
sa6Host->sin6_addr.s6_addr[i]) !=
(in6aBitmask.s6_addr[i] &
sa6Range->sin6_addr.s6_addr[i])) {
bIsHostInRange = false;
}
}
}
freeaddrinfo(aiHost);
freeaddrinfo(aiRange);
if (bIsHostInRange) return true;
}
}
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <znc/User.h>
#include <znc/znc.h>
class UserTest : public ::testing::Test {
protected:
// A CZNC instance is required to instantiate CUsers
UserTest() { CZNC::CreateInstance(); }
~UserTest() { CZNC::DestroyInstance(); }
};
TEST_F(UserTest, IsHostAllowed) {
struct hostTest {
CString sTestHost;
CString sIP;
bool bExpectedResult;
};
hostTest aHostTests[] = {
{"127.0.0.1", "127.0.0.1", true},
{"127.0.0.20", "127.0.0.1", false},
{"127.0.0.20/24", "127.0.0.1", true},
{"127.0.0.20/0", "127.0.0.1", true},
{"127.0.0.20/32", "127.0.0.1", false},
{"127.0.0.1", "127.0.0.0", false},
{"127.0.0.20", "127.0.0.0", false},
{"127.0.0.20/24", "127.0.0.0", true},
{"127.0.0.20/0", "127.0.0.0", true},
{"127.0.0.20/32", "127.0.0.0", false},
{"127.0.0.1", "127.0.0.255", false},
{"127.0.0.20", "127.0.0.255", false},
{"127.0.0.20/24", "127.0.0.255", true},
{"127.0.0.20/0", "127.0.0.255", true},
{"127.0.0.20/32", "127.0.0.255", false},
{"127.0.0.1", "127.0.1.1", false},
{"127.0.0.20", "127.0.1.1", false},
{"127.0.0.20/24", "127.0.1.1", false},
{"127.0.0.20/16", "127.0.1.1", true},
{"127.0.0.20/0", "127.0.1.1", true},
{"127.0.0.20/32", "127.0.1.1", false},
{"127.0.0.1", "0.0.0.0", false},
{"127.0.0.20", "0.0.0.0", false},
{"127.0.0.20/24", "0.0.0.0", false},
{"127.0.0.20/16", "0.0.0.0", false},
{"127.0.0.20/0", "0.0.0.0", true},
{"127.0.0.20/32", "0.0.0.0", false},
{"127.0.0.1", "255.255.255.255", false},
{"127.0.0.20", "255.255.255.255", false},
{"127.0.0.20/24", "255.255.255.255", false},
{"127.0.0.20/16", "255.255.255.255", false},
{"127.0.0.20/0", "255.255.255.255", true},
{"127.0.0.20/32", "255.255.255.255", false},
{"127.0.0.1", "::1", false},
{"::1", "::1", true},
{"::20/120", "::1", true},
{"::20/120", "::ffff", false},
{"::ff/0", "::1", true},
{"::ff/0", "127.0.0.1", false},
{"127.0.0.20/0", "::1", false},
{"127.0.0.1/-1", "127.0.0.1", false},
{"::0/-1", "::0", false},
{"127.0.0.a/0", "127.0.0.1", false},
{"::g/0", "::0", false},
{"127.0.0.1/0", "127.0.0.a", false},
{"::0/0", "::g", false},
{"::0/0/0", "::0", false},
{"2001:db8::/33", "2001:db9:0::", false},
{"2001:db8::/32", "2001:db8:8000::", true},
{"2001:db8::/33", "2001:db8:8000::", false},
{"0.0.0.0/0", "::1", false},
{"0.0.0.0/0", "127.0.0.1", true},
{"::0/0", "127.0.0.1", false},
{"::0/0", "::1", true},
{"0.0.0.0", "127.0.0.1", false},
{"0.0.0.0", "::1", false},
{"::0", "::1", false},
{"::0", "127.0.0.1", false},
{"127.0.0.2/abc", "127.0.0.1", false},
{"::2/abc", "::1", false},
{"127.0.0.1/33", "127.0.0.1", false},
{"::1/129", "::1", false},
{"::2/00000000000", "::1", false},
{"::2/0a", "::1", false},
};
for (const hostTest& h : aHostTests) {
CUser user("user");
user.AddAllowedHost(h.sTestHost);
EXPECT_EQ(h.bExpectedResult, user.IsHostAllowed(h.sIP))
<< "Allow-host is " << h.sTestHost;
}
}