toaruos/libc/sys/network.c

441 lines
11 KiB
C

/*
* socket methods (mostly unimplemented)
*/
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <syscall.h>
#include <syscall_nums.h>
DEFN_SYSCALL3(socket, SYS_SOCKET, int, int, int);
DEFN_SYSCALL5(setsockopt, SYS_SETSOCKOPT, int,int,int,const void*,size_t);
DEFN_SYSCALL3(bind, SYS_BIND, int,const void*,size_t);
DEFN_SYSCALL4(accept, SYS_ACCEPT, int,void*,size_t*,int);
DEFN_SYSCALL2(listen, SYS_LISTEN, int,int);
DEFN_SYSCALL3(connect, SYS_CONNECT, int,const void*,size_t);
DEFN_SYSCALL5(getsockopt, SYS_GETSOCKOPT, int,int,int,void*,size_t*);
DEFN_SYSCALL3(recv, SYS_RECV, int,void*,int);
DEFN_SYSCALL3(send, SYS_SEND, int,const void*,int);
DEFN_SYSCALL2(shutdown, SYS_SHUTDOWN, int, int);
DEFN_SYSCALL3(getsockname, SYS_GETSOCKNAME, int,void*,size_t*);
DEFN_SYSCALL3(getpeername, SYS_GETPEERNAME, int,void*,size_t*);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
__sets_errno(syscall_connect(sockfd,addr,addrlen));
}
/* All of these should just be reads. */
ssize_t recv(int sockfd, void *buf, size_t len, int flags) {
struct iovec _iovec = {
buf, len
};
struct msghdr _header = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &_iovec,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0,
};
return recvmsg(sockfd, &_header, flags);
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) {
struct iovec _iovec = {
buf, len
};
struct msghdr _header = {
.msg_name = src_addr,
.msg_namelen = addrlen ? *addrlen : 0,
.msg_iov = &_iovec,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0,
};
ssize_t result = recvmsg(sockfd, &_header, flags);
if (addrlen) *addrlen = _header.msg_namelen;
return result;
}
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) {
__sets_errno(syscall_recv(sockfd,msg,flags));
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
struct iovec _iovec = {
(void*)buf, len
};
struct msghdr _header = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &_iovec,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0,
};
return sendmsg(sockfd, &_header, flags);
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
struct iovec _iovec = {
(void*)buf, len
};
struct msghdr _header = {
.msg_name = (void*)dest_addr,
.msg_namelen = addrlen,
.msg_iov = &_iovec,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0,
};
return sendmsg(sockfd, &_header, flags);
}
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
__sets_errno(syscall_send(sockfd,msg,flags));
}
int socket(int domain, int type, int protocol) {
/* Thin wrapper around a new system call, I guess. */
__sets_errno(syscall_socket(domain,type,protocol));
}
uint32_t htonl(uint32_t hostlong) {
return ( (((hostlong) & 0xFF) << 24) | (((hostlong) & 0xFF00) << 8) | (((hostlong) & 0xFF0000) >> 8) | (((hostlong) & 0xFF000000) >> 24));
}
uint16_t htons(uint16_t hostshort) {
return ( (((hostshort) & 0xFF) << 8) | (((hostshort) & 0xFF00) >> 8) );
}
uint32_t ntohl(uint32_t netlong) {
return htonl(netlong);
}
uint16_t ntohs(uint16_t netshort) {
return htons(netshort);
}
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
__sets_errno(syscall_bind(sockfd,addr,addrlen));
}
int accept(int sockfd, struct sockaddr * addr, socklen_t * addrlen) {
__sets_errno(syscall_accept(sockfd,addr,addrlen,0));
}
int accept4(int sockfd, struct sockaddr * addr, socklen_t * addrlen, int flags) {
__sets_errno(syscall_accept(sockfd,addr,addrlen,flags));
}
int listen(int sockfd, int backlog) {
__sets_errno(syscall_listen(sockfd,backlog));
}
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
__sets_errno(syscall_getsockopt(sockfd,level,optname,optval,optlen));
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
__sets_errno(syscall_setsockopt(sockfd,level,optname,optval,optlen));
}
int shutdown(int sockfd, int how) {
__sets_errno(syscall_shutdown(sockfd,how));
}
#define UNIMPLEMENTED fprintf(stderr, "[libnetwork] Unimplemented: %s\n", __FUNCTION__)
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
__sets_errno(syscall_getsockname(sockfd, addr, addrlen));
}
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
__sets_errno(syscall_getpeername(sockfd, addr, addrlen));
}
in_addr_t inet_addr(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]));
}
char * inet_ntoa(struct in_addr in) {
static char buf[17];
uint32_t hostOrder = ntohl(in.s_addr);
snprintf(buf,17,"%d.%d.%d.%d",
(hostOrder >> 24) & 0xFF,
(hostOrder >> 16) & 0xFF,
(hostOrder >> 8) & 0xFF,
(hostOrder >> 0) & 0xFF);
return buf;
}
static struct hostent _hostent = {0};
static uint32_t _hostent_addr = 0;
static char * _host_entry_list[2] = {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) {
/* Is name just an IP address? */
int maybe_ip = 1;
int dots = 0;
for (const char * c = name; *c; ++c) {
if ((*c < '0' || *c > '9') && *c != '.') {
maybe_ip = 0;
break;
}
if (*c == '.') {
dots++;
if (dots > 3) {
maybe_ip = 0;
break;
}
}
}
if (maybe_ip && dots == 3) {
_hostent.h_name = (char*)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] = (char*)&_hostent_addr;
_hostent_addr = inet_addr(name);
return &_hostent;
}
if (!strcmp(name,"localhost")) {
_hostent.h_name = (char*)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] = (char*)&_hostent_addr;
_hostent_addr = inet_addr("127.0.0.1");
return &_hostent;
}
/* Try to open /etc/resolv.conf */
FILE * resolv = fopen("/etc/resolv.conf","r");
if (!resolv) resolv = fopen("/var/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 = inet_addr(tmp + strlen("nameserver "));
/* 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);
/* 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;
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, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr, "gethostbyname: failed to send\n");
return NULL;
}
/* Wait for a response, but don't wait too long. */
struct pollfd fds[1];
fds[0].fd = sock;
fds[0].events = POLLIN;
int ret = poll(fds,1,2000); /* Two seconds? Is that okay? */
if (ret <= 0) {
fprintf(stderr, "gethostbyname: timed out\n");
return NULL;
}
char buf[1550];
ssize_t len = recv(sock, buf, 1550, 0);
close(sock);
if (len < 0) {
fprintf(stderr, "gethostbyname: failed to recv\n");
return NULL;
}
/* Now examine the response */
struct dns_packet * response = (struct dns_packet *)&buf;
if (ntohs(response->answers) == 0) {
fprintf(stderr, "gethostbyname: no answer\n");
return NULL;
}
uint16_t answers = ntohs(response->answers);
uint16_t queries = ntohs(response->questions);
const unsigned char * d = response->data;
for (uint16_t i = 0; i < queries; ++i) {
while (1) {
if (d - response->data >= len) goto _nope;
int l = *d++;
if ((l & 0xc0) == 0xc0) { d++; break; }
if (!l) break;
d += l;
}
d += 4;
}
for (uint16_t i = 0; i < answers; ++i) {
while (1) {
if (d - response->data >= len) goto _nope;
int l = *d++;
if ((l & 0xc0) == 0xc0) { d++; break; }
if (!l) break;
d += l;
}
if (d - response->data > len + 10) goto _nope;
d += 2; /* skip type */
uint16_t cls = d[0] * 256 + d[1]; d += 2;
d += 4; /* skip ttl */
uint16_t dlen = d[0] * 256 + d[1]; d += 2;
if (dlen == 4 && cls == 1) {
if (d - response->data > len + dlen) goto _nope;
/* Get a return value */
_hostent.h_name = (char*)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] = (char*)&_hostent_addr;
_hostent_addr = *(uint32_t*)(d);
return &_hostent;
}
d += dlen;
}
_nope:
fprintf(stderr, "gethostbyname: no viable answer\n");
return NULL;
}
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int flags) {
return -ENOSYS;
}
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res) {
struct hostent * ent = gethostbyname(node);
if (!ent) return -EINVAL; /* EAI_FAIL */
*res = malloc(sizeof(struct addrinfo));
(*res)->ai_flags = 0;
(*res)->ai_family = AF_INET;
(*res)->ai_socktype = 0;
(*res)->ai_protocol = 0;
(*res)->ai_addrlen = sizeof(struct sockaddr_in);
struct sockaddr_in * addr = malloc(sizeof(struct sockaddr_in));
addr->sin_family = AF_INET;
memcpy(&addr->sin_addr.s_addr, ent->h_addr, ent->h_length);
(*res)->ai_addr = (struct sockaddr *)addr;
(*res)->ai_canonname = NULL;
(*res)->ai_next = NULL;
return 0;
}
void freeaddrinfo(struct addrinfo *res) {
if (res->ai_addr) free(res->ai_addr);
free(res);
}