c4e406b62f
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
211 linhas
6.6 KiB
C++
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;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|