From 6213de288f6741e9254f09ed88556284db85b841 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Tue, 4 Oct 2016 17:02:15 +1000 Subject: [PATCH 01/11] + portknock.c : skeleton --- src/sources/portknock.c | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/sources/portknock.c diff --git a/src/sources/portknock.c b/src/sources/portknock.c new file mode 100644 index 0000000..5ea55f1 --- /dev/null +++ b/src/sources/portknock.c @@ -0,0 +1,94 @@ +/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "source.h" + +struct _config { + char error[256]; + void (*errcb)(char *errstr); +}; + +static void +errcb_stub(char *str) { + assert(str != NULL); + (void)(str); +} + +cfg_t * +create(const char *init) { + cfg_t *cfg = NULL; + assert(init != NULL); + if ((cfg = calloc(1, sizeof(cfg_t))) == NULL) + return NULL; + strlcpy(cfg->path, init, sizeof(cfg->path)); + cfg->errcb = &errcb_stub; + return cfg; +} + +bool +config(cfg_t *cfg, const char *key, const char *value) { + assert(cfg != NULL); + assert(key != NULL); + assert(value != NULL); + /* no options */ + return false; +} + +bool +ready(cfg_t *cfg) { + assert(cfg != NULL); + if (cfg->path[0] == '\0') + return false; + return true; +} + +char * +error(cfg_t *cfg) { + assert(cfg != NULL); + + return cfg->error; +} + +void +errcb(cfg_t *cfg, void (*cb)(char *errstr)) { + assert(cfg != NULL); + assert(cb != NULL); + + cfg->errcb = cb; +} + +bool +start(cfg_t *cfg) { + assert(cfg != NULL); + + /* TODO */ + return true; +} + +bool +stop(cfg_t *cfg) { + assert(cfg != NULL); + + /* TODO */ + return true; +} + +bool +next(cfg_t *cfg, char *buf, size_t bufsize, bool reset) { + assert(cfg != NULL); + assert(buf != NULL); + assert(bufsize > 0); + + /* TODO */ + return false; +} + +void +destroy(cfg_t *cfg) { + assert(cfg != NULL); + + free(cfg); +} From 27abc06df91e9510fc1c180e1d75d290f1c10929 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Tue, 4 Oct 2016 17:04:35 +1000 Subject: [PATCH 02/11] * update cmake --- src/sources/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sources/CMakeLists.txt b/src/sources/CMakeLists.txt index 25b0953..1912e0f 100644 --- a/src/sources/CMakeLists.txt +++ b/src/sources/CMakeLists.txt @@ -4,6 +4,9 @@ set(SOURCES "") add_library("f2b_source_files" MODULE "files.c" "../strlcpy.c") list(APPEND SOURCES "f2b_source_files") +add_library("f2b_source_portknock" MODULE "portknock.c" "../strlcpy.c") +list(APPEND SOURCES "f2b_source_portknock") + find_library(REDIS_FOUND "hiredis") if (WITH_REDIS AND REDIS_FOUND) add_library("f2b_source_redis" MODULE "redis.c" "../strlcpy.c") From a35aeb97c850b5dbc1d6c60115055fe8e2b6c1b8 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Tue, 4 Oct 2016 17:49:25 +1000 Subject: [PATCH 03/11] * portknock.c : implement config() & ready() --- src/sources/portknock.c | 53 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index 5ea55f1..29c47c9 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -6,9 +6,24 @@ */ #include "source.h" +#include +#include +#include + +#define MAX_PORTS 32 + struct _config { + char name[32]; char error[256]; + int listen_af; + union { + struct in_addr v4; + struct in6_addr v6; + } listen_addr; void (*errcb)(char *errstr); + size_t ports_used; + uint16_t ports[MAX_PORTS]; + int sockets[MAX_PORTS]; }; static void @@ -23,7 +38,7 @@ create(const char *init) { assert(init != NULL); if ((cfg = calloc(1, sizeof(cfg_t))) == NULL) return NULL; - strlcpy(cfg->path, init, sizeof(cfg->path)); + strlcpy(cfg->name, init, sizeof(cfg->name)); cfg->errcb = &errcb_stub; return cfg; } @@ -33,16 +48,44 @@ config(cfg_t *cfg, const char *key, const char *value) { assert(cfg != NULL); assert(key != NULL); assert(value != NULL); - /* no options */ + + if (strcmp(key, "listen") == 0) { + void *buf = NULL; + if (strchr(value, ':') == NULL) { + cfg->listen_af = AF_INET; + buf = &cfg->listen_addr.v4; + } else { + cfg->listen_af = AF_INET6; + buf = &cfg->listen_addr.v6; + } + if (inet_pton(cfg->listen_af, value, buf) <= 0) { + snprintf(cfg->error, sizeof(cfg->error), "invalid listen address: %s", value); + return false; + } + return true; + } + if (strcmp(key, "port") == 0) { + if (cfg->ports_used >= MAX_PORTS) { + strlcpy(cfg->error, "max ports number reached in this portknock instance", sizeof(cfg->error)); + return false; + } + cfg->ports[cfg->ports_used] = atoi(value); + if (cfg->ports[cfg->ports_used] == 0) { + snprintf(cfg->error, sizeof(cfg->error), "invalid port number: %s", value); + return false; + } + cfg->ports_used++; + } + return false; } bool ready(cfg_t *cfg) { assert(cfg != NULL); - if (cfg->path[0] == '\0') - return false; - return true; + if (cfg->ports_used > 0) + return true; + return false; } char * From 8d7141948ce77e0d2cc7ceb5b51d9765e1225ce6 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Wed, 5 Oct 2016 11:52:02 +1000 Subject: [PATCH 04/11] * source/portknock: use linked list instead fixed list --- src/sources/portknock.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index 29c47c9..7b3164c 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -10,20 +10,22 @@ #include #include -#define MAX_PORTS 32 +#define HOST_MAX 48 +#define PORT_MAX 6 + +typedef struct f2b_port_t { + struct f2b_port_t *next; + char host[HOST_MAX]; + char port[PORT_MAX]; + int fd; +} f2b_port_t; struct _config { char name[32]; char error[256]; - int listen_af; - union { - struct in_addr v4; - struct in6_addr v6; - } listen_addr; void (*errcb)(char *errstr); - size_t ports_used; - uint16_t ports[MAX_PORTS]; - int sockets[MAX_PORTS]; + f2b_port_t *ports; + f2b_port_t *current; }; static void @@ -83,7 +85,7 @@ config(cfg_t *cfg, const char *key, const char *value) { bool ready(cfg_t *cfg) { assert(cfg != NULL); - if (cfg->ports_used > 0) + if (cfg->ports) return true; return false; } From 0d10807a75e54515b0efb18a48678c16b8263ab9 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Wed, 5 Oct 2016 11:53:26 +1000 Subject: [PATCH 05/11] * source/portknock: update config() --- src/sources/portknock.c | 66 ++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index 7b3164c..3554711 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -6,6 +6,7 @@ */ #include "source.h" +#include #include #include #include @@ -28,6 +29,41 @@ struct _config { f2b_port_t *current; }; +static bool +try_parse_listen_opt(f2b_port_t *port, const char *value) { + char buf[256]; + char *p; + + strlcpy(buf, value, sizeof(buf)); + if (*buf == '[') { + /* IPv6, expected: [XXXX::XXXX:XXXX]:YYYY */ + if ((p = strstr(buf, "]:")) == NULL) { + *p = '\0', p += 2; + strlcpy(port->port, p, sizeof(port->port)); + p = buf + 1; + strlcpy(port->host, p, sizeof(port->host)); + return true; + } + return false; /* can't find port */ + } + if ((p = strchr(buf, ':')) != NULL) { + /* IPv4, expected: XX.XX.XX.XX:YYYY */ + *p = '\0', p += 1; + strlcpy(port->port, p, sizeof(port->port)); + p = buf; + strlcpy(port->host, p, sizeof(port->host)); + return true; + } + if (isdigit(*buf) && strlen(buf) <= 5) { + /* IPv4, expected: YYYY */ + strlcpy(port->host, "0.0.0.0", sizeof(port->host)); + strlcpy(port->port, buf, sizeof(port->port)); + return true; + } + + return false; +} + static void errcb_stub(char *str) { assert(str != NULL); @@ -52,31 +88,19 @@ config(cfg_t *cfg, const char *key, const char *value) { assert(value != NULL); if (strcmp(key, "listen") == 0) { - void *buf = NULL; - if (strchr(value, ':') == NULL) { - cfg->listen_af = AF_INET; - buf = &cfg->listen_addr.v4; - } else { - cfg->listen_af = AF_INET6; - buf = &cfg->listen_addr.v6; - } - if (inet_pton(cfg->listen_af, value, buf) <= 0) { - snprintf(cfg->error, sizeof(cfg->error), "invalid listen address: %s", value); - return false; - } - return true; - } - if (strcmp(key, "port") == 0) { - if (cfg->ports_used >= MAX_PORTS) { - strlcpy(cfg->error, "max ports number reached in this portknock instance", sizeof(cfg->error)); + f2b_port_t *port = NULL; + if ((port = calloc(1, sizeof(f2b_port_t))) == NULL) { + strlcpy(cfg->error, "out of memory", sizeof(cfg->error)); return false; } - cfg->ports[cfg->ports_used] = atoi(value); - if (cfg->ports[cfg->ports_used] == 0) { - snprintf(cfg->error, sizeof(cfg->error), "invalid port number: %s", value); + if (try_parse_listen_opt(port, value) == false) { + snprintf(cfg->error, sizeof(cfg->error), "can't parse: %s", value); + free(port); return false; } - cfg->ports_used++; + port->next = cfg->ports; + cfg->ports = port; + return true; } return false; From ad8ada98b2339ede07cad25523c184e01168eca8 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Wed, 5 Oct 2016 13:01:29 +1000 Subject: [PATCH 06/11] * source/portknock: implement start() --- src/sources/portknock.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index 3554711..2b85f0b 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -10,6 +10,7 @@ #include #include #include +#include #define HOST_MAX 48 #define PORT_MAX 6 @@ -18,7 +19,7 @@ typedef struct f2b_port_t { struct f2b_port_t *next; char host[HOST_MAX]; char port[PORT_MAX]; - int fd; + int sock; } f2b_port_t; struct _config { @@ -131,9 +132,41 @@ errcb(cfg_t *cfg, void (*cb)(char *errstr)) { bool start(cfg_t *cfg) { + struct addrinfo hints; + struct addrinfo *result; assert(cfg != NULL); - /* TODO */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + for (f2b_port_t *port = cfg->ports; port != 0; port = port->next) { + port->sock = -1; + int ret = getaddrinfo(port->host, port->port, &hints, &result); + if (ret != 0) { + snprintf(cfg->error, sizeof(cfg->error), "getaddrinfo: %s", gai_strerror(ret)); + cfg->errcb(cfg->error); + continue; + } + for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { + port->sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (port->sock == -1) + continue; + if (bind(port->sock, rp->ai_addr, rp->ai_addrlen) == 0) { + if (listen(port->sock, 5) == 0) /* TODO: hardcoded */ + break; /* success */ + close(port->sock); + port->sock = -1; + } + } + freeaddrinfo(result); + if (port->sock < 0) { + snprintf(cfg->error, sizeof(cfg->error), "can't bind/listen on %s:%s", port->host, port->port); + cfg->errcb(cfg->error); + } + } + return true; } From 0c52552acad1124f21ceea1320f995e0a18e5c03 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Wed, 5 Oct 2016 15:43:49 +1000 Subject: [PATCH 07/11] * source/portknock : enable SO_REUSEADDR --- src/sources/portknock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index 2b85f0b..dd2ac4b 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -144,6 +144,7 @@ start(cfg_t *cfg) { for (f2b_port_t *port = cfg->ports; port != 0; port = port->next) { port->sock = -1; int ret = getaddrinfo(port->host, port->port, &hints, &result); + int opt = 1; if (ret != 0) { snprintf(cfg->error, sizeof(cfg->error), "getaddrinfo: %s", gai_strerror(ret)); cfg->errcb(cfg->error); @@ -153,6 +154,7 @@ start(cfg_t *cfg) { port->sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (port->sock == -1) continue; + setsockopt(port->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); if (bind(port->sock, rp->ai_addr, rp->ai_addrlen) == 0) { if (listen(port->sock, 5) == 0) /* TODO: hardcoded */ break; /* success */ From a626b8e164b377eaa71024951748a0e8225f8813 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Wed, 5 Oct 2016 15:50:09 +1000 Subject: [PATCH 08/11] * source/portknock : implement stop() & destroy() --- src/sources/portknock.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index dd2ac4b..ed66945 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -176,7 +176,9 @@ bool stop(cfg_t *cfg) { assert(cfg != NULL); - /* TODO */ + for (f2b_port_t *port = cfg->ports; port != NULL; port = port->next) + close(port->sock); + return true; } @@ -192,7 +194,13 @@ next(cfg_t *cfg, char *buf, size_t bufsize, bool reset) { void destroy(cfg_t *cfg) { + f2b_port_t *next; assert(cfg != NULL); + for (; cfg->ports != NULL; cfg->ports = next) { + next = cfg->ports->next; + free(cfg->ports); + } + free(cfg); } From 2c1147fccf91c1285c6e22a900817017bdf21434 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Wed, 5 Oct 2016 16:52:22 +1000 Subject: [PATCH 09/11] * source/portknock: enable non-blocking mode for sockets --- src/sources/portknock.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index ed66945..0eddbc9 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -7,6 +7,7 @@ #include "source.h" #include +#include #include #include #include @@ -134,6 +135,8 @@ bool start(cfg_t *cfg) { struct addrinfo hints; struct addrinfo *result; + int opt; + assert(cfg != NULL); memset(&hints, 0, sizeof(struct addrinfo)); @@ -141,10 +144,9 @@ start(cfg_t *cfg) { hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; - for (f2b_port_t *port = cfg->ports; port != 0; port = port->next) { + for (f2b_port_t *port = cfg->ports; port != NULL; port = port->next) { port->sock = -1; int ret = getaddrinfo(port->host, port->port, &hints, &result); - int opt = 1; if (ret != 0) { snprintf(cfg->error, sizeof(cfg->error), "getaddrinfo: %s", gai_strerror(ret)); cfg->errcb(cfg->error); @@ -154,6 +156,10 @@ start(cfg_t *cfg) { port->sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (port->sock == -1) continue; + if ((opt = fcntl(port->sock, F_GETFL, 0)) < 0) + continue; + fcntl(port->sock, F_SETFL, opt | O_NONBLOCK); + opt = 1; setsockopt(port->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); if (bind(port->sock, rp->ai_addr, rp->ai_addrlen) == 0) { if (listen(port->sock, 5) == 0) /* TODO: hardcoded */ From 98b49c7069a30c2587b923b7095a5ee2a1819943 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Wed, 5 Oct 2016 16:52:33 +1000 Subject: [PATCH 10/11] * source/portknock: implement next() --- src/sources/portknock.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/sources/portknock.c b/src/sources/portknock.c index 0eddbc9..38aaab7 100644 --- a/src/sources/portknock.c +++ b/src/sources/portknock.c @@ -190,11 +190,40 @@ stop(cfg_t *cfg) { bool next(cfg_t *cfg, char *buf, size_t bufsize, bool reset) { + struct sockaddr_storage addr; + socklen_t addrlen; + assert(cfg != NULL); assert(buf != NULL); assert(bufsize > 0); - /* TODO */ + if (reset || cfg->current == NULL) + cfg->current = cfg->ports; + + for (f2b_port_t *port = cfg->current; port != NULL; port = port->next) { + if (port->sock < 0) + continue; + addrlen = sizeof(addr); + int sock = accept(port->sock, (struct sockaddr *) &addr, &addrlen); + if (sock < 0 && errno == EAGAIN) + continue; + if (sock < 0) { + snprintf(cfg->error, sizeof(cfg->error), "accept error: %s", strerror(errno)); + cfg->errcb(cfg->error); + continue; + } + close(sock); + if (addr.ss_family == AF_INET) { + inet_ntop(AF_INET, &(((struct sockaddr_in *) &addr)->sin_addr), buf, bufsize); + return true; + } + if (addr.ss_family == AF_INET6) { + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) &addr)->sin6_addr), buf, bufsize); + return true; + } + cfg->errcb("can't convert sockaddr to string: unknown AF"); + } + return false; } From dfb0867b99e720744d465c0cafd4979d435895e3 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Fri, 7 Oct 2016 17:48:52 +1000 Subject: [PATCH 11/11] * add example config --- configs/conf-available/05-source-portknock.conf | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 configs/conf-available/05-source-portknock.conf diff --git a/configs/conf-available/05-source-portknock.conf b/configs/conf-available/05-source-portknock.conf new file mode 100644 index 0000000..159b437 --- /dev/null +++ b/configs/conf-available/05-source-portknock.conf @@ -0,0 +1,5 @@ +[source:portknock] +load = libf2b_source_portknock.so +; listen = 0.0.0.0:23 # telnet +; listen = 0.0.0.0:5060 # sip +; listen = 0.0.0.0:6667 # irc