HOME


Mini Shell 1.0
DIR:/lib64/cbpolicyd-2.1/cbp/protocols/
Upload File :
Current File : //lib64/cbpolicyd-2.1/cbp/protocols/Milter.pm
# Milter protocol support module
# Copyright (C) 2011, AllWorldIT
# Copyright (C) 2008, LinuxRulz
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


package cbp::protocols::Milter;


use strict;
use warnings;


use cbp::logging;
use awitpt::db::dblayer;
use cbp::protocols;


# User plugin info
our $pluginInfo = {
	name 			=> "Milter Protocol Suppot Module",
	init		 	=> \&init,
	priority	 	=> 75,
	protocol_init	=> \&protocol_init,
	protocol_check	=> \&protocol_check,
	protocol_parse	=> \&protocol_parse,
	protocol_response	=> \&protocol_response,
	protocol_getresponse	=> \&protocol_getresponse,
	protocol_validate	=> \&protocol_validate,
};


# Milter constants (from Sendmail-PMilter)
# Protocols
use constant SMFIA_UNKNOWN      => 'U';
use constant SMFIA_UNIX         => 'L';
use constant SMFIA_INET         => '4';
use constant SMFIA_INET6        => '6';
# Commands
use constant SMFIC_ABORT        => 'A';
use constant SMFIC_BODY         => 'B';
use constant SMFIC_CONNECT      => 'C';
use constant SMFIC_MACRO        => 'D';
use constant SMFIC_BODYEOB      => 'E';
use constant SMFIC_HELO         => 'H';
use constant SMFIC_HEADER       => 'L';
use constant SMFIC_MAIL         => 'M';
use constant SMFIC_EOH          => 'N';
use constant SMFIC_OPTNEG       => 'O';
use constant SMFIC_RCPT         => 'R';
use constant SMFIC_QUIT         => 'Q';
use constant SMFIC_DATA         => 'T'; # v4
use constant SMFIC_UNKNOWN      => 'U'; # v3
# Replies
use constant SMFIR_ADDRCPT      => '+';
use constant SMFIR_DELRCPT      => '-';
use constant SMFIR_ACCEPT       => 'a';
use constant SMFIR_REPLBODY     => 'b';
use constant SMFIR_CONTINUE     => 'c';
use constant SMFIR_DISCARD      => 'd';
use constant SMFIR_ADDHEADER    => 'h';
use constant SMFIR_INSHEADER    => 'i'; # v3, or v2 and Sendmail 8.13+
use constant SMFIR_CHGHEADER    => 'm';
use constant SMFIR_PROGRESS     => 'p';
use constant SMFIR_QUARANTINE   => 'q';
use constant SMFIR_REJECT       => 'r';
use constant SMFIR_SETSENDER    => 's';
use constant SMFIR_TEMPFAIL     => 't';
use constant SMFIR_REPLYCODE    => 'y';
# Protocol flags?
use constant SMFIP_NOCONNECT    => 0x01;
use constant SMFIP_NOHELO       => 0x02;
use constant SMFIP_NOMAIL       => 0x04;
use constant SMFIP_NORCPT       => 0x08;
use constant SMFIP_NOBODY       => 0x10;
use constant SMFIP_NOHDRS       => 0x20;
use constant SMFIP_NOEOH        => 0x40;
use constant SMFIP_NONE         => 0x7F;

# Reply constants
use constant SMFIS_CONTINUE     => 100;
use constant SMFIS_REJECT       => 101;
use constant SMFIS_DISCARD      => 102;
use constant SMFIS_ACCEPT       => 103;
use constant SMFIS_TEMPFAIL     => 104;

use constant SMFIF_ADDHDRS      => 0x01;
use constant SMFIF_CHGBODY      => 0x02;
use constant SMFIF_ADDRCPT      => 0x04;
use constant SMFIF_DELRCPT      => 0x08;
use constant SMFIF_CHGHDRS      => 0x10;
use constant SMFIF_MODBODY      => SMFIF_CHGBODY;

use constant SMFI_V1_ACTS       => SMFIF_ADDHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT;
use constant SMFI_V2_ACTS       => SMFI_V1_ACTS|SMFIF_CHGHDRS;
use constant SMFI_CURR_ACTS     => SMFI_V2_ACTS;


# Module configuration
my %config;

# Response data
my ($response,$response_data);


# Create a child specific context
sub init
{
	my $server = shift;
	my $inifile = $server->{'inifile'};

	# Defaults
	$config{'enable'} = 1;

	# Check if enabled
	if ($config{'enable'} =~ /^\s*(y|yes|1|on)\s*$/i) {
		$server->log(LOG_NOTICE,"  => Protocol(Milter): enabled");
		$config{'enable'} = 1;
	}
}



# Initialize per request data...
sub protocol_init
{
	$response = undef;
	$response_data = undef;
}



