Coverage Report

Created: 2025-10-14 06:14

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.48k
T _cond_byte_swap(T value) {
53
1.48k
    uint32_t endian_test = 1;
54
1.48k
    if (*((char *)&endian_test)) {
55
1.48k
        union {
56
1.48k
            T i;
57
1.48k
            uint8_t b[sizeof(T)];
58
1.48k
        } src = { value }, dst;
59
60
4.45k
        for (unsigned int i = 0; i < sizeof(value); i++) {
61
2.96k
            dst.b[i] = src.b[sizeof(value) - 1 - i];
62
2.96k
        }
63
64
1.48k
        return dst.i;
65
1.48k
    }
66
0
    return value;
67
1.48k
}
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
13.8k
    std::string_view getSourceAddress() {
79
80
        // UNSPEC family and protocol
81
13.8k
        if (family == 0) {
82
11.2k
            return {};
83
11.2k
        }
84
85
2.62k
        if ((family & 0xf0) >> 4 == 1) {
86
            /* Family 1 is INET4 */
87
1.91k
            return {(char *) &addr.ipv4_addr.src_addr, 4};
88
1.91k
        } else {
89
            /* Family 2 is INET6 */
90
715
            return {(char *) &addr.ipv6_addr.src_addr, 16};
91
715
        }
92
2.62k
    }
93
94
    /* Returns [done, consumed] where done = false on failure */
95
104k
    std::pair<bool, unsigned int> parse(std::string_view data) {
96
97
        /* We require at least four bytes to determine protocol */
98
104k
        if (data.length() < 4) {
99
1.01k
            return {false, 0};
100
1.01k
        }
101
102
        /* HTTP can never start with "\r\n\r\n", but PROXY always does */
103
103k
        if (memcmp(data.data(), "\r\n\r\n", 4)) {
104
            /* This is HTTP, so be done */
105
101k
            return {true, 0};
106
101k
        }
107
108
        /* We assume we are parsing PROXY V2 here */
109
110
        /* We require 16 bytes here */
111
2.69k
        if (data.length() < 16) {
112
446
            return {false, 0};
113
446
        }
114
115
        /* Header is 16 bytes */
116
2.24k
        struct proxy_hdr_v2 header;
117
2.24k
        memcpy(&header, data.data(), 16);
118
119
2.24k
        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
391
            return {false, 0};
122
391
        }
123
124
        /* We only support version 2 */
125
1.85k
        if ((header.ver_cmd & 0xf0) >> 4 != 2) {
126
372
            return {false, 0};
127
372
        }
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
1.48k
        uint16_t hostLength = _cond_byte_swap<uint16_t>(header.len);
134
135
        /* We must have all the data available */
136
1.48k
        if (data.length() < 16u + hostLength) {
137
259
            return {false, 0};
138
259
        }
139
140
        /* Payload cannot be more than sizeof proxy_addr */
141
1.22k
        if (sizeof(proxy_addr) < hostLength) {
142
219
            return {false, 0};
143
219
        }
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
1.00k
        family = header.fam;
150
151
        /* Copy payload */
152
1.00k
        memcpy(&addr, data.data() + 16, hostLength);
153
154
        /* We consumed everything */
155
1.00k
        return {true, 16 + hostLength};
156
1.22k
    }
157
};
158
159
}
160
161
#endif
162
163
#endif // UWS_PROXY_PARSER_H