---
name: "osmtpd_run"
section: "3"
platform: "linux"
package: "libopensmtpd"
version: "1.0-2"
release: "Arch Linux 2026-11-01"
platforms: ["linux"]
url: "https://new.carta.tech/osmtpd_run"
---

# osmtpd\_run(3)

C filter API for smtpd(8)

## NAME

**osmtpd\_register\_filter\_connect**, **osmtpd\_register\_filter\_helo**, **osmtpd\_register\_filter\_ehlo**, **osmtpd\_register\_filter\_starttls**, **osmtpd\_register\_filter\_auth**, **osmtpd\_register\_filter\_mailfrom**, **osmtpd\_register\_filter\_rcptto**, **osmtpd\_register\_filter\_data**, **osmtpd\_register\_filter\_dataline**, **osmtpd\_register\_filter\_rset**, **osmtpd\_register\_filter\_quit**, **osmtpd\_register\_filter\_noop**, **osmtpd\_register\_filter\_help**, **osmtpd\_register\_filter\_wiz**, **osmtpd\_register\_filter\_commit**, **osmtpd\_register\_report\_connect**, **osmtpd\_register\_report\_disconnect**, **osmtpd\_register\_report\_identify**, **osmtpd\_register\_report\_tls**, **osmtpd\_register\_report\_auth**, **osmtpd\_register\_report\_reset**, **osmtpd\_register\_report\_begin**, **osmtpd\_register\_report\_mail**, **osmtpd\_register\_report\_rcpt**, **osmtpd\_register\_report\_envelope**, **osmtpd\_register\_report\_data**, **osmtpd\_register\_report\_commit**, **osmtpd\_register\_report\_rollback**, **osmtpd\_register\_report\_client**, **osmtpd\_register\_report\_server**, **osmtpd\_register\_report\_response**, **osmtpd\_register\_report\_timeout**, **osmtpd\_local\_session**, **osmtpd\_local\_message**, **osmtpd\_need**, **osmtpd\_filter\_proceed**, **osmtpd\_filter\_reject**, **osmtpd\_filter\_disconnect**, **osmtpd\_filter\_rewrite**, **osmtpd\_filter\_dataline**, **osmtpd\_run**, **osmtpd\_warn**, **osmtpd\_warnx**, **osmtpd\_err**, **osmtpd\_errx** - C filter API for [smtpd(8)](/smtpd/8.md)

## SYNOPSIS

**#include \<opensmtpd.h\>**

*void*\

**osmtpd\_register\_filter\_connect**(*int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*hostname, struct sockaddr\_storage \*ss)*);

*void*\

**osmtpd\_register\_filter\_helo**(*int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*helo)*);

*void*\

**osmtpd\_register\_filter\_ehlo**(*int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*ehlo)*);

*void*\

**osmtpd\_register\_filter\_starttls**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_filter\_auth**(*int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*auth)*);

*void*\

**osmtpd\_register\_filter\_mailfrom**(*int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*from)*);

*void*\

**osmtpd\_register\_filter\_rcptto**(*int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*rcpt)*);

*void*\

**osmtpd\_register\_filter\_data**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_filter\_dataline**(*int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*line)*);

*void*\

**osmtpd\_register\_filter\_rset**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_filter\_quit**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_filter\_noop**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_filter\_help**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_filter\_wiz**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_filter\_commit**(*int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_report\_connect**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*rdns, enum osmtpd\_status fcrdns, struct sockaddr\_storage \*src, struct sockaddr\_storage \*dst)*);

*void*\

**osmtpd\_register\_report\_disconnect**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_register\_report\_identify**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*identity)*);

*void*\

**osmtpd\_register\_report\_tls**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*ciphers)*);

*void*\

**osmtpd\_register\_report\_auth**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*username, enum osmtpd\_auth\_status status)*);

*void*\

**osmtpd\_register\_report\_reset**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid)*);

*void*\

**osmtpd\_register\_report\_begin**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid)*);

*void*\

**osmtpd\_register\_report\_mail**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid, const char \*mailfrom, enum osmtpd\_status status)*);

*void*\

**osmtpd\_register\_report\_rcpt**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid, const char \*rcptto, enum osmtpd\_status status)*);

