1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2017 Nathan Osman
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include <qmdnsengine/abstractserver.h>
26 #include <qmdnsengine/dns.h>
27 #include <qmdnsengine/message.h>
28 #include <qmdnsengine/prober.h>
29 #include <qmdnsengine/query.h>
30 
31 #include "prober_p.h"
32 
33 using namespace QMdnsEngine;
34 
ProberPrivate(Prober * prober,AbstractServer * server,const Record & record)35 ProberPrivate::ProberPrivate(Prober *prober, AbstractServer *server, const Record &record)
36     : QObject(prober),
37       q(prober),
38       server(server),
39       confirmed(false),
40       proposedRecord(record),
41       suffix(1)
42 {
43     // All records should contain at least one "."
44     int index = record.name().indexOf('.');
45     name = record.name().left(index);
46     type = record.name().mid(index);
47 
48     connect(server, &AbstractServer::messageReceived, this, &ProberPrivate::onMessageReceived);
49     connect(&timer, &QTimer::timeout, this, &ProberPrivate::onTimeout);
50 
51     timer.setSingleShot(true);
52 
53     assertRecord();
54 }
55 
assertRecord()56 void ProberPrivate::assertRecord()
57 {
58     // Use the current suffix to set the name of the proposed record
59     proposedRecord.setName(suffix == 1 ?
60         name + type : name + "-" + QByteArray::number(suffix) + type);
61 
62     // Broadcast a query for the proposed name (using an ANY query) and
63     // include the proposed record in the query
64     Query query;
65     query.setName(proposedRecord.name());
66     query.setType(ANY);
67     Message message;
68     message.addQuery(query);
69     message.addRecord(proposedRecord);
70     server->sendMessageToAll(message);
71 
72     // Wait two seconds to confirm it is unique
73     timer.stop();
74     timer.start(2 * 1000);
75 }
76 
onMessageReceived(const Message & message)77 void ProberPrivate::onMessageReceived(const Message &message)
78 {
79     // If the response matches the proposed record, increment the suffix and
80     // try with the new name
81 
82     if (confirmed || !message.isResponse()) {
83         return;
84     }
85     foreach (Record record, message.records()) {
86         if (record.name() == proposedRecord.name() && record.type() == proposedRecord.type()) {
87             ++suffix;
88             assertRecord();
89         }
90     }
91 }
92 
onTimeout()93 void ProberPrivate::onTimeout()
94 {
95     confirmed = true;
96     emit q->nameConfirmed(proposedRecord.name());
97 }
98 
Prober(AbstractServer * server,const Record & record,QObject * parent)99 Prober::Prober(AbstractServer *server, const Record &record, QObject *parent)
100     : QObject(parent),
101       d(new ProberPrivate(this, server, record))
102 {
103 }
104