17 Commits

Author SHA1 Message Date
Dolf Schimmel (Freeaqingme)
01964e5391 Merge with upstream 2015-11-24 17:49:00 +01:00
Dolf Schimmel (Freeaqingme)
0c9e549560 go fmt 2015-11-24 17:46:44 +01:00
Dolf Schimmel (Freeaqingme)
d6bf69609f Merge branch 'master' of https://github.com/leonrbaker/gomilter 2015-11-24 17:44:53 +01:00
Dolf Schimmel (Freeaqingme)
6a57e8c3fc Make gobencode() optional in SetPriv(), return errors instead of integers 2015-11-24 17:39:31 +01:00
Dolf Schimmel (Freeaqingme)
39a4baf02e Recover from errors in Go_xxfi_connect()
There appears to be a race condition in libmilter in which
hostaddr is set to nil before or while calling the connect
callback if the client aborts its connection during that
window.
2015-11-24 17:38:59 +01:00
Leon Baker
c99ec2226f Merge pull request #7 from Freeaqingme/master
Fix IPv6 addresses that sometimes caused memory stomping
2015-09-25 09:36:22 +02:00
Dolf Schimmel
00d59ae5ab Fix IPv6 addresses that sometimes caused memory stomping 2015-09-16 19:42:24 +02:00
Leon Baker
dc84b56af1 Merge pull request #6 from leonrbaker/logging
Implemented the Log package for logging
2015-09-03 12:14:40 +02:00
Leon Baker
64fb9af89b Merge pull request #5 from Freeaqingme/master
Max IPv6 address length is 45 characters, not 16
2015-09-03 12:07:19 +02:00
Dolf Schimmel (Freeaqingme)
385e5106c2 Max IPv6 address length is 45 characters, not 16
The idea was to reserve 16 bytes for an IPv6 address. But given that a human representation is used rather than a
128 bit integer, we need 45 characters at most.
2015-09-01 21:15:14 +02:00
Leon Baker
882ca25f76 Implemented the Log package for logging 2015-06-16 12:34:30 +02:00
Leon Baker
8746179952 Update README and fix sample filter 2015-06-15 16:44:41 +02:00
Leon Baker
4dc71bdbbc Merge pull request #4 from Freeaqingme/master
Implement stop(), Support IPv6, Custom log callbacks, reformatted code
2015-06-13 18:22:12 +02:00
Dolf Schimmel
a6870a91fe Also include netinet/in.h necessary for FreeBSD 2015-06-11 11:40:55 +02:00
Dolf Schimmel (Freeaqingme)
24ed8dcbc3 Add support for ipv6 2015-06-01 22:16:02 +02:00
Dolf Schimmel (Freeaqingme)
c0f4b47c5e Implement stop() 2015-06-01 00:04:36 +02:00
Dolf Schimmel (Freeaqingme)
13b82e7dd5 Reformatted code 2015-05-31 01:02:17 +02:00
5 changed files with 763 additions and 621 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Compiled test programs
samplefilter
samplefilter2

View File

