Last active
February 22, 2016 18:49
-
-
Save DelphicOkami/7ebf0fec1fdc2d9b4c99 to your computer and use it in GitHub Desktop.
Checks redirects for urls, returning the final destination
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* This script is for checking what redirects a url returns, | |
* only response based redirects are checked (IE: no javascript is checked) | |
*/ | |
error_reporting(0); | |
ini_set('allow_url_fopen', 1); | |
class Response { | |
protected $responseHeaders; | |
protected $responseCode; | |
protected $destination; | |
public function __construct($responseHeaders) | |
{ | |
if(count($responseHeaders) == 0) { | |
throw new Exception('Failed to create response object'); | |
} | |
$this->responseHeaders = $responseHeaders; | |
} | |
public function getResponseCode() { | |
if(!isset($this->responseCode)) { | |
foreach($this->responseHeaders as $header) { | |
if(!!strstr($header, 'HTTP')) { | |
$parts = explode(' ', $header); | |
foreach($parts as $part) { | |
if(is_numeric($part)) { | |
$this->responseCode = $part; | |
} | |
} | |
} | |
} | |
if(!isset($this->responseCode)) { | |
throw new Exception('Response code not found'); | |
} | |
} | |
return $this->responseCode; | |
} | |
public function getRedirectDestination() { | |
if(!isset($this->destination)) { | |
foreach ($this->responseHeaders as $header) { | |
if (strstr($header, 'Location')) { | |
preg_match("/^Location: (.*)$/", $header, $matches); | |
$this->destination = $matches[1]; | |
} | |
} | |
if(!isset($this->destination)) { | |
throw new Exception('Redirect Location not found'); | |
} | |
} | |
return $this->destination; | |
} | |
public function addDomainToPartialURL($domain) { | |
$this->destination = $domain . $this->destination; | |
} | |
} | |
class getHops { | |
protected $verbose = false; | |
protected $opts; | |
public function run() { | |
if(isset($this->getOpts()['v']) || isset($this->getOpts()['verbose'])) { | |
$this->verbose = true; | |
$this->verbose($this->embolden('Verbose mode on')); | |
} | |
if(isset($this->getOpts()['target']) && $this->getOpts()['target'] != '') { | |
$this->getRedirectHops($this->getOpts()['target']); | |
$mode = 'target'; | |
} | |
if(isset($this->getOpts()['continuous'])) { | |
$this->say('Enter a blank line or press ctrl + c to halt operation'); | |
while(true) { | |
$target = readline("Give me a url >> "); | |
if($target == "") { | |
$this->say('Ok stopping'); | |
break; | |
} | |
$this->getRedirectHops($target); | |
} | |
$mode = 'continuous'; | |
} | |
if(!isset($mode)) { | |
$this->usage(); | |
exit(isset($this->getOpts()['help']) || isset($this->getOpts()['h']) ? 0 : 1); | |
} | |
exit(0); | |
} | |
public function getRedirectHops($targetUrl) { | |
$check = new Response(['Location: ' . $targetUrl]); | |
$hits = []; | |
try { | |
while (true) { | |
$this->verbose(' Checking ' . $check->getRedirectDestination()); | |
$target = $this->getRedirectTarget($check->getRedirectDestination()); | |
if($target) { | |
if($target->getResponseCode() >= 300 && $target->getResponseCode() <= 399) { | |
if(substr($target->getRedirectDestination(), 0, 1) == '/') { | |
$target->addDomainToPartialURL($this->getDomain($check->getRedirectDestination())); | |
} | |
$this->say($this->embolden($target->getResponseCode()) . ' redirected to: ' . $this->embolden($target->getRedirectDestination())); | |
if(in_array($target->getRedirectDestination(), $hits)){ | |
$this->say('Infinite loop detected, stopping'); | |
break; | |
} | |
$hits[] = $target->getRedirectDestination(); | |
$check = $target; | |
} else { | |
$this->say('Final target: ' . $this->embolden($check->getRedirectDestination())); | |
break; | |
} | |
} else { | |
break; | |
} | |
} | |
} catch(Exception $e) { | |
$this->say($e->getMessage()); | |
} | |
} | |
/** | |
* @param $target | |
* @return Response | |
*/ | |
protected function getRedirectTarget($target) { | |
$fixedTarget = !!strstr($target, 'http') ? $target : 'http://' . $target; | |
file_get_contents($fixedTarget, false, stream_context_create([ | |
'http' => [ | |
'method' => 'GET', | |
'ignore_errors' => true, | |
'max_redirects' => 0, | |
'timeout' => 10, | |
] | |
])); | |
if(!isset($http_response_header)) { | |
$this->say($this->embolden('Could not connect to ' . $target)); | |
return false; | |
} else { | |
return new Response($http_response_header); | |
} | |
} | |
protected function usage() { | |
$this->say("Expected usage:"); | |
$this->say(' ' . basename(__FILE__) . $this->embolden(' --help | -h') . ': Displays this message'); | |
$this->say(' ' . basename(__FILE__) . $this->embolden(' [-v|--verbose] --target=<URL>') . ': Retrieves the redirect list for the given url'); | |
$this->say(' ' . basename(__FILE__) . $this->embolden(' [-v|--verbose] --continuous') . ': Enters prompt mode, useful for checking multiple urls'); | |
} | |
protected function getOpts() { | |
if(!isset($this->opts)) { | |
$this->opts = getopt('hv', ['continuous', 'target:', 'verbose', 'help']); | |
} | |
return $this->opts; | |
} | |
protected function verbose($line) { | |
if($this->verbose) { | |
$this->say($line); | |
} | |
} | |
protected function say($line) { | |
echo $line . PHP_EOL; | |
} | |
protected function embolden($toEnbolden) { | |
return chr(27) . '[1m' . $toEnbolden . chr(27) . '[0m'; | |
} | |
protected function getDomain($url) { | |
$parse = parse_url($url); | |
return $parse['host']; | |
} | |
} | |
(new getHops)->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment