libnetconf CHANGE support for new draft netconf-server nodes
diff --git a/src/netconf.h b/src/netconf.h
index 4022617..ce1642d 100644
--- a/src/netconf.h
+++ b/src/netconf.h
@@ -126,15 +126,6 @@
} NC_PARAMTYPE;
/**
- * @brief Enumeration of SSH key types.
- */
-typedef enum NC_SSH_KEY {
- NC_SSH_DSA, /**< DSA SSH key */
- NC_SSH_RSA, /**< RSA SSH key */
- NC_SSH_ECDSA /**< ECDSA SSH key */
-} NC_SSH_KEY;
-
-/**
* @brief Transform given time_t (seconds since the epoch) into the RFC 3339 format
* accepted by NETCONF functions.
*
diff --git a/src/session.c b/src/session.c
index 25fc410..5aa1bda 100644
--- a/src/session.c
+++ b/src/session.c
@@ -117,14 +117,14 @@
}
const char *
-nc_keytype2str(NC_SSH_KEY type)
+nc_keytype2str(NC_SSH_KEY_TYPE type)
{
switch (type) {
- case NC_SSH_DSA:
+ case NC_SSH_KEY_DSA:
return "DSA";
- case NC_SSH_RSA:
+ case NC_SSH_KEY_RSA:
return "RSA";
- case NC_SSH_ECDSA:
+ case NC_SSH_KEY_ECDSA:
return "EC";
default:
break;
@@ -134,35 +134,38 @@
}
int
-nc_sock_enable_keepalive(int sock)
+nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka)
{
int opt;
- opt = 1;
+ opt = ka->enabled;
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof opt) == -1) {
ERR("Could not set SO_KEEPALIVE option (%s).", strerror(errno));
return -1;
}
+ if (!ka->enabled) {
+ return 0;
+ }
#ifdef TCP_KEEPIDLE
- opt = 1;
+ opt = ka->idle_time;
if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &opt, sizeof opt) == -1) {
ERR("Setsockopt failed (%s).", strerror(errno));
return -1;
}
#endif
-#ifdef TCP_KEEPINTVL
- opt = 5;
- if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &opt, sizeof opt) == -1) {
+#ifdef TCP_KEEPCNT
+ opt = ka->max_probes;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &opt, sizeof opt) == -1) {
ERR("Setsockopt failed (%s).", strerror(errno));
return -1;
}
#endif
-#ifdef TCP_KEEPCNT
- opt = 10;
- if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &opt, sizeof opt) == -1) {
+#ifdef TCP_KEEPINTVL
+ opt = ka->probe_interval;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &opt, sizeof opt) == -1) {
ERR("Setsockopt failed (%s).", strerror(errno));
return -1;
}
diff --git a/src/session.h b/src/session.h
index 6b667bd..2b4fa54 100644
--- a/src/session.h
+++ b/src/session.h
@@ -92,7 +92,8 @@
*/
typedef enum {
NC_CH_FIRST_LISTED = 0, //default
- NC_CH_LAST_CONNECTED
+ NC_CH_LAST_CONNECTED,
+ NC_CH_RANDOM
} NC_CH_START_WITH;
/**
diff --git a/src/session_client.c b/src/session_client.c
index 2ede355..5cc0d1a 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -51,7 +51,12 @@
static pthread_key_t nc_client_context_key;
#ifdef __linux__
static struct nc_client_context context_main = {
- /* .opts zeroed */
+ .opts.ka = {
+ .enabled = 1,
+ .idle_time = 1,
+ .max_probes = 10,
+ .probe_interval = 5
+ },
#ifdef NC_ENABLED_SSH
.ssh_opts = {
.auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
@@ -1218,7 +1223,7 @@
concept for e.g. call home settings). For more details see nc_sock_connect().
*/
static int
-_non_blocking_connect(int timeout, int* sock_pending, struct addrinfo *res)
+_non_blocking_connect(int timeout, int* sock_pending, struct addrinfo *res, struct nc_keepalives *ka)
{
int flags, ret=0;
int sock = -1;
@@ -1288,7 +1293,7 @@
}
/* enable keep-alive */
- if (nc_sock_enable_keepalive(sock)) {
+ if (nc_sock_enable_keepalive(sock, ka)) {
goto cleanup;
}
@@ -1311,7 +1316,7 @@
has to be invoked, until it returns a valid socket.
*/
int
-nc_sock_connect(const char *host, uint16_t port, int timeout, int *sock_pending, char **ip_host)
+nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepalives *ka, int *sock_pending, char **ip_host)
{
int i, opt;
int sock = sock_pending ? *sock_pending : -1;
@@ -1336,7 +1341,7 @@
}
for (res = res_list; res != NULL; res = res->ai_next) {
- sock = _non_blocking_connect(timeout, sock_pending, res);
+ sock = _non_blocking_connect(timeout, sock_pending, res, ka);
if (sock == -1 && (!sock_pending || *sock_pending == -1)) {
/* try the next resource */
continue;
@@ -1378,7 +1383,7 @@
} else {
/* try to get a connection with the pending socket */
assert(sock_pending);
- sock = _non_blocking_connect(timeout, sock_pending, NULL);
+ sock = _non_blocking_connect(timeout, sock_pending, NULL, ka);
}
return sock;
@@ -1838,7 +1843,7 @@
return -1;
}
- sock = nc_sock_listen_inet(address, port);
+ sock = nc_sock_listen_inet(address, port, &client_opts.ka);
if (sock == -1) {
return -1;
}
diff --git a/src/session_client_ssh.c b/src/session_client_ssh.c
index a2f3dbf..5b5a2c8 100644
--- a/src/session_client_ssh.c
+++ b/src/session_client_ssh.c
@@ -1487,7 +1487,8 @@
}
static struct nc_session *
-_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_client_ssh_opts *opts, int timeout)
+_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
+ struct nc_client_ssh_opts *opts, int timeout)
{
char *host = NULL, *username = NULL, *ip_host;
unsigned short port = 0;
@@ -1528,7 +1529,7 @@
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
/* create and connect socket */
- sock = nc_sock_connect(host, port, -1, NULL, &ip_host);
+ sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
if (sock == -1) {
ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
goto fail;
@@ -1684,7 +1685,7 @@
#endif
/* create and assign communication socket */
- sock = nc_sock_connect(host, port, -1, NULL, &ip_host);
+ sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
if (sock == -1) {
ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
goto fail;
@@ -1731,7 +1732,7 @@
API struct nc_session *
nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
{
- return _nc_connect_libssh(ssh_session, ctx, &ssh_opts, NC_TRANSPORT_TIMEOUT);
+ return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
}
API struct nc_session *
@@ -1843,7 +1844,7 @@
"ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
#endif
- session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts, timeout);
+ session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
if (!session) {
ssh_free(sess);
return NULL;
diff --git a/src/session_client_tls.c b/src/session_client_tls.c
index 71bb134..4f67ab0 100644
--- a/src/session_client_tls.c
+++ b/src/session_client_tls.c
@@ -618,7 +618,7 @@
}
/* create and assign socket */
- sock = nc_sock_connect(host, port, -1, NULL, &ip_host);
+ sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
if (sock == -1) {
ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
goto fail;
diff --git a/src/session_p.h b/src/session_p.h
index 92daa59..269fa67 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -39,6 +39,14 @@
# define NC_SSH_AUTH_COUNT 3
/* ACCESS unlocked */
+struct nc_keepalives {
+ int enabled;
+ uint16_t idle_time;
+ uint16_t max_probes;
+ uint16_t probe_interval;
+};
+
+/* ACCESS unlocked */
struct nc_client_ssh_opts {
/* SSH authentication method preferences */
struct {
@@ -134,6 +142,7 @@
char *schema_searchpath;
ly_module_imp_clb schema_clb;
void *schema_clb_data;
+ struct nc_keepalives ka;
struct nc_bind {
const char *address;
@@ -189,17 +198,17 @@
int (*user_verify_clb)(const struct nc_session *session);
int (*server_cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,char **privkey_path,
- char **privkey_data, NC_SSH_KEY *privkey_type);
+ char **privkey_data, NC_SSH_KEY_TYPE *privkey_type);
void *server_cert_data;
void (*server_cert_data_free)(void *data);
int (*server_cert_chain_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
- char ***cert_data, int *cert_data_count);
+ char ***cert_data, int *cert_data_count);
void *server_cert_chain_data;
void (*server_cert_chain_data_free)(void *data);
int (*trusted_cert_list_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
- char ***cert_data, int *cert_data_count);
+ char ***cert_data, int *cert_data_count);
void *trusted_cert_list_data;
void (*trusted_cert_list_data_free)(void *data);
#endif
@@ -215,7 +224,7 @@
uint16_t authkey_count;
pthread_mutex_t authkey_lock;
- int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path, char **privkey_data, NC_SSH_KEY *privkey_type);
+ int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type);
void *hostkey_data;
void (*hostkey_data_free)(void *data);
#endif
@@ -229,6 +238,7 @@
struct nc_endpt {
const char *name;
NC_TRANSPORT_IMPL ti;
+ struct nc_keepalives ka;
union {
#ifdef NC_ENABLED_SSH
struct nc_server_ssh_opts *ssh;
@@ -252,6 +262,7 @@
const char *address;
uint16_t port;
int sock_pending;
+ struct nc_keepalives ka;
} *ch_endpts;
uint16_t ch_endpt_count;
union {
@@ -265,13 +276,9 @@
NC_CH_CONN_TYPE conn_type;
union {
struct {
- uint32_t idle_timeout;
- uint16_t ka_max_wait;
- uint8_t ka_max_attempts;
- } persist;
- struct {
+ uint16_t period;
+ time_t anchor_time;
uint16_t idle_timeout;
- uint16_t reconnect_timeout;
} period;
} conn;
NC_CH_START_WITH start_with;
@@ -409,7 +416,7 @@
/* other */
struct ly_ctx *ctx; /**< libyang context of the session */
void *data; /**< arbitrary user data */
- uint8_t flags; /**< various flags of the session - TODO combine with status and/or side */
+ uint8_t flags; /**< various flags of the session */
#define NC_SESSION_SHAREDCTX 0x01
#define NC_SESSION_CALLHOME 0x02
@@ -505,9 +512,9 @@
void nc_addtimespec(struct timespec *ts, uint32_t msec);
-const char *nc_keytype2str(NC_SSH_KEY type);
+const char *nc_keytype2str(NC_SSH_KEY_TYPE type);
-int nc_sock_enable_keepalive(int sock);
+int nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka);
struct nc_session *nc_new_session(NC_SIDE side, int shared_ti);
@@ -548,11 +555,12 @@
* @param[in] host Hostname to connect to.
* @param[in] port Port to connect on.
* @param[in] timeout for blocking the connect+select call (-1 for infinite).
+ * @param[in] ka Keepalives parameters.
* @param[in,out] sock_pending for exchanging the pending socket, if the blocking timeout was != -1
* @param[out] ip_host Optional parameter with string IP address of the connected host.
* @return Connected socket or -1 on error.
*/
-int nc_sock_connect(const char *host, uint16_t port, int timeout, int* sock_pending, char **ip_host);
+int nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepalives *ka, int *sock_pending, char **ip_host);
/**
* @brief Accept a new socket connection.
@@ -570,9 +578,10 @@
*
* @param[in] address IP address to listen on.
* @param[in] port Port to listen on.
+ * @param[in] ka Keepalives parameters.
* @return Listening socket, -1 on error.
*/
-int nc_sock_listen_inet(const char *address, uint16_t port);
+int nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka);
/**
* @brief Create a listening socket (AF_UNIX).
diff --git a/src/session_server.c b/src/session_server.c
index 9f0ac4b..cf697db 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -174,7 +174,7 @@
}
int
-nc_sock_listen_inet(const char *address, uint16_t port)
+nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
{
int opt;
int is_ipv4, sock;
@@ -207,7 +207,7 @@
goto fail;
}
- if (nc_sock_enable_keepalive(sock)) {
+ if (nc_sock_enable_keepalive(sock, ka)) {
goto fail;
}
@@ -1765,8 +1765,12 @@
ret = -1;
goto cleanup;
}
+ memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
+ server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
+ server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
+ server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
if (!server_opts.binds) {
@@ -1775,10 +1779,8 @@
goto cleanup;
}
- server_opts.binds[server_opts.endpt_count - 1].address = NULL;
- server_opts.binds[server_opts.endpt_count - 1].port = 0;
+ memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
server_opts.binds[server_opts.endpt_count - 1].sock = -1;
- server_opts.binds[server_opts.endpt_count - 1].pollin = 0;
switch (ti) {
#ifdef NC_ENABLED_SSH
@@ -1832,145 +1834,6 @@
return ret;
}
-int
-nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
-{
- struct nc_endpt *endpt;
- struct nc_bind *bind = NULL;
- uint16_t i;
- int sock = -1, set_addr, ret = 0;
-
- if (!endpt_name) {
- ERRARG("endpt_name");
- return -1;
- } else if ((!address && !port) || (address && port)) {
- ERRARG("address and port");
- return -1;
- }
-
- if (address) {
- set_addr = 1;
- } else {
- set_addr = 0;
- }
-
- /* BIND LOCK */
- pthread_mutex_lock(&server_opts.bind_lock);
-
- /* ENDPT LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
- if (!endpt) {
- /* BIND UNLOCK */
- pthread_mutex_unlock(&server_opts.bind_lock);
- return -1;
- }
-
- bind = &server_opts.binds[i];
-
- if (set_addr) {
- port = bind->port;
- } else {
- address = bind->address;
- }
-
- if (!set_addr && endpt->ti == NC_TI_UNIX) {
- ret = -1;
- goto cleanup;
- }
-
- /* we have all the information we need to create a listening socket */
- if (address && (port || endpt->ti == NC_TI_UNIX)) {
- /* create new socket, close the old one */
- if (endpt->ti == NC_TI_UNIX)
- sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
- else
- sock = nc_sock_listen_inet(address, port);
- if (sock == -1) {
- ret = -1;
- goto cleanup;
- }
-
- if (bind->sock > -1) {
- close(bind->sock);
- }
- bind->sock = sock;
- } /* else we are just setting address or port */
-
- if (set_addr) {
- lydict_remove(server_opts.ctx, bind->address);
- bind->address = lydict_insert(server_opts.ctx, address, 0);
- } else {
- bind->port = port;
- }
-
- if (sock > -1) {
-#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
- VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
-#elif defined(NC_ENABLED_SSH)
- VRB("Listening on %s:%u for SSH connections.", address, port);
-#else
- VRB("Listening on %s:%u for TLS connections.", address, port);
-#endif
- }
-
-cleanup:
- /* ENDPT UNLOCK */
- pthread_rwlock_unlock(&server_opts.endpt_lock);
-
- /* BIND UNLOCK */
- pthread_mutex_unlock(&server_opts.bind_lock);
-
- return ret;
-}
-
-API int
-nc_server_endpt_set_address(const char *endpt_name, const char *address)
-{
- return nc_server_endpt_set_address_port(endpt_name, address, 0);
-}
-
-API int
-nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
-{
- return nc_server_endpt_set_address_port(endpt_name, NULL, port);
-}
-
-API int
-nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
-{
- struct nc_endpt *endpt;
- uint16_t i;
- int ret = 0;
-
- if (!endpt_name) {
- ERRARG("endpt_name");
- return -1;
- } else if (mode == 0) {
- ERRARG("mode");
- return -1;
- }
-
- /* ENDPT LOCK */
- endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
- if (!endpt)
- return -1;
-
- if (endpt->ti != NC_TI_UNIX) {
- ret = -1;
- goto cleanup;
- }
-
- endpt->opts.unixsock->mode = mode;
- endpt->opts.unixsock->uid = uid;
- endpt->opts.unixsock->gid = gid;
-
-cleanup:
- /* ENDPT UNLOCK */
- pthread_rwlock_unlock(&server_opts.endpt_lock);
-
- return ret;
-}
-
API int
nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
{
@@ -2087,6 +1950,209 @@
return ret;
}
+API int
+nc_server_endpt_count(void)
+{
+ return server_opts.endpt_count;
+}
+
+int
+nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
+{
+ struct nc_endpt *endpt;
+ struct nc_bind *bind = NULL;
+ uint16_t i;
+ int sock = -1, set_addr, ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ } else if ((!address && !port) || (address && port)) {
+ ERRARG("address and port");
+ return -1;
+ }
+
+ if (address) {
+ set_addr = 1;
+ } else {
+ set_addr = 0;
+ }
+
+ /* BIND LOCK */
+ pthread_mutex_lock(&server_opts.bind_lock);
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
+ if (!endpt) {
+ /* BIND UNLOCK */
+ pthread_mutex_unlock(&server_opts.bind_lock);
+ return -1;
+ }
+
+ bind = &server_opts.binds[i];
+
+ if (set_addr) {
+ port = bind->port;
+ } else {
+ address = bind->address;
+ }
+
+ if (!set_addr && endpt->ti == NC_TI_UNIX) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* we have all the information we need to create a listening socket */
+ if (address && (port || endpt->ti == NC_TI_UNIX)) {
+ /* create new socket, close the old one */
+ if (endpt->ti == NC_TI_UNIX)
+ sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
+ else
+ sock = nc_sock_listen_inet(address, port, &endpt->ka);
+ if (sock == -1) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (bind->sock > -1) {
+ close(bind->sock);
+ }
+ bind->sock = sock;
+ } /* else we are just setting address or port */
+
+ if (set_addr) {
+ lydict_remove(server_opts.ctx, bind->address);
+ bind->address = lydict_insert(server_opts.ctx, address, 0);
+ } else {
+ bind->port = port;
+ }
+
+ if (sock > -1) {
+#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
+ VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
+#elif defined(NC_ENABLED_SSH)
+ VRB("Listening on %s:%u for SSH connections.", address, port);
+#else
+ VRB("Listening on %s:%u for TLS connections.", address, port);
+#endif
+ }
+
+cleanup:
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ /* BIND UNLOCK */
+ pthread_mutex_unlock(&server_opts.bind_lock);
+
+ return ret;
+}
+
+API int
+nc_server_endpt_set_address(const char *endpt_name, const char *address)
+{
+ return nc_server_endpt_set_address_port(endpt_name, address, 0);
+}
+
+API int
+nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
+{
+ return nc_server_endpt_set_address_port(endpt_name, NULL, port);
+}
+
+API int
+nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
+{
+ struct nc_endpt *endpt;
+ uint16_t i;
+ int ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ } else if (mode == 0) {
+ ERRARG("mode");
+ return -1;
+ }
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
+ if (!endpt)
+ return -1;
+
+ if (endpt->ti != NC_TI_UNIX) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ endpt->opts.unixsock->mode = mode;
+ endpt->opts.unixsock->uid = uid;
+ endpt->opts.unixsock->gid = gid;
+
+cleanup:
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ return ret;
+}
+
+API int
+nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
+{
+ struct nc_endpt *endpt;
+ int ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
+ if (!endpt) {
+ return -1;
+ }
+
+ endpt->ka.enabled = (enable ? 1 : 0);
+
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ return ret;
+}
+
+API int
+nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
+{
+ struct nc_endpt *endpt;
+ int ret = 0;
+
+ if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* ENDPT LOCK */
+ endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
+ if (!endpt) {
+ return -1;
+ }
+
+ if (idle_time > -1) {
+ endpt->ka.idle_time = idle_time;
+ }
+ if (max_probes > -1) {
+ endpt->ka.max_probes = max_probes;
+ }
+ if (probe_interval > -1) {
+ endpt->ka.probe_interval = probe_interval;
+ }
+
+ /* ENDPT UNLOCK */
+ pthread_rwlock_unlock(&server_opts.endpt_lock);
+
+ return ret;
+}
+
API NC_MSG_TYPE
nc_accept(int timeout, struct nc_session **session)
{
@@ -2455,10 +2521,12 @@
return -1;
}
+ memset(&client->ch_endpts[client->ch_endpt_count - 1], 0, sizeof *client->ch_endpts);
client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
- client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
- client->ch_endpts[client->ch_endpt_count - 1].port = 0;
client->ch_endpts[client->ch_endpt_count - 1].sock_pending = -1;
+ client->ch_endpts[client->ch_endpt_count - 1].ka.idle_time = 1;
+ client->ch_endpts[client->ch_endpt_count - 1].ka.max_probes = 10;
+ client->ch_endpts[client->ch_endpt_count - 1].ka.probe_interval = 5;
/* UNLOCK */
nc_server_ch_client_unlock(client);
@@ -2613,6 +2681,95 @@
}
API int
+nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
+{
+ uint16_t i;
+ int ret = -1;
+ struct nc_ch_client *client;
+
+ if (!client_name) {
+ ERRARG("client_name");
+ return -1;
+ } else if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* LOCK */
+ client = nc_server_ch_client_lock(client_name, 0, NULL);
+ if (!client) {
+ return -1;
+ }
+
+ for (i = 0; i < client->ch_endpt_count; ++i) {
+ if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
+ client->ch_endpts[i].ka.enabled = (enable ? 1 : 0);
+
+ ret = 0;
+ break;
+ }
+ }
+
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+
+ if (ret == -1) {
+ ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
+ }
+
+ return ret;
+}
+
+API int
+nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
+ int probe_interval)
+{
+ uint16_t i;
+ int ret = -1;
+ struct nc_ch_client *client;
+
+ if (!client_name) {
+ ERRARG("client_name");
+ return -1;
+ } else if (!endpt_name) {
+ ERRARG("endpt_name");
+ return -1;
+ }
+
+ /* LOCK */
+ client = nc_server_ch_client_lock(client_name, 0, NULL);
+ if (!client) {
+ return -1;
+ }
+
+ for (i = 0; i < client->ch_endpt_count; ++i) {
+ if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
+ if (idle_time > -1) {
+ client->ch_endpts[i].ka.idle_time = idle_time;
+ }
+ if (max_probes > -1) {
+ client->ch_endpts[i].ka.max_probes = max_probes;
+ }
+ if (probe_interval > -1) {
+ client->ch_endpts[i].ka.probe_interval = probe_interval;
+ }
+
+ ret = 0;
+ break;
+ }
+ }
+
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+
+ if (ret == -1) {
+ ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
+ }
+
+ return ret;
+}
+
+API int
nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
{
struct nc_ch_client *client;
@@ -2637,13 +2794,12 @@
/* set default options */
switch (conn_type) {
case NC_CH_PERSIST:
- client->conn.persist.idle_timeout = 86400;
- client->conn.persist.ka_max_wait = 30;
- client->conn.persist.ka_max_attempts = 3;
+ /* no options */
break;
case NC_CH_PERIOD:
- client->conn.period.idle_timeout = 300;
- client->conn.period.reconnect_timeout = 60;
+ client->conn.period.period = 60;
+ client->conn.period.anchor_time = 0;
+ client->conn.period.idle_timeout = 120;
break;
default:
ERRINT;
@@ -2658,7 +2814,41 @@
}
API int
-nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
+nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
+{
+ struct nc_ch_client *client;
+
+ if (!client_name) {
+ ERRARG("client_name");
+ return -1;
+ } else if (!period) {
+ ERRARG("period");
+ return -1;
+ }
+
+ /* LOCK */
+ client = nc_server_ch_client_lock(client_name, 0, NULL);
+ if (!client) {
+ return -1;
+ }
+
+ if (client->conn_type != NC_CH_PERIOD) {
+ ERR("Call Home client \"%s\" is not of periodic connection type.");
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+ return -1;
+ }
+
+ client->conn.period.period = period;
+
+ /* UNLOCK */
+ nc_server_ch_client_unlock(client);
+
+ return 0;
+}
+
+API int
+nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
{
struct nc_ch_client *client;
@@ -2673,14 +2863,14 @@
return -1;
}
- if (client->conn_type != NC_CH_PERSIST) {
- ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
+ if (client->conn_type != NC_CH_PERIOD) {
+ ERR("Call Home client \"%s\" is not of periodic connection type.");
/* UNLOCK */
nc_server_ch_client_unlock(client);
return -1;
}
- client->conn.persist.idle_timeout = idle_timeout;
+ client->conn.period.anchor_time = anchor_time;
/* UNLOCK */
nc_server_ch_client_unlock(client);
@@ -2689,72 +2879,7 @@
}
API int
-nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
-{
- struct nc_ch_client *client;
-
- if (!client_name) {
- ERRARG("client_name");
- return -1;
- } else if (!max_wait) {
- ERRARG("max_wait");
- return -1;
- }
-
- /* LOCK */
- client = nc_server_ch_client_lock(client_name, 0, NULL);
- if (!client) {
- return -1;
- }
-
- if (client->conn_type != NC_CH_PERSIST) {
- ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
- return -1;
- }
-
- client->conn.persist.ka_max_wait = max_wait;
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return 0;
-}
-
-API int
-nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
-{
- struct nc_ch_client *client;
-
- if (!client_name) {
- ERRARG("client_name");
- return -1;
- }
-
- /* LOCK */
- client = nc_server_ch_client_lock(client_name, 0, NULL);
- if (!client) {
- return -1;
- }
-
- if (client->conn_type != NC_CH_PERSIST) {
- ERR("Call Home client \"%s\" is not of persistent connection type.", client_name);
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
- return -1;
- }
-
- client->conn.persist.ka_max_attempts = max_attempts;
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return 0;
-}
-
-API int
-nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
+nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
{
struct nc_ch_client *client;
@@ -2785,40 +2910,6 @@
}
API int
-nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
-{
- struct nc_ch_client *client;
-
- if (!client_name) {
- ERRARG("client_name");
- return -1;
- } else if (!reconnect_timeout) {
- ERRARG("reconnect_timeout");
- return -1;
- }
-
- /* LOCK */
- client = nc_server_ch_client_lock(client_name, 0, NULL);
- if (!client) {
- return -1;
- }
-
- if (client->conn_type != NC_CH_PERIOD) {
- ERR("Call Home client \"%s\" is not of periodic connection type.");
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
- return -1;
- }
-
- client->conn.period.reconnect_timeout = reconnect_timeout;
-
- /* UNLOCK */
- nc_server_ch_client_unlock(client);
-
- return 0;
-}
-
-API int
nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
{
struct nc_ch_client *client;
@@ -2878,7 +2969,7 @@
struct timespec ts_cur;
char *ip_host;
- sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->sock_pending, &ip_host);
+ sock = nc_sock_connect(endpt->address, endpt->port, 5, &endpt->ka, &endpt->sock_pending, &ip_host);
if (sock < 0) {
return NC_MSG_ERROR;
}
@@ -3043,11 +3134,10 @@
break;
}
- if (client->conn_type == NC_CH_PERSIST) {
- /* TODO keep-alives */
- idle_timeout = client->conn.persist.idle_timeout;
- } else {
+ if (client->conn_type == NC_CH_PERIOD) {
idle_timeout = client->conn.period.idle_timeout;
+ } else {
+ idle_timeout = 0;
}
nc_gettimespec_mono(&ts);
@@ -3085,6 +3175,7 @@
struct nc_session *session;
struct nc_ch_client *client;
uint32_t client_id;
+ time_t reconnect_in;
/* LOCK */
client = nc_server_ch_client_with_endpt_lock(data->client_name);
@@ -3121,13 +3212,14 @@
}
/* session changed status -> it was disconnected for whatever reason,
- * persistent connection immediately tries to reconnect, periodic waits some first */
+ * persistent connection immediately tries to reconnect, periodic connects at specific times */
if (client->conn_type == NC_CH_PERIOD) {
/* UNLOCK */
nc_server_ch_client_unlock(client);
- /* TODO wake up sometimes to check for new notifications */
- usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
+ /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
+ reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
+ sleep(reconnect_in);
/* LOCK */
client = nc_server_ch_client_with_endpt_lock(data->client_name);
@@ -3143,7 +3235,7 @@
/* set next endpoint to try */
if (client->start_with == NC_CH_FIRST_LISTED) {
next_endpt_index = 0;
- } else {
+ } else if (client->start_with == NC_CH_LAST_CONNECTED) {
/* we keep the current one but due to unlock/lock we have to find it again */
for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
@@ -3154,6 +3246,9 @@
/* endpoint was removed, start with the first one */
next_endpt_index = 0;
}
+ } else {
+ /* just get a random index */
+ next_endpt_index = rand() % client->ch_endpt_count;
}
} else {
@@ -3258,12 +3353,6 @@
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
-API int
-nc_server_endpt_count(void)
-{
- return server_opts.endpt_count;
-}
-
API time_t
nc_session_get_start_time(const struct nc_session *session)
{
diff --git a/src/session_server.h b/src/session_server.h
index 9e20fd2..8f8472c 100644
--- a/src/session_server.h
+++ b/src/session_server.h
@@ -417,6 +417,26 @@
*/
int nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid);
+/**
+ * @brief Change endpoint keepalives state. Affects only new connections.
+ *
+ * @param[in] endpt_name Existing endpoint name.
+ * @param[in] enable Whether to enable or disable keepalives.
+ * @return 0 on success, -1 on error.
+ */
+int nc_server_endpt_enable_keepalives(const char *endpt_name, int enable);
+
+/**
+ * @brief Change endpoint keepalives parameters. Affects only new connections.
+ *
+ * @param[in] endpt_name Existing endpoint name.
+ * @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
+ * @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
+ * @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
+ * @return 0 on success, -1 on error.
+ */
+int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval);
+
/**@} Server */
/**
@@ -477,7 +497,7 @@
/**
* @brief Add an authorized client SSH public key. This public key can be used for
- * publickey authentication (for any SSH connection, even Call Home) afterwards.
+ * publickey authentication (for any SSH connection, even Call Home) afterwards.
*
* @param[in] pubkey_base64 Authorized public key binary content encoded in base64.
* @param[in] type Authorized public key SSH type.
@@ -558,8 +578,7 @@
* @param[in] free_user_data Optional callback that will be called during cleanup to free any \p user_data.
*/
void nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
- char **privkey_data, NC_SSH_KEY *privkey_type),
- void *user_data, void (*free_user_data)(void *user_data));
+ char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Add endpoint SSH host keys the server will identify itself with. Only the name is set, the key itself
@@ -677,8 +696,8 @@
* @param[in] free_user_data Optional callback that will be called during cleanup to free any \p user_data.
*/
void nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,
- char **privkey_path, char **privkey_data, NC_SSH_KEY *privkey_type),
- void *user_data, void (*free_user_data)(void *user_data));
+ char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data,
+ void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for retrieving server certificate chain
@@ -694,8 +713,7 @@
* @param[in] free_user_data Optional callback that will be called during cleanup to free any \p user_data.
*/
void nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
- int *cert_path_count, char ***cert_data, int *cert_data_count),
- void *user_data, void (*free_user_data)(void *user_data));
+ int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Add a trusted certificate list. Can be both a CA or a client one. Can be
@@ -721,8 +739,7 @@
* @param[in] free_user_data Optional callback that will be called during cleanup to free any \p user_data.
*/
void nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths,
- int *cert_path_count, char ***cert_data, int *cert_data_count),
- void *user_data, void (*free_user_data)(void *user_data));
+ int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Remove a trusted certificate.
diff --git a/src/session_server_ch.h b/src/session_server_ch.h
index 17b4b90..975e845 100644
--- a/src/session_server_ch.h
+++ b/src/session_server_ch.h
@@ -98,6 +98,29 @@
int nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port);
/**
+ * @brief Change Call Home client endpoint keepalives state. Affects only new connections.
+ *
+ * @param[in] client_name Existing Call Home client name.
+ * @param[in] endpt_name Existing endpoint name of \p client_name.
+ * @param[in] enable Whether to enable or disable keepalives.
+ * @return 0 on success, -1 on error.
+ */
+int nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable);
+
+/**
+ * @brief Change Call Home client endpoint keepalives parameters. Affects only new connections.
+ *
+ * @param[in] client_name Existing Call Home client name.
+ * @param[in] endpt_name Existing endpoint name of \p client_name.
+ * @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
+ * @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
+ * @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
+ * @return 0 on success, -1 on error.
+ */
+int nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time,
+ int max_probes, int probe_interval);
+
+/**
* @brief Set Call Home client connection type.
*
* @param[in] client_name Existing Call Home client name.
@@ -107,31 +130,22 @@
int nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type);
/**
- * @brief Set Call Home client persistent connection idle timeout.
+ * @brief Set Call Home client periodic connection period for reconnecting.
*
* @param[in] client_name Existing Call Home client name.
- * @param[in] idle_timeout Call Home persistent idle timeout.
+ * @param[in] period Call Home periodic connection period in minutes.
* @return 0 on success, -1 on error.
*/
-int nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout);
+int nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period);
/**
- * @brief Set Call Home client persistent connection keep-alive max wait time.
+ * @brief Set Call Home client periodic connection period anchor time.
*
* @param[in] client_name Existing Call Home client name.
- * @param[in] max_wait Call Home persistent max wait time for keep-alive reply.
+ * @param[in] anchor_time Call Home periodic connection anchor time for the period.
* @return 0 on success, -1 on error.
*/
-int nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait);
-
-/**
- * @brief Set Call Home client persistent connection keep-alive max attempts.
- *
- * @param[in] client_name Existing Call Home client name.
- * @param[in] max_attempts Call Home persistent keep-alive maximum contact attempts.
- * @return 0 on success, -1 on error.
- */
-int nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts);
+int nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time);
/**
* @brief Set Call Home client periodic connection idle timeout.
@@ -140,16 +154,7 @@
* @param[in] idle_timeout Call Home periodic idle timeout.
* @return 0 on success, -1 on error.
*/
-int nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout);
-
-/**
- * @brief Set Call Home client periodic reconnect timeout.
- *
- * @param[in] client_name Existing Call Home client name.
- * @param[in] reconnect_timeout Call Home periodic reconnect timeout.
- * @return 0 on success, -1 on error.
- */
-int nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout);
+int nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout);
/**
* @brief Set Call Home client start-with policy.
diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c
index 73a2ded..f0fd245 100644
--- a/src/session_server_ssh.c
+++ b/src/session_server_ssh.c
@@ -181,8 +181,7 @@
API void
nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
- char **privkey_data, NC_SSH_KEY *privkey_type),
- void *user_data, void (*free_user_data)(void *user_data))
+ char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data))
{
if (!hostkey_clb) {
ERRARG("hostkey_clb");
@@ -1311,7 +1310,7 @@
uint8_t i;
char *privkey_path, *privkey_data;
int ret;
- NC_SSH_KEY privkey_type;
+ NC_SSH_KEY_TYPE privkey_type;
if (!server_opts.hostkey_clb) {
ERR("Callback for retrieving SSH host keys not set.");
diff --git a/src/session_server_tls.c b/src/session_server_tls.c
index 9e2db74..5432418 100644
--- a/src/session_server_tls.c
+++ b/src/session_server_tls.c
@@ -974,8 +974,8 @@
API void
nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,
- char **privkey_path, char **privkey_data, NC_SSH_KEY *privkey_type),
- void *user_data, void (*free_user_data)(void *user_data))
+ char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data,
+ void (*free_user_data)(void *user_data))
{
if (!cert_clb) {
ERRARG("cert_clb");
@@ -989,8 +989,7 @@
API void
nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
- int *cert_path_count, char ***cert_data, int *cert_data_count),
- void *user_data, void (*free_user_data)(void *user_data))
+ int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data))
{
if (!cert_chain_clb) {
ERRARG("cert_chain_clb");
@@ -1798,7 +1797,7 @@
{
char *cert_path = NULL, *cert_data = NULL, *privkey_path = NULL, *privkey_data = NULL;
int ret = 0;
- NC_SSH_KEY privkey_type;
+ NC_SSH_KEY_TYPE privkey_type;
X509 *cert = NULL;
EVP_PKEY *pkey = NULL;
@@ -1811,7 +1810,7 @@
}
if (server_opts.server_cert_clb(cert_name, server_opts.server_cert_data, &cert_path, &cert_data, &privkey_path,
- &privkey_data, &privkey_type)) {
+ &privkey_data, &privkey_type)) {
ERR("Server certificate callback failed.");
return -1;
}