PageRenderTime 124ms CodeModel.GetById 81ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/core/IP.test.php

https://github.com/quarkness/piwik
PHP | 677 lines | 541 code | 87 blank | 49 comment | 6 complexity | 839aeda781b681b653e6581c1cf18cef MD5 | raw file
  1. <?php
  2. if(!defined('PIWIK_CONFIG_TEST_INCLUDED'))
  3. {
  4. require_once dirname(__FILE__)."/../../tests/config_test.php";
  5. }
  6. require_once 'IP.php';
  7. class Test_Piwik_IP extends UnitTestCase
  8. {
  9. function __construct( $title = '')
  10. {
  11. parent::__construct( $title );
  12. }
  13. public function setUp()
  14. {
  15. parent::setUp();
  16. $_GET = $_POST = array();
  17. }
  18. public function tearDown()
  19. {
  20. parent::tearDown();
  21. }
  22. function test_sanitizeIp()
  23. {
  24. $tests = array(
  25. // single IPv4 address
  26. '127.0.0.1' => '127.0.0.1',
  27. // single IPv6 address (ambiguous)
  28. '::1' => '::1',
  29. '::ffff:127.0.0.1' => '::ffff:127.0.0.1',
  30. '2001:5c0:1000:b::90f8' => '2001:5c0:1000:b::90f8',
  31. // single IPv6 address
  32. '[::1]' => '::1',
  33. '[2001:5c0:1000:b::90f8]' => '2001:5c0:1000:b::90f8',
  34. '[::ffff:127.0.0.1]' => '::ffff:127.0.0.1',
  35. // single IPv4 address (CIDR notation)
  36. '192.168.1.1/32' => '192.168.1.1',
  37. // single IPv6 address (CIDR notation)
  38. '::1/128' => '::1',
  39. '::ffff:127.0.0.1/128' => '::ffff:127.0.0.1',
  40. '2001:5c0:1000:b::90f8/128' => '2001:5c0:1000:b::90f8',
  41. // IPv4 address with port
  42. '192.168.1.2:80' => '192.168.1.2',
  43. // IPv6 address with port
  44. '[::1]:80' => '::1',
  45. '[::ffff:127.0.0.1]:80' => '::ffff:127.0.0.1',
  46. '[2001:5c0:1000:b::90f8]:80' => '2001:5c0:1000:b::90f8',
  47. // hostnames with port?
  48. 'localhost' => 'localhost',
  49. 'localhost:80' => 'localhost',
  50. 'www.example.com' => 'www.example.com',
  51. 'example.com:80' => 'example.com',
  52. );
  53. foreach($tests as $ip => $expected)
  54. {
  55. $this->assertEqual( Piwik_IP::sanitizeIp($ip), $expected, "$ip" );
  56. }
  57. }
  58. function test_sanitizeIpRange()
  59. {
  60. $tests = array(
  61. '' => false,
  62. ' 127.0.0.1 ' => '127.0.0.1/32',
  63. '192.168.1.0' => '192.168.1.0/32',
  64. '192.168.1.1/24' => '192.168.1.1/24',
  65. '192.168.1.2/16' => '192.168.1.2/16',
  66. '192.168.1.3/8' => '192.168.1.3/8',
  67. '192.168.2.*' => '192.168.2.0/24',
  68. '192.169.*.*' => '192.169.0.0/16',
  69. '193.*.*.*' => '193.0.0.0/8',
  70. '*.*.*.*' => '0.0.0.0/0',
  71. '*.*.*.1' => false,
  72. '*.*.1.1' => false,
  73. '*.1.1.1' => false,
  74. '1.*.1.1' => false,
  75. '1.1.*.1' => false,
  76. '1.*.*.1' => false,
  77. '::1' => '::1/128',
  78. '::ffff:127.0.0.1' => '::ffff:127.0.0.1/128',
  79. '2001:5c0:1000:b::90f8' => '2001:5c0:1000:b::90f8/128',
  80. '::1/64' => '::1/64',
  81. '::ffff:127.0.0.1/64' => '::ffff:127.0.0.1/64',
  82. '2001:5c0:1000:b::90f8/64' => '2001:5c0:1000:b::90f8/64',
  83. );
  84. foreach($tests as $ip => $expected)
  85. {
  86. $this->assertEqual( Piwik_IP::sanitizeIpRange($ip), $expected, "$ip" );
  87. }
  88. }
  89. private function getP2NTestData()
  90. {
  91. return array(
  92. // IPv4
  93. '0.0.0.0' => "\x00\x00\x00\x00",
  94. '127.0.0.1' => "\x7F\x00\x00\x01",
  95. '192.168.1.12' => "\xc0\xa8\x01\x0c",
  96. '255.255.255.255' => "\xff\xff\xff\xff",
  97. // IPv6
  98. '::' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  99. '::1' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
  100. '::fffe:7f00:1' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x7f\x00\x00\x01",
  101. '::ffff:127.0.0.1' => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\x01",
  102. '2001:5c0:1000:b::90f8' => "\x20\x01\x05\xc0\x10\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x90\xf8",
  103. );
  104. }
  105. function test_P2N()
  106. {
  107. foreach($this->getP2NTestData() as $P => $N)
  108. {
  109. $this->assertEqual( Piwik_IP::P2N($P), $N, "$P" );
  110. }
  111. }
  112. function test_P2N_invalidInput()
  113. {
  114. $tests = array(
  115. // not a series of dotted numbers
  116. null,
  117. '',
  118. 'alpha',
  119. '...',
  120. // missing an octet
  121. '.0.0.0',
  122. '0..0.0',
  123. '0.0..0',
  124. '0.0.0.',
  125. // octets must be 0-255
  126. '-1.0.0.0',
  127. '1.1.1.256',
  128. // leading zeros not supported (i.e., can be ambiguous, e.g., octal)
  129. '07.07.07.07',
  130. );
  131. foreach($tests as $P)
  132. {
  133. $this->assertEqual( Piwik_IP::P2N($P), "\x00\x00\x00\x00", "$P" );
  134. }
  135. }
  136. private function getN2PTestData()
  137. {
  138. // a valid network address is either 4 or 16 bytes; those lines are intentionally left blank ;)
  139. return array(
  140. null,
  141. '',
  142. "\x01",
  143. "\x01\x00",
  144. "\x01\x00\x00",
  145. "\x01\x00\x00\x00\x00",
  146. "\x01\x00\x00\x00\x00\x00",
  147. "\x01\x00\x00\x00\x00\x00\x00",
  148. "\x01\x00\x00\x00\x00\x00\x00\x00",
  149. "\x01\x00\x00\x00\x00\x00\x00\x00\x00",
  150. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  151. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  152. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  153. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  154. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  155. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  156. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
  157. );
  158. }
  159. function test_N2P()
  160. {
  161. foreach($this->getP2NTestData() as $P => $N)
  162. {
  163. $this->assertEqual( Piwik_IP::N2P($N), $P, "$P vs" . Piwik_IP::N2P($N) );
  164. }
  165. }
  166. function test_N2P_invalidInput()
  167. {
  168. foreach($this->getN2PTestData() as $N)
  169. {
  170. $this->assertEqual( Piwik_IP::N2P($N), "0.0.0.0", bin2hex($N) );
  171. }
  172. }
  173. function test_prettyPrint()
  174. {
  175. foreach($this->getP2NTestData() as $P => $N)
  176. {
  177. $this->assertEqual( Piwik_IP::prettyPrint($N), $P, "$P vs" . Piwik_IP::N2P($N) );
  178. }
  179. }
  180. function test_prettyPrint_invalidInput()
  181. {
  182. foreach($this->getN2PTestData() as $N)
  183. {
  184. $this->assertEqual( Piwik_IP::prettyPrint($N), "0.0.0.0", bin2hex($N) );
  185. }
  186. }
  187. function test_long2ip()
  188. {
  189. // a valid network address is either 4 or 16 bytes; those lines are intentionally left blank ;)
  190. $tests = array(
  191. // invalid
  192. null => '0.0.0.0',
  193. "" => '0.0.0.0',
  194. // IPv4
  195. "\x7f\x00\x00\x01" => '127.0.0.1',
  196. // IPv4-compatible (this transitional format is deprecated in RFC 4291, section 2.5.5.1)
  197. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8\x01\x01" => '192.168.1.1',
  198. // IPv4-mapped (RFC 4291, 2.5.5.2)
  199. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xc0\xa8\x01\x02" => '192.168.1.2',
  200. // other IPv6 address
  201. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\xc0\xa8\x01\x03" => '0.0.0.0',
  202. "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\xa8\x01\x04" => '0.0.0.0',
  203. "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8\x01\x05" => '0.0.0.0',
  204. /*
  205. * We assume all stored IP addresses (pre-Piwik 1.4) were converted from UNSIGNED INT to VARBINARY.
  206. * The following is just for informational purposes.
  207. */
  208. // 192.168.1.0
  209. '-1062731520' => '0.0.0.0',
  210. '3232235776' => '0.0.0.0',
  211. // 10.10.10.10
  212. '168430090' => '0.0.0.0',
  213. // 0.0.39.15 - this is the ambiguous case (i.e., 4 char string)
  214. '9999' => '57.57.57.57',
  215. "\x39\x39\x39\x39" => '57.57.57.57',
  216. // 0.0.3.231
  217. '999' => '0.0.0.0',
  218. "\x39\x39\x39" => '0.0.0.0',
  219. );
  220. foreach($tests as $N => $P)
  221. {
  222. $this->assertEqual( Piwik_IP::long2ip($N), $P, bin2hex($N) );
  223. // this is our compatibility function
  224. $this->assertEqual( Piwik_Common::long2ip($N), $P, bin2hex($N) );
  225. }
  226. }
  227. function test_getIpsForRange()
  228. {
  229. $tests = array(
  230. // invalid ranges
  231. null => false,
  232. '' => false,
  233. '0' => false,
  234. // single IPv4
  235. '127.0.0.1' => array( "\x7f\x00\x00\x01", "\x7f\x00\x00\x01" ),
  236. // IPv4 with wildcards
  237. '192.168.1.*' => array( "\xc0\xa8\x01\x00", "\xc0\xa8\x01\xff" ),
  238. '192.168.*.*' => array( "\xc0\xa8\x00\x00", "\xc0\xa8\xff\xff" ),
  239. '192.*.*.*' => array( "\xc0\x00\x00\x00", "\xc0\xff\xff\xff" ),
  240. '*.*.*.*' => array( "\x00\x00\x00\x00", "\xff\xff\xff\xff" ),
  241. // single IPv4 in expected CIDR notation
  242. '192.168.1.1/24' => array( "\xc0\xa8\x01\x00", "\xc0\xa8\x01\xff" ),
  243. '192.168.1.127/32' => array( "\xc0\xa8\x01\x7f", "\xc0\xa8\x01\x7f" ),
  244. '192.168.1.127/31' => array( "\xc0\xa8\x01\x7e", "\xc0\xa8\x01\x7f" ),
  245. '192.168.1.127/30' => array( "\xc0\xa8\x01\x7c", "\xc0\xa8\x01\x7f" ),
  246. '192.168.1.127/29' => array( "\xc0\xa8\x01\x78", "\xc0\xa8\x01\x7f" ),
  247. '192.168.1.127/28' => array( "\xc0\xa8\x01\x70", "\xc0\xa8\x01\x7f" ),
  248. '192.168.1.127/27' => array( "\xc0\xa8\x01\x60", "\xc0\xa8\x01\x7f" ),
  249. '192.168.1.127/26' => array( "\xc0\xa8\x01\x40", "\xc0\xa8\x01\x7f" ),
  250. '192.168.1.127/25' => array( "\xc0\xa8\x01\x00", "\xc0\xa8\x01\x7f" ),
  251. '192.168.1.255/32' => array( "\xc0\xa8\x01\xff", "\xc0\xa8\x01\xff" ),
  252. '192.168.1.255/31' => array( "\xc0\xa8\x01\xfe", "\xc0\xa8\x01\xff" ),
  253. '192.168.1.255/30' => array( "\xc0\xa8\x01\xfc", "\xc0\xa8\x01\xff" ),
  254. '192.168.1.255/29' => array( "\xc0\xa8\x01\xf8", "\xc0\xa8\x01\xff" ),
  255. '192.168.1.255/28' => array( "\xc0\xa8\x01\xf0", "\xc0\xa8\x01\xff" ),
  256. '192.168.1.255/27' => array( "\xc0\xa8\x01\xe0", "\xc0\xa8\x01\xff" ),
  257. '192.168.1.255/26' => array( "\xc0\xa8\x01\xc0", "\xc0\xa8\x01\xff" ),
  258. '192.168.1.255/25' => array( "\xc0\xa8\x01\x80", "\xc0\xa8\x01\xff" ),
  259. '192.168.255.255/24' => array( "\xc0\xa8\xff\x00", "\xc0\xa8\xff\xff" ),
  260. '192.168.255.255/23' => array( "\xc0\xa8\xfe\x00", "\xc0\xa8\xff\xff" ),
  261. '192.168.255.255/22' => array( "\xc0\xa8\xfc\x00", "\xc0\xa8\xff\xff" ),
  262. '192.168.255.255/21' => array( "\xc0\xa8\xf8\x00", "\xc0\xa8\xff\xff" ),
  263. '192.168.255.255/20' => array( "\xc0\xa8\xf0\x00", "\xc0\xa8\xff\xff" ),
  264. '192.168.255.255/19' => array( "\xc0\xa8\xe0\x00", "\xc0\xa8\xff\xff" ),
  265. '192.168.255.255/18' => array( "\xc0\xa8\xc0\x00", "\xc0\xa8\xff\xff" ),
  266. '192.168.255.255/17' => array( "\xc0\xa8\x80\x00", "\xc0\xa8\xff\xff" ),
  267. // single IPv6
  268. '::1' => array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" ),
  269. // single IPv6 in expected CIDR notation
  270. '::1/128' => array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" ),
  271. '::1/127' => array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" ),
  272. '::fffe:7f00:1/120' => array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x7f\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x7f\x00\x00\xff" ),
  273. '::ffff:127.0.0.1/120' => array( "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x7f\x00\x00\xff" ),
  274. '2001:ca11:911::b0b:15:dead/128' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad" ),
  275. '2001:ca11:911::b0b:15:dead/127' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xac", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xad" ),
  276. '2001:ca11:911::b0b:15:dead/126' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xac", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf" ),
  277. '2001:ca11:911::b0b:15:dead/125' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa8", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf" ),
  278. '2001:ca11:911::b0b:15:dead/124' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa0", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xaf" ),
  279. '2001:ca11:911::b0b:15:dead/123' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xa0", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xbf" ),
  280. '2001:ca11:911::b0b:15:dead/122' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x80", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xbf" ),
  281. '2001:ca11:911::b0b:15:dead/121' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x80", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xff" ),
  282. '2001:ca11:911::b0b:15:dead/120' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\xff" ),
  283. '2001:ca11:911::b0b:15:dead/119' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xde\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" ),
  284. '2001:ca11:911::b0b:15:dead/118' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdc\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" ),
  285. '2001:ca11:911::b0b:15:dead/117' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xd8\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" ),
  286. '2001:ca11:911::b0b:15:dead/116' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xd0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" ),
  287. '2001:ca11:911::b0b:15:dead/115' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xc0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xdf\xff" ),
  288. '2001:ca11:911::b0b:15:dead/114' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xc0\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff" ),
  289. '2001:ca11:911::b0b:15:dead/113' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\x80\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff" ),
  290. '2001:ca11:911::b0b:15:dead/112' => array( "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\x00\x00", "\x20\x01\xca\x11\x09\x11\x00\x00\x00\x00\x0b\x0b\x00\x15\xff\xff" ),
  291. );
  292. foreach($tests as $range => $expected)
  293. {
  294. $this->assertEqual( Piwik_IP::getIpsForRange($range), $expected, $range );
  295. }
  296. }
  297. function test_isIpInRange()
  298. {
  299. $tests = array(
  300. '192.168.1.10' => array(
  301. '192.168.1.9' => false,
  302. '192.168.1.10' => true,
  303. '192.168.1.11' => false,
  304. // IPv6 addresses (including IPv4 mapped) have to be compared against IPv6 address ranges
  305. '::ffff:192.168.1.10' => false,
  306. ),
  307. '::ffff:192.168.1.10' => array(
  308. '::ffff:192.168.1.9' => false,
  309. '::ffff:192.168.1.10' => true,
  310. '::ffff:c0a8:010a' => true,
  311. '0000:0000:0000:0000:0000:ffff:c0a8:010a' => true,
  312. '::ffff:192.168.1.11' => false,
  313. // conversely, IPv4 addresses have to be compared against IPv4 address ranges
  314. '192.168.1.10' => false,
  315. ),
  316. '192.168.1.10/32' => array(
  317. '192.168.1.9' => false,
  318. '192.168.1.10' => true,
  319. '192.168.1.11' => false,
  320. ),
  321. '192.168.1.10/31' => array(
  322. '192.168.1.9' => false,
  323. '192.168.1.10' => true,
  324. '192.168.1.11' => true,
  325. '192.168.1.12' => false,
  326. ),
  327. '192.168.1.128/25' => array(
  328. '192.168.1.127' => false,
  329. '192.168.1.128' => true,
  330. '192.168.1.255' => true,
  331. '192.168.2.0' => false,
  332. ),
  333. '192.168.1.10/24' => array(
  334. '192.168.0.255' => false,
  335. '192.168.1.0' => true,
  336. '192.168.1.1' => true,
  337. '192.168.1.2' => true,
  338. '192.168.1.3' => true,
  339. '192.168.1.4' => true,
  340. '192.168.1.7' => true,
  341. '192.168.1.8' => true,
  342. '192.168.1.15' => true,
  343. '192.168.1.16' => true,
  344. '192.168.1.31' => true,
  345. '192.168.1.32' => true,
  346. '192.168.1.63' => true,
  347. '192.168.1.64' => true,
  348. '192.168.1.127' => true,
  349. '192.168.1.128' => true,
  350. '192.168.1.255' => true,
  351. '192.168.2.0' => false,
  352. ),
  353. '192.168.1.*' => array(
  354. '192.168.0.255' => false,
  355. '192.168.1.0' => true,
  356. '192.168.1.1' => true,
  357. '192.168.1.2' => true,
  358. '192.168.1.3' => true,
  359. '192.168.1.4' => true,
  360. '192.168.1.7' => true,
  361. '192.168.1.8' => true,
  362. '192.168.1.15' => true,
  363. '192.168.1.16' => true,
  364. '192.168.1.31' => true,
  365. '192.168.1.32' => true,
  366. '192.168.1.63' => true,
  367. '192.168.1.64' => true,
  368. '192.168.1.127' => true,
  369. '192.168.1.128' => true,
  370. '192.168.1.255' => true,
  371. '192.168.2.0' => false,
  372. ),
  373. );
  374. // testing with a single range
  375. foreach($tests as $range => $test)
  376. {
  377. foreach($test as $ip => $expected)
  378. {
  379. // range as a string
  380. $this->assertEqual( Piwik_IP::isIpInRange(Piwik_IP::P2N($ip), array($range)), $expected, "$ip in $range" );
  381. // range as an array(low, high)
  382. $aRange = Piwik_IP::getIpsForRange($range);
  383. $aRange[0] = Piwik_IP::N2P($aRange[0]);
  384. $aRange[1] = Piwik_IP::N2P($aRange[1]);
  385. $this->assertEqual( Piwik_IP::isIpInRange(Piwik_IP::P2N($ip), array($aRange)), $expected, "$ip in $range" );
  386. }
  387. }
  388. }
  389. private function saveGlobals($names)
  390. {
  391. $saved = array();
  392. foreach($names as $name)
  393. {
  394. $saved[$name] = isset($_SERVER[$name]) ? $_SERVER[$name] : null;
  395. }
  396. return $saved;
  397. }
  398. private function restoreGlobals($saved)
  399. {
  400. foreach($saved as $name => $value)
  401. {
  402. if(is_null($value))
  403. {
  404. unset($_SERVER[$name]);
  405. }
  406. else
  407. {
  408. $_SERVER[$name] = $value;
  409. }
  410. }
  411. }
  412. function test_getIpFromHeader()
  413. {
  414. Piwik::createConfigObject();
  415. Zend_Registry::get('config')->setTestEnvironment();
  416. $saved = $this->saveGlobals(array('REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR'));
  417. $tests = array(
  418. 'localhost inside LAN' => array('127.0.0.1', '', null, null, '127.0.0.1'),
  419. 'outside LAN, no proxy' => array('128.252.135.4', '', null, null, '128.252.135.4'),
  420. 'outside LAN, no (trusted) proxy' => array('128.252.135.4', '137.18.2.13, 128.252.135.4', '', null, '128.252.135.4'),
  421. 'outside LAN, one trusted proxy' => array('192.168.1.10', '137.18.2.13, 128.252.135.4, 192.168.1.10', 'HTTP_X_FORWARDED_FOR', null, '128.252.135.4'),
  422. 'outside LAN, proxy' => array('192.168.1.10', '128.252.135.4, 192.168.1.10', 'HTTP_X_FORWARDED_FOR', null, '128.252.135.4'),
  423. 'outside LAN, misconfigured proxy' => array('192.168.1.10', '128.252.135.4, 192.168.1.10, 192.168.1.10', 'HTTP_X_FORWARDED_FOR', null, '128.252.135.4'),
  424. 'outside LAN, multiple proxies' => array('192.168.1.10', '128.252.135.4, 192.168.1.20, 192.168.1.10', 'HTTP_X_FORWARDED_FOR', '192.168.1.*', '128.252.135.4'),
  425. 'outside LAN, multiple proxies' => array('[::ffff:7f00:10]', '128.252.135.4, [::ffff:7f00:20], [::ffff:7f00:10]', 'HTTP_X_FORWARDED_FOR', '::ffff:7f00:0/120', '128.252.135.4'),
  426. );
  427. foreach($tests as $description => $test)
  428. {
  429. $_SERVER['REMOTE_ADDR'] = $test[0];
  430. $_SERVER['HTTP_X_FORWARDED_FOR'] = $test[1];
  431. Zend_Registry::get('config')->General->proxy_client_headers = array($test[2]);
  432. Zend_Registry::get('config')->General->proxy_ips = array($test[3]);
  433. $this->assertEqual( Piwik_IP::getIpFromHeader(), $test[4], $description );
  434. }
  435. $this->restoreGlobals($saved);
  436. }
  437. function test_getNonProxyIpFromHeader()
  438. {
  439. Piwik::createConfigObject();
  440. Zend_Registry::get('config')->setTestEnvironment();
  441. $saved = $this->saveGlobals(array('REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR'));
  442. $ips = array(
  443. '0.0.0.0',
  444. '72.14.204.99',
  445. '127.0.0.1',
  446. '169.254.0.1',
  447. '208.80.152.2',
  448. '224.0.0.1',
  449. );
  450. // no proxies
  451. foreach($ips as $ip)
  452. {
  453. $this->assertEqual( Piwik_IP::getNonProxyIpFromHeader($ip, array()), $ip, $ip );
  454. }
  455. // 1.1.1.1 is not a trusted proxy
  456. $_SERVER['REMOTE_ADDR'] = '1.1.1.1';
  457. foreach($ips as $ip)
  458. {
  459. $_SERVER['HTTP_X_FORWARDED_FOR'] = '';
  460. $this->assertEqual( Piwik_IP::getNonProxyIpFromHeader('1.1.1.1', array('HTTP_X_FORWARDED_FOR')), '1.1.1.1', $ip);
  461. }
  462. // 1.1.1.1 is a trusted proxy
  463. $_SERVER['REMOTE_ADDR'] = '1.1.1.1';
  464. foreach($ips as $ip)
  465. {
  466. $_SERVER['HTTP_X_FORWARDED_FOR'] = $ip;
  467. $this->assertEqual( Piwik_IP::getNonProxyIpFromHeader('1.1.1.1', array('HTTP_X_FORWARDED_FOR')), $ip, $ip);
  468. $_SERVER['HTTP_X_FORWARDED_FOR'] = '1.2.3.4, ' . $ip;
  469. $this->assertEqual( Piwik_IP::getNonProxyIpFromHeader('1.1.1.1', array('HTTP_X_FORWARDED_FOR')), $ip, $ip);
  470. // misconfiguration
  471. $_SERVER['HTTP_X_FORWARDED_FOR'] = $ip . ', 1.1.1.1';
  472. $this->assertEqual( Piwik_IP::getNonProxyIpFromHeader('1.1.1.1', array('HTTP_X_FORWARDED_FOR')), $ip, $ip);
  473. }
  474. $this->restoreGlobals($saved);
  475. }
  476. function test_getLastIpFromList()
  477. {
  478. $tests = array(
  479. '' => '',
  480. '127.0.0.1' => '127.0.0.1',
  481. ' 127.0.0.1 ' => '127.0.0.1',
  482. ' 192.168.1.1, 127.0.0.1' => '127.0.0.1',
  483. '192.168.1.1 ,127.0.0.1 ' => '127.0.0.1',
  484. '192.168.1.1,' => '',
  485. );
  486. foreach($tests as $csv => $expected)
  487. {
  488. // without excluded IPs
  489. $this->assertEqual( Piwik_IP::getLastIpFromList($csv), $expected);
  490. // with excluded Ips
  491. $this->assertEqual( Piwik_IP::getLastIpFromList($csv . ', 10.10.10.10', array('10.10.10.10')), $expected);
  492. }
  493. }
  494. function test_getHostByAddr()
  495. {
  496. $hosts = array( 'localhost', strtolower(@php_uname('n')), '127.0.0.1' );
  497. $this->assertTrue( in_array(strtolower(Piwik_IP::getHostByAddr('127.0.0.1')), $hosts), '127.0.0.1 -> localhost' );
  498. if (!Piwik_Common::isWindows() || PHP_VERSION >= '5.3')
  499. {
  500. $hosts = array( 'ip6-localhost', strtolower(@php_uname('n')), '::1' );
  501. $this->assertTrue( in_array(strtolower(Piwik_IP::getHostByAddr('::1')), $hosts), '::1 -> ip6-localhost' );
  502. }
  503. }
  504. function test_php_compat_inet_ntop()
  505. {
  506. $adds = array(
  507. '127.0.0.1' => '7f000001',
  508. '192.232.131.222' => 'c0e883de',
  509. '255.0.0.0' => 'ff000000',
  510. '255.255.255.255' => 'ffffffff',
  511. '::1' => '00000000000000000000000000000001',
  512. '::101' => '00000000000000000000000000000101',
  513. '::0.1.1.1' => '00000000000000000000000000010101',
  514. '2001:260:0:10::1' => '20010260000000100000000000000001',
  515. '2001:0:0:260::1' => '20010000000002600000000000000001',
  516. '2001::260:0:0:10:1' => '20010000000002600000000000100001',
  517. '2001:5c0:1000:b::90f8' => '200105c01000000b00000000000090f8',
  518. 'fe80::200:4cff:fe43:172f' => 'fe8000000000000002004cfffe43172f',
  519. '::ffff:127.0.0.1' => '00000000000000000000ffff7f000001',
  520. '::127.0.0.1' => '0000000000000000000000007f000001',
  521. '::fff0:7f00:1' => '00000000000000000000fff07f000001',
  522. );
  523. foreach ($adds as $k => $v) {
  524. $this->assertEqual( php_compat_inet_ntop(pack('H*', $v)), $k, $k );
  525. if(!Piwik_Common::isWindows())
  526. $this->assertEqual( @inet_ntop(pack('H*', $v)), $k, $k );
  527. }
  528. }
  529. function test_php_compat_inet_pton()
  530. {
  531. $adds = array(
  532. '127.0.0.1' => '7f000001',
  533. '192.232.131.222' => 'c0e883de',
  534. '255.0.0.0' => 'ff000000',
  535. '255.255.255.255' => 'ffffffff',
  536. '::' => '00000000000000000000000000000000',
  537. '::0' => '00000000000000000000000000000000',
  538. '0::' => '00000000000000000000000000000000',
  539. '0::0' => '00000000000000000000000000000000',
  540. '::1' => '00000000000000000000000000000001',
  541. '2001:260:0:10::1' => '20010260000000100000000000000001',
  542. '2001:5c0:1000:b::90f8' => '200105c01000000b00000000000090f8',
  543. 'fe80::200:4cff:fe43:172f' => 'fe8000000000000002004cfffe43172f',
  544. '::ffff:127.0.0.1' => '00000000000000000000ffff7f000001',
  545. '::127.0.0.1' => '0000000000000000000000007f000001',
  546. // relaxed rules
  547. '00000::' => '00000000000000000000000000000000',
  548. '1:2:3:4:5:ffff:127.0.0.1' => '00010002000300040005ffff7f000001',
  549. // invalid input
  550. null => false,
  551. false => false,
  552. true => false,
  553. '' => false,
  554. '0' => false,
  555. '07.07.07.07' => false,
  556. '1.' => false,
  557. '.1' => false,
  558. '1.1' => false,
  559. '.1.1.' => false,
  560. '1.1.1.' => false,
  561. '.1.1.1' => false,
  562. '1.2.3.4.' => false,
  563. '.1.2.3.4' => false,
  564. '1.2.3.256' => false,
  565. 'a.b.c.d' => false,
  566. '::1::' => false,
  567. '1:2:3:4:::5:6' => false,
  568. '1:2:3:4:5:6:' => false,
  569. ':1:2:3:4:5:6' => false,
  570. '1:2:3:4:5:6:7:' => false,
  571. ':1:2:3:4:5:6:7' => false,
  572. '::11111:0' => false,
  573. '::g' => false,
  574. '::ffff:127.00.0.1' => false,
  575. '::ffff:127.0.0.01' => false,
  576. '::ffff:256.0.0.1' => false,
  577. '::ffff:1.256.0.1' => false,
  578. '::ffff:65536.0.0.1' => false,
  579. '::ffff:256.65536.0.1' => false,
  580. '::ffff:65536.65536.0.1' => false,
  581. '::ffff:7f01:0.1' => false,
  582. 'ffff:127.0.0.1:ffff::' => false,
  583. );
  584. foreach ($adds as $k => $v) {
  585. $this->assertEqual( bin2hex(php_compat_inet_pton($k)), $v, $k );
  586. if(!Piwik_Common::isWindows())
  587. $this->assertEqual( bin2hex(@inet_pton($k)), $v, $k );
  588. }
  589. }
  590. }