| 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 |  | 
| 10 | QT_BEGIN_NAMESPACE | 
| 11 |  | 
| 12 | using namespace Qt::StringLiterals; | 
| 13 |  | 
| 14 | namespace QIPAddressUtils { | 
| 15 |  | 
| 16 | static QString number(quint8 val) | 
| 17 | { | 
| 18 |     QString zero = QStringLiteral("0" ); | 
| 19 |     return val ? qulltoa(l: val, base: 10, zero) : zero; | 
| 20 | } | 
| 21 |  | 
| 22 | typedef QVarLengthArray<char, 64> Buffer; | 
| 23 | static 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(sz: 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 |  | 
| 41 | static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero); | 
| 42 | bool parseIp4(IPv4Address &address, const QChar *begin, const QChar *end) | 
| 43 | { | 
| 44 |     Q_ASSERT(begin != end); | 
| 45 |     Buffer buffer; | 
| 46 |     if (checkedToAscii(buffer, begin, end)) | 
| 47 |         return false; | 
| 48 |  | 
| 49 |     const char *ptr = buffer.data(); | 
| 50 |     return parseIp4Internal(address, ptr, acceptLeadingZero: true); | 
| 51 | } | 
| 52 |  | 
| 53 | static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero) | 
| 54 | { | 
| 55 |     address = 0; | 
| 56 |     int dotCount = 0; | 
| 57 |     const char *const stop = ptr + qstrlen(str: 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(nptr: ptr, size: stop - ptr, base: 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 |  | 
| 94 | void toString(QString &appendTo, IPv4Address address) | 
| 95 | { | 
| 96 |     // reconstructing is easy | 
| 97 |     // use the fast operator% that pre-calculates the size | 
| 98 |     appendTo += number(val: address >> 24) % u'.' | 
| 99 |                 % number(val: address >> 16) % u'.' | 
| 100 |                 % number(val: address >> 8) % u'.' | 
| 101 |                 % number(val: address); | 
| 102 | } | 
| 103 |  | 
| 104 | /*! | 
| 105 |     \internal | 
| 106 |     \since 5.0 | 
| 107 |  | 
| 108 |     Parses one IPv6 address from \a begin to \a end and stores the | 
| 109 |     representation in \a address. Returns null if everything was parsed | 
| 110 |     correctly, or the pointer to the first bad character where parsing failed. | 
| 111 |     If the parsing failed for a reason not related to a particular character, | 
| 112 |     returns \a end. | 
| 113 | */ | 
| 114 | const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end) | 
| 115 | { | 
| 116 |     Q_ASSERT(begin != end); | 
| 117 |     Buffer buffer; | 
| 118 |     const QChar *ret = checkedToAscii(buffer, 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(s: address, c: 0, n: 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(nptr: ptr, size: stop - ptr, base: 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(address&: ip4, ptr, acceptLeadingZero: 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 |  | 
| 215 | static inline QChar toHex(uchar c) | 
| 216 | { | 
| 217 |     return QChar::fromLatin1(c: QtMiscUtils::toHexLower(value: c)); | 
| 218 | } | 
| 219 |  | 
| 220 | void 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(s1: address, s2: zeroes, n: 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(s: "::"_L1 ); | 
| 247 |                 return; | 
| 248 |             } | 
| 249 |         } | 
| 250 |     } | 
| 251 |  | 
| 252 |     // QString::reserve doesn't shrink, so it's fine to us | 
| 253 |     appendTo.reserve(asize: 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(c: colon); | 
| 281 |  | 
| 282 |     for (int i = 0; i < 16; i += 2) { | 
| 283 |         if (i == zeroRunOffset) { | 
| 284 |             appendTo.append(c: 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, address: ip4); | 
| 295 |             return; | 
| 296 |         } | 
| 297 |  | 
| 298 |         if (address[i]) { | 
| 299 |             if (address[i] >> 4) { | 
| 300 |                 appendTo.append(c: toHex(c: address[i] >> 4)); | 
| 301 |                 appendTo.append(c: toHex(c: address[i] & 0xf)); | 
| 302 |                 appendTo.append(c: toHex(c: address[i + 1] >> 4)); | 
| 303 |                 appendTo.append(c: toHex(c: address[i + 1] & 0xf)); | 
| 304 |             } else if (address[i] & 0xf) { | 
| 305 |                 appendTo.append(c: toHex(c: address[i] & 0xf)); | 
| 306 |                 appendTo.append(c: toHex(c: address[i + 1] >> 4)); | 
| 307 |                 appendTo.append(c: toHex(c: address[i + 1] & 0xf)); | 
| 308 |             } | 
| 309 |         } else if (address[i + 1] >> 4) { | 
| 310 |             appendTo.append(c: toHex(c: address[i + 1] >> 4)); | 
| 311 |             appendTo.append(c: toHex(c: address[i + 1] & 0xf)); | 
| 312 |         } else { | 
| 313 |             appendTo.append(c: toHex(c: address[i + 1] & 0xf)); | 
| 314 |         } | 
| 315 |  | 
| 316 |         if (i != 14) | 
| 317 |             appendTo.append(c: colon); | 
| 318 |     } | 
| 319 | } | 
| 320 |  | 
| 321 | } | 
| 322 | QT_END_NAMESPACE | 
| 323 |  |