diff --git a/Userland/Libraries/LibC/netdb.cpp b/Userland/Libraries/LibC/netdb.cpp index d64bde52b6..3235b27742 100644 --- a/Userland/Libraries/LibC/netdb.cpp +++ b/Userland/Libraries/LibC/netdb.cpp @@ -46,6 +46,7 @@ static in_addr_t* __gethostbyname_address_list_buffer[2]; static hostent __gethostbyaddr_buffer; static in_addr_t* __gethostbyaddr_address_list_buffer[2]; +static constexpr i32 lookup_server_endpoint_magic = 9001; // Get service entry buffers and file information for the getservent() family of functions. static FILE* services_file = nullptr; @@ -123,32 +124,74 @@ hostent* gethostbyname(const char* name) close(fd); }); - auto line = String::formatted("L{}\n", name); - int nsent = write(fd, line.characters(), line.length()); + size_t name_length = strlen(name); + + struct [[gnu::packed]] { + u32 message_size; + i32 endpoint_magic; + i32 message_id; + i32 name_length; + } request_header = { + sizeof(request_header) - sizeof(request_header.message_size) + name_length, + lookup_server_endpoint_magic, + 1, + (i32)name_length, + }; + int nsent = write(fd, &request_header, sizeof(request_header)); if (nsent < 0) { perror("write"); return nullptr; } + ASSERT((size_t)nsent == sizeof(request_header)); + nsent = write(fd, name, name_length); + if (nsent < 0) { + perror("write"); + return nullptr; + } + ASSERT((size_t)nsent == name_length); - ASSERT((size_t)nsent == line.length()); + struct [[gnu::packed]] { + u32 message_size; + i32 endpoint_magic; + i32 message_id; + i32 code; + u64 addresses_count; + } response_header; - char buffer[1024]; - int nrecv = read(fd, buffer, sizeof(buffer) - 1); + int nrecv = read(fd, &response_header, sizeof(response_header)); if (nrecv < 0) { perror("recv"); return nullptr; } - - if (!memcmp(buffer, "Not found.", sizeof("Not found.") - 1)) + ASSERT((size_t)nrecv == sizeof(response_header)); + if (response_header.endpoint_magic != lookup_server_endpoint_magic || response_header.message_id != 2) { + dbgln("Received an unexpected message"); return nullptr; - - auto responses = String(buffer, nrecv).split('\n'); - if (responses.is_empty()) + } + if (response_header.code != 0) { + // TODO: return a specific error. return nullptr; + } + ASSERT(response_header.addresses_count > 0); - auto& response = responses[0]; + i32 response_length; + nrecv = read(fd, &response_length, sizeof(response_length)); + if (nrecv < 0) { + perror("recv"); + return nullptr; + } + ASSERT((size_t)nrecv == sizeof(response_length)); - int rc = inet_pton(AF_INET, response.characters(), &__gethostbyname_address); + char response[response_length + 1]; + nrecv = read(fd, response, response_length); + if (nrecv < 0) { + perror("recv"); + return nullptr; + } + ASSERT(nrecv == response_length); + response[response_length] = 0; + + int rc = inet_pton(AF_INET, response, &__gethostbyname_address); if (rc <= 0) return nullptr; @@ -188,36 +231,66 @@ hostent* gethostbyaddr(const void* addr, socklen_t addr_size, int type) }); IPv4Address ipv4_address((const u8*)&((const in_addr*)addr)->s_addr); + auto address = ipv4_address.to_string(); - auto line = String::formatted("R{}.{}.{}.{}.in-addr.arpa\n", - ipv4_address[3], - ipv4_address[2], - ipv4_address[1], - ipv4_address[0]); - int nsent = write(fd, line.characters(), line.length()); + struct [[gnu::packed]] { + u32 message_size; + i32 endpoint_magic; + i32 message_id; + i32 address_length; + } request_header = { + sizeof(request_header) - sizeof(request_header.message_size) + address.length(), + lookup_server_endpoint_magic, + 3, + (i32)address.length(), + }; + int nsent = write(fd, &request_header, sizeof(request_header)); if (nsent < 0) { perror("write"); return nullptr; } + ASSERT((size_t)nsent == sizeof(request_header)); + nsent = write(fd, address.characters(), address.length()); + if (nsent < 0) { + perror("write"); + return nullptr; + } + ASSERT((size_t)nsent == address.length()); - ASSERT((size_t)nsent == line.length()); + struct [[gnu::packed]] { + u32 message_size; + i32 endpoint_magic; + i32 message_id; + i32 code; + i32 name_length; + } response_header; - char buffer[1024]; - int nrecv = read(fd, buffer, sizeof(buffer) - 1); + int nrecv = read(fd, &response_header, sizeof(response_header)); if (nrecv < 0) { perror("recv"); return nullptr; } - - if (!memcmp(buffer, "Not found.", sizeof("Not found.") - 1)) + ASSERT((size_t)nrecv == sizeof(response_header)); + if (response_header.endpoint_magic != lookup_server_endpoint_magic || response_header.message_id != 4) { + dbgln("Received an unexpected message"); return nullptr; - - auto responses = String(buffer, nrecv).split('\n'); - if (responses.is_empty()) + } + if (response_header.code != 0) { + // TODO: return a specific error. return nullptr; + } - gethostbyaddr_name_buffer = responses[0]; - __gethostbyaddr_buffer.h_name = const_cast(gethostbyaddr_name_buffer.characters()); + char* buffer; + auto string_impl = StringImpl::create_uninitialized(response_header.name_length, buffer); + nrecv = read(fd, buffer, response_header.name_length); + if (nrecv < 0) { + perror("recv"); + return nullptr; + } + ASSERT(nrecv == response_header.name_length); + + gethostbyaddr_name_buffer = move(string_impl); + __gethostbyaddr_buffer.h_name = buffer; __gethostbyaddr_buffer.h_aliases = nullptr; __gethostbyaddr_buffer.h_addrtype = AF_INET; // FIXME: Should we populate the hostent's address list here with a sockaddr_in for the provided host? diff --git a/Userland/Services/LookupServer/CMakeLists.txt b/Userland/Services/LookupServer/CMakeLists.txt index 89769d4abc..0204aec286 100644 --- a/Userland/Services/LookupServer/CMakeLists.txt +++ b/Userland/Services/LookupServer/CMakeLists.txt @@ -1,10 +1,16 @@ +compile_ipc(LookupServer.ipc LookupServerEndpoint.h) +compile_ipc(LookupClient.ipc LookupClientEndpoint.h) + set(SOURCES DNSAnswer.cpp DNSRequest.cpp DNSResponse.cpp LookupServer.cpp + LookupServerEndpoint.h + LookupClientEndpoint.h + ClientConnection.cpp main.cpp ) serenity_bin(LookupServer) -target_link_libraries(LookupServer LibCore) +target_link_libraries(LookupServer LibCore LibIPC) diff --git a/Userland/Services/LookupServer/ClientConnection.cpp b/Userland/Services/LookupServer/ClientConnection.cpp new file mode 100644 index 0000000000..bfbf7a3cc2 --- /dev/null +++ b/Userland/Services/LookupServer/ClientConnection.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021, Sergey Bugaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ClientConnection.h" +#include "DNSRequest.h" +#include "LookupServer.h" +#include + +namespace LookupServer { + +static HashMap> s_connections; + +ClientConnection::ClientConnection(AK::NonnullRefPtr socket, int client_id) + : IPC::ClientConnection(*this, move(socket), client_id) +{ + s_connections.set(client_id, *this); +} + +ClientConnection::~ClientConnection() +{ +} + +void ClientConnection::die() +{ + s_connections.remove(client_id()); +} + +OwnPtr ClientConnection::handle(const Messages::LookupServer::LookupName& message) +{ + auto addresses = LookupServer::the().lookup(message.name(), T_A); + if (addresses.is_empty()) + return make(1, Vector()); + return make(0, move(addresses)); +} + +OwnPtr ClientConnection::handle(const Messages::LookupServer::LookupAddress& message) +{ + auto optional_address = IPv4Address::from_string(message.address()); + if (!optional_address.has_value()) + return make(1, String()); + auto& address = optional_address.value(); + auto name = String::formatted("{}.{}.{}.{}.in-addr.arpa", + address[3], + address[2], + address[1], + address[0]); + auto hosts = LookupServer::the().lookup(name, T_PTR); + if (hosts.is_empty()) + return make(1, String()); + return make(0, hosts[0]); +} +} diff --git a/Userland/Services/LookupServer/ClientConnection.h b/Userland/Services/LookupServer/ClientConnection.h new file mode 100644 index 0000000000..68af7c6ab8 --- /dev/null +++ b/Userland/Services/LookupServer/ClientConnection.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, Sergey Bugaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace LookupServer { + +class ClientConnection final + : public IPC::ClientConnection + , public LookupServerEndpoint { + + C_OBJECT(ClientConnection); + +public: + explicit ClientConnection(NonnullRefPtr, int client_id); + virtual ~ClientConnection() override; + + virtual void die() override; + +private: + virtual OwnPtr handle(const Messages::LookupServer::LookupName&) override; + virtual OwnPtr handle(const Messages::LookupServer::LookupAddress&) override; +}; + +} diff --git a/Userland/Services/LookupServer/LookupClient.ipc b/Userland/Services/LookupServer/LookupClient.ipc new file mode 100644 index 0000000000..6455140d91 --- /dev/null +++ b/Userland/Services/LookupServer/LookupClient.ipc @@ -0,0 +1,4 @@ +endpoint LookupClient = 9002 +{ + Dummy() =| +} diff --git a/Userland/Services/LookupServer/LookupServer.cpp b/Userland/Services/LookupServer/LookupServer.cpp index 1f6ac5081e..cbad77f392 100644 --- a/Userland/Services/LookupServer/LookupServer.cpp +++ b/Userland/Services/LookupServer/LookupServer.cpp @@ -25,6 +25,7 @@ */ #include "LookupServer.h" +#include "ClientConnection.h" #include "DNSRequest.h" #include "DNSResponse.h" #include @@ -43,8 +44,19 @@ namespace LookupServer { +static LookupServer* s_the; + +LookupServer& LookupServer::the() +{ + ASSERT(s_the); + return *s_the; +} + LookupServer::LookupServer() { + ASSERT(s_the == nullptr); + s_the = this; + auto config = Core::ConfigFile::get_for_system("LookupServer"); dbgln("Using network config file at {}", config->file_name()); m_nameservers = config->read_entry("DNS", "Nameservers", "1.1.1.1,1.0.0.1").split(','); @@ -54,13 +66,13 @@ LookupServer::LookupServer() m_local_server = Core::LocalServer::construct(this); m_local_server->on_ready_to_accept = [this]() { auto socket = m_local_server->accept(); - if (!socket) + if (!socket) { + dbgln("Failed to accept a client connection"); return; - socket->on_ready_to_read = [this, socket]() { - service_client(*socket); - NonnullRefPtr keeper = *socket; - const_cast(*socket).on_ready_to_read = [] {}; - }; + } + static int s_next_client_id = 0; + int client_id = ++s_next_client_id; + IPC::new_client_connection(socket.release_nonnull(), client_id); }; bool ok = m_local_server->take_over_from_system_server(); ASSERT(ok); @@ -101,32 +113,17 @@ void LookupServer::load_etc_hosts() } } -void LookupServer::service_client(NonnullRefPtr socket) +Vector LookupServer::lookup(const String& name, unsigned short record_type) { - u8 client_buffer[1024]; - int nrecv = socket->read(client_buffer, sizeof(client_buffer) - 1); - if (nrecv < 0) { - perror("read"); - return; - } - - client_buffer[nrecv] = '\0'; - - char lookup_type = client_buffer[0]; - if (lookup_type != 'L' && lookup_type != 'R') { - dbgln("Invalid lookup_type '{}'", lookup_type); - return; - } - auto hostname = String((const char*)client_buffer + 1, nrecv - 1, Chomp); #if LOOKUPSERVER_DEBUG - dbgln("Got request for '{}'", hostname); + dbgln("Got request for '{}'", name); #endif Vector responses; - if (auto known_host = m_etc_hosts.get(hostname); known_host.has_value()) { + if (auto known_host = m_etc_hosts.get(name); known_host.has_value()) { responses.append(known_host.value()); - } else if (!hostname.is_empty()) { + } else if (!name.is_empty()) { for (auto& nameserver : m_nameservers) { #if LOOKUPSERVER_DEBUG dbgln("Doing lookup using nameserver '{}'", nameserver); @@ -134,10 +131,7 @@ void LookupServer::service_client(NonnullRefPtr socket) bool did_get_response = false; int retries = 3; do { - if (lookup_type == 'L') - responses = lookup(hostname, nameserver, did_get_response, T_A); - else if (lookup_type == 'R') - responses = lookup(hostname, nameserver, did_get_response, T_PTR); + responses = lookup(name, nameserver, did_get_response, record_type); if (did_get_response) break; } while (--retries); @@ -152,24 +146,11 @@ void LookupServer::service_client(NonnullRefPtr socket) } if (responses.is_empty()) { fprintf(stderr, "LookupServer: Tried all nameservers but never got a response :(\n"); - return; + return {}; } } - if (responses.is_empty()) { - int nsent = socket->write("Not found.\n"); - if (nsent < 0) - perror("write"); - return; - } - for (auto& response : responses) { - auto line = String::formatted("{}\n", response); - int nsent = socket->write(line); - if (nsent < 0) { - perror("write"); - break; - } - } + return move(responses); } Vector LookupServer::lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase should_randomize_case) diff --git a/Userland/Services/LookupServer/LookupServer.h b/Userland/Services/LookupServer/LookupServer.h index 3f1bcfe574..3297c5b5c7 100644 --- a/Userland/Services/LookupServer/LookupServer.h +++ b/Userland/Services/LookupServer/LookupServer.h @@ -38,11 +38,13 @@ class LookupServer final : public Core::Object { C_OBJECT(LookupServer); public: + static LookupServer& the(); + Vector lookup(const String& name, unsigned short record_type); + private: LookupServer(); void load_etc_hosts(); - void service_client(NonnullRefPtr); Vector lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes); struct CachedLookup { diff --git a/Userland/Services/LookupServer/LookupServer.ipc b/Userland/Services/LookupServer/LookupServer.ipc new file mode 100644 index 0000000000..e23182346a --- /dev/null +++ b/Userland/Services/LookupServer/LookupServer.ipc @@ -0,0 +1,5 @@ +endpoint LookupServer = 9001 +{ + LookupName(String name) => (int code, Vector addresses) + LookupAddress(String address) => (int code, String name) +}