[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Changing clients to automatically find their proxy?



[Florian Reitmeir]
> What i don't understand is the effort of doing the proxy settings
> per application. why not just use a transparent proxy? squid has
> very good support for it, and for ftp there are debian packages like
> 'frox' which are able to provide an transparent ftp proxy with a
> squid backend.

Good point.  Perhaps it is time to reconsider that part of the Debian
Edu network design.  Part of the reason why it isn't done with a
transparent proxy is to avoid having to enforce a specific
gateway/router configuration.

Anyway, to be able to update /etc/environment using a WPAD file, one
need to run the javascript from the command line.  Here is a draft
script to do just that.  Give it the URL to the WPAD javascript, and
it will output the http and ftp proxy settings.  It work for the
simple WPAD file provided with Debian Edu, but not with the more
complex one provided by Microsoft AD.  Anyone got any idea why it fail
for some scripts?

The license might need to be adjusted, as it include a copy of
javascript code from firefox.

#!/usr/bin/perl
#
# Process WPAD file to extract proxy settings
# http://docs.huihoo.com/gnu_linux/squid/html/x1187.html
#
# Author: Petter Reinholdtsen
# License: GNU General Public License v2 or later

use strict;
use warnings;

use JavaScript; # From the libjavascript-perl debian package
use LWP::Simple; # From the libwww-perl debian package

my $debug = 1;

sub get_wpad_script {
    my $wpadurl = shift;
    my $wpadbody;
    if ($wpadurl =~ m/^http[s]?:/) {
        print "Fetching URL $wpadurl\n" if $debug;
        $wpadbody = LWP::Simple::get($wpadurl);
        die "Could not get url $wpadurl!" unless defined $wpadbody;
    } elsif ($wpadurl =~ m/^file:\/\/(\/?.+)$/) {
        open(FILE, "<", $1) || die "Unable to read $1";
        $wpadbody = "";
        while (<FILE>) {
            $wpadbody .= $_;
        }
        close(FILE);
    } else {
        die "Unhandled URL type $wpadurl!";
    }
    return $wpadbody;
}

sub usage {
    my $retval = shift;
    print <<EOF;
Usage: $0 [URL-to-wpad-file]
EOF
    exit $retval;
}

my $wpadurl = $ARGV[0];
usage(1) unless $wpadurl;

my $wpadbody = get_wpad_script($wpadurl);
my $httpproxy = get_proxy("http", $wpadbody) || "";
my $ftpproxy = get_proxy("ftp", $wpadbody) || "";
print "http_proxy=$httpproxy\n";
print "ftp_proxy=$ftpproxy\n";

exit 0;

sub get_proxy {
    my ($type, $wpadbody) = @_;
    my $rt = new JavaScript::Runtime();
    my $cx = $rt->create_context();
    $cx->set_error_handler(sub {
        my ($message, $lineno, $linebuff) = @_;
        die "Error at line: $lineno\nMessage is: $message\nSource is: $linebuff\n";
    });

    # Load proxy autoconfig helper functions
    $cx->eval(get_pac_functions());

    # Load the WPAD script
    $cx->eval($wpadbody);

    my ($url, $host);
    if ("http" eq $type) {
        $url =  "http://www.debian.org/";;
        $host = "www.debian.org";
    } elsif ("ftp" eq $type) {
        $url =  "ftp://ftp.debian.org/";;
        $host = "ftp.debian.org";
    } else {
        die "Unhandled proxy type requested";
    }
    my $setting = $cx->call("FindProxyForURL", $url, $host);
    print "S[$type]: $setting\n" if $debug;
    for my $entry (split(/; */, $setting)) {
        print "  E: $entry\n" if $debug;
        return undef if $entry eq "DIRECT";
        return "http://$1"; if $entry =~ /^PROXY (.+)$/;
        # Not handling SOCKS proxy settings
    }
    return undef;
}

# Make common functions available.  Copied from
# http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsProxyAutoConfig.js
sub get_pac_functions {
    my $pacUtils = <<'EOF';
function dnsDomainIs(host, domain) {
    return (host.length >= domain.length &&
            host.substring(host.length - domain.length) == domain);
}

function dnsDomainLevels(host) {
    return host.split('.').length-1;
}

function convert_addr(ipchars) {
    var bytes = ipchars.split('.');
    var result = ((bytes[0] & 0xff) << 24) |
                 ((bytes[1] & 0xff) << 16) |
                 ((bytes[2] & 0xff) <<  8) |
                  (bytes[3] & 0xff);
    return result;
}

function isInNet(ipaddr, pattern, maskstr) {
    var test = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/(ipaddr);
    if (test == null) {
        ipaddr = dnsResolve(ipaddr);
        if (ipaddr == null)
            return false;
    } else if (test[1] > 255 || test[2] > 255 ||
               test[3] > 255 || test[4] > 255) {
        return false;    // not an IP address
    }
    var host = convert_addr(ipaddr);
    var pat  = convert_addr(pattern);
    var mask = convert_addr(maskstr);
    return ((host & mask) == (pat & mask));

}

function isPlainHostName(host) {
    return (host.search('\\.') == -1);
}

function isResolvable(host) {
    var ip = dnsResolve(host);
    return (ip != null);
}

function localHostOrDomainIs(host, hostdom) {
    return (host == hostdom) ||
           (hostdom.lastIndexOf(host + '.', 0) == 0);
}

function shExpMatch(url, pattern) {
   pattern = pattern.replace(/\./g, '\\.');
   pattern = pattern.replace(/\*/g, '.*');
   pattern = pattern.replace(/\?/g, '.');
   var newRe = new RegExp('^'+pattern+'$');
   return newRe.test(url);
}

var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};
var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};

