From 01f13338566211b3d20bd922597f546d184a70c3 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 6 Jun 2019 05:35:03 +0200 Subject: [PATCH] LookupServer+LibC: Add support for reverse DNS lookups via gethostbyaddr(). LookupServer can now take two types of requests: * L: Lookup * R: Reverse lookup The /bin/host program now does a reverse lookup if the input string is a valid IPv4 address. :^) --- LibC/netdb.cpp | 129 +++++++++++++++++++++++++++------- LibC/netdb.h | 1 + Servers/LookupServer/main.cpp | 52 +++++++++----- Userland/host.cpp | 20 +++++- 4 files changed, 156 insertions(+), 46 deletions(-) diff --git a/LibC/netdb.cpp b/LibC/netdb.cpp index 5cc97425767..2826027b5b6 100644 --- a/LibC/netdb.cpp +++ b/LibC/netdb.cpp @@ -15,6 +15,39 @@ static char __gethostbyname_name_buffer[512]; static in_addr_t __gethostbyname_address; static in_addr_t* __gethostbyname_address_list_buffer[2]; +static hostent __gethostbyaddr_buffer; +static char __gethostbyaddr_name_buffer[512]; +static in_addr_t* __gethostbyaddr_address_list_buffer[2]; + +static int connect_to_lookup_server() +{ + int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + + sockaddr_un address; + address.sun_family = AF_LOCAL; + strcpy(address.sun_path, "/tmp/.LookupServer-socket"); + + int retries = 3; + int rc = 0; + while (retries) { + rc = connect(fd, (const sockaddr*)&address, sizeof(address)); + if (rc == 0) + break; + --retries; + sleep(1); + } + + if (rc < 0) { + close(fd); + return -1; + } + return fd; +} + hostent* gethostbyname(const char* name) { unsigned a; @@ -36,35 +69,14 @@ hostent* gethostbyname(const char* name) return &__gethostbyname_buffer; } - int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - perror("socket"); + int fd = connect_to_lookup_server(); + if (fd < 0) return nullptr; - } - sockaddr_un address; - address.sun_family = AF_LOCAL; - strcpy(address.sun_path, "/tmp/.LookupServer-socket"); - - int retries = 3; - int rc = 0; - while (retries) { - rc = connect(fd, (const sockaddr*)&address, sizeof(address)); - if (rc == 0) - break; - --retries; - sleep(1); - } - - if (rc < 0) { - close(fd); - return nullptr; - } - - auto line = String::format("%s\n", name); + auto line = String::format("L%s\n", name); int nsent = write(fd, line.characters(), line.length()); if (nsent < 0) { - perror("send"); + perror("write"); close(fd); return nullptr; } @@ -84,8 +96,8 @@ hostent* gethostbyname(const char* name) if (!memcmp(buffer, "Not found.", sizeof("Not found.") - 1)) return nullptr; - rc = inet_pton(AF_INET, buffer, &__gethostbyname_address); - if (rc < 0) + int rc = inet_pton(AF_INET, buffer, &__gethostbyname_address); + if (rc <= 0) return nullptr; strncpy(__gethostbyname_name_buffer, name, strlen(name)); @@ -101,4 +113,67 @@ hostent* gethostbyname(const char* name) return &__gethostbyname_buffer; } +hostent* gethostbyaddr(const void* addr, socklen_t addr_size, int type) +{ + if (type != AF_INET) { + errno = EAFNOSUPPORT; + return nullptr; + } + + if (addr_size < sizeof(in_addr)) { + errno = EINVAL; + return nullptr; + } + + int fd = connect_to_lookup_server(); + if (fd < 0) + return nullptr; + + IPv4Address ipv4_address((const byte*)&((const in_addr*)addr)->s_addr); + + auto line = String::format("R%d.%d.%d.%d.in-addr.arpa\n", + ipv4_address[3], + ipv4_address[2], + ipv4_address[1], + ipv4_address[0] + ); + int nsent = write(fd, line.characters(), line.length()); + if (nsent < 0) { + perror("write"); + close(fd); + return nullptr; + } + + ASSERT(nsent == line.length()); + + char buffer[1024]; + int nrecv = read(fd, buffer, sizeof(buffer) - 1); + if (nrecv < 0) { + perror("recv"); + close(fd); + return nullptr; + } + if (nrecv > 1) { + // Strip newline. + buffer[nrecv - 1] = '\0'; + } + buffer[nrecv] = '\0'; + close(fd); + + if (!memcmp(buffer, "Not found.", sizeof("Not found.") - 1)) + return nullptr; + + strncpy(__gethostbyaddr_name_buffer, buffer, max(sizeof(__gethostbyaddr_name_buffer), (size_t)nrecv)); + + __gethostbyaddr_buffer.h_name = __gethostbyaddr_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? + __gethostbyaddr_address_list_buffer[0] = nullptr; + __gethostbyaddr_buffer.h_addr_list = (char**)__gethostbyaddr_address_list_buffer; + __gethostbyaddr_buffer.h_length = 4; + + return &__gethostbyaddr_buffer; +} + } diff --git a/LibC/netdb.h b/LibC/netdb.h index ed75e482405..6f7faffa853 100644 --- a/LibC/netdb.h +++ b/LibC/netdb.h @@ -15,6 +15,7 @@ struct hostent { }; struct hostent* gethostbyname(const char*); +struct hostent* gethostbyaddr(const void* addr, socklen_t len, int type); struct servent { char* s_name; diff --git a/Servers/LookupServer/main.cpp b/Servers/LookupServer/main.cpp index 283aadec911..446a53f6cb8 100644 --- a/Servers/LookupServer/main.cpp +++ b/Servers/LookupServer/main.cpp @@ -24,7 +24,7 @@ #define C_IN 1 -static Vector lookup(const String& hostname, bool& did_timeout, const String& DNS_IP); +static Vector lookup(const String& hostname, bool& did_timeout, const String& DNS_IP, unsigned short record_type); static String parse_dns_name(const byte*, int& offset, int max_offset); int main(int argc, char**argv) @@ -98,19 +98,28 @@ int main(int argc, char**argv) client_buffer[nrecv] = '\0'; - auto hostname = String(client_buffer, nrecv, Chomp); + char lookup_type = client_buffer[0]; + if (lookup_type != 'L' && lookup_type != 'R') { + dbgprintf("LookupServer: Invalid lookup_type '%c'\n", lookup_type); + close(client_fd); + continue; + } + auto hostname = String(client_buffer + 1, nrecv - 1, Chomp); dbgprintf("LookupServer: Got request for '%s' (using IP %s)\n", hostname.characters(), DNS_IP.characters()); - Vector addresses; + Vector responses; if (!hostname.is_empty()) { bool did_timeout; int retries = 3; do { did_timeout = false; - addresses = lookup(hostname, did_timeout, DNS_IP); + if (lookup_type == 'L') + responses = lookup(hostname, did_timeout, DNS_IP, T_A); + else if (lookup_type == 'R') + responses = lookup(hostname, did_timeout, DNS_IP, T_PTR); if (!did_timeout) break; } while (--retries); @@ -121,15 +130,15 @@ int main(int argc, char**argv) } } - if (addresses.is_empty()) { + if (responses.is_empty()) { int nsent = write(client_fd, "Not found.\n", sizeof("Not found.\n")); if (nsent < 0) perror("write"); close(client_fd); continue; } - for (auto& address : addresses) { - auto line = String::format("%s\n", address.to_string().characters()); + for (auto& response : responses) { + auto line = String::format("%s\n", response.characters()); int nsent = write(client_fd, line.characters(), line.length()); if (nsent < 0) { perror("write"); @@ -147,10 +156,8 @@ static word get_next_id() return ++s_next_id; } -Vector lookup(const String& hostname, bool& did_timeout, const String& DNS_IP) +Vector lookup(const String& hostname, bool& did_timeout, const String& DNS_IP, unsigned short record_type) { - // FIXME: First check if it's an IP address in a string! - DNSPacket request_header; request_header.set_id(get_next_id()); request_header.set_is_query(); @@ -170,7 +177,7 @@ Vector lookup(const String& hostname, bool& did_timeout, const Stri stream << part; } stream << '\0'; - stream << htons(T_A); + stream << htons(record_type); stream << htons(C_IN); stream.snip(); } @@ -249,22 +256,31 @@ Vector lookup(const String& hostname, bool& did_timeout, const Stri auto question = parse_dns_name((const byte*)response_header.payload(), offset, nrecv); offset += 4; - Vector addresses; + Vector addresses; for (word i = 0; i < response_header.answer_count(); ++i) { auto& record = *(const DNSRecord*)(&((const byte*)response_header.payload())[offset]); - auto ipv4_address = IPv4Address((const byte*)record.data()); - dbgprintf("LookupServer: Answer #%u: (question: %s), type=%u, ttl=%u, length=%u, data=%s\n", + dbgprintf("LookupServer: Answer #%u: (question: %s), type=%u, ttl=%u, length=%u, data=", i, question.characters(), record.type(), record.ttl(), - record.data_length(), - ipv4_address.to_string().characters()); + record.data_length()); offset += sizeof(DNSRecord) + record.data_length(); - if (record.type() == T_A) - addresses.append(ipv4_address); + if (record.type() == T_PTR) { + int dummy = 0; + auto name = parse_dns_name((const byte*)record.data(), dummy, record.data_length()); + dbgprintf("%s\n", name.characters()); + addresses.append(name); + } else if (record.type() == T_A) { + auto ipv4_address = IPv4Address((const byte*)record.data()); + dbgprintf("%s\n", ipv4_address.to_string().characters()); + addresses.append(ipv4_address.to_string()); + } else { + dbgprintf("(unimplemented)\n"); + dbgprintf("LookupServer: FIXME: Handle record type %u\n", record.type()); + } // FIXME: Parse some other record types perhaps? } diff --git a/Userland/host.cpp b/Userland/host.cpp index 179e5a649eb..52e307ad9ba 100644 --- a/Userland/host.cpp +++ b/Userland/host.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include int main(int argc, char** argv) @@ -10,9 +11,26 @@ int main(int argc, char** argv) return 0; } + // If input looks like an IPv4 address, we should do a reverse lookup. + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(53); + int rc = inet_pton(AF_INET, argv[1], &addr.sin_addr); + if (rc == 1) { + // Okay, let's do a reverse lookup. + auto* hostent = gethostbyaddr(&addr.sin_addr, sizeof(in_addr), AF_INET); + if (!hostent) { + fprintf(stderr, "Reverse lookup failed for '%s'\n", argv[1]); + return 1; + } + printf("%s is %s\n", argv[1], hostent->h_name); + return 0; + } + auto* hostent = gethostbyname(argv[1]); if (!hostent) { - printf("Lookup failed for '%s'\n", argv[1]); + fprintf(stderr, "Lookup failed for '%s'\n", argv[1]); return 1; }