@@ -94,6 +94,34 @@ func checkDomain(ctx context.Context, resolver dns.Resolver, cfg List, domain st
9494 }
9595}
9696
97+ func matchResponseRules (addrs []net.IPAddr , rules []ResponseRule ) (score int , messages []string , reasons []string , matched bool ) {
98+ // Track which rules have been matched to avoid counting the same rule multiple times
99+ matchedRules := make (map [int ]bool )
100+
101+ for _ , addr := range addrs {
102+ for ruleIdx , rule := range rules {
103+ // Skip if this rule has already been matched
104+ if matchedRules [ruleIdx ] {
105+ continue
106+ }
107+
108+ for _ , respNet := range rule .Networks {
109+ if respNet .Contains (addr .IP ) {
110+ score += rule .Score
111+ if rule .Message != "" {
112+ messages = append (messages , rule .Message )
113+ }
114+ reasons = append (reasons , addr .IP .String ())
115+ matchedRules [ruleIdx ] = true
116+ matched = true
117+ break // Move to next rule
118+ }
119+ }
120+ }
121+ }
122+ return
123+ }
124+
97125func checkIP (ctx context.Context , resolver dns.Resolver , cfg List , ip net.IP ) error {
98126 ipv6 := true
99127 if ipv4 := ip .To4 (); ipv4 != nil {
@@ -119,111 +147,72 @@ func checkIP(ctx context.Context, resolver dns.Resolver, cfg List, ip net.IP) er
119147 return err
120148 }
121149
150+ var filteredAddrs []net.IPAddr
151+ var score int
152+ var customMessage string
153+
122154 // If ResponseRules is configured, use new behavior
123155 if len (cfg .ResponseRules ) > 0 {
124- totalScore := 0
125- var matchedMessages []string
126- var matchedReasons []string
127-
128- // Track which rules have been matched to avoid counting the same rule multiple times
129- matchedRules := make (map [int ]bool )
130-
131- for _ , addr := range addrs {
132- for ruleIdx , rule := range cfg .ResponseRules {
133- // Skip if this rule has already been matched
134- if matchedRules [ruleIdx ] {
135- continue
136- }
137-
138- for _ , respNet := range rule .Networks {
139- if respNet .Contains (addr .IP ) {
140- totalScore += rule .Score
141- if rule .Message != "" {
142- matchedMessages = append (matchedMessages , rule .Message )
143- }
144- matchedReasons = append (matchedReasons , addr .IP .String ())
145- matchedRules [ruleIdx ] = true
146- break // Move to next rule
147- }
148- }
149- }
150- }
151-
152- if totalScore == 0 {
156+ matchedScore , matchedMessages , matchedReasons , matched := matchResponseRules (addrs , cfg .ResponseRules )
157+ if ! matched {
153158 return nil
154159 }
155-
156- // Attempt to extract explanation string from TXT records
157- txts , err := resolver .LookupTXT (ctx , query )
158- var reason string
159- if err == nil && len (txts ) > 0 {
160- reason = strings .Join (txts , "; " )
161- } else {
162- reason = strings .Join (matchedReasons , "; " )
163- }
164-
160+ score = matchedScore
161+
165162 // Use first matched message if available
166- message := ""
167163 if len (matchedMessages ) > 0 {
168- message = matchedMessages [0 ]
164+ customMessage = matchedMessages [0 ]
169165 }
170-
171- return ListedErr {
172- Identity : ip .String (),
173- List : cfg .Zone ,
174- Reason : reason ,
175- Score : totalScore ,
176- Message : message ,
166+
167+ // Build filteredAddrs from matched reasons for TXT lookup fallback
168+ for _ , reason := range matchedReasons {
169+ filteredAddrs = append (filteredAddrs , net.IPAddr {IP : net .ParseIP (reason )})
177170 }
178- }
179-
180- // Legacy behavior: use flat Responses filter
181- filteredAddrs := make ([]net.IPAddr , 0 , len (addrs ))
182- addrsLoop:
183- for _ , addr := range addrs {
184- // No responses whitelist configured - permit all.
185- if len (cfg .Responses ) == 0 {
186- filteredAddrs = append (filteredAddrs , addr )
187- continue
188- }
189-
190- for _ , respNet := range cfg .Responses {
191- if respNet .Contains (addr .IP ) {
171+ } else {
172+ // Legacy behavior: use flat Responses filter
173+ filteredAddrs = make ([]net.IPAddr , 0 , len (addrs ))
174+ addrsLoop:
175+ for _ , addr := range addrs {
176+ // No responses whitelist configured - permit all.
177+ if len (cfg .Responses ) == 0 {
192178 filteredAddrs = append (filteredAddrs , addr )
193- continue addrsLoop
179+ continue
180+ }
181+
182+ for _ , respNet := range cfg .Responses {
183+ if respNet .Contains (addr .IP ) {
184+ filteredAddrs = append (filteredAddrs , addr )
185+ continue addrsLoop
186+ }
194187 }
195188 }
196- }
197189
198- if len (filteredAddrs ) == 0 {
199- return nil
190+ if len (filteredAddrs ) == 0 {
191+ return nil
192+ }
200193 }
201194
202- // Attempt to extract explanation string.
195+ // Attempt to extract explanation string from TXT records (shared by both paths)
203196 txts , err := resolver .LookupTXT (ctx , query )
204- if err != nil || len (txts ) == 0 {
197+ var reason string
198+ if err == nil && len (txts ) > 0 {
199+ reason = strings .Join (txts , "; " )
200+ } else {
205201 // Not significant, include addresses as reason. Usually they are
206202 // mapped to some predefined 'reasons' by BL.
207-
208203 reasonParts := make ([]string , 0 , len (filteredAddrs ))
209204 for _ , addr := range filteredAddrs {
210205 reasonParts = append (reasonParts , addr .IP .String ())
211206 }
212-
213- return ListedErr {
214- Identity : ip .String (),
215- List : cfg .Zone ,
216- Reason : strings .Join (reasonParts , "; " ),
217- }
207+ reason = strings .Join (reasonParts , "; " )
218208 }
219209
220- // Some BLs provide multiple reasons (meta-BLs such as Spamhaus Zen) so
221- // don't mangle them by joining with "", instead join with "; ".
222-
223210 return ListedErr {
224211 Identity : ip .String (),
225212 List : cfg .Zone ,
226- Reason : strings .Join (txts , "; " ),
213+ Reason : reason ,
214+ Score : score ,
215+ Message : customMessage ,
227216 }
228217}
229218
0 commit comments