HSL Control Structures
From Halon Security
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
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;
}

