lots of progress
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PortScannerController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('port-scanner.index');
|
||||
}
|
||||
|
||||
public function scan(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ip' => 'required|ip',
|
||||
'port' => 'required|integer|min:1|max:65535',
|
||||
]);
|
||||
|
||||
$ip = $request->input('ip');
|
||||
$port = $request->input('port');
|
||||
$timeout = 2; // Timeout in seconds
|
||||
|
||||
try {
|
||||
$startTime = microtime(true);
|
||||
$connection = @fsockopen($ip, $port, $errno, $errstr, $timeout);
|
||||
$endTime = microtime(true);
|
||||
$responseTime = round(($endTime - $startTime) * 1000, 2); // Convert to milliseconds
|
||||
|
||||
if ($connection) {
|
||||
fclose($connection);
|
||||
$status = 'open';
|
||||
$message = "Port $port is open on $ip";
|
||||
} else {
|
||||
$status = 'closed';
|
||||
$message = "Port $port is closed on $ip";
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'status' => $status,
|
||||
'message' => $message,
|
||||
'response_time' => $responseTime,
|
||||
'ip' => $ip,
|
||||
'port' => $port
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Port scan error', [
|
||||
'ip' => $ip,
|
||||
'port' => $port,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'An error occurred while scanning the port',
|
||||
'error' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\ProxyService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProxyController extends Controller
|
||||
{
|
||||
protected $proxyService;
|
||||
|
||||
public function __construct(ProxyService $proxyService)
|
||||
{
|
||||
$this->proxyService = $proxyService;
|
||||
}
|
||||
|
||||
public function home()
|
||||
{
|
||||
return view('proxy.home');
|
||||
}
|
||||
|
||||
public function browse(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'url' => 'required'
|
||||
]);
|
||||
|
||||
try {
|
||||
// Decode the URL if it's base64 encoded
|
||||
$url = $request->url;
|
||||
if (base64_encode(base64_decode($url, true)) === $url) {
|
||||
// Decode base64 and then URL decode
|
||||
$url = urldecode(base64_decode($url));
|
||||
}
|
||||
|
||||
// Log the URL processing
|
||||
\Log::info('Processing URL', [
|
||||
'original' => $request->url,
|
||||
'decoded' => $url
|
||||
]);
|
||||
|
||||
// Validate the decoded URL
|
||||
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
throw new \Exception('Invalid URL format: ' . $url);
|
||||
}
|
||||
|
||||
$content = $this->proxyService->fetchUrl($url);
|
||||
|
||||
// Get content type from response
|
||||
$contentType = $this->proxyService->getLastContentType();
|
||||
|
||||
// Log the content type for debugging
|
||||
\Log::info('Content type received', [
|
||||
'url' => $url,
|
||||
'content_type' => $contentType,
|
||||
'is_css' => strpos($contentType, 'text/css') !== false ||
|
||||
strpos($contentType, 'application/css') !== false ||
|
||||
strpos($url, '.css') !== false
|
||||
]);
|
||||
|
||||
// If it's a CSS file, serve it with the correct content type
|
||||
if (strpos($contentType, 'text/css') !== false ||
|
||||
strpos($contentType, 'application/css') !== false ||
|
||||
strpos($url, '.css') !== false) {
|
||||
|
||||
\Log::info('Serving CSS file', [
|
||||
'url' => $url,
|
||||
'content_type' => $contentType,
|
||||
'content_length' => strlen($content)
|
||||
]);
|
||||
|
||||
return response($content)
|
||||
->header('Content-Type', 'text/css')
|
||||
->header('Cache-Control', 'public, max-age=3600')
|
||||
->header('Access-Control-Allow-Origin', '*')
|
||||
->header('X-Content-Type-Options', 'nosniff');
|
||||
}
|
||||
|
||||
// For HTML content, return the view
|
||||
return view('proxy.browse', [
|
||||
'url' => $url,
|
||||
'content' => $content
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Proxy error', [
|
||||
'url' => $request->url,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return back()->withErrors(['url' => 'Failed to fetch URL: ' . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,17 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Proxy;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProxyListController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('proxies.index');
|
||||
$proxies = Proxy::orderBy('score', 'desc')
|
||||
->orderBy('last_tested_at', 'desc')
|
||||
->paginate(20);
|
||||
|
||||
return view('proxies.index', compact('proxies'));
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Services;
|
||||
|
||||
use App\Models\Proxy;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
class ProxyService
|
||||
@@ -17,6 +18,13 @@ class ProxyService
|
||||
'timeout' => 30,
|
||||
'verify' => false,
|
||||
'http_errors' => false,
|
||||
'cookies' => new CookieJar(), // Enables cookies
|
||||
'allow_redirects' => [
|
||||
'max' => 10, // Allow up to 10 redirects
|
||||
'strict' => false,
|
||||
'referer' => true,
|
||||
'track_redirects' => true
|
||||
],
|
||||
'headers' => [
|
||||
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
||||
@@ -44,6 +52,9 @@ class ProxyService
|
||||
public function fetchUrl($url)
|
||||
{
|
||||
try {
|
||||
|
||||
$url = $this->replaceUrl($url);
|
||||
|
||||
// Normalize and encode the URL
|
||||
$url = $this->normalizeUrl($url);
|
||||
|
||||
@@ -69,20 +80,31 @@ class ProxyService
|
||||
$parsedUrl = parse_url($url);
|
||||
$host = $parsedUrl['host'];
|
||||
$ip = gethostbyname($host);
|
||||
|
||||
|
||||
if ($ip === $host) {
|
||||
throw new \Exception("Could not resolve hostname: $host");
|
||||
}
|
||||
|
||||
// Replace hostname with IP in URL
|
||||
$url = str_replace($host, $ip, $url);
|
||||
|
||||
|
||||
// Add Host header to maintain virtual hosting
|
||||
$response = $this->client->get($url, [
|
||||
'headers' => [
|
||||
'Host' => $host
|
||||
'Host' => $host,
|
||||
'Accept-Encoding' => 'identity' // Request uncompressed data
|
||||
]
|
||||
]);
|
||||
|
||||
$finalUrl = $response->getHeaderLine('X-Guzzle-Redirect-History') ?: $url;
|
||||
|
||||
// If redirected to a YouTube consent page, modify the request
|
||||
if (strpos($finalUrl, 'consent.youtube.com') !== false) {
|
||||
\Log::info('YouTube consent page detected, bypassing...');
|
||||
$url = str_replace('consent.youtube.com', 'www.youtube.com', $finalUrl);
|
||||
$response = $this->client->get($url);
|
||||
}
|
||||
|
||||
} catch (\Exception $dnsError) {
|
||||
\Log::error('DNS resolution failed', [
|
||||
'url' => $url,
|
||||
@@ -176,7 +198,7 @@ class ProxyService
|
||||
return $this->processCssContent($content, $baseUrl);
|
||||
}
|
||||
|
||||
// Process stylesheet links that don't start with http or https
|
||||
// Process stylesheet links first
|
||||
$content = preg_replace_callback(
|
||||
'/<link[^>]*rel=["\']stylesheet["\'][^>]*href=["\'](?!https?:\/\/)([^"\']+)["\']/i',
|
||||
function($matches) use ($baseUrl) {
|
||||
@@ -189,63 +211,50 @@ class ProxyService
|
||||
// Otherwise, append to the base URL
|
||||
$url = rtrim($baseUrl, '/') . '/' . ltrim($url, '/');
|
||||
}
|
||||
|
||||
|
||||
// Log the transformation
|
||||
\Log::info('Stylesheet URL transformed', [
|
||||
'original' => $matches[0],
|
||||
'transformed' => $url
|
||||
]);
|
||||
|
||||
|
||||
return str_replace($matches[1], $url, $matches[0]);
|
||||
},
|
||||
$content
|
||||
);
|
||||
|
||||
// For HTML content, process only anchor tags with external URLs
|
||||
$patterns = [
|
||||
// External links in href attributes (only for anchor tags)
|
||||
'/(<a[^>]*href)=["\'](https?:\/\/[^"\']+)["\']/i'
|
||||
];
|
||||
// Process external links (only anchor tags)
|
||||
$content = preg_replace_callback(
|
||||
'/(<a[^>]*href)=["\'](https?:\/\/[^"\']+)["\']/i',
|
||||
function($matches) use ($baseUrl) {
|
||||
$url = $matches[2];
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
$content = preg_replace_callback(
|
||||
$pattern,
|
||||
function($matches) use ($baseUrl) {
|
||||
$url = $matches[2];
|
||||
// Skip URLs that are already going through our proxy
|
||||
if (strpos($url, route('proxy.browse')) === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
// Skip URLs that are already going through our proxy
|
||||
if (strpos($url, route('proxy.browse')) === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
// Skip URLs that are already proxied
|
||||
if (strpos($url, route('proxy.browse')) === 0) {
|
||||
return $matches[0];
|
||||
}
|
||||
// Skip URLs from the same domain
|
||||
if (strpos($url, parse_url($baseUrl, PHP_URL_HOST)) !== false) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
// Proxy all http/https URLs
|
||||
if (preg_match('/^https?:\/\//i', $url)) {
|
||||
$url = route('proxy.browse') . '?url=' . base64_encode($url);
|
||||
return $matches[1] . '="' . htmlspecialchars($url) . '"';
|
||||
}
|
||||
// Proxy external URLs
|
||||
$encodedUrl = base64_encode($url);
|
||||
$proxiedUrl = route('proxy.browse') . '?url=' . $encodedUrl;
|
||||
|
||||
// Only proxy external URLs
|
||||
if (strpos($url, 'http') === 0 && strpos($url, parse_url($baseUrl, PHP_URL_HOST)) === false) {
|
||||
$url = route('proxy.browse') . '?url=' . base64_encode($url);
|
||||
}
|
||||
// Log the URL transformation
|
||||
\Log::info('URL transformed', [
|
||||
'original' => $matches[0],
|
||||
'transformed' => $proxiedUrl
|
||||
]);
|
||||
|
||||
// Log the URL transformation
|
||||
\Log::info('URL transformed', [
|
||||
'original' => $matches[0],
|
||||
'transformed' => $url
|
||||
]);
|
||||
return $matches[1] . '="' . htmlspecialchars($proxiedUrl) . '"';
|
||||
},
|
||||
$content
|
||||
);
|
||||
|
||||
return $matches[1] . '="' . htmlspecialchars($url) . '"';
|
||||
},
|
||||
$content
|
||||
);
|
||||
}
|
||||
|
||||
// Add JavaScript to handle dynamic links and resources
|
||||
// Add JavaScript to handle dynamic content
|
||||
$content = $this->addLinkHandlerScript($content);
|
||||
|
||||
return $content;
|
||||
@@ -359,21 +368,31 @@ class ProxyService
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Function to encode URL using base64
|
||||
function encodeUrl(url) {
|
||||
// First encode the URL to handle special characters
|
||||
var encoded = encodeURIComponent(url);
|
||||
// Then convert to base64
|
||||
return btoa(encoded);
|
||||
return btoa(encodeURIComponent(url));
|
||||
}
|
||||
|
||||
// Function to convert relative URL to absolute
|
||||
function toAbsoluteUrl(url) {
|
||||
if (!url) return url;
|
||||
if (url.startsWith('http')) return url;
|
||||
if (url.startsWith('//')) return window.location.protocol + url;
|
||||
if (url.startsWith('/')) return window.location.origin + url;
|
||||
return window.location.origin + '/' + url;
|
||||
}
|
||||
|
||||
// Handle dynamically added links
|
||||
// Function to check if URL should be proxied
|
||||
function shouldProxyUrl(url) {
|
||||
if (!url) return false;
|
||||
if (url.startsWith('{{ route('proxy.browse') }}')) return false;
|
||||
if (url.startsWith('data:')) return false;
|
||||
if (url.startsWith('javascript:')) return false;
|
||||
if (url.startsWith('mailto:')) return false;
|
||||
if (url.startsWith('tel:')) return false;
|
||||
if (url.includes('youtube.com') && !url.includes('watch?')) return false; // Allow YouTube navigation
|
||||
return url.startsWith('http') && !url.includes(window.location.hostname);
|
||||
}
|
||||
|
||||
// Handle clicks on anchor tags
|
||||
document.addEventListener('click', function(e) {
|
||||
var target = e.target;
|
||||
while (target && target.tagName !== 'A') {
|
||||
@@ -383,7 +402,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
var href = target.getAttribute('href');
|
||||
if (href) {
|
||||
href = toAbsoluteUrl(href);
|
||||
if (href.startsWith('http') && !href.includes(window.location.hostname)) {
|
||||
if (shouldProxyUrl(href)) {
|
||||
e.preventDefault();
|
||||
var encodedUrl = encodeUrl(href);
|
||||
window.location.href = '{{ route('proxy.browse') }}?url=' + encodedUrl;
|
||||
@@ -398,7 +417,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
var action = form.getAttribute('action');
|
||||
if (action) {
|
||||
action = toAbsoluteUrl(action);
|
||||
if (action.startsWith('http') && !action.includes(window.location.hostname)) {
|
||||
if (shouldProxyUrl(action)) {
|
||||
e.preventDefault();
|
||||
var encodedUrl = encodeUrl(action);
|
||||
form.action = '{{ route('proxy.browse') }}?url=' + encodedUrl;
|
||||
@@ -413,7 +432,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
var href = link.getAttribute('href');
|
||||
if (href) {
|
||||
href = toAbsoluteUrl(href);
|
||||
if (href.startsWith('http') && !href.includes(window.location.hostname)) {
|
||||
if (shouldProxyUrl(href)) {
|
||||
var encodedUrl = encodeUrl(href);
|
||||
link.setAttribute('href', '{{ route('proxy.browse') }}?url=' + encodedUrl);
|
||||
}
|
||||
@@ -429,12 +448,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
mutations.forEach(function(mutation) {
|
||||
mutation.addedNodes.forEach(function(node) {
|
||||
if (node.nodeType === 1) { // Element node
|
||||
// Only process anchor tags
|
||||
if (node.tagName === 'A') {
|
||||
var href = node.getAttribute('href');
|
||||
if (href) {
|
||||
href = toAbsoluteUrl(href);
|
||||
if (href.startsWith('http') && !href.includes(window.location.hostname)) {
|
||||
if (shouldProxyUrl(href)) {
|
||||
var encodedUrl = encodeUrl(href);
|
||||
node.setAttribute('href', '{{ route('proxy.browse') }}?url=' + encodedUrl);
|
||||
}
|
||||
@@ -445,7 +463,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// Start observing the document with the configured parameters
|
||||
// Start observing the document
|
||||
observer.observe(document.documentElement, { childList: true, subtree: true });
|
||||
});
|
||||
</script>
|
||||
@@ -456,4 +474,15 @@ EOT;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function replaceUrl($url)
|
||||
{
|
||||
$parsedUrl = parse_url($url);
|
||||
|
||||
if ($parsedUrl['host'] === 'www.youtube.com') {
|
||||
$url = str_replace('www.youtube.com', 'tube.cadence.moe', $url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class Utils
|
||||
{
|
||||
public static function proxifyUrl(string $url, string $siteBaseUrl)
|
||||
{
|
||||
if (!self::isNotAbsoluteUrl($url)) {
|
||||
// should append site url
|
||||
$url = $siteBaseUrl.$url;
|
||||
}
|
||||
|
||||
return route('proxy.browse') . '?url=' . base64_encode($url);
|
||||
}
|
||||
|
||||
public static function isNotAbsoluteUrl($url) {
|
||||
return parse_url($url, PHP_URL_SCHEME) !== null;
|
||||
}
|
||||
|
||||
public static function isAbsoluteUrl($url) {
|
||||
return parse_url($url, PHP_URL_SCHEME) === null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user