View Javadoc

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  
22  
23  package org.apache.james.smtpserver.core.filter.fastfail;
24  
25  import java.io.IOException;
26  import java.net.UnknownHostException;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.HashSet;
30  import java.util.Iterator;
31  
32  import javax.mail.MessagingException;
33  import javax.mail.internet.MimeBodyPart;
34  import javax.mail.internet.MimeMessage;
35  import javax.mail.internet.MimeMultipart;
36  import javax.mail.internet.MimePart;
37  
38  import org.apache.avalon.framework.configuration.Configuration;
39  import org.apache.avalon.framework.configuration.ConfigurationException;
40  
41  import org.apache.avalon.framework.service.ServiceException;
42  import org.apache.avalon.framework.service.ServiceManager;
43  import org.apache.avalon.framework.service.Serviceable;
44  import org.apache.james.api.dnsservice.DNSService;
45  import org.apache.james.dsn.DSNStatus;
46  import org.apache.james.smtpserver.MessageHandler;
47  import org.apache.james.smtpserver.SMTPSession;
48  import org.apache.james.smtpserver.urirbl.URIScanner;
49  
50  /**
51   * Extract domains from message and check against URIRBLServer. For more informations see http://www.surbl.org
52   */
53  public class URIRBLHandler extends AbstractJunkHandler implements MessageHandler,
54      Serviceable {
55  
56      private DNSService dnsServer;
57  
58      private Collection uriRbl;
59  
60      private boolean getDetail = false;
61  
62      private boolean checkAuthNetworks = false;
63      
64      private final static String LISTED_DOMAIN ="LISTED_DOMAIN";
65      
66      private final static String URBLSERVER = "URBL_SERVER";
67      
68      /**
69       * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
70       */
71      public void service(ServiceManager serviceMan) throws ServiceException {
72          setDnsServer((DNSService) serviceMan.lookup(DNSService.ROLE));
73      }
74  
75      /**
76       * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
77       */
78      public void configure(Configuration arg0) throws ConfigurationException {
79          boolean invalidConfig = false;
80      
81          Configuration serverConfiguration = arg0.getChild("uriRblServers", false);
82          if ( serverConfiguration != null ) {
83              ArrayList serverCollection = new ArrayList();
84              Configuration[] children = serverConfiguration.getChildren("server");
85              if ( children != null ) {
86                  for ( int i = 0 ; i < children.length ; i++ ) {
87                      String rblServerName = children[i].getValue();
88                      serverCollection.add(rblServerName);
89                      if (getLogger().isInfoEnabled()) {
90                          getLogger().info("Adding uriRBL server: " + rblServerName);
91                      }
92                  }
93                  if (serverCollection != null && serverCollection.size() > 0) {
94                      setUriRblServer(serverCollection);
95                  } else {
96                      invalidConfig = true;
97                  }
98              }
99          } else {
100             invalidConfig = true;
101         }
102         
103         if (invalidConfig == true) {
104             throw new ConfigurationException("Please provide at least one server");
105         }
106     
107         Configuration configuration = arg0.getChild("getDetail",false);
108         if(configuration != null) {
109            getDetail = configuration.getValueAsBoolean();
110         }
111         
112         Configuration configRelay = arg0.getChild("checkAuthNetworks", false);
113         if (configRelay != null) {
114             setCheckAuthNetworks(configRelay.getValueAsBoolean(false));
115         }
116         
117         super.configure(arg0);
118 
119     }
120    
121     /**
122      * Set the UriRBL Servers
123      * 
124      * @param uriRbl The Collection holding the servers
125      */
126     public void setUriRblServer(Collection uriRbl) {
127         this.uriRbl = uriRbl;
128     }
129     
130     /**
131      * Set to true if AuthNetworks should be included in the EHLO check
132      * 
133      * @param checkAuthNetworks
134      *            Set to true to enable
135      */
136     public void setCheckAuthNetworks(boolean checkAuthNetworks) {
137         this.checkAuthNetworks = checkAuthNetworks;
138     }
139 
140     /**
141      * Set the DNSService
142      * 
143      * @param dnsServer
144      *            The DNSService
145      */
146     public void setDnsServer(DNSService dnsServer) {
147         this.dnsServer = dnsServer;
148     }
149 
150     /**
151      * Set for try to get a TXT record for the blocked record. 
152      * 
153      * @param getDetail Set to ture for enable
154      */
155     public void setGetDetail(boolean getDetail) {
156         this.getDetail = getDetail;
157     }
158     
159     /**
160      * @see org.apache.james.smtpserver.MessageHandler#onMessage(SMTPSession)
161      */
162     public void onMessage(SMTPSession session) {
163         doProcessing(session);
164     }
165 
166     /**
167      * Recursively scans all MimeParts of an email for domain strings. Domain
168      * strings that are found are added to the supplied HashSet.
169      *
170      * @param part MimePart to scan
171      * @return domains The HashSet that contains the domains which were extracted
172      */
173     private HashSet scanMailForDomains(MimePart part) throws MessagingException, IOException {
174         HashSet domains = new HashSet();
175         getLogger().debug("mime type is: \"" + part.getContentType() + "\"");
176        
177         if (part.isMimeType("text/plain") || part.isMimeType("text/html")) {
178             getLogger().debug("scanning: \"" + part.getContent().toString() + "\"");
179             HashSet newDom = URIScanner.scanContentForDomains(domains, part.getContent().toString());
180            
181             // Check if new domains are found and add the domains 
182             if (newDom != null && newDom.size() > 0) {
183                 domains.addAll(newDom);
184             }
185         } else if (part.isMimeType("multipart/*")) {
186             MimeMultipart multipart = (MimeMultipart) part.getContent();
187             int count = multipart.getCount();
188             getLogger().debug("multipart count is: " + count);
189           
190             for (int index = 0; index < count; index++) {
191                 getLogger().debug("recursing index: " + index);
192                 MimeBodyPart mimeBodyPart = (MimeBodyPart) multipart.getBodyPart(index);
193                 HashSet newDomains = scanMailForDomains(mimeBodyPart);
194                 
195                 // Check if new domains are found and add the domains 
196                 if(newDomains != null && newDomains.size() > 0) {
197                     domains.addAll(newDomains);
198                 }
199             }
200         }
201         return domains;
202     }
203 
204     /**
205      * @see org.apache.james.smtpserver.core.filter.fastfail.AbstractJunkHandler#check(org.apache.james.smtpserver.SMTPSession)
206      */
207     protected boolean check(SMTPSession session) {
208         MimeMessage message;
209         
210         // Not scan the message if relaying allowed
211         if (session.isRelayingAllowed() && !checkAuthNetworks) {
212             return false;
213         }
214         
215         try {
216             message = session.getMail().getMessage();
217 
218             HashSet domains = scanMailForDomains(message);
219 
220             Iterator fDomains = domains.iterator();
221 
222             while (fDomains.hasNext()) {
223                 Iterator uRbl = uriRbl.iterator();
224                 String target = fDomains.next().toString();
225                 
226                 while (uRbl.hasNext()) {
227                     try {
228                         String uRblServer = uRbl.next().toString();
229                         String address = target + "." + uRblServer;
230                         
231                         if (getLogger().isDebugEnabled()) {
232                             getLogger().debug("Lookup " + address);
233                         }
234                         
235                         dnsServer.getByName(address);
236             
237                         // store server name for later use
238                         session.getState().put(URBLSERVER, uRblServer);
239                         session.getState().put(LISTED_DOMAIN,target);
240 
241                         session.abortMessage();
242                         return true;
243 
244                     } catch (UnknownHostException uhe) {
245                         // domain not found. keep processing
246                     }
247                 }
248             }
249         } catch (MessagingException e) {
250             getLogger().error(e.getMessage());
251         } catch (IOException e) {
252             getLogger().error(e.getMessage());
253         }
254         return false;
255     }
256 
257     /**
258      * @see org.apache.james.smtpserver.core.filter.fastfail.AbstractJunkHandler#getJunkHandlerData(org.apache.james.smtpserver.SMTPSession)
259      */
260     public JunkHandlerData getJunkHandlerData(SMTPSession session) {
261         JunkHandlerData data = new JunkHandlerData();
262     
263         String uRblServer = (String) session.getState().get(URBLSERVER);
264         String target = (String) session.getState().get(LISTED_DOMAIN);
265         String detail = null;
266 
267         // we should try to retrieve details
268         if (getDetail) {
269             Collection txt = dnsServer.findTXTRecords(target+ "." + uRblServer);
270 
271             // Check if we found a txt record
272             if (!txt.isEmpty()) {
273                 // Set the detail
274                 detail = txt.iterator().next().toString();
275 
276             }
277         }
278 
279         if (detail != null) {
280            
281             data.setRejectResponseString("554 " + DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.SECURITY_OTHER)
282                 + "Rejected: message contains domain " + target + " listed by " + uRblServer +" . Details: " 
283                 + detail);
284         } else {
285             data.setRejectResponseString("554 " + DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.SECURITY_OTHER)
286                 + " Rejected: message contains domain " + target + " listed by " + uRblServer);
287         }  
288 
289         data.setJunkScoreLogString("Message sent by " + session.getRemoteIPAddress() + " restricted by " +  uRblServer + " because " + target + " is listed. Add junkScore: " + getScore());
290         data.setRejectLogString("Rejected: message contains domain " + target + " listed by " + uRblServer);
291         data.setScoreName("UriRBLCheck");
292         return data;
293     }
294 
295 }