Coverage Report

Created: 2025-06-13 06:09

/src/uWebSockets/src/ProxyParser.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Authored by Alex Hultman, 2018-2020.
3
 * Intellectual property of third-party.
4
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/* This module implements The PROXY Protocol v2 */
19
20
#ifndef UWS_PROXY_PARSER_H
21
#define UWS_PROXY_PARSER_H
22
23
#ifdef UWS_WITH_PROXY
24
25
namespace uWS {
26
27
struct proxy_hdr_v2 {
28
    uint8_t sig[12];  /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
29
    uint8_t ver_cmd;  /* protocol version and command */
30
    uint8_t fam;      /* protocol family and address */
31
    uint16_t len;     /* number of following bytes part of the header */
32
};
33
34
union proxy_addr {
35
    struct {        /* for TCP/UDP over IPv4, len = 12 */
36
        uint32_t src_addr;
37
        uint32_t dst_addr;
38
        uint16_t src_port;
39
        uint16_t dst_port;
40
    } ipv4_addr;
41
    struct {        /* for TCP/UDP over IPv6, len = 36 */
42
            uint8_t  src_addr[16];
43
            uint8_t  dst_addr[16];
44
            uint16_t src_port;
45
            uint16_t dst_port;
46
    } ipv6_addr;
47
};
48
49
/* Byte swap for little-endian systems */
50
/* Todo: This functions should be shared with the one in WebSocketProtocol.h! */
51
template <typename T>
52
2.03k
T _cond_byte_swap(T value) {
53
2.03k
    uint32_t endian_test = 1;
54
2.03k
    if (*((char *)&endian_test)) {
55
2.03k
        union {
56
2.03k
            T i;
57
2.03k
            uint8_t b[sizeof(T)];
58
2.03k
        } src = { value }, dst;
59
60
6.09k
        for (unsigned int i = 0; i < sizeof(value); i++) {
61
4.06k
            dst.b[i] = src.b[sizeof(value) - 1 - i];
62
4.06k
        }
63
64
2.03k
        return dst.i;
65
2.03k
    }
66
0
    return value;
67
2.03k
}
68
69
struct ProxyParser {
70
private:
71
    union proxy_addr addr;
72
73
    /* Default family of 0 signals no proxy address */
74
    uint8_t family = 0;
75
76
public:
77
    /* Returns 4 or 16 bytes source address */
78
14.7k
    std::string_view getSourceAddress() {
79
80
        // UNSPEC family and protocol
81
14.7k
        if (family == 0) {
82
11.0k
            return {};
83
11.0k
        }
84
85
3.69k
        if ((family & 0xf0) >> 4 == 1) {
86
            /* Family 1 is INET4 */
87
2.90k
            return {(char *) &addr.ipv4_addr.src_addr, 4};
88
2.90k
        } else {
89
            /* Family 2 is INET6 */
90
788
            return {(char *) &addr.ipv6_addr.src_addr, 16};
91
788
        }
92
3.69k
    }
93
94
    /* Returns [done, consumed] where done = false on failure */
95
98.5k
    std::pair<bool, unsigned int> parse(std::string_view data) {
96
97
        /* We require at least four bytes to determine protocol */
98
98.5k
        if (data.length() < 4) {
99
1.03k
            return {false, 0};
100
1.03k
        }
101
102
        /* HTTP can never start with "\r\n\r\n", but PROXY always does */
103
97.5k
        if (memcmp(data.data(), "\r\n\r\n", 4)) {
104
            /* This is HTTP, so be done */
105
94.4k
            return {true, 0};
106
94.4k
        }
107
108
        /* We assume we are parsing PROXY V2 here */
109
110
        /* We require 16 bytes here */
111
3.09k
        if (data.length() < 16) {
112
447
            return {false, 0};
113
447
        }
114
115
        /* Header is 16 bytes */
116
2.64k
        struct proxy_hdr_v2 header;
117
2.64k
        memcpy(&header, data.data(), 16);
118
119
2.64k
        if (memcmp(header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12)) {
120
            /* This is not PROXY protocol at all */
121
405
            return {false, 0};
122
405
        }
123
124
        /* We only support version 2 */
125
2.24k
        if ((header.ver_cmd & 0xf0) >> 4 != 2) {
126
211
            return {false, 0};
127
211
        }
128
129
        //printf("Version: %d\n", (header.ver_cmd & 0xf0) >> 4);
130
        //printf("Command: %d\n", (header.ver_cmd & 0x0f));
131
132
        /* We get length in network byte order (todo: share this function with the rest) */
133
2.03k
        uint16_t hostLength = _cond_byte_swap<uint16_t>(header.len);
134
135
        /* We must have all the data available */
136
2.03k
        if (data.length() < 16u + hostLength) {
137
242
            return {false, 0};
138
242
        }
139
140
        /* Payload cannot be more than sizeof proxy_addr */
141
1.78k
        if (sizeof(proxy_addr) < hostLength) {
142
894
            return {false, 0};
143
894
        }
144
145
        //printf("Family: %d\n", (header.fam & 0xf0) >> 4);
146
        //printf("Transport: %d\n", (header.fam & 0x0f));
147
148
        /* We have 0 family by default, and UNSPEC is 0 as well */
149
894
        family = header.fam;
150
151
        /* Copy payload */
152
894
        memcpy(&addr, data.data() + 16, hostLength);
153
154
        /* We consumed everything */
155
894
        return {true, 16 + hostLength};
156
1.78k
    }
157
};
158
159
}
160
161
#endif
162
163
#endif // UWS_PROXY_PARSER_H