#! /usr/local/bin/perl
# $Id: metaprox.pl,v 1.91 2004/07/04 14:29:34 user Exp $
=head1 NAME
metaprox - an HTTP meta-proxy (i.e., a proxy proxy)
=head1 SYNOPSIS
metaprox [--port=8338] [--database=/tmp/foo] [--daemon]
=head1 DESCRIPTION
Are you having problems accessing certain web sites through
privacy-enhancing web proxies like squid, ijb and privoxy? Perhaps
you'd like to go through proxies for some web sites and not others?
If so, you're in luck. This program allows you to do just that.
This program works as an HTTP proxy. In other words, you configure your
web browser to send requests to it. It examines the request URL and
looks up the domain name in an internal table. This table, coupled with
a default, determines which proxy, if any, to which it should forward
requests. If it should not use a proxy, it forwards it to the ultimate
destination directly.
This proxy is different than others in that it does not require nor use
a configuration file. This proxy recognizes certain HTTP requests as
commands for it to execute. Currently it recognizes URLs with any of
the hostnames of the machine it is running on, as long as the port
number is correct.
In other words, if you are running this proxy on your local machine,
then telling your web browser to fetch "http://localhost:8338/" will
be recognized by metaprox and you will see the metaprox GUI in your
browser. You may always use "metaprox" as a special case, even if
that is not one of your machine's hostnames.
You should avoid configuring any domains as not requiring a proxy in your
browser or via the no_proxy environment variable. This program is
designed to replace that functionality. This is a win because you can
use multiple web browsers and not worry about configuring them individually.
=head1 INSTALLATION
You will probably need to run the following commands to use this program:
# perl -MCPAN -e shell
cpan> force install POE
cpan> force install POE::Component::Client::HTTP
Sadly, I have never gotten POE to install without using "force" keyword.
=head1 USE
First start the program:
yourhost$ ./metaprox.pl --daemon
If you have a firewall on this machine, configure it to allow incoming
connections to whatever port metaprox listens to (the default is 8338).
Next, configure your browser to use the proxy wherever you ran it.
For some browsers, you can configure them through environment variables:
anyhost$ export http_proxy=http://yourhost:8338/
anyhost$ your_browser
If you are launching your browser from a GUI, you may want to put
this environment variable in your startup scripts (.profile).
To configure metaprox, simply access the following URL:
http://metaprox:8338/
=head1 RECREATING DATABASE
To store the database, run this command:
lynx -source 'http://metaprox:8338/script' > /tmp/foo.sh
Then, edit it as desired and run it to restore the database:
sh /tmp/foo.sh
=head1 TODO
Allow picking proxies for portions of a site (match URLs instead of domains).
Consider treating default entry more generally as a proxy for domain "".
Allow chaining proxies.
Allow picking a proxy at random from a list.
Determine which HTTP headers are necessary to get a particular page to load.
Get SSL working.
Automatically determine the capabilities of proxies.
=head1 COPYRIGHT
Copyright 2004 travis+web@subspacefield.org
This program is free for non-commercial use.
Contact author for terms of commercial use.
=cut
use strict;
use warnings;
use integer;
# This is the default listen port.
my $port = 8338;
# This is the default database name.
my $database_name = "metaprox.dat";
# Should we detach from the terminal?
my $daemon = 0;
# Allow a user to override the port on the command line.
use Getopt::Long;
# Process command-line options.
my $result = GetOptions("port=i" => \$port, "database=s" => \$database_name,
"daemon" => \$daemon);
BEGIN {
package ProxyMap;
use Storable;
sub new {
my ($class) = @_;
my $self = {};
$self->{"map"} = {};
$self->{"list"} = [];
bless $self, $class;
return $self;
}
## These routines handle the domain-to-proxy mapping.
sub add_mapping {
my ($self, $domain, $proxy) = @_;
my $old_proxy = $self->{"map"}->{$domain};
$self->{"map"}->{$domain} = $proxy;
return $old_proxy;
}
sub get_mapping {
my ($self, $domain) = @_;
return $self->{"map"}->{$domain};
}
sub delete_mapping {
my ($self, $domain) = @_;
return delete $self->{"map"}->{$domain};
}
# Save our state to a file.
sub save {
my ($self, $file) = @_;
store $self, $file;
}
# Load from an external file.
sub load {
my ($self, $file) = @_;
my $hashref = retrieve($file);
%$self = %$hashref;
}
# Return current state as a human-readable string.
sub stringify {
my ($self) = @_;
use Data::Dumper;
return Data::Dumper->Dump([$self], ["self"]);
}
# Return the current state as an HTML table.
sub htmlify {
my ($self) = @_;
my $html = "";
$html .= "