Serenity Operating System
1/*
2 * Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
3 * Copyright (c) 2022, Alexander Narsudinov <a.narsudinov@gmail.com>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include "MulticastDNS.h"
9#include <AK/DeprecatedString.h>
10#include <AK/IPv4Address.h>
11#include <AK/JsonArray.h>
12#include <AK/JsonObject.h>
13#include <AK/JsonValue.h>
14#include <LibCore/DeprecatedFile.h>
15#include <LibCore/System.h>
16#include <limits.h>
17#include <poll.h>
18#include <sys/socket.h>
19#include <unistd.h>
20
21namespace LookupServer {
22
23MulticastDNS::MulticastDNS(Object* parent)
24 : Core::UDPServer(parent)
25 , m_hostname("courage.local")
26{
27 char buffer[_POSIX_HOST_NAME_MAX];
28 if (gethostname(buffer, sizeof(buffer)) < 0) {
29 perror("gethostname");
30 } else {
31 m_hostname = DeprecatedString::formatted("{}.local", buffer);
32 }
33
34 u8 zero = 0;
35 if (setsockopt(fd(), IPPROTO_IP, IP_MULTICAST_LOOP, &zero, 1) < 0)
36 perror("setsockopt(IP_MULTICAST_LOOP)");
37 ip_mreq mreq = {
38 mdns_addr.sin_addr,
39 { htonl(INADDR_ANY) },
40 };
41 if (setsockopt(fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
42 perror("setsockopt(IP_ADD_MEMBERSHIP)");
43
44 bind(IPv4Address(), 5353);
45
46 on_ready_to_receive = [this]() {
47 if (auto result = handle_packet(); result.is_error()) {
48 dbgln("Failed to handle packet: {}", result.error());
49 }
50 };
51
52 // TODO: Announce on startup. We cannot just call announce() here,
53 // because it races with the network interfaces getting configured.
54}
55
56ErrorOr<void> MulticastDNS::handle_packet()
57{
58 auto buffer = TRY(receive(1024));
59 auto optional_packet = Packet::from_raw_packet(buffer.data(), buffer.size());
60 if (!optional_packet.has_value()) {
61 dbgln("Got an invalid mDNS packet");
62 return {};
63 }
64 auto& packet = optional_packet.value();
65
66 if (packet.is_query())
67 handle_query(packet);
68 return {};
69}
70
71void MulticastDNS::handle_query(Packet const& packet)
72{
73 bool should_reply = false;
74
75 for (auto& question : packet.questions())
76 if (question.name() == m_hostname)
77 should_reply = true;
78
79 if (!should_reply)
80 return;
81
82 announce();
83}
84
85void MulticastDNS::announce()
86{
87 Packet response;
88 response.set_is_response();
89 response.set_code(Packet::Code::NOERROR);
90 response.set_authoritative_answer(true);
91 response.set_recursion_desired(false);
92 response.set_recursion_available(false);
93
94 for (auto& address : local_addresses()) {
95 auto raw_addr = address.to_in_addr_t();
96 Answer answer {
97 m_hostname,
98 RecordType::A,
99 RecordClass::IN,
100 120,
101 DeprecatedString { (char const*)&raw_addr, sizeof(raw_addr) },
102 true,
103 };
104 response.add_answer(answer);
105 }
106
107 if (emit_packet(response).is_error())
108 perror("Failed to emit response packet");
109}
110
111ErrorOr<size_t> MulticastDNS::emit_packet(Packet const& packet, sockaddr_in const* destination)
112{
113 auto buffer = TRY(packet.to_byte_buffer());
114 if (!destination)
115 destination = &mdns_addr;
116
117 return send(buffer, *destination);
118}
119
120Vector<IPv4Address> MulticastDNS::local_addresses() const
121{
122 auto file = Core::DeprecatedFile::construct("/sys/kernel/net/adapters");
123 if (!file->open(Core::OpenMode::ReadOnly)) {
124 dbgln("Failed to open /sys/kernel/net/adapters: {}", file->error_string());
125 return {};
126 }
127
128 auto file_contents = file->read_all();
129 auto json = JsonValue::from_string(file_contents).release_value_but_fixme_should_propagate_errors();
130
131 Vector<IPv4Address> addresses;
132
133 json.as_array().for_each([&addresses](auto& value) {
134 auto if_object = value.as_object();
135 auto address = if_object.get_deprecated_string("ipv4_address"sv).value_or({});
136 auto ipv4_address = IPv4Address::from_string(address);
137 // Skip unconfigured interfaces.
138 if (!ipv4_address.has_value())
139 return;
140 // Skip loopback adapters.
141 if (ipv4_address.value()[0] == IN_LOOPBACKNET)
142 return;
143 addresses.append(ipv4_address.value());
144 });
145
146 return addresses;
147}
148
149ErrorOr<Vector<Answer>> MulticastDNS::lookup(Name const& name, RecordType record_type)
150{
151 Packet request;
152 request.set_is_query();
153 request.set_recursion_desired(false);
154 request.add_question({ name, record_type, RecordClass::IN, false });
155
156 TRY(emit_packet(request));
157 Vector<Answer> answers;
158
159 // FIXME: It would be better not to block
160 // the main loop while we wait for a response.
161 while (true) {
162 auto pfd = pollfd { fd(), POLLIN, 0 };
163 auto rc = TRY(Core::System::poll({ &pfd, 1 }, 1000));
164 if (rc == 0) {
165 // Timed out.
166 return Vector<Answer> {};
167 }
168 auto buffer = TRY(receive(1024));
169 if (buffer.is_empty())
170 return Vector<Answer> {};
171 auto optional_packet = Packet::from_raw_packet(buffer.data(), buffer.size());
172 if (!optional_packet.has_value()) {
173 dbgln("Got an invalid mDNS packet");
174 continue;
175 }
176 auto& packet = optional_packet.value();
177
178 if (packet.is_query())
179 continue;
180
181 for (auto& answer : packet.answers())
182 if (answer.name() == name && answer.type() == record_type)
183 answers.append(answer);
184 if (!answers.is_empty())
185 return answers;
186 }
187}
188
189}