Line 0
Link Here
|
|
|
1 |
/* |
2 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
3 |
* contributor license agreements. See the NOTICE file distributed with |
4 |
* this work for additional information regarding copyright ownership. |
5 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
6 |
* (the "License"); you may not use this file except in compliance with |
7 |
* the License. You may obtain a copy of the License at |
8 |
* |
9 |
* http://www.apache.org/licenses/LICENSE-2.0 |
10 |
* |
11 |
* Unless required by applicable law or agreed to in writing, software |
12 |
* distributed under the License is distributed on an "AS IS" BASIS, |
13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 |
* See the License for the specific language governing permissions and |
15 |
* limitations under the License. |
16 |
*/ |
17 |
|
18 |
|
19 |
package org.apache.catalina.filters; |
20 |
|
21 |
|
22 |
import org.apache.catalina.comet.CometEvent; |
23 |
import org.apache.catalina.comet.CometFilter; |
24 |
import org.apache.catalina.comet.CometFilterChain; |
25 |
import org.apache.catalina.util.NetMask; |
26 |
|
27 |
import javax.servlet.FilterChain; |
28 |
import javax.servlet.ServletException; |
29 |
import javax.servlet.ServletRequest; |
30 |
import javax.servlet.ServletResponse; |
31 |
import javax.servlet.http.HttpServletResponse; |
32 |
import java.io.IOException; |
33 |
import java.net.InetAddress; |
34 |
import java.net.UnknownHostException; |
35 |
import java.util.ArrayList; |
36 |
import java.util.List; |
37 |
|
38 |
/** |
39 |
* Implementation of a Filter that performs filtering based on comparing the |
40 |
* appropriate request property (selected based on which subclass you choose |
41 |
* to configure into your Container's pipeline) against the regular expressions |
42 |
* configured for this Filter. |
43 |
* <p> |
44 |
* This filter is configured by setting the <code>allow</code> and/or |
45 |
* <code>deny</code> properties to a regular expressions (in the syntax |
46 |
* supported by {@link java.util.regex.Pattern}) to which the appropriate request property will |
47 |
* be compared. Evaluation proceeds as follows: |
48 |
* <ul> |
49 |
* <li>The subclass extracts the request property to be filtered, and |
50 |
* calls the common <code>process()</code> method. |
51 |
* <li>If there is a deny expression configured, the property will be compared |
52 |
* to the expression. If a match is found, this request will be rejected |
53 |
* with a "Forbidden" HTTP response.</li> |
54 |
* <li>If there is a allow expression configured, the property will be compared |
55 |
* to the expression. If a match is found, this request will be allowed to |
56 |
* pass through to the next filter in the current pipeline.</li> |
57 |
* <li>If a deny expression was specified but no allow expression, allow this |
58 |
* request to pass through (because none of the deny expressions matched |
59 |
* it). |
60 |
* <li>The request will be rejected with a "Forbidden" HTTP response.</li> |
61 |
* </ul> |
62 |
*/ |
63 |
|
64 |
public abstract class RequestNetmaskFilter |
65 |
extends FilterBase implements CometFilter { |
66 |
|
67 |
|
68 |
// ----------------------------------------------------- Instance Variables |
69 |
|
70 |
/** |
71 |
* The regular expression used to test for allowed requests. |
72 |
*/ |
73 |
protected final List<NetMask> allow = new ArrayList<NetMask>(); |
74 |
|
75 |
/** |
76 |
* The regular expression used to test for denied requests. |
77 |
*/ |
78 |
protected final List<NetMask> deny = new ArrayList<NetMask>(); |
79 |
|
80 |
/** |
81 |
* mime type -- "text/plain" |
82 |
*/ |
83 |
private static final String PLAIN_TEXT_MIME_TYPE = "text/plain"; |
84 |
|
85 |
|
86 |
// ------------------------------------------------------------- Properties |
87 |
|
88 |
|
89 |
/** |
90 |
* Return a string representation of the NetMask list in allow. |
91 |
*/ |
92 |
public String getAllow() { |
93 |
return allow.toString(); |
94 |
} |
95 |
|
96 |
|
97 |
/** |
98 |
* Fill the allow list with the list of netmasks provided as an argument, |
99 |
* if any. |
100 |
* |
101 |
* @param input The list of netmasks, as a comma separated string |
102 |
*/ |
103 |
public void setAllow(final String input) { |
104 |
if (input == null || input.length() == 0) |
105 |
return; |
106 |
|
107 |
NetMask nm; |
108 |
|
109 |
for (final String s: input.split("\\s*,\\s*")) |
110 |
try { |
111 |
nm = new NetMask(s); |
112 |
allow.add(nm); |
113 |
} catch (IllegalArgumentException e) { |
114 |
// FIXME: log |
115 |
} |
116 |
} |
117 |
|
118 |
|
119 |
/** |
120 |
* Return a string representation of the NetMask list in deny. |
121 |
*/ |
122 |
public String getDeny() { |
123 |
if (deny == null) { |
124 |
return null; |
125 |
} |
126 |
return deny.toString(); |
127 |
} |
128 |
|
129 |
|
130 |
/** |
131 |
* Fill the deny list with the list of netmasks provided as an argument, |
132 |
* if any. |
133 |
* |
134 |
* @param input The list of netmasks, as a comma separated string |
135 |
*/ |
136 |
public void setDeny(String input) { |
137 |
if (input == null || input.length() == 0) |
138 |
return; |
139 |
|
140 |
NetMask nm; |
141 |
|
142 |
for (final String s: input.split("\\s*,\\s*")) |
143 |
try { |
144 |
nm = new NetMask(s); |
145 |
deny.add(nm); |
146 |
} catch (IllegalArgumentException e) { |
147 |
// FIXME: log |
148 |
} |
149 |
} |
150 |
|
151 |
|
152 |
// --------------------------------------------------------- Public Methods |
153 |
|
154 |
|
155 |
/** |
156 |
* Extract the desired request property, and pass it (along with the |
157 |
* specified request and response objects) to the protected |
158 |
* <code>process()</code> method to perform the actual filtering. |
159 |
* This method must be implemented by a concrete subclass. |
160 |
* |
161 |
* @param request The servlet request to be processed |
162 |
* @param response The servlet response to be created |
163 |
* @param chain The filter chain |
164 |
* |
165 |
* @exception java.io.IOException if an input/output error occurs |
166 |
* @exception javax.servlet.ServletException if a servlet error occurs |
167 |
*/ |
168 |
@Override |
169 |
public abstract void doFilter(ServletRequest request, |
170 |
ServletResponse response, FilterChain chain) throws IOException, |
171 |
ServletException; |
172 |
|
173 |
|
174 |
// ------------------------------------------------------ Protected Methods |
175 |
|
176 |
|
177 |
/** |
178 |
* Perform the filtering that has been configured for this Filter, matching |
179 |
* against the specified request property. |
180 |
* |
181 |
* @param property The request property on which to filter |
182 |
* @param request The servlet request to be processed |
183 |
* @param response The servlet response to be processed |
184 |
* @param chain The filter chain |
185 |
* |
186 |
* @exception java.io.IOException if an input/output error occurs |
187 |
* @exception javax.servlet.ServletException if a servlet error occurs |
188 |
*/ |
189 |
protected void process(String property, ServletRequest request, |
190 |
ServletResponse response, FilterChain chain) |
191 |
throws IOException, ServletException { |
192 |
|
193 |
if (isAllowed(property)) { |
194 |
chain.doFilter(request, response); |
195 |
return; |
196 |
} |
197 |
|
198 |
if (!(response instanceof HttpServletResponse)) { |
199 |
sendErrorWhenNotHttp(response); |
200 |
return; |
201 |
} |
202 |
|
203 |
((HttpServletResponse) response) |
204 |
.sendError(HttpServletResponse.SC_FORBIDDEN); |
205 |
} |
206 |
|
207 |
/** |
208 |
* Perform the filtering that has been configured for this Filter, matching |
209 |
* against the specified request property. |
210 |
* |
211 |
* @param property The property to check against the allow/deny rules |
212 |
* @param event The comet event to be filtered |
213 |
* @param chain The comet filter chain |
214 |
* @exception java.io.IOException if an input/output error occurs |
215 |
* @exception javax.servlet.ServletException if a servlet error occurs |
216 |
*/ |
217 |
protected void processCometEvent(String property, CometEvent event, |
218 |
CometFilterChain chain) throws IOException, ServletException { |
219 |
HttpServletResponse response = event.getHttpServletResponse(); |
220 |
|
221 |
if (isAllowed(property)) { |
222 |
chain.doFilterEvent(event); |
223 |
return; |
224 |
} |
225 |
|
226 |
response.sendError(HttpServletResponse.SC_FORBIDDEN); |
227 |
event.close(); |
228 |
} |
229 |
|
230 |
/** |
231 |
* Process the allow and deny rules for the provided property. |
232 |
* |
233 |
* @param property The property to test against the allow and deny lists |
234 |
* @return <code>true</code> if this request should be allowed, |
235 |
* <code>false</code> otherwise |
236 |
*/ |
237 |
private boolean isAllowed(String property) { |
238 |
final InetAddress addr; |
239 |
|
240 |
try { |
241 |
addr = InetAddress.getByName(property); |
242 |
} catch (UnknownHostException e) { |
243 |
//Eh? |
244 |
return false; |
245 |
} |
246 |
|
247 |
for (final NetMask nm: deny) |
248 |
if (nm.matches(addr)) |
249 |
return false; |
250 |
|
251 |
for (final NetMask nm: allow) |
252 |
if (nm.matches(addr)) |
253 |
return true; |
254 |
|
255 |
// Allow if denies specified but not allows |
256 |
if (!deny.isEmpty() && allow.isEmpty()) |
257 |
return true; |
258 |
|
259 |
// Deny this request |
260 |
return false; |
261 |
} |
262 |
|
263 |
private void sendErrorWhenNotHttp(ServletResponse response) |
264 |
throws IOException { |
265 |
response.setContentType(PLAIN_TEXT_MIME_TYPE); |
266 |
response.getWriter().write(sm.getString("http.403")); |
267 |
response.getWriter().flush(); |
268 |
} |
269 |
} |