blob: 74b085189860e1ce7c57d28e7b1a93bfe4606480 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
apropp-molex4e903c32020-04-20 03:06:58 -040014#define _QNX_SOURCE /* getpeereid */
Olivier Matzac7fa2f2018-10-11 10:02:04 +020015#define _GNU_SOURCE /* signals, threads, SO_PEERCRED */
Michal Vasko086311b2016-01-08 09:53:11 +010016
Michal Vaskob83a3fa2021-05-26 09:53:42 +020017#include <arpa/inet.h>
Michal Vasko086311b2016-01-08 09:53:11 +010018#include <errno.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020019#include <fcntl.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020020#include <netinet/in.h>
21#include <netinet/tcp.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020022#include <poll.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010023#include <pthread.h>
Olivier Matzac7fa2f2018-10-11 10:02:04 +020024#include <pwd.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020025#include <signal.h>
26#include <stdint.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/socket.h>
30#include <sys/types.h>
31#include <sys/un.h>
32#include <time.h>
33#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010034
Michal Vasko7a20d2e2021-05-19 16:40:23 +020035#include "compat.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010036#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037#include "session_server.h"
Michal Vasko0bdf70b2019-06-24 19:20:20 +020038#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010039
Michal Vaskob48aa812016-01-18 14:13:09 +010040struct nc_server_opts server_opts = {
Michal Vaskoade892d2017-02-22 13:40:35 +010041#ifdef NC_ENABLED_SSH
42 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
43#endif
44 .bind_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko2e6defd2016-10-07 15:48:15 +020045 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
46 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010047};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010048
fanchanghu966f2de2016-07-21 02:28:57 -040049static nc_rpc_clb global_rpc_clb = NULL;
50
Michal Vasko3031aae2016-01-27 16:07:18 +010051struct nc_endpt *
Michal Vaskoade892d2017-02-22 13:40:35 +010052nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010053{
54 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010055 struct nc_endpt *endpt = NULL;
56
Michal Vaskoddce1212019-05-24 09:58:49 +020057 if (!name) {
58 ERRARG("endpt_name");
59 return NULL;
60 }
61
Michal Vaskoade892d2017-02-22 13:40:35 +010062 /* WRITE LOCK */
63 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010064
65 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020066 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010067 endpt = &server_opts.endpts[i];
68 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010069 }
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 if (!endpt) {
Michal Vasko05532772021-06-03 12:12:38 +020073 ERR(NULL, "Endpoint \"%s\" was not found.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +010074 /* UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020075 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010076 return NULL;
77 }
78
Michal Vaskoe2713da2016-08-22 16:06:40 +020079 if (idx) {
80 *idx = i;
81 }
82
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010083 return endpt;
84}
85
Michal Vaskoadf30f02019-06-24 09:34:47 +020086struct nc_ch_endpt *
87nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_IMPL ti, struct nc_ch_client **client_p)
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010088{
Michal Vaskoadf30f02019-06-24 09:34:47 +020089 uint16_t i, j;
Michal Vasko2e6defd2016-10-07 15:48:15 +020090 struct nc_ch_client *client = NULL;
Michal Vaskoadf30f02019-06-24 09:34:47 +020091 struct nc_ch_endpt *endpt = NULL;
92
93 *client_p = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +020094
Michal Vaskoddce1212019-05-24 09:58:49 +020095 if (!name) {
96 ERRARG("client_name");
97 return NULL;
98 }
99
Michal Vasko2e6defd2016-10-07 15:48:15 +0200100 /* READ LOCK */
101 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
102
103 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200104 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200105 client = &server_opts.ch_clients[i];
Michal Vaskoadf30f02019-06-24 09:34:47 +0200106 if (!endpt_name && !ti) {
107 /* return only client */
108 break;
109 }
110 for (j = 0; j < client->ch_endpt_count; ++j) {
Michal Vasko530d95c2021-05-28 13:32:02 +0200111 if ((!endpt_name || !strcmp(client->ch_endpts[j].name, endpt_name)) &&
112 (!ti || (ti == client->ch_endpts[j].ti))) {
Michal Vaskoadf30f02019-06-24 09:34:47 +0200113 endpt = &client->ch_endpts[j];
114 break;
115 }
116 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200117 break;
118 }
119 }
120
121 if (!client) {
Michal Vasko05532772021-06-03 12:12:38 +0200122 ERR(NULL, "Call Home client \"%s\" was not found.", name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200123
Michal Vasko2e6defd2016-10-07 15:48:15 +0200124 /* READ UNLOCK */
125 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200126 } else if (endpt_name && ti && !endpt) {
Michal Vasko05532772021-06-03 12:12:38 +0200127 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" was not found.", name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +0200128
129 /* READ UNLOCK */
130 pthread_rwlock_unlock(&server_opts.ch_client_lock);
131 } else {
132 /* CH CLIENT LOCK */
133 pthread_mutex_lock(&client->lock);
134
135 *client_p = client;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200136 }
137
Michal Vaskoadf30f02019-06-24 09:34:47 +0200138 return endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200139}
140
141void
142nc_server_ch_client_unlock(struct nc_ch_client *client)
143{
144 /* CH CLIENT UNLOCK */
145 pthread_mutex_unlock(&client->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100146
147 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200148 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100149}
Michal Vasko086311b2016-01-08 09:53:11 +0100150
Michal Vasko1a38c862016-01-15 15:50:07 +0100151API void
152nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
153{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200154 if (!session) {
155 ERRARG("session");
156 return;
157 } else if (!reason) {
158 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100159 return;
160 }
161
Michal Vasko142cfea2017-08-07 10:12:11 +0200162 if ((reason != NC_SESSION_TERM_KILLED) && (session->term_reason == NC_SESSION_TERM_KILLED)) {
163 session->killed_by = 0;
164 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100165 session->term_reason = reason;
166}
167
Michal Vasko142cfea2017-08-07 10:12:11 +0200168API void
169nc_session_set_killed_by(struct nc_session *session, uint32_t sid)
170{
171 if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) {
172 ERRARG("session");
173 return;
174 } else if (!sid) {
175 ERRARG("sid");
176 return;
177 }
178
179 session->killed_by = sid;
180}
181
182API void
183nc_session_set_status(struct nc_session *session, NC_STATUS status)
184{
185 if (!session) {
186 ERRARG("session");
187 return;
188 } else if (!status) {
189 ERRARG("status");
190 return;
191 }
192
193 session->status = status;
194}
195
Michal Vasko086311b2016-01-08 09:53:11 +0100196int
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200197nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
Michal Vasko086311b2016-01-08 09:53:11 +0100198{
Michal Vasko06c860d2018-07-09 16:08:52 +0200199 int opt;
Michal Vasko086311b2016-01-08 09:53:11 +0100200 int is_ipv4, sock;
201 struct sockaddr_storage saddr;
202
203 struct sockaddr_in *saddr4;
204 struct sockaddr_in6 *saddr6;
205
Michal Vasko086311b2016-01-08 09:53:11 +0100206 if (!strchr(address, ':')) {
207 is_ipv4 = 1;
208 } else {
209 is_ipv4 = 0;
210 }
211
212 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
213 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200214 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100215 goto fail;
216 }
217
Michal Vaskobe52dc22018-10-17 09:28:17 +0200218 /* these options will be inherited by accepted sockets */
Michal Vasko06c860d2018-07-09 16:08:52 +0200219 opt = 1;
220 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200221 ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko06c860d2018-07-09 16:08:52 +0200222 goto fail;
223 }
Michal Vasko83ad17e2019-01-30 10:11:37 +0100224 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200225 ERR(NULL, "Could not set TCP_NODELAY socket option (%s).", strerror(errno));
Michal Vasko83ad17e2019-01-30 10:11:37 +0100226 goto fail;
227 }
Michal Vaskobe52dc22018-10-17 09:28:17 +0200228
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200229 if (nc_sock_enable_keepalive(sock, ka)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100230 goto fail;
231 }
232
Michal Vaskof22d5ff2020-04-15 11:10:27 +0200233 memset(&saddr, 0, sizeof(struct sockaddr_storage));
Michal Vasko086311b2016-01-08 09:53:11 +0100234 if (is_ipv4) {
235 saddr4 = (struct sockaddr_in *)&saddr;
236
237 saddr4->sin_family = AF_INET;
238 saddr4->sin_port = htons(port);
239
240 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200241 ERR(NULL, "Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100242 goto fail;
243 }
244
245 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200246 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100247 goto fail;
248 }
249
250 } else {
251 saddr6 = (struct sockaddr_in6 *)&saddr;
252
253 saddr6->sin6_family = AF_INET6;
254 saddr6->sin6_port = htons(port);
255
256 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200257 ERR(NULL, "Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100258 goto fail;
259 }
260
261 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200262 ERR(NULL, "Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100263 goto fail;
264 }
265 }
266
Michal Vaskofb89d772016-01-08 12:25:35 +0100267 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200268 ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100269 goto fail;
270 }
271
272 return sock;
273
274fail:
275 if (sock > -1) {
276 close(sock);
277 }
278
279 return -1;
280}
281
282int
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200283nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts)
284{
285 struct sockaddr_un sun;
286 int sock = -1;
287
Michal Vasko93e96f12021-09-30 10:02:09 +0200288 if (strlen(address) > sizeof(sun.sun_path) - 1) {
289 ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", address, (int)(sizeof(sun.sun_path) - 1));
290 goto fail;
291 }
292
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200293 sock = socket(AF_UNIX, SOCK_STREAM, 0);
294 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200295 ERR(NULL, "Failed to create socket (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200296 goto fail;
297 }
298
299 memset(&sun, 0, sizeof(sun));
300 sun.sun_family = AF_UNIX;
Michal Vasko93e96f12021-09-30 10:02:09 +0200301 snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", address);
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200302
303 unlink(sun.sun_path);
304 if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200305 ERR(NULL, "Could not bind \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200306 goto fail;
307 }
308
309 if (opts->mode != (mode_t)-1) {
310 if (chmod(sun.sun_path, opts->mode) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200311 ERR(NULL, "Failed to set unix socket permissions (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200312 goto fail;
313 }
314 }
315
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200316 if ((opts->uid != (uid_t)-1) || (opts->gid != (gid_t)-1)) {
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200317 if (chown(sun.sun_path, opts->uid, opts->gid) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200318 ERR(NULL, "Failed to set unix socket uid/gid (%s).", strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200319 goto fail;
320 }
321 }
322
323 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200324 ERR(NULL, "Unable to start listening on \"%s\" (%s).", address, strerror(errno));
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200325 goto fail;
326 }
327
328 return sock;
329
330fail:
331 if (sock > -1) {
332 close(sock);
333 }
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200334 return -1;
335}
336
aPiecek90ff0242021-02-14 14:58:01 +0100337/**
338 * @brief Evaluate socket name for AF_UNIX socket.
339 * @param[in] acc_sock_fd is file descriptor for the accepted socket (a nonnegative).
340 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
341 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
342 * @return 0 if the stream socket is unnamed. Parameter host is set to NULL.
343 * @return -1 in case of error. Parameter host is set to NULL.
344 */
345static int
346sock_host_unix(int acc_sock_fd, char **host)
347{
348 char *sun_path;
349 struct sockaddr_storage saddr;
350 socklen_t addr_len;
351
352 *host = NULL;
353 saddr.ss_family = AF_UNIX;
354 addr_len = sizeof(saddr);
355
356 if (getsockname(acc_sock_fd, (struct sockaddr *)&saddr, &addr_len)) {
Michal Vasko05532772021-06-03 12:12:38 +0200357 ERR(NULL, "getsockname failed (%s).", strerror(errno));
aPiecek90ff0242021-02-14 14:58:01 +0100358 return -1;
359 }
360
361 sun_path = ((struct sockaddr_un *)&saddr)->sun_path;
362 if (!sun_path) {
363 /* stream socket is unnamed */
364 return 0;
365 }
366
367 if (!(*host = strdup(sun_path))) {
368 ERRMEM;
369 return -1;
370 }
371
372 return 0;
373}
374
375/**
376 * @brief Evaluate socket name and port number for AF_INET socket.
377 * @param[in] addr is pointing to structure filled by accept function which was successful.
378 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
379 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
380 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
381 * @return -1 in case of error. Parameter host is set to NULL and port is unchanged.
382 */
383static int
384sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port)
385{
386 *host = malloc(INET_ADDRSTRLEN);
387 if (!(*host)) {
388 ERRMEM;
389 return -1;
390 }
391
aPiecek3da9b342021-02-18 15:00:03 +0100392 if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200393 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100394 free(*host);
395 *host = NULL;
396 return -1;
397 }
398
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200399 *port = ntohs(addr->sin_port);
aPiecek90ff0242021-02-14 14:58:01 +0100400
401 return 0;
402}
403
404/**
405 * @brief Evaluate socket name and port number for AF_INET6 socket.
406 * @param[in] addr is pointing to structure filled by accept function which was successful.
407 * @param[out] host is pointer to char* to which the socket name will be set. It must not be NULL.
408 * @param[out] port is pointer to uint16_t to which the port number will be set. It must not be NULL.
409 * @return 0 in case of success. Call free function for parameter host to avoid a memory leak.
410 * @return -1 in case of error. Parameter host is set to the NULL and port is unchanged.
411 */
412static int
413sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port)
414{
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200415 *host = malloc(INET6_ADDRSTRLEN);
aPiecek90ff0242021-02-14 14:58:01 +0100416 if (!(*host)) {
417 ERRMEM;
418 return -1;
419 }
420
aPiecek3da9b342021-02-18 15:00:03 +0100421 if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) {
Michal Vasko05532772021-06-03 12:12:38 +0200422 ERR(NULL, "inet_ntop failed(%s).");
aPiecek90ff0242021-02-14 14:58:01 +0100423 free(*host);
424 *host = NULL;
425 return -1;
426 }
427
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200428 *port = ntohs(addr->sin6_port);
aPiecek90ff0242021-02-14 14:58:01 +0100429
430 return 0;
431}
432
Olivier Matzac7fa2f2018-10-11 10:02:04 +0200433int
Michal Vasko3031aae2016-01-27 16:07:18 +0100434nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100435{
Michal Vaskof54cd352017-02-22 13:42:02 +0100436 sigset_t sigmask, origmask;
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200437 uint16_t i, j, pfd_count, client_port;
438 char *client_address;
Michal Vasko086311b2016-01-08 09:53:11 +0100439 struct pollfd *pfd;
440 struct sockaddr_storage saddr;
441 socklen_t saddr_len = sizeof(saddr);
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200442 int ret, client_sock, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100443
444 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100445 if (!pfd) {
446 ERRMEM;
447 return -1;
448 }
449
Michal Vaskoac2f6182017-01-30 14:32:03 +0100450 for (i = 0, pfd_count = 0; i < bind_count; ++i) {
Michal Vasko94acafc2016-09-23 13:40:10 +0200451 if (binds[i].sock < 0) {
452 /* invalid socket */
Michal Vasko94acafc2016-09-23 13:40:10 +0200453 continue;
454 }
Michal Vasko0a3f3752016-10-13 14:58:38 +0200455 if (binds[i].pollin) {
456 binds[i].pollin = 0;
457 /* leftover pollin */
458 sock = binds[i].sock;
Michal Vasko086311b2016-01-08 09:53:11 +0100459 break;
460 }
Michal Vaskoac2f6182017-01-30 14:32:03 +0100461 pfd[pfd_count].fd = binds[i].sock;
462 pfd[pfd_count].events = POLLIN;
463 pfd[pfd_count].revents = 0;
464
465 ++pfd_count;
Michal Vasko086311b2016-01-08 09:53:11 +0100466 }
467
Michal Vasko0a3f3752016-10-13 14:58:38 +0200468 if (sock == -1) {
469 /* poll for a new connection */
Michal Vaskof54cd352017-02-22 13:42:02 +0100470 sigfillset(&sigmask);
471 pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
Michal Vaskoac2f6182017-01-30 14:32:03 +0100472 ret = poll(pfd, pfd_count, timeout);
Michal Vaskof54cd352017-02-22 13:42:02 +0100473 pthread_sigmask(SIG_SETMASK, &origmask, NULL);
474
Michal Vasko0a3f3752016-10-13 14:58:38 +0200475 if (!ret) {
476 /* we timeouted */
477 free(pfd);
478 return 0;
479 } else if (ret == -1) {
Michal Vasko05532772021-06-03 12:12:38 +0200480 ERR(NULL, "Poll failed (%s).", strerror(errno));
Michal Vasko0a3f3752016-10-13 14:58:38 +0200481 free(pfd);
482 return -1;
483 }
Michal Vasko086311b2016-01-08 09:53:11 +0100484
Michal Vaskoac2f6182017-01-30 14:32:03 +0100485 for (i = 0, j = 0; j < pfd_count; ++i, ++j) {
486 /* adjust i so that indices in binds and pfd always match */
487 while (binds[i].sock != pfd[j].fd) {
488 ++i;
489 }
490
491 if (pfd[j].revents & POLLIN) {
Michal Vasko0a3f3752016-10-13 14:58:38 +0200492 --ret;
493
494 if (!ret) {
495 /* the last socket with an event, use it */
Michal Vaskoac2f6182017-01-30 14:32:03 +0100496 sock = pfd[j].fd;
Michal Vasko0a3f3752016-10-13 14:58:38 +0200497 break;
498 } else {
499 /* just remember the event for next time */
500 binds[i].pollin = 1;
501 }
502 }
Michal Vasko086311b2016-01-08 09:53:11 +0100503 }
504 }
505 free(pfd);
506
507 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100508 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100509 return -1;
510 }
511
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200512 /* accept connection */
513 client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
514 if (client_sock < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200515 ERR(NULL, "Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100516 return -1;
517 }
518
Michal Vasko0190bc32016-03-02 15:47:49 +0100519 /* make the socket non-blocking */
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200520 if (((flags = fcntl(client_sock, F_GETFL)) == -1) || (fcntl(client_sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200521 ERR(NULL, "Fcntl failed (%s).", strerror(errno));
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200522 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100523 }
524
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200525 /* learn information about the client end */
526 if (saddr.ss_family == AF_UNIX) {
527 if (sock_host_unix(client_sock, &client_address)) {
528 goto fail;
529 }
530 client_port = 0;
531 } else if (saddr.ss_family == AF_INET) {
532 if (sock_host_inet((struct sockaddr_in *)&saddr, &client_address, &client_port)) {
533 goto fail;
534 }
535 } else if (saddr.ss_family == AF_INET6) {
536 if (sock_host_inet6((struct sockaddr_in6 *)&saddr, &client_address, &client_port)) {
537 goto fail;
538 }
539 } else {
540 ERR(NULL, "Source host of an unknown protocol family.");
541 goto fail;
aPiecek90ff0242021-02-14 14:58:01 +0100542 }
Michal Vasko086311b2016-01-08 09:53:11 +0100543
aPiecek90ff0242021-02-14 14:58:01 +0100544 if (saddr.ss_family == AF_UNIX) {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200545 VRB(NULL, "Accepted a connection on %s.", binds[i].address);
aPiecek90ff0242021-02-14 14:58:01 +0100546 } else {
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200547 VRB(NULL, "Accepted a connection on %s:%u from %s:%u.", binds[i].address, binds[i].port, client_address, client_port);
Michal Vasko086311b2016-01-08 09:53:11 +0100548 }
549
Michal Vasko89ffa8a2021-06-25 08:40:08 +0200550 if (host) {
551 *host = client_address;
552 } else {
553 free(client_address);
554 }
555 if (port) {
556 *port = client_port;
557 }
558 if (idx) {
559 *idx = i;
560 }
561 return client_sock;
562
563fail:
564 close(client_sock);
565 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100566}
567
Michal Vasko05ba9df2016-01-13 14:40:27 +0100568static struct nc_server_reply *
Michal Vasko05532772021-06-03 12:12:38 +0200569nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100570{
Michal Vasko77367452021-02-16 16:32:18 +0100571 const char *identifier = NULL, *revision = NULL, *format = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100572 char *model_data = NULL;
Michal Vasko77367452021-02-16 16:32:18 +0100573 struct ly_out *out;
Michal Vasko9b1a9522021-03-15 16:24:26 +0100574 const struct lys_module *module = NULL, *mod;
Michal Vasko77367452021-02-16 16:32:18 +0100575 const struct lysp_submodule *submodule = NULL;
576 struct lyd_node *child, *err, *data = NULL;
577 LYS_OUTFORMAT outformat = 0;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100578
Michal Vasko77367452021-02-16 16:32:18 +0100579 LY_LIST_FOR(lyd_child(rpc), child) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100580 if (!strcmp(child->schema->name, "identifier")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200581 identifier = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100582 } else if (!strcmp(child->schema->name, "version")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200583 revision = lyd_get_value(child);
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200584 if (revision && (revision[0] == '\0')) {
Michal Vasko77367452021-02-16 16:32:18 +0100585 revision = NULL;
Radek Krejci1afa7792017-03-26 11:24:16 -0500586 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100587 } else if (!strcmp(child->schema->name, "format")) {
Michal Vaskoe97b10a2021-04-28 08:52:52 +0200588 format = lyd_get_value(child);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100589 }
590 }
Michal Vasko05532772021-06-03 12:12:38 +0200591 VRB(session, "Schema \"%s@%s\" was requested.", identifier, revision ? revision : "<any>");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100592
Michal Vasko77367452021-02-16 16:32:18 +0100593 /* check revision */
594 if (revision && (strlen(revision) != 10) && strcmp(revision, "1.0")) {
595 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100596 nc_err_set_msg(err, "The requested version is not supported.", "en");
597 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100598 }
599
Michal Vasko77367452021-02-16 16:32:18 +0100600 if (revision) {
601 /* get specific module */
602 module = ly_ctx_get_module(server_opts.ctx, identifier, revision);
603 if (!module) {
604 submodule = ly_ctx_get_submodule(server_opts.ctx, identifier, revision);
605 }
606 } else {
607 /* try to get implemented, then latest module */
608 module = ly_ctx_get_module_implemented(server_opts.ctx, identifier);
609 if (!module) {
610 module = ly_ctx_get_module_latest(server_opts.ctx, identifier);
611 }
612 if (!module) {
613 submodule = ly_ctx_get_submodule_latest(server_opts.ctx, identifier);
614 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200615 }
Michal Vasko77367452021-02-16 16:32:18 +0100616 if (!module && !submodule) {
617 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100618 nc_err_set_msg(err, "The requested schema was not found.", "en");
619 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100620 }
621
622 /* check format */
Radek Krejci90fba642016-12-07 15:59:45 +0100623 if (!format || !strcmp(format, "ietf-netconf-monitoring:yang")) {
Michal Vasko77367452021-02-16 16:32:18 +0100624 outformat = LYS_OUT_YANG;
Radek Krejci90fba642016-12-07 15:59:45 +0100625 } else if (!strcmp(format, "ietf-netconf-monitoring:yin")) {
Michal Vasko77367452021-02-16 16:32:18 +0100626 outformat = LYS_OUT_YIN;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100627 } else {
Michal Vasko77367452021-02-16 16:32:18 +0100628 err = nc_err(server_opts.ctx, NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
Michal Vasko1a38c862016-01-15 15:50:07 +0100629 nc_err_set_msg(err, "The requested format is not supported.", "en");
630 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100631 }
Michal Vasko77367452021-02-16 16:32:18 +0100632
633 /* print */
634 ly_out_new_memory(&model_data, 0, &out);
635 if (module) {
636 lys_print_module(out, module, outformat, 0, 0);
637 } else {
638 lys_print_submodule(out, submodule, outformat, 0, 0);
639 }
640 ly_out_free(out, NULL, 0);
Michal Vaskod91f6e62016-04-05 11:34:22 +0200641 if (!model_data) {
642 ERRINT;
643 return NULL;
644 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100645
Michal Vasko9b1a9522021-03-15 16:24:26 +0100646 /* create reply */
647 mod = ly_ctx_get_module_implemented(server_opts.ctx, "ietf-netconf-monitoring");
648 if (!mod || lyd_new_inner(NULL, mod, "get-schema", 0, &data)) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100649 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200650 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100651 return NULL;
652 }
Michal Vasko9b1a9522021-03-15 16:24:26 +0100653 lydict_insert_zc(server_opts.ctx, model_data, (const char **)&model_data);
654 if (lyd_new_any(data, NULL, "data", model_data, 1, LYD_ANYDATA_STRING, 1, NULL)) {
655 ERRINT;
656 lydict_remove(server_opts.ctx, model_data);
657 lyd_free_tree(data);
658 return NULL;
659 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100660
Radek Krejci36dfdb32016-09-01 16:56:35 +0200661 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100662}
663
664static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100665nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100666{
Michal Vasko428087d2016-01-14 16:04:28 +0100667 session->term_reason = NC_SESSION_TERM_CLOSED;
668 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100669}
670
Michal Vasko086311b2016-01-08 09:53:11 +0100671API int
672nc_server_init(struct ly_ctx *ctx)
673{
Michal Vasko77367452021-02-16 16:32:18 +0100674 struct lysc_node *rpc;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000675 pthread_rwlockattr_t attr;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100676
Michal Vasko086311b2016-01-08 09:53:11 +0100677 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200678 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100679 return -1;
680 }
681
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100682 nc_init();
683
Michal Vasko05ba9df2016-01-13 14:40:27 +0100684 /* set default <get-schema> callback if not specified */
Michal Vasko77367452021-02-16 16:32:18 +0100685 rpc = NULL;
686 if (ly_ctx_get_module_implemented(ctx, "ietf-netconf-monitoring")) {
687 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf-monitoring:get-schema", 0);
688 }
Michal Vasko88639e92017-08-03 14:38:10 +0200689 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100690 rpc->priv = nc_clb_default_get_schema;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100691 }
692
693 /* set default <close-session> callback if not specififed */
Michal Vasko77367452021-02-16 16:32:18 +0100694 rpc = (struct lysc_node *)lys_find_path(ctx, NULL, "/ietf-netconf:close-session", 0);
Michal Vasko88639e92017-08-03 14:38:10 +0200695 if (rpc && !rpc->priv) {
Michal Vasko77367452021-02-16 16:32:18 +0100696 rpc->priv = nc_clb_default_close_session;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100697 }
698
Michal Vasko086311b2016-01-08 09:53:11 +0100699 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100700
701 server_opts.new_session_id = 1;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -0500702 server_opts.new_client_id = 1;
Michal Vaskob48aa812016-01-18 14:13:09 +0100703
Michal Vasko77367452021-02-16 16:32:18 +0100704 errno = 0;
Frank Rimpler9f838b02018-07-25 06:44:03 +0000705
706 if (pthread_rwlockattr_init(&attr) == 0) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200707#if defined (HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
Frank Rimpler9f838b02018-07-25 06:44:03 +0000708 if (pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) == 0) {
709 if (pthread_rwlock_init(&server_opts.endpt_lock, &attr) != 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200710 ERR(NULL, "%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000711 }
712 if (pthread_rwlock_init(&server_opts.ch_client_lock, &attr) != 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200713 ERR(NULL, "%s: failed to init rwlock(%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000714 }
715 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200716 ERR(NULL, "%s: failed set attribute (%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000717 }
Rosen Penevef2f3ac2019-07-15 18:15:28 -0700718#endif
Frank Rimpler9f838b02018-07-25 06:44:03 +0000719 pthread_rwlockattr_destroy(&attr);
720 } else {
Michal Vasko05532772021-06-03 12:12:38 +0200721 ERR(NULL, "%s: failed init attribute (%s).", __FUNCTION__, strerror(errno));
Frank Rimpler9f838b02018-07-25 06:44:03 +0000722 }
Michal Vasko086311b2016-01-08 09:53:11 +0100723 return 0;
724}
725
Michal Vaskob48aa812016-01-18 14:13:09 +0100726API void
727nc_server_destroy(void)
728{
Michal Vasko1440a742021-03-31 11:11:03 +0200729 uint32_t i;
Radek Krejci658782b2016-12-04 22:04:55 +0100730
731 for (i = 0; i < server_opts.capabilities_count; i++) {
732 lydict_remove(server_opts.ctx, server_opts.capabilities[i]);
733 }
734 free(server_opts.capabilities);
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200735 server_opts.capabilities = NULL;
736 server_opts.capabilities_count = 0;
Michal Vasko1440a742021-03-31 11:11:03 +0200737 if (server_opts.content_id_data && server_opts.content_id_data_free) {
738 server_opts.content_id_data_free(server_opts.content_id_data);
739 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200740
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200741#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko59050372016-11-22 14:33:55 +0100742 nc_server_del_endpt(NULL, 0);
Michal Vasko0bdf70b2019-06-24 19:20:20 +0200743 nc_server_ch_del_client(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100744#endif
Michal Vasko17dfda92016-12-01 14:06:16 +0100745#ifdef NC_ENABLED_SSH
Michal Vaskoebba7602018-03-23 13:14:08 +0100746 if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) {
747 server_opts.passwd_auth_data_free(server_opts.passwd_auth_data);
748 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200749 server_opts.passwd_auth_data = NULL;
750 server_opts.passwd_auth_data_free = NULL;
Michal Vaskoebba7602018-03-23 13:14:08 +0100751
Michal Vasko17dfda92016-12-01 14:06:16 +0100752 nc_server_ssh_del_authkey(NULL, NULL, 0, NULL);
Michal Vasko4c1fb492017-01-30 14:31:07 +0100753
754 if (server_opts.hostkey_data && server_opts.hostkey_data_free) {
755 server_opts.hostkey_data_free(server_opts.hostkey_data);
756 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200757 server_opts.hostkey_data = NULL;
758 server_opts.hostkey_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100759#endif
760#ifdef NC_ENABLED_TLS
761 if (server_opts.server_cert_data && server_opts.server_cert_data_free) {
762 server_opts.server_cert_data_free(server_opts.server_cert_data);
763 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200764 server_opts.server_cert_data = NULL;
765 server_opts.server_cert_data_free = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100766 if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) {
767 server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data);
768 }
Michal Vaskodd6e4f72018-06-01 10:21:27 +0200769 server_opts.trusted_cert_list_data = NULL;
770 server_opts.trusted_cert_list_data_free = NULL;
Michal Vaskob48aa812016-01-18 14:13:09 +0100771#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100772 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100773}
774
Michal Vasko086311b2016-01-08 09:53:11 +0100775API int
776nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
777{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200778 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
779 ERRARG("basic_mode");
780 return -1;
781 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
782 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100783 return -1;
784 }
785
786 server_opts.wd_basic_mode = basic_mode;
787 server_opts.wd_also_supported = also_supported;
788 return 0;
789}
790
Michal Vasko1a38c862016-01-15 15:50:07 +0100791API void
Michal Vasko55f03972016-04-13 08:56:01 +0200792nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
793{
794 if (!basic_mode && !also_supported) {
795 ERRARG("basic_mode and also_supported");
796 return;
797 }
798
799 if (basic_mode) {
800 *basic_mode = server_opts.wd_basic_mode;
801 }
802 if (also_supported) {
803 *also_supported = server_opts.wd_also_supported;
804 }
805}
806
Michal Vasko55f03972016-04-13 08:56:01 +0200807API int
Radek Krejci658782b2016-12-04 22:04:55 +0100808nc_server_set_capability(const char *value)
Michal Vasko55f03972016-04-13 08:56:01 +0200809{
Radek Krejci658782b2016-12-04 22:04:55 +0100810 const char **new;
811
812 if (!value || !value[0]) {
813 ERRARG("value must not be empty");
814 return EXIT_FAILURE;
815 }
816
817 server_opts.capabilities_count++;
818 new = realloc(server_opts.capabilities, server_opts.capabilities_count * sizeof *server_opts.capabilities);
819 if (!new) {
820 ERRMEM;
821 return EXIT_FAILURE;
822 }
823 server_opts.capabilities = new;
Michal Vasko77367452021-02-16 16:32:18 +0100824 lydict_insert(server_opts.ctx, value, 0, &server_opts.capabilities[server_opts.capabilities_count - 1]);
Radek Krejci658782b2016-12-04 22:04:55 +0100825
826 return EXIT_SUCCESS;
Michal Vasko55f03972016-04-13 08:56:01 +0200827}
828
Michal Vasko1a38c862016-01-15 15:50:07 +0100829API void
Michal Vasko1440a742021-03-31 11:11:03 +0200830nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
831 void (*free_user_data)(void *user_data))
832{
833 server_opts.content_id_clb = content_id_clb;
834 server_opts.content_id_data = user_data;
835 server_opts.content_id_data_free = free_user_data;
836}
837
838API void
Michal Vasko086311b2016-01-08 09:53:11 +0100839nc_server_set_hello_timeout(uint16_t hello_timeout)
840{
Michal Vasko086311b2016-01-08 09:53:11 +0100841 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100842}
843
Michal Vasko55f03972016-04-13 08:56:01 +0200844API uint16_t
845nc_server_get_hello_timeout(void)
846{
847 return server_opts.hello_timeout;
848}
849
Michal Vasko1a38c862016-01-15 15:50:07 +0100850API void
Michal Vasko086311b2016-01-08 09:53:11 +0100851nc_server_set_idle_timeout(uint16_t idle_timeout)
852{
Michal Vasko086311b2016-01-08 09:53:11 +0100853 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100854}
855
Michal Vasko55f03972016-04-13 08:56:01 +0200856API uint16_t
857nc_server_get_idle_timeout(void)
858{
859 return server_opts.idle_timeout;
860}
861
Michal Vasko71090fc2016-05-24 16:37:28 +0200862API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100863nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100864{
Michal Vasko71090fc2016-05-24 16:37:28 +0200865 NC_MSG_TYPE msgtype;
Michal Vasko9fb42272017-10-05 13:50:05 +0200866 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +0200867
Michal Vasko45e53ae2016-04-07 11:46:03 +0200868 if (!server_opts.ctx) {
869 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200870 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200871 } else if (fdin < 0) {
872 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200873 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200874 } else if (fdout < 0) {
875 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200876 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200877 } else if (!username) {
878 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200879 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200880 } else if (!session) {
881 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200882 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100883 }
884
885 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +0200886 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko1a38c862016-01-15 15:50:07 +0100887 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100888 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200889 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100890 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100891 (*session)->status = NC_STATUS_STARTING;
Michal Vaskoade892d2017-02-22 13:40:35 +0100892
Michal Vasko086311b2016-01-08 09:53:11 +0100893 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100894 (*session)->ti_type = NC_TI_FD;
895 (*session)->ti.fd.in = fdin;
896 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100897
898 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100899 (*session)->flags = NC_SESSION_SHAREDCTX;
900 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100901
Michal Vaskob48aa812016-01-18 14:13:09 +0100902 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +0200903 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +0100904
Michal Vasko086311b2016-01-08 09:53:11 +0100905 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +0200906 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +0200907 if (msgtype != NC_MSG_HELLO) {
908 nc_session_free(*session, NULL);
909 *session = NULL;
910 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100911 }
Michal Vasko9fb42272017-10-05 13:50:05 +0200912
913 nc_gettimespec_mono(&ts_cur);
914 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
915 nc_gettimespec_real(&ts_cur);
916 (*session)->opts.server.session_start = ts_cur.tv_sec;
917
Michal Vasko1a38c862016-01-15 15:50:07 +0100918 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100919
Michal Vasko71090fc2016-05-24 16:37:28 +0200920 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100921}
Michal Vasko9e036d52016-01-08 10:49:26 +0100922
Michal Vaskob30b99c2016-07-26 11:35:43 +0200923static void
Michal Vasko74c345f2018-02-07 10:37:11 +0100924nc_ps_queue_add_id(struct nc_pollsession *ps, uint8_t *id)
925{
926 uint8_t q_last;
927
928 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
929 ERRINT;
930 return;
931 }
932
933 /* get a unique queue value (by adding 1 to the last added value, if any) */
934 if (ps->queue_len) {
935 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
936 *id = ps->queue[q_last] + 1;
937 } else {
938 *id = 0;
939 }
940
941 /* add the id into the queue */
942 ++ps->queue_len;
943 q_last = (ps->queue_begin + ps->queue_len - 1) % NC_PS_QUEUE_SIZE;
944 ps->queue[q_last] = *id;
945}
946
947static void
Michal Vaskob30b99c2016-07-26 11:35:43 +0200948nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
949{
Michal Vasko74c345f2018-02-07 10:37:11 +0100950 uint8_t i, q_idx, found = 0;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200951
952 for (i = 0; i < ps->queue_len; ++i) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100953 /* get the actual queue idx */
954 q_idx = (ps->queue_begin + i) % NC_PS_QUEUE_SIZE;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200955
956 if (found) {
Michal Vasko74c345f2018-02-07 10:37:11 +0100957 if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200958 /* another equal value, simply cannot be */
959 ERRINT;
960 }
Michal Vaskod8340032018-02-12 14:41:00 +0100961 if (found == 2) {
962 /* move the following values */
963 ps->queue[q_idx ? q_idx - 1 : NC_PS_QUEUE_SIZE - 1] = ps->queue[q_idx];
964 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100965 } else if (ps->queue[q_idx] == id) {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200966 /* found our id, there can be no more equal valid values */
Michal Vaskod8340032018-02-12 14:41:00 +0100967 if (i == 0) {
968 found = 1;
969 } else {
970 /* this is not okay, our id is in the middle of the queue */
971 found = 2;
972 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200973 }
974 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200975 if (!found) {
976 ERRINT;
Michal Vasko103fe632018-02-12 16:37:45 +0100977 return;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200978 }
Michal Vasko74c345f2018-02-07 10:37:11 +0100979
Michal Vasko103fe632018-02-12 16:37:45 +0100980 --ps->queue_len;
Michal Vaskod8340032018-02-12 14:41:00 +0100981 if (found == 1) {
Michal Vasko103fe632018-02-12 16:37:45 +0100982 /* remove the id by moving the queue, otherwise all the values in the queue were moved */
Michal Vaskod8340032018-02-12 14:41:00 +0100983 ps->queue_begin = (ps->queue_begin + 1) % NC_PS_QUEUE_SIZE;
984 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200985}
986
Michal Vaskof04a52a2016-04-07 10:52:10 +0200987int
Michal Vasko26043172016-07-26 14:08:59 +0200988nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200989{
990 int ret;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200991 struct timespec ts;
992
Michal Vasko77a6abe2017-10-05 10:02:20 +0200993 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +0100994 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200995
996 /* LOCK */
997 ret = pthread_mutex_timedlock(&ps->lock, &ts);
998 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +0200999 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001000 return -1;
1001 }
1002
Michal Vasko74c345f2018-02-07 10:37:11 +01001003 /* check that the queue is long enough */
Michal Vasko8bc747c2018-02-09 16:37:04 +01001004 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko05532772021-06-03 12:12:38 +02001005 ERR(NULL, "%s: pollsession queue size (%d) too small.", func, NC_PS_QUEUE_SIZE);
Michal Vasko8bc747c2018-02-09 16:37:04 +01001006 pthread_mutex_unlock(&ps->lock);
1007 return -1;
1008 }
Michal Vasko74c345f2018-02-07 10:37:11 +01001009
1010 /* add ourselves into the queue */
1011 nc_ps_queue_add_id(ps, id);
Michal Vasko05532772021-06-03 12:12:38 +02001012 DBL(NULL, "PS 0x%p TID %lu queue: added %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), *id,
Michal Vasko91290952019-09-27 11:30:55 +02001013 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001014
1015 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001016 while (ps->queue[ps->queue_begin] != *id) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001017 nc_gettimespec_real(&ts);
Michal Vasko2b768092018-02-12 16:37:12 +01001018 nc_addtimespec(&ts, NC_PS_QUEUE_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001019
1020 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
1021 if (ret) {
preetbhansalif3edf492018-12-14 17:53:32 +05301022 /**
1023 * This may happen when another thread releases the lock and broadcasts the condition
1024 * and this thread had already timed out. When this thread is scheduled, it returns timed out error
1025 * but when actually this thread was ready for condition.
1026 */
preetbhansali629dfc42018-12-17 16:04:40 +05301027 if ((ETIMEDOUT == ret) && (ps->queue[ps->queue_begin] == *id)) {
preetbhansalif3edf492018-12-14 17:53:32 +05301028 break;
1029 }
Michal Vasko66032bc2019-01-22 15:03:12 +01001030
Michal Vasko05532772021-06-03 12:12:38 +02001031 ERR(NULL, "%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001032 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001033 nc_ps_queue_remove_id(ps, *id);
1034 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001035 return -1;
1036 }
1037 }
1038
Michal Vaskobe86fe32016-04-07 10:43:03 +02001039 /* UNLOCK */
1040 pthread_mutex_unlock(&ps->lock);
1041
1042 return 0;
1043}
1044
Michal Vaskof04a52a2016-04-07 10:52:10 +02001045int
Michal Vasko26043172016-07-26 14:08:59 +02001046nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +02001047{
1048 int ret;
1049 struct timespec ts;
1050
Michal Vasko77a6abe2017-10-05 10:02:20 +02001051 nc_gettimespec_real(&ts);
Michal Vasko81c5b302017-03-15 12:10:40 +01001052 nc_addtimespec(&ts, NC_PS_LOCK_TIMEOUT);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001053
1054 /* LOCK */
1055 ret = pthread_mutex_timedlock(&ps->lock, &ts);
1056 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02001057 ERR(NULL, "%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +02001058 ret = -1;
1059 }
1060
Michal Vaskob30b99c2016-07-26 11:35:43 +02001061 /* we must be the first, it was our turn after all, right? */
1062 if (ps->queue[ps->queue_begin] != id) {
1063 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +02001064 /* UNLOCK */
1065 if (!ret) {
1066 pthread_mutex_unlock(&ps->lock);
1067 }
Michal Vaskob30b99c2016-07-26 11:35:43 +02001068 return -1;
1069 }
1070
Michal Vaskobe86fe32016-04-07 10:43:03 +02001071 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +02001072 nc_ps_queue_remove_id(ps, id);
Michal Vasko05532772021-06-03 12:12:38 +02001073 DBL(NULL, "PS 0x%p TID %lu queue: removed %u, head %u, lenght %u", ps, (long unsigned int)pthread_self(), id,
Michal Vasko91290952019-09-27 11:30:55 +02001074 ps->queue[ps->queue_begin], ps->queue_len);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001075
1076 /* broadcast to all other threads that the queue moved */
1077 pthread_cond_broadcast(&ps->cond);
1078
Michal Vaskobe86fe32016-04-07 10:43:03 +02001079 /* UNLOCK */
1080 if (!ret) {
1081 pthread_mutex_unlock(&ps->lock);
1082 }
1083
1084 return ret;
1085}
1086
Michal Vasko428087d2016-01-14 16:04:28 +01001087API struct nc_pollsession *
1088nc_ps_new(void)
1089{
Michal Vasko48a63ed2016-03-01 09:48:21 +01001090 struct nc_pollsession *ps;
1091
1092 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001093 if (!ps) {
1094 ERRMEM;
1095 return NULL;
1096 }
Michal Vaskobe86fe32016-04-07 10:43:03 +02001097 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001098 pthread_mutex_init(&ps->lock, NULL);
1099
1100 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +01001101}
1102
1103API void
1104nc_ps_free(struct nc_pollsession *ps)
1105{
fanchanghu3d4e7212017-08-09 09:42:30 +08001106 uint16_t i;
1107
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001108 if (!ps) {
1109 return;
1110 }
1111
Michal Vaskobe86fe32016-04-07 10:43:03 +02001112 if (ps->queue_len) {
Michal Vasko05532772021-06-03 12:12:38 +02001113 ERR(NULL, "FATAL: Freeing a pollsession structure that is currently being worked with!");
Michal Vaskobe86fe32016-04-07 10:43:03 +02001114 }
1115
fanchanghu3d4e7212017-08-09 09:42:30 +08001116 for (i = 0; i < ps->session_count; i++) {
1117 free(ps->sessions[i]);
1118 }
1119
Michal Vasko428087d2016-01-14 16:04:28 +01001120 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001121 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +02001122 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001123
Michal Vasko428087d2016-01-14 16:04:28 +01001124 free(ps);
1125}
1126
1127API int
1128nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
1129{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001130 uint8_t q_id;
1131
Michal Vasko45e53ae2016-04-07 11:46:03 +02001132 if (!ps) {
1133 ERRARG("ps");
1134 return -1;
1135 } else if (!session) {
1136 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +01001137 return -1;
1138 }
1139
Michal Vasko48a63ed2016-03-01 09:48:21 +01001140 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001141 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001142 return -1;
1143 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001144
Michal Vasko428087d2016-01-14 16:04:28 +01001145 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001146 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
Michal Vasko9a327362017-01-11 11:31:46 +01001147 if (!ps->sessions) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001148 ERRMEM;
1149 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001150 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001151 return -1;
1152 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001153 ps->sessions[ps->session_count - 1] = calloc(1, sizeof **ps->sessions);
1154 if (!ps->sessions[ps->session_count - 1]) {
1155 ERRMEM;
1156 --ps->session_count;
1157 /* UNLOCK */
1158 nc_ps_unlock(ps, q_id, __func__);
1159 return -1;
1160 }
1161 ps->sessions[ps->session_count - 1]->session = session;
1162 ps->sessions[ps->session_count - 1]->state = NC_PS_STATE_NONE;
Michal Vasko428087d2016-01-14 16:04:28 +01001163
Michal Vasko48a63ed2016-03-01 09:48:21 +01001164 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001165 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001166}
1167
Michal Vasko48a63ed2016-03-01 09:48:21 +01001168static int
Radek Krejcid5f978f2016-03-03 13:14:45 +01001169_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +01001170{
1171 uint16_t i;
1172
Radek Krejcid5f978f2016-03-03 13:14:45 +01001173 if (index >= 0) {
1174 i = (uint16_t)index;
1175 goto remove;
1176 }
Michal Vasko428087d2016-01-14 16:04:28 +01001177 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001178 if (ps->sessions[i]->session == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +01001179remove:
Michal Vasko428087d2016-01-14 16:04:28 +01001180 --ps->session_count;
fanchanghu3d4e7212017-08-09 09:42:30 +08001181 if (i <= ps->session_count) {
1182 free(ps->sessions[i]);
Michal Vasko58005732016-02-02 15:50:52 +01001183 ps->sessions[i] = ps->sessions[ps->session_count];
fanchanghu3d4e7212017-08-09 09:42:30 +08001184 }
1185 if (!ps->session_count) {
Michal Vasko58005732016-02-02 15:50:52 +01001186 free(ps->sessions);
1187 ps->sessions = NULL;
Michal Vasko58005732016-02-02 15:50:52 +01001188 }
Michal Vasko11d2f6a2017-02-02 11:15:06 +01001189 ps->last_event_session = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001190 return 0;
1191 }
1192 }
1193
Michal Vaskof0537d82016-01-29 14:42:38 +01001194 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +01001195}
1196
Michal Vasko48a63ed2016-03-01 09:48:21 +01001197API int
1198nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
1199{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001200 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001201 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001202
Michal Vasko45e53ae2016-04-07 11:46:03 +02001203 if (!ps) {
1204 ERRARG("ps");
1205 return -1;
1206 } else if (!session) {
1207 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +01001208 return -1;
1209 }
1210
1211 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001212 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001213 return -1;
1214 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001215
Radek Krejcid5f978f2016-03-03 13:14:45 +01001216 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001217
1218 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001219 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001220
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001221 return ret || ret2 ? -1 : 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001222}
1223
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001224API struct nc_session *
Michal Vasko4871c9d2017-10-09 14:48:39 +02001225nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx)
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001226{
1227 uint8_t q_id;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001228 struct nc_session *ret = NULL;
1229
1230 if (!ps) {
1231 ERRARG("ps");
1232 return NULL;
1233 }
1234
1235 /* LOCK */
1236 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1237 return NULL;
1238 }
1239
Michal Vasko4871c9d2017-10-09 14:48:39 +02001240 if (idx < ps->session_count) {
1241 ret = ps->sessions[idx]->session;
Michal Vaskoe1ee05b2017-03-21 10:10:18 +01001242 }
1243
1244 /* UNLOCK */
1245 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1246
1247 return ret;
1248}
1249
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001250API uint16_t
1251nc_ps_session_count(struct nc_pollsession *ps)
1252{
Michal Vasko47003942019-03-14 12:25:23 +01001253 uint8_t q_id;
1254 uint16_t session_count;
1255
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001256 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001257 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001258 return 0;
1259 }
1260
Michal Vasko47003942019-03-14 12:25:23 +01001261 /* LOCK (just for memory barrier so that we read the current value) */
1262 if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) {
1263 return 0;
1264 }
1265
1266 session_count = ps->session_count;
1267
1268 /* UNLOCK */
1269 nc_ps_unlock((struct nc_pollsession *)ps, q_id, __func__);
1270
1271 return session_count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +01001272}
1273
Michal Vasko131120a2018-05-29 15:44:02 +02001274/* should be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001275 * returns: NC_PSPOLL_ERROR,
Michal Vasko77367452021-02-16 16:32:18 +01001276 * NC_PSPOLL_TIMEOUT,
Michal Vasko71090fc2016-05-24 16:37:28 +02001277 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
1278 * NC_PSPOLL_RPC
1279 */
1280static int
Michal Vasko131120a2018-05-29 15:44:02 +02001281nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001282{
Michal Vasko77367452021-02-16 16:32:18 +01001283 struct ly_in *msg;
Radek Krejcif93c7d42016-04-06 13:41:15 +02001284 struct nc_server_reply *reply = NULL;
Michal Vasko939ffce2021-04-12 13:02:01 +02001285 struct lyd_node *e;
Michal Vasko77367452021-02-16 16:32:18 +01001286 int r, ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001287
Michal Vasko45e53ae2016-04-07 11:46:03 +02001288 if (!session) {
1289 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001290 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001291 } else if (!rpc) {
1292 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +02001293 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001294 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vasko05532772021-06-03 12:12:38 +02001295 ERR(session, "Invalid session to receive RPCs.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001296 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001297 }
1298
Michal Vasko77367452021-02-16 16:32:18 +01001299 /* get a message */
1300 r = nc_read_msg_io(session, io_timeout, &msg, 0);
1301 if (r == -2) {
1302 /* malformed message */
1303 ret = NC_PSPOLL_REPLY_ERROR;
1304 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1305 goto send_reply;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001306 }
1307 if (r == -1) {
Michal Vasko77367452021-02-16 16:32:18 +01001308 return NC_PSPOLL_ERROR;
1309 } else if (!r) {
1310 return NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001311 }
1312
Michal Vasko77367452021-02-16 16:32:18 +01001313 *rpc = calloc(1, sizeof **rpc);
1314 if (!*rpc) {
1315 ERRMEM;
1316 ret = NC_PSPOLL_REPLY_ERROR;
1317 goto cleanup;
1318 }
1319
1320 /* parse the RPC */
1321 if (lyd_parse_op(server_opts.ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) {
1322 /* bad RPC received */
1323 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
1324
1325 if ((*rpc)->envp) {
1326 /* at least the envelopes were parsed */
Michal Vasko939ffce2021-04-12 13:02:01 +02001327 e = nc_err(server_opts.ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
1328 nc_err_set_msg(e, ly_errmsg(server_opts.ctx), "en");
1329 reply = nc_server_reply_err(e);
Michal Vasko77367452021-02-16 16:32:18 +01001330 } else if (session->version == NC_VERSION_11) {
1331 /* completely malformed message, NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
1332 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_MALFORMED_MSG));
1333 }
1334
1335send_reply:
1336 if (reply) {
1337 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, (*rpc)->envp, reply);
1338 nc_server_reply_free(reply);
1339 if (r != NC_MSG_REPLY) {
Michal Vasko05532772021-06-03 12:12:38 +02001340 ERR(session, "Failed to write reply (%s), terminating session.", nc_msgtype2str[r]);
Michal Vasko77367452021-02-16 16:32:18 +01001341 if (session->status != NC_STATUS_INVALID) {
1342 session->status = NC_STATUS_INVALID;
1343 session->term_reason = NC_SESSION_TERM_OTHER;
1344 }
1345 }
1346 }
1347 } else {
1348 ret = NC_PSPOLL_RPC;
1349 }
1350
1351cleanup:
1352 ly_in_free(msg, 1);
1353 if (ret != NC_PSPOLL_RPC) {
1354 nc_server_rpc_free(*rpc);
1355 *rpc = NULL;
1356 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001357 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001358}
1359
fanchanghu966f2de2016-07-21 02:28:57 -04001360API void
1361nc_set_global_rpc_clb(nc_rpc_clb clb)
1362{
1363 global_rpc_clb = clb;
1364}
1365
Radek Krejci93e80222016-10-03 13:34:25 +02001366API NC_MSG_TYPE
1367nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
1368{
Michal Vasko131120a2018-05-29 15:44:02 +02001369 NC_MSG_TYPE ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001370
1371 /* check parameters */
Michal Vasko3486a7c2017-03-03 13:28:07 +01001372 if (!session || (session->side != NC_SERVER) || !session->opts.server.ntf_status) {
Radek Krejci93e80222016-10-03 13:34:25 +02001373 ERRARG("session");
1374 return NC_MSG_ERROR;
Michal Vasko77367452021-02-16 16:32:18 +01001375 } else if (!notif || !notif->ntf || !notif->eventtime) {
Radek Krejci93e80222016-10-03 13:34:25 +02001376 ERRARG("notif");
1377 return NC_MSG_ERROR;
1378 }
1379
Michal Vasko131120a2018-05-29 15:44:02 +02001380 /* we do not need RPC lock for this, IO lock will be acquired properly */
1381 ret = nc_write_msg_io(session, timeout, NC_MSG_NOTIF, notif);
Michal Vasko8fe604c2020-02-10 15:25:04 +01001382 if (ret != NC_MSG_NOTIF) {
Michal Vasko05532772021-06-03 12:12:38 +02001383 ERR(session, "Failed to write notification (%s).", nc_msgtype2str[ret]);
Radek Krejci93e80222016-10-03 13:34:25 +02001384 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001385
Michal Vasko131120a2018-05-29 15:44:02 +02001386 return ret;
Radek Krejci93e80222016-10-03 13:34:25 +02001387}
1388
Michal Vasko131120a2018-05-29 15:44:02 +02001389/* must be called holding the session RPC lock! IO lock will be acquired as needed
Michal Vasko71090fc2016-05-24 16:37:28 +02001390 * returns: NC_PSPOLL_ERROR,
1391 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1392 * NC_PSPOLL_REPLY_ERROR,
1393 * 0
1394 */
1395static int
Michal Vasko131120a2018-05-29 15:44:02 +02001396nc_server_send_reply_io(struct nc_session *session, int io_timeout, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001397{
1398 nc_rpc_clb clb;
1399 struct nc_server_reply *reply;
Michal Vasko77367452021-02-16 16:32:18 +01001400 const struct lysc_node *rpc_act = NULL;
1401 struct lyd_node *elem;
Michal Vasko131120a2018-05-29 15:44:02 +02001402 int ret = 0;
1403 NC_MSG_TYPE r;
Michal Vasko428087d2016-01-14 16:04:28 +01001404
Michal Vasko4a827e52016-03-03 10:59:00 +01001405 if (!rpc) {
1406 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001407 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001408 }
1409
Michal Vasko77367452021-02-16 16:32:18 +01001410 if (rpc->rpc->schema->nodetype == LYS_RPC) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001411 /* RPC */
Michal Vasko77367452021-02-16 16:32:18 +01001412 rpc_act = rpc->rpc->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001413 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001414 /* action */
Michal Vasko77367452021-02-16 16:32:18 +01001415 LYD_TREE_DFS_BEGIN(rpc->rpc, elem) {
Michal Vasko90e8e692016-07-13 12:27:57 +02001416 if (elem->schema->nodetype == LYS_ACTION) {
1417 rpc_act = elem->schema;
1418 break;
1419 }
Michal Vasko77367452021-02-16 16:32:18 +01001420 LYD_TREE_DFS_END(rpc->rpc, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001421 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001422 if (!rpc_act) {
1423 ERRINT;
1424 return NC_PSPOLL_ERROR;
1425 }
1426 }
1427
1428 if (!rpc_act->priv) {
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001429 if (!global_rpc_clb) {
1430 /* no callback, reply with a not-implemented error */
Michal Vasko77367452021-02-16 16:32:18 +01001431 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001432 } else {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001433 reply = global_rpc_clb(rpc->rpc, session);
Petr A. Sokolov16284ca2019-02-04 09:02:40 +03001434 }
Michal Vasko428087d2016-01-14 16:04:28 +01001435 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001436 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko77367452021-02-16 16:32:18 +01001437 reply = clb(rpc->rpc, session);
Michal Vasko428087d2016-01-14 16:04:28 +01001438 }
1439
1440 if (!reply) {
Michal Vasko77367452021-02-16 16:32:18 +01001441 reply = nc_server_reply_err(nc_err(server_opts.ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001442 }
Michal Vasko77367452021-02-16 16:32:18 +01001443 r = nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, rpc->envp, reply);
Michal Vasko71090fc2016-05-24 16:37:28 +02001444 if (reply->type == NC_RPL_ERROR) {
1445 ret |= NC_PSPOLL_REPLY_ERROR;
1446 }
1447 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001448
Michal Vasko131120a2018-05-29 15:44:02 +02001449 if (r != NC_MSG_REPLY) {
Michal Vasko15469492021-06-09 08:40:48 +02001450 ERR(session, "Failed to write reply (%s).", nc_msgtype2str[r]);
Michal Vasko71090fc2016-05-24 16:37:28 +02001451 ret |= NC_PSPOLL_ERROR;
1452 }
Michal Vasko428087d2016-01-14 16:04:28 +01001453
1454 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1455 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1456 session->status = NC_STATUS_INVALID;
1457 }
1458
Michal Vasko71090fc2016-05-24 16:37:28 +02001459 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001460}
1461
Michal Vasko131120a2018-05-29 15:44:02 +02001462/* session must be running and session RPC lock held!
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001463 * returns: NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR, (msg filled)
1464 * NC_PSPOLL_ERROR, (msg filled)
1465 * NC_PSPOLL_TIMEOUT,
1466 * NC_PSPOLL_RPC (some application data available),
1467 * NC_PSPOLL_SSH_CHANNEL,
1468 * NC_PSPOLL_SSH_MSG
1469 */
1470static int
Michal Vasko131120a2018-05-29 15:44:02 +02001471nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mono, char *msg)
Michal Vasko428087d2016-01-14 16:04:28 +01001472{
Michal Vasko9a327362017-01-11 11:31:46 +01001473 struct pollfd pfd;
Michal Vasko2260f552018-06-06 10:06:12 +02001474 int r, ret = 0;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001475
Michal Vasko9a327362017-01-11 11:31:46 +01001476#ifdef NC_ENABLED_SSH
1477 struct nc_session *new;
1478#endif
Michal Vasko428087d2016-01-14 16:04:28 +01001479
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001480 /* check timeout first */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001481 if (!(session->flags & NC_SESSION_CALLHOME) && !session->opts.server.ntf_status && server_opts.idle_timeout &&
1482 (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001483 sprintf(msg, "session idle timeout elapsed");
1484 session->status = NC_STATUS_INVALID;
1485 session->term_reason = NC_SESSION_TERM_TIMEOUT;
1486 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1487 }
1488
Michal Vasko131120a2018-05-29 15:44:02 +02001489 r = nc_session_io_lock(session, io_timeout, __func__);
1490 if (r < 0) {
1491 sprintf(msg, "session IO lock failed to be acquired");
1492 return NC_PSPOLL_ERROR;
1493 } else if (!r) {
1494 return NC_PSPOLL_TIMEOUT;
1495 }
1496
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001497 switch (session->ti_type) {
1498#ifdef NC_ENABLED_SSH
1499 case NC_TI_LIBSSH:
1500 r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
Michal Vasko8dcaa882017-10-19 14:28:42 +02001501 if (r == SSH_EOF) {
1502 sprintf(msg, "SSH channel unexpected EOF");
1503 session->status = NC_STATUS_INVALID;
1504 session->term_reason = NC_SESSION_TERM_DROPPED;
1505 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1506 } else if (r == SSH_ERROR) {
1507 sprintf(msg, "SSH channel poll error (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001508 session->status = NC_STATUS_INVALID;
1509 session->term_reason = NC_SESSION_TERM_OTHER;
1510 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko8dcaa882017-10-19 14:28:42 +02001511 } else if (!r) {
1512 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1513 /* new SSH message */
1514 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1515 if (session->ti.libssh.next) {
1516 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001517 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel &&
1518 (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko8dcaa882017-10-19 14:28:42 +02001519 /* new NETCONF SSH channel */
1520 ret = NC_PSPOLL_SSH_CHANNEL;
1521 break;
1522 }
1523 }
1524 if (new != session) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001525 break;
1526 }
1527 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001528
Michal Vasko8dcaa882017-10-19 14:28:42 +02001529 /* just some SSH message */
1530 ret = NC_PSPOLL_SSH_MSG;
1531 } else {
1532 ret = NC_PSPOLL_TIMEOUT;
1533 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001534 } else {
1535 /* we have some application data */
1536 ret = NC_PSPOLL_RPC;
1537 }
1538 break;
1539#endif
1540#ifdef NC_ENABLED_TLS
1541 case NC_TI_OPENSSL:
1542 r = SSL_pending(session->ti.tls);
1543 if (!r) {
1544 /* no data pending in the SSL buffer, poll fd */
1545 pfd.fd = SSL_get_rfd(session->ti.tls);
1546 if (pfd.fd < 0) {
1547 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1548 ret = NC_PSPOLL_ERROR;
1549 break;
1550 }
1551 pfd.events = POLLIN;
1552 pfd.revents = 0;
1553 r = poll(&pfd, 1, 0);
1554
1555 if ((r < 0) && (errno != EINTR)) {
1556 sprintf(msg, "poll failed (%s)", strerror(errno));
1557 session->status = NC_STATUS_INVALID;
1558 ret = NC_PSPOLL_ERROR;
1559 } else if (r > 0) {
1560 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1561 sprintf(msg, "communication socket unexpectedly closed");
1562 session->status = NC_STATUS_INVALID;
1563 session->term_reason = NC_SESSION_TERM_DROPPED;
1564 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1565 } else if (pfd.revents & POLLERR) {
1566 sprintf(msg, "communication socket error");
1567 session->status = NC_STATUS_INVALID;
1568 session->term_reason = NC_SESSION_TERM_OTHER;
1569 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1570 } else {
1571 ret = NC_PSPOLL_RPC;
1572 }
1573 } else {
1574 ret = NC_PSPOLL_TIMEOUT;
1575 }
1576 } else {
1577 ret = NC_PSPOLL_RPC;
1578 }
1579 break;
1580#endif
1581 case NC_TI_FD:
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001582 case NC_TI_UNIX:
1583 pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001584 pfd.events = POLLIN;
1585 pfd.revents = 0;
1586 r = poll(&pfd, 1, 0);
1587
1588 if ((r < 0) && (errno != EINTR)) {
1589 sprintf(msg, "poll failed (%s)", strerror(errno));
1590 session->status = NC_STATUS_INVALID;
1591 ret = NC_PSPOLL_ERROR;
1592 } else if (r > 0) {
1593 if (pfd.revents & (POLLHUP | POLLNVAL)) {
1594 sprintf(msg, "communication socket unexpectedly closed");
1595 session->status = NC_STATUS_INVALID;
1596 session->term_reason = NC_SESSION_TERM_DROPPED;
1597 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1598 } else if (pfd.revents & POLLERR) {
1599 sprintf(msg, "communication socket error");
1600 session->status = NC_STATUS_INVALID;
1601 session->term_reason = NC_SESSION_TERM_OTHER;
1602 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1603 } else {
1604 ret = NC_PSPOLL_RPC;
1605 }
1606 } else {
1607 ret = NC_PSPOLL_TIMEOUT;
1608 }
1609 break;
1610 case NC_TI_NONE:
1611 sprintf(msg, "internal error (%s:%d)", __FILE__, __LINE__);
1612 ret = NC_PSPOLL_ERROR;
1613 break;
1614 }
1615
Michal Vasko131120a2018-05-29 15:44:02 +02001616 nc_session_io_unlock(session, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001617 return ret;
1618}
1619
1620API int
1621nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
1622{
1623 int ret, r;
1624 uint8_t q_id;
1625 uint16_t i, j;
1626 char msg[256];
1627 struct timespec ts_timeout, ts_cur;
1628 struct nc_session *cur_session;
fanchanghu3d4e7212017-08-09 09:42:30 +08001629 struct nc_ps_session *cur_ps_session;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001630 struct nc_server_rpc *rpc = NULL;
1631
Michal Vasko30a5d6b2017-02-15 14:29:39 +01001632 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001633 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001634 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001635 }
1636
Michal Vaskoade892d2017-02-22 13:40:35 +01001637 /* PS LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001638 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001639 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001640 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001641
Michal Vaskoade892d2017-02-22 13:40:35 +01001642 if (!ps->session_count) {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001643 nc_ps_unlock(ps, q_id, __func__);
1644 return NC_PSPOLL_NOSESSIONS;
Michal Vaskoade892d2017-02-22 13:40:35 +01001645 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001646
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001647 /* fill timespecs */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001648 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001649 if (timeout > -1) {
Michal Vasko77a6abe2017-10-05 10:02:20 +02001650 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001651 nc_addtimespec(&ts_timeout, timeout);
1652 }
1653
1654 /* poll all the sessions one-by-one */
Michal Vasko9a327362017-01-11 11:31:46 +01001655 do {
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001656 /* loop from i to j once (all sessions) */
Michal Vasko9a327362017-01-11 11:31:46 +01001657 if (ps->last_event_session == ps->session_count - 1) {
1658 i = j = 0;
1659 } else {
1660 i = j = ps->last_event_session + 1;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001661 }
Michal Vasko9a327362017-01-11 11:31:46 +01001662 do {
fanchanghu3d4e7212017-08-09 09:42:30 +08001663 cur_ps_session = ps->sessions[i];
1664 cur_session = cur_ps_session->session;
Michal Vasko428087d2016-01-14 16:04:28 +01001665
Michal Vasko131120a2018-05-29 15:44:02 +02001666 /* SESSION RPC LOCK */
1667 r = nc_session_rpc_lock(cur_session, 0, __func__);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001668 if (r == -1) {
1669 ret = NC_PSPOLL_ERROR;
1670 } else if (r == 1) {
1671 /* no one else is currently working with the session, so we can, otherwise skip it */
Michal Vaskoc97cb162017-10-16 12:10:23 +02001672 switch (cur_ps_session->state) {
1673 case NC_PS_STATE_NONE:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001674 if (cur_session->status == NC_STATUS_RUNNING) {
1675 /* session is fine, work with it */
fanchanghu3d4e7212017-08-09 09:42:30 +08001676 cur_ps_session->state = NC_PS_STATE_BUSY;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001677
Michal Vasko131120a2018-05-29 15:44:02 +02001678 ret = nc_ps_poll_session_io(cur_session, NC_SESSION_LOCK_TIMEOUT, ts_cur.tv_sec, msg);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001679 switch (ret) {
1680 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001681 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001682 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001683 break;
1684 case NC_PSPOLL_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001685 ERR(cur_session, "%s.", msg);
fanchanghu3d4e7212017-08-09 09:42:30 +08001686 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001687 break;
1688 case NC_PSPOLL_TIMEOUT:
1689#ifdef NC_ENABLED_SSH
1690 case NC_PSPOLL_SSH_CHANNEL:
1691 case NC_PSPOLL_SSH_MSG:
1692#endif
fanchanghu3d4e7212017-08-09 09:42:30 +08001693 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001694 break;
1695 case NC_PSPOLL_RPC:
1696 /* let's keep the state busy, we are not done with this session */
1697 break;
1698 }
1699 } else {
1700 /* session is not fine, let the caller know */
1701 ret = NC_PSPOLL_SESSION_TERM;
1702 if (cur_session->term_reason != NC_SESSION_TERM_CLOSED) {
1703 ret |= NC_PSPOLL_SESSION_ERROR;
1704 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001705 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001706 }
Michal Vaskoc97cb162017-10-16 12:10:23 +02001707 break;
1708 case NC_PS_STATE_BUSY:
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001709 /* it definitely should not be busy because we have the lock */
1710 ERRINT;
Michal Vasko4992d802017-08-09 12:20:01 +02001711 ret = NC_PSPOLL_ERROR;
Michal Vaskoc97cb162017-10-16 12:10:23 +02001712 break;
1713 case NC_PS_STATE_INVALID:
1714 /* we got it locked, but it will be freed, let it be */
1715 ret = NC_PSPOLL_TIMEOUT;
1716 break;
Michal Vasko428087d2016-01-14 16:04:28 +01001717 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001718
Michal Vasko131120a2018-05-29 15:44:02 +02001719 /* keep RPC lock in this one case */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001720 if (ret != NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001721 /* SESSION RPC UNLOCK */
1722 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vaskoade892d2017-02-22 13:40:35 +01001723 }
Michal Vaskoade892d2017-02-22 13:40:35 +01001724 } else {
1725 /* timeout */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001726 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko428087d2016-01-14 16:04:28 +01001727 }
Michal Vasko428087d2016-01-14 16:04:28 +01001728
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001729 /* something happened */
1730 if (ret != NC_PSPOLL_TIMEOUT) {
1731 break;
1732 }
1733
Michal Vasko9a327362017-01-11 11:31:46 +01001734 if (i == ps->session_count - 1) {
1735 i = 0;
1736 } else {
1737 ++i;
1738 }
1739 } while (i != j);
1740
Michal Vaskoade892d2017-02-22 13:40:35 +01001741 /* no event, no session remains locked */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001742 if (ret == NC_PSPOLL_TIMEOUT) {
Michal Vasko9a327362017-01-11 11:31:46 +01001743 usleep(NC_TIMEOUT_STEP);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001744 /* update current time */
Michal Vasko77a6abe2017-10-05 10:02:20 +02001745 nc_gettimespec_mono(&ts_cur);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001746
1747 if ((timeout > -1) && (nc_difftimespec(&ts_cur, &ts_timeout) < 1)) {
1748 /* final timeout */
1749 break;
Michal Vasko9a327362017-01-11 11:31:46 +01001750 }
Michal Vasko428087d2016-01-14 16:04:28 +01001751 }
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001752 } while (ret == NC_PSPOLL_TIMEOUT);
Michal Vasko428087d2016-01-14 16:04:28 +01001753
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001754 /* do we want to return the session? */
1755 switch (ret) {
1756 case NC_PSPOLL_RPC:
1757 case NC_PSPOLL_SESSION_TERM:
1758 case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR:
1759#ifdef NC_ENABLED_SSH
1760 case NC_PSPOLL_SSH_CHANNEL:
1761 case NC_PSPOLL_SSH_MSG:
1762#endif
1763 if (session) {
1764 *session = cur_session;
1765 }
1766 ps->last_event_session = i;
1767 break;
1768 default:
1769 break;
Michal Vasko71090fc2016-05-24 16:37:28 +02001770 }
Michal Vasko428087d2016-01-14 16:04:28 +01001771
Michal Vaskoade892d2017-02-22 13:40:35 +01001772 /* PS UNLOCK */
1773 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001774
Michal Vasko131120a2018-05-29 15:44:02 +02001775 /* we have some data available and the session is RPC locked (but not IO locked) */
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001776 if (ret == NC_PSPOLL_RPC) {
Michal Vasko131120a2018-05-29 15:44:02 +02001777 ret = nc_server_recv_rpc_io(cur_session, timeout, &rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001778 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1779 if (cur_session->status != NC_STATUS_RUNNING) {
1780 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
fanchanghu3d4e7212017-08-09 09:42:30 +08001781 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001782 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001783 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001784 }
1785 } else {
Michal Vasko9fb42272017-10-05 13:50:05 +02001786 cur_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001787
Michal Vasko7f1ee932018-10-11 09:41:42 +02001788 /* process RPC */
Michal Vasko131120a2018-05-29 15:44:02 +02001789 ret |= nc_server_send_reply_io(cur_session, timeout, rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001790 if (cur_session->status != NC_STATUS_RUNNING) {
1791 ret |= NC_PSPOLL_SESSION_TERM;
1792 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1793 ret |= NC_PSPOLL_SESSION_ERROR;
1794 }
fanchanghu3d4e7212017-08-09 09:42:30 +08001795 cur_ps_session->state = NC_PS_STATE_INVALID;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001796 } else {
fanchanghu3d4e7212017-08-09 09:42:30 +08001797 cur_ps_session->state = NC_PS_STATE_NONE;
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001798 }
Michal Vasko428087d2016-01-14 16:04:28 +01001799 }
Michal Vasko77367452021-02-16 16:32:18 +01001800 nc_server_rpc_free(rpc);
Michal Vaskoefbc5e32017-05-26 14:02:16 +02001801
Michal Vasko131120a2018-05-29 15:44:02 +02001802 /* SESSION RPC UNLOCK */
1803 nc_session_rpc_unlock(cur_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +01001804 }
1805
Michal Vasko48a63ed2016-03-01 09:48:21 +01001806 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001807}
1808
Michal Vaskod09eae62016-02-01 10:32:52 +01001809API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001810nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001811{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001812 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001813 uint16_t i;
1814 struct nc_session *session;
1815
Michal Vasko9a25e932016-02-01 10:36:42 +01001816 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001817 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001818 return;
1819 }
1820
Michal Vasko48a63ed2016-03-01 09:48:21 +01001821 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001822 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001823 return;
1824 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001825
Michal Vasko48a63ed2016-03-01 09:48:21 +01001826 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001827 for (i = 0; i < ps->session_count; i++) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001828 nc_session_free(ps->sessions[i]->session, data_free);
1829 free(ps->sessions[i]);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001830 }
1831 free(ps->sessions);
1832 ps->sessions = NULL;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001833 ps->session_count = 0;
Michal Vasko9a327362017-01-11 11:31:46 +01001834 ps->last_event_session = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001835 } else {
1836 for (i = 0; i < ps->session_count; ) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001837 if (ps->sessions[i]->session->status != NC_STATUS_RUNNING) {
1838 session = ps->sessions[i]->session;
Radek Krejcid5f978f2016-03-03 13:14:45 +01001839 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001840 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001841 continue;
1842 }
1843
1844 ++i;
1845 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001846 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001847
1848 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001849 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001850}
1851
Michal Vasko5f352c52019-07-10 16:12:06 +02001852static int
apropp-molex4e903c32020-04-20 03:06:58 -04001853nc_get_uid(int sock, uid_t *uid)
1854{
Michal Vaskod3910912020-04-20 09:12:49 +02001855 int ret;
apropp-molex4e903c32020-04-20 03:06:58 -04001856
Michal Vaskod3910912020-04-20 09:12:49 +02001857#ifdef SO_PEERCRED
1858 struct ucred ucred;
1859 socklen_t len;
1860 len = sizeof(ucred);
1861 ret = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
1862 if (!ret) {
1863 *uid = ucred.uid;
1864 }
1865#else
1866 ret = getpeereid(sock, uid, NULL);
1867#endif
1868
1869 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001870 ERR(NULL, "Failed to get credentials from unix socket (%s).", strerror(errno));
Michal Vaskod3910912020-04-20 09:12:49 +02001871 return -1;
1872 }
apropp-molex4e903c32020-04-20 03:06:58 -04001873 return 0;
1874}
1875
1876static int
Michal Vasko5f352c52019-07-10 16:12:06 +02001877nc_accept_unix(struct nc_session *session, int sock)
1878{
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001879#if defined (SO_PEERCRED) || defined (HAVE_GETPEEREID)
Michal Vasko5f352c52019-07-10 16:12:06 +02001880 const struct passwd *pw;
Michal Vasko5f352c52019-07-10 16:12:06 +02001881 char *username;
Michal Vasko5f352c52019-07-10 16:12:06 +02001882 session->ti_type = NC_TI_UNIX;
apropp-molex4e903c32020-04-20 03:06:58 -04001883 uid_t uid;
Michal Vasko5f352c52019-07-10 16:12:06 +02001884
Michal Vaskod3910912020-04-20 09:12:49 +02001885 if (nc_get_uid(sock, &uid)) {
1886 close(sock);
Michal Vasko5f352c52019-07-10 16:12:06 +02001887 return -1;
1888 }
1889
apropp-molex4e903c32020-04-20 03:06:58 -04001890 pw = getpwuid(uid);
Michal Vasko5f352c52019-07-10 16:12:06 +02001891 if (pw == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001892 ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno));
Michal Vasko5f352c52019-07-10 16:12:06 +02001893 close(sock);
1894 return -1;
1895 }
1896
1897 username = strdup(pw->pw_name);
1898 if (username == NULL) {
1899 ERRMEM;
1900 close(sock);
1901 return -1;
1902 }
Michal Vasko77367452021-02-16 16:32:18 +01001903 lydict_insert_zc(server_opts.ctx, username, &session->username);
Michal Vasko5f352c52019-07-10 16:12:06 +02001904
1905 session->ti.unixsock.sock = sock;
1906
1907 return 1;
Claus Klein22091912020-01-20 13:45:47 +01001908#else
1909 return -1;
1910#endif
Michal Vasko5f352c52019-07-10 16:12:06 +02001911}
1912
Michal Vaskoe2713da2016-08-22 16:06:40 +02001913API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001914nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001915{
Michal Vasko3031aae2016-01-27 16:07:18 +01001916 uint16_t i;
Michal Vaskoade892d2017-02-22 13:40:35 +01001917 int ret = 0;
Michal Vasko9e036d52016-01-08 10:49:26 +01001918
Michal Vasko45e53ae2016-04-07 11:46:03 +02001919 if (!name) {
1920 ERRARG("name");
1921 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001922 }
1923
Michal Vaskoade892d2017-02-22 13:40:35 +01001924 /* BIND LOCK */
1925 pthread_mutex_lock(&server_opts.bind_lock);
1926
1927 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001928 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001929
1930 /* check name uniqueness */
1931 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001932 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02001933 ERR(NULL, "Endpoint \"%s\" already exists.", name);
Michal Vaskoade892d2017-02-22 13:40:35 +01001934 ret = -1;
1935 goto cleanup;
Michal Vasko3031aae2016-01-27 16:07:18 +01001936 }
1937 }
1938
Michal Vasko3031aae2016-01-27 16:07:18 +01001939 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001940 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001941 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001942 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001943 ret = -1;
1944 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001945 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001946 memset(&server_opts.endpts[server_opts.endpt_count - 1], 0, sizeof *server_opts.endpts);
Michal Vasko77367452021-02-16 16:32:18 +01001947 lydict_insert(server_opts.ctx, name, 0, &server_opts.endpts[server_opts.endpt_count - 1].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001948 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001949 server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1;
1950 server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10;
1951 server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001952
Michal Vaskoe2713da2016-08-22 16:06:40 +02001953 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001954 if (!server_opts.binds) {
1955 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001956 ret = -1;
1957 goto cleanup;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001958 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001959
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001960 memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001961 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1962
1963 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001964#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001965 case NC_TI_LIBSSH:
1966 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1967 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1968 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001969 ret = -1;
1970 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001971 }
1972 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
Michal Vasko77367452021-02-16 16:32:18 +01001973 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001974 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02001975 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001976 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001977#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001978#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001979 case NC_TI_OPENSSL:
1980 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1981 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1982 ERRMEM;
Michal Vaskoade892d2017-02-22 13:40:35 +01001983 ret = -1;
1984 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001985 }
1986 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001987#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02001988 case NC_TI_UNIX:
1989 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts));
1990 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) {
1991 ERRMEM;
1992 ret = -1;
1993 goto cleanup;
1994 }
1995 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1;
1996 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1;
1997 server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1;
1998 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001999 default:
2000 ERRINT;
Michal Vaskoade892d2017-02-22 13:40:35 +01002001 ret = -1;
2002 goto cleanup;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002003 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002004
Michal Vaskoade892d2017-02-22 13:40:35 +01002005cleanup:
2006 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002007 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002008
Michal Vaskoade892d2017-02-22 13:40:35 +01002009 /* BIND UNLOCK */
2010 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002011
Michal Vaskoade892d2017-02-22 13:40:35 +01002012 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002013}
2014
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002015API int
Michal Vasko59050372016-11-22 14:33:55 +01002016nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01002017{
2018 uint32_t i;
2019 int ret = -1;
2020
Michal Vaskoade892d2017-02-22 13:40:35 +01002021 /* BIND LOCK */
2022 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002023
Michal Vaskoade892d2017-02-22 13:40:35 +01002024 /* ENDPT WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002025 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002026
Michal Vasko59050372016-11-22 14:33:55 +01002027 if (!name && !ti) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002028 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01002029 for (i = 0; i < server_opts.endpt_count; ++i) {
2030 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002031 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002032#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002033 case NC_TI_LIBSSH:
2034 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2035 free(server_opts.endpts[i].opts.ssh);
2036 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002037#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002038#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002039 case NC_TI_OPENSSL:
2040 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2041 free(server_opts.endpts[i].opts.tls);
2042 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002043#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002044 case NC_TI_UNIX:
2045 free(server_opts.endpts[i].opts.unixsock);
2046 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002047 default:
2048 ERRINT;
2049 /* won't get here ...*/
2050 break;
2051 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002052 ret = 0;
2053 }
Michal Vasko3031aae2016-01-27 16:07:18 +01002054 free(server_opts.endpts);
2055 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02002056
2057 /* remove all binds */
2058 for (i = 0; i < server_opts.endpt_count; ++i) {
2059 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2060 if (server_opts.binds[i].sock > -1) {
2061 close(server_opts.binds[i].sock);
2062 }
2063 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02002064 free(server_opts.binds);
2065 server_opts.binds = NULL;
2066
Michal Vasko3031aae2016-01-27 16:07:18 +01002067 server_opts.endpt_count = 0;
2068
Michal Vasko1a38c862016-01-15 15:50:07 +01002069 } else {
Michal Vasko59050372016-11-22 14:33:55 +01002070 /* remove one endpoint with bind(s) or all endpoints using one transport protocol */
Michal Vasko3031aae2016-01-27 16:07:18 +01002071 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko59050372016-11-22 14:33:55 +01002072 if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02002073 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01002074 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002075 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01002076#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002077 case NC_TI_LIBSSH:
2078 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
2079 free(server_opts.endpts[i].opts.ssh);
2080 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002081#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002082#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002083 case NC_TI_OPENSSL:
2084 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
2085 free(server_opts.endpts[i].opts.tls);
2086 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01002087#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002088 case NC_TI_UNIX:
2089 free(server_opts.endpts[i].opts.unixsock);
2090 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002091 default:
2092 ERRINT;
2093 break;
2094 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002095
Michal Vaskoe2713da2016-08-22 16:06:40 +02002096 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02002097 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
2098 if (server_opts.binds[i].sock > -1) {
2099 close(server_opts.binds[i].sock);
2100 }
2101
2102 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01002103 --server_opts.endpt_count;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002104 if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01002105 free(server_opts.binds);
2106 server_opts.binds = NULL;
2107 free(server_opts.endpts);
2108 server_opts.endpts = NULL;
Michal Vasko9ed67a72016-10-13 15:00:51 +02002109 } else if (i < server_opts.endpt_count) {
2110 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
2111 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoc0256492016-02-02 12:19:06 +01002112 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002113
2114 ret = 0;
Michal Vasko59050372016-11-22 14:33:55 +01002115 if (name) {
2116 break;
2117 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002118 }
2119 }
Michal Vasko9e036d52016-01-08 10:49:26 +01002120 }
2121
Michal Vaskoade892d2017-02-22 13:40:35 +01002122 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002123 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01002124
Michal Vaskoade892d2017-02-22 13:40:35 +01002125 /* BIND UNLOCK */
2126 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01002127
2128 return ret;
2129}
2130
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002131API int
2132nc_server_endpt_count(void)
2133{
2134 return server_opts.endpt_count;
2135}
2136
Michal Vasko1b5973e2020-01-30 16:05:46 +01002137API int
2138nc_server_is_endpt(const char *name)
2139{
2140 uint16_t i;
2141 int found = 0;
2142
Michal Vaskofb1724b2020-01-31 11:02:00 +01002143 if (!name) {
2144 return found;
2145 }
2146
Michal Vasko1b5973e2020-01-30 16:05:46 +01002147 /* ENDPT READ LOCK */
2148 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2149
2150 /* check name uniqueness */
2151 for (i = 0; i < server_opts.endpt_count; ++i) {
2152 if (!strcmp(server_opts.endpts[i].name, name)) {
2153 found = 1;
2154 break;
2155 }
2156 }
2157
2158 /* ENDPT UNLOCK */
2159 pthread_rwlock_unlock(&server_opts.endpt_lock);
2160
2161 return found;
2162}
2163
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002164int
2165nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
2166{
2167 struct nc_endpt *endpt;
2168 struct nc_bind *bind = NULL;
2169 uint16_t i;
2170 int sock = -1, set_addr, ret = 0;
2171
2172 if (!endpt_name) {
2173 ERRARG("endpt_name");
2174 return -1;
2175 } else if ((!address && !port) || (address && port)) {
2176 ERRARG("address and port");
2177 return -1;
2178 }
2179
2180 if (address) {
2181 set_addr = 1;
2182 } else {
2183 set_addr = 0;
2184 }
2185
2186 /* BIND LOCK */
2187 pthread_mutex_lock(&server_opts.bind_lock);
2188
2189 /* ENDPT LOCK */
2190 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
2191 if (!endpt) {
2192 /* BIND UNLOCK */
2193 pthread_mutex_unlock(&server_opts.bind_lock);
2194 return -1;
2195 }
2196
2197 bind = &server_opts.binds[i];
2198
2199 if (set_addr) {
2200 port = bind->port;
2201 } else {
2202 address = bind->address;
2203 }
2204
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002205 if (!set_addr && (endpt->ti == NC_TI_UNIX)) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002206 ret = -1;
2207 goto cleanup;
2208 }
2209
2210 /* we have all the information we need to create a listening socket */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002211 if (address && (port || (endpt->ti == NC_TI_UNIX))) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002212 /* create new socket, close the old one */
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002213 if (endpt->ti == NC_TI_UNIX) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002214 sock = nc_sock_listen_unix(address, endpt->opts.unixsock);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002215 } else {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002216 sock = nc_sock_listen_inet(address, port, &endpt->ka);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002217 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002218 if (sock == -1) {
2219 ret = -1;
2220 goto cleanup;
2221 }
2222
2223 if (bind->sock > -1) {
2224 close(bind->sock);
2225 }
2226 bind->sock = sock;
2227 } /* else we are just setting address or port */
2228
2229 if (set_addr) {
2230 lydict_remove(server_opts.ctx, bind->address);
Michal Vasko77367452021-02-16 16:32:18 +01002231 lydict_insert(server_opts.ctx, address, 0, &bind->address);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002232 } else {
2233 bind->port = port;
2234 }
2235
2236 if (sock > -1) {
Michal Vasko946cacb2020-08-12 11:18:08 +02002237 switch (endpt->ti) {
2238 case NC_TI_UNIX:
Michal Vasko05532772021-06-03 12:12:38 +02002239 VRB(NULL, "Listening on %s for UNIX connections.", address);
Michal Vasko946cacb2020-08-12 11:18:08 +02002240 break;
2241#ifdef NC_ENABLED_SSH
2242 case NC_TI_LIBSSH:
Michal Vasko05532772021-06-03 12:12:38 +02002243 VRB(NULL, "Listening on %s:%u for SSH connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002244 break;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002245#endif
Michal Vasko946cacb2020-08-12 11:18:08 +02002246#ifdef NC_ENABLED_TLS
2247 case NC_TI_OPENSSL:
Michal Vasko05532772021-06-03 12:12:38 +02002248 VRB(NULL, "Listening on %s:%u for TLS connections.", address, port);
Michal Vasko946cacb2020-08-12 11:18:08 +02002249 break;
2250#endif
2251 default:
2252 ERRINT;
2253 break;
2254 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002255 }
2256
2257cleanup:
2258 /* ENDPT UNLOCK */
2259 pthread_rwlock_unlock(&server_opts.endpt_lock);
2260
2261 /* BIND UNLOCK */
2262 pthread_mutex_unlock(&server_opts.bind_lock);
2263
2264 return ret;
2265}
2266
2267API int
2268nc_server_endpt_set_address(const char *endpt_name, const char *address)
2269{
2270 return nc_server_endpt_set_address_port(endpt_name, address, 0);
2271}
2272
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002273#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002274
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002275API int
2276nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
2277{
2278 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
2279}
2280
Michal Vasko946cacb2020-08-12 11:18:08 +02002281#endif
2282
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002283API int
2284nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid)
2285{
2286 struct nc_endpt *endpt;
2287 uint16_t i;
2288 int ret = 0;
2289
2290 if (!endpt_name) {
2291 ERRARG("endpt_name");
2292 return -1;
2293 } else if (mode == 0) {
2294 ERRARG("mode");
2295 return -1;
2296 }
2297
2298 /* ENDPT LOCK */
2299 endpt = nc_server_endpt_lock_get(endpt_name, 0, &i);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002300 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002301 return -1;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002302 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002303
2304 if (endpt->ti != NC_TI_UNIX) {
2305 ret = -1;
2306 goto cleanup;
2307 }
2308
2309 endpt->opts.unixsock->mode = mode;
2310 endpt->opts.unixsock->uid = uid;
2311 endpt->opts.unixsock->gid = gid;
2312
2313cleanup:
2314 /* ENDPT UNLOCK */
2315 pthread_rwlock_unlock(&server_opts.endpt_lock);
2316
2317 return ret;
2318}
2319
2320API int
2321nc_server_endpt_enable_keepalives(const char *endpt_name, int enable)
2322{
2323 struct nc_endpt *endpt;
2324 int ret = 0;
2325
2326 if (!endpt_name) {
2327 ERRARG("endpt_name");
2328 return -1;
2329 }
2330
2331 /* ENDPT LOCK */
2332 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2333 if (!endpt) {
2334 return -1;
2335 }
2336
2337 endpt->ka.enabled = (enable ? 1 : 0);
2338
2339 /* ENDPT UNLOCK */
2340 pthread_rwlock_unlock(&server_opts.endpt_lock);
2341
2342 return ret;
2343}
2344
2345API int
2346nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval)
2347{
2348 struct nc_endpt *endpt;
2349 int ret = 0;
2350
2351 if (!endpt_name) {
2352 ERRARG("endpt_name");
2353 return -1;
2354 }
2355
2356 /* ENDPT LOCK */
2357 endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL);
2358 if (!endpt) {
2359 return -1;
2360 }
2361
2362 if (idle_time > -1) {
2363 endpt->ka.idle_time = idle_time;
2364 }
2365 if (max_probes > -1) {
2366 endpt->ka.max_probes = max_probes;
2367 }
2368 if (probe_interval > -1) {
2369 endpt->ka.probe_interval = probe_interval;
2370 }
2371
2372 /* ENDPT UNLOCK */
2373 pthread_rwlock_unlock(&server_opts.endpt_lock);
2374
2375 return ret;
2376}
2377
Michal Vasko71090fc2016-05-24 16:37:28 +02002378API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01002379nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01002380{
Michal Vasko71090fc2016-05-24 16:37:28 +02002381 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01002382 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01002383 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002384 uint16_t port, bind_idx;
Michal Vasko9fb42272017-10-05 13:50:05 +02002385 struct timespec ts_cur;
Michal Vasko9e036d52016-01-08 10:49:26 +01002386
Michal Vasko45e53ae2016-04-07 11:46:03 +02002387 if (!server_opts.ctx) {
2388 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02002389 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02002390 } else if (!session) {
2391 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02002392 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002393 }
2394
Michal Vaskoade892d2017-02-22 13:40:35 +01002395 /* BIND LOCK */
2396 pthread_mutex_lock(&server_opts.bind_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01002397
2398 if (!server_opts.endpt_count) {
Michal Vasko05532772021-06-03 12:12:38 +02002399 ERR(NULL, "No endpoints to accept sessions on.");
Michal Vaskoade892d2017-02-22 13:40:35 +01002400 /* BIND UNLOCK */
2401 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002402 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01002403 }
Michal Vaskob48aa812016-01-18 14:13:09 +01002404
Michal Vaskoe2713da2016-08-22 16:06:40 +02002405 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vasko50456e82016-02-02 12:16:08 +01002406 if (ret < 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01002407 /* BIND UNLOCK */
2408 pthread_mutex_unlock(&server_opts.bind_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01002409 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02002410 if (!ret) {
2411 return NC_MSG_WOULDBLOCK;
2412 }
Michal Vasko71090fc2016-05-24 16:37:28 +02002413 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01002414 }
Michal Vaskoade892d2017-02-22 13:40:35 +01002415
2416 /* switch bind_lock for endpt_lock, so that another thread can accept another session */
2417 /* ENDPT READ LOCK */
2418 pthread_rwlock_rdlock(&server_opts.endpt_lock);
2419
2420 /* BIND UNLOCK */
2421 pthread_mutex_unlock(&server_opts.bind_lock);
2422
Michal Vaskob48aa812016-01-18 14:13:09 +01002423 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01002424
Michal Vasko131120a2018-05-29 15:44:02 +02002425 *session = nc_new_session(NC_SERVER, 0);
Michal Vasko686aa312016-01-21 15:58:18 +01002426 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01002427 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002428 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01002429 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02002430 msgtype = NC_MSG_ERROR;
2431 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002432 }
Michal Vasko1a38c862016-01-15 15:50:07 +01002433 (*session)->status = NC_STATUS_STARTING;
Michal Vasko1a38c862016-01-15 15:50:07 +01002434 (*session)->ctx = server_opts.ctx;
2435 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01002436 lydict_insert_zc(server_opts.ctx, host, &(*session)->host);
Michal Vasko1a38c862016-01-15 15:50:07 +01002437 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01002438
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002439 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002440#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002441 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
2442 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002443 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002444 if (ret < 0) {
2445 msgtype = NC_MSG_ERROR;
2446 goto cleanup;
2447 } else if (!ret) {
2448 msgtype = NC_MSG_WOULDBLOCK;
2449 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002450 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002451 } else
2452#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002453#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002454 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
2455 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vaskob70c8b82017-03-17 09:09:29 +01002456 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko71090fc2016-05-24 16:37:28 +02002457 if (ret < 0) {
2458 msgtype = NC_MSG_ERROR;
2459 goto cleanup;
2460 } else if (!ret) {
2461 msgtype = NC_MSG_WOULDBLOCK;
2462 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002463 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002464 } else
2465#endif
Olivier Matzac7fa2f2018-10-11 10:02:04 +02002466 if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) {
2467 (*session)->data = server_opts.endpts[bind_idx].opts.unixsock;
2468 ret = nc_accept_unix(*session, sock);
2469 if (ret < 0) {
2470 msgtype = NC_MSG_ERROR;
2471 goto cleanup;
2472 }
2473 } else {
Michal Vasko9e036d52016-01-08 10:49:26 +01002474 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01002475 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002476 msgtype = NC_MSG_ERROR;
2477 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01002478 }
2479
Michal Vasko2cc4c682016-03-01 09:16:48 +01002480 (*session)->data = NULL;
2481
Michal Vaskoade892d2017-02-22 13:40:35 +01002482 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002483 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002484
Michal Vaskob48aa812016-01-18 14:13:09 +01002485 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002486 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob48aa812016-01-18 14:13:09 +01002487
Michal Vasko9e036d52016-01-08 10:49:26 +01002488 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02002489 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02002490 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002491 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01002492 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002493 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002494 }
Michal Vasko9fb42272017-10-05 13:50:05 +02002495
2496 nc_gettimespec_mono(&ts_cur);
2497 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
2498 nc_gettimespec_real(&ts_cur);
2499 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vasko1a38c862016-01-15 15:50:07 +01002500 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01002501
Michal Vasko71090fc2016-05-24 16:37:28 +02002502 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002503
Michal Vasko71090fc2016-05-24 16:37:28 +02002504cleanup:
Michal Vaskoade892d2017-02-22 13:40:35 +01002505 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002506 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01002507
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002508 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01002509 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002510 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01002511}
2512
Michal Vaskob83a3fa2021-05-26 09:53:42 +02002513#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
Michal Vasko946cacb2020-08-12 11:18:08 +02002514
Michal Vaskoadf30f02019-06-24 09:34:47 +02002515/* client is expected to be locked */
2516static int
2517_nc_server_ch_client_del_endpt(struct nc_ch_client *client, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002518{
2519 uint16_t i;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002520 int ret = -1;
2521
2522 if (!endpt_name) {
2523 /* remove all endpoints */
2524 for (i = 0; i < client->ch_endpt_count; ++i) {
2525 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2526 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2527 if (client->ch_endpts[i].sock_pending != -1) {
2528 close(client->ch_endpts[i].sock_pending);
2529 }
2530 switch (client->ch_endpts[i].ti) {
2531#ifdef NC_ENABLED_SSH
2532 case NC_TI_LIBSSH:
2533 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2534 free(client->ch_endpts[i].opts.ssh);
2535 break;
2536#endif
2537#ifdef NC_ENABLED_TLS
2538 case NC_TI_OPENSSL:
2539 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2540 free(client->ch_endpts[i].opts.tls);
2541 break;
2542#endif
2543 default:
2544 ERRINT;
2545 /* won't get here ...*/
2546 break;
2547 }
2548 }
2549 free(client->ch_endpts);
2550 client->ch_endpts = NULL;
2551 client->ch_endpt_count = 0;
2552
2553 ret = 0;
2554 } else {
2555 for (i = 0; i < client->ch_endpt_count; ++i) {
2556 if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) {
2557 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2558 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2559 if (client->ch_endpts[i].sock_pending != -1) {
2560 close(client->ch_endpts[i].sock_pending);
2561 }
2562 switch (client->ch_endpts[i].ti) {
2563#ifdef NC_ENABLED_SSH
2564 case NC_TI_LIBSSH:
2565 nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh);
2566 free(client->ch_endpts[i].opts.ssh);
2567 break;
2568#endif
2569#ifdef NC_ENABLED_TLS
2570 case NC_TI_OPENSSL:
2571 nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls);
2572 free(client->ch_endpts[i].opts.tls);
2573 break;
2574#endif
2575 default:
2576 ERRINT;
2577 /* won't get here ...*/
2578 break;
2579 }
2580
2581 /* move last endpoint to the empty space */
2582 --client->ch_endpt_count;
2583 if (i < client->ch_endpt_count) {
2584 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2585 } else if (!server_opts.ch_client_count) {
2586 free(server_opts.ch_clients);
2587 server_opts.ch_clients = NULL;
2588 }
2589
2590 ret = 0;
2591 break;
2592 }
2593 }
2594 }
2595
2596 return ret;
2597}
2598
2599API int
2600nc_server_ch_add_client(const char *name)
2601{
2602 uint16_t i;
2603 struct nc_ch_client *client;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002604
2605 if (!name) {
2606 ERRARG("name");
2607 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002608 }
2609
2610 /* WRITE LOCK */
2611 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2612
2613 /* check name uniqueness */
2614 for (i = 0; i < server_opts.ch_client_count; ++i) {
2615 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002616 ERR(NULL, "Call Home client \"%s\" already exists.", name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002617 /* WRITE UNLOCK */
2618 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2619 return -1;
2620 }
2621 }
2622
2623 ++server_opts.ch_client_count;
2624 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
2625 if (!server_opts.ch_clients) {
2626 ERRMEM;
2627 /* WRITE UNLOCK */
2628 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2629 return -1;
2630 }
Michal Vaskoadf30f02019-06-24 09:34:47 +02002631 client = &server_opts.ch_clients[server_opts.ch_client_count - 1];
Michal Vasko2e6defd2016-10-07 15:48:15 +02002632
Michal Vasko77367452021-02-16 16:32:18 +01002633 lydict_insert(server_opts.ctx, name, 0, &client->name);
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02002634 client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002635 client->ch_endpts = NULL;
2636 client->ch_endpt_count = 0;
2637 client->conn_type = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02002638
Michal Vasko2e6defd2016-10-07 15:48:15 +02002639 /* set CH default options */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002640 client->start_with = NC_CH_FIRST_LISTED;
2641 client->max_attempts = 3;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002642
Michal Vaskoadf30f02019-06-24 09:34:47 +02002643 pthread_mutex_init(&client->lock, NULL);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002644
2645 /* WRITE UNLOCK */
2646 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2647
2648 return 0;
2649}
2650
2651API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002652nc_server_ch_del_client(const char *name)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002653{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002654 uint16_t i;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002655 int ret = -1;
2656
2657 /* WRITE LOCK */
2658 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
2659
Michal Vaskoadf30f02019-06-24 09:34:47 +02002660 if (!name) {
2661 /* remove all CH clients with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002662 for (i = 0; i < server_opts.ch_client_count; ++i) {
2663 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2664
2665 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002666 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002667
2668 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002669 ret = 0;
2670 }
2671 free(server_opts.ch_clients);
2672 server_opts.ch_clients = NULL;
2673
2674 server_opts.ch_client_count = 0;
2675
2676 } else {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002677 /* remove one client with endpoints */
Michal Vasko2e6defd2016-10-07 15:48:15 +02002678 for (i = 0; i < server_opts.ch_client_count; ++i) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02002679 if (!strcmp(server_opts.ch_clients[i].name, name)) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002680 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
2681
Michal Vasko2e6defd2016-10-07 15:48:15 +02002682 /* remove all endpoints */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002683 _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002684
2685 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
2686
2687 /* move last client and endpoint(s) to the empty space */
2688 --server_opts.ch_client_count;
2689 if (i < server_opts.ch_client_count) {
2690 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
Michal Vaskoadf30f02019-06-24 09:34:47 +02002691 sizeof *server_opts.ch_clients);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002692 } else if (!server_opts.ch_client_count) {
2693 free(server_opts.ch_clients);
2694 server_opts.ch_clients = NULL;
2695 }
2696
2697 ret = 0;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002698 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002699 }
2700 }
2701 }
2702
2703 /* WRITE UNLOCK */
2704 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2705
2706 return ret;
2707}
2708
2709API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002710nc_server_ch_is_client(const char *name)
2711{
2712 uint16_t i;
2713 int found = 0;
2714
2715 if (!name) {
2716 return found;
2717 }
2718
2719 /* READ LOCK */
2720 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2721
2722 /* check name uniqueness */
2723 for (i = 0; i < server_opts.ch_client_count; ++i) {
2724 if (!strcmp(server_opts.ch_clients[i].name, name)) {
2725 found = 1;
2726 break;
2727 }
2728 }
2729
2730 /* UNLOCK */
2731 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2732
2733 return found;
2734}
2735
2736API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002737nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002738{
2739 uint16_t i;
2740 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002741 struct nc_ch_endpt *endpt;
2742 int ret = -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002743
2744 if (!client_name) {
2745 ERRARG("client_name");
2746 return -1;
2747 } else if (!endpt_name) {
2748 ERRARG("endpt_name");
2749 return -1;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002750 } else if (!ti) {
2751 ERRARG("ti");
2752 return -1;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002753 }
2754
2755 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002756 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002757 if (!client) {
2758 return -1;
2759 }
2760
2761 for (i = 0; i < client->ch_endpt_count; ++i) {
2762 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
Michal Vasko05532772021-06-03 12:12:38 +02002763 ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002764 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002765 }
2766 }
2767
2768 ++client->ch_endpt_count;
2769 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
2770 if (!client->ch_endpts) {
2771 ERRMEM;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002772 goto cleanup;
2773 }
2774 endpt = &client->ch_endpts[client->ch_endpt_count - 1];
2775
2776 memset(endpt, 0, sizeof *client->ch_endpts);
Michal Vasko77367452021-02-16 16:32:18 +01002777 lydict_insert(server_opts.ctx, endpt_name, 0, &endpt->name);
Michal Vaskoadf30f02019-06-24 09:34:47 +02002778 endpt->ti = ti;
2779 endpt->sock_pending = -1;
2780 endpt->ka.idle_time = 1;
2781 endpt->ka.max_probes = 10;
2782 endpt->ka.probe_interval = 5;
2783
2784 switch (ti) {
2785#ifdef NC_ENABLED_SSH
2786 case NC_TI_LIBSSH:
2787 endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
2788 if (!endpt->opts.ssh) {
2789 ERRMEM;
2790 goto cleanup;
2791 }
2792 endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
2793 endpt->opts.ssh->auth_attempts = 3;
Michal Vaskocbad4c52019-06-27 16:30:35 +02002794 endpt->opts.ssh->auth_timeout = 30;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002795 break;
2796#endif
2797#ifdef NC_ENABLED_TLS
2798 case NC_TI_OPENSSL:
2799 endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
2800 if (!endpt->opts.tls) {
2801 ERRMEM;
2802 goto cleanup;
2803 }
2804 break;
2805#endif
2806 default:
2807 ERRINT;
2808 goto cleanup;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002809 }
2810
Michal Vaskoadf30f02019-06-24 09:34:47 +02002811 /* success */
2812 ret = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002813
Michal Vaskoadf30f02019-06-24 09:34:47 +02002814cleanup:
Michal Vasko2e6defd2016-10-07 15:48:15 +02002815 /* UNLOCK */
2816 nc_server_ch_client_unlock(client);
2817
Michal Vaskoadf30f02019-06-24 09:34:47 +02002818 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002819}
2820
2821API int
Michal Vaskoadf30f02019-06-24 09:34:47 +02002822nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti)
Michal Vasko2e6defd2016-10-07 15:48:15 +02002823{
Michal Vaskoadf30f02019-06-24 09:34:47 +02002824 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002825 struct nc_ch_client *client;
2826
2827 if (!client_name) {
2828 ERRARG("client_name");
2829 return -1;
2830 }
2831
2832 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002833 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002834 if (!client) {
2835 return -1;
2836 }
2837
Michal Vaskoadf30f02019-06-24 09:34:47 +02002838 ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002839
2840 /* UNLOCK */
2841 nc_server_ch_client_unlock(client);
2842
2843 return ret;
2844}
2845
2846API int
Michal Vaskofb1724b2020-01-31 11:02:00 +01002847nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name)
2848{
2849 uint16_t i;
2850 struct nc_ch_client *client = NULL;
2851 int found = 0;
2852
2853 if (!client_name || !endpt_name) {
2854 return found;
2855 }
2856
2857 /* READ LOCK */
2858 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
2859
2860 for (i = 0; i < server_opts.ch_client_count; ++i) {
2861 if (!strcmp(server_opts.ch_clients[i].name, client_name)) {
2862 client = &server_opts.ch_clients[i];
2863 break;
2864 }
2865 }
2866
2867 if (!client) {
2868 goto cleanup;
2869 }
2870
2871 for (i = 0; i < client->ch_endpt_count; ++i) {
2872 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2873 found = 1;
2874 break;
2875 }
2876 }
2877
2878cleanup:
2879 /* UNLOCK */
2880 pthread_rwlock_unlock(&server_opts.ch_client_lock);
2881 return found;
2882}
2883
2884API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02002885nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2886{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002887 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002888 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002889
2890 if (!client_name) {
2891 ERRARG("client_name");
2892 return -1;
2893 } else if (!endpt_name) {
2894 ERRARG("endpt_name");
2895 return -1;
2896 } else if (!address) {
2897 ERRARG("address");
2898 return -1;
2899 }
2900
2901 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002902 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2903 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002904 return -1;
2905 }
2906
Michal Vaskoadf30f02019-06-24 09:34:47 +02002907 lydict_remove(server_opts.ctx, endpt->address);
Michal Vasko77367452021-02-16 16:32:18 +01002908 lydict_insert(server_opts.ctx, address, 0, &endpt->address);
Michal Vasko2e6defd2016-10-07 15:48:15 +02002909
2910 /* UNLOCK */
2911 nc_server_ch_client_unlock(client);
2912
Michal Vaskoadf30f02019-06-24 09:34:47 +02002913 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002914}
2915
2916API int
2917nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2918{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002919 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002920 struct nc_ch_endpt *endpt;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002921
2922 if (!client_name) {
2923 ERRARG("client_name");
2924 return -1;
2925 } else if (!endpt_name) {
2926 ERRARG("endpt_name");
2927 return -1;
2928 } else if (!port) {
2929 ERRARG("port");
2930 return -1;
2931 }
2932
2933 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002934 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2935 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02002936 return -1;
2937 }
2938
Michal Vaskoadf30f02019-06-24 09:34:47 +02002939 endpt->port = port;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002940
2941 /* UNLOCK */
2942 nc_server_ch_client_unlock(client);
2943
ravsz5c5a4422020-03-31 15:53:21 +02002944 return 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002945}
2946
2947API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002948nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable)
2949{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002950 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002951 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002952
2953 if (!client_name) {
2954 ERRARG("client_name");
2955 return -1;
2956 } else if (!endpt_name) {
2957 ERRARG("endpt_name");
2958 return -1;
2959 }
2960
2961 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002962 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2963 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002964 return -1;
2965 }
2966
Michal Vaskoadf30f02019-06-24 09:34:47 +02002967 endpt->ka.enabled = (enable ? 1 : 0);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002968
2969 /* UNLOCK */
2970 nc_server_ch_client_unlock(client);
2971
Michal Vasko9af829a2019-09-12 13:50:00 +02002972 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002973}
2974
2975API int
2976nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes,
2977 int probe_interval)
2978{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002979 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +02002980 struct nc_ch_endpt *endpt;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002981
2982 if (!client_name) {
2983 ERRARG("client_name");
2984 return -1;
2985 } else if (!endpt_name) {
2986 ERRARG("endpt_name");
2987 return -1;
2988 }
2989
2990 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02002991 endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client);
2992 if (!endpt) {
Michal Vaskoe49a15f2019-05-27 14:18:36 +02002993 return -1;
2994 }
2995
Michal Vaskoadf30f02019-06-24 09:34:47 +02002996 if (idle_time > -1) {
2997 endpt->ka.idle_time = idle_time;
2998 }
2999 if (max_probes > -1) {
3000 endpt->ka.max_probes = max_probes;
3001 }
3002 if (probe_interval > -1) {
3003 endpt->ka.probe_interval = probe_interval;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003004 }
3005
3006 /* UNLOCK */
3007 nc_server_ch_client_unlock(client);
3008
Michal Vasko9af829a2019-09-12 13:50:00 +02003009 return 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003010}
3011
3012API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003013nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
3014{
3015 struct nc_ch_client *client;
3016
3017 if (!client_name) {
3018 ERRARG("client_name");
3019 return -1;
3020 } else if (!conn_type) {
3021 ERRARG("conn_type");
3022 return -1;
3023 }
3024
3025 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003026 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003027 if (!client) {
3028 return -1;
3029 }
3030
3031 if (client->conn_type != conn_type) {
3032 client->conn_type = conn_type;
3033
3034 /* set default options */
3035 switch (conn_type) {
3036 case NC_CH_PERSIST:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003037 /* no options */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003038 break;
3039 case NC_CH_PERIOD:
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003040 client->conn.period.period = 60;
3041 client->conn.period.anchor_time = 0;
3042 client->conn.period.idle_timeout = 120;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003043 break;
3044 default:
3045 ERRINT;
3046 break;
3047 }
3048 }
3049
3050 /* UNLOCK */
3051 nc_server_ch_client_unlock(client);
3052
3053 return 0;
3054}
3055
3056API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003057nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period)
3058{
3059 struct nc_ch_client *client;
3060
3061 if (!client_name) {
3062 ERRARG("client_name");
3063 return -1;
3064 } else if (!period) {
3065 ERRARG("period");
3066 return -1;
3067 }
3068
3069 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003070 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003071 if (!client) {
3072 return -1;
3073 }
3074
3075 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003076 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003077 /* UNLOCK */
3078 nc_server_ch_client_unlock(client);
3079 return -1;
3080 }
3081
3082 client->conn.period.period = period;
3083
3084 /* UNLOCK */
3085 nc_server_ch_client_unlock(client);
3086
3087 return 0;
3088}
3089
3090API int
3091nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003092{
3093 struct nc_ch_client *client;
3094
3095 if (!client_name) {
3096 ERRARG("client_name");
3097 return -1;
3098 }
3099
3100 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003101 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003102 if (!client) {
3103 return -1;
3104 }
3105
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003106 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003107 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003108 /* UNLOCK */
3109 nc_server_ch_client_unlock(client);
3110 return -1;
3111 }
3112
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003113 client->conn.period.anchor_time = anchor_time;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003114
3115 /* UNLOCK */
3116 nc_server_ch_client_unlock(client);
3117
3118 return 0;
3119}
3120
3121API int
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003122nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
Michal Vasko2e6defd2016-10-07 15:48:15 +02003123{
3124 struct nc_ch_client *client;
3125
3126 if (!client_name) {
3127 ERRARG("client_name");
3128 return -1;
3129 }
3130
3131 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003132 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003133 if (!client) {
3134 return -1;
3135 }
3136
3137 if (client->conn_type != NC_CH_PERIOD) {
Michal Vasko05532772021-06-03 12:12:38 +02003138 ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003139 /* UNLOCK */
3140 nc_server_ch_client_unlock(client);
3141 return -1;
3142 }
3143
3144 client->conn.period.idle_timeout = idle_timeout;
3145
3146 /* UNLOCK */
3147 nc_server_ch_client_unlock(client);
3148
3149 return 0;
3150}
3151
3152API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02003153nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
3154{
3155 struct nc_ch_client *client;
3156
3157 if (!client_name) {
3158 ERRARG("client_name");
3159 return -1;
3160 }
3161
3162 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003163 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003164 if (!client) {
3165 return -1;
3166 }
3167
3168 client->start_with = start_with;
3169
3170 /* UNLOCK */
3171 nc_server_ch_client_unlock(client);
3172
3173 return 0;
3174}
3175
3176API int
3177nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
3178{
3179 struct nc_ch_client *client;
3180
3181 if (!client_name) {
3182 ERRARG("client_name");
3183 return -1;
3184 } else if (!max_attempts) {
3185 ERRARG("max_attempts");
3186 return -1;
3187 }
3188
3189 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003190 nc_server_ch_client_lock(client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003191 if (!client) {
3192 return -1;
3193 }
3194
3195 client->max_attempts = max_attempts;
3196
3197 /* UNLOCK */
3198 nc_server_ch_client_unlock(client);
3199
3200 return 0;
3201}
3202
3203/* client lock is expected to be held */
3204static NC_MSG_TYPE
Michal Vaskoadf30f02019-06-24 09:34:47 +02003205nc_connect_ch_endpt(struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01003206{
Michal Vasko71090fc2016-05-24 16:37:28 +02003207 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003208 int sock, ret;
Michal Vasko9fb42272017-10-05 13:50:05 +02003209 struct timespec ts_cur;
Michal Vasko66032bc2019-01-22 15:03:12 +01003210 char *ip_host;
Michal Vaskob05053d2016-01-22 16:12:06 +01003211
Michal Vasko4c612cd2021-02-05 08:53:42 +01003212 sock = nc_sock_connect(endpt->address, endpt->port, NC_SOCKET_CH_TIMEOUT, &endpt->ka, &endpt->sock_pending, &ip_host);
Michal Vaskoc61c4492016-01-25 11:13:34 +01003213 if (sock < 0) {
Michal Vasko0b30e452021-03-03 10:30:15 +01003214 if (endpt->sock_pending > -1) {
3215 ++endpt->sock_retries;
3216 if (endpt->sock_retries == NC_SOCKET_CH_RETRIES) {
Michal Vasko05532772021-06-03 12:12:38 +02003217 ERR(NULL, "Failed to connect socket %d after %d retries, closing.", endpt->sock_pending, NC_SOCKET_CH_RETRIES);
Michal Vasko0b30e452021-03-03 10:30:15 +01003218 close(endpt->sock_pending);
3219 endpt->sock_pending = -1;
3220 endpt->sock_retries = 0;
3221 }
Michal Vasko4c612cd2021-02-05 08:53:42 +01003222 }
Michal Vasko71090fc2016-05-24 16:37:28 +02003223 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003224 }
Frank Rimpler9f838b02018-07-25 06:44:03 +00003225 /* no need to store the socket as pending any longer */
3226 endpt->sock_pending = -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01003227
Michal Vasko131120a2018-05-29 15:44:02 +02003228 *session = nc_new_session(NC_SERVER, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01003229 if (!(*session)) {
3230 ERRMEM;
3231 close(sock);
Michal Vasko66032bc2019-01-22 15:03:12 +01003232 free(ip_host);
Michal Vasko71090fc2016-05-24 16:37:28 +02003233 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003234 }
3235 (*session)->status = NC_STATUS_STARTING;
Michal Vaskob05053d2016-01-22 16:12:06 +01003236 (*session)->ctx = server_opts.ctx;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003237 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko77367452021-02-16 16:32:18 +01003238 lydict_insert_zc(server_opts.ctx, ip_host, &(*session)->host);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003239 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01003240
Michal Vaskob05053d2016-01-22 16:12:06 +01003241 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01003242#ifdef NC_ENABLED_SSH
Michal Vaskoadf30f02019-06-24 09:34:47 +02003243 if (endpt->ti == NC_TI_LIBSSH) {
3244 (*session)->data = endpt->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01003245 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003246 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003247
Michal Vasko71090fc2016-05-24 16:37:28 +02003248 if (ret < 0) {
3249 msgtype = NC_MSG_ERROR;
3250 goto fail;
3251 } else if (!ret) {
3252 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003253 goto fail;
3254 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003255 } else
3256#endif
Radek Krejci53691be2016-02-22 13:58:37 +01003257#ifdef NC_ENABLED_TLS
Michal Vaskoadf30f02019-06-24 09:34:47 +02003258 if (endpt->ti == NC_TI_OPENSSL) {
3259 (*session)->data = endpt->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01003260 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01003261 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01003262
Michal Vasko71090fc2016-05-24 16:37:28 +02003263 if (ret < 0) {
3264 msgtype = NC_MSG_ERROR;
3265 goto fail;
3266 } else if (!ret) {
3267 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01003268 goto fail;
3269 }
Michal Vasko3d865d22016-01-28 16:00:53 +01003270 } else
3271#endif
3272 {
Michal Vaskob05053d2016-01-22 16:12:06 +01003273 ERRINT;
3274 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02003275 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01003276 goto fail;
3277 }
3278
3279 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02003280 (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskob05053d2016-01-22 16:12:06 +01003281
3282 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02003283 msgtype = nc_handshake_io(*session);
Michal Vasko71090fc2016-05-24 16:37:28 +02003284 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01003285 goto fail;
3286 }
Michal Vasko9fb42272017-10-05 13:50:05 +02003287
3288 nc_gettimespec_mono(&ts_cur);
3289 (*session)->opts.server.last_rpc = ts_cur.tv_sec;
3290 nc_gettimespec_real(&ts_cur);
3291 (*session)->opts.server.session_start = ts_cur.tv_sec;
Michal Vaskob05053d2016-01-22 16:12:06 +01003292 (*session)->status = NC_STATUS_RUNNING;
3293
Michal Vasko71090fc2016-05-24 16:37:28 +02003294 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003295
3296fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01003297 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01003298 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02003299 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01003300}
3301
Michal Vasko2e6defd2016-10-07 15:48:15 +02003302struct nc_ch_client_thread_arg {
3303 char *client_name;
Michal Vaskof1c26c22021-04-12 16:34:33 +02003304 int (*session_clb)(const char *client_name, struct nc_session *new_session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003305};
3306
3307static struct nc_ch_client *
3308nc_server_ch_client_with_endpt_lock(const char *name)
3309{
3310 struct nc_ch_client *client;
3311
3312 while (1) {
3313 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003314 nc_server_ch_client_lock(name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003315 if (!client) {
3316 return NULL;
3317 }
3318 if (client->ch_endpt_count) {
3319 return client;
3320 }
3321 /* no endpoints defined yet */
3322
3323 /* UNLOCK */
3324 nc_server_ch_client_unlock(client);
3325
3326 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
3327 }
3328
3329 return NULL;
3330}
3331
3332static int
3333nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
3334{
Michal Vasko3f05a092018-03-13 10:39:49 +01003335 int ret = 0, r;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003336 uint32_t idle_timeout;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003337 struct timespec ts;
3338 struct nc_ch_client *client;
3339
Michal Vasko2e6defd2016-10-07 15:48:15 +02003340 /* CH LOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003341 pthread_mutex_lock(&session->opts.server.ch_lock);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003342
Michal Vasko0db3db52021-03-03 10:45:42 +01003343 session->flags |= NC_SESSION_CALLHOME;
3344
Michal Vasko2e6defd2016-10-07 15:48:15 +02003345 /* give the session to the user */
Michal Vaskof1c26c22021-04-12 16:34:33 +02003346 if (data->session_clb(data->client_name, session)) {
3347 /* something is wrong, free the session */
3348 session->flags &= ~NC_SESSION_CALLHOME;
3349
3350 /* CH UNLOCK */
3351 pthread_mutex_unlock(&session->opts.server.ch_lock);
3352
3353 nc_session_free(session, NULL);
3354 return 0;
3355 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003356
3357 do {
Michal Vasko77a6abe2017-10-05 10:02:20 +02003358 nc_gettimespec_real(&ts);
Michal Vaskoe39ae6b2017-03-14 09:03:46 +01003359 nc_addtimespec(&ts, NC_CH_NO_ENDPT_WAIT);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003360
Michal Vasko0db3db52021-03-03 10:45:42 +01003361 /* CH COND WAIT */
Michal Vaskoacf98472021-02-04 15:33:57 +01003362 r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts);
Michal Vasko3f05a092018-03-13 10:39:49 +01003363 if (!r) {
3364 /* we were woken up, something probably happened */
3365 if (session->status != NC_STATUS_RUNNING) {
3366 break;
3367 }
3368 } else if (r != ETIMEDOUT) {
Michal Vasko05532772021-06-03 12:12:38 +02003369 ERR(session, "Pthread condition timedwait failed (%s).", strerror(r));
Michal Vasko3f05a092018-03-13 10:39:49 +01003370 ret = -1;
3371 break;
Michal Vasko2e39ed92018-03-12 13:51:44 +01003372 }
3373
Michal Vasko2e6defd2016-10-07 15:48:15 +02003374 /* check whether the client was not removed */
3375 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +02003376 nc_server_ch_client_lock(data->client_name, NULL, 0, &client);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003377 if (!client) {
3378 /* client was removed, finish thread */
Michal Vasko05532772021-06-03 12:12:38 +02003379 VRB(session, "Call Home client \"%s\" removed, but an established session will not be terminated.",
Michal Vaskoadf30f02019-06-24 09:34:47 +02003380 data->client_name);
Michal Vasko3f05a092018-03-13 10:39:49 +01003381 ret = 1;
3382 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003383 }
3384
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003385 if (client->conn_type == NC_CH_PERIOD) {
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003386 idle_timeout = client->conn.period.idle_timeout;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003387 } else {
3388 idle_timeout = 0;
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003389 }
3390
Michal Vasko9fb42272017-10-05 13:50:05 +02003391 nc_gettimespec_mono(&ts);
Michal Vasko3486a7c2017-03-03 13:28:07 +01003392 if (!session->opts.server.ntf_status && idle_timeout && (ts.tv_sec >= session->opts.server.last_rpc + idle_timeout)) {
Michal Vasko05532772021-06-03 12:12:38 +02003393 VRB(session, "Call Home client \"%s\": session idle timeout elapsed.", client->name);
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003394 session->status = NC_STATUS_INVALID;
3395 session->term_reason = NC_SESSION_TERM_TIMEOUT;
3396 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003397
3398 /* UNLOCK */
3399 nc_server_ch_client_unlock(client);
3400
3401 } while (session->status == NC_STATUS_RUNNING);
3402
Michal Vasko0db3db52021-03-03 10:45:42 +01003403 /* signal to nc_session_free() that CH registered this session not being valid anymore */
3404 session->flags &= ~NC_SESSION_CALLHOME;
3405
Michal Vasko27377422018-03-15 08:59:35 +01003406 /* CH UNLOCK */
Michal Vaskoacf98472021-02-04 15:33:57 +01003407 pthread_mutex_unlock(&session->opts.server.ch_lock);
Michal Vasko27377422018-03-15 08:59:35 +01003408
Michal Vasko3f05a092018-03-13 10:39:49 +01003409 return ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003410}
3411
3412static void *
3413nc_ch_client_thread(void *arg)
3414{
3415 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
3416 NC_MSG_TYPE msgtype;
3417 uint8_t cur_attempts = 0;
Peter Feiged05f2252018-09-03 08:09:47 +00003418 uint16_t next_endpt_index;
Michal Vasko9550cf12017-03-21 15:33:58 +01003419 char *cur_endpt_name = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003420 struct nc_ch_endpt *cur_endpt;
3421 struct nc_session *session;
3422 struct nc_ch_client *client;
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003423 uint32_t client_id;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003424 time_t reconnect_in;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003425
3426 /* LOCK */
3427 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3428 if (!client) {
3429 goto cleanup;
3430 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003431 client_id = client->id;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003432
3433 cur_endpt = &client->ch_endpts[0];
3434 cur_endpt_name = strdup(cur_endpt->name);
3435
Michal Vasko05532772021-06-03 12:12:38 +02003436 VRB(NULL, "Call Home client \"%s\" connecting...", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003437 while (1) {
Michal Vaskoadf30f02019-06-24 09:34:47 +02003438 msgtype = nc_connect_ch_endpt(cur_endpt, &session);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003439
3440 if (msgtype == NC_MSG_HELLO) {
3441 /* UNLOCK */
3442 nc_server_ch_client_unlock(client);
3443
Michal Vasko05532772021-06-03 12:12:38 +02003444 VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003445 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
3446 goto cleanup;
3447 }
Michal Vasko05532772021-06-03 12:12:38 +02003448 VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003449
3450 /* LOCK */
3451 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3452 if (!client) {
3453 goto cleanup;
3454 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003455 if (client->id != client_id) {
3456 nc_server_ch_client_unlock(client);
3457 goto cleanup;
3458 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003459
3460 /* session changed status -> it was disconnected for whatever reason,
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003461 * persistent connection immediately tries to reconnect, periodic connects at specific times */
Michal Vasko2e6defd2016-10-07 15:48:15 +02003462 if (client->conn_type == NC_CH_PERIOD) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003463 /* UNLOCK */
3464 nc_server_ch_client_unlock(client);
3465
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003466 /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */
3467 reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60);
Michal Vasko05532772021-06-03 12:12:38 +02003468 VRB(NULL, "Call Home client \"%s\" reconnecting in %d seconds.", data->client_name, reconnect_in);
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003469 sleep(reconnect_in);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003470
3471 /* LOCK */
3472 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3473 if (!client) {
3474 goto cleanup;
3475 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003476 if (client->id != client_id) {
3477 nc_server_ch_client_unlock(client);
3478 goto cleanup;
3479 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02003480 }
3481
3482 /* set next endpoint to try */
3483 if (client->start_with == NC_CH_FIRST_LISTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003484 next_endpt_index = 0;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003485 } else if (client->start_with == NC_CH_LAST_CONNECTED) {
Peter Feiged05f2252018-09-03 08:09:47 +00003486 /* we keep the current one but due to unlock/lock we have to find it again */
3487 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3488 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
3489 break;
3490 }
3491 }
3492 if (next_endpt_index >= client->ch_endpt_count) {
3493 /* endpoint was removed, start with the first one */
3494 next_endpt_index = 0;
3495 }
Michal Vaskoe49a15f2019-05-27 14:18:36 +02003496 } else {
3497 /* just get a random index */
3498 next_endpt_index = rand() % client->ch_endpt_count;
Peter Feiged05f2252018-09-03 08:09:47 +00003499 }
3500
Michal Vasko2e6defd2016-10-07 15:48:15 +02003501 } else {
Michal Vasko6bb116b2016-10-26 13:53:46 +02003502 /* UNLOCK */
3503 nc_server_ch_client_unlock(client);
3504
Michal Vasko2e6defd2016-10-07 15:48:15 +02003505 /* session was not created */
Michal Vaskoc4bc5812016-10-13 10:59:36 +02003506 usleep(NC_CH_ENDPT_FAIL_WAIT * 1000);
3507
Michal Vasko6bb116b2016-10-26 13:53:46 +02003508 /* LOCK */
3509 client = nc_server_ch_client_with_endpt_lock(data->client_name);
3510 if (!client) {
3511 goto cleanup;
3512 }
Andrew Langefeld6ed922d2018-09-12 14:08:32 -05003513 if (client->id != client_id) {
3514 nc_server_ch_client_unlock(client);
3515 goto cleanup;
3516 }
Michal Vasko6bb116b2016-10-26 13:53:46 +02003517
Michal Vasko2e6defd2016-10-07 15:48:15 +02003518 ++cur_attempts;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003519
3520 /* try to find our endpoint again */
Peter Feiged05f2252018-09-03 08:09:47 +00003521 for (next_endpt_index = 0; next_endpt_index < client->ch_endpt_count; ++next_endpt_index) {
3522 if (!strcmp(client->ch_endpts[next_endpt_index].name, cur_endpt_name)) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003523 break;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003524 }
Michal Vasko4cb8de52018-04-23 14:38:07 +02003525 }
3526
Peter Feiged05f2252018-09-03 08:09:47 +00003527 if (next_endpt_index >= client->ch_endpt_count) {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003528 /* endpoint was removed, start with the first one */
Peter Feiged05f2252018-09-03 08:09:47 +00003529 next_endpt_index = 0;
Michal Vasko4cb8de52018-04-23 14:38:07 +02003530 cur_attempts = 0;
3531 } else if (cur_attempts == client->max_attempts) {
3532 /* we have tried to connect to this endpoint enough times */
Peter Feiged05f2252018-09-03 08:09:47 +00003533 if (next_endpt_index < client->ch_endpt_count - 1) {
Michal Vasko2e6defd2016-10-07 15:48:15 +02003534 /* just go to the next endpoint */
Peter Feiged05f2252018-09-03 08:09:47 +00003535 ++next_endpt_index;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003536 } else {
Michal Vasko4cb8de52018-04-23 14:38:07 +02003537 /* cur_endpoint is the last, start with the first one */
Michal Vasko2a225342018-09-05 08:38:34 +02003538 next_endpt_index = 0;
Michal Vasko2e6defd2016-10-07 15:48:15 +02003539 }
3540
3541 cur_attempts = 0;
3542 } /* else we keep the current one */
3543 }
Peter Feiged05f2252018-09-03 08:09:47 +00003544
3545 cur_endpt = &client->ch_endpts[next_endpt_index];
3546 free(cur_endpt_name);
3547 cur_endpt_name = strdup(cur_endpt->name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003548 }
3549
3550cleanup:
Michal Vasko05532772021-06-03 12:12:38 +02003551 VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name);
Michal Vasko9550cf12017-03-21 15:33:58 +01003552 free(cur_endpt_name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02003553 free(data->client_name);
3554 free(data);
3555 return NULL;
3556}
3557
3558API int
Michal Vaskof1c26c22021-04-12 16:34:33 +02003559nc_connect_ch_client_dispatch(const char *client_name, int (*session_clb)(const char *client_name,
Michal Vaskoadf30f02019-06-24 09:34:47 +02003560 struct nc_session *new_session))
Michal Vasko3f05a092018-03-13 10:39:49 +01003561{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003562 int ret;
3563 pthread_t tid;
3564 struct nc_ch_client_thread_arg *arg;
3565
3566 if (!client_name) {
3567 ERRARG("client_name");
3568 return -1;
3569 } else if (!session_clb) {
3570 ERRARG("session_clb");
3571 return -1;
3572 }
3573
3574 arg = malloc(sizeof *arg);
3575 if (!arg) {
3576 ERRMEM;
3577 return -1;
3578 }
3579 arg->client_name = strdup(client_name);
3580 if (!arg->client_name) {
3581 ERRMEM;
3582 free(arg);
3583 return -1;
3584 }
3585 arg->session_clb = session_clb;
3586
3587 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
3588 if (ret) {
Michal Vasko05532772021-06-03 12:12:38 +02003589 ERR(NULL, "Creating a new thread failed (%s).", strerror(ret));
Michal Vasko2e6defd2016-10-07 15:48:15 +02003590 free(arg->client_name);
3591 free(arg);
3592 return -1;
3593 }
3594 /* the thread now manages arg */
3595
3596 pthread_detach(tid);
3597
3598 return 0;
3599}
3600
Radek Krejci53691be2016-02-22 13:58:37 +01003601#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02003602
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003603API time_t
3604nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02003605{
Michal Vasko2e6defd2016-10-07 15:48:15 +02003606 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02003607 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02003608 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02003609 }
3610
Michal Vasko2e6defd2016-10-07 15:48:15 +02003611 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02003612}
Michal Vasko3486a7c2017-03-03 13:28:07 +01003613
3614API void
Michal Vasko71dbd772021-03-23 14:08:37 +01003615nc_session_inc_notif_status(struct nc_session *session)
Michal Vasko3486a7c2017-03-03 13:28:07 +01003616{
3617 if (!session || (session->side != NC_SERVER)) {
3618 ERRARG("session");
3619 return;
3620 }
3621
Michal Vasko71dbd772021-03-23 14:08:37 +01003622 ++session->opts.server.ntf_status;
3623}
3624
3625API void
3626nc_session_dec_notif_status(struct nc_session *session)
3627{
3628 if (!session || (session->side != NC_SERVER)) {
3629 ERRARG("session");
3630 return;
3631 }
3632
3633 if (session->opts.server.ntf_status) {
3634 --session->opts.server.ntf_status;
3635 }
Michal Vasko3486a7c2017-03-03 13:28:07 +01003636}
3637
3638API int
3639nc_session_get_notif_status(const struct nc_session *session)
3640{
3641 if (!session || (session->side != NC_SERVER)) {
3642 ERRARG("session");
3643 return 0;
3644 }
3645
3646 return session->opts.server.ntf_status;
Michal Vasko086311b2016-01-08 09:53:11 +01003647}
Michal Vasko8f430592019-02-26 08:32:54 +01003648
3649API int
3650nc_session_is_callhome(const struct nc_session *session)
3651{
3652 if (!session || (session->side != NC_SERVER)) {
3653 ERRARG("session");
3654 return 0;
3655 }
3656
3657 if (session->flags & NC_SESSION_CALLHOME) {
3658 return 1;
3659 }
3660
3661 return 0;
3662}