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 
45 QT_BEGIN_NAMESPACE
46 namespace QIPAddressUtils {
47 
number(quint8 val,int base=10)48 static QString number(quint8 val, int base = 10)
49 {
50     QChar zero(0x30);
51     return val ? qulltoa(val, base, zero) : zero;
52 }
53 
54 typedef QVarLengthArray<char, 64> Buffer;
checkedToAscii(Buffer & buffer,const QChar * begin,const QChar * end)55 static 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 
73 static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero);
parseIp4(IPv4Address & address,const QChar * begin,const QChar * end)74 bool 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 
parseIp4Internal(IPv4Address & address,const char * ptr,bool acceptLeadingZero)85 static 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 
toString(QString & appendTo,IPv4Address address)129 void 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 */
parseIp6(IPv6Address & address,const QChar * begin,const QChar * end)152 const 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 
toHex(uchar c)254 static inline QChar toHex(uchar c)
255 {
256     return QChar::fromLatin1(QtMiscUtils::toHexLower(c));
257 }
258 
toString(QString & appendTo,const IPv6Address address)259 void 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 }
361 QT_END_NAMESPACE
362