├── oauth.ini.dist ├── .gitignore ├── credentials.php.dist ├── composer.json ├── README.md ├── LICENSE ├── config.dist.php ├── webhostconfig.php.dist ├── public_html ├── splash.php ├── iprange.php ├── stats.php ├── oauth.php └── index.php ├── API.MD ├── checkhost ├── checkhost.php └── computehosts.php ├── views ├── base.html.twig └── results.html.twig ├── EXPLANATION.md └── composer.lock /oauth.ini.dist: -------------------------------------------------------------------------------- 1 | agent = IPCheck 2 | consumerKey = ''; 3 | consumerSecret = ''; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .idea/ 3 | webhostconfig.php 4 | credentials.php 5 | /sources/ 6 | /cache/ 7 | /stats/ 8 | -------------------------------------------------------------------------------- /credentials.php.dist: -------------------------------------------------------------------------------- 1 | "", 16 | ); 17 | 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sql-enwiki/ipcheck", 3 | "description": "Tool to assist with identify IP proxies", 4 | "type": "project", 5 | "homepage": "https://tools.wmflabs.org/ipcheck/", 6 | "license": "MIT", 7 | "require": { 8 | "php" : "^7.2", 9 | "ext-curl": "*", 10 | "ext-json": "*", 11 | "twig/twig": "^2.6", 12 | "guzzlehttp/guzzle": "^6.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipcheck 2 | 3 | IPCheck - Proxy Checker 4 | 5 | * Live at https://tools.wmflabs.org/ipcheck/ 6 | * Development staging at https://tools.wmflabs.org/ipcheck-dev/ 7 | 8 | ## Installation 9 | 10 | Requires PHP 7.2. 11 | 12 | 1. `cp credentials.php.dist credentials.php` and fill in your API keys. 13 | 1. `cp webhostconfig.php.dist webhostconfig.php` and fill in your webhost detection settings. 14 | 1. `cp oauth.ini.dist oauth.ini` and fill in your OAuth consumer keys. 15 | 1. `cp config.dist.php config.php` and change the configuration to your liking. 16 | 1. `composer install` 17 | 1. `cd public_html && php -S localhost:8000` 18 | 19 | You should now be up and running at http://localhost:8000 20 | 21 | ## Interpreting results 22 | 23 | * There are some tips / hints at [EXPLANATION.md](EXPLANATION.md) 24 | * Please don't hesitate to send a pull request for this file! 25 | 26 | ## API 27 | 28 | * Instructions on using the API can be found at [API.MD](API.MD) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2018 SQL at the English Wikipedia 4 | ( https://en.wikipedia.org/wiki/User:SQL ) and contributors. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /config.dist.php: -------------------------------------------------------------------------------- 1 | 'min', 'limit' => 15, 'type2' => 'day', 'limit2' => 5000 ); 9 | $lservice['iphub'] = array( 'type' => 'day', 'limit' => 1000, 'type2' => 'day', 'limit2' => 1000 ); 10 | $lservice['iphunter'] = array( 'type' => 'day', 'limit' => 1000, 'type2' => 'day', 'limit2' => 1000 ); 11 | $lservice['ipqs'] = array( 'type' => 'month', 'limit' => 50000, 'type2' => 'month', 'limit2' => 50000 ); 12 | /* $lservice['nofraud'] = array( 'type' => 'day', 'limit' => 600, 'type2' => 'day', 'limit2' => 600 ); */ 13 | $lservice['proxycheck-io'] = array( 'type' => 'day', 'limit' => 1000, 'type2' => 'day', 'limit2' => 1000 ); 14 | $lservice['sorbs'] = array( 'type' => 'min', 'limit' => 1000, 'type2' => 'min', 'limit2' => 1000 ); 15 | $lservice['spamhaus'] = array( 'type' => 'min', 'limit' => 1000, 'type2' => 'min', 'limit2' => 1000 ); 16 | $lservice['teoh'] = array( 'type' => 'day', 'limit' => 5000, 'type2' => 'day', 'limit2' => 5000 ); 17 | $lservice['dshield'] = array( 'type' => 'min', 'limit' => 1000, 'type2' => 'min', 'limit2' => 1000 ); 18 | $lservice['ipstack'] = array( 'type' => 'month', 'limit' => 10000, 'type2' => 'min', 'limit2' => 1000 ); 19 | return( $lservice ); 20 | } 21 | 22 | ?> -------------------------------------------------------------------------------- /webhostconfig.php.dist: -------------------------------------------------------------------------------- 1 | "", //Set the name to be displayed in headers 7 | 'asns' => array( '' ), //List of ASNs 8 | 'searchterms' => array( '' ), //List of items to search for in the whois 9 | 'blockname' => '' ); //Name to display in the block message 10 | */ 11 | $hosts['azure'] = array( 12 | 'name' => "Microsoft Azure", //Set the name to be displayed in headers 13 | 'asns' => array( 'azure' ), //List of ASNs 14 | 'searchterms' => array( ), //List of items to search for in the whois 15 | 'blockname' => 'Microsoft Azure' ); //Name to display in the block message 16 | $hosts['amazon'] = array( 17 | 'name' => "Amazon AWS", //Set the name to be displayed in headers 18 | 'asns' => array( 'amazon' ), //List of ASNs 19 | 'searchterms' => array( ), //List of items to search for in the whois 20 | 'blockname' => 'Amazon AWS' ); //Name to display in the block message 21 | $hosts['google'] = array( 22 | 'name' => "Google Cloud", //Set the name to be displayed in headers 23 | 'asns' => array( 'google' ), //List of ASNs 24 | 'searchterms' => array( ), //List of items to search for in the whois 25 | 'blockname' => 'Google Cloud' ); //Name to display in the block message 26 | $hosts['digitalocean'] = array( 27 | 'name' => "DigitalOcean", //Set the name to be displayed in headers 28 | 'asns' => array( '14061' ), //List of ASNs 29 | 'searchterms' => array( 'digitalocean', 'serverstack' ), //List of items to search for in the whois 30 | 'blockname' => 'DigitalOcean' ); //Name to display in the block message 31 | ?> -------------------------------------------------------------------------------- /public_html/splash.php: -------------------------------------------------------------------------------- 1 | true ] ); 11 | $twig->addExtension(new Twig_Extension_Debug()); 12 | 13 | $currentver = substr( file_get_contents( __DIR__. '/../.git/refs/heads/master' ), 0, 7 ); 14 | 15 | $ts_pw = posix_getpwuid(posix_getuid()); 16 | $ts_mycnf = parse_ini_file($ts_pw['dir'] . "/replica.my.cnf"); 17 | 18 | $mysqli = new mysqli('meta.web.db.svc.eqiad.wmflabs', $ts_mycnf['user'], $ts_mycnf['password'], 'meta_p'); 19 | 20 | $query = 'select url, lang, family, dbname from wiki where is_closed = 0 order by dbname asc;'; 21 | session_start(); 22 | if( isset( $_GET['ip'] ) ) { $_SESSION['ip'] = $_GET['ip']; } 23 | session_write_close(); 24 | $res = mysqli_query( $mysqli, $query ); 25 | $opt = array(); 26 | $enwikiIndex = 0; 27 | $commonswikiIndex = 0; 28 | $metawikiIndex = 0; 29 | $baseindex = 0; 30 | while( $row = mysqli_fetch_assoc( $res ) ) { 31 | $murl = parse_url( $row['url'], PHP_URL_HOST ); 32 | $murl = substr( $murl, 0, -4 ); 33 | $row['url'] = $murl; 34 | if( $row['dbname'] == "enwiki" ) { $enwikiIndex = $baseindex; } 35 | if( $row['dbname'] == "commonswiki" ) { $commonswikiIndex = $baseindex; } 36 | if( $row['dbname'] == "metawiki" ) { $metawikiIndex = $baseindex; } 37 | array_push( $opt, $row ); 38 | $baseindex++; 39 | } 40 | echo $twig->render( 'base.html.twig', [ 41 | 'splash' => '1', 42 | 'currentver' => $currentver, 43 | 'options' => $opt, 44 | 'commonswiki' => $commonswikiIndex, 45 | 'enwiki' => $enwikiIndex, 46 | 'metawiki' => $metawikiIndex 47 | ] ); 48 | ?> -------------------------------------------------------------------------------- /API.MD: -------------------------------------------------------------------------------- 1 | # API Usage 2 | 3 | * Get paramaters: 4 | * ip - The IP Address 5 | * api - Set to true if using the API 6 | * key - Your personal API key (found at the bottom of the page after logging in) 7 | * Example URL: https://ipcheck.toolforge.org/index.php?ip=8.8.8.8&api=true&key=APIKEYHERE 8 | 9 | Example result: 10 | ```javascript 11 | { 12 | "proxycheck": { 13 | "title": "proxycheck.io", 14 | "result": { 15 | "proxy": false 16 | } 17 | }, 18 | "getIPIntel": { 19 | "title": "GetIPIntel", 20 | "result": { 21 | "chance": "0.0" 22 | } 23 | }, 24 | "ipQualityScore": { 25 | "title": "IPQualityScore", 26 | "result": { 27 | "proxy": true, 28 | "isp": "Google", 29 | "vpn": true, 30 | "mobile": false 31 | } 32 | }, 33 | "ipHub": { 34 | "title": "IPHub", 35 | "result": { 36 | "isp": "GOOGLE", 37 | "block": 1 38 | } 39 | }, 40 | "teohio": { 41 | "title": "Teoh.io", 42 | "result": { 43 | "hosting": false, 44 | "vpnOrProxy": true, 45 | "type": "business", 46 | "risk": "high" 47 | } 48 | }, 49 | "ipHunter": { 50 | "title": "IPHunter", 51 | "result": { 52 | "isp": "Google Inc.", 53 | "block": 1 54 | } 55 | }, 56 | "noFraud": { 57 | "title": "Nofraud", 58 | "result": { 59 | "chance": 100 60 | } 61 | }, 62 | "computeHosts": { 63 | "title": "Compute Hosts", 64 | "result": { 65 | "cloud": "This IP is not an AWS/Azure/GoogleCloud node.\n" 66 | } 67 | }, 68 | "sorbs": { 69 | "title": "SORBS DNSBL" 70 | }, 71 | "spamhaus": { 72 | "title": "Spamhaus ZEN DNSBL" 73 | }, 74 | "dshield": { 75 | "title": "DShield", 76 | "result": { 77 | "attacks": 45, 78 | "tfeeds": "forumspam lastseen(2018-12-05), qakbot lastseen(2015-04-03)" 79 | } 80 | }, 81 | "hola": { 82 | "title": "Hola" 83 | }, 84 | "cache": { 85 | "title": "Cache", 86 | "result": { 87 | "cached": "yes", 88 | "cachedate": "Feb 4 19:26:17 UTC 2019", 89 | "cacheuntil": "Feb 11 19:26:17 UTC 2019" 90 | } 91 | } 92 | } 93 | ``` 94 | 95 | #Notes 96 | 97 | * Misusing, or overusing the API is grounds for a ban 98 | * We have finite resources, please don't take more than your share. 99 | -------------------------------------------------------------------------------- /checkhost/checkhost.php: -------------------------------------------------------------------------------- 1 | 73 | -------------------------------------------------------------------------------- /checkhost/computehosts.php: -------------------------------------------------------------------------------- 1 | 99 | -------------------------------------------------------------------------------- /public_html/iprange.php: -------------------------------------------------------------------------------- 1 | 26 | IP Range Resolver by [[User:SQL]]
Check out my other tools!
27 | '; 28 | } 29 | 30 | function showheader() { 31 | echo ' 32 | 33 | 34 | 35 | IP Range Resolver 36 | 37 |

IP Range Resolver

'; 38 | } 39 | function showform() { 40 | echo ' 41 |
42 |

IP Range: 43 | .-

44 |

45 | 46 |
'; 47 | 48 | } 49 | if( @!isset($_GET['ipbase']) | @!isset($_GET['ipstart']) | @!isset($_GET['ipend']) ) { 50 | showheader(); 51 | showform(); 52 | showfooter(); 53 | die(); 54 | } else { 55 | $range_start = $_GET['ipstart']; 56 | $range_end = $_GET['ipend']; 57 | $range = $_GET['ipbase']; 58 | $rangebasecheck = explode( ".", $_GET['ipbase'] ); 59 | if( $rangebasecheck[0] < 0 | $rangebasecheck[0] > 255 | $rangebasecheck[1] < 0 | $rangebasecheck[1] > 255 | $rangebasecheck[2] < 0 | $rangebasecheck[2] > 255 ) { 60 | showheader(); 61 | echo "

