1/****************************************************************************
2**
3** Copyright (C) 2016 Intel Corporation.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qipaddress_p.h"
41#include "private/qlocale_tools_p.h"
42#include "private/qtools_p.h"
43#include "qvarlengtharray.h"
44
45QT_BEGIN_NAMESPACE
46namespace QIPAddressUtils {
47
48static QString number(quint8 val, int base = 10)
49{
50 QString zero = QStringLiteral("0");
51 return val ? qulltoa(val, base, zero) : zero;
52}
53
54typedef QVarLengthArray<char, 64> Buffer;
55static const QChar *checkedToAscii(Buffer &buffer, const QChar *begin, const QChar *end)
56{
57 const ushort *const ubegin = reinterpret_cast<const ushort *>(begin);
58 const ushort *const uend = reinterpret_cast<const ushort *>(end);
59 const ushort *src = ubegin;
60
61 buffer.resize(uend - ubegin + 1);
62 char *dst = buffer.data();
63
64 while (src != uend) {
65 if (*src >= 0x7f)
66 return reinterpret_cast<const QChar *>(src);
67 *dst++ = *src++;
68 }
69 *dst = '\0';
70 return nullptr;
71}
72
73static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero);
74bool parseIp4(IPv4Address &address, const QChar *begin, const QChar *end)
75{
76 Q_ASSERT(begin != end);
77 Buffer buffer;
78 if (checkedToAscii(buffer, begin, end))
79 return false;
80
81 const char *ptr = buffer.data();
82 return parseIp4Internal(address, ptr, true);
83}
84
85static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero)
86{
87 address = 0;
88 int dotCount = 0;
89 while (dotCount < 4) {
90 if (!acceptLeadingZero && *ptr == '0' &&
91 ptr[1] != '.' && ptr[1] != '\0')
92 return false;
93
94 const char *endptr;
95 bool ok;
96 quint64 ll = qstrtoull(ptr, &endptr, 0, &ok);
97 quint32 x = ll;
98 if (!ok || endptr == ptr || ll != x)
99 return false;
100
101 if (*endptr == '.' || dotCount == 3) {
102 if (x & ~0xff)
103 return false;
104 address <<= 8;
105 } else if (dotCount == 2) {
106 if (x & ~0xffff)
107 return false;
108 address <<= 16;
109 } else if (dotCount == 1) {
110 if (x & ~0xffffff)
111 return false;
112 address <<= 24;
113 }
114 address |= x;
115
116 if (dotCount == 3 && *endptr != '\0')
117 return false;
118 else if (dotCount == 3 || *endptr == '\0')
119 return true;
120 if (*endptr != '.')
121 return false;
122
123 ++dotCount;
124 ptr = endptr + 1;
125 }
126 return false;
127}
128
129void toString(QString &appendTo, IPv4Address address)
130{
131 // reconstructing is easy
132 // use the fast operator% that pre-calculates the size
133 appendTo += number(address >> 24)
134 % QLatin1Char('.')
135 % number(address >> 16)
136 % QLatin1Char('.')
137 % number(address >> 8)
138 % QLatin1Char('.')
139 % number(address);
140}
141
142/*!
143 \internal
144 \since 5.0
145
146 Parses one IPv6 address from \a begin to \a end and stores the
147 representation in \a address. Returns null if everything was parsed
148 correctly, or the pointer to the first bad character where parsing failed.
149 If the parsing failed for a reason not related to a particular character,
150 returns \a end.
151*/
152const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end)
153{
154 Q_ASSERT(begin != end);
155 Buffer buffer;
156 const QChar *ret = checkedToAscii(buffer, begin, end);
157 if (ret)
158 return ret;
159
160 const char *ptr = buffer.data();
161
162 // count the colons
163 int colonCount = 0;
164 int dotCount = 0;
165 while (*ptr) {
166 if (*ptr == ':')
167 ++colonCount;
168 if (*ptr == '.')
169 ++dotCount;
170 ++ptr;
171 }
172 // IPv4-in-IPv6 addresses are stricter in what they accept
173 if (dotCount != 0 && dotCount != 3)
174 return end;
175
176 memset(address, 0, sizeof address);
177 if (colonCount == 2 && end - begin == 2) // "::"
178 return nullptr;
179
180 // if there's a double colon ("::"), this is how many zeroes it means
181 int zeroWordsToFill;
182 ptr = buffer.data();
183
184 // there are two cases where 8 colons are allowed: at the ends
185 // so test that before the colon-count test
186 if ((ptr[0] == ':' && ptr[1] == ':') ||
187 (ptr[end - begin - 2] == ':' && ptr[end - begin - 1] == ':')) {
188 zeroWordsToFill = 9 - colonCount;
189 } else if (colonCount < 2 || colonCount > 7) {
190 return end;
191 } else {
192 zeroWordsToFill = 8 - colonCount;
193 }
194 if (dotCount)
195 --zeroWordsToFill;
196
197 int pos = 0;
198 while (pos < 15) {
199 if (*ptr == ':') {
200 // empty field, we hope it's "::"
201 if (zeroWordsToFill < 1)
202 return begin + (ptr - buffer.data());
203 if (pos == 0 || pos == colonCount * 2) {
204 if (ptr[0] == '\0' || ptr[1] != ':')
205 return begin + (ptr - buffer.data());
206 ++ptr;
207 }
208 pos += zeroWordsToFill * 2;
209 zeroWordsToFill = 0;
210 ++ptr;
211 continue;
212 }
213
214 const char *endptr;
215 bool ok;
216 quint64 ll = qstrtoull(ptr, &endptr, 16, &ok);
217 quint16 x = ll;
218
219 // Reject malformed fields:
220 // - failed to parse
221 // - too many hex digits
222 if (!ok || endptr > ptr + 4)
223 return begin + (ptr - buffer.data());
224
225 if (*endptr == '.') {
226 // this could be an IPv4 address
227 // it's only valid in the last element
228 if (pos != 12)
229 return begin + (ptr - buffer.data());
230
231 IPv4Address ip4;
232 if (!parseIp4Internal(ip4, ptr, false))
233 return begin + (ptr - buffer.data());
234
235 address[12] = ip4 >> 24;
236 address[13] = ip4 >> 16;
237 address[14] = ip4 >> 8;
238 address[15] = ip4;
239 return nullptr;
240 }
241
242 address[pos++] = x >> 8;
243 address[pos++] = x & 0xff;
244
245 if (*endptr == '\0')
246 break;
247 if (*endptr != ':')
248 return begin + (endptr - buffer.data());
249 ptr = endptr + 1;
250 }
251 return pos == 16 ? nullptr : end;
252}
253
254static inline QChar toHex(uchar c)
255{
256 return QChar::fromLatin1(QtMiscUtils::toHexLower(c));
257}
258
259void toString(QString &appendTo, const IPv6Address address)
260{
261 // the longest IPv6 address possible is:
262 // "1111:2222:3333:4444:5555:6666:255.255.255.255"
263 // however, this function never generates that. The longest it does
264 // generate without an IPv4 address is:
265 // "1111:2222:3333:4444:5555:6666:7777:8888"
266 // and the longest with an IPv4 address is:
267 // "::ffff:255.255.255.255"
268 static const int Ip6AddressMaxLen = sizeof "1111:2222:3333:4444:5555:6666:7777:8888";
269 static const int Ip6WithIp4AddressMaxLen = sizeof "::ffff:255.255.255.255";
270
271 // check for the special cases
272 const quint64 zeroes[] = { 0, 0 };
273 bool embeddedIp4 = false;
274
275 // we consider embedded IPv4 for:
276 // ::ffff:x.x.x.x
277 // ::x.x.x.y except if the x are 0 too
278 if (memcmp(address, zeroes, 10) == 0) {
279 if (address[10] == 0xff && address[11] == 0xff) {
280 embeddedIp4 = true;
281 } else if (address[10] == 0 && address[11] == 0) {
282 if (address[12] != 0 || address[13] != 0 || address[14] != 0) {
283 embeddedIp4 = true;
284 } else if (address[15] == 0) {
285 appendTo.append(QLatin1String("::"));
286 return;
287 }
288 }
289 }
290
291 // QString::reserve doesn't shrink, so it's fine to us
292 appendTo.reserve(appendTo.size() +
293 (embeddedIp4 ? Ip6WithIp4AddressMaxLen : Ip6AddressMaxLen));
294
295 // for finding where to place the "::"
296 int zeroRunLength = 0; // in octets
297 int zeroRunOffset = 0; // in octets
298 for (int i = 0; i < 16; i += 2) {
299 if (address[i] == 0 && address[i + 1] == 0) {
300 // found a zero, scan forward to see how many more there are
301 int j;
302 for (j = i; j < 16; j += 2) {
303 if (address[j] != 0 || address[j+1] != 0)
304 break;
305 }
306
307 if (j - i > zeroRunLength) {
308 zeroRunLength = j - i;
309 zeroRunOffset = i;
310 i = j;
311 }
312 }
313 }
314
315 const QChar colon = u':';
316 if (zeroRunLength < 4)
317 zeroRunOffset = -1;
318 else if (zeroRunOffset == 0)
319 appendTo.append(colon);
320
321 for (int i = 0; i < 16; i += 2) {
322 if (i == zeroRunOffset) {
323 appendTo.append(colon);
324 i += zeroRunLength - 2;
325 continue;
326 }
327
328 if (i == 12 && embeddedIp4) {
329 IPv4Address ip4 = address[12] << 24 |
330 address[13] << 16 |
331 address[14] << 8 |
332 address[15];
333 toString(appendTo, ip4);
334 return;
335 }
336
337 if (address[i]) {
338 if (address[i] >> 4) {
339 appendTo.append(toHex(address[i] >> 4));
340 appendTo.append(toHex(address[i] & 0xf));
341 appendTo.append(toHex(address[i + 1] >> 4));
342 appendTo.append(toHex(address[i + 1] & 0xf));
343 } else if (address[i] & 0xf) {
344 appendTo.append(toHex(address[i] & 0xf));
345 appendTo.append(toHex(address[i + 1] >> 4));
346 appendTo.append(toHex(address[i + 1] & 0xf));
347 }
348 } else if (address[i + 1] >> 4) {
349 appendTo.append(toHex(address[i + 1] >> 4));
350 appendTo.append(toHex(address[i + 1] & 0xf));
351 } else {
352 appendTo.append(toHex(address[i + 1] & 0xf));
353 }
354
355 if (i != 14)
356 appendTo.append(colon);
357 }
358}
359
360}
361QT_END_NAMESPACE
362