@@ -1,2 +1,95 @@
# gomilter # gomilter
Go Bindings for libmilter Go Bindings for Sendmail's libmilter
Tested on Linux and FreeBSD
## Installation
The Sendmail header file libmilter/mfapi.h is required. For Redhat/CentOS, install the sendmail-devel package:
```sh
yum install sendmail-devel
```
Install the gomilter package:
```sh
go get github.com/leonrbaker/gomilter
```
##Usage
The milter is implemented in a struct. Start by defining your own struct type and embeding the gomilter MilterRaw struct.
```go
type Mymilter struct {
gomilter.MilterRaw // Embed the basic functionality.
}
```
Milter callbacks are added by implementing methods for the struct with matching predefined names.
### Callbacks
* Connect
* Helo
* EnvFrom
* EnvRcpt
* Header
* Eoh
* Body
* Eom
* Abort
* Close
Not all the callbacks need to be defined. The callbacks are explained on the milter.org site. Unfortunately the milter.org site has been shut down but it is still on [web.archive.org](http://web.archive.org/web/20150510034154/https://www.milter.org/developers/api/index)
### Message Modification Functions
* AddHeader
* ChgHeader
* InsHeader
* ChgFrom
* AddRcpt
* AddRcpt_Par
* DelRcpt
* ReplaceBody
### Other Message Handling Functions
* progress
### Startup
The Socket field of the milter struct must be set. For example:
```go
mymilter.Socket = "unix:/var/gomilter/socket"
```
Control is handed over to the libmilter smfi_main function by calling the Run method and passing it a pointer to your milter struct
```go
gomilter.Run(mymilter)
```
The milter has a Stop method which calls the libmilter smfi_stop function.
### Private Data
libmilter is able to store private data for a connection. This data can be accessed from other functions and callbacks for the same connection. You can pass a pointer to any data structure to SetPriv. The data is retrieved with GetPriv
```go
t := T{1, 2, 3}
m.SetPriv(ctx, &t)
```
Retrieve the data with
```go
var t T
m.GetPriv(ctx, &t))
```
GetPriv should only be called once. If the private data is needed in another function or callback then call SetPriv again.
## Sample Programs
There are two sample programs included, samplefilter.go and samplefilter2.go
##Other Libraries
A usefull MIME parsing library is [go.enmime](https://github.com/jhillyerd/go.enmime)

View File

@@ -17,21 +17,25 @@ package gomilter
#cgo LDFLAGS: -lmilter #cgo LDFLAGS: -lmilter
#include <stdlib.h> #include <stdlib.h>
//#include <string.h> #include <netinet/in.h>
#include <arpa/inet.h>
#include "libmilter/mfapi.h" #include "libmilter/mfapi.h"
#include "filter.h" #include "filter.h"
*/ */
import "C" import "C"
import ( import (
"fmt"
"unsafe"
"encoding/binary"
"reflect"
"os"
"strings"
"bytes" "bytes"
"encoding/binary"
"encoding/gob" "encoding/gob"
"errors"
"fmt"
"log"
"net"
"os"
"reflect"
"strings"
"unsafe"
) )
type sockaddr_in struct { type sockaddr_in struct {
@@ -73,6 +77,7 @@ type Milter interface {
GetDebug() bool GetDebug() bool
GetFlags() int GetFlags() int
GetSocket() string GetSocket() string
GetLogger() *log.Logger
} }
// An "empty" Milter with no callback functions // An "empty" Milter with no callback functions
@@ -81,6 +86,7 @@ type MilterRaw struct {
Debug bool Debug bool
Flags int Flags int
Socket string Socket string
Logger *log.Logger
} }
func (m *MilterRaw) GetFilterName() string { func (m *MilterRaw) GetFilterName() string {
@@ -99,9 +105,13 @@ func (m *MilterRaw) GetSocket() string {
return m.Socket return m.Socket
} }
func (m *MilterRaw) GetLogger() *log.Logger {
return m.Logger
}
// ********* Callback checking types ********* // ********* Callback checking types *********
type checkForConnect interface { type checkForConnect interface {
Connect(ctx uintptr, hostname, ip string) (sfsistat int8) Connect(ctx uintptr, hostname string, ip net.IP) (sfsistat int8)
} }
type checkForHelo interface { type checkForHelo interface {
@@ -142,6 +152,7 @@ type checkForClose interface {
// ********* Global Milter variable ********* // ********* Global Milter variable *********
var milter Milter var milter Milter
var logger *log.Logger
// ********* Utility Functions (not exported) ********* // ********* Utility Functions (not exported) *********
@@ -193,45 +204,52 @@ func GobDecode(buf []byte, data interface{}) error {
return nil return nil
} }
// ********* Filter Callback functions ********* // ********* Filter Callback functions *********
// These are registered with Milter // These are registered with Milter
// They are only called if they get registered but need to be defined anyway // They are only called if they get registered but need to be defined anyway
//export Go_xxfi_connect //export Go_xxfi_connect
func Go_xxfi_connect(ctx *C.SMFICTX, hostname *C.char, hostaddr *C._SOCK_ADDR) C.sfsistat { func Go_xxfi_connect(ctx *C.SMFICTX, hostname *C.char, hostaddr *C._SOCK_ADDR) (sfsistat C.sfsistat) {
ctxptr := ctx2int(ctx) defer func(sfsistat *C.sfsistat) {
// Check if the host address is a regular ipv4 address if r := recover(); r != nil {
if hostaddr.sa_family == C.AF_INET { logger.Printf("Panic caught in Go_xxfi_connect(): %s", r)
//LoggerPrintln(hostaddr.sa_data) *sfsistat = 75 // tempfail
}
}(&sfsistat)
// hostaddrin is a parallel data structure of the C type hostaddr ctxptr := ctx2int(ctx)
//var hostaddrin *sockaddr_in var ip net.IP
if hostaddr.sa_family == C.AF_INET {
hostaddrin := (*sockaddr_in)(unsafe.Pointer(hostaddr)) hostaddrin := (*sockaddr_in)(unsafe.Pointer(hostaddr))
//LoggerPrintln(hostaddrin)
ip_addr := make([]byte, 4) ip_addr := make([]byte, 4)
binary.LittleEndian.PutUint32(ip_addr, hostaddrin.sin_addr) binary.LittleEndian.PutUint32(ip_addr, hostaddrin.sin_addr)
ip = net.IPv4(ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3])
// Call our application's callback } else if hostaddr.sa_family == C.AF_INET6 {
m := milter.(checkForConnect) sa_in := (*C.struct_sockaddr_in6)(unsafe.Pointer(hostaddr))
code := m.Connect(ctxptr, C.GoString(hostname), fmt.Sprintf("%d.%d.%d.%d", ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3])) ip = net.IP(C.GoBytes(unsafe.Pointer(&sa_in.sin6_addr), 16))
} else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Connect callback returned: %d\n", code) logger.Println("hostaddr.sa_family value not implemented")
}
ip = net.ParseIP("::")
}
m := milter.(checkForConnect)
code := m.Connect(ctxptr, C.GoString(hostname), ip)
if milter.GetDebug() {
logger.Printf("Connect callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
if milter.GetDebug() {
LoggerPrintln("hostaddr.sa_family value not implemented")
}
return C.SMFIS_CONTINUE
}
//export Go_xxfi_helo //export Go_xxfi_helo
func Go_xxfi_helo(ctx *C.SMFICTX, helohost *C.char) C.sfsistat { func Go_xxfi_helo(ctx *C.SMFICTX, helohost *C.char) C.sfsistat {
m := milter.(checkForHelo) m := milter.(checkForHelo)
code := m.Helo(ctx2int(ctx), C.GoString(helohost)) code := m.Helo(ctx2int(ctx), C.GoString(helohost))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Helo callback returned: %d\n", code) logger.Printf("Helo callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -242,7 +260,7 @@ func Go_xxfi_envfrom(ctx *C.SMFICTX, argv **C.char) C.sfsistat {
m := milter.(checkForEnvFrom) m := milter.(checkForEnvFrom)
code := m.EnvFrom(ctx2int(ctx), cStringArrayToSlice(argv)) code := m.EnvFrom(ctx2int(ctx), cStringArrayToSlice(argv))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("EnvFrom callback returned: %d\n", code) logger.Printf("EnvFrom callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -253,7 +271,7 @@ func Go_xxfi_envrcpt(ctx *C.SMFICTX, argv **C.char) C.sfsistat {
m := milter.(checkForEnvRcpt) m := milter.(checkForEnvRcpt)
code := m.EnvRcpt(ctx2int(ctx), cStringArrayToSlice(argv)) code := m.EnvRcpt(ctx2int(ctx), cStringArrayToSlice(argv))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("EnvRcpt callback returned: %d\n", code) logger.Printf("EnvRcpt callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -263,7 +281,7 @@ func Go_xxfi_header(ctx *C.SMFICTX, headerf, headerv *C.char) C.sfsistat {
m := milter.(checkForHeader) m := milter.(checkForHeader)
code := m.Header(ctx2int(ctx), C.GoString(headerf), C.GoString(headerv)) code := m.Header(ctx2int(ctx), C.GoString(headerf), C.GoString(headerv))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Header callback returned: %d\n", code) logger.Printf("Header callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -274,7 +292,7 @@ func Go_xxfi_eoh(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForEoh) m := milter.(checkForEoh)
code := m.Eoh(ctx2int(ctx)) code := m.Eoh(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Eoh callback returned: %d\n", code) logger.Printf("Eoh callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -291,7 +309,7 @@ func Go_xxfi_body(ctx *C.SMFICTX, bodyp *C.uchar, bodylen C.size_t) C.sfsistat {
m := milter.(checkForBody) m := milter.(checkForBody)
code := m.Body(ctx2int(ctx), b) code := m.Body(ctx2int(ctx), b)
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Body callback returned: %d\n", code) logger.Printf("Body callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -302,7 +320,7 @@ func Go_xxfi_eom(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForEom) m := milter.(checkForEom)
code := m.Eom(ctx2int(ctx)) code := m.Eom(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Eom callback returned: %d\n", code) logger.Printf("Eom callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -313,7 +331,7 @@ func Go_xxfi_abort(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForAbort) m := milter.(checkForAbort)
code := m.Abort(ctx2int(ctx)) code := m.Abort(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Abort callback returned: %d\n", code) logger.Printf("Abort callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -324,7 +342,7 @@ func Go_xxfi_close(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForClose) m := milter.(checkForClose)
code := m.Close(ctx2int(ctx)) code := m.Close(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Close callback returned: %d\n", code) logger.Printf("Close callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -340,13 +358,24 @@ func GetSymVal(ctx uintptr, symname string) string {
return C.GoString(cval) return C.GoString(cval)
} }
func SetPriv(ctx uintptr, privatedata interface{}) int { // Beware that if your struct is too large it will be discarded
// without storing. You may want to simply provide a reference
// to something you keep in a map or similar structure yourself.
func SetPriv(ctx uintptr, privatedata interface{}) error {
// privatedata seems to work for any data type // privatedata seems to work for any data type
// Structs must have exported fields // Structs must have exported fields
// Serialize Go privatedata into a byte slice // Serialize Go privatedata into a byte slice
bytedata, _ := GobEncode(privatedata) bytedata, err := GobEncode(privatedata)
if err != nil {
return err
}
return SetPrivBytes(ctx, bytedata)
}
// See also: http://bit.ly/1HVWA9I
func SetPrivBytes(ctx uintptr, bytedata []byte) error {
// length and size // length and size
// length is a uint32 (usually 4 bytes) // length is a uint32 (usually 4 bytes)
// the length will be stored in front of the byte sequence // the length will be stored in front of the byte sequence
@@ -355,7 +384,7 @@ func SetPriv(ctx uintptr, privatedata interface{}) int {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err := binary.Write(buf, binary.BigEndian, length) err := binary.Write(buf, binary.BigEndian, length)
if err != nil { if err != nil {
return -1 return errors.New("Could not write binary data into buffer: " + err.Error())
} }
lengthbytes := buf.Bytes() lengthbytes := buf.Bytes()
@@ -381,10 +410,29 @@ func SetPriv(ctx uintptr, privatedata interface{}) int {
// Call libmilter smfi_setpriv // Call libmilter smfi_setpriv
type CtxPtr *C.struct_smfi_str type CtxPtr *C.struct_smfi_str
return int(C.smfi_setpriv(int2ctx(ctx), unsafe.Pointer(lenStart))) res := int(C.smfi_setpriv(int2ctx(ctx), unsafe.Pointer(lenStart)))
if res != int(C.MI_SUCCESS) {
return errors.New("smfi_setpriv() returned a failure")
} }
func GetPriv(ctx uintptr, privatedata interface{}) int { return nil
}
func GetPriv(ctx uintptr, privatedata interface{}) error {
databytes, err := GetPrivBytes(ctx)
if err != nil {
return err
}
err = GobDecode(databytes, privatedata)
if err != nil {
return err
}
return nil
}
func GetPrivBytes(ctx uintptr) ([]byte, error) {
/* Retrieve the private data stored by the milter /* Retrieve the private data stored by the milter
Retrieving the data will release the memory allocated for it Retrieving the data will release the memory allocated for it
Don't try to retrieve it again unless you call SetPriv first Don't try to retrieve it again unless you call SetPriv first
@@ -395,7 +443,7 @@ func GetPriv(ctx uintptr, privatedata interface{}) int {
// Make sure data has been set with a previous call to SetPriv // Make sure data has been set with a previous call to SetPriv
if CArray == nil { if CArray == nil {
return -1 return nil, errors.New("smfi_getpriv() call failed")
} }
// Read uint32 size bytes from the start of the pointer // Read uint32 size bytes from the start of the pointer
@@ -415,7 +463,7 @@ func GetPriv(ctx uintptr, privatedata interface{}) int {
buf := bytes.NewBuffer(lengthbytes) buf := bytes.NewBuffer(lengthbytes)
err := binary.Read(buf, binary.BigEndian, &length) err := binary.Read(buf, binary.BigEndian, &length)
if err != nil { if err != nil {
return -1 return nil, errors.New("Could not parse binary data")
} }
// Read byte sequence of data // Read byte sequence of data
@@ -430,12 +478,7 @@ func GetPriv(ctx uintptr, privatedata interface{}) int {
C.smfi_setpriv(int2ctx(ctx), nil) C.smfi_setpriv(int2ctx(ctx), nil)
// Unserialize the data bytes back into a data structure // Unserialize the data bytes back into a data structure
err = GobDecode(databytes, privatedata) return databytes, nil
if err != nil {
return -1
}
return 0
} }
func SetReply(ctx uintptr, rcode, xcode, message string) int { func SetReply(ctx uintptr, rcode, xcode, message string) int {
@@ -491,7 +534,7 @@ func AddHeader(ctx uintptr, headerf, headerv string) int {
defer C.free(unsafe.Pointer(cheaderv)) defer C.free(unsafe.Pointer(cheaderv))
// Call smfi_addheader // Call smfi_addheader
return int(C.smfi_addheader(int2ctx(ctx), cheaderf, cheaderv)); return int(C.smfi_addheader(int2ctx(ctx), cheaderf, cheaderv))
} }
func ChgHeader(ctx uintptr, headerf string, hdridx int, headerv string) int { func ChgHeader(ctx uintptr, headerf string, hdridx int, headerv string) int {
@@ -504,7 +547,7 @@ func ChgHeader(ctx uintptr, headerf string, hdridx int, headerv string) int {
defer C.free(unsafe.Pointer(cheaderv)) defer C.free(unsafe.Pointer(cheaderv))
// Call smfi_chgheader // Call smfi_chgheader
return int(C.smfi_chgheader(int2ctx(ctx), cheaderf, C.int(hdridx), cheaderv)); return int(C.smfi_chgheader(int2ctx(ctx), cheaderf, C.int(hdridx), cheaderv))
} }
func InsHeader(ctx uintptr, hdridx int, headerf, headerv string) int { func InsHeader(ctx uintptr, hdridx int, headerf, headerv string) int {
@@ -517,10 +560,9 @@ func InsHeader(ctx uintptr, hdridx int, headerf, headerv string) int {
defer C.free(unsafe.Pointer(cheaderv)) defer C.free(unsafe.Pointer(cheaderv))
// Call smfi_insheader // Call smfi_insheader
return int(C.smfi_insheader(int2ctx(ctx), C.int(hdridx), cheaderf, cheaderv)); return int(C.smfi_insheader(int2ctx(ctx), C.int(hdridx), cheaderf, cheaderv))
} }
func ChgFrom(ctx uintptr, mail, args string) int { func ChgFrom(ctx uintptr, mail, args string) int {
/* Change the envelope sender address. SMFIF_CHGFROM /* Change the envelope sender address. SMFIF_CHGFROM
*/ */
@@ -595,12 +637,17 @@ func progress(ctx uintptr) int {
// ********* Run the milter ********* // ********* Run the milter *********
func Run(amilter Milter) int { func Run(amilter Milter) int {
milter = amilter milter = amilter
// Setup logging
logger = milter.GetLogger()
if logger == nil {
// No logger defined so create a simple logger to Stdout
logger = log.New(os.Stdout, "", 0)
}
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf ("Debugging enabled") logger.Println("Debugging enabled")
} }
// Declare an empty smfiDesc structure // Declare an empty smfiDesc structure
@@ -611,7 +658,7 @@ func Run(amilter Milter) int {
defer C.free(unsafe.Pointer(fname)) defer C.free(unsafe.Pointer(fname))
smfilter.xxfi_name = fname smfilter.xxfi_name = fname
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Filter Name: %s\n", C.GoString(smfilter.xxfi_name)) logger.Printf("Filter Name: %s\n", C.GoString(smfilter.xxfi_name))
} }
// Set version code // Set version code
@@ -620,7 +667,7 @@ func Run(amilter Milter) int {
// Set Flags // Set Flags
smfilter.xxfi_flags = C.ulong(milter.GetFlags()) smfilter.xxfi_flags = C.ulong(milter.GetFlags())
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Flags: 0x%b\n", smfilter.xxfi_flags) logger.Printf("Flags: 0x%b\n", smfilter.xxfi_flags)
} }
// Set Callbacks if they are implemented // Set Callbacks if they are implemented
@@ -628,117 +675,117 @@ func Run(amilter Milter) int {
// Check if Connect method was implemented // Check if Connect method was implemented
if _, ok := milter.(checkForConnect); ok { if _, ok := milter.(checkForConnect); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Connect callback implemented") logger.Println("Connect callback implemented")
} }
C.setConnect(&smfilter) C.setConnect(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Connect callback not implemented") logger.Println("Connect callback not implemented")
} }
} }
// Check if Helo method was implemented // Check if Helo method was implemented
if _, ok := milter.(checkForHelo); ok { if _, ok := milter.(checkForHelo); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Helo callback implemented") logger.Println("Helo callback implemented")
} }
C.setHelo(&smfilter) C.setHelo(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Helo callback not implemented") logger.Println("Helo callback not implemented")
} }
} }
// Check if EnvFrom method was implemented // Check if EnvFrom method was implemented
if _, ok := milter.(checkForEnvFrom); ok { if _, ok := milter.(checkForEnvFrom); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvFrom callback implemented") logger.Println("EnvFrom callback implemented")
} }
C.setEnvFrom(&smfilter) C.setEnvFrom(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvFrom callback not implemented") logger.Println("EnvFrom callback not implemented")
} }
} }
// Check if EnvRcpt method was implemented // Check if EnvRcpt method was implemented
if _, ok := milter.(checkForEnvRcpt); ok { if _, ok := milter.(checkForEnvRcpt); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvRcpt callback implemented") logger.Println("EnvRcpt callback implemented")
} }
C.setEnvRcpt(&smfilter) C.setEnvRcpt(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvRcpt callback not implemented") logger.Println("EnvRcpt callback not implemented")
} }
} }
// Check if Header method was implemented // Check if Header method was implemented
if _, ok := milter.(checkForHeader); ok { if _, ok := milter.(checkForHeader); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Header callback implemented") logger.Println("Header callback implemented")
} }
C.setHeader(&smfilter) C.setHeader(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Header callback not implemented") logger.Println("Header callback not implemented")
} }
} }
// Check if Eoh method was implemented // Check if Eoh method was implemented
if _, ok := milter.(checkForEoh); ok { if _, ok := milter.(checkForEoh); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eoh callback implemented") logger.Println("Eoh callback implemented")
} }
C.setEoh(&smfilter) C.setEoh(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eoh callback not implemented") logger.Println("Eoh callback not implemented")
} }
} }
// Check if Body method was implemented // Check if Body method was implemented
if _, ok := milter.(checkForBody); ok { if _, ok := milter.(checkForBody); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Body callback implemented") logger.Println("Body callback implemented")
} }
C.setBody(&smfilter) C.setBody(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Body callback not implemented") logger.Println("Body callback not implemented")
} }
} }
// Check if Eom method was implemented // Check if Eom method was implemented
if _, ok := milter.(checkForEom); ok { if _, ok := milter.(checkForEom); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eom callback implemented") logger.Println("Eom callback implemented")
} }
C.setEom(&smfilter) C.setEom(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eom callback not implemented") logger.Println("Eom callback not implemented")
} }
} }
// Check if Abort method was implemented // Check if Abort method was implemented
if _, ok := milter.(checkForAbort); ok { if _, ok := milter.(checkForAbort); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Abort callback implemented") logger.Println("Abort callback implemented")
} }
C.setAbort(&smfilter) C.setAbort(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Abort callback not implemented") logger.Println("Abort callback not implemented")
} }
} }
// Check if Close method was implemented // Check if Close method was implemented
if _, ok := milter.(checkForClose); ok { if _, ok := milter.(checkForClose); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Close callback implemented") logger.Println("Close callback implemented")
} }
C.setClose(&smfilter) C.setClose(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Close callback not implemented") logger.Println("Close callback not implemented")
} }
} }
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("smfilter:") logger.Println("smfilter:")
LoggerPrintln(fmt.Sprint(smfilter)) logger.Println(fmt.Sprint(smfilter))
} }
// Setup socket connection // Setup socket connection
@@ -755,29 +802,25 @@ func Run(amilter Milter) int {
csocket := C.CString(socket) csocket := C.CString(socket)
defer C.free(unsafe.Pointer(csocket)) defer C.free(unsafe.Pointer(csocket))
if code := C.smfi_setconn(csocket); code != 0 { if code := C.smfi_setconn(csocket); code != 0 {
LoggerPrintf("smfi_setconn failed: %d\n", code) logger.Printf("smfi_setconn failed: %d\n", code)
} }
// Register the filter // Register the filter
if code := C.smfi_register(smfilter); code == C.MI_FAILURE { if code := C.smfi_register(smfilter); code == C.MI_FAILURE {
LoggerPrintf("smfi_register failed: %d\n", code) logger.Printf("smfi_register failed: %d\n", code)
} }
// Hand control to libmilter // Hand control to libmilter
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Handing over to libmilter") logger.Println("Handing over to libmilter")
} }
result := C.smfi_main() result := C.smfi_main()
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("smfi_main returned: %v\n", result) logger.Printf("smfi_main returned: %v\n", result)
} }
return int(result) return int(result)
} }
var LoggerPrintln func(...interface {}) func Stop() {
var LoggerPrintf func(string, ...interface {}) C.smfi_stop()
func init() {
LoggerPrintln = func(i ...interface {}) { fmt.Println(i...) }
LoggerPrintf = func(i string, j ...interface {}) { fmt.Printf(i, j...) }
} }