Invalid base IP

"; 62 | showform(); 63 | showfooter(); 64 | die(); 65 | } 66 | if( $range_start < 0 | $range_start > 255 ) { 67 | showheader(); 68 | echo "

Invalid range start

"; 69 | showform(); 70 | showfooter(); 71 | die(); 72 | } 73 | if( $range_end < 0 | $range_end > 255 ) { 74 | showheader(); 75 | echo "

Invalid range start

"; 76 | showform(); 77 | showfooter(); 78 | die(); 79 | } 80 | } 81 | 82 | $iprange = array(); 83 | for( $range_add = $range_start; $range_add <= $range_end; $range_add++ ) { 84 | $iprange["$range.$range_add"] = gethostbyaddr( "$range.$range_add" ); 85 | } 86 | 87 | showheader(); 88 | foreach( $iprange as $ip=>$resolv ) { 89 | if( $ip == $resolv ) { 90 | $resolv = "NotResolved"; 91 | } 92 | echo "$ip - $resolv ( who / blocks / global )
\n"; 93 | } 94 | echo "

\n"; 95 | showform(); 96 | showfooter(); 97 | -------------------------------------------------------------------------------- /public_html/stats.php: -------------------------------------------------------------------------------- 1 | true ] ); 35 | $twig->addExtension(new Twig_Extension_Debug()); 36 | 37 | $currentver = substr( file_get_contents( __DIR__. '/../.git/refs/heads/master' ), 0, 7 ); 38 | 39 | 40 | $month = date( "n" ); 41 | $year = date( "Y" ); 42 | 43 | $lastmonth = strtotime( "$month/1/$year" ); 44 | 45 | if( $month == 1 ) { $lmm = 12; } else { $lmm = $month - 1; } 46 | $lm = strtotime( "$lmm/1/$year" ); 47 | 48 | $query = "select log_user, count(*) from logging where log_timestamp > $lastmonth and log_cached = 1 group by log_user order by count(*) desc limit 25;"; 49 | $res = mysqli_query( $mysqli, $query ); 50 | 51 | $thismonth = array(); 52 | while( $row = mysqli_fetch_assoc( $res ) ) { 53 | $user = $row['log_user']; 54 | $count = $row['count(*)']; 55 | $now = array(); 56 | $now['user'] = $user; 57 | $now['count'] = $count; 58 | array_push( $thismonth, $now ); 59 | } 60 | 61 | $query = "select count(*) as num from logging where log_timestamp > $lastmonth and log_cached = 1;"; 62 | $res = mysqli_query( $mysqli, $query ); 63 | $c = mysqli_fetch_array( $res ); 64 | $thismonth_num = $c['num']; 65 | 66 | $query = "select count(*) as num from logging where log_timestamp > $lm and log_timestamp < $lastmonth and log_cached = 1;"; 67 | $res = mysqli_query( $mysqli, $query ); 68 | $c = mysqli_fetch_array( $res ); 69 | $lastmonth_num = $c['num']; 70 | 71 | $query = "select count( distinct log_user ) as num from logging;"; 72 | $res = mysqli_query( $mysqli, $query ); 73 | $c = mysqli_fetch_array( $res ); 74 | $distinctusers = $c['num']; 75 | 76 | 77 | $query = "select log_user, count(*) from logging where log_cached = 1 group by log_user order by count(*) desc limit 25;"; 78 | $res = mysqli_query( $mysqli, $query ); 79 | $alltime = array(); 80 | while( $row = mysqli_fetch_assoc( $res ) ) { 81 | $user = $row['log_user']; 82 | $count = $row['count(*)']; 83 | $now = array(); 84 | $now['user'] = $user; 85 | $now['count'] = $count; 86 | array_push( $alltime, $now ); 87 | } 88 | 89 | $query = "select count(*) as num from logging where log_cached = 1;"; 90 | $res = mysqli_query( $mysqli, $query ); 91 | $c = mysqli_fetch_array( $res ); 92 | $alltime_num = $c['num']; 93 | 94 | 95 | echo $twig->render( 'base.html.twig', [ 96 | 'stats' => '1', 97 | 'thismonth' => $thismonth, 98 | 'currentver' => $currentver, 99 | 'alltime' => $alltime, 100 | 'alltime_num' => $alltime_num, 101 | 'lastmonth_num' => $lastmonth_num, 102 | 'thismonth_num' => $thismonth_num, 103 | 'distinctusers' => $distinctusers 104 | ] ); 105 | 106 | ?> 107 | -------------------------------------------------------------------------------- /views/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Proxy API Checker 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

14 | Proxy API Checker 15 |

16 |
17 |
18 |
19 | {% if username is defined %} 20 |
21 |
22 | 23 | 24 |
25 |
26 | 29 |
30 | 31 |
32 | {% endif %} 33 | {% if stats is defined %} 34 | 40 |

Top 25 users this month

41 |
    42 | {% for item in thismonth %} 43 |
  1. {{ item.user }} - {{ item.count }}
  2. 44 | {% endfor %} 45 |
46 |

Top 25 users all time

47 |
    48 | {% for item in alltime %} 49 |
  1. {{ item.user }} - {{ item.count }}
  2. 50 | {% endfor %} 51 |
52 | {% endif %} 53 | {% if splash is defined %} 54 |

Log in

55 |
56 |

57 |
58 | {% endif %} 59 | {% block results %} 60 | {% endblock %} 61 |
62 | 93 | 123 |
124 | 125 | 126 | -------------------------------------------------------------------------------- /EXPLANATION.md: -------------------------------------------------------------------------------- 1 | # Interpreting Results 2 | 3 | ## Proxycheck.io 4 | 5 | - Api Documentation at: https://proxycheck.io/api/ 6 | - Will typically give a vanilla yes/no 7 | - May also give a type (e.g. "VPN", "SOCKS", etc) 8 | - May also give a port number to check 9 | 10 | ## GetIPIntel.net 11 | 12 | - API Documentation at: https://getipintel.net/#API 13 | - Gives a 0-100% chance of the IP in question being a proxy/VPN 14 | 15 | ## IP Quality Score 16 | 17 | - API Documentation at: https://www.ipqualityscore.com/user/proxy-detection-api/documentation (Requires login - FREE) 18 | - Provides an ISP Name 19 | - Provides a yes/no: Proxy, VPN, Mobile 20 | 21 | ## IPHub 22 | 23 | - API Documentation at: https://iphub.info/api 24 | - Returns a "block number", or connection type. 25 | - Block 0: Residential or unknown 26 | - Block 1: Non-residential (e.g. webhost / proxy / vpn ) 27 | 28 | ## Teoh.io 29 | 30 | - API Documentation at: https://ip.teoh.io/vpn-proxy-api 31 | - Returns a yes/no: Hosting, Proxy 32 | - Returns a risk factor (Low, Medium, High, etc) 33 | - Returns an ISP type ( isp, Hosting/Datacenter, etc ) 34 | - Teoh has generously donated an API key to us 35 | 36 | ## IPHunter 37 | 38 | - API Documentation at: https://www.iphunter.info/api 39 | - Provides an ISP Name 40 | - Returns a "block number", or connection type. 41 | - Block 0: Residential or unknown 42 | - Block 1: Non-residential (e.g. webhost / proxy / vpn ) 43 | 44 | ## NoFraud.co 45 | 46 | - API Documentation at: https://nofraud.co/v1/api.php 47 | - Gives a 0-100% chance of the IP in question being a proxy/VPN 48 | 49 | ## Compute hosts 50 | 51 | - Detects the big 3: Google Cloud, Amazon AWS, and Microsoft Azure 52 | - A hit here is a gauranteed colocation webhost. 53 | - Amazon detection via an easily accessible JSON file: https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html 54 | - Microsoft Azure detection via XML. File location must be scraped from: https://www.microsoft.com/en-us/download/details.aspx?id=41653 55 | - Google Cloud detection via convoluted DNS queries: https://cloud.google.com/compute/docs/faq#find_ip_range 56 | - Google cloud does not appear to list all of it's IP's via the officially supported method. I have manually hardcoded several ranges that I have found as a long-time google cloud customer. 57 | 58 | ## SORBS 59 | 60 | - DNSBL Documentation at: http://www.sorbs.net/general/using.shtml 61 | - The "Spam and Open Relay Blocking System" 62 | - SORBS blacklists a variety of spam sources. 63 | - The main results to be interested in are: 64 | - 127.0.0.2 (HTTP Proxy) 65 | - 127.0.0.2 (SOCKS Proxy) 66 | - 127.0.0.2 (Misc Proxy) 67 | - Other results may help determine if a host is compromised. Many spam sources are compromised hosts or open proxies. 68 | 69 | ## Spamhaus 70 | 71 | - DNSBL Documentation at: https://www.spamhaus.org/zen/ 72 | - Very similar to SORBS 73 | - Results will normally have a link to a report explaining why the IP is listed 74 | - Primarily interesting results are 127.0.0.2, and 127.0.0.4-7. 75 | - 127.0.0.4-7 can be a strong indicator of a compromised host, or a proxy. See the linked report for more information. 76 | - 127.0.0.2 indicates that a host is sending, or has sent spam email. Many spam sources are compromised hosts or open proxies. 77 | 78 | ## DShield / internet storm center 79 | 80 | - API Documentation at: https://dshield.org/api/ 81 | - This is a more advanced/experimental feed, and requires additional manual research 82 | - Attacks: Number of attacks originating from this IP 83 | - This number should not be taken as 100% accurate - see: https://en.wikipedia.org/wiki/Denial-of-service_attack#Backscatter 84 | - Threatfeeds: Threat feeds that this IP appears in, as well as the last time that this IP was seen. 85 | - Google the threat feed name for more information. 86 | - Many will allow a lookup of the IP in question 87 | 88 | ## Hola 89 | 90 | - Confirmed Hola VPN Nodes 91 | - Hola VPN is a peer-to-peer free VPN application 92 | - See: https://en.wikipedia.org/wiki/Denial-of-service_attack#Backscatter 93 | - Hola nodes often look like residential / non-proxy connections 94 | - There are two detection methods, both are 100% accurate, and are evidence of a recent Hola node. 95 | - I am unable to publicly disclose my detection methods at this time, as this might encourage Hola to make changes to avoid detection 96 | - Results will indicate the last time that the IP was seen, and by which method. 97 | 98 | ## Cache 99 | 100 | - As we have a finite amount of resources, most results are cached. 101 | - More information at: https://en.wikipedia.org/wiki/Cache_(computing) 102 | - This will indicate a yes/no as to wether the results were cached, as well as when they were cached, and when they will expire. 103 | 104 | ## Port Scan 105 | 106 | * NO SCANS ARE MADE FROM TOOLFORGE. 107 | - For performance reasons, this check is not enabled by default. 108 | - More information on ports can be found at: https://en.wikipedia.org/wiki/Port_scanner and https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers 109 | - The tool checks the most common 1221 proxy ports, and returns if they are open or not. 110 | - This is a fairly advanced feed technique that often requires experience in interperting this sort of result. 111 | - Common indicators: 112 | - 1723: VPN: Not 100% evidence of an open proxy in and of itself. Some residential users (myself included) run a VPN out of the home to connect to various resources (cameras, home automation, etc) 113 | - 1080, 8080: Often a proxy server 114 | - 25, 109. 110, 585, 587, 993: Often evidence of a mail server. Most residential ISP's dissalow this sort of server, and explicitly block it. 115 | - 53: DNS - Domain name server. This service translates domain names into IP addresses. Uncommon on residential connections. 116 | - 80, 443: Web servers. This should be uncommon on residential connections, but is becoming more common. Often times on residential connections, it is a router configuration page 117 | - 21: FTP: File Transfer Protocol, Commonly used to upload / download files from a host. Becoming less common with SFTP operating on port 22. Can be an indicator of a webhost with other factors, but is not uncommon on residential connections 118 | - 22: SSH / SFTP: Typically a remote commandline used to configure and control linux/unix boxes. Traffic can be tunneled over SSH just like with a VPN / Proxy, and this can be an indicator of a webhost with other factors (behavioral, and other indicators listed above). 119 | - Further investigation: 120 | - nmap can be helpful. 121 | - If you want to know what's running on a port, you can use: 122 | - nmap -sV -p (port number, range, or numbers - comma seperated) (IP) 123 | - e.g.: nmap -sV -p 1-1000,1080,8080,923 127.0.0.1 124 | - Googling the port number can also be helpful 125 | - Port scans alone are rarely useful evidence of an open proxy. 126 | - Commonly used to confirm theories based on behavioral evidence 127 | 128 | ## Other useful tools 129 | 130 | - IPRange: https://tools.wmflabs.org/ipcheck/iprange.php 131 | - Resolves an IP range 132 | - Often useful to look for nearby hosts with names like "mail.example.com" or "www.foo.bar". 133 | - This can be very helpful in mapping host ranges, such as when mixed with residential ranges (looking at you, OVH) 134 | - ISP Rangefinder: https://tools.wmflabs.org/isprangefinder/index.php 135 | - Attempts to find all IP ranges owned by a specific ISP 136 | - Provides links to block / unblock each range 137 | - Hurricane BGP Toolkit: https://bgp.he.net/ 138 | - Similar to ISP Rangefinder, but can help catch some hosts that it misses 139 | - Can be given an IP range, and be used to find more hosts 140 | - NativeForeigners range calculator: https://nativeforeigner.com/calc/ 141 | - Davidc's subnet calculator: http://www.davidc.net/sites/default/subnets/subnets.html 142 | - I use this a lot to split ranges 143 | 144 | ## Addittions or errors 145 | 146 | * Everything above is based on my own personal experience, and may be completely wrong! 147 | - Please, feel free to leave a message for me at https://en.wikipedia.org/wiki/User_talk:SQL 148 | - Or, issue a pull request with changes 149 | 150 | -------------------------------------------------------------------------------- /views/results.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | {% import _self as helper %} 3 | 4 | {% macro boolValue(val) %} 5 | {% if val is same as(true) %} {# data.result === true, not just truthy #} 6 | 7 | 8 | 9 | {% elseif val is same as(false) %} 10 | 11 | 12 | 13 | {% else %} 14 | No results. 15 | {% endif %} 16 | {% endmacro %} 17 | 18 | {% macro blockValue(val) %} 19 | {% import _self as helper %} 20 | {% if val == 0 %} 21 | {{ helper.boolValue(false) }} Residential/Unclassified IP (i.e. safe IP) 22 | {% elseif val == 1 %} 23 | {{ helper.boolValue(true) }} Non-residential IP (hosting provider, proxy, etc.) 24 | {% else %} 25 | Non-residential & residential IP (warning, may flag innocent people) 26 | {% endif %} 27 | {% endmacro %} 28 | 29 | {% block results %} 30 |
31 |
32 |
33 |

