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