PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/runtime/server/ip-block-map.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 221 lines | 162 code | 31 blank | 28 comment | 35 complexity | 299005cd00915249b925c4422199df2b MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/server/ip-block-map.h"
  17. #include <arpa/inet.h>
  18. #include "hphp/util/logger.h"
  19. #include "hphp/runtime/base/config.h"
  20. namespace HPHP {
  21. ///////////////////////////////////////////////////////////////////////////////
  22. IpBlockMap::Acl::Acl() : m_networks(true) {}
  23. IpBlockMap::BinaryPrefixTrie::BinaryPrefixTrie(bool allow) {
  24. m_children[0] = m_children[1] = nullptr;
  25. setAllowed(allow);
  26. }
  27. void IpBlockMap::BinaryPrefixTrie::setAllowed(bool allow) {
  28. m_allow = allow;
  29. }
  30. bool IpBlockMap::BinaryPrefixTrie::isAllowed(
  31. const void *search,
  32. const int num_bits) {
  33. return isAllowedImpl(search, num_bits, 0);
  34. }
  35. bool IpBlockMap::BinaryPrefixTrie::isAllowedImpl(
  36. const void *search,
  37. const int num_bits,
  38. const int bit_offset) {
  39. const unsigned char *search_bytes = (const unsigned char *)search;
  40. BinaryPrefixTrie *child;
  41. if (bit_offset == num_bits) {
  42. if (m_children[0] != nullptr || m_children[1] != nullptr) {
  43. // This should never happen because the trie should only ever contain
  44. // prefixes of fixed-size network addresses, so the trie should never be
  45. // any deeper than the network address size.
  46. Logger::Error("trie depth exceeds search depth");
  47. return false;
  48. }
  49. return m_allow;
  50. }
  51. assert(bit_offset < num_bits);
  52. child = m_children[(*search_bytes >> (7 - bit_offset)) & 1];
  53. if (child) {
  54. if (bit_offset < 7) {
  55. return child->isAllowedImpl(search_bytes, num_bits, bit_offset + 1);
  56. } else {
  57. return child->isAllowedImpl(search_bytes + 1, num_bits - 8, 0);
  58. }
  59. }
  60. return m_allow;
  61. }
  62. void IpBlockMap::BinaryPrefixTrie::InsertNewPrefix(
  63. BinaryPrefixTrie *root,
  64. const void *value,
  65. const int num_bits,
  66. bool allow) {
  67. const unsigned char *bytes = (const unsigned char *)value;
  68. BinaryPrefixTrie *node = root;
  69. int curr_bit_num = 0;
  70. int curr_bit_val;
  71. bool next_allow = root->m_allow;
  72. while (curr_bit_num < num_bits) {
  73. // Peel off the bit at the current position
  74. curr_bit_val = (bytes[curr_bit_num / 8] >> (7 - (curr_bit_num & 7))) & 1;
  75. curr_bit_num++;
  76. if (!node->m_children[curr_bit_val]) {
  77. // When inserting the leaf node, stop inheriting the "allow" value
  78. // from ancestor nodes.
  79. if (curr_bit_num == num_bits) {
  80. next_allow = allow;
  81. }
  82. BinaryPrefixTrie *new_node = new BinaryPrefixTrie(next_allow);
  83. node->m_children[curr_bit_val] = new_node;
  84. }
  85. node = node->m_children[curr_bit_val];
  86. next_allow = node->m_allow;
  87. }
  88. }
  89. bool IpBlockMap::ReadIPv6Address(const char *text,
  90. struct in6_addr *output,
  91. int &significant_bits) {
  92. #define STRING_IPV4_ADDR_MAX_LENGTH 15
  93. #define STRING_IPV6_ADDR_MAX_LENGTH 39
  94. char address[STRING_IPV6_ADDR_MAX_LENGTH + 1];
  95. int address_len;
  96. const char *slash;
  97. bool is_ipv6 = (nullptr != strchr(text, ':'));
  98. // Find the bit count, if any.
  99. slash = strchr(text, '/');
  100. if (slash) {
  101. significant_bits = atoi(slash + 1);
  102. if (!significant_bits) {
  103. Logger::Error("invalid bit count: %s", text);
  104. return false;
  105. }
  106. address_len = slash - text;
  107. } else {
  108. significant_bits = is_ipv6 ? 128 : 32;
  109. address_len = strlen(text);
  110. }
  111. if (is_ipv6) {
  112. memcpy(address, text, address_len);
  113. address[address_len] = '\0';
  114. } else {
  115. // An IPv4 mapped address.
  116. if (address_len > STRING_IPV4_ADDR_MAX_LENGTH) {
  117. Logger::Error("invalid IPv4 address: %s", text);
  118. return false;
  119. }
  120. memcpy(address, "::ffff:", 7);
  121. memcpy(address + 7, text, address_len);
  122. address[address_len + 7] = '\0';
  123. significant_bits += 96;
  124. }
  125. if (inet_pton(AF_INET6, address, output) <= 0) {
  126. Logger::Error("invalid IPv6 address: %s", address);
  127. return false;
  128. }
  129. return true;
  130. }
  131. void IpBlockMap::LoadIpList(std::shared_ptr<Acl> acl,
  132. const IniSetting::Map& ini, const Hdf& hdf,
  133. const std::string& name, bool allow) {
  134. for (Hdf child = hdf[name].firstChild(); child.exists();
  135. child = child.next()) {
  136. std::string ip = Config::GetString(ini, child, "", "", false);
  137. int bits;
  138. struct in6_addr address;
  139. if (ReadIPv6Address(ip.c_str(), &address, bits)) {
  140. BinaryPrefixTrie::InsertNewPrefix(&acl->m_networks,
  141. &address,
  142. bits,
  143. allow);
  144. }
  145. }
  146. }
  147. IpBlockMap::IpBlockMap(const IniSetting::Map& ini, const Hdf& config) {
  148. for (Hdf hdf = config.firstChild(); hdf.exists(); hdf = hdf.next()) {
  149. auto acl = std::make_shared<Acl>();
  150. // sgrimm note: not sure AllowFirst is relevant with my implementation
  151. // since we always search for the narrowest matching rule -- it really
  152. // just sets whether we deny or allow by default, I think.
  153. bool allow = Config::GetBool(ini, hdf, "AllowFirst", false);
  154. if (allow) {
  155. acl->m_networks.setAllowed(true);
  156. LoadIpList(acl, ini, hdf, "Ip.Deny", false);
  157. LoadIpList(acl, ini, hdf, "Ip.Allow", true);
  158. } else {
  159. acl->m_networks.setAllowed(false);
  160. LoadIpList(acl, ini, hdf, "Ip.Allow", true);
  161. LoadIpList(acl, ini, hdf, "Ip.Deny", false);
  162. }
  163. std::string location = Config::GetString(ini, hdf, "Location", "", false);
  164. if (!location.empty() && location[0] == '/') {
  165. location = location.substr(1);
  166. }
  167. m_acls[location] = acl;
  168. }
  169. }
  170. bool IpBlockMap::isBlocking(const std::string &command,
  171. const std::string &ip) const {
  172. bool translated = false;
  173. struct in6_addr address;
  174. int bits;
  175. for (auto iter = m_acls.begin(); iter != m_acls.end(); ++iter) {
  176. const std::string &path = iter->first;
  177. if (command.size() >= path.size() &&
  178. strncmp(command.c_str(), path.c_str(), path.size()) == 0) {
  179. if (!translated) {
  180. ReadIPv6Address(ip.c_str(), &address, bits);
  181. assert(bits == 128);
  182. translated = true;
  183. }
  184. return !iter->second->m_networks.isAllowed(&address);
  185. }
  186. }
  187. return false;
  188. }
  189. ///////////////////////////////////////////////////////////////////////////////
  190. }