[4/5] ids-functions.pl: Use If-Modified-Since header to reduce file downloads.

Message ID 20220322194059.3030-4-stefan.schantl@ipfire.org
State Dropped
Headers
Series [1/5] ids-functions.pl: Drop downloader code for sourcefire based ruleset. |

Commit Message

Stefan Schantl March 22, 2022, 7:40 p.m. UTC
  When using the "If-Modified-Since" header, the server can be requested
if a modified version of the file can be served.

In case that is true, the file will be sent and stored by the downloader
function. If the file has not been touched since the last time, the
server will respond with the code "304" (Not modified).

This tells us, that the current stored file is the latest one (still up-to-date)
and we safely can skip the download attempt for this provider.

Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
---
 config/cfgroot/ids-functions.pl | 38 ++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)
  

Patch

diff --git a/config/cfgroot/ids-functions.pl b/config/cfgroot/ids-functions.pl
index dfbeb1a7d..d7df41dd1 100644
--- a/config/cfgroot/ids-functions.pl
+++ b/config/cfgroot/ids-functions.pl
@@ -365,9 +365,25 @@  sub downloadruleset ($) {
 		my $tmp = File::Temp->new( SUFFIX => ".tmp", DIR => "/var/tmp/", UNLINK => 0 );
 		my $tmpfile = $tmp->filename();
 
+		# Genarate and assign file name and path to store the downloaded rules file.
+		my $dl_rulesfile = &_get_dl_rulesfile($provider);
+
+		# Load perl module to deal with file atributes.
+		use File::stat;
+
+		# Get the mtime of the rulesfile if it exists.
+		my $mtime = (stat($dl_rulesfile)->mtime) if (-f $dl_rulesfile);
+
+		# Convert the mtime into gmtime format.
+		my $gmtime = gmtime($mtime || 0);
+
 		# Pass the requested url to the downloader.
 		my $request = HTTP::Request->new(GET => $url);
 
+		# Add the If-Modified-Since header to the request, containing the omited and converted
+		# mtime of the downloaded rules file, if one is present.
+		$request->header( 'If-Modified-Since' => "$gmtime" );
+
 		my $dl_attempt = 1;
 		my $response;
 
@@ -381,6 +397,14 @@  sub downloadruleset ($) {
 				# Break loop.
 				last;
 
+			# Check if the server responds with 304 (Not Modified).
+			} elsif ($response->code == 304) {
+				# Log to syslog.
+				&_log_to_syslog("Ruleset is up-to-date, no update required.");
+
+				# Nothing to be done, the ruleset is up-to-date.
+				return;
+
 			# Check if we ran out of download re-tries.
 			} elsif ($dl_attempt eq $max_dl_attempts) {
 				# Obtain error.
@@ -406,6 +430,10 @@  sub downloadruleset ($) {
 		# Get the remote size of the downloaded file.
 		my $remote_filesize = $headers->content_length;
 
+		# Get the timestamp from header, when the file has been modified the
+		# last time.
+		my $last_modified = $headers->last_modified;
+
 		# Load perl stat module.
 		use File::stat;
 
@@ -428,9 +456,6 @@  sub downloadruleset ($) {
 			return 1;
 		}
 
-		# Genarate and assign file name and path to store the downloaded rules file.
-		my $dl_rulesfile = &_get_dl_rulesfile($provider);
-
 		# Check if a file name could be obtained.
 		unless ($dl_rulesfile) {
 			# Log error message.
@@ -449,6 +474,13 @@  sub downloadruleset ($) {
 		# Overwrite the may existing rulefile or tarball with the downloaded one.
 		move("$tmpfile", "$dl_rulesfile");
 
+		# Check if the server respond contained a last_modified value.
+		if ($last_modified) {
+			# Assign the last modified timestamp from server as mtime to the
+			# rules file.
+			utime(time(), "$last_modified", "$dl_rulesfile");
+		}
+
 		# Delete temporary file.
 		unlink("$tmpfile");