From 6694bb6db78a8b37a4689f31bf8cf24d16e0ddaf Mon Sep 17 00:00:00 2001 From: Thomas Gschwantner Date: Tue, 20 Aug 2019 02:28:33 +0200 Subject: Improve error handling and serialization --- common.c | 72 +++++++++++++++++++++++++---------------------- common.h | 23 +++++++++++---- wg-dynamic-server.c | 81 +++++++++++++++++++++-------------------------------- 3 files changed, 88 insertions(+), 88 deletions(-) diff --git a/common.c b/common.c index 28f0ba0..3edf872 100644 --- a/common.c +++ b/common.c @@ -254,8 +254,8 @@ static int parse_request(struct wg_dynamic_request *req, unsigned char *buf, } bool handle_request(struct wg_dynamic_request *req, - bool (*success)(int, struct wg_dynamic_request *), - bool (*error)(int, int)) + bool (*success)(struct wg_dynamic_request *), + bool (*error)(struct wg_dynamic_request *, int)) { ssize_t bytes; int ret; @@ -279,64 +279,70 @@ bool handle_request(struct wg_dynamic_request *req, ret = parse_request(req, buf, bytes); if (ret < 0) - return error(req->fd, -ret); + return error(req, -ret); else if (ret == 0) - return success(req->fd, req); + return success(req); } return false; } -size_t send_message(int fd, unsigned char *buf, size_t *len) +bool send_message(struct wg_dynamic_request *req, const void *buf, size_t len) { - ssize_t bytes; size_t offset = 0; - while (*len) { - bytes = write(fd, buf + offset, *len); - if (bytes < 0) { + while (1) { + ssize_t written = write(req->fd, buf + offset, len - offset); + if (written < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN) break; // TODO: handle EINTR - debug("Writing to socket %d failed: %s\n", fd, + debug("Writing to socket %d failed: %s\n", req->fd, strerror(errno)); - *len = 0; - return 0; + return true; } - *len -= bytes; - offset += bytes; + offset += written; + if (offset == len) + return true; } - return offset; -} + debug("Socket %d blocking on write with %lu bytes left, postponing\n", + req->fd, len - 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); + if (!req->buf) { + req->buflen = len - offset; + req->buf = malloc(req->buflen); + if (!req->buf) + fatal("malloc()"); - free(req->buf); - req->buf = newbuf; - req->buflen = msglen; + memcpy(req->buf, buf + offset, req->buflen); + } else { + req->buflen = len - offset; + memmove(req->buf, buf + offset, req->buflen); + } + + return false; } -int print_to_buf(char *buf, size_t bufsize, size_t offset, char *fmt, ...) +void 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); + int n = vsnprintf(buf + *offset, bufsize - *offset, fmt, ap); va_end(ap); - if (n < 0) - fatal("Failed snprintf"); - if (n + offset >= bufsize) - die("Outbuffer too small"); - return n; + + if (n < 0) { + fatal("vsnprintf()"); + } else if (n + *offset >= bufsize) { + debug("Outbuffer too small: %d + %zu >= %zu\n", n, *offset, + bufsize); + BUG(); + } + + *offset += n; } uint32_t current_time() diff --git a/common.h b/common.h index b2bd054..fcb0f0e 100644 --- a/common.h +++ b/common.h @@ -49,6 +49,19 @@ static const char *const WG_DYNAMIC_KEY[] = { ITEMS }; #undef E #undef ITEMS +#define ITEMS \ + E(E_NO_ERROR, "Success") /* must be the first entry */ \ + E(E_INVALID_REQ, "Invalid request") \ + E(E_IP_UNAVAIL, "Chosen IP unavailable") + +#define E(x, y) x, +enum wg_dynamic_err { ITEMS }; +#undef E +#define E(x, y) y, +static const char *const WG_DYNAMIC_ERR[] = { ITEMS }; +#undef E +#undef ITEMS + struct wg_dynamic_attr { enum wg_dynamic_key key; size_t len; @@ -79,12 +92,10 @@ struct wg_combined_ip { void free_wg_dynamic_request(struct wg_dynamic_request *req); bool handle_request(struct wg_dynamic_request *req, - bool (*success)(int, struct wg_dynamic_request *), - bool (*error)(int, int)); -size_t send_message(int fd, unsigned char *buf, size_t *len); -void send_later(struct wg_dynamic_request *req, unsigned char *const buf, - size_t msglen); -int print_to_buf(char *buf, size_t bufsize, size_t len, char *fmt, ...); + bool (*success)(struct wg_dynamic_request *), + bool (*error)(struct wg_dynamic_request *, int)); +bool send_message(struct wg_dynamic_request *req, const void *buf, size_t len); +void print_to_buf(char *buf, size_t bufsize, size_t *offset, char *fmt, ...); uint32_t current_time(); void close_connection(struct wg_dynamic_request *req); bool is_link_local(unsigned char *addr); diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c index a5e707a..fca1dfc 100644 --- a/wg-dynamic-server.c +++ b/wg-dynamic-server.c @@ -223,36 +223,41 @@ static int accept_connection(int sockfd, wg_key *dest) return fd; } -static bool send_error(int fd, int ret) +static bool send_error(struct wg_dynamic_request *req, int error) { - UNUSED(fd); - debug("Error: %s\n", strerror(ret)); - return true; + char buf[MAX_RESPONSE_SIZE]; + size_t msglen = 0; + + print_to_buf(buf, sizeof buf, &msglen, "errno=%d\nerrmsg=%s\n\n", error, + WG_DYNAMIC_ERR[error]); + + return send_message(req, buf, msglen); } -static void serialise_lease(char *buf, size_t bufsize, size_t *offset, - const struct wg_dynamic_lease *lease) +static size_t serialize_lease(char *buf, size_t len, + const struct wg_dynamic_lease *lease) { char addrbuf[INET6_ADDRSTRLEN]; + size_t off = 0; if (lease->ipv4.s_addr) { if (!inet_ntop(AF_INET, &lease->ipv4, addrbuf, sizeof addrbuf)) fatal("inet_ntop()"); - *offset += print_to_buf(buf, bufsize, *offset, "ipv4=%s/%d\n", - addrbuf, 32); + + print_to_buf(buf, len, &off, "ipv4=%s/%d\n", addrbuf, 32); } if (!IN6_IS_ADDR_UNSPECIFIED(&lease->ipv6)) { if (!inet_ntop(AF_INET6, &lease->ipv6, addrbuf, sizeof addrbuf)) fatal("inet_ntop()"); - *offset += print_to_buf(buf, bufsize, *offset, "ipv6=%s/%d\n", - addrbuf, 128); + + print_to_buf(buf, len, &off, "ipv6=%s/%d\n", addrbuf, 128); } - *offset += print_to_buf(buf, bufsize, *offset, "leasestart=%u\n", - lease->start_real); - *offset += print_to_buf(buf, bufsize, *offset, "leasetime=%u\n", - lease->leasetime); + print_to_buf(buf, len, &off, "leasestart=%u\nleasetime=%u\nerrno=0\n\n", + lease->start_real, lease->leasetime); + + return off; } static void add_allowed_ips(wg_key pubkey, struct in_addr *ipv4, @@ -317,60 +322,41 @@ static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey, } if (ipv4 && ipv6 && !ipv4->s_addr && IN6_IS_ADDR_UNSPECIFIED(ipv6)) - return 2; /* TODO: invalid request */ + return E_INVALID_REQ; *lease = new_lease(pubkey, leasetime, ipv4, ipv6); if (!*lease) - return 1; /* TODO: either out of IPs or IP unavailable */ + return E_IP_UNAVAIL; - return 0; + return E_NO_ERROR; } -static bool send_response(int fd, struct wg_dynamic_request *req) +static bool send_response(struct wg_dynamic_request *req) { - char *errmsg = "OK"; + char buf[MAX_RESPONSE_SIZE]; struct wg_dynamic_attr *cur = req->first; struct wg_dynamic_lease *lease; - unsigned char buf[MAX_RESPONSE_SIZE + 1]; size_t msglen; - size_t written; - int ret = 0; + int ret; switch (req->cmd) { case WGKEY_REQUEST_IP: - msglen = print_to_buf((char *)buf, sizeof buf, 0, "%s=%d\n", - WG_DYNAMIC_KEY[req->cmd], 1); ret = response_request_ip(cur, req->pubkey, &lease); - if (ret) { - errmsg = "Out of IP addresses"; /* TODO: distinguish */ + if (ret) break; - } add_allowed_ips(req->pubkey, &lease->ipv4, &lease->ipv6); - serialise_lease((char *)buf, sizeof buf, &msglen, lease); + msglen = serialize_lease(buf, sizeof buf, lease); break; default: debug("Unknown command: %d\n", req->cmd); - return true; + BUG(); } - msglen += print_to_buf((char *)buf, sizeof buf, msglen, "errno=%d\n", - ret); if (ret) - msglen += print_to_buf((char *)buf, sizeof buf, msglen, - "errmsg=%s\n", errmsg); - if (msglen == sizeof buf) - fatal("Outbuffer too small"); - buf[msglen++] = '\n'; - - written = send_message(fd, buf, &msglen); - if (msglen == 0) - return true; - - debug("Socket %d blocking on write with %lu bytes left, postponing\n", - fd, msglen); - send_later(req, buf + written, msglen); - return false; + return send_error(req, ret); + + return send_message(req, buf, msglen); } static void setup_sockets() @@ -537,10 +523,7 @@ static void handle_event(void *ptr, uint32_t events) } if (events & EPOLLOUT) { - size_t off = send_message(req->fd, req->buf, &req->buflen); - if (req->buflen) - memmove(req->buf, req->buf + off, req->buflen); - else + if (send_message(req, req->buf, req->buflen)) close_connection(req); } } -- cgit v1.2.3-59-g8ed1b