function weekdayRange() {
    function getDay(weekday) {
        if (weekday in wdays) {
            return wdays[weekday];
        }
        return -1;
    }
    var date = new Date();
    var argc = arguments.length;
    var wday;
    if (argc < 1)
        return false;
    if (arguments[argc - 1] == 'GMT') {
        argc--;
        wday = date.getUTCDay();
    } else {
        wday = date.getDay();
    }
    var wd1 = getDay(arguments[0]);
    var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1;
    return (wd1 == -1 || wd2 == -1) ? false
                                    : (wd1 <= wday && wday <= wd2);
}

function dateRange() {
    function getMonth(name) {
        if (name in months) {
            return months[name];
        }
        return -1;
    }
    var date = new Date();
    var argc = arguments.length;
    if (argc < 1) {
        return false;
    }
    var isGMT = (arguments[argc - 1] == 'GMT');

    if (isGMT) {
        argc--;
    }
    // function will work even without explict handling of this case
    if (argc == 1) {
        var tmp = parseInt(arguments[0]);
        if (isNaN(tmp)) {
            return ((isGMT ? date.getUTCMonth() : date.getMonth()) ==
getMonth(arguments[0]));
        } else if (tmp < 32) {
            return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp);
        } else {
            return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) ==
tmp);
        }
    }
    var year = date.getFullYear();
    var date1, date2;
    date1 = new Date(year,  0,  1,  0,  0,  0);
    date2 = new Date(year, 11, 31, 23, 59, 59);
    var adjustMonth = false;
    for (var i = 0; i < (argc >> 1); i++) {
        var tmp = parseInt(arguments[i]);
        if (isNaN(tmp)) {
            var mon = getMonth(arguments[i]);
            date1.setMonth(mon);
        } else if (tmp < 32) {
            adjustMonth = (argc <= 2);
            date1.setDate(tmp);
        } else {
            date1.setFullYear(tmp);
        }
    }
    for (var i = (argc >> 1); i < argc; i++) {
        var tmp = parseInt(arguments[i]);
        if (isNaN(tmp)) {
            var mon = getMonth(arguments[i]);
            date2.setMonth(mon);
        } else if (tmp < 32) {
            date2.setDate(tmp);
        } else {
            date2.setFullYear(tmp);
        }
    }
    if (adjustMonth) {
        date1.setMonth(date.getMonth());
        date2.setMonth(date.getMonth());
    }
    if (isGMT) {
    var tmp = date;
        tmp.setFullYear(date.getUTCFullYear());
        tmp.setMonth(date.getUTCMonth());
        tmp.setDate(date.getUTCDate());
        tmp.setHours(date.getUTCHours());
        tmp.setMinutes(date.getUTCMinutes());
        tmp.setSeconds(date.getUTCSeconds());
        date = tmp;
    }
    return ((date1 <= date) && (date <= date2));
}

function timeRange() {
    var argc = arguments.length;
    var date = new Date();
    var isGMT= false;

    if (argc < 1) {
        return false;
    }
    if (arguments[argc - 1] == 'GMT') {
        isGMT = true;
        argc--;
    }

    var hour = isGMT ? date.getUTCHours() : date.getHours();
    var date1, date2;
    date1 = new Date();
    date2 = new Date();

    if (argc == 1) {
        return (hour == arguments[0]);
    } else if (argc == 2) {
        return ((arguments[0] <= hour) && (hour <= arguments[1]));
    } else {
        switch (argc) {
        case 6:
            date1.setSeconds(arguments[2]);
            date2.setSeconds(arguments[5]);
        case 4:
            var middle = argc >> 1;
            date1.setHours(arguments[0]);
            date1.setMinutes(arguments[1]);
            date2.setHours(arguments[middle]);
            date2.setMinutes(arguments[middle + 1]);
            if (middle == 2) {
                date2.setSeconds(59);
            }
            break;
        default:
          throw 'timeRange: bad number of arguments'
        }
    }

    if (isGMT) {
        date.setFullYear(date.getUTCFullYear());
        date.setMonth(date.getUTCMonth());
        date.setDate(date.getUTCDate());
        date.setHours(date.getUTCHours());
        date.setMinutes(date.getUTCMinutes());
        date.setSeconds(date.getUTCSeconds());
    }
    return ((date1 <= date) && (date <= date2));
}
EOF
    return $pacUtils;
}


Reply to: