From 0e4a1936ca3767c0d1ab2953b3061c5324bf4834 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 20 Mar 2019 01:15:22 +0100 Subject: [PATCH] LibC: Implement gethostbyname() by talking to the DNSLookupServer. We now talk to the lookup server over a local socket and it does the lookup on our behalf. Including some retry logic, which is nice, because it seems like DNS requests disappear in the ether pretty damn often where I am. --- Kernel/init.cpp | 8 +- Kernel/sync.sh | 1 + LibC/netdb.cpp | 80 +++++++++++++++---- Servers/DNSLookupServer/main.cpp | 130 +++++++++++++++++++++++++------ Userland/.gitignore | 1 + Userland/Makefile | 5 ++ Userland/host.cpp | 24 ++++++ 7 files changed, 213 insertions(+), 36 deletions(-) create mode 100644 Userland/host.cpp diff --git a/Kernel/init.cpp b/Kernel/init.cpp index a957b112d7c..728a72c411a 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -85,9 +85,15 @@ VFS* vfs; int error; + auto* dns_lookup_server_process = Process::create_user_process("/bin/DNSLookupServer", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, { }, tty0); + if (error != 0) { + dbgprintf("error spawning DNSLookupServer: %d\n", error); + hang(); + } + auto* window_server_process = Process::create_user_process("/bin/WindowServer", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, { }, tty0); if (error != 0) { - dbgprintf("error: %d\n", error); + dbgprintf("error spawning WindowServer: %d\n", error); hang(); } window_server_process->set_priority(Process::HighPriority); diff --git a/Kernel/sync.sh b/Kernel/sync.sh index 6836b774783..be6c3bbbfb7 100755 --- a/Kernel/sync.sh +++ b/Kernel/sync.sh @@ -77,6 +77,7 @@ cp -v ../Userland/stat mnt/bin/stat cp -v ../Userland/ping mnt/bin/ping cp -v ../Userland/uc mnt/bin/uc cp -v ../Userland/tc mnt/bin/tc +cp -v ../Userland/host mnt/bin/host chmod 4755 mnt/bin/su cp -v ../Applications/Terminal/Terminal mnt/bin/Terminal cp -v ../Applications/FontEditor/FontEditor mnt/bin/FontEditor diff --git a/LibC/netdb.cpp b/LibC/netdb.cpp index 3be3bc868ed..a03e34f5500 100644 --- a/LibC/netdb.cpp +++ b/LibC/netdb.cpp @@ -3,29 +3,83 @@ #include #include #include +#include +#include +#include +#include extern "C" { static hostent __gethostbyname_buffer; +static char __gethostbyname_name_buffer[512]; static in_addr_t __gethostbyname_address; static in_addr_t* __gethostbyname_address_list_buffer[2]; hostent* gethostbyname(const char* name) { - if (!strcmp(name, "boards.4channel.org")) { - __gethostbyname_buffer.h_name = "boards.4channel.org"; - __gethostbyname_buffer.h_aliases = nullptr; - __gethostbyname_buffer.h_addrtype = AF_INET; - __gethostbyname_address = 0x4b4f1168; - __gethostbyname_address_list_buffer[0] = &__gethostbyname_address; - __gethostbyname_address_list_buffer[1] = nullptr; - __gethostbyname_buffer.h_addr_list = (char**)__gethostbyname_address_list_buffer; - __gethostbyname_buffer.h_length = 4; - return &__gethostbyname_buffer; + int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("socket"); + return nullptr; } - dbgprintf("FIXME(LibC): gethostbyname(%s)\n", name); - return nullptr; + + sockaddr_un address; + address.sun_family = AF_LOCAL; + strcpy(address.sun_path, "/tmp/.DNSLookupServer-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); + int nsent = write(fd, line.characters(), line.length()); + if (nsent < 0) { + perror("send"); + 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; + } + buffer[nrecv] = '\0'; + close(fd); + + if (!memcmp(buffer, "Not found.", sizeof("Not found.") - 1)) + return nullptr; + + rc = inet_pton(AF_INET, buffer, &__gethostbyname_address); + if (rc < 0) + return nullptr; + + strncpy(__gethostbyname_name_buffer, name, strlen(name)); + + __gethostbyname_buffer.h_name = __gethostbyname_name_buffer; + __gethostbyname_buffer.h_aliases = nullptr; + __gethostbyname_buffer.h_addrtype = AF_INET; + __gethostbyname_address_list_buffer[0] = &__gethostbyname_address; + __gethostbyname_address_list_buffer[1] = nullptr; + __gethostbyname_buffer.h_addr_list = (char**)__gethostbyname_address_list_buffer; + __gethostbyname_buffer.h_length = 4; + + return &__gethostbyname_buffer; } } - diff --git a/Servers/DNSLookupServer/main.cpp b/Servers/DNSLookupServer/main.cpp index 233410a86ad..9f280c0f503 100644 --- a/Servers/DNSLookupServer/main.cpp +++ b/Servers/DNSLookupServer/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -22,7 +23,7 @@ #define C_IN 1 -static Vector lookup(const String& hostname); +static Vector lookup(const String& hostname, bool& did_timeout); static String parse_dns_name(const byte*, int& offset, int max_offset); int main(int argc, char**argv) @@ -30,24 +31,105 @@ int main(int argc, char**argv) (void)argc; (void)argv; - String hostname = "disney.com"; - - if (argc == 2) { - hostname = argv[1]; - } + unlink("/tmp/.DNSLookupServer-socket"); HashMap dns_cache; - auto ipv4_addresses = lookup(hostname); - if (ipv4_addresses.is_empty()) { - printf("Lookup failed\n"); - } else { - printf("DNS lookup result:\n"); - for (auto& ipv4_address : ipv4_addresses) { - printf(" '%s' => %s\n", hostname.characters(), ipv4_address.to_string().characters()); - } + int server_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (server_fd < 0) { + perror("socket"); + return 1; } + sockaddr_un address; + address.sun_family = AF_LOCAL; + strcpy(address.sun_path, "/tmp/.DNSLookupServer-socket"); + + int rc = bind(server_fd, (const sockaddr*)&address, sizeof(address)); + if (rc < 0) { + perror("bind"); + return 1; + } + rc = listen(server_fd, 5); + if (rc < 0) { + perror("listen"); + return 1; + } + + for (;;) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(server_fd, &rfds); + rc = select(server_fd + 1, &rfds, nullptr, nullptr, nullptr); + if (rc < 1) { + perror("select"); + return 1; + } + + sockaddr_un client_address; + socklen_t client_address_size = sizeof(client_address); + int client_fd = accept(server_fd, (sockaddr*)&client_address, &client_address_size); + if (client_fd < 0) { + perror("accept"); + continue; + } + + FD_ZERO(&rfds); + FD_SET(client_fd, &rfds); + rc = select(client_fd + 1, &rfds, nullptr, nullptr, nullptr); + if (rc < 1) { + perror("select"); + return 1; + } + + char client_buffer[1024]; + int nrecv = read(client_fd, client_buffer, sizeof(client_buffer) - 1); + if (nrecv < 0) { + perror("recv"); + close(client_fd); + continue; + } + + client_buffer[nrecv] = '\0'; + + auto hostname = String(client_buffer, nrecv, Chomp); + dbgprintf("DNSLookupServer: Got request for '%s'\n", hostname.characters()); + + Vector addresses; + + if (!hostname.is_empty()) { + bool did_timeout; + int retries = 3; + do { + did_timeout = false; + addresses = lookup(hostname, did_timeout); + if (!did_timeout) + break; + } while (--retries); + if (did_timeout) { + fprintf(stderr, "DNSLookupServer: Out of retries :(\n"); + close(client_fd); + continue; + } + } + + if (addresses.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()); + int nsent = write(client_fd, line.characters(), line.length()); + if (nsent < 0) { + perror("write"); + break; + } + } + close(client_fd); + } return 0; } @@ -57,7 +139,7 @@ static word get_next_id() return ++s_next_id; } -Vector lookup(const String& hostname) +Vector lookup(const String& hostname, bool& did_timeout) { // FIXME: First check if it's an IP address in a string! @@ -91,7 +173,7 @@ Vector lookup(const String& hostname) return { }; } - struct timeval timeout { 5, 0 }; + struct timeval timeout { 1, 0 }; int rc = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); if (rc < 0) { perror("setsockopt"); @@ -118,7 +200,11 @@ Vector lookup(const String& hostname) byte response_buffer[4096]; ssize_t nrecv = recvfrom(fd, response_buffer, sizeof(response_buffer) - 1, 0, (struct sockaddr*)&src_addr, &src_addr_len); if (nrecv < 0) { - perror("recvfrom"); + if (errno == EAGAIN) { + did_timeout = true; + } else { + perror("recvfrom"); + } close(fd); return { }; } @@ -127,7 +213,7 @@ Vector lookup(const String& hostname) response_buffer[nrecv] = '\0'; if (nrecv < (int)sizeof(DNSPacket)) { - printf("Response not big enough (%d) to be a DNS packet :(\n", nrecv); + dbgprintf("DNSLookupServer: Response not big enough (%d) to be a DNS packet :(\n", nrecv); return { }; } @@ -139,15 +225,15 @@ Vector lookup(const String& hostname) //printf("Additional count: %u\n", response_header.additional_count()); if (response_header.id() != request_header.id()) { - printf("ID mismatch (%u vs %u) :(\n", response_header.id(), request_header.id()); + dbgprintf("DNSLookupServer: ID mismatch (%u vs %u) :(\n", response_header.id(), request_header.id()); return { }; } if (response_header.question_count() != 1) { - printf("Question count (%u vs %u) :(\n", response_header.question_count(), request_header.question_count()); + dbgprintf("DNSLookupServer: Question count (%u vs %u) :(\n", response_header.question_count(), request_header.question_count()); return { }; } if (response_header.answer_count() < 1) { - printf("Not enough answers (%u) :(\n", response_header.answer_count()); + dbgprintf("DNSLookupServer: Not enough answers (%u) :(\n", response_header.answer_count()); return { }; } @@ -160,7 +246,7 @@ Vector lookup(const String& hostname) 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()); - printf(" Answer #%u: (question: %s), ttl=%u, length=%u, data=%s\n", + dbgprintf("DNSLookupServer: Answer #%u: (question: %s), ttl=%u, length=%u, data=%s\n", i, question.characters(), record.ttl(), diff --git a/Userland/.gitignore b/Userland/.gitignore index 65bc7283b95..b3e65808989 100644 --- a/Userland/.gitignore +++ b/Userland/.gitignore @@ -40,3 +40,4 @@ stat ping uc tc +host diff --git a/Userland/Makefile b/Userland/Makefile index 84d425e40bf..b9e65cb5fa7 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -36,6 +36,7 @@ OBJS = \ ping.o \ uc.o \ tc.o \ + host.o \ rm.o APPS = \ @@ -77,6 +78,7 @@ APPS = \ ping \ uc \ tc \ + host \ rm ARCH_FLAGS = @@ -213,6 +215,9 @@ uc: uc.o tc: tc.o $(LD) -o $@ $(LDFLAGS) $< -lc +host: host.o + $(LD) -o $@ $(LDFLAGS) $< -lc + .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Userland/host.cpp b/Userland/host.cpp new file mode 100644 index 00000000000..179e5a649eb --- /dev/null +++ b/Userland/host.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc < 2) { + printf("usage: host \n"); + return 0; + } + + auto* hostent = gethostbyname(argv[1]); + if (!hostent) { + printf("Lookup failed for '%s'\n", argv[1]); + return 1; + } + + char buffer[32]; + const char* ip_str = inet_ntop(AF_INET, hostent->h_addr_list[0], buffer, sizeof(buffer)); + + printf("%s is %s\n", argv[1], ip_str); + return 0; +}