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  package org.apache.james.smtpserver.core.filter.fastfail;
23  
24  import org.apache.avalon.framework.service.ServiceException;
25  import org.apache.avalon.framework.service.ServiceManager;
26  import org.apache.avalon.framework.service.Serviceable;
27  import org.apache.avalon.framework.configuration.Configuration;
28  import org.apache.avalon.framework.configuration.Configurable;
29  import org.apache.avalon.framework.configuration.ConfigurationException;
30  import org.apache.james.api.dnsservice.DNSService;
31  import org.apache.james.dsn.DSNStatus;
32  import org.apache.james.smtpserver.CommandHandler;
33  import org.apache.james.smtpserver.ConnectHandler;
34  import org.apache.james.smtpserver.SMTPSession;
35  import org.apache.james.smtpserver.junkscore.JunkScore;
36  import org.apache.mailet.MailAddress;
37  
38  import java.util.ArrayList;
39  import java.util.Collection;
40  import java.util.StringTokenizer;
41  
42  /**
43    * Connect handler for DNSRBL processing
44    */
45  public class DNSRBLHandler
46      extends AbstractJunkHandler
47      implements ConnectHandler, CommandHandler, Configurable, Serviceable {
48      /**
49       * The lists of rbl servers to be checked to limit spam
50       */
51      private String[] whitelist;
52      private String[] blacklist;
53      
54      private DNSService dnsServer = null;
55      
56      private boolean getDetail = false;
57      
58      private String blocklistedDetail = null;
59      
60      public static final String RBL_BLOCKLISTED_MAIL_ATTRIBUTE_NAME = "org.apache.james.smtpserver.rbl.blocklisted";
61      
62      public static final String RBL_DETAIL_MAIL_ATTRIBUTE_NAME = "org.apache.james.smtpserver.rbl.detail";
63  
64      /**
65       * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
66       */
67      public void configure(Configuration handlerConfiguration) throws ConfigurationException {
68          boolean validConfig = false;
69  
70          Configuration rblserverConfiguration = handlerConfiguration.getChild("rblservers", false);
71          if ( rblserverConfiguration != null ) {
72              ArrayList rblserverCollection = new ArrayList();
73              Configuration[] children = rblserverConfiguration.getChildren("whitelist");
74              if ( children != null ) {
75                  for ( int i = 0 ; i < children.length ; i++ ) {
76                      String rblServerName = children[i].getValue();
77                      rblserverCollection.add(rblServerName);
78                      if (getLogger().isInfoEnabled()) {
79                          getLogger().info("Adding RBL server to whitelist: " + rblServerName);
80                      }
81                  }
82                  if (rblserverCollection != null && rblserverCollection.size() > 0) {
83                      setWhitelist((String[]) rblserverCollection.toArray(new String[rblserverCollection.size()]));
84                      rblserverCollection.clear();
85                      validConfig = true;
86                  }
87              }
88              children = rblserverConfiguration.getChildren("blacklist");
89              if ( children != null ) {
90                  for ( int i = 0 ; i < children.length ; i++ ) {
91                      String rblServerName = children[i].getValue();
92                      rblserverCollection.add(rblServerName);
93                      if (getLogger().isInfoEnabled()) {
94                          getLogger().info("Adding RBL server to blacklist: " + rblServerName);
95                      }
96                  }
97                  if (rblserverCollection != null && rblserverCollection.size() > 0) {
98                      setBlacklist((String[]) rblserverCollection.toArray(new String[rblserverCollection.size()]));
99                      rblserverCollection.clear();
100                     validConfig = true;
101                 }
102             }
103         }
104         
105         // Throw an ConfiigurationException on invalid config
106         if (validConfig == false){
107             throw new ConfigurationException("Please configure whitelist or blacklist");
108         }
109         
110         Configuration configuration = handlerConfiguration.getChild("getDetail",false);
111         if(configuration != null) {
112            getDetail = configuration.getValueAsBoolean();
113         }
114         
115         super.configure(handlerConfiguration);
116 
117     }
118 
119     /**
120      * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
121      */
122     public void service(ServiceManager serviceMan) throws ServiceException {
123         setDNSServer((DNSService) serviceMan.lookup(DNSService.ROLE));
124     }
125     
126     /**
127      * check if the remote Ip address is block listed
128      *
129      * @see org.apache.james.smtpserver.ConnectHandler#onConnect(SMTPSession)
130     **/
131     public void onConnect(SMTPSession session) {
132         checkDNSRBL(session, session.getRemoteIPAddress());
133     }
134     
135     /**
136      * Set the whitelist array
137      * 
138      * @param whitelist The array which contains the whitelist
139      */
140     public void setWhitelist(String[] whitelist) {
141         this.whitelist = whitelist;
142     }
143     
144     /**
145      * Set the blacklist array
146      * 
147      * @param blacklist The array which contains the blacklist
148      */
149     public void setBlacklist(String[] blacklist) {
150         this.blacklist = blacklist;
151     }
152     
153     /**
154      * Set the DNSService
155      * 
156      * @param dnsServer The DNSService
157      */
158     public void setDNSServer(DNSService dnsServer) {
159         this.dnsServer = dnsServer;
160     }
161 
162     /**
163      * Set for try to get a TXT record for the blocked record. 
164      * 
165      * @param getDetail Set to ture for enable
166      */
167     public void setGetDetail(boolean getDetail) {
168         this.getDetail = getDetail;
169     }
170 
171     /**
172      *
173      * This checks DNSRBL whitelists and blacklists.  If the remote IP is whitelisted
174      * it will be permitted to send e-mail, otherwise if the remote IP is blacklisted,
175      * the sender will only be permitted to send e-mail to postmaster (RFC 2821) or
176      * abuse (RFC 2142), unless authenticated.
177      */
178 
179     public void checkDNSRBL(SMTPSession session, String ipAddress) {
180         
181         /*
182          * don't check against rbllists if the client is allowed to relay..
183          * This whould make no sense.
184          */
185         if (session.isRelayingAllowed()) {
186             getLogger().info("Ipaddress " + session.getRemoteIPAddress() + " is allowed to relay. Don't check it");
187             return;
188         }
189         
190         if (whitelist != null || blacklist != null) {
191             StringBuffer sb = new StringBuffer();
192             StringTokenizer st = new StringTokenizer(ipAddress, " .", false);
193             while (st.hasMoreTokens()) {
194                 sb.insert(0, st.nextToken() + ".");
195             }
196             String reversedOctets = sb.toString();
197 
198             if (whitelist != null) {
199                 String[] rblList = whitelist;
200                 for (int i = 0 ; i < rblList.length ; i++) try {
201                     dnsServer.getByName(reversedOctets + rblList[i]);
202                     if (getLogger().isInfoEnabled()) {
203                         getLogger().info("Connection from " + ipAddress + " whitelisted by " + rblList[i]);
204                     }
205                     
206                     return;
207                 } catch (java.net.UnknownHostException uhe) {
208                     if (getLogger().isDebugEnabled()) {
209                         getLogger().debug("IpAddress " + session.getRemoteIPAddress() + " not listed on " + rblList[i]);
210                     }
211                 }
212             }
213 
214             if (blacklist != null) {
215                 String[] rblList = blacklist;
216                 for (int i = 0 ; i < rblList.length ; i++) try {
217                     dnsServer.getByName(reversedOctets + rblList[i]);
218                     if (getLogger().isInfoEnabled()) {
219                         getLogger().info("Connection from " + ipAddress + " restricted by " + rblList[i] + " to SMTP AUTH/postmaster/abuse.");
220                     }
221                     
222                     // we should try to retrieve details
223                     if (getDetail) {
224                         Collection txt = dnsServer.findTXTRecords(reversedOctets + rblList[i]);
225                         
226                         // Check if we found a txt record
227                         if (!txt.isEmpty()) {
228                             // Set the detail
229                             String blocklistedDetail = txt.iterator().next().toString();
230                             
231                             session.getConnectionState().put(RBL_DETAIL_MAIL_ATTRIBUTE_NAME, blocklistedDetail);
232                         }
233                     }
234                     
235                     session.getConnectionState().put(RBL_BLOCKLISTED_MAIL_ATTRIBUTE_NAME, "true");
236                     return;
237                 } catch (java.net.UnknownHostException uhe) {
238                     // if it is unknown, it isn't blocked
239                     if (getLogger().isDebugEnabled()) {
240                         getLogger().debug("unknown host exception thrown:" + rblList[i]);
241                     }
242                 }
243             }
244         }
245     }
246 
247     /**
248      * @see org.apache.james.smtpserver.CommandHandler#getImplCommands()
249      */
250     public Collection getImplCommands() {
251         Collection commands = new ArrayList();
252         commands.add("RCPT");
253         return commands;
254     }
255 
256     /**
257      * @see org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession)
258      */
259     public void onCommand(SMTPSession session) {
260         doProcessing(session);       
261     }
262 
263     /**
264      * @see org.apache.james.smtpserver.core.filter.fastfail.AbstractJunkHandler#check(org.apache.james.smtpserver.SMTPSession)
265      */
266     protected boolean check(SMTPSession session) {
267         String blocklisted = (String) session.getConnectionState().get(RBL_BLOCKLISTED_MAIL_ATTRIBUTE_NAME);
268         MailAddress recipientAddress = (MailAddress) session.getState().get(
269                 SMTPSession.CURRENT_RECIPIENT);
270 
271         return (blocklisted != null && // was found in the RBL
272                 !(session.isAuthRequired() && session.getUser() != null) && // Not (SMTP AUTH is enabled and not authenticated)
273                 !(recipientAddress.getUser().equalsIgnoreCase("postmaster") || recipientAddress.getUser().equalsIgnoreCase("abuse")));
274     }
275 
276     /**
277      * @see org.apache.james.smtpserver.core.filter.fastfail.AbstractJunkHandler#getJunkScore(org.apache.james.smtpserver.SMTPSession)
278      */
279     protected JunkScore getJunkScore(SMTPSession session) {
280         return (JunkScore) session.getConnectionState().get(JunkScore.JUNK_SCORE_SESSION);
281     }
282     
283     /**
284      * @see org.apache.james.smtpserver.core.filter.fastfail.AbstractJunkHandler#getJunkHandlerData(org.apache.james.smtpserver.SMTPSession)
285      */
286     public JunkHandlerData getJunkHandlerData(SMTPSession session) {
287         JunkHandlerData data = new JunkHandlerData();
288         
289         data.setJunkScoreLogString("Ipaddress " + session.getRemoteIPAddress() + " listed on RBL. Add junkScore: " + getScore());
290         data.setRejectLogString("ipaddress " + session.getRemoteIPAddress() + " listed on RBL. Reject email");
291     
292         if (blocklistedDetail != null) {
293             data.setRejectResponseString("530 "+ DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SECURITY_AUTH) + " " + blocklistedDetail);
294         } else {
295             data.setRejectResponseString("530 "+ DSNStatus.getStatus(DSNStatus.PERMANENT,
296                             DSNStatus.SECURITY_AUTH)  + " Rejected: unauthenticated e-mail from " + session.getRemoteIPAddress() 
297                             + " is restricted.  Contact the postmaster for details.");
298         }
299         data.setScoreName("DNSRBLCheck");
300         return data;
301     }
302 }