1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  
21  package org.apache.james.smtpserver;
22  
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.avalon.framework.container.ContainerUtil;
29  import org.apache.james.jspf.core.DNSRequest;
30  import org.apache.james.jspf.core.DNSService;
31  import org.apache.james.jspf.core.exceptions.TimeoutException;
32  import org.apache.james.smtpserver.core.filter.fastfail.SPFHandler;
33  import org.apache.james.smtpserver.junkscore.JunkScore;
34  import org.apache.james.smtpserver.junkscore.JunkScoreImpl;
35  import org.apache.james.test.mock.avalon.MockLogger;
36  import org.apache.mailet.base.test.FakeMail;
37  import org.apache.mailet.Mail;
38  import org.apache.mailet.MailAddress;
39  
40  import junit.framework.TestCase;
41  
42  public class SPFHandlerTest extends TestCase {
43  
44      private DNSService mockedDnsService;
45  
46      private SMTPSession mockedSMTPSession;;
47  
48      private boolean relaying = false;
49  
50      private String command = "MAIL";
51  
52      protected void setUp() throws Exception {
53          super.setUp();
54          setupMockedDnsService();
55          setRelayingAllowed(false);
56      }
57  
58      /**
59       * Set relayingAllowed
60       * 
61       * @param relaying
62       *            true or false
63       */
64      private void setRelayingAllowed(boolean relaying) {
65          this.relaying = relaying;
66      }
67  
68      /**
69       * Setup the mocked dnsserver
70       * 
71       */
72      private void setupMockedDnsService() {
73          mockedDnsService = new DNSService() {
74  
75              public List getLocalDomainNames() {
76                  throw new UnsupportedOperationException(
77                          "Unimplemented mock service");
78              }
79  
80              public void setTimeOut(int arg0) {
81                  // do nothing
82              }
83  
84              public int getRecordLimit() {
85                  return 0;
86              }
87  
88              public void setRecordLimit(int arg0) {
89                  throw new UnsupportedOperationException(
90                  "Unimplemented mock service");
91              }
92  
93              public List getRecords(DNSRequest req) throws TimeoutException {
94                  switch (req.getRecordType()) {
95                      case DNSRequest.TXT:
96                      case DNSRequest.SPF:
97                          List l = new ArrayList();
98                          if (req.getHostname().equals("spf1.james.apache.org")) {
99                              // pass
100                             l.add("v=spf1 +all");
101                             return l;
102                         } else if (req.getHostname().equals("spf2.james.apache.org")) {
103                             // fail
104                             l.add("v=spf1 -all");
105                             return l;
106                         } else if (req.getHostname().equals("spf3.james.apache.org")) {
107                             // softfail
108                             l.add("v=spf1 ~all");
109                             return l;
110                         } else if (req.getHostname().equals("spf4.james.apache.org")) {
111                             // permerror
112                             l.add("v=spf1 badcontent!");
113                             return l;
114                         } else if (req.getHostname().equals("spf5.james.apache.org")) {
115                             // temperror
116                             throw new TimeoutException("TIMEOUT");
117                         } else {
118                             return null;
119                         }
120                     default:
121                         throw new UnsupportedOperationException(
122                         "Unimplemented mock service");
123                 }
124             }
125 
126         };
127     }
128 
129     private void setCommand(String command) {
130         this.command = command;
131     }
132 
133     /**
134      * Setup mocked smtpsession
135      */
136     private void setupMockedSMTPSession(final String ip, final String helo,
137             final MailAddress sender, final MailAddress recipient) {
138         mockedSMTPSession = new AbstractSMTPSession() {
139             HashMap state = new HashMap();
140 
141             HashMap connectionState = new HashMap();
142 
143             Mail mail = new FakeMail();
144 
145             boolean stopHandler = false;
146 
147             public void writeResponse(String respString) {
148                 // Do nothing
149             }
150 
151             public String getCommandName() {
152                 return command;
153             }
154 
155             public Mail getMail() {
156                 return mail;
157             }
158 
159             public String getRemoteIPAddress() {
160                 return ip;
161             }
162 
163             public Map getState() {
164                 state.put(SMTPSession.CURRENT_HELO_NAME, helo);
165                 state.put(SMTPSession.SENDER, sender);
166                 state.put(SMTPSession.CURRENT_RECIPIENT, recipient);
167                 return state;
168             }
169 
170             public boolean isRelayingAllowed() {
171                 return relaying;
172             }
173 
174             public boolean isAuthRequired() {
175                 return false;
176             }
177 
178             public int getRcptCount() {
179                 return 0;
180             }
181 
182             public void setStopHandlerProcessing(boolean b) {
183                 stopHandler = b;
184             }
185 
186             public boolean getStopHandlerProcessing() {
187                 return stopHandler;
188             }
189 
190             public Map getConnectionState() {
191                 return connectionState;
192             }
193 
194             public void resetConnectionState() {
195                 connectionState.clear();
196             }
197 
198         };
199     }
200 
201     private void runHandlers(SPFHandler spf, SMTPSession mockedSMTPSession) {
202 
203         setCommand("MAIL");
204         spf.onCommand(mockedSMTPSession);
205 
206         setCommand("RCPT");
207         spf.onCommand(mockedSMTPSession);
208 
209         spf.onMessage(mockedSMTPSession);
210     }
211 
212     public void testSPFpass() throws Exception {
213         setupMockedSMTPSession("192.168.100.1", "spf1.james.apache.org",
214                 new MailAddress("test@spf1.james.apache.org"), new MailAddress(
215                         "test@localhost"));
216         SPFHandler spf = new SPFHandler();
217 
218 
219         ContainerUtil.enableLogging(spf, new MockLogger());
220         
221         spf.setDNSService(mockedDnsService);
222         
223         spf.initialize();
224 
225         runHandlers(spf, mockedSMTPSession);
226 
227         assertNull("Not reject", mockedSMTPSession.getState().get(
228                 SPFHandler.SPF_BLOCKLISTED));
229         assertNull("Not blocked so no details", mockedSMTPSession.getState()
230                 .get(SPFHandler.SPF_DETAIL));
231         assertNull("No tempError", mockedSMTPSession.getState().get(
232                 SPFHandler.SPF_TEMPBLOCKLISTED));
233         assertNotNull("Header should present", mockedSMTPSession.getState()
234                 .get(SPFHandler.SPF_HEADER));
235         assertEquals("header", mockedSMTPSession.getState().get(
236                 SPFHandler.SPF_HEADER), mockedSMTPSession.getMail()
237                 .getAttribute(SPFHandler.SPF_HEADER_MAIL_ATTRIBUTE_NAME));
238         assertFalse(mockedSMTPSession.getStopHandlerProcessing());
239     }
240 
241     public void testSPFfail() throws Exception {
242         setupMockedSMTPSession("192.168.100.1", "spf2.james.apache.org",
243                 new MailAddress("test@spf2.james.apache.org"), new MailAddress(
244                         "test@localhost"));
245         SPFHandler spf = new SPFHandler();
246 
247         ContainerUtil.enableLogging(spf, new MockLogger());
248         
249         spf.setDNSService(mockedDnsService);     
250         
251         spf.initialize();
252 
253         runHandlers(spf, mockedSMTPSession);
254 
255         assertNotNull("reject", mockedSMTPSession.getState().get(
256                 SPFHandler.SPF_BLOCKLISTED));
257         assertNotNull("blocked", mockedSMTPSession.getState().get(
258                 SPFHandler.SPF_DETAIL));
259         assertNull("No tempError", mockedSMTPSession.getState().get(
260                 SPFHandler.SPF_TEMPBLOCKLISTED));
261         assertNotNull("Header should present", mockedSMTPSession.getState()
262                 .get(SPFHandler.SPF_HEADER));
263         assertTrue(mockedSMTPSession.getStopHandlerProcessing());
264     }
265 
266     public void testSPFsoftFail() throws Exception {
267         setupMockedSMTPSession("192.168.100.1", "spf3.james.apache.org",
268                 new MailAddress("test@spf3.james.apache.org"), new MailAddress(
269                         "test@localhost"));
270         SPFHandler spf = new SPFHandler();
271 
272         ContainerUtil.enableLogging(spf, new MockLogger());
273         
274         spf.setDNSService(mockedDnsService);
275         
276         spf.initialize();
277 
278         runHandlers(spf, mockedSMTPSession);
279 
280         assertNull("not reject", mockedSMTPSession.getState().get(
281                 SPFHandler.SPF_BLOCKLISTED));
282         assertNull("no details ", mockedSMTPSession.getState().get(
283                 SPFHandler.SPF_DETAIL));
284         assertNull("No tempError", mockedSMTPSession.getState().get(
285                 SPFHandler.SPF_TEMPBLOCKLISTED));
286         assertNotNull("Header should present", mockedSMTPSession.getState()
287                 .get(SPFHandler.SPF_HEADER));
288         assertEquals("header", mockedSMTPSession.getState().get(
289                 SPFHandler.SPF_HEADER), mockedSMTPSession.getMail()
290                 .getAttribute(SPFHandler.SPF_HEADER_MAIL_ATTRIBUTE_NAME));
291         assertFalse(mockedSMTPSession.getStopHandlerProcessing());
292     }
293 
294     public void testSPFsoftFailRejectEnabled() throws Exception {
295         setupMockedSMTPSession("192.168.100.1", "spf3.james.apache.org",
296                 new MailAddress("test@spf3.james.apache.org"), new MailAddress(
297                         "test@localhost"));
298         SPFHandler spf = new SPFHandler();
299 
300         ContainerUtil.enableLogging(spf, new MockLogger());
301        
302         spf.setDNSService(mockedDnsService);
303        
304         spf.initialize();
305         
306         spf.setBlockSoftFail(true);
307 
308         setCommand("MAIL");
309         spf.onCommand(mockedSMTPSession);
310 
311         setCommand("RCPT");
312         spf.onCommand(mockedSMTPSession);
313 
314         assertNotNull("reject", mockedSMTPSession.getState().get(
315                 SPFHandler.SPF_BLOCKLISTED));
316         assertNotNull("details ", mockedSMTPSession.getState().get(
317                 SPFHandler.SPF_DETAIL));
318         assertNull("No tempError", mockedSMTPSession.getState().get(
319                 SPFHandler.SPF_TEMPBLOCKLISTED));
320         assertNotNull("Header should present", mockedSMTPSession.getState()
321                 .get(SPFHandler.SPF_HEADER));
322         assertTrue(mockedSMTPSession.getStopHandlerProcessing());
323     }
324 
325     public void testSPFpermError() throws Exception {
326         setupMockedSMTPSession("192.168.100.1", "spf4.james.apache.org",
327                 new MailAddress("test@spf4.james.apache.org"), new MailAddress(
328                         "test@localhost"));
329         SPFHandler spf = new SPFHandler();
330 
331         ContainerUtil.enableLogging(spf, new MockLogger());
332         
333         spf.setDNSService(mockedDnsService);
334         
335         spf.initialize();
336         
337         spf.setBlockSoftFail(true);
338 
339         runHandlers(spf, mockedSMTPSession);
340 
341         assertNotNull("reject", mockedSMTPSession.getState().get(
342                 SPFHandler.SPF_BLOCKLISTED));
343         assertNotNull("details ", mockedSMTPSession.getState().get(
344                 SPFHandler.SPF_DETAIL));
345         assertNull("No tempError", mockedSMTPSession.getState().get(
346                 SPFHandler.SPF_TEMPBLOCKLISTED));
347         assertNotNull("Header should present", mockedSMTPSession.getState()
348                 .get(SPFHandler.SPF_HEADER));
349         assertTrue(mockedSMTPSession.getStopHandlerProcessing());
350     }
351 
352     public void testSPFtempError() throws Exception {
353         setupMockedSMTPSession("192.168.100.1", "spf5.james.apache.org",
354                 new MailAddress("test@spf5.james.apache.org"), new MailAddress(
355                         "test@localhost"));
356         SPFHandler spf = new SPFHandler();
357 
358         ContainerUtil.enableLogging(spf, new MockLogger());
359         
360         spf.setDNSService(mockedDnsService);
361 
362         spf.initialize();
363         
364         runHandlers(spf, mockedSMTPSession);
365 
366         assertNull("no reject", mockedSMTPSession.getState().get(
367                 SPFHandler.SPF_BLOCKLISTED));
368         assertNull("no details ", mockedSMTPSession.getState().get(
369                 SPFHandler.SPF_DETAIL));
370         assertNotNull("tempError", mockedSMTPSession.getState().get(
371                 SPFHandler.SPF_TEMPBLOCKLISTED));
372         assertNotNull("Header should present", mockedSMTPSession.getState()
373                 .get(SPFHandler.SPF_HEADER));
374         assertTrue(mockedSMTPSession.getStopHandlerProcessing());
375     }
376 
377     public void testSPFNoRecord() throws Exception {
378         setupMockedSMTPSession("192.168.100.1", "spf6.james.apache.org",
379                 new MailAddress("test@spf6.james.apache.org"), new MailAddress(
380                         "test@localhost"));
381         SPFHandler spf = new SPFHandler();
382 
383         ContainerUtil.enableLogging(spf, new MockLogger());
384 
385         spf.setDNSService(mockedDnsService);
386 
387         spf.initialize();
388         
389         runHandlers(spf, mockedSMTPSession);
390 
391         assertNull("no reject", mockedSMTPSession.getState().get(
392                 SPFHandler.SPF_BLOCKLISTED));
393         assertNull("no details ", mockedSMTPSession.getState().get(
394                 SPFHandler.SPF_DETAIL));
395         assertNull("no tempError", mockedSMTPSession.getState().get(
396                 SPFHandler.SPF_TEMPBLOCKLISTED));
397         assertNotNull("Header should present", mockedSMTPSession.getState()
398                 .get(SPFHandler.SPF_HEADER));
399         assertEquals("header", mockedSMTPSession.getState().get(
400                 SPFHandler.SPF_HEADER), mockedSMTPSession.getMail()
401                 .getAttribute(SPFHandler.SPF_HEADER_MAIL_ATTRIBUTE_NAME));
402         assertFalse(mockedSMTPSession.getStopHandlerProcessing());
403     }
404 
405     public void testSPFpermErrorNotRejectPostmaster() throws Exception {
406         setupMockedSMTPSession("192.168.100.1", "spf4.james.apache.org",
407                 new MailAddress("test@spf4.james.apache.org"), new MailAddress(
408                         "postmaster@localhost"));
409         SPFHandler spf = new SPFHandler();
410 
411         ContainerUtil.enableLogging(spf, new MockLogger());
412 
413         spf.setDNSService(mockedDnsService);
414         
415         spf.initialize();
416         
417         spf.setBlockSoftFail(true);
418 
419         runHandlers(spf, mockedSMTPSession);
420 
421         assertNotNull("not removed this state", mockedSMTPSession.getState().get(
422                 SPFHandler.SPF_BLOCKLISTED));
423         assertNotNull("not removed this state", mockedSMTPSession.getState().get(
424                 SPFHandler.SPF_DETAIL));
425         assertNotNull("not removed this state", mockedSMTPSession.getState()
426                 .get(SPFHandler.SPF_HEADER));
427         assertFalse("not rejected", mockedSMTPSession.getStopHandlerProcessing());
428     }
429 
430     public void testSPFpermErrorNotRejectAbuse() throws Exception {
431         setupMockedSMTPSession("192.168.100.1", "spf4.james.apache.org",
432                 new MailAddress("test@spf4.james.apache.org"), new MailAddress("abuse@localhost"));
433         SPFHandler spf = new SPFHandler();
434 
435         ContainerUtil.enableLogging(spf, new MockLogger());
436         
437         spf.initialize();
438 
439         spf.setDNSService(mockedDnsService);
440         spf.setBlockSoftFail(true);
441 
442         runHandlers(spf, mockedSMTPSession);
443 
444         assertFalse("not rejected",mockedSMTPSession.getStopHandlerProcessing());
445     }
446     
447     public void testSPFpermErrorRejectDisabled() throws Exception {
448         setupMockedSMTPSession("192.168.100.1", "spf4.james.apache.org",
449                 new MailAddress("test@spf4.james.apache.org"), new MailAddress(
450                         "test@localhost"));
451         SPFHandler spf = new SPFHandler();
452 
453         ContainerUtil.enableLogging(spf, new MockLogger());
454         
455         spf.setDNSService(mockedDnsService);
456         
457         spf.initialize();
458         
459         spf.setBlockPermError(false);
460 
461         runHandlers(spf, mockedSMTPSession);
462 
463         assertNull("not reject", mockedSMTPSession.getState().get(
464                 SPFHandler.SPF_BLOCKLISTED));
465         assertNull("details ", mockedSMTPSession.getState().get(
466                 SPFHandler.SPF_DETAIL));
467         assertNull("No tempError", mockedSMTPSession.getState().get(
468                 SPFHandler.SPF_TEMPBLOCKLISTED));
469         assertNotNull("Header should present", mockedSMTPSession.getState()
470                 .get(SPFHandler.SPF_HEADER));
471         assertFalse(mockedSMTPSession.getStopHandlerProcessing());
472     }
473     
474     public void testSPFfailAddJunkScore() throws Exception {
475         setupMockedSMTPSession("192.168.100.1", "spf2.james.apache.org",
476                 new MailAddress("test@spf2.james.apache.org"), new MailAddress(
477                         "test@localhost"));
478         mockedSMTPSession.getState().put(JunkScore.JUNK_SCORE, new JunkScoreImpl());
479         
480         SPFHandler spf = new SPFHandler();
481 
482         ContainerUtil.enableLogging(spf, new MockLogger());
483         spf.setAction("junkScore");
484         spf.setScore(20);
485         spf.setDNSService(mockedDnsService);     
486         
487         spf.initialize();
488 
489         runHandlers(spf, mockedSMTPSession);
490 
491         assertNotNull("reject", mockedSMTPSession.getState().get(
492                 SPFHandler.SPF_BLOCKLISTED));
493         assertNotNull("blocked", mockedSMTPSession.getState().get(
494                 SPFHandler.SPF_DETAIL));
495         assertNull("No tempError", mockedSMTPSession.getState().get(
496                 SPFHandler.SPF_TEMPBLOCKLISTED));
497         assertNotNull("Header should present", mockedSMTPSession.getState()
498                 .get(SPFHandler.SPF_HEADER));
499         assertFalse("Not stopped", mockedSMTPSession.getStopHandlerProcessing());
500         assertEquals("Score added",((JunkScore) mockedSMTPSession.getState().get(JunkScore.JUNK_SCORE)).getStoredScore("SPFCheck"), 20.0, 0d);
501     }
502 }