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 Buffer buffer;
119 const QChar *ret = checkedToAscii(buffer, begin, end);
120 if (ret)
121 return ret;
122
123 const char *ptr = buffer.data();
124 const char *const stop = ptr + buffer.size();
125
126 // count the colons
127 int colonCount = 0;
128 int dotCount = 0;
129 while (*ptr) {
130 if (*ptr == ':')
131 ++colonCount;
132 if (*ptr == '.')
133 ++dotCount;
134 ++ptr;
135 }
136 // IPv4-in-IPv6 addresses are stricter in what they accept
137 if (dotCount != 0 && dotCount != 3)
138 return end;
139
140 memset(address, 0, sizeof address);
141 if (colonCount == 2 && end - begin == 2) // "::"
142 return nullptr;
143
144 // if there's a double colon ("::"), this is how many zeroes it means
145 int zeroWordsToFill;
146 ptr = buffer.data();
147
148 // there are two cases where 8 colons are allowed: at the ends
149 // so test that before the colon-count test
150 if ((ptr[0] == ':' && ptr[1] == ':') ||
151 (ptr[end - begin - 2] == ':' && ptr[end - begin - 1] == ':')) {
152 zeroWordsToFill = 9 - colonCount;
153 } else if (colonCount < 2 || colonCount > 7) {
154 return end;
155 } else {
156 zeroWordsToFill = 8 - colonCount;
157 }
158 if (dotCount)
159 --zeroWordsToFill;
160
161 int pos = 0;
162 while (pos < 15) {
163 if (*ptr == ':') {
164 // empty field, we hope it's "::"
165 if (zeroWordsToFill < 1)
166 return begin + (ptr - buffer.data());
167 if (pos == 0 || pos == colonCount * 2) {
168 if (ptr[0] == '\0' || ptr[1] != ':')
169 return begin + (ptr - buffer.data());
170 ++ptr;
171 }
172 pos += zeroWordsToFill * 2;
173 zeroWordsToFill = 0;
174 ++ptr;
175 continue;
176 }
177
178 auto [ll, used] = qstrntoull(ptr, stop - ptr, 16);
179 quint16 x = ll;
180
181 // Reject malformed fields:
182 // - failed to parse
183 // - too many hex digits
184 if (used <= 0 || used > 4)
185 return begin + (ptr - buffer.data());
186
187 if (ptr[used] == '.') {
188 // this could be an IPv4 address
189 // it's only valid in the last element
190 if (pos != 12)
191 return begin + (ptr - buffer.data());
192
193 IPv4Address ip4;
194 if (!parseIp4Internal(ip4, ptr, false))
195 return begin + (ptr - buffer.data());
196
197 address[12] = ip4 >> 24;
198 address[13] = ip4 >> 16;
199 address[14] = ip4 >> 8;
200 address[15] = ip4;
201 return nullptr;
202 }
203
204 address[pos++] = x >> 8;
205 address[pos++] = x & 0xff;
206
207 if (ptr[used] == '\0')
208 break;
209 if (ptr[used] != ':')
210 return begin + (used + ptr - buffer.data());
211 ptr += used + 1;
212 }
213 return pos == 16 ? nullptr : end;
214}
215
216static inline QChar toHex(uchar c)
217{
218 return QChar::fromLatin1(QtMiscUtils::toHexLower(c));
219}
220
221void toString(QString &appendTo, const IPv6Address address)
222{
223 // the longest IPv6 address possible is:
224 // "1111:2222:3333:4444:5555:6666:255.255.255.255"
225 // however, this function never generates that. The longest it does
226 // generate without an IPv4 address is:
227 // "1111:2222:3333:4444:5555:6666:7777:8888"
228 // and the longest with an IPv4 address is:
229 // "::ffff:255.255.255.255"
230 static const int Ip6AddressMaxLen = sizeof "1111:2222:3333:4444:5555:6666:7777:8888";
231 static const int Ip6WithIp4AddressMaxLen = sizeof "::ffff:255.255.255.255";
232
233 // check for the special cases
234 const quint64 zeroes[] = { 0, 0 };
235 bool embeddedIp4 = false;
236
237 // we consider embedded IPv4 for:
238 // ::ffff:x.x.x.x
239 // ::x.x.x.y except if the x are 0 too
240 if (memcmp(address, zeroes, 10) == 0) {
241 if (address[10] == 0xff && address[11] == 0xff) {
242 embeddedIp4 = true;
243 } else if (address[10] == 0 && address[11] == 0) {
244 if (address[12] != 0 || address[13] != 0 || address[14] != 0) {
245 embeddedIp4 = true;
246 } else if (address[15] == 0) {
247 appendTo.append("::"_L1);
248 return;
249 }
250 }
251 }
252
253 // QString::reserve doesn't shrink, so it's fine to us
254 appendTo.reserve(appendTo.size() +
255 (embeddedIp4 ? Ip6WithIp4AddressMaxLen : Ip6AddressMaxLen));
256
257 // for finding where to place the "::"
258 int zeroRunLength = 0; // in octets
259 int zeroRunOffset = 0; // in octets
260 for (int i = 0; i < 16; i += 2) {
261 if (address[i] == 0 && address[i + 1] == 0) {
262 // found a zero, scan forward to see how many more there are
263 int j;
264 for (j = i; j < 16; j += 2) {
265 if (address[j] != 0 || address[j+1] != 0)
266 break;
267 }
268
269 if (j - i > zeroRunLength) {
270 zeroRunLength = j - i;
271 zeroRunOffset = i;
272 i = j;
273 }
274 }
275 }
276
277 const QChar colon = u':';
278 if (zeroRunLength < 4)
279 zeroRunOffset = -1;
280 else if (zeroRunOffset == 0)
281 appendTo.append(colon);
282
283 for (int i = 0; i < 16; i += 2) {
284 if (i == zeroRunOffset) {
285 appendTo.append(colon);
286 i += zeroRunLength - 2;
287 continue;
288 }
289
290 if (i == 12 && embeddedIp4) {
291 IPv4Address ip4 = address[12] << 24 |
292 address[13] << 16 |
293 address[14] << 8 |
294 address[15];
295 toString(appendTo, ip4);
296 return;
297 }
298
299 if (address[i]) {
300 if (address[i] >> 4) {
301 appendTo.append(toHex(address[i] >> 4));
302 appendTo.append(toHex(address[i] & 0xf));
303 appendTo.append(toHex(address[i + 1] >> 4));
304 appendTo.append(toHex(address[i + 1] & 0xf));
305 } else if (address[i] & 0xf) {
306 appendTo.append(toHex(address[i] & 0xf));
307 appendTo.append(toHex(address[i + 1] >> 4));
308 appendTo.append(toHex(address[i + 1] & 0xf));
309 }
310 } else if (address[i + 1] >> 4) {
311 appendTo.append(toHex(address[i + 1] >> 4));
312 appendTo.append(toHex(address[i + 1] & 0xf));
313 } else {
314 appendTo.append(toHex(address[i + 1] & 0xf));
315 }
316
317 if (i != 14)
318 appendTo.append(colon);
319 }
320}
321
322}
323QT_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)
#define QStringLiteral(str)
Definition qstring.h:1826