*void*\

**osmtpd\_register\_report\_envelope**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid, uint64\_t evpid)*);

*void*\

**osmtpd\_register\_report\_data**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid, enum osmtpd\_status status)*);

*void*\

**osmtpd\_register\_report\_commit**(*int*, *incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid, size\_t msgsz)*);

*void*\

**osmtpd\_register\_report\_rollback**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, uint32\_t msgid)*);

*void*\

**osmtpd\_register\_report\_client**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*cmd)*);

*void*\

**osmtpd\_register\_report\_server**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*resp)*);

*void*\

**osmtpd\_register\_report\_response**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx, const char \*resp)*);

*void*\

**osmtpd\_register\_report\_timeout**(*int incoming*, *int (\*cb)(struct osmtpd\_ctx \*ctx)*);

*void*\

**osmtpd\_local\_session**(*void \*(\*oncreate)(struct osmtpd\_ctx \*ctx)*, *void (\*ondelete)(struct osmtpd\_ctx \*ctx, void \*data)*);

*void*\

**osmtpd\_local\_message**(*void \*(\*oncreate)(struct osmtpd\_ctx \*ctx)*, *void (\*ondelete)(struct osmtpd\_ctx \*ctx, void \*data)*);

*void*\

**osmtpd\_need**(*int needs*);

*void*\

**osmtpd\_filter\_proceed**(*struct osmtpd\_ctx \*ctx*);

*void*\

**osmtpd\_filter\_reject**(*struct osmtpd\_ctx \*ctx*, *int error*, *const char \*msg*, *...*);

*void*\

**osmtpd\_filter\_disconnect**(*struct osmtpd\_ctx \*ctx*, *const char \*msg*, *...*);

*void*\

**osmtpd\_filter\_rewrite**(*struct osmtpd\_ctx \*ctx*, *const char \*value*, *...*);

*void*\

**osmtpd\_filter\_dataline**(*struct osmtpd\_ctx \*ctx*, *const char \*line*, *...*);

*void*\

**osmtpd\_run**(*void*);

*void*\

**osmtpd\_warn**(*struct osmtpd\_ctx \*ctx*, *const char \*fmt*, *...*);

*void*\

**osmtpd\_warnx**(*struct osmtpd\_ctx \*ctx*, *const char \*fmt*, *...*);

*void*\

**osmtpd\_err**(*int eval*, *const char \*fmt*, *...*);

*void*\

**osmtpd\_errx**(*int eval*, *const char \*fmt*, *...*);

## DESCRIPTION

The **osmtpd** API is an event based interface for writing [smtpd(8)](/smtpd/8.md) filters. Filter and report callbacks are registered via the **osmtpd\_register** class of functions, followed by **osmtpd\_run**. A callback should return 0 in the case of success, or -1 in the case of error which leads to disconnect of this session.

**osmtpd\_run** starts the communication with the server and transforms network queries to callbacks. Internally it uses event\_dispatch(3), which allows filters to be written fully asynchronously.

Each callback *cb* gets at least a pointer of the type *struct*, *osmtpd\_ctx*. It contains the following elements:

- *enum osmtpd\_type type*

  The type of request being made. The possible values are OSMTPD\_TYPE\_FILTER and OSMTPD\_TYPE\_REPORT.

- *enum osmtpd\_phase* *phase*

  The phase in the transaction which triggered the callback. The following values match their respective **osmtpd\_register** function:

- OSMTPD\_PHASE\_CONNECT

  **osmtpd\_register\_filter\_connect**

- OSMTPD\_PHASE\_HELO

  **osmtpd\_register\_filter\_helo**

- OSMTPD\_PHASE\_EHLO

  **osmtpd\_register\_filter\_ehlo**

- OSMTPD\_PHASE\_STARTTLS

  **osmtpd\_register\_filter\_starttls**

- OSMTPD\_PHASE\_AUTH

  **osmtpd\_register\_filter\_auth**

- OSMTPD\_PHASE\_MAIL\_FROM

  **osmtpd\_register\_filter\_mailfrom**

- OSMTPD\_PHASE\_RCPT\_TO

  **osmtpd\_register\_filter\_rcptto**

