File indexing completed on 2024-04-14 14:10:27

0001 /*
0002     SPDX-FileCopyrightText: 2016 Alex Spataru
0003     SPDX-License-Identifier: MIT
0004 */
0005 
0006 #include "qMDNS.h"
0007 
0008 #include <QHostInfo>
0009 #include <QUdpSocket>
0010 #include <QHostAddress>
0011 #include <QNetworkInterface>
0012 
0013 #ifdef Q_OS_LINUX
0014 #include <sys/socket.h>
0015 #endif
0016 
0017 #include "kstars_debug.h"
0018 /*
0019  * DNS port and mutlicast addresses
0020  */
0021 const quint16 MDNS_PORT = 5353;
0022 const QHostAddress IPV6_ADDRESS = QHostAddress ("FF02::FB");
0023 const QHostAddress IPV4_ADDRESS = QHostAddress ("224.0.0.251");
0024 
0025 /*
0026  * mDNS/DNS operation flags
0027  */
0028 const quint16 kQR_Query       = 0x0000;
0029 const quint16 kQR_Response    = 0x8000;
0030 const quint16 kRecordA        = 0x0001;
0031 const quint16 kRecordAAAA     = 0x001C;
0032 const quint16 kNsecType       = 0x002F;
0033 const quint16 kFQDN_Separator = 0x0000;
0034 const quint16 kFQDN_Length    = 0xC00C;
0035 const quint16 kIN_BitFlush    = 0x8001;
0036 const quint16 kIN_Normal      = 0x0001;
0037 
0038 /*
0039  * DNS query properties
0040  */
0041 const quint16 kQuery_QDCOUNT = 0x02;
0042 const quint16 kQuery_ANCOUNT = 0x00;
0043 const quint16 kQuery_NSCOUNT = 0x00;
0044 const quint16 kQuery_ARCOUNT = 0x00;
0045 
0046 /*
0047  * DNS response properties
0048  */
0049 const quint16 kResponse_QDCOUNT = 0x00;
0050 const quint16 kResponse_ANCOUNT = 0x01;
0051 const quint16 kResponse_NSCOUNT = 0x00;
0052 const quint16 kResponse_ARCOUNT = 0x02;
0053 
0054 /* Packet constants */
0055 const int MIN_LENGTH = 13;
0056 const int IPI_LENGTH = 10;
0057 const int IP4_LENGTH = IPI_LENGTH + 4;
0058 const int IP6_LENGTH = IPI_LENGTH + 16;
0059 
0060 /**
0061  * Encodes the 16-bit \a number as two 8-bit numbers in a byte array
0062  */
0063 QByteArray ENCODE_16_BIT (quint16 number)
0064 {
0065     QByteArray data;
0066     data.append ((number & 0xff00) >> 8);
0067     data.append ((number & 0xff));
0068     return data;
0069 }
0070 
0071 /**
0072  * Encodes the 32-bit \a number as four 8-bit numbers
0073  */
0074 QByteArray ENCODE_32_BIT (quint32 number)
0075 {
0076     QByteArray data;
0077     data.append ((number & 0xff000000UL) >> 24);
0078     data.append ((number & 0x00ff0000UL) >> 16);
0079     data.append ((number & 0x0000ff00UL) >>  8);
0080     data.append ((number & 0x000000ffUL));
0081     return data;
0082 }
0083 
0084 /**
0085  * Obtains the 16-bit number stored in the \a upper and \a lower 8-bit numbers
0086  */
0087 quint16 DECODE_16_BIT (quint8 upper, quint8 lower)
0088 {
0089     return (quint16) ((upper << 8) | lower);
0090 }
0091 
0092 /**
0093  * Binds the given \a socket to the given \a address and \a port.
0094  * Under GNU/Linux, this function implements a workaround of QTBUG-33419.
0095  */
0096 bool BIND (QUdpSocket* socket, const QHostAddress &address, const int port)
0097 {
0098     if (!socket)
0099         return false;
0100 
0101 #ifdef Q_OS_LINUX
0102     int reuse = 1;
0103     int domain = PF_UNSPEC;
0104 
0105     if (address.protocol() == QAbstractSocket::IPv4Protocol)
0106         domain = PF_INET;
0107     else if (address.protocol() == QAbstractSocket::IPv6Protocol)
0108         domain = PF_INET6;
0109 
0110     socket->setSocketDescriptor (::socket (domain, SOCK_DGRAM, 0),
0111                                  QUdpSocket::UnconnectedState);
0112 
0113     setsockopt (socket->socketDescriptor(), SOL_SOCKET, SO_REUSEADDR,
0114                 &reuse, sizeof (reuse));
0115 #endif
0116 
0117     return socket->bind (address, port,
0118                          QUdpSocket::ShareAddress |
0119                          QUdpSocket::ReuseAddressHint);
0120 }
0121 
0122 qMDNS::qMDNS()
0123 {
0124     /* Set default TTL to 4500 seconds */
0125     m_ttl = 4500;
0126 
0127     /* Initialize sockets */
0128     m_IPv4Socket = new QUdpSocket (this);
0129     m_IPv6Socket = new QUdpSocket (this);
0130 
0131     /* Read and interpret data received from mDNS group */
0132     connect (m_IPv4Socket, &QUdpSocket::readyRead, this, &qMDNS::onReadyRead);
0133     connect (m_IPv6Socket, &QUdpSocket::readyRead, this, &qMDNS::onReadyRead);
0134 
0135     /* Bind the sockets to the mDNS multicast group */
0136     if (BIND (m_IPv4Socket, QHostAddress::AnyIPv4, MDNS_PORT))
0137         m_IPv4Socket->joinMulticastGroup (IPV4_ADDRESS);
0138     if (BIND (m_IPv6Socket, QHostAddress::AnyIPv6, MDNS_PORT))
0139         m_IPv6Socket->joinMulticastGroup (IPV6_ADDRESS);
0140 }
0141 
0142 qMDNS::~qMDNS()
0143 {
0144     delete m_IPv4Socket;
0145     delete m_IPv6Socket;
0146 }
0147 
0148 /**
0149  * Returns the only running instance of this class
0150  */
0151 qMDNS* qMDNS::getInstance()
0152 {
0153     static qMDNS instance;
0154     return &instance;
0155 }
0156 
0157 /**
0158  * Returns the mDNS name assigned to the client computer
0159  */
0160 QString qMDNS::hostName() const
0161 {
0162     return m_hostName;
0163 }
0164 
0165 /**
0166  * Ensures that the given \a string is a valid mDNS/DNS address.
0167  */
0168 QString qMDNS::getAddress (const QString &string)
0169 {
0170     QString address = string;
0171 
0172     if (!string.endsWith (".local") && !string.contains ("."))
0173         address = string + ".local";
0174 
0175     if (string.endsWith ("."))
0176         return "";
0177 
0178     return address;
0179 }
0180 
0181 /**
0182  * Changes the TTL send to other computers in the mDNS network
0183  */
0184 void qMDNS::setTTL (const quint32 ttl)
0185 {
0186     m_ttl = ttl;
0187 }
0188 
0189 /**
0190  * Performs a mDNS lookup to find the given host \a name.
0191  * If \a preferIPv6 is set to \c true, then this function will generate a
0192  * packet that requests an AAAA-type Resource Record instead of an A-type
0193  * Resource Record.
0194  */
0195 void qMDNS::lookup (const QString &name)
0196 {
0197     /* The host name is empty, abort lookup */
0198     if (name.isEmpty())
0199     {
0200         qCWarning(KSTARS) << Q_FUNC_INFO << "Empty host name specified";
0201         return;
0202     }
0203 
0204     qCInfo(KSTARS) << "Starting lookup for service" << name;
0205 
0206     m_serviceName = name;
0207 
0208     /* Ensure that we host name is a valid DNS address */
0209     QString address = getAddress (name);
0210     if (address.isEmpty())
0211         return;
0212 
0213     /* Check if we are dealing with a normal DNS address */
0214     if (!address.endsWith (".local", Qt::CaseInsensitive))
0215     {
0216         QHostInfo::lookupHost (address, this, SIGNAL (hostFound(QHostInfo)));
0217         return;
0218     }
0219 
0220     /* Perform a mDNS lookup */
0221     else
0222     {
0223         QByteArray data;
0224 
0225         /* Get the host name and domain */
0226         QString host = address.split (".").first();
0227         QString domain = address.split (".").last();
0228 
0229         /* Check that domain length is valid */
0230         if (host.length() > 255)
0231         {
0232             qWarning() << Q_FUNC_INFO << host << "is too long!";
0233             return;
0234         }
0235 
0236         /* Create header & flags */
0237         data.append (ENCODE_16_BIT (0));
0238         data.append (ENCODE_16_BIT (kQR_Query));
0239         data.append (ENCODE_16_BIT (kQuery_QDCOUNT));
0240         data.append (ENCODE_16_BIT (kQuery_ANCOUNT));
0241         data.append (ENCODE_16_BIT (kQuery_NSCOUNT));
0242         data.append (ENCODE_16_BIT (kQuery_ARCOUNT));
0243 
0244         /* Add name data */
0245         data.append (host.length());
0246         data.append (host.toUtf8());
0247 
0248         /* Add domain data */
0249         data.append (domain.length());
0250         data.append (domain.toUtf8());
0251 
0252         /* Add FQDN/TLD separator */
0253         data.append ((char) kFQDN_Separator);
0254 
0255         /* Add IPv4 record type */
0256         data.append (ENCODE_16_BIT (kRecordA));
0257         data.append (ENCODE_16_BIT (kIN_Normal));
0258 
0259         /* Add FQDN length */
0260         data.append (ENCODE_16_BIT (kFQDN_Length));
0261 
0262         /* Add IPv6 record type */
0263         data.append (ENCODE_16_BIT (kRecordAAAA));
0264         data.append (ENCODE_16_BIT (kIN_Normal));
0265 
0266         /* Send the datagram */
0267         sendPacket (data);
0268     }
0269 }
0270 
0271 /**
0272  * Changes the host name of the client computer
0273  */
0274 void qMDNS::setHostName (const QString &name)
0275 {
0276     if (name.contains (".") && !name.endsWith (".local"))
0277     {
0278         qWarning() << "Invalid domain name";
0279         return;
0280     }
0281 
0282     m_hostName = getAddress (name);
0283 }
0284 
0285 /**
0286  * Called when we receive data from a mDNS client on the network.
0287  */
0288 void qMDNS::onReadyRead()
0289 {
0290     QByteArray data;
0291     QUdpSocket* socket = qobject_cast<QUdpSocket*> (sender());
0292 
0293     /* Read data from the socket */
0294     if (socket)
0295     {
0296         while (socket->hasPendingDatagrams())
0297         {
0298             data.resize (socket->pendingDatagramSize());
0299             socket->readDatagram (data.data(), data.size());
0300         }
0301     }
0302 
0303     /* Packet is a valid mDNS datagram */
0304     if (data.length() > MIN_LENGTH)
0305     {
0306         quint16 flag = DECODE_16_BIT (data.at (2), data.at (3));
0307 
0308         if (flag == kQR_Query)
0309             readQuery (data);
0310 
0311         else if (flag >= kQR_Response)
0312             readResponse (data);
0313     }
0314 }
0315 
0316 /**
0317  * Reads the given query \a data and instructs the class to send a response
0318  * packet if the query is looking for the host name assigned to this computer.
0319  */
0320 void qMDNS::readQuery (const QByteArray &data)
0321 {
0322     /* Query packet is invalid */
0323     if (data.length() < MIN_LENGTH)
0324         return;
0325 
0326     /* Get the lengths of the host name and domain */
0327     int n = 12;
0328     int hostLength = data.at (n);
0329     int domainLength = data.at (n + hostLength + 1);
0330 
0331     /* Read the host name until we stumble with the domain length character */
0332     QString name;
0333     int h = n + 1;
0334     while (data.at (h) != (char) domainLength)
0335     {
0336         name.append (data.at (h));
0337         ++h;
0338     }
0339 
0340     /* Read domain length until we stumble with the FQDN/TLD separator */
0341     QString domain;
0342     int d = n + hostLength + 2;
0343     while (data.at (d) != kFQDN_Separator)
0344     {
0345         domain.append (data.at (d));
0346         ++d;
0347     }
0348 
0349     /* Construct the full host name (name + domain) */
0350     QString host = getAddress (name + "." + domain);
0351 
0352     /* The query packet wants to know more about us */
0353     if (host.toLower() == hostName().toLower())
0354         sendResponse (DECODE_16_BIT (data.at (0), data.at (1)));
0355 }
0356 
0357 /**
0358  * Sends the given \a data to both the IPv4 and IPv6 mDNS multicast groups
0359  */
0360 void qMDNS::sendPacket (const QByteArray &data)
0361 {
0362     if (!data.isEmpty())
0363     {
0364         m_IPv4Socket->writeDatagram (data, IPV4_ADDRESS, MDNS_PORT);
0365         m_IPv6Socket->writeDatagram (data, IPV6_ADDRESS, MDNS_PORT);
0366     }
0367 }
0368 
0369 /**
0370  * Reads the given \a data of a response packet and obtains:
0371  * - The remote host name
0372  * - The remote IPv4
0373  * - The remote IPv6
0374  */
0375 void qMDNS::readResponse (const QByteArray &data)
0376 {
0377     if (data.length() < MIN_LENGTH)
0378         return;
0379 
0380     qCDebug(KSTARS) << data;
0381 
0382     // data must contain service name
0383     if (data.contains(m_serviceName.toLatin1()) == false)
0384         return;
0385 
0386     QString host = getHostNameFromResponse (data);
0387     QList<QHostAddress> addresses = getAddressesFromResponse (data, host);
0388 
0389     if (!host.isEmpty() && !addresses.isEmpty())
0390     {
0391         QHostInfo info;
0392         info.setHostName (host);
0393         info.setAddresses (addresses);
0394         info.setError (QHostInfo::NoError);
0395 
0396         qCInfo(KSTARS) << "Found service on" << host;
0397 
0398         emit hostFound (info);
0399     }
0400 }
0401 
0402 /**
0403  * Sends a response packet with:
0404  * - Our mDNS host name
0405  * - Our IPv4 address
0406  * - Our IPv6 address
0407  */
0408 void qMDNS::sendResponse (const quint16 query_id)
0409 {
0410     if (!hostName().isEmpty() && hostName().endsWith (".local"))
0411     {
0412         QByteArray data;
0413 
0414         /* Get the host name and domain */
0415         QString host = hostName().split (".").first();
0416         QString domain = hostName().split (".").last();
0417 
0418         /* Get local IPs */
0419         quint32 ipv4 = 0;
0420         QList<QIPv6Address> ipv6;
0421         foreach (QHostAddress address, QNetworkInterface::allAddresses())
0422         {
0423             if (!address.isLoopback())
0424             {
0425                 if (address.protocol() == QAbstractSocket::IPv4Protocol)
0426                     ipv4 = (ipv4 == 0 ? address.toIPv4Address() : ipv4);
0427 
0428                 if (address.protocol() == QAbstractSocket::IPv6Protocol)
0429                     ipv6.append (address.toIPv6Address());
0430             }
0431         }
0432 
0433         /* Check that domain length is valid */
0434         if (host.length() > 255)
0435         {
0436             qCWarning(KSTARS) << Q_FUNC_INFO << host << "is too long!";
0437             return;
0438         }
0439 
0440         /* Create header and flags */
0441         data.append (ENCODE_16_BIT (query_id));
0442         data.append (ENCODE_16_BIT (kQR_Response));
0443         data.append (ENCODE_16_BIT (kResponse_QDCOUNT));
0444         data.append (ENCODE_16_BIT (kResponse_ANCOUNT));
0445         data.append (ENCODE_16_BIT (kResponse_NSCOUNT));
0446         data.append (ENCODE_16_BIT (kResponse_ARCOUNT));
0447 
0448         /* Add name data */
0449         data.append (host.length());
0450         data.append (host.toUtf8());
0451 
0452         /* Add domain data and FQDN/TLD separator */
0453         data.append (domain.length());
0454         data.append (domain.toUtf8());
0455         data.append ((char) kFQDN_Separator);
0456 
0457         /* Add IPv4 address header */
0458         data.append (ENCODE_16_BIT (kRecordA));
0459         data.append (ENCODE_16_BIT (kIN_BitFlush));
0460         data.append (ENCODE_32_BIT (m_ttl));
0461         data.append (ENCODE_16_BIT (sizeof (ipv4)));
0462 
0463         /* Add IPv4 bytes */
0464         data.append (ENCODE_32_BIT (ipv4));
0465 
0466         /* Add FQDN offset */
0467         data.append (ENCODE_16_BIT (kFQDN_Length));
0468 
0469         /* Add IPv6 addresses */
0470         foreach (QIPv6Address ip, ipv6)
0471         {
0472             data.append (ENCODE_16_BIT (kRecordAAAA));
0473             data.append (ENCODE_16_BIT (kIN_BitFlush));
0474             data.append (ENCODE_32_BIT (m_ttl));
0475             data.append (ENCODE_16_BIT (sizeof (ip.c)));
0476 
0477             /* Add IPv6 bytes */
0478             for (unsigned long i = 0; i < sizeof (ip.c); ++i)
0479                 data.append (ip.c [i]);
0480 
0481             /* Add FQDN offset */
0482             data.append (ENCODE_16_BIT (kFQDN_Length));
0483         }
0484 
0485         /* TODO: Generate NSEC code block */
0486         int nsec_length = 0;
0487 
0488         /* Add NSEC data */
0489         data.append (ENCODE_16_BIT (kNsecType));
0490         data.append (ENCODE_16_BIT (kIN_BitFlush));
0491         data.append (ENCODE_32_BIT (m_ttl));
0492         data.append (ENCODE_16_BIT (nsec_length));
0493 
0494         /* Send the response */
0495         sendPacket (data);
0496     }
0497 }
0498 
0499 /**
0500  * Extracts the host name from the \a data received from the mDNS network.
0501  * The host name begins at byte #12 (when the header and flags end) and ends
0502  * with a mandatory NUL character after the domain.
0503  *
0504  * The host name is constructed in the following way (without spaces):
0505  * \c NAME_LENGTH + \c NAME + \c DOMAIN_LENGTH + \c DOMAIN + \c NUL
0506  *
0507  * For example, appletv.local would be formatted as:
0508  * \c 0x07 + \c appletv + \c 0x05 + \c local + \c 0x00
0509  *
0510  * Or, if you prefer hex data:
0511  * \c { 07 61 70 70 6c 65 74 76 05 6c 6f 63 61 6c 00 }
0512  * \c {  7 a  p  p  l  e  t  v   5 l  o  c  a  l   0 }
0513  *
0514  * In order to obtain the full host name (and its mDNS domain), we construct
0515  * the string backwards. When the code notices that the current character is
0516  * the same as the domain length, we know that the domain name has been
0517  * extracted, and thus we can replace the domain length with a dot (.) and
0518  * begin extracting the host name.
0519  */
0520 QString qMDNS::getHostNameFromResponse (const QByteArray &data)
0521 {
0522     QList<char> list;
0523     QString address = "";
0524 
0525     /* Begin reading host name at byte 13 (byte 12 is the host name length) */
0526     int n = 13;
0527 
0528     /* Read the host name until we stumble with the FQDN/TLD separator */
0529     while (data.at (n) != kFQDN_Separator)
0530     {
0531         list.append (data.at (n));
0532         ++n;
0533     }
0534 
0535     /* Construct the string backwards (to replace domain length with a dot) */
0536     for (int i = 0; i < list.count(); ++i)
0537     {
0538         char character = list.at (list.count() - i - 1);
0539 
0540         if (character == (char) address.length())
0541             address.prepend (".");
0542         else
0543             address.prepend (character);
0544     }
0545 
0546     return address;
0547 }
0548 
0549 /**
0550  * Extracts the IPv4 from the \a data received from the mDNS network.
0551  * The IPv4 data begins when the host name data ends.
0552  *
0553  * For the packet to contain IPv4 information, the DNS Record Type code must
0554  * be "A" (IPv4) and the DNS Class code should correspond to "IN" (Internet).
0555  *
0556  * Here is the layout of the IPv4 section of the packet:
0557  *
0558  * - DNS Record Type
0559  * - DNS Class Code
0560  * - TTL
0561  * - IP length
0562  * - IP address bytes
0563  *
0564  * This is an example IPv4 section:
0565  * \c {00 01 80 01 00 00 78 00 00 04 99 6d 07 5a}
0566  *
0567  * Data in example section:
0568  * - \c {00 01} Type Codes
0569  * - \c {80 01} Class Codes
0570  * - \c {00 00 78 00} IP TTL
0571  * - \c {00 04} Number of address bytes (length in layman's terms)
0572  * - \c {99 6d 07 5a} IPv4 Address bytes (153, 109, 7, 90)
0573  */
0574 QString qMDNS::getIPv4FromResponse (const QByteArray &data,
0575                                     const QString &host)
0576 {
0577     QString ip = "";
0578 
0579     /* n stands for the byte index in which the host name data ends */
0580     int n = MIN_LENGTH + host.length();
0581 
0582     /* Packet is too small */
0583     if (data.length() < n + IP4_LENGTH)
0584         return ip;
0585 
0586     /* Get the IP type and class codes */
0587     quint16 typeCode  = DECODE_16_BIT (data.at (n + 1), data.at (n + 2));
0588     quint16 classCode = DECODE_16_BIT (data.at (n + 3), data.at (n + 4));
0589 
0590     /* Check if type and class codes are good */
0591     if (typeCode != kRecordA || classCode != kIN_BitFlush)
0592         return ip;
0593 
0594     /* Skip TTL indicator and obtain the number of address bytes */
0595     quint8 length = data.at (n + IPI_LENGTH);
0596 
0597     /* Append each IPv4 address byte (and decimal dots) to the IP string */
0598     for (int i = 1; i < length + 1; ++i)
0599     {
0600         ip += QString::number ((quint8) data.at (n + IPI_LENGTH + i));
0601         ip += (i < length) ? "." : "";
0602     }
0603 
0604     return ip;
0605 }
0606 
0607 /**
0608  * Extracts the IPv6 from the \a data received from the mDNS network.
0609  * The IPv6 data begins when the host name data ends.
0610  *
0611  * For the packet to contain IPv6 information, the DNS Record Type code must
0612  * be "AAAA" (IPv6) and the DNS Class code should correspond to "IN" (Internet).
0613  *
0614  * Here is the layout of the IPv4 section of the packet:
0615  *
0616  * - DNS Record Type
0617  * - DNS Class Code
0618  * - TTL
0619  * - IP length
0620  * - IP address bytes
0621  *
0622  * This is an example IPv6 section:
0623  * \c { 00 1c 80 01 00 00 78 00 00 10 fe 80 00 00 00 00 00 00 02 23 32 ff fe b1 21 52 }
0624  *
0625  * Data in example section:
0626  * - \c {00 1c} Type Codes
0627  * - \c {80 01} Class Codes
0628  * - \c {00 00 78 00} IP TTL
0629  * - \c {00 10} Number of address bytes (length in layman's terms)
0630  * - \c {fe 80 00 00 ... 52} IPv6 Address bytes (there are 16 of them)
0631  */
0632 QStringList qMDNS::getIPv6FromResponse (const QByteArray &data,
0633                                         const QString &host)
0634 {
0635     QStringList list;
0636 
0637     /* Skip the FQDN and IPv4 section */
0638     int n = MIN_LENGTH + IP4_LENGTH + host.length();
0639 
0640     /* Get the IPv6 list */
0641     bool isIPv6 = true;
0642     while (isIPv6)
0643     {
0644         /* Skip FQDN bytes */
0645         n += 2;
0646 
0647         /* Packet is invalid */
0648         if (data.length() < n + IP6_LENGTH)
0649             break;
0650 
0651         /* Get the IP type and class codes */
0652         quint16 typeCode  = DECODE_16_BIT (data.at (n + 1), data.at (n + 2));
0653         quint16 classCode = DECODE_16_BIT (data.at (n + 3), data.at (n + 4));
0654         isIPv6 = (typeCode == kRecordAAAA && classCode == kIN_BitFlush);
0655 
0656         /* IP type and class codes are OK, extract IP */
0657         if (isIPv6)
0658         {
0659             /* Skip TTL indicator and obtain the number of address bytes */
0660             quint8 length = data.at (n + IPI_LENGTH);
0661 
0662             /* Append each IPv6 address byte (encoded as hex) to the IP string */
0663             QString ip = "";
0664             for (int i = 1; i < length + 1; ++i)
0665             {
0666                 /* Get the hexadecimal representation of the byte */
0667                 QString byte;
0668                 byte.setNum ((quint8) data.at (n + i + IPI_LENGTH), 16);
0669 
0670                 /* Add the obtained string */
0671                 ip += byte;
0672 
0673                 /* Append colons after even indexes (except in the last byte) */
0674                 if ((i & 1) == 0 && (i < length))
0675                     ip += ":";
0676             }
0677 
0678             /* Increase the counter to 'jump' to the next section */
0679             n += 26;
0680 
0681             /* Append the obtained IP to the list */
0682             if (!list.contains (ip))
0683                 list.append (ip);
0684         }
0685     }
0686 
0687     return list;
0688 }
0689 
0690 /**
0691  * Obtains the IPv4 and IPv6 addresses from the received data.
0692  * \note This function will only generate a list with the valid IP addresses.
0693  */
0694 QList<QHostAddress> qMDNS::getAddressesFromResponse (const QByteArray &data,
0695         const QString &host)
0696 {
0697     QList<QHostAddress> list;
0698 
0699     /* Add IPv4 address */
0700     QHostAddress IPv4Address = QHostAddress (getIPv4FromResponse (data, host));
0701     if (!IPv4Address.isNull())
0702         list.append (IPv4Address);
0703 
0704     /* Add IPv6 addresses */
0705     foreach (QString ip, getIPv6FromResponse (data, host))
0706     {
0707         QHostAddress address = QHostAddress (ip);
0708         if (!address.isNull())
0709             list.append (address);
0710     }
0711 
0712     return list;
0713 }