Halon 5.6 with native plugins and Linux admin

Spring is approaching, and we’re getting ready to ship the next quarterly Halon MTA release, 5.6 “pingy”. For the first time, we’re shipping the web administration interface as separate Linux packages, making it more convenient to setup Halon clusters on Ubuntu and CentOS. This is great news for example if you’re using containers to build an elastic cluster for cost or scalability reasons.

The biggest news however is the new native plugin system, which complements Halon script and FFI. It allows you to add functions to the MTA written in any language compatible with the C library ABI (such as C++ and Go). In addition to adding your own HSL functions, it also enables you to work with the queue and delivery subsystems. Queue plugins can implement things like specific delivery times and customised IP warmup. Delivery plugins can be used to implement for example HTTP delivery to an object storage. Modules should employ the suspend/schedule pattern to avoid blocking, since the Halon MTA is event-based (uses asynchronous IO). This will be described in great detail in an upcoming blog post.

To give you an idea of how it works, consider the example below. It implements a simple sleep function in Go. The Halon_hsl_register function registers a sleep function in HSL. The sleep function itself dispatches a sleepTask, and immediately returns (suspends the script execution). Finally, sleepTask first sleeps, and then wakes the script up again (schedule).

// go build -buildmode c-shared -o sleep.so sleep.go
package main

// #cgo CFLAGS: -I/opt/halon/include
// #cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-all
// #include <HalonMTA.h>
import "C"
import "unsafe"
import "time"

func main() {}
func Halon_version() C.int {
    return C.HALONMTA_PLUGIN_VERSION;
}
func sleepTask(hhc *C.HalonHSLContext, ret *C.HalonHSLValue, sec float64) {
    time.Sleep(time.Duration(sec * 1000.0) * time.Millisecond);
    C.HalonMTA_hsl_schedule(hhc);
}
func sleep(hhc *C.HalonHSLContext, args *C.HalonHSLArguments, ret *C.HalonHSLValue) {
    var t C.double = 0;
    var arg0 = C.HalonMTA_hsl_argument_list(args, 0);
    if (arg0 != nil)
        C.HalonMTA_hsl_value_get(arg0, C.HALONMTA_HSL_TYPE_NUMBER, unsafe.Pointer(&t), nil);
    go sleepTask(hhc, ret, float64(t));
    C.HalonMTA_hsl_suspend_return(ptr); // go can’t use HalonMTA_hsl_suspend()
}
func Halon_hsl_register(hhrc *C.HalonHSLRegisterContext) C.bool {
    C.HalonMTA_hsl_register_function(hhrc, C.CString("sleep"), nil); // go can’t easily get the C function point of an exported method. “Nil” will make the HalonMTA_hsl_register_function use dlsym()
    return true;
}

As usual, the release is packed with other various improvements such as;

  • The FFI now provides the email body (MIME) and other File classes as a C-compatible FILE natively, making it easier to access to the modified email body when integrating with C libraries.
  • The integrated (virtual machine) package now includes Spamhaus DQS which provides additional functionality such as HBL.
  • Support for CSV schemas when using import so that imported CSV files get correctly typed out of the box.
  • Added In-Reply-To header to DSNs to support threaded bounces, which makes it easier and quicker to know which message is associated with what bounce.

See the release notes and detailed changelog for a full list of changes. We hope that you will enjoy this new quarterly release! If you have any questions, or are new to Halon, don’t hesitate to reach out!

Are you operating email services?