getIP.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  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, Country 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. error_reporting(0);
  8. define('API_KEY_FILE', 'getIP_ipInfo_apikey.php');
  9. define('SERVER_LOCATION_CACHE_FILE', 'getIP_serverLocation.php');
  10. require_once 'getIP_util.php';
  11. /**
  12. * @param string $ip
  13. *
  14. * @return string|null
  15. */
  16. function getLocalOrPrivateIpInfo($ip)
  17. {
  18. // ::1/128 is the only localhost ipv6 address. there are no others, no need to strpos this
  19. if ('::1' === $ip) {
  20. return 'localhost IPv6 access';
  21. }
  22. // simplified IPv6 link-local address (should match fe80::/10)
  23. if (stripos($ip, 'fe80:') === 0) {
  24. return 'link-local IPv6 access';
  25. }
  26. // anything within the 127/8 range is localhost ipv4, the ip must start with 127.0
  27. if (strpos($ip, '127.') === 0) {
  28. return 'localhost IPv4 access';
  29. }
  30. // 10/8 private IPv4
  31. if (strpos($ip, '10.') === 0) {
  32. return 'private IPv4 access';
  33. }
  34. // 172.16/12 private IPv4
  35. if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) {
  36. return 'private IPv4 access';
  37. }
  38. // 192.168/16 private IPv4
  39. if (strpos($ip, '192.168.') === 0) {
  40. return 'private IPv4 access';
  41. }
  42. // IPv4 link-local
  43. if (strpos($ip, '169.254.') === 0) {
  44. return 'link-local IPv4 access';
  45. }
  46. return null;
  47. }
  48. /**
  49. * @return string
  50. */
  51. function getIpInfoTokenString()
  52. {
  53. if (
  54. !file_exists(API_KEY_FILE)
  55. || !is_readable(API_KEY_FILE)
  56. ) {
  57. return '';
  58. }
  59. require API_KEY_FILE;
  60. if (empty($IPINFO_APIKEY)) {
  61. return '';
  62. }
  63. return '?token=' . $IPINFO_APIKEY;
  64. }
  65. /**
  66. * @param string $ip
  67. *
  68. * @return array|null
  69. */
  70. function getIspInfo($ip)
  71. {
  72. $json = file_get_contents('https://ipinfo.io/' . $ip . '/json' . getIpInfoTokenString());
  73. if (!is_string($json)) {
  74. return null;
  75. }
  76. $data = json_decode($json, true);
  77. if (!is_array($data)) {
  78. return null;
  79. }
  80. return $data;
  81. }
  82. /**
  83. * @param array|null $rawIspInfo
  84. *
  85. * @return string
  86. */
  87. function getIsp($rawIspInfo)
  88. {
  89. if (is_array($rawIspInfo)) {
  90. /* variant with no token
  91. has json like:
  92. {
  93. "ip": "xxx.xxx.xxx.xxx",
  94. "hostname": "example.com",
  95. "city": "Vienna",
  96. "region": "Vienna",
  97. "country": "AT",
  98. "loc": "48.2085,16.3721",
  99. "org": "ASxxxx T-Mobile Austria GmbH",
  100. "postal": "nnnn",
  101. "timezone": "Europe/Vienna",
  102. "readme": "https://ipinfo.io/missingauth"
  103. }
  104. */
  105. if (
  106. array_key_exists('org', $rawIspInfo)
  107. && is_string($rawIspInfo['org'])
  108. && !empty($rawIspInfo['org'])
  109. ) {
  110. // Remove AS##### from ISP name, if present
  111. return preg_replace('/AS\\d+\\s/', '', $rawIspInfo['org']);
  112. }
  113. /*
  114. variant with valid token has json:
  115. {
  116. "ip": "xxx.xxx.xxx.xxx",
  117. "hostname": "example.com",
  118. "city": "Vienna",
  119. "region": "Vienna",
  120. "country": "AT",
  121. "loc": "48.2085,16.3721",
  122. "postal": "1010",
  123. "timezone": "Europe/Vienna",
  124. "asn": {
  125. "asn": "ASxxxx",
  126. "name": "T-Mobile Austria GmbH",
  127. "domain": "t-mobile.at",
  128. "route": "xxx.xxx.xxx.xxx/xx",
  129. "type": "isp"
  130. },
  131. "company": {
  132. "name": "XX",
  133. "domain": "example.com",
  134. "type": "isp"
  135. },
  136. "privacy": {
  137. "vpn": true,
  138. "proxy": false,
  139. "tor": false,
  140. "relay": false,
  141. "hosting": false,
  142. "service": ""
  143. },
  144. "abuse": {
  145. "address": "...",
  146. "country": "AT",
  147. "email": "[email protected]",
  148. "name": "XXX",
  149. "network": "xxx.xxx.xxx.xxx-xxx.xxx.xxx.xxx",
  150. "phone": ""
  151. },
  152. "domains": {
  153. "total": 0,
  154. "domains": [
  155. ]
  156. }
  157. }
  158. */
  159. if (
  160. array_key_exists('asn', $rawIspInfo)
  161. && is_array($rawIspInfo['asn'])
  162. && !empty($rawIspInfo['asn'])
  163. && array_key_exists('name', $rawIspInfo['asn'])
  164. && is_string($rawIspInfo['asn']['name'])
  165. ) {
  166. // Remove AS##### from ISP name, if present
  167. return $rawIspInfo['asn']['name'];
  168. }
  169. }
  170. return 'Unknown ISP';
  171. }
  172. /**
  173. * @return string|null
  174. */
  175. function getServerLocation()
  176. {
  177. $serverLoc = null;
  178. if (
  179. file_exists(SERVER_LOCATION_CACHE_FILE)
  180. && is_readable(SERVER_LOCATION_CACHE_FILE)
  181. ) {
  182. require SERVER_LOCATION_CACHE_FILE;
  183. }
  184. if (is_string($serverLoc) && !empty($serverLoc)) {
  185. return $serverLoc;
  186. }
  187. $json = file_get_contents('https://ipinfo.io/json' . getIpInfoTokenString());
  188. if (!is_string($json)) {
  189. return null;
  190. }
  191. $details = json_decode($json, true);
  192. if (
  193. !is_array($details)
  194. || !array_key_exists('loc', $details)
  195. || !is_string($details['loc'])
  196. || empty($details['loc'])
  197. ) {
  198. return null;
  199. }
  200. $serverLoc = $details['loc'];
  201. $cacheData = "<?php\n\n\$serverLoc = '" . addslashes($serverLoc) . "';\n";
  202. file_put_contents(SERVER_LOCATION_CACHE_FILE, $cacheData);
  203. return $serverLoc;
  204. }
  205. /**
  206. * Optimized algorithm from http://www.codexworld.com
  207. *
  208. * @param float $latitudeFrom
  209. * @param float $longitudeFrom
  210. * @param float $latitudeTo
  211. * @param float $longitudeTo
  212. *
  213. * @return float [km]
  214. */
  215. function distance(
  216. $latitudeFrom,
  217. $longitudeFrom,
  218. $latitudeTo,
  219. $longitudeTo
  220. ) {
  221. $rad = M_PI / 180;
  222. $theta = $longitudeFrom - $longitudeTo;
  223. $dist = sin($latitudeFrom * $rad)
  224. * sin($latitudeTo * $rad)
  225. + cos($latitudeFrom * $rad)
  226. * cos($latitudeTo * $rad)
  227. * cos($theta * $rad);
  228. return acos($dist) / $rad * 60 * 1.853;
  229. }
  230. /**
  231. * @param array|null $rawIspInfo
  232. *
  233. * @return string|null
  234. */
  235. function getDistance($rawIspInfo)
  236. {
  237. if (
  238. !is_array($rawIspInfo)
  239. || !array_key_exists('loc', $rawIspInfo)
  240. || !isset($_GET['distance'])
  241. || !in_array($_GET['distance'], ['mi', 'km'], true)
  242. ) {
  243. return null;
  244. }
  245. $unit = $_GET['distance'];
  246. $clientLocation = $rawIspInfo['loc'];
  247. $serverLocation = getServerLocation();
  248. if (!is_string($serverLocation)) {
  249. return null;
  250. }
  251. return calculateDistance(
  252. $serverLocation,
  253. $clientLocation,
  254. $unit
  255. );
  256. }
  257. /**
  258. * @param string $clientLocation
  259. * @param string $serverLocation
  260. * @param string $unit
  261. *
  262. * @return string
  263. */
  264. function calculateDistance($clientLocation, $serverLocation, $unit)
  265. {
  266. list($clientLatitude, $clientLongitude) = explode(',', $clientLocation);
  267. list($serverLatitude, $serverLongitude) = explode(',', $serverLocation);
  268. $dist = distance(
  269. $clientLatitude,
  270. $clientLongitude,
  271. $serverLatitude,
  272. $serverLongitude
  273. );
  274. if ('mi' === $unit) {
  275. $dist /= 1.609344;
  276. $dist = round($dist, -1);
  277. if ($dist < 15) {
  278. $dist = '<15';
  279. }
  280. return $dist . ' mi';
  281. }
  282. if ('km' === $unit) {
  283. $dist = round($dist, -1);
  284. if ($dist < 20) {
  285. $dist = '<20';
  286. }
  287. return $dist . ' km';
  288. }
  289. return null;
  290. }
  291. /**
  292. * @return void
  293. */
  294. function sendHeaders()
  295. {
  296. header('Content-Type: application/json; charset=utf-8');
  297. if (isset($_GET['cors'])) {
  298. header('Access-Control-Allow-Origin: *');
  299. header('Access-Control-Allow-Methods: GET, POST');
  300. }
  301. header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
  302. header('Cache-Control: post-check=0, pre-check=0', false);
  303. header('Pragma: no-cache');
  304. }
  305. /**
  306. * @param string $ip
  307. * @param string|null $ipInfo
  308. * @param string|null $distance
  309. * @param array|null $rawIspInfo
  310. *
  311. * @return void
  312. */
  313. function sendResponse(
  314. $ip,
  315. $ipInfo = null,
  316. $distance = null,
  317. $rawIspInfo = null
  318. ) {
  319. $processedString = $ip;
  320. if (is_string($ipInfo)) {
  321. $processedString .= ' - ' . $ipInfo;
  322. }
  323. if (
  324. is_array($rawIspInfo)
  325. && array_key_exists('country', $rawIspInfo)
  326. ) {
  327. $processedString .= ', ' . $rawIspInfo['country'];
  328. }
  329. if (is_string($distance)) {
  330. $processedString .= ' (' . $distance . ')';
  331. }
  332. sendHeaders();
  333. echo json_encode([
  334. 'processedString' => $processedString,
  335. 'rawIspInfo' => $rawIspInfo ?: '',
  336. ]);
  337. }
  338. $ip = getClientIp();
  339. $localIpInfo = getLocalOrPrivateIpInfo($ip);
  340. // local ip, no need to fetch further information
  341. if (is_string($localIpInfo)) {
  342. sendResponse($ip, $localIpInfo);
  343. exit;
  344. }
  345. if (!isset($_GET['isp'])) {
  346. sendResponse($ip);
  347. exit;
  348. }
  349. $rawIspInfo = getIspInfo($ip);
  350. $isp = getIsp($rawIspInfo);
  351. $distance = getDistance($rawIspInfo);
  352. sendResponse($ip, $isp, $distance, $rawIspInfo);