View File

@@ -15,13 +15,10 @@ Sample Milter
package main package main
import ( import (
"." "."
"fmt" "fmt"
) )
type Mymilter struct { type Mymilter struct {
gomilter.MilterRaw // Embed the basic functionality. No callbacks yet gomilter.MilterRaw // Embed the basic functionality. No callbacks yet
} }
@@ -43,7 +40,8 @@ func main() {
mymilter := new(Mymilter) mymilter := new(Mymilter)
mymilter.FilterName = "TestFilter" mymilter.FilterName = "TestFilter"
mymilter.Debug = true mymilter.Debug = true
mymilter.Flags = gomilter.AddHdrs|gomilter.AddRcpt mymilter.Flags = gomilter.ADDHDRS | gomilter.ADDRCPT
mymilter.Socket = "unix:/var/milterattachcheck/socket"
// Start Milter // Start Milter
gomilter.Run(mymilter) gomilter.Run(mymilter)

View File

@@ -7,10 +7,9 @@ Copyright (c) 2015 Leon Baker
package main package main
import ( import (
m "github.com/leonrbaker/gomilter" m "github.com/leonrbaker/gomilter"
"fmt" "log"
"os"
) )
type Mymilter struct { type Mymilter struct {
@@ -24,11 +23,13 @@ type T struct {
C string C string
} }
var logger *log.Logger
// Define the callback functions we are going to use // Define the callback functions we are going to use
func (milter *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8) { func (milter *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8) {
fmt.Println("mymilter.Connect was called") logger.Println("mymilter.Connect was called")
fmt.Printf("hostname: %s\n", hostname) logger.Printf("hostname: %s\n", hostname)
fmt.Printf("ip: %s\n", ip) logger.Printf("ip: %s\n", ip)
t := T{1, hostname, "Test"} t := T{1, hostname, "Test"}
m.SetPriv(ctx, &t) m.SetPriv(ctx, &t)
@@ -37,53 +38,53 @@ func (milter *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8
} }
func (milter *Mymilter) Helo(ctx uintptr, helohost string) (sfsistat int8) { func (milter *Mymilter) Helo(ctx uintptr, helohost string) (sfsistat int8) {
fmt.Println("mymilter.Helo was called") logger.Println("mymilter.Helo was called")
fmt.Printf("helohost: %s\n", helohost) logger.Printf("helohost: %s\n", helohost)
return return
} }
func (milter *Mymilter) EnvFrom(ctx uintptr, myargv []string) (sfsistat int8) { func (milter *Mymilter) EnvFrom(ctx uintptr, myargv []string) (sfsistat int8) {
fmt.Println("mymilter.EnvFrom was called") logger.Println("mymilter.EnvFrom was called")
fmt.Printf("myargv: %s\n", myargv) logger.Printf("myargv: %s\n", myargv)
// Show the value of a symbol // Show the value of a symbol
fmt.Printf("{mail_addr}: %v\n", m.GetSymVal(ctx, "{mail_addr}")) logger.Printf("{mail_addr}: %v\n", m.GetSymVal(ctx, "{mail_addr}"))
return return
} }
func (milter *Mymilter) EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8) { func (milter *Mymilter) EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8) {
fmt.Println("mymilter.EnvRcpt was called") logger.Println("mymilter.EnvRcpt was called")
fmt.Printf("myargv: %s\n", myargv) logger.Printf("myargv: %s\n", myargv)
// Show the value of a symbol // Show the value of a symbol
fmt.Printf("{rcpt_addr}: %v\n", m.GetSymVal(ctx, "{rcpt_addr}")) logger.Printf("{rcpt_addr}: %v\n", m.GetSymVal(ctx, "{rcpt_addr}"))
return return
} }
func (milter *Mymilter) Header(ctx uintptr, headerf, headerv string) (sfsistat int8) { func (milter *Mymilter) Header(ctx uintptr, headerf, headerv string) (sfsistat int8) {
fmt.Println("mymilter.Header was called") logger.Println("mymilter.Header was called")
fmt.Printf("header field: %s\n", headerf) logger.Printf("header field: %s\n", headerf)
fmt.Printf("header value: %s\n", headerv) logger.Printf("header value: %s\n", headerv)
return return
} }
func (milter *Mymilter) Eoh(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Eoh(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Eoh was called") logger.Println("mymilter.Eoh was called")
return return
} }
func (milter *Mymilter) Body(ctx uintptr, body []byte) (sfsistat int8) { func (milter *Mymilter) Body(ctx uintptr, body []byte) (sfsistat int8) {
// Be careful as a conversion of body to string will make a copy of body // Be careful as a conversion of body to string will make a copy of body
fmt.Println("mymilter.Body was called") logger.Println("mymilter.Body was called")
fmt.Println(string(body)) logger.Println(string(body))
fmt.Printf("Body Length: %d\n", len(body)) logger.Printf("Body Length: %d\n", len(body))
return return
} }
func (milter *Mymilter) Eom(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Eom(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Eom was called") logger.Println("mymilter.Eom was called")
var t T var t T
fmt.Println("m.GetPri:", m.GetPriv(ctx, &t)) logger.Println("m.GetPri:", m.GetPriv(ctx, &t))
fmt.Println("t:", t) logger.Println("t:", t)
m.AddHeader(ctx, "LEONUX-Mailer", m.AddHeader(ctx, "LEONUX-Mailer",
"test server;\n\ttest1=\"foobar\"") "test server;\n\ttest1=\"foobar\"")
@@ -94,19 +95,23 @@ func (milter *Mymilter) Eom(ctx uintptr) (sfsistat int8) {
} }
func (milter *Mymilter) Abort(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Abort(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Abort was called") logger.Println("mymilter.Abort was called")
return return
} }
func (milter *Mymilter) Close(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Close(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Close was called") logger.Println("mymilter.Close was called")
return return
} }
func main() { func main() {
mymilter := new(Mymilter) mymilter := new(Mymilter)
mymilter.FilterName = "TestFilter" mymilter.FilterName = "TestFilter"
logger = log.New(os.Stdout, "", log.LstdFlags)
mymilter.Logger = logger
mymilter.Debug = true mymilter.Debug = true
mymilter.Flags = m.ADDHDRS | m.ADDRCPT | m.CHGFROM | m.CHGBODY mymilter.Flags = m.ADDHDRS | m.ADDRCPT | m.CHGFROM | m.CHGBODY
mymilter.Socket = "unix:/var/milterattachcheck/socket" mymilter.Socket = "unix:/var/milterattachcheck/socket"