Coverage Report

Created: 2026-04-01 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/uWebSockets/src/ProxyParser.h
Line
Count
Source
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
1.02k
T _cond_byte_swap(T value) {
53
1.02k
    uint32_t endian_test = 1;
54
1.02k
    if (*((char *)&endian_test)) {
55
1.02k
        union {
56
1.02k
            T i;
57
1.02k
            uint8_t b[sizeof(T)];
58
1.02k
        } src = { value }, dst;
59
60
3.07k
        for (unsigned int i = 0; i < sizeof(value); i++) {
61
2.05k
            dst.b[i] = src.b[sizeof(value) - 1 - i];
62
2.05k
        }
63
64
1.02k
        return dst.i;
65
1.02k
    }
66
0
    return value;
67
1.02k
}
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
17.8k
    std::string_view getSourceAddress() {
79
80
        // UNSPEC family and protocol
81
17.8k
        if (family == 0) {
82
13.5k
            return {};
83
13.5k
        }
84
85
4.33k
        if ((family & 0xf0) >> 4 == 1) {
86
            /* Family 1 is INET4 */
87
3.48k
            return {(char *) &addr.ipv4_addr.src_addr, 4};
88
3.48k
        } else {
89
            /* Family 2 is INET6 */
90
856
            return {(char *) &addr.ipv6_addr.src_addr, 16};
91
856
        }
92
4.33k
    }
93
94
0
    unsigned int getSourcePort() {
95
0
96
0
        // UNSPEC family and protocol
97
0
        if (family == 0) {
98
0
            return {};
99
0
        }
100
0
101
0
        if ((family & 0xf0) >> 4 == 1) {
102
0
            /* Family 1 is INET4 */
103
0
            return addr.ipv4_addr.src_port;
104
0
        } else {
105
0
            /* Family 2 is INET6 */
106
0
            return addr.ipv6_addr.src_port;
107
0
        }
108
0
    }
109
110
    /* Returns [done, consumed] where done = false on failure */
111
93.1k
    std::pair<bool, unsigned int> parse(std::string_view data) {
112
113
        /* We require at least four bytes to determine protocol */
114
93.1k
        if (data.length() < 4) {
115
1.06k
            return {false, 0};
116
1.06k
        }
117
118
        /* HTTP can never start with "\r\n\r\n", but PROXY always does */
119
92.1k
        if (memcmp(data.data(), "\r\n\r\n", 4)) {
120
            /* This is HTTP, so be done */
121
90.1k
            return {true, 0};
122
90.1k
        }
123
124
        /* We assume we are parsing PROXY V2 here */
125
126
        /* We require 16 bytes here */
127
1.96k
        if (data.length() < 16) {
128
325
            return {false, 0};
129
325
        }
130
131
        /* Header is 16 bytes */
132
1.63k
        struct proxy_hdr_v2 header;
133
1.63k
        memcpy(&header, data.data(), 16);
134
135
1.63k
        if (memcmp(header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12)) {
136
            /* This is not PROXY protocol at all */
137
410
            return {false, 0};
138
410
        }
139
140
        /* We only support version 2 */
141
1.22k
        if ((header.ver_cmd & 0xf0) >> 4 != 2) {
142
200
            return {false, 0};
143
200
        }
144
145
        //printf("Version: %d\n", (header.ver_cmd & 0xf0) >> 4);
146
        //printf("Command: %d\n", (header.ver_cmd & 0x0f));
147
148
        /* We get length in network byte order (todo: share this function with the rest) */
149
1.02k
        uint16_t hostLength = _cond_byte_swap<uint16_t>(header.len);
150
151
        /* We must have all the data available */
152
1.02k
        if (data.length() < 16u + hostLength) {
153
213
            return {false, 0};
154
213
        }
155
156
        /* Payload cannot be more than sizeof proxy_addr */
157
812
        if (sizeof(proxy_addr) < hostLength) {
158
209
            return {false, 0};
159
209
        }
160
161
        //printf("Family: %d\n", (header.fam & 0xf0) >> 4);
162
        //printf("Transport: %d\n", (header.fam & 0x0f));
163
164
        /* We have 0 family by default, and UNSPEC is 0 as well */
165
603
        family = header.fam;
166
167
        /* Copy payload */
168
603
        memcpy(&addr, data.data() + 16, hostLength);
169
170
        /* We consumed everything */
171
603
        return {true, 16 + hostLength};
172
812
    }
173
};
174
175
}
176
177
#endif
178
179
#endif // UWS_PROXY_PARSER_H