Serenity Operating System
at master 189 lines 5.5 kB view raw
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}