249 lines
3.4 KiB
Go
249 lines
3.4 KiB
Go
package cgp
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"git.vsu.ru/ai/rspamd-cgp/utils"
|
|
)
|
|
|
|
func getHeader(m *bufio.Reader) (hdr string, err error) {
|
|
|
|
var c byte
|
|
var line []byte
|
|
|
|
var b strings.Builder
|
|
b.Grow(384)
|
|
|
|
for {
|
|
c, err = m.ReadByte()
|
|
if err == io.EOF {
|
|
if c == 0 {
|
|
break
|
|
} else {
|
|
err = nil
|
|
}
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if b.Len() == 0 {
|
|
|
|
if c == ' ' || c == '\t' {
|
|
err = m.UnreadByte()
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = fmt.Errorf("bad header")
|
|
return
|
|
|
|
} else if c == '\n' {
|
|
|
|
b.WriteByte(c)
|
|
break
|
|
|
|
} else {
|
|
|
|
b.WriteByte(c)
|
|
|
|
line, err = m.ReadSlice('\n')
|
|
if err == io.EOF {
|
|
err = nil
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
b.Write(line)
|
|
}
|
|
|
|
} else {
|
|
|
|
if c == ' ' || c == '\t' {
|
|
|
|
b.WriteByte(c)
|
|
|
|
line, err = m.ReadSlice('\n')
|
|
if err == io.EOF {
|
|
err = nil
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
b.Write(line)
|
|
|
|
} else {
|
|
err = m.UnreadByte()
|
|
if err != nil {
|
|
return
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
hdr = b.String()
|
|
|
|
return
|
|
}
|
|
|
|
func getHelo(body []byte) (helo string) {
|
|
|
|
var hdr string
|
|
var err error
|
|
|
|
m := bufio.NewReader(bytes.NewReader(body))
|
|
|
|
for {
|
|
|
|
hdr, err = m.ReadString('\n')
|
|
if err == io.EOF {
|
|
err = nil
|
|
if len(hdr) == 0 {
|
|
break
|
|
}
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if hdr == "\n" {
|
|
// конец RFC5322 заголовка
|
|
break
|
|
}
|
|
|
|
if strings.HasPrefix(hdr, recvHdr) {
|
|
if s := reHELO1.FindAllStringSubmatch(hdr, -1); s != nil {
|
|
helo = s[0][1]
|
|
} else if s := reHELO2.FindAllStringSubmatch(hdr, -1); s != nil {
|
|
helo = s[0][1]
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func isSeen(m *bufio.Reader) (seen bool, err error) {
|
|
|
|
var found bool
|
|
var seenSum string
|
|
var hdr string
|
|
|
|
for {
|
|
|
|
hdr, err = getHeader(m)
|
|
if err == io.EOF {
|
|
err = nil
|
|
if len(hdr) == 0 {
|
|
break
|
|
}
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if hdr == "\n" {
|
|
// конец RFC5322 заголовка
|
|
break
|
|
}
|
|
|
|
if !found {
|
|
|
|
if seenSum, found = strings.CutPrefix(hdr, seenHdr); found {
|
|
seenSum = strings.TrimSpace(seenSum)
|
|
}
|
|
|
|
} else if strings.HasPrefix(hdr, recvHdr) {
|
|
|
|
bs := sha256.Sum224([]byte(utils.NoSpace(hdr)))
|
|
sum := hex.EncodeToString(bs[:])
|
|
if seenSum == sum {
|
|
seen = true
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func makeSeen(body []byte) (seenhdr string, err error) {
|
|
|
|
var hdr string
|
|
|
|
m := bufio.NewReader(bytes.NewReader(body))
|
|
|
|
for {
|
|
|
|
hdr, err = getHeader(m)
|
|
if err == io.EOF {
|
|
err = nil
|
|
if len(hdr) == 0 {
|
|
break
|
|
}
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if hdr == "\n" {
|
|
break
|
|
}
|
|
|
|
if strings.HasPrefix(hdr, recvHdr) {
|
|
bs := sha256.Sum224([]byte(utils.NoSpace(hdr)))
|
|
seenhdr = seenHdr + " " + hex.EncodeToString(bs[:])
|
|
break
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func replaceSpecChars(msg string) string {
|
|
|
|
var sb strings.Builder
|
|
sb.Grow(len(msg) + 128)
|
|
|
|
for _, symbol := range msg {
|
|
|
|
switch symbol {
|
|
case rune('\\'):
|
|
// replace \ -> \\ (CGP backslash)
|
|
sb.WriteString("\\\\")
|
|
|
|
case 0x0000:
|
|
fallthrough
|
|
case rune('\r'):
|
|
continue
|
|
|
|
case rune('\n'):
|
|
// replace \n -> \\e (CGP End-of-Line)
|
|
sb.WriteString("\\e")
|
|
|
|
case rune('\t'):
|
|
// replace \t -> \\t (CGP Tab)
|
|
sb.WriteString("\\t")
|
|
|
|
case rune('"'):
|
|
// replace \" -> \\" (CGP quote)
|
|
sb.WriteString("\\\"")
|
|
|
|
default:
|
|
sb.WriteRune(symbol)
|
|
}
|
|
}
|
|
|
|
return sb.String()
|
|
}
|