From 4d8ee9840accbd8de80391275252548462179e6c Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Mon, 14 Jun 2021 14:18:23 +0900 Subject: [PATCH] net: hacked up udp stuff, dns resolver --- apps/dhclient.c | 3 + apps/fetch.c | 30 +++++- apps/nslookup.c | 2 +- base/usr/include/kernel/net/netif.h | 2 + base/usr/include/sys/socket.h | 4 + kernel/net/ipv4.c | 144 +++++++++++++++++++++++++- kernel/net/netif.c | 7 ++ kernel/net/socket.c | 7 +- libc/sys/network.c | 151 ++++++++++++++++++++++++++-- 9 files changed, 334 insertions(+), 16 deletions(-) diff --git a/apps/dhclient.c b/apps/dhclient.c index 990d35e0..ed0f5bef 100644 --- a/apps/dhclient.c +++ b/apps/dhclient.c @@ -386,6 +386,9 @@ static int configure_interface(const char * if_name) { char addr[16]; ip_ntoa(ntohl(ip_data), addr); printf("%s: %s: nameserver %s\n", _argv_0, if_name, addr); + FILE * resolve = fopen("/etc/resolv.conf","w"); + fprintf(resolve, "nameserver %s\n", addr); + fclose(resolve); } opt += len; } diff --git a/apps/fetch.c b/apps/fetch.c index afac1b37..7d939058 100644 --- a/apps/fetch.c +++ b/apps/fetch.c @@ -11,9 +11,10 @@ #include #include #include -#include #include #include +#include +#include #include @@ -289,9 +290,6 @@ int main(int argc, char * argv[]) { struct http_req my_req; parse_url(argv[optind], &my_req); - char file[600]; - sprintf(file, "/dev/net/%s", my_req.domain); - if (fetch_options.calculate_output) { char * tmp = strdup(my_req.path); char * x = strrchr(tmp,'/'); @@ -306,7 +304,29 @@ int main(int argc, char * argv[]) { fetch_options.out = fopen(fetch_options.output_file, "w+"); } - FILE * f = fopen(file,"r+"); + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket"); + return 1; + } + + struct hostent * remote = gethostbyname(my_req.domain); + + if (!remote) { + perror("gethostbyname"); + return 1; + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + memcpy(&addr.sin_addr.s_addr, remote->h_addr, remote->h_length); + + if (connect(sock, &addr, sizeof(struct sockaddr_in)) < 0) { + perror("connect"); + return 1; + } + + FILE * f = fdopen(sock,"w+"); if (!f) { fprintf(stderr, "Nope.\n"); diff --git a/apps/nslookup.c b/apps/nslookup.c index 0be41285..08ca23fc 100644 --- a/apps/nslookup.c +++ b/apps/nslookup.c @@ -32,7 +32,7 @@ int main(int argc, char * argv[]) { } char addr[16] = {0}; - ip_ntoa(*(uint32_t *)host->h_addr_list[0], addr); + ip_ntoa(ntohl(*(uint32_t *)host->h_addr_list[0]), addr); fprintf(stderr, "%s: %s\n", host->h_name, addr); return 0; diff --git a/base/usr/include/kernel/net/netif.h b/base/usr/include/kernel/net/netif.h index 00acd2f6..f161eaa4 100644 --- a/base/usr/include/kernel/net/netif.h +++ b/base/usr/include/kernel/net/netif.h @@ -19,6 +19,8 @@ typedef struct SockData { list_t * rx_wait; list_t * rx_queue; + uint16_t priv[4]; + long (*sock_recv)(struct SockData * sock, struct msghdr * msg, int flags); long (*sock_send)(struct SockData * sock, const struct msghdr *msg, int flags); void (*sock_close)(struct SockData * sock); diff --git a/base/usr/include/sys/socket.h b/base/usr/include/sys/socket.h index 169ec13b..0b2a5a37 100644 --- a/base/usr/include/sys/socket.h +++ b/base/usr/include/sys/socket.h @@ -32,6 +32,10 @@ struct hostent { char **h_addr_list; /* list of addresses */ }; +#ifndef _KERNEL_ +#define h_addr h_addr_list[0] +#endif + typedef size_t socklen_t; struct sockaddr { diff --git a/kernel/net/ipv4.c b/kernel/net/ipv4.c index 9b06c835..e8023dbd 100644 --- a/kernel/net/ipv4.c +++ b/kernel/net/ipv4.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,14 @@ struct icmp_header { uint8_t data[]; } __attribute__((packed)) __attribute__((aligned(2))); +struct udp_packet { + uint16_t source_port; + uint16_t destination_port; + uint16_t length; + uint16_t checksum; + uint8_t payload[]; +} __attribute__ ((packed)) __attribute__((aligned(2))); + #define IPV4_PROT_UDP 17 #define IPV4_PROT_TCP 6 @@ -112,6 +121,10 @@ static void icmp_handle(struct ipv4_packet * packet, const char * src, const cha } } +extern void net_sock_add(sock_t * sock, void * frame); + +static hashmap_t * udp_sockets = NULL; + void net_ipv4_handle(struct ipv4_packet * packet, fs_node_t * nic) { char dest[16]; @@ -124,21 +137,146 @@ void net_ipv4_handle(struct ipv4_packet * packet, fs_node_t * nic) { case 1: icmp_handle(packet, src, dest, nic); break; - case IPV4_PROT_UDP: + case IPV4_PROT_UDP: { + uint16_t dest_port = ntohs(((uint16_t*)&packet->payload)[1]); printf("net: ipv4: %s: %s -> %s udp %d to %d\n", nic->name, src, dest, ntohs(((uint16_t*)&packet->payload)[0]), ntohs(((uint16_t*)&packet->payload)[1])); + if (udp_sockets && hashmap_has(udp_sockets, (void*)(uintptr_t)dest_port)) { + printf("net: udp: received and have a waiting endpoint!\n"); + void * tmp = malloc(ntohs(packet->length)); + memcpy(tmp, packet, ntohs(packet->length)); + sock_t * sock = hashmap_get(udp_sockets, (void*)(uintptr_t)dest_port); + net_sock_add(sock, tmp); + } break; + } case IPV4_PROT_TCP: printf("net: ipv4: %s: %s -> %s tcp %d to %d\n", nic->name, src, dest, ntohs(((uint16_t*)&packet->payload)[0]), ntohs(((uint16_t*)&packet->payload)[1])); break; } } +extern fs_node_t * net_if_any(void); + +static spin_lock_t udp_port_lock = {0}; + +static int next_port = 12345; +static int udp_get_port(sock_t * sock) { + spin_lock(udp_port_lock); + int out = next_port++; + if (!udp_sockets) { + udp_sockets = hashmap_create_int(10); + } + hashmap_set(udp_sockets, (void*)(uintptr_t)out, sock); + sock->priv[0] = out; + spin_unlock(udp_port_lock); + return out; +} + +static long sock_udp_send(sock_t * sock, const struct msghdr *msg, int flags) { + printf("udp: send called\n"); + if (msg->msg_iovlen > 1) { + printf("net: todo: can't send multiple iovs\n"); + return -ENOTSUP; + } + if (msg->msg_iovlen == 0) return 0; + if (msg->msg_namelen != sizeof(struct sockaddr_in)) { + printf("udp: invalid destination address size %ld\n", msg->msg_namelen); + return -EINVAL; + } + + if (sock->priv[0] == 0) { + udp_get_port(sock); + printf("udp: assigning port %d to socket\n", sock->priv[0]); + } + + struct sockaddr_in * name = msg->msg_name; + + char dest[16]; + ip_ntoa(ntohl(name->sin_addr.s_addr), dest); + printf("udp: want to send to %s\n", dest); + + /* Routing: We need a device to send this on... */ + fs_node_t * nic = net_if_any(); + + size_t total_length = sizeof(struct ipv4_packet) + msg->msg_iov[0].iov_len + sizeof(struct udp_packet); + + struct ipv4_packet * response = malloc(total_length); + response->length = htons(total_length); + response->destination = name->sin_addr.s_addr; + response->source = ((struct EthernetDevice*)nic->device)->ipv4_addr; + response->ttl = 64; + response->protocol = IPV4_PROT_UDP; + response->ident = 0; + response->flags_fragment = htons(0x4000); + response->version_ihl = 0x45; + response->dscp_ecn = 0; + response->checksum = 0; + response->checksum = htons(calculate_ipv4_checksum(response)); + + /* Stick UDP header into payload */ + struct udp_packet * udp_packet = &response->payload; + udp_packet->source_port = htons(sock->priv[0]); + udp_packet->destination_port = name->sin_port; + udp_packet->length = htons(sizeof(struct udp_packet) + msg->msg_iov[0].iov_len); + udp_packet->checksum = 0; + + memcpy(response->payload + sizeof(struct udp_packet), msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); + + net_eth_send((struct EthernetDevice*)nic->device, ntohs(response->length), response, ETHERNET_TYPE_IPV4, ETHERNET_BROADCAST_MAC); + free(response); + + return 0; +} + +static long sock_udp_recv(sock_t * sock, struct msghdr * msg, int flags) { + printf("udp: recv called\n"); + if (!sock->priv[0]) { + printf("udp: recv() but socket has no port\n"); + return -EINVAL; + } + + if (msg->msg_iovlen > 1) { + printf("net: todo: can't recv multiple iovs\n"); + return -ENOTSUP; + } + if (msg->msg_iovlen == 0) return 0; + + struct ipv4_packet * data = net_sock_get(sock); + printf("udp: got response, size is %u - sizeof(ipv4) - sizeof(udp) = %lu\n", + ntohs(data->length), ntohs(data->length) - sizeof(struct ipv4_packet) - sizeof(struct udp_packet)); + memcpy(msg->msg_iov[0].iov_base, data->payload + 8, ntohs(data->length) - sizeof(struct ipv4_packet) - sizeof(struct udp_packet)); + + printf("udp: data copied to iov 0, return length?\n"); + + long resp = ntohs(data->length) - sizeof(struct ipv4_packet) - sizeof(struct udp_packet); + free(data); + return resp; +} + + +static void sock_udp_close(sock_t * sock) { + if (sock->priv[0] && udp_sockets) { + printf("udp: removing port %d from bound map\n", sock->priv[0]); + spin_lock(udp_port_lock); + hashmap_remove(udp_sockets, (void*)(uintptr_t)sock->priv[0]); + spin_unlock(udp_port_lock); + } +} + +static int udp_socket(void) { + printf("udp socket...\n"); + sock_t * sock = net_sock_create(); + sock->sock_recv = sock_udp_recv; + sock->sock_send = sock_udp_send; + sock->sock_close = sock_udp_close; + return process_append_fd((process_t *)this_core->current_process, (fs_node_t *)sock); +} + long net_ipv4_socket(int type, int protocol) { /* Ignore protocol, make socket for 'type' only... */ switch (type) { case SOCK_DGRAM: - printf("udp socket...\n"); - return -EINVAL; + return udp_socket(); case SOCK_STREAM: printf("tcp socket...\n"); return -EINVAL; diff --git a/kernel/net/netif.c b/kernel/net/netif.c index 8b1f128a..41e854a2 100644 --- a/kernel/net/netif.c +++ b/kernel/net/netif.c @@ -21,6 +21,7 @@ static hashmap_t * interfaces = NULL; extern list_t * net_raw_sockets_list; +static fs_node_t * _if_first = NULL; void net_install(void) { /* Set up virtual devices */ @@ -37,9 +38,15 @@ int net_add_interface(const char * name, fs_node_t * deviceNode) { snprintf(tmp,100,"/dev/net/%s", name); vfs_mount(tmp, deviceNode); + if (!_if_first) _if_first = deviceNode; + return 0; } fs_node_t * net_if_lookup(const char * name) { return hashmap_get(interfaces, name); } + +fs_node_t * net_if_any(void) { + return _if_first; +} diff --git a/kernel/net/socket.c b/kernel/net/socket.c index e6e01358..9a892146 100644 --- a/kernel/net/socket.c +++ b/kernel/net/socket.c @@ -196,22 +196,27 @@ long net_setsockopt(int sockfd, int level, int optname, const void *optval, sock } long net_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { + if (!FD_CHECK(sockfd)) return -EBADF; return -EINVAL; } long net_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + if (!FD_CHECK(sockfd)) return -EBADF; return -EINVAL; } long net_accept(int sockfd, struct sockaddr * addr, socklen_t * addrlen) { + if (!FD_CHECK(sockfd)) return -EBADF; return -EINVAL; } -long net_listen(int sockd, int backlog) { +long net_listen(int sockfd, int backlog) { + if (!FD_CHECK(sockfd)) return -EBADF; return -EINVAL; } long net_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + if (!FD_CHECK(sockfd)) return -EBADF; return -EINVAL; } diff --git a/libc/sys/network.c b/libc/sys/network.c index cb109d0e..ecfc67cf 100644 --- a/libc/sys/network.c +++ b/libc/sys/network.c @@ -162,11 +162,150 @@ int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { return -1; } -struct hostent * gethostbyname(const char * name) { - /* This formerly called into the kernel network device to perform - * DNS lookups, but we're going to resolve directly with a UDP DNS - * client with timeouts and everything, right here in the libc... */ - UNIMPLEMENTED; - return NULL; +static uint32_t ip_aton(const char * in) { + char ip[16]; + char * c = ip; + uint32_t out[4]; + char * i; + memcpy(ip, in, strlen(in) < 15 ? strlen(in) + 1 : 15); + ip[15] = '\0'; + + i = (char *)strchr(c, '.'); + *i = '\0'; + out[0] = atoi(c); + c += strlen(c) + 1; + + i = (char *)strchr(c, '.'); + *i = '\0'; + out[1] = atoi(c); + c += strlen(c) + 1; + + i = (char *)strchr(c, '.'); + *i = '\0'; + out[2] = atoi(c); + c += strlen(c) + 1; + + out[3] = atoi(c); + + return htonl((out[0] << 24) | (out[1] << 16) | (out[2] << 8) | (out[3])); +} + +static struct hostent _hostent = {0}; +static uint32_t _hostent_addr = 0; +static char * _host_entry_list[1] = {0}; + +struct dns_packet { + uint16_t qid; + uint16_t flags; + uint16_t questions; + uint16_t answers; + uint16_t authorities; + uint16_t additional; + uint8_t data[]; +} __attribute__((packed)) __attribute__((aligned(2))); + +struct hostent * gethostbyname(const char * name) { + /* Try to open /etc/resolv.conf */ + FILE * resolv = fopen("/etc/resolv.conf","r"); + if (!resolv) { + fprintf(stderr, "gethostbyname: no resolver\n"); + return NULL; + } + + /* Try to get a udp socket */ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + fprintf(stderr, "gethostbyname: could not get a socket\n"); + return NULL; + } + + /* Try to send something to the name server */ + char tmp[256]; + fread(tmp, 256, 1, resolv); + if (strncmp(tmp,"nameserver ",strlen("nameserver "))) { + fprintf(stderr, "gethostbyname: resolv doesn't look right?\n"); + } + + /* Try to convert so we can connect... */ + uint32_t ns_addr = ip_aton(tmp + strlen("nameserver ")); + fprintf(stderr, "gethostbyname: asking %#x\n", ns_addr); + + /* Form a DNS request */ + char dat[256]; + struct dns_packet * req = (struct dns_packet*)&dat; + uint16_t qid = rand() & 0xFFFF; + req->qid = htons(qid); + req->flags = htons(0x0100); + req->questions = htons(1); + req->answers = htons(0); + req->authorities = htons(0); + req->additional = htons(0); + + fprintf(stderr, "gethostbyname: Frobbing bits...\n"); + + /* Turn requested name into DNS request */ + ssize_t i = 0; + const char * c = name; + while (*c) { + const char * n = strchr(c,'.'); + if (!n) n = c + strlen(c); + ssize_t len = n - c; + fprintf(stderr, "gethostbyname: segment of len %zd\n", len); + req->data[i++] = len; + for (; c < n; ++c, ++i) { + req->data[i] = *c; + } + if (!*c) break; + c++; + } + req->data[i++] = 0x00; + req->data[i++] = 0x00; + req->data[i++] = 0x01; /* A */ + req->data[i++] = 0x00; + req->data[i++] = 0x01; /* IN */ + + struct sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_port = htons(53); + memcpy(&dest.sin_addr.s_addr, &ns_addr, sizeof(ns_addr)); + + if (sendto(sock, &dat, sizeof(struct dns_packet) + i, 0, &dest, sizeof(struct sockaddr_in)) < 0) { + fprintf(stderr, "gethostbyname: failed to send\n"); + return NULL; + } + + char buf[1550]; + /* TODO timeout... */ + ssize_t len = recv(sock, buf, 1550, 0); + if (len < 0) { + fprintf(stderr, "gethostbyname: failed to recv\n"); + return NULL; + } + + /* Now examine the response */ + fprintf(stderr, "gethostbyname: response from server\n"); + + struct dns_packet * response = (struct dns_packet *)&buf; + + if (ntohs(response->answers) == 0) { + fprintf(stderr, "gethostbyname: no answer\n"); + return NULL; + } + + fprintf(stderr, "gethostbyname: got %d answers\n", ntohs(response->answers)); + + /* XXX Just kinda blindly assume the answer is at the end */ + fprintf(stderr, "packet is of len %d, so answer must be %x\n", (int)len, *(uint32_t*)(buf+len-4)); + + /* Get a return value */ + _hostent.h_name = name; + _hostent.h_aliases = NULL; + _hostent.h_addrtype = AF_INET; + _hostent.h_length = sizeof(uint32_t); + _hostent.h_addr_list = &_host_entry_list; + _host_entry_list[0] = &_hostent_addr; + _hostent_addr = *(uint32_t*)(buf+len-4); + + return &_hostent; }