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