aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/serverLib/RateLimiter.js
blob: 43cf077f9bd17238a1267555c8329d90d237ac58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
/**
  * Tracks frequency of occurances based on `id` (remote address), then allows or
  * denies command execution based on comparison with `threshold`
  *
  * Version: v2.0.0
  * Developer: Marzavec ( https://github.com/marzavec )
  * License: WTFPL ( http://www.wtfpl.net/txt/copying/ )
  *
  */

class RateLimiter {
  /**
   * Create a ratelimiter instance.
   */
  constructor () {
    this.records = {};
    this.halflife = 30 * 1000; // milliseconds
    this.threshold = 25;
    this.hashes = [];
  }

  /**
    * Finds current score by `id`
    *
    * @param {String} id target id / address
    *
    * @return {Object} Object containing the record meta
    */
  search (id) {
    let record = this.records[id];

    if (!record) {
      record = this.records[id] = {
        time: Date.now(),
        score: 0
      }
    }

    return record;
  }

  /**
    * Adjusts the current ratelimit score by `deltaScore`
    *
    * @param {String} id target id / address
    * @param {Number} deltaScore amount to adjust current score by
    *
    * @return {Boolean} True if record threshold has been exceeded
    */
  frisk (id, deltaScore) {
    let record = this.search(id);

    if (record.arrested) {
      return true;
    }

    record.score *= Math.pow(2, -(Date.now() - record.time ) / this.halflife);
    record.score += deltaScore;
    record.time = Date.now();

    if (record.score >= this.threshold) {
      return true;
    }

    return false;
  }

  /**
    * Statically set server to no longer accept traffic from `id`
    *
    * @param {String} id target id / address
    */
  arrest (id, hash) {
    let record = this.search(id);

    record.arrested = true;
    this.hashes[hash] = id;
  }

  /**
    * Remove statically assigned limit from `id`
    *
    * @param {String} id target id / address
    */
  pardon (id) {
    if (typeof this.hashes[id] !== 'undefined') {
      id = this.hashes[id];
    }

    let record = this.search(id);
    record.arrested = false;
  }
}

module.exports = RateLimiter;