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