summaryrefslogtreecommitdiffstats
path: root/Spline
diff options
context:
space:
mode:
authorAlexander Sulfrian <alex@spline.inf.fu-berlin.de>2016-06-21 02:42:25 +0200
committerAlexander Sulfrian <alex@spline.inf.fu-berlin.de>2016-06-21 02:42:25 +0200
commit339312c92a71a0943ae34aab27b1026bb3479d0d (patch)
treecea49efa50ec436b517a25dc1d1ff90f575e2f18 /Spline
parentd9ba41806745b830fd229b4cbcd8b5386203af27 (diff)
downloadsrs-339312c92a71a0943ae34aab27b1026bb3479d0d.tar.gz
srs-339312c92a71a0943ae34aab27b1026bb3479d0d.tar.bz2
srs-339312c92a71a0943ae34aab27b1026bb3479d0d.zip
Make subclasses of Net::Server
Spline::Socketmap is now a subclasses of Net::Server and Spline::Socketmap::Srs is a subclass of Spline::Socketmap. This way the Srs stuff is completly independent of the postfix-socketmap protocol handling. The config is located in the instances and configurable with Net::Server command line options or config file.
Diffstat (limited to 'Spline')
-rw-r--r--Spline/Socketmap.pm103
-rw-r--r--Spline/Socketmap/Srs.pm111
-rw-r--r--Spline/Srs.pm108
3 files changed, 232 insertions, 90 deletions
diff --git a/Spline/Socketmap.pm b/Spline/Socketmap.pm
index d677166..af31c31 100644
--- a/Spline/Socketmap.pm
+++ b/Spline/Socketmap.pm
@@ -2,34 +2,29 @@ package Spline::Socketmap;
use strict;
use warnings;
-use base qw(Net::Server::PreFork);
+use Scalar::Util qw( openhandle );
-use base 'Exporter';
+use base qw( Exporter Net::Server::PreFork );
our @EXPORT = qw( );
-our @EXPORT_OK = qw( );
-
-our $timeout = 10;
-our $handler = undef;
-
-
-sub call_handler($@) {
- die 'No handler configured' unless ref($handler) eq 'CODE';
-
- return &$handler(@_);
+our @EXPORT_OK = qw(
+ lookup
+ handle_request
+ netstring_read
+ netstring_write
+ process_request
+ socketmap_protocol
+);
+
+sub lookup($$$) {
+ die 'Not implemented';
}
sub handle_request($$) {
my $self = shift;
- my ($data) = @_;
+ my $data = shift;
my ($map, $key) = split(/ /, $data, 2);
- my $result = call_handler($map, $key);
- if (defined $result) {
- $self->reply($result);
- }
- else {
- $self->reply('TEMP Protocol error');
- }
+ return $self->lookup($map, $key);
}
sub netstring_read_length($) {
@@ -38,9 +33,10 @@ sub netstring_read_length($) {
local $/ = ':';
$length = <$fd>;
- die "Cannot read netstring length" unless defined($length);
+ die 'Cannot read netstring length' unless defined($length);
chomp $length;
+ die 'Invalid length' unless $length =~ m/\A\d+\z/;
return $length;
}
@@ -48,9 +44,12 @@ sub netstring_read($) {
my $fd = shift;
my ($length, $data);
+ die 'Filehandle required' unless openhandle($fd);
+
$length = netstring_read_length($fd);
if (read($fd, $data, $length) == $length) {
- (getc() eq ',') or die "Closing , missing";
+ my $char = getc($fd);
+ die "Closing , missing" if not defined $char or $char ne ',';
}
else {
die 'Received only ' . length($data) . " of $length bytes";
@@ -65,32 +64,47 @@ sub netstring_write($$) {
print $fd length($data).':'.$data.',';
}
-sub process_request($) {
+sub socketmap_protocol($$) {
my $self = shift;
+ my $input = shift;
+ my $result;
eval {
local $SIG{'ALRM'} = sub { die "Timed Out!\n" };
- alarm($timeout);
+ alarm($self->{server}->{timeout} // 0);
- $self->handle_request(netstring_read(*STDIN));
+ if (ref($input) eq 'CODE') {
+ $input = &$input;
+ }
+ $result = $self->handle_request($input);
alarm(0);
};
my $err = $@;
alarm(0);
-
if ($err) {
if ($err =~ /timed out/i) {
- $self->reply('TEMP Timeout');
+ return 'TEMP Timeout';
}
else {
chomp $err;
- $self->reply("TEMP $err");
+ return "TEMP $err";
}
}
+ else {
+ return $result;
+ }
}
-sub reply($$) {
+sub recv($) {
+ my $self = shift;
+
+ my $input = netstring_read(*STDIN);
+ $self->log(3, $input);
+ return $input;
+}
+
+sub send($$) {
my $self = shift;
my ($data) = @_;
@@ -98,6 +112,37 @@ sub reply($$) {
netstring_write(*STDOUT, $data);
}
+sub process_request($) {
+ my $self = shift;
+
+ my $recv = sub { return $self->recv() };
+ $self->send($self->socketmap_protocol($recv) // 'TEMP Protocol error');
+}
+
+sub options($$) {
+ my $self = shift;
+ my $prop = $self->{'server'};
+ my $template = shift;
+
+ $self->SUPER::options($template);
+
+ # Timeout for one request
+ $prop->{'timeout'} ||= undef;
+ $template->{'timeout'} = \$prop->{'timeout'};
+}
+
+sub post_configure_hook {
+ my $self = shift;
+ my $prop = $self->{'server'};
+
+ if (!defined($prop->{'timeout'}) || $prop->{'timeout'} !~ /^\d+$/) {
+ $prop->{'timeout'} = 10;
+ }
+ elsif ($prop->{'timeout'} < 0) {
+ $prop->{'timeout'} = 0;
+ }
+}
+
1;
# vim: set et ts=4:
diff --git a/Spline/Socketmap/Srs.pm b/Spline/Socketmap/Srs.pm
new file mode 100644
index 0000000..5917c00
--- /dev/null
+++ b/Spline/Socketmap/Srs.pm
@@ -0,0 +1,111 @@
+package Spline::Socketmap::Srs;
+
+use strict;
+use warnings;
+
+use base qw( Spline::Socketmap );
+use Spline::Srs;
+
+sub lookup($$) {
+ my $self = shift;
+ my ($map, $key) = @_;
+
+ $self->{srs}->handle($map, $key);
+}
+
+sub options($$) {
+ my $self = shift;
+ my $template = shift;
+ my $prop = $self->{server};
+
+ $self->SUPER::options($template);
+
+ # single value options
+ for my $opt (qw(alias secret_file max_age hash_length hash_min ignore_time srsalt_fallback)) {
+ $template->{$opt} = \$prop->{$opt};
+ }
+
+ # array options
+ for my $opt (qw(excludes secret)) {
+ if (! defined $prop->{$opt}) {
+ $prop->{$opt} = [];
+ } elsif (! ref $prop->{$opt}) {
+ $prop->{$opt} = [$prop->{$opt}];
+ }
+ $template->{$opt} = $prop->{$opt};
+ }
+
+}
+
+sub post_configure_hook {
+ my $self = shift;
+ my $prop = $self->{'server'};
+
+ # boolean values
+ for my $opt (qw(ignore_time srsalt_fallback)) {
+ if (defined $prop->{$opt}) {
+ $prop->{$opt} = 1;
+ }
+ else {
+ $prop->{$opt} = 0;
+ }
+ }
+
+ # secrets
+ my @secrets = ();
+ if (defined $prop->{secret_file}) {
+ if (open(my $file, '<', $prop->{secret_file})) {
+ while (<$file>) {
+ chomp;
+ push @secrets, $_;
+ }
+ close($file);
+ }
+ else {
+ $self->log(1, 'ERROR: Cannot open secret_file: "' . $prop->{secret_file} . '"');
+ }
+ }
+
+ if (defined $prop->{secret} && scalar @{$prop->{secret}} ne 0) {
+ if (scalar @secrets gt 0) {
+ $self->log(1, 'WARNING: Using both "--secret" and "--secret-file" will '.
+ 'use the first line from the file for generating hashes');
+ }
+
+ for (@{$prop->{secret}}) {
+ push @secrets, $_;
+ }
+ }
+
+ # default values
+ my %defaults = (
+ max_age => 49,
+ hash_length => 5,
+ hash_min => 5,
+ );
+
+ for my $opt (keys %defaults) {
+ if (!defined($prop->{$opt}) || $prop->{$opt} !~ m/^\d+$/ || $prop->{$opt} le 0) {
+ if (defined $prop->{$opt}) {
+ $self->log(2, "Invalid value for '$opt', using default value '$defaults{$opt}'");
+ }
+
+ $prop->{$opt} = $defaults{$opt};
+ }
+ }
+
+ $self->{srs} = new Spline::Srs({
+ alias => $prop->{alias},
+ excludes => $prop->{excludes},
+ secret => \@secrets,
+ max_age => $prop->{max_age},
+ hash_length => $prop->{hash_length},
+ hash_min => $prop->{hash_min},
+ ignore_time => $prop->{ignore_time},
+ srsalt_fallback => $prop->{srsalt_fallback},
+ });
+}
+
+1;
+
+# vim: set et ts=4:
diff --git a/Spline/Srs.pm b/Spline/Srs.pm
index e799245..597e3ed 100644
--- a/Spline/Srs.pm
+++ b/Spline/Srs.pm
@@ -8,37 +8,44 @@ use Mail::SRS;
use base 'Exporter';
our @EXPORT = qw( );
our @EXPORT_OK = qw(
- config_set
- config_get
check_exclude
- srs_forward
- srs_reverse
);
-my $config = {
- alias => '',
- excludes => [],
- secret => '',
- max_age => 49,
- hash_length => 5,
- hash_min => 5,
- ignore_time => 0,
- srsalt_fallback => 0,
-};
-
-my $srs = undef;
-
-sub config_set($$) {
- my ($key, $value) = @_;
- return unless defined $config->{$key};
-
- $config->{$key} = $value;
- $srs = undef;
-}
+sub new {
+ my $class = shift;
+ my $opts = shift;
+
+ my $active_secret;
+ if (ref $opts->{secret} eq 'ARRAY') {
+ $active_secret = $opts->{secret}->[0];
+ }
+ else {
+ $active_secret = $opts->{secret};
+ }
+
+ if (length($active_secret // '') < 20) {
+ die 'You need to configure a suitable secret';
+ }
+
+ if (!$opts->{alias}) {
+ die 'You need to configure an alias domain';
+ }
-sub config_get($) {
- my $key = shift;
- return $config->{$key};
+ my $self = {
+ alias => $opts->{alias} // '',
+ excludes => $opts->{excludes} // [],
+ srsalt_fallback => $opts->{srsalt_fallback} // 0,
+ srs => new Mail::SRS(
+ Secret => $opts->{secret} // '',
+ MaxAge => $opts->{max_age} // 49,
+ HashLength => $opts->{hash_length} // 5,
+ HashMin => $opts->{hash_min} // 5,
+ IgnoreTimestamp => $opts->{ignore_time} // 0,
+ ),
+ };
+ bless $self, $class;
+
+ return $self;
}
sub check_exclude($@) {
@@ -72,31 +79,29 @@ sub replace_srsalt_chars($) {
return $addr;
}
-sub srs_forward($) {
+sub forward($) {
+ my $self = shift;
my $addr = shift;
- return $addr if check_exclude($addr, $config->{excludes});
-
- check_configured();
- return $srs->forward($addr, $config->{alias});
+ return $addr if check_exclude($addr, $self->{excludes});
+ return $self->{srs}->forward($addr, $self->{alias});
}
-sub srs_reverse($) {
+sub reverse($) {
+ my $self = shift;
my $addr = shift;
- check_configured();
-
- if ($config->{srsalt_fallback}) {
+ if ($self->{srsalt_fallback}) {
my $result;
eval {
- $result = $srs->reverse($addr);
+ $result = $self->{srs}->reverse($addr);
};
my $err = $@;
if ($err) {
if ($err =~ m/Invalid hash/) {
my $fallback = replace_srsalt_chars($addr);
- return $srs->reverse($fallback);
+ return $self->{srs}->reverse($fallback);
}
die $err;
@@ -105,38 +110,19 @@ sub srs_reverse($) {
return $result;
}
- return $srs->reverse($addr);
-}
-
-sub check_configured() {
- return if defined $srs;
-
- if (length($config->{secret}) < 20) {
- die 'You need to configure a suitable secret';
- }
-
- if (!$config->{alias}) {
- die 'You need to configure an alias domain';
- }
-
- $srs = new Mail::SRS(
- Secret => $config->{secret},
- MaxAge => $config->{max_age},
- HashLength => $config->{hash_length},
- HashMin => $config->{hash_min},
- IgnoreTimestamp => $config->{ignore_time},
- );
+ return $self->{srs}->reverse($addr);
}
sub handle($$) {
+ my $self = shift;
my ($map, $key) = @_;
my $result;
if ($map eq 'forward') {
- $result = srs_forward($key);
+ $result = $self->forward($key);
}
elsif ($map eq 'reverse') {
- $result = srs_reverse($key);
+ $result = $self->reverse($key);
}
else {
return "PERM Invalid request";