# Check the buffer to see if this protocol is what we want
sub protocol_check
{
	my ($server,$buffer) = @_;
	my $log = defined($server->{'config'}{'logging'}{'protocols'});
	

	# If we not enabled, don't do anything
	return undef if (!$config{'enable'});

	# Check that we have at least 5 bytes , len = 4 , cmd = 1
	return 0 if (length($buffer) < 5);


	# Pull off length & command
	my $rawLen = substr($buffer,0,4);
	my $rawCmd = substr($buffer,4,1);

	# Pull out len and see if its valid
	my $len = unpack('N',$rawLen);
	if ($len < 1 || $len > 131072) {
		return 0;
	}

	# Pull in raw data
	my $rawData = substr($buffer,5, $len - 1);

	# Check if this is a protocol negotiation, if it is ... WE FOUND IT!
	if ($rawCmd eq SMFIC_OPTNEG) {
		# Packet MUST be 12 bytes long
		if (length($rawData) == 12) {	
			# Process protocol
			my ($ver, $sActions, $sProtocols) = unpack('NNN', $rawData);

			# Check version
			if ($ver == 2) {
				$server->log(LOG_INFO,"[PROTOCOLS/Milter] Identified Milter protocol version '$ver'") if ($log);

				my $protocols = SMFIP_NONE;
				$protocols &= ~SMFIP_NOCONNECT; # CONNECT callback
				$protocols &= ~SMFIP_NOMAIL; # MAIL FROM callback
				$protocols &= ~SMFIP_NOHELO; # HELO callback
				$protocols &= ~SMFIP_NORCPT; # RCPT TO callback
				$protocols &= ~SMFIP_NOBODY; # BODY callback
				$protocols &= ~SMFIP_NOHDRS; # HEADER callback
				$protocols &= ~SMFIP_NOEOH; # EOH callback

				my $callback_flags = SMFI_V2_ACTS;

				# Reply...
				send_packet(SMFIC_OPTNEG, 
						pack("NNN", $ver, $callback_flags & $sActions, $protocols & $sProtocols)
				);

				return 1;
			} else {
				$server->log(LOG_INFO,"[PROTOCOLS/Milter] Unknown Milter protocol version '$ver'") if ($log);
			}

		} else {
			$server->log(LOG_INFO,"[PROTOCOLS/Milter] Milter SMFIC_OPTNEG packet wrong size") if ($log);
		}

	}


	return 0;
}



# Process buffer into sessionData
sub protocol_parse
{
	my ($server,$oldbuffer) = @_;

	my %res;


	# Buffer
	my $buffer = "";

	# Create an FDSET for use in select()
	my $fdset = "";
	vec($fdset, fileno(STDIN), 1) = 1;
	# Loop
	while (1) {
		$server->log(LOG_DEBUG,"NK DEBUG: LOOP");

		# Check for timeout....
		my $n = select($fdset,undef,undef,$server->{'server'}->{'timeout'});
		if (!$n) {
			$server->log(LOG_WARN,"[PROTOCOLS/Milter] Timeout from => Peer: ".$server->{'server'}->{'peeraddr'}.":".
					$server->{'server'}->{'peerport'}.", Local: ".$server->{'server'}->{'sockaddr'}.":".$server->{'server'}->{'sockport'});
			return;
		}

				
		# Read in 8kb
		$n = sysread(STDIN,$buffer,8192,length($buffer));
		if (!$n) {
			my $reason = defined($n) ? "Client closed connection" : "sysread[$!]";
			$server->log(LOG_WARN,"[CBPOLICYD/Milter] $reason => Peer: ".$server->{'server'}->{'peeraddr'}.":".$server->{'server'}->{'peerport'}.
					", Local: ".$server->{'server'}->{'sockaddr'}.":".$server->{'server'}->{'sockport'});
			return;
		}

		$server->log(LOG_DEBUG,"NK DEBUG: ".unpack("H*",$buffer));

#######################
		# Check that we have at least 5 bytes , len = 4 , cmd = 1
		next if (length($buffer) < 5);

		# Pull off length & command
		my $rawLen = substr($buffer,0,4);
		my $rawCmd = substr($buffer,4,1);

		# Pull out len and see if its valid
		my $len = unpack('N',$rawLen);
		if ($len < 1 || $len > 131072) {
			return 0;
		}

		# Pull in raw data
		my $rawData = substr($buffer,5, $len - 1);
		
		$server->log(LOG_DEBUG,"NK DEBUG: Len = $len, Command = $rawCmd: $rawData");

		use Data::Dumper;

		# Check packet type
		if ($rawCmd eq SMFIC_MACRO) {
			$server->log(LOG_DEBUG,"NK DEBUG: SMFIC_MACRO: ".Dumper(split_buffer($rawData)));
		}
	}


	$res{'_protocol_transport'} = "Milter";

	return \%res;
}



# Process response
sub protocol_response 
{
	my ($server,$resp,$data) = @_;

	# Not yet implemented
	return CBP_CONTINUE;
}



# Get protocol response
sub protocol_getresponse 
{
	my $resp;


	# If its undefined, set to DUNNO
	if (!defined($response)) {
		$response = "DUNNO";
	}

	# Build string we need
	$resp = "action=$response" . ( defined($response_data) ? " $response_data" : "" );

	return "$resp\n\n"
}


# Validate protocol data
sub protocol_validate
{
	my ($server,$request) = @_;
	my $log = defined($server->{'config'}{'logging'}{'protocols'});
	

	return "protocol 'Milter' not supported at present";
}



# Function to build and send a packet
sub send_packet
{
	my ($code,$data) = @_;

	# Blank data if its undefined
	$data = '' unless defined($data);

	# Encode lengh in network byte order
	my $len = pack('N', length($data) + 1);

	# Write
	syswrite(STDOUT,$len);
	syswrite(STDOUT,$code);
	syswrite(STDOUT,$data);
}



# Function to split up buffer
sub split_buffer
{
	my $buffer = shift;

	# Remove trailing NUL
	$buffer =~ s/\0$//; 
	# Split on NULL
	my @results = split(/\0/, $buffer);

	return @results;
};



1;
# vim: ts=4