1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "discovery/mdns/mdns_receiver.h"
6 
7 #include <utility>
8 
9 #include "discovery/mdns/mdns_reader.h"
10 #include "util/trace_logging.h"
11 
12 namespace openscreen {
13 namespace discovery {
14 
15 MdnsReceiver::ResponseClient::~ResponseClient() = default;
16 
MdnsReceiver(Config config)17 MdnsReceiver::MdnsReceiver(Config config) : config_(std::move(config)) {}
18 
~MdnsReceiver()19 MdnsReceiver::~MdnsReceiver() {
20   if (state_ == State::kRunning) {
21     Stop();
22   }
23 
24   OSP_DCHECK(response_clients_.empty());
25 }
26 
SetQueryCallback(std::function<void (const MdnsMessage &,const IPEndpoint &)> callback)27 void MdnsReceiver::SetQueryCallback(
28     std::function<void(const MdnsMessage&, const IPEndpoint&)> callback) {
29   // This check verifies that either new or stored callback has a target. It
30   // will fail in case multiple objects try to set or clear the callback.
31   OSP_DCHECK(static_cast<bool>(query_callback_) != static_cast<bool>(callback));
32   query_callback_ = callback;
33 }
34 
AddResponseCallback(ResponseClient * callback)35 void MdnsReceiver::AddResponseCallback(ResponseClient* callback) {
36   auto it =
37       std::find(response_clients_.begin(), response_clients_.end(), callback);
38   OSP_DCHECK(it == response_clients_.end());
39 
40   response_clients_.push_back(callback);
41 }
42 
RemoveResponseCallback(ResponseClient * callback)43 void MdnsReceiver::RemoveResponseCallback(ResponseClient* callback) {
44   auto it =
45       std::find(response_clients_.begin(), response_clients_.end(), callback);
46   OSP_DCHECK(it != response_clients_.end());
47 
48   response_clients_.erase(it);
49 }
50 
Start()51 void MdnsReceiver::Start() {
52   state_ = State::kRunning;
53 }
54 
Stop()55 void MdnsReceiver::Stop() {
56   state_ = State::kStopped;
57 }
58 
OnRead(UdpSocket * socket,ErrorOr<UdpPacket> packet_or_error)59 void MdnsReceiver::OnRead(UdpSocket* socket,
60                           ErrorOr<UdpPacket> packet_or_error) {
61   if (state_ != State::kRunning || packet_or_error.is_error()) {
62     return;
63   }
64 
65   UdpPacket packet = std::move(packet_or_error.value());
66 
67   TRACE_SCOPED(TraceCategory::kMdns, "MdnsReceiver::OnRead");
68   MdnsReader reader(config_, packet.data(), packet.size());
69   const ErrorOr<MdnsMessage> message = reader.Read();
70   if (message.is_error()) {
71     if (message.error().code() == Error::Code::kMdnsNonConformingFailure) {
72       OSP_DVLOG << "mDNS message dropped due to invalid rcode or opcode...";
73     } else {
74       OSP_DVLOG << "mDNS message failed to parse...";
75     }
76     return;
77   }
78 
79   if (message.value().type() == MessageType::Response) {
80     for (ResponseClient* client : response_clients_) {
81       client->OnMessageReceived(message.value());
82     }
83     if (response_clients_.empty()) {
84       OSP_DVLOG
85           << "mDNS response message dropped. No response client registered...";
86     }
87   } else {
88     if (query_callback_) {
89       query_callback_(message.value(), packet.source());
90     } else {
91       OSP_DVLOG << "mDNS query message dropped. No query client registered...";
92     }
93   }
94 }
95 
96 }  // namespace discovery
97 }  // namespace openscreen
98