#
# netams4-freeradius-bridge.pl
# http://www.netams.com/
#
#

use strict;
use vars qw(%RAD_REQUEST %RAD_REPLY %RAD_CHECK);
use Data::Dumper;
use IO::Socket::INET;
use IO::Select;
use MIME::Base64;

$| = 1; 

use constant    RLM_MODULE_REJECT=>    0;#  /* immediately reject the request */
use constant	RLM_MODULE_FAIL=>      1;#  /* module failed, don't reply */
use constant	RLM_MODULE_OK=>        2;#  /* the module is OK, continue */
use constant	RLM_MODULE_HANDLED=>   3;#  /* the module handled the request, so stop. */
use constant	RLM_MODULE_INVALID=>   4;#  /* the module considers the request invalid. */
use constant	RLM_MODULE_USERLOCK=>  5;#  /* reject the request (user is locked out) */
use constant	RLM_MODULE_NOTFOUND=>  6;#  /* user not found */
use constant	RLM_MODULE_NOOP=>      7;#  /* module succeeded without doing anything */
use constant	RLM_MODULE_UPDATED=>   8;#  /* OK (pairs modified) */
use constant	RLM_MODULE_NUMCODES=>  9;#  /* How many return codes there are */

sub authorize {
	&log_request_attributes;
	my $result = &call_netams4("auth", \%RAD_CHECK);
        if ($result eq 'OK') { return RLM_MODULE_OK; }
        elsif ($result eq 'FAIL') { return RLM_MODULE_REJECT; }
        else { return RLM_MODULE_NOOP; }  
}

sub accounting {
        &log_request_attributes;
        if (not defined $RAD_REQUEST{"User-Name"}) { return RLM_MODULE_NOOP; } 
        my $result = &call_netams4("acct", \%RAD_CHECK);
        if ($result eq 'OK') { return RLM_MODULE_OK; }
        elsif ($result eq 'FAIL') { return RLM_MODULE_REJECT; }
        else { return RLM_MODULE_NOOP; }
}

sub post_auth {
        &log_request_attributes;
        my $result = &call_netams4("post-auth", \%RAD_REPLY);
        if ($result eq 'OK') { return RLM_MODULE_UPDATED; }
        elsif ($result eq 'FAIL') { return RLM_MODULE_REJECT; }
        else { return RLM_MODULE_NOOP; }
}

sub detach {
	&radiusd::radlog(0,"rlm_perl::Detaching. Reloading. Done.");
}

sub log_request_attributes {
	for (keys %RAD_REQUEST) {
		&radiusd::radlog(1, "RAD_REQUEST: $_ = $RAD_REQUEST{$_}");
	}
	&radiusd::radlog(1, "RAD_REPLY: --");
	for (keys %RAD_REPLY) {
		&radiusd::radlog(1, "RAD_REPLY: $_ = $RAD_REPLY{$_}");
	}
	&radiusd::radlog(1, "RAD_CHECK: --");
	for (keys %RAD_CHECK) {
		&radiusd::radlog(1, "RAD_CHECK: $_ = $RAD_CHECK{$_}");
	}
}

sub call_netams4 {
        my $cmd=shift;
        my $onto=shift;
        my $sock = new IO::Socket::INET(PeerAddr => 'localhost', PeerPort => 20003, Proto => 'udp', Timeout => 1) or return "FAIL";
        my @request;
        for (keys %RAD_REQUEST) { push (@request, "$_=$RAD_REQUEST{$_}"); }
        my $data = join ("\n", @request);
        $data = encode_base64($data, '');
        print $sock "radius $cmd $data";
        &radiusd::radlog(1, "Q: $cmd $data");

        my $datagram;
        my @ready = IO::Select->new($sock)->can_read(3);
        if (@ready) {
                $sock->recv($datagram, 16384);
        }
        &radiusd::radlog(1, "A: $cmd $datagram");

        my $status;
        ($status, $data) = split (/ /, $datagram, 2);
        if ($status ne "OK") { return $status; }
        $data = decode_base64($data);
        my @response = split (/\s+/, $data);
        for (@response) {
                my($a,$b)=split(/=/, $_);
                $onto->{$a}=$b;
        }

        return $status;
}
