replaced the old nonfunctional perl SAM lib with postman's new

implementation (thanks postman!)
This commit is contained in:
jrandom
2005-11-24 09:41:01 +00:00
committed by zzz
parent dbb4b3d0c2
commit fc858bc950
7 changed files with 1030 additions and 435 deletions

922
apps/sam/perl/SAM.pm Normal file
View File

@ -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;
}