Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qipaddress.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include "qipaddress_p.h"
7#include "private/qlocale_tools_p.h"
8#include "private/qtools_p.h"
10
12
13using namespace Qt::StringLiterals;
14
15namespace QIPAddressUtils {
16
17static QString number(quint8 val)
18{
19 QString zero = QStringLiteral("0");
20 return val ? qulltoa(val, 10, zero) : zero;
21}
22
23typedef QVarLengthArray<char, 64> Buffer;
24static const QChar *checkedToAscii(Buffer &buffer, const QChar *begin, const QChar *end)
25{
26 const auto *const ubegin = reinterpret_cast<const char16_t *>(begin);
27 const auto *const uend = reinterpret_cast<const char16_t *>(end);
28 auto *src = ubegin;
29
30 buffer.resize(uend - ubegin + 1);
31 char *dst = buffer.data();
32
33 while (src != uend) {
34 if (*src >= 0x7f)
35 return reinterpret_cast<const QChar *>(src);
36 *dst++ = *src++;
37 }
38 *dst = '\0';
39 return nullptr;
40}
41
42static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero);
43bool parseIp4(IPv4Address &address, const QChar *begin, const QChar *end)
44{
45 Q_ASSERT(begin != end);
46 Buffer buffer;
47 if (checkedToAscii(buffer, begin, end))
48 return false;
49
50 const char *ptr = buffer.data();
51 return parseIp4Internal(address, ptr, true);
52}
53
54static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero)
55{
56 address = 0;
57 int dotCount = 0;
58 const char *const stop = ptr + qstrlen(ptr);
59 while (dotCount < 4) {
60 if (!acceptLeadingZero && *ptr == '0' &&
61 ptr[1] != '.' && ptr[1] != '\0')
62 return false;
63
64 auto [ll, used] = qstrntoull(ptr, stop - ptr, 0);
65 const quint32 x = quint32(ll);
66 if (used <= 0 || ll != x)
67 return false;
68
69 if (ptr[used] == '.' || dotCount == 3) {
70 if (x & ~0xff)
71 return false;
72 address <<= 8;
73 } else if (dotCount == 2) {
74 if (x & ~0xffff)
75 return false;
76 address <<= 16;
77 } else if (dotCount == 1) {
78 if (x & ~0xffffff)
79 return false;
80 address <<= 24;
81 }
82 address |= x;
83
84 if (dotCount == 3 || ptr[used] == '\0')
85 return ptr[used] == '\0';
86 if (ptr[used] != '.')
87 return false;
88
89 ++dotCount;
90 ptr += used + 1;
91 }
92 return false;
93}
94
95void toString(QString &appendTo, IPv4Address address)
96{
97 // reconstructing is easy
98 // use the fast operator% that pre-calculates the size
99 appendTo += number(address >> 24) % u'.'
100 % number(address >> 16) % u'.'
101 % number(address >> 8) % u'.'
102 % number(address);
103}
104
105/*!
106 \internal
107 \since 5.0
108
109 Parses one IPv6 address from \a begin to \a end and stores the
110 representation in \a address. Returns null if everything was parsed
111 correctly, or the pointer to the first bad character where parsing failed.
112 If the parsing failed for a reason not related to a particular character,
113 returns \a end.
114*/
115const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end)
116{
117 Q_ASSERT(begin != end);
118
119 if (end - begin < 2)
120 return end;
121
122 Buffer buffer;
123 const QChar *ret = checkedToAscii(buffer, begin, end);
124 if (ret)
125 return ret;
126
127 const char *ptr = buffer.data();
128 const char *const stop = ptr + buffer.size();
129
130 // count the colons
131 int colonCount = 0;
132 int dotCount = 0;
133 while (*ptr) {
134 if (*ptr == ':')
135 ++colonCount;
136 if (*ptr == '.')
137 ++dotCount;
138 ++ptr;
139 }
140 // IPv4-in-IPv6 addresses are stricter in what they accept
141 if (dotCount != 0 && dotCount != 3)
142 return end;
143
144 memset(address, 0, sizeof address);
145 if (colonCount == 2 && end - begin == 2) // "::"
146 return nullptr;
147
148 // if there's a double colon ("::"), this is how many zeroes it means
149 int zeroWordsToFill;
150 ptr = buffer.data();
151
152 // there are two cases where 8 colons are allowed: at the ends
153 // so test that before the colon-count test
154 if ((ptr[0] == ':' && ptr[1] == ':') ||
155 (ptr[end - begin - 2] == ':' && ptr[end - begin - 1] == ':')) {
156 zeroWordsToFill = 9 - colonCount;
157 } else if (colonCount < 2 || colonCount > 7) {
158 return end;
159 } else {
160 zeroWordsToFill = 8 - colonCount;
161 }
162 if (dotCount)
163 --zeroWordsToFill;
164
165 int pos = 0;
166 while (pos < 15) {
167 if (*ptr == ':') {
168 // empty field, we hope it's "::"
169 if (zeroWordsToFill < 1)
170 return begin + (ptr - buffer.data());
171 if (pos == 0 || pos == colonCount * 2) {
172 if (ptr[0] == '\0' || ptr[1] != ':')
173 return begin + (ptr - buffer.data());
174 ++ptr;
175 }
176 pos += zeroWordsToFill * 2;
177 zeroWordsToFill = 0;
178 ++ptr;
179 continue;
180 }
181
182 auto [ll, used] = qstrntoull(ptr, stop - ptr, 16);
183 quint16 x = ll;
184
185 // Reject malformed fields:
186 // - failed to parse
187 // - too many hex digits
188 if (used <= 0 || used > 4)
189 return begin + (ptr - buffer.data());
190
191 if (ptr[used] == '.') {
192 // this could be an IPv4 address
193 // it's only valid in the last element
194 if (pos != 12)
195 return begin + (ptr - buffer.data());
196
197 IPv4Address ip4;
198 if (!parseIp4Internal(ip4, ptr, false))
199 return begin + (ptr - buffer.data());
200
201 address[12] = ip4 >> 24;
202 address[13] = ip4 >> 16;
203 address[14] = ip4 >> 8;
204 address[15] = ip4;
205 return nullptr;
206 }
207
208 address[pos++] = x >> 8;
209 address[pos++] = x & 0xff;
210
211 if (ptr[used] == '\0')
212 break;
213 if (ptr[used] != ':')
214 return begin + (used + ptr - buffer.data());
215 ptr += used + 1;
216 }
217 return pos == 16 ? nullptr : end;
218}
219
220static inline QChar toHex(uchar c)
221{
222 return QChar::fromLatin1(QtMiscUtils::toHexLower(c));
223}
224
225void toString(QString &appendTo, const IPv6Address address)
226{
227 // the longest IPv6 address possible is:
228 // "1111:2222:3333:4444:5555:6666:255.255.255.255"
229 // however, this function never generates that. The longest it does
230 // generate without an IPv4 address is:
231 // "1111:2222:3333:4444:5555:6666:7777:8888"
232 // and the longest with an IPv4 address is:
233 // "::ffff:255.255.255.255"
234 static const int Ip6AddressMaxLen = sizeof "1111:2222:3333:4444:5555:6666:7777:8888";
235 static const int Ip6WithIp4AddressMaxLen = sizeof "::ffff:255.255.255.255";
236
237 // check for the special cases
238 const quint64 zeroes[] = { 0, 0 };
239 bool embeddedIp4 = false;
240
241 // we consider embedded IPv4 for:
242 // ::ffff:x.x.x.x
243 // ::x.x.x.y except if the x are 0 too
244 if (memcmp(address, zeroes, 10) == 0) {
245 if (address[10] == 0xff && address[11] == 0xff) {
246 embeddedIp4 = true;
247 } else if (address[10] == 0 && address[11] == 0) {
248 if (address[12] != 0 || address[13] != 0 || address[14] != 0) {
249 embeddedIp4 = true;
250 } else if (address[15] == 0) {
251 appendTo.append("::"_L1);
252 return;
253 }
254 }
255 }
256
257 // QString::reserve doesn't shrink, so it's fine to us
258 appendTo.reserve(appendTo.size() +
259 (embeddedIp4 ? Ip6WithIp4AddressMaxLen : Ip6AddressMaxLen));
260
261 // for finding where to place the "::"
262 int zeroRunLength = 0; // in octets
263 int zeroRunOffset = 0; // in octets
264 for (int i = 0; i < 16; i += 2) {
265 if (address[i] == 0 && address[i + 1] == 0) {
266 // found a zero, scan forward to see how many more there are
267 int j;
268 for (j = i; j < 16; j += 2) {
269 if (address[j] != 0 || address[j+1] != 0)
270 break;
271 }
272
273 if (j - i > zeroRunLength) {
274 zeroRunLength = j - i;
275 zeroRunOffset = i;
276 i = j;
277 }
278 }
279 }
280
281 const QChar colon = u':';
282 if (zeroRunLength < 4)
283 zeroRunOffset = -1;
284 else if (zeroRunOffset == 0)
285 appendTo.append(colon);
286
287 for (int i = 0; i < 16; i += 2) {
288 if (i == zeroRunOffset) {
289 appendTo.append(colon);
290 i += zeroRunLength - 2;
291 continue;
292 }
293
294 if (i == 12 && embeddedIp4) {
295 IPv4Address ip4 = address[12] << 24 |
296 address[13] << 16 |
297 address[14] << 8 |
298 address[15];
299 toString(appendTo, ip4);
300 return;
301 }
302
303 if (address[i]) {
304 if (address[i] >> 4) {
305 appendTo.append(toHex(address[i] >> 4));
306 appendTo.append(toHex(address[i] & 0xf));
307 appendTo.append(toHex(address[i + 1] >> 4));
308 appendTo.append(toHex(address[i + 1] & 0xf));
309 } else if (address[i] & 0xf) {
310 appendTo.append(toHex(address[i] & 0xf));
311 appendTo.append(toHex(address[i + 1] >> 4));
312 appendTo.append(toHex(address[i + 1] & 0xf));
313 }
314 } else if (address[i + 1] >> 4) {
315 appendTo.append(toHex(address[i + 1] >> 4));
316 appendTo.append(toHex(address[i + 1] & 0xf));
317 } else {
318 appendTo.append(toHex(address[i + 1] & 0xf));
319 }
320
321 if (i != 14)
322 appendTo.append(colon);
323 }
324}
325
326}
327QT_END_NAMESPACE
static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero)
quint8 IPv6Address[16]
static QString number(quint8 val)
void toString(QString &appendTo, IPv4Address address)
const QChar * parseIp6(IPv6Address &address, const QChar *begin, const QChar *end)
static QChar toHex(uchar c)
bool parseIp4(IPv4Address &address, const QChar *begin, const QChar *end)
quint32 IPv4Address
void toString(QString &appendTo, const IPv6Address address)
QVarLengthArray< char, 64 > Buffer
static const QChar * checkedToAscii(Buffer &buffer, const QChar *begin, const QChar *end)
Combined button and popup list for selecting options.
#define QStringLiteral(str)
Definition qstring.h:1825