- OSMTPD\_PHASE\_DATA

  **osmtpd\_register\_filter\_data**

- OSMTPD\_PHASE\_DATA\_LINE

  **osmtpd\_register\_filter\_dataline**

- OSMTPD\_PHASE\_RSET

  **osmtpd\_register\_filter\_rset**

- OSMTPD\_PHASE\_QUIT

  **osmtpd\_register\_filter\_quit**

- OSMTPD\_PHASE\_NOOP

  **osmtpd\_register\_filter\_noop**

- OSMTPD\_PHASE\_HELP

  **osmtpd\_register\_filter\_help**

- OSMTPD\_PHASE\_WIZ

  **osmtpd\_register\_filter\_wiz**

- OSMTPD\_PHASE\_COMMIT

  **osmtpd\_register\_filter\_commit**

- OSMTPD\_PHASE\_LINK\_CONNECT

  **osmtpd\_register\_report\_connect**

- OSMTPD\_PHASE\_LINK\_DISCONNECT

  **osmtpd\_register\_report\_disconnect**

- OSMTPD\_PHASE\_LINK\_IDENTIFY

  **osmtpd\_register\_report\_identify**

- OSMTPD\_PHASE\_LINK\_TLS

  **osmtpd\_register\_report\_tls**

- OSMTPD\_PHASE\_LINK\_AUTH

  **osmtpd\_register\_report\_auth**

- OSMTPD\_PHASE\_TX\_RESET

  **osmtpd\_register\_report\_reset**

- OSMTPD\_PHASE\_TX\_BEGIN

  **osmtpd\_register\_report\_begin**

- OSMTPD\_PHASE\_TX\_MAIL

  **osmtpd\_register\_report\_mail**

- OSMTPD\_PHASE\_TX\_RCPT

  **osmtpd\_register\_report\_rcpt**

- OSMTPD\_PHASE\_TX\_ENVELOPE

  **osmtpd\_register\_report\_envelope**

- OSMTPD\_PHASE\_TX\_DATA

  **osmtpd\_register\_report\_data**

- OSMTPD\_PHASE\_TX\_COMMIT

  **osmtpd\_register\_report\_commit**

- OSMTPD\_PHASE\_TX\_ROLLBACK

  **osmtpd\_register\_report\_rollback**

- OSMTPD\_PHASE\_PROTOCOL\_CLIENT

  **osmtpd\_register\_report\_client**

- OSMTPD\_PHASE\_PROTOCOL\_SERVER

  **osmtpd\_register\_report\_server**

- OSMTPD\_PHASE\_FILTER\_RESPONSE

  **osmtpd\_register\_report\_response**

- OSMTPD\_PHASE\_TIMEOUT.

  **osmtpd\_register\_report\_timeout**

- *int* *version\_major*

  The major version number of the protocol. Most filters don't need this information.

- *int* *version\_minor*

  The minor version number of the protocol. Most filters don't need this information.

- *struct timespec* *tm*

  The time the event was triggered inside [smtpd(8)](/smtpd/8.md).

- *int* *incoming*

  Set to 1 if the event was based on an incoming connection, 0 if it's an outgoing connection. The **osmtpd\_register\_filter** class of functions is always based on incoming connections. **osmtpd\_register\_report** can be both incoming and outgoing.

- *uint64\_t* *reqid*

  The request ID of the connection the event was issued on. This value can be useful for logging. Filters in need of filter specific data can use **osmtpd\_local\_session** and *local\_session*.

- *uint64\_t* *token*

  The filter specific token. Most filters don't need this information.

- *struct sockaddr\_storage* *src*

  The source address and port of the connection. This needs to be cast to the appropriate sockaddr type based on the *ss\_family* attribute. It can have the following families: AF\_INET, AF\_INET6 and AF\_UNIX. To use this attribute, initialize **osmtpd\_need** with OSMTPD\_NEED\_SRC. If not available the entire attribute is zeroed out.

