More changes for Elastic Beanstalk -- logging, no need for DoS

git-svn-id: https://zxing.googlecode.com/svn/trunk@2537 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2012-11-27 23:57:07 +00:00
parent ceca8b4ff7
commit 6698e919e9
3 changed files with 15 additions and 254 deletions

View file

@ -56,8 +56,6 @@ import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -84,7 +82,6 @@ public final class DecodeServlet extends HttpServlet {
// No real reason to deal with more than maybe 2 megapixels
private static final int MAX_PIXELS = 1 << 21;
private static final byte[] REMAINDER_BUFFER = new byte[8192];
private static final long GC_HACK_INTERVAL_MS = 60 * 1000;
private static final Map<DecodeHintType,Object> HINTS;
private static final Map<DecodeHintType,Object> HINTS_PURE;
@ -97,19 +94,11 @@ public final class DecodeServlet extends HttpServlet {
}
private DiskFileItemFactory diskFileItemFactory;
private Timer gcHackTimer;
@Override
public void init(ServletConfig servletConfig) {
Logger logger = Logger.getLogger("com.google.zxing");
logger.addHandler(new ServletContextLogHandler(servletConfig.getServletContext()));
gcHackTimer = new Timer();
gcHackTimer.schedule(new TimerTask() {
@Override
public void run() {
System.gc(); // Hack: GC may close these weird stuck CLOSE_WAIT sockets?
}
}, GC_HACK_INTERVAL_MS, GC_HACK_INTERVAL_MS);
diskFileItemFactory = new DiskFileItemFactory();
log.info("DecodeServlet configured");
}
@ -120,7 +109,7 @@ public final class DecodeServlet extends HttpServlet {
String imageURIString = request.getParameter("u");
if (imageURIString == null || imageURIString.isEmpty()) {
log.fine("URI was empty");
log.info("URI was empty");
response.sendRedirect("badurl.jspx");
return;
}
@ -135,15 +124,11 @@ public final class DecodeServlet extends HttpServlet {
try {
imageURL = new URI(imageURIString).toURL();
} catch (URISyntaxException urise) {
if (log.isLoggable(Level.FINE)) {
log.fine("URI was not valid: " + imageURIString);
}
log.info("URI was not valid: " + imageURIString);
response.sendRedirect("badurl.jspx");
return;
} catch (MalformedURLException mue) {
if (log.isLoggable(Level.FINE)) {
log.fine("URI was not valid: " + imageURIString);
}
log.info("URI was not valid: " + imageURIString);
response.sendRedirect("badurl.jspx");
return;
}
@ -152,9 +137,7 @@ public final class DecodeServlet extends HttpServlet {
try {
connection = (HttpURLConnection) imageURL.openConnection();
} catch (IllegalArgumentException iae) {
if (log.isLoggable(Level.FINE)) {
log.fine("URI could not be opened: " + imageURL);
}
log.info("URI could not be opened: " + imageURL);
response.sendRedirect("badurl.jspx");
return;
}
@ -175,9 +158,7 @@ public final class DecodeServlet extends HttpServlet {
// javax.net.ssl.SSLPeerUnverifiedException,
// org.apache.http.NoHttpResponseException,
// org.apache.http.client.ClientProtocolException,
if (log.isLoggable(Level.FINE)) {
log.fine(ioe.toString());
}
log.info(ioe.toString());
response.sendRedirect("badurl.jspx");
return;
}
@ -188,14 +169,12 @@ public final class DecodeServlet extends HttpServlet {
is = connection.getInputStream();
if (connection.getResponseCode() != HttpServletResponse.SC_OK) {
if (log.isLoggable(Level.FINE)) {
log.fine("Unsuccessful return code: " + connection.getResponseCode());
}
log.info("Unsuccessful return code: " + connection.getResponseCode());
response.sendRedirect("badurl.jspx");
return;
}
if (connection.getHeaderFieldInt("Content-Length", 0) > MAX_IMAGE_SIZE) {
log.fine("Too large");
log.info("Too large");
response.sendRedirect("badimage.jspx");
return;
}
@ -204,9 +183,7 @@ public final class DecodeServlet extends HttpServlet {
processStream(is, request, response);
} catch (IOException ioe) {
if (log.isLoggable(Level.FINE)) {
log.fine(ioe.toString());
}
log.info(ioe.toString());
response.sendRedirect("badurl.jspx");
} finally {
if (is != null) {
@ -239,7 +216,7 @@ public final class DecodeServlet extends HttpServlet {
throws ServletException, IOException {
if (!ServletFileUpload.isMultipartContent(request)) {
log.fine("File upload was not multipart");
log.info("File upload was not multipart");
response.sendRedirect("badimage.jspx");
return;
}
@ -260,16 +237,14 @@ public final class DecodeServlet extends HttpServlet {
is.close();
}
} else {
log.fine("Too large");
log.info("Too large");
response.sendRedirect("badimage.jspx");
}
break;
}
}
} catch (FileUploadException fue) {
if (log.isLoggable(Level.FINE)) {
log.fine(fue.toString());
}
log.info(fue.toString());
response.sendRedirect("badimage.jspx");
}
@ -283,23 +258,17 @@ public final class DecodeServlet extends HttpServlet {
try {
image = ImageIO.read(is);
} catch (IOException ioe) {
if (log.isLoggable(Level.FINE)) {
log.fine(ioe.toString());
}
log.info(ioe.toString());
// Includes javax.imageio.IIOException
response.sendRedirect("badimage.jspx");
return;
} catch (CMMException cmme) {
if (log.isLoggable(Level.FINE)) {
log.fine(cmme.toString());
}
log.info(cmme.toString());
// Have seen this in logs
response.sendRedirect("badimage.jspx");
return;
} catch (IllegalArgumentException iae) {
if (log.isLoggable(Level.FINE)) {
log.fine(iae.toString());
}
log.info(iae.toString());
// Have seen this in logs for some JPEGs
response.sendRedirect("badimage.jspx");
return;
@ -310,9 +279,7 @@ public final class DecodeServlet extends HttpServlet {
}
if (image.getHeight() <= 1 || image.getWidth() <= 1 ||
image.getHeight() * image.getWidth() > MAX_PIXELS) {
if (log.isLoggable(Level.FINE)) {
log.fine("Dimensions too large: " + image.getWidth() + 'x' + image.getHeight());
}
log.info("Dimensions too large: " + image.getWidth() + 'x' + image.getHeight());
response.sendRedirect("badimage.jspx");
return;
}
@ -413,7 +380,6 @@ public final class DecodeServlet extends HttpServlet {
@Override
public void destroy() {
log.config("DecodeServlet shutting down...");
gcHackTimer.cancel();
}
}

View file

@ -1,129 +0,0 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.web;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
/**
* A {@link Filter} that rejects requests from hosts that are sending too many
* requests in too short a time.
*
* @author Sean Owen
*/
public final class DoSFilter implements Filter {
private static final int MAX_ACCESSES_PER_IP_PER_TIME = 100;
private static final long MAX_ACCESS_INTERVAL_MSEC = 60L * 1000L;
private static final long UNBAN_INTERVAL_MSEC = 15L * 60L * 1000L;
private static final String BAD_IPS_INIT_PARAM = "bad.ips";
private final IPTrie numRecentAccesses;
private final Timer timer;
private final Set<String> badIPAddresses;
private final Set<String> bannedIPAddresses;
private ServletContext context;
public DoSFilter() {
numRecentAccesses = new IPTrie();
timer = new Timer("DosFilter reset timer");
bannedIPAddresses = Collections.synchronizedSet(new HashSet<String>());
badIPAddresses = new HashSet<String>();
}
@Override
public void init(FilterConfig filterConfig) {
context = filterConfig.getServletContext();
timer.scheduleAtFixedRate(new ResetTask(), 0L, MAX_ACCESS_INTERVAL_MSEC);
timer.scheduleAtFixedRate(new UnbanTask(), 0L, UNBAN_INTERVAL_MSEC);
badIPAddresses.clear();
String badIPsString = filterConfig.getInitParameter(BAD_IPS_INIT_PARAM);
if (badIPsString != null) {
badIPAddresses.addAll(Arrays.asList(badIPsString.split(",")));
}
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (isBanned(request)) {
HttpServletResponse servletResponse = (HttpServletResponse) response;
servletResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
} else {
chain.doFilter(request, response);
}
}
private boolean isBanned(ServletRequest request) {
String remoteIPAddressString = request.getRemoteAddr();
if (bannedIPAddresses.contains(remoteIPAddressString) || badIPAddresses.contains(remoteIPAddressString)) {
return true;
}
InetAddress remoteIPAddress;
try {
remoteIPAddress = InetAddress.getByName(remoteIPAddressString);
} catch (UnknownHostException uhe) {
context.log("Can't determine host from: " + remoteIPAddressString + "; assuming banned");
return true;
}
if (numRecentAccesses.incrementAndGet(remoteIPAddress) > MAX_ACCESSES_PER_IP_PER_TIME) {
context.log("Possible DoS attack from " + remoteIPAddressString);
bannedIPAddresses.add(remoteIPAddressString);
return true;
}
return false;
}
@Override
public void destroy() {
timer.cancel();
numRecentAccesses.clear();
bannedIPAddresses.clear();
badIPAddresses.clear();
}
private final class ResetTask extends TimerTask {
@Override
public void run() {
numRecentAccesses.clear();
}
}
private final class UnbanTask extends TimerTask {
@Override
public void run() {
bannedIPAddresses.clear();
}
}
}

View file

@ -1,76 +0,0 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.web;
import java.net.InetAddress;
import java.util.Arrays;
/**
* A trie data structure for storing a set of IP addresses efficiently.
*
* @author Sean Owen
*/
final class IPTrie {
private final IPTrieNode root;
IPTrie() {
root = new IPTrieNode(false);
}
int incrementAndGet(InetAddress ipAddress) {
byte[] octets = ipAddress.getAddress();
synchronized (root) {
IPTrieNode current = root;
int max = octets.length - 1;
for (int offset = 0; offset < max; offset++) {
int index = 0xFF & octets[offset];
IPTrieNode child = current.children[index];
if (child == null) {
child = new IPTrieNode(offset == max - 1);
current.children[index] = child;
}
current = child;
}
int index = 0xFF & octets[max];
current.values[index]++;
return current.values[index];
}
}
void clear() {
synchronized (root) {
Arrays.fill(root.children, null);
}
}
private static final class IPTrieNode {
final IPTrieNode[] children;
final int[] values;
private IPTrieNode(boolean terminal) {
if (terminal) {
children = null;
values = new int[256];
} else {
children = new IPTrieNode[256];
values = null;
}
}
}
}