getIP.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. <?php
  2. /*
  3. * This script detects the client's IP address and fetches ISP info from ipinfo.io/
  4. * Output from this script is a JSON string composed of 2 objects: a string called processedString which contains the combined IP, ISP, Contry and distance as it can be presented to the user; and an object called rawIspInfo which contains the raw data from ipinfo.io (will be empty if isp detection is disabled).
  5. * Client side, the output of this script can be treated as JSON or as regular text. If the output is regular text, it will be shown to the user as is.
  6. */
  7. require_once "./config.php";
  8. error_reporting(0);
  9. define('API_KEY_FILE', 'getIP_ipInfo_apikey.php');
  10. define('SERVER_LOCATION_CACHE_FILE', 'getIP_serverLocation.php');
  11. /**
  12. * @return string
  13. */
  14. function getClientIp()
  15. {
  16. if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
  17. $ip = $_SERVER['HTTP_CLIENT_IP'];
  18. } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
  19. $ip = $_SERVER['HTTP_X_REAL_IP'];
  20. } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  21. $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  22. $ip = preg_replace('/,.*/', '', $ip); # hosts are comma-separated, client is first
  23. } else {
  24. $ip = $_SERVER['REMOTE_ADDR'];
  25. }
  26. return preg_replace('/^::ffff:/', '', $ip);
  27. }
  28. /**
  29. * @param string $ip
  30. *
  31. * @return string|null
  32. */
  33. function getLocalOrPrivateIpInfo($ip)
  34. {
  35. // ::1/128 is the only localhost ipv6 address. there are no others, no need to strpos this
  36. if ('::1' === $ip) {
  37. return 'localhost IPv6 access';
  38. }
  39. // simplified IPv6 link-local address (should match fe80::/10)
  40. if (stripos($ip, 'fe80:') === 0) {
  41. return 'link-local IPv6 access';
  42. }
  43. // anything within the 127/8 range is localhost ipv4, the ip must start with 127.0
  44. if (strpos($ip, '127.') === 0) {
  45. return 'localhost IPv4 access';
  46. }
  47. // 10/8 private IPv4
  48. if (strpos($ip, '10.') === 0) {
  49. return 'private IPv4 access';
  50. }
  51. // 172.16/12 private IPv4
  52. if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) {
  53. return 'private IPv4 access';
  54. }
  55. // 192.168/16 private IPv4
  56. if (strpos($ip, '192.168.') === 0) {
  57. return 'private IPv4 access';
  58. }
  59. // IPv4 link-local
  60. if (strpos($ip, '169.254.') === 0) {
  61. return 'link-local IPv4 access';
  62. }
  63. return null;
  64. }
  65. /**
  66. * @return string
  67. */
  68. function getIpInfoTokenString()
  69. {
  70. if (!file_exists(API_KEY_FILE)) {
  71. return '';
  72. }
  73. require API_KEY_FILE;
  74. if (empty($IPINFO_APIKEY)) {
  75. return '';
  76. }
  77. return '?token='.$IPINFO_APIKEY;
  78. }
  79. /**
  80. * @param string $ip
  81. *
  82. * @return array|null
  83. */
  84. function getIspInfo($ip, $ipService)
  85. {
  86. $json = '';
  87. if ($ipService == 'ip.sb') {
  88. $json = file_get_contents('https://api.ip.sb/geoip/' . $ip);
  89. } elseif ($ipService == 'ipinfo.io') {
  90. $json = file_get_contents('https://ipinfo.io/'.$ip.'/json'.getIpInfoTokenString());
  91. }
  92. if (!is_string($json)) {
  93. return null;
  94. }
  95. $data = json_decode($json, true);
  96. if (!is_array($data)) {
  97. return null;
  98. }
  99. return $data;
  100. }
  101. /**
  102. * @param array|null $rawIspInfo
  103. *
  104. * @return string
  105. */
  106. function getIsp($rawIspInfo, $ipService)
  107. {
  108. if ($ipService == 'ip.sb') {
  109. if (
  110. !is_array($rawIspInfo)
  111. || !array_key_exists('organization', $rawIspInfo)
  112. || !is_string($rawIspInfo['organization'])
  113. || empty($rawIspInfo['organization'])
  114. ) {
  115. return 'Unknown';
  116. }
  117. return $rawIspInfo['organization'];
  118. } elseif ($ipService == 'ipinfo.io') {
  119. if (
  120. !is_array($rawIspInfo)
  121. || !array_key_exists('org', $rawIspInfo)
  122. || !is_string($rawIspInfo['org'])
  123. || empty($rawIspInfo['org'])
  124. ) {
  125. return 'Unknown';
  126. }
  127. return preg_replace('/AS\\d+\\s/', '', $rawIspInfo['org']);
  128. }
  129. return 'Unknown';
  130. }
  131. /**
  132. * @return string|null
  133. */
  134. function getServerLocation()
  135. {
  136. $serverLoc = null;
  137. if (file_exists(SERVER_LOCATION_CACHE_FILE)) {
  138. require SERVER_LOCATION_CACHE_FILE;
  139. }
  140. if (is_string($serverLoc) && !empty($serverLoc)) {
  141. return $serverLoc;
  142. }
  143. $json = file_get_contents('https://ipinfo.io/json'.getIpInfoTokenString());
  144. if (!is_string($json)) {
  145. return null;
  146. }
  147. $details = json_decode($json, true);
  148. if (
  149. !is_array($details)
  150. || !array_key_exists('loc', $details)
  151. || !is_string($details['loc'])
  152. || empty($details['loc'])
  153. ) {
  154. return null;
  155. }
  156. $serverLoc = $details['loc'];
  157. $cacheData = "<?php\n\n\$serverLoc = '".addslashes($serverLoc)."';\n";
  158. file_put_contents(SERVER_LOCATION_CACHE_FILE, $cacheData);
  159. return $serverLoc;
  160. }
  161. /**
  162. * Optimized algorithm from http://www.codexworld.com
  163. *
  164. * @param float $latitudeFrom
  165. * @param float $longitudeFrom
  166. * @param float $latitudeTo
  167. * @param float $longitudeTo
  168. *
  169. * @return float [km]
  170. */
  171. function distance(
  172. $latitudeFrom,
  173. $longitudeFrom,
  174. $latitudeTo,
  175. $longitudeTo
  176. ) {
  177. $rad = M_PI / 180;
  178. $theta = $longitudeFrom - $longitudeTo;
  179. $dist = sin($latitudeFrom * $rad)
  180. * sin($latitudeTo * $rad)
  181. + cos($latitudeFrom * $rad)
  182. * cos($latitudeTo * $rad)
  183. * cos($theta * $rad);
  184. return acos($dist) / $rad * 60 * 1.853;
  185. }
  186. /**
  187. * @param array|null $rawIspInfo
  188. *
  189. * @return string|null
  190. */
  191. function getDistance($rawIspInfo)
  192. {
  193. if (
  194. !is_array($rawIspInfo)
  195. || !array_key_exists('loc', $rawIspInfo)
  196. || !isset($_GET['distance'])
  197. || !in_array($_GET['distance'], ['mi', 'km'], true)
  198. ) {
  199. return null;
  200. }
  201. $unit = $_GET['distance'];
  202. $clientLocation = $rawIspInfo['loc'];
  203. $serverLocation = getServerLocation();
  204. if (!is_string($serverLocation)) {
  205. return null;
  206. }
  207. return calculateDistance(
  208. $serverLocation,
  209. $clientLocation,
  210. $unit
  211. );
  212. }
  213. /**
  214. * @param string $clientLocation
  215. * @param string $serverLocation
  216. * @param string $unit
  217. *
  218. * @return string
  219. */
  220. function calculateDistance($clientLocation, $serverLocation, $unit)
  221. {
  222. list($clientLatitude, $clientLongitude) = explode(',', $clientLocation);
  223. list($serverLatitude, $serverLongitude) = explode(',', $serverLocation);
  224. $dist = distance(
  225. $clientLatitude,
  226. $clientLongitude,
  227. $serverLatitude,
  228. $serverLongitude
  229. );
  230. if ('mi' === $unit) {
  231. $dist /= 1.609344;
  232. $dist = round($dist, -1);
  233. if ($dist < 15) {
  234. $dist = '<15';
  235. }
  236. return $dist.' mi';
  237. }
  238. if ('km' === $unit) {
  239. $dist = round($dist, -1);
  240. if ($dist < 20) {
  241. $dist = '<20';
  242. }
  243. return $dist.' km';
  244. }
  245. return null;
  246. }
  247. /**
  248. * @return void
  249. */
  250. function sendHeaders()
  251. {
  252. header('Content-Type: application/json; charset=utf-8');
  253. if (isset($_GET['cors'])) {
  254. header('Access-Control-Allow-Origin: *');
  255. header('Access-Control-Allow-Methods: GET, POST');
  256. }
  257. header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
  258. header('Cache-Control: post-check=0, pre-check=0', false);
  259. header('Pragma: no-cache');
  260. }
  261. /**
  262. * @param string $ip
  263. * @param string|null $ipInfo
  264. * @param string|null $distance
  265. * @param array|null $rawIspInfo
  266. *
  267. * @return void
  268. */
  269. function sendResponse(
  270. $ip,
  271. $ipInfo = null,
  272. $rawIspInfo = null
  273. ) {
  274. $processedString = $ip;
  275. if (is_string($ipInfo)) {
  276. $processedString .= ' - '.$ipInfo;
  277. }
  278. if (
  279. is_array($rawIspInfo)
  280. && array_key_exists('country', $rawIspInfo)
  281. ) {
  282. $processedString .= ' - '.$rawIspInfo['country'] . ',' . $rawIspInfo['region'] . ',' . $rawIspInfo['city'];
  283. }
  284. sendHeaders();
  285. echo json_encode([
  286. 'processedString' => $processedString,
  287. 'rawIspInfo' => $rawIspInfo ?: '',
  288. ]);
  289. }
  290. $ip = getClientIp();
  291. $localIpInfo = getLocalOrPrivateIpInfo($ip);
  292. // local ip, no need to fetch further information
  293. if (is_string($localIpInfo)) {
  294. sendResponse($ip, $localIpInfo);
  295. exit;
  296. }
  297. if (!isset($_GET['isp'])) {
  298. sendResponse($ip);
  299. exit;
  300. }
  301. $rawIspInfo = getIspInfo($ip, IP_SERVICE);
  302. $isp = getIsp($rawIspInfo, IP_SERVICE);
  303. //$distance = getDistance($rawIspInfo);
  304. sendResponse($ip, $isp, $rawIspInfo);