|
4 | 4 |
|
5 | 5 | #include "windowssplittunnel.h"
|
6 | 6 |
|
7 |
| -#include <qassert.h> |
| 7 | +#include <WS2tcpip.h> |
| 8 | +#include <iphlpapi.h> |
| 9 | +#include <windows.h> |
| 10 | +#include <winsock2.h> |
| 11 | +#include <ws2ipdef.h> |
8 | 12 |
|
9 | 13 | #include <memory>
|
10 | 14 |
|
|
17 | 21 | #include "windowsfirewall.h"
|
18 | 22 |
|
19 | 23 | #define PSAPI_VERSION 2
|
20 |
| -#include <Windows.h> |
21 | 24 | #include <psapi.h>
|
22 | 25 |
|
23 | 26 | #include <QCoreApplication>
|
24 | 27 | #include <QFileInfo>
|
25 | 28 | #include <QNetworkInterface>
|
26 | 29 | #include <QScopeGuard>
|
| 30 | +#include <QtEndian> |
27 | 31 |
|
28 | 32 | #pragma region
|
29 | 33 |
|
@@ -341,7 +345,7 @@ bool WindowsSplitTunnel::start(int inetAdapterIndex) {
|
341 | 345 |
|
342 | 346 | auto config = generateIPConfiguration(inetAdapterIndex);
|
343 | 347 | if (config.empty()) {
|
344 |
| - logger.error() << "Failed to set Network Config"; |
| 348 | + logger.error() << "Failed to generate Network Config"; |
345 | 349 | return false;
|
346 | 350 | }
|
347 | 351 | auto ok = DeviceIoControl(m_driver, IOCTL_REGISTER_IP_ADDRESSES, &config[0],
|
@@ -468,33 +472,76 @@ std::vector<std::byte> WindowsSplitTunnel::generateIPConfiguration(
|
468 | 472 | }
|
469 | 473 | bool WindowsSplitTunnel::getAddress(int adapterIndex, IN_ADDR* out_ipv4,
|
470 | 474 | IN6_ADDR* out_ipv6) {
|
471 |
| - QNetworkInterface target = |
472 |
| - QNetworkInterface::interfaceFromIndex(adapterIndex); |
473 |
| - logger.debug() << "Getting adapter info for:" << target.humanReadableName(); |
| 475 | + MIB_UNICASTIPADDRESS_TABLE* table; |
| 476 | + DWORD result = GetUnicastIpAddressTable(AF_UNSPEC, &table); |
| 477 | + if (result != NO_ERROR) { |
| 478 | + logger.warning() << "GetUnicastIpAddressTable() failed:" |
| 479 | + << WindowsUtils::getErrorMessage(result); |
| 480 | + return false; |
| 481 | + } |
| 482 | + auto guard = qScopeGuard([table]() { FreeMibTable(table); }); |
| 483 | + |
| 484 | + // Find the best unicast addresses on this interface. |
| 485 | + const MIB_UNICASTIPADDRESS_ROW* bestIpv4 = nullptr; |
| 486 | + const MIB_UNICASTIPADDRESS_ROW* bestIpv6 = nullptr; |
| 487 | + for (ULONG i = 0; i < table->NumEntries; i++) { |
| 488 | + const MIB_UNICASTIPADDRESS_ROW* row = &table->Table[i]; |
| 489 | + if (row->InterfaceIndex != adapterIndex) { |
| 490 | + continue; |
| 491 | + } |
| 492 | + if (row->SkipAsSource) { |
| 493 | + continue; |
| 494 | + } |
| 495 | + |
| 496 | + if (row->Address.si_family == AF_INET) { |
| 497 | + // Check IPv4 addresses |
| 498 | + quint32 rawAddr = row->Address.Ipv4.sin_addr.s_addr; |
| 499 | + QHostAddress addr(qFromBigEndian<quint32>(rawAddr)); |
| 500 | + if (!addr.isGlobal()) { |
| 501 | + continue; |
| 502 | + } |
| 503 | + // Prefer the address with the highest DAD state. |
| 504 | + if ((bestIpv4 != nullptr) && (bestIpv4->DadState >= row->DadState)) { |
| 505 | + continue; |
| 506 | + } |
| 507 | + bestIpv4 = row; |
| 508 | + } else if (row->Address.si_family == AF_INET6) { |
| 509 | + QHostAddress addr(row->Address.Ipv6.sin6_addr.s6_addr); |
| 510 | + // Check IPv6 addresses |
| 511 | + if (!addr.isGlobal()) { |
| 512 | + continue; |
| 513 | + } |
| 514 | + |
| 515 | + if (!bestIpv6) { |
| 516 | + bestIpv6 = row; |
| 517 | + continue; |
| 518 | + } |
| 519 | + QHostAddress other(bestIpv6->Address.Ipv6.sin6_addr.s6_addr); |
474 | 520 |
|
475 |
| - auto get = [&target](QAbstractSocket::NetworkLayerProtocol protocol) { |
476 |
| - for (auto address : target.addressEntries()) { |
477 |
| - if (address.ip().protocol() != protocol) { |
| 521 | + // Ignore site-local addresses if a global address is already known. |
| 522 | + if (addr.isUniqueLocalUnicast() && !other.isUniqueLocalUnicast()) { |
478 | 523 | continue;
|
479 | 524 | }
|
480 |
| - return address.ip().toString().toStdWString(); |
| 525 | + // Prefer the address with the highest DAD state. |
| 526 | + if ((bestIpv6 != nullptr) && (bestIpv6->DadState >= row->DadState)) { |
| 527 | + continue; |
| 528 | + } |
| 529 | + bestIpv6 = row; |
481 | 530 | }
|
482 |
| - return std::wstring{}; |
483 |
| - }; |
484 |
| - auto ipv4 = get(QAbstractSocket::IPv4Protocol); |
485 |
| - auto ipv6 = get(QAbstractSocket::IPv6Protocol); |
| 531 | + } |
486 | 532 |
|
487 |
| - if (InetPtonW(AF_INET, ipv4.c_str(), out_ipv4) != 1) { |
488 |
| - logger.debug() << "Ipv4 Conversation error" << WSAGetLastError(); |
| 533 | + // An IPv4 address is required for split tunnelling. |
| 534 | + if (bestIpv4) { |
| 535 | + out_ipv4->s_addr = bestIpv4->Address.Ipv4.sin_addr.s_addr; |
| 536 | + } else { |
489 | 537 | return false;
|
490 | 538 | }
|
491 |
| - if (ipv6.empty()) { |
| 539 | + |
| 540 | + // Output the IPv6 address, if any. |
| 541 | + if (bestIpv6) { |
| 542 | + std::memcpy(out_ipv6, &bestIpv6->Address.Ipv6.sin6_addr, sizeof(IN6_ADDR)); |
| 543 | + } else { |
492 | 544 | std::memset(out_ipv6, 0x00, sizeof(IN6_ADDR));
|
493 |
| - return true; |
494 |
| - } |
495 |
| - if (InetPtonW(AF_INET6, ipv6.c_str(), out_ipv6) != 1) { |
496 |
| - logger.debug() << "Ipv6 Conversation error" << WSAGetLastError(); |
497 |
| - return false; |
498 | 545 | }
|
499 | 546 | return true;
|
500 | 547 | }
|
|
0 commit comments