Complete binary serialization toolkit for Go. Code generator for binary parsing and encoding. Golang port of the original implementation for the Tor project with full bidirectional serialization support.
This is an actively maintained fork of the original trunnel project, which became unmaintained for 4+ years. This fork provides:
- β Full Parser Support - All original trunnel parsing functionality
- β
Complete Encoder Support - NEW: Binary encoding with
MarshalBinary()
- β Modern Go Modules - Updated for current Go ecosystem
- β Production Ready - Comprehensive testing and validation
- β
Standard Compliance - Implements
encoding.BinaryMarshaler
interface
Writing parsers for binary formats is error-prone and tedious. Critical security vulnerabilities are frequently found in parser code, especially in low-level languages such as C. Trunnel is a domain-specific language for describing binary formats and a parser generator for those formats.
Trunnel was initially designed and implemented by Nick Mathewson for the Tor Project. This Golang port is intended to support efforts to implement a Tor Relay in Go, with the goal of sharing trunnel files with the core Tor codebase.
Install trunnel
with
go get -u github.com/katzenpost/trunnel/cmd/trunnel
As a very simple example, we can define a color struct in trunnel as follows.
struct color {
u8 r;
u8 g;
u8 b;
};
Compile this with the trunnel
tool as follows.
trunnel build -p color color.trunnel
The result will be a Golang package called color
with a Color
type and
methods for complete bidirectional binary serialization.
This generates a complete Go package with both parsing and encoding capabilities:
// Code generated by trunnel. DO NOT EDIT.
package color
import (
"binary"
"errors"
)
type Color struct {
R uint8
G uint8
B uint8
}
// Parse method for decoding binary data
func (c *Color) Parse(data []byte) ([]byte, error) {
cur := data
{
if len(cur) < 1 {
return nil, errors.New("data too short")
}
c.R = cur[0]
cur = cur[1:]
}
{
if len(cur) < 1 {
return nil, errors.New("data too short")
}
c.G = cur[0]
cur = cur[1:]
}
{
if len(cur) < 1 {
return nil, errors.New("data too short")
}
c.B = cur[0]
cur = cur[1:]
}
return cur, nil
}
func ParseColor(data []byte) (*Color, error) {
c := new(Color)
_, err := c.Parse(data)
if err != nil {
return nil, err
}
return c, nil
}
// NEW: Encoder methods for binary serialization
func (c *Color) encodeBinary() []byte {
var buf []byte
buf = append(buf, byte(c.R))
buf = append(buf, byte(c.G))
buf = append(buf, byte(c.B))
return buf
}
func (c *Color) MarshalBinary() ([]byte, error) {
if err := c.validate(); err != nil {
return nil, err
}
return c.encodeBinary(), nil
}
func (c *Color) validate() error {
return nil
}
package main
import (
"fmt"
"log"
"github.com/katzenpost/trunnel/examples/color"
)
func main() {
// Create a color struct
original := &color.Color{R: 255, G: 128, B: 64}
// Encode to binary (NEW!)
data, err := original.MarshalBinary()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Encoded: %v\n", data) // [255 128 64]
// Decode from binary
parsed, err := color.ParseColor(data)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decoded: R=%d G=%d B=%d\n", parsed.R, parsed.G, parsed.B)
}
Integer fields support constraints that are validated during encoding:
struct date {
u16 year IN [ 1970..65535 ];
u8 month IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ];
u8 day IN [ 1,2,3..31 ];
};
// Valid date
date := &Date{Year: 2023, Month: 12, Day: 25}
data, err := date.MarshalBinary() // β
Success
// Invalid date - constraint violation
invalid := &Date{Year: 1969, Month: 13, Day: 32}
data, err := invalid.MarshalBinary() // β Returns error
Trunnel supports arrays, unions, nested structs, and more:
struct packet {
u8 type;
union payload[type] {
1: u8 byte_data;
2: u16 word_data;
3: nulterm string_data;
};
};
struct message {
u8 count;
struct packet items[count];
};
All types support full round-trip serialization with automatic validation.
- Integer Types:
u8
,u16
,u32
,u64
with big-endian encoding - Character Type:
char
(mapped to u8) - Strings:
nulterm
null-terminated strings with UTF-8 support - Arrays: Fixed
type[N]
, variabletype[field]
, and leftovertype[]
- Nested Structs: Full recursive encoding/decoding
- Unions: Conditional data based on tag fields
- Constants: Named constants and computed values
- Integer Ranges:
IN [min..max]
for range validation - Value Sets:
IN [val1, val2, val3]
for discrete values - Array Lengths: Automatic validation of array size constraints
- Union Tags: Validation of union discriminator values
Parse([]byte) ([]byte, error)
- Parse binary data into structParseStructName([]byte) (*StructName, error)
- Constructor functionMarshalBinary() ([]byte, error)
- Encode struct to binary (implementsencoding.BinaryMarshaler
)validate() error
- Validate all constraints before encodingencodeBinary() []byte
- Internal encoding without validation
- Error Handling: Comprehensive error reporting for parsing and validation
- Memory Safety: Bounds checking and safe array access
- Performance: Efficient encoding/decoding with minimal allocations
- Standard Compliance: Compatible with Go's
encoding
package interfaces - Complex Protocols: Tested with real-world protocols like SOCKS5
go get -u github.com/katzenpost/trunnel/cmd/trunnel
- Trunnel Manual - Complete language reference
- Go Package Docs - API documentation
- Examples - Working examples with tests
- CHANGELOG.md - Detailed change history
This fork is actively maintained by the Katzenpost project. The original github.com/mmcloughlin/trunnel
became unmaintained for 4+ years, so we forked it to continue development and add essential features like binary encoding.
- β All original trunnel parsing functionality
- β
Complete binary encoding with
MarshalBinary()
- β Full constraint validation system
- β Complex data types (unions, arrays, nested structs)
- β Modern Go module support
- β Comprehensive test coverage
- β Real-world protocol support (SOCKS5, etc.)
If you're using github.com/mmcloughlin/trunnel
, simply update your imports:
// Old
import "github.com/mmcloughlin/trunnel/..."
// New
import "github.com/katzenpost/trunnel/..."
All existing functionality is preserved with full backward compatibility.
We welcome contributions! Please see our contribution guidelines for details.
git clone https://github.com/katzenpost/trunnel.git
cd trunnel
go mod download
go test ./...
This project maintains the same license as the original implementation. See LICENSE for details.