#include "networkUtilities.h" #ifdef Q_OS_WIN #include #include #include #include #include #include #include #include #include #include #include "qendian.h" #include #endif #ifdef Q_OS_LINUX #include #include #include #include #include #include #include #endif #if defined(Q_OS_MAC) && !defined(Q_OS_IOS) #include #include #include #include #include #include #endif #include #include QRegularExpression NetworkUtilities::ipAddressRegExp() { return QRegularExpression("^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}$"); } QRegularExpression NetworkUtilities::ipAddressPortRegExp() { return QRegularExpression("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}" "(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(\\:[0-9]{1,5}){0,1}$"); } QRegExp NetworkUtilities::ipAddressWithSubnetRegExp() { return QRegExp("(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}" "(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(\\/[0-9]{1,2}){0,1}"); } QRegExp NetworkUtilities::ipNetwork24RegExp() { return QRegExp("^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}" "0$"); } QRegExp NetworkUtilities::ipPortRegExp() { return QRegExp("^()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$"); } QRegExp NetworkUtilities::domainRegExp() { return QRegExp("(((?!\\-))(xn\\-\\-)?[a-z0-9\\-_]{0,61}[a-z0-9]{1,1}\\.)*(xn\\-\\-)?([a-z0-9\\-]{1,61}|[a-z0-" "9\\-]{1,30})\\.[a-z]{2,}"); } QString NetworkUtilities::netMaskFromIpWithSubnet(const QString ip) { if (!ip.contains("/")) return "255.255.255.255"; bool ok; int prefix = ip.split("/").at(1).toInt(&ok); if (!ok) return "255.255.255.255"; unsigned long mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF; return QString("%1.%2.%3.%4").arg(mask >> 24).arg((mask >> 16) & 0xFF).arg((mask >> 8) & 0xFF).arg(mask & 0xFF); } QString NetworkUtilities::ipAddressFromIpWithSubnet(const QString ip) { if (ip.count(".") != 3) return ""; return ip.split("/").first(); } QStringList NetworkUtilities::summarizeRoutes(const QStringList &ips, const QString cidr) { // QMap // QHostAddress // QMap subnets; // <"a.b", > // for (const QString &ip : ips) { // if (ip.count(".") != 3) continue; // const QStringList &parts = ip.split("."); // subnets[parts.at(0) + "." + parts.at(1)].append(ip); // } return QStringList(); } QString NetworkUtilities::getIPAddress(const QString &host) { QHostAddress address(host); if (QAbstractSocket::IPv4Protocol == address.protocol()) { return host; } else if (QAbstractSocket::IPv6Protocol == address.protocol()) { return host; } QList addresses = QHostInfo::fromName(host).addresses(); if (!addresses.isEmpty()) { return addresses.first().toString(); } qDebug() << "Unable to resolve address for " << host; return ""; } QString NetworkUtilities::getStringBetween(const QString &s, const QString &a, const QString &b) { int ap = s.indexOf(a), bp = s.indexOf(b, ap + a.length()); if (ap < 0 || bp < 0) return QString(); ap += a.length(); if (bp - ap <= 0) return QString(); return s.mid(ap, bp - ap).trimmed(); } bool NetworkUtilities::checkIPv4Format(const QString &ip) { if (ip.isEmpty()) return false; int count = ip.count("."); if (count != 3) return false; QHostAddress addr(ip); return (addr.protocol() == QAbstractSocket::NetworkLayerProtocol::IPv4Protocol); } bool NetworkUtilities::checkIpSubnetFormat(const QString &ip) { if (!ip.contains("/")) return checkIPv4Format(ip); QStringList parts = ip.split("/"); if (parts.size() != 2) return false; bool ok; int subnet = parts.at(1).toInt(&ok); if (subnet >= 0 && subnet <= 32 && ok) return checkIPv4Format(parts.at(0)); else return false; } // static int NetworkUtilities::AdapterIndexTo(const QHostAddress& dst) { #ifdef Q_OS_WIN qDebug() << "Getting Current Internet Adapter that routes to" << dst.toString(); quint32_be ipBigEndian; quint32 ip = dst.toIPv4Address(); qToBigEndian(ip, &ipBigEndian); _MIB_IPFORWARDROW routeInfo; auto result = GetBestRoute(ipBigEndian, 0, &routeInfo); if (result != NO_ERROR) { return -1; } auto adapter = QNetworkInterface::interfaceFromIndex(routeInfo.dwForwardIfIndex); qDebug() << "Internet Adapter:" << adapter.name(); return routeInfo.dwForwardIfIndex; #endif return 0; } bool NetworkUtilities::checkIpv6Enabled() { #ifdef Q_OS_WIN QSettings RegHLM("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters", QSettings::NativeFormat); int ret = RegHLM.value("DisabledComponents", 0).toInt(); qDebug() << "Check for Windows disabled IPv6 return " << ret; return (ret != 255); #endif return true; } #ifdef Q_OS_WIN DWORD GetAdaptersAddressesWrapper(const ULONG Family, const ULONG Flags, const PVOID Reserved, _Out_ PIP_ADAPTER_ADDRESSES& pAdapterAddresses) { DWORD dwRetVal = 0; int iter = 0; constexpr int max_iter = 3; ULONG AdapterAddressesLen = 15000; do { // xassert2(pAdapterAddresses == nullptr); pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)malloc(AdapterAddressesLen); if (pAdapterAddresses == nullptr) { qDebug() << "can not malloc" << AdapterAddressesLen << "bytes"; return ERROR_OUTOFMEMORY; } dwRetVal = GetAdaptersAddresses(Family, Flags, NULL, pAdapterAddresses, &AdapterAddressesLen); if (dwRetVal == ERROR_BUFFER_OVERFLOW) { free(pAdapterAddresses); pAdapterAddresses = nullptr; } else { break; } iter++; } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (iter < max_iter)); if (dwRetVal != NO_ERROR) { qDebug() << "Family: " << Family << ", Flags: " << Flags << " AdapterAddressesLen: " << AdapterAddressesLen << ", dwRetVal:" << dwRetVal << ", iter: " << iter; if (pAdapterAddresses) { free(pAdapterAddresses); pAdapterAddresses = nullptr; } } return dwRetVal; } #endif QString NetworkUtilities::getGatewayAndIface() { #ifdef Q_OS_WIN constexpr int BUFF_LEN = 100; char buff[BUFF_LEN] = {'\0'}; QString result; PIP_ADAPTER_ADDRESSES pAdapterAddresses = nullptr; DWORD dwRetVal = GetAdaptersAddressesWrapper(AF_INET, GAA_FLAG_INCLUDE_GATEWAYS, NULL, pAdapterAddresses); if (dwRetVal != NO_ERROR) { qDebug() << "ipv4 stack detect GetAdaptersAddresses failed."; return ""; } PIP_ADAPTER_ADDRESSES pCurAddress = pAdapterAddresses; while (pCurAddress) { PIP_ADAPTER_GATEWAY_ADDRESS_LH gateway = pCurAddress->FirstGatewayAddress; if (gateway) { SOCKET_ADDRESS gateway_address = gateway->Address; if (gateway->Address.lpSockaddr->sa_family == AF_INET) { sockaddr_in* sa_in = (sockaddr_in*)gateway->Address.lpSockaddr; QString gw = inet_ntop(AF_INET, &(sa_in->sin_addr), buff, BUFF_LEN); qDebug() << "gateway IPV4:" << gw; struct sockaddr_in addr; if (inet_pton(AF_INET, buff, &addr.sin_addr) == 1) { qDebug() << "this is true v4 !"; result = gw; } } } pCurAddress = pCurAddress->Next; } free(pAdapterAddresses); return result; #endif #ifdef Q_OS_LINUX constexpr int BUFFER_SIZE = 100; int received_bytes = 0, msg_len = 0, route_attribute_len = 0; int sock = -1, msgseq = 0; struct nlmsghdr *nlh, *nlmsg; struct rtmsg *route_entry; // This struct contain route attributes (route type) struct rtattr *route_attribute; char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE]; char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE]; char *ptr = buffer; struct timeval tv; if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { perror("socket failed"); return ""; } memset(msgbuf, 0, sizeof(msgbuf)); memset(gateway_address, 0, sizeof(gateway_address)); memset(interface, 0, sizeof(interface)); memset(buffer, 0, sizeof(buffer)); /* point the header and the msg structure pointers into the buffer */ nlmsg = (struct nlmsghdr *)msgbuf; /* Fill in the nlmsg header*/ nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet. nlmsg->nlmsg_pid = getpid(); // PID of process sending the request. /* 1 Sec Timeout to avoid stall */ tv.tv_sec = 1; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); /* send msg */ if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) { perror("send failed"); return ""; } /* receive response */ do { received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); if (received_bytes < 0) { perror("Error in recv"); return ""; } nlh = (struct nlmsghdr *) ptr; /* Check if the header is valid */ if((NLMSG_OK(nlmsg, received_bytes) == 0) || (nlmsg->nlmsg_type == NLMSG_ERROR)) { perror("Error in received packet"); return ""; } /* If we received all data break */ if (nlh->nlmsg_type == NLMSG_DONE) break; else { ptr += received_bytes; msg_len += received_bytes; } /* Break if its not a multi part message */ if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) break; } while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); /* parse response */ for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) { /* Get the route data */ route_entry = (struct rtmsg *) NLMSG_DATA(nlh); /* We are just interested in main routing table */ if (route_entry->rtm_table != RT_TABLE_MAIN) continue; route_attribute = (struct rtattr *) RTM_RTA(route_entry); route_attribute_len = RTM_PAYLOAD(nlh); /* Loop through all attributes */ for ( ; RTA_OK(route_attribute, route_attribute_len); route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) { switch(route_attribute->rta_type) { case RTA_OIF: if_indextoname(*(int *)RTA_DATA(route_attribute), interface); break; case RTA_GATEWAY: inet_ntop(AF_INET, RTA_DATA(route_attribute), gateway_address, sizeof(gateway_address)); break; default: break; } } if ((*gateway_address) && (*interface)) { qDebug() << "Gateway " << gateway_address << " for interface " << interface; break; } } close(sock); return gateway_address; #endif #if defined(Q_OS_MAC) && !defined(Q_OS_IOS) QString gateway; int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY}; int afinet_type[] = {AF_INET, AF_INET6}; for (int ip_type = 0; ip_type <= 1; ip_type++) { mib[3] = afinet_type[ip_type]; size_t needed = 0; if (sysctl(mib, sizeof(mib) / sizeof(int), nullptr, &needed, nullptr, 0) < 0) return ""; char* buf; if ((buf = new char[needed]) == 0) return ""; if (sysctl(mib, sizeof(mib) / sizeof(int), buf, &needed, nullptr, 0) < 0) { qDebug() << "sysctl: net.route.0.0.dump"; delete[] buf; return gateway; } struct rt_msghdr* rt; for (char* p = buf; p < buf + needed; p += rt->rtm_msglen) { rt = reinterpret_cast(p); struct sockaddr* sa = reinterpret_cast(rt + 1); struct sockaddr* sa_tab[RTAX_MAX]; for (int i = 0; i < RTAX_MAX; i++) { if (rt->rtm_addrs & (1 << i)) { sa_tab[i] = sa; sa = reinterpret_cast( reinterpret_cast(sa) + ((sa->sa_len) > 0 ? (1 + (((sa->sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); } else { sa_tab[i] = nullptr; } } if (((rt->rtm_addrs & (RTA_DST | RTA_GATEWAY)) == (RTA_DST | RTA_GATEWAY)) && sa_tab[RTAX_DST]->sa_family == afinet_type[ip_type] && sa_tab[RTAX_GATEWAY]->sa_family == afinet_type[ip_type]) { if (afinet_type[ip_type] == AF_INET) { if ((reinterpret_cast(sa_tab[RTAX_DST]))->sin_addr.s_addr == 0) { char dstStr4[INET_ADDRSTRLEN]; char srcStr4[INET_ADDRSTRLEN]; memcpy(srcStr4, &(reinterpret_cast(sa_tab[RTAX_GATEWAY]))->sin_addr, sizeof(struct in_addr)); if (inet_ntop(AF_INET, srcStr4, dstStr4, INET_ADDRSTRLEN) != nullptr) gateway = dstStr4; break; } } else if (afinet_type[ip_type] == AF_INET6) { if ((reinterpret_cast(sa_tab[RTAX_DST]))->sin_addr.s_addr == 0) { char dstStr6[INET6_ADDRSTRLEN]; char srcStr6[INET6_ADDRSTRLEN]; memcpy(srcStr6, &(reinterpret_cast(sa_tab[RTAX_GATEWAY]))->sin6_addr, sizeof(struct in6_addr)); if (inet_ntop(AF_INET6, srcStr6, dstStr6, INET6_ADDRSTRLEN) != nullptr) gateway = dstStr6; break; } } } } free(buf); } return gateway; #endif }