diff options
author | Alexander Sulfrian <alex@spline.inf.fu-berlin.de> | 2016-06-21 02:42:25 +0200 |
---|---|---|
committer | Alexander Sulfrian <alex@spline.inf.fu-berlin.de> | 2016-06-21 02:42:25 +0200 |
commit | 339312c92a71a0943ae34aab27b1026bb3479d0d (patch) | |
tree | cea49efa50ec436b517a25dc1d1ff90f575e2f18 /Spline | |
parent | d9ba41806745b830fd229b4cbcd8b5386203af27 (diff) | |
download | srs-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.pm | 103 | ||||
-rw-r--r-- | Spline/Socketmap/Srs.pm | 111 | ||||
-rw-r--r-- | Spline/Srs.pm | 108 |
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"; |