summaryrefslogtreecommitdiff
path: root/lib/mlibc/options/posix/generic/lookup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mlibc/options/posix/generic/lookup.cpp')
-rw-r--r--lib/mlibc/options/posix/generic/lookup.cpp512
1 files changed, 512 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/lookup.cpp b/lib/mlibc/options/posix/generic/lookup.cpp
new file mode 100644
index 0000000..f877fe5
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/lookup.cpp
@@ -0,0 +1,512 @@
+#include <mlibc/lookup.hpp>
+#include <mlibc/resolv_conf.hpp>
+#include <mlibc/debug.hpp>
+#include <bits/ensure.h>
+
+#include <frg/string.hpp>
+#include <mlibc/allocator.hpp>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+
+namespace mlibc {
+
+namespace {
+ constexpr unsigned int RECORD_A = 1;
+ constexpr unsigned int RECORD_CNAME = 5;
+ constexpr unsigned int RECORD_PTR = 12;
+}
+
+static frg::string<MemoryAllocator> read_dns_name(char *buf, char *&it) {
+ frg::string<MemoryAllocator> res{getAllocator()};
+ while (true) {
+ char code = *it++;
+ if ((code & 0xC0) == 0xC0) {
+ // pointer
+ uint8_t offset = ((code & 0x3F) << 8) | *it++;
+ auto offset_it = buf + offset;
+ return res + read_dns_name(buf, offset_it);
+ } else if (!(code & 0xC0)) {
+ if (!code)
+ break;
+
+ for (int i = 0; i < code; i++)
+ res += (*it++);
+
+ if (*it)
+ res += '.';
+ } else {
+ break;
+ }
+ }
+
+ return res;
+}
+
+int lookup_name_dns(struct lookup_result &buf, const char *name,
+ frg::string<MemoryAllocator> &canon_name) {
+ frg::string<MemoryAllocator> request{getAllocator()};
+
+ int num_q = 1;
+ struct dns_header header;
+ header.identification = htons(123);
+ header.flags = htons(0x100);
+ header.no_q = htons(num_q);
+ header.no_ans = htons(0);
+ header.no_auths = htons(0);
+ header.no_additional = htons(0);
+
+ request.resize(sizeof(header));
+ memcpy(request.data(), &header, sizeof(header));
+
+ const char *end = name;
+ while (*end != '\0') {
+ end = strchrnul(name, '.');
+ size_t length = end - name;
+ frg::string_view substring{name, length};
+ name += length + 1;
+ request += char(length);
+ request += substring;
+ }
+
+ request += char(0);
+ // set question type to fetch A records
+ request += 0;
+ request += 1;
+ // set CLASS to IN
+ request += 0;
+ request += 1;
+
+ struct sockaddr_in sin = {};
+ sin.sin_family = AF_INET;
+ // TODO(geert): we could probably make this use the service lookup
+ // for dns
+ sin.sin_port = htons(53);
+
+ auto nameserver = get_nameserver();
+ if (!inet_aton(nameserver ? nameserver->name.data() : "127.0.0.1", &sin.sin_addr)) {
+ mlibc::infoLogger() << "lookup_name_dns(): inet_aton() failed!" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ mlibc::infoLogger() << "lookup_name_dns(): socket() failed" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ size_t sent = sendto(fd, request.data(), request.size(), 0,
+ (struct sockaddr*)&sin, sizeof(sin));
+ if (sent != request.size()) {
+ mlibc::infoLogger() << "lookup_name_dns(): sendto() failed to send everything" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ char response[256];
+ ssize_t rlen;
+ int num_ans = 0;
+ while ((rlen = recvfrom(fd, response, 256, 0, NULL, NULL)) >= 0) {
+ if ((size_t)rlen < sizeof(struct dns_header))
+ continue;
+ auto response_header = reinterpret_cast<struct dns_header*>(response);
+ if (response_header->identification != header.identification)
+ return -EAI_FAIL;
+
+ auto it = response + sizeof(struct dns_header);
+ for (int i = 0; i < ntohs(response_header->no_q); i++) {
+ auto dns_name = read_dns_name(response, it);
+ (void) dns_name;
+ it += 4;
+ }
+
+ for (int i = 0; i < ntohs(response_header->no_ans); i++) {
+ struct dns_addr_buf buffer;
+ auto dns_name = read_dns_name(response, it);
+
+ uint16_t rr_type = (it[0] << 8) | it[1];
+ uint16_t rr_class = (it[2] << 8) | it[3];
+ uint16_t rr_length = (it[8] << 8) | it[9];
+ it += 10;
+ (void)rr_class;
+
+ switch (rr_type) {
+ case RECORD_A:
+ memcpy(buffer.addr, it, rr_length);
+ it += rr_length;
+ buffer.family = AF_INET;
+ buffer.name = std::move(dns_name);
+ buf.buf.push(std::move(buffer));
+ break;
+ case RECORD_CNAME:
+ canon_name = read_dns_name(response, it);
+ buf.aliases.push(std::move(dns_name));
+ break;
+ default:
+ mlibc::infoLogger() << "lookup_name_dns: unknown rr type "
+ << rr_type << frg::endlog;
+ break;
+ }
+ }
+ num_ans += ntohs(response_header->no_ans);
+
+ if (num_ans >= num_q)
+ break;
+ }
+
+ close(fd);
+ return buf.buf.size();
+}
+
+int lookup_addr_dns(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family) {
+ frg::string<MemoryAllocator> request{getAllocator()};
+
+ int num_q = 1;
+ struct dns_header header;
+ header.identification = htons(123);
+ header.flags = htons(0x100);
+ header.no_q = htons(num_q);
+ header.no_ans = htons(0);
+ header.no_auths = htons(0);
+ header.no_additional = htons(0);
+
+ request.resize(sizeof(header));
+ memcpy(request.data(), &header, sizeof(header));
+
+ char addr_str[64];
+ if(!inet_ntop(family, addr.data(), addr_str, sizeof(addr_str))) {
+ switch(errno) {
+ case EAFNOSUPPORT:
+ return -EAI_FAMILY;
+ case ENOSPC:
+ return -EAI_OVERFLOW;
+ default:
+ return -EAI_FAIL;
+ }
+ }
+ frg::string<MemoryAllocator> req_str{getAllocator(), addr_str};
+ req_str += ".in-addr.arpa";
+
+ frg::string_view req_view{req_str.data(), req_str.size()};
+ size_t ptr = 0;
+ do {
+ size_t next = req_view.find_first('.', ptr);
+ size_t length = next != (size_t)-1 ? next - ptr : req_view.size() - ptr;
+ frg::string_view substring = req_view.sub_string(ptr, length);
+ request += char(length);
+ request += substring;
+ ptr = next + 1;
+ } while(ptr != 0);
+
+ request += char(0);
+ // set question type to fetch PTR records
+ request += 0;
+ request += 12;
+ // set CLASS to IN
+ request += 0;
+ request += 1;
+
+
+ struct sockaddr_in sin = {};
+ sin.sin_family = AF_INET;
+ // TODO(geert): we could probably make this use the service lookup
+ // for dns
+ sin.sin_port = htons(53);
+
+ auto nameserver = get_nameserver();
+ if (!inet_aton(nameserver ? nameserver->name.data() : "127.0.0.1", &sin.sin_addr)) {
+ mlibc::infoLogger() << "lookup_name_dns(): inet_aton() failed!" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ mlibc::infoLogger() << "lookup_name_dns(): socket() failed" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ size_t sent = sendto(fd, request.data(), request.size(), 0,
+ (struct sockaddr*)&sin, sizeof(sin));
+ if (sent != request.size()) {
+ mlibc::infoLogger() << "lookup_name_dns(): sendto() failed to send everything" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ char response[256];
+ ssize_t rlen;
+ int num_ans = 0;
+ while ((rlen = recvfrom(fd, response, 256, 0, NULL, NULL)) >= 0) {
+ if ((size_t)rlen < sizeof(struct dns_header))
+ continue;
+ auto response_header = reinterpret_cast<struct dns_header*>(response);
+ if (response_header->identification != header.identification)
+ return -EAI_FAIL;
+
+ auto it = response + sizeof(struct dns_header);
+ for (int i = 0; i < ntohs(response_header->no_q); i++) {
+ auto dns_name = read_dns_name(response, it);
+ (void) dns_name;
+ it += 4;
+ }
+
+ for (int i = 0; i < ntohs(response_header->no_ans); i++) {
+ struct dns_addr_buf buffer;
+ auto dns_name = read_dns_name(response, it);
+
+ uint16_t rr_type = (it[0] << 8) | it[1];
+ uint16_t rr_class = (it[2] << 8) | it[3];
+ uint16_t rr_length = (it[8] << 8) | it[9];
+ it += 10;
+ (void)rr_class;
+ (void)rr_length;
+
+ (void)dns_name;
+
+ switch (rr_type) {
+ case RECORD_PTR: {
+ auto ptr_name = read_dns_name(response, it);
+ if (ptr_name.size() >= name.size())
+ return -EAI_OVERFLOW;
+ std::copy(ptr_name.begin(), ptr_name.end(), name.data());
+ name.data()[ptr_name.size()] = '\0';
+ return 1;
+ }
+ default:
+ mlibc::infoLogger() << "lookup_addr_dns: unknown rr type "
+ << rr_type << frg::endlog;
+ break;
+ }
+ num_ans += ntohs(response_header->no_ans);
+
+ if (num_ans >= num_q)
+ break;
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+
+int lookup_name_hosts(struct lookup_result &buf, const char *name,
+ frg::string<MemoryAllocator> &canon_name) {
+ auto file = fopen("/etc/hosts", "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ char line[128];
+ int name_length = strlen(name);
+ while (fgets(line, 128, file)) {
+ char *pos;
+ // same way to deal with comments as in services.cpp
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+
+ for(pos = line + 1; (pos = strstr(pos, name)) &&
+ (!isspace(pos[-1]) || !isspace(pos[name_length])); pos++);
+ if (!pos)
+ continue;
+
+ for (pos = line; !isspace(*pos); pos++);
+ *pos = '\0';
+
+ // TODO(geert): we assume ipv4 for now
+ struct in_addr addr;
+ if (!inet_aton(line, &addr))
+ continue;
+
+ pos++;
+ for(; *pos && isspace(*pos); pos++);
+ char *end;
+ for(end = pos; *end && !isspace(*end); end++);
+
+ struct dns_addr_buf buffer;
+ memcpy(buffer.addr, &addr, 4);
+ buffer.family = AF_INET;
+ buffer.name = frg::string<MemoryAllocator>{pos,
+ static_cast<size_t>(end - pos), getAllocator()};
+ canon_name = buffer.name;
+
+ buf.buf.push(std::move(buffer));
+
+ pos = end;
+ while (pos[1]) {
+ for (; *pos && isspace(*pos); pos++);
+ for (end = pos; *end && !isspace(*end); end++);
+ auto name = frg::string<MemoryAllocator>{pos,
+ static_cast<size_t>(end - pos), getAllocator()};
+ buf.aliases.push(std::move(name));
+ pos = end;
+ }
+ }
+
+ fclose(file);
+ return buf.buf.size();
+}
+
+int lookup_addr_hosts(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family) {
+ auto file = fopen("/etc/hosts", "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ // Buffer to hold ASCII version of address
+ char addr_str[64];
+ if(!inet_ntop(family, addr.data(), addr_str, sizeof(addr_str))) {
+ switch(errno) {
+ case EAFNOSUPPORT:
+ return -EAI_FAMILY;
+ case ENOSPC:
+ return -EAI_OVERFLOW;
+ default:
+ return -EAI_FAIL;
+ }
+ }
+ int addr_str_len = strlen(addr_str);
+
+ char line[128];
+ while (fgets(line, 128, file)) {
+ char *pos;
+ // same way to deal with comments as in services.cpp
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+ if (strncmp(line, addr_str, addr_str_len))
+ continue;
+
+ for (pos = line + addr_str_len + 1; isspace(*pos); pos++);
+ char *begin = pos;
+ for (; !isspace(*pos); pos++);
+ char *end = pos;
+
+ size_t size = end - begin;
+ if (size >= name.size())
+ return -EAI_OVERFLOW;
+ std::copy(begin, end, name.data());
+ name.data()[size] = '\0';
+ return 1;
+ }
+ return 0;
+}
+
+int lookup_name_null(struct lookup_result &buf, int flags, int family) {
+ if (flags & AI_PASSIVE) {
+ if (family != AF_INET6) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+
+ in_addr_t addr = INADDR_ANY;
+ memcpy(&addr_buf.addr, &addr, 4);
+
+ buf.buf.push_back(addr_buf);
+ }
+ if (family != AF_INET) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+
+ struct in6_addr addr = IN6ADDR_ANY_INIT;
+ memcpy(&addr_buf.addr, &addr, 16);
+
+ buf.buf.push_back(addr_buf);
+ }
+ } else {
+ if (family != AF_INET6) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+
+ in_addr_t addr = INADDR_LOOPBACK;
+ memcpy(&addr_buf.addr, &addr, 4);
+
+ buf.buf.push_back(addr_buf);
+ }
+ if (family != AF_INET) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+
+ struct in6_addr addr = IN6ADDR_LOOPBACK_INIT;
+ memcpy(&addr_buf.addr, &addr, 16);
+
+ buf.buf.push_back(addr_buf);
+ }
+ }
+ return buf.buf.size();
+}
+
+int lookup_name_ip(struct lookup_result &buf, const char *name, int family) {
+ if (family == AF_INET) {
+ in_addr_t addr = 0;
+ int res = inet_pton(AF_INET, name, &addr);
+
+ if (res <= 0)
+ return -EAI_NONAME;
+
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+ memcpy(&addr_buf.addr, &addr, 4);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+ }
+
+ if (family == AF_INET6) {
+ struct in6_addr addr{0};
+ int res = inet_pton(AF_INET6, name, &addr);
+
+ if (res <= 0)
+ return -EAI_NONAME;
+
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+ memcpy(&addr_buf.addr, &addr, 16);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+ }
+
+ // If no family was specified we try ipv4 and then ipv6.
+ in_addr_t addr4 = 0;
+ int res = inet_pton(AF_INET, name, &addr4);
+
+ if (res > 0) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+ memcpy(&addr_buf.addr, &addr4, 4);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+ }
+
+ struct in6_addr addr6{0};
+ res = inet_pton(AF_INET6, name, &addr6);
+
+ if (res <= 0)
+ return -EAI_NONAME;
+
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+ memcpy(&addr_buf.addr, &addr6, 16);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+}
+
+} // namespace mlibc