34 | {{ hostname }} 35 |

36 | ( whois | 37 | {%if ipbase != "" %} 38 | resolve range | 39 | {% endif %} 40 | bgp.he.net | talos | block log | active blocks | global blocks | contribs | filter log | block | block globally ) 41 |
42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {% for serviceId, data in out %} 52 | 53 | 54 | 243 | 244 | {% endfor %} 245 | 246 |
ServiceResult
{{ data.title }} 55 | {## 56 | # TODO: This is all a bit messy... At some point we'll want to localize the application. 57 | # When we do this, the different keys in `data` can correspond to the i18n message key. 58 | # This way, we can refactor all the helper.boolValue stuff, since they're all the same, 59 | # and do something like: 60 | # {% foreach key in ['proxy', 'vpn', 'mobile', ...] %} 61 | # {% if data.result[key] is defined %} 62 | # {{ msg(key) }}: {{ helper.boolValue(data.result[key]) }} 63 | # {% endif %} 64 | # {% endfor %} 65 | #} 66 | {% if data.result is iterable and data.result is not empty %} 67 | {## Things that don't use boolValue() (e.g. no tick or cross) go here #} 68 | {% if data.result.isp is defined %} 69 | ISP: {{ data.result.isp }}
70 | {% endif %} 71 | {% if data.result.webhost is defined %} 72 | {% if data.result.webhost != false %} 73 | Likely webhost: {{ data.result.webhost }}
74 | {% else %} 75 | Not a known hosting ASN 76 | {% endif %} 77 | {% endif %} 78 | {% if data.result.cached is defined %} 79 | {% if data.result.cached == "yes" %} 80 | Cached: {{ data.result.cached }} Refresh
81 | {% endif %} 82 | {% if data.result.cached == "no" %} 83 | Cached: {{ data.result.cached }}
84 | {% endif %} 85 | {% endif %} 86 | {% if data.result.city is defined %} 87 | City: {{ data.result.city }}
88 | {% endif %} 89 | {% if data.result.state is defined %} 90 | State: {{ data.result.state }}
91 | {% endif %} 92 | {% if data.result.zip is defined %} 93 | Zip: {{ data.result.zip }}
94 | {% endif %} 95 | {% if data.result.country is defined %} 96 | Country: {{ data.result.country }}
97 | {% endif %} 98 | {% if data.result.appears is defined %} 99 | {% if data.result.appears == 1 %} 100 | Listed, see details
101 | Frequency: {{ data.result.frequency }}
102 | Last Seen: {{ data.result.lastseen }}
103 | Confidence: {{ data.result.confidence }}
104 | Country: {{ data.result.sfscountry }}
105 | Delegated: {{ data.result.delegated }} 106 | {% else %} 107 | Not listed 108 | {% endif %} 109 | {% endif %} 110 | {% if data.result.attacks is defined %} 111 | Attacks seen: {{ data.result.attacks }}
112 | {% endif %} 113 | 114 | {% if data.result.tfeeds is defined %} 115 | Appears in threat feeds: {{ data.result.tfeeds }}
116 | {% endif %} 117 | 118 | {% if data.result.cachedate is defined %} 119 | Retreived: {{ data.result.cachedate }}
120 | {% endif %} 121 | 122 | {% if data.result.cacheuntil is defined %} 123 | Cached until: {{ data.result.cacheuntil }}
124 | {% endif %} 125 | 126 | {% if data.result.pctype is defined %} 127 | Type: {{ data.result.pctype }}
128 | {% endif %} 129 | {% if data.result.riska is defined %} 130 | Risk Assessment: {{ data.result.riska }}
131 | {% endif %} 132 | 133 | {% if data.result.port is defined %} 134 | Port: {{ data.result.port }}
135 | {% endif %} 136 | 137 | {% if data.result.seen is defined %} 138 | Last Seen: {{ data.result.seen }}
139 | {% endif %} 140 | 141 | {% if data.result.cloud is defined %} 142 | {{ data.result.cloud }}
143 | {% endif %} 144 | 145 | {% if data.cache is defined %} 146 | Cached: {{ data.cache }}
147 | {% endif %} 148 | {% if data.result.listed is defined %} 149 | {{ helper.boolValue( data.result.listed ) }} 150 | {% if data.result.listed == TRUE %} 151 | Listed
152 | {% else %} 153 | Not Listed
154 | {% endif %} 155 | {% endif %} 156 | {% if data.result.tornode is defined %} 157 | {{ helper.boolValue(data.result.tornode) }} TOR Node
158 | {% endif %} 159 | {% if data.result.chance %} 160 | {% if data.result.chance > 90 %} 161 | {{ helper.boolValue(true) }} 162 | {% elseif data.result.chance < 20 %} 163 | {{ helper.boolValue(false) }} 164 | {% endif %} 165 | Prediction: {{ data.result.chance }}%
166 | {% endif %} 167 | {% if data.result.proxy is defined %} 168 | {{ helper.boolValue(data.result.proxy) }} Proxy
169 | {% endif %} 170 | {% if data.result.vpn is defined %} 171 | {{ helper.boolValue(data.result.vpn) }} VPN
172 | {% endif %} 173 | {% if data.result.mobile is defined %} 174 | {{ helper.boolValue(data.result.mobile) }} Mobile
175 | {% endif %} 176 | {% if data.result.recent_abuse is defined %} 177 | {{ helper.boolValue(data.result.recent_abuse) }} Recent Abuse
178 | {% endif %} 179 | {% if data.result.tor is defined %} 180 | {{ helper.boolValue(data.result.tor) }} Tor
181 | {% endif %} 182 | {% if data.result.bot_status is defined %} 183 | {{ helper.boolValue(data.result.bot_status) }} Bot
184 | {% endif %} 185 | {% if data.result.fraud_score is defined %} 186 | Fraud Score: {{ data.result.fraud_score }}
187 | {% endif %} 188 | {% if data.result.organization is defined %} 189 | Organization: {{ data.result.organization }}
190 | {% endif %} 191 | {% if data.result.hosting is defined %} 192 | {{ helper.boolValue(data.result.hosting) }} Hosting
193 | {% endif %} 194 | {% if data.result.vpnOrProxy is defined %} 195 | {{ helper.boolValue(data.result.vpnOrProxy) }} VPN or proxy
196 | {% endif %} 197 | {% if data.result.risk is defined %} 198 | {% if data.result.risk == 'high' %} 199 | {{ helper.boolValue(true) }} 200 | {% endif %} 201 | Risk: {{ data.result.risk }}
202 | {% endif %} 203 | 204 | {## "Block" is sort of like a "type", so presumably there can't be both. #} 205 | {% if data.result.block is defined %} 206 | {{ helper.blockValue(data.result.block) }} 207 | {% elseif data.result.type %} 208 | Type: {{ data.result.type }} 209 | {% endif %} 210 | 211 | {## Special format for Hola (method 1) #} 212 | {% for hola in data.result.holas %} 213 | {% if hola.port is defined %} 214 | Port: {{ hola.port }}
215 | {% endif %} 216 | {% if hola.country is defined %} 217 | Country: {{ hola.country|upper }}
218 | {% endif %} 219 | {% if hola.seen is defined %} 220 | Last seen: {{ hola.seen }}
221 | {% endif %} 222 | {% endfor %} 223 | 224 | {## 'entries' are just strings. #} 225 | {% if data.result.entries is defined %} 226 | {% if data.result.entries is empty %} 227 | No results 228 | {% else %} 229 | {% for entry in data.result.entries %} 230 | {{ entry|raw }}
231 | {% endfor %} 232 | {% endif %} 233 | {% endif %} 234 | {% elseif data.error is defined %} 235 | 236 | Error: 237 | {{ data.error is same as(true) ? 'Uknown error. Bad IP maybe?' : data.error }} 238 | 239 | {% else %} 240 | {{ helper.boolValue(data.result) }} 241 | {% endif %} 242 |
247 |
248 |
249 | {% endblock %} 250 | -------------------------------------------------------------------------------- /public_html/oauth.php: -------------------------------------------------------------------------------- 1 | for a copy of the 10 | * CC0 Public Domain Dedication. 11 | */ 12 | 13 | // ******************** CONFIGURATION ******************** 14 | 15 | /** 16 | * Set this to point to a file (outside the webserver root!) containing the 17 | * following keys: 18 | * - agent: The HTTP User-Agent to use 19 | * - consumerKey: The "consumer token" given to you when registering your app 20 | * - consumerSecret: The "secret token" given to you when registering your app 21 | */ 22 | $inifile = __DIR__ . '/../oauth.ini'; 23 | 24 | /** 25 | * Set this to the Special:OAuth/authorize URL. 26 | * To work around MobileFrontend redirection, use /wiki/ rather than /w/index.php. 27 | */ 28 | $mwOAuthAuthorizeUrl = 'https://meta.wikimedia.org/wiki/Special:OAuth/authorize'; 29 | // 30 | /** 31 | * Set this to the Special:OAuth URL. 32 | * Note that /wiki/Special:OAuth fails when checking the signature, while 33 | * index.php?title=Special:OAuth works fine. 34 | */ 35 | //Set this to the wiki you are checking 36 | // Disable for production 37 | //$mwOAuthUrl = 'https://en.wikipedia.beta.wmflabs.org/w/index.php?title=Special:OAuth'; 38 | 39 | /** 40 | * Set this to the interwiki prefix for the OAuth central wiki. 41 | */ 42 | $mwOAuthIW = 'meta'; 43 | 44 | 45 | /** 46 | * This should normally be "500". But Tool Labs insists on overriding valid 500 47 | * responses with a useless error page. 48 | */ 49 | $errorCode = 200; 50 | 51 | // ****************** END CONFIGURATION ****************** 52 | 53 | // Setup the session cookie 54 | session_name( 'IPCheck' ); 55 | $params = session_get_cookie_params(); 56 | session_set_cookie_params( 57 | $params['lifetime'], 58 | dirname( $_SERVER['SCRIPT_NAME'] ) 59 | ); 60 | 61 | 62 | // Read the ini file 63 | $ini = parse_ini_file( $inifile ); 64 | if ( $ini === false ) { 65 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 66 | echo 'The ini file could not be read'; 67 | exit(0); 68 | } 69 | if ( !isset( $ini['agent'] ) || 70 | !isset( $ini['consumerKey'] ) || 71 | !isset( $ini['consumerSecret'] ) 72 | ) { 73 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 74 | echo 'Required configuration directives not found in ini file'; 75 | exit(0); 76 | } 77 | $gUserAgent = $ini['agent']; 78 | $gConsumerKey = $ini['consumerKey']; 79 | $gConsumerSecret = $ini['consumerSecret']; 80 | 81 | // Load the user token (request or access) from the session 82 | $gTokenKey = ''; 83 | $gTokenSecret = ''; 84 | session_start(); 85 | if( @isset( $persist_ip ) === TRUE ) { 86 | $_SESSION['ip'] = $persist_ip; 87 | } 88 | if( isset( $_SESSION['mwOAuthUrl'] ) ) { 89 | $mwOAuthUrl = $_SESSION['mwOAuthUrl']; 90 | } 91 | 92 | $_SESSION['wiki'] = 'enwiki'; 93 | $wiki = 'enwiki'; 94 | 95 | $mwOAuthUrl = 'https://meta.wikimedia.org/w/index.php?title=Special:OAuth'; 96 | $_SESSION['mwOAuthUrl'] = $mwOAuthUrl; 97 | 98 | $wiki = $_SESSION['wiki']; 99 | if ( isset( $_SESSION['tokenKey'] ) ) { 100 | $gTokenKey = $_SESSION['tokenKey']; 101 | $gTokenSecret = $_SESSION['tokenSecret']; 102 | } 103 | 104 | 105 | 106 | // Fetch the access token if this is the callback from requesting authorization 107 | if ( isset( $_GET['oauth_verifier'] ) && $_GET['oauth_verifier'] ) { 108 | $mwOAuthUrl = 'https://meta.wikimedia.org/w/index.php?title=Special:OAuth'; 109 | fetchAccessToken(); 110 | session_write_close(); 111 | header('Location: index.php'); 112 | } 113 | 114 | // Take any requested action 115 | switch ( isset( $_GET['action'] ) ? $_GET['action'] : '' ) { 116 | 117 | case 'authorize': 118 | doAuthorizationRedirect(); 119 | return; 120 | 121 | case 'identify': 122 | doIdentify(); 123 | break; 124 | 125 | } 126 | $identity = doIdentify( ); 127 | if( $identity !== FALSE ) { 128 | $username = $identity->username; 129 | $editcount = $identity->editcount; 130 | $registration = $identity->registered; 131 | $blocked = $identity->blocked; 132 | if( isset( $_SESSION['ip'] ) ) { $theip = $_SESSION['ip']; unset( $_SESSION['ip'] ); } 133 | session_write_close(); 134 | } else { session_write_close(); die(); } 135 | 136 | // ******************** CODE ******************** 137 | 138 | 139 | /** 140 | * Utility function to sign a request 141 | * 142 | * Note this doesn't properly handle the case where a parameter is set both in 143 | * the query string in $url and in $params, or non-scalar values in $params. 144 | * 145 | * @param string $method Generally "GET" or "POST" 146 | * @param string $url URL string 147 | * @param array $params Extra parameters for the Authorization header or post 148 | * data (if application/x-www-form-urlencoded). 149 | * @return string Signature 150 | */ 151 | function sign_request( $method, $url, $params = array() ) { 152 | global $gConsumerSecret, $gTokenSecret; 153 | 154 | $parts = parse_url( $url ); 155 | 156 | // We need to normalize the endpoint URL 157 | $scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'http'; 158 | $host = isset( $parts['host'] ) ? $parts['host'] : ''; 159 | $port = isset( $parts['port'] ) ? $parts['port'] : ( $scheme == 'https' ? '443' : '80' ); 160 | $path = isset( $parts['path'] ) ? $parts['path'] : ''; 161 | if ( ( $scheme == 'https' && $port != '443' ) || 162 | ( $scheme == 'http' && $port != '80' ) 163 | ) { 164 | // Only include the port if it's not the default 165 | $host = "$host:$port"; 166 | } 167 | 168 | // Also the parameters 169 | $pairs = array(); 170 | parse_str( isset( $parts['query'] ) ? $parts['query'] : '', $query ); 171 | $query += $params; 172 | unset( $query['oauth_signature'] ); 173 | if ( $query ) { 174 | $query = array_combine( 175 | // rawurlencode follows RFC 3986 since PHP 5.3 176 | array_map( 'rawurlencode', array_keys( $query ) ), 177 | array_map( 'rawurlencode', array_values( $query ) ) 178 | ); 179 | ksort( $query, SORT_STRING ); 180 | foreach ( $query as $k => $v ) { 181 | $pairs[] = "$k=$v"; 182 | } 183 | } 184 | 185 | $toSign = rawurlencode( strtoupper( $method ) ) . '&' . 186 | rawurlencode( "$scheme://$host$path" ) . '&' . 187 | rawurlencode( join( '&', $pairs ) ); 188 | $key = rawurlencode( $gConsumerSecret ) . '&' . rawurlencode( $gTokenSecret ); 189 | return base64_encode( hash_hmac( 'sha1', $toSign, $key, true ) ); 190 | } 191 | 192 | /** 193 | * Request authorization 194 | * @return void 195 | */ 196 | function doAuthorizationRedirect() { 197 | global $mwOAuthUrl, $mwOAuthAuthorizeUrl, $gUserAgent, $gConsumerKey, $gTokenSecret, $errorCode; 198 | 199 | // First, we need to fetch a request token. 200 | // The request is signed with an empty token secret and no token key. 201 | $gTokenSecret = ''; 202 | $url = $mwOAuthUrl . '/initiate'; 203 | $url .= strpos( $url, '?' ) ? '&' : '?'; 204 | $url .= http_build_query( array( 205 | 'format' => 'json', 206 | 207 | // OAuth information 208 | 'oauth_callback' => 'oob', // Must be "oob" or something prefixed by the configured callback URL 209 | 'oauth_consumer_key' => $gConsumerKey, 210 | 'oauth_version' => '1.0', 211 | 'oauth_nonce' => md5( microtime() . mt_rand() ), 212 | 'oauth_timestamp' => time(), 213 | 214 | // We're using secret key signatures here. 215 | 'oauth_signature_method' => 'HMAC-SHA1', 216 | ) ); 217 | $signature = sign_request( 'GET', $url ); 218 | $url .= "&oauth_signature=" . urlencode( $signature ); 219 | $ch = curl_init(); 220 | //TEST111 echo "$url\n"; 221 | curl_setopt( $ch, CURLOPT_URL, $url ); 222 | //curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); 223 | curl_setopt( $ch, CURLOPT_USERAGENT, $gUserAgent ); 224 | curl_setopt( $ch, CURLOPT_HEADER, 0 ); 225 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); 226 | $data = curl_exec( $ch ); 227 | if ( !$data ) { 228 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 229 | echo 'Curl error (doAuthorizationRedirect): ' . htmlspecialchars( curl_error( $ch ) ); 230 | exit(0); 231 | } 232 | curl_close( $ch ); 233 | $token = json_decode( $data ); 234 | if ( is_object( $token ) && isset( $token->error ) ) { 235 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 236 | echo 'Error retrieving token: ' . htmlspecialchars( $token->error ) . '
' . htmlspecialchars( $token->message ); 237 | exit(0); 238 | } 239 | if ( !is_object( $token ) || !isset( $token->key ) || !isset( $token->secret ) ) { 240 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 241 | echo 'Invalid response from token request'; 242 | exit(0); 243 | } 244 | 245 | // Now we have the request token, we need to save it for later. 246 | session_start(); 247 | $_SESSION['tokenKey'] = $token->key; 248 | $_SESSION['tokenSecret'] = $token->secret; 249 | session_write_close(); 250 | 251 | // Then we send the user off to authorize 252 | $url = $mwOAuthAuthorizeUrl; 253 | $url .= strpos( $url, '?' ) ? '&' : '?'; 254 | $url .= http_build_query( array( 255 | 'oauth_token' => $token->key, 256 | 'oauth_consumer_key' => $gConsumerKey, 257 | ) ); 258 | header( "Location: $url" ); 259 | echo 'Please see ' . htmlspecialchars( $url ) . ''; 260 | } 261 | 262 | /** 263 | * Handle a callback to fetch the access token 264 | * @return void 265 | */ 266 | function fetchAccessToken() { 267 | global $mwOAuthUrl, $gUserAgent, $gConsumerKey, $gTokenKey, $gTokenSecret, $errorCode; 268 | 269 | $url = $mwOAuthUrl . '/token'; 270 | $url .= strpos( $url, '?' ) ? '&' : '?'; 271 | $url .= http_build_query( array( 272 | 'format' => 'json', 273 | 'oauth_verifier' => $_GET['oauth_verifier'], 274 | 275 | // OAuth information 276 | 'oauth_consumer_key' => $gConsumerKey, 277 | 'oauth_token' => $gTokenKey, 278 | 'oauth_version' => '1.0', 279 | 'oauth_nonce' => md5( microtime() . mt_rand() ), 280 | 'oauth_timestamp' => time(), 281 | 282 | // We're using secret key signatures here. 283 | 'oauth_signature_method' => 'HMAC-SHA1', 284 | ) ); 285 | $signature = sign_request( 'GET', $url ); 286 | $url .= "&oauth_signature=" . urlencode( $signature ); 287 | $ch = curl_init(); 288 | curl_setopt( $ch, CURLOPT_URL, $url ); 289 | //curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); 290 | curl_setopt( $ch, CURLOPT_USERAGENT, $gUserAgent ); 291 | curl_setopt( $ch, CURLOPT_HEADER, 0 ); 292 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); 293 | $data = curl_exec( $ch ); 294 | if ( !$data ) { 295 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 296 | echo 'Curl error (fetchAccessToken): ' . htmlspecialchars( curl_error( $ch ) ); 297 | exit(0); 298 | } 299 | curl_close( $ch ); 300 | $token = json_decode( $data ); 301 | if ( is_object( $token ) && isset( $token->error ) ) { 302 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 303 | echo 'Error retrieving token: ' . htmlspecialchars( $token->error ) . '
' . htmlspecialchars( $token->message ); 304 | exit(0); 305 | } 306 | if ( !is_object( $token ) || !isset( $token->key ) || !isset( $token->secret ) ) { 307 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 308 | echo 'Invalid response from token request'; 309 | exit(0); 310 | } 311 | 312 | // Save the access token 313 | session_start(); 314 | $_SESSION['tokenKey'] = $gTokenKey = $token->key; 315 | $_SESSION['tokenSecret'] = $gTokenSecret = $token->secret; 316 | session_write_close(); 317 | } 318 | 319 | /** 320 | * Request a JWT and verify it 321 | * @return void 322 | */ 323 | function doIdentify() { 324 | global $mwOAuthUrl, $gUserAgent, $gConsumerKey, $gTokenKey, $gConsumerSecret, $errorCode; 325 | 326 | $url = $mwOAuthUrl . '/identify'; 327 | $headerArr = array( 328 | // OAuth information 329 | 'oauth_consumer_key' => $gConsumerKey, 330 | 'oauth_token' => $gTokenKey, 331 | 'oauth_version' => '1.0', 332 | 'oauth_nonce' => md5( microtime() . mt_rand() ), 333 | 'oauth_timestamp' => time(), 334 | 335 | // We're using secret key signatures here. 336 | 'oauth_signature_method' => 'HMAC-SHA1', 337 | ); 338 | $signature = sign_request( 'GET', $url, $headerArr ); 339 | $headerArr['oauth_signature'] = $signature; 340 | 341 | $header = array(); 342 | foreach ( $headerArr as $k => $v ) { 343 | $header[] = rawurlencode( $k ) . '="' . rawurlencode( $v ) . '"'; 344 | } 345 | $header = 'Authorization: OAuth ' . join( ', ', $header ); 346 | 347 | $ch = curl_init(); 348 | 349 | curl_setopt( $ch, CURLOPT_URL, $url ); 350 | curl_setopt( $ch, CURLOPT_HTTPHEADER, array( $header ) ); 351 | //curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); 352 | curl_setopt( $ch, CURLOPT_USERAGENT, $gUserAgent ); 353 | curl_setopt( $ch, CURLOPT_HEADER, 0 ); 354 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); 355 | $data = curl_exec( $ch ); 356 | if ( !$data ) { 357 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 358 | echo 'Curl error (doIdentify): ' . htmlspecialchars( curl_error( $ch ) ); 359 | exit(0); 360 | } 361 | $err = json_decode( $data ); 362 | if ( is_object( $err ) && isset( $err->error ) && $err->error === 'mwoauthdatastore-access-token-not-found' ) { 363 | // We're not authorized! 364 | //echo 'You haven\'t authorized this application yet! Go here to do that.'; 365 | header('Location: ' . htmlspecialchars( $_SERVER['SCRIPT_NAME'] ) . '?action=authorize'); 366 | return false; 367 | } 368 | 369 | // There are three fields in the response 370 | $fields = explode( '.', $data ); 371 | if ( count( $fields ) !== 3 ) { 372 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 373 | echo 'Invalid identify response: ' . htmlspecialchars( $data ); 374 | exit(0); 375 | } 376 | 377 | // Validate the header. MWOAuth always returns alg "HS256". 378 | $header = base64_decode( strtr( $fields[0], '-_', '+/' ), true ); 379 | if ( $header !== false ) { 380 | $header = json_decode( $header ); 381 | } 382 | if ( !is_object( $header ) || $header->typ !== 'JWT' || $header->alg !== 'HS256' ) { 383 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 384 | echo 'Invalid header in identify response: ' . htmlspecialchars( $data ); 385 | exit(0); 386 | } 387 | 388 | // Verify the signature 389 | $sig = base64_decode( strtr( $fields[2], '-_', '+/' ), true ); 390 | $check = hash_hmac( 'sha256', $fields[0] . '.' . $fields[1], $gConsumerSecret, true ); 391 | if ( $sig !== $check ) { 392 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 393 | echo 'JWT signature validation failed: ' . htmlspecialchars( $data ); 394 | echo '
'; var_dump( base64_encode($sig), base64_encode($check) ); echo '
'; 395 | exit(0); 396 | } 397 | 398 | // Decode the payload 399 | $payload = base64_decode( strtr( $fields[1], '-_', '+/' ), true ); 400 | if ( $payload !== false ) { 401 | $payload = json_decode( $payload ); 402 | } 403 | if ( !is_object( $payload ) ) { 404 | header( "HTTP/1.1 $errorCode Internal Server Error" ); 405 | echo 'Invalid payload in identify response: ' . htmlspecialchars( $data ); 406 | exit(0); 407 | } 408 | 409 | //echo 'JWT payload:
' . htmlspecialchars( var_export( $payload, 1 ) ) . '
'; 410 | //echo '
'; 411 | return( $payload ); 412 | } 413 | 414 | ?> 415 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "0935197568500069a68b10152053600a", 8 | "packages": [ 9 | { 10 | "name": "guzzlehttp/guzzle", 11 | "version": "6.3.3", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/guzzle/guzzle.git", 15 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", 20 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "guzzlehttp/promises": "^1.0", 25 | "guzzlehttp/psr7": "^1.4", 26 | "php": ">=5.5" 27 | }, 28 | "require-dev": { 29 | "ext-curl": "*", 30 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", 31 | "psr/log": "^1.0" 32 | }, 33 | "suggest": { 34 | "psr/log": "Required for using the Log middleware" 35 | }, 36 | "type": "library", 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "6.3-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "files": [ 44 | "src/functions_include.php" 45 | ], 46 | "psr-4": { 47 | "GuzzleHttp\\": "src/" 48 | } 49 | }, 50 | "notification-url": "https://packagist.org/downloads/", 51 | "license": [ 52 | "MIT" 53 | ], 54 | "authors": [ 55 | { 56 | "name": "Michael Dowling", 57 | "email": "mtdowling@gmail.com", 58 | "homepage": "https://github.com/mtdowling" 59 | } 60 | ], 61 | "description": "Guzzle is a PHP HTTP client library", 62 | "homepage": "http://guzzlephp.org/", 63 | "keywords": [ 64 | "client", 65 | "curl", 66 | "framework", 67 | "http", 68 | "http client", 69 | "rest", 70 | "web service" 71 | ], 72 | "time": "2018-04-22T15:46:56+00:00" 73 | }, 74 | { 75 | "name": "guzzlehttp/promises", 76 | "version": "v1.3.1", 77 | "source": { 78 | "type": "git", 79 | "url": "https://github.com/guzzle/promises.git", 80 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" 81 | }, 82 | "dist": { 83 | "type": "zip", 84 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", 85 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", 86 | "shasum": "" 87 | }, 88 | "require": { 89 | "php": ">=5.5.0" 90 | }, 91 | "require-dev": { 92 | "phpunit/phpunit": "^4.0" 93 | }, 94 | "type": "library", 95 | "extra": { 96 | "branch-alias": { 97 | "dev-master": "1.4-dev" 98 | } 99 | }, 100 | "autoload": { 101 | "psr-4": { 102 | "GuzzleHttp\\Promise\\": "src/" 103 | }, 104 | "files": [ 105 | "src/functions_include.php" 106 | ] 107 | }, 108 | "notification-url": "https://packagist.org/downloads/", 109 | "license": [ 110 | "MIT" 111 | ], 112 | "authors": [ 113 | { 114 | "name": "Michael Dowling", 115 | "email": "mtdowling@gmail.com", 116 | "homepage": "https://github.com/mtdowling" 117 | } 118 | ], 119 | "description": "Guzzle promises library", 120 | "keywords": [ 121 | "promise" 122 | ], 123 | "time": "2016-12-20T10:07:11+00:00" 124 | }, 125 | { 126 | "name": "guzzlehttp/psr7", 127 | "version": "1.5.2", 128 | "source": { 129 | "type": "git", 130 | "url": "https://github.com/guzzle/psr7.git", 131 | "reference": "9f83dded91781a01c63574e387eaa769be769115" 132 | }, 133 | "dist": { 134 | "type": "zip", 135 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", 136 | "reference": "9f83dded91781a01c63574e387eaa769be769115", 137 | "shasum": "" 138 | }, 139 | "require": { 140 | "php": ">=5.4.0", 141 | "psr/http-message": "~1.0", 142 | "ralouphie/getallheaders": "^2.0.5" 143 | }, 144 | "provide": { 145 | "psr/http-message-implementation": "1.0" 146 | }, 147 | "require-dev": { 148 | "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "1.5-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "psr-4": { 158 | "GuzzleHttp\\Psr7\\": "src/" 159 | }, 160 | "files": [ 161 | "src/functions_include.php" 162 | ] 163 | }, 164 | "notification-url": "https://packagist.org/downloads/", 165 | "license": [ 166 | "MIT" 167 | ], 168 | "authors": [ 169 | { 170 | "name": "Michael Dowling", 171 | "email": "mtdowling@gmail.com", 172 | "homepage": "https://github.com/mtdowling" 173 | }, 174 | { 175 | "name": "Tobias Schultze", 176 | "homepage": "https://github.com/Tobion" 177 | } 178 | ], 179 | "description": "PSR-7 message implementation that also provides common utility methods", 180 | "keywords": [ 181 | "http", 182 | "message", 183 | "psr-7", 184 | "request", 185 | "response", 186 | "stream", 187 | "uri", 188 | "url" 189 | ], 190 | "time": "2018-12-04T20:46:45+00:00" 191 | }, 192 | { 193 | "name": "psr/http-message", 194 | "version": "1.0.1", 195 | "source": { 196 | "type": "git", 197 | "url": "https://github.com/php-fig/http-message.git", 198 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 199 | }, 200 | "dist": { 201 | "type": "zip", 202 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 203 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 204 | "shasum": "" 205 | }, 206 | "require": { 207 | "php": ">=5.3.0" 208 | }, 209 | "type": "library", 210 | "extra": { 211 | "branch-alias": { 212 | "dev-master": "1.0.x-dev" 213 | } 214 | }, 215 | "autoload": { 216 | "psr-4": { 217 | "Psr\\Http\\Message\\": "src/" 218 | } 219 | }, 220 | "notification-url": "https://packagist.org/downloads/", 221 | "license": [ 222 | "MIT" 223 | ], 224 | "authors": [ 225 | { 226 | "name": "PHP-FIG", 227 | "homepage": "http://www.php-fig.org/" 228 | } 229 | ], 230 | "description": "Common interface for HTTP messages", 231 | "homepage": "https://github.com/php-fig/http-message", 232 | "keywords": [ 233 | "http", 234 | "http-message", 235 | "psr", 236 | "psr-7", 237 | "request", 238 | "response" 239 | ], 240 | "time": "2016-08-06T14:39:51+00:00" 241 | }, 242 | { 243 | "name": "ralouphie/getallheaders", 244 | "version": "2.0.5", 245 | "source": { 246 | "type": "git", 247 | "url": "https://github.com/ralouphie/getallheaders.git", 248 | "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" 249 | }, 250 | "dist": { 251 | "type": "zip", 252 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", 253 | "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", 254 | "shasum": "" 255 | }, 256 | "require": { 257 | "php": ">=5.3" 258 | }, 259 | "require-dev": { 260 | "phpunit/phpunit": "~3.7.0", 261 | "satooshi/php-coveralls": ">=1.0" 262 | }, 263 | "type": "library", 264 | "autoload": { 265 | "files": [ 266 | "src/getallheaders.php" 267 | ] 268 | }, 269 | "notification-url": "https://packagist.org/downloads/", 270 | "license": [ 271 | "MIT" 272 | ], 273 | "authors": [ 274 | { 275 | "name": "Ralph Khattar", 276 | "email": "ralph.khattar@gmail.com" 277 | } 278 | ], 279 | "description": "A polyfill for getallheaders.", 280 | "time": "2016-02-11T07:05:27+00:00" 281 | }, 282 | { 283 | "name": "symfony/polyfill-ctype", 284 | "version": "v1.10.0", 285 | "source": { 286 | "type": "git", 287 | "url": "https://github.com/symfony/polyfill-ctype.git", 288 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" 289 | }, 290 | "dist": { 291 | "type": "zip", 292 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", 293 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", 294 | "shasum": "" 295 | }, 296 | "require": { 297 | "php": ">=5.3.3" 298 | }, 299 | "suggest": { 300 | "ext-ctype": "For best performance" 301 | }, 302 | "type": "library", 303 | "extra": { 304 | "branch-alias": { 305 | "dev-master": "1.9-dev" 306 | } 307 | }, 308 | "autoload": { 309 | "psr-4": { 310 | "Symfony\\Polyfill\\Ctype\\": "" 311 | }, 312 | "files": [ 313 | "bootstrap.php" 314 | ] 315 | }, 316 | "notification-url": "https://packagist.org/downloads/", 317 | "license": [ 318 | "MIT" 319 | ], 320 | "authors": [ 321 | { 322 | "name": "Symfony Community", 323 | "homepage": "https://symfony.com/contributors" 324 | }, 325 | { 326 | "name": "Gert de Pagter", 327 | "email": "BackEndTea@gmail.com" 328 | } 329 | ], 330 | "description": "Symfony polyfill for ctype functions", 331 | "homepage": "https://symfony.com", 332 | "keywords": [ 333 | "compatibility", 334 | "ctype", 335 | "polyfill", 336 | "portable" 337 | ], 338 | "time": "2018-08-06T14:22:27+00:00" 339 | }, 340 | { 341 | "name": "symfony/polyfill-mbstring", 342 | "version": "v1.10.0", 343 | "source": { 344 | "type": "git", 345 | "url": "https://github.com/symfony/polyfill-mbstring.git", 346 | "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" 347 | }, 348 | "dist": { 349 | "type": "zip", 350 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", 351 | "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", 352 | "shasum": "" 353 | }, 354 | "require": { 355 | "php": ">=5.3.3" 356 | }, 357 | "suggest": { 358 | "ext-mbstring": "For best performance" 359 | }, 360 | "type": "library", 361 | "extra": { 362 | "branch-alias": { 363 | "dev-master": "1.9-dev" 364 | } 365 | }, 366 | "autoload": { 367 | "psr-4": { 368 | "Symfony\\Polyfill\\Mbstring\\": "" 369 | }, 370 | "files": [ 371 | "bootstrap.php" 372 | ] 373 | }, 374 | "notification-url": "https://packagist.org/downloads/", 375 | "license": [ 376 | "MIT" 377 | ], 378 | "authors": [ 379 | { 380 | "name": "Nicolas Grekas", 381 | "email": "p@tchwork.com" 382 | }, 383 | { 384 | "name": "Symfony Community", 385 | "homepage": "https://symfony.com/contributors" 386 | } 387 | ], 388 | "description": "Symfony polyfill for the Mbstring extension", 389 | "homepage": "https://symfony.com", 390 | "keywords": [ 391 | "compatibility", 392 | "mbstring", 393 | "polyfill", 394 | "portable", 395 | "shim" 396 | ], 397 | "time": "2018-09-21T13:07:52+00:00" 398 | }, 399 | { 400 | "name": "twig/twig", 401 | "version": "v2.6.2", 402 | "source": { 403 | "type": "git", 404 | "url": "https://github.com/twigphp/Twig.git", 405 | "reference": "7d7342c8a4059fefb9b8d07db0cc14007021f9b7" 406 | }, 407 | "dist": { 408 | "type": "zip", 409 | "url": "https://api.github.com/repos/twigphp/Twig/zipball/7d7342c8a4059fefb9b8d07db0cc14007021f9b7", 410 | "reference": "7d7342c8a4059fefb9b8d07db0cc14007021f9b7", 411 | "shasum": "" 412 | }, 413 | "require": { 414 | "php": "^7.0", 415 | "symfony/polyfill-ctype": "^1.8", 416 | "symfony/polyfill-mbstring": "^1.3" 417 | }, 418 | "require-dev": { 419 | "psr/container": "^1.0", 420 | "symfony/debug": "^2.7", 421 | "symfony/phpunit-bridge": "^3.4.19|^4.1.8" 422 | }, 423 | "type": "library", 424 | "extra": { 425 | "branch-alias": { 426 | "dev-master": "2.6-dev" 427 | } 428 | }, 429 | "autoload": { 430 | "psr-0": { 431 | "Twig_": "lib/" 432 | }, 433 | "psr-4": { 434 | "Twig\\": "src/" 435 | } 436 | }, 437 | "notification-url": "https://packagist.org/downloads/", 438 | "license": [ 439 | "BSD-3-Clause" 440 | ], 441 | "authors": [ 442 | { 443 | "name": "Fabien Potencier", 444 | "email": "fabien@symfony.com", 445 | "homepage": "http://fabien.potencier.org", 446 | "role": "Lead Developer" 447 | }, 448 | { 449 | "name": "Armin Ronacher", 450 | "email": "armin.ronacher@active-4.com", 451 | "role": "Project Founder" 452 | }, 453 | { 454 | "name": "Twig Team", 455 | "homepage": "https://twig.symfony.com/contributors", 456 | "role": "Contributors" 457 | } 458 | ], 459 | "description": "Twig, the flexible, fast, and secure template language for PHP", 460 | "homepage": "https://twig.symfony.com", 461 | "keywords": [ 462 | "templating" 463 | ], 464 | "time": "2019-01-14T15:00:48+00:00" 465 | } 466 | ], 467 | "packages-dev": [], 468 | "aliases": [], 469 | "minimum-stability": "stable", 470 | "stability-flags": [], 471 | "prefer-stable": false, 472 | "prefer-lowest": false, 473 | "platform": { 474 | "php": "^7.2", 475 | "ext-curl": "*", 476 | "ext-json": "*" 477 | }, 478 | "platform-dev": [] 479 | } 480 | -------------------------------------------------------------------------------- /public_html/index.php: -------------------------------------------------------------------------------- 1 | true ] ); 208 | $twig->addExtension(new Twig_Extension_Debug()); 209 | 210 | $currentver = substr( file_get_contents( __DIR__. '/../.git/refs/heads/master' ), 0, 7 ); 211 | 212 | function logit( $user, $search, $method, $cached, $mysqli ) { 213 | $hash = @md5( $_SERVER['HTTP_ACCEPT_LANGUAGE'] . $_SERVER['HTTP_ACCEPT_ENCODING'] . $_SERVER['HTTP_ACCEPT'] . $_SERVER['HTTP_USER_AGENT'] . $_SERVER['HTTP_DNT'] ); 214 | $ua = $_SERVER['HTTP_USER_AGENT']; 215 | $timestamp = time(); 216 | $insert = "INSERT INTO logging( log_user, log_user_hash, log_user_ua, log_search, log_method, log_timestamp, log_cached ) values ( '$user', '$hash', '$ua', '$search', '$method', '$timestamp', $cached );"; 217 | mysqli_query( $mysqli, $insert ); 218 | } 219 | 220 | function reportHit( $service ) { 221 | //Record monthly stats on how many hits to each API service we're using. 222 | $min = date( "YMdGi" ); 223 | $day = date( "YMd" ); 224 | $month = date( "YM" ); 225 | if( !file_exists( __DIR__ . "/../stats" ) ) { 226 | mkdir( __DIR__ . "/../stats" ); 227 | } 228 | if( file_exists( __DIR__ . "/../stats/$service." . date( "Ym" ) . ".json" ) !== FALSE ) { 229 | $stat = json_decode( file_get_contents( __DIR__ . "/../stats/$service." . date( "Ym" ) . ".json" ), TRUE ); 230 | if( $stat['month'] == $month ) { $stat['rmonth']++; } else { $stat['rmonth'] = 1; } 231 | if( $stat['day'] == $day ) { $stat['rday']++; } else { $stat['rday'] = 1; } 232 | if( $stat['min'] == $min ) { $stat['rmin']++; } else { $stat['rmin'] = 1; } 233 | $stat['raw']++; 234 | $stat['month'] = $month; 235 | $stat['day'] = $day; 236 | $stat['min'] = $min; 237 | } else { 238 | $stat['raw'] = 1; 239 | $stat['month'] = $month; 240 | $stat['day'] = $day; 241 | $stat['min'] = $min; 242 | $stat['rmonth'] = 1; 243 | $stat['rday'] = 1; 244 | $stat['rmin'] = 1; 245 | } 246 | $lservice = getLimits(); 247 | 248 | //Check first limit 249 | if( $lservice[$service]['type'] == 'min' ) { 250 | if( $stat['rmin'] > $lservice[$service]['limit'] ) { return TRUE; } 251 | } 252 | if( $lservice[$service]['type'] == 'day' ) { 253 | if( $stat['rday'] > $lservice[$service]['limit'] ) { return TRUE; } 254 | } 255 | if( $lservice[$service]['type'] == 'month' ) { 256 | if( $stat['rmonth'] > $lservice[$service]['limit'] ) { return TRUE; } 257 | } 258 | 259 | //Check second limit 260 | if( $lservice[$service]['type2'] == 'min' ) { 261 | if( $stat['rmin'] > $lservice[$service]['limit2'] ) { return TRUE; } 262 | } 263 | if( $lservice[$service]['type2'] == 'day' ) { 264 | if( $stat['rday'] > $lservice[$service]['limit2'] ) { return TRUE; } 265 | } 266 | if( $lservice[$service]['type2'] == 'month' ) { 267 | if( $stat['rmonth'] > $lservice[$service]['limit2'] ) { return TRUE; } 268 | } 269 | file_put_contents( __DIR__ . "/../stats/$service." . date( "Ym" ) . ".json", json_encode( $stat ) ); 270 | return( FALSE ); 271 | } 272 | 273 | function checkSpamhaus( $ip ) { 274 | //Check spamhaus ZEN DNSBL, more information at https://www.spamhaus.org/zen/ 275 | global $mysqli; 276 | $origip = $ip; 277 | $expip = explode( ".", $ip ); 278 | $newip = array_reverse( $expip ); 279 | $ip = implode( ".", $newip ); 280 | $dnsres = dns_get_record( $ip . ".zen.spamhaus.org", DNS_A ); 281 | $spamhaus_result = array(); 282 | $value = 0; 283 | if( count( $dnsres ) == 0 ) { $spamhaus_result = FALSE; } else { 284 | foreach( $dnsres as $dns ) { 285 | $results = array(); 286 | array_push( $results, $dns['ip'] ); 287 | switch( $dns['ip'] ) { 288 | case "127.0.0.2": 289 | array_push( $results, "SBL Listed (possible spam source), see: details" ); 290 | $value++; 291 | break; 292 | case "127.0.0.3": 293 | array_push( $results, "SBL/CSS Listed (possible spam source), see: details" ); 294 | $value++; 295 | break; 296 | case "127.0.0.4": 297 | array_push( $results, "CBL Listed (proxy/trojan/botnet), see: details" ); 298 | $value++; 299 | break; 300 | case "127.0.0.5": 301 | array_push( $results, "CBL Listed (proxy/trojan/botnet), see: details" ); 302 | $value++; 303 | break; 304 | case "127.0.0.6": 305 | array_push( $results, "CBL Listed (proxy/trojan/botnet), see: details" ); 306 | $value++; 307 | break; 308 | case "127.0.0.7": 309 | array_push( $results, "CBL Listed (proxy/trojan/botnet), see: details" ); 310 | $value++; 311 | break; 312 | case "127.0.0.10": 313 | array_push( $results, "PBL Listed (Should not be sending email), see: details" ); 314 | $value--; 315 | break; 316 | case "127.0.0.11": 317 | array_push( $results, "PBL Listed (Should not be sending email), see: details" ); 318 | $value--; 319 | break; 320 | } 321 | array_push( $spamhaus_result, $results ); 322 | } 323 | } 324 | updateRes( $mysqli, "res_spamhaus", $value ); 325 | return( $spamhaus_result ); 326 | } 327 | 328 | function checkSpamcop( $ip ) { 329 | global $mysqli; 330 | $r_ip = explode( ".", $ip ); 331 | $ip = $r_ip[3] . "." . $r_ip[2] . "." . $r_ip[1] . "." . $r_ip[0]; 332 | $dnsres = dns_get_record( $ip . ".bl.spamcop.net", DNS_A ); 333 | if( $dnsres[0]['ip'] != "127.0.0.2" ) { 334 | updateRes( $mysqli, "res_spamcop", 0 ); 335 | return( FALSE ); 336 | } else { 337 | updateRes( $mysqli, "res_spamcop", 1 ); 338 | return( TRUE ); 339 | } 340 | } 341 | 342 | function checkTor( $ip ) { 343 | global $mysqli; 344 | $torapi = "https://onionoo.torproject.org/summary?search=$ip"; 345 | $tor = json_decode( file_get_contents( $torapi ), TRUE ); 346 | if( @$tor['relays'][0]['a'][0] == $ip ) { 347 | updateRes( $mysqli, "res_tor", 1 ); 348 | return( TRUE ); 349 | } else { 350 | updateRes( $mysqli, "res_tor", 0 ); 351 | return( FALSE ); 352 | } 353 | } 354 | 355 | function checkSorbs( $ip ) { 356 | //Check sorbs DNSBL, more information at http://www.sorbs.net/general/using.shtml 357 | global $mysqli; 358 | $dnsres = dns_get_record( $ip . ".dnsbl.sorbs.net", DNS_A ); 359 | $sorbs_result = array(); 360 | $value = 0; 361 | if( count( $dnsres ) == 0 ) { $sorbs_result = FALSE; } else { 362 | foreach( $dnsres as $dns ) { 363 | $results = array(); 364 | array_push( $results, $dns['ip'] ); 365 | switch( $dns['ip'] ) { 366 | case "127.0.0.2": 367 | array_push( $results, "HTTP Proxy" ); 368 | $value++; 369 | break; 370 | case "127.0.0.3": 371 | array_push( $results, "SOCKS Proxy" ); 372 | $value++; 373 | break; 374 | case "127.0.0.4": 375 | array_push( $results, "MISC Proxy" ); 376 | $value++; 377 | break; 378 | case "127.0.0.5": 379 | array_push( $results, "SMTP Server" ); 380 | $value++; 381 | break; 382 | case "127.0.0.6": 383 | array_push( $results, "Possible Spam Source" ); 384 | $value++; 385 | break; 386 | case "127.0.0.7": 387 | array_push( $results, "Vunerable Web server" ); 388 | $value++; 389 | break; 390 | case "127.0.0.8": 391 | array_push( $results, "Asked not to be testeb by SORBS" ); 392 | $value++; 393 | break; 394 | case "127.0.0.9": 395 | array_push( $results, "Zombie - Possibly Hijacked Netblock" ); 396 | $value++; 397 | break; 398 | case "127.0.0.10": 399 | array_push( $results, "Dynamic IP" ); 400 | $value--; 401 | break; 402 | case "127.0.0.11": 403 | array_push( $results, "Badconf - Invalid A or MX address" ); 404 | $value++; 405 | break; 406 | case "127.0.0.12": 407 | array_push( $results, "ISP indicates no mail should originate here" ); 408 | $value--; 409 | break; 410 | case "127.0.0.14": 411 | array_push( $results, "ISP indicates servers should not be present" ); 412 | $value--; 413 | break; 414 | } 415 | array_push( $sorbs_result, $results ); 416 | } 417 | } 418 | updateRes( $mysqli, "res_sorbs", $value ); 419 | return( $sorbs_result ); 420 | } 421 | 422 | if( isset( $theip ) ) { $ip = $theip; } else { $ip = $_GET['ip']; } 423 | 424 | $ip = ltrim( rtrim ( $ip ) ); 425 | 426 | if ( $ip == '' || inet_pton( $ip ) === FALSE ) { 427 | echo $twig->render( 'base.html.twig', [ 428 | 'username' => $username, 429 | 'editcount' => $editcount, 430 | 'registration' => $registration, 431 | 'ip' => '', 432 | 'apikey' => $apikey, 433 | 'currentver' => $currentver, 434 | 'portscan' => isset( $_GET['portscan'] ), 435 | ] ); 436 | die(); 437 | } 438 | 439 | $san_ip = mysqli_real_escape_string( $mysqli, $ip ); 440 | $res_id_query = "select res_id from results where res_ip = '$san_ip';"; 441 | $res_id_result = mysqli_query( $mysqli, $res_id_query ); 442 | 443 | if( mysqli_num_rows( $res_id_result ) < 1 ) { 444 | $res_id_update = "insert into results ( res_ip ) values( '$san_ip' );"; 445 | $res_id_update_result = mysqli_query( $mysqli, $res_id_update ); 446 | $res_id_query = "select res_id from results where res_ip = '$san_ip';"; 447 | $res_id_result = mysqli_query( $mysqli, $res_id_query ); 448 | } 449 | 450 | $res_id_array = mysqli_fetch_assoc( $res_id_result ); 451 | $res_id = $res_id_array['res_id']; 452 | 453 | $refresh = FALSE; 454 | $mtype = ""; 455 | if( isset( $_GET['refresh'] ) ) { 456 | $refresh = TRUE; 457 | $mtype = "refresh"; 458 | } elseif( file_exists( __DIR__ . "/../cache/$ip.json" ) ) { 459 | $out = json_decode( file_get_contents( __DIR__ . "/../cache/$ip.json" ), true ); 460 | if( filemtime( __DIR__ . "/../cache/$ip.json" ) + 604800 < time() ) { 461 | $refresh = TRUE; 462 | } elseif ( @!isset( $out['portscan'] ) && @$_GET['portscan'] == 1 ) { 463 | $refresh = TRUE; 464 | } 465 | } else { 466 | $refresh = TRUE; 467 | } 468 | if( $refresh === TRUE ) { 469 | $out = [ 470 | 'webhost' => [ 471 | 'title' => 'ASN Webhost Detection' 472 | ], 473 | 'proxycheck' => [ 474 | 'title' => 'proxycheck.io' 475 | ], 476 | 'getIPIntel' => [ 477 | 'title' => 'GetIPIntel' 478 | ], 479 | 'ipQualityScore' => [ 480 | 'title' => 'IPQualityScore' 481 | ], 482 | 'ipHub' => [ 483 | 'title' => 'IPHub' 484 | ], 485 | 'teohio' => [ 486 | 'title' => 'Teoh.io' 487 | ], 488 | 'ipHunter' => [ 489 | 'title' => 'IPHunter' 490 | ], 491 | /*'noFraud' => [ 492 | 'title' => 'Nofraud' 493 | ],*/ 494 | 'ipstack' => [ 495 | 'title' => 'ipstack.com' 496 | ], 497 | 'stopforumspam' => [ 498 | 'title' => 'StopForumSpam' 499 | ], 500 | 'hola' => [ 501 | 'title' => 'Hola' 502 | ], 503 | 'tor' => [ 504 | 'title' => 'TOR' 505 | ], 506 | 'computeHosts' => [ 507 | 'title' => 'Compute Hosts' 508 | ], 509 | 'sorbs' => [ 510 | 'title' => 'SORBS DNSBL' 511 | ], 512 | 'spamhaus' => [ 513 | 'title' => 'Spamhaus ZEN DNSBL' 514 | ], 515 | 'spamcop' => [ 516 | 'title' => 'Spamcop DNSBL' 517 | ], 518 | 'dshield' => [ 519 | 'title' => 'DShield' 520 | ], 521 | 'cache' => [ 522 | 'title' => 'Cache' 523 | ] 524 | ]; 525 | //ComputeHost Detection 526 | $wh = checkWebhost( $ip ); 527 | $out['webhost']['result']['webhost'] = $wh; 528 | 529 | // Proxycheck.io setup 530 | if( reportHit( "proxycheck-io" ) === TRUE ) { $out['proxycheck']['error'] = "API Queries exceeded. Try back later."; } else { 531 | $proxycheckio = json_decode( file_get_contents( "http://proxycheck.io/v2/$ip?key=$proxycheckkey&vpn=1&port=1&seen=1&risk=1" ), TRUE ); 532 | if( isset( $proxycheckio['error'] ) ) { 533 | $out['proxycheck']['error'] = $proxycheckio['error']; 534 | } else { 535 | $out['proxycheck']['result']['proxy'] = $proxycheckio[$ip]['proxy'] === 'yes'; 536 | if( $proxycheckio[$ip]['proxy'] === 'yes' ) { 537 | if( isset ( $proxycheckio[$ip]['last seen human'] ) ) { $out['proxycheck']['result']['seen'] = $proxycheckio[$ip]['last seen human']; } 538 | if( isset ( $proxycheckio[$ip]['port'] ) ) { $out['proxycheck']['result']['port'] = $proxycheckio[$ip]['port']; } 539 | if( isset ( $proxycheckio[$ip]['type'] ) ) { $out['proxycheck']['result']['pctype'] = $proxycheckio[$ip]['type']; } 540 | if( isset ( $proxycheckio[$ip]['risk'] ) ) { $out['proxycheck']['result']['riska'] = $proxycheckio[$ip]['risk']; } 541 | updateRes( $mysqli, "res_proxycheck", 1 ); 542 | } else { 543 | updateRes( $mysqli, "res_proxycheck", 0 ); 544 | } 545 | } 546 | } 547 | 548 | // GetIPIntel.net setup 549 | if( reportHit( "getipintel" ) === TRUE ) { $out['getipintel']['error'] = "API Queries exceeded. Try back later."; } else { 550 | $getipintel = json_decode( file_get_contents( "http://check.getipintel.net/check.php?ip=$ip&contact=$email&flags=f&format=json" ), TRUE ); 551 | if( $getipintel['status'] === "error" ) { 552 | $out['getIPIntel']['error'] = $getipintel['message']; 553 | } else { 554 | $chance = round ( (int)$getipintel['result'] * 100, 3 ); 555 | updateRes( $mysqli, "res_getipintel", $chance ); 556 | if( $chance == 0 ) { $chance = number_format( $chance, 1 ); } 557 | $out['getIPIntel']['result'] = [ 558 | 'chance' => $chance, 559 | ]; 560 | } 561 | } 562 | 563 | // IPQualityScore setup 564 | if( reportHit( "ipqs" ) === TRUE ) { $out['ipqs']['error'] = "API Queries exceeded. Try back later."; } else { 565 | $ipqualityscore = json_decode( file_get_contents( "https://www.ipqualityscore.com/api/json/ip/$ipqualityscorekey/$ip" ), TRUE ); 566 | if( $ipqualityscore['success'] === "false" ) { 567 | $out['ipQualityScore']['error'] = $ipqualityscore['message']; 568 | } else { 569 | $out['ipQualityScore']['result'] = [ 570 | 'proxy' => (bool)$ipqualityscore['proxy'], 571 | 'isp' => $ipqualityscore['ISP'], 572 | 'organization' => $ipqualityscore['organization'], 573 | 'vpn' => (bool)$ipqualityscore['vpn'], 574 | 'mobile' => (bool)$ipqualityscore['mobile'], 575 | 'tor' => (bool)$ipqualityscore['tor'], 576 | 'recent_abuse' => (bool)$ipqualityscore['recent_abuse'], 577 | 'bot_status' => (bool)$ipqualityscore['bot_status'], 578 | 'fraud_score' => $ipqualityscore['fraud_score'] 579 | ]; 580 | updateRes( $mysqli, "res_ipqs_isp", $ipqualityscore['ISP'] ); 581 | updateRes( $mysqli, "res_ipqs_proxy", $ipqualityscore['proxy'] ); 582 | updateRes( $mysqli, "res_ipqs_vpn", $ipqualityscore['vpn'] ); 583 | updateRes( $mysqli, "res_ipqs_mobile", $ipqualityscore['mobile'] ); 584 | updateRes( $mysqli, "res_ipqs_recent_abuse", $ipqualityscore['recent_abuse'] ); 585 | updateRes( $mysqli, "res_ipqs_tor", $ipqualityscore['tor'] ); 586 | updateRes( $mysqli, "res_ipqs_bot", $ipqualityscore['bot_status'] ); 587 | updateRes( $mysqli, "res_ipqs_fraudscore", $ipqualityscore['fraud_score'] ); 588 | updateRes( $mysqli, "res_ipqs_org", $ipqualityscore['organization'] ); 589 | 590 | } 591 | } 592 | 593 | // IPHub.info setup 594 | if( reportHit( "iphub" ) === TRUE ) { $out['iphub']['error'] = "API Queries exceeded. Try back later."; } else { 595 | $opts = array( 'http'=> array( 'header'=>"X-Key: $iphubkey" ) ); 596 | $context = stream_context_create( $opts ); 597 | $iphub = json_decode( file_get_contents( "http://v2.api.iphub.info/ip/$ip", FALSE, $context ), TRUE ); 598 | if( !is_array( $iphub ) ) { 599 | $out['ipHub']['error'] = true; 600 | } else { 601 | $out['ipHub']['result'] = []; 602 | 603 | if( isset( $iphub['isp'] ) ) { 604 | $out['ipHub']['result']['isp'] = $iphub['isp']; 605 | } 606 | updateRes( $mysqli, "res_iphub_res", $iphub['block'] ); 607 | if ($iphub['block'] < 3) { 608 | $out['ipHub']['result']['block'] = $iphub['block']; 609 | } else { 610 | $out['ipHub']['error'] = true; 611 | } 612 | } 613 | } 614 | 615 | // stopforumspam setup 616 | if( reportHit( "stopforumspam" ) === TRUE ) { $out['stopforumspam']['error'] = "API Queries exceeded. Try back later."; } else { 617 | $sfsurl = "http://api.stopforumspam.org/api?ip=$ip&json"; 618 | $sfs = json_decode( file_get_contents( $sfsurl ), true ); 619 | if( $sfs['success'] !== 1 ) { 620 | $out['stopforumspam']['error'] = true; 621 | } else { 622 | $appears = $sfs['ip']['appears']; 623 | if( $appears == 1 ) { 624 | $frequency = $sfs['ip']['frequency']; 625 | $confidence = $sfs['ip']['confidence']; 626 | $lastseen = $sfs['ip']['lastseen']; 627 | $country = $sfs['ip']['country']; 628 | $delegated = $sfs['ip']['delegated']; 629 | updateRes( $mysqli, "res_stopforumspam_listed", $appears ); 630 | updateRes( $mysqli, "res_stopforumspam_freq", $frequency ); 631 | updateRes( $mysqli, "res_stopforumspam_lastseen", $lastseen ); 632 | updateRes( $mysqli, "res_stopforumspam_confidence", $confidence ); 633 | updateRes( $mysqli, "res_stopforumspam_country", $country ); 634 | updateRes( $mysqli, "res_stopforumspam_delegated", $delegated ); 635 | 636 | $out['stopforumspam']['result'] = [ 637 | 'appears' => $appears, 638 | 'confidence' => $confidence, 639 | 'frequency' => $frequency, 640 | 'lastseen' => $lastseen, 641 | 'sfscountry' => $country, 642 | 'delegated' => $delegated 643 | ]; 644 | } else { 645 | $out['stopforumspam']['result'] = [ 646 | 'appears' => $appears 647 | ]; 648 | } 649 | } 650 | } 651 | 652 | // Teoh.io setup 653 | if( reportHit( "teoh" ) === TRUE ) { $out['teoh']['error'] = "API Queries exceeded. Try back later."; } else { 654 | $teohurl = "https://ip.teoh.io/api/vpn/$ip?key=$teohkey"; 655 | $teohio = json_decode( file_get_contents( $teohurl ), true ); 656 | if( @!isset( $teohio['ip'] ) ) { 657 | $out['teohio']['error'] = true; 658 | } else { 659 | $type = $teohio['type']; 660 | $risk = $teohio['risk']; 661 | if( $teohio['vpn_or_proxy'] == "yes" ) { $vpnorproxy = "1"; } else { $vpnorproxy = "0"; } 662 | switch( $risk ) { 663 | case "high": 664 | $numrisk = 3; 665 | break; 666 | case "medium": 667 | $numrisk = 2; 668 | break; 669 | case "low": 670 | $numrisk = 1; 671 | break; 672 | } 673 | updateRes( $mysqli, "res_teoh_hosting", $teohio['is_hosting'] ); 674 | updateRes( $mysqli, "res_teoh_vpnproxy", $vpnorproxy ); 675 | updateRes( $mysqli, "res_teoh_risk", $numrisk ); 676 | updateRes( $mysqli, "res_teoh_type", $type ); 677 | 678 | $out['teohio']['result'] = [ 679 | 'hosting' => true === $teohio['is_hosting'], 680 | 'vpnOrProxy' => 'yes' === $teohio['vpn_or_proxy'], 681 | 'type' => $teohio['type'], 682 | 'risk' => $teohio['risk'], 683 | ]; 684 | } 685 | } 686 | 687 | // IPHunter.info setup 688 | if( reportHit( "iphunter" ) === TRUE ) { $out['iphunter']['error'] = "API Queries exceeded. Try back later."; } else { 689 | $opts = array( 'http'=> array( 'header'=>"X-Key: $iphunterkey" ) ); 690 | $context = stream_context_create( $opts ); 691 | $iphunter = json_decode( file_get_contents( "https://www.iphunter.info:8082/v1/ip/$ip", false, $context ), true ); 692 | if( $iphunter['status'] === "error" ) { 693 | $out['ipHunter']['error'] = true; 694 | } else { 695 | $out['ipHunter']['result'] = []; 696 | 697 | if ( isset( $iphunter['data']['isp'] ) ) { 698 | $out['ipHunter']['result']['isp'] = $iphunter['data']['isp']; 699 | } 700 | updateRes( $mysqli, "res_iphunter", $iphunter['data']['block'] ); 701 | 702 | if ( $iphunter['data']['block'] < 3 ) { 703 | $out['ipHunter']['result']['block'] = $iphunter['data']['block']; 704 | } else { 705 | $out['ipHunter']['error'] = true; 706 | } 707 | } 708 | } 709 | 710 | // Nofraud.co setup 711 | /* Nofraud is unfortunately down. Will uncomment if/when they return. 712 | if( reportHit( "nofraud" ) === TRUE ) { $out['nofraud']['error'] = "API Queries exceeded. Try back later."; } else { 713 | if( strpos( $ip, ":" ) === FALSE ) { 714 | $nofraud = file_get_contents( "http://api.nofraud.co/ip.php?ip=$ip" ); 715 | $chance = round( $nofraud * 100, 3 ); 716 | $out['noFraud']['result'] = [ 717 | 'chance' => $chance, 718 | ]; 719 | } else { 720 | $out['noFraud']['error'] = "Only IPv4 is supported"; 721 | } 722 | } 723 | */ 724 | // ipstack.com setup 725 | if( reportHit( "ipstack" ) === TRUE ) { $out['ipstack']['error'] = "API Queries exceeded. Try back later."; } else { 726 | $ipstack = json_decode( file_get_contents( "http://api.ipstack.com/$ip?access_key=$ipstackkey" ), TRUE ); 727 | if( @isset( $ipstack['city'] ) ) { 728 | $out['ipstack']['result'] = [ 729 | 'city' => $ipstack['city'], 730 | ]; 731 | } 732 | if( @isset( $ipstack['region_name'] ) ) { 733 | $out['ipstack']['result'] = [ 734 | 'state' => $ipstack['region_name'], 735 | ]; 736 | } 737 | if( @isset( $ipstack['zip'] ) ) { 738 | $out['ipstack']['result'] = [ 739 | 'zip' => $ipstack['zip'], 740 | ]; 741 | } 742 | if( @isset( $ipstack['country_name'] ) ) { 743 | $out['ipstack']['result'] = [ 744 | 'country' => $ipstack['country_name'], 745 | ]; 746 | } 747 | } 748 | //Check for google compute, amazon aws, and microsoft azure 749 | $check = checkCompute( $ip ); 750 | $cRes = ""; 751 | if( $check !== FALSE ) { 752 | $chisp = $check['service']; 753 | $range = $check['range']; 754 | if( $chisp == "google" ) { $chisp = "Google Cloud"; } 755 | if( $chisp == "azure" ) { $chisp = "Microsoft Azure"; } 756 | if( $chisp == "amazon" ) { $chisp = "Amazon AWS"; } 757 | } else { 758 | $chisp = "This IP is not an AWS/Azure/GoogleCloud node.\n"; 759 | } 760 | $out['computeHosts']['result'] = [ 761 | 'cloud' => $chisp, 762 | ]; 763 | 764 | //Check if this is a tor node via onionoo 765 | if( checkTor( $ip ) === TRUE ) { 766 | $out['tor']['result']['tornode'] = TRUE; 767 | updateRes( $mysqli, "res_tor", 1 ); 768 | 769 | } else { 770 | $out['tor']['result']['tornode'] = FALSE; 771 | updateRes( $mysqli, "res_tor", 0 ); 772 | } 773 | 774 | // Check Sorbs setup 775 | if( reportHit( "sorbs" ) === TRUE ) { $out['sorbs']['error'] = "API Queries exceeded. Try back later."; } else { 776 | $sorbsResult = checkSorbs( $ip ); 777 | if( $sorbsResult !== false ) { 778 | $out['sorbs']['result']['entries'] = []; 779 | foreach( $sorbsResult as $sr ) { 780 | $out['sorbs']['result']['entries'][] = $sr[0] . " - " . $sr[1]; 781 | } 782 | } 783 | } 784 | 785 | // Check Spamhaus setup 786 | if( reportHit( "spamhaus" ) === TRUE ) { $out['spamhaus']['error'] = "API Queries exceeded. Try back later."; } else { 787 | $spamhausResult = checkSpamhaus( $ip ); 788 | if( $spamhausResult !== false ) { 789 | $out['spamhaus']['result']['entries'] = []; 790 | foreach( $spamhausResult as $sr ) { 791 | $out['spamhaus']['result']['entries'][] = $sr[0] . " - " . $sr[1]; 792 | } 793 | } 794 | } 795 | 796 | // Check SpamCop setup 797 | $spamcopResult = checkSpamcop( $ip ); 798 | if( $spamcopResult !== false ) { 799 | $out['spamcop']['result']['listed'] = TRUE; 800 | } else { 801 | $out['spamcop']['result']['listed'] = FALSE; 802 | } 803 | 804 | //DShield setup 805 | if( reportHit( "dshield" ) === TRUE ) { $out['dshield']['error'] = "API Queries exceeded. Try back later."; } else { 806 | $ds = file_get_contents( "https://www.dshield.org/api/ip/$ip?json" ); 807 | $dshield = json_decode( $ds, TRUE ); 808 | if( @count( $dshield['ip']['attacks'] ) > 0 ) { $out['dshield']['result']['attacks'] = $dshield['ip']['attacks']; } 809 | $feeds = array(); 810 | if( @count( $dshield['ip']['threatfeeds'] ) > 0 ) { 811 | foreach( $dshield['ip']['threatfeeds'] as $feed=>$data ) { 812 | $ls = $data['lastseen']; 813 | $feed = $feed . " lastseen($ls)"; 814 | array_push( $feeds, $feed ); 815 | } 816 | $tfeeds = implode( ", ", $feeds ); 817 | $out['dshield']['result']['tfeeds'] = $tfeeds; 818 | } 819 | } 820 | 821 | // Portscan setup 822 | if( isset( $_GET['portscan'] ) ) { 823 | if( reportHit( "portscan" ) === TRUE ) { $out['portscan']['error'] = "API Queries exceeded. Try back later."; } else { 824 | $out['portscan'] = [ 825 | 'title' => 'Open ports' 826 | ]; 827 | $porturl = $purl . "$ip&auth=$auth"; 828 | $ch = curl_init( $porturl ); 829 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); 830 | $scanres = json_decode( curl_exec( $ch ), true ); 831 | $scandate = $scanres['date']; 832 | unset( $scanres['date'] ); 833 | $out['portscan']['result']['entries'] = $scanres; 834 | } 835 | } 836 | 837 | $m1_hola = json_decode( file_get_contents( __DIR__ . "/../sources/proxies.json" ), true ); 838 | $m2_hola = json_decode( file_get_contents( __DIR__ . "/../sources/hola_dns.json" ), true ); 839 | 840 | /* Hola - Method 1 */ 841 | foreach( $m1_hola as $h ) { 842 | if( $ip == $h['ip'] ) { 843 | $out['hola']['result']['holas'][] = [ 844 | 'port' => $h['info']['port'], 845 | 'country' => $h['country'], 846 | ]; 847 | } 848 | } 849 | 850 | /* Hola - Method 2 */ 851 | foreach( $m2_hola as $h ) { 852 | if( $ip == $h['ip'] ) { 853 | $out['hola']['result']['holas'][] = [ 854 | 'seen' => date( "F d, Y", $h['seen'] ), 855 | ]; 856 | } 857 | } 858 | $ipbase = ""; 859 | if( strpos( $ip, ":" ) === FALSE ) { 860 | $ipbase_e = explode( ".", $ip ); 861 | $ipbase = $ipbase_e[0] . "." . $ipbase_e[1] . "." . $ipbase_e[2]; 862 | if( substr( $ipbase, -1 ) == "." ) { 863 | $ipbase = substr( $ipbase, 0, -1 ); 864 | } 865 | } 866 | $out['cache']['result']['cached'] = 'no'; 867 | file_put_contents( __DIR__ . "/../cache/$ip.json", json_encode( $out ) ); 868 | if( isset( $_GET['api'] ) ) { 869 | logit( $username, $ip, "api" . $mtype, 1, $mysqli ); 870 | } else { 871 | logit( $username, $ip, "manual" . $mtype, 1, $mysqli ); 872 | } 873 | } else { 874 | $ipbase = ""; 875 | if( strpos( $ip, ":" ) === FALSE ) { 876 | $ipbase_e = explode( ".", $ip ); 877 | $ipbase = $ipbase_e[0] . "." . $ipbase_e[1] . "." . $ipbase_e[2] . "."; 878 | if( substr( $ipbase, -1 ) == "." ) { 879 | $ipbase = substr( $ipbase, 0, -1 ); 880 | } 881 | } 882 | $out['cache']['result']['cached'] = 'yes'; 883 | $out['cache']['result']['cachedate'] = date( "M j G:i:s T Y", filemtime( __DIR__ . "/../cache/$ip.json" ) ); 884 | $out['cache']['result']['cacheuntil'] = date( "M j G:i:s T Y", filemtime( __DIR__ . "/../cache/$ip.json" ) + 604800 ); 885 | if( isset( $_GET['api'] ) ) { 886 | logit( $username, $ip, "api" . $mtype, 0, $mysqli ); 887 | } else { 888 | logit( $username, $ip, "manual" . $mtype, 0, $mysqli ); 889 | } 890 | } 891 | 892 | $host = gethostbyaddr( $ip ); 893 | if( $host == $ip ) { $hostname = $ip; } else { $hostname = "$ip - $host"; } 894 | 895 | if( isset( $_GET['api'] ) ) { 896 | echo json_encode( $out ); 897 | } else { 898 | echo $twig->render( 'results.html.twig', [ 899 | 'username' => $username, 900 | 'hostname' => $hostname, 901 | 'currentver' => $currentver, 902 | 'ip' => $ip, 903 | 'out' => $out, 904 | 'wikiurl' => $wikiurl, 905 | 'ipbase' => $ipbase, 906 | 'apikey' => $apikey, 907 | 'portscan' => isset( $_GET['portscan'] ), 908 | ] ); 909 | } 910 | ?> 911 | --------------------------------------------------------------------------------