From b43de5932ba01c8acbcdfc7aebd48b87514f4af2 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 4 Apr 2019 11:54:42 +0200 Subject: WIP poc0 --- common.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 160 insertions(+), 11 deletions(-) (limited to 'common.c') diff --git a/common.c b/common.c index 10883fe..9da90fd 100644 --- a/common.c +++ b/common.c @@ -9,10 +9,14 @@ #include #include #include +#include #include #include +#include #include +#include +#include #include "common.h" #include "dbg.h" @@ -47,6 +51,9 @@ static struct wg_dynamic_attr *parse_value(enum wg_dynamic_key key, char *value) uintmax_t uresult; union { uint32_t leasetime; + uint32_t leasestart; + uint32_t err; + char errmsg[72]; struct wg_combined_ip ip; } data = { 0 }; @@ -64,6 +71,14 @@ static struct wg_dynamic_attr *parse_value(enum wg_dynamic_key key, char *value) if (!parse_ip_cidr(&data.ip, value)) return NULL; + break; + case WGKEY_LEASESTART: + len = sizeof data.leasestart; + uresult = strtoumax(value, &endptr, 10); + if (uresult > UINT32_MAX || *endptr != '\0') + return NULL; + + data.leasestart = (uint32_t)uresult; break; case WGKEY_LEASETIME: len = sizeof data.leasetime; @@ -73,6 +88,19 @@ static struct wg_dynamic_attr *parse_value(enum wg_dynamic_key key, char *value) data.leasetime = (uint32_t)uresult; break; + case WGKEY_ERRNO: + len = sizeof data.err; + uresult = strtoumax(value, &endptr, 10); + if (uresult > UINT32_MAX || *endptr != '\0') + return NULL; + + data.err = (uint32_t)uresult; + break; + case WGKEY_ERRMSG: + strncpy(data.errmsg, value, sizeof data.errmsg); + len = MIN(sizeof data.errmsg, + strlen(value) + 1); /* Copying the NUL byte too. */ + break; default: debug("Invalid key %d, aborting\n", key); abort(); @@ -192,6 +220,9 @@ void free_wg_dynamic_request(struct wg_dynamic_request *req) req->cmd = WGKEY_UNKNOWN; req->version = 0; + free(req->buf); + req->buf = NULL; + req->buflen = 0; req->first = NULL; req->last = NULL; } @@ -249,8 +280,8 @@ static int parse_request(struct wg_dynamic_request *req, unsigned char *buf, } bool handle_request(int fd, struct wg_dynamic_request *req, - void (*success)(int, struct wg_dynamic_request *req), - void (*error)(int, int)) + bool (*success)(int, struct wg_dynamic_request *), + bool (*error)(int, int)) { ssize_t bytes; int ret; @@ -264,23 +295,141 @@ bool handle_request(int fd, struct wg_dynamic_request *req, // TODO: handle EINTR - debug("Reading from socket failed: %s\n", + debug("Reading from socket %d failed: %s\n", fd, strerror(errno)); return true; } else if (bytes == 0) { - debug("Client disconnected unexpectedly\n"); + debug("Peer disconnected unexpectedly\n"); return true; } ret = parse_request(req, buf, bytes); - if (ret < 0) { - error(fd, -ret); - return true; - } else if (ret == 0) { - success(fd, req); - return true; - } + if (ret < 0) + return error(fd, -ret); + else if (ret == 0) + return success(fd, req); } return false; } + +size_t send_message(int fd, unsigned char *buf, size_t *len) +{ + ssize_t bytes; + size_t offset = 0; + + while (*len) { + bytes = write(fd, buf + offset, *len); + if (bytes < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + break; + + // TODO: handle EINTR + + debug("Writing to socket %d failed: %s\n", fd, + strerror(errno)); + *len = 0; + return 0; + } + + *len -= bytes; + offset += bytes; + } + + return offset; +} + +void send_later(struct wg_dynamic_request *req, unsigned char *const buf, + size_t msglen) +{ + unsigned char *newbuf = malloc(msglen); + if (!newbuf) + fatal("Failed malloc()"); + memcpy(newbuf, buf, msglen); + + free(req->buf); + req->buf = newbuf; + req->buflen = msglen; +} + +size_t print_to_buf(char *buf, size_t bufsize, size_t offset, char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(buf + offset, bufsize - offset, fmt, ap); + va_end(ap); + if (n < 0) + fatal("Failed snprintf"); + if (n + offset >= bufsize) + fatal("Outbuffer too small"); + return n; +} + +uint32_t current_time() +{ + struct timespec tp; + if (clock_gettime(CLOCK_REALTIME, &tp)) + fatal("clock_gettime(CLOCK_REALTIME)"); + return tp.tv_sec; +} + +void close_connection(int *fd, struct wg_dynamic_request *req) +{ + if (close(*fd)) + debug("Failed to close socket\n"); + + *fd = -1; + free_wg_dynamic_request(req); +} + +bool is_link_local(unsigned char *addr) +{ + /* TODO: check if the remaining 48 bits are 0 */ + return IN6_IS_ADDR_LINKLOCAL(addr); +} + +void iface_get_all_addrs(uint8_t family, mnl_cb_t data_cb, void *cb_data) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + /* TODO: rtln-addr-dump from libmnl uses rtgenmsg here? */ + struct ifaddrmsg *ifaddr; + int ret; + unsigned int seq, portid; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + fatal("mnl_socket_open"); + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + fatal("mnl_socket_bind"); + + /* You'd think that we could just request addresses from a specific + * interface, via NLM_F_MATCH or something, but we can't. See also: + * https://marc.info/?l=linux-netdev&m=132508164508217 + */ + seq = time(NULL); + portid = mnl_socket_get_portid(nl); + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq; + ifaddr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); + ifaddr->ifa_family = family; + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + fatal("mnl_socket_sendto"); + + do { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, cb_data); + } while (ret > 0); + + if (ret == -1) + fatal("mnl_cb_run/mnl_socket_recvfrom"); + + mnl_socket_close(nl); +} -- cgit v1.2.3-59-g8ed1b