HSL Control Structures

From Halon Security

(Redirected from HSL/Core/Control Structures)
Jump to: navigation, search

In the HSL there are a few control structures available for you to build up your scripts. Most control structures are statements. Unlike an expression which is/returnes a value, a statement does not return a value. The two most common control structures are "if", for conditions, and "cache", which caches the retuned data of a function.

Contents

cache

HSL Cache

The HSL language features a application global function cache for high performance. This cache may cache the result of any HSL function, and should therefore be used with some caution, some functions are NOT intended to be cached; that is functions like Deliver(), Accept(), Reject(), CopyTo() and scan functions like ScanRPD(), ScanKAV(). If they are cached, it will cause a very strange and bad behavior. It's much easier to pinpoint which functon are suitable for caching. It's function that takes arguments and returns a value like; any dns()-function, spf(), smtp_lookup_rcpt(), smtp_lookup_auth(), http() or ldap().

  • This cache is applications global, not per -message or -connection.
  • This cache may be cleared in the SPG/VSP at Mail Gateway → Settings in the Web Administration.


cache [ cache-option, cache-option,... ] function-call()
Option Default Example Description
"ttl" 60 "ttl" => 3600 Time to Live (TTL) in seconds for the cache entry if added to the cache during the call.
"ttl_override" "ttl_override" => array(-1=>0) If you want to change the default TTL for a return value, eg. errors or "not found"
"force" false "force" => true Advanced: For the cache to be updated by this call, this may disable the "caching" if used unwisely.
"size" 32 "size" => 100 The maximum number of cache entries in the cache for this namespace + function-name.
"argv_filter" "argv_filter" => array(1, 2) Advanced: If the function to be cached takes more arguments than you would like to consider using the cache lookup.
"namespace" "" "namespace" => "special_123" Advanced: The namespace and function name makes a unique cache.
"per_message" false "per_message" => true Very Advanced: Caches a function call per message (or per session in Recipient Flow)
"lru" true "lru" => false Advanced: If the cache size limit is reached, it will remove 10% of the Least Recently Used (LRU) entires in order to be able to store new entires.

A example implementation of a dns() cache may look like this.

if (cache [ "ttl" => 3600, "size" => 1000 ] dns($senderhelo) != $senderip)
{
 Reject("sender HELO does not resolve to $ip");
}

A example implementation of a smtp_lookup_rcpt() cache may look like this. All found recipient will be cached for 24 hours, non-existent users will be cached for 5 minutes and all errors will be cached for 60 seconds.

switch(
 cache
 [
  "ttl"=>86400, "ttl_override"=>array(-1=>60, 0=>300), "size"=>4096
 ]
  smtp_lookup_rcpt("mailtransport:1", "", $recipient)
 )
{
 case 1:
  Accept();
 break;
 case 0:
  Reject("Unknown User");
 break;
 case -1:
  Accept();
//Defer("Service unavailable");
 break;
}


echo

As much as echo() is a core function, it can be used without parenthesis, making it also a statement.

Code example(s)

echo "Hello World";
echo("Hello World");

Expected result

Hello World
Hello World

if

If statements executes different statements depending on if a expression is true or false.

if(expression)
 statement

if(expression)
 statement
else
 statement

If expression is true, the first statement will be executed, if statement is false and a else statement is defined, it will be executed.

Code example(s)

if(rand(0,1)) {
 echo "returned true (1)";
} else {
 echo "returned false (0)";
}

There can be multiple if/else recursive to simulate a switch-statement.

$random = rand(0,2);
if($random == 1) {
 echo "returned true (1)";
} else if($random == 2) {
 echo "returned true (2)";
} else {
 echo "returned false (0)";
}

ternary operators

Unlike the if-statement, a tenary operator actually returns a expression/value. The syntax is therefor a little bit different than for a if-statement.

expression?if_true_expression:if_false_expression;

If expression is true, the if_true_expression will be executed and returned, if expression is false if_false_expression will be executed and returned.

Code example(s)

echo rand(0,1)?"1":"0";

foreach

The foreach construct is very similar to the ones in PHP and Perl, it gives you and easy way to iterate through an array. Its defined as following.

foreach(array_expression as $variable)
 statement

foreach(array_expression as $key => $variable)
 statement

As the array is iterated the $key value will become the value of the index and $variable will become the value of the appropriate element.

Code example

// Create an array with fruites
$list = array("Apple", "Banana", "Orange");

// Iterate through the array and print each key and value
foreach($list as $key => $fruit) {
    echo "$key = $fruit";
}

Expected result

0 = Apple
1 = Banana
2 = Orange

switch

The switch statement is very similar to the one in PHP and Perl, it's concept is very similar to building a statement with a series of IF-statements.

switch(expression) {
 case expression:
  statement
 break;
 case expression:
  statement
 break;
 default:
  statement
 break;
}

As the array is iterated the $variable will become the value of each element in the array. If you forget the "break;" after each case, it will continue to execute the code in the switch-statement.

Code example

// Create a random number between 1 and 5
$number = rand(1,5);

// Switch statement
switch($number) {
    case 1:
        echo "Number is 1";
    break;
    case 2:
        echo "Number is 2";
    break;
    default:
        echo "Number is any other value...";
    break;
}

// Matching statement with if-statements
if ($number == 1) {
    echo "Number is 1";
} else if ($number == 2) {
    echo "Number is 2";
} else {
    echo "Number is any other value...";
}

break

The break keyword can be used within foreach or a switch statements, it will skip stop the execution of the current statement and continue the next one

Code example(s)

$list = array(1,2,3,4);

foreach($list as $number) {
 // If number is 2, stop execution of this statement
 if ($number == 2)
  break;

 // Print numbers
 echo $number;
}

Expected result

1

continue

The continue keyword can be used within foreach statements, it will skip the rest of the statement and move on to the next value in the array.

Code example(s)

$list = array(1,2,3,4);

foreach($list as $number) {
 // If number is 2, continue to next item in the list..
 if ($number == 2)
  continue;

 // Print numbers
 echo $number;
}

Expected result

1
3
4

include

The include keyword allows for external/shared script among flows. A script file may be uploaded to the ftp-root. If this file is changed, it will be reloaded by all running processes.

Code examples(s)

include "file://test.hsl";

function

The function control structure allows one to write his own HSL functions. A function may return any type of value using the return keyword. Function arguments are stored in the two variables $argc (for argument count) and $argv (for argument values). The current stack restriction prevents recursive calls anywhere in the stack-trace (funcA -> funcB -> funcA = not allowed).

Code examples(s)

function myFunction
{
 echo "arguments: $argc";
 echo "argument values: $argv";
 return "Hello World";
}

echo myFunction(123, "Hello", array("123", 456));

Expected result

arguments: 3
argument values: (0=>123,1=>"Hello",2=>(0=>"123",1=>456))
Hello World

return

The return statements exits a function and returns the value of it's argument to the caller. See documentation for function.

barrier

/!\ This control structure is very cool, but in production environments please consult Halon Security's support before deployment :)

A barrier behaves like a system-wide mutually exclusive lock which only allows one processes (mailpolicyd, mailscand...) or thread to enter it's context at the time. Waiting processes are queued for execution in random order. Many independent barriers can be used by specifying different identifiers and they will be locked independently of each other.

Optionally with every barrier comes a globally owned variable ("shared memory"), which can by updated and shared among processes and threads.


Code examples(s)

barrier "identifer1" {
 // do something cool
}
barrier "identifer1" => $var {
 if (!isset($var))
  $var = 0;
 echo $var;
 $var = $var + 1;
}
Personal tools