diff --git a/daemon/aux.h b/daemon/aux.h index 0128d52af..af7e1b3a4 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -146,6 +146,14 @@ static inline int smart_pton(int af, char *src, void *dst) { return inet_pton(af, src, dst); } +static inline int strmemcmp(const void *mem, int len, const char *str) { + if (strlen(str) < len) + return 1; + if (strlen(str) > len) + return -1; + return memcmp(mem, str, len); +} + typedef pthread_mutex_t mutex_t; diff --git a/daemon/control_ng.c b/daemon/control_ng.c index d91343267..11caf0d38 100644 --- a/daemon/control_ng.c +++ b/daemon/control_ng.c @@ -2,11 +2,105 @@ #include "obj.h" #include "poller.h" #include "bencode.h" +#include "log.h" +#include "cookie_cache.h" -static void control_ng_incoming(struct obj *obj, char *buf, int len, struct sockaddr_in6 *sin, char *addr) { +static void control_ng_incoming(struct obj *obj, char *buf, int buf_len, struct sockaddr_in6 *sin, char *addr) { + struct control_ng *c = (void *) obj; + char *data; + bencode_buffer_t bencbuf; + bencode_item_t *dict, *resp; + char *reply; + const char *cmd, *errstr, *cookie; + int cmd_len, cookie_len, data_len, reply_len; + struct msghdr mh; + struct iovec iov[3]; + + data = memchr(buf, ' ', buf_len); + if (!data || data == buf) { + mylog(LOG_WARNING, "Received invalid data on NG port (no cookie) from %s:%u: %.*s", addr, ntohs(sin->sin6_port), buf_len, buf); + return; + } + + bencode_buffer_init(&bencbuf); + resp = bencode_dictionary(&bencbuf); + + cookie = buf; + cookie_len = data - buf; + *data++ = '\0'; + data_len = buf_len - cookie_len - 1; + + errstr = "Invalid data (no payload)"; + if (data_len <= 0) + goto err_send; + + reply = cookie_cache_lookup(&c->cookie_cache, cookie); + if (reply) { + mylog(LOG_INFO, "Detected command from %s:%u as a duplicate", addr, ntohs(sin->sin6_port)); + reply_len = strlen(reply); + resp = NULL; + goto send_only; + } + + dict = bencode_decode_expect(&bencbuf, data, data_len, BENCODE_DICTIONARY); + errstr = "Could not decode dictionary"; + if (!dict) + goto err_send; + + cmd = bencode_dictionary_get_string(dict, "command", &cmd_len); + errstr = "Dictionary contains no key \"command\""; + if (!cmd) + goto err_send; + + mylog(LOG_INFO, "Got valid command from %s:%u: %.*s [%.*s]", addr, ntohs(sin->sin6_port), cmd_len, cmd, data_len, data); + + if (!strmemcmp(cmd, cmd_len, "ping")) + bencode_dictionary_add_string(resp, "result", "pong"); + else { + errstr = "Unrecognized command"; + goto err_send; + } + + goto send_out; + +err_send: + mylog(LOG_WARNING, "Protocol error in packet from %s:%u: %s [%.*s]", addr, ntohs(sin->sin6_port), errstr, data_len, data); + bencode_dictionary_add_string(resp, "result", "error"); + bencode_dictionary_add_string(resp, "error-reason", errstr); + goto send_out; + +send_out: + reply = bencode_collapse(resp, &reply_len); + +send_only: + ZERO(mh); + mh.msg_name = sin; + mh.msg_namelen = sizeof(*sin); + mh.msg_iov = iov; + mh.msg_iovlen = 3; + + iov[0].iov_base = (void *) cookie; + iov[0].iov_len = cookie_len; + iov[1].iov_base = " "; + iov[1].iov_len = 1; + iov[2].iov_base = reply; + iov[2].iov_len = reply_len; + + sendmsg(c->udp_listener.fd, &mh, 0); + + if (resp) + cookie_cache_insert(&c->cookie_cache, cookie, reply, reply_len); + + goto out; + + cookie_cache_remove(&c->cookie_cache, cookie); +out: + bencode_buffer_free(&bencbuf); } + + struct control_ng *control_ng_new(struct poller *p, struct in6_addr ip, u_int16_t port, struct callmaster *m) { struct control_ng *c;