1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.servlets;
20
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Queue;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentLinkedQueue;
29 import java.util.concurrent.CopyOnWriteArrayList;
30 import java.util.concurrent.Semaphore;
31 import java.util.concurrent.TimeUnit;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import javax.servlet.AsyncContext;
36 import javax.servlet.AsyncEvent;
37 import javax.servlet.AsyncListener;
38 import javax.servlet.DispatcherType;
39 import javax.servlet.Filter;
40 import javax.servlet.FilterChain;
41 import javax.servlet.FilterConfig;
42 import javax.servlet.ServletContext;
43 import javax.servlet.ServletException;
44 import javax.servlet.ServletRequest;
45 import javax.servlet.ServletResponse;
46 import javax.servlet.http.HttpServletRequest;
47 import javax.servlet.http.HttpServletResponse;
48 import javax.servlet.http.HttpSession;
49 import javax.servlet.http.HttpSessionActivationListener;
50 import javax.servlet.http.HttpSessionBindingEvent;
51 import javax.servlet.http.HttpSessionBindingListener;
52 import javax.servlet.http.HttpSessionEvent;
53
54 import org.eclipse.jetty.server.handler.ContextHandler;
55 import org.eclipse.jetty.util.annotation.ManagedAttribute;
56 import org.eclipse.jetty.util.annotation.ManagedObject;
57 import org.eclipse.jetty.util.annotation.ManagedOperation;
58 import org.eclipse.jetty.util.annotation.Name;
59 import org.eclipse.jetty.util.log.Log;
60 import org.eclipse.jetty.util.log.Logger;
61 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
62 import org.eclipse.jetty.util.thread.Scheduler;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 @ManagedObject("limits exposure to abuse from request flooding, whether malicious, or as a result of a misconfigured client")
137 public class DoSFilter implements Filter
138 {
139 private static final Logger LOG = Log.getLogger(DoSFilter.class);
140
141 private static final String IPv4_GROUP = "(\\d{1,3})";
142 private static final Pattern IPv4_PATTERN = Pattern.compile(IPv4_GROUP+"\\."+IPv4_GROUP+"\\."+IPv4_GROUP+"\\."+IPv4_GROUP);
143 private static final String IPv6_GROUP = "(\\p{XDigit}{1,4})";
144 private static final Pattern IPv6_PATTERN = Pattern.compile(IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP);
145 private static final Pattern CIDR_PATTERN = Pattern.compile("([^/]+)/(\\d+)");
146
147 private static final String __TRACKER = "DoSFilter.Tracker";
148 private static final String __THROTTLED = "DoSFilter.Throttled";
149
150 private static final int __DEFAULT_MAX_REQUESTS_PER_SEC = 25;
151 private static final int __DEFAULT_DELAY_MS = 100;
152 private static final int __DEFAULT_THROTTLE = 5;
153 private static final int __DEFAULT_MAX_WAIT_MS = 50;
154 private static final long __DEFAULT_THROTTLE_MS = 30000L;
155 private static final long __DEFAULT_MAX_REQUEST_MS_INIT_PARAM = 30000L;
156 private static final long __DEFAULT_MAX_IDLE_TRACKER_MS_INIT_PARAM = 30000L;
157
158 static final String MANAGED_ATTR_INIT_PARAM = "managedAttr";
159 static final String MAX_REQUESTS_PER_S_INIT_PARAM = "maxRequestsPerSec";
160 static final String DELAY_MS_INIT_PARAM = "delayMs";
161 static final String THROTTLED_REQUESTS_INIT_PARAM = "throttledRequests";
162 static final String MAX_WAIT_INIT_PARAM = "maxWaitMs";
163 static final String THROTTLE_MS_INIT_PARAM = "throttleMs";
164 static final String MAX_REQUEST_MS_INIT_PARAM = "maxRequestMs";
165 static final String MAX_IDLE_TRACKER_MS_INIT_PARAM = "maxIdleTrackerMs";
166 static final String INSERT_HEADERS_INIT_PARAM = "insertHeaders";
167 static final String TRACK_SESSIONS_INIT_PARAM = "trackSessions";
168 static final String REMOTE_PORT_INIT_PARAM = "remotePort";
169 static final String IP_WHITELIST_INIT_PARAM = "ipWhitelist";
170 static final String ENABLED_INIT_PARAM = "enabled";
171
172 private static final int USER_AUTH = 2;
173 private static final int USER_SESSION = 2;
174 private static final int USER_IP = 1;
175 private static final int USER_UNKNOWN = 0;
176
177 private final String _suspended = "DoSFilter@" + Integer.toHexString(hashCode()) + ".SUSPENDED";
178 private final String _resumed = "DoSFilter@" + Integer.toHexString(hashCode()) + ".RESUMED";
179 private final ConcurrentHashMap<String, RateTracker> _rateTrackers = new ConcurrentHashMap<>();
180 private final List<String> _whitelist = new CopyOnWriteArrayList<>();
181 private volatile long _delayMs;
182 private volatile long _throttleMs;
183 private volatile long _maxWaitMs;
184 private volatile long _maxRequestMs;
185 private volatile long _maxIdleTrackerMs;
186 private volatile boolean _insertHeaders;
187 private volatile boolean _trackSessions;
188 private volatile boolean _remotePort;
189 private volatile boolean _enabled;
190 private Semaphore _passes;
191 private volatile int _throttledRequests;
192 private volatile int _maxRequestsPerSec;
193 private Queue<AsyncContext>[] _queues;
194 private AsyncListener[] _listeners;
195 private Scheduler _scheduler;
196
197 public void init(FilterConfig filterConfig) throws ServletException
198 {
199 _queues = new Queue[getMaxPriority() + 1];
200 _listeners = new AsyncListener[_queues.length];
201 for (int p = 0; p < _queues.length; p++)
202 {
203 _queues[p] = new ConcurrentLinkedQueue<>();
204 _listeners[p] = new DoSAsyncListener(p);
205 }
206
207 _rateTrackers.clear();
208
209 int maxRequests = __DEFAULT_MAX_REQUESTS_PER_SEC;
210 String parameter = filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM);
211 if (parameter != null)
212 maxRequests = Integer.parseInt(parameter);
213 setMaxRequestsPerSec(maxRequests);
214
215 long delay = __DEFAULT_DELAY_MS;
216 parameter = filterConfig.getInitParameter(DELAY_MS_INIT_PARAM);
217 if (parameter != null)
218 delay = Long.parseLong(parameter);
219 setDelayMs(delay);
220
221 int throttledRequests = __DEFAULT_THROTTLE;
222 parameter = filterConfig.getInitParameter(THROTTLED_REQUESTS_INIT_PARAM);
223 if (parameter != null)
224 throttledRequests = Integer.parseInt(parameter);
225 setThrottledRequests(throttledRequests);
226
227 long maxWait = __DEFAULT_MAX_WAIT_MS;
228 parameter = filterConfig.getInitParameter(MAX_WAIT_INIT_PARAM);
229 if (parameter != null)
230 maxWait = Long.parseLong(parameter);
231 setMaxWaitMs(maxWait);
232
233 long throttle = __DEFAULT_THROTTLE_MS;
234 parameter = filterConfig.getInitParameter(THROTTLE_MS_INIT_PARAM);
235 if (parameter != null)
236 throttle = Long.parseLong(parameter);
237 setThrottleMs(throttle);
238
239 long maxRequestMs = __DEFAULT_MAX_REQUEST_MS_INIT_PARAM;
240 parameter = filterConfig.getInitParameter(MAX_REQUEST_MS_INIT_PARAM);
241 if (parameter != null)
242 maxRequestMs = Long.parseLong(parameter);
243 setMaxRequestMs(maxRequestMs);
244
245 long maxIdleTrackerMs = __DEFAULT_MAX_IDLE_TRACKER_MS_INIT_PARAM;
246 parameter = filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM);
247 if (parameter != null)
248 maxIdleTrackerMs = Long.parseLong(parameter);
249 setMaxIdleTrackerMs(maxIdleTrackerMs);
250
251 String whiteList = "";
252 parameter = filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM);
253 if (parameter != null)
254 whiteList = parameter;
255 setWhitelist(whiteList);
256
257 parameter = filterConfig.getInitParameter(INSERT_HEADERS_INIT_PARAM);
258 setInsertHeaders(parameter == null || Boolean.parseBoolean(parameter));
259
260 parameter = filterConfig.getInitParameter(TRACK_SESSIONS_INIT_PARAM);
261 setTrackSessions(parameter == null || Boolean.parseBoolean(parameter));
262
263 parameter = filterConfig.getInitParameter(REMOTE_PORT_INIT_PARAM);
264 setRemotePort(parameter != null && Boolean.parseBoolean(parameter));
265
266 parameter = filterConfig.getInitParameter(ENABLED_INIT_PARAM);
267 setEnabled(parameter == null || Boolean.parseBoolean(parameter));
268
269 _scheduler = startScheduler();
270
271 ServletContext context = filterConfig.getServletContext();
272 if (context != null && Boolean.parseBoolean(filterConfig.getInitParameter(MANAGED_ATTR_INIT_PARAM)))
273 context.setAttribute(filterConfig.getFilterName(), this);
274 }
275
276 protected Scheduler startScheduler() throws ServletException
277 {
278 try
279 {
280 Scheduler result = new ScheduledExecutorScheduler();
281 result.start();
282 return result;
283 }
284 catch (Exception x)
285 {
286 throw new ServletException(x);
287 }
288 }
289
290 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
291 {
292 doFilter((HttpServletRequest)request, (HttpServletResponse)response, filterChain);
293 }
294
295 protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException
296 {
297 if (!isEnabled())
298 {
299 filterChain.doFilter(request, response);
300 return;
301 }
302
303
304 RateTracker tracker = (RateTracker)request.getAttribute(__TRACKER);
305 if (tracker == null)
306 {
307
308 if (LOG.isDebugEnabled())
309 LOG.debug("Filtering {}", request);
310
311
312 tracker = getRateTracker(request);
313
314
315 final boolean overRateLimit = tracker.isRateExceeded(System.currentTimeMillis());
316
317
318 if (!overRateLimit)
319 {
320 if (LOG.isDebugEnabled())
321 LOG.debug("Allowing {}", request);
322 doFilterChain(filterChain, request, response);
323 return;
324 }
325
326
327
328
329 long delayMs = getDelayMs();
330 boolean insertHeaders = isInsertHeaders();
331 switch ((int)delayMs)
332 {
333 case -1:
334 {
335
336 LOG.warn("DOS ALERT: Request rejected ip={}, session={}, user={}", request.getRemoteAddr(), request.getRequestedSessionId(), request.getUserPrincipal());
337 if (insertHeaders)
338 response.addHeader("DoSFilter", "unavailable");
339 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
340 return;
341 }
342 case 0:
343 {
344
345 LOG.warn("DOS ALERT: Request throttled ip={}, session={}, user={}", request.getRemoteAddr(), request.getRequestedSessionId(), request.getUserPrincipal());
346 request.setAttribute(__TRACKER, tracker);
347 break;
348 }
349 default:
350 {
351
352
353 LOG.warn("DOS ALERT: Request delayed={}ms, ip={}, session={}, user={}", delayMs, request.getRemoteAddr(), request.getRequestedSessionId(), request.getUserPrincipal());
354 if (insertHeaders)
355 response.addHeader("DoSFilter", "delayed");
356 request.setAttribute(__TRACKER, tracker);
357 AsyncContext asyncContext = request.startAsync();
358 if (delayMs > 0)
359 asyncContext.setTimeout(delayMs);
360 asyncContext.addListener(new DoSTimeoutAsyncListener());
361 return;
362 }
363 }
364 }
365
366 if (LOG.isDebugEnabled())
367 LOG.debug("Throttling {}", request);
368
369
370 boolean accepted = false;
371 try
372 {
373
374 accepted = _passes.tryAcquire(getMaxWaitMs(), TimeUnit.MILLISECONDS);
375 if (!accepted)
376 {
377
378
379 Boolean throttled = (Boolean)request.getAttribute(__THROTTLED);
380 long throttleMs = getThrottleMs();
381 if (throttled != Boolean.TRUE && throttleMs > 0)
382 {
383 int priority = getPriority(request, tracker);
384 request.setAttribute(__THROTTLED, Boolean.TRUE);
385 if (isInsertHeaders())
386 response.addHeader("DoSFilter", "throttled");
387 AsyncContext asyncContext = request.startAsync();
388 request.setAttribute(_suspended, Boolean.TRUE);
389 if (throttleMs > 0)
390 asyncContext.setTimeout(throttleMs);
391 asyncContext.addListener(_listeners[priority]);
392 _queues[priority].add(asyncContext);
393 if (LOG.isDebugEnabled())
394 LOG.debug("Throttled {}, {}ms", request, throttleMs);
395 return;
396 }
397
398 Boolean resumed = (Boolean)request.getAttribute(_resumed);
399 if (resumed == Boolean.TRUE)
400 {
401
402 _passes.acquire();
403 accepted = true;
404 }
405 }
406
407
408 if (accepted)
409 {
410
411 if (LOG.isDebugEnabled())
412 LOG.debug("Allowing {}", request);
413 doFilterChain(filterChain, request, response);
414 }
415 else
416 {
417
418 if (LOG.isDebugEnabled())
419 LOG.debug("Rejecting {}", request);
420 if (isInsertHeaders())
421 response.addHeader("DoSFilter", "unavailable");
422 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
423 }
424 }
425 catch (InterruptedException e)
426 {
427 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
428 }
429 finally
430 {
431 if (accepted)
432 {
433
434 for (int p = _queues.length - 1; p >= 0; --p)
435 {
436 AsyncContext asyncContext = _queues[p].poll();
437 if (asyncContext != null)
438 {
439 ServletRequest candidate = asyncContext.getRequest();
440 Boolean suspended = (Boolean)candidate.getAttribute(_suspended);
441 if (suspended == Boolean.TRUE)
442 {
443 if (LOG.isDebugEnabled())
444 LOG.debug("Resuming {}", request);
445 candidate.setAttribute(_resumed, Boolean.TRUE);
446 asyncContext.dispatch();
447 break;
448 }
449 }
450 }
451 _passes.release();
452 }
453 }
454 }
455
456 protected void doFilterChain(FilterChain chain, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException
457 {
458 final Thread thread = Thread.currentThread();
459 Runnable requestTimeout = new Runnable()
460 {
461 @Override
462 public void run()
463 {
464 closeConnection(request, response, thread);
465 }
466 };
467 Scheduler.Task task = _scheduler.schedule(requestTimeout, getMaxRequestMs(), TimeUnit.MILLISECONDS);
468 try
469 {
470 chain.doFilter(request, response);
471 }
472 finally
473 {
474 task.cancel();
475 }
476 }
477
478
479
480
481
482
483
484
485
486 protected void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
487 {
488
489 if (!response.isCommitted())
490 {
491 response.setHeader("Connection", "close");
492 }
493 try
494 {
495 try
496 {
497 response.getWriter().close();
498 }
499 catch (IllegalStateException e)
500 {
501 response.getOutputStream().close();
502 }
503 }
504 catch (IOException e)
505 {
506 LOG.warn(e);
507 }
508
509
510 thread.interrupt();
511 }
512
513
514
515
516
517
518
519
520 protected int getPriority(HttpServletRequest request, RateTracker tracker)
521 {
522 if (extractUserId(request) != null)
523 return USER_AUTH;
524 if (tracker != null)
525 return tracker.getType();
526 return USER_UNKNOWN;
527 }
528
529
530
531
532 protected int getMaxPriority()
533 {
534 return USER_AUTH;
535 }
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553 public RateTracker getRateTracker(ServletRequest request)
554 {
555 HttpSession session = ((HttpServletRequest)request).getSession(false);
556
557 String loadId = extractUserId(request);
558 final int type;
559 if (loadId != null)
560 {
561 type = USER_AUTH;
562 }
563 else
564 {
565 if (isTrackSessions() && session != null && !session.isNew())
566 {
567 loadId = session.getId();
568 type = USER_SESSION;
569 }
570 else
571 {
572 loadId = isRemotePort() ? (request.getRemoteAddr() + request.getRemotePort()) : request.getRemoteAddr();
573 type = USER_IP;
574 }
575 }
576
577 RateTracker tracker = _rateTrackers.get(loadId);
578
579 if (tracker == null)
580 {
581 boolean allowed = checkWhitelist(_whitelist, request.getRemoteAddr());
582 int maxRequestsPerSec = getMaxRequestsPerSec();
583 tracker = allowed ? new FixedRateTracker(loadId, type, maxRequestsPerSec)
584 : new RateTracker(loadId, type, maxRequestsPerSec);
585 RateTracker existing = _rateTrackers.putIfAbsent(loadId, tracker);
586 if (existing != null)
587 tracker = existing;
588
589 if (type == USER_IP)
590 {
591
592 _scheduler.schedule(tracker, getMaxIdleTrackerMs(), TimeUnit.MILLISECONDS);
593 }
594 else if (session != null)
595 {
596
597 session.setAttribute(__TRACKER, tracker);
598 }
599 }
600
601 return tracker;
602 }
603
604 protected boolean checkWhitelist(List<String> whitelist, String candidate)
605 {
606 for (String address : whitelist)
607 {
608 if (address.contains("/"))
609 {
610 if (subnetMatch(address, candidate))
611 return true;
612 }
613 else
614 {
615 if (address.equals(candidate))
616 return true;
617 }
618 }
619 return false;
620 }
621
622 protected boolean subnetMatch(String subnetAddress, String address)
623 {
624 Matcher cidrMatcher = CIDR_PATTERN.matcher(subnetAddress);
625 if (!cidrMatcher.matches())
626 return false;
627
628 String subnet = cidrMatcher.group(1);
629 int prefix;
630 try
631 {
632 prefix = Integer.parseInt(cidrMatcher.group(2));
633 }
634 catch (NumberFormatException x)
635 {
636 LOG.info("Ignoring malformed CIDR address {}", subnetAddress);
637 return false;
638 }
639
640 byte[] subnetBytes = addressToBytes(subnet);
641 if (subnetBytes == null)
642 {
643 LOG.info("Ignoring malformed CIDR address {}", subnetAddress);
644 return false;
645 }
646 byte[] addressBytes = addressToBytes(address);
647 if (addressBytes == null)
648 {
649 LOG.info("Ignoring malformed remote address {}", address);
650 return false;
651 }
652
653
654 int length = subnetBytes.length;
655 if (length != addressBytes.length)
656 return false;
657
658 byte[] mask = prefixToBytes(prefix, length);
659
660 for (int i = 0; i < length; ++i)
661 {
662 if ((subnetBytes[i] & mask[i]) != (addressBytes[i] & mask[i]))
663 return false;
664 }
665
666 return true;
667 }
668
669 private byte[] addressToBytes(String address)
670 {
671 Matcher ipv4Matcher = IPv4_PATTERN.matcher(address);
672 if (ipv4Matcher.matches())
673 {
674 byte[] result = new byte[4];
675 for (int i = 0; i < result.length; ++i)
676 result[i] = Integer.valueOf(ipv4Matcher.group(i + 1)).byteValue();
677 return result;
678 }
679 else
680 {
681 Matcher ipv6Matcher = IPv6_PATTERN.matcher(address);
682 if (ipv6Matcher.matches())
683 {
684 byte[] result = new byte[16];
685 for (int i = 0; i < result.length; i += 2)
686 {
687 int word = Integer.valueOf(ipv6Matcher.group(i / 2 + 1), 16);
688 result[i] = (byte)((word & 0xFF00) >>> 8);
689 result[i + 1] = (byte)(word & 0xFF);
690 }
691 return result;
692 }
693 }
694 return null;
695 }
696
697 private byte[] prefixToBytes(int prefix, int length)
698 {
699 byte[] result = new byte[length];
700 int index = 0;
701 while (prefix / 8 > 0)
702 {
703 result[index] = -1;
704 prefix -= 8;
705 ++index;
706 }
707
708 result[index] = (byte)~((1 << (8 - prefix)) - 1);
709 return result;
710 }
711
712 public void destroy()
713 {
714 LOG.debug("Destroy {}",this);
715 stopScheduler();
716 _rateTrackers.clear();
717 _whitelist.clear();
718 }
719
720 protected void stopScheduler()
721 {
722 try
723 {
724 _scheduler.stop();
725 }
726 catch (Exception x)
727 {
728 LOG.ignore(x);
729 }
730 }
731
732
733
734
735
736
737
738
739 protected String extractUserId(ServletRequest request)
740 {
741 return null;
742 }
743
744
745
746
747
748
749
750
751 @ManagedAttribute("maximum number of requests allowed from a connection per second")
752 public int getMaxRequestsPerSec()
753 {
754 return _maxRequestsPerSec;
755 }
756
757
758
759
760
761
762
763
764 public void setMaxRequestsPerSec(int value)
765 {
766 _maxRequestsPerSec = value;
767 }
768
769
770
771
772
773 @ManagedAttribute("delay applied to all requests over the rate limit (in ms)")
774 public long getDelayMs()
775 {
776 return _delayMs;
777 }
778
779
780
781
782
783
784
785 public void setDelayMs(long value)
786 {
787 _delayMs = value;
788 }
789
790
791
792
793
794
795
796 @ManagedAttribute("maximum time the filter will block waiting throttled connections, (0 for no delay, -1 to reject requests)")
797 public long getMaxWaitMs()
798 {
799 return _maxWaitMs;
800 }
801
802
803
804
805
806
807
808 public void setMaxWaitMs(long value)
809 {
810 _maxWaitMs = value;
811 }
812
813
814
815
816
817
818
819 @ManagedAttribute("number of requests over rate limit")
820 public int getThrottledRequests()
821 {
822 return _throttledRequests;
823 }
824
825
826
827
828
829
830
831 public void setThrottledRequests(int value)
832 {
833 int permits = _passes == null ? 0 : _passes.availablePermits();
834 _passes = new Semaphore((value - _throttledRequests + permits), true);
835 _throttledRequests = value;
836 }
837
838
839
840
841
842
843 @ManagedAttribute("amount of time to async wait for semaphore")
844 public long getThrottleMs()
845 {
846 return _throttleMs;
847 }
848
849
850
851
852
853
854 public void setThrottleMs(long value)
855 {
856 _throttleMs = value;
857 }
858
859
860
861
862
863
864
865 @ManagedAttribute("maximum time to allow requests to process (in ms)")
866 public long getMaxRequestMs()
867 {
868 return _maxRequestMs;
869 }
870
871
872
873
874
875
876
877 public void setMaxRequestMs(long value)
878 {
879 _maxRequestMs = value;
880 }
881
882
883
884
885
886
887
888
889 @ManagedAttribute("maximum time to track of request rates for connection before discarding")
890 public long getMaxIdleTrackerMs()
891 {
892 return _maxIdleTrackerMs;
893 }
894
895
896
897
898
899
900
901
902 public void setMaxIdleTrackerMs(long value)
903 {
904 _maxIdleTrackerMs = value;
905 }
906
907
908
909
910
911
912 @ManagedAttribute("inser DoSFilter headers in response")
913 public boolean isInsertHeaders()
914 {
915 return _insertHeaders;
916 }
917
918
919
920
921
922
923 public void setInsertHeaders(boolean value)
924 {
925 _insertHeaders = value;
926 }
927
928
929
930
931
932
933 @ManagedAttribute("usage rate is tracked by session if one exists")
934 public boolean isTrackSessions()
935 {
936 return _trackSessions;
937 }
938
939
940
941
942
943
944 public void setTrackSessions(boolean value)
945 {
946 _trackSessions = value;
947 }
948
949
950
951
952
953
954
955 @ManagedAttribute("usage rate is tracked by IP+port is session tracking not used")
956 public boolean isRemotePort()
957 {
958 return _remotePort;
959 }
960
961
962
963
964
965
966
967 public void setRemotePort(boolean value)
968 {
969 _remotePort = value;
970 }
971
972
973
974
975 @ManagedAttribute("whether this filter is enabled")
976 public boolean isEnabled()
977 {
978 return _enabled;
979 }
980
981
982
983
984 public void setEnabled(boolean enabled)
985 {
986 _enabled = enabled;
987 }
988
989
990
991
992
993
994 @ManagedAttribute("list of IPs that will not be rate limited")
995 public String getWhitelist()
996 {
997 StringBuilder result = new StringBuilder();
998 for (Iterator<String> iterator = _whitelist.iterator(); iterator.hasNext();)
999 {
1000 String address = iterator.next();
1001 result.append(address);
1002 if (iterator.hasNext())
1003 result.append(",");
1004 }
1005 return result.toString();
1006 }
1007
1008
1009
1010
1011
1012
1013 public void setWhitelist(String value)
1014 {
1015 List<String> result = new ArrayList<>();
1016 for (String address : value.split(","))
1017 addWhitelistAddress(result, address);
1018 clearWhitelist();
1019 _whitelist.addAll(result);
1020 LOG.debug("Whitelisted IP addresses: {}", result);
1021 }
1022
1023
1024
1025
1026 @ManagedOperation("clears the list of IP addresses that will not be rate limited")
1027 public void clearWhitelist()
1028 {
1029 _whitelist.clear();
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040 @ManagedOperation("adds an IP address that will not be rate limited")
1041 public boolean addWhitelistAddress(@Name("address") String address)
1042 {
1043 return addWhitelistAddress(_whitelist, address);
1044 }
1045
1046 private boolean addWhitelistAddress(List<String> list, String address)
1047 {
1048 address = address.trim();
1049 return address.length() > 0 && list.add(address);
1050 }
1051
1052
1053
1054
1055
1056
1057
1058
1059 @ManagedOperation("removes an IP address that will not be rate limited")
1060 public boolean removeWhitelistAddress(@Name("address") String address)
1061 {
1062 return _whitelist.remove(address);
1063 }
1064
1065
1066
1067
1068
1069 class RateTracker implements Runnable, HttpSessionBindingListener, HttpSessionActivationListener, Serializable
1070 {
1071 private static final long serialVersionUID = 3534663738034577872L;
1072
1073 protected transient final String _id;
1074 protected transient final int _type;
1075 protected transient final long[] _timestamps;
1076 protected transient int _next;
1077
1078 public RateTracker(String id, int type, int maxRequestsPerSecond)
1079 {
1080 _id = id;
1081 _type = type;
1082 _timestamps = new long[maxRequestsPerSecond];
1083 _next = 0;
1084 }
1085
1086
1087
1088
1089 public boolean isRateExceeded(long now)
1090 {
1091 final long last;
1092 synchronized (this)
1093 {
1094 last = _timestamps[_next];
1095 _timestamps[_next] = now;
1096 _next = (_next + 1) % _timestamps.length;
1097 }
1098
1099 return last != 0 && (now - last) < 1000L;
1100 }
1101
1102 public String getId()
1103 {
1104 return _id;
1105 }
1106
1107 public int getType()
1108 {
1109 return _type;
1110 }
1111
1112 public void valueBound(HttpSessionBindingEvent event)
1113 {
1114 if (LOG.isDebugEnabled())
1115 LOG.debug("Value bound: {}", getId());
1116 }
1117
1118 public void valueUnbound(HttpSessionBindingEvent event)
1119 {
1120
1121 _rateTrackers.remove(_id);
1122 if (LOG.isDebugEnabled())
1123 LOG.debug("Tracker removed: {}", getId());
1124 }
1125
1126 public void sessionWillPassivate(HttpSessionEvent se)
1127 {
1128
1129
1130 _rateTrackers.remove(_id);
1131 se.getSession().removeAttribute(__TRACKER);
1132 if (LOG.isDebugEnabled())
1133 LOG.debug("Value removed: {}", getId());
1134 }
1135
1136 public void sessionDidActivate(HttpSessionEvent se)
1137 {
1138 LOG.warn("Unexpected session activation");
1139 }
1140
1141 @Override
1142 public void run()
1143 {
1144 int latestIndex = _next == 0 ? (_timestamps.length - 1) : (_next - 1);
1145 long last = _timestamps[latestIndex];
1146 boolean hasRecentRequest = last != 0 && (System.currentTimeMillis() - last) < 1000L;
1147
1148 if (hasRecentRequest)
1149 _scheduler.schedule(this, getMaxIdleTrackerMs(), TimeUnit.MILLISECONDS);
1150 else
1151 _rateTrackers.remove(_id);
1152 }
1153
1154 @Override
1155 public String toString()
1156 {
1157 return "RateTracker/" + _id + "/" + _type;
1158 }
1159 }
1160
1161 class FixedRateTracker extends RateTracker
1162 {
1163 public FixedRateTracker(String id, int type, int numRecentRequestsTracked)
1164 {
1165 super(id, type, numRecentRequestsTracked);
1166 }
1167
1168 @Override
1169 public boolean isRateExceeded(long now)
1170 {
1171
1172
1173
1174 synchronized (this)
1175 {
1176 _timestamps[_next] = now;
1177 _next = (_next + 1) % _timestamps.length;
1178 }
1179
1180 return false;
1181 }
1182
1183 @Override
1184 public String toString()
1185 {
1186 return "Fixed" + super.toString();
1187 }
1188 }
1189
1190 private class DoSTimeoutAsyncListener implements AsyncListener
1191 {
1192 @Override
1193 public void onStartAsync(AsyncEvent event) throws IOException
1194 {
1195 }
1196
1197 @Override
1198 public void onComplete(AsyncEvent event) throws IOException
1199 {
1200 }
1201
1202 @Override
1203 public void onTimeout(AsyncEvent event) throws IOException
1204 {
1205 event.getAsyncContext().dispatch();
1206 }
1207
1208 @Override
1209 public void onError(AsyncEvent event) throws IOException
1210 {
1211 }
1212 }
1213
1214 private class DoSAsyncListener extends DoSTimeoutAsyncListener
1215 {
1216 private final int priority;
1217
1218 public DoSAsyncListener(int priority)
1219 {
1220 this.priority = priority;
1221 }
1222
1223 @Override
1224 public void onTimeout(AsyncEvent event) throws IOException
1225 {
1226 _queues[priority].remove(event.getAsyncContext());
1227 super.onTimeout(event);
1228 }
1229 }
1230 }