Browse Source

Standardised filter; Added examples

tags/v52
Wijnand 2 years ago
parent
commit
2315a54ab5
4 changed files with 135 additions and 34 deletions
  1. 80
    34
      filter.go
  2. 31
    0
      filter_test.go
  3. 1
    0
      table.go
  4. 23
    0
      table_test.go

+ 80
- 34
filter.go View File

@@ -148,58 +148,75 @@ func responseName(c int) string {

// Filter implements the OpenSMTPD filter API
type Filter struct {
// Connect callback
Connect func(*Session, *ConnectQuery) error

// HELO callback
HELO func(*Session, string) error

// MAIL FROM callback
MAIL func(*Session, string, string) error

// RCPT TO callback
RCPT func(*Session, string, string) error

// DATA callback
DATA func(*Session) error

// DataLine callback
DataLine func(*Session, string) error

// EOM (end of message) callback
EOM func(*Session, uint32) error

// Reset callback
Reset func(*Session) error

// Disconnect callback
Disconnect func(*Session) error

// Commit callback
Commit func(*Session) error

Name string
Version uint32

c net.Conn
m *Message

hooks int
flags int
ready bool

hook struct {
connect func(*Session, *ConnectQuery) error
helo func(*Session, string) error
mail func(*Session, string, string) error
rcpt func(*Session, string, string) error
data func(*Session) error
dataline func(*Session, string) error
eom func(*Session, uint32) error
reset func(*Session) error
disconnect func(*Session) error
commit func(*Session) error
}
hooks int
flags int
ready bool
session *lru.Cache
}

func (f *Filter) OnConnect(fn func(*Session, *ConnectQuery) error) {
f.hook.connect = fn
f.Connect = fn
f.hooks |= HookConnect
}

func (f *Filter) OnHELO(fn func(*Session, string) error) {
f.hook.helo = fn
f.HELO = fn
f.hooks |= HookHELO
}

func (f *Filter) OnMAIL(fn func(*Session, string, string) error) {
f.hook.mail = fn
f.MAIL = fn
f.hooks |= HookMAIL
}

func (f *Filter) OnRCPT(fn func(*Session, string, string) error) {
f.hook.rcpt = fn
f.RCPT = fn
f.hooks |= HookRCPT
}

func (f *Filter) OnDATA(fn func(*Session) error) {
f.hook.data = fn
f.DATA = fn
f.hooks |= HookDATA
}

func (f *Filter) OnDataLine(fn func(*Session, string) error) {
f.hook.dataline = fn
f.DataLine = fn
f.hooks |= HookDataLine
}

@@ -218,6 +235,35 @@ func (f *Filter) Register() error {
return err
}

// Fill hooks mask
if f.Connect != nil {
f.hooks |= HookConnect
}
if f.HELO != nil {
f.hooks |= HookHELO
}
if f.MAIL != nil {
f.hooks |= HookMAIL
}
if f.RCPT != nil {
f.hooks |= HookRCPT
}
if f.DATA != nil {
f.hooks |= HookDATA
}
if f.DataLine != nil {
f.hooks |= HookDataLine
}
if f.EOM != nil {
f.hooks |= HookEOM
}
if f.Disconnect != nil {
f.hooks |= HookDisconnect
}
if f.Commit != nil {
f.hooks |= HookCommit
}

if t, ok := filterTypeName[f.m.Type]; ok {
log.Printf("filter: imsg %s\n", t)
} else {
@@ -388,8 +434,8 @@ func (f *Filter) handleQuery() (err error) {
}

log.Printf("query connect: %s\n", query)
if f.hook.connect != nil {
return f.hook.connect(s, &query)
if f.Connect != nil {
return f.Connect(s, &query)
}

log.Printf("filter: WARNING: no connect callback\n")
@@ -401,8 +447,8 @@ func (f *Filter) handleQuery() (err error) {
}

log.Printf("query HELO: %q\n", line)
if f.hook.helo != nil {
return f.hook.helo(s, line)
if f.HELO != nil {
return f.HELO(s, line)
}

log.Printf("filter: WARNING: no HELO callback\n")
@@ -415,8 +461,8 @@ func (f *Filter) handleQuery() (err error) {
}

log.Printf("query MAIL: %s\n", user+"@"+domain)
if f.hook.mail != nil {
return f.hook.mail(s, user, domain)
if f.MAIL != nil {
return f.MAIL(s, user, domain)
}

log.Printf("filter: WARNING: no MAIL callback\n")
@@ -429,16 +475,16 @@ func (f *Filter) handleQuery() (err error) {
}

log.Printf("query RCPT: %s\n", user+"@"+domain)
if f.hook.rcpt != nil {
return f.hook.rcpt(s, user, domain)
if f.RCPT != nil {
return f.RCPT(s, user, domain)
}

log.Printf("filter: WARNING: no RCPT callback\n")
return f.respond(s, FilterOK, 0, "")

case QueryDATA:
if f.hook.data != nil {
return f.hook.data(s)
if f.DATA != nil {
return f.DATA(s)
}

log.Printf("filter: WARNING: no DATA callback\n")
@@ -450,8 +496,8 @@ func (f *Filter) handleQuery() (err error) {
return
}

if f.hook.eom != nil {
return f.hook.eom(s, dataLen)
if f.EOM != nil {
return f.EOM(s, dataLen)
}

log.Printf("filter: WARNING: no EOM callback\n")

+ 31
- 0
filter_test.go View File

@@ -0,0 +1,31 @@
package opensmtpd

import "strings"

func ExampleFilter() {
// Build our filter
filter := &Filter{
HELO: func(session *Session, helo string) error {
if helo == "test" {
return session.Reject()
}
return session.Accept()
},
}

// Add another hook
filter.OnMAIL(func(session *Session, user, domain string) error {
if strings.ToLower(domain) == "example.org" {
return session.Reject()
}
return session.Accept()
})

// Register our filter with smtpd
if err := filter.Register(); err != nil {
panic(err)
}

// And keep serving until smtpd stops
filter.Serve()
}

+ 1
- 0
table.go View File

@@ -43,6 +43,7 @@ func procTableName(t uint32) string {
return fmt.Sprintf("UNKNOWN %d", t)
}

// Table implements the OpenSMTPD table API
type Table struct {
// Update callback
Update func() (int, error)

+ 23
- 0
table_test.go View File

@@ -0,0 +1,23 @@
package opensmtpd

func ExampleTable() {
// In smtpd.conf:
//
// table aliases <name-of-filter>:
// accept for local alias <aliases> ...

aliases := map[string]string{
"root": "user@example.org",
}

table := &Table{
Lookup: func(service int, params Dict, key string) (string, error) {
// We are only valid for aliases
if service&ServiceAlias != 0 {
return aliases[key], nil
}
return "", nil
},
}
table.Serve()
}

Loading…
Cancel
Save