- *struct sockaddr\_storage* *dst*

  The destination address and port of the connection. This needs to be cast to the appropriate sockaddr type based on the *ss\_family* attribute. It can have the following families: AF\_INET, AF\_INET6 and AF\_UNIX. To use this attribute, initialize **osmtpd\_need** with OSMTPD\_NEED\_DST. If not available the entire attribute is zeroed out.

- *char* *\*rdns*

  The reverse DNS hostname of the connection. To use this attribute, initialize **osmtpd\_need** with OSMTPD\_NEED\_RDNS. If not available the attribute is set to NULL.

- *enum osmtpd\_status* *fcrdns*

  Whether the reverse DNS hostname is forward confirmed. To use this attribute, initialize **osmtpd\_need** with OSMTPD\_NEED\_FCRDNS. If not available the attribute is set to OSMTPD\_STATUS\_TEMPFAIL.

- *char* *\*identity*

  The identity of the remote host as presented by the HELO or EHLO SMTP command. To use this attribute, initialize **osmtpd\_need** with OSMTPD\_NEED\_IDENTITY. If not available the attribute is set to NULL.

- *char* *\*ciphers*

  The ciphers used during (start)tls. To use this attribute, initialize **osmtpd\_need** with OSMTPD\_NEED\_CIPHERS. If not available the attribute is set to NULL.

- *uint32\_t* *msgid*

  The message ID of the current message being handled in the SMTP transaction. This value can be useful for logging. **osmtpd\_need** needs to be initialized with OSMTPD\_NEED\_MSGID. If not available the attribute is set to 0. Filters in need of filter specific data can use **osmtpd\_local\_message** and *local\_message*.

- *char* *\*username*

  The username with which the session was successfully authenticated. **osmtpd\_need** needs to be initialized with If not available or authentication failed is set to NULL.

- *char* *\*mailfrom*

  The envelope MAIL FROM address in the SMTP transaction. **osmtpd\_need** needs to be initialized with OSMTPD\_NEED\_MAILFROM. If not available the attribute is set to NULL.

- *char* *\*\*rcptto*

  The envelope RCPT TO address in the SMTP transaction. **osmtpd\_need** needs to be initialized with OSMTPD\_NEED\_RCPTTO. This attribute is a NULL-terminated array of address strings. If not available the first element in the array is set to NULL.

- *uint64\_t* *evpid*

  The envelope ID we're currently working on. **osmtpd\_need** needs to be initialized with OSMTPD\_NEED\_EVPID. If not available the attribute is set to 0.

- *void* *\*local\_session*

  Any filter specific data that needs to be stored during the session. This is initialized on *ctx* creation by calling *oncreate* argument from **osmtpd\_local\_session**.

- *void* *\*local\_message*

  Any filter specific data that needs to be stored during the message transaction. This is initialized on *ctx* creation by calling *oncreate* argument from **osmtpd\_local\_message**.

The **osmtpd\_register\_filter** class of functions must call one of **osmtpd\_filter\_proceed**, **osmtpd\_filter\_rewrite**, **osmtpd\_filter\_reject** and **osmtpd\_filter\_disconnect**. This can be done either in the callback function itself, or at a later moment through another callback. Note that the session stalls until the **osmtpd\_filter** has been called.

Exceptions to the above reply options are:

- **•**

  **osmtpd\_register\_filter\_connect**'s and **osmtpd\_register\_filter** functions' callbacks without argument can't use **osmtpd\_filter\_rewrite**.

- **•**

  **osmtpd\_register\_filter\_dataline**'s callback can only use osmtpd\_filter\_dataline.

**osmtpd\_warn**, **osmtpd\_warnx**, **osmtpd\_err** and **osmtpd\_errx** can be used as a standin for [warn(3)](/warn/3.md) , [warnx(3)](/warnx/3.md) , [err(3)](/err/3.md) and [errx(3)](/errx/3.md) without printing the program name to stderr. Additionally, **osmtpd\_warn** and **osmtpd\_warnx** prints the request ID on the logline when *ctx* is not NULL.

## SEE ALSO

event\_init(3), [smtpd.conf(5)](/smtpd.conf/5.md)

## HISTORY

The **osmtpd\_run** API first appeared in OpenBSD 6.6.

## AUTHORS

Martijn van Duren \<*martijn@openbsd.org*\>
