user: implement mlibc as the libc, finally.
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
@@ -0,0 +1,548 @@
|
||||
#include <mlibc/lookup.hpp>
|
||||
#include <mlibc/resolv_conf.hpp>
|
||||
#include <mlibc/debug.hpp>
|
||||
#include <mlibc/services.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 short RETURN_NOERROR [[maybe_unused]] = 0x0;
|
||||
constexpr unsigned short RETURN_NXDOMAIN = 0x3;
|
||||
|
||||
constexpr unsigned int RECORD_A = 1;
|
||||
constexpr unsigned int RECORD_CNAME = 5;
|
||||
constexpr unsigned int RECORD_PTR = 12;
|
||||
constexpr unsigned int RECORD_AAAA = 28;
|
||||
} // namespace
|
||||
|
||||
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, 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));
|
||||
|
||||
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 or AAAA records
|
||||
uint16_t qtype = RECORD_A;
|
||||
if (family == AF_INET6)
|
||||
qtype = RECORD_AAAA;
|
||||
|
||||
request += qtype >> 8;
|
||||
request += qtype & 0xFF;
|
||||
// set CLASS to IN
|
||||
request += 0;
|
||||
request += 1;
|
||||
|
||||
mlibc::service_result serv_buf{getAllocator()};
|
||||
int serv_count = mlibc::lookup_serv_by_name(serv_buf, "domain", IPPROTO_UDP, SOCK_DGRAM, 0);
|
||||
if (serv_count < 0) {
|
||||
mlibc::infoLogger() << "mlibc: could not resolve DNS service" << frg::endlog;
|
||||
return -EAI_SERVICE;
|
||||
}
|
||||
|
||||
struct sockaddr_in sin = {};
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(serv_buf[0].port);
|
||||
|
||||
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, nullptr, nullptr)) >= 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;
|
||||
|
||||
if ((ntohs(response_header->flags) & 0xF) == RETURN_NXDOMAIN)
|
||||
return -EAI_NONAME;
|
||||
|
||||
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:
|
||||
if (family != AF_UNSPEC && family != AF_INET)
|
||||
continue;
|
||||
|
||||
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_AAAA:
|
||||
if (family != AF_UNSPEC && family != AF_INET6)
|
||||
continue;
|
||||
|
||||
memcpy(buffer.addr, it, rr_length);
|
||||
it += rr_length;
|
||||
buffer.family = AF_INET6;
|
||||
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;
|
||||
|
||||
mlibc::service_result serv_buf{getAllocator()};
|
||||
int serv_count = mlibc::lookup_serv_by_name(serv_buf, "domain", IPPROTO_UDP, SOCK_DGRAM, 0);
|
||||
if (serv_count < 0) {
|
||||
mlibc::infoLogger() << "mlibc: could not resolve DNS service" << frg::endlog;
|
||||
return -EAI_SERVICE;
|
||||
}
|
||||
|
||||
struct sockaddr_in sin = {};
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(serv_buf[0].port);
|
||||
|
||||
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, nullptr, nullptr)) >= 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, 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;
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
struct dns_addr_buf buffer;
|
||||
|
||||
if ((family == AF_UNSPEC || family == AF_INET) && inet_pton(AF_INET, line, buffer.addr)) {
|
||||
buffer.family = AF_INET;
|
||||
} else if((family == AF_UNSPEC || family == AF_INET6) && inet_pton(AF_INET6, line, buffer.addr)) {
|
||||
buffer.family = AF_INET6;
|
||||
} else {
|
||||
continue; // not a valid address
|
||||
}
|
||||
|
||||
pos++;
|
||||
for(; *pos && isspace(*pos); pos++);
|
||||
char *end;
|
||||
for(end = pos; *end && !isspace(*end); end++);
|
||||
|
||||
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{};
|
||||
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{};
|
||||
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
|
||||
Reference in New Issue
Block a user