Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various improvements #22

Merged
merged 4 commits into from
Dec 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ listen on all filter "senderscore"

`-junkBelow` will prepend the 'X-Spam: yes' header to messages.

`-slowFactor` will delay all answers to a reputation-related percentage of its value in milliseconds. The formula is `delay - (delay / 100) * score` where `delay` is the argument to the `-slowFactor` parameter and `score` is the reputation score. By default, connections are never delayed.
`-slowFactor` will delay all answers to a reputation-related percentage of its value in milliseconds. The formula is `delay * (100 - score) / 100` where `delay` is the argument to the `-slowFactor` parameter and `score` is the reputation score. By default, connections are never delayed.

`-scoreHeader` will add an X-SenderScore header with reputation value if known.

Expand Down
87 changes: 45 additions & 42 deletions filter-senderscore.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var scoreHeader *bool
var whitelistFile *string
var disableConcurrency *bool
var whitelist = make(map[string]bool)
var subnetWhitelist = make([]*net.IPNet, 0)
var whitelistMasks = make(map[int]bool)

var version string

Expand Down Expand Up @@ -91,15 +91,16 @@ func linkConnect(phase string, sessionId string, params []string) {
return
}

if whitelist[addr.String()] {
fmt.Fprintf(os.Stderr, "IP address %s found on whitelist\n", addr)
s.score = 100
return
}
defer func(addr net.IP, s *session) {
fmt.Fprintf(os.Stderr, "link-connect addr=%s score=%d\n", addr, s.score)
}(addr, s)

for _, subnet := range subnetWhitelist {
if subnet.Contains(addr) {
fmt.Fprintf(os.Stderr, "IP address %s matches whitelisted subnet %s\n", addr, subnet)
for maskOnes := range whitelistMasks {
mask := net.CIDRMask(maskOnes, 32)
maskedAddr := addr.Mask(mask).String()
query := fmt.Sprintf("%s/%d", maskedAddr, maskOnes)
if whitelist[query] {
fmt.Fprintf(os.Stderr, "IP address %s matches whitelisted subnet %s\n", addr, query)
s.score = 100
return
}
Expand All @@ -120,8 +121,6 @@ func linkConnect(phase string, sessionId string, params []string) {

s.category = int8(category)
s.score = int8(score)

fmt.Fprintf(os.Stderr, "link-connect addr=%s score=%d\n", addr, score)
}

func linkDisconnect(phase string, sessionId string, params []string) {
Expand All @@ -131,17 +130,22 @@ func linkDisconnect(phase string, sessionId string, params []string) {
delete(sessions, sessionId)
}

func filterConnect(phase string, sessionId string, params []string) {
func getSession(sessionId string) *session {
s, ok := sessions[sessionId]
if !ok {
log.Fatalf("invalid session ID: %s", sessionId)
}
return s
}

// no slow factor, neutral or 100% good IP
if *slowFactor == -1 || s.score == -1 || s.score == 100 {
s.delay = -1
func filterConnect(phase string, sessionId string, params []string) {
s := getSession(sessionId)

if *slowFactor > 0 && s.score > 0 {
s.delay = *slowFactor * (100 - int(s.score)) / 100
} else {
s.delay = *slowFactor - ((*slowFactor / 100) * int(s.score))
// no slow factor or neutral IP address
s.delay = 0
}

if s.score != -1 && s.score < int8(*blockBelow) && *blockPhase == "connect" {
Expand Down Expand Up @@ -171,14 +175,10 @@ func produceOutput(msgType string, sessionId string, token string, format string
}

func dataline(phase string, sessionId string, params []string) {
s := getSession(sessionId)
token := params[0]
line := strings.Join(params[1:], "|")

s, ok := sessions[sessionId]
if !ok {
log.Fatalf("invalid session ID: %s", sessionId)
}

if s.first_line == true {
if s.score != -1 && *scoreHeader {
produceOutput("filter-dataline", sessionId, token, "X-SenderScore: %d", s.score)
Expand All @@ -190,10 +190,7 @@ func dataline(phase string, sessionId string, params []string) {
}

func delayedAnswer(phase string, sessionId string, params []string) {
s, ok := sessions[sessionId]
if !ok {
log.Fatalf("invalid session ID: %s", sessionId)
}
s := getSession(sessionId)

if s.score != -1 && s.score < int8(*blockBelow) && *blockPhase == phase {
delayedDisconnect(sessionId, params)
Expand All @@ -204,8 +201,8 @@ func delayedAnswer(phase string, sessionId string, params []string) {
}

func delayedJunk(sessionId string, params []string) {
s := getSession(sessionId)
token := params[0]
s := sessions[sessionId]
if *disableConcurrency {
waitThenAction(sessionId, token, s.delay, "junk")
} else {
Expand All @@ -214,8 +211,8 @@ func delayedJunk(sessionId string, params []string) {
}

func delayedProceed(sessionId string, params []string) {
s := getSession(sessionId)
token := params[0]
s := sessions[sessionId]
if *disableConcurrency {
waitThenAction(sessionId, token, s.delay, "proceed")
} else {
Expand All @@ -224,8 +221,8 @@ func delayedProceed(sessionId string, params []string) {
}

func delayedDisconnect(sessionId string, params []string) {
s := getSession(sessionId)
token := params[0]
s := sessions[sessionId]
if *disableConcurrency {
waitThenAction(sessionId, token, s.delay, "disconnect|550 your IP reputation is too low for this MX")
} else {
Expand All @@ -234,7 +231,7 @@ func delayedDisconnect(sessionId string, params []string) {
}

func waitThenAction(sessionId string, token string, delay int, format string, a ...interface{}) {
if delay != -1 {
if delay > 0 {
time.Sleep(time.Duration(delay) * time.Millisecond)
}
produceOutput("filter-result", sessionId, token, format, a...)
Expand Down Expand Up @@ -291,24 +288,30 @@ func loadWhitelists() {

scanner := bufio.NewScanner(file)
for scanner.Scan() {
addr := scanner.Text()
line := scanner.Text()

// remove comments and whitespace, skip empty lines
addr = strings.TrimSpace(strings.Split(addr, "#")[0])
if addr == "" {
line = strings.TrimSpace(strings.Split(line, "#")[0])
if line == "" {
continue
}

if strings.Contains(addr, "/") {
_, subnet, err := net.ParseCIDR(addr)
if err != nil {
log.Fatalf("invalid subnet: %s", addr)
}
fmt.Fprintf(os.Stderr, "Subnet %s added to whitelist\n", addr)
subnetWhitelist = append(subnetWhitelist, subnet)
} else {
fmt.Fprintf(os.Stderr, "IP address %s added to whitelist\n", addr)
whitelist[addr] = true
if !strings.Contains(line, "/") {
line += "/32"
}
_, subnet, err := net.ParseCIDR(line)
if err != nil {
log.Fatalf("invalid subnet: %s", subnet)
}

maskOnes, _ := subnet.Mask.Size()
if !whitelistMasks[maskOnes] {
whitelistMasks[maskOnes] = true
}
subnetStr := subnet.String()
if !whitelist[subnetStr] {
whitelist[subnetStr] = true
fmt.Fprintf(os.Stderr, "Subnet %s added to whitelist\n", subnetStr)
}
}
if err := scanner.Err(); err != nil {
Expand Down