summaryrefslogtreecommitdiff
path: root/userland/libc/netdb
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2024-06-20 23:13:51 +0200
committerAnton Kling <anton@kling.gg>2024-06-20 23:13:51 +0200
commit9725791024d159ba0c5b6e8eef13a0e9077523a9 (patch)
tree91555c2f74e3d0794964f9f9cbb948b62550e46b /userland/libc/netdb
parentd7f2c29a6b238d678c6f76237b35b14eda258e7d (diff)
Add getaddrinfo and change other libc stuff
Diffstat (limited to 'userland/libc/netdb')
-rw-r--r--userland/libc/netdb/getaddrinfo.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/userland/libc/netdb/getaddrinfo.c b/userland/libc/netdb/getaddrinfo.c
new file mode 100644
index 0000000..d550b39
--- /dev/null
+++ b/userland/libc/netdb/getaddrinfo.c
@@ -0,0 +1,239 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <tb/sv.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#define PORT 53
+
+u16 add_domain_label(u8 *buffer, struct sv label) {
+ assert(label.length < 64);
+ u16 size = 0;
+ u8 ll = label.length;
+ memcpy(buffer + size, &ll, sizeof(u8));
+ size += sizeof(u8);
+ memcpy(buffer + size, label.s, label.length);
+ size += label.length;
+ return size;
+}
+
+u16 send_domain_request(int sockfd, const char *domain, u16 *id) {
+ *id = 0;
+ u8 message[512];
+ memset(message, 0, sizeof(message));
+
+ u16 size = 0;
+ size += 6 * sizeof(u16); // header
+
+ u16 flags = htons(0x0100);
+ u16 qdcount = htons(1);
+ memcpy(message, id, sizeof(u16));
+ memcpy(message + sizeof(u16), &flags, sizeof(u16));
+ memcpy(message + sizeof(u16) * 2, &qdcount, sizeof(u16));
+
+ struct sv labels = C_TO_SV(domain);
+ for (; !sv_isempty(labels);) {
+ struct sv label = sv_split_delim(labels, &labels, '.');
+ size += add_domain_label(message + size, label);
+ }
+
+ size += add_domain_label(message + size, C_TO_SV(""));
+
+ u16 query_type = htons(1); // A type
+ memcpy(message + size, &query_type, sizeof(u16));
+ size += sizeof(u16);
+
+ u16 q_class = htons(1); // IN
+ memcpy(message + size, &q_class, sizeof(u16));
+ size += sizeof(u16);
+
+ printf("sending: %d\n", size);
+
+ write(sockfd, (char *)message, size);
+ return size;
+}
+
+int connect_dns(void) {
+ int sockfd;
+ struct sockaddr_in servaddr;
+
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_addr.a[0] = 8;
+ servaddr.sin_addr.a[1] = 8;
+ servaddr.sin_addr.a[2] = 8;
+ servaddr.sin_addr.a[3] = 8;
+ servaddr.sin_port = htons(PORT);
+ servaddr.sin_family = AF_INET;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
+ return -1;
+ }
+ return sockfd;
+}
+
+/*
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = type;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_protocol = 0;
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+*/
+
+#define EAI_AGAIN 1
+#define EAI_FAIL 2
+#define EAI_NONAME 3
+#define EAI_MEMORY 4
+
+u16 service_to_port(const char *service) {
+ int is_number = 0;
+ for (int i = 0; service[i]; i++) {
+ if (isdigit(service[i])) {
+ is_number = 1;
+ }
+ }
+ if (is_number) {
+ return htons(atoi(service));
+ }
+
+#define SERVICE(_name, _port) \
+ if (0 == strcmp(_name, service)) { \
+ return htons(_port); \
+ }
+ SERVICE("http", 80)
+ SERVICE("https", 443)
+#undef SERVICE
+ return 0;
+}
+
+int getaddrinfo(const char *restrict node, const char *restrict service,
+ const struct addrinfo *restrict hints,
+ struct addrinfo **restrict res) {
+ int sockfd = connect_dns();
+ if (-1 == sockfd) {
+ return EAI_AGAIN;
+ }
+
+ // TODO: Use the hints to make decisions about what data to request.
+ (void)hints;
+
+ u16 id;
+ u16 size = send_domain_request(sockfd, node, &id);
+
+ u8 buffer[512];
+ memset(buffer, 0, sizeof(buffer));
+ int rc = read(sockfd, buffer, sizeof(buffer));
+ if (rc < size) {
+ close(sockfd);
+ return EAI_FAIL;
+ }
+
+ if (0 != memcmp(buffer, &id, sizeof(u16))) {
+ close(sockfd);
+ return EAI_FAIL;
+ }
+
+ u16 flags;
+ memcpy(&flags, buffer + sizeof(u16), sizeof(u16));
+ flags = ntohs(flags);
+
+ if (!(flags & (1 << 15))) { // QR
+ close(sockfd);
+ return EAI_FAIL;
+ }
+ if (0 != (flags & 0x1F)) { // Error field
+ close(sockfd);
+ return EAI_AGAIN;
+ }
+
+ u16 qcount = ntohs(*(u16 *)(buffer + sizeof(u16) * 2));
+ if (1 != qcount) {
+ close(sockfd);
+ return EAI_FAIL;
+ }
+
+ u16 ancount = ntohs(*(u16 *)(buffer + sizeof(u16) * 3));
+ if (1 != ancount) {
+ close(sockfd);
+ return EAI_NONAME; // TODO: Check if this is correct
+ }
+
+ u8 *answer = buffer + size;
+ u16 answer_length = rc - size;
+ if (0 == answer_length) {
+ close(sockfd);
+ return EAI_FAIL;
+ }
+
+ /*
+ // get labels
+ {
+ u16 offset = ntohs(*(u16 *)answer);
+ assert(3 == offset >> 14);
+ offset &= ~(3 << 14);
+
+ printf("offset: %d\n", offset);
+ printf("name: ");
+ u8 length = buffer[offset];
+ for (int i = 0; i < length; i++) {
+ printf("%c", buffer[offset + 1 + i]);
+ }
+ printf("\n");
+ }
+ */
+
+ // type
+ u16 type = ntohs(*(u16 *)(answer + sizeof(u16)));
+ u16 class = ntohs(*(u16 *)(answer + sizeof(u16) * 2));
+ if (1 != type) {
+ close(sockfd);
+ return EAI_FAIL;
+ }
+ if (1 != class) {
+ close(sockfd);
+ return EAI_FAIL;
+ }
+
+ u16 rdlength = ntohs(*(u16 *)(answer + sizeof(u16) * 3 + sizeof(u32)));
+
+ if (4 != rdlength) {
+ close(sockfd);
+ return EAI_FAIL;
+ }
+ u32 ip = *(u32 *)(answer + sizeof(u16) * 4 + sizeof(u32));
+
+ if (res) {
+ *res = calloc(1, sizeof(struct addrinfo));
+ if (!(*res)) {
+ close(sockfd);
+ return EAI_MEMORY;
+ }
+ struct sockaddr_in *sa = calloc(1, sizeof(struct sockaddr_in));
+ if (!sa) {
+ free(*res);
+ close(sockfd);
+ return EAI_MEMORY;
+ }
+ (*res)->ai_addr = (struct sockaddr *)sa;
+ sa->sin_addr.s_addr = ip;
+ sa->sin_port = service_to_port(service);
+ sa->sin_family = AF_INET;
+ }
+ close(sockfd);
+ return 0;
+}
+
+void freeaddrinfo(struct addrinfo *res) {
+ free(res->ai_addr);
+ free(res);
+}