From: Don Armstrong Date: Sun, 1 Mar 2009 00:51:12 +0000 (+0000) Subject: add iodine-jigger and substitution solver X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=2b464ba4132397e6ce4f0760cf820c34030729ff;p=bin.git add iodine-jigger and substitution solver --- diff --git a/iodine-jigger b/iodine-jigger new file mode 100755 index 0000000..0bb32ae --- /dev/null +++ b/iodine-jigger @@ -0,0 +1,248 @@ +#! /bin/bash + +### Script to set up an iodine tunnel route traffic through it +### +### Copyright 2008 Barak A. Pearlmutter +### +### License: MIT +### +### Permission to use, copy, modify, and distribute this software for +### any purpose with or without fee is hereby granted, provided that +### the above copyright notice and this permission notice appear in +### all copies. +### +### THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +### WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +### WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +### AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +### CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +### LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +### NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +### CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +## Cause script to bail immediately on failed command +set -e + +## Options for user to set. + +## Minimal customization: put the two lines +## subdomain=your.tunnel.sub.domain +## passed=password_for_that_tunnel +## in the file /etc/default/iodine-client. + +echo "${iodine_client_rc:=/etc/default/iodine-client}" > /dev/null + +if [ -r ${iodine_client_rc} ]; then + . ${iodine_client_rc} +else + echo WARNING: Cannot read ${iodine_client_rc} +fi + +if [ -z ${subdomain} ]; then + read -p "DNS tunnel DNS subdomain: " subdomain +fi + +if [ -z ${subdomain} ]; then + echo ERROR: Must set subdomain. + exit 1 +fi + +if [ -z ${passwd} ]; then + read -p "Password for DNS tunnel over ${subdomain}: " passwd +fi + +## This is a host name used for testing DNS and for pinging +echo "${testhost:=slashdot.org}" > /dev/null + +## Set if local network should be taken down and then up +echo "${bounce_localnet:=true}" > /dev/null + +## Set for testing network availability via ping at various points +echo "${test_ping_localnet:=true}" > /dev/null +echo "${test_ping_tunnel:=true}" > /dev/null +echo "${test_ping_final:=true}" > /dev/null + +## Set if the script cannot find and then incorrectly guesses the +## local network router +echo "${default_router}" > /dev/null + +## Set if script uses the wrong hardware interface +echo "{interface}" > /dev/null + +## Set if the script should continue even if a command fails. +## Used to test script when running as non-root. +if [ $(whoami) = root ]; then + echo "${continue_on_error:=false}" > /dev/null +else + echo "${continue_on_error:=true}" > /dev/null +fi + +## DEBIAN PACKAGES TO INSTALL: these are needed to run this script +## iodine (for /usr/sbin/iodine) +## iproute (for /bin/ip) +## ipcalc (for /usr/bin/ipcalc) +## dnsutils (for /usr/bin/dig) +## fping (for /usr/bin/fping) + +## The default tunnel MTU is 1024. +## If local DNS server restricts to 512 byte packets then do this: +# ifconfig ${d} mtu 220 + +## TO DO +## - avoid double ping when DNS server and local router are the same +## - option to not kill existing iodine DNS tunnels, in case there +## are meant to be more than one +## - sanify check whether default_router is on local network + +echo ==== Creating IP-over-DNS tunnel over local network connection... + + +## Find a network interface + +if [ -z ${interface} ]; then + interface=$(tail --lines=+3 /proc/net/wireless \ + | head -1 | tr -d : | awk '{print $1}') +fi + +if [ -z ${interface} ]; then + interface=$(ifconfig -a | egrep '^[^ ].*encap:Ethernet' \ + | head -1 | awk '{print $1}') +fi + +if [ -z ${interface} ]; then + echo ERROR: No network interface found. + exit 1 +fi + +echo ==== Local network interface: ${interface} + +## Down any existing DNS tunnel (wish there were "approved" way to do this) + +echo ==== Killing existing DNS tunnels... +if killall --quiet --wait --verbose --signal HUP iodine; then + sleep 2 +fi + +## Stabilize local network + +if ${bounce_localnet}; then + echo ==== Bouncing local network connection... + ifdown --force ${interface} || true + ifup ${interface} || ${continue_on_error} +fi + +## Fetch some information about the local network + +addr=$(ip -4 addr show dev ${interface} scope global \ + | tail -1 | awk '{print $2}') +prefix_len=$(echo ${addr} | sed 'sX^.*/XX') +local_net=$(ipcalc --nobinary ${addr} | awk '$1=="Network:" {print $2}') + +echo ==== Local address: ${addr} +echo ==== Local network: ${local_net} + +router=$(ip -4 route list dev ${interface} \ + | awk '$1=="default" {print $3}' | head -1) +if [ -z ${router} ]; then + ## This can happen when the default local route is already deleted + if [ -z ${default_router} ]; then + echo WARNING: no default route, guessing local router IP address. + ## Minimum address on local net is usually right + router=$(ipcalc --nobinary ${addr} | awk '$1=="HostMin:" {print $2}') + else + echo WARNING: no default route, using configured default router. + ## But sometimes need to hardwire... + router=${default_router} + fi +fi + +echo ==== Local network router: ${router} + +## Test DNS service + +testhost_ip=$(dig +short -t A -q ${testhost}) +if [ -z ${testhost_ip} ]; then + echo WARNING: Failure on DNS lookup of ${testhost}. +fi + +## fetch DNS servers + +nameservers=$(awk '$1=="nameserver" {print $2}' /etc/resolv.conf) +if [ -n "${nameservers}" ]; then + echo ==== DNS servers: ${nameservers} +else + echo ERROR: No DNS servers found. + exit 1 +fi + +## Test if local network is up + +if ${test_ping_localnet}; then + echo ==== Ping test of local network router and DNS servers... + fping -C1 ${router} ${nameservers} \ + || echo WARNING: Ping test failed. +fi + +## Add point-to-point routes for any non-local DNS servers + +for n in ${nameservers}; do + n_net=$(ipcalc --nobinary ${n}/${prefix_len} | awk '$1=="Network:" {print $2}') + if [ "${n_net}" != "${local_net}" ]; then + echo ==== Adding point-to-point route for DNS server ${n} + ip -4 route add ${n}/32 via ${router} || ${continue_on_error} + fi +done + +## Bring up DNS tunnel + +echo ==== Creating IP-over-DNS tunnel... +iodine -P ${passwd} ${subdomain} || ${continue_on_error} + +## Find DNS tunnel interface + +tunnel_interface=$(ifconfig -a | egrep '^dns' | awk '{print $1}' | head -1) +if [ -z "${tunnel_interface}" ]; then + echo WARNING: Cannot find DNS tunnel interface, using default. + tunnel_interface=dns0 +fi +echo ==== DNS tunnel interface: ${tunnel_interface} + +## Figure out router at other end of tunnel, assuming router uses final octet .1 +## (There should be some way to get this information out of iodine, since +## it *prints* it as it sets up the tunnel, so it does know it.) + +tunnel_remote=$(ip -4 address show dev ${tunnel_interface} \ + | awk '$1=="inet" {print gensub("[.][0-9]*/.*", ".1", 1, $2)}' | head -1) + +if [ -z ${tunnel_remote} ]; then + echo ERROR: Cannot find DNS tunnel remote endpoint. + ${continue_on_error} + ## set something random if debugging + echo WARNING: Confabulating DNS tunnel remote endpoint. + tunnel_remote=192.168.253.1 +fi + +echo ==== DNS tunnel remote endpoint: ${tunnel_remote} + +if ${test_ping_tunnel}; then + echo ==== Ping test of local router, nameserver, and DNS tunnel... + fping -C1 ${router} ${nameservers} ${tunnel_remote} \ + || echo WARNING: Ping test failed. +fi + +## Modify routing table to send trafic via DNS tunnel + +echo ==== Setting default route through DNS tunnel... + +## Remove default route via local router +ip -4 route del default via ${router} || ${continue_on_error} +## Add default via tunnel +ip -4 route add default via ${tunnel_remote} || ${continue_on_error} + +## Test if all is well + +if ${test_ping_final}; then + echo ==== Ping test of local router, nameserver, DNS tunnel, external test host... + fping -C1 ${router} ${nameservers} ${tunnel_remote} ${testhost_ip:-${testhost}} \ + || echo WARNING: Ping test failed. +fi diff --git a/substitution_solver b/substitution_solver new file mode 100755 index 0000000..a6faf7b --- /dev/null +++ b/substitution_solver @@ -0,0 +1,346 @@ +#! /usr/bin/perl +# substitution_solver solves substitution problems, and is released +# under the terms of the GPL version 2, or any later version, at your +# option. See the file README and COPYING for more information. +# Copyright 2006 by Don Armstrong . +# $Id: perl_script 495 2006-08-10 08:02:01Z don $ + + +use warnings; +use strict; + +use Getopt::Long; +use Pod::Usage; + +=head1 NAME + +substitution_solver - Solve substitution puzzles using known substitution methods + +=head1 SYNOPSIS + + substitution_solver [options] + + Options: + --log, -l file to log to + --input, -i initial input + --substitutions, -s directory of substitutions + --debug, -d debugging level (Default 0) + --help, -h display this help + --man, -m display manual + +=head1 OPTIONS + +=over + +=item B<--log,-l> + +An optional file to log the puzzle solution process + +=item B<--input,-i> + +Initial input to start puzzle solving with + +=item B<--transforms,-t> + +Directory of transforms to use; defaults to ~/.substitution_solver/transforms + +=item B<--debug, -d> + +Debug verbosity. (Default 0) + +=item B<--help, -h> + +Display brief useage information. + +=item B<--man, -m> + +Display this manual. + +=back + +=head1 EXAMPLES + + +=cut + + +use vars qw($DEBUG); + +my %options = (debug => 0, + help => 0, + man => 0, + transforms => [$ENV{HOME}.'/lib/substitution_solver/transforms'], + ); + +GetOptions(\%options,'debug|d+','help|h|?','man|m', + 'log|l=s','input|i=s', + 'transforms|t=s@', + ); + +pod2usage() if $options{help}; +pod2usage({verbose=>2}) if $options{man}; + +$DEBUG = $options{debug}; + +use IO::File; +use File::Temp qw(tempfile); +use Term::ReadLine; +use File::Spec qw(catfile rel2abs); +use Params::Validate qw(validate_with :types); + +my $logfh; + +if (defined $options{log}) { + $logfh = IO::File->new($options{log},'w') or die "Unable to open $logfh for writing: $!"; +} +else { + # we write to a tempfile for now + if ( -w '/dev/null') { + $logfh = IO::File->new('/dev/null','w') or die "Unable to open /dev/null for writing: $!"; + } + else { + $logfh = tempfile(); + } +} + +my %transforms; + +my @input_stack = (''); +my $input_pos = 0; + +if (defined $options{input}) { + read_input($options{input}); +} + +# read transformations + +my $surpress_status = 0; + + +my $done = 0; + +load_transforms(@{$options{transforms}}); + + +my $readline = Term::ReadLine->new('substitutionsolver'); + +while (not $done) { + my $line = $readline->readline('['.$input_pos.']: '); + local $_ = $line; + s/^\s+//; + chomp; + print {$logfh} $line; + if (/^quit\s*$/) { + $done = 1; + } + elsif (/^input\s+(.+)/) { + read_input($1); + } + elsif (/^stat(?:s|istics)?\s*$/) { + output_statistics($1); + $surpress_status = 1; + } + elsif (/^list\s*$/) { + list_transformations(); + $surpress_status = 1; + } + elsif (/^applicable/) { + list_transformations(applicable=>1); + $surpress_status = 1; + } + elsif (/^print\s+stack\s*$/) { + print join("\n",map {"$_: $input_stack[$_]\n"} 0..$#input_stack); + } + elsif (/^p(?:rint)?\s*$/) { + print "Current input:\n"; + print $input_stack[$input_pos]; + print "\n"; + } + elsif (/^perl\s*(.+)/) { + do_perl_eval($1); + } + elsif (/^output\s+(.+)/) { + write_output($1); + } + elsif (/^undo\s*$/) { + $input_pos-- if $input_pos > 0; + } + elsif (/^redo\s*$/) { + $input_pos++ if ($input_pos+1) < $#input_stack; + } + elsif (/^\#/) { + # ignore comments + } + else { + print "Unknown command '$_'\n"; + } +} + + +sub read_input { + my ($file) = @_; + my $fh = IO::File->new($file,'r'); + if (not $fh) { + print "Unable to open $file for reading: $!\n"; + return; + } + local $/; + my $content = <$fh>; + @input_stack = ($content); + $input_pos = 0; +} + +sub output_statistics { + my $current_input = $input_stack[$input_pos]; + my %counts; + $counts{uc($_)}++ foreach grep /\S/, split //, $current_input; + for my $key (sort {$counts{$b} <=> $counts{$a}} keys %counts) { + print "$key: $counts{$key}\n"; + } +} + +sub do_perl_eval{ + my ($perl) = @_; + local $_ = $input_stack[$input_pos]; + my $return = eval $perl; + if ($@) { + print "Failure while evaluating perl code\n"; + print $@; + print "\n"; + } + else { + increment_stack($return); + } +} + +sub write_output{ + my ($file) = @_; + my $fh = IO::File->new($file,'w'); + if (not $fh) { + print "Unable to open $file for writing: $!\n"; + return; + } + print {$fh} $input_stack[$input_pos]; +} + +sub increment_stack{ + $input_stack[++$input_pos] = $_[0]; +} + +sub load_transformations{ + my @transforms = @_; + my @transform_files; + for my $transform (@transforms) { + if (-f $transform) { + push @transform_files,rel2abs($transform); + } + elsif (-d $transform) { + my $dir = IO::Dir->new($transform); + if (not $dir) { + print "Unable to open $transform for reading: $!"; + next; + } + my $file; + while ($file = $dir->readdir) { + next unless /^[a-z0-9][a-z0-9-]*$/; + next unless -f rel2abs(catfile($transform,$file)); + push @transform_files,rel2abs(catfile($transform,$file)); + } + } + } + for my $transform_file (@transform_files) { + my $fh = IO::File->new($transform_file); + if (not $fh) { + print "Unable to open $transform_file for reading: $!"; + } + my ($type,$name,$content) = (undef,undef,''); + while (<$fh>) { + if (/^name\:/ and not defined $name) { + chomp; + s/^name\:\s*//; + $name = $_; + next; + } + elsif (/^type:/ and not defined $type) { + chomp; + s/^type\:\s*//; + $type = $_; + next; + } + elsif (/^\#/) { + next; + } + else { + $content .= $_; + } + } + if (not defined $type or not defined $name) { + print "Badly formed transform file $transform_file; no type or name\n"; + next; + } + if ($type eq 'tr') { + my @content = grep /./, split /\n/, $content; + $transforms{$name} = {type => 'tr', + name => $name, + content => $content, + from => $content[0], + to => $content[1], + }; + } + elsif ($type eq 'map') { + my @content = grep /./, split /\n/, $content; + my %map; + my %map_reverse; + for my $line (@content) { + my ($from,$to) = split /\t+/,$line; + $map{$from} = $to; + $map_reverse{$to} = $from; + } + $transforms{$name} = {type => 'map', + name => $name, + content => $content, + map => \%map, + map_reverse => \%map, + }; + } + elsif ($type eq 'perl') { + $transforms{$name} = {type => 'perl', + name => $name, + content => $content, + }; + } + else { + print "Unknown type $type for transform $transform_file\n"; + next; + } + } +} + +sub list_transformations{ + my %param = validate_with(params => \@_, + spec => {applicable => {type => BOOLEAN, + default => 0, + }, + }, + ); + for my $transform (keys %transforms) { + if ($param{applicable}) { + # figure out if we can apply a transform + my $output = attempt_transform($transform); + if (not defined $output) { + next; + } + } + print "$transform\n"; + } +} + + +sub attempt_transform{ + my ($name) = @_; + my $transform = $transforms{$name}; + $transform->{} +} + + +__END__