15 Commits

Author SHA1 Message Date
ai 36f4f49ff1 добавлена проверка типа 2022-10-23 17:27:53 +03:00
ai 081fac831d в Rspamd 3.3 изменилось формирование response JSON/milter/add_header: был хеш, стал массив. обрабатываются оба варианта 2022-10-22 00:56:13 +03:00
ai c2d8263e51 убрана ненужная передача по указателю 2022-08-23 18:00:42 +03:00
ai ab4d093b8e увеличение размера буфера 2022-08-23 08:18:17 +03:00
ai 415b8d932f оптимизация конкатенации строк 2022-08-23 03:06:20 +03:00
ai 91237e1000 оптимизация конкатенации строк 2022-08-23 02:14:01 +03:00
ai 1e2da3603d оптимизация создания headers 2022-08-23 00:53:36 +03:00
ai fedbb53942 replaceSpecChars сделана внутренней функцией модуля cgp. и сопутствующие изменения. 2022-08-22 21:59:03 +03:00
ai 0e670b786a добавлены actions discard и quarantine. добавлена отсылка сообщений на mirror-to. переработан action reject. 2022-08-22 08:52:50 +03:00
ai 3314c74a10 добавлен -discard-score 2022-07-25 22:21:08 +03:00
ai e8ea2d06ef добавлен DiscardScore 2022-07-25 18:40:36 +03:00
ai 717aba1e48 несколько параметров для go fmt 2021-10-02 18:17:33 +03:00
ai 73161ebae3 изменена обработка 'soft reject' 2021-10-02 18:16:10 +03:00
ai 8bf84dc18d gitignore 2021-02-28 04:20:55 +03:00
ai 0cef2a76ea switch to tidy 2021-02-28 04:20:13 +03:00
9 changed files with 133 additions and 69 deletions
-1
View File
@@ -1,3 +1,2 @@
rspamd-cgp rspamd-cgp
*.CU* *.CU*
vendor/github.com
+6 -4
View File
@@ -5,8 +5,8 @@
http://www.communigate.com/CGPMcAfee/#Integrate http://www.communigate.com/CGPMcAfee/#Integrate
Copyright (C) 2017-2021 Andrey Igoshin <ai@vsu.ru> Copyright (C) 2017-2022 Andrey Igoshin <ai@vsu.ru>
Version 1.3.0 Version 1.4.0
https://git.vsu.ru/ai/rspamd-cgp https://git.vsu.ru/ai/rspamd-cgp
@@ -16,7 +16,9 @@
Authentication Identifier (default CommuniGate Pro Main Domain) Authentication Identifier (default CommuniGate Pro Main Domain)
-host string -host string
Rspamd host to connect (default "localhost:11333") Rspamd host to connect (default "localhost:11333")
-reject-action string -mirror-to string
Reject action: "add_header" or "discard" (default "add_header") Mirror selected messages to email
-reject-score float
Reject score to discard (default +Inf)
-timeout duration -timeout duration
Rspamd request timeout (default 15s) Rspamd request timeout (default 15s)
Executable
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
export GOPATH="${HOME}/src/rspamd-cgp"
if [ "$1" == "fmt" ]; then
go fmt $*
elif [ "$1" == "tidy" ]; then
go mod tidy
elif [ "$1" == "vet" ]; then
echo "vet..."
go vet
else
go build
fi
+43 -15
View File
@@ -27,11 +27,14 @@ func init() {
} }
} }
func AddHeader(seq int, headers *string) { func AddHeader(seq int, headers []string) {
hdrs := replaceSpecChars(strings.Join(headers, "\n"))
if protocol >= 4 { if protocol >= 4 {
Putline("%d ADDHEADER \"%s\" OK\n", seq, *headers) Putline("%d ADDHEADER \"%s\" OK\n", seq, hdrs)
} else { } else {
Putline("%d ADDHEADER \"%s\"\n", seq, *headers) Putline("%d ADDHEADER \"%s\"\n", seq, hdrs)
} }
} }
@@ -49,14 +52,14 @@ func Intf(seq int, ver string) {
Putline("%d INTF %d\n", seq, protocol) Putline("%d INTF %d\n", seq, protocol)
} }
func Message(filename *string) (from string, rcpts []string, auth string, ip string, qid int, body []byte, err error) { func Message(filename string) (from string, rcpts []string, auth string, ip string, qid int, body []byte, err error) {
qid, err = strconv.Atoi((*filename)[strings.LastIndexByte(*filename, '/')+1 : strings.LastIndexByte(*filename, '.')]) qid, err = strconv.Atoi((filename)[strings.LastIndexByte(filename, '/')+1 : strings.LastIndexByte(filename, '.')])
if err != nil { if err != nil {
return return
} }
h, err := os.Open(*filename) h, err := os.Open(filename)
if err != nil { if err != nil {
return return
} }
@@ -123,6 +126,30 @@ func Message(filename *string) (from string, rcpts []string, auth string, ip str
return return
} }
func MirrorTo(seq int, to []string, headers []string) {
hdrs := replaceSpecChars(strings.Join(headers, "\n"))
if protocol >= 4 {
if len(to) > 0 {
mirrorTo := []string{}
for _, m := range to {
mirrorTo = append(mirrorTo, fmt.Sprintf("MIRRORTO \"%s\"", m))
}
Putline("%d %s ADDHEADER \"%s\" OK\n", seq, strings.Join(mirrorTo, " "), hdrs)
} else {
Putline("%d ADDHEADER \"%s\" OK\n", seq, hdrs)
}
} else {
Putline("%d ADDHEADER \"%s\"\n", seq, hdrs)
}
}
func Ok(seq int) { func Ok(seq int) {
Putline("%d OK\n", seq) Putline("%d OK\n", seq)
} }
@@ -136,16 +163,17 @@ func Reject(seq int) {
Putline("%d REJECT Try again later\n", seq) Putline("%d REJECT Try again later\n", seq)
} }
func ReplaceSpecChars(msgo *string) *string { func replaceSpecChars(msg string) string {
var msgn string var sb strings.Builder
sb.Grow(len(msg) + 128)
for _, symbol := range *msgo { for _, symbol := range msg {
switch symbol { switch symbol {
case rune('\\'): case rune('\\'):
// replace \ -> \\ (CGP backslash) // replace \ -> \\ (CGP backslash)
msgn += "\\\\" sb.WriteString("\\\\")
case 0x0000: case 0x0000:
fallthrough fallthrough
@@ -154,22 +182,22 @@ func ReplaceSpecChars(msgo *string) *string {
case rune('\n'): case rune('\n'):
// replace \n -> \\e (CGP End-of-Line) // replace \n -> \\e (CGP End-of-Line)
msgn += "\\e" sb.WriteString("\\e")
case rune('\t'): case rune('\t'):
// replace \t -> \\t (CGP Tab) // replace \t -> \\t (CGP Tab)
msgn += "\\t" sb.WriteString("\\t")
case rune('"'): case rune('"'):
// replace \" -> \\" (CGP quote) // replace \" -> \\" (CGP quote)
msgn += "\\\"" sb.WriteString("\\\"")
default: default:
msgn += string(symbol) sb.WriteRune(symbol)
} }
} }
return &msgn return sb.String()
} }
func setMainDomain() (err error) { func setMainDomain() (err error) {
+14 -8
View File
@@ -2,31 +2,37 @@ package config
import ( import (
"flag" "flag"
"math"
"strings"
"time" "time"
) )
type Config struct { type Config struct {
AuthservId string AuthservId string
Discard bool Debug bool
Host string Host string
Timeout time.Duration MirrorTo []string
RejectScore float64
Timeout time.Duration
} }
func New() *Config { func New() *Config {
config := new(Config) config := new(Config)
var rejectAction string var mirrorTo string
flag.StringVar(&config.AuthservId, "authserv-id", "", "Authentication Identifier (default CommuniGate Pro Main Domain)") flag.StringVar(&config.AuthservId, "authserv-id", "", "Authentication Identifier (default CommuniGate Pro Main Domain)")
flag.StringVar(&config.Host, "host", "localhost:11333", "Rspamd host to connect") flag.StringVar(&config.Host, "host", "localhost:11333", "Rspamd host to connect")
flag.StringVar(&rejectAction, "reject-action", "add_header", "Reject action: \"add_header\" or \"discard\"") flag.StringVar(&mirrorTo, "mirror-to", "", "Mirror selected messages to email")
flag.Float64Var(&config.RejectScore, "reject-score", math.Inf(1), "Reject score to discard")
flag.DurationVar(&config.Timeout, "timeout", 15*time.Second, "Rspamd request timeout") flag.DurationVar(&config.Timeout, "timeout", 15*time.Second, "Rspamd request timeout")
flag.BoolVar(&config.Debug, "debug", false, "Export debug information (for developers)")
flag.Parse() flag.Parse()
if rejectAction == "discard" { if len(mirrorTo) > 0 {
config.Discard = true config.MirrorTo = strings.Split(strings.ReplaceAll(mirrorTo, " ", ""), ",")
} }
if config.Timeout < time.Second { if config.Timeout < time.Second {
+7 -2
View File
@@ -1,5 +1,10 @@
module git.vsu.ru/ai/rspamd-cgp module git.vsu.ru/ai/rspamd-cgp
go 1.16 go 1.18
require github.com/json-iterator/go v1.1.10 require github.com/json-iterator/go v1.1.12
require (
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
)
+7 -4
View File
@@ -1,12 +1,15 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+42 -28
View File
@@ -5,7 +5,10 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os"
"reflect"
"strconv" "strconv"
"strings"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
@@ -21,8 +24,10 @@ var (
var authservId string var authservId string
var client *http.Client var client *http.Client
var discard bool var mirrorTo []string
var rejectScore float64
var host string var host string
var debug bool
func init() { func init() {
@@ -34,8 +39,10 @@ func init() {
authservId = cgp.MainDomain authservId = cgp.MainDomain
} }
discard = config.Discard mirrorTo = config.MirrorTo
rejectScore = config.RejectScore
host = "http://" + config.Host + "/checkv2" host = "http://" + config.Host + "/checkv2"
debug = config.Debug
tr := &http.Transport{ tr := &http.Transport{
DisableCompression: true, DisableCompression: true,
@@ -47,18 +54,14 @@ func init() {
} }
} }
func appendHeaders(hdrs, junk *string) *string { func printResponse(v any) {
if *hdrs != "" { printed, _ := json.MarshalIndent(v, "", " ")
headers := *hdrs + "\n" + *junk fmt.Fprintln(os.Stderr, string(printed))
return &headers
} else {
return junk
}
} }
func Scan(seq int, filename string) { func Scan(seq int, filename string) {
from, rcpts, auth, ip, qid, body, err := cgp.Message(&filename) from, rcpts, auth, ip, qid, body, err := cgp.Message(filename)
if err != nil { if err != nil {
cgp.Failure(seq, qid, err) cgp.Failure(seq, qid, err)
return return
@@ -103,20 +106,24 @@ func Scan(seq int, filename string) {
return return
} }
var headers string if debug {
printResponse(js)
}
var headers []string
if _, ok := js["dkim-signature"]; ok { if _, ok := js["dkim-signature"]; ok {
headers = "DKIM-Signature: " + js["dkim-signature"].(string) headers = append(headers, strings.Join([]string{"DKIM-Signature: ", js["dkim-signature"].(string)}, ""))
} }
if milter, ok := js["milter"]; ok { if milter, ok := js["milter"]; ok {
if hdrs, ok := milter.(map[string]interface{})["add_headers"]; ok { if hdrs, ok := milter.(map[string]interface{})["add_headers"]; ok {
for h, vh := range hdrs.(map[string]interface{}) { if reflect.TypeOf(hdrs).String() == "map[string]interface {}" {
if v, ok := vh.(map[string]interface{})["value"].(string); ok && v != "" { for h, vh := range hdrs.(map[string]interface{}) {
if headers != "" { if reflect.TypeOf(vh).String() == "map[string]interface {}" {
headers += "\n" + h + ": " + v if v, ok := vh.(map[string]interface{})["value"].(string); ok && v != "" {
} else { headers = append(headers, strings.Join([]string{h, v}, ": "))
headers = h + ": " + v }
} }
} }
} }
@@ -124,38 +131,45 @@ func Scan(seq int, filename string) {
} }
action := js["action"] action := js["action"]
score := (js["score"]).(float64)
cgp.Putline("* %d [%d]: Action: %s; Score: %.2f/%.2f; Time elapsed: %.3fs\n", cgp.Putline("* %d [%d]: Action: %s; Score: %.2f/%.2f; Time elapsed: %.3fs\n",
seq, qid, action, js["score"], js["required_score"], js["time_real"]) seq, qid, action, score, js["required_score"], js["time_real"])
switch action { switch action {
case "no action": case "no action":
if headers != "" { if len(headers) > 0 {
cgp.AddHeader(seq, cgp.ReplaceSpecChars(&headers)) cgp.AddHeader(seq, headers)
} else { } else {
cgp.Ok(seq) cgp.Ok(seq)
} }
case "discard":
cgp.Discard(seq)
case "quarantine":
cgp.MirrorTo(seq, mirrorTo, append(headers, headerJunkR))
case "reject": case "reject":
if discard { if score >= rejectScore {
cgp.Putline("* %d [%d]: Action set to discard due to Score(%.2f) >= rejectScore(%d)\n",
seq, qid, score, rejectScore)
cgp.Discard(seq) cgp.Discard(seq)
} else { } else {
cgp.AddHeader(seq, cgp.ReplaceSpecChars(appendHeaders(&headers, &headerJunkR))) cgp.AddHeader(seq, append(headers, headerJunkR))
} }
case "rewrite subject": case "rewrite subject":
fallthrough fallthrough
case "add header": case "add header":
cgp.AddHeader(seq, cgp.ReplaceSpecChars(appendHeaders(&headers, &headerJunkA))) cgp.AddHeader(seq, append(headers, headerJunkA))
case "greylist": case "greylist":
cgp.AddHeader(seq, cgp.ReplaceSpecChars(appendHeaders(&headers, &headerJunkG))) fallthrough
case "soft reject": case "soft reject":
cgp.Reject(seq) cgp.AddHeader(seq, append(headers, headerJunkG))
default: default:
cgp.Failure(seq, qid, fmt.Errorf("Unknown action: %v", action)) cgp.Failure(seq, qid, fmt.Errorf("Unknown action: %v", action))
} }
} }
-7
View File
@@ -1,7 +0,0 @@
# github.com/json-iterator/go v1.1.10
## explicit
github.com/json-iterator/go
# github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
github.com/modern-go/concurrent
# github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
github.com/modern-go/reflect2