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