Skip to content

Instantly share code, notes, and snippets.

@DelphicOkami
Last active February 22, 2016 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DelphicOkami/7ebf0fec1fdc2d9b4c99 to your computer and use it in GitHub Desktop.
Save DelphicOkami/7ebf0fec1fdc2d9b4c99 to your computer and use it in GitHub Desktop.
Checks redirects for urls, returning the final destination
<?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