Arquivos
hhvm/hphp/runtime/server/ip_block_map.cpp
T
Edwin Smith c4e406b62f Move runtime/base/server to runtime/server
This is an incremental step towards moving it all the way
to hphp/server.  This flattens base but doesn't untangle
the server files from lib_hphp_runtime
2013-07-15 18:13:25 -07:00

211 linhas
6.6 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/server/ip_block_map.h"
#include "hphp/util/logger.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
IpBlockMap::Acl::Acl() : m_networks(true) {}
IpBlockMap::BinaryPrefixTrie::BinaryPrefixTrie(bool allow) {
m_children[0] = m_children[1] = nullptr;
setAllowed(allow);
}
void IpBlockMap::BinaryPrefixTrie::setAllowed(bool allow) {
m_allow = allow;
}
bool IpBlockMap::BinaryPrefixTrie::isAllowed(
const void *search,
const int num_bits) {
return isAllowedImpl(search, num_bits, 0);
}
bool IpBlockMap::BinaryPrefixTrie::isAllowedImpl(
const void *search,
const int num_bits,
const int bit_offset) {
const unsigned char *search_bytes = (const unsigned char *)search;
BinaryPrefixTrie *child;
if (bit_offset > num_bits) {
// This should never happen because the trie should only ever contain
// prefixes of fixed-size network addresses, so the trie should never be
// any deeper than the network address size.
Logger::Error("trie depth exceeds search depth");
return false;
}
child = m_children[(*search_bytes >> (7 - bit_offset)) & 1];
if (child) {
if (bit_offset < 7) {
return child->isAllowedImpl(search_bytes, num_bits, bit_offset + 1);
} else {
return child->isAllowedImpl(search_bytes + 1, num_bits - 8, 0);
}
}
return m_allow;
}
void IpBlockMap::BinaryPrefixTrie::InsertNewPrefix(
BinaryPrefixTrie *root,
const void *value,
const int num_bits,
bool allow) {
const unsigned char *bytes = (const unsigned char *)value;
BinaryPrefixTrie *node = root;
int curr_bit_num = 0;
int curr_bit_val;
bool next_allow = root->m_allow;
while (curr_bit_num < num_bits) {
// Peel off the bit at the current position
curr_bit_val = (bytes[curr_bit_num / 8] >> (7 - (curr_bit_num & 7))) & 1;
curr_bit_num++;
if (!node->m_children[curr_bit_val]) {
// When inserting the leaf node, stop inheriting the "allow" value
// from ancestor nodes.
if (curr_bit_num == num_bits) {
next_allow = allow;
}
BinaryPrefixTrie *new_node = new BinaryPrefixTrie(next_allow);
node->m_children[curr_bit_val] = new_node;
}
node = node->m_children[curr_bit_val];
next_allow = node->m_allow;
}
}
bool IpBlockMap::ReadIPv6Address(const char *text,
struct in6_addr *output,
int &significant_bits) {
#define STRING_IPV4_ADDR_MAX_LENGTH 15
#define STRING_IPV6_ADDR_MAX_LENGTH 39
char address[STRING_IPV6_ADDR_MAX_LENGTH + 1];
int address_len;
const char *slash;
bool is_ipv6 = (nullptr != strchr(text, ':'));
// Find the bit count, if any.
slash = strchr(text, '/');
if (slash) {
significant_bits = atoi(slash + 1);
if (!significant_bits) {
Logger::Error("invalid bit count: %s", text);
return false;
}
address_len = slash - text;
} else {
significant_bits = is_ipv6 ? 128 : 32;
address_len = strlen(text);
}
if (is_ipv6) {
memcpy(address, text, address_len);
address[address_len] = '\0';
} else {
// An IPv4 mapped address.
if (address_len > STRING_IPV4_ADDR_MAX_LENGTH) {
Logger::Error("invalid IPv4 address: %s", text);
return false;
}
memcpy(address, "::ffff:", 7);
memcpy(address + 7, text, address_len);
address[address_len + 7] = '\0';
significant_bits += 96;
}
if (inet_pton(AF_INET6, address, output) <= 0) {
Logger::Error("invalid IPv6 address: %s", address);
return false;
}
return true;
}
void IpBlockMap::LoadIpList(AclPtr acl, Hdf hdf, bool allow) {
for (Hdf child = hdf.firstChild(); child.exists(); child = child.next()) {
string ip = child.getString();
int bits;
struct in6_addr address;
if (ReadIPv6Address(ip.c_str(), &address, bits)) {
BinaryPrefixTrie::InsertNewPrefix(&acl->m_networks,
&address,
bits,
allow);
}
}
}
IpBlockMap::IpBlockMap(Hdf config) {
for (Hdf hdf = config.firstChild(); hdf.exists(); hdf = hdf.next()) {
AclPtr acl(new Acl());
// sgrimm note: not sure AllowFirst is relevant with my implementation
// since we always search for the narrowest matching rule -- it really
// just sets whether we deny or allow by default, I think.
bool allow = hdf["AllowFirst"].getBool(false);
if (allow) {
acl->m_networks.setAllowed(true);
LoadIpList(acl, hdf["Ip.Deny"], false);
LoadIpList(acl, hdf["Ip.Allow"], true);
} else {
acl->m_networks.setAllowed(false);
LoadIpList(acl, hdf["Ip.Allow"], true);
LoadIpList(acl, hdf["Ip.Deny"], false);
}
string location = hdf["Location"].getString();
if (!location.empty() && location[0] == '/') {
location = location.substr(1);
}
m_acls[location] = acl;
}
}
bool IpBlockMap::isBlocking(const std::string &command,
const std::string &ip) const {
bool translated = false;
struct in6_addr address;
int bits;
for (StringToAclPtrMap::const_iterator iter = m_acls.begin();
iter != m_acls.end(); ++iter) {
const string &path = iter->first;
if (command.size() >= path.size() &&
strncmp(command.c_str(), path.c_str(), path.size()) == 0) {
if (!translated) {
ReadIPv6Address(ip.c_str(), &address, bits);
assert(bits == 128);
translated = true;
}
return !iter->second->m_networks.isAllowed(&address);
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
}