#!/usr/bin/perl use strict; use warnings; use Socket; # Disable buffering for STDOUT $| = 1; # Configuration my $mail_dir = '/var/mails'; my $log_file = '/var/log/smtp'; # Create directory to store emails mkdir $mail_dir unless -d $mail_dir; # Open log file for appending open(my $log_fh, '>>', $log_file) or die "Could not open log file: $!"; # Log function sub log_message { my ($message) = @_; my $timestamp = localtime; print $log_fh "[$timestamp] $message\n"; } # Get client IP address my $client_ip = get_client_ip(); log_message("Server started. Client IP: $client_ip"); # SMTP protocol handling print "220 morena.rip Perl SMTP Server\r\n"; my ($sender, $recipient, $data); while () { chomp; my $line = $_; if ($line =~ /^HELO\s+(.+)/i) { print "250 Hello $1, pleased to meet you hero\r\n"; log_message("HELO received from $1 (Client IP: $client_ip)"); } elsif ($line =~ /^EHLO\s+(.+)/i) { # If EHLO is not implemented print "502 Command not implemented\r\n"; log_message("EHLO received but not implemented (Client IP: $client_ip)"); } elsif ($line =~ /^MAIL FROM:\s*(<)?(.+?)(>)?\s*$/i) { $sender = $2; print "250 Sender $sender OK\r\n"; log_message("MAIL FROM received: $sender (Client IP: $client_ip)"); } elsif ($line =~ /^RCPT TO:\s*(<)?(.+?)(>)?\s*$/i) { my $recipient_email = $2; # Extract the email address if ($recipient_email =~ /\@morena\.rip$/i) { $recipient = $recipient_email; print "250 Recipient $recipient OK\r\n"; log_message("RCPT TO received: $recipient (Client IP: $client_ip)"); } else { print "550 Error: Recipient must be from the domain morena.rip\r\n"; log_message("RCPT TO rejected: Invalid domain for $recipient_email (Client IP: $client_ip)"); } } elsif ($line =~ /^DATA/i) { # Check if sender and recipient are defined if (!defined $sender || !defined $recipient) { print "550 Error: Missing sender or recipient\r\n"; log_message("DATA command rejected: Missing sender or recipient (Client IP: $client_ip)"); next; # Skip processing DATA } print "354 Enter mail, end with . on a line by itself\r\n"; log_message("DATA command received (Client IP: $client_ip)"); $data = ''; while () { last if /^\.\r?\n/; $data .= $_; } print "250 Message accepted for delivery\r\n"; log_message("Email data received (Client IP: $client_ip)"); # Normalize line endings (remove \r) $data =~ s/\r\n/\n/g; # Save the email to a file my $filename = "$mail_dir/mail_" . time() . ".eml"; open(my $fh, '>', $filename) or do { log_message("Error: Could not open file $filename: $! (Client IP: $client_ip)"); next; }; print $fh "From: $sender\nTo: $recipient\n$data"; close($fh); log_message("Email saved to $filename (Client IP: $client_ip)"); # Reset state for the next email $sender = undef; $recipient = undef; $data = undef; } elsif ($line =~ /^QUIT/i) { print "221 Bye\r\n"; log_message("QUIT command received (Client IP: $client_ip)"); last; } else { print "500 Command not recognized\r\n"; log_message("Unknown command received: $line (Client IP: $client_ip)"); } } # Close log file close($log_fh); # Function to get client IP address sub get_client_ip { # Get the peer address from STDIN my $peeraddr = getpeername(STDIN) or return 'unknown'; my ($port, $ip) = sockaddr_in($peeraddr); return inet_ntoa($ip); }