imsg

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 10, 2020 License: ISC Imports: 6 Imported by: 0

README

go-imsg

Tests Go Report Card GoDoc License

This package provides tooling around OpenBSD's imsg functions.These imsg functions are used for inter-process communication, generally over unix sockets, and generally where the communicating processes have different privileges. Examples of imsg usage can be found in several utilities including OpenBGP and tmux.

Usage

Message Creation

The easiest way to create an imsg using this library is to call the ComposeIMsg() package method. Doing so will automatically populate the PID by calling os.Getpid() as a convenience. For example:

package main

import (
  "log"

  "github.com/schultz-is/go-imsg"
)

const MsgTypeSaySomething = 1234

func main() {
  im, err := imsg.ComposeIMsg(
    MsgTypeSaySomething,      // Type
    0,                        // Peer ID
    []byte("Hello, world!"),  // Data
  )
  if err != nil {
    log.Fatal(err)
  }

  log.Printf("%#v", im)
}
$ go run main.go
2009/11/10 23:00:00 &imsg.IMsg{Type:0x4d2, PeerID:0x0, PID:0x3, Data:[]uint8{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21}, flags:0x0}

Open in go playground

Creating an imsg can also be done manually:

package main

import (
  "log"

  "github.com/schultz-is/go-imsg"
)

const MsgTypeSaySomething = 1234

func main() {
  im := &imsg.IMsg{
    Type: MsgTypeSaySomething,
    Data: []byte("Hello, world!"),
  }

  log.Printf("%#v", im)
}
$ go run main.go
2009/11/10 23:00:00 &imsg.IMsg{Type:0x4d2, PeerID:0x0, PID:0x0, Data:[]uint8{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21}, flags:0x0}

Open in go playground

Message Serialization

To prepare an imsg for transmission across the wire, simply marshal it into a binary representation:

package main

import (
  "log"

  "github.com/schultz-is/go-imsg"
)

const MsgTypeSaySomething = 1234

func main() {
  im := &imsg.IMsg{
    Type: MsgTypeSaySomething,
    Data: []byte("Hello, world!"),
  }

  bs, err := im.MarshalBinary()
  if err != nil {
    log.Fatal(err)
  }

  log.Printf("% x", bs)
}
$ go run main.go
2009/11/10 23:00:00 d2 04 00 00 1d 00 00 00 00 00 00 00 00 00 00 00 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21

Open in go playground

Message Parsing

Reading and parsing an imsg directly from a socket is as simple as providing the socket (or any io.Reader,) to the ReadIMsg() package method:

package main

import (
  "bytes"
  "log"

  "github.com/schultz-is/go-imsg"
)

func main() {
  buf := bytes.NewReader(
    []byte{
      0xaa, 0xaa, 0xaa, 0xaa, // type
      0x1d, 0x00,             // len
      0x00, 0x00,             // flags
      0xbb, 0xbb, 0xbb, 0xbb, // peerid
      0xcc, 0xcc, 0xcc, 0xcc, // pid

      // Hello, world!
      0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
    },
  )

  im, err := imsg.ReadIMsg(buf)
  if err != nil {
    log.Fatal(err)
  }

  log.Printf("%#v", im)
}
$ go run main.go
2009/11/10 23:00:00 &imsg.IMsg{Type:0xaaaaaaaa, PeerID:0xbbbbbbbb, PID:0xcccccccc, Data:[]uint8{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21}, flags:0x0}

Open in go playground

Data Layout

An imsg is constructed of a 16 byte header followed by accompanying data of a length provided by the len field in the header. An imsg must be at least 16 bytes (this occurs when the message contains no ancillary data,) and can be no larger than the currently defined maximum of 16384 bytes. When written to disk (or across a socket,) the data is laid out as follows:

Field Type Description
type uint32_t Describes the intent of the message
len uint16_t Total length of the message (including header)
flags uint16_t (Internal use only)
peerid uint32_t Free for use by caller
pid uint32_t Free for use by caller
data void * Ancillary data accompanying the message

For a very simple example, on an amd64 system, the hexidecimal values of an imsg with small ancillary data (the word "hi") might look like so:

aa aa aa aa 12 00 00 00 bb bb bb bb cc cc cc cc 68 69

When grouped by field, that data looks like so:

aa aa aa aa  // type
12 00        // len (0x0012 / 18 bytes)
00 00        // flags
bb bb bb bb  // peerid
cc cc cc cc  // pid
68 69        // data (0x6869 / "hi")

Documentation

Documentation for this package is available at pkg.go.dev.

Documentation

Overview

Package imsg provides tools for working with OpenBSD's imsg library.

Index

Constants

View Source
const (
	// HeaderSizeInBytes is the size in bytes of the fixed-length header that
	// prepends each imsg.
	HeaderSizeInBytes = 16
	// MaxSizeInBytes is the maximum allowed size in bytes of a single imsg.
	MaxSizeInBytes = 16384
)

Variables

This section is empty.

Functions

func SystemEndianness added in v1.0.0

func SystemEndianness() binary.ByteOrder

SystemEndianness returns the determined system byte order.

Types

type ErrDataTooLarge added in v1.1.0

type ErrDataTooLarge struct {
	DataLengthInBytes int
	MaxLengthInBytes  uint16
}

ErrDataTooLarge is returned when the provided ancillary data is larger than is allowed.

func (*ErrDataTooLarge) Error added in v1.1.0

func (e *ErrDataTooLarge) Error() string

Error implements the error interface.

type ErrInsufficientData added in v1.1.0

type ErrInsufficientData struct {
	ExpectedBytes uint16
	ReadBytes     int
}

ErrInsufficientData is returned when reading an imsg produces less data than is expected.

func (*ErrInsufficientData) Error added in v1.1.0

func (e *ErrInsufficientData) Error() string

Error implements the error interface.

type ErrLengthOutOfBounds added in v1.1.0

type ErrLengthOutOfBounds struct {
	LengthInBytes    uint16
	MinLengthInBytes uint16
	MaxLengthInBytes uint16
}

ErrLengthOutOfBounds is returned when the length parameter is either smaller than the imsg header size or larger than the allowed maximum size.

func (*ErrLengthOutOfBounds) Error added in v1.1.0

func (e *ErrLengthOutOfBounds) Error() string

Error implements the error interface.

type IMsg

type IMsg struct {
	Type   uint32 // Describes the meaning of the message
	PeerID uint32 // Free for use by caller; intended to identify message sender
	PID    uint32 // Free for use by caller; intended to identify message sender
	Data   []byte // Ancillary data included with the imsg
	// contains filtered or unexported fields
}

An IMsg is a message used to aid inter-process communication over sockets, often when processes with different privileges are required to cooperate.

func ComposeIMsg

func ComposeIMsg(
	typ, peerID uint32,
	data []byte,
) (*IMsg, error)

ComposeIMsg constructs an IMsg of the provided type. If the included ancillary data is too large, an error is returned. When composing an IMsg using this function, the PID field is filled in automatically by a call to os.Getpid(). This can be overwritten as desired.

func ReadIMsg

func ReadIMsg(r io.Reader) (*IMsg, error)

ReadIMsg constructs an IMsg by reading from an io.Reader. If the incoming data is malformed, this function can block by attempting to read more data than is present.

func (*IMsg) Len

func (im *IMsg) Len() int

Len returns the size in bytes of the imsg.

func (IMsg) MarshalBinary

func (im IMsg) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface.

func (*IMsg) UnmarshalBinary added in v1.2.0

func (im *IMsg) UnmarshalBinary(data []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL