Browse Source

Added table API

tags/v52
Wijnand 3 years ago
parent
commit
4494732db5
2 changed files with 356 additions and 0 deletions
  1. +55
    -0
      cmd/table-dummy/main.go
  2. +301
    -0
      table.go

+ 55
- 0
cmd/table-dummy/main.go View File

@@ -0,0 +1,55 @@
package main

import (
"log"
"os"

opensmtpd "gopkg.in/opensmtpd.v0"
)

var (
proc = os.Args[0]
)

func main() {
opensmtpd.Debug = true

t := &opensmtpd.Table{
// Update callback
Update: func() (int, error) {
log.Printf("%s: update")
return 0, nil
},

// Check callback
Check: func(service int, params opensmtpd.Dict, key string) (int, error) {
log.Printf("%s: check service=%d, params=%+v, key=%q\n", proc, service, params, key)
return 0, nil
},

// Lookup callback
Lookup: func(service int, params opensmtpd.Dict, key string) (string, error) {
log.Printf("%s: lookup service=%d,params=%+v,key=%q\n", proc, service, params, key)
if key == "table-dummy-test" {
return "maze@maze.io", nil
}
return "", nil
},

// Fetch callback
Fetch: func(service int, params opensmtpd.Dict) (string, error) {
log.Printf("%s: fetch service=%d,params=%+v,key=%q\n", proc, service, params)
return "", nil
},

// Close callback, called at stop
Close: func() error {
log.Printf("%s: close\n")
return nil
},
}

if err := t.Serve(); err != nil {
log.Fatalln(err)
}
}

+ 301
- 0
table.go View File

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

import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"os"
)

const (
TableAPIVersion = 1
)

const (
ProcTableOK = iota
ProcTableFail
ProcTableOpen
ProcTableClose
ProcTableUpdate
ProcTableCheck
ProcTableLookup
ProcTableFetch
)

var procTableTypeName = map[uint32]string{
ProcTableOK: "PROC_TABLE_OK",
ProcTableFail: "PROC_TABLE_FAIL",
ProcTableOpen: "PROC_TABLE_OPEN",
ProcTableClose: "PROC_TABLE_CLOSE",
ProcTableUpdate: "PROC_TABLE_UPDATE",
ProcTableCheck: "PROC_TABLE_CHECK",
ProcTableLookup: "PROC_TABLE_LOOKUP",
ProcTableFetch: "PROC_TABLE_FETCH",
}

func procTableName(t uint32) string {
if s, ok := procTableTypeName[t]; ok {
return s
}
return fmt.Sprintf("UNKNOWN %d", t)
}

type Table struct {
// Update callback
Update func() (int, error)

// Check callback
Check func(service int, params Dict, key string) (int, error)

// Lookup callback
Lookup func(service int, params Dict, key string) (string, error)

// Fetch callback
Fetch func(service int, params Dict) (string, error)

// Close callback, called at stop
Close func() error

c net.Conn
m *Message
closed bool
}

func (t *Table) Serve() error {
var err error

if t.c, err = NewConn(0); err != nil {
return err
}

t.m = new(Message)

for !t.closed {
if err = t.m.ReadFrom(t.c); err != nil {
if err.Error() != "resource temporarily unavailable" {
break
}
}
debugf("table: %s", procTableName(t.m.Type))
if err = t.dispatch(); err != nil {
break
}
}

return err
}

type tableOpenParams struct {
Version uint32
Name [maxLineSize]byte
}

func (t *Table) dispatch() (err error) {
switch t.m.Type {
case ProcTableOpen:
/*
var op tableOpenParams
if err = t.getMessage(&op, maxLineSize+4); err != nil {
return
}

if op.Version != TableAPIVersion {
fatalf("table: bad API version %d (we support %d)", op.Version, TableAPIVersion)
}
if bytes.IndexByte(op.Name[:], 0) <= 0 {
fatal("table: no name supplied")
}
*/
var version uint32
if version, err = t.m.GetUint32(); err != nil {
return
} else if version != TableAPIVersion {
fatalf("table: expected API version %d, got %d", TableAPIVersion, version)
}

var name string
if name, err = t.m.GetString(); err != nil {
return
} else if name == "" {
fatal("table: no name supplied by smtpd!?")
}

debugf("table: version=%d,name=%q\n", version, name)

m := new(Message)
m.Type = ProcTableOK
m.Len = imsgHeaderSize
m.PID = uint32(os.Getpid())
if err = m.WriteTo(t.c); err != nil {
return
}

case ProcTableUpdate:
var r = 1

if t.Update != nil {
if r, err = t.Update(); err != nil {
return
}
}

m := new(Message)
m.Type = ProcTableOK
m.PutInt(r)
if err = m.WriteTo(t.c); err != nil {
return
}

case ProcTableClose:
if t.Close != nil {
if err = t.Close(); err != nil {
return
}
}

t.closed = true
return

case ProcTableCheck:
var service int
if service, err = t.m.GetInt(); err != nil {
return
}

var params Dict
if params, err = t.getParams(); err != nil {
return
}

var key string
if key, err = t.m.GetString(); err != nil {
return
}

debugf("table_check: service=%s,params=%+v,key=%q",
serviceName(service), params, key)

var r = -1
if t.Check != nil {
if r, err = t.Check(service, params, key); err != nil {
return
}
}

log.Printf("table_check: result=%d\n", r)

case ProcTableLookup:
var service int
if service, err = t.m.GetInt(); err != nil {
return
}

var params Dict
if params, err = t.getParams(); err != nil {
return
}

var key string
if key, err = t.m.GetString(); err != nil {
return
}

debugf("table_lookup: service=%s,params=%+v,key=%q",
serviceName(service), params, key)

var val string
if t.Lookup != nil {
if val, err = t.Lookup(service, params, key); err != nil {
return
}
}

m := new(Message)
m.Type = ProcTableOK
m.PID = uint32(os.Getpid())
if val == "" {
m.PutInt(-1)
} else {
m.PutInt(1)
m.PutString(val)
}
if err = m.WriteTo(t.c); err != nil {
return
}

case ProcTableFetch:
var service int
if service, err = t.m.GetInt(); err != nil {
return
}

var params Dict
if params, err = t.getParams(); err != nil {
return
}

debugf("table_fetch: service=%s,params=%+v",
serviceName(service), params)

var val string
if t.Fetch != nil {
if val, err = t.Fetch(service, params); err != nil {
return
}
}

m := new(Message)
m.Type = ProcTableOK
m.PID = uint32(os.Getpid())
if val == "" {
m.PutInt(-1)
} else {
m.PutInt(1)
m.PutString(val)
}
if err = m.WriteTo(t.c); err != nil {
return
}

}

return nil
}

func (t *Table) getMessage(data interface{}, size int) (err error) {
buf := make([]byte, size)
if _, err = io.ReadFull(t.c, buf); err != nil {
return
}

return binary.Read(bytes.NewBuffer(buf), binary.LittleEndian, data)
}

func (t *Table) getParams() (params Dict, err error) {
var count uint64
if count, err = t.m.GetSize(); err != nil {
return
}
debugf("params: %d pairs", count)

params = make(Dict, count)
if count == 0 {
return
}

var k, v string
for ; count != 0; count-- {
if k, err = t.m.GetString(); err != nil {
return
}
if v, err = t.m.GetString(); err != nil {
return
}
params[k] = v
}

return
}

Loading…
Cancel
Save