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