summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--Spline/Socketmap.pm103
-rw-r--r--Spline/Socketmap/Srs.pm111
-rw-r--r--Spline/Srs.pm108
-rwxr-xr-xsrs27
-rw-r--r--t/netstring.t44
-rw-r--r--t/socketmap.t132
-rw-r--r--t/srs.t184
7 files changed, 530 insertions, 179 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";
diff --git a/srs b/srs
index d7f6dfd..e8a2b32 100755
--- a/srs
+++ b/srs
@@ -4,18 +4,25 @@ use warnings;
use FindBin;
use lib $FindBin::Bin;
-use Spline::Socketmap;
-use Spline::Srs;
+use English;
+
+use Spline::Socketmap::Srs;
-$Spline::Socketmap::timeout = 10;
-$Spline::Socketmap::handler = sub {
- my ($map, $key) = @_;
- Spline::Srs::handle($map, $key);
+my $defaults = {
+ syslog_ident => 'srs',
+ log_level => 1,
};
-Spline::Socketmap->run({
- syslog_ident => 'srs',
- conf_file => ($< == 0 ? '' : $ENV{"HOME"}) . '/etc/srs.conf',
-});
+# Load config file by default if it exists
+my $config_file = '/etc/srs.conf';
+$config_file = $ENV{HOME} . $config_file if $UID != 0;
+$defaults->{conf_file} = $config_file if -e $config_file;
+
+# Default to current user (if not root)
+$defaults->{user} = $EUID if $EUID != 0;
+$defaults->{group} = $EGID if $EGID != 0;
+
+my $server = Spline::Socketmap::Srs->new();
+$server->run($defaults);
# vim: set et ts=4:
diff --git a/t/netstring.t b/t/netstring.t
new file mode 100644
index 0000000..de0b61e
--- /dev/null
+++ b/t/netstring.t
@@ -0,0 +1,44 @@
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+use Test::Exception;
+
+BEGIN {
+ use_ok 'Spline::Socketmap', qw(
+ netstring_read
+ netstring_write
+ ) or BAIL_OUT;
+}
+
+sub output($) {
+ my $data = shift;
+
+ my $output;
+ open(my $fh, '>', \$output);
+ netstring_write($fh, $data);
+ return $output;
+}
+
+sub input($) {
+ my $data = shift;
+
+ open(my $fh, '<', \$data);
+ return netstring_read($fh);
+}
+
+# Netstring output
+is(output('test'), '4:test,', 'Netstring output');
+is(output(''), '0:,', 'Empty netstring output');
+is(output('. '), '2:. ,', 'Preserve spaces');
+
+# Netstring input
+is(input('4:test,'), 'test', 'Basic netstring input');
+is(input('0:,'), '', 'Empty netstring input');
+throws_ok { input('4:test') } qr/, missing/, 'Missing final ,';
+throws_ok { input('5:test') } qr/4 of 5 bytes/, 'Data too short';
+throws_ok { input('4test') } qr/Invalid length/, 'Format error';
+throws_ok { input('') } qr/Cannot read .* length/, 'Empty input';
+throws_ok { netstring_read('') } qr/Filehandle required/, 'No filehandle';
+
+# vim: set et ts=4:
diff --git a/t/socketmap.t b/t/socketmap.t
new file mode 100644
index 0000000..a788663
--- /dev/null
+++ b/t/socketmap.t
@@ -0,0 +1,132 @@
+use strict;
+use warnings;
+
+use Test::More tests => 19;
+use Test::Exception;
+
+BEGIN {
+ use_ok 'Spline::Socketmap', qw(
+ lookup
+ handle_request
+ socketmap_protocol
+ process_request
+ ) or BAIL_OUT;
+}
+
+# Default handler
+throws_ok { lookup(undef, 'map', 'key') } qr/Not implemented/, 'No handler';
+
+my $socketmap = new Spline::Socketmap();
+
+# Parsing socketmap input
+my @args;
+{
+ no warnings 'redefine';
+ local *Spline::Socketmap::lookup = sub { shift; @args = @_; return };
+ is($socketmap->handle_request('map key with whitespace'), undef,
+ 'Protocol parsed');
+ is_deeply(\@args, ['map', 'key with whitespace'], 'Parsing with whitespace');
+}
+
+{
+ no warnings 'redefine';
+ local *Spline::Socketmap::lookup = sub { return 'OK test' };
+ is(handle_request($socketmap, 'map key'), 'OK test', 'Result passed');
+}
+
+# Socketmap protocol handling
+{
+ no warnings 'redefine';
+ local *Spline::Socketmap::lookup = sub { };
+ is($socketmap->socketmap_protocol('map key'), undef, 'Pass undef return value');
+}
+
+{
+ no warnings 'redefine';
+ local *Spline::Socketmap::lookup = sub { shift; shift; return shift };
+ is($socketmap->socketmap_protocol('map key'), 'key', 'Pass return value');
+}
+
+{
+ no warnings 'redefine';
+ local *Spline::Socketmap::lookup = sub { sleep 2 };
+ $socketmap->{server}->{timeout} = 1;
+
+ is($socketmap->socketmap_protocol('map key'), 'TEMP Timeout',
+ 'Temporary error on timeout');
+}
+
+{
+ no warnings 'redefine';
+ local *Spline::Socketmap::lookup = sub { die 'Other error' };
+ like($socketmap->socketmap_protocol('map key'), qr/TEMP Other error/,
+ 'Temporary error on die');
+}
+
+# process_request (Net::Server)
+my ($input, $output, $log);
+my $dummy = {};
+{
+ package Dummy;
+ use base 'Spline::Socketmap';
+ sub recv { return $input }
+ sub send { shift; $output = shift }
+ sub log { shift; shift; $log = shift }
+ bless $dummy, 'Dummy';
+}
+
+{
+ no warnings 'redefine';
+ local *Dummy::socketmap_protocol = sub { shift; return &{shift()} };
+
+ $input = 'OK test';
+ process_request($dummy);
+ is($output, 'OK test', 'Send output if defined (ok)');
+
+ $input = 'NOTFOUND ';
+ process_request($dummy);
+ is($output, 'NOTFOUND ', 'Send output if defined (notfound)');
+}
+
+{
+ no warnings 'redefine';
+ local *Dummy::socketmap_protocol = sub { };
+
+ process_request($dummy);
+ like($output, qr/TEMP .* error/, 'Temporary error if result is undef');
+}
+
+{
+ no warnings 'redefine';
+ local *Dummy::recv = sub { sleep 10 };
+ $dummy->{server}->{timeout} = 1;
+
+ lives_ok { process_request($dummy) } 'Temporary error with hanging input';
+ like($output, qr/TEMP Timeout/, 'Timeout on hanging input');
+}
+
+# recv
+{
+ my $recv_data;
+ open(my $fh, '<', \$recv_data);
+ local *STDIN = $fh;
+
+ $log = undef;
+ $recv_data = '7:OK test,';
+ is(Spline::Socketmap::recv($dummy), 'OK test', 'Receive works');
+ is($log, 'OK test', 'Log data after receiving');
+}
+
+# send
+{
+ my $send_data;
+ open(my $fh, '>', \$send_data);
+ local *STDOUT = $fh;
+
+ $log = undef;
+ lives_ok { Spline::Socketmap::send($dummy, 'OK test') } 'Sending works';
+ is($send_data, '7:OK test,', 'Send data in netstring format');
+ is($log, 'OK test', 'Log data before sending');
+}
+
+# vim: set et ts=4:
diff --git a/t/srs.t b/t/srs.t
index a1c8104..b6482b4 100644
--- a/t/srs.t
+++ b/t/srs.t
@@ -1,30 +1,16 @@
use strict;
use warnings;
-use Test::More tests => 39;
+use Test::More tests => 35;
use Test::Exception;
use Test::MockModule;
BEGIN {
use_ok 'Spline::Srs', qw(
- config_set
- config_get
check_exclude
- srs_forward
- srs_reverse
) or BAIL_OUT;
}
-# Test config
-my $old_value = config_get('hash_length');
-isnt($old_value, undef, 'Config value exists');
-config_set('hash_length', $old_value+1);
-isnt(config_get('hash_length'), $old_value, 'Config changed');
-
-is(config_get('invalid'), undef, 'Invalid config value is undef');
-config_set('invalid', 'test');
-is(config_get('invalid'), undef, 'Cannot set invalid config values');
-
# Testing ignores
is(check_exclude('test@example.com',['example.com']), 1, 'Ignore matching domain');
is(check_exclude('test@example.de', ['example.com']), 0, 'Do not ignore non-matching domain');
@@ -32,80 +18,120 @@ is(check_exclude('test@test.example.com', ['example.com']), 0, 'Do not ignore su
is(check_exclude('test@test.example.com', ['.example.com']), 1, 'Ignore sub-domain if requested');
# Try without config
-throws_ok { srs_forward('something@example.com') } qr/configure.*secret/, 'Secret required';
-config_set('secret', 'abc');
-throws_ok { srs_forward('something@example.com') } qr/configure.*secret/, 'Long secret required';
-config_set('secret', '12345678901234567890');
-throws_ok { srs_forward('something@example.com') } qr/configure.*alias/, 'Alias required';
-config_set('alias', 'something');
-lives_ok { srs_forward('something@example.com') } 'Config complete';
+my %config = ();
+throws_ok { new Spline::Srs(\%config) } qr/configure.*secret/, 'Secret required';
+$config{secret} = 'abc';
+throws_ok { new Spline::Srs(\%config) } qr/configure.*secret/, 'Long secret required';
+$config{secret} = '12345678901234567890';
+throws_ok { new Spline::Srs(\%config) } qr/configure.*alias/, 'Alias required';
+$config{alias} = 'something';
+lives_ok { new Spline::Srs(\%config) } 'Config complete';
# Testing ignores with config
-config_set('alias', 'domain.invalid');
-is(srs_forward('test@domain.invalid'), 'test@domain.invalid', 'Exclude alias domain by default');
-isnt(srs_forward('test@example.com'), 'test@example.com', 'Not excluding something other');
+$config{alias} = 'domain.invalid';
+{
+ my $srs = new Spline::Srs(\%config);
+ is($srs->forward('test@domain.invalid'), 'test@domain.invalid',
+ 'Exclude alias domain by default');
+ isnt($srs->forward('test@example.com'), 'test@example.com',
+ 'Not excluding something other');
+}
-config_set('excludes', ['example.com']);
-is(srs_forward('test@domain.invalid'), 'test@domain.invalid', 'Exclude alias domain by default');
-is(srs_forward('test@example.com'), 'test@example.com', 'Exclude is working');
-isnt(srs_forward('test@test.example.com'), 'test@test.example.com', 'Excluded domain should not match subdomain');
+$config{excludes} = ['example.com'];
+{
+ my $srs = new Spline::Srs(\%config);
+ is($srs->forward('test@domain.invalid'), 'test@domain.invalid',
+ 'Exclude alias domain by default');
+ is($srs->forward('test@example.com'), 'test@example.com',
+ 'Exclude is working');
+ isnt($srs->forward('test@test.example.com'), 'test@test.example.com',
+ 'Excluded domain should not match subdomain');
+}
-config_set('excludes', ['.example.com']);
-is(srs_forward('test@domain.invalid'), 'test@domain.invalid', 'Exclude alias domain by default');
-is(srs_forward('test@test.example.com'), 'test@test.example.com', 'Exclude subdomains');
-isnt(srs_forward('test@example.com'), 'test@example.com', 'Excluded subdomain should not match domain');
+$config{excludes} = ['.example.com'];
+{
+ my $srs = new Spline::Srs(\%config);
+ is($srs->forward('test@domain.invalid'), 'test@domain.invalid',
+ 'Exclude alias domain by default');
+ is($srs->forward('test@test.example.com'), 'test@test.example.com',
+ 'Exclude subdomains');
+ isnt($srs->forward('test@example.com'), 'test@example.com',
+ 'Excluded subdomain should not match domain');
+}
-config_set('excludes', ['example.com', '.example.com']);
-is(srs_forward('test@domain.invalid'), 'test@domain.invalid', 'Exclude alias domain by default');
-is(srs_forward('test@test.example.com'), 'test@test.example.com', 'Multiple excludes');
-is(srs_forward('test@example.com'), 'test@example.com', 'Multiple excludes');
+$config{excludes} = ['example.com', '.example.com'];
+{
+ my $srs = new Spline::Srs(\%config);
+ is($srs->forward('test@domain.invalid'), 'test@domain.invalid',
+ 'Exclude alias domain by default');
+ is($srs->forward('test@test.example.com'), 'test@test.example.com',
+ 'Multiple excludes');
+ is($srs->forward('test@example.com'), 'test@example.com',
+ 'Multiple excludes');
+}
# SRS Forward
-config_set('secret', '12345678901234567890');
-config_set('alias', 'domain.invalid');
-config_set('excludes', undef);
-my $result = srs_forward('alex@example.com');
-isnt($result, undef, 'Not undef');
-isnt($result, 'alex@example.com', 'Not rewritten');
-like($result, qr/^SRS0[+=-]/, 'SRS0 Prefix');
-like($result, qr/\@domain\.invalid$/, 'Rewrite to alias-Domain');
-
-# Check SRS forward an backwards
-my $address = 'alex@example.com';
-my $srs_address = srs_forward($address);
-isnt($srs_address, undef, 'Forward works');
-isnt($srs_address, $address, 'Forward works');
-is(srs_reverse($srs_address), $address, 'Reverse maps back to original');
-
-# Some checks requires a fixed time for deterministic output
+$config{secret} = '12345678901234567890';
+$config{alias} = 'domain.invalid';
+$config{excludes} = undef;
+$config{hash_length} = 6;
my ($old_srs_address, $broken);
{
- no warnings 'redefine';
- my $timestamp_create = \&Mail::SRS::timestamp_create;
- local *Mail::SRS::timestamp_create = sub {
- my $self = shift;
- $timestamp_create->($self, 1);
- };
-
- $old_srs_address = srs_forward($address);
- isnt($old_srs_address, $srs_address, 'Time should have changed');
- is($old_srs_address, 'SRS0=R0OuN1=AA=example.com=alex@domain.invalid', 'exakt match with fixed time');
-
- # Hash with broken chars
- $broken = srs_forward('nk@example.com');
- is($broken, 'SRS0=s/6g+P=AA=example.com=nk@domain.invalid', 'srs address is as expected');
+ my $srs = new Spline::Srs(\%config);
+ my $result = $srs->forward('alex@example.com');
+ isnt($result, undef, 'Not undef');
+ isnt($result, 'alex@example.com', 'Not rewritten');
+ like($result, qr/^SRS0[+=-]/, 'SRS0 Prefix');
+ like($result, qr/\@domain\.invalid$/, 'Rewrite to alias-Domain');
+
+ # Check SRS forward an backwards
+ my $address = 'alex@example.com';
+ my $srs_address = $srs->forward($address);
+ isnt($srs_address, undef, 'Forward works');
+ isnt($srs_address, $address, 'Forward works');
+ is($srs->reverse($srs_address), $address, 'Reverse maps back to original');
+
+ # Some checks requires a fixed time for deterministic output
+ {
+ no warnings 'redefine';
+ my $timestamp_create = \&Mail::SRS::timestamp_create;
+ local *Mail::SRS::timestamp_create = sub {
+ my $self = shift;
+ $timestamp_create->($self, 1);
+ };
+
+ $old_srs_address = $srs->forward($address);
+ isnt($old_srs_address, $srs_address, 'Time should have changed');
+ is($old_srs_address, 'SRS0=R0OuN1=AA=example.com=alex@domain.invalid',
+ 'exakt match with fixed time');
+
+ # Hash with broken chars
+ $broken = $srs->forward('nk@example.com');
+ is($broken, 'SRS0=s/6g+P=AA=example.com=nk@domain.invalid',
+ 'srs address is as expected');
+ }
+
+ # Timestamp should timeout (but old timestamps can be ignored)
+ throws_ok { $srs->reverse($old_srs_address) } qr/Invalid timestamp/,
+ 'Timestamp should be to old';
}
-# Timestamp should timeout (but old timestamps can be ignored)
-throws_ok { srs_reverse($old_srs_address) } qr/Invalid timestamp/, 'Timestamp should be to old';
-config_set('ignore_time', 1);
-lives_ok { srs_reverse($old_srs_address) } 'Ignore old timestamp';
-
-# libsrs_alt uses different base64 chars and should also work
-lives_ok { srs_reverse($broken) } 'Reverse with special chars';
-$broken = 'SRS0=s.6g_P=AA=example.com=nk@domain.invalid';
-throws_ok { srs_reverse($broken) } qr/Invalid hash/, 'Reverse with legacy chars';
-config_set('srsalt_fallback', 1);
-is(srs_reverse($broken), 'nk@example.com', 'Reverse with legacy chars');
+$config{'ignore_time'} = 1;
+{
+ my $srs = new Spline::Srs(\%config);
+ lives_ok { $srs->reverse($old_srs_address) } 'Ignore old timestamp';
+
+ # libsrs_alt uses different base64 chars and should also work
+ lives_ok { $srs->reverse($broken) } 'Reverse with special chars';
+ $broken = 'SRS0=s.6g_P=AA=example.com=nk@domain.invalid';
+ throws_ok { $srs->reverse($broken) } qr/Invalid hash/,
+ 'Reverse with legacy chars';
+}
+
+$config{'srsalt_fallback'} = 1;
+{
+ my $srs = new Spline::Srs(\%config);
+ is($srs->reverse($broken), 'nk@example.com', 'Reverse with legacy chars');
+}
# vim: set et ts=4: