aboutsummaryrefslogtreecommitdiffstats
path: root/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'common.c')
-rw-r--r--common.c171
1 files changed, 160 insertions, 11 deletions
diff --git a/common.c b/common.c
index 10883fe..9da90fd 100644
--- a/common.c
+++ b/common.c
@@ -9,10 +9,14 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <unistd.h>
+#include <sys/param.h>
#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/rtnetlink.h>
#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 };
@@ -65,6 +72,14 @@ static struct wg_dynamic_attr *parse_value(enum wg_dynamic_key key, char *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;
uresult = strtoumax(value, &endptr, 10);
@@ -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);
+}