Mercurial > repo
diff interps/clc-intercal/CLC-INTERCAL-INET-1.-94.-2/INTERCAL/Theft.pm @ 996:859f9b4339e6
<Gregor> tar xf egobot.tar.xz
author | HackBot |
---|---|
date | Sun, 09 Dec 2012 19:30:08 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interps/clc-intercal/CLC-INTERCAL-INET-1.-94.-2/INTERCAL/Theft.pm Sun Dec 09 19:30:08 2012 +0000 @@ -0,0 +1,312 @@ +package Language::INTERCAL::Theft; + +# Implementation of "theft protocol" for the INTERcal NETworking + +# This file is part of CLC-INTERCAL + +# Copyright (c) 2007-2008 Claudio Calvelli, all rights reserved. + +# CLC-INTERCAL is copyrighted software. However, permission to use, modify, +# and distribute it is granted provided that the conditions set out in the +# licence agreement are met. See files README and COPYING in the distribution. + +use strict; +use vars qw($VERSION $PERVERSION); +($VERSION) = ($PERVERSION = "CLC-INTERCAL/Base INTERCAL/Theft.pm 1.-94.-2") =~ /\s(\S+)$/; + +use Carp; +use Socket qw(:DEFAULT :crlf); +use FindBin qw($Bin); +use File::Spec::Functions qw(catfile); +use IO::Socket::INET; +use Language::INTERCAL::Exporter '1.-94.-2'; +use Language::INTERCAL::Splats '1.-94.-2', qw(:SP); +use Language::INTERCAL::Server '1.-94.-2'; +use Language::INTERCAL::HostIP '1.-94.-2', qw(find_interfaces); + +use constant DEFAULT_PORT => 64928; + +my $if_cache = 0; +my @if_list = (); +my %if_map = (); + +sub new { + @_ == 5 or croak "Usage: Language::INTERCAL::Theft->new" + . "(SERVER, RC, CODE, ARGS)"; + my ($class, $server, $rc, $code, $args) = @_; + my $port; + my %options = $rc->program_options('INTERNET'); + $port = exists $options{PORT} ? $options{PORT}[0] : DEFAULT_PORT; + my $host = '127.0.0.1'; + $port or faint(SP_INTERNET, $host, "INTERNET disabled by configuration"); + $@ = ''; + my $id = eval { $server->tcp_socket($host, $port) }; + if ($@) { + my $tf = 'theft-server'; + my $ftf = catfile($Bin, $tf); + -f $ftf and $tf = $ftf; + $ftf = catfile(qw(blib script), $tf); + -f $ftf and $tf = $ftf; + my @I = map { "-I$_" } @INC; + system $^X, @I, '-S', $tf, "--port=$port"; + my $timeout = 10; + while ($timeout-- > 0) { + select undef, undef, undef, 0.1; + $@ = ''; + $id = eval { $server->tcp_socket($host, $port) }; + $@ or last; + } + $@ and faint(SP_INTERNET, $host, $!); + } + my $t = bless { + server => $server, + id => $id, + host => $host, + port => $port, + broadcast => {}, + code => $code, + args => $args, + }, $class; + my $line = $t->_getline; + defined $line or faint(SP_INTERNET, $host, "Connection lost"); + $line =~ /^2/ or faint(SP_INTERNET, $host, $line); + _get_interfaces($rc); + my $lp = $server->tcp_listen(\&_open, \&_line, \&_close, $t); + $t->{victim_port} = $lp; + $t->_command("VICTIM $$ ON PORT $lp"); + $t; +} + +sub server { + @_ == 1 or croak "Usage: THEFT->server"; + my ($t) = @_; + $t->{server}; +} + +sub find_theft_servers { + @_ == 1 || @_ == 2 + or croak "Usage: THEFT->find_theft_servers[(BROADCAST)]"; + my ($t, $bcast) = @_; + if (defined $bcast) { + $bcast eq INADDR_ANY || $bcast eq INADDR_BROADCAST and $bcast = undef; + } + my $index = defined $bcast ? $bcast : ''; + return @{$t->{all_servers}{$index}} + if $t->{all_servers}{$index} && $t->{servers_valid}{$index} >= time; + # send all requests... + my $port = $t->{port}; + my @sockets = (); + my $select = ''; + for my $item (@if_list) { + my ($if, $ip, $bc) = @$item; + next if defined $bcast && $bcast ne inet_aton($bc); + my $socket = IO::Socket::INET->new( + PeerPort => $port, + Proto => 'udp', + Type => SOCK_DGRAM, + Broadcast => 1, + ReuseAddr => 1, + ) or faint(SP_INTERNET, "broadcast", $!); + defined $socket->send('x', 0, pack_sockaddr_in($port, inet_aton($bc))) + or faint(SP_INTERNET, "broadcast", $!); + vec($select, fileno($socket), 1) = 1; + push @sockets, $socket; + } + # wait for all replies, with a timeout of 5 seconds + my $list; + my %ips = (); + defined $bcast or $ips{inet_ntoa(INADDR_LOOPBACK)} = 1; + # 2 seconds should be plenty on a LAN... + my $timeout = 2; + my $limit = time + $timeout; + while ($timeout >= 0 && select($list = $select, undef, undef, $timeout)) { + for my $socket (@sockets) { + vec($list, fileno($socket), 1) or next; + my $buffer = ''; + my $ip = $socket->recv($buffer, 1, 0) + or faint(SP_INTERNET, "broadcast", $!); + my ($port, $addr) = unpack_sockaddr_in($ip); + $ips{inet_ntoa($addr)} = 1; + } + $timeout = $limit - time; + } + my @s = keys %ips; + $t->{all_servers}{$index} = \@s; + $t->{servers_valid}{$index} = time + 30; + return @s; +} + +sub make_broadcast { + @_ == 2 or croak "THEFT->make_broadcast(ADDR)"; + my ($t, $addr) = @_; + my $ip = inet_aton($addr) or return undef; + $ip eq INADDR_ANY || $ip eq INADDR_BROADCAST + and return INADDR_BROADCAST; + $ip = inet_ntoa($ip); + exists $if_map{$ip} or return undef; + return $if_map{$ip}; +} + +sub _getline { + my ($t, $id) = @_; + my $server = $t->{server}; + $id = $t->{id} if ! defined $id; + $server->progress(0); # in case I'm talking to myself + while (1) { + my $count = $server->data_count($id, 1); + defined $count or return undef; + $count and return $server->write_in($id, 0); + $server->progress(0.01); # in case I'm talking to myself + } +} + +sub _putline { + my ($t, $line, $id) = @_; + my $server = $t->{server}; + $id = $t->{id} if ! defined $id; + $server->read_out($id, $line); + $server->progress(0); # in case I'm talking to myself +} + +sub _get_interfaces { + my ($rc) = @_; + return if $if_cache; + %if_map = (); + @if_list = (); + my %options = $rc->program_options('INTERNET'); + if (keys %options) { + require Net::Netmask; + for my $name (keys %options) { + $name =~ /^DEVICE\.(.*)$/ or next; + my $if = $1; + my ($ip, $map) = @{$options{$name}}; + my $pack = inet_aton($ip) or next; + @{$map->{''}} or next; + my ($bits) = @{$map->{''}}; + my $net = new2 Net::Netmask("$ip/$bits") or next; + my $bc = $net->broadcast; + my $pbc = inet_aton($bc) or next; + $if_map{$bc} = $pbc; + push @if_list, [$if, $ip, $bc]; + $if_cache = 1; + } + } + return if $if_cache; + my $if = find_interfaces; + for my $name (keys %$if) { + $if->{$name}{addr} or next; + my $ip = inet_aton($if->{$name}{addr}) or next; + $if->{$name}{bcast} or next; + my $pbc = inet_aton($if->{$name}{bcast}) or next; + my $bc = inet_ntoa($pbc); + $if_map{$bc} = $pbc; + push @if_list, [$name, $ip, $bc]; + } + $if_cache = 1; +} + +sub _command { + @_ == 2 || @_ == 3 + or croak "Usage: THEFT->_command(COMMAND [, ID])"; + my ($t, $cmd, $id) = @_; + $t->_putline($cmd, $id); + my $reply = $t->_getline($id); + defined $reply or faint(SP_INTERNET, $t->{host}, "($cmd) Connection lost"); + $reply =~ /^2/ or faint(SP_INTERNET, $t->{host}, $reply); + $reply; +} + +sub _getlist { + @_ == 1 || @_ == 2 or croak "Usage: THEFT->_getlist [(ID)]"; + my ($t, $id) = @_; + my @list = (); + while (1) { + my $r = $t->_getline($id); + defined $r or faint(SP_INTERNET, $t->{host}, "Connection lost"); + $r eq '.' and last; + push @list, $r; + } + @list; +} + +sub _open { + my ($id, $sockhost, $peerhost, $close, $t) = @_; + return "201 INTERNET (VICTIM) on $sockhost ($VERSION)"; +} + +sub _line { + my ($server, $id, $close, $line, $t) = @_; + if ($line =~ /^\s*(STEAL|SMUGGLE)\s+(\S+)/i) { + my $code = $t->{code}; + return $code->(uc($1), $2, $id, $t, $t->{args}); + } elsif ($line =~ /^\s*THANKS/i) { + $$close = 1; + return "251 You are welcome"; + } else { + return "550 Bad request"; + } +} + +sub _close { + my ($id, $t) = @_; + # nothing to do here +} + +sub pids { + @_ == 1 || @_ == 2 or croak "Usage: THEFT->pids"; + my ($t, $server) = @_; + my $id = undef; + if (defined $server) { + $id = $t->{server}->tcp_socket($server, $t->{port}); + $t->_getline($id); + } + $t->_command("CASE PID", $id); + my @pids = map { /^(\d+)/ ? $1 : () } $t->_getlist($id); + defined $id and $t->{server}->tcp_socket_close($id); + @pids; +} + +sub start_request { + @_ == 4 or croak "Usage: THEFT->start_request(HOST, PID, TYPE)"; + my ($t, $host, $pid, $type) = @_; + $type = uc($type); + $type eq 'STEAL' || $type eq 'SMUGGLE' + or faint(SP_INTERNET, $host, "Invalid type $type"); + $t->{req_type} = $type; + my $id = $t->{server}->tcp_socket($host, $t->{port}); + $t->_getline($id); + my $port = $t->_command("CASE PORT $pid", $id); + $t->{server}->tcp_socket_close($id); + $port =~ /^520/ + and faint(SP_INTERNET, $host, "No such PID $pid"); + $port =~ /^2\d+\s+(\d+)/ + or faint(SP_INTERNET, $host, "Invalid reply $port"); + $port = $1; + my $request = $t->{server}->tcp_socket($host, $port); + $t->_getline($request); + $t->{request} = $request; + $t; +} + +sub finish_request { + @_ == 1 or croak "Usage: THEFT->end_request"; + my ($t) = @_; + exists $t->{request} or faint(SP_INTERNET, $t->{host}, "Not in request"); + my $request = $t->{request}; + $t->_putline("THANKS", $request); + $t->{server}->tcp_socket_close($request); + delete $t->{request}; + $t; +} + +sub request { + @_ == 2 or croak "Usage: THEFT->request(REGISTER)"; + my ($t, $reg) = @_; + exists $t->{req_type} or faint(SP_INTERNET, $t->{host}, "No TYPE"); + exists $t->{request} or faint(SP_INTERNET, $t->{host}, "Not in request"); + my $request = $t->{request}; + $t->_command($t->{req_type} . ' ' . $reg, $request); + return $t->_getlist($request); +} + +1;