diff --git a/apps/sam/perl/Net/SAM.pm b/apps/sam/perl/Net/SAM.pm deleted file mode 100644 index 109330e1bbaf22953fdeb8451930e41e678e892a..0000000000000000000000000000000000000000 --- a/apps/sam/perl/Net/SAM.pm +++ /dev/null @@ -1,321 +0,0 @@ -#!/usr/bin/perl - -## Copyright 2004 Brian Ristuccia. This program is Free Software; -## You can redistribute it and/or modify it under the same terms as -## Perl itself. - -package Net::SAM; - -@ISA = ( "IO::Socket::INET" ); - -use strict; - -use POSIX; - -use Switch; - -use IO::Socket; -use IO::Select; - -#use Net::SAM::StreamSession; -#use Net::SAM::DatagramSession; -#use Net::SAM::RawSession; - -sub new { - my ($class) = shift; - my $type = ref($class) || $class; - my $self = $type->SUPER::new("127.0.0.1:7656"); - - - ${*$self}->{incomingraw} = []; - - # Connect us to the local SAM proxy. - # my $samsock = IO::Socket::INET->new('127.0.0.1:7657'); - #$self->{samsock}=$samsock; - - # Say hello, read response. - $self->SUPER::send("HELLO VERSION MIN=1.0 MAX=1.0\n"); - - while (! ${*$self}->{greeted}) { - $self->readprocess(); - } - print "Created SAM object\n"; - return $self; -} - -sub lookup { - my $self = shift; - my $name= shift; - - $self->SUPER::send("NAMING LOOKUP NAME=$name\n"); - undef ${*$self}->{RESULT}; - while (! ${*$self}->{RESULT}) { - $self->readprocess(); - } - if ( ${*$self}->{RESULT} == "OK" ) { - return ${*$self}->{VALUE}; - } else { - return undef; - } -} - -#sub createsession { -# my ($self) = shift; -# my ($sesstype) = shift; -# print $self->{samsock} "SESSION CREATE STYLE=$SESSTYPE DESTINATION=$DEST, DIRECTION= -#} - -#sub waitfor { -# my ($self) = shift; -# my ($prefix) = shift; -# my ($response) = <$samsock>;# - - # if $response =~ - - -#} - - -sub readprocesswrite { - my $self = shift; - $self->readprocess(); - $self->dowrite(); -} - -sub doread { - my $self = shift; - my $rv; - my $data; - - $rv = $self->recv($data, $POSIX::BUFSIZE, 0); - - if ( defined($rv) && ( length($data) >= 1 ) ) { - # We received some data. Put it in our buffer. - ${*$self}->{inbuffer} += $data; - } else { - # No data. Either we're on a non-blocking socket, or there - # was an error or EOF - if ( $!{EAGAIN} ) { - return 1; - } else { - # I suppose caller can look at $! for details - return undef; - } - } -} - - -sub dowrite { - my $self = shift; - my $rv; - my $data; - - $rv = $self->send(${*$self}->{outbuffer}, 0); - - if ( ! defined($rv) ) { - warn "SAM::dowrite - Couldn't write for no apparent reason.\n"; - return undef; - } - - if ( $rv == length(${*$self}->{outbuffer}) || $!{EWOULDBLOCK} ) { - substr(${*$self}->{outbuffer},0, $rv) = ''; # Remove from buffer - - # Nuke buffer if empty - delete ${*$self}->{outbuffer} unless length(${*$self}->{outbuffer}); - } else { - # Socket closed on us or something? - return undef; - } -} - -sub messages { - my $self = shift; - - return @{ ${*$self}->{messages} }; -} - -sub queuemessage { - - my $self = shift; - my $message = shift; - - push @{ ${*$self}->{messages} } , $message; -} - -sub unqueuemessage { - my $self = shift; - - return unshift(@{ ${*$self}->{messages} } ); - -} - -sub readprocess { - my $self = shift; - - $self->doread(); - $self->process(); -} - -sub process { - my $self = shift; - my %tvhash; - my $payload; - - - # Before we can read any new messages, if an existing message has payload - # we must read it in. Otherwise we'll create garbage messages containing - # the payload of previous messages. - - if ( ${*$self}->{payloadrequired} >= 1 ) { - - if ( length( ${*$self}->{inbuffer} ) >= ${*$self}->{payloadrequired} ) { - # Scarf payload from inbuffer into $payload - $payload = substr(${*$self}->{inbuffer}, 0, - ${*$self}->{payloadrequired}); - - # Nuke payload from inbuffer - substr(${*$self}->{inbuffer}, 0, - ${*$self}->{payloadrequired} ) = ''; - - # Put message with payload into spool - push @{ ${*$self}->{messages} } , - ${*$self}->{messagerequiringpayload}.$payload; - - # Delete the saved message requiring payload - delete ${*$self}->{messagerequiringpayload}; - } else { - # Insufficient payload in inbuffer. Try again later. - return 1; - } - - } - - - if ( ${*$self}->{inbuffer} =~ s/(.*\n)// ) { - %tvhash = $self->_hashtv($1); # Returns a tag/value hash - if ( $tvhash{SIZE} ) { - # We've got a message with payload on our hands. :( - ${*$self}->{payloadrequired} = $tvhash{SIZE}; - ${*$self}->{messagerequiringpayload} = $1; - return 1; # Could call ourself here, but we'll get called again. - } else { - push @{ ${*$self}->{messages} } , $1; - } - } - return 1; -} - -# sub junk { - - -# print "readprocess: " . $self->connected() . "\n"; - -# # May block if the SAM bridge gets hosed -# my $response = <$self>; - -# print "readprocess: $!" . $self->connected() . "\n"; - -# chomp $response; -# my ($primative, $more, $extra) = split (' ', $response, 3); - -# $primative = uc($primative); - -# print "readprocess: " . $self->connected() . " -- $primative -- $more -- $extra\n"; - -# switch ($primative) { - -# case "HELLO" { -# if ($more !~ m/REPLY/ ) { die ("Bogus HELLO response") } -# if ($extra =~ m/NOVERSION/ ) { -# die("SAM Bridge Doesn't support my version") ; -# } -# $self->_hashtv($extra); -# ${*$self}->{greeted} = 1; -# }; -# case "SESSION" { -# if ( $more !~ m/STATUS/ ) { -# die("Bogus SESSION response"); -# } -# $self->_hashtv($extra); -# } -# case "STREAM" {}; -# case "DATAGRAM" { -# if ( $more !~ m/RECEIVE/ ) { -# die("Bogus DATAGRAM response."); -# } -# $self->_hashtv($extra); -# push @{ ${*$self}->{incomingdatagram } }, -# [ ${*$self}->{DESTINATION}, -# $self->_readblock(${*$self}->{SIZE}) ]; - -# }; -# case "RAW" { -# if ( $more !~ m/RECEIVE/ ) { -# die("Bogus RAW response."); -# } -# $self->_hashtv($extra); - -# push @{ $self->{incomingraw} }, $self->_readblock($self->{SIZE}); -# }; -# case "NAMING" { -# if ( $more !~ m/REPLY/ ) { -# die("Bogus NAMING response"); -# } -# $self->_hashtv($extra); -# }; -# case "DEST" {}; -# } -# return 1; -# } - -sub getfh { - # Return the FH of the SAM socket so apps can select() or poll() on it - my $self = shift; - return $self->{samsock}; -} - -sub _readblock { - my $self = shift; - my $size = shift; - my $chunk; - my $payload; - - while ( $size > 1 ) { - # XXX: May block. No error checking. - print "readblock: $size\n"; - $size -= $self->SUPER::recv($chunk, $size); - $payload .= $chunk; - } - return $payload; -} - -sub _hashtv { - my $self = shift; - my $tvstring = shift; - my $tvhash; - - while ( $tvstring =~ m/(\S+)=(\S+)/sg ) { - $tvhash->{$1}=$2; - print "hashtv: $1=$2\n" - } - return $tvhash; -} - -sub DESTROY { - # Do nothing yet. -} - -#sub StreamSession { -# my $self = shift; -# return Net::SAM::StreamSession->new($self); -#} - -#sub DatagramSession { -# return Net::SAM::DatagramSession->new($self); -#} - -#sub RawSession { -# return Net::SAM::RawSession->new($self); -#} - -1; diff --git a/apps/sam/perl/Net/SAM/DatagramSession.pm b/apps/sam/perl/Net/SAM/DatagramSession.pm deleted file mode 100644 index ba583d0fc4335c6539d6e2a1c8600b8bac890a57..0000000000000000000000000000000000000000 --- a/apps/sam/perl/Net/SAM/DatagramSession.pm +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/perl - -package Net::SAM::DatagramSession; - -use Net::SAM; - -@ISA = ("Net::SAM"); - -sub new { - my ($class) = shift; - my ($dest , $direction, $options) = shift; - - my $self = $class->SUPER::new(@_); - - $self->SUPER::send("SESSION CREATE STYLE=DATAGRAM DESTINATION=$dest DIRECTION=$direction $options\n"); - - undef ${*$self}->{RESULT}; - - while ( ! ${*$self}->{RESULT} ) { - $self->readprocess() || return undef; - - } - - if ( ${*$self}->{RESULT} == "OK" ) { - return $self; - } else { - return undef; # sorry. - } -} - -sub send { - my $self = shift; - my $destination = shift; - my $message = shift; - my $size = length($message); - $self->SUPER::send("DATAGRAM SEND DESTINATION=$destination SIZE=$size\n$message"); -} - -sub receive { - my $self = shift; - - # Shift one off the fifo array. Returns undef if none wait. - return shift @{ $self->{incomingdatagram} }; -} - - - -1; diff --git a/apps/sam/perl/Net/SAM/RawSession.pm b/apps/sam/perl/Net/SAM/RawSession.pm deleted file mode 100644 index 971d405df065de2b6d1f6aa83e3f1483004fecb5..0000000000000000000000000000000000000000 --- a/apps/sam/perl/Net/SAM/RawSession.pm +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/perl - -package Net::SAM::RawSession; - -use Net::SAM; - -@ISA = ("Net::SAM"); - -sub new { - my ($class) = shift; - my ($dest , $direction, $options) = shift; - - my $self = $class->SUPER::new(@_); - - $self->send("SESSION CREATE STYLE=RAW DESTINATION=$dest DIRECTION=$direction $options\n"); - - undef $self->{result}; - while ( ! $self->{RESULT} ) { - $self->readprocess(); - } - - if ( $self->{RESULT} == "OK" ) { - return $self; - } else { - return 0; # sorry. - } -} - -sub send { - my $self = shift; - my $destination = shift; - my $message = shift; - my $samsock = $self->{samsock}; - my $size = length($message); - print $samsock "RAW SEND DESTINATION=$destination SIZE=$size\n$message"; -} - -sub receive { - my $self = shift; - - # Shift one off the fifo array. Returns undef if none wait. - return shift @{ $self->{incomingraw} }; -} - -1; diff --git a/apps/sam/perl/Net/SAM/StreamSession.pm b/apps/sam/perl/Net/SAM/StreamSession.pm deleted file mode 100644 index 562ec880de93f06db0df28dd7cccba1c31862b5c..0000000000000000000000000000000000000000 --- a/apps/sam/perl/Net/SAM/StreamSession.pm +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/perl - -1; diff --git a/apps/sam/perl/README b/apps/sam/perl/README new file mode 100644 index 0000000000000000000000000000000000000000..865377e625f2b9b6e1daabeeabd0e0c405f74c6f --- /dev/null +++ b/apps/sam/perl/README @@ -0,0 +1,108 @@ +# BASIC Perl SAM Module +# created 2005 by postman (postman@i2pmail.org) + + +1. What does it do? + +The SAM module is a little Perl add-on that - on one side - +establishes communication with a I2P router's (http://www.i2p.net) SAM bridge +(Simple anonymous messaging ( http://www.i2p.net/sam)). On the +other side it exposes a simple socket like interface to the user. +Over this interface the user can send or receive datastreams from I2P +destinations as if he would communicate with a normal IP socket. + +The SAM module can be integrated into perl scripts that +want to communicate with I2P services. + + +2. Is this code usable? + +This perl module should be considered as proof-of-concept +quality. It did surely work for me and my test setups, but +it might not work at all on your system. If you run into problems +you can contact me. + + +3. Does ist support DATAGRAM and RAW sessions? + +No, at the moment the module only supports STREAM sessions. +Support for other session types might be added in the future. + + +4. How to install it? +Create a Subfolder called I2P in your Perl Installation's Net Module +folder (i.e. /usr/lib/perl5/5.8.4./Net/I2P ) and copy the module there. +You can now use it with use Net::I2P::SAM. +The module only depends on Net::IO::Socket for operations. This +should be already installed. + + +5. How to debug? +You can switch on debugging with the constructor ( see below ). + + +6. How to use it? + + +$sam = new Net::I2P::SAM('127.0.0.1','7656',1); + +# you can omit host/port - then the defult is assumed +# the 3rd argument is the debugging switch. If you switched it on +# there'll be a default debug in /tmp/sam-debug +# $sam will now either contain a object reference or 0 +# if it's 0 then we coudl not talk to the SAM bridge at all ( connect failed) +# or we could not agree to a version + +# next we can tune the tunnel settings we want for this session: +# The syntax is just like the one used on www.i2p.net/sam + +$sam->change_settings("inbound.length=1,inbound.lenghVariance=0,outbound.length=1,outbound.lengthVariance=0,inbound.nickname=fun,outbound.nickname=fun"); + +# next we open a new session. +# only stream is supported +# most of the time we use a transient destination +# otherwise state the hosts.txt name you want to use as in your session +# direction is most of the times both :) +# this cab return 1 for success or 0 for failure +$sam->create_session("STREAM","TRANSIENT","BOTH"); + + +# now connect to our service +$sam->connect($sam->lookup("myservice.i2p")); + +# or + +$sam->connect("I2PDESTINATIONKEY.....AAAA"); + +# if this returns 1 - we got a stream session and can now receive and send data +# otherwise consult the debug. + + + +# Send data is just like the the raw perl send +# we send the data as scalar var and optional flags (most of the times 0) +$sam->send($data,0); + + +# receiving data is similar to the perl recv +# we define the mac number of bytes and optional flags + +$indata = $sam->receive(512,0); + +# close the session + +$sam->close(); + +# that's the most important things to know. + + + + + + + + + + + + diff --git a/apps/sam/perl/SAM.pm b/apps/sam/perl/SAM.pm new file mode 100644 index 0000000000000000000000000000000000000000..54ffdc0485593b22cbd8ba05b46a9afc82ea50a3 --- /dev/null +++ b/apps/sam/perl/SAM.pm @@ -0,0 +1,922 @@ +# Perl Basic SAM module +# created 2005 by postman (postman@i2pmail.org) +# This code is under GPL. +# This code is proof of concept + + +package Net::I2P::SAM; +require 5.001; +use strict; +use POSIX; + + +# we do not extend the IO::Socket:INET Package +# we just use it, so we keep our stuff sorted +# This is a testsetup - the next release but +# be an extension of IO::Socket::INET; + +use IO::Socket::INET; + + +use vars qw($VERSION @ISA @EXPORT); + + +sub new { + my ($this,$host,$port,$debug) = @_; + my $classname=ref($this) || $this; + my $self = {}; + bless($self,$classname); + + + #%{$self->{conf}} = The hash where we store tunnel / SAM Settings + #$self->{debug} = Whether we should output debugging lines ( this is very helpful when having problems) + #$self->{debugfile} = Where to log + #%{$self->{samreply}} = This hash stores the key=VALUE pairs of sam reply + #$self->{sock} = The INET Socket over which we talk to the SAM bridge + #$self->{inbuffer} = a simple layouted inbuffer + #$self->{outbuffer} = a simple layouted outbuffer + # ( the buffers are for dealing with differences between user wanted in/outputsizes + # and what we're able to deliver on a machine side) + #$self->{sessiontype} = unused ( will be used when we support different sessiontypes ) + #$self->{lookupresult}= contains the result of a SAM host/userhost naming lookup; + #$self->{samerror} = The human readable SAM error message ( if wanted ) + #$self->{streamid} = The virtual streamid. It will be created upon connect; + #$self->{bytestoread} = contains the reported size of a packet from sam + #$self->{bytestosend} = contains the wanted size of a packet to sam + #$self->{hasbanner} = is set to 1 when we receive a greeting string upon connect + + # + + %{$self->{conf}}=(); + if($debug==1) { + $self->{debug}=1; + } + $self->{debugfile}="/tmp/sam-debug"; + $self->{debughandle}=undef; + %{$self->{samreply}}=undef; + $self->{sock}=undef; + $self->{inbuffer}=undef; + $self->{outbuffer}=undef; + $self->{sessiontype}=undef; + $self->{lookupresult}=undef; + $self->{samerror}=undef; + $self->{streamid}=undef; + $self->{bytestoread}=undef; + $self->{bytestosend}=undef; + $self->{hasbanner}=1; + + # state == -1 (no socket exists) + # state == 0 (socket exists, but we're not helloed) + # state == 1 (socket exists, and we're helloed) + # state == 200 ( we bound a session) + # state == 250 ( we have a virtual stream) + + $self->{state}=-1; + + + # if the user has specified a host/port for contacting the SAM + # Bridge, we'll override the defaults. Otherwise we just use + # defaults. + # + if($host) { + ${$self->{conf}}{host}=$host; + } else { + ${$self->{conf}}{host}="127.0.0.1"; + } + + if($port) { + ${$self->{conf}}{port}=$port; + } else { + ${$self->{conf}}{port}=7656; + } + # defaults for the tunnelparameters + # see www.i2p.net/sam + ${$self->{conf}}{iblength}=2; + ${$self->{conf}}{oblength}=2; + ${$self->{conf}}{ibquant}=2; + ${$self->{conf}}{obquant}=2; + ${$self->{conf}}{ibbackup}=0; + ${$self->{conf}}{obbackup}=0; + ${$self->{conf}}{ibvariance}=0; + ${$self->{conf}}{obvariance}=0; + ${$self->{conf}}{iballowzero}="true"; + ${$self->{conf}}{oballowzero}="true"; + ${$self->{conf}}{ibduration}=600000; + ${$self->{conf}}{obduration}=600000; + ${$self->{conf}}{ibnickname}="SAM-Perl-Destination"; + ${$self->{conf}}{obnickname}="SAM-Perl-Destination"; + + + + # ok, let's open a simple debug file + # if the user wants us + if($self->{debug} == 1) { + if ( ! open (LOGFILE,">" .$self->{debugfile})) { + print "constructor: Cannot open debugging file, switching debugging off."; + $self->{debug}=0; + } + + # switch off the nerveracking buffer for the debugfile + select((select(LOGFILE), $| = 1)[0]); + } + + # ok now, lets move to the manager + # he manages connecting and hello + # if the proc_mgr returns 1 the user will get our + # object reference. if not, he'll just get a 0. + if($self->proc_mgr()) { + return $self; + } else { + return 0; + } + +} + +sub proc_mgr { + my ($self) = @_; + my $return=undef; + + if ($self->{state} == -1) { + $self->log("Debug: SAM::proc_mgr(): Opening Socket Connection to ".${$self->{conf}}{host}.":".${$self->{conf}}{port}); + $return=$self->sam_connect(); + if($return==0) { + return 0; + } + if($return == 1) { + $self->log("Debug: SAM::proc_mgr(): State Transition -1 => 0"); + $self->{state}=0; + } + } + + + if($self->{state}==0) { + if(!$self->hello()) { + $self->log("Debug: SAM::proc_mgr(): Closing Socket"); + $self->{sock}->close(); + $self->log("State SAM::proc_mgr(): Transition 0 => -1"); + return 0; + } + } + + return 1; + +} + + +sub change_settings { + my ($self,$list)=@_; + my (@tochange,$id,$v,$k); + + # we cannot change the settings if we have a session already + # so our state must be 1 + + if($self->{state} >1) { + $self->log("Debug: SAM::change_settings(): Cannot change tunnel settings after establishing a session."); + return 0; + } + + @tochange=split(",",$list); + foreach $id (@tochange) { + ($k,$v)=split("=",$id); + lc($v); + lc($k); + + + $self->log("Debug: SAM::change_settings(): Parsed Setting: Key: $k - Value: $v"); + if($k eq "inbound.length" || $k eq "outbound.length") { + # make v an int + $v*1; + if ($v >3 || $v < 0) { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)"); + return 0; + } + + if(${$self->{conf}}{iballowzero} eq "false" && $k eq "inbound.length" && $v==0) { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)"); + return 0; + } + + if(${$self->{conf}}{oballowzero} eq "false" && $k eq "outbound.length" && $v==0) { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)"); + return 0; + } + + if($k eq "inbound.length") { + ${$self->{conf}}{iblength}=$v; + } + if($k eq "outbound.length") { + ${$self->{conf}}{oblength}=$v; + } + + + + } + + if($k eq "inbound.quantity" || $k eq "outbound.quantity") { + $v*1; + if($v < 0 || $v >3 ) { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)"); + return 0; + } + + if ($k eq "inbound.quantity") { + ${$self->{conf}}{ibquant}=$v; + } + if($k eq "outbound.quantity") { + ${$self->{conf}}{obquant}=$v; + } + } + + if($k eq "inbound.backupquantity" || $k eq "outbound.backupquantity") { + $v*1; + if($v < 0 || $v >2 ) { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)"); + return 0; + } + + if ($k eq "inbound.backupquantity") { + ${$self->{conf}}{ibbackup}=$v; + } + if($k eq "outbound.backupquantity") { + ${$self->{conf}}{obbackup}=$v; + } + } + + if($k eq "inbound.lengthvariance" || $k eq "outbound.lengthvariance") { + $v*1; + if($v < -2 || $v >2 ) { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)"); + return 0; + } + + if ($k eq "inbound.lengthvariance") { + ${$self->{conf}}{ibvariance}=$v; + } + if($k eq "outbound.lengthvariance") { + ${$self->{conf}}{ibvariance}=$v; + } + } + + if($k eq "inbound.duration" || $k eq "outbound.duration") { + $v*1; + if($v < 300000 || $v >1200000 ) { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)"); + return 0; + } + + if ($k eq "inbound.duration") { + ${$self->{conf}}{ibduration}=$v; + } + if($k eq "outbound.duration") { + ${$self->{conf}}{obduration}=$v; + } + } + + if($k eq "inbound.nickname" || $k eq "outbound.nickname") { + $v=substr($v,0,20); + + if ($k eq "inbound.nickname") { + ${$self->{conf}}{ibnickname}=$v; + } + if($k eq "outbound.nickname") { + ${$self->{conf}}{obnickname}=$v; + } + } + + if($k eq "inbound.allowzerohop" || $k eq "outbound.allowzerohop") { + if($v ne "true" && $v ne "false") { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (must be boolean)"); + return 0; + } + + if(${$self->{conf}}{iblength} ==0 && $k eq "inbound.allowzerohop" && $v eq "false") { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)"); + return 0; + } + + if(${$self->{conf}}{oblength} == 0 && $k eq "outbound.allowzerohop" && $v eq "false") { + $self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)"); + return 0; + } + + + if ($k eq "inbound.allowzerohop") { + ${$self->{conf}}{iballowzero}=$v; + } + if($k eq "outbound.allowzerohop") { + ${$self->{conf}}{oballowzero}=$v; + } + } + + $self->log("Debug: SAM::change_settings(): Setting $k to $v"); + + + } + + return 1; + +} + + +sub hello { + my ($self) = @_; + my $greeting="HELLO VERSION MIN=1.0 MAX=1.0\n"; + my $return=undef; + my $return2=undef; + + $self->{outbuffer} .= $greeting; + $return=$self->raw_send(); + if($return == 1) { + if($self->raw_read()) { + if($self->parse_sam("HELLO")) { + $self->{state}=1; + $self->log("Debug: SAM::hello(): State Transition 0 => 1"); + delete $self->{inbuffer}; + delete $self->{outbuffer}; + return 1; + } + } else { + $self->log("Error: SAM::hello(): HELLO Failed. Cannot read HELLO response"); + return 0; + } + } + + if($return == 0) { + $self->log("Error: SAM::hello(): HELLO Failed. Cannot send HELLO String"); + return 0; + } + +} + + +sub create_session { + my ($self,$type,$destination,$direction) = @_; + my $line="SESSION CREATE "; + my $return; + + uc($type); + # WE ARE ONLY DOING STREAMS ATM + if ($type ne "STREAM") { + $self->log("Error: SAM::create_session(): SESSION failed. Only session of STREAM type are allowed"); + return 0; + } + if(length($destination)==0) { + $self->log("Warn: SAM::create_session(): SESSION: fallback setting on destination to TRANSIENT."); + $destination="TRANSIENT"; + } + + $line.="STYLE=$type DESTINATION=$destination"; + + uc($direction); + if($direction ne "BOTH" && $direction ne "CREATE" && $direction ne "RECEIVE") { + $self->log("Warn: SAM::create_session(): SESSION: fallback setting on direction to BOTH."); + $direction="BOTH"; + } + + $line .= " DIRECTION=$direction"; + $line .= " inbound.length=".${$self->{conf}}{iblength}." outbound.length=".${$self->{conf}}{oblength}; + $line .= " inbound.quantity=".${$self->{conf}}{ibquant}." outbound.quantity=".${$self->{conf}}{obquant}; + $line .= " inbound.backupQuantity=".${$self->{conf}}{ibbackup}." outbound.backupQuantity=".${$self->{conf}}{obbackup}; + $line .= " inbound.lengthVariance=".${$self->{conf}}{ibvariance}." outbound.lengthVariance=".${$self->{conf}}{obvariance}; + $line .= " inbound.duration=".${$self->{conf}}{ibduration}." outbound.duration=".${$self->{conf}}{obduration}; + $line .= " inbound.nickname=".${$self->{conf}}{ibnickname}." outbound.nickname=".${$self->{conf}}{obnickname}; + $line .= " inbound.allowZeroHop=".${$self->{conf}}{iballowzero}." outbound.allowZeroHop=".${$self->{conf}}{oballowzero}; + $line .="\n"; + + $self->{outbuffer}.=$line; + $return=$self->raw_send(); + + if($return == 1) { + if($self->raw_read()) { + if($self->parse_sam("SESSION ")) { + $self->{state}=200; + $self->log("Debug: SAM::create_session(): State Transition 1 => 200"); + # flush the whole inbuffer; + delete $self->{inbuffer}; + delete $self->{outbuffer}; + return 1; + } + } else { + $self->log("Error: SAM::create_session(): SESSION Failed. Cannot read SAM Response"); + return 0; + } + } + + if($return == 0) { + $self->log("Error: SAM::create_session(): SESSION Failed. Cannot send SESSION String"); + return 0; + } + + + +} + + + + +sub parse_sam { + + # this is the main function that parses all SAM replies + # depending on wanted action and state we'll set different + # properties like $self->{bytestoread} etc. + # parse_sam does not CUT OUT SAM messages from the payloads + # (look at $self->recv for that) + # + my ($self,$state) = @_; + my (@data,$id,$k,$v); + %{$self->{samreply}}=(); + + + uc($state); + + if( $self->{inbuffer} =~ /^(.[^ ]*) (.[^ ]*) (.*)$/m ) { + ${$self->{samreply}}{COMMAND}=$1; + ${$self->{samreply}}{REPLY}=$2; + + @data=split(" ",$3); + + foreach $id (@data) { + ($k,$v)=split("=",$id); + + #print "k: $k - v: $v\n"; + ${$self->{samreply}}{$k}=$v; + } + } else { + $self->log("Error: SAM::parse_sam(): Could not parse the SAM Reply. Has the specs changed?"); + return 0; + } + + if($state eq "HELLO") { + if (${$self->{samreply}}{COMMAND} ne "HELLO") { + $self->log("Error: SAM::parse_sam(): We're in state HELLO but got no proper response from SAM"); + return 0; + } + + if(${$self->{samreply}}{REPLY} eq "REPLY") { + if(${$self->{samreply}}{RESULT} eq "OK") { + $self->log("Debug: SAM::parse_sam(): Got a OK result for HELLO"); + return 1; + } else { + $self->log("Error :SAM::parse_sam(): Got no OK Result for HELLO"); + return 0; + } + } else { + $self->log("Error: SAM::parse_sam(): Unknown Reply type for HELLO dialogue"); + return 0; + } + } + + if($state eq "SESSION") { + if (${$self->{samreply}}{COMMAND} ne "SESSION") { + $self->log("Error: SAM::parse_sam(): We're in state SESSION but got no proper response from SAM"); + return 0; + } + + if(${$self->{samreply}}{REPLY} eq "STATUS") { + if(${$self->{samreply}}{RESULT} eq "OK") { + $self->log("Debug: SAM::parse_sam(): Got a OK result for SESSION"); + return 1; + } else { + $self->log("Error: SAM::parse_sam(): Got no OK Result for SESSION: ".${$self->{samreply}}{RESULT}); + return 0; + } + } else { + $self->log("Error: SAM::parse_sam(): Unknown Reply type for SESSION dialogue"); + return 0; + } + } + + if($state eq "NAMING") { + if (${$self->{samreply}}{COMMAND} ne "NAMING") { + $self->log("Error: SAM::parse_sam(): We're in state NAMING but got no proper response from SAM"); + return 0; + } + + if(${$self->{samreply}}{REPLY} eq "REPLY") { + if(${$self->{samreply}}{RESULT} eq "OK") { + $self->log("Debug: SAM::parse_sam(): Got a OK result for NAMING"); + $self->{lookupresult}=${$self->{samreply}}{VALUE}; + return 1; + } else { + $self->log("Error: SAM::parse_sam(): Got no OK Result for NAMING: ".${$self->{samreply}}{RESULT}); + return 0; + } + } else { + $self->log("Error: SAM::parse_sam(): Unknown Reply type for NAMING dialogue"); + return 0; + } + } + + if($state eq "STREAM") { + if (${$self->{samreply}}{COMMAND} ne "STREAM") { + $self->log("Error: SAM::parse_sam(): We're in state STREAM but got no proper response from SAM"); + return 0; + } + + # CREATING STREAMS + if(${$self->{samreply}}{REPLY} eq "STATUS") { + if(${$self->{samreply}}{RESULT} eq "OK") { + $self->log("Debug: SAM::parse_sam(): STREAM STATUS OK - Next action is awaited"); + return 1; + } else { + $self->log("Error: SAM::parse_sam(): STREAM STATUS NOT OK.: ".${$self->{samreply}}{RESULT}); + + if(length(${$self->{samreply}}{MESSAGE}) == 0) { + $self->{samerror}=${$self->{samreply}}{RESULT}; + } else { + $self->{samerror}=${$self->{samreply}}{MESSAGE}; + } + return 0; + } + } + + # SEND/RECEIVING STREAMS + # this can happen directly after a connect + if(${$self->{samreply}}{REPLY} eq "RECEIVED") { + if(${$self->{samreply}}{SIZE} > 0) { + $self->log("Debug: SAM::parse_sam(): SAM notify: RECEIVED Data. SIZE=".${$self->{samreply}}{SIZE}); + $self->{bytestoread}=${$self->{samreply}}{SIZE}; + return 1; + } else { + $self->log("Error: SAM::parse_sam(): Received empty payload"); + return 0; + } + } + + # STREAMS are closed - bad thing + # this can happen directly after a connect + if(${$self->{samreply}}{REPLY} eq "CLOSED") { + $self->log("Error: SAM::parse_sam(): Stream is closed: We need to interupt the session"); + return 0; + } + + + + } + + +} + + +sub raw_send { + + # this function sends a crafted SAM request + payload to the + # SAM socket + my ($self) = @_; + my $return; + + $self->log("Debug: SAM::raw_send(): >>> ".$self->{outbuffer}); + $return = $self->{sock}->send($self->{outbuffer},0); + if(! defined($return)) { + $self->log("Error: SAM::raw_send(): Cannot send to Socket"); + $self->close(); + return 0; + } + + if ( $return == length($self->{outbuffer}) || $!{EWOULDBLOCK} ) { + substr($self->{outbuffer},0, $return) = ''; + delete $self->{outbuffer} unless length($self->{outbuffer}); + return 1; + } else { + $self->log("Error :SAM::raw_send(): Could send, but length does not match. Closing"); + $self->close(); + return 0; + } +} + +sub raw_read { + my ($self,$size) = @_; + my $input; + my $data; + + # this reads SAM replies from the SAM socket and fills the + # inbuffer + + if(!$size || $size > POSIX::BUFSIZ) { + $size=POSIX::BUFSIZ; + } + + + $input = $self->{sock}->recv($data, $size, 0); + + if(defined($input) && length($data) >= 1) { + + + $self->log("Debug: SAM::raw_read(): <<< $data"); + if(length($self->{inbuffer}) == 0 ) { + $self->{inbuffer} = $data; + } else { + $self->{inbuffer} .= $data; + } + + return 1; + + } else { + if ( $!{EAGAIN} ) { + $self->{bytestoread}=0; + return 1; + } else { + return 0; + } + } + + + +} + +sub sam_connect { + # bsic connect to the sam bridge socket itself + my ($self)=@_; + my $return=undef; + + + + $return=$self->{sock}=IO::Socket::INET->new(${$self->{conf}}{host}.":".${$self->{conf}}{port}); + + if($return==0) { + $self->log("Debug: SAM::sam_connect(): Connection failed to ".${$self->{conf}}{host}.":".${$self->{conf}}{port}); + return 0; + } else { + $self->log("Debug: SAM::sam_connect(): Connection established to ".${$self->{conf}}{host}.":".${$self->{conf}}{port}); + return 1; + } +} + +sub send { + + # the public fumction to send data to sam + # the user gives his payload and we create + # valid SAM requests + payload from it ( as much as needed) + my ($self,$content,$flags)=@_; + my $return; + my $maxsize=(POSIX::BUFSIZ-100); + + if($self->{state}!=250) { + $self->log("Error: SAM::send(): wrong state for send command: Needed:250 Recent:".$self->{state}); + return 0; + } + # ok, what can happen? + # it could be that $content is far bigger than + # POSIX::BUFSIZ; so we need to do a little loop + # apart from that sending is in our hand + + if(length($content) > $maxsize) { + $self->{outbuffer}.="STREAM SEND ID=".$self->{streamid}." SIZE=$maxsize\n".substr($content,0,$maxsize); + $content=substr($content,$maxsize,length($content)); + } else { + $self->{outbuffer}.="STREAM SEND ID=".$self->{streamid}." SIZE=".length($content)."\n".$content; + } + + if( $self->raw_send()) { + return 1; + } else { + $self->log("Error: SAM::send(): Could not send. Closing Link"); + } +} + +sub recv { + my($self,$varname,$size,$flags)=@_; + my $return; + my $tunebuffer; + my $counter; + my $chunk; + + + # main recv wrapper. We need to invest a few thoughts + # for this. To the user it will be like a $socket->recv + # (hopefully) + # at first some sanity checks + if(!$flags) { + $flags=0; + } + + $self->{bytestoread}=0; + # size must not exceed The posix limit; + + if(!$size || $size > POSIX::BUFSIZ) { + $self->log("Warn: SAM::recv(): Setting buffersize to POSIX::BUFSIZ"); + $size=POSIX::BUFSIZ; + } + + + # nobody should call is prior to state 250 + if($self->{state}!=250) { + $self->log("Error: SAM::recv(): wrong state for rcv command: Needed:250 Recent:".$self->{state}); + return 0; + } + + + # we have a greeting banner left from connect + # flush it to the user. This can happen on several services + # like smtp/pop3/nntp but not on HTTP and other stuff + + if($self->{hasbanner}) { + + #print "D: ".$self->{inbuffer}; + + if(length($self->{inbuffer}) >0 ) { + $chunk=substr($self->{inbuffer},0, $size); + $self->{hasbanner}=0; + substr($self->{inbuffer},0, $size)=''; + return $chunk; + } else { + $self->log("Error: SAM::recv(): Should have a banner but i have empty inbuffer?"); + return 0; + } + # should never reach here + return 1; + } + # when there's something in the inbuffer left + # flush it to the user. If the amount of data is bigger than + # the userspecified limit, only transfer $size Bytes to the + # client + if(length($self->{inbuffer}) > 0) { + $chunk=substr($self->{inbuffer},0, $size); + substr($self->{inbuffer},0, $size)=''; + return $chunk; + } + + # OK, we got noting in the inbuffer + # we'll fetch a new chunk of data and then add the data to the inbuffer + # if bytestoread is bigger than POSIX::BUFSIZ we'll internally use + # a loop of reads and fill the buffer til bytestoread is 0 + + if(length($self->{inbuffer}) == 0) { + # read the first packet + if($self->raw_read()) { + if($self->parse_sam("STREAM") && ${$self->{samreply}}{REPLY} eq "RECEIVED") { + # it's possible that the packet does not contain any payload at all!! + # if this is the case we need + if($self->{inbuffer}=~/^.*$/) { + $self->log("Warn: SAM::recv(): Got only only one line from SAM - but i expected some payload too. Calling raw_read again "); + $self->raw_read(); + } + # ok, cut the SAM HEADER from the payload + $self->{inbuffer}=substr($self->{inbuffer},(length($self->{inbuffer})-$self->{bytestoread}), length($self->{inbuffer})); + $self->log("Debug: SAM::recv(): Recived a Stream Packet and cut the SAM header out"); + # ok, check if bytestoread is still bigger than our buffersize + # this means we can load more. Dangerous loop but well... + + while(length($self->{inbuffer}) < $self->{bytestoread}) { + # this should definately end some day + $counter++; + if($counter > 10000) { + $self->log("Error: SAM::recv(): WTF, could not fill inbuffer as predicted by SAM header"); + last; + } + # read as long til we have read all of the payload provided by SAM + + $self->log("Debug: SAM::recv(): Load another chunk. Buffersize:".length($self->{inbuffer})." / Bytestoread:".$self->{bytestoread} ); + $self->raw_read(); + } + + } else { + $self->log("Error: SAM::recv(): parse_sam() did not succeed. Interupting"); + delete $self->{inbuffer}; + return 0; + } + + } else { + $self->log("Error: SAM::recv(): Could not read from the socket"); + delete $self->{inbuffer}; + return 0; + } + + + $chunk=substr($self->{inbuffer},0, $size); + substr($self->{inbuffer},0, $size)=''; + return $chunk; + } + + return 1; + +} + +sub lookup { + my($self,$name)=@_; + my $return; + + $self->{outbuffer}="NAMING LOOKUP NAME=$name\n"; + $return=$self->raw_send(); + + if($return == 1) { + if($self->raw_read()) { + if($self->parse_sam("NAMING")) { + $self->log("Debug: SAM::lookup(): Naming Lookup successful"); + delete $self->{inbuffer}; + delete $self->{outbuffer}; + return $self->{lookupresult}; + } + } else { + $self->log("Error :SAM::lookup(): NAMING Failed. Cannot read SAM Response"); + return 0; + } + } + + if($return == 0) { + $self->log("Error :SAM::lookup(): NAMING Failed. Cannot send NAMING String"); + return 0; + } + +} + + +sub sam_close { + my ($self) =@_; + $self->log("Debug: SAM::sam_close(): Closing Socket to SAM"); + $self->{sock}->close(); + return 1; +} + + +sub close { + my ($self)=@_; + my $return; + $self->{outbuffer}.="STREAM CLOSE ID=".$self->{streamid}."\n"; + $self->log("Debug: SAM::close(): CLosing Stream with id: ".$self->{streamid}); + $return=$self->raw_send(); + # well, we do not care wether this worked or not + $self->sam_close(); + return 1; +} + + +sub connect { + my ($self,$destination)=@_; + my $return; + + $self->{streamid}= int(rand(200000000)); + if(length($destination) == 0) { + $self->log("Error: SAM::connect(): need I2P destination to connect to with the SAM Bridge"); + return 0; + } + + $self->{outbuffer}.="STREAM CONNECT ID=".$self->{streamid}." DESTINATION=$destination\n"; + $return=$self->raw_send(); + + if($return == 1) { + if($self->raw_read()) { + if($self->parse_sam("STREAM")) { + if(${$self->{samreply}}{REPLY} eq "STATUS") { + $self->{state}=250; + $self->log("Debug: SAM::connect(): State Transition 200 => 250"); + # flush the whole inbuffer; + delete $self->{inbuffer}; + delete $self->{outbuffer}; + return 1; + } + + if(${$self->{samreply}}{REPLY} eq "RECEIVED") { + $self->{state}=250; + $self->log("Debug: SAM::connect(): State Transition 200 => 250. Got a banner"); + delete $self->{inbuffer}; + + #print "D: toread:".$self->{bytestoread}; + #print "D: buffer:".$self->{inbuffer}."\n"; + #print "D: buffersize:".length($self->{inbuffer})."\n"; + + $self->raw_read(); + #print "D: toread:".$self->{bytestoread}; + #print "D: buffer:".$self->{inbuffer}."\n"; + #print "D: buffersize:".length($self->{inbuffer})."\n"; + + $self->{inbuffer}=substr($self->{inbuffer}, 0, $self->{bytestoread}); + $self->{hasbanner}=1; + + #print "D: toread:".$self->{bytestoread}; + #print "D: buffer:".$self->{inbuffer}."\n"; + #print "D: buffersize:".length($self->{inbuffer})."\n"; + + return 1; + } + + } + } else { + $self->log("Error: SAM::connect(): STREAM Failed. Cannot read SAM Response"); + return 0; + } + } + + if($return == 0) { + $self->log("Error: SAM::connect(): STREAM Failed. Cannot send SESSION String"); + return 0; + } + + +} + + + +sub log { + my($self,$line)=@_; + if($line=~/.*\n$/) { + chomp $line; + } + if ( $self->{debug} ) { + print LOGFILE "$line\n"; + } + return 1; + +} + + diff --git a/apps/sam/perl/samcat.pl b/apps/sam/perl/samcat.pl deleted file mode 100644 index ba766f75d3449a14e00ef1bc57a9411eda88a3bf..0000000000000000000000000000000000000000 --- a/apps/sam/perl/samcat.pl +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/perl - -use Net::SAM::RawSession; -use Net::SAM::DatagramSession; - -$sam=Net::SAM::DatagramSession->new($ARGV[0], "BOTH", "tunnels.depthInbound=0"); -print "Connected? " . $sam->connected() . "\n"; - -$me = $sam->lookup("ME"); -print "Sending to $me.\n"; -$sam->send($me,"fooquux"); - -$sam->readprocess(); -($source, $message) = @{ $sam->receive() }; -print "$source -- $message"; - - -