summaryrefslogtreecommitdiff
path: root/decoders/session_flags/fet.cpp
blob: 61327d4fd919c68a8602f3381afbe303245ec39a (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
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
/*ref: https://github.com/apernet/OpenGFW/blob/1dce82745d0bc8b3813aea147954ba3a2294ef30/analyzer/tcp/fet.go
https://www.usenix.org/system/files/usenixsecurity23-wu-mingshi.pdf
*/

#include <string.h>
#include <stdlib.h>
#include "fet.h"

bool isPrintable(unsigned char b) {
    return b >= 0x20 && b <= 0x7e;
}

int popCount(unsigned char b) {
    int count = 0;
    while (b != 0) {
        count += (int)(b & 1);
        b >>= 1;
    }
    return count;
}

float averagePopCount(const unsigned char* bytes, int bytes_len) {
    if (bytes_len == 0) {
        return 0;
    }
    int total = 0;
    for (int i = 0; i < bytes_len; i++) {
        total += popCount(bytes[i]);
    }
    return (float)total / (float)bytes_len;
}

bool isFirstSixPrintable(const unsigned char* bytes, int bytes_len) {
    if (bytes_len < 6) {
        return false;
    }
    for (int i = 0; i < 6; i++) {
        if (!isPrintable(bytes[i])) {
            return false;
        }
    }
    return true;
}

float printablePercentage(const unsigned char* bytes, int bytes_len) {
    if (bytes_len == 0) {
        return 0;
    }
    int count = 0;
    for (int i = 0; i < bytes_len; i++) {
        if (isPrintable(bytes[i])) {
            count++;
        }
    }
    return (float)count / (float)bytes_len;
}

int contiguousPrintable(const unsigned char* bytes, int bytes_len) {
    if (bytes_len == 0) {
        return 0;
    }
    int maxCount = 0;
    int current = 0;
    for (int i = 0; i < bytes_len; i++) {
        if (isPrintable(bytes[i])) {
            current++;
        } else {
            if (current > maxCount) {
                maxCount = current;
            }
            current = 0;
        }
    }
    if (current > maxCount) {
        maxCount = current;
    }
    return maxCount;
}

bool isTLS(const unsigned char* bytes, int bytes_len) {
    if (bytes_len < 3) {
        return false;
    }
    // "We observe that the GFW exempts any connection whose first
    // three bytes match the following regular expression:
    // [\x16-\x17]\x03[\x00-\x09]" - from the paper in Section 4.3
    if (bytes[0] >= 0x16 && bytes[0] <= 0x17 &&
        bytes[1] == 0x03 && bytes[2] <= 0x09) {
        return true;
    }
    
    return false;
}

bool isHTTP(const unsigned char* bytes, int bytes_len) {
    if (bytes_len < 3) {
        return false;
    }

    // HTTP request
    bool result = memcmp(bytes, "GET", 3) == 0 || memcmp(bytes, "HEA", 3) == 0 || memcmp(bytes, "POS", 3) == 0 ||
                  memcmp(bytes, "PUT", 3) == 0 || memcmp(bytes, "DEL", 3) == 0 || memcmp(bytes, "CON", 3) == 0 ||
                  memcmp(bytes, "OPT", 3) == 0 || memcmp(bytes, "TRA", 3) == 0 || memcmp(bytes, "PAT", 3) == 0;

    return result;
}

int is_data_fet(unsigned char* data, int data_len, struct fet_detail* detail) {
    if (data_len == 0) {
        return 0;
    }
    float ex1 = averagePopCount(data, data_len);
    bool ex2 = isFirstSixPrintable(data, data_len);
    float ex3 = printablePercentage(data, data_len);
    int ex4 = contiguousPrintable(data, data_len);
    bool ex5 = isTLS(data, data_len);
    bool ex6 = isHTTP(data, data_len);
    bool exempt = (ex1 <= 3.4 || ex1 >= 4.6) || ex2 || ex3 > 0.5 || ex4 > 20 || ex5 || ex6;

    detail->is_tls = ex5;

    